@stevenvo780/st-lang 3.1.2 → 3.2.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 (66) hide show
  1. package/dist/cli/index.js +6 -11
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/parser/parser.d.ts.map +1 -1
  4. package/dist/parser/parser.js +29 -6
  5. package/dist/parser/parser.js.map +1 -1
  6. package/dist/profiles/classical/cdcl.js +1 -1
  7. package/dist/profiles/classical/cdcl.js.map +1 -1
  8. package/dist/profiles/classical/first-order.d.ts +0 -1
  9. package/dist/profiles/classical/first-order.d.ts.map +1 -1
  10. package/dist/profiles/classical/first-order.js +182 -81
  11. package/dist/profiles/classical/first-order.js.map +1 -1
  12. package/dist/profiles/classical/propositional.d.ts.map +1 -1
  13. package/dist/profiles/classical/propositional.js +353 -27
  14. package/dist/profiles/classical/propositional.js.map +1 -1
  15. package/dist/profiles/intuitionistic/propositional.d.ts.map +1 -1
  16. package/dist/profiles/intuitionistic/propositional.js +21 -2
  17. package/dist/profiles/intuitionistic/propositional.js.map +1 -1
  18. package/dist/profiles/paraconsistent/belnap.d.ts.map +1 -1
  19. package/dist/profiles/paraconsistent/belnap.js +1 -0
  20. package/dist/profiles/paraconsistent/belnap.js.map +1 -1
  21. package/dist/profiles/probabilistic/basic.d.ts.map +1 -1
  22. package/dist/profiles/probabilistic/basic.js +1 -0
  23. package/dist/profiles/probabilistic/basic.js.map +1 -1
  24. package/dist/profiles/shared/base-profile.d.ts.map +1 -1
  25. package/dist/profiles/shared/base-profile.js +1 -0
  26. package/dist/profiles/shared/base-profile.js.map +1 -1
  27. package/dist/profiles/shared/tableau-engine.d.ts +6 -0
  28. package/dist/profiles/shared/tableau-engine.d.ts.map +1 -1
  29. package/dist/profiles/shared/tableau-engine.js +33 -6
  30. package/dist/profiles/shared/tableau-engine.js.map +1 -1
  31. package/dist/profiles/temporal/ltl.d.ts +2 -1
  32. package/dist/profiles/temporal/ltl.d.ts.map +1 -1
  33. package/dist/profiles/temporal/ltl.js +5 -1
  34. package/dist/profiles/temporal/ltl.js.map +1 -1
  35. package/dist/protocol/handler.d.ts +0 -2
  36. package/dist/protocol/handler.d.ts.map +1 -1
  37. package/dist/protocol/handler.js +2 -6
  38. package/dist/protocol/handler.js.map +1 -1
  39. package/dist/runtime/fallacies.js +18 -18
  40. package/dist/runtime/fallacies.js.map +1 -1
  41. package/dist/runtime/interpreter.d.ts +8 -0
  42. package/dist/runtime/interpreter.d.ts.map +1 -1
  43. package/dist/runtime/interpreter.js +188 -49
  44. package/dist/runtime/interpreter.js.map +1 -1
  45. package/dist/runtime/known-theorems.d.ts.map +1 -1
  46. package/dist/runtime/known-theorems.js +17 -5
  47. package/dist/runtime/known-theorems.js.map +1 -1
  48. package/dist/tests/core.test.js +133 -1
  49. package/dist/tests/core.test.js.map +1 -1
  50. package/dist/tests/parser.test.js +10 -0
  51. package/dist/tests/parser.test.js.map +1 -1
  52. package/dist/tests/profiles.test.js +16 -0
  53. package/dist/tests/profiles.test.js.map +1 -1
  54. package/dist/tests/propositional-nd-audit.test.d.ts +2 -0
  55. package/dist/tests/propositional-nd-audit.test.d.ts.map +1 -0
  56. package/dist/tests/propositional-nd-audit.test.js +116 -0
  57. package/dist/tests/propositional-nd-audit.test.js.map +1 -0
  58. package/dist/tests/protocol-text-layer.test.js +18 -0
  59. package/dist/tests/protocol-text-layer.test.js.map +1 -1
  60. package/dist/tests/regressions.test.d.ts +2 -0
  61. package/dist/tests/regressions.test.d.ts.map +1 -0
  62. package/dist/tests/regressions.test.js +167 -0
  63. package/dist/tests/regressions.test.js.map +1 -0
  64. package/dist/types/index.d.ts +16 -0
  65. package/dist/types/index.d.ts.map +1 -1
  66. package/package.json +1 -1
@@ -110,11 +110,14 @@ function* generateValuationsLazy(atoms) {
110
110
  yield {};
111
111
  return;
112
112
  }
113
- const total = 1 << n;
113
+ if (n > 23)
114
+ throw new Error('Demasiadas variables para tabla de verdad (>23)');
115
+ const total = 2 ** n;
114
116
  for (let i = 0; i < total; i++) {
115
117
  const v = {};
116
118
  for (let j = 0; j < n; j++) {
117
- v[atoms[j]] = Boolean((i >> (n - 1 - j)) & 1);
119
+ const divisor = 2 ** (n - 1 - j);
120
+ v[atoms[j]] = Math.floor(i / divisor) % 2 === 1;
118
121
  }
119
122
  yield v;
120
123
  }
@@ -793,8 +796,18 @@ function isRelevantToGoal(f, goal) {
793
796
  }
794
797
  function addDerivedFormula(state, formula, justification, premises, source = 'rule') {
795
798
  const hash = formulaHash(formula);
796
- if (state.known.has(hash))
799
+ if (state.known.has(hash)) {
800
+ const variants = state.alternativeDerivations.get(hash) ?? [];
801
+ const isDuplicate = variants.some((variant) => variant.justification === justification &&
802
+ variant.source === source &&
803
+ variant.premises.length === premises.length &&
804
+ variant.premises.every((premise, index) => premise === premises[index]));
805
+ if (!isDuplicate) {
806
+ variants.push({ justification, premises: [...premises], source });
807
+ state.alternativeDerivations.set(hash, variants);
808
+ }
797
809
  return false;
810
+ }
798
811
  state.stepCount++;
799
812
  state.steps.push({
800
813
  stepNumber: state.stepCount,
@@ -804,6 +817,8 @@ function addDerivedFormula(state, formula, justification, premises, source = 'ru
804
817
  source,
805
818
  });
806
819
  state.known.set(hash, formula);
820
+ state.formulas.push(formula);
821
+ state.stepByHash.set(hash, state.stepCount);
807
822
  return true;
808
823
  }
809
824
  function buildPremiseRefs(theory, premiseNames) {
@@ -812,7 +827,12 @@ function buildPremiseRefs(theory, premiseNames) {
812
827
  location: (theory.axioms.get(name) || theory.theorems.get(name))?.source,
813
828
  }));
814
829
  }
815
- function buildProof(goal, steps, premiseNames, theory, method = 'natural_deduction', subproofs) {
830
+ function buildProof(goal, steps, premiseNames, theory, method = 'natural_deduction', subproofs, metadataExtras = {}) {
831
+ const metadata = {
832
+ createdAt: new Date().toISOString(),
833
+ profile: theory.profile,
834
+ ...metadataExtras,
835
+ };
816
836
  return {
817
837
  goal,
818
838
  steps,
@@ -820,11 +840,56 @@ function buildProof(goal, steps, premiseNames, theory, method = 'natural_deducti
820
840
  derivedFrom: premiseNames,
821
841
  premiseRefs: buildPremiseRefs(theory, premiseNames),
822
842
  method,
823
- subproofs,
824
- metadata: {
825
- createdAt: new Date().toISOString(),
826
- profile: theory.profile,
827
- },
843
+ subproofs: subproofs ? [...subproofs] : undefined,
844
+ metadata,
845
+ };
846
+ }
847
+ function buildDerivationMetadata(state, retainedSteps, semanticFallback = false) {
848
+ const sampledAlternatives = Array.from(state.alternativeDerivations.entries())
849
+ .filter(([, variants]) => variants.length > 0)
850
+ .sort((left, right) => right[1].length - left[1].length)
851
+ .slice(0, 12)
852
+ .map(([hash, variants]) => ({
853
+ formula: hash,
854
+ primaryStep: state.stepByHash.get(hash) ?? 0,
855
+ variants: variants.slice(0, 6).map((variant) => ({
856
+ justification: variant.justification,
857
+ premises: [...variant.premises],
858
+ source: variant.source,
859
+ })),
860
+ }));
861
+ const alternativeDerivationCount = Array.from(state.alternativeDerivations.values()).reduce((count, variants) => count + variants.length, 0);
862
+ return {
863
+ exploredStepCount: state.steps.length,
864
+ retainedStepCount: retainedSteps.length,
865
+ uniqueFormulaCount: state.formulas.length,
866
+ alternativeDerivationCount,
867
+ alternativeDerivationSamples: sampledAlternatives,
868
+ semanticFallback,
869
+ };
870
+ }
871
+ function buildCompositeDerivationMetadata(state, retainedSteps, subProofs = [], semanticFallback = false) {
872
+ const baseMetadata = buildDerivationMetadata(state, retainedSteps, semanticFallback);
873
+ const baseExploredStepCount = Number(baseMetadata.exploredStepCount ?? 0);
874
+ const baseAlternativeDerivationCount = Number(baseMetadata.alternativeDerivationCount ?? 0);
875
+ const subExploredStepCount = subProofs.reduce((count, subProof) => {
876
+ const subMetadata = subProof.metadata ?? {};
877
+ const explored = Number(subMetadata.exploredStepCount ?? subProof.steps.length);
878
+ return count + explored;
879
+ }, 0);
880
+ const subAlternativeDerivationCount = subProofs.reduce((count, subProof) => {
881
+ const subMetadata = subProof.metadata ?? {};
882
+ const alternatives = Number(subMetadata.alternativeDerivationCount ?? 0);
883
+ return count + alternatives;
884
+ }, 0);
885
+ const exploredStepCount = baseExploredStepCount + subExploredStepCount;
886
+ const alternativeDerivationCount = baseAlternativeDerivationCount + subAlternativeDerivationCount;
887
+ return {
888
+ ...baseMetadata,
889
+ exploredStepCount,
890
+ retainedStepCount: retainedSteps.length,
891
+ alternativeDerivationCount,
892
+ alternativeDerivationSamples: [],
828
893
  };
829
894
  }
830
895
  function isNegationOf(a, b) {
@@ -835,6 +900,116 @@ function isExcludedMiddleFormula(formula) {
835
900
  return false;
836
901
  return (isNegationOf(formula.args[0], formula.args[1]) || isNegationOf(formula.args[1], formula.args[0]));
837
902
  }
903
+ function buildSingleAssumptionProof(premiseSteps, assumption, assumptionJustification, subProof, goal, finalJustification) {
904
+ const mainSteps = [];
905
+ let stepNum = 0;
906
+ for (const s of premiseSteps) {
907
+ if (s.source === 'premise') {
908
+ stepNum++;
909
+ mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
910
+ }
911
+ }
912
+ stepNum++;
913
+ const assumptionStepNum = stepNum;
914
+ mainSteps.push({
915
+ stepNumber: stepNum,
916
+ formula: assumption,
917
+ justification: assumptionJustification,
918
+ premises: [],
919
+ source: 'assumption',
920
+ });
921
+ const subStepMap = new Map();
922
+ for (const s of subProof.steps) {
923
+ if (s.source === 'premise' && formulasEqual(s.formula, assumption)) {
924
+ subStepMap.set(s.stepNumber, assumptionStepNum);
925
+ continue;
926
+ }
927
+ if (s.source === 'premise') {
928
+ const existing = mainSteps.find((ms) => ms.source === 'premise' && formulasEqual(ms.formula, s.formula));
929
+ if (existing) {
930
+ subStepMap.set(s.stepNumber, existing.stepNumber);
931
+ continue;
932
+ }
933
+ }
934
+ stepNum++;
935
+ subStepMap.set(s.stepNumber, stepNum);
936
+ mainSteps.push({
937
+ stepNumber: stepNum,
938
+ formula: s.formula,
939
+ justification: s.justification,
940
+ premises: s.premises.map((p) => subStepMap.get(p) || p),
941
+ source: s.source,
942
+ });
943
+ }
944
+ stepNum++;
945
+ const subGoalStepNum = subStepMap.get(subProof.steps[subProof.steps.length - 1]?.stepNumber ?? 0) ?? stepNum - 1;
946
+ mainSteps.push({
947
+ stepNumber: stepNum,
948
+ formula: goal,
949
+ justification: finalJustification,
950
+ premises: [assumptionStepNum, subGoalStepNum],
951
+ subproofs: [subProof],
952
+ source: 'rule',
953
+ });
954
+ return mainSteps;
955
+ }
956
+ function buildMergedGoalProof(premiseSteps, subProofs, goal, finalJustification) {
957
+ const mainSteps = [];
958
+ let stepNum = 0;
959
+ const baseStepByHash = new Map();
960
+ for (const s of premiseSteps) {
961
+ if (s.source === 'premise') {
962
+ stepNum++;
963
+ mainSteps.push({ ...s, stepNumber: stepNum, premises: [] });
964
+ baseStepByHash.set(formulaHash(s.formula), stepNum);
965
+ }
966
+ }
967
+ const finalPremises = [];
968
+ for (const subProof of subProofs) {
969
+ const stepMap = new Map();
970
+ for (const s of subProof.steps) {
971
+ const hash = formulaHash(s.formula);
972
+ if (s.source === 'premise' && baseStepByHash.has(hash)) {
973
+ stepMap.set(s.stepNumber, baseStepByHash.get(hash) ?? 0);
974
+ continue;
975
+ }
976
+ if (s.source !== 'assumption' && baseStepByHash.has(hash)) {
977
+ stepMap.set(s.stepNumber, baseStepByHash.get(hash) ?? 0);
978
+ continue;
979
+ }
980
+ stepNum++;
981
+ stepMap.set(s.stepNumber, stepNum);
982
+ mainSteps.push({
983
+ stepNumber: stepNum,
984
+ formula: s.formula,
985
+ justification: s.justification,
986
+ premises: s.premises.map((p) => stepMap.get(p) || p),
987
+ subproofs: s.subproofs ? [...s.subproofs] : undefined,
988
+ source: s.source,
989
+ });
990
+ if (s.source !== 'assumption') {
991
+ baseStepByHash.set(hash, stepNum);
992
+ }
993
+ }
994
+ const mappedFinal = stepMap.get(subProof.steps[subProof.steps.length - 1]?.stepNumber ?? 0);
995
+ if (mappedFinal)
996
+ finalPremises.push(mappedFinal);
997
+ }
998
+ stepNum++;
999
+ mainSteps.push({
1000
+ stepNumber: stepNum,
1001
+ formula: goal,
1002
+ justification: finalJustification,
1003
+ premises: finalPremises,
1004
+ subproofs: subProofs,
1005
+ source: 'rule',
1006
+ });
1007
+ return mainSteps;
1008
+ }
1009
+ function buildKnownDerivationProof(state, formula, premiseNames, theory) {
1010
+ const relevantSteps = traceBack(state.steps, formula);
1011
+ return buildProof(formula, relevantSteps, premiseNames, theory, 'natural_deduction', undefined, buildDerivationMetadata(state, relevantSteps));
1012
+ }
838
1013
  function getCommutativeVariant(formula) {
839
1014
  if ((formula.kind === 'and' || formula.kind === 'or') && formula.args?.[0] && formula.args?.[1]) {
840
1015
  return { kind: formula.kind, args: [formula.args[1], formula.args[0]] };
@@ -880,8 +1055,11 @@ function getAbsorptionResult(formula) {
880
1055
  function tryDerive(goal, theory, premiseNames, depth = 0) {
881
1056
  const state = {
882
1057
  known: new Map(),
1058
+ formulas: [],
883
1059
  steps: [],
884
1060
  stepCount: 0,
1061
+ stepByHash: new Map(),
1062
+ alternativeDerivations: new Map(),
885
1063
  };
886
1064
  // Cargar premisas
887
1065
  for (const name of premiseNames) {
@@ -914,6 +1092,8 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
914
1092
  source: 'premise',
915
1093
  });
916
1094
  state.known.set(formulaHash(f), f);
1095
+ state.formulas.push(f);
1096
+ state.stepByHash.set(formulaHash(f), state.stepCount);
917
1097
  }
918
1098
  }
919
1099
  if (isExcludedMiddleFormula(goal)) {
@@ -927,7 +1107,7 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
927
1107
  while (changed && iterations < maxIterations && state.known.size < MAX_KNOWN) {
928
1108
  changed = false;
929
1109
  iterations++;
930
- const currentFormulas = Array.from(state.known.values());
1110
+ const currentFormulas = state.formulas;
931
1111
  const prevProcessedIndex = lastProcessedIndex;
932
1112
  lastProcessedIndex = currentFormulas.length;
933
1113
  for (let i = 0; i < currentFormulas.length; i++) {
@@ -941,6 +1121,15 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
941
1121
  const f2 = currentFormulas[j];
942
1122
  if (state.known.has(formulaHash(goal)))
943
1123
  break;
1124
+ // Contradicción explícita: de A y !A, derivar false (⊥)
1125
+ if ((f1.kind === 'not' && f1.args?.[0] && formulasEqual(f1.args[0], f2)) ||
1126
+ (f2.kind === 'not' && f2.args?.[0] && formulasEqual(f2.args[0], f1))) {
1127
+ changed =
1128
+ addDerivedFormula(state, { kind: 'false' }, 'Contradiccion', [
1129
+ findStep(state.steps, f1),
1130
+ findStep(state.steps, f2),
1131
+ ]) || changed;
1132
+ }
944
1133
  // Modus Ponens: de A y (A -> B), derivar B
945
1134
  if (f2.kind === 'implies' &&
946
1135
  f2.args?.[0] &&
@@ -975,6 +1164,19 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
975
1164
  findStep(state.steps, f2),
976
1165
  ]) || changed;
977
1166
  }
1167
+ // Modus Tollens con consecuente negado: de B y (A -> !B), derivar !A
1168
+ if (f2.kind === 'implies' &&
1169
+ f2.args?.[0] &&
1170
+ f2.args?.[1]?.kind === 'not' &&
1171
+ f2.args[1].args?.[0] &&
1172
+ formulasEqual(f1, f2.args[1].args[0])) {
1173
+ const conclusion = { kind: 'not', args: [f2.args[0]] };
1174
+ changed =
1175
+ addDerivedFormula(state, conclusion, 'Modus Tollens', [
1176
+ findStep(state.steps, f1),
1177
+ findStep(state.steps, f2),
1178
+ ]) || changed;
1179
+ }
978
1180
  // Conjunction Introduction: de A y B, derivar A & B
979
1181
  // Only produce conjunctions that are relevant to the goal to avoid O(n²) explosion
980
1182
  if (f1 !== f2) {
@@ -1163,6 +1365,11 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
1163
1365
  ]) || changed;
1164
1366
  }
1165
1367
  }
1368
+ // Eliminación de contradicción: de false (⊥), derivar cualquier meta
1369
+ if (f1.kind === 'false' && !formulasEqual(f1, goal)) {
1370
+ changed =
1371
+ addDerivedFormula(state, goal, 'Explosion', [findStep(state.steps, f1)]) || changed;
1372
+ }
1166
1373
  // Conjunction Elimination: de A & B, derivar A y B
1167
1374
  if (f1.kind === 'and' && f1.args) {
1168
1375
  for (const sub of f1.args) {
@@ -1503,10 +1710,10 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
1503
1710
  if (state.known.has(formulaHash(goal))) {
1504
1711
  // Filtrar solo pasos relevantes para la derivación
1505
1712
  const relevantSteps = traceBack(state.steps, goal);
1506
- return buildProof(goal, relevantSteps, premiseNames, theory);
1713
+ return buildProof(goal, relevantSteps, premiseNames, theory, 'natural_deduction', undefined, buildDerivationMetadata(state, relevantSteps));
1507
1714
  }
1508
1715
  // --- Sub-derivaciones recursivas (antes del fallback semántico) ---
1509
- const MAX_SUB_DEPTH = 2;
1716
+ const MAX_SUB_DEPTH = 3;
1510
1717
  // Prueba Condicional real (→-Introducción / Deduction Theorem):
1511
1718
  // Para derivar A→B, asumimos A como premisa temporal y derivamos B.
1512
1719
  if (depth < MAX_SUB_DEPTH && goal.kind === 'implies' && goal.args?.[0] && goal.args?.[1]) {
@@ -1584,14 +1791,14 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
1584
1791
  subproofs: [subProof],
1585
1792
  source: 'rule',
1586
1793
  });
1587
- return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProof]);
1794
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProof], buildCompositeDerivationMetadata(state, mainSteps, [subProof]));
1588
1795
  }
1589
1796
  }
1590
1797
  }
1591
1798
  // Prueba por Casos (∨-Eliminación / Disjunction Elimination):
1592
1799
  // Si tenemos A|B y queremos derivar C, asumimos A→C y B→C por separado.
1593
1800
  if (depth < MAX_SUB_DEPTH) {
1594
- const disjunctions = Array.from(state.known.values()).filter((f) => f.kind === 'or' && f.args?.[0] && f.args?.[1]);
1801
+ const disjunctions = state.formulas.filter((f) => f.kind === 'or' && f.args?.[0] && f.args?.[1]);
1595
1802
  for (const disj of disjunctions) {
1596
1803
  const left = disj.args?.[0];
1597
1804
  const right = disj.args?.[1];
@@ -1720,10 +1927,113 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
1720
1927
  subproofs: [subProofL, subProofR],
1721
1928
  source: 'rule',
1722
1929
  });
1723
- return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [
1724
- subProofL,
1725
- subProofR,
1726
- ]);
1930
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProofL, subProofR], buildCompositeDerivationMetadata(state, mainSteps, [subProofL, subProofR]));
1931
+ }
1932
+ }
1933
+ // Backchaining dirigido por meta: si tenemos A → objetivo,
1934
+ // intentamos derivar A para cerrar con Modus Ponens sin esperar a que aparezca espontáneamente.
1935
+ if (depth < MAX_SUB_DEPTH) {
1936
+ const candidateImplications = state.formulas.filter((formula) => formula.kind === 'implies' &&
1937
+ !!formula.args?.[0] &&
1938
+ !!formula.args?.[1] &&
1939
+ formulasEqual(formula.args[1], goal));
1940
+ for (const implication of candidateImplications) {
1941
+ const antecedent = implication.args?.[0];
1942
+ if (!antecedent || formulasEqual(antecedent, goal))
1943
+ continue;
1944
+ const antecedentProof = tryDerive(antecedent, theory, premiseNames, depth + 1);
1945
+ if (!antecedentProof || antecedentProof.status !== 'complete')
1946
+ continue;
1947
+ const antecedentIsSyntactic = antecedentProof.steps.every((step) => step.source !== 'semantic');
1948
+ if (!antecedentIsSyntactic)
1949
+ continue;
1950
+ const implicationProof = buildKnownDerivationProof(state, implication, premiseNames, theory);
1951
+ const mainSteps = buildMergedGoalProof(state.steps, [implicationProof, antecedentProof], goal, 'Modus Ponens');
1952
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [implicationProof, antecedentProof], buildCompositeDerivationMetadata(state, mainSteps, [implicationProof, antecedentProof]));
1953
+ }
1954
+ }
1955
+ // Meta conjuntiva (∧-Introducción dirigida por meta): derivar ambos componentes por separado.
1956
+ if (depth < MAX_SUB_DEPTH && goal.kind === 'and' && goal.args?.[0] && goal.args?.[1]) {
1957
+ const leftGoal = goal.args[0];
1958
+ const rightGoal = goal.args[1];
1959
+ const leftProof = tryDerive(leftGoal, theory, premiseNames, depth + 1);
1960
+ const rightProof = tryDerive(rightGoal, theory, premiseNames, depth + 1);
1961
+ if (leftProof &&
1962
+ rightProof &&
1963
+ leftProof.status === 'complete' &&
1964
+ rightProof.status === 'complete') {
1965
+ const leftSyntactic = leftProof.steps.every((s) => s.source !== 'semantic');
1966
+ const rightSyntactic = rightProof.steps.every((s) => s.source !== 'semantic');
1967
+ if (leftSyntactic && rightSyntactic) {
1968
+ const mainSteps = buildMergedGoalProof(state.steps, [leftProof, rightProof], goal, 'Introduccion de conjuncion');
1969
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [leftProof, rightProof], buildCompositeDerivationMetadata(state, mainSteps, [leftProof, rightProof]));
1970
+ }
1971
+ }
1972
+ }
1973
+ // Meta bicondicional (↔-Introducción dirigida por meta): demostrar ambos sentidos.
1974
+ if (depth < MAX_SUB_DEPTH && goal.kind === 'biconditional' && goal.args?.[0] && goal.args?.[1]) {
1975
+ const leftToRight = { kind: 'implies', args: [goal.args[0], goal.args[1]] };
1976
+ const rightToLeft = { kind: 'implies', args: [goal.args[1], goal.args[0]] };
1977
+ const leftProof = tryDerive(leftToRight, theory, premiseNames, depth + 1);
1978
+ const rightProof = tryDerive(rightToLeft, theory, premiseNames, depth + 1);
1979
+ if (leftProof &&
1980
+ rightProof &&
1981
+ leftProof.status === 'complete' &&
1982
+ rightProof.status === 'complete') {
1983
+ const leftSyntactic = leftProof.steps.every((s) => s.source !== 'semantic');
1984
+ const rightSyntactic = rightProof.steps.every((s) => s.source !== 'semantic');
1985
+ if (leftSyntactic && rightSyntactic) {
1986
+ const mainSteps = buildMergedGoalProof(state.steps, [leftProof, rightProof], goal, 'Introduccion de bicondicional');
1987
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [leftProof, rightProof], buildCompositeDerivationMetadata(state, mainSteps, [leftProof, rightProof]));
1988
+ }
1989
+ }
1990
+ }
1991
+ // Introducción de negación (¬-Introducción): para derivar ¬A,
1992
+ // asumimos A y derivamos contradicción explícita (false / ⊥).
1993
+ if (depth < MAX_SUB_DEPTH && goal.kind === 'not' && goal.args?.[0]) {
1994
+ const assumption = goal.args[0];
1995
+ const contradiction = { kind: 'false' };
1996
+ const tempTheory = {
1997
+ profile: theory.profile,
1998
+ axioms: new Map(theory.axioms),
1999
+ theorems: new Map(theory.theorems),
2000
+ claims: theory.claims,
2001
+ judgments: theory.judgments,
2002
+ };
2003
+ const assumptionName = `__neg_assumption_${depth}_${formulaHash(assumption)}`;
2004
+ tempTheory.axioms.set(assumptionName, assumption);
2005
+ const subPremises = [...premiseNames, assumptionName];
2006
+ const subProof = tryDerive(contradiction, tempTheory, subPremises, depth + 1);
2007
+ if (subProof && subProof.status === 'complete') {
2008
+ const isSyntactic = subProof.steps.every((s) => s.source !== 'semantic');
2009
+ if (isSyntactic) {
2010
+ const mainSteps = buildSingleAssumptionProof(state.steps, assumption, 'Supuesto (para negacion)', subProof, goal, 'Introduccion de negacion');
2011
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProof], buildCompositeDerivationMetadata(state, mainSteps, [subProof]));
2012
+ }
2013
+ }
2014
+ }
2015
+ // RAA genérica: para derivar A en lógica clásica,
2016
+ // asumimos ¬A y buscamos contradicción explícita.
2017
+ if (depth < MAX_SUB_DEPTH && goal.kind !== 'not' && goal.kind !== 'false') {
2018
+ const assumption = { kind: 'not', args: [goal] };
2019
+ const contradiction = { kind: 'false' };
2020
+ const tempTheory = {
2021
+ profile: theory.profile,
2022
+ axioms: new Map(theory.axioms),
2023
+ theorems: new Map(theory.theorems),
2024
+ claims: theory.claims,
2025
+ judgments: theory.judgments,
2026
+ };
2027
+ const assumptionName = `__raa_assumption_${depth}_${formulaHash(assumption)}`;
2028
+ tempTheory.axioms.set(assumptionName, assumption);
2029
+ const subPremises = [...premiseNames, assumptionName];
2030
+ const subProof = tryDerive(contradiction, tempTheory, subPremises, depth + 1);
2031
+ if (subProof && subProof.status === 'complete') {
2032
+ const isSyntactic = subProof.steps.every((s) => s.source !== 'semantic');
2033
+ if (isSyntactic) {
2034
+ const mainSteps = buildSingleAssumptionProof(state.steps, assumption, 'Supuesto (para RAA)', subProof, goal, 'RAA (Reduccion al Absurdo)');
2035
+ return buildProof(goal, mainSteps, premiseNames, theory, 'natural_deduction', [subProof], buildCompositeDerivationMetadata(state, mainSteps, [subProof]));
2036
+ }
1727
2037
  }
1728
2038
  }
1729
2039
  // Fallback: verificar semánticamente
@@ -1796,13 +2106,19 @@ function tryDerive(goal, theory, premiseNames, depth = 0) {
1796
2106
  state.known.set(goalHash, goal);
1797
2107
  }
1798
2108
  const relevantSteps = traceBack(state.steps, goal);
1799
- return buildProof(goal, relevantSteps, premiseNames, theory, 'semantic');
2109
+ return buildProof(goal, relevantSteps, premiseNames, theory, 'semantic', undefined, buildDerivationMetadata(state, relevantSteps, true));
1800
2110
  }
1801
2111
  }
1802
2112
  return null;
1803
2113
  }
1804
- function findStep(steps, formula) {
2114
+ function findStep(stateOrSteps, formula) {
1805
2115
  const hash = formulaHash(formula);
2116
+ if (!Array.isArray(stateOrSteps)) {
2117
+ const cached = stateOrSteps.stepByHash.get(hash);
2118
+ if (cached !== undefined)
2119
+ return cached;
2120
+ }
2121
+ const steps = Array.isArray(stateOrSteps) ? stateOrSteps : stateOrSteps.steps;
1806
2122
  for (const s of steps) {
1807
2123
  if (formulaHash(s.formula) === hash)
1808
2124
  return s.stepNumber;
@@ -2188,13 +2504,23 @@ class ClassicalPropositional {
2188
2504
  : `${formulaToString(goal)} derivado exitosamente`,
2189
2505
  proof,
2190
2506
  reasoningType,
2191
- reasoningSchema: rulesUsed.has('Modus Ponens')
2192
- ? 'φ ψ, φ ψ'
2193
- : rulesUsed.has('Modus Tollens')
2194
- ? 'φ ψ, ¬ψ ¬φ'
2195
- : rulesUsed.has('Silogismo Hipotetico')
2196
- ? 'φ ψ, ψ → χ φ → χ'
2197
- : undefined,
2507
+ reasoningSchema: rulesUsed.has('Introduccion de negacion')
2508
+ ? '[φ] ⊥, por lo tanto ¬φ'
2509
+ : rulesUsed.has('RAA (Reduccion al Absurdo)')
2510
+ ? '[¬φ] ⊥, por lo tanto φ'
2511
+ : rulesUsed.has('Contradiccion')
2512
+ ? 'φ, ¬φ'
2513
+ : rulesUsed.has('Explosion')
2514
+ ? '⊥ ⊢ ψ'
2515
+ : rulesUsed.has('Prueba Condicional (Teorema de Deduccion)')
2516
+ ? '[φ] ⊢ ψ, por lo tanto φ → ψ'
2517
+ : rulesUsed.has('Modus Ponens')
2518
+ ? 'φ → ψ, φ ⊢ ψ'
2519
+ : rulesUsed.has('Modus Tollens')
2520
+ ? 'φ → ψ, ¬ψ ⊢ ¬φ'
2521
+ : rulesUsed.has('Silogismo Hipotetico')
2522
+ ? 'φ → ψ, ψ → χ ⊢ φ → χ'
2523
+ : undefined,
2198
2524
  educationalNote: (0, educational_notes_1.pickEducationalNote)({
2199
2525
  op: 'derive',
2200
2526
  ok: true,