verifiable-thinking-mcp 0.4.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.
- package/LICENSE +21 -0
- package/README.md +339 -0
- package/package.json +75 -0
- package/src/index.ts +38 -0
- package/src/lib/cache.ts +246 -0
- package/src/lib/compression.ts +804 -0
- package/src/lib/compute/cache.ts +86 -0
- package/src/lib/compute/classifier.ts +555 -0
- package/src/lib/compute/confidence.ts +79 -0
- package/src/lib/compute/context.ts +154 -0
- package/src/lib/compute/extract.ts +200 -0
- package/src/lib/compute/filter.ts +224 -0
- package/src/lib/compute/index.ts +171 -0
- package/src/lib/compute/math.ts +247 -0
- package/src/lib/compute/patterns.ts +564 -0
- package/src/lib/compute/registry.ts +145 -0
- package/src/lib/compute/solvers/arithmetic.ts +65 -0
- package/src/lib/compute/solvers/calculus.ts +249 -0
- package/src/lib/compute/solvers/derivation-core.ts +371 -0
- package/src/lib/compute/solvers/derivation-latex.ts +160 -0
- package/src/lib/compute/solvers/derivation-mistakes.ts +1046 -0
- package/src/lib/compute/solvers/derivation-simplify.ts +451 -0
- package/src/lib/compute/solvers/derivation-transform.ts +620 -0
- package/src/lib/compute/solvers/derivation.ts +67 -0
- package/src/lib/compute/solvers/facts.ts +120 -0
- package/src/lib/compute/solvers/formula.ts +728 -0
- package/src/lib/compute/solvers/index.ts +36 -0
- package/src/lib/compute/solvers/logic.ts +422 -0
- package/src/lib/compute/solvers/probability.ts +307 -0
- package/src/lib/compute/solvers/statistics.ts +262 -0
- package/src/lib/compute/solvers/word-problems.ts +408 -0
- package/src/lib/compute/types.ts +107 -0
- package/src/lib/concepts.ts +111 -0
- package/src/lib/domain.ts +731 -0
- package/src/lib/extraction.ts +912 -0
- package/src/lib/index.ts +122 -0
- package/src/lib/judge.ts +260 -0
- package/src/lib/math/ast.ts +842 -0
- package/src/lib/math/index.ts +8 -0
- package/src/lib/math/operators.ts +171 -0
- package/src/lib/math/tokenizer.ts +477 -0
- package/src/lib/patterns.ts +200 -0
- package/src/lib/session.ts +825 -0
- package/src/lib/think/challenge.ts +323 -0
- package/src/lib/think/complexity.ts +504 -0
- package/src/lib/think/confidence-drift.ts +507 -0
- package/src/lib/think/consistency.ts +347 -0
- package/src/lib/think/guidance.ts +188 -0
- package/src/lib/think/helpers.ts +568 -0
- package/src/lib/think/hypothesis.ts +216 -0
- package/src/lib/think/index.ts +127 -0
- package/src/lib/think/prompts.ts +262 -0
- package/src/lib/think/route.ts +358 -0
- package/src/lib/think/schema.ts +98 -0
- package/src/lib/think/scratchpad-schema.ts +662 -0
- package/src/lib/think/spot-check.ts +961 -0
- package/src/lib/think/types.ts +93 -0
- package/src/lib/think/verification.ts +260 -0
- package/src/lib/tokens.ts +177 -0
- package/src/lib/verification.ts +620 -0
- package/src/prompts/index.ts +10 -0
- package/src/prompts/templates.ts +336 -0
- package/src/resources/index.ts +8 -0
- package/src/resources/sessions.ts +196 -0
- package/src/tools/compress.ts +138 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/scratchpad.ts +2659 -0
- package/src/tools/sessions.ts +144 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solver Registry - Registration of compute solvers
|
|
3
|
+
*
|
|
4
|
+
* Each solver declares its type mask and priority.
|
|
5
|
+
*
|
|
6
|
+
* To add a new solver:
|
|
7
|
+
* 1. Create a file in ./solvers/
|
|
8
|
+
* 2. Export `solver` (single) or `solvers` (array) with Solver interface
|
|
9
|
+
* 3. Import and register it below
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { type SolverMask, SolverType, shouldTrySolver } from "./classifier.ts";
|
|
13
|
+
// Import all solver modules
|
|
14
|
+
import * as arithmeticModule from "./solvers/arithmetic.ts";
|
|
15
|
+
import * as calculusModule from "./solvers/calculus.ts";
|
|
16
|
+
import * as derivationModule from "./solvers/derivation.ts";
|
|
17
|
+
import * as factsModule from "./solvers/facts.ts";
|
|
18
|
+
import * as formulaModule from "./solvers/formula.ts";
|
|
19
|
+
import * as logicModule from "./solvers/logic.ts";
|
|
20
|
+
import * as probabilityModule from "./solvers/probability.ts";
|
|
21
|
+
import * as statisticsModule from "./solvers/statistics.ts";
|
|
22
|
+
import * as wordProblemsModule from "./solvers/word-problems.ts";
|
|
23
|
+
import type { ComputeResult, Solver } from "./types.ts";
|
|
24
|
+
|
|
25
|
+
// Re-export Solver type for convenience
|
|
26
|
+
export type { Solver } from "./types.ts";
|
|
27
|
+
|
|
28
|
+
// =============================================================================
|
|
29
|
+
// REGISTRY
|
|
30
|
+
// =============================================================================
|
|
31
|
+
|
|
32
|
+
const solvers: Solver[] = [];
|
|
33
|
+
let sortedCache: Solver[] | null = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Register a solver. Call this at module load time.
|
|
37
|
+
*/
|
|
38
|
+
export function registerSolver(solver: Solver): void {
|
|
39
|
+
solvers.push(solver);
|
|
40
|
+
sortedCache = null; // Invalidate cache
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get all registered solvers, sorted by priority
|
|
45
|
+
*/
|
|
46
|
+
export function getSolvers(): Solver[] {
|
|
47
|
+
if (!sortedCache) {
|
|
48
|
+
sortedCache = [...solvers].sort((a, b) => a.priority - b.priority);
|
|
49
|
+
}
|
|
50
|
+
return sortedCache;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get solvers that match a given mask, sorted by priority
|
|
55
|
+
*/
|
|
56
|
+
export function getSolversForMask(mask: SolverMask): Solver[] {
|
|
57
|
+
return getSolvers().filter((s) => shouldTrySolver(mask, s.types));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Run solvers matching the mask until one succeeds
|
|
62
|
+
*/
|
|
63
|
+
export function runSolvers(text: string, lower: string, mask: SolverMask): ComputeResult {
|
|
64
|
+
const matchingSolvers = getSolversForMask(mask);
|
|
65
|
+
|
|
66
|
+
for (const solver of matchingSolvers) {
|
|
67
|
+
const result = solver.solve(text, lower);
|
|
68
|
+
if (result.solved) {
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { solved: false, confidence: 0 };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get registry stats for debugging
|
|
78
|
+
*/
|
|
79
|
+
export function getRegistryStats(): { count: number; byType: Record<string, number> } {
|
|
80
|
+
const byType: Record<string, number> = {};
|
|
81
|
+
|
|
82
|
+
for (const solver of solvers) {
|
|
83
|
+
if (solver.types & SolverType.ARITHMETIC) byType.arithmetic = (byType.arithmetic || 0) + 1;
|
|
84
|
+
if (solver.types & SolverType.FORMULA_TIER1)
|
|
85
|
+
byType.formula_tier1 = (byType.formula_tier1 || 0) + 1;
|
|
86
|
+
if (solver.types & SolverType.FORMULA_TIER2)
|
|
87
|
+
byType.formula_tier2 = (byType.formula_tier2 || 0) + 1;
|
|
88
|
+
if (solver.types & SolverType.FORMULA_TIER3)
|
|
89
|
+
byType.formula_tier3 = (byType.formula_tier3 || 0) + 1;
|
|
90
|
+
if (solver.types & SolverType.FORMULA_TIER4)
|
|
91
|
+
byType.formula_tier4 = (byType.formula_tier4 || 0) + 1;
|
|
92
|
+
if (solver.types & SolverType.WORD_PROBLEM)
|
|
93
|
+
byType.word_problem = (byType.word_problem || 0) + 1;
|
|
94
|
+
if (solver.types & SolverType.MULTI_STEP) byType.multi_step = (byType.multi_step || 0) + 1;
|
|
95
|
+
if (solver.types & SolverType.CALCULUS) byType.calculus = (byType.calculus || 0) + 1;
|
|
96
|
+
if (solver.types & SolverType.FACTS) byType.facts = (byType.facts || 0) + 1;
|
|
97
|
+
if (solver.types & SolverType.LOGIC) byType.logic = (byType.logic || 0) + 1;
|
|
98
|
+
if (solver.types & SolverType.PROBABILITY) byType.probability = (byType.probability || 0) + 1;
|
|
99
|
+
if (solver.types & SolverType.DERIVATION) byType.derivation = (byType.derivation || 0) + 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return { count: solvers.length, byType };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* List all registered solvers with their descriptions (for debugging/docs)
|
|
107
|
+
*/
|
|
108
|
+
export function listSolvers(): Array<{ name: string; description: string; priority: number }> {
|
|
109
|
+
return getSolvers().map((s) => ({
|
|
110
|
+
name: s.name,
|
|
111
|
+
description: s.description ?? "(no description)",
|
|
112
|
+
priority: s.priority,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =============================================================================
|
|
117
|
+
// EXPLICIT SOLVER REGISTRATION
|
|
118
|
+
// Register all solvers from modules (no Bun.Glob needed - works with Node.js too)
|
|
119
|
+
// =============================================================================
|
|
120
|
+
|
|
121
|
+
// Helper to register solvers from a module
|
|
122
|
+
function registerFromModule(mod: Record<string, unknown>): void {
|
|
123
|
+
// Register single solver
|
|
124
|
+
if (mod.solver && typeof mod.solver === "object") {
|
|
125
|
+
registerSolver(mod.solver as Solver);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Register multiple solvers
|
|
129
|
+
if (mod.solvers && Array.isArray(mod.solvers)) {
|
|
130
|
+
for (const s of mod.solvers) {
|
|
131
|
+
registerSolver(s as Solver);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Register all solver modules
|
|
137
|
+
registerFromModule(arithmeticModule);
|
|
138
|
+
registerFromModule(calculusModule);
|
|
139
|
+
registerFromModule(derivationModule);
|
|
140
|
+
registerFromModule(factsModule);
|
|
141
|
+
registerFromModule(formulaModule);
|
|
142
|
+
registerFromModule(logicModule);
|
|
143
|
+
registerFromModule(probabilityModule);
|
|
144
|
+
registerFromModule(statisticsModule);
|
|
145
|
+
registerFromModule(wordProblemsModule);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arithmetic solver - safe evaluation of math expressions
|
|
3
|
+
* Uses recursive descent parser instead of eval/Function
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { SolverType } from "../classifier.ts";
|
|
7
|
+
import { safeEvaluate } from "../math.ts";
|
|
8
|
+
import { ARITHMETIC } from "../patterns.ts";
|
|
9
|
+
import type { ComputeResult, Solver } from "../types.ts";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Safe arithmetic evaluation using recursive descent parser
|
|
13
|
+
* Supports: +, -, *, /, ^, (), negative numbers, decimals
|
|
14
|
+
*/
|
|
15
|
+
export function tryArithmetic(text: string): ComputeResult {
|
|
16
|
+
const start = performance.now();
|
|
17
|
+
|
|
18
|
+
const patterns = [
|
|
19
|
+
ARITHMETIC.whatIs,
|
|
20
|
+
ARITHMETIC.calculate,
|
|
21
|
+
ARITHMETIC.compute,
|
|
22
|
+
ARITHMETIC.evaluate,
|
|
23
|
+
ARITHMETIC.equalsQuestion,
|
|
24
|
+
ARITHMETIC.bareExpression,
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
let expr: string | null = null;
|
|
28
|
+
for (const pattern of patterns) {
|
|
29
|
+
const match = text.match(pattern);
|
|
30
|
+
if (match?.[1]) {
|
|
31
|
+
expr = match[1].trim();
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!expr) return { solved: false, confidence: 0 };
|
|
37
|
+
|
|
38
|
+
// Use safe recursive descent parser instead of new Function()
|
|
39
|
+
const result = safeEvaluate(expr);
|
|
40
|
+
|
|
41
|
+
if (result.success && result.value !== undefined) {
|
|
42
|
+
const time_ms = performance.now() - start;
|
|
43
|
+
return {
|
|
44
|
+
solved: true,
|
|
45
|
+
result: Number.isInteger(result.value) ? result.value : +result.value.toFixed(10),
|
|
46
|
+
method: "arithmetic",
|
|
47
|
+
confidence: 1.0,
|
|
48
|
+
time_ms,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { solved: false, confidence: 0 };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// =============================================================================
|
|
56
|
+
// SOLVER REGISTRATION
|
|
57
|
+
// =============================================================================
|
|
58
|
+
|
|
59
|
+
export const solver: Solver = {
|
|
60
|
+
name: "arithmetic",
|
|
61
|
+
description: "Safe evaluation of math expressions (+, -, *, /, ^, parentheses)",
|
|
62
|
+
types: SolverType.ARITHMETIC,
|
|
63
|
+
priority: 10,
|
|
64
|
+
solve: (text, _lower) => tryArithmetic(text),
|
|
65
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calculus solver - derivatives and definite integrals of polynomials
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { SolverType } from "../classifier.ts";
|
|
6
|
+
import { formatResult, normalizeUnicodeSuperscripts } from "../math.ts";
|
|
7
|
+
import { CALCULUS } from "../patterns.ts";
|
|
8
|
+
import type { ComputeResult, PolyTerm, Solver } from "../types.ts";
|
|
9
|
+
|
|
10
|
+
/** Parse a polynomial string like "3x^2 + 2x - 5" into terms @internal */
|
|
11
|
+
function parsePolynomial(text: string): PolyTerm[] {
|
|
12
|
+
// Normalize: whitespace, case, and Unicode superscripts
|
|
13
|
+
const clean = normalizeUnicodeSuperscripts(text).replace(/\s+/g, "").toLowerCase();
|
|
14
|
+
|
|
15
|
+
const terms: PolyTerm[] = [];
|
|
16
|
+
|
|
17
|
+
// Match terms like: "3x^2", "-x", "5", "+2x^-1"
|
|
18
|
+
const termRegex = /([+-]?)(\d*\.?\d*)x(?:\^([+-]?\d+))?|([+-]?)(\d+\.?\d*)/g;
|
|
19
|
+
let match: RegExpExecArray | null;
|
|
20
|
+
|
|
21
|
+
while ((match = termRegex.exec(clean)) !== null) {
|
|
22
|
+
if (match[0] === "") continue;
|
|
23
|
+
|
|
24
|
+
// Variable term (ax^n)
|
|
25
|
+
if (match[2] !== undefined && match[0].includes("x")) {
|
|
26
|
+
const sign = match[1] === "-" ? -1 : 1;
|
|
27
|
+
const coeffStr = match[2];
|
|
28
|
+
const coeff =
|
|
29
|
+
sign * (coeffStr === "" || coeffStr === "+" || coeffStr === "-" ? 1 : parseFloat(coeffStr));
|
|
30
|
+
const exp = match[3] ? parseInt(match[3], 10) : 1;
|
|
31
|
+
terms.push({ coeff, exp });
|
|
32
|
+
}
|
|
33
|
+
// Constant term
|
|
34
|
+
else if (match[5] !== undefined) {
|
|
35
|
+
const sign = match[4] === "-" ? -1 : 1;
|
|
36
|
+
const coeff = sign * parseFloat(match[5]);
|
|
37
|
+
terms.push({ coeff, exp: 0 });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return terms;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Differentiate polynomial terms using power rule @internal */
|
|
44
|
+
function differentiateTerms(terms: PolyTerm[]): PolyTerm[] {
|
|
45
|
+
return terms
|
|
46
|
+
.map((t) => ({ coeff: t.coeff * t.exp, exp: t.exp - 1 }))
|
|
47
|
+
.filter((t) => t.coeff !== 0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Integrate polynomial terms using power rule @internal */
|
|
51
|
+
function integrateTerms(terms: PolyTerm[]): PolyTerm[] {
|
|
52
|
+
return terms.map((t) => {
|
|
53
|
+
if (t.exp === -1) throw new Error("Cannot integrate 1/x in polynomial mode");
|
|
54
|
+
return { coeff: t.coeff / (t.exp + 1), exp: t.exp + 1 };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Simpson's Rule for numerical integration
|
|
60
|
+
* Works for any continuous function, not just polynomials
|
|
61
|
+
* Accuracy: O(h^4) where h = (b-a)/n
|
|
62
|
+
*/
|
|
63
|
+
export function simpsonIntegrate(fn: (x: number) => number, a: number, b: number, n = 100): number {
|
|
64
|
+
// Handle edge cases
|
|
65
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return NaN;
|
|
66
|
+
if (a === b) return 0;
|
|
67
|
+
|
|
68
|
+
// n must be even for Simpson's rule, and capped for safety
|
|
69
|
+
if (n % 2 !== 0) n++;
|
|
70
|
+
n = Math.min(n, 10000);
|
|
71
|
+
|
|
72
|
+
const h = (b - a) / n;
|
|
73
|
+
let sum = fn(a) + fn(b);
|
|
74
|
+
|
|
75
|
+
for (let i = 1; i < n; i++) {
|
|
76
|
+
const x = a + i * h;
|
|
77
|
+
// Odd indices get weight 4, even indices get weight 2
|
|
78
|
+
sum += (i % 2 === 0 ? 2 : 4) * fn(x);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (h / 3) * sum;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a safe evaluator function for simple math expressions
|
|
86
|
+
* Supports: x, numbers, +, -, *, /, ^, sin, cos, tan, exp, ln, sqrt
|
|
87
|
+
* Returns null if expression is not safe to evaluate
|
|
88
|
+
* @internal
|
|
89
|
+
*/
|
|
90
|
+
function createSafeFunction(expr: string): ((x: number) => number) | null {
|
|
91
|
+
// Normalize
|
|
92
|
+
const clean = expr.toLowerCase().replace(/\s+/g, "").replace(/\*\*/g, "^");
|
|
93
|
+
|
|
94
|
+
// Whitelist: only allow safe math characters and functions
|
|
95
|
+
const safePattern = /^[x\d+\-*/^().,sincotaexplnqr]+$/;
|
|
96
|
+
if (!safePattern.test(clean)) return null;
|
|
97
|
+
|
|
98
|
+
// Build function by replacing math operations with safe JS equivalents
|
|
99
|
+
// This is still not using eval - we construct the function from parsed components
|
|
100
|
+
try {
|
|
101
|
+
// For now, only support polynomial-like expressions that our parser handles
|
|
102
|
+
// More complex expressions (sin, cos, etc.) would need the full parser
|
|
103
|
+
const terms = parsePolynomial(clean);
|
|
104
|
+
if (terms.length === 0) return null;
|
|
105
|
+
|
|
106
|
+
return (x: number) => evaluatePolynomial(terms, x);
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Evaluate polynomial at a point @internal */
|
|
113
|
+
function evaluatePolynomial(terms: PolyTerm[], x: number): number {
|
|
114
|
+
return terms.reduce((sum, t) => sum + t.coeff * x ** t.exp, 0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Try to solve calculus problems (derivatives and integrals)
|
|
119
|
+
*/
|
|
120
|
+
export function tryCalculus(text: string): ComputeResult {
|
|
121
|
+
const start = performance.now();
|
|
122
|
+
|
|
123
|
+
// Normalize Unicode superscripts early so regex patterns work
|
|
124
|
+
const normalizedText = normalizeUnicodeSuperscripts(text);
|
|
125
|
+
|
|
126
|
+
// =========================================================================
|
|
127
|
+
// DERIVATIVES: d/dx of f(x) at x=a
|
|
128
|
+
// =========================================================================
|
|
129
|
+
for (const pattern of CALCULUS.derivative) {
|
|
130
|
+
const match = normalizedText.match(pattern);
|
|
131
|
+
if (match?.[1]) {
|
|
132
|
+
try {
|
|
133
|
+
// Extract just the expression part
|
|
134
|
+
let expr = match[1].trim();
|
|
135
|
+
// Remove trailing punctuation, extra phrases, and redundant "d/dx of"
|
|
136
|
+
expr = expr
|
|
137
|
+
.replace(/[.?!]+$/, "")
|
|
138
|
+
.replace(/\s*(at|evaluated|when).*$/i, "")
|
|
139
|
+
.replace(/^d\/dx\s*(of\s*)?/i, "")
|
|
140
|
+
.replace(/^of\s+/i, "")
|
|
141
|
+
.trim();
|
|
142
|
+
|
|
143
|
+
const terms = parsePolynomial(expr);
|
|
144
|
+
if (terms.length === 0) continue;
|
|
145
|
+
|
|
146
|
+
const diffTerms = differentiateTerms(terms);
|
|
147
|
+
|
|
148
|
+
// If evaluating at a point (group 2 present)
|
|
149
|
+
if (match[2]) {
|
|
150
|
+
const xVal = parseFloat(match[2]);
|
|
151
|
+
const result = evaluatePolynomial(diffTerms, xVal);
|
|
152
|
+
const time_ms = performance.now() - start;
|
|
153
|
+
return {
|
|
154
|
+
solved: true,
|
|
155
|
+
result: formatResult(result),
|
|
156
|
+
method: "derivative_eval",
|
|
157
|
+
confidence: 1.0,
|
|
158
|
+
time_ms,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Return symbolic derivative (format as string)
|
|
163
|
+
const resultStr = diffTerms
|
|
164
|
+
.map((t, i) => {
|
|
165
|
+
const sign = t.coeff >= 0 ? (i > 0 ? " + " : "") : " - ";
|
|
166
|
+
const absCoeff = Math.abs(t.coeff);
|
|
167
|
+
const cStr = absCoeff === 1 && t.exp !== 0 ? "" : String(absCoeff);
|
|
168
|
+
const xStr = t.exp === 0 ? "" : t.exp === 1 ? "x" : `x^${t.exp}`;
|
|
169
|
+
return `${sign}${cStr}${xStr}`;
|
|
170
|
+
})
|
|
171
|
+
.join("")
|
|
172
|
+
.trim();
|
|
173
|
+
|
|
174
|
+
const time_ms = performance.now() - start;
|
|
175
|
+
return {
|
|
176
|
+
solved: true,
|
|
177
|
+
result: resultStr || "0",
|
|
178
|
+
method: "derivative_symbolic",
|
|
179
|
+
confidence: 1.0,
|
|
180
|
+
time_ms,
|
|
181
|
+
};
|
|
182
|
+
} catch {
|
|
183
|
+
// Parsing failed, continue
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// =========================================================================
|
|
189
|
+
// DEFINITE INTEGRALS: integral of f(x) from a to b
|
|
190
|
+
// =========================================================================
|
|
191
|
+
for (const pattern of CALCULUS.integral) {
|
|
192
|
+
const match = text.match(pattern);
|
|
193
|
+
if (match?.[1] && match[2] && match[3]) {
|
|
194
|
+
const a = parseFloat(match[2]);
|
|
195
|
+
const b = parseFloat(match[3]);
|
|
196
|
+
|
|
197
|
+
// First try exact polynomial integration
|
|
198
|
+
try {
|
|
199
|
+
const terms = parsePolynomial(match[1]);
|
|
200
|
+
if (terms.length > 0) {
|
|
201
|
+
const integrated = integrateTerms(terms);
|
|
202
|
+
const result = evaluatePolynomial(integrated, b) - evaluatePolynomial(integrated, a);
|
|
203
|
+
const time_ms = performance.now() - start;
|
|
204
|
+
return {
|
|
205
|
+
solved: true,
|
|
206
|
+
result: formatResult(result),
|
|
207
|
+
method: "definite_integral",
|
|
208
|
+
confidence: 1.0,
|
|
209
|
+
time_ms,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
// Polynomial parsing/integration failed, try numerical
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Fallback: Simpson's rule for numerical integration
|
|
217
|
+
const fn = createSafeFunction(match[1]);
|
|
218
|
+
if (fn) {
|
|
219
|
+
try {
|
|
220
|
+
const result = simpsonIntegrate(fn, a, b, 1000);
|
|
221
|
+
const time_ms = performance.now() - start;
|
|
222
|
+
return {
|
|
223
|
+
solved: true,
|
|
224
|
+
result: formatResult(result),
|
|
225
|
+
method: "numerical_integral",
|
|
226
|
+
confidence: 0.95, // Slightly lower confidence for numerical
|
|
227
|
+
time_ms,
|
|
228
|
+
};
|
|
229
|
+
} catch {
|
|
230
|
+
// Numerical integration failed
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return { solved: false, confidence: 0 };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// =============================================================================
|
|
240
|
+
// SOLVER REGISTRATION
|
|
241
|
+
// =============================================================================
|
|
242
|
+
|
|
243
|
+
export const solver: Solver = {
|
|
244
|
+
name: "calculus",
|
|
245
|
+
description: "Derivatives and definite integrals of polynomials, with Simpson's rule fallback",
|
|
246
|
+
types: SolverType.CALCULUS,
|
|
247
|
+
priority: 50,
|
|
248
|
+
solve: (text, _lower) => tryCalculus(text),
|
|
249
|
+
};
|