; fizzbuzz

(define range
  (case-lambda
    ((stop) (range 0 stop (if (negative? stop) -1 1)))
    ((start stop) (range start stop (if (< start stop) 1 -1)))
    ((start stop step)
      (let ((le? (if (negative? step) >= <=)))
        (let loop ((x start) (xs (list)))
          (if (le? stop x) (reverse xs)
            (loop (+ x step) (cons x xs))))))
    (else (error 'range "too many arguments"))))

(define (fb1 n) ; straight forward method
  (define (fb n)
    (cond ((zero? (modulo n 15)) 'FizzBuzz)
          ((zero? (modulo n 5)) 'Buzz)
          ((zero? (modulo n 3)) 'Fizz)
          (else n)))
  (map fb (range 1 n)))

(display (fb1 26)) (newline)

(define (fb2 n) ; number theory method
  (define (fb n)
    (cdr (assoc (modulo (* n n n n) 15)
      `((1 . ,n) (6 . Fizz) (10 . Buzz) (0 . FizzBuzz)))))
  (map fb (range 1 n)))

(display (fb2 26)) (newline)

(define (fb3 n) ; sieving method
  (let ((xs (list->vector (range n))))
    (do ((i 3 (+ i 3))) ((<= n i))
      (vector-set! xs i 'Fizz))
    (do ((i 5 (+ i 5))) ((<= n i))
      (vector-set! xs i 'Buzz))
    (do ((i 15 (+ i 15))) ((<= n i))
      (vector-set! xs i 'FizzBuzz))
    (cdr (vector->list xs))))

(display (fb3 26)) (newline)

(define (fb4 n) ; simple euler method
  (let loop ((i 1) (s 0))
    (cond ((= n i) s)
          ((or (zero? (modulo i 3))
               (zero? (modulo i 5)))
            (loop (+ i 1) (+ s i)))
          (else (loop (+ i 1) s)))))

(display (fb4 1000)) (newline)

(define (fb5 n) ; gauss summation method
  (define (g n) (* n (+ n 1) 1/2))
  (+ (* 3 (g (quotient (- n 1) 3)))
     (* 5 (g (quotient (- n 1) 5)))
     (* -15 (g (quotient (- n 1) 15)))))

(display (fb5 1000)) (newline)