( ce: ComputeEngine, e: Entry, matchW: MathJSON, replaceW: MathJSON, guards: ReadonlyArray<GuardSpec> )
| 963 | * Box the candidate rule and require it to fire on a seeded instantiation |
| 964 | * satisfying the guards (symbolic seed first, numeric backtracking fallback). |
| 965 | */ |
| 966 | export function selfTest( |
| 967 | ce: ComputeEngine, |
| 968 | e: Entry, |
| 969 | matchW: MathJSON, |
| 970 | replaceW: MathJSON, |
| 971 | guards: ReadonlyArray<GuardSpec> |
| 972 | ): SelfTestResult { |
| 973 | // All boxing happens inside a per-entry scope where the wildcards carry |
| 974 | // their guard-implied types: strict validation of typed slots |
| 975 | // (Totient(_n), Fibonacci(_n), …) passes, and inferred wildcard types |
| 976 | // never leak across entries. |
| 977 | const seedTypes = variableSeedTypes(e, guards); |
| 978 | ce.pushScope(); |
| 979 | try { |
| 980 | for (const v of e.variables) { |
| 981 | try { |
| 982 | ce.declare('_' + v, seedTypes[v]); |
| 983 | } catch { |
| 984 | /* tolerate */ |
| 985 | } |
| 986 | } |
| 987 | return selfTestScoped(ce, e, matchW, replaceW, guards, seedTypes); |
| 988 | } finally { |
| 989 | ce.popScope(); |
| 990 | } |
| 991 | } |
| 992 | |
| 993 | function selfTestScoped( |
no test coverage detected