eyeling 1.34.1 → 1.34.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +28 -26
  2. package/docs/eyelang-guide.md +6 -21
  3. package/docs/eyelang-language-reference.md +3 -1
  4. package/examples/context-schema-audit.n3 +72 -0
  5. package/examples/eyelang/basic-monadic.pl +16 -2
  6. package/examples/eyelang/output/basic-monadic.pl +1314 -1314
  7. package/examples/eyelang/output/path-discovery.pl +3 -3
  8. package/examples/eyelang/path-discovery.pl +22 -7
  9. package/examples/output/context-schema-audit.md +11 -0
  10. package/lib/eyelang/builtins/registry.js +1 -2
  11. package/package.json +1 -1
  12. package/test/eyelang/conformance/README.md +1 -1
  13. package/examples/eyelang/dense-hamiltonian-cycle.pl +0 -92
  14. package/examples/eyelang/hamiltonian-cycle.pl +0 -55
  15. package/examples/eyelang/n-queens.pl +0 -23
  16. package/examples/eyelang/output/dense-hamiltonian-cycle.pl +0 -4
  17. package/examples/eyelang/output/hamiltonian-cycle.pl +0 -4
  18. package/examples/eyelang/output/n-queens.pl +0 -93
  19. package/examples/eyelang/output/quine-mccluskey.pl +0 -3
  20. package/examples/eyelang/output/sat-dpll.pl +0 -5
  21. package/examples/eyelang/output/traveling-salesman.pl +0 -1
  22. package/examples/eyelang/quine-mccluskey.pl +0 -143
  23. package/examples/eyelang/sat-dpll.pl +0 -80
  24. package/examples/eyelang/traveling-salesman.pl +0 -64
  25. package/lib/eyelang/builtins/search.js +0 -519
  26. package/test/eyelang/conformance/cases/extension/042_n_queens_small.pl +0 -3
  27. package/test/eyelang/conformance/cases/extension/043_cnf_model.pl +0 -3
  28. package/test/eyelang/conformance/expected/extension/042_n_queens_small.out +0 -2
  29. package/test/eyelang/conformance/expected/extension/043_cnf_model.out +0 -1
@@ -1,519 +0,0 @@
1
- // Reusable finite-search builtins for examples that would otherwise spend most
2
- // of their time in small relational generators. These predicates are generic
3
- // entry points (graph, CNF, QMC, and n-queens helpers), not compiled
4
- // replacements for particular example predicate names.
5
- import { atom, compound, deref, listFromItems, numberTerm, properListItems, unify } from '../term.js';
6
- import { compareLexicalOrNumeric } from './arithmetic.js';
7
-
8
- export const searchBuiltins = {
9
- register(registry) {
10
- registry.add('n_queens', 2, nQueens, { fallbackWhenNotReady: true, ready: firstIntReady });
11
- registry.add('weighted_hamiltonian_cycle', 4, weightedHamiltonianCycle, { fallbackWhenNotReady: true, ready: weightedGraphReady });
12
- registry.add('weighted_hamiltonian_path', 4, weightedHamiltonianPath, { fallbackWhenNotReady: true, ready: weightedGraphReady });
13
- registry.add('hamiltonian_cycle', 3, hamiltonianCycle, { fallbackWhenNotReady: true, ready: graphReady });
14
- registry.add('fixed_length_cycle', 4, fixedLengthCycle, { fallbackWhenNotReady: true, ready: fixedCycleReady });
15
- registry.add('bounded_path', 5, boundedPath, { fallbackWhenNotReady: true, ready: boundedPathReady });
16
- registry.add('cnf_model', 3, cnfModel, { fallbackWhenNotReady: true, ready: cnfReady });
17
- registry.add('qm_prime_implicants', 4, qmPrimeImplicants, { deterministic: true, ready: qmReady });
18
- registry.add('qm_minimal_cover', 4, qmMinimalCover, { deterministic: true, ready: qmReady });
19
- }
20
- };
21
-
22
- function firstIntReady(goal, env) { return intTerm(goal.args[0], env) !== null; }
23
- function graphReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== null && properListItems(goal.args[1], env) !== null; }
24
- function weightedGraphReady(goal, env) { return graphReady(goal, env); }
25
- function fixedCycleReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== null && intTerm(goal.args[1], env) !== null; }
26
- function boundedPathReady(goal, env) { return atomKey(deref(goal.args[0], env)) !== null && atomKey(deref(goal.args[1], env)) !== null && atomKey(deref(goal.args[2], env)) !== null && intTerm(goal.args[3], env) !== null; }
27
- function cnfReady(goal, env) { return atomList(goal.args[0], env) !== null && clauseList(goal.args[1], env) !== null; }
28
- function qmReady(goal, env) { return numberList(goal.args[0], env) !== null && numberList(goal.args[1], env) !== null && bitTable(goal.args[2], env) !== null; }
29
-
30
-
31
- function* nQueens({ goal, env }) {
32
- // n_queens(N, Solution) performs the same finite search as the declarative
33
- // select/not_member version, but keeps occupied diagonals in sets.
34
- const n = intTerm(goal.args[0], env);
35
- if (n == null || n < 0 || n > 14) return;
36
- const cols = Array.from({ length: n }, (_, i) => i + 1);
37
- const chosen = [];
38
- const down = new Set();
39
- const up = new Set();
40
- function* place(row, remaining) {
41
- if (remaining.length === 0) { yield chosen.slice(); return; }
42
- for (let i = 0; i < remaining.length; i++) {
43
- const q = remaining[i];
44
- const d = row + q;
45
- const u = row - q;
46
- if (down.has(d) || up.has(u)) continue;
47
- chosen.push(q); down.add(d); up.add(u);
48
- const rest = remaining.slice(0, i).concat(remaining.slice(i + 1));
49
- yield* place(row + 1, rest);
50
- up.delete(u); down.delete(d); chosen.pop();
51
- }
52
- }
53
- for (const solution of place(1, cols)) {
54
- const next = env.clone();
55
- if (unify(goal.args[1], listFromItems(solution.map(numberTerm)), next)) yield next;
56
- }
57
- }
58
-
59
- function* weightedHamiltonianCycle({ solver, goal, env }) {
60
- // weighted_hamiltonian_cycle(EdgePred, Cities, Cycle, Cost) treats
61
- // EdgePred/3 facts as undirected weighted edges and enumerates
62
- // symmetry-broken cycles beginning at the first city.
63
- const predicate = atomKey(deref(goal.args[0], env));
64
- const cities = properListItems(goal.args[1], env);
65
- if (!predicate || !cities || cities.length < 2) return;
66
- const weights = edgeWeightMap(solver.program, predicate);
67
- if (!weights) return;
68
- const start = cities[0];
69
- const rest = cities.slice(1);
70
- for (const order of permutations(rest)) {
71
- if (compareLexicalOrNumeric(atomKey(order[0]), atomKey(order[order.length - 1])) >= 0) continue;
72
- const cost = weightedPathCost([start, ...order, start], weights);
73
- if (cost == null) continue;
74
- const next = env.clone();
75
- if (unify(goal.args[2], listFromItems([start, ...order, start]), next) && unify(goal.args[3], numberTerm(cost), next)) yield next;
76
- }
77
- }
78
-
79
- function* weightedHamiltonianPath({ solver, goal, env }) {
80
- // weighted_hamiltonian_path(EdgePred, Cities, Path, Cost) enumerates paths
81
- // from the first city through every remaining city exactly once.
82
- const predicate = atomKey(deref(goal.args[0], env));
83
- const cities = properListItems(goal.args[1], env);
84
- if (!predicate || !cities || cities.length < 1) return;
85
- const weights = edgeWeightMap(solver.program, predicate);
86
- if (!weights) return;
87
- const start = cities[0];
88
- const rest = cities.slice(1);
89
- for (const order of permutations(rest)) {
90
- const path = [start, ...order];
91
- const cost = weightedPathCost(path, weights);
92
- if (cost == null) continue;
93
- const next = env.clone();
94
- if (unify(goal.args[2], listFromItems(path), next) && unify(goal.args[3], numberTerm(cost), next)) yield next;
95
- }
96
- }
97
-
98
- function weightedPathCost(path, weights) {
99
- let cost = 0;
100
- for (let i = 1; i < path.length; i++) {
101
- const w = weights.get(`${atomKey(path[i - 1])}\x1f${atomKey(path[i])}`);
102
- if (w == null) return null;
103
- cost += w;
104
- }
105
- return cost;
106
- }
107
-
108
- function edgeWeightMap(program, predicate) {
109
- const map = new Map();
110
- let count = 0;
111
- for (const clause of program.clauses) {
112
- if (clause.body.length !== 0) continue;
113
- const h = clause.head;
114
- if (h.type !== 'compound' || h.name !== predicate || h.arity !== 3) continue;
115
- const a = atomKey(h.args[0]), b = atomKey(h.args[1]);
116
- const w = intTerm(h.args[2], null);
117
- if (a == null || b == null || w == null) continue;
118
- map.set(`${a}\x1f${b}`, w);
119
- map.set(`${b}\x1f${a}`, w);
120
- count++;
121
- }
122
- return count ? map : null;
123
- }
124
-
125
- function* hamiltonianCycle({ solver, goal, env }) {
126
- // hamiltonian_cycle(EdgePred, Vertices, Cycle) treats EdgePred/2 facts as an
127
- // undirected graph and enumerates cycles starting at Vertices[0].
128
- const predicate = atomKey(deref(goal.args[0], env));
129
- const vertices = properListItems(goal.args[1], env);
130
- if (!predicate || !vertices || vertices.length < 2) return;
131
- const graph = undirectedGraph(solver.program, predicate);
132
- if (!graph) return;
133
- const start = atomKey(vertices[0]);
134
- if (start == null) return;
135
- const rest = vertices.slice(1).map(atomKey);
136
- if (rest.some((v) => v == null)) return;
137
- const path = [start];
138
- function* dfs(current, remaining) {
139
- if (remaining.length === 0) {
140
- if (!graph.has(`${current}\x1f${start}`)) return;
141
- const next = env.clone();
142
- if (unify(goal.args[2], listFromItems([...path, start].map(atom)), next)) yield next;
143
- return;
144
- }
145
- for (let i = 0; i < remaining.length; i++) {
146
- const v = remaining[i];
147
- if (!graph.has(`${current}\x1f${v}`)) continue;
148
- path.push(v);
149
- yield* dfs(v, remaining.slice(0, i).concat(remaining.slice(i + 1)));
150
- path.pop();
151
- }
152
- }
153
- yield* dfs(start, rest);
154
- }
155
-
156
- function undirectedGraph(program, predicate) {
157
- const edges = new Set();
158
- let count = 0;
159
- for (const clause of program.clauses) {
160
- if (clause.body.length !== 0) continue;
161
- const h = clause.head;
162
- if (h.type !== 'compound' || h.name !== predicate || h.arity !== 2) continue;
163
- const a = atomKey(h.args[0]), b = atomKey(h.args[1]);
164
- if (a == null || b == null) continue;
165
- edges.add(`${a}\x1f${b}`);
166
- edges.add(`${b}\x1f${a}`);
167
- count++;
168
- }
169
- return count ? edges : null;
170
- }
171
-
172
- function* fixedLengthCycle({ solver, goal, env }) {
173
- // fixed_length_cycle(EdgePred, Length, Relation, Cycle) is useful for
174
- // labelled edge(Source, Relation, Target) data. It enumerates closed walks
175
- // of exactly Length steps and returns the relation label and node list.
176
- const predicate = atomKey(deref(goal.args[0], env));
177
- const length = intTerm(goal.args[1], env);
178
- if (!predicate || length == null || length < 0) return;
179
- const graph = labelledGraph(solver.program, predicate);
180
- if (!graph) return;
181
-
182
- const requestedRelation = atomKey(deref(goal.args[2], env));
183
- const requestedCycle = fixedLengthCycleRequestedPath(goal, env, length);
184
- if (requestedRelation && requestedCycle) {
185
- const byStart = graph.byRelation.get(requestedRelation);
186
- if (byStart && fixedLengthCyclePathExists(byStart, requestedCycle)) {
187
- const next = env.clone();
188
- if (unify(goal.args[2], atom(requestedRelation), next) && unify(goal.args[3], listFromItems(requestedCycle.map(atom)), next)) yield next;
189
- }
190
- return;
191
- }
192
-
193
- const relations = requestedRelation
194
- ? [[requestedRelation, graph.byRelation.get(requestedRelation)]].filter(([, byStart]) => byStart)
195
- : graph.byRelation.entries();
196
- for (const [relation, byStart] of relations) {
197
- for (const start of byStart.keys()) {
198
- const path = [atom(start)];
199
- yield* fixedLengthCycleDfs(goal, env, byStart, relation, start, start, length, path);
200
- }
201
- }
202
- }
203
-
204
- function fixedLengthCycleRequestedPath(goal, env, length) {
205
- const items = properListItems(goal.args[3], env);
206
- if (!items || items.length !== length + 1) return null;
207
- const nodes = items.map((item) => atomKey(deref(item, env)));
208
- if (nodes.some((node) => node == null)) return null;
209
- if (nodes[0] !== nodes[nodes.length - 1]) return null;
210
- return nodes;
211
- }
212
-
213
- function fixedLengthCyclePathExists(byStart, nodes) {
214
- for (let i = 0; i < nodes.length - 1; i++) {
215
- if (!(byStart.get(nodes[i]) ?? []).includes(nodes[i + 1])) return false;
216
- }
217
- return true;
218
- }
219
-
220
- function* fixedLengthCycleDfs(goal, env, byStart, relation, start, current, remaining, path) {
221
- if (remaining === 0) {
222
- if (current !== start) return;
223
- const next = env.clone();
224
- if (unify(goal.args[2], atom(relation), next) && unify(goal.args[3], listFromItems(path), next)) yield next;
225
- return;
226
- }
227
- const nexts = byStart.get(current) ?? [];
228
- for (const dst of nexts) {
229
- path.push(atom(dst));
230
- yield* fixedLengthCycleDfs(goal, env, byStart, relation, start, dst, remaining - 1, path);
231
- path.pop();
232
- }
233
- }
234
-
235
- function labelledGraph(program, predicate) {
236
- let count = 0;
237
- const byRelation = new Map();
238
- for (const clause of program.clauses) {
239
- if (clause.body.length !== 0) continue;
240
- const h = clause.head;
241
- if (h.type !== 'compound' || h.name !== predicate || h.arity !== 3) continue;
242
- const src = atomKey(h.args[0]), rel = atomKey(h.args[1]), dst = atomKey(h.args[2]);
243
- if (src == null || rel == null || dst == null) continue;
244
- let byStart = byRelation.get(rel);
245
- if (!byStart) byRelation.set(rel, byStart = new Map());
246
- let arr = byStart.get(src);
247
- if (!arr) byStart.set(src, arr = []);
248
- arr.push(dst); count++;
249
- }
250
- return count ? { byRelation } : null;
251
- }
252
-
253
-
254
- function* boundedPath({ solver, goal, env }) {
255
- // bounded_path(EdgePred, Source, Target, MaxEdges, Path) enumerates simple
256
- // directed paths with at most MaxEdges edges. EdgePred is read from EdgePred/2
257
- // facts in source order so declarative examples retain stable answer order.
258
- const predicate = atomKey(deref(goal.args[0], env));
259
- const source = atomKey(deref(goal.args[1], env));
260
- const target = atomKey(deref(goal.args[2], env));
261
- const maxEdges = intTerm(goal.args[3], env);
262
- if (!predicate || source == null || target == null || maxEdges == null || maxEdges < 0) return;
263
- const graph = directedAdjacency(solver.program, predicate);
264
- if (!graph) return;
265
- const path = [source];
266
- const visited = new Set([source]);
267
- function* dfs(current, remaining) {
268
- if (current === target) {
269
- const next = env.clone();
270
- if (unify(goal.args[4], listFromItems(path.map(atom)), next)) yield next;
271
- return;
272
- }
273
- if (remaining <= 0) return;
274
- for (const dst of graph.get(current) ?? []) {
275
- if (visited.has(dst)) continue;
276
- visited.add(dst);
277
- path.push(dst);
278
- yield* dfs(dst, remaining - 1);
279
- path.pop();
280
- visited.delete(dst);
281
- }
282
- }
283
- yield* dfs(source, maxEdges);
284
- }
285
-
286
- function directedAdjacency(program, predicate) {
287
- const map = new Map();
288
- let count = 0;
289
- for (const clause of program.clauses) {
290
- if (clause.body.length !== 0) continue;
291
- const h = clause.head;
292
- if (h.type !== 'compound' || h.name !== predicate || h.arity !== 2) continue;
293
- const a = atomKey(h.args[0]), b = atomKey(h.args[1]);
294
- if (a == null || b == null) continue;
295
- let arr = map.get(a);
296
- if (!arr) map.set(a, arr = []);
297
- arr.push(b);
298
- count++;
299
- }
300
- return count ? map : null;
301
- }
302
-
303
- function* cnfModel({ goal, env }) {
304
- // cnf_model(Variables, Clauses, Assignment) enumerates finite truth
305
- // assignments in false-before-true order. It deliberately preserves the
306
- // proof multiplicity of the declarative clause_true/2 + cnf_true/2 program:
307
- // a clause with two true literals has two derivations.
308
- const variables = atomList(goal.args[0], env);
309
- const clauses = clauseList(goal.args[1], env);
310
- if (!variables || !clauses) return;
311
- for (const assignment of enumerateAssignments(variables)) {
312
- const derivations = cnfDerivationCount(clauses, assignment);
313
- if (derivations === 0) continue;
314
- const terms = variables.map((v) => compound('value', [atom(v), atom(assignment.get(v) ? 'true' : 'false')]));
315
- for (let i = 0; i < derivations; i++) {
316
- const next = env.clone();
317
- if (unify(goal.args[2], listFromItems(terms), next)) yield next;
318
- }
319
- }
320
- }
321
-
322
- function* enumerateAssignments(vars, index = 0, assignment = new Map()) {
323
- if (index >= vars.length) { yield new Map(assignment); return; }
324
- const v = vars[index];
325
- assignment.set(v, false); yield* enumerateAssignments(vars, index + 1, assignment);
326
- assignment.set(v, true); yield* enumerateAssignments(vars, index + 1, assignment);
327
- assignment.delete(v);
328
- }
329
-
330
- function cnfDerivationCount(clauses, assignment) {
331
- let count = 1;
332
- for (const clause of clauses) {
333
- let trueLiterals = 0;
334
- for (const lit of clause) if (assignment.get(lit.var) === lit.positive) trueLiterals++;
335
- if (trueLiterals === 0) return 0;
336
- count *= trueLiterals;
337
- }
338
- return count;
339
- }
340
-
341
- function* qmPrimeImplicants({ goal, env }) {
342
- const data = qmDataFromArgs(goal, env);
343
- if (!data) return;
344
- const primes = computePrimeImplicants(data).map(patternTerm);
345
- const next = env.clone();
346
- if (unify(goal.args[3], listFromItems(primes), next)) yield next;
347
- }
348
-
349
- function* qmMinimalCover({ goal, env }) {
350
- const data = qmDataFromArgs(goal, env);
351
- if (!data) return;
352
- const primes = computePrimeImplicants(data);
353
- const cover = computeMinimalCover(primes, data.minterms);
354
- if (!cover) return;
355
- const next = env.clone();
356
- if (unify(goal.args[3], listFromItems(cover.map(patternTerm)), next)) yield next;
357
- }
358
-
359
- function qmDataFromArgs(goal, env) {
360
- const minterms = numberList(goal.args[0], env);
361
- const dontCares = numberList(goal.args[1], env);
362
- const bits = bitTable(goal.args[2], env);
363
- if (!minterms || !dontCares || !bits?.size) return null;
364
- return { minterms, dontCares, bits };
365
- }
366
-
367
- function computePrimeImplicants(data) {
368
- const initial = [...data.minterms, ...data.dontCares].map((n) => data.bits.get(n)).filter(Boolean);
369
- const once = [];
370
- for (const a of initial) for (const b of initial) { const c = combinePatterns(a, b); if (c) once.push(c); }
371
- const twice = [];
372
- for (const a of once) for (const b of once) { const c = combinePatterns(a, b); if (c) twice.push(c); }
373
- const raw = [];
374
- for (const p of initial) if (!initial.some((q) => combinePatterns(p, q))) raw.push(p);
375
- for (const p of once) if (!once.some((q) => combinePatterns(p, q))) raw.push(p);
376
- raw.push(...twice);
377
- return uniqueSortedPatterns(raw);
378
- }
379
-
380
- function combinePatterns(a, b) {
381
- if (a.length !== b.length) return null;
382
- let diffs = 0;
383
- const out = [];
384
- for (let i = 0; i < a.length; i++) {
385
- if (a[i] === b[i]) out.push(a[i]);
386
- else {
387
- diffs++;
388
- if (diffs > 1) return null;
389
- out.push('x');
390
- }
391
- }
392
- return diffs === 1 ? out : null;
393
- }
394
-
395
- function uniqueSortedPatterns(patterns) {
396
- const map = new Map();
397
- for (const p of patterns) map.set(patternKey(p), p);
398
- return [...map.values()].sort(comparePattern);
399
- }
400
-
401
- function comparePattern(a, b) {
402
- for (let i = 0; i < a.length; i++) {
403
- const ra = a[i] === 'x' ? 2 : 1;
404
- const rb = b[i] === 'x' ? 2 : 1;
405
- if (ra !== rb) return ra - rb;
406
- if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1;
407
- }
408
- return 0;
409
- }
410
-
411
- function computeMinimalCover(primes, minterms) {
412
- const sortedMinterms = [...new Set(minterms)].sort((a, b) => a - b);
413
- const candidates = [];
414
- for (let i = 0; i < primes.length; i++) {
415
- for (let j = i + 1; j < primes.length; j++) {
416
- const cover = uniqueSortedPatterns([primes[i], primes[j]]);
417
- if (sortedMinterms.every((m) => cover.some((p) => patternCoversInt(p, m)))) candidates.push(cover);
418
- }
419
- }
420
- candidates.sort((a, b) => comparePatternList(a, b));
421
- return candidates[0] ?? null;
422
- }
423
-
424
- function patternCoversInt(pattern, n) {
425
- const bits = n.toString(2).padStart(pattern.length, '0').split('').map(Number);
426
- return pattern.every((v, i) => v === 'x' || v === bits[i]);
427
- }
428
-
429
- function comparePatternList(a, b) {
430
- const len = Math.min(a.length, b.length);
431
- for (let i = 0; i < len; i++) { const c = comparePattern(a[i], b[i]); if (c) return c; }
432
- return a.length - b.length;
433
- }
434
-
435
- function patternTerm(pattern) {
436
- return listFromItems(pattern.map((v) => v === 'x' ? atom('x') : numberTerm(v)));
437
- }
438
-
439
- function patternKey(pattern) { return pattern.join(''); }
440
-
441
- function* permutations(items, start = 0) {
442
- if (start >= items.length) { yield items.slice(); return; }
443
- for (let i = start; i < items.length; i++) {
444
- [items[start], items[i]] = [items[i], items[start]];
445
- yield* permutations(items, start + 1);
446
- [items[start], items[i]] = [items[i], items[start]];
447
- }
448
- }
449
-
450
- function atomKey(term) {
451
- if (!term) return null;
452
- return (term.type === 'atom' || term.type === 'string' || term.type === 'number') ? term.name : null;
453
- }
454
-
455
- function intTerm(term, env) {
456
- const t = env ? deref(term, env) : term;
457
- if (!t || t.type !== 'number' || !/^-?\d+$/.test(t.name)) return null;
458
- const n = Number(t.name);
459
- return Number.isSafeInteger(n) ? n : null;
460
- }
461
-
462
- function numberList(term, env) {
463
- const items = properListItems(term, env);
464
- if (!items) return null;
465
- const out = [];
466
- for (const item of items) {
467
- const n = intTerm(item, env);
468
- if (n == null) return null;
469
- out.push(n);
470
- }
471
- return out;
472
- }
473
-
474
- function atomList(term, env) {
475
- const items = properListItems(term, env);
476
- if (!items) return null;
477
- const out = [];
478
- for (const item of items) {
479
- const v = atomKey(deref(item, env));
480
- if (v == null) return null;
481
- out.push(v);
482
- }
483
- return out;
484
- }
485
-
486
- function bitTable(term, env) {
487
- const items = properListItems(term, env);
488
- if (!items) return null;
489
- const map = new Map();
490
- for (const item of items) {
491
- const entry = deref(item, env);
492
- if (entry.type !== 'compound' || entry.name !== 'bit' || entry.arity !== 2) return null;
493
- const n = intTerm(entry.args[0], env);
494
- const bits = numberList(entry.args[1], env);
495
- if (n == null || !bits) return null;
496
- map.set(n, bits);
497
- }
498
- return map;
499
- }
500
-
501
- function clauseList(term, env) {
502
- const clauses = properListItems(term, env);
503
- if (!clauses) return null;
504
- const out = [];
505
- for (const clause of clauses) {
506
- const lits = properListItems(clause, env);
507
- if (!lits) return null;
508
- const row = [];
509
- for (const litTerm of lits) {
510
- const lit = deref(litTerm, env);
511
- if (lit.type !== 'compound' || lit.arity !== 1 || (lit.name !== 'pos' && lit.name !== 'neg')) return null;
512
- const v = atomKey(deref(lit.args[0], env));
513
- if (v == null) return null;
514
- row.push({ var: v, positive: lit.name === 'pos' });
515
- }
516
- out.push(row);
517
- }
518
- return out;
519
- }
@@ -1,3 +0,0 @@
1
- % Reference 9.5: n_queens/2 enumerates finite board solutions.
2
- answer(solution, Qs) :- n_queens(4, Qs).
3
- materialize(answer, 2).
@@ -1,3 +0,0 @@
1
- % Reference 9.5: cnf_model/3 enumerates satisfying truth assignments for a CNF.
2
- answer(model, M) :- cnf_model([a, b], [[pos(a), pos(b)], [neg(a)]], M).
3
- materialize(answer, 2).
@@ -1,2 +0,0 @@
1
- answer(solution, [2, 4, 1, 3]).
2
- answer(solution, [3, 1, 4, 2]).
@@ -1 +0,0 @@
1
- answer(model, [value(a, false), value(b, true)]).