@hviana/sema 0.1.5 → 0.1.7

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 (170) hide show
  1. package/AGENTS.md +6 -5
  2. package/CITATION.cff +49 -0
  3. package/HOW_IT_WORKS.md +11 -12
  4. package/README.md +7 -5
  5. package/dist/example/demo.js +25 -25
  6. package/dist/example/train_base.d.ts +19 -21
  7. package/dist/example/train_base.js +1700 -1952
  8. package/dist/src/alphabet.d.ts +3 -3
  9. package/dist/src/alphabet.js +24 -27
  10. package/dist/src/alu/src/alu.d.ts +169 -194
  11. package/dist/src/alu/src/alu.js +374 -414
  12. package/dist/src/alu/src/expr.d.ts +36 -43
  13. package/dist/src/alu/src/expr.js +252 -278
  14. package/dist/src/alu/src/index.d.ts +9 -98
  15. package/dist/src/alu/src/index.js +10 -64
  16. package/dist/src/alu/src/kernel-arith.d.ts +1 -5
  17. package/dist/src/alu/src/kernel-arith.js +195 -285
  18. package/dist/src/alu/src/kernel-bits.d.ts +1 -5
  19. package/dist/src/alu/src/kernel-bits.js +81 -115
  20. package/dist/src/alu/src/kernel-logic.js +33 -63
  21. package/dist/src/alu/src/kernel-nd.js +155 -195
  22. package/dist/src/alu/src/kernel-numeric.d.ts +16 -80
  23. package/dist/src/alu/src/kernel-numeric.js +259 -331
  24. package/dist/src/alu/src/operation.d.ts +112 -132
  25. package/dist/src/alu/src/operation.js +149 -163
  26. package/dist/src/alu/src/parser.d.ts +168 -175
  27. package/dist/src/alu/src/parser.js +382 -421
  28. package/dist/src/alu/src/resonance.d.ts +16 -25
  29. package/dist/src/alu/src/resonance.js +49 -56
  30. package/dist/src/alu/src/text.d.ts +3 -7
  31. package/dist/src/alu/src/text.js +31 -37
  32. package/dist/src/alu/src/value.d.ts +14 -14
  33. package/dist/src/alu/src/value.js +150 -160
  34. package/dist/src/alu/test/alu.test.js +543 -656
  35. package/dist/src/bytes.d.ts +1 -5
  36. package/dist/src/bytes.js +33 -40
  37. package/dist/src/config.d.ts +99 -102
  38. package/dist/src/config.js +78 -83
  39. package/dist/src/derive/src/deduction.d.ts +60 -62
  40. package/dist/src/derive/src/deduction.js +103 -109
  41. package/dist/src/derive/src/index.d.ts +1 -7
  42. package/dist/src/derive/src/priority-queue.d.ts +8 -8
  43. package/dist/src/derive/src/priority-queue.js +57 -61
  44. package/dist/src/derive/src/rewrite.d.ts +15 -18
  45. package/dist/src/derive/src/rewrite.js +59 -67
  46. package/dist/src/derive/src/trie.d.ts +56 -56
  47. package/dist/src/derive/src/trie.js +175 -179
  48. package/dist/src/derive/test/derive.test.js +96 -100
  49. package/dist/src/extension.d.ts +13 -16
  50. package/dist/src/geometry.d.ts +29 -43
  51. package/dist/src/geometry.js +324 -268
  52. package/dist/src/index.d.ts +2 -17
  53. package/dist/src/index.js +2 -12
  54. package/dist/src/ingest-cache.d.ts +25 -30
  55. package/dist/src/ingest-cache.js +108 -127
  56. package/dist/src/mind/articulation.d.ts +1 -5
  57. package/dist/src/mind/articulation.js +77 -112
  58. package/dist/src/mind/attention.d.ts +40 -108
  59. package/dist/src/mind/attention.js +754 -872
  60. package/dist/src/mind/canonical.d.ts +4 -19
  61. package/dist/src/mind/canonical.js +27 -31
  62. package/dist/src/mind/graph-search.d.ts +201 -225
  63. package/dist/src/mind/graph-search.js +742 -821
  64. package/dist/src/mind/index.d.ts +2 -10
  65. package/dist/src/mind/junction.d.ts +31 -58
  66. package/dist/src/mind/junction.js +172 -237
  67. package/dist/src/mind/learning.d.ts +16 -52
  68. package/dist/src/mind/learning.js +143 -165
  69. package/dist/src/mind/match.d.ts +20 -69
  70. package/dist/src/mind/match.js +259 -318
  71. package/dist/src/mind/mechanisms/alu.js +16 -16
  72. package/dist/src/mind/mechanisms/cast.d.ts +9 -13
  73. package/dist/src/mind/mechanisms/cast.js +363 -456
  74. package/dist/src/mind/mechanisms/confluence.d.ts +8 -12
  75. package/dist/src/mind/mechanisms/confluence.js +152 -183
  76. package/dist/src/mind/mechanisms/cover.d.ts +2 -8
  77. package/dist/src/mind/mechanisms/cover.js +148 -210
  78. package/dist/src/mind/mechanisms/extraction.d.ts +8 -34
  79. package/dist/src/mind/mechanisms/extraction.js +234 -288
  80. package/dist/src/mind/mechanisms/recall.d.ts +6 -10
  81. package/dist/src/mind/mechanisms/recall.js +141 -185
  82. package/dist/src/mind/mind.d.ts +194 -155
  83. package/dist/src/mind/mind.js +392 -289
  84. package/dist/src/mind/pipeline-mechanism.d.ts +112 -124
  85. package/dist/src/mind/pipeline-mechanism.js +161 -172
  86. package/dist/src/mind/pipeline.d.ts +4 -14
  87. package/dist/src/mind/pipeline.js +125 -189
  88. package/dist/src/mind/primitives.d.ts +20 -33
  89. package/dist/src/mind/primitives.js +124 -120
  90. package/dist/src/mind/rationale.d.ts +86 -98
  91. package/dist/src/mind/rationale.js +113 -121
  92. package/dist/src/mind/reasoning.d.ts +2 -13
  93. package/dist/src/mind/reasoning.js +129 -166
  94. package/dist/src/mind/recognition.d.ts +1 -4
  95. package/dist/src/mind/recognition.js +198 -210
  96. package/dist/src/mind/resonance.d.ts +5 -22
  97. package/dist/src/mind/resonance.js +0 -0
  98. package/dist/src/mind/trace.d.ts +6 -25
  99. package/dist/src/mind/trace.js +50 -58
  100. package/dist/src/mind/traverse.d.ts +29 -58
  101. package/dist/src/mind/traverse.js +344 -356
  102. package/dist/src/mind/types.d.ts +122 -127
  103. package/dist/src/mind/types.js +60 -69
  104. package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
  105. package/dist/src/rabitq-ivf/src/database.js +201 -0
  106. package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
  107. package/dist/src/{rabitq-hnsw → rabitq-ivf}/src/index.js +1 -3
  108. package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
  109. package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
  110. package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
  111. package/dist/src/rabitq-ivf/src/prng.js +36 -0
  112. package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
  113. package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
  114. package/dist/src/sema.d.ts +9 -13
  115. package/dist/src/sema.js +26 -40
  116. package/dist/src/store-sqlite.d.ts +171 -166
  117. package/dist/src/store-sqlite.js +768 -662
  118. package/dist/src/store.d.ts +597 -630
  119. package/dist/src/store.js +1434 -1581
  120. package/dist/src/vec.d.ts +5 -9
  121. package/dist/src/vec.js +61 -73
  122. package/example/train_base.ts +33 -15
  123. package/index.html +65 -0
  124. package/package.json +1 -1
  125. package/src/alu/README.md +1 -1
  126. package/src/alu/src/index.ts +1 -1
  127. package/src/config.ts +19 -27
  128. package/src/geometry.ts +93 -1
  129. package/src/index.ts +6 -11
  130. package/src/mind/attention.ts +11 -6
  131. package/src/mind/mechanisms/recall.ts +29 -13
  132. package/src/mind/mind.ts +250 -19
  133. package/src/mind/primitives.ts +43 -6
  134. package/src/mind/recognition.ts +26 -8
  135. package/src/mind/traverse.ts +47 -0
  136. package/src/mind/types.ts +20 -21
  137. package/src/rabitq-ivf/README.md +56 -0
  138. package/src/rabitq-ivf/src/database.ts +276 -0
  139. package/src/{rabitq-hnsw → rabitq-ivf}/src/index.ts +2 -5
  140. package/src/rabitq-ivf/src/ivf.ts +1330 -0
  141. package/src/{rabitq-hnsw → rabitq-ivf}/src/prng.ts +1 -1
  142. package/src/store-sqlite.ts +204 -9
  143. package/src/store.ts +27 -34
  144. package/test/08-storage.test.mjs +3 -3
  145. package/test/13-conversation.test.mjs +190 -20
  146. package/test/14-scaling.test.mjs +2 -2
  147. package/test/35-ivf.test.mjs +263 -0
  148. package/test/36-bloom.test.mjs +123 -0
  149. package/dist/src/rabitq-hnsw/src/database.d.ts +0 -202
  150. package/dist/src/rabitq-hnsw/src/database.js +0 -405
  151. package/dist/src/rabitq-hnsw/src/heap.d.ts +0 -22
  152. package/dist/src/rabitq-hnsw/src/heap.js +0 -94
  153. package/dist/src/rabitq-hnsw/src/hnsw.d.ts +0 -125
  154. package/dist/src/rabitq-hnsw/src/hnsw.js +0 -500
  155. package/dist/src/rabitq-hnsw/src/index.d.ts +0 -15
  156. package/dist/src/rabitq-hnsw/src/prng.d.ts +0 -19
  157. package/dist/src/rabitq-hnsw/src/prng.js +0 -38
  158. package/dist/src/rabitq-hnsw/src/rabitq.d.ts +0 -95
  159. package/dist/src/rabitq-hnsw/src/rabitq.js +0 -299
  160. package/dist/src/rabitq-hnsw/src/store.d.ts +0 -162
  161. package/dist/src/rabitq-hnsw/src/store.js +0 -916
  162. package/dist/src/rabitq-hnsw/test/hnsw.test.d.ts +0 -1
  163. package/dist/src/rabitq-hnsw/test/hnsw.test.js +0 -1197
  164. package/src/rabitq-hnsw/README.md +0 -303
  165. package/src/rabitq-hnsw/src/database.ts +0 -492
  166. package/src/rabitq-hnsw/src/heap.ts +0 -90
  167. package/src/rabitq-hnsw/src/hnsw.ts +0 -514
  168. package/src/rabitq-hnsw/src/store.ts +0 -994
  169. package/src/rabitq-hnsw/test/hnsw.test.ts +0 -1213
  170. /package/src/{rabitq-hnsw → rabitq-ivf}/src/rabitq.ts +0 -0
@@ -23,164 +23,130 @@ import { asInt, bit, int } from "./value.js";
23
23
  * Width is chosen by the caller wide enough that the signed result cannot
24
24
  * overflow, so interpreting the top bit as the sign is always correct. */
25
25
  function toBits(n, width) {
26
- const mod = 1n << BigInt(width);
27
- let u = ((n % mod) + mod) % mod; // two's-complement representation
28
- const bits = new Array(width);
29
- for (let i = 0; i < width; i++) {
30
- bits[i] = Number(u & 1n);
31
- u >>= 1n;
32
- }
33
- return bits;
26
+ const mod = 1n << BigInt(width);
27
+ let u = ((n % mod) + mod) % mod; // two's-complement representation
28
+ const bits = new Array(width);
29
+ for (let i = 0; i < width; i++) {
30
+ bits[i] = Number(u & 1n);
31
+ u >>= 1n;
32
+ }
33
+ return bits;
34
34
  }
35
35
  /** Interpret a two's-complement little-endian bit array as a signed bigint. */
36
36
  function fromBits(bits) {
37
- const width = bits.length;
38
- let u = 0n;
39
- for (let i = 0; i < width; i++) {
40
- if (bits[i]) {
41
- u |= 1n << BigInt(i);
42
- }
43
- }
44
- const signBitSet = bits[width - 1] === 1;
45
- return signBitSet ? u - (1n << BigInt(width)) : u;
37
+ const width = bits.length;
38
+ let u = 0n;
39
+ for (let i = 0; i < width; i++)
40
+ if (bits[i])
41
+ u |= 1n << BigInt(i);
42
+ const signBitSet = bits[width - 1] === 1;
43
+ return signBitSet ? u - (1n << BigInt(width)) : u;
46
44
  }
47
45
  /** Bits needed to hold |n| (at least 1). */
48
46
  function magnitudeBits(n) {
49
- const a = n < 0n ? -n : n;
50
- return a === 0n ? 1 : a.toString(2).length;
47
+ const a = n < 0n ? -n : n;
48
+ return a === 0n ? 1 : a.toString(2).length;
51
49
  }
52
50
  /** A single full adder built from the logic gates: sum = a⊕b⊕cin, carry =
53
51
  * (a∧b) ∨ (cin ∧ (a⊕b)). Every gate goes through ctx.apply → nand. */
54
52
  function fullAdder(ctx, a, b, cin) {
55
- const A = bit(a), B = bit(b), C = bit(cin);
56
- const axb = ctx.apply("xor", [A, B]);
57
- const sum = ctx.apply("xor", [axb, C]);
58
- const cout = ctx.apply("or", [
59
- ctx.apply("and", [A, B]),
60
- ctx.apply("and", [C, axb]),
61
- ]);
62
- return {
63
- sum: sum.domain === "bit" ? sum.b : 0,
64
- cout: cout.domain === "bit" ? cout.b : 0,
65
- };
53
+ const A = bit(a), B = bit(b), C = bit(cin);
54
+ const axb = ctx.apply("xor", [A, B]);
55
+ const sum = ctx.apply("xor", [axb, C]);
56
+ const cout = ctx.apply("or", [
57
+ ctx.apply("and", [A, B]),
58
+ ctx.apply("and", [C, axb]),
59
+ ]);
60
+ return {
61
+ sum: sum.domain === "bit" ? sum.b : 0,
62
+ cout: cout.domain === "bit" ? cout.b : 0,
63
+ };
66
64
  }
67
65
  /** Ripple-carry add of two two's-complement bit arrays (equal width). */
68
66
  function rippleAdd(ctx, x, y) {
69
- const width = x.length;
70
- const out = new Array(width);
71
- let carry = 0;
72
- for (let i = 0; i < width; i++) {
73
- const { sum, cout } = fullAdder(ctx, x[i], y[i], carry);
74
- out[i] = sum;
75
- carry = cout;
76
- }
77
- return out;
67
+ const width = x.length;
68
+ const out = new Array(width);
69
+ let carry = 0;
70
+ for (let i = 0; i < width; i++) {
71
+ const { sum, cout } = fullAdder(ctx, x[i], y[i], carry);
72
+ out[i] = sum;
73
+ carry = cout;
74
+ }
75
+ return out;
78
76
  }
79
77
  /** Bitwise NOT of a bit array, via the not gate. */
80
78
  function notBits(ctx, x) {
81
- return x.map((b) => {
82
- const r = ctx.apply("not", [bit(b)]);
83
- return (r.domain === "bit" ? r.b : 0);
84
- });
79
+ return x.map((b) => {
80
+ const r = ctx.apply("not", [bit(b)]);
81
+ return (r.domain === "bit" ? r.b : 0);
82
+ });
85
83
  }
86
84
  /** Two's-complement negate: invert every bit, then add one. */
87
85
  function negateBitsArr(ctx, x) {
88
- const width = x.length;
89
- const one = toBits(1n, width);
90
- return rippleAdd(ctx, notBits(ctx, x), one);
86
+ const width = x.length;
87
+ const one = toBits(1n, width);
88
+ return rippleAdd(ctx, notBits(ctx, x), one);
91
89
  }
92
90
  // ── public pure functions (used by the tests and the explicit ops) ──────────
93
91
  /** Exact addition through the ripple of full_adders. */
94
92
  export function addBits(ctx, a, b) {
95
- const width = Math.max(magnitudeBits(a), magnitudeBits(b)) + 2;
96
- return fromBits(rippleAdd(ctx, toBits(a, width), toBits(b, width)));
93
+ const width = Math.max(magnitudeBits(a), magnitudeBits(b)) + 2;
94
+ return fromBits(rippleAdd(ctx, toBits(a, width), toBits(b, width)));
97
95
  }
98
96
  /** Exact two's-complement negation. */
99
97
  export function negateBits(ctx, a) {
100
- const width = magnitudeBits(a) + 2;
101
- return fromBits(negateBitsArr(ctx, toBits(a, width)));
98
+ const width = magnitudeBits(a) + 2;
99
+ return fromBits(negateBitsArr(ctx, toBits(a, width)));
102
100
  }
103
101
  /** Exact shift-add multiplication: for each set bit i of b, add (a << i) to the
104
102
  * accumulator. The per-bit gate is an AND mask of the shifted a with bᵢ; the
105
103
  * accumulation is rippleAdd — so multiply traces entirely to nand. */
106
104
  export function mulBits(ctx, a, b) {
107
- const width = magnitudeBits(a) + magnitudeBits(b) + 2;
108
- const aBits = toBits(a, width);
109
- const bBits = toBits(b, width);
110
- let acc = toBits(0n, width);
111
- for (let i = 0; i < width; i++) {
112
- if (bBits[i] === 0) {
113
- continue;
114
- }
115
- // a shifted left by i (within width), masked by bᵢ (= 1 here).
116
- const shifted = new Array(width);
117
- for (let k = 0; k < width; k++) {
118
- const src = k - i >= 0 ? aBits[k - i] : 0;
119
- const m = ctx.apply("and", [bit(src), bit(bBits[i])]);
120
- shifted[k] = m.domain === "bit" ? m.b : 0;
105
+ const width = magnitudeBits(a) + magnitudeBits(b) + 2;
106
+ const aBits = toBits(a, width);
107
+ const bBits = toBits(b, width);
108
+ let acc = toBits(0n, width);
109
+ for (let i = 0; i < width; i++) {
110
+ if (bBits[i] === 0)
111
+ continue;
112
+ // a shifted left by i (within width), masked by bᵢ (= 1 here).
113
+ const shifted = new Array(width);
114
+ for (let k = 0; k < width; k++) {
115
+ const src = k - i >= 0 ? aBits[k - i] : 0;
116
+ const m = ctx.apply("and", [bit(src), bit(bBits[i])]);
117
+ shifted[k] = (m.domain === "bit" ? m.b : 0);
118
+ }
119
+ acc = rippleAdd(ctx, acc, shifted);
121
120
  }
122
- acc = rippleAdd(ctx, acc, shifted);
123
- }
124
- return fromBits(acc);
121
+ return fromBits(acc);
125
122
  }
126
123
  /** Sign of an integer as {-1, 0, 1}: zero when every bit is zero, −1 when the
127
124
  * two's-complement sign bit is set, +1 otherwise — read off the bit array. */
128
125
  export function signBits(ctx, a) {
129
- if (a === 0n) {
130
- return 0n;
131
- }
132
- const width = magnitudeBits(a) + 2;
133
- const bits = toBits(a, width);
134
- return bits[width - 1] === 1 ? -1n : 1n;
126
+ if (a === 0n)
127
+ return 0n;
128
+ const width = magnitudeBits(a) + 2;
129
+ const bits = toBits(a, width);
130
+ return bits[width - 1] === 1 ? -1n : 1n;
135
131
  }
136
132
  /** Compare via sign(subtract): −1 if a<b, 0 if a=b, +1 if a>b, where subtract is
137
133
  * add ∘ negate — so comparison is structured arithmetic, not a new primitive. */
138
134
  export function compareBits(ctx, a, b) {
139
- return signBits(ctx, addBits(ctx, a, negateBits(ctx, b)));
135
+ return signBits(ctx, addBits(ctx, a, negateBits(ctx, b)));
140
136
  }
141
137
  /** Register the bit-bootstrap ops (int domain) into `r`. They have no surface
142
138
  * forms — they are an internal exhibit, exercised by the tests and by anyone
143
139
  * who wants to walk the nand→arithmetic chain, not matched from a query. */
144
140
  export function registerBits(r) {
145
- // full_adder exposed as a 3-bit op returning the 2-bit {sum, carry} as an int
146
- // 0..3 (carry in bit 1, sum in bit 0) so it is callable/testable on its own.
147
- r.derive("bits.fullAdder", 3, [], (args, ctx) => {
148
- const s = fullAdder(
149
- ctx,
150
- asInt(args[0]) === 0n ? 0 : 1,
151
- asInt(args[1]) === 0n ? 0 : 1,
152
- asInt(args[2]) === 0n ? 0 : 1,
153
- );
154
- return int(BigInt(s.sum) | (BigInt(s.cout) << 1n));
155
- });
156
- r.derive(
157
- "bits.add",
158
- 2,
159
- [],
160
- (args, ctx) => int(addBits(ctx, asInt(args[0]), asInt(args[1]))),
161
- );
162
- r.derive(
163
- "bits.negate",
164
- 1,
165
- [],
166
- (args, ctx) => int(negateBits(ctx, asInt(args[0]))),
167
- );
168
- r.derive(
169
- "bits.multiply",
170
- 2,
171
- [],
172
- (args, ctx) => int(mulBits(ctx, asInt(args[0]), asInt(args[1]))),
173
- );
174
- r.derive(
175
- "bits.sign",
176
- 1,
177
- [],
178
- (args, ctx) => int(signBits(ctx, asInt(args[0]))),
179
- );
180
- r.derive(
181
- "bits.compare",
182
- 2,
183
- [],
184
- (args, ctx) => int(compareBits(ctx, asInt(args[0]), asInt(args[1]))),
185
- );
141
+ // full_adder exposed as a 3-bit op returning the 2-bit {sum, carry} as an int
142
+ // 0..3 (carry in bit 1, sum in bit 0) so it is callable/testable on its own.
143
+ r.derive("bits.fullAdder", 3, [], (args, ctx) => {
144
+ const s = fullAdder(ctx, asInt(args[0]) === 0n ? 0 : 1, asInt(args[1]) === 0n ? 0 : 1, asInt(args[2]) === 0n ? 0 : 1);
145
+ return int(BigInt(s.sum) | (BigInt(s.cout) << 1n));
146
+ });
147
+ r.derive("bits.add", 2, [], (args, ctx) => int(addBits(ctx, asInt(args[0]), asInt(args[1]))));
148
+ r.derive("bits.negate", 1, [], (args, ctx) => int(negateBits(ctx, asInt(args[0]))));
149
+ r.derive("bits.multiply", 2, [], (args, ctx) => int(mulBits(ctx, asInt(args[0]), asInt(args[1]))));
150
+ r.derive("bits.sign", 1, [], (args, ctx) => int(signBits(ctx, asInt(args[0]))));
151
+ r.derive("bits.compare", 2, [], (args, ctx) => int(compareBits(ctx, asInt(args[0]), asInt(args[1]))));
186
152
  }
@@ -24,67 +24,37 @@ import { asBit, bit } from "./value.js";
24
24
  /** Register the logic layer into `r`. Idempotent in effect (re-registering
25
25
  * overwrites with the same definitions). */
26
26
  export function registerLogic(r) {
27
- // The one irreducible gate. ¬(a ∧ b).
28
- r.prim("nand", 2, [], (args) => {
29
- const a = asBit(args[0]);
30
- const b = asBit(args[1]);
31
- return bit(a & b ? 0 : 1);
32
- });
33
- // Exposed-but-derived basic gates.
34
- r.derive(
35
- "not",
36
- 1,
37
- ["not", "!", "~", "¬"],
38
- (args, ctx) => ctx.apply("nand", [args[0], args[0]]),
39
- );
40
- r.derive(
41
- "and",
42
- 2,
43
- ["and", "&", "&&", ""],
44
- (args, ctx) => ctx.apply("not", [ctx.apply("nand", [args[0], args[1]])]),
45
- );
46
- r.derive("or", 2, ["or", "|", "||", "∨"], (args, ctx) => {
47
- const na = ctx.apply("not", [args[0]]);
48
- const nb = ctx.apply("not", [args[1]]);
49
- return ctx.apply("nand", [na, nb]);
50
- });
51
- // Fully derived gates.
52
- r.derive(
53
- "nor",
54
- 2,
55
- ["nor", "⊽"],
56
- (args, ctx) => ctx.apply("not", [ctx.apply("or", [args[0], args[1]])]),
57
- );
58
- r.derive("xor", 2, ["xor", "^", "⊕"], (args, ctx) => {
59
- const a = args[0], b = args[1];
60
- const left = ctx.apply("and", [a, ctx.apply("not", [b])]);
61
- const right = ctx.apply("and", [ctx.apply("not", [a]), b]);
62
- return ctx.apply("or", [left, right]);
63
- });
64
- r.derive(
65
- "xnor",
66
- 2,
67
- ["xnor", "⊙"],
68
- (args, ctx) => ctx.apply("not", [ctx.apply("xor", [args[0], args[1]])]),
69
- );
70
- r.derive(
71
- "implies",
72
- 2,
73
- ["implies", "=>", "→", "⇒"],
74
- (args, ctx) => ctx.apply("or", [ctx.apply("not", [args[0]]), args[1]]),
75
- );
76
- r.derive(
77
- "iff",
78
- 2,
79
- ["iff", "<=>", "↔", "⇔"],
80
- (args, ctx) => ctx.apply("xnor", [args[0], args[1]]),
81
- );
82
- // mux(s, a, b): select a when s = 0, b when s = 1. The bridge to control
83
- // flow — conditional selection, and (with recursion in the caller) looping.
84
- r.derive("mux", 3, ["mux", "select", "?:"], (args, ctx) => {
85
- const s = args[0], a = args[1], b = args[2];
86
- const lo = ctx.apply("and", [ctx.apply("not", [s]), a]);
87
- const hi = ctx.apply("and", [s, b]);
88
- return ctx.apply("or", [lo, hi]);
89
- });
27
+ // The one irreducible gate. ¬(a ∧ b).
28
+ r.prim("nand", 2, [], (args) => {
29
+ const a = asBit(args[0]);
30
+ const b = asBit(args[1]);
31
+ return bit(a & b ? 0 : 1);
32
+ });
33
+ // Exposed-but-derived basic gates.
34
+ r.derive("not", 1, ["not", "!", "~", "¬"], (args, ctx) => ctx.apply("nand", [args[0], args[0]]));
35
+ r.derive("and", 2, ["and", "&", "&&", "∧"], (args, ctx) => ctx.apply("not", [ctx.apply("nand", [args[0], args[1]])]));
36
+ r.derive("or", 2, ["or", "|", "||", "∨"], (args, ctx) => {
37
+ const na = ctx.apply("not", [args[0]]);
38
+ const nb = ctx.apply("not", [args[1]]);
39
+ return ctx.apply("nand", [na, nb]);
40
+ });
41
+ // Fully derived gates.
42
+ r.derive("nor", 2, ["nor", "⊽"], (args, ctx) => ctx.apply("not", [ctx.apply("or", [args[0], args[1]])]));
43
+ r.derive("xor", 2, ["xor", "^", ""], (args, ctx) => {
44
+ const a = args[0], b = args[1];
45
+ const left = ctx.apply("and", [a, ctx.apply("not", [b])]);
46
+ const right = ctx.apply("and", [ctx.apply("not", [a]), b]);
47
+ return ctx.apply("or", [left, right]);
48
+ });
49
+ r.derive("xnor", 2, ["xnor", "⊙"], (args, ctx) => ctx.apply("not", [ctx.apply("xor", [args[0], args[1]])]));
50
+ r.derive("implies", 2, ["implies", "=>", "→", "⇒"], (args, ctx) => ctx.apply("or", [ctx.apply("not", [args[0]]), args[1]]));
51
+ r.derive("iff", 2, ["iff", "<=>", "↔", "⇔"], (args, ctx) => ctx.apply("xnor", [args[0], args[1]]));
52
+ // mux(s, a, b): select a when s = 0, b when s = 1. The bridge to control
53
+ // flow — conditional selection, and (with recursion in the caller) looping.
54
+ r.derive("mux", 3, ["mux", "select", "?:"], (args, ctx) => {
55
+ const s = args[0], a = args[1], b = args[2];
56
+ const lo = ctx.apply("and", [ctx.apply("not", [s]), a]);
57
+ const hi = ctx.apply("and", [s, b]);
58
+ return ctx.apply("or", [lo, hi]);
59
+ });
90
60
  }