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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -0
  3. package/package.json +75 -0
  4. package/src/index.ts +38 -0
  5. package/src/lib/cache.ts +246 -0
  6. package/src/lib/compression.ts +804 -0
  7. package/src/lib/compute/cache.ts +86 -0
  8. package/src/lib/compute/classifier.ts +555 -0
  9. package/src/lib/compute/confidence.ts +79 -0
  10. package/src/lib/compute/context.ts +154 -0
  11. package/src/lib/compute/extract.ts +200 -0
  12. package/src/lib/compute/filter.ts +224 -0
  13. package/src/lib/compute/index.ts +171 -0
  14. package/src/lib/compute/math.ts +247 -0
  15. package/src/lib/compute/patterns.ts +564 -0
  16. package/src/lib/compute/registry.ts +145 -0
  17. package/src/lib/compute/solvers/arithmetic.ts +65 -0
  18. package/src/lib/compute/solvers/calculus.ts +249 -0
  19. package/src/lib/compute/solvers/derivation-core.ts +371 -0
  20. package/src/lib/compute/solvers/derivation-latex.ts +160 -0
  21. package/src/lib/compute/solvers/derivation-mistakes.ts +1046 -0
  22. package/src/lib/compute/solvers/derivation-simplify.ts +451 -0
  23. package/src/lib/compute/solvers/derivation-transform.ts +620 -0
  24. package/src/lib/compute/solvers/derivation.ts +67 -0
  25. package/src/lib/compute/solvers/facts.ts +120 -0
  26. package/src/lib/compute/solvers/formula.ts +728 -0
  27. package/src/lib/compute/solvers/index.ts +36 -0
  28. package/src/lib/compute/solvers/logic.ts +422 -0
  29. package/src/lib/compute/solvers/probability.ts +307 -0
  30. package/src/lib/compute/solvers/statistics.ts +262 -0
  31. package/src/lib/compute/solvers/word-problems.ts +408 -0
  32. package/src/lib/compute/types.ts +107 -0
  33. package/src/lib/concepts.ts +111 -0
  34. package/src/lib/domain.ts +731 -0
  35. package/src/lib/extraction.ts +912 -0
  36. package/src/lib/index.ts +122 -0
  37. package/src/lib/judge.ts +260 -0
  38. package/src/lib/math/ast.ts +842 -0
  39. package/src/lib/math/index.ts +8 -0
  40. package/src/lib/math/operators.ts +171 -0
  41. package/src/lib/math/tokenizer.ts +477 -0
  42. package/src/lib/patterns.ts +200 -0
  43. package/src/lib/session.ts +825 -0
  44. package/src/lib/think/challenge.ts +323 -0
  45. package/src/lib/think/complexity.ts +504 -0
  46. package/src/lib/think/confidence-drift.ts +507 -0
  47. package/src/lib/think/consistency.ts +347 -0
  48. package/src/lib/think/guidance.ts +188 -0
  49. package/src/lib/think/helpers.ts +568 -0
  50. package/src/lib/think/hypothesis.ts +216 -0
  51. package/src/lib/think/index.ts +127 -0
  52. package/src/lib/think/prompts.ts +262 -0
  53. package/src/lib/think/route.ts +358 -0
  54. package/src/lib/think/schema.ts +98 -0
  55. package/src/lib/think/scratchpad-schema.ts +662 -0
  56. package/src/lib/think/spot-check.ts +961 -0
  57. package/src/lib/think/types.ts +93 -0
  58. package/src/lib/think/verification.ts +260 -0
  59. package/src/lib/tokens.ts +177 -0
  60. package/src/lib/verification.ts +620 -0
  61. package/src/prompts/index.ts +10 -0
  62. package/src/prompts/templates.ts +336 -0
  63. package/src/resources/index.ts +8 -0
  64. package/src/resources/sessions.ts +196 -0
  65. package/src/tools/compress.ts +138 -0
  66. package/src/tools/index.ts +5 -0
  67. package/src/tools/scratchpad.ts +2659 -0
  68. package/src/tools/sessions.ts +144 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * Pre-compiled regex patterns for the compute module
3
+ *
4
+ * All patterns are compiled once at module load time, not per-call.
5
+ * This saves ~0.05ms per unique pattern on first call.
6
+ */
7
+
8
+ import type { NegativeSignal, WeightedSignal, WordProblemMatch } from "./types.ts";
9
+
10
+ // =============================================================================
11
+ // GUARDS: Cheap character-based pre-checks
12
+ // =============================================================================
13
+
14
+ export const GUARDS = {
15
+ hasDigit: (t: string) => /\d/.test(t),
16
+ hasX: (t: string) => /x/i.test(t),
17
+ hasPercent: (t: string) => t.includes("%"),
18
+ hasCaret: (t: string) => t.includes("^") || t.includes("**"),
19
+ hasBracket: (t: string) => t.includes("["),
20
+ hasDollar: (t: string) => t.includes("$"),
21
+ hasExclaim: (t: string) => t.includes("!"),
22
+ } as const;
23
+
24
+ // =============================================================================
25
+ // ARITHMETIC PATTERNS
26
+ // =============================================================================
27
+
28
+ export const ARITHMETIC = {
29
+ whatIs: /what\s+is\s+([\d\s+\-*/().]+)/i,
30
+ calculate: /calculate[:\s]+\s*([\d\s+\-*/().]+)/i,
31
+ compute: /compute[:\s]+\s*([\d\s+\-*/().]+)/i,
32
+ evaluate: /evaluate[:\s]+\s*([\d\s+\-*/().]+)/i,
33
+ equalsQuestion: /([\d\s+\-*/().]+)\s*=\s*\?/,
34
+ bareExpression: /^([\d\s+\-*/().]+)$/,
35
+ validChars: /^[\d+\-*/().]+$/,
36
+ invalidPatterns: /\(\)|\+\+|--|\*\*|\/\/|\+-|-\+|\*\/|\/\*/,
37
+ } as const;
38
+
39
+ // =============================================================================
40
+ // FORMULA PATTERNS - Tier 1 (Ultra-fast)
41
+ // =============================================================================
42
+
43
+ export const TIER1 = {
44
+ percentage: /(\d+(?:\.\d+)?)\s*%\s*(?:of\s+)?(\d+(?:\.\d+)?)/i,
45
+ factorial: /(\d+)!|factorial\s*(?:of\s*)?(\d+)/i,
46
+ moduloBasic: /(\d+)\s*(?:mod|modulo)\s*(\d+)/i,
47
+ moduloRemainder: /remainder.*?(\d+).*?(?:divided\s+by|\/)\s*(\d+)/i,
48
+ moduloLastDigitGuard: /\^\s*\d+\s*mod\s*10/i,
49
+ prime: /is\s+(\d+)\s+(?:a\s+)?prime/i,
50
+ fibonacci: /(\d+)(?:th|st|nd|rd)\s+fibonacci/i,
51
+ } as const;
52
+
53
+ // =============================================================================
54
+ // FORMULA PATTERNS - Tier 2 (Fast)
55
+ // =============================================================================
56
+
57
+ export const TIER2 = {
58
+ sqrt: /(?:\u221A|sqrt\s*\(?\s*|square\s+root\s+(?:of\s+)?)(\d+(?:\.\d+)?)/i,
59
+ power: /(\d+(?:\.\d+)?)\s*(?:\^|\*\*|to\s+the\s+(?:power\s+(?:of\s+)?)?)\s*(\d+(?:\.\d+)?)/i,
60
+ powerLastDigitGuard: /last\s+digit/i,
61
+ powerModGuard: /\^\s*\d+\s*mod/i,
62
+ gcd: /(?:gcd|greatest\s+common\s+divisor).*?(\d+).*?(\d+)/i,
63
+ lcm: /(?:lcm|least\s+common\s+multiple).*?(\d+).*?(\d+)/i,
64
+ } as const;
65
+
66
+ // =============================================================================
67
+ // FORMULA PATTERNS - Tier 3 (Medium)
68
+ // =============================================================================
69
+
70
+ export const TIER3 = {
71
+ logBase10: /log[₁1]?[₀0]?\s*\(?\s*(\d+)\s*\)?/gi,
72
+ logNatural: /ln\s*\(?\s*(\d+(?:\.\d+)?)\s*\)?/i,
73
+ quadratic: /(\d*)x[\u00B22^2]\s*([+-])\s*(\d+)x\s*([+-])\s*(\d+)\s*=\s*0/i,
74
+ combinationsChoose: /(\d+)\s*(?:choose|C|c)\s*(\d+)/i,
75
+ combinationsFrom:
76
+ /(?:choose|combination|ways\s+to\s+choose)\s+(\d+)\s+(?:from|items?\s+from)\s+(\d+)/i,
77
+ combinationsHowMany: /how\s+many\s+ways\s+(?:to\s+)?choose\s+(\d+)\s+(?:items?\s+)?from\s+(\d+)/i,
78
+ permutationsP: /(\d+)\s*(?:P|p)\s*(\d+)/i,
79
+ permutationsWord: /(?:permutation|arrangement).*?(\d+).*?(\d+)/i,
80
+ lastDigitMod: /(\d+)\s*\^\s*(\d+)\s*mod\s*10|last\s+digit\s+(?:of\s+)?(\d+)\s*\^\s*(\d+)/i,
81
+ } as const;
82
+
83
+ // =============================================================================
84
+ // FORMULA PATTERNS - Tier 4 (Expensive)
85
+ // =============================================================================
86
+
87
+ export const TIER4 = {
88
+ pythagorean: [
89
+ /(?:legs?|sides?)\s*(?:of\s*)?(\d+(?:\.\d+)?)\s*and\s*(\d+(?:\.\d+)?)[^]*?hypoten/i,
90
+ /triangle[^]*?(\d+(?:\.\d+)?)[^]*?(\d+(?:\.\d+)?)[^]*?hypoten/i,
91
+ /right\s*triangle[^]*?(\d+(?:\.\d+)?)[^,]*?(\d+(?:\.\d+)?)/i,
92
+ ] as const,
93
+ trailingZeros: /trailing\s+zeros?\s+(?:in\s+)?(\d+)[!]?\s*(?:factorial)?/i,
94
+ geometricSeries: [
95
+ /1\s*\+\s*1\/(\d+)\s*\+\s*1\/\d+\s*\+.*(?:sum|infinite|\.\.\.)/i,
96
+ /sum.*1\s*\+\s*1\/(\d+)\s*\+\s*1\/\d+/i,
97
+ ] as const,
98
+ matrixDet:
99
+ /(?:determinant|det).*?\[\s*\[\s*(-?\d+)\s*,\s*(-?\d+)\s*\]\s*,\s*\[\s*(-?\d+)\s*,\s*(-?\d+)\s*\]\s*\]/i,
100
+ compoundInterest:
101
+ /\$?(\d+(?:,\d{3})*(?:\.\d+)?)\s*(?:at|with)\s*(\d+(?:\.\d+)?)\s*%\s*(?:annual\s+)?(?:compound\s+)?interest\s*(?:for\s+)?(\d+)\s*years?/i,
102
+ } as const;
103
+
104
+ // =============================================================================
105
+ // CALCULUS PATTERNS
106
+ // =============================================================================
107
+
108
+ export const CALCULUS = {
109
+ derivative: [
110
+ /(?:derivative|d\/dx)\s+(?:of\s+)?(.+?)\s+(?:at|evaluated\s+at|when)\s+x\s*=\s*(\d+)/i,
111
+ /(?:derivative|d\/dx)\s+(?:of\s+)?([^.?!]+)/i,
112
+ /(?:differentiate)\s+(.+?)\s+(?:at|evaluated\s+at)\s+x\s*=\s*(\d+)/i,
113
+ /(?:differentiate)\s+([^.?!]+)/i,
114
+ ] as const,
115
+ integral: [
116
+ /(?:integral|integrate)\s+(?:of\s+)?(.+?)\s+from\s+(-?\d+)\s+to\s+(-?\d+)/i,
117
+ /\u222B\s*(.+?)\s*(?:dx)?\s*from\s*(-?\d+)\s*to\s*(-?\d+)/i,
118
+ ] as const,
119
+ polynomial: /([+-]?)(\d*\.?\d*)x(?:\^([+-]?\d+))?|([+-]?)(\d+\.?\d*)/g,
120
+ } as const;
121
+
122
+ // =============================================================================
123
+ // WORD PROBLEM PATTERNS
124
+ // =============================================================================
125
+
126
+ export const WORD_PROBLEM_PATTERNS: WordProblemMatch[] = [
127
+ // Multiplication patterns
128
+ {
129
+ pattern: /twice\s+(?:as\s+(?:many|much)\s+(?:as\s+)?)?(\d+(?:\.\d+)?)/i,
130
+ compute: (m) => (m[1] ? parseFloat(m[1]) * 2 : null),
131
+ method: "word_twice",
132
+ },
133
+ {
134
+ pattern: /(\d+(?:\.\d+)?)\s+times\s+(\d+(?:\.\d+)?)/i,
135
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) * parseFloat(m[2]) : null),
136
+ method: "word_times",
137
+ },
138
+ {
139
+ pattern: /triple\s+(?:of\s+)?(\d+(?:\.\d+)?)/i,
140
+ compute: (m) => (m[1] ? parseFloat(m[1]) * 3 : null),
141
+ method: "word_triple",
142
+ },
143
+ {
144
+ pattern: /double\s+(?:of\s+)?(\d+(?:\.\d+)?)/i,
145
+ compute: (m) => (m[1] ? parseFloat(m[1]) * 2 : null),
146
+ method: "word_double",
147
+ },
148
+
149
+ // Division patterns
150
+ {
151
+ pattern: /half\s+(?:of\s+)?(\d+(?:\.\d+)?)/i,
152
+ compute: (m) => (m[1] ? parseFloat(m[1]) / 2 : null),
153
+ method: "word_half",
154
+ },
155
+ {
156
+ pattern: /(?:one\s+)?third\s+(?:of\s+)?(\d+(?:\.\d+)?)/i,
157
+ compute: (m) => (m[1] ? parseFloat(m[1]) / 3 : null),
158
+ method: "word_third",
159
+ },
160
+ {
161
+ pattern: /(?:one\s+)?quarter\s+(?:of\s+)?(\d+(?:\.\d+)?)/i,
162
+ compute: (m) => (m[1] ? parseFloat(m[1]) / 4 : null),
163
+ method: "word_quarter",
164
+ },
165
+ {
166
+ pattern: /(\d+(?:\.\d+)?)\s+divided\s+by\s+(\d+(?:\.\d+)?)/i,
167
+ compute: (m) => {
168
+ if (!m[1] || !m[2]) return null;
169
+ const b = parseFloat(m[2]);
170
+ return b !== 0 ? parseFloat(m[1]) / b : null;
171
+ },
172
+ method: "word_divide",
173
+ },
174
+
175
+ // Addition patterns
176
+ {
177
+ pattern: /sum\s+of\s+(\d+(?:\.\d+)?)\s+and\s+(\d+(?:\.\d+)?)/i,
178
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) + parseFloat(m[2]) : null),
179
+ method: "word_sum",
180
+ },
181
+ {
182
+ pattern: /(\d+(?:\.\d+)?)\s+plus\s+(\d+(?:\.\d+)?)/i,
183
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) + parseFloat(m[2]) : null),
184
+ method: "word_plus",
185
+ },
186
+ {
187
+ pattern: /(\d+(?:\.\d+)?)\s+added\s+to\s+(\d+(?:\.\d+)?)/i,
188
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) + parseFloat(m[2]) : null),
189
+ method: "word_add",
190
+ },
191
+ {
192
+ pattern: /total\s+of\s+(\d+(?:\.\d+)?)\s+and\s+(\d+(?:\.\d+)?)/i,
193
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) + parseFloat(m[2]) : null),
194
+ method: "word_total",
195
+ },
196
+
197
+ // Subtraction patterns
198
+ {
199
+ pattern: /difference\s+(?:between|of)\s+(\d+(?:\.\d+)?)\s+and\s+(\d+(?:\.\d+)?)/i,
200
+ compute: (m) => (m[1] && m[2] ? Math.abs(parseFloat(m[1]) - parseFloat(m[2])) : null),
201
+ method: "word_difference",
202
+ },
203
+ {
204
+ pattern: /(\d+(?:\.\d+)?)\s+minus\s+(\d+(?:\.\d+)?)/i,
205
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) - parseFloat(m[2]) : null),
206
+ method: "word_minus",
207
+ },
208
+ {
209
+ pattern: /(\d+(?:\.\d+)?)\s+less\s+than\s+(\d+(?:\.\d+)?)/i,
210
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[2]) - parseFloat(m[1]) : null), // Note: reversed!
211
+ method: "word_less_than",
212
+ },
213
+ {
214
+ pattern: /subtract\s+(\d+(?:\.\d+)?)\s+from\s+(\d+(?:\.\d+)?)/i,
215
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[2]) - parseFloat(m[1]) : null), // Note: reversed!
216
+ method: "word_subtract",
217
+ },
218
+
219
+ // Product pattern
220
+ {
221
+ pattern: /product\s+of\s+(\d+(?:\.\d+)?)\s+and\s+(\d+(?:\.\d+)?)/i,
222
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) * parseFloat(m[2]) : null),
223
+ method: "word_product",
224
+ },
225
+
226
+ // Quotient pattern
227
+ {
228
+ pattern: /quotient\s+of\s+(\d+(?:\.\d+)?)\s+and\s+(\d+(?:\.\d+)?)/i,
229
+ compute: (m) => {
230
+ if (!m[1] || !m[2]) return null;
231
+ const b = parseFloat(m[2]);
232
+ return b !== 0 ? parseFloat(m[1]) / b : null;
233
+ },
234
+ method: "word_quotient",
235
+ },
236
+
237
+ // "X more than Y"
238
+ {
239
+ pattern: /(\d+(?:\.\d+)?)\s+more\s+than\s+(\d+(?:\.\d+)?)/i,
240
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[2]) + parseFloat(m[1]) : null),
241
+ method: "word_more_than",
242
+ },
243
+
244
+ // Squared / Cubed
245
+ {
246
+ pattern: /(\d+(?:\.\d+)?)\s+squared/i,
247
+ compute: (m) => (m[1] ? parseFloat(m[1]) ** 2 : null),
248
+ method: "word_squared",
249
+ },
250
+ {
251
+ pattern: /(\d+(?:\.\d+)?)\s+cubed/i,
252
+ compute: (m) => (m[1] ? parseFloat(m[1]) ** 3 : null),
253
+ method: "word_cubed",
254
+ },
255
+
256
+ // Rate calculations
257
+ {
258
+ pattern:
259
+ /(\d+(?:\.\d+)?)\s+(?:items?|things?|units?)?\s*(?:at|for)\s+\$?(\d+(?:\.\d+)?)\s+(?:each|per|apiece)/i,
260
+ compute: (m) => (m[1] && m[2] ? parseFloat(m[1]) * parseFloat(m[2]) : null),
261
+ method: "word_rate",
262
+ },
263
+
264
+ // Average of numbers
265
+ {
266
+ pattern: /average\s+of\s+([\d,\s]+(?:and\s+\d+)?)/i,
267
+ compute: (m) => {
268
+ if (!m[1]) return null;
269
+ const nums = m[1].match(/\d+(?:\.\d+)?/g);
270
+ if (!nums || nums.length === 0) return null;
271
+ const sum = nums.reduce((a, b) => a + parseFloat(b), 0);
272
+ return sum / nums.length;
273
+ },
274
+ method: "word_average",
275
+ },
276
+ ];
277
+
278
+ // =============================================================================
279
+ // MULTI-STEP WORD PROBLEM PATTERNS
280
+ // =============================================================================
281
+
282
+ export const MULTI_STEP = {
283
+ twice: /(\b[A-Z][a-z]+\b)\s+has\s+twice\s+(?:as\s+many\s+(?:as\s+)?)?(\b[A-Z][a-z]+\b)/gi,
284
+ half: /(\b[A-Z][a-z]+\b)\s+has\s+half\s+(?:as\s+many\s+(?:as\s+)?)?(\b[A-Z][a-z]+\b)/gi,
285
+ more: /(\b[A-Z][a-z]+\b)\s+has\s+(\d+)\s+more\s+than\s+(\b[A-Z][a-z]+\b)/gi,
286
+ less: /(\b[A-Z][a-z]+\b)\s+has\s+(\d+)\s+(?:less|fewer)\s+than\s+(\b[A-Z][a-z]+\b)/gi,
287
+ triple:
288
+ /(\b[A-Z][a-z]+\b)\s+has\s+(?:three|triple)\s+(?:times\s+)?(?:as\s+many\s+(?:as\s+)?)?(\b[A-Z][a-z]+\b)/gi,
289
+ directValue: /(\b[A-Z][a-z]+\b)\s+(?:has|have|had|owns?|bought|got|earned)\s+(\d+(?:\.\d+)?)/gi,
290
+ question: /(?:how\s+many|what)\s+(?:does|do)\s+(\b[A-Z][a-z]+\b)\s+have/i,
291
+ } as const;
292
+
293
+ // =============================================================================
294
+ // EXTRACT PATTERNS
295
+ // =============================================================================
296
+
297
+ export const EXTRACT = {
298
+ binaryOp: /\b(\d+(?:\.\d+)?)\s*([+\-*/])\s*(\d+(?:\.\d+)?)\b/g,
299
+ } as const;
300
+
301
+ // =============================================================================
302
+ // CONFIDENCE PATTERNS
303
+ // =============================================================================
304
+
305
+ export const CONFIDENCE_POSITIVE: WeightedSignal[] = [
306
+ // Very high confidence (0.9+)
307
+ { pattern: /^[\d\s+\-*/().]+$/, weight: 0.95, name: "pure_arithmetic" },
308
+ { pattern: /what\s+is\s+\d+\s*[+\-*/]\s*\d+/i, weight: 0.95, name: "explicit_arithmetic" },
309
+ { pattern: /calculate\s+\d+/i, weight: 0.9, name: "calculate_number" },
310
+ { pattern: /\d+!/i, weight: 0.9, name: "factorial" },
311
+ { pattern: /fibonacci\s*\d+|(\d+)(?:st|nd|rd|th)\s*fibonacci/i, weight: 0.9, name: "fibonacci" },
312
+
313
+ // High confidence (0.7-0.9)
314
+ { pattern: /sqrt|square\s*root|\u221A/i, weight: 0.85, name: "sqrt" },
315
+ { pattern: /log[\u2081\u2080]?\s*\(?\d/i, weight: 0.85, name: "logarithm" },
316
+ { pattern: /\d+\s*\^\s*\d+|\d+\s*\*\*\s*\d+/i, weight: 0.85, name: "power" },
317
+ { pattern: /\d+\s*%\s*of\s*\d+/i, weight: 0.85, name: "percentage" },
318
+ { pattern: /gcd|lcm/i, weight: 0.85, name: "gcd_lcm" },
319
+ { pattern: /\d+\s*mod(ulo)?\s*\d+/i, weight: 0.85, name: "modulo" },
320
+ { pattern: /is\s+\d+\s+(?:a\s+)?prime/i, weight: 0.85, name: "primality" },
321
+ { pattern: /legs?\s+\d+\s+and\s+\d+.*hypoten/i, weight: 0.85, name: "pythagorean" },
322
+ { pattern: /x[\u00B22]\s*[+-]\s*\d+x\s*[+-]\s*\d+\s*=\s*0/i, weight: 0.85, name: "quadratic" },
323
+ { pattern: /trailing\s+zeros?\s+.*\d+[!]/i, weight: 0.85, name: "trailing_zeros" },
324
+ { pattern: /last\s+digit.*\^\d+|\d+\s*\^\s*\d+\s*mod\s*10/i, weight: 0.85, name: "last_digit" },
325
+ // Calculus
326
+ { pattern: /derivative.*x\^?\d|d\/dx/i, weight: 0.85, name: "derivative" },
327
+ { pattern: /integral.*from.*to|integrate.*from/i, weight: 0.85, name: "definite_integral" },
328
+ // Combinatorics
329
+ { pattern: /\d+\s*choose\s*\d+/i, weight: 0.9, name: "combinations" },
330
+ { pattern: /\d+\s*[CP]\s*\d+/i, weight: 0.85, name: "perm_comb" },
331
+ // Matrix
332
+ { pattern: /determinant.*\[\[/i, weight: 0.85, name: "determinant" },
333
+ // Compound interest
334
+ { pattern: /compound\s+interest.*\d+\s*%.*\d+\s*year/i, weight: 0.85, name: "compound_interest" },
335
+ // Math facts (rationality)
336
+ { pattern: /rational\s+or\s+irrational/i, weight: 0.95, name: "rationality" },
337
+ {
338
+ pattern: /is\s+(?:sqrt|√|pi|e|phi)\b.*\b(?:rational|irrational)/i,
339
+ weight: 0.95,
340
+ name: "known_rationality",
341
+ },
342
+
343
+ // Logic patterns (modus ponens, modus tollens, syllogism, XOR)
344
+ {
345
+ pattern: /if\s+.+?,?\s+(?:then\s+)?.+?\.\s+.+?\.\s+(?:is\s+)?/i,
346
+ weight: 0.9,
347
+ name: "conditional_logic",
348
+ },
349
+ {
350
+ pattern: /all\s+\w+\s+are\s+\w+.*all\s+\w+\s+are\s+\w+.*valid/i,
351
+ weight: 0.95,
352
+ name: "syllogism",
353
+ },
354
+ { pattern: /exclusive.*both|both.*exclusive/i, weight: 0.9, name: "xor_violation" },
355
+ { pattern: /yes\s+or\s+no.*\?/i, weight: 0.8, name: "yes_no_question" },
356
+
357
+ // Probability patterns (independent events, gambler's fallacy)
358
+ {
359
+ pattern: /fair\s+coin.*(?:probability|chance)/i,
360
+ weight: 0.95,
361
+ name: "fair_coin_prob",
362
+ },
363
+ {
364
+ pattern: /independent.*(?:probability|chance)/i,
365
+ weight: 0.9,
366
+ name: "independent_event",
367
+ },
368
+ {
369
+ pattern: /in\s+a\s+row.*(?:probability|chance|what['']?s)/i,
370
+ weight: 0.85,
371
+ name: "streak_probability",
372
+ },
373
+ {
374
+ pattern: /(?:heads|tails)\s*\d+\s*(?:times?)?.*(?:next|probability)/i,
375
+ weight: 0.9,
376
+ name: "coin_streak",
377
+ },
378
+
379
+ // Medium confidence (0.5-0.7)
380
+ { pattern: /twice\s+\d+/i, weight: 0.75, name: "twice_number" },
381
+ { pattern: /half\s+(?:of\s+)?\d+/i, weight: 0.75, name: "half_number" },
382
+ { pattern: /\d+\s+(?:plus|minus|times)\s+\d+/i, weight: 0.75, name: "word_operation" },
383
+ { pattern: /sum\s+of\s+\d+\s+and\s+\d+/i, weight: 0.75, name: "sum_of" },
384
+ { pattern: /average\s+of.*\d+/i, weight: 0.7, name: "average" },
385
+ { pattern: /\d+\s+(?:more|less)\s+than\s+\d+/i, weight: 0.7, name: "more_less" },
386
+
387
+ // Lower confidence (multi-step word problems)
388
+ { pattern: /[A-Z][a-z]+\s+has\s+twice.*[A-Z][a-z]+/i, weight: 0.6, name: "entity_twice" },
389
+ { pattern: /[A-Z][a-z]+\s+has\s+\d+\s+more\s+than/i, weight: 0.6, name: "entity_more" },
390
+ { pattern: /how\s+many\s+does\s+[A-Z][a-z]+\s+have/i, weight: 0.5, name: "entity_question" },
391
+ { pattern: /total|altogether|combined/i, weight: 0.5, name: "total_question" },
392
+
393
+ // Generic signals (low weight)
394
+ { pattern: /calculate/i, weight: 0.4, name: "calculate" },
395
+ { pattern: /compute/i, weight: 0.4, name: "compute" },
396
+ { pattern: /what\s+is/i, weight: 0.3, name: "what_is" },
397
+ { pattern: /\d+\s*[+\-*/]\s*\d+/, weight: 0.5, name: "has_operation" },
398
+ ];
399
+
400
+ export const CONFIDENCE_NEGATIVE: NegativeSignal[] = [
401
+ { pattern: /prove/i, penalty: 0.8, name: "prove" },
402
+ { pattern: /why/i, penalty: 0.7, name: "why" },
403
+ { pattern: /explain/i, penalty: 0.7, name: "explain" },
404
+ { pattern: /derive/i, penalty: 0.6, name: "derive" },
405
+ { pattern: /show\s+(that|how|why)/i, penalty: 0.6, name: "show" },
406
+ { pattern: /compare/i, penalty: 0.5, name: "compare" },
407
+ { pattern: /what\s+is\s+the\s+best/i, penalty: 0.5, name: "best" },
408
+ { pattern: /should/i, penalty: 0.4, name: "should" },
409
+ // Removed: rational/irrational penalty - now handled by math facts solver
410
+ { pattern: /true\s+or\s+false/i, penalty: 0.8, name: "boolean" },
411
+ { pattern: /infinite.*(?!sum|series)/i, penalty: 0.5, name: "infinite_general" },
412
+ ];
413
+
414
+ // =============================================================================
415
+ // LIKELY COMPUTABLE PATTERNS
416
+ // =============================================================================
417
+
418
+ export const LIKELY_COMPUTABLE_POSITIVE = [
419
+ /what\s+is\s+\d/i,
420
+ /what\s+is\s+x[\u00B22^]/i,
421
+ /calculate/i,
422
+ /compute/i,
423
+ /\d+\s*[+\-*/]\s*\d+/,
424
+ /fibonacci/i,
425
+ /factorial|\d+!/,
426
+ /sqrt|square\s*root|\u221A/i,
427
+ /log[\u2081\u2080]?\s*\(?/i,
428
+ /\^|to\s+the\s+power/i,
429
+ /%\s*of\s*\d/i,
430
+ /hypoten/i,
431
+ /quadratic/i,
432
+ /gcd|lcm/i,
433
+ /mod(ulo)?/i,
434
+ /=\s*0.*root/i,
435
+ /larger\s+root|smaller\s+root/i,
436
+ /legs?\s+\d+\s+and\s+\d+/i,
437
+ /is\s+\d+\s+(?:a\s+)?prime/i,
438
+ /twice\s+(?:as\s+)?/i,
439
+ /half\s+(?:of\s+)?/i,
440
+ /double\s+(?:of\s+)?/i,
441
+ /triple\s+(?:of\s+)?/i,
442
+ /sum\s+of\s+\d+\s+and\s+\d+/i,
443
+ /difference\s+(?:between|of)/i,
444
+ /product\s+of\s+\d+\s+and\s+\d+/i,
445
+ /quotient\s+of/i,
446
+ /\d+\s+(?:plus|minus|times)\s+\d+/i,
447
+ /\d+\s+(?:divided\s+by|multiplied\s+by)\s+\d+/i,
448
+ /\d+\s+(?:more|less)\s+than\s+\d+/i,
449
+ /\d+\s+squared|\d+\s+cubed/i,
450
+ /average\s+of/i,
451
+ /third\s+(?:of\s+)?/i,
452
+ /quarter\s+(?:of\s+)?/i,
453
+ /trailing\s+zeros?/i,
454
+ /infinite\s+(?:series|sum)|geometric\s+series/i,
455
+ /last\s+digit/i,
456
+ /derivative|d\/dx|differentiate/i,
457
+ /integral|integrate|\u222B/i,
458
+ /\d+\s*choose\s*\d+/i,
459
+ /combination|permutation/i,
460
+ /ways\s+to\s+choose/i,
461
+ /determinant|det\s*\(/i,
462
+ /compound\s+interest/i,
463
+ // Math facts (rationality questions)
464
+ /rational\s+or\s+irrational/i,
465
+ /is\s+(?:sqrt|√|pi|e|phi)\b.*\b(?:rational|irrational)/i,
466
+ // Logic patterns (modus ponens, modus tollens, syllogism, XOR)
467
+ /if\s+[^,]+,\s*[^.]+\.\s*[^.]+\.\s*is\s+/i, // If X, Y. Z. Is ...?
468
+ /all\s+\w+\s+are\s+\w+.*valid/i,
469
+ /exclusive.*both.*violated/i,
470
+ // Probability patterns (independent events, gambler's fallacy, birthday paradox)
471
+ /fair\s+coin.*(?:probability|chance)/i,
472
+ /independent.*(?:probability|chance)/i,
473
+ /in\s+a\s+row.*(?:probability|chance|what['']?s)/i,
474
+ /birthday.*share|share.*birthday/i,
475
+ /\d+\s*(?:people|persons?).*birthday/i,
476
+ ] as const;
477
+
478
+ export const LIKELY_COMPUTABLE_NEGATIVE = [
479
+ /prove/i,
480
+ /why/i,
481
+ /explain/i,
482
+ /derive/i,
483
+ /show\s+(that|how|why)/i,
484
+ /what\s+is\s+the\s+best/i,
485
+ /should/i,
486
+ /compare/i,
487
+ // Removed: /rational\s+or\s+irrational/i - now handled by math facts solver
488
+ /true\s+or\s+false/i,
489
+ ] as const;
490
+
491
+ // =============================================================================
492
+ // SPAN PATTERNS - For multi-expression extraction (combined regex approach)
493
+ // These patterns can identify computable spans within arbitrary text.
494
+ // Used by extractAndCompute() for O(n) text scanning.
495
+ // =============================================================================
496
+
497
+ export interface SpanPattern {
498
+ /** Regex to match the span (should capture the full computable expression) */
499
+ regex: RegExp;
500
+ /** Name for debugging/tracking */
501
+ name: string;
502
+ }
503
+
504
+ /**
505
+ * Patterns suitable for span extraction from text.
506
+ * Each pattern should match a complete, self-contained computable expression.
507
+ * Excludes patterns that require question context (e.g., "is X prime?")
508
+ */
509
+ export const SPAN_PATTERNS: SpanPattern[] = [
510
+ // Tier 1: Ultra-fast
511
+ { regex: /(\d+(?:\.\d+)?)\s*%\s*of\s*(\d+(?:\.\d+)?)/i, name: "percentage" },
512
+ { regex: /(\d+)!/i, name: "factorial_symbol" },
513
+ { regex: /factorial\s*(?:of\s*)?(\d+)/i, name: "factorial_word" },
514
+
515
+ // Tier 2: Fast
516
+ { regex: /sqrt\s*\(\s*(\d+(?:\.\d+)?)\s*\)/i, name: "sqrt_parens" },
517
+ { regex: /√(\d+(?:\.\d+)?)/i, name: "sqrt_symbol" },
518
+ { regex: /square\s+root\s+of\s+(\d+(?:\.\d+)?)/i, name: "sqrt_words" },
519
+ { regex: /(\d+(?:\.\d+)?)\s*\^\s*(\d+(?:\.\d+)?)/i, name: "power_caret" },
520
+ { regex: /(\d+(?:\.\d+)?)\s*\*\*\s*(\d+(?:\.\d+)?)/i, name: "power_stars" },
521
+ { regex: /gcd\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)/i, name: "gcd" },
522
+ { regex: /lcm\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)/i, name: "lcm" },
523
+
524
+ // Tier 3: Medium
525
+ { regex: /(\d+)\s*choose\s*(\d+)/i, name: "combinations" },
526
+ { regex: /(\d+)\s*C\s*(\d+)/i, name: "combinations_c" },
527
+ { regex: /(\d+)\s*P\s*(\d+)/i, name: "permutations" },
528
+ { regex: /log\s*\(\s*(\d+(?:\.\d+)?)\s*\)/i, name: "log" },
529
+ { regex: /ln\s*\(\s*(\d+(?:\.\d+)?)\s*\)/i, name: "ln" },
530
+
531
+ // Tier 4: Expensive
532
+ { regex: /det\s*\(\s*\[\[[\d,\s\-[\]]+\]\]\s*\)/i, name: "determinant" },
533
+ { regex: /determinant\s+of\s+\[\[[\d,\s\-[\]]+\]\]/i, name: "determinant_of" },
534
+
535
+ // Calculus
536
+ {
537
+ regex: /derivative\s+of\s+([x\d\s+\-*^]+)\s+at\s+x\s*=\s*(\d+)/i,
538
+ name: "derivative_at",
539
+ },
540
+ {
541
+ regex: /d\/dx\s*\(\s*([x\d\s+\-*^]+)\s*\)\s*at\s+x\s*=\s*(\d+)/i,
542
+ name: "derivative_dx_at",
543
+ },
544
+ {
545
+ regex: /integrate\s+([x\d\s+\-*^]+)\s+from\s+(-?\d+)\s+to\s+(-?\d+)/i,
546
+ name: "integral_from_to",
547
+ },
548
+ {
549
+ regex: /integral\s+of\s+([x\d\s+\-*^]+)\s+from\s+(-?\d+)\s+to\s+(-?\d+)/i,
550
+ name: "integral_of_from_to",
551
+ },
552
+ ];
553
+
554
+ /**
555
+ * Build combined regex for O(n) scanning.
556
+ * Uses alternation which V8 optimizes into an efficient automaton.
557
+ */
558
+ export function buildCombinedSpanRegex(): RegExp {
559
+ const combined = SPAN_PATTERNS.map((p) => `(?:${p.regex.source})`).join("|");
560
+ return new RegExp(combined, "gi");
561
+ }
562
+
563
+ /** Pre-built combined regex for span extraction */
564
+ export const COMBINED_SPAN_REGEX = buildCombinedSpanRegex();