@stevenvo780/st-lang 3.0.1 → 3.1.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.
@@ -704,7 +704,7 @@ function addDerivedFormula(state, formula, justification, premises) {
704
704
  state.known.set(hash, formula);
705
705
  return true;
706
706
  }
707
- function tryDerive(goal, theory, premiseNames) {
707
+ function tryDerive(goal, theory, premiseNames, depth = 0) {
708
708
  const state = {
709
709
  known: new Map(),
710
710
  steps: [],
@@ -781,9 +781,10 @@ function tryDerive(goal, theory, premiseNames) {
781
781
  ]) || changed;
782
782
  }
783
783
  // Conjunction Introduction: de A y B, derivar A & B
784
+ // Only produce conjunctions that are relevant to the goal to avoid O(n²) explosion
784
785
  if (f1 !== f2) {
785
786
  const conj = { kind: 'and', args: [f1, f2] };
786
- if (formulasEqual(conj, goal)) {
787
+ if (formulasEqual(conj, goal) || isRelevantToGoal(conj, goal)) {
787
788
  changed =
788
789
  addDerivedFormula(state, conj, 'Introduccion de conjuncion', [
789
790
  findStep(state.steps, f1),
@@ -909,6 +910,33 @@ function tryDerive(goal, theory, premiseNames) {
909
910
  }
910
911
  }
911
912
  }
913
+ // Dilema Constructivo (implicaciones separadas): P->Q, R->S, P|R ⊢ Q|S
914
+ // No requiere que las implicaciones estén en conjunción
915
+ if (f1.kind === 'implies' &&
916
+ f1.args?.[0] &&
917
+ f1.args?.[1] &&
918
+ f2.kind === 'implies' &&
919
+ f2.args?.[0] &&
920
+ f2.args?.[1] &&
921
+ !formulasEqual(f1, f2)) {
922
+ // Search for a disjunction P|R in known formulas
923
+ const p = f1.args[0];
924
+ const q = f1.args[1];
925
+ const r = f2.args[0];
926
+ const s = f2.args[1];
927
+ const disjHash = formulaHash({ kind: 'or', args: [p, r] });
928
+ const disjHashRev = formulaHash({ kind: 'or', args: [r, p] });
929
+ if (state.known.has(disjHash) || state.known.has(disjHashRev)) {
930
+ const qs = { kind: 'or', args: [q, s] };
931
+ const disjFormula = state.known.get(disjHash) || state.known.get(disjHashRev);
932
+ changed =
933
+ addDerivedFormula(state, qs, 'Dilema Constructivo', [
934
+ findStep(state.steps, f1),
935
+ findStep(state.steps, f2),
936
+ findStep(state.steps, disjFormula),
937
+ ]) || changed;
938
+ }
939
+ }
912
940
  // Resolución: P|Q, !P|R derivar Q|R
913
941
  if (f1.kind === 'or' &&
914
942
  f1.args?.[0] &&
@@ -947,7 +975,8 @@ function tryDerive(goal, theory, premiseNames) {
947
975
  ]) || changed;
948
976
  }
949
977
  }
950
- // Disjunction Introduction: de A, derivar A | B (si A|B es la meta)
978
+ // Disjunction Introduction: de A, derivar A | B
979
+ // Relaxed: also allow intermediate disjunctions that are relevant to goal
951
980
  if (goal.kind === 'or' && goal.args?.[0] && goal.args?.[1]) {
952
981
  if (formulasEqual(f1, goal.args[0]) || formulasEqual(f1, goal.args[1])) {
953
982
  changed =
@@ -956,6 +985,23 @@ function tryDerive(goal, theory, premiseNames) {
956
985
  ]) || changed;
957
986
  }
958
987
  }
988
+ // Also check if f1 can form a disjunction relevant to some intermediate goal
989
+ if (goal.kind !== 'or') {
990
+ // If the goal is e.g. (A|B) -> C, and we have A, generate A|B as intermediate
991
+ const checkDisjGoals = (g) => {
992
+ if (g.kind === 'or' && g.args?.[0] && g.args?.[1]) {
993
+ if (formulasEqual(f1, g.args[0]) || formulasEqual(f1, g.args[1])) {
994
+ const disj = { kind: 'or', args: [g.args[0], g.args[1]] };
995
+ changed =
996
+ addDerivedFormula(state, disj, 'Introduccion de disyuncion', [
997
+ findStep(state.steps, f1),
998
+ ]) || changed;
999
+ }
1000
+ }
1001
+ g.args?.forEach(checkDisjGoals);
1002
+ };
1003
+ checkDisjGoals(goal);
1004
+ }
959
1005
  // Double Negation Elimination: de !!A, derivar A
960
1006
  if (f1.kind === 'not' && f1.args?.[0]?.kind === 'not' && f1.args[0].args?.[0]) {
961
1007
  const inner = f1.args[0].args[0];
@@ -970,13 +1016,14 @@ function tryDerive(goal, theory, premiseNames) {
970
1016
  findStep(state.steps, f1),
971
1017
  ]) || changed;
972
1018
  }
973
- // Introducción de implicación simple: si la meta es A -> B y ya conocemos B, se permite cerrar
1019
+ // Weakening (Debilitamiento): si la meta es A -> B y ya conocemos B,
1020
+ // entonces A -> B es válida (B ⊢ A -> B en lógica clásica).
974
1021
  if (goal.kind === 'implies' &&
975
1022
  goal.args?.[0] &&
976
1023
  goal.args?.[1] &&
977
1024
  formulasEqual(goal.args[1], f1)) {
978
1025
  changed =
979
- addDerivedFormula(state, goal, 'Introduccion de implicacion', [
1026
+ addDerivedFormula(state, goal, 'Debilitamiento (B ⊢ A → B)', [
980
1027
  findStep(state.steps, f1),
981
1028
  ]) || changed;
982
1029
  }
@@ -1081,6 +1128,82 @@ function tryDerive(goal, theory, premiseNames) {
1081
1128
  changed =
1082
1129
  addDerivedFormula(state, dm2, 'De Morgan (OR)', [findStep(state.steps, f1)]) || changed;
1083
1130
  }
1131
+ // Distribución 1: P & (Q | R) ⊢ (P & Q) | (P & R)
1132
+ if (f1.kind === 'and' &&
1133
+ f1.args?.[0] &&
1134
+ f1.args?.[1]?.kind === 'or' &&
1135
+ f1.args[1].args?.[0] &&
1136
+ f1.args[1].args?.[1]) {
1137
+ const dist = {
1138
+ kind: 'or',
1139
+ args: [
1140
+ { kind: 'and', args: [f1.args[0], f1.args[1].args[0]] },
1141
+ { kind: 'and', args: [f1.args[0], f1.args[1].args[1]] },
1142
+ ],
1143
+ };
1144
+ changed =
1145
+ addDerivedFormula(state, dist, 'Distribucion (AND sobre OR)', [
1146
+ findStep(state.steps, f1),
1147
+ ]) || changed;
1148
+ }
1149
+ // Distribución 1b: (Q | R) & P ⊢ (Q & P) | (R & P)
1150
+ if (f1.kind === 'and' &&
1151
+ f1.args?.[0]?.kind === 'or' &&
1152
+ f1.args[0].args?.[0] &&
1153
+ f1.args[0].args?.[1] &&
1154
+ f1.args?.[1]) {
1155
+ const dist = {
1156
+ kind: 'or',
1157
+ args: [
1158
+ { kind: 'and', args: [f1.args[0].args[0], f1.args[1]] },
1159
+ { kind: 'and', args: [f1.args[0].args[1], f1.args[1]] },
1160
+ ],
1161
+ };
1162
+ changed =
1163
+ addDerivedFormula(state, dist, 'Distribucion (AND sobre OR)', [
1164
+ findStep(state.steps, f1),
1165
+ ]) || changed;
1166
+ }
1167
+ // Distribución 2: P | (Q & R) ⊢ (P | Q) & (P | R)
1168
+ if (f1.kind === 'or' &&
1169
+ f1.args?.[0] &&
1170
+ f1.args?.[1]?.kind === 'and' &&
1171
+ f1.args[1].args?.[0] &&
1172
+ f1.args[1].args?.[1]) {
1173
+ const dist = {
1174
+ kind: 'and',
1175
+ args: [
1176
+ { kind: 'or', args: [f1.args[0], f1.args[1].args[0]] },
1177
+ { kind: 'or', args: [f1.args[0], f1.args[1].args[1]] },
1178
+ ],
1179
+ };
1180
+ if (isRelevantToGoal(dist, goal)) {
1181
+ changed =
1182
+ addDerivedFormula(state, dist, 'Distribucion (OR sobre AND)', [
1183
+ findStep(state.steps, f1),
1184
+ ]) || changed;
1185
+ }
1186
+ }
1187
+ // Distribución 2b: (Q & R) | P ⊢ (Q | P) & (R | P)
1188
+ if (f1.kind === 'or' &&
1189
+ f1.args?.[0]?.kind === 'and' &&
1190
+ f1.args[0].args?.[0] &&
1191
+ f1.args[0].args?.[1] &&
1192
+ f1.args?.[1]) {
1193
+ const dist = {
1194
+ kind: 'and',
1195
+ args: [
1196
+ { kind: 'or', args: [f1.args[0].args[0], f1.args[1]] },
1197
+ { kind: 'or', args: [f1.args[0].args[1], f1.args[1]] },
1198
+ ],
1199
+ };
1200
+ if (isRelevantToGoal(dist, goal)) {
1201
+ changed =
1202
+ addDerivedFormula(state, dist, 'Distribucion (OR sobre AND)', [
1203
+ findStep(state.steps, f1),
1204
+ ]) || changed;
1205
+ }
1206
+ }
1084
1207
  // RAA (Reductio ad Absurdum) #29:
1085
1208
  // Si tenemos P→Q y P→¬Q (o ¬Q→P y Q→P), derivar ¬P
1086
1209
  if (f1.kind === 'implies' && f1.args?.[0] && f1.args?.[1]) {
@@ -1135,6 +1258,226 @@ function tryDerive(goal, theory, premiseNames) {
1135
1258
  derivedFrom: premiseNames,
1136
1259
  };
1137
1260
  }
1261
+ // --- Sub-derivaciones recursivas (antes del fallback semántico) ---
1262
+ const MAX_SUB_DEPTH = 2;
1263
+ // Prueba Condicional real (→-Introducción / Deduction Theorem):
1264
+ // Para derivar A→B, asumimos A como premisa temporal y derivamos B.
1265
+ if (depth < MAX_SUB_DEPTH &&
1266
+ goal.kind === 'implies' &&
1267
+ goal.args?.[0] &&
1268
+ goal.args?.[1]) {
1269
+ const assumption = goal.args[0];
1270
+ const subGoal = goal.args[1];
1271
+ // Create a temporary theory with the assumption added
1272
+ const tempTheory = {
1273
+ profile: theory.profile,
1274
+ axioms: new Map(theory.axioms),
1275
+ theorems: new Map(theory.theorems),
1276
+ claims: theory.claims,
1277
+ judgments: theory.judgments,
1278
+ };
1279
+ const assumptionName = `__assumption_${depth}_${formulaHash(assumption)}`;
1280
+ tempTheory.axioms.set(assumptionName, assumption);
1281
+ const subPremises = [...premiseNames, assumptionName];
1282
+ const subProof = tryDerive(subGoal, tempTheory, subPremises, depth + 1);
1283
+ if (subProof && subProof.status === 'complete') {
1284
+ // Check the sub-proof doesn't rely solely on semantic fallback
1285
+ const isSyntactic = subProof.steps.every((s) => !s.justification.startsWith('Verificacion semantica'));
1286
+ if (isSyntactic) {
1287
+ // Build the main proof: premises + sub-derivation steps + conditional proof conclusion
1288
+ const mainSteps = [];
1289
+ let stepNum = 0;
1290
+ // Copy premise steps from current state
1291
+ for (const s of state.steps) {
1292
+ if (s.justification.startsWith('Premisa')) {
1293
+ stepNum++;
1294
+ mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
1295
+ }
1296
+ }
1297
+ // Add assumption step
1298
+ stepNum++;
1299
+ const assumptionStepNum = stepNum;
1300
+ mainSteps.push({
1301
+ stepNumber: stepNum,
1302
+ formula: assumption,
1303
+ justification: 'Supuesto (para prueba condicional)',
1304
+ premises: [],
1305
+ });
1306
+ // Add sub-derivation steps (renumber, adjusting premise references)
1307
+ const subStepOffset = stepNum;
1308
+ const subStepMap = new Map();
1309
+ for (const s of subProof.steps) {
1310
+ if (s.justification.startsWith('Premisa') && formulasEqual(s.formula, assumption)) {
1311
+ subStepMap.set(s.stepNumber, assumptionStepNum);
1312
+ continue;
1313
+ }
1314
+ if (s.justification.startsWith('Premisa')) {
1315
+ // Find existing premise step in main
1316
+ const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
1317
+ if (existing) {
1318
+ subStepMap.set(s.stepNumber, existing.stepNumber);
1319
+ continue;
1320
+ }
1321
+ }
1322
+ stepNum++;
1323
+ subStepMap.set(s.stepNumber, stepNum);
1324
+ mainSteps.push({
1325
+ stepNumber: stepNum,
1326
+ formula: s.formula,
1327
+ justification: s.justification,
1328
+ premises: s.premises.map((p) => subStepMap.get(p) || p),
1329
+ });
1330
+ }
1331
+ // Add final conditional proof step
1332
+ stepNum++;
1333
+ const subGoalStepNum = subStepMap.get(subProof.steps[subProof.steps.length - 1]?.stepNumber ?? 0) ?? (stepNum - 1);
1334
+ mainSteps.push({
1335
+ stepNumber: stepNum,
1336
+ formula: goal,
1337
+ justification: 'Prueba Condicional (Teorema de Deduccion)',
1338
+ premises: [assumptionStepNum, subGoalStepNum],
1339
+ });
1340
+ return {
1341
+ goal,
1342
+ steps: mainSteps,
1343
+ status: 'complete',
1344
+ derivedFrom: premiseNames,
1345
+ };
1346
+ }
1347
+ }
1348
+ }
1349
+ // Prueba por Casos (∨-Eliminación / Disjunction Elimination):
1350
+ // Si tenemos A|B y queremos derivar C, asumimos A→C y B→C por separado.
1351
+ if (depth < MAX_SUB_DEPTH) {
1352
+ const disjunctions = Array.from(state.known.values()).filter((f) => f.kind === 'or' && f.args?.[0] && f.args?.[1]);
1353
+ for (const disj of disjunctions) {
1354
+ const left = disj.args[0];
1355
+ const right = disj.args[1];
1356
+ // Try to derive goal assuming left
1357
+ const tempTheoryL = {
1358
+ profile: theory.profile,
1359
+ axioms: new Map(theory.axioms),
1360
+ theorems: new Map(theory.theorems),
1361
+ claims: theory.claims,
1362
+ judgments: theory.judgments,
1363
+ };
1364
+ const leftName = `__case_left_${depth}_${formulaHash(left)}`;
1365
+ tempTheoryL.axioms.set(leftName, left);
1366
+ const subPremisesL = [...premiseNames, leftName];
1367
+ const subProofL = tryDerive(goal, tempTheoryL, subPremisesL, depth + 1);
1368
+ if (!subProofL || subProofL.status !== 'complete')
1369
+ continue;
1370
+ const isSyntacticL = subProofL.steps.every((s) => !s.justification.startsWith('Verificacion semantica'));
1371
+ if (!isSyntacticL)
1372
+ continue;
1373
+ // Try to derive goal assuming right
1374
+ const tempTheoryR = {
1375
+ profile: theory.profile,
1376
+ axioms: new Map(theory.axioms),
1377
+ theorems: new Map(theory.theorems),
1378
+ claims: theory.claims,
1379
+ judgments: theory.judgments,
1380
+ };
1381
+ const rightName = `__case_right_${depth}_${formulaHash(right)}`;
1382
+ tempTheoryR.axioms.set(rightName, right);
1383
+ const subPremisesR = [...premiseNames, rightName];
1384
+ const subProofR = tryDerive(goal, tempTheoryR, subPremisesR, depth + 1);
1385
+ if (!subProofR || subProofR.status !== 'complete')
1386
+ continue;
1387
+ const isSyntacticR = subProofR.steps.every((s) => !s.justification.startsWith('Verificacion semantica'));
1388
+ if (!isSyntacticR)
1389
+ continue;
1390
+ // Both cases succeed — build proof by cases
1391
+ const mainSteps = [];
1392
+ let stepNum = 0;
1393
+ // Copy premise steps
1394
+ for (const s of state.steps) {
1395
+ if (s.justification.startsWith('Premisa')) {
1396
+ stepNum++;
1397
+ mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
1398
+ }
1399
+ }
1400
+ const disjStepNum = mainSteps.find((ms) => formulasEqual(ms.formula, disj))?.stepNumber ?? 0;
1401
+ // Left case sub-derivation
1402
+ stepNum++;
1403
+ const leftAssumptionStep = stepNum;
1404
+ mainSteps.push({
1405
+ stepNumber: stepNum,
1406
+ formula: left,
1407
+ justification: 'Supuesto (caso izquierdo)',
1408
+ premises: [],
1409
+ });
1410
+ const leftStepMap = new Map();
1411
+ for (const s of subProofL.steps) {
1412
+ if (s.justification.startsWith('Premisa') && formulasEqual(s.formula, left)) {
1413
+ leftStepMap.set(s.stepNumber, leftAssumptionStep);
1414
+ continue;
1415
+ }
1416
+ if (s.justification.startsWith('Premisa')) {
1417
+ const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
1418
+ if (existing) {
1419
+ leftStepMap.set(s.stepNumber, existing.stepNumber);
1420
+ continue;
1421
+ }
1422
+ }
1423
+ stepNum++;
1424
+ leftStepMap.set(s.stepNumber, stepNum);
1425
+ mainSteps.push({
1426
+ stepNumber: stepNum,
1427
+ formula: s.formula,
1428
+ justification: s.justification,
1429
+ premises: s.premises.map((p) => leftStepMap.get(p) || p),
1430
+ });
1431
+ }
1432
+ const leftGoalStep = leftStepMap.get(subProofL.steps[subProofL.steps.length - 1]?.stepNumber ?? 0) ?? stepNum;
1433
+ // Right case sub-derivation
1434
+ stepNum++;
1435
+ const rightAssumptionStep = stepNum;
1436
+ mainSteps.push({
1437
+ stepNumber: stepNum,
1438
+ formula: right,
1439
+ justification: 'Supuesto (caso derecho)',
1440
+ premises: [],
1441
+ });
1442
+ const rightStepMap = new Map();
1443
+ for (const s of subProofR.steps) {
1444
+ if (s.justification.startsWith('Premisa') && formulasEqual(s.formula, right)) {
1445
+ rightStepMap.set(s.stepNumber, rightAssumptionStep);
1446
+ continue;
1447
+ }
1448
+ if (s.justification.startsWith('Premisa')) {
1449
+ const existing = mainSteps.find((ms) => ms.justification.startsWith('Premisa') && formulasEqual(ms.formula, s.formula));
1450
+ if (existing) {
1451
+ rightStepMap.set(s.stepNumber, existing.stepNumber);
1452
+ continue;
1453
+ }
1454
+ }
1455
+ stepNum++;
1456
+ rightStepMap.set(s.stepNumber, stepNum);
1457
+ mainSteps.push({
1458
+ stepNumber: stepNum,
1459
+ formula: s.formula,
1460
+ justification: s.justification,
1461
+ premises: s.premises.map((p) => rightStepMap.get(p) || p),
1462
+ });
1463
+ }
1464
+ const rightGoalStep = rightStepMap.get(subProofR.steps[subProofR.steps.length - 1]?.stepNumber ?? 0) ?? stepNum;
1465
+ // Final disjunction elimination step
1466
+ stepNum++;
1467
+ mainSteps.push({
1468
+ stepNumber: stepNum,
1469
+ formula: goal,
1470
+ justification: 'Eliminacion de disyuncion (prueba por casos)',
1471
+ premises: [disjStepNum, leftGoalStep, rightGoalStep],
1472
+ });
1473
+ return {
1474
+ goal,
1475
+ steps: mainSteps,
1476
+ status: 'complete',
1477
+ derivedFrom: premiseNames,
1478
+ };
1479
+ }
1480
+ }
1138
1481
  // Fallback: verificar semánticamente
1139
1482
  const allAxiomFormulas = premiseNames
1140
1483
  .map((n) => theory.axioms.get(n) || theory.theorems.get(n))
@@ -1145,6 +1488,7 @@ function tryDerive(goal, theory, premiseNames) {
1145
1488
  collectAtoms(f).forEach((a) => atoms.add(a));
1146
1489
  collectAtoms(goal).forEach((a) => atoms.add(a));
1147
1490
  const atomList = Array.from(atoms).sort();
1491
+ let semanticResult;
1148
1492
  // Fast path: bitset semantic check
1149
1493
  const allPure = allAxiomFormulas.every(isPurePropositional) && isPurePropositional(goal);
1150
1494
  if (allPure && atomList.length <= 26) {
@@ -1157,19 +1501,10 @@ function tryDerive(goal, theory, premiseNames) {
1157
1501
  premisesConj = bvAnd(premisesConj, pb);
1158
1502
  // Valid if: wherever premises are true, goal is also true
1159
1503
  // i.e., premisesConj & ~goalBits === 0
1160
- if (bvIsZero(bvAnd(premisesConj, bvNot(goalBits, allOnes)))) {
1161
- return {
1162
- goal,
1163
- steps: state.steps,
1164
- status: 'complete',
1165
- derivedFrom: premiseNames,
1166
- };
1167
- }
1504
+ semanticResult = bvIsZero(bvAnd(premisesConj, bvNot(goalBits, allOnes)));
1168
1505
  }
1169
1506
  else if (allPure && atomList.length > 26) {
1170
1507
  // DPLL fallback for >26 atoms
1171
- // Build: (premise1 & premise2 & ... & premiseN) -> goal
1172
- // Valid iff NOT satisfiable: (premises & !goal)
1173
1508
  let conjunction = allAxiomFormulas[0];
1174
1509
  for (let i = 1; i < allAxiomFormulas.length; i++) {
1175
1510
  conjunction = { kind: 'and', args: [conjunction, allAxiomFormulas[i]] };
@@ -1177,34 +1512,47 @@ function tryDerive(goal, theory, premiseNames) {
1177
1512
  const negGoal = { kind: 'not', args: [goal] };
1178
1513
  const check = { kind: 'and', args: [conjunction, negGoal] };
1179
1514
  const result = (0, dpll_1.dpll)(check);
1180
- if (!result.satisfiable) {
1181
- return {
1182
- goal,
1183
- steps: state.steps,
1184
- status: 'complete',
1185
- derivedFrom: premiseNames,
1186
- };
1187
- }
1515
+ semanticResult = !result.satisfiable;
1188
1516
  }
1189
1517
  else {
1190
1518
  // Classic fallback
1191
1519
  const valuations = generateValuations(atomList);
1192
- let semanticallyValid = true;
1520
+ semanticResult = true;
1193
1521
  for (const v of valuations) {
1194
1522
  const premisesTrue = allAxiomFormulas.every((f) => evaluateClassical(f, v));
1195
1523
  if (premisesTrue && !evaluateClassical(goal, v)) {
1196
- semanticallyValid = false;
1524
+ semanticResult = false;
1197
1525
  break;
1198
1526
  }
1199
1527
  }
1200
- if (semanticallyValid) {
1201
- return {
1202
- goal,
1203
- steps: state.steps,
1204
- status: 'complete',
1205
- derivedFrom: premiseNames,
1206
- };
1528
+ }
1529
+ if (semanticResult) {
1530
+ // Generate a synthetic final proof step so the user sees a complete derivation
1531
+ // instead of just "derivado exitosamente" with no proof trace.
1532
+ const goalHash = formulaHash(goal);
1533
+ if (!state.known.has(goalHash)) {
1534
+ const premiseStepNums = premiseNames
1535
+ .map((n) => {
1536
+ const f = theory.axioms.get(n) || theory.theorems.get(n);
1537
+ return f ? findStep(state.steps, f) : 0;
1538
+ })
1539
+ .filter((n) => n > 0);
1540
+ state.stepCount++;
1541
+ state.steps.push({
1542
+ stepNumber: state.stepCount,
1543
+ formula: goal,
1544
+ justification: 'Verificacion semantica (todas las valuaciones satisfacen la consecuencia)',
1545
+ premises: premiseStepNums,
1546
+ });
1547
+ state.known.set(goalHash, goal);
1207
1548
  }
1549
+ const relevantSteps = traceBack(state.steps, goal);
1550
+ return {
1551
+ goal,
1552
+ steps: relevantSteps,
1553
+ status: 'complete',
1554
+ derivedFrom: premiseNames,
1555
+ };
1208
1556
  }
1209
1557
  }
1210
1558
  return null;