;; Fibonacci Sequence Implementations
;; Demonstrates recursion, iteration, and functional approaches
;;
;; NOTE: Arl's compiler implements self-tail-call optimization (self-TCO).
;; The `generate-next` helper inside `fib-sequence` benefits from this:
;; `letrec` expands to `set!`, and the compiler applies self-TCO to
;; `(set! name (lambda ...))` just as it does for `(define name (lambda ...))`,
;; so the self-call in tail position is rewritten as a loop.
;;
;; The classic `fib-recursive` is NOT tail-recursive (it's doubly recursive),
;; so TCO does not apply there. For large inputs, prefer `fib-iterative`
;; or the sequence-based approach.

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

;; ============================================================================
;; Classic Recursive Implementation
;; ============================================================================

(define fib-recursive
  (lambda (n)
    (cond
      ((= n 0) 0)
      ((= n 1) 1)
      (else (+ (fib-recursive (- n 1))
               (fib-recursive (- n 2)))))))

(println "Recursive Fibonacci:")
(assert-equal 0 (fib-recursive 0))
(println (string-concat "fib(0) = " (fib-recursive 0)))
(assert-equal 1 (fib-recursive 1))
(println (string-concat "fib(1) = " (fib-recursive 1)))
(assert-equal 5 (fib-recursive 5))
(println (string-concat "fib(5) = " (fib-recursive 5)))
(assert-equal 55 (fib-recursive 10))
(println (string-concat "fib(10) = " (fib-recursive 10)))

;; ============================================================================
;; Iterative Implementation with While Loop
;; ============================================================================

(define fib-iterative
  (lambda (n)
    (if (< n 2)
        n
        (begin
          (define a 0)
          (define b 1)
          (define i 2)
          (while (<= i n)
            (define temp b)
            (set! b (+ a b))
            (set! a temp)
            (set! i (+ i 1)))
          b))))

(println "\nIterative Fibonacci:")
(assert-equal 55 (fib-iterative 10))
(println (string-concat "fib(10) = " (fib-iterative 10)))
(assert-equal 610 (fib-iterative 15))
(println (string-concat "fib(15) = " (fib-iterative 15)))

;; ============================================================================
;; Sequence Generation using unfold-like pattern
;; ============================================================================

(define fib-sequence
  (lambda (n)
    (if (< n 1)
        ()
        (if (= n 1)
            (list 0)
            (if (= n 2)
                (list 0 1)
                (let* ((init (list 0 1)))
                  (letrec ((generate-next
                            (lambda (lst count)
                              (if (>= count n)
                                  lst
                                  (let* ((len (length lst))
                                         (a (nth lst (- len 2)))
                                         (b (nth lst (- len 1)))
                                         (next (+ a b)))
                                    (generate-next (append lst (list next))
                                                  (+ count 1)))))))
                    (generate-next init 2))))))))

(println "\nFibonacci Sequence:")
(assert-equal (list 0 1 1 2 3 5 8 13 21 34) (fib-sequence 10))
(println (string-concat "First 10: " (fib-sequence 10)))
(assert-equal (list 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377) (fib-sequence 15))
(println (string-concat "First 15: " (fib-sequence 15)))

;; ============================================================================
;; Using reduce to sum fibonacci sequence
;; ============================================================================

(define sum-fib
  (lambda (n)
    (reduce + (fib-sequence n))))

(println "\nSum of Fibonacci:")
(assert-equal 88 (sum-fib 10))
(println (string-concat "Sum of first 10: " (sum-fib 10)))

;; ============================================================================
;; Memoized Fibonacci (using R environment for cache)
;; ============================================================================

(define make-memoized-fib
  (lambda ()
    (let ((cache (new.env)))
      (lambda (n)
        (if (< n 2)
            n
            (let ((cached (get (string-concat n) cache)))
              (if (null? cached)
                  (let ((result (+ (fib-recursive (- n 1))
                                  (fib-recursive (- n 2)))))
                    (assign (string-concat n) result cache)
                    result)
                  cached)))))))

;; Note: Full memoization would require modifying recursive calls to use cache
;; This is a simplified demonstration of the concept

(define fib10 (fib-recursive 10))
(define fib15 (fib-iterative 15))
(define seq10 (fib-sequence 10))
(define sum10 (sum-fib 10))
(assert-equal 55 fib10)
(assert-equal 610 fib15)
(assert-equal (list 0 1 1 2 3 5 8 13 21 34) seq10)
(assert-equal 88 sum10)

(println "\nExample complete!")
