@stevenvo780/st-lang 2.6.1 → 2.7.0

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 (56) hide show
  1. package/dist/ast/nodes.d.ts +18 -1
  2. package/dist/ast/nodes.d.ts.map +1 -1
  3. package/dist/parser/parser.d.ts +2 -0
  4. package/dist/parser/parser.d.ts.map +1 -1
  5. package/dist/parser/parser.js +82 -0
  6. package/dist/parser/parser.js.map +1 -1
  7. package/dist/profiles/classical/first-order.d.ts +1 -0
  8. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  9. package/dist/profiles/classical/first-order.js +35 -0
  10. package/dist/profiles/classical/first-order.js.map +1 -1
  11. package/dist/profiles/classical/propositional.d.ts +6 -1
  12. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  13. package/dist/profiles/classical/propositional.js +334 -35
  14. package/dist/profiles/classical/propositional.js.map +1 -1
  15. package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
  16. package/dist/profiles/paraconsistent/belnap.js +151 -3
  17. package/dist/profiles/paraconsistent/belnap.js.map +1 -1
  18. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  19. package/dist/profiles/shared/tableau-engine.js +49 -22
  20. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  21. package/dist/runtime/format.d.ts.map +1 -1
  22. package/dist/runtime/format.js +4 -0
  23. package/dist/runtime/format.js.map +1 -1
  24. package/dist/runtime/formula-factory.d.ts +26 -0
  25. package/dist/runtime/formula-factory.d.ts.map +1 -0
  26. package/dist/runtime/formula-factory.js +67 -0
  27. package/dist/runtime/formula-factory.js.map +1 -0
  28. package/dist/runtime/interpreter.d.ts +41 -0
  29. package/dist/runtime/interpreter.d.ts.map +1 -1
  30. package/dist/runtime/interpreter.js +1029 -245
  31. package/dist/runtime/interpreter.js.map +1 -1
  32. package/dist/tests/arithmetic.test.js +2 -1
  33. package/dist/tests/arithmetic.test.js.map +1 -1
  34. package/dist/tests/examples.test.js +12 -1
  35. package/dist/tests/examples.test.js.map +1 -1
  36. package/dist/tests/exhaustive-matrix.test.js +1 -1
  37. package/dist/tests/exhaustive-matrix.test.js.map +1 -1
  38. package/dist/tests/limits.test.js +15 -3
  39. package/dist/tests/limits.test.js.map +1 -1
  40. package/dist/tests/result-bindings.test.d.ts +2 -0
  41. package/dist/tests/result-bindings.test.d.ts.map +1 -0
  42. package/dist/tests/result-bindings.test.js +59 -0
  43. package/dist/tests/result-bindings.test.js.map +1 -0
  44. package/dist/tests/stress-hardware.test.d.ts +2 -0
  45. package/dist/tests/stress-hardware.test.d.ts.map +1 -0
  46. package/dist/tests/stress-hardware.test.js +516 -0
  47. package/dist/tests/stress-hardware.test.js.map +1 -0
  48. package/dist/tests/v1-features.test.js +1 -1
  49. package/dist/tests/v1-features.test.js.map +1 -1
  50. package/dist/types/index.d.ts +1 -1
  51. package/dist/types/index.d.ts.map +1 -1
  52. package/dist/utils/memo.d.ts +3 -0
  53. package/dist/utils/memo.d.ts.map +1 -1
  54. package/dist/utils/memo.js +30 -0
  55. package/dist/utils/memo.js.map +1 -1
  56. package/package.json +2 -1
@@ -5,6 +5,8 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ClassicalPropositional = void 0;
7
7
  exports.collectAtoms = collectAtoms;
8
+ exports.evaluateClassical = evaluateClassical;
9
+ exports.generateValuationsLazy = generateValuationsLazy;
8
10
  exports.formulaToString = formulaToString;
9
11
  exports.toNNF = toNNF;
10
12
  exports.toCNF = toCNF;
@@ -32,39 +34,39 @@ function computeCollectAtoms(f) {
32
34
  walk(f);
33
35
  return atoms;
34
36
  }
35
- function evaluate(f, v) {
37
+ function evaluateClassical(f, v) {
36
38
  switch (f.kind) {
37
39
  case 'atom':
38
40
  return f.name ? (v[f.name] ?? false) : false;
39
41
  case 'not':
40
- return f.args && f.args[0] ? !evaluate(f.args[0], v) : false;
42
+ return f.args && f.args[0] ? !evaluateClassical(f.args[0], v) : false;
41
43
  case 'and':
42
44
  return f.args && f.args[0] && f.args[1]
43
- ? evaluate(f.args[0], v) && evaluate(f.args[1], v)
45
+ ? evaluateClassical(f.args[0], v) && evaluateClassical(f.args[1], v)
44
46
  : false;
45
47
  case 'or':
46
48
  return f.args && f.args[0] && f.args[1]
47
- ? evaluate(f.args[0], v) || evaluate(f.args[1], v)
49
+ ? evaluateClassical(f.args[0], v) || evaluateClassical(f.args[1], v)
48
50
  : false;
49
51
  case 'implies':
50
52
  return f.args && f.args[0] && f.args[1]
51
- ? !evaluate(f.args[0], v) || evaluate(f.args[1], v)
53
+ ? !evaluateClassical(f.args[0], v) || evaluateClassical(f.args[1], v)
52
54
  : false;
53
55
  case 'biconditional':
54
56
  return f.args && f.args[0] && f.args[1]
55
- ? evaluate(f.args[0], v) === evaluate(f.args[1], v)
57
+ ? evaluateClassical(f.args[0], v) === evaluateClassical(f.args[1], v)
56
58
  : false;
57
59
  case 'nand':
58
60
  return f.args && f.args[0] && f.args[1]
59
- ? !(evaluate(f.args[0], v) && evaluate(f.args[1], v))
61
+ ? !(evaluateClassical(f.args[0], v) && evaluateClassical(f.args[1], v))
60
62
  : false;
61
63
  case 'nor':
62
64
  return f.args && f.args[0] && f.args[1]
63
- ? !(evaluate(f.args[0], v) || evaluate(f.args[1], v))
65
+ ? !(evaluateClassical(f.args[0], v) || evaluateClassical(f.args[1], v))
64
66
  : false;
65
67
  case 'xor':
66
68
  return f.args && f.args[0] && f.args[1]
67
- ? evaluate(f.args[0], v) !== evaluate(f.args[1], v)
69
+ ? evaluateClassical(f.args[0], v) !== evaluateClassical(f.args[1], v)
68
70
  : false;
69
71
  default:
70
72
  throw new Error(`Operador lógico no soportado en evaluación clásica: ${f.kind}`);
@@ -93,6 +95,184 @@ function generateValuations(atoms) {
93
95
  }
94
96
  return valuations;
95
97
  }
98
+ /**
99
+ * Generador lazy de valuaciones para streaming (usado por el intérprete para truth_table masivas).
100
+ */
101
+ function* generateValuationsLazy(atoms) {
102
+ const n = atoms.length;
103
+ if (n === 0) {
104
+ yield {};
105
+ return;
106
+ }
107
+ const total = 1 << n;
108
+ for (let i = 0; i < total; i++) {
109
+ const v = {};
110
+ for (let j = 0; j < n; j++) {
111
+ v[atoms[j]] = Boolean((i >> (n - 1 - j)) & 1);
112
+ }
113
+ yield v;
114
+ }
115
+ }
116
+ function bvCreate(total) {
117
+ return new Uint32Array((total + 31) >>> 5);
118
+ }
119
+ function bvOnes(total) {
120
+ const words = (total + 31) >>> 5;
121
+ const v = new Uint32Array(words);
122
+ v.fill(0xffffffff);
123
+ // Clear trailing bits in last word
124
+ const tail = total & 31;
125
+ if (tail)
126
+ v[words - 1] = (1 << tail) - 1;
127
+ return v;
128
+ }
129
+ function bvAnd(a, b) {
130
+ const r = new Uint32Array(a.length);
131
+ for (let i = 0; i < a.length; i++)
132
+ r[i] = a[i] & b[i];
133
+ return r;
134
+ }
135
+ function bvOr(a, b) {
136
+ const r = new Uint32Array(a.length);
137
+ for (let i = 0; i < a.length; i++)
138
+ r[i] = a[i] | b[i];
139
+ return r;
140
+ }
141
+ function bvXor(a, b) {
142
+ const r = new Uint32Array(a.length);
143
+ for (let i = 0; i < a.length; i++)
144
+ r[i] = a[i] ^ b[i];
145
+ return r;
146
+ }
147
+ function bvNot(a, ones) {
148
+ const r = new Uint32Array(a.length);
149
+ for (let i = 0; i < a.length; i++)
150
+ r[i] = ~a[i] & ones[i];
151
+ return r;
152
+ }
153
+ function bvIsZero(a) {
154
+ for (let i = 0; i < a.length; i++)
155
+ if (a[i] !== 0)
156
+ return false;
157
+ return true;
158
+ }
159
+ function bvEquals(a, b) {
160
+ for (let i = 0; i < a.length; i++)
161
+ if (a[i] !== b[i])
162
+ return false;
163
+ return true;
164
+ }
165
+ function bvPopcount(a) {
166
+ let count = 0;
167
+ for (let i = 0; i < a.length; i++) {
168
+ let v = a[i];
169
+ v = v - ((v >>> 1) & 0x55555555);
170
+ v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
171
+ count += (((v + (v >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24;
172
+ }
173
+ return count;
174
+ }
175
+ function bvTestBit(a, i) {
176
+ return (a[i >>> 5] & (1 << (i & 31))) !== 0;
177
+ }
178
+ // Find first set bit, or -1
179
+ function bvFirstSet(a) {
180
+ for (let w = 0; w < a.length; w++) {
181
+ if (a[w] !== 0)
182
+ return ((w << 5) + Math.clz32(a[w] & (-a[w] | 0))) ^ 31;
183
+ }
184
+ return -1;
185
+ }
186
+ function evaluateBitset(formula, atoms) {
187
+ const n = atoms.length;
188
+ if (n > 26)
189
+ throw new Error('Demasiadas variables para evaluación bitset (>26)');
190
+ const total = 1 << n;
191
+ const allOnes = bvOnes(total);
192
+ const words = allOnes.length;
193
+ // Build atom masks: atom j is true when bit j of the row index is 1.
194
+ // Row index i has bit j set when (i >>> (n-1-j)) & 1.
195
+ // Equivalent: word w, bit b (i = w*32+b), atom j true iff ((w*32+b) >>> (n-1-j)) & 1.
196
+ const atomMasks = new Map();
197
+ for (let j = 0; j < n; j++) {
198
+ const shift = n - 1 - j;
199
+ const mask = bvCreate(total);
200
+ // The pattern for atom j repeats with period 2^(shift+1).
201
+ // Within each period, the first 2^shift bits are 0, next 2^shift are 1.
202
+ // For shift < 5, the pattern fits within single words and we can use word-level fill.
203
+ if (shift < 5) {
204
+ // Pattern period in bits
205
+ const period = 1 << (shift + 1);
206
+ const halfPeriod = 1 << shift;
207
+ // Build a 32-bit pattern
208
+ let pattern = 0;
209
+ for (let b = 0; b < 32; b++) {
210
+ if (b % period >= halfPeriod)
211
+ pattern |= 1 << b;
212
+ }
213
+ mask.fill(pattern);
214
+ }
215
+ else {
216
+ // shift >= 5: consecutive words are all-0 or all-1
217
+ const wordPeriod = 1 << (shift - 5 + 1); // period in words
218
+ const halfWordPeriod = wordPeriod >>> 1;
219
+ for (let w = 0; w < words; w++) {
220
+ const posInPeriod = w % wordPeriod;
221
+ mask[w] = posInPeriod >= halfWordPeriod ? 0xffffffff : 0;
222
+ }
223
+ }
224
+ // Clear trailing bits
225
+ const tail = total & 31;
226
+ if (tail && words > 0)
227
+ mask[words - 1] &= (1 << tail) - 1;
228
+ atomMasks.set(atoms[j], mask);
229
+ }
230
+ function evalBits(f) {
231
+ switch (f.kind) {
232
+ case 'atom':
233
+ return atomMasks.get(f.name) ?? bvCreate(total);
234
+ case 'not':
235
+ return bvNot(evalBits(f.args[0]), allOnes);
236
+ case 'and':
237
+ return bvAnd(evalBits(f.args[0]), evalBits(f.args[1]));
238
+ case 'or':
239
+ return bvOr(evalBits(f.args[0]), evalBits(f.args[1]));
240
+ case 'implies':
241
+ return bvOr(bvNot(evalBits(f.args[0]), allOnes), evalBits(f.args[1]));
242
+ case 'biconditional':
243
+ return bvNot(bvXor(evalBits(f.args[0]), evalBits(f.args[1])), allOnes);
244
+ case 'xor':
245
+ return bvXor(evalBits(f.args[0]), evalBits(f.args[1]));
246
+ case 'nand':
247
+ return bvNot(bvAnd(evalBits(f.args[0]), evalBits(f.args[1])), allOnes);
248
+ case 'nor':
249
+ return bvNot(bvOr(evalBits(f.args[0]), evalBits(f.args[1])), allOnes);
250
+ default:
251
+ throw new Error(`Operador no soportado en evaluación bitset: ${f.kind}`);
252
+ }
253
+ }
254
+ return { result: evalBits(formula), atomMasks, total, allOnes };
255
+ }
256
+ function bitsetPopcount(a) {
257
+ return bvPopcount(a);
258
+ }
259
+ function isPurePropositional(f) {
260
+ switch (f.kind) {
261
+ case 'atom':
262
+ return true;
263
+ case 'not':
264
+ case 'and':
265
+ case 'or':
266
+ case 'implies':
267
+ case 'biconditional':
268
+ case 'xor':
269
+ case 'nand':
270
+ case 'nor':
271
+ return (f.args || []).every(isPurePropositional);
272
+ default:
273
+ return false;
274
+ }
275
+ }
96
276
  /**
97
277
  * Aplana recursivamente nodos binarios del mismo kind asociativo.
98
278
  * Ej: or(or(P,Q), R) → [P, Q, R]
@@ -219,6 +399,9 @@ function computeFormulaToString(f) {
219
399
  }
220
400
  }
221
401
  function toNNF(f) {
402
+ return (0, memo_1.memoizeNNF)(f, computeNNF);
403
+ }
404
+ function computeNNF(f) {
222
405
  const simplify = (node, negated) => {
223
406
  const k = node.kind;
224
407
  const args = node.args || [];
@@ -351,7 +534,7 @@ function distributeOrOverAnd(f) {
351
534
  return f;
352
535
  }
353
536
  function toCNF(f) {
354
- return distributeOrOverAnd(toNNF(f));
537
+ return (0, memo_1.memoizeCNF)(f, (formula) => distributeOrOverAnd(toNNF(formula)));
355
538
  }
356
539
  function distributeAndOverOr(f) {
357
540
  if (f.kind === 'and' && f.args?.[0] && f.args?.[1]) {
@@ -382,7 +565,7 @@ function distributeAndOverOr(f) {
382
565
  return f;
383
566
  }
384
567
  function toDNF(f) {
385
- return distributeAndOverOr(toNNF(f));
568
+ return (0, memo_1.memoizeDNF)(f, (formula) => distributeAndOverOr(toNNF(formula)));
386
569
  }
387
570
  /**
388
571
  * Extracts clauses from a CNF formula for resolution analysis (#28)
@@ -943,23 +1126,47 @@ function tryDerive(goal, theory, premiseNames) {
943
1126
  for (const f of allAxiomFormulas)
944
1127
  collectAtoms(f).forEach((a) => atoms.add(a));
945
1128
  collectAtoms(goal).forEach((a) => atoms.add(a));
946
- const atomList = Array.from(atoms);
947
- const valuations = generateValuations(atomList);
948
- let semanticallyValid = true;
949
- for (const v of valuations) {
950
- const premisesTrue = allAxiomFormulas.every((f) => evaluate(f, v));
951
- if (premisesTrue && !evaluate(goal, v)) {
952
- semanticallyValid = false;
953
- break;
1129
+ const atomList = Array.from(atoms).sort();
1130
+ // Fast path: bitset semantic check
1131
+ const allPure = allAxiomFormulas.every(isPurePropositional) && isPurePropositional(goal);
1132
+ if (allPure && atomList.length <= 26) {
1133
+ const premiseBits = allAxiomFormulas.map((f) => evaluateBitset(f, atomList).result);
1134
+ const goalBits = evaluateBitset(goal, atomList).result;
1135
+ const allOnes = bvOnes(1 << atomList.length);
1136
+ // Conjunction of all premises
1137
+ let premisesConj = allOnes;
1138
+ for (const pb of premiseBits)
1139
+ premisesConj = bvAnd(premisesConj, pb);
1140
+ // Valid if: wherever premises are true, goal is also true
1141
+ // i.e., premisesConj & ~goalBits === 0
1142
+ if (bvIsZero(bvAnd(premisesConj, bvNot(goalBits, allOnes)))) {
1143
+ return {
1144
+ goal,
1145
+ steps: state.steps,
1146
+ status: 'complete',
1147
+ derivedFrom: premiseNames,
1148
+ };
954
1149
  }
955
1150
  }
956
- if (semanticallyValid) {
957
- return {
958
- goal,
959
- steps: state.steps,
960
- status: 'complete',
961
- derivedFrom: premiseNames,
962
- };
1151
+ else {
1152
+ // Classic fallback
1153
+ const valuations = generateValuations(atomList);
1154
+ let semanticallyValid = true;
1155
+ for (const v of valuations) {
1156
+ const premisesTrue = allAxiomFormulas.every((f) => evaluateClassical(f, v));
1157
+ if (premisesTrue && !evaluateClassical(goal, v)) {
1158
+ semanticallyValid = false;
1159
+ break;
1160
+ }
1161
+ }
1162
+ if (semanticallyValid) {
1163
+ return {
1164
+ goal,
1165
+ steps: state.steps,
1166
+ status: 'complete',
1167
+ derivedFrom: premiseNames,
1168
+ };
1169
+ }
963
1170
  }
964
1171
  }
965
1172
  return null;
@@ -1054,6 +1261,20 @@ class ClassicalPropositional {
1054
1261
  if (wf.length > 0) {
1055
1262
  return { status: 'error', diagnostics: wf, formula };
1056
1263
  }
1264
+ // Fast path: bitset validity check (no row materialization)
1265
+ const atoms = Array.from(collectAtoms(formula)).sort();
1266
+ if (isPurePropositional(formula) && atoms.length <= 26) {
1267
+ const { result, allOnes } = evaluateBitset(formula, atoms);
1268
+ const isValid = bvEquals(result, allOnes);
1269
+ return {
1270
+ status: isValid ? 'valid' : 'invalid',
1271
+ output: isValid
1272
+ ? `${formulaToString(formula)} es VALIDA (tautologia)`
1273
+ : `${formulaToString(formula)} NO es valida`,
1274
+ diagnostics: [],
1275
+ formula,
1276
+ };
1277
+ }
1057
1278
  const tt = this.truthTable(formula);
1058
1279
  if (tt.isTautology) {
1059
1280
  return {
@@ -1065,7 +1286,6 @@ class ClassicalPropositional {
1065
1286
  };
1066
1287
  }
1067
1288
  else {
1068
- // Encontrar contramodelo
1069
1289
  const cm = tt.rows.find((r) => !r.result);
1070
1290
  return {
1071
1291
  status: 'invalid',
@@ -1082,6 +1302,20 @@ class ClassicalPropositional {
1082
1302
  if (wf.length > 0) {
1083
1303
  return { status: 'error', diagnostics: wf, formula };
1084
1304
  }
1305
+ // Fast path: bitset satisfiability check (no row materialization)
1306
+ const atoms = Array.from(collectAtoms(formula)).sort();
1307
+ if (isPurePropositional(formula) && atoms.length <= 26) {
1308
+ const { result } = evaluateBitset(formula, atoms);
1309
+ const isSat = !bvIsZero(result);
1310
+ return {
1311
+ status: isSat ? 'satisfiable' : 'unsatisfiable',
1312
+ output: isSat
1313
+ ? `${formulaToString(formula)} es SATISFACIBLE`
1314
+ : `${formulaToString(formula)} es INSATISFACIBLE (contradiccion)`,
1315
+ diagnostics: [],
1316
+ formula,
1317
+ };
1318
+ }
1085
1319
  const tt = this.truthTable(formula);
1086
1320
  if (tt.isSatisfiable) {
1087
1321
  const sat = tt.rows.find((r) => r.result);
@@ -1171,11 +1405,39 @@ class ClassicalPropositional {
1171
1405
  if (wf.length > 0) {
1172
1406
  return { status: 'error', diagnostics: wf, formula };
1173
1407
  }
1174
- const atoms = Array.from(collectAtoms(formula));
1408
+ const atoms = Array.from(collectAtoms(formula)).sort();
1409
+ const n = atoms.length;
1410
+ // Fast path: bitset finds countermodel in one pass
1411
+ if (isPurePropositional(formula) && n <= 18) {
1412
+ const { result, allOnes } = evaluateBitset(formula, atoms);
1413
+ if (bvEquals(result, allOnes)) {
1414
+ return {
1415
+ status: 'valid',
1416
+ output: `${formulaToString(formula)} es tautologia, no hay contramodelo`,
1417
+ diagnostics: [],
1418
+ formula,
1419
+ };
1420
+ }
1421
+ // Find first 0 bit (first falsifying row)
1422
+ const inverted = bvNot(result, allOnes);
1423
+ const idx = bvFirstSet(inverted);
1424
+ const v = {};
1425
+ for (let j = 0; j < n; j++) {
1426
+ v[atoms[j]] = Boolean((idx >> (n - 1 - j)) & 1);
1427
+ }
1428
+ const valStr = atoms.map((a) => `${a}=${v[a] ? 'V' : 'F'}`).join(', ');
1429
+ return {
1430
+ status: 'invalid',
1431
+ output: `Contramodelo encontrado para ${formulaToString(formula)}\n ← ${valStr}`,
1432
+ model: { type: 'propositional', valuation: v },
1433
+ diagnostics: [],
1434
+ formula,
1435
+ };
1436
+ }
1437
+ // Fallback: classic evaluation
1175
1438
  const valuations = generateValuations(atoms);
1176
1439
  for (const v of valuations) {
1177
- if (!evaluate(formula, v)) {
1178
- // #25: mark the countermodel valuation with ←
1440
+ if (!evaluateClassical(formula, v)) {
1179
1441
  const valStr = atoms.map((a) => `${a}=${v[a] ? 'V' : 'F'}`).join(', ');
1180
1442
  return {
1181
1443
  status: 'invalid',
@@ -1296,17 +1558,54 @@ class ClassicalPropositional {
1296
1558
  }
1297
1559
  truthTable(formula) {
1298
1560
  const atoms = Array.from(collectAtoms(formula)).sort();
1299
- const valuations = generateValuations(atoms);
1561
+ const n = atoms.length;
1300
1562
  const subForms = getSubFormulas(formula);
1563
+ const subFormulasInfo = subForms.map((sf) => ({ formula: sf, label: formulaToString(sf) }));
1564
+ // Fast path: bitset evaluation for pure propositional formulas
1565
+ // Limit to n<=20 for row materialization (2^20 = ~1M rows)
1566
+ if (isPurePropositional(formula) && n <= 20) {
1567
+ const { result, atomMasks, total, allOnes } = evaluateBitset(formula, atoms);
1568
+ const satisfyingCount = bitsetPopcount(result);
1569
+ // Evaluate subformulas with bitsets too
1570
+ const subBitsets = subForms.map((sf) => isPurePropositional(sf) ? evaluateBitset(sf, atoms).result : bvCreate(total));
1571
+ // Materialize rows from bitset results
1572
+ const rows = new Array(total);
1573
+ for (let i = 0; i < total; i++) {
1574
+ const v = {};
1575
+ for (let j = 0; j < n; j++) {
1576
+ v[atoms[j]] = bvTestBit(atomMasks.get(atoms[j]), i);
1577
+ }
1578
+ rows[i] = { valuation: v, result: bvTestBit(result, i) };
1579
+ }
1580
+ const subFormulaValues = rows.map((_, i) => {
1581
+ const vals = {};
1582
+ subForms.forEach((sf, si) => {
1583
+ vals[formulaToString(sf)] = bvTestBit(subBitsets[si], i);
1584
+ });
1585
+ return vals;
1586
+ });
1587
+ return {
1588
+ variables: atoms,
1589
+ rows,
1590
+ isTautology: bvEquals(result, allOnes),
1591
+ isContradiction: bvIsZero(result),
1592
+ isSatisfiable: !bvIsZero(result),
1593
+ subFormulas: subFormulasInfo,
1594
+ subFormulaValues,
1595
+ satisfyingCount,
1596
+ totalCount: total,
1597
+ };
1598
+ }
1599
+ // Fallback: classic evaluation
1600
+ const valuations = generateValuations(atoms);
1301
1601
  const rows = valuations.map((v) => ({
1302
1602
  valuation: v,
1303
- result: evaluate(formula, v),
1603
+ result: evaluateClassical(formula, v),
1304
1604
  }));
1305
- const subFormulasInfo = subForms.map((sf) => ({ formula: sf, label: formulaToString(sf) }));
1306
1605
  const subFormulaValues = valuations.map((v) => {
1307
1606
  const vals = {};
1308
1607
  subForms.forEach((sf) => {
1309
- vals[formulaToString(sf)] = evaluate(sf, v);
1608
+ vals[formulaToString(sf)] = evaluateClassical(sf, v);
1310
1609
  });
1311
1610
  return vals;
1312
1611
  });
@@ -1317,7 +1616,7 @@ class ClassicalPropositional {
1317
1616
  isContradiction: rows.every((r) => !r.result),
1318
1617
  isSatisfiable: rows.some((r) => r.result),
1319
1618
  subFormulas: subFormulasInfo,
1320
- subFormulaValues: subFormulaValues,
1619
+ subFormulaValues,
1321
1620
  satisfyingCount: rows.filter((r) => r.result).length,
1322
1621
  totalCount: rows.length,
1323
1622
  };