; End-to-end tests of the classic-program archetypes — running APL
; source through the full pipeline (tokenize → parse → eval-ast → runtime).
;
; These mirror the algorithms documented in lib/apl/tests/programs/*.apl
; but use forms our pipeline supports today (named functions instead of
; the inline ⍵← rebinding idiom; multi-stmt over single one-liners).

(define mkrv (fn (arr) (get arr :ravel)))
(define mksh (fn (arr) (get arr :shape)))

; ---------- factorial via ∇ recursion (cf. n-queens style) ----------

(apl-test
  "e2e: factorial 5! = 120"
  (mkrv (apl-run "fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 5"))
  (list 120))

(apl-test
  "e2e: factorial 7! = 5040"
  (mkrv (apl-run "fact ← {0=⍵:1 ⋄ ⍵×∇⍵-1} ⋄ fact 7"))
  (list 5040))

(apl-test
  "e2e: factorial via ×/⍳N (no recursion)"
  (mkrv (apl-run "fact ← {×/⍳⍵} ⋄ fact 6"))
  (list 720))

; ---------- sum / triangular numbers (sum-1..N) ----------

(apl-test
  "e2e: triangular(10) = 55"
  (mkrv (apl-run "tri ← {+/⍳⍵} ⋄ tri 10"))
  (list 55))

(apl-test
  "e2e: triangular(100) = 5050"
  (mkrv (apl-run "tri ← {+/⍳⍵} ⋄ tri 100"))
  (list 5050))

; ---------- sum of squares ----------

(apl-test
  "e2e: sum-of-squares 1..5 = 55"
  (mkrv (apl-run "ss ← {+/⍵×⍵} ⋄ ss ⍳5"))
  (list 55))

(apl-test
  "e2e: sum-of-squares 1..10 = 385"
  (mkrv (apl-run "ss ← {+/⍵×⍵} ⋄ ss ⍳10"))
  (list 385))

; ---------- divisor-counting (prime-sieve building blocks) ----------

(apl-test
  "e2e: divisor counts 1..5 via outer mod"
  (mkrv (apl-run "P ← ⍳ 5 ⋄ +⌿ 0 = P ∘.| P"))
  (list 1 2 2 3 2))

(apl-test
  "e2e: divisor counts 1..10"
  (mkrv (apl-run "P ← ⍳ 10 ⋄ +⌿ 0 = P ∘.| P"))
  (list 1 2 2 3 2 4 2 4 3 4))

(apl-test
  "e2e: prime-mask 1..10 (count==2)"
  (mkrv (apl-run "P ← ⍳ 10 ⋄ 2 = +⌿ 0 = P ∘.| P"))
  (list 0 1 1 0 1 0 1 0 0 0))

; ---------- monadic primitives chained ----------

(apl-test
  "e2e: sum of |abs| = 15"
  (mkrv (apl-run "+/|¯1 ¯2 ¯3 ¯4 ¯5"))
  (list 15))

(apl-test
  "e2e: max of squares 1..6"
  (mkrv (apl-run "⌈/(⍳6)×⍳6"))
  (list 36))

; ---------- nested named functions ----------

(apl-test
  "e2e: compose dbl and sq via two named fns"
  (mkrv (apl-run "dbl ← {⍵+⍵} ⋄ sq ← {⍵×⍵} ⋄ sq dbl 3"))
  (list 36))

(apl-test
  "e2e: max-of-two as named dyadic fn"
  (mkrv (apl-run "mx ← {⍺⌈⍵} ⋄ 5 mx 3"))
  (list 5))

(apl-test
  "e2e: sqrt-via-newton 1 step from 1 → 2.5"
  (mkrv (apl-run "step ← {(⍵+⍺÷⍵)÷2} ⋄ 4 step 1"))
  (list 2.5))

(begin
  (apl-test
    "life.apl: blinker 5×5 → vertical blinker"
    (mkrv
      (apl-run
        "life ← {⊃1 ⍵ ∨.∧ 3 4 = +/ +/ ¯1 0 1 ∘.⊖ ¯1 0 1 ⌽¨ ⊂⍵} ⋄ life 5 5 ⍴ 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0"))
    (list 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0))
  (apl-test
    "life.apl: blinker oscillates (period 2)"
    (mkrv
      (apl-run
        "life ← {⊃1 ⍵ ∨.∧ 3 4 = +/ +/ ¯1 0 1 ∘.⊖ ¯1 0 1 ⌽¨ ⊂⍵} ⋄ life life 5 5 ⍴ 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0"))
    (list 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0))
  (apl-test
    "life.apl: 2×2 block stable"
    (mkrv
      (apl-run
        "life ← {⊃1 ⍵ ∨.∧ 3 4 = +/ +/ ¯1 0 1 ∘.⊖ ¯1 0 1 ⌽¨ ⊂⍵} ⋄ life 4 4 ⍴ 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0"))
    (list 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0))
  (apl-test
    "life.apl: empty grid stays empty"
    (mkrv
      (apl-run
        "life ← {⊃1 ⍵ ∨.∧ 3 4 = +/ +/ ¯1 0 1 ∘.⊖ ¯1 0 1 ⌽¨ ⊂⍵} ⋄ life 5 5 ⍴ 0"))
    (list 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
  (apl-test
    "life.apl: source-file as-written runs"
    (let
      ((dfn (apl-run-file "lib/apl/tests/programs/life.apl"))
        (board
          (apl-run "5 5 ⍴ 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0")))
      (get (apl-call-dfn-m dfn board) :ravel))
    (list 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0)))

(begin
  (apl-test
    "quicksort.apl: 11-element with duplicates"
    (begin
      (apl-rng-seed! 42)
      (mkrv
        (apl-run
          "quicksort ← {1≥≢⍵:⍵ ⋄ p←⍵⌷⍨?≢⍵ ⋄ (∇⍵⌿⍨⍵<p),(p=⍵)/⍵,∇⍵⌿⍨⍵>p} ⋄ quicksort 3 1 4 1 5 9 2 6 5 3 5")))
    (list 1 1 2 3 3 4 5 5 5 6 9))
  (apl-test
    "quicksort.apl: already sorted"
    (begin
      (apl-rng-seed! 42)
      (mkrv
        (apl-run
          "quicksort ← {1≥≢⍵:⍵ ⋄ p←⍵⌷⍨?≢⍵ ⋄ (∇⍵⌿⍨⍵<p),(p=⍵)/⍵,∇⍵⌿⍨⍵>p} ⋄ quicksort 1 2 3 4 5")))
    (list 1 2 3 4 5))
  (apl-test
    "quicksort.apl: reverse sorted"
    (begin
      (apl-rng-seed! 42)
      (mkrv
        (apl-run
          "quicksort ← {1≥≢⍵:⍵ ⋄ p←⍵⌷⍨?≢⍵ ⋄ (∇⍵⌿⍨⍵<p),(p=⍵)/⍵,∇⍵⌿⍨⍵>p} ⋄ quicksort 5 4 3 2 1")))
    (list 1 2 3 4 5))
  (apl-test
    "quicksort.apl: all equal"
    (begin
      (apl-rng-seed! 42)
      (mkrv
        (apl-run
          "quicksort ← {1≥≢⍵:⍵ ⋄ p←⍵⌷⍨?≢⍵ ⋄ (∇⍵⌿⍨⍵<p),(p=⍵)/⍵,∇⍵⌿⍨⍵>p} ⋄ quicksort 7 7 7 7")))
    (list 7 7 7 7))
  (apl-test
    "quicksort.apl: single element"
    (begin
      (apl-rng-seed! 42)
      (mkrv
        (apl-run
          "quicksort ← {1≥≢⍵:⍵ ⋄ p←⍵⌷⍨?≢⍵ ⋄ (∇⍵⌿⍨⍵<p),(p=⍵)/⍵,∇⍵⌿⍨⍵>p} ⋄ quicksort ,42")))
    (list 42))
  (apl-test
    "quicksort.apl: matches grade-up"
    (begin
      (apl-rng-seed! 42)
      (mkrv
        (apl-run
          "V ← 8 3 1 9 2 7 5 6 4 ⋄ quicksort ← {1≥≢⍵:⍵ ⋄ p←⍵⌷⍨?≢⍵ ⋄ (∇⍵⌿⍨⍵<p),(p=⍵)/⍵,∇⍵⌿⍨⍵>p} ⋄ quicksort V")))
    (list 1 2 3 4 5 6 7 8 9))
  (apl-test
    "quicksort.apl: source-file as-written runs"
    (begin
      (apl-rng-seed! 42)
      (let
        ((dfn (apl-run-file "lib/apl/tests/programs/quicksort.apl"))
          (vec (apl-run "5 2 8 1 9 3 7 4 6")))
        (get (apl-call-dfn-m dfn vec) :ravel)))
    (list 1 2 3 4 5 6 7 8 9)))
