eyeling 1.34.3 → 1.34.4

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 (48) hide show
  1. package/README.md +7 -9
  2. package/docs/eyelang-guide.md +5 -10
  3. package/docs/eyelang-language-reference.md +31 -5
  4. package/examples/eyelang/bayes-therapy.pl +4 -4
  5. package/examples/eyelang/output/reusable-builtins.pl +5 -0
  6. package/examples/eyelang/output/term-tools.pl +6 -0
  7. package/examples/eyelang/reusable-builtins.pl +32 -0
  8. package/examples/eyelang/term-tools.pl +23 -0
  9. package/lib/eyelang/builtins/arithmetic.js +19 -6
  10. package/lib/eyelang/builtins/control.js +12 -0
  11. package/lib/eyelang/builtins/lists.js +146 -7
  12. package/lib/eyelang/builtins/registry.js +2 -3
  13. package/lib/eyelang/builtins/strings.js +165 -1
  14. package/lib/eyelang/builtins/terms.js +66 -0
  15. package/package.json +1 -1
  16. package/test/eyelang/conformance/README.md +1 -1
  17. package/test/eyelang/conformance/cases/extension/036_reusable_numeric_builtins.pl +10 -0
  18. package/test/eyelang/conformance/cases/extension/037_reusable_list_builtins.pl +11 -0
  19. package/test/eyelang/conformance/cases/extension/038_reusable_string_builtins.pl +12 -0
  20. package/test/eyelang/conformance/cases/extension/039_reusable_term_control_builtins.pl +11 -0
  21. package/test/eyelang/conformance/expected/extension/036_reusable_numeric_builtins.out +8 -0
  22. package/test/eyelang/conformance/expected/extension/037_reusable_list_builtins.out +9 -0
  23. package/test/eyelang/conformance/expected/extension/038_reusable_string_builtins.out +10 -0
  24. package/test/eyelang/conformance/expected/extension/039_reusable_term_control_builtins.out +6 -0
  25. package/examples/eyelang/collatz-1000.pl +0 -14
  26. package/examples/eyelang/complex-matrix-stability.pl +0 -45
  27. package/examples/eyelang/gcd-bezout-identity.pl +0 -48
  28. package/examples/eyelang/goldbach-1000.pl +0 -185
  29. package/examples/eyelang/kaprekar.pl +0 -32
  30. package/examples/eyelang/matrix.pl +0 -296
  31. package/examples/eyelang/output/collatz-1000.pl +0 -1000
  32. package/examples/eyelang/output/complex-matrix-stability.pl +0 -5
  33. package/examples/eyelang/output/gcd-bezout-identity.pl +0 -36
  34. package/examples/eyelang/output/goldbach-1000.pl +0 -667
  35. package/examples/eyelang/output/kaprekar.pl +0 -8
  36. package/examples/eyelang/output/matrix.pl +0 -10
  37. package/lib/eyelang/builtins/matrix.js +0 -226
  38. package/lib/eyelang/builtins/number-theory.js +0 -114
  39. package/test/eyelang/conformance/cases/extension/036_extended_gcd.pl +0 -3
  40. package/test/eyelang/conformance/cases/extension/037_collatz_trajectory.pl +0 -3
  41. package/test/eyelang/conformance/cases/extension/038_kaprekar_steps.pl +0 -3
  42. package/test/eyelang/conformance/cases/extension/039_goldbach_pair.pl +0 -3
  43. package/test/eyelang/conformance/cases/extension/040_matrix_operations.pl +0 -5
  44. package/test/eyelang/conformance/expected/extension/036_extended_gcd.out +0 -1
  45. package/test/eyelang/conformance/expected/extension/037_collatz_trajectory.out +0 -1
  46. package/test/eyelang/conformance/expected/extension/038_kaprekar_steps.out +0 -1
  47. package/test/eyelang/conformance/expected/extension/039_goldbach_pair.out +0 -2
  48. package/test/eyelang/conformance/expected/extension/040_matrix_operations.out +0 -3
@@ -1,226 +0,0 @@
1
- // Small dense-matrix arithmetic builtins.
2
- // These are reusable numeric kernels for ground matrices; when arguments are not
3
- // ground proper numeric lists, user-defined matrix predicates remain available.
4
- import { deref, isDecimalInteger, listFromItems, numberTerm, numberTextFromDouble, parseFiniteNumber, properListItems, unify } from '../term.js';
5
-
6
- export const matrixBuiltins = {
7
- register(registry) {
8
- registry.add('matrix_sum', 2, matrixSum, { deterministic: true, fallbackWhenNotReady: true, ready: matrixPairReady });
9
- registry.add('matrix_multiply', 2, matrixMultiply, { deterministic: true, fallbackWhenNotReady: true, ready: matrixPairReady });
10
- registry.add('cholesky_decomposition', 2, choleskyDecomposition, { deterministic: true, fallbackWhenNotReady: true, ready: firstMatrixReady });
11
- registry.add('determinant', 2, determinant, { deterministic: true, fallbackWhenNotReady: true, ready: firstMatrixReady });
12
- registry.add('matrix_inv_triang', 2, matrixInvTriang, { deterministic: true, fallbackWhenNotReady: true, ready: firstMatrixReady });
13
- registry.add('matrix_inversion', 2, matrixInversion, { deterministic: true, fallbackWhenNotReady: true, ready: firstMatrixReady });
14
- }
15
- };
16
-
17
- function firstMatrixReady(goal, env) { return parseMatrix(goal.args[0], env) !== null; }
18
- function matrixPairReady(goal, env) { return parseMatrixPair(goal.args[0], env) !== null; }
19
-
20
- function* matrixSum({ goal, env }) {
21
- const pair = parseMatrixPair(goal.args[0], env);
22
- if (!pair) return;
23
- const [a, b] = pair;
24
- if (!sameShape(a, b)) return;
25
- const out = a.map((row, i) => row.map((cell, j) => addNum(cell, b[i][j])));
26
- yield* unifyMatrix(goal.args[1], out, env);
27
- }
28
-
29
- function* matrixMultiply({ goal, env }) {
30
- const pair = parseMatrixPair(goal.args[0], env);
31
- if (!pair) return;
32
- const out = multiplyMatrices(pair[0], pair[1]);
33
- if (!out) return;
34
- yield* unifyMatrix(goal.args[1], out, env);
35
- }
36
-
37
- function* choleskyDecomposition({ goal, env }) {
38
- const matrix = parseMatrix(goal.args[0], env);
39
- const out = cholesky(matrix);
40
- if (!out) return;
41
- yield* unifyMatrix(goal.args[1], out, env);
42
- }
43
-
44
- function* determinant({ goal, env }) {
45
- const matrix = parseMatrix(goal.args[0], env);
46
- const l = cholesky(matrix);
47
- if (!l) return;
48
- let prod = num(1n);
49
- for (let i = l.length - 1; i >= 0; i--) prod = mulNum(l[i][i], prod);
50
- const det = mulNum(prod, prod);
51
- const next = env.clone();
52
- if (unify(goal.args[1], numTerm(det), next)) yield next;
53
- }
54
-
55
- function* matrixInvTriang({ goal, env }) {
56
- const matrix = parseMatrix(goal.args[0], env);
57
- const out = invertLowerTriangular(matrix);
58
- if (!out) return;
59
- yield* unifyMatrix(goal.args[1], out, env);
60
- }
61
-
62
- function* matrixInversion({ goal, env }) {
63
- const matrix = parseMatrix(goal.args[0], env);
64
- const l = cholesky(matrix);
65
- if (!l) return;
66
- const li = invertLowerTriangular(l);
67
- if (!li) return;
68
- const lit = transpose(li);
69
- const out = multiplyMatrices(lit, li);
70
- if (!out) return;
71
- yield* unifyMatrix(goal.args[1], out, env);
72
- }
73
-
74
- function cholesky(a) {
75
- if (!isSquare(a)) return null;
76
- const n = a.length;
77
- const l = Array.from({ length: n }, () => Array.from({ length: n }, () => num(0n)));
78
- for (let i = 0; i < n; i++) {
79
- for (let j = 0; j <= i; j++) {
80
- let sum = num(0n);
81
- for (let k = 0; k < j; k++) sum = addNum(sum, mulNum(l[i][k], l[j][k]));
82
- if (j === i) {
83
- const v2 = subNum(a[i][i], sum);
84
- const v = Math.sqrt(toNumber(v2));
85
- if (!Number.isFinite(v)) return null;
86
- l[i][j] = num(v);
87
- } else {
88
- const numerator = subNum(a[i][j], sum);
89
- l[i][j] = divNum(numerator, l[j][j]);
90
- }
91
- }
92
- }
93
- return l;
94
- }
95
-
96
- function invertLowerTriangular(l) {
97
- if (!isSquare(l)) return null;
98
- const n = l.length;
99
- const previous = [];
100
- const out = [];
101
- for (let i = 0; i < n; i++) {
102
- const row = [];
103
- for (let j = 0; j < n; j++) {
104
- if (j > i) row.push(num(0n));
105
- else if (i === j) row.push(divNum(num(1), l[i][i]));
106
- else {
107
- let sum = num(0n);
108
- for (let k = j; k < i; k++) sum = addNum(sum, mulNum(l[i][k], previous[k][j]));
109
- row.push(divNum(negNum(sum), l[i][i]));
110
- }
111
- }
112
- previous.push(row);
113
- out.push(row);
114
- }
115
- return out;
116
- }
117
-
118
- function multiplyMatrices(a, b) {
119
- if (!a.length || !b.length || !a[0].length || !b[0].length) return null;
120
- const rows = a.length, inner = a[0].length, cols = b[0].length;
121
- if (b.length !== inner || a.some((row) => row.length !== inner) || b.some((row) => row.length !== cols)) return null;
122
- const out = [];
123
- for (let i = 0; i < rows; i++) {
124
- const row = [];
125
- for (let j = 0; j < cols; j++) {
126
- let total = num(0n);
127
- for (let k = inner - 1; k >= 0; k--) total = addNum(mulNum(a[i][k], b[k][j]), total);
128
- row.push(total);
129
- }
130
- out.push(row);
131
- }
132
- return out;
133
- }
134
-
135
- function transpose(a) {
136
- if (!a.length) return [];
137
- return a[0].map((_, i) => a.map((row) => row[i]));
138
- }
139
-
140
- function sameShape(a, b) {
141
- return a.length === b.length && a.every((row, i) => row.length === b[i].length);
142
- }
143
-
144
- function isSquare(a) {
145
- return Array.isArray(a) && a.length > 0 && a.every((row) => row.length === a.length);
146
- }
147
-
148
- function parseMatrixPair(term, env) {
149
- const pair = properListItems(term, env);
150
- if (!pair || pair.length !== 2) return null;
151
- const a = parseMatrix(pair[0], env), b = parseMatrix(pair[1], env);
152
- return a && b ? [a, b] : null;
153
- }
154
-
155
- function parseMatrix(term, env) {
156
- const rows = properListItems(term, env);
157
- if (!rows) return null;
158
- const matrix = [];
159
- let width = null;
160
- for (const rowTerm of rows) {
161
- const rowItems = properListItems(rowTerm, env);
162
- if (!rowItems) return null;
163
- if (width == null) width = rowItems.length;
164
- else if (width !== rowItems.length) return null;
165
- const row = [];
166
- for (const item of rowItems) {
167
- const n = parseNum(item, env);
168
- if (!n) return null;
169
- row.push(n);
170
- }
171
- matrix.push(row);
172
- }
173
- return matrix;
174
- }
175
-
176
- function parseNum(term, env) {
177
- const value = deref(term, env);
178
- if (value.type !== 'number') return null;
179
- if (isDecimalInteger(value.name)) return num(BigInt(value.name));
180
- const f = parseFiniteNumber(value.name);
181
- return f == null ? null : num(f);
182
- }
183
-
184
- function* unifyMatrix(target, matrix, env) {
185
- const next = env.clone();
186
- if (unify(target, matrixTerm(matrix), next)) yield next;
187
- }
188
-
189
- function matrixTerm(matrix) {
190
- return listFromItems(matrix.map((row) => listFromItems(row.map(numTerm))));
191
- }
192
-
193
- function numTerm(value) {
194
- if (value.kind === 'int') return numberTerm(value.value.toString());
195
- return numberTerm(numberTextFromDouble(value.value));
196
- }
197
-
198
- function num(value) {
199
- return typeof value === 'bigint' ? { kind: 'int', value } : { kind: 'float', value: Number(value) };
200
- }
201
-
202
- function toNumber(value) {
203
- return value.kind === 'int' ? Number(value.value) : value.value;
204
- }
205
-
206
- function addNum(a, b) {
207
- if (a.kind === 'int' && b.kind === 'int') return num(a.value + b.value);
208
- return num(toNumber(a) + toNumber(b));
209
- }
210
- function subNum(a, b) {
211
- if (a.kind === 'int' && b.kind === 'int') return num(a.value - b.value);
212
- return num(toNumber(a) - toNumber(b));
213
- }
214
- function mulNum(a, b) {
215
- if (a.kind === 'int' && b.kind === 'int') return num(a.value * b.value);
216
- return num(toNumber(a) * toNumber(b));
217
- }
218
- function divNum(a, b) {
219
- if (b.kind === 'int' && b.value === 0n) return num(Number.NaN);
220
- if (b.kind === 'float' && b.value === 0) return num(Number.NaN);
221
- if (a.kind === 'int' && b.kind === 'int' && a.value % b.value === 0n) return num(a.value / b.value);
222
- return num(toNumber(a) / toNumber(b));
223
- }
224
- function negNum(a) {
225
- return a.kind === 'int' ? num(-a.value) : num(-a.value);
226
- }
@@ -1,114 +0,0 @@
1
- // Integer number-theory helpers for examples that would otherwise encode
2
- // arithmetic algorithms with many small recursive rule calls.
3
- // The predicates are intentionally general: extended GCD, Collatz trajectories,
4
- // Kaprekar iteration counts, and Goldbach pair generation.
5
- import { deref, listFromItems, numberTerm, unify } from '../term.js';
6
-
7
- export const numberTheoryBuiltins = {
8
- register(registry) {
9
- registry.add('extended_gcd', 5, extendedGcd, { deterministic: true, fallbackWhenNotReady: true, ready: firstTwoIntsReady });
10
- registry.add('collatz_trajectory', 2, collatzTrajectory, { deterministic: true, fallbackWhenNotReady: true, ready: firstIntReady });
11
- registry.add('kaprekar_steps', 2, kaprekarSteps, { deterministic: true, fallbackWhenNotReady: true, ready: firstIntReady });
12
- registry.add('goldbach_pair', 3, goldbachPair, { fallbackWhenNotReady: true, ready: firstIntReady });
13
- }
14
- };
15
-
16
- function firstIntReady(goal, env) { return intValue(goal.args[0], env) !== null; }
17
- function firstTwoIntsReady(goal, env) { return intValue(goal.args[0], env) !== null && intValue(goal.args[1], env) !== null; }
18
-
19
- function* extendedGcd({ goal, env }) {
20
- const a = intValue(goal.args[0], env);
21
- const b = intValue(goal.args[1], env);
22
- if (a === null || b === null) return;
23
- const { gcd, s, t } = egcdSigned(a, b);
24
- const next = env.clone();
25
- if (unify(goal.args[2], numberTerm(gcd.toString()), next) &&
26
- unify(goal.args[3], numberTerm(s.toString()), next) &&
27
- unify(goal.args[4], numberTerm(t.toString()), next)) yield next;
28
- }
29
-
30
- function egcdSigned(a, b) {
31
- const signA = a < 0n ? -1n : 1n;
32
- const signB = b < 0n ? -1n : 1n;
33
- let oldR = absBigInt(a), r = absBigInt(b);
34
- let oldS = 1n, s = 0n;
35
- let oldT = 0n, t = 1n;
36
- while (r !== 0n) {
37
- const q = oldR / r;
38
- [oldR, r] = [r, oldR - q * r];
39
- [oldS, s] = [s, oldS - q * s];
40
- [oldT, t] = [t, oldT - q * t];
41
- }
42
- return { gcd: oldR, s: oldS * signA, t: oldT * signB };
43
- }
44
-
45
- function absBigInt(n) { return n < 0n ? -n : n; }
46
-
47
- function* collatzTrajectory({ goal, env }) {
48
- const n = intValue(goal.args[0], env);
49
- if (n === null || n < 1n) return;
50
- const seen = [];
51
- let current = n;
52
- while (current >= 1n) {
53
- seen.push(numberTerm(current.toString()));
54
- if (current === 1n) break;
55
- current = current % 2n === 0n ? current / 2n : current * 3n + 1n;
56
- if (seen.length > 100000) return;
57
- }
58
- const next = env.clone();
59
- if (unify(goal.args[1], listFromItems(seen), next)) yield next;
60
- }
61
-
62
- function* kaprekarSteps({ goal, env }) {
63
- const n = intValue(goal.args[0], env);
64
- if (n === null || n < 0n || n > 9999n) return;
65
- const count = kaprekarCount(Number(n));
66
- if (count === null) return;
67
- const next = env.clone();
68
- if (unify(goal.args[1], numberTerm(String(count)), next)) yield next;
69
- }
70
-
71
- function kaprekarCount(start) {
72
- if (start === 6174) return 0;
73
- let current = start;
74
- for (let count = 1; count <= 100; count++) {
75
- const digits = String(current).padStart(4, '0').slice(-4).split('').map(Number).sort((a, b) => a - b);
76
- const low = digitsToNumber(digits);
77
- const high = digitsToNumber([...digits].reverse());
78
- current = high - low;
79
- if (current === 6174) return count;
80
- if (current <= 0) return null;
81
- }
82
- return null;
83
- }
84
-
85
- function digitsToNumber(digits) {
86
- return (((digits[0] * 10 + digits[1]) * 10 + digits[2]) * 10 + digits[3]);
87
- }
88
-
89
- function* goldbachPair({ goal, env }) {
90
- const n = intValue(goal.args[0], env);
91
- if (n === null || n < 4n || n % 2n !== 0n || n > BigInt(Number.MAX_SAFE_INTEGER)) return;
92
- const limit = Number(n / 2n);
93
- const total = Number(n);
94
- for (let p = 2; p <= limit; p = p === 2 ? 3 : p + 2) {
95
- const q = total - p;
96
- if (!isPrimeNumber(p) || !isPrimeNumber(q)) continue;
97
- const next = env.clone();
98
- if (unify(goal.args[1], numberTerm(String(p)), next) && unify(goal.args[2], numberTerm(String(q)), next)) yield next;
99
- }
100
- }
101
-
102
- function isPrimeNumber(n) {
103
- if (n < 2) return false;
104
- if (n === 2) return true;
105
- if (n % 2 === 0) return false;
106
- for (let d = 3; d <= Math.floor(Math.sqrt(n)); d += 2) if (n % d === 0) return false;
107
- return true;
108
- }
109
-
110
- function intValue(term, env) {
111
- const value = deref(term, env);
112
- if (value.type !== 'number' || !/^-?\d+$/.test(value.name)) return null;
113
- return BigInt(value.name);
114
- }
@@ -1,3 +0,0 @@
1
- % Reference 9.2: extended_gcd/5 returns gcd and Bezout coefficients.
2
- answer(gcd, result(G, S, T)) :- extended_gcd(240, 46, G, S, T).
3
- materialize(answer, 2).
@@ -1,3 +0,0 @@
1
- % Reference 9.2: collatz_trajectory/2 returns the full path to 1.
2
- answer(path, T) :- collatz_trajectory(6, T).
3
- materialize(answer, 2).
@@ -1,3 +0,0 @@
1
- % Reference 9.2: kaprekar_steps/2 counts iterations to 6174.
2
- answer(steps, N) :- kaprekar_steps(3524, N).
3
- materialize(answer, 2).
@@ -1,3 +0,0 @@
1
- % Reference 9.2: goldbach_pair/3 enumerates prime decompositions in ascending first prime order.
2
- answer(pair, pair(P, Q)) :- goldbach_pair(28, P, Q).
3
- materialize(answer, 2).
@@ -1,5 +0,0 @@
1
- % Reference 9.2: matrix helpers handle small ground numeric matrices.
2
- answer(sum, M) :- matrix_sum([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], M).
3
- answer(product, M) :- matrix_multiply([[[1, 2], [3, 4]], [[2, 0], [1, 2]]], M).
4
- answer(determinant, D) :- determinant([[4, 2], [2, 3]], D).
5
- materialize(answer, 2).
@@ -1 +0,0 @@
1
- answer(gcd, result(2, -9, 47)).
@@ -1 +0,0 @@
1
- answer(path, [6, 3, 10, 5, 16, 8, 4, 2, 1]).
@@ -1,2 +0,0 @@
1
- answer(pair, pair(5, 23)).
2
- answer(pair, pair(11, 17)).
@@ -1,3 +0,0 @@
1
- answer(sum, [[6, 8], [10, 12]]).
2
- answer(product, [[4, 4], [10, 8]]).
3
- answer(determinant, 8.0000000000000018).