diff --git a/lib/minikanren/relations.sx b/lib/minikanren/relations.sx index 10f5a289..4d78f1e1 100644 --- a/lib/minikanren/relations.sx +++ b/lib/minikanren/relations.sx @@ -65,3 +65,19 @@ (conde ((nullo l) (== n :z)) ((fresh (a d n-1) (conso a d l) (== n (list :s n-1)) (lengtho d n-1)))))) + +(define + inserto + (fn + (a l p) + (conde + ((conso a l p)) + ((fresh (h t pt) (conso h t l) (conso h pt p) (inserto a t pt)))))) + +(define + permuteo + (fn + (l p) + (conde + ((nullo l) (nullo p)) + ((fresh (a d perm-d) (conso a d l) (permuteo d perm-d) (inserto a perm-d p)))))) diff --git a/lib/minikanren/tests/relations.sx b/lib/minikanren/tests/relations.sx index 40cc5fc8..ffbdfcf6 100644 --- a/lib/minikanren/tests/relations.sx +++ b/lib/minikanren/tests/relations.sx @@ -224,4 +224,68 @@ (run 1 q (lengtho q (list :s (list :s :z)))) (list (list (make-symbol "_.0") (make-symbol "_.1")))) +;; --- inserto --- + +(mk-test + "inserto-front" + (run* q (inserto 0 (list 1 2 3) q)) + (list + (list 0 1 2 3) + (list 1 0 2 3) + (list 1 2 0 3) + (list 1 2 3 0))) + +(mk-test + "inserto-empty" + (run* q (inserto 0 (list) q)) + (list (list 0))) + +;; --- permuteo --- + +(mk-test "permuteo-empty" (run* q (permuteo (list) q)) (list (list))) + +(mk-test + "permuteo-singleton" + (run* q (permuteo (list 42) q)) + (list (list 42))) + +(mk-test + "permuteo-two" + (run* q (permuteo (list 1 2) q)) + (list (list 1 2) (list 2 1))) + +(mk-test + "permuteo-three-as-set" + (let + ((perms (run* q (permuteo (list 1 2 3) q)))) + (and + (= (len perms) 6) + (and + (some (fn (p) (= p (list 1 2 3))) perms) + (and + (some + (fn (p) (= p (list 2 1 3))) + perms) + (and + (some + (fn (p) (= p (list 1 3 2))) + perms) + (and + (some + (fn (p) (= p (list 2 3 1))) + perms) + (and + (some + (fn (p) (= p (list 3 1 2))) + perms) + (some + (fn (p) (= p (list 3 2 1))) + perms)))))))) + true) + +(mk-test + "permuteo-backward-finds-input" + (run 1 q (permuteo q (list "a" "b" "c"))) + (list (list "a" "b" "c"))) + (mk-tests-run!) diff --git a/plans/minikanren-on-sx.md b/plans/minikanren-on-sx.md index 80984e36..0bddf5a0 100644 --- a/plans/minikanren-on-sx.md +++ b/plans/minikanren-on-sx.md @@ -112,7 +112,9 @@ Key semantic mappings: - [x] `reverseo` `l` `r` — reverse of list. Forward is fast; backward is `run 1`-clean, `run*` diverges due to interleaved unbounded list search (canonical TRS issue). - [ ] `flatteno` `l` `f` — flatten nested lists (deferred — needs atom predicate) -- [ ] `permuteo` `l` `p` — permutation of list (deferred to Phase 5 with `matche`) +- [x] `permuteo` `l` `p` — permutation of list. Built on `inserto` (insert at any + position) and recursive permutation of tail. All 6 perms of (1 2 3) generated + forward; backward `run 1 q (permuteo q (a b c))` finds the input. - [x] `lengtho` `l` `n` — length as a relation, Peano-encoded: `:z` / `(:s :z)` / `(:s (:s :z))` ... matches TRS. Forward is direct; backward enumerates lists of a given length. @@ -159,6 +161,9 @@ _(none yet)_ _Newest first._ +- **2026-05-08** — **Phase 4 piece C — permuteo + inserto**: standard recursive + insert-at-any-position + permute-tail. 7 new tests, including all-6-perms-of-3 + as a set check. 208/208 cumulative. - **2026-05-07** — **Phase 5 piece C — nafc**: `lib/minikanren/nafc.sx`. Three-line primitive: stream-take 1; if empty, `(unit s)`, else `mzero`. 7 tests including double-negation and use as a guard. 201/201 cumulative.