| 137 | k: () => boolean |
| 138 | ): boolean { |
| 139 | tickDeadline(); // every backtracking step funnels through here |
| 140 | switch (pat.kind) { |
| 141 | case 'var': |
| 142 | // The integration variable, compared by name AND binding — see |
| 143 | // `sameBoundName` for why the binding half is answerable now. |
| 144 | return sameBoundName(expr, x) && k(); |
| 145 | case 'const': |
| 146 | return pat.value.isSame(expr) && k(); |
| 147 | case 'slot': |
| 148 | case 'optslot': |
| 149 | return bindIn(env, pat.name, expr, k); |
| 150 | case 'node': { |
| 151 | if (expr.operator === pat.op && expr.ops) { |
| 152 | if (pat.ac) { |
| 153 | if (mAC(pat, expr, x, env, k)) return true; |
| 154 | } else if (expr.ops.length === pat.ops.length) { |
| 155 | if (mSeq(pat.ops, expr.ops, x, env, k)) return true; |
| 156 | } |
| 157 | } |
| 158 | // Collapse: default away the optional operands and match the single |
| 159 | // remaining operand against the whole expression. |
| 160 | return mCollapse(pat, expr, x, env, k); |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | // Power(base, m_.) vs a non-Power expression matches base with m → 1; |
| 166 | // Add/Multiply nodes with exactly one non-optional operand match a |