( ce: ComputeEngine, e: Entry, guards: ReadonlyArray<GuardSpec>, closures: ReadonlyArray<(sub: Sub) => boolean>, seedTypes: Record<string, string> )
| 1280 | replace: ce.expr(replaceW as never) as never, |
| 1281 | id: 'fungrim:' + e.id, |
| 1282 | }, |
| 1283 | ], |
| 1284 | { canonical: true } |
| 1285 | ); |
| 1286 | const inst = ce.expr(substituteWildcards(matchW, dewild) as never); |
| 1287 | const expected = ce.expr(substituteWildcards(replaceW, dewild) as never); |
| 1288 | if (inst.isValid && expected.isValid) { |
| 1289 | const r = inst.replace(unconditioned); |
| 1290 | // Same comparison ladder as `fireTest` above: `isSame` compares |
| 1291 | // binder identity (a rule that transplants a lambda-bound occurrence |
| 1292 | // yields a name-identical but differently-bound result), and |
| 1293 | // `isEqual` is the cheap arithmetic tier — it answers `undefined`, |
| 1294 | // not `true`, for an identity in free variables. Without |
| 1295 | // `sameSyntactic` this branch rejects rewrites whose result is |
| 1296 | // structurally identical to the expectation (e.g. 50f72f, the Sinc |
| 1297 | // derivative). |
| 1298 | if ( |
| 1299 | r !== null && |
| 1300 | (r.isSame(expected) || |
| 1301 | sameSyntactic(r, expected) || |
| 1302 | r.isEqual(expected) === true) |
| 1303 | ) |
| 1304 | return { ok: true, sampleKind: 'symbolic' }; |
| 1305 | } |
| 1306 | } finally { |
| 1307 | ce.popScope(); |
| 1308 | } |
| 1309 | } |
| 1310 | |
| 1311 | return { ok: false, reason: 'no-fire', detail: symbolicDetail }; |
| 1312 | } |
| 1313 | |
| 1314 | function findNumericSeed( |
| 1315 | ce: ComputeEngine, |
| 1316 | e: Entry, |
| 1317 | guards: ReadonlyArray<GuardSpec>, |
| 1318 | closures: ReadonlyArray<(sub: Sub) => boolean>, |
| 1319 | seedTypes: Record<string, string> |
| 1320 | ): Record<string, MathJSON> | null { |
| 1321 | const vars = e.variables; |
| 1322 | const sub: Sub = {}; |
| 1323 | |
| 1324 | // Guards are checked at the depth where their last referenced variable is |
| 1325 | // assigned (massive pruning for multi-variable entries). |
| 1326 | const wcIndex = new Map<string, number>(vars.map((v, i) => ['_' + v, i])); |
| 1327 | const guardDepth = guards.map((g) => { |
| 1328 | let depth = -1; |
| 1329 | for (const w of guardWildcards(g)) depth = Math.max(depth, wcIndex.get(w) ?? vars.length - 1); |
| 1330 | return depth; |
| 1331 | }); |
| 1332 | |
| 1333 | // Variable-free guards must hold outright |
| 1334 | for (let i = 0; i < guards.length; i++) |
| 1335 | if (guardDepth[i] === -1 && !closures[i](sub)) return null; |
| 1336 | if (vars.length === 0) return {}; |
| 1337 | |
| 1338 | // A variable the entry APPLIES (`chi` in `chi(m)`) is typed `function` by |
| 1339 | // `variableTypes` in `load.ts`. Every candidate below is a number literal, |
no test coverage detected