( ce: ComputeEngine, name: string, def: SequenceDefinition )
| 270 | * |
| 271 | * The handler evaluates expressions like `F_{10}` or `P_{5,2}` by: |
| 272 | * 1. Checking base cases first (with pattern matching for multi-index) |
| 273 | * 2. Looking up memoized values |
| 274 | * 3. Recursively evaluating the recurrence relation |
| 275 | * |
| 276 | * Supports both single-index and multi-index sequences: |
| 277 | * - Single-index: `F_{10}` with subscript as a number |
| 278 | * - Multi-index: `P_{5,2}` with subscript as `Sequence(5, 2)` |
| 279 | */ |
| 280 | export function createSequenceHandler( |
| 281 | ce: ComputeEngine, |
| 282 | name: string, |
| 283 | def: SequenceDefinition |
| 284 | ): ( |
| 285 | subscript: Expression, |
| 286 | options: { engine: ComputeEngine; numericApproximation?: boolean } |
| 287 | ) => Expression | undefined { |
| 288 | // Determine if this is a multi-index sequence |
| 289 | const isMultiIndex = def.variables !== undefined && def.variables.length > 1; |
| 290 | const variables = def.variables ?? [def.variable ?? 'n']; |
| 291 | const variable = variables[0]; // For single-index backward compatibility |
| 292 | |
| 293 | const memoize = def.memoize ?? true; |
| 294 | // Use string keys for multi-index, number keys for single-index |
| 295 | const memo = memoize ? new Map<number | string, Expression>() : null; |
| 296 | const domain = def.domain ?? {}; |
| 297 | |
| 298 | // Store recurrence source for lazy parsing |
| 299 | const recurrenceSource = def.recurrence; |
| 300 | let recurrence: Expression | null = null; |
| 301 | |
| 302 | // Parse and box constraint expression |
| 303 | let constraintsExpr: Expression | null = null; |
| 304 | if (def.constraints) { |
| 305 | constraintsExpr = |
| 306 | typeof def.constraints === 'string' |
| 307 | ? ce.parse(def.constraints)! |
| 308 | : def.constraints; |
| 309 | } |
| 310 | |
| 311 | // Box base cases |
| 312 | const base = new Map<number | string, Expression>(); |
| 313 | for (const [k, v] of Object.entries(def.base)) { |
| 314 | const key = isMultiIndex ? String(k) : Number(k); |
| 315 | base.set(key, typeof v === 'number' ? ce.number(v) : v); |
| 316 | } |
| 317 | |
| 318 | // For multi-index: prepare sorted base cases for pattern matching |
| 319 | const preparedBaseCases = isMultiIndex ? prepareBaseCases(base) : null; |
| 320 | |
| 321 | // Register sequence for introspection (SUB-7) |
| 322 | registerSequence(ce, { |
| 323 | name, |
| 324 | variable: isMultiIndex ? undefined : variable, |
| 325 | variables: isMultiIndex ? variables : undefined, |
| 326 | isMultiIndex, |
| 327 | base, |
| 328 | memoize, |
| 329 | memo, |
no test coverage detected