(x: Expression | undefined)
| 165 | for (const e of exprs) |
| 166 | for (const n of e.unknowns) if (n !== '_') names.add(n); |
| 167 | if (names.size === 1) return names.values().next().value; |
| 168 | if (names.size > 1 && names.has('x')) return 'x'; |
| 169 | return undefined; |
| 170 | } |
| 171 | |
| 172 | /** |
| 173 | * Operator heads whose evaluation is a pure expression-transformation step: |
| 174 | * the result is an expression in the same free variables — no symbol-value |
| 175 | * substitution, no relational collapse. |
| 176 | * |
| 177 | * A structural algorithm that *holds* its expression operand (`Solve`, |
| 178 | * `Integrate`, `Limit`, …) should reduce such a head before running: |
| 179 | * `Solve(Simplify(eq), x)` means "simplify, then solve", not "solve an |
| 180 | * expression whose operator is `Simplify`" (which finds no roots). This is |
no test coverage detected