* * Print/check boxed expression variants in a similar way to function 'checkJson', but only prints * for variants 'boxed' (non-canonical), 'canonical', but also 'canonForms' (i.e. partial-canonical). * * Only prints 'canonical' if this differs from 'boxed'. * * <!-- * **NOTE**: * -Unlike 'c
( inExpr: string | ExpressionInput, forms: CanonicalForm[], engine?: ComputeEngine )
| 1243 | }); |
| 1244 | |
| 1245 | test('function({ structural: true }) keeps Power instead of folding to Root', () => { |
| 1246 | const ce = new ComputeEngine(); |
| 1247 | const p = ce.function('Power', [ce.symbol('x'), ce.number([1, 3])], { |
| 1248 | structural: true, |
| 1249 | }); |
| 1250 | expect((p as any).isStructural).toBe(true); |
| 1251 | expect(p.json).toEqual(['Power', 'x', ['Rational', 1, 3]]); |
| 1252 | }); |
| 1253 | |
| 1254 | test('explicit `form` takes precedence over `canonical`', () => { |
| 1255 | const ce = new ComputeEngine(); |
| 1256 | expect( |
| 1257 | ce.expr(['Add', 'a', 1], { |
| 1258 | form: 'canonical', |
| 1259 | canonical: false, |
| 1260 | } as any).isCanonical |
| 1261 | ).toBe(true); |
| 1262 | }); |
| 1263 | |
| 1264 | test('assume() canonicalizes a non-canonical predicate', () => { |
| 1265 | // The RHS `Subtract(3, 3)` must fold to `0` for the bound-extraction to |
| 1266 | // record `x > 0`, even though the caller boxed the predicate |
| 1267 | // non-canonically. Without canonicalization in assume() the raw |
| 1268 | // `Greater(x, Subtract(3, 3))` does not yield a numeric bound. |
| 1269 | const ce = new ComputeEngine(); |
| 1270 | const pred = ce.expr(['Greater', 'x', ['Subtract', 3, 3]], { |
| 1271 | canonical: false, |
| 1272 | }); |
| 1273 | expect(pred.isCanonical).toBe(false); |
| 1274 | expect(ce.assume(pred)).toBe('ok'); |
| 1275 | expect(ce.expr('x').isPositive).toBe(true); |
| 1276 | }); |
| 1277 | }); |
| 1278 | |
| 1279 | describe('structural-aware subs', () => { |
| 1280 | // Regression: `.subs()` on a STRUCTURAL receiver used to request a CANONICAL |
| 1281 | // rebuild, silently erasing the parse vocabulary that structural form exists |
| 1282 | // to preserve (`Subtract`, `Divide`, `InvisibleOperator`, `Delimiter`, |
| 1283 | // operand order) and folding its exact literals. The receiver's form is now |
| 1284 | // preserved three ways: canonical → canonical, structural → structural, |
| 1285 | // raw → raw. An EXPLICIT `canonical` option keeps its former meaning. |
| 1286 | const LATEX = '2(x+1) - \\frac{y}{3}'; |
| 1287 | |
| 1288 | test('a structural receiver stays structural, shape preserved', () => { |
| 1289 | const ce = new ComputeEngine(); |
| 1290 | const expr = ce.parse(LATEX, { form: 'structural' }); |
| 1291 | expect((expr as any).isStructural).toBe(true); |
| 1292 |
no test coverage detected