;; Fib server — long-lived process that computes fibonacci numbers on
;; request. Tests recursive function evaluation inside a server loop.

(define er-fib-test-count 0)
(define er-fib-test-pass 0)
(define er-fib-test-fails (list))

(define
  er-fib-test
  (fn
    (name actual expected)
    (set! er-fib-test-count (+ er-fib-test-count 1))
    (if
      (= actual expected)
      (set! er-fib-test-pass (+ er-fib-test-pass 1))
      (append! er-fib-test-fails {:actual actual :expected expected :name name}))))

(define fib-ev erlang-eval-ast)

;; Fib + server-loop source. Standalone so each test can chain queries.
(define
  er-fib-server-src
  "Fib = fun (0) -> 0; (1) -> 1; (N) -> Fib(N-1) + Fib(N-2) end,
   FibSrv = fun () ->
     Loop = fun () ->
       receive
         {fib, N, From} -> From ! Fib(N), Loop();
         stop -> ok
       end
     end,
     Loop()
   end")

;; Base cases.
(er-fib-test
  "fib(0)"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Srv ! {fib, 0, Me},
       receive R -> Srv ! stop, R end"))
  0)

(er-fib-test
  "fib(1)"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Srv ! {fib, 1, Me},
       receive R -> Srv ! stop, R end"))
  1)

;; Larger values.
(er-fib-test
  "fib(10) = 55"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Srv ! {fib, 10, Me},
       receive R -> Srv ! stop, R end"))
  55)

(er-fib-test
  "fib(15) = 610"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Srv ! {fib, 15, Me},
       receive R -> Srv ! stop, R end"))
  610)

;; Multiple sequential queries to one server. Sum to avoid dict-equality.
(er-fib-test
  "sequential fib(5..8) sum"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Srv ! {fib, 5, Me}, A = receive Ra -> Ra end,
       Srv ! {fib, 6, Me}, B = receive Rb -> Rb end,
       Srv ! {fib, 7, Me}, C = receive Rc -> Rc end,
       Srv ! {fib, 8, Me}, D = receive Rd -> Rd end,
       Srv ! stop,
       A + B + C + D"))
  47)

;; Verify Fib obeys the recurrence — fib(n) = fib(n-1) + fib(n-2).
(er-fib-test
  "fib recurrence at n=12"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Srv ! {fib, 10, Me}, A = receive Ra -> Ra end,
       Srv ! {fib, 11, Me}, B = receive Rb -> Rb end,
       Srv ! {fib, 12, Me}, C = receive Rc -> Rc end,
       Srv ! stop,
       C - (A + B)"))
  0)

;; Two clients each get their own answer; main sums the results.
(er-fib-test
  "two clients sum"
  (fib-ev
    (str
      er-fib-server-src
      ", Me = self(),
       Srv = spawn(FibSrv),
       Client = fun (N) ->
         spawn(fun () ->
           Srv ! {fib, N, self()},
           receive R -> Me ! {result, R} end
         end)
       end,
       Client(7),
       Client(9),
       {result, A} = receive M1 -> M1 end,
       {result, B} = receive M2 -> M2 end,
       Srv ! stop,
       A + B"))
  47)

;; Trace queries via io-buffer.
(er-fib-test
  "trace fib 0..6"
  (do
    (er-io-flush!)
    (fib-ev
      (str
        er-fib-server-src
        ", Me = self(),
         Srv = spawn(FibSrv),
         Ask = fun (N) -> Srv ! {fib, N, Me}, receive R -> io:format(\"~p \", [R]) end end,
         Ask(0), Ask(1), Ask(2), Ask(3), Ask(4), Ask(5), Ask(6),
         Srv ! stop,
         done"))
    (er-io-buffer-content))
  "0 1 1 2 3 5 8 ")

(define
  er-fib-test-summary
  (str "fib " er-fib-test-pass "/" er-fib-test-count))
