diff --git a/lib/minikanren/tests/latin.sx b/lib/minikanren/tests/latin.sx new file mode 100644 index 00000000..8a0afabd --- /dev/null +++ b/lib/minikanren/tests/latin.sx @@ -0,0 +1,61 @@ +;; lib/minikanren/tests/latin.sx — 2x2 Latin square via ino + all-distincto. +;; +;; A 2x2 Latin square has 2 distinct fillings: +;; ((1 2) (2 1)) and ((2 1) (1 2)). +;; The 3x3 version has 12 fillings but takes minutes under naive search; +;; full CLP(FD) (Phase 6 proper) would handle it in milliseconds. + +(define + latin-2x2 + (fn + (cells) + (let + ((c11 (nth cells 0)) + (c12 (nth cells 1)) + (c21 (nth cells 2)) + (c22 (nth cells 3)) + (dom (list 1 2))) + (mk-conj + (ino c11 dom) + (ino c12 dom) + (ino c21 dom) + (ino c22 dom) + (all-distincto (list c11 c12)) + (all-distincto (list c21 c22)) + (all-distincto (list c11 c21)) + (all-distincto (list c12 c22)))))) ;; col 2 + +(mk-test + "latin-2x2-count" + (let + ((squares (run* q (fresh (a b c d) (== q (list a b c d)) (latin-2x2 (list a b c d)))))) + (len squares)) + 2) + +(mk-test + "latin-2x2-as-set" + (let + ((squares (run* q (fresh (a b c d) (== q (list a b c d)) (latin-2x2 (list a b c d)))))) + (and + (= (len squares) 2) + (and + (some + (fn (s) (= s (list 1 2 2 1))) + squares) + (some + (fn (s) (= s (list 2 1 1 2))) + squares)))) + true) + +(mk-test + "latin-2x2-with-clue" + (run* + q + (fresh + (a b c d) + (== a 1) + (== q (list a b c d)) + (latin-2x2 (list a b c d)))) + (list (list 1 2 2 1))) + +(mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index 117f88b7..82f2c568 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -173,6 +173,12 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **2x2 Latin square**: small classic constraint demo using + `ino` + 4 `all-distincto` constraints. Enumerates exactly 2 squares + (`((1 2)(2 1))` and `((2 1)(1 2))`); a clue (top-left = 1) narrows to one. + 3 new tests, 269/269 cumulative. Note: 3x3 (12 squares) is the natural + showcase but too slow under naive enumerate-then-filter — needs Phase 6 + arc-consistency. - **2026-05-08** — **rembero / assoco / nth-o**: more standard list relations. rembero removes the first occurrence (uses `nafc (== a x)` to gate the skip clause, so it's well-defined on ground lists). assoco is alist