Commit Graph

2514 Commits

Author SHA1 Message Date
f2817bb6be mk: phase 6C — fd-in + fd-label (domain narrowing + labelling)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
fd-in x dom-list: narrows x's domain. If x is a ground number, checks
membership; if x is a logic var, intersects existing domain (or sets
fresh) and stores via fd-set-domain. Fails if domain becomes empty.

fd-label vars: drives search by enumerating each var's domain. Each
var is unified with each value in its domain, in order, via mk-mplus
of singleton streams.

Forward: (fd-in x dom) (fd-label (list x)) iterates x over dom.
Intersection: two fd-in goals on the same var compose via dom-intersect.
Disjoint domains -> empty answer set. Ground value membership check
gates pass/fail. Composes with the rest of the miniKanren machinery —
fresh / conde / membero etc. all work alongside.

9 new tests, 586/586 cumulative.
2026-05-08 14:09:18 +00:00
c71da0e1cf mk: phase 6B — clpfd.sx domain primitives
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 58s
Foundation for native CLP(FD). The substitution dict carries a reserved
"_fd" key holding a constraint store:
  {:domains {var-name -> sorted-int-list}
   :constraints (... pending constraints ...)}

This commit ships only the domain machinery + accessors:
  fd-dom-from-list / fd-dom-range / fd-dom-empty? / fd-dom-singleton?
  fd-dom-min / fd-dom-max / fd-dom-member? / fd-dom-intersect /
  fd-dom-without

  fd-store-of / fd-domain-of / fd-set-domain / fd-with-store

fd-set-domain returns nil when the domain becomes empty (failure),
which is the wire signal subsequent constraint goals will consume.
The constraints field is reserved for the next iteration.

26 new tests, 577/577 cumulative.
2026-05-08 14:06:19 +00:00
25f709549e GUEST-plan: session snapshot — 551 tests, phases 1-5 complete
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
Capture the current state: 17 library files (1229 LOC), 61 test files
(4360 LOC), 551/551 tests passing. Phases 1-5 fully done; Phase 6
covered by minimal FD (ino, all-distincto) plus an intarith escape
hatch; Phase 7 documented via the cyclic-graph divergence test as
motivation for future tabling work.

The lib-guest validation experiment is conclusive: lib/minikanren/
unify.sx adds ~50 lines of local logic over lib/guest/match.sx's
~100-line kit. The kit earns its keep at roughly 3x by line count.

Classic miniKanren tests green: appendo forwards/backwards, Peano
arithmetic enumeration (pluso, *o, lto), 4-queens (both solutions),
Pythagorean triples, family-relation inference, symbolic
differentiation, pet/colour permutation puzzle, Latin square 2x2,
binary tree walker.
2026-05-08 12:32:57 +00:00
f8b9bde1a5 mk: zip-with-o — element-wise combine of two lists
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Like Haskell's zipWith but relational. (zip-with-o rel l1 l2 result)
applies a 3-arg combiner relation pointwise: rel l1[i] l2[i] result[i].

  (zip-with-o pluso-i (list 1 2 3) (list 10 20 30) q)
    -> ((11 22 33))

  (zip-with-o (fn (a b r) (== r (list a b))) (list :x :y) (list 1 2) q)
    -> (((:x 1) (:y 2)))

Different-length lists fail.

5 new tests, 551/551 cumulative.
2026-05-08 12:31:53 +00:00
2a36e692f4 mk: take-while-o + drop-while-o — predicate-driven prefix/suffix
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
(take-while-o pred l result): take elements from l while pred holds,
stopping at the first element that fails. (drop-while-o pred l result):
drop matching elements, return the rest including the first non-match.

Together: (take-while p l) ⊕ (drop-while p l) = l, verified by an
end-to-end roundtrip test.

8 new tests, 546/546 cumulative.
2026-05-08 12:30:53 +00:00
d1e00e2e9e mk: arith-progo — arithmetic progression generation
Some checks are pending
Test, Build, and Deploy / test-build-deploy (push) Has started running
(arith-progo start step len result): result is the list
(start, start+step, ..., start+(len-1)*step). Length 0 yields the
empty list. Negative steps and zero step are supported.

Useful for FD-style domain construction:
  (arith-progo 1 1 9 dom)  -> (1 2 3 4 5 6 7 8 9)

6 new tests, 538/538 cumulative.
2026-05-08 12:29:34 +00:00
de6fd1b183 mk: counto — count occurrences of x in l (intarith)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Walks the list with a recursive count. On a head match, recurse and
add 1 via pluso-i; on no match (nafc), recurse forwarding the count.
Empty list yields 0.

6 new tests, 532/532 cumulative.
2026-05-08 12:28:31 +00:00
f4a902a6df mk: nub-o — dedupe by keeping the last occurrence
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 56s
Walks the list; if the head appears in the tail (membero), drop it and
recurse; otherwise keep it and recurse. Result preserves only the
*last* occurrence of each value.

Caveat: with input like (1 1 1) the membero check succeeds with
multiplicity, so multiple (1) answers may emerge — each is shape-
identical, but the test deliberately checks every-result-is-(1) rather
than asserting answer count.

5 new tests, 526/526 cumulative.
2026-05-08 12:27:03 +00:00
d891831f08 mk: simplify-step-o — algebraic-identity simplifier (conda demo)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
Demonstrates conda for first-match-wins dispatch over a set of rewrite
rules: 0+x = x, x+0 = x, 0*y = 0, x*0 = 0, 1*x = x, x*1 = x, default
unchanged.

Six rules + a fall-through default, all wrapped in a single conda. The
first clause whose head succeeds commits to that rewrite. The fall-
through default ensures the relation always succeeds with at least the
unchanged input.

6 new tests, 521/521 cumulative.
2026-05-08 12:25:36 +00:00
091030f13e mk: flat-mapo — concatMap-style relation
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 34s
(flat-mapo rel l result): each element x of l is mapped to a list via
rel x list-from-x, and all such lists are concatenated to form result.

  (flat-mapo (fn (x r) (== r (list x x))) (list 1 2 3) q)
    -> ((1 1 2 2 3 3))

5 new tests, 515/515 cumulative.
2026-05-08 12:24:23 +00:00
f5ab66e1a3 mk: foldl-o — relational left fold
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 33s
Complement to foldr-o. The combiner relation has signature
(acc head new-acc) — accumulator first.

Examples:
  (foldl-o pluso-i (list 1 2 3 4 5) 0 q)  -> (15)
  (foldl-o *o-i    (list 1 2 3 4)   1 q)  -> (24)
  (foldl-o (fn (acc x r) (conso x acc r))  ; flipped conso
           (list 1 2 3 4) (list) q)        -> ((4 3 2 1))   ; reverse

5 new tests, 510/510 cumulative.
2026-05-08 12:23:40 +00:00
c51d52dae2 GUEST-plan: log foldr-o (post-commit oversight)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
2026-05-08 12:21:54 +00:00
3842496f3b mk: foldr-o — relational right fold
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Takes a 3-arg combiner relation, a list, and an initial accumulator,
produces the right-fold result. (rel a tail-result result) combines
the head with the recursive result.

Examples:
  (foldr-o appendo (list (list 1 2) (list 3) (list 4 5)) (list) q)
    -> ((1 2 3 4 5))                ; flatten

  (foldr-o conso (list 1 2 3) (list) q)
    -> ((1 2 3))                    ; rebuild list

4 new tests, 505/505 cumulative.
2026-05-08 12:21:42 +00:00
08f4a7babd mk: enumerate-i / enumerate-from-i — 501/501 milestone
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
(enumerate-i l result): result is l with each element paired with its
0-based index. (enumerate-from-i n l result): same but starts at n.

  (enumerate-i (list :a :b :c) q) -> (((0 :a) (1 :b) (2 :c)))

5 new tests, 501/501 cumulative.
2026-05-08 12:20:03 +00:00
221c7fef35 mk: partitiono — split list by predicate
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
(partitiono pred l yes no) — yes is the elements of l where pred
succeeds; no is the rest. Conde dispatches on each element via the
predicate goal vs nafc-of-the-predicate, threading the head through
the matching output list.

Composes with intarith / membero / etc. for any predicate-shaped goal:
  (partitiono (fn (x) (lto-i x 5)) (list 1 7 2 8 3) yes no)
    yes -> (1 2 3); no -> (7 8)

5 new tests, 496/496 cumulative.
2026-05-08 12:18:25 +00:00
363ebc8f04 mk: appendo3 — 3-list append
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Composes two appendos: (appendo a b mid) ∧ (appendo mid c r). Runs
forward (concatenate three known lists) and backward (recover any of
the three from the other two and the result).

5 new tests, 491/491 cumulative.
2026-05-08 12:16:40 +00:00
7ff72cefb2 mk: lengtho-i — integer-indexed length
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Drop-in fast replacement for Peano lengtho when the count fits in a
host integer. Two conde clauses: empty list -> 0; recurse, n = 1 +
length(tail). Uses pluso-i so the length walks to a native int.

5 new tests, 486/486 cumulative.
2026-05-08 12:15:53 +00:00
064ab2900b mk: sumo + producto — fold list to integer
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Sum and product over a list of ground integers via fold + intarith.
Empty list yields the identity (0 for sum, 1 for product). Recurse
combines the head with the recursively-computed tail value via
pluso-i / *o-i.

9 new tests, 481/481 cumulative.
2026-05-08 12:14:15 +00:00
4f5f8015fb mk: mino + maxo — find min/max of a list
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Two conde clauses each: singleton -> the element; multi -> compare head
against the recursive min/max of the tail and pick. Uses lteo-i / lto-i
for the comparisons, so the input must be ground integers.

mino + maxo can run together: (fresh (mn mx) (mino l mn) (maxo l mx)
(== q (list mn mx))) recovers both.

9 new tests, 472/472 cumulative.
2026-05-08 12:12:32 +00:00
c4b6f1fa0f mk: sortedo — list is non-decreasing (intarith)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
Three conde clauses: empty list / singleton list / two-or-more (where
the first two satisfy lteo-i and the rest is recursively sorted). Uses
ground-only integer comparison (intarith), so the input list must
walk to ground integers.

7 new tests, 463/463 cumulative.
2026-05-08 12:10:52 +00:00
6454603568 mk: subseto — every element of l1 is in l2
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 26s
Recursive: empty l1 trivially holds; otherwise the head is in l2 (via
membero) and the tail is a subset. Duplicates in l1 are allowed since
each is independently checked.

7 new tests, 456/456 cumulative.
2026-05-08 12:09:06 +00:00
4df277803d mk: cycle-free path search test
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 44s
Mitigation for the cyclic-graph divergence (see tests/cyclic-graph.sx).
Threads a `visited` accumulator through the recursion; each candidate
next-step is gated by `nafc (membero z visited)`. Terminates on graphs
with cycles, no Phase-7 tabling required for the simple acyclic-path
query.

Demonstrates a viable alternative to tabling for the common case where
the user wants finite path enumeration over a graph with cycles.

3 new tests, 449/449 cumulative.
2026-05-08 12:07:00 +00:00
58d78de32a mk: removeo-allo — remove every occurrence
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 49s
Three conde clauses: empty list -> empty result; head matches x ->
skip and recurse; head differs (nafc-gated) -> keep and recurse.
Distinct from rembero, which removes only the first occurrence.

5 new tests, 446/446 cumulative.
2026-05-08 12:04:17 +00:00
6bc3c14dac mk: btree-walko — binary-tree walker via matche
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 44s
Demo of matche dispatch + conde + recursion for tree traversal:
  (matche tree
    ((:leaf x)   (== v x))
    ((:node l r) (conde ((btree-walko l v)) ((btree-walko r v)))))

Test tree ((1 2) (3 (4 5))) yields all 5 leaves under run*. Also tests
membership (run 1) and absence.

4 new tests, 441/441 cumulative.
2026-05-08 12:02:13 +00:00
eb69039935 mk: swap-firsto — swap first two list elements
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s
Four conso calls express the (a b . rest) -> (b a . rest) rewrite as a
purely relational constraint. Self-inverse on length-2+ lists; runs
forward (swap given input) and backward (recover original from the
swapped form). Fails on lists shorter than 2.

6 new tests, 437/437 cumulative.
2026-05-08 11:59:41 +00:00
c04ddd105b mk: pairlisto — relational zip
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 39s
(pairlisto l1 l2 pairs): pairs is the zipped list of pairs (l1[i] l2[i]).
Recurses on both l1 and l2 in lockstep, building pairs in parallel.

Runs forward, can recover l1 given l2 and pairs, can recover l2 given
l1 and pairs. Different-length lists fail.

5 new tests, 431/431 cumulative.
2026-05-08 11:57:12 +00:00
136cacbd3f mk: iterate-no — apply a relation n times
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
(iterate-no rel n x result) holds when applying the 2-arg relation rel
n times (Peano n) starting from x produces result. Base case: zero
iterations means result equals x. Recursive case: rel x mid, then
iterate-no n-1 from mid.

Generalises common chains:
  succ iteration:  (iterate-no succ-rel n :z q) -> n in Peano
  list growth:     (iterate-no cons-rel n () q) -> n-element list

4 new tests, 426/426 cumulative.
2026-05-08 11:54:24 +00:00
6fc155ddd8 mk: rev-acco + rev-2o — accumulator-style reverse
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 29s
rev-acco is the standard tail-recursive reverse with an accumulator;
rev-2o starts the accumulator at the empty list. Faster than the
appendo-driven reverseo for forward queries because there is no nested
appendo per element.

Trade-off: rev-acco is asymmetric. The accumulator's initial-empty
cannot be enumerated backwards the way reverseo does, so reverseo is
still the right choice when both directions matter.

A test verifies rev-2o and reverseo agree on forward queries.

6 new tests, 422/422 cumulative.
2026-05-08 11:51:51 +00:00
d992788a03 mk: even-i / odd-i — host-arithmetic parity goals
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
Two-line definitions over project + host even?/odd?. Ground-only — no
relational behaviour, but they pair cleanly with membero for filtered
enumeration:
  (fresh (x) (membero x (list 1 2 3 4 5 6)) (even-i x) (== q x))
    -> (2 4 6)

5 new tests, 416/416 cumulative.
2026-05-08 11:49:47 +00:00
4d861575df mk: selecto — choose element + rest of list
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 29s
Classic miniKanren relation. (selecto x rest l) holds when l contains
x at any position with `rest` being everything else. Direct base case
(l = (x . rest)) plus the skip-head recursion that threads the head
through to the result rest.

Run modes: enumerate every (x, rest) split; recover rest given an
element; recover an element given the rest; (and ground/all combinations).

6 new tests, 411/411 cumulative.
2026-05-08 11:47:27 +00:00
e202c81a0d mk: subo — contiguous sublist relation
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 32s
Composes two appendos: l = front ++ s ++ back, equivalently
  (appendo front-and-s back l) and (appendo front s front-and-s).

Goal order matters: doing the (appendo ground:l) split first makes the
search finitary; the second appendo is then deterministic given
front-and-s and ground s. Reversing the order causes divergence on
failing inputs (the front search becomes unbounded).

7 new tests, 405/405 cumulative.
2026-05-08 11:45:31 +00:00
fc14a8063b mk: prefixo + suffixo — appendo-derived sublist relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s
Two-line definitions over appendo:
  (prefixo p l) ≡ ∃rest. (appendo p rest l)
  (suffixo s l) ≡ ∃front. (appendo front s l)

Both enumerate all prefixes/suffixes when called with a fresh first
arg, and serve as decision relations when called with both grounded.

9 new tests, 398/398 cumulative.
2026-05-08 11:40:28 +00:00
6ee02db2ab mk: palindromeo — list reads same forwards/backwards
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Two-line definition: a list is a palindrome iff it equals its reverse.
Direct composition of reverseo + ==.

7 new tests: empty / singleton / equal pair / unequal pair /
5-element-yes / 5-element-no / strings.

389/389 cumulative.
2026-05-08 11:38:29 +00:00
7b6cb64548 mk: not-membero — relational "x is not in l"
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
Mirrors the structure all-distincto already uses internally: walk the
list, ensure each element is not equal to x via nafc, recurse on tail.
Useful as a constraint-style filter:

  (membero x (list 1 2 3 4 5))
  (not-membero x (list 2 4))
    -> x in {1, 3, 5}

4 new tests, 382/382 cumulative.
2026-05-08 11:36:14 +00:00
c2b238635f mk: repeato + concato
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
repeato: produces (or recognizes) a list of n copies of a value, with n
Peano-encoded. Runs forward, backward (recover the count from a uniform
list), and bidirectionally.

concato: fold-appendo over a list-of-lists. (concato (list (list 1 2)
(list) (list 3 4 5)) q) -> ((1 2 3 4 5)).

10 new tests, 378/378 cumulative.
2026-05-08 11:34:28 +00:00
8c48a0be63 mk: tako + dropo — Peano-indexed prefix and suffix
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
(tako n l prefix) — prefix is the first n elements of l.
(dropo n l suffix) — suffix is l after dropping the first n.

Both use a Peano natural for the count. Round-trip holds:
  (tako n l) ⊕ (dropo n l) = l   (verified by an end-to-end test)

9 new tests, 368/368 cumulative.
2026-05-08 11:32:33 +00:00
54a58c704e mk: eveno + oddo — Peano parity predicates
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
eveno: zero, or (s (s m)) when m is even.
oddo:  one, or (s (s m)) when m is odd.

Both run forward (predicate test on a Peano number) and backward
(enumerate even / odd numbers). The two are mutually exclusive — no
number satisfies both.

12 new tests, 359/359 cumulative.
2026-05-08 11:30:02 +00:00
ada405b37b mk: defrel — Prolog-style relation-definition macro
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
(defrel (NAME ARGS...) (CLAUSE1 ...) (CLAUSE2 ...) ...) expands to
(define NAME (fn (ARGS...) (conde (CLAUSE1 ...) (CLAUSE2 ...) ...))).

Mirrors Prolog's `name(Args) :- goals.` shape. Inherits the Zzz-on-each-
clause laziness from conde, so user relations defined via defrel
terminate on partial answers without needing manual delay. Tests
redefine membero / listo / pluso through defrel and verify equivalence.

3 new tests, 347/347 cumulative.
2026-05-08 11:27:49 +00:00
99066430fd mk: lasto + init-o — last and not-last list relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 52s
lasto: x is the final element of l. Direct base case (l = (x)) plus
recurse-on-cdr.

init-o: init is l without its last element. Base case for singleton:
(== init ()). Otherwise recurse, threading the head through to the
init result via conso.

Together with appendo, the round-trip init append (list last) = l
holds, which is exercised by an end-to-end test.

8 new tests, 344/344 cumulative.
2026-05-08 11:25:12 +00:00
48835f2d4f mk: relational database queries — Datalog-style demo
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
tests/rdb.sx shows the library as a small Datalog engine over fact
tables. Each table is an SX list of tuples, wrapped by a relation that
does (membero (list ...) table). Queries compose selection, projection,
and joins entirely in run* / fresh / conde / membero / intarith / nafc.

Five queries: dept filter, salary > threshold, employee-project join,
intersection (engineers on a specific project), anyone on multiple
distinct projects.

5 new tests, 336/336 cumulative.
2026-05-08 11:22:12 +00:00
16fe22669a mk: cyclic-graph behaviour test — motivates Phase 7 tabling
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 28s
Demonstrates the naive-patho behaviour on a 2-cycle (a <-> b, plus
b -> c). Without Phase-7 tabling, the search produces ever-longer
paths: (a b), (a b a b), (a b a b a b), ... `run 5` truncates to a
finite prefix; `run*` diverges. Documenting this as a regression-style
test gives Phase 7 a concrete starting point.

3 new tests, 331/331 cumulative.
2026-05-08 11:19:29 +00:00
2d51a8c4ea mk: numbero / stringo / symbolo type predicates
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Ground-only type tests via project. Each succeeds iff its argument
walks to the corresponding host value type. Composes with membero for
type-filtered enumeration:

  (fresh (x) (membero x (list 1 "a" 2 "b" 3)) (numbero x) (== q x))
    -> (1 2 3)

12 new tests, 328/328 cumulative. Caveat: SX keywords are strings, so
(stringo :k) succeeds.
2026-05-08 11:17:27 +00:00
b4c1253891 mk: graph reachability via patho — classic miniKanren
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Defines a small graph as a fact list, edgeo for fact lookup, and patho
that recursively constructs paths. Direct-edge clause yields (x y);
otherwise traverse one edge to z, recurse for z->y, prepend x.

Enumerates all paths between two nodes, including alternates through
shortcut edges:
  (run* q (patho :a :d q))
    -> ((:a :b :c :d) (:a :c :d))    ; both routes

6 new tests, 316/316 cumulative.
2026-05-08 11:15:24 +00:00
e7dca2675c mk: everyo / someo — predicate-style relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
(everyo rel l): every element of l satisfies the unary relation rel.
(someo rel l): some element does. Both compose with intarith and other
predicate-shaped goals:
  (everyo (fn (x) (lto-i x 10)) (list 1 5 9))    -> succeeds
  (someo  (fn (x) (lto-i 100 x)) (list 5 50 200)) -> succeeds

10 new tests, 310/310 cumulative.
2026-05-08 11:13:22 +00:00
f00054309d mk: mapo (relational map) — 300/300 milestone
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
(mapo rel l1 l2) takes a 2-argument relation rel and asserts l2 is l1
with each element rel-related to its counterpart. Recursive on both
lists in lockstep. Works forward (fixed l1, find l2), backward (fixed
l2, find l1), or constraining mid-pipeline.

Composes with intarith for arithmetic transforms:
  (mapo (fn (a b) (*o-i a a b)) (list 1 2 3 4) q) -> ((1 4 9 16))

7 new tests, 300/300 cumulative.
2026-05-08 11:11:26 +00:00
cfb43a3cdf mk: samelengtho — equal-length relation
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
Recurses positionally, dropping a head from each list each step. Both
arguments can be unbound, giving the natural enumeration:

  (run 3 q (fresh (l1 l2) (samelengtho l1 l2) (== q (list l1 l2))))
    -> (((), ())                         empty/empty
        ((_.0), (_.1))                   pair of 1-element lists
        ((_.0 _.1), (_.2 _.3)))          pair of 2-element lists

5 new tests, 293/293 cumulative.
2026-05-08 11:09:48 +00:00
186171fec3 mk: pythagorean triples search — intarith showcase
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
Finds all (a, b, c) with a, b, c in [1..10], a <= b, a^2 + b^2 = c^2.
Result: ((3 4 5) (6 8 10)) — the two smallest Pythagorean triples
within the domain.

Demonstrates the enumerate-then-filter pattern:
  (ino a dom) (ino b dom) (ino c dom) — generate
  (lteo-i a b) — symmetry break
  (*o-i a a a-sq) (*o-i b b b-sq) (*o-i c c c-sq) — squares
  (pluso-i a-sq b-sq sum) (== sum c-sq) — Pythagorean equation

288/288 cumulative.
2026-05-08 11:07:33 +00:00
9795532f7d mk: intarith.sx — ground-only integer arithmetic via project
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
pluso-i / minuso-i / *o-i / lto-i / lteo-i / neqo-i wrap host arithmetic
in project. They run at native speed but require their inputs to walk
to ground numbers — they are NOT relational the way Peano pluso is.
Use them when puzzle size makes Peano impractical (which is most cases
beyond toy examples).

Composes with relational goals — for instance,
  (fresh (x) (membero x (1 2 3 4 5)) (lto-i x 3) (== q x))
filters the domain by < 3 and returns (1 2).

18 new tests, 287/287 cumulative.
2026-05-08 11:02:09 +00:00
b89b0def93 mk: 2x2 Latin square — small classic FD constraint demo
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
Defines latin-2x2 over 4 cells and 4 all-distincto constraints. Enumerates
exactly 2 squares ((1 2)(2 1)) and ((2 1)(1 2)); a corner clue narrows to
one. 3 new tests, 269/269 cumulative.

3x3 (12 squares, the natural showcase) is too slow under naive enumerate-
then-filter — that is the motivating test for Phase 6 arc-consistency.
2026-05-08 11:00:12 +00:00
428ca79f61 mk: rembero / assoco / nth-o — more list relations
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
rembero (remove-first) uses nafc to gate the skip-element clause so
the result is well-defined on ground lists. assoco is alist lookup —
runs forward (key -> value) and backward (find keys with a given
value). nth-o uses Peano-encoded indices into a list, mirroring lengtho.

13 new tests, 266/266 cumulative.
2026-05-08 10:50:28 +00:00