(
ce: ComputeEngine,
expr: null | undefined | NumericValue | ExpressionInput,
options?: {
canonical?: CanonicalOptions;
structural?: boolean;
scope?: Scope;
}
)
| 418 | // or 'de-number' some 'borderline invalid' boxed number-like expressions |
| 419 | // (!@note: this procedure is similarly repeated within the 'number' |
| 420 | // CanonicalForm, but the numberForm variant more simply applies to fully |
| 421 | // BoxedExprs., and during partial canonicalization only) |
| 422 | if (canonicalNumber) { |
| 423 | // |
| 424 | // Rational (as Divide) |
| 425 | // |
| 426 | if ((name === 'Divide' || name === 'Rational') && ops.length === 2) { |
| 427 | const n = asBigint(ops[0]); |
| 428 | if (n !== null) { |
| 429 | const d = asBigint(ops[1]); |
| 430 | if (d !== null) { |
| 431 | // Handle division by zero: 0/0 = NaN, a/0 = ~∞ |
| 432 | if (d === 0n) return n === 0n ? ce.NaN : ce.ComplexInfinity; |
| 433 | return ce.number([n, d], options); |
| 434 | } |
| 435 | } |
| 436 | name = 'Divide'; |
| 437 | } |
| 438 | |
| 439 | // |
| 440 | // Complex |
| 441 | // |
| 442 | if (name === 'Complex') { |
| 443 | if (ops.length === 1) { |
| 444 | // If single argument, assume it's imaginary |
| 445 | const op1 = ops[0]; |
| 446 | if (op1 instanceof _BoxedExpression && op1.isNumberLiteral) |
| 447 | return ce.number(ce.complex(0, op1.re), options); |
| 448 | |
| 449 | const im = machineValue(ops[0] as MathJsonExpression); |
| 450 | if (im !== null && im !== 0) |
| 451 | return ce.number(ce.complex(0, im), options); |
| 452 | |
| 453 | return ce.expr(op1).mul(ce.I); |
| 454 | } |
| 455 | if (ops.length === 2) { |
| 456 | // Box the real operand so a high-precision bignum literal (e.g. a |
| 457 | // 50-digit √2) is not truncated to a machine float. When the operand |
| 458 | // arrives as raw MathJSON (`{ num: '1.414…' }`), reading `machineValue` |
| 459 | // alone would silently discard the extra digits on re-boxing |
| 460 | // (`ce.expr(z.json)`). |
| 461 | const reOp = |
| 462 | ops[0] instanceof _BoxedExpression |
| 463 | ? ops[0] |
| 464 | : box(ce, ops[0], options); |
| 465 | const imOp = |
| 466 | ops[1] instanceof _BoxedExpression |
| 467 | ? ops[1] |
| 468 | : box(ce, ops[1], options); |
| 469 | |
| 470 | // Exact components (integers, rationals, radicals) reconstruct an |
| 471 | // EXACT complex value when the pair is representable (a Gaussian |
| 472 | // rational, or a pure-imaginary radical). This is what makes |
| 473 | // `ExactNumericValue.toJSON()` lossless: `['Complex', ['Rational',1,2], 3]` |
| 474 | // re-boxes to the exact `1/2 + 3i`, not a machine float. |
| 475 | { |
| 476 | const reC = exactRealComponent(reOp); |
| 477 | if (reC !== null) { |
no test coverage detected