diff --git a/lib/apl/conformance.sh b/lib/apl/conformance.sh index 4788fc6e..22df7a5a 100755 --- a/lib/apl/conformance.sh +++ b/lib/apl/conformance.sh @@ -13,7 +13,7 @@ if [ ! -x "$SX_SERVER" ]; then exit 1 fi -SUITES=(structural operators dfn tradfn valence programs system) +SUITES=(structural operators dfn tradfn valence programs system idioms) OUT_JSON="lib/apl/scoreboard.json" OUT_MD="lib/apl/scoreboard.md" diff --git a/lib/apl/scoreboard.json b/lib/apl/scoreboard.json index c776d7a3..342aece9 100644 --- a/lib/apl/scoreboard.json +++ b/lib/apl/scoreboard.json @@ -6,9 +6,10 @@ "tradfn": {"pass": 20, "fail": 0}, "valence": {"pass": 14, "fail": 0}, "programs": {"pass": 46, "fail": 0}, - "system": {"pass": 13, "fail": 0} + "system": {"pass": 13, "fail": 0}, + "idioms": {"pass": 34, "fail": 0} }, - "total_pass": 328, + "total_pass": 362, "total_fail": 0, - "total": 328 + "total": 362 } diff --git a/lib/apl/scoreboard.md b/lib/apl/scoreboard.md index 4f6592d5..60ec34b7 100644 --- a/lib/apl/scoreboard.md +++ b/lib/apl/scoreboard.md @@ -11,7 +11,8 @@ _Generated by `lib/apl/conformance.sh`_ | valence | 14 | 0 | 14 | | programs | 46 | 0 | 46 | | system | 13 | 0 | 13 | -| **Total** | **328** | **0** | **328** | +| idioms | 34 | 0 | 34 | +| **Total** | **362** | **0** | **362** | ## Notes diff --git a/lib/apl/test.sh b/lib/apl/test.sh index fbd0f025..4c48ba02 100755 --- a/lib/apl/test.sh +++ b/lib/apl/test.sh @@ -33,6 +33,7 @@ cat > "$TMPFILE" << 'EPOCHS' (load "lib/apl/tests/valence.sx") (load "lib/apl/tests/programs.sx") (load "lib/apl/tests/system.sx") +(load "lib/apl/tests/idioms.sx") (epoch 4) (eval "(list apl-test-pass apl-test-fail)") EPOCHS diff --git a/lib/apl/tests/idioms.sx b/lib/apl/tests/idioms.sx new file mode 100644 index 00000000..e9de393f --- /dev/null +++ b/lib/apl/tests/idioms.sx @@ -0,0 +1,224 @@ +; 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)) diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index 2dfbba53..30d98d1d 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -102,7 +102,7 @@ Core mapping: - [x] `quicksort.apl` — the classic Roger Hui one-liner - [x] System functions: `⎕FMT`, `⎕FR` (float repr), `⎕TS` (timestamp), `⎕IO`, `⎕ML` (migration level — fixed at 1), `⎕←` (print) - [x] Drive corpus to 100+ green -- [ ] Idiom corpus — `lib/apl/tests/idioms.sx` covering classic Roger Hui / Phil Last idioms +- [x] Idiom corpus — `lib/apl/tests/idioms.sx` covering classic Roger Hui / Phil Last idioms ## SX primitive baseline @@ -118,6 +118,7 @@ data; format for string templating. _Newest first._ +- 2026-05-07: Phase 6 idiom corpus — lib/apl/tests/idioms.sx; 34 classic idioms (sum, mean, max/min/range, scan, sort, reverse, first/last, take/drop, tally, mod, identity matrix, mult-table, factorial, parity count, all/any, mean-centered, ravel, rank); **all unchecked items in plan now ticked**; 362/362 - 2026-05-07: Phase 6 system fns + 100+ corpus — apl-quad-{io,ml,fr,ts,fmt,print}; ⎕FMT formats scalar/vector/matrix; ⎕TS returns 7-vector (epoch default); 328 tests >> 100 target; **drive-to-100 ticked**; +13 tests - 2026-05-07: Phase 6 quicksort — recursive less/eq/greater partition via apl-compress, deterministic-pivot variant; tests cover empty/single/sorted/reverse/duplicates/negatives; **all 5 classic programs done**; +9 tests; 315/315 - 2026-05-07: Phase 6 n-queens — permutation enumerate + diagonal-conflict filter; counts q(1..8) = 1,0,0,2,10,4,40,92 (OEIS A000170); apl-permutations + apl-queens; bumped test timeout 60→180s for q(8); +10 tests; 306/306