;; For loops for integer ranges and lists. (1.01)

(use-modules (srfi srfi-1))

(define-syntax for
  (syntax-rules (in .. .= by)
    ;; Integer range exclusive by step.
    ((for index in start .. end by step body ...)
     (let* ((count (max (ceiling-quotient (- end start) step) 0))
            (final (+ start (* count step))))
       (do ((index start (+ index step)))
           ((= index final))
           body ...)))
    ;; Integer range inclusive by step.
    ((for index in start .= end by step body ...)
     (for index in start .. ((if (negative? step) - +) end 1) by step
          body ...))
    ;; Integer range exclusive default step.
    ((for index in start .. end body ...)
     (for index in start .. end by 1
          body ...))
    ;; Integer range inclusive default step.
    ((for index in start .= end body ...)
     (for index in start .= end by 1
          body ...))
    ;; Iterate over a list.
    ((for item in list body ...)
     (for-each (lambda (item)
                 body ...)
               list))))

;; Main.

(define (print-range start end step)
  (for i in start .. end by step
       (format #t "~A " i))
  (newline)
  (for i in start .= end by step
       (format #t "~A " i))
  (newline)
  (let ((n (max (ceiling-quotient (- end start) step) 0)))
    (for i in (iota n start step)
         (format #t "~A " i)))
  (newline))

(print-range   1   5  1)
(print-range   1  11  2)
(print-range -11  -1  2)
(print-range   5   1 -1)
(print-range  11   1 -2)
(print-range  -1 -11 -2)

;; Nested default step.

(define (pythagorean-triples n)
  (define (primitive-pythagorean-triple? x y z)
    (and (< x y z)
         (= (+ (* x x) (* y y)) (* z z))
         (= (gcd x y z) 1)))
  (let ((result '()))
    (for x in 1 .= n
         (for y in (+ x 1) .= n
              (for z in (+ y 1) .= n
                   (when (primitive-pythagorean-triple? x y z)
                         (set! result (cons (list x y z) result))))))
    (sort result (lambda (x y) (< (third x) (third y))))))

(display (pythagorean-triples 37))
(newline)