;; FizzBuzz Implementations
;; Demonstrates control flow, looping, and conditionals

(import assert :refer :all)
(import binding :refer :all)
(import control :refer :all)
(import dict :refer :all)
(import display :refer :all)
(import looping :refer :all)
(import math :refer :all)
(import threading :refer :all)

;; ============================================================================
;; Classic FizzBuzz using cond
;; ============================================================================

(define fizzbuzz
  (lambda (n)
    (cond
      ((= (% n 15) 0) "FizzBuzz")
      ((= (% n 3) 0) "Fizz")
      ((= (% n 5) 0) "Buzz")
      (else (string-concat n)))))

(assert-equal "1" (fizzbuzz 1))
(assert-equal "2" (fizzbuzz 2))
(assert-equal "Fizz" (fizzbuzz 3))
(assert-equal "Buzz" (fizzbuzz 5))
(assert-equal "FizzBuzz" (fizzbuzz 15))

(println "Classic FizzBuzz (1-20):")
(define i 1)
(while (<= i 20)
  (println (fizzbuzz i))
  (set! i (+ i 1)))

;; ============================================================================
;; Using for loop
;; ============================================================================

(println "\nUsing for loop (1-15):")
(do-list (n (seq 1 15))
  (println (fizzbuzz n)))

;; ============================================================================
;; Functional approach with map
;; ============================================================================

(println "\nFunctional approach (1-10):")
(define fizzbuzz-list
  (lambda (n)
    (map fizzbuzz (seq 1 n))))

(assert-equal (list "1" "2" "Fizz" "4" "Buzz" "Fizz" "7" "8" "Fizz" "Buzz") (fizzbuzz-list 10))
(println (fizzbuzz-list 10))

;; ============================================================================
;; FizzBuzz with when/unless
;; ============================================================================

(define fizzbuzz-when
  (lambda (n)
    (let ((result ""))
      (when (= (% n 3) 0)
        (set! result (string-concat result "Fizz")))
      (when (= (% n 5) 0)
        (set! result (string-concat result "Buzz")))
      (when (= result "")
        (set! result (string-concat n)))
      result)))

(assert-equal "Fizz" (fizzbuzz-when 3))
(assert-equal "Buzz" (fizzbuzz-when 5))
(assert-equal "FizzBuzz" (fizzbuzz-when 15))
(assert-equal "1" (fizzbuzz-when 1))

(println "\nUsing when (1-10):")
(do-list (n (seq 1 10))
  (println (fizzbuzz-when n)))

;; ============================================================================
;; Case statement version
;; ============================================================================

(define fizzbuzz-case
  (lambda (n)
    (case (% n 15)
      ((0) "FizzBuzz")
      ((3 6 9 12) "Fizz")
      ((5 10) "Buzz")
      (else (string-concat n)))))

(println "\nUsing case (note: case checks exact value, not pattern):")
(println "1-15 with case-based FizzBuzz:")
(do-list (n (seq 1 15))
  (println (fizzbuzz-case n)))

;; ============================================================================
;; Filter to find Fizz, Buzz, and FizzBuzz numbers
;; ============================================================================

(println "\nFiltering FizzBuzz numbers:")

(define range-1-30 (seq 1 30))

(define fizz-numbers
  (filter (lambda (n) (= (% n 3) 0)) range-1-30))

(define buzz-numbers
  (filter (lambda (n) (= (% n 5) 0)) range-1-30))

(define fizzbuzz-numbers
  (filter (lambda (n) (= (% n 15) 0)) range-1-30))

(assert-equal (list 3 6 9 12 15 18 21 24 27 30) fizz-numbers)
(assert-equal (list 5 10 15 20 25 30) buzz-numbers)
(assert-equal (list 15 30) fizzbuzz-numbers)
(println (string-concat "Fizz numbers (1-30): " fizz-numbers))
(println (string-concat "Buzz numbers (1-30): " buzz-numbers))
(println (string-concat "FizzBuzz numbers (1-30): " fizzbuzz-numbers))

;; ============================================================================
;; Count FizzBuzz occurrences
;; ============================================================================

(define count-fizzbuzz
  (lambda (n)
    (let ((results (map fizzbuzz (seq 1 n))))
      (dict
       :fizz (length (filter (lambda (x) (= x "Fizz")) results))
       :buzz (length (filter (lambda (x) (= x "Buzz")) results))
       :fizzbuzz (length (filter (lambda (x) (= x "FizzBuzz")) results))
       :numbers (length (filter (lambda (x) (not (or (= x "Fizz")
                                                       (= x "Buzz")
                                                       (= x "FizzBuzz"))))
                                results))))))

(println "\nCounts for 1-100:")
(define counts-100 (count-fizzbuzz 100))
(assert-equal 27 (get "fizz" counts-100))
(assert-equal 14 (get "buzz" counts-100))
(assert-equal 6 (get "fizzbuzz" counts-100))
(assert-equal 53 (get "numbers" counts-100))
(println counts-100)

(println "\nExample complete!")
