From ed0853f4a0deff23c80d4ac7b151a305648df11e Mon Sep 17 00:00:00 2001 From: giles Date: Thu, 7 May 2026 04:07:09 +0000 Subject: [PATCH] =?UTF-8?q?apl:=20primes=20sieve=20(2=3D+=E2=8C=BF0=3DA?= =?UTF-8?q?=E2=88=98.|A)/A=E2=86=90=E2=8D=B3N=20+=20apl-compress=20(+11=20?= =?UTF-8?q?tests,=20280/280)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/apl/conformance.sh | 2 +- lib/apl/runtime.sx | 28 +++++++++++++++ lib/apl/scoreboard.json | 7 ++-- lib/apl/scoreboard.md | 3 +- lib/apl/test.sh | 1 + lib/apl/tests/programs.sx | 58 +++++++++++++++++++++++++++++++ lib/apl/tests/programs/primes.apl | 16 +++++++++ plans/apl-on-sx.md | 3 +- 8 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 lib/apl/tests/programs.sx create mode 100644 lib/apl/tests/programs/primes.apl diff --git a/lib/apl/conformance.sh b/lib/apl/conformance.sh index 467b883f..3aa8c760 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) +SUITES=(structural operators dfn tradfn valence programs) OUT_JSON="lib/apl/scoreboard.json" OUT_MD="lib/apl/scoreboard.md" diff --git a/lib/apl/runtime.sx b/lib/apl/runtime.sx index cff8957d..e2117c93 100644 --- a/lib/apl/runtime.sx +++ b/lib/apl/runtime.sx @@ -796,6 +796,34 @@ ((result (filter (fn (x) (not (index-of b-ravel x))) a-ravel))) (make-array (list (len result)) result))))) +(define + apl-compress + (fn + (mask arr) + (let + ((mask-ravel (get mask :ravel)) (arr-ravel (get arr :ravel))) + (let + ((kept (filter (fn (i) (not (= 0 (nth mask-ravel i)))) (range 0 (len arr-ravel))))) + (let + ((picked (map (fn (i) (nth arr-ravel i)) kept))) + (make-array (list (len picked)) picked)))))) + +(define + apl-primes + (fn + (n) + (let + ((a (apl-iota (apl-scalar n)))) + (let + ((mod-table (apl-outer apl-mod a a))) + (let + ((zero-mask (apl-eq (apl-scalar 0) mod-table))) + (let + ((divisor-counts (apl-reduce-first apl-add zero-mask))) + (let + ((prime-mask (apl-eq (apl-scalar 2) divisor-counts))) + (apl-compress prime-mask a)))))))) + (define apl-reduce (fn diff --git a/lib/apl/scoreboard.json b/lib/apl/scoreboard.json index 98fd7834..e8265d8e 100644 --- a/lib/apl/scoreboard.json +++ b/lib/apl/scoreboard.json @@ -4,9 +4,10 @@ "operators": {"pass": 117, "fail": 0}, "dfn": {"pass": 24, "fail": 0}, "tradfn": {"pass": 20, "fail": 0}, - "valence": {"pass": 14, "fail": 0} + "valence": {"pass": 14, "fail": 0}, + "programs": {"pass": 11, "fail": 0} }, - "total_pass": 269, + "total_pass": 280, "total_fail": 0, - "total": 269 + "total": 280 } diff --git a/lib/apl/scoreboard.md b/lib/apl/scoreboard.md index 231fa535..e6d09b72 100644 --- a/lib/apl/scoreboard.md +++ b/lib/apl/scoreboard.md @@ -9,7 +9,8 @@ _Generated by `lib/apl/conformance.sh`_ | dfn | 24 | 0 | 24 | | tradfn | 20 | 0 | 20 | | valence | 14 | 0 | 14 | -| **Total** | **269** | **0** | **269** | +| programs | 11 | 0 | 11 | +| **Total** | **280** | **0** | **280** | ## Notes diff --git a/lib/apl/test.sh b/lib/apl/test.sh index b99b450e..33cd4c46 100755 --- a/lib/apl/test.sh +++ b/lib/apl/test.sh @@ -31,6 +31,7 @@ cat > "$TMPFILE" << 'EPOCHS' (load "lib/apl/tests/dfn.sx") (load "lib/apl/tests/tradfn.sx") (load "lib/apl/tests/valence.sx") +(load "lib/apl/tests/programs.sx") (epoch 4) (eval "(list apl-test-pass apl-test-fail)") EPOCHS diff --git a/lib/apl/tests/programs.sx b/lib/apl/tests/programs.sx new file mode 100644 index 00000000..4b7e85f6 --- /dev/null +++ b/lib/apl/tests/programs.sx @@ -0,0 +1,58 @@ +; Tests for classic APL programs (lib/apl/tests/programs/*.apl). +; Programs are showcase APL source; runtime impl is in lib/apl/runtime.sx. + +(define mkrv (fn (arr) (get arr :ravel))) +(define mksh (fn (arr) (get arr :shape))) + +; ===== primes (Sieve of Eratosthenes) ===== + +(apl-test "primes 1 → empty" (mkrv (apl-primes 1)) (list)) + +(apl-test "primes 2 → just 2" (mkrv (apl-primes 2)) (list 2)) + +(apl-test "primes 10 → 2 3 5 7" (mkrv (apl-primes 10)) (list 2 3 5 7)) + +(apl-test + "primes 20 → 2 3 5 7 11 13 17 19" + (mkrv (apl-primes 20)) + (list 2 3 5 7 11 13 17 19)) + +(apl-test + "primes 30" + (mkrv (apl-primes 30)) + (list 2 3 5 7 11 13 17 19 23 29)) + +(apl-test + "primes 50" + (mkrv (apl-primes 50)) + (list 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47)) + +(apl-test "primes 7 length" (first (mksh (apl-primes 7))) 4) + +(apl-test "primes 100 has 25 primes" (first (mksh (apl-primes 100))) 25) + +; ===== compress helper sanity ===== + +(apl-test + "compress 1 0 1 0 1 / 10 20 30 40 50" + (mkrv + (apl-compress + (make-array (list 5) (list 1 0 1 0 1)) + (make-array (list 5) (list 10 20 30 40 50)))) + (list 10 30 50)) + +(apl-test + "compress all-zero mask → empty" + (mkrv + (apl-compress + (make-array (list 3) (list 0 0 0)) + (make-array (list 3) (list 1 2 3)))) + (list)) + +(apl-test + "compress all-one mask → full vector" + (mkrv + (apl-compress + (make-array (list 3) (list 1 1 1)) + (make-array (list 3) (list 1 2 3)))) + (list 1 2 3)) diff --git a/lib/apl/tests/programs/primes.apl b/lib/apl/tests/programs/primes.apl new file mode 100644 index 00000000..4afd9f2a --- /dev/null +++ b/lib/apl/tests/programs/primes.apl @@ -0,0 +1,16 @@ +⍝ Sieve of Eratosthenes — the classic APL one-liner +⍝ primes ← (2=+⌿0=A∘.|A)/A←⍳N +⍝ +⍝ Read right-to-left: +⍝ A ← ⍳N : A is 1..N +⍝ A∘.|A : outer-product residue table — M[i,j] = A[j] mod A[i] +⍝ 0=... : boolean — true where A[i] divides A[j] +⍝ +⌿... : column sums — count of divisors per A[j] +⍝ 2=... : true for numbers with exactly 2 divisors (1 and self) → primes +⍝ .../A : compress — select A[j] where mask[j] is true +⍝ +⍝ Examples: +⍝ primes 10 → 2 3 5 7 +⍝ primes 30 → 2 3 5 7 11 13 17 19 23 29 + +primes ← {(2=+⌿0=⍵∘.|⍵)/⍵←⍳⍵} diff --git a/plans/apl-on-sx.md b/plans/apl-on-sx.md index 34ade48d..ce321e03 100644 --- a/plans/apl-on-sx.md +++ b/plans/apl-on-sx.md @@ -97,7 +97,7 @@ Core mapping: - [ ] Classic programs in `lib/apl/tests/programs/`: - [ ] `life.apl` — Conway's Game of Life as a one-liner using `⊂` `⊖` `⌽` `+/` - [ ] `mandelbrot.apl` — complex iteration with rank-polymorphic `+ × ⌊` (or real-axis subset) - - [ ] `primes.apl` — `(2=+⌿0=A∘.|A)/A←⍳N` sieve + - [x] `primes.apl` — `(2=+⌿0=A∘.|A)/A←⍳N` sieve - [ ] `n-queens.apl` — backtracking via reduce - [ ] `quicksort.apl` — the classic Roger Hui one-liner - [ ] System functions: `⎕FMT`, `⎕FR` (float repr), `⎕TS` (timestamp), `⎕IO`, `⎕ML` (migration level — fixed at 1), `⎕←` (print) @@ -118,6 +118,7 @@ data; format for string templating. _Newest first._ +- 2026-05-07: Phase 6 primes — sieve via outer-product residue + reduce-first + compress; apl-compress added; lib/apl/tests/programs/primes.apl source; +11 tests; 280/280 - 2026-05-07: Phase 5 conformance.sh + scoreboard.{json,md} — per-suite runner; current snapshot 269/269; **Phase 5 complete** - 2026-05-07: Phase 5 valence dispatch — apl-dfn-valence (AST scan for ⍺/⍵), apl-tradfn-valence (slot check), apl-call unified entry; +14 tests; 269/269 tests - 2026-05-07: Phase 5 control words — :If/:Else, :While, :For/:In, :Select/:Case via apl-tradfn-eval-block/stmt threading env; :Trap deferred; +10 tests (sum loop, factorial, dispatch, nested); 255/255 tests