; APL idiom corpus — classic Roger Hui / Phil Last idioms expressed
; through our runtime primitives.  Each test names the APL one-liner
; and verifies the equivalent runtime call.

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

; ---------- reductions ----------

(apl-test
  "+/⍵ — sum"
  (mkrv (apl-reduce apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 15))

(apl-test
  "(+/⍵)÷⍴⍵ — mean"
  (mkrv
    (apl-div
      (apl-reduce apl-add (make-array (list 5) (list 1 2 3 4 5)))
      (apl-scalar 5)))
  (list 3))

(apl-test
  "⌈/⍵ — max"
  (mkrv (apl-reduce apl-max (make-array (list 6) (list 3 1 4 1 5 9))))
  (list 9))

(apl-test
  "⌊/⍵ — min"
  (mkrv (apl-reduce apl-min (make-array (list 6) (list 3 1 4 1 5 9))))
  (list 1))

(apl-test
  "(⌈/⍵)-⌊/⍵ — range"
  (mkrv
    (apl-sub
      (apl-reduce apl-max (make-array (list 6) (list 3 1 4 1 5 9)))
      (apl-reduce apl-min (make-array (list 6) (list 3 1 4 1 5 9)))))
  (list 8))

(apl-test
  "×/⍵ — product"
  (mkrv (apl-reduce apl-mul (make-array (list 4) (list 1 2 3 4))))
  (list 24))

(apl-test
  "+\\⍵ — running sum"
  (mkrv (apl-scan apl-add (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 3 6 10 15))

; ---------- sort / order ----------

(apl-test
  "⍵[⍋⍵] — sort ascending"
  (mkrv (apl-quicksort (make-array (list 5) (list 3 1 4 1 5))))
  (list 1 1 3 4 5))

(apl-test
  "⌽⍵ — reverse"
  (mkrv (apl-reverse (make-array (list 5) (list 1 2 3 4 5))))
  (list 5 4 3 2 1))

(apl-test
  "⊃⌽⍵ — last element"
  (mkrv
    (apl-disclose (apl-reverse (make-array (list 4) (list 10 20 30 40)))))
  (list 40))

(apl-test
  "1↑⍵ — first element"
  (mkrv
    (apl-take (apl-scalar 1) (make-array (list 4) (list 10 20 30 40))))
  (list 10))

(apl-test
  "1↓⍵ — drop first"
  (mkrv
    (apl-drop (apl-scalar 1) (make-array (list 4) (list 10 20 30 40))))
  (list 20 30 40))

(apl-test
  "¯1↓⍵ — drop last"
  (mkrv
    (apl-drop (apl-scalar -1) (make-array (list 4) (list 10 20 30 40))))
  (list 10 20 30))

; ---------- counts / membership ----------

(apl-test
  "≢⍵ — tally"
  (mkrv (apl-tally (make-array (list 7) (list 9 8 7 6 5 4 3))))
  (list 7))

(apl-test
  "+/⍵=v — count occurrences of v"
  (mkrv
    (apl-reduce
      apl-add
      (apl-eq (make-array (list 7) (list 1 2 3 2 1 3 2)) (apl-scalar 2))))
  (list 3))

(apl-test
  "0=N|M — divisibility test"
  (mkrv (apl-eq (apl-scalar 0) (apl-mod (apl-scalar 3) (apl-scalar 12))))
  (list 1))

; ---------- shape constructors ----------

(apl-test
  "N⍴1 — vector of N ones"
  (mkrv (apl-reshape (apl-scalar 5) (apl-scalar 1)))
  (list 1 1 1 1 1))

(apl-test
  "(N N)⍴0 — N×N zero matrix"
  (mkrv (apl-reshape (make-array (list 2) (list 3 3)) (apl-scalar 0)))
  (list 0 0 0 0 0 0 0 0 0))

(apl-test
  "⍳∘.=⍳ — N×N identity matrix"
  (mkrv
    (apl-outer apl-eq (apl-iota (apl-scalar 3)) (apl-iota (apl-scalar 3))))
  (list 1 0 0 0 1 0 0 0 1))

(apl-test
  "⍳∘.×⍳ — multiplication table"
  (mkrv
    (apl-outer apl-mul (apl-iota (apl-scalar 3)) (apl-iota (apl-scalar 3))))
  (list 1 2 3 2 4 6 3 6 9))

; ---------- numerical idioms ----------

(apl-test
  "+\\⍳N — triangular numbers"
  (mkrv (apl-scan apl-add (apl-iota (apl-scalar 5))))
  (list 1 3 6 10 15))

(apl-test
  "+/⍳N=N×(N+1)÷2 — sum of 1..N"
  (mkrv (apl-reduce apl-add (apl-iota (apl-scalar 10))))
  (list 55))

(apl-test
  "×/⍳N — factorial via iota"
  (mkrv (apl-reduce apl-mul (apl-iota (apl-scalar 5))))
  (list 120))

(apl-test
  "2|⍵ — parity (1=odd)"
  (mkrv (apl-mod (apl-scalar 2) (make-array (list 5) (list 1 2 3 4 5))))
  (list 1 0 1 0 1))

(apl-test
  "+/2|⍵ — count odd"
  (mkrv
    (apl-reduce
      apl-add
      (apl-mod (apl-scalar 2) (make-array (list 5) (list 1 2 3 4 5)))))
  (list 3))

; ---------- boolean idioms ----------

(apl-test
  "∧/⍵ — all-true"
  (mkrv (apl-reduce apl-and (make-array (list 4) (list 1 1 1 1))))
  (list 1))

(apl-test
  "∧/⍵ — all-true with zero is false"
  (mkrv (apl-reduce apl-and (make-array (list 4) (list 1 1 0 1))))
  (list 0))

(apl-test
  "∨/⍵ — any-true"
  (mkrv (apl-reduce apl-or (make-array (list 4) (list 0 0 1 0))))
  (list 1))

(apl-test
  "∨/⍵ — any-true all zero is false"
  (mkrv (apl-reduce apl-or (make-array (list 4) (list 0 0 0 0))))
  (list 0))

; ---------- selection / scaling ----------

(apl-test
  "⍵×⍵ — square each"
  (mkrv
    (apl-mul
      (make-array (list 4) (list 1 2 3 4))
      (make-array (list 4) (list 1 2 3 4))))
  (list 1 4 9 16))

(apl-test
  "+/⍵×⍵ — sum of squares"
  (mkrv
    (apl-reduce
      apl-add
      (apl-mul
        (make-array (list 4) (list 1 2 3 4))
        (make-array (list 4) (list 1 2 3 4)))))
  (list 30))

(apl-test
  "⍵-(+/⍵)÷⍴⍵ — mean-centered"
  (mkrv
    (apl-sub
      (make-array (list 5) (list 2 4 6 8 10))
      (apl-div
        (apl-reduce apl-add (make-array (list 5) (list 2 4 6 8 10)))
        (apl-scalar 5))))
  (list -4 -2 0 2 4))

; ---------- shape / structure ----------

(apl-test
  ",⍵ — ravel"
  (mkrv (apl-ravel (make-array (list 2 3) (list 1 2 3 4 5 6))))
  (list 1 2 3 4 5 6))

(apl-test
  "⍴⍴⍵ — rank"
  (mkrv
    (apl-shape (apl-shape (make-array (list 2 3) (list 1 2 3 4 5 6)))))
  (list 2))

(apl-test
  "src: +/⍳N → triangular(N)"
  (mkrv (apl-run "+/⍳100"))
  (list 5050))

(apl-test "src: ×/⍳N → N!" (mkrv (apl-run "×/⍳6")) (list 720))

(apl-test
  "src: ⌈/V — max"
  (mkrv (apl-run "⌈/3 1 4 1 5 9 2 6"))
  (list 9))

(apl-test
  "src: ⌊/V — min"
  (mkrv (apl-run "⌊/3 1 4 1 5 9 2 6"))
  (list 1))

(apl-test
  "src: range = (⌈/V) - ⌊/V"
  (mkrv (apl-run "(⌈/3 1 4 1 5 9 2 6) - ⌊/3 1 4 1 5 9 2 6"))
  (list 8))

(apl-test
  "src: +\\V — running sum"
  (mkrv (apl-run "+\\1 2 3 4 5"))
  (list 1 3 6 10 15))

(apl-test
  "src: ×\\V — running product"
  (mkrv (apl-run "×\\1 2 3 4 5"))
  (list 1 2 6 24 120))

(apl-test
  "src: V × V — squares"
  (mkrv (apl-run "(⍳5) × ⍳5"))
  (list 1 4 9 16 25))

(apl-test
  "src: +/V × V — sum of squares"
  (mkrv (apl-run "+/(⍳5) × ⍳5"))
  (list 55))

(apl-test "src: ∧/V — all-true" (mkrv (apl-run "∧/1 1 1 1")) (list 1))

(apl-test "src: ∨/V — any-true" (mkrv (apl-run "∨/0 0 1 0")) (list 1))

(apl-test "src: 0 = N|M — divides" (mkrv (apl-run "0 = 3 | 12")) (list 1))

(apl-test
  "src: 2 | V — parity"
  (mkrv (apl-run "2 | 1 2 3 4 5 6"))
  (list 1 0 1 0 1 0))

(apl-test
  "src: +/2|V — count odd"
  (mkrv (apl-run "+/2 | 1 2 3 4 5 6"))
  (list 3))

(apl-test "src: ⍴ V" (mkrv (apl-run "⍴ 1 2 3 4 5")) (list 5))

(apl-test
  "src: ⍴⍴ M — rank"
  (mkrv (apl-run "⍴ ⍴ (2 3) ⍴ ⍳6"))
  (list 2))

(apl-test
  "src: N⍴1 — vector of ones"
  (mkrv (apl-run "5 ⍴ 1"))
  (list 1 1 1 1 1))

(apl-test
  "src: ⍳N ∘.= ⍳N — identity matrix"
  (mkrv (apl-run "(⍳3) ∘.= ⍳3"))
  (list 1 0 0 0 1 0 0 0 1))

(apl-test
  "src: ⍳N ∘.× ⍳N — multiplication table"
  (mkrv (apl-run "(⍳3) ∘.× ⍳3"))
  (list 1 2 3 2 4 6 3 6 9))

(apl-test
  "src: V +.× V — dot product"
  (mkrv (apl-run "1 2 3 +.× 4 5 6"))
  (list 32))

(apl-test
  "src: ∧.= V — vectors equal?"
  (mkrv (apl-run "1 2 3 ∧.= 1 2 3"))
  (list 1))

(apl-test
  "src: V[1] — first element"
  (mkrv (apl-run "(10 20 30 40)[1]"))
  (list 10))

(apl-test
  "src: 1↑V — first via take"
  (mkrv (apl-run "1 ↑ 10 20 30 40"))
  (list 10))

(apl-test
  "src: 1↓V — drop first"
  (mkrv (apl-run "1 ↓ 10 20 30 40"))
  (list 20 30 40))

(apl-test
  "src: ¯1↓V — drop last"
  (mkrv (apl-run "¯1 ↓ 10 20 30 40"))
  (list 10 20 30))

(apl-test
  "src: ⌽V — reverse"
  (mkrv (apl-run "⌽ 1 2 3 4 5"))
  (list 5 4 3 2 1))

(apl-test
  "src: ≢V — tally"
  (mkrv (apl-run "≢ 9 8 7 6 5 4 3 2 1"))
  (list 9))

(apl-test
  "src: ,M — ravel"
  (mkrv (apl-run ", (2 3) ⍴ ⍳6"))
  (list 1 2 3 4 5 6))

(apl-test
  "src: A=V — count occurrences"
  (mkrv (apl-run "+/2 = 1 2 3 2 1 3 2"))
  (list 3))

(apl-test
  "src: ⌈/(V × V) — max squared"
  (mkrv (apl-run "⌈/(1 2 3 4 5) × 1 2 3 4 5"))
  (list 25))
