Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1239,21 +1239,36 @@ and a number that passes every round is left open, since passing is not a proof
| `"(10^30 + 1) in PP".ToEntity().Evaled` | `1000000000000000000000000000001 in PP` | `False` |
| `"(10^30 + 57) in PP".ToEntity().Evaled` | `1000000000000000000000000000057 in PP` | `1000000000000000000000000000057 in PP` |

### An exact power too large to build is the decimal

**Not silent: what did not return now answers.** An exact number to a whole power was built exactly however large it
was. `46655^(10^20)` has 4.7e20 digits, and its evaluation neither returned nor stopped when cancelled. Nor did a
`Simplify` of anything holding it. Past sixteen million bits, about five million digits, a power is now evaluated as the
decimal, as the same power with a half added already was: `+oo` past the context's range, `-oo` for a negative base to
an odd power, and `0` below the range. An exact simplification leaves it as written, since the infinity is the
decimal's. Each of -1, i and -i keeps its exact power. No issue was filed; a check of compiled expressions against
evaluation found it.
### An exact power too large to build is left written

**Not silent: what did not return now answers, or stays written.** An exact number to a whole power was built exactly
however large it was. `46655^(10^20)` has 4.7e20 digits, and its evaluation neither returned nor stopped when cancelled.
Nor did a `Simplify` of anything holding it. Past sixteen million bits, about five million digits, such a power is now
left as written, by evaluation and by `Simplify` alike, and `EvalNumerical` refuses it with `CannotEvalException`. It
is finite and not zero, so it is never `+oo`, `-oo` or `0`. An expression holding it is settled where structure settles
it, as with `a - a` or `0 * a`, or where the exponents settle it, as with `2^m / 2^n`. A comparison is settled by the
signs, by the exponents for one positive base, or by the logarithms of the magnitudes where they differ by more than
their rounding. Anything else stays written. Each of -1, i and -i keeps its exact power. Measured on 2.6.0 with a
twenty-second limit. https://github.com/asc-community/AngouriMath/issues/1448

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"46655^(10^20)".EvalNumerical()` | does not return | `+oo` |
| `"(5/2)^(10^7)".EvalNumerical()` | does not return | `+oo` |
| `"2^(10^9)".ToEntity().Evaled` | does not return | `2 ^ 1000000000` |
| `"2^(-(10^9))".ToEntity().Evaled` | does not return | `2 ^ (-1000000000)` |
| `"46655^(10^20)".EvalNumerical()` | does not return | throws `CannotEvalException` |
| `"2^(10^10)".Simplify()` | does not return | `2 ^ 10000000000` |
| `"2^(10^9) - 2^(10^9)".Simplify()` | does not return | `0` |
| `"x - x + 2^(10^9) - 2^(10^9)".Simplify()` | does not return | `0` |
| `"2^(10^9) / 2^(10^9 - 1)".Simplify()` | does not return | `2` |
| `"2^(10^9) * 2^(-(10^9))".Simplify()` | does not return | `1` |
| `"0 * 2^(10^9)".ToEntity().Evaled` | does not return | `0` |
| `"2^(10^9) > 2^(10^9 - 1)".ToEntity().Evaled` | does not return | `True` |
| `"2^(10^9) = 2^(10^9 - 1)".ToEntity().Evaled` | does not return | `False` |
| `"3^(10^9) > 2^(10^9)".ToEntity().Evaled` | does not return | `True` |
| `"2^(10^9) > 5".ToEntity().Evaled` | does not return | `True` |
| `"2^(-(10^9)) > 0".Simplify()` | does not return | `True` |
| `"2^(10^9) = 4^(5 * 10^8)".ToEntity().Evaled` | does not return | `2 ^ 1000000000 = 4 ^ 500000000` |
| `"forall n in ZZ* : 3^(n + 1) divides 2^(3^n) + 1".ToEntity().Evaled` | does not return | as written |

### The inverse of a symbolic matrix carries no condition of a pivot

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,8 +627,10 @@ static Complex BinaryIntPow(Complex num, EInteger val)
}
// An exact power past MaxExactPowerBits is not built: 46655^(10^20) would have 4.7e20
// digits, and the multiplications for it never returned or stopped when cancelled, so
// a simplification of the expression did not either. It is the decimal, as the same
// power with a half added is: +oo past the context's range, 0 below it.
// a simplification of the expression did not either. Evaluating a power node leaves it
// written before this is reached (https://github.com/asc-community/AngouriMath/issues/1448);
// a caller of Pow itself gets the decimal, as the same power with a half added does:
// +oo past the context's range, 0 below it.
if (@base.IsFinite && pow is { } && IsTooLargeToBuild(@base, pow))
{
var largeContext = MathS.Settings.DecimalPrecisionContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,17 @@ private bool IsEvenRoot
=> Exponent is Rational and not Integer and Rational ratio
&& ratio.ERational.Denominator.Remainder(2).IsZero;

/// <summary>
/// Whether an exact <paramref name="base"/> to the rational <paramref name="exponent"/> is past the size an
/// exact power is built to, a fraction <c>p / q</c> read as the whole power just above <c>|p / q|</c>: a
/// <c>q</c>-th root makes the power no longer than that, and one that is not exact is a decimal of the
/// same magnitude. A whole exponent allocates nothing here, being asked of every exact power.
/// </summary>
internal static bool IsTooLargeToBuild(Complex @base, Rational exponent)
=> exponent is Integer { EInteger: var whole }
? Number.IsTooLargeToBuild(@base, whole)
: Number.IsTooLargeToBuild(@base, exponent.ERational.Numerator.Abs().Divide(exponent.ERational.Denominator).Add(PeterO.Numbers.EInteger.One));

private static bool TryPower(Matrix m, int exp, out Entity res)
{
// Only a square matrix has powers; Pow throws for any other. And only one with
Expand Down Expand Up @@ -461,6 +472,12 @@ protected override Entity InnerSimplify(bool isExact) =>
? 0
: MathS.NaN
: new Providedf(0, Re(x) > 0),
// An exact power too large to build stays a power. It is finite and not zero, and as
// the decimal it was +oo or 0, which then fed exact arithmetic: 2^(10^9) - 2^(10^9)
// was NaN, 0 * 2^(10^9) was NaN and 2^(10^9) > 2^(10^9 - 1) was False. Left written,
// what can be said of it is said by structure or by its exponent, and the rest is
// left undecided. https://github.com/asc-community/AngouriMath/issues/1448
(Complex n1, Rational n2) when IsTooLargeToBuild(n1, n2) => null,
(Complex n1, Complex n2) when !isExact => Number.Pow(n1, n2), // returns 1 for 0^0 and 0 for 0^(negative real part), we need to handle these cases above
(Integer(1), { DomainCondition: var condition }) => Integer.One.Provided(condition),
(var n1, Integer(-1)) => (1 / n1).InnerSimplified(isExact),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ protected override Entity InnerSimplify(bool isExact)
// A size that is not a number is compared as a size, never by the difference
// of two expressions: card(ZZ) - card(QQ) is not zero, and the two are equal.
: AngouriMath.Core.Sets.Cardinality.IsSize(left) || AngouriMath.Core.Sets.Cardinality.IsSize(right) ? AngouriMath.Core.Sets.Cardinality.Compare(left, right, static sign => sign == 0, isExact)
// An exact power too large to build is not a constant, its value being left written, and is
// told apart from a number or another such power where its sign, exponent or magnitude decides it.
// https://github.com/asc-community/AngouriMath/issues/1448
: OrderOfUnbuiltPowers(left, right) is { } order ? order == 0
: left.IsConstant && right.IsConstant ? (HasAnUnreadSize(left) || HasAnUnreadSize(right) ? null : ConstantsAreEqual(left, right))
: left is Set setLeft && right is Set setRight ? SetsAreEqual(setLeft, setRight, isExact)
: null,
Expand All @@ -347,6 +351,80 @@ protected override Entity InnerSimplify(bool isExact)
_ => null,
};

/// <summary>
/// An exact power too large to build, which evaluation leaves written: an exact base that is not zero to a
/// rational exponent. <see langword="null"/> for anything else.
/// https://github.com/asc-community/AngouriMath/issues/1448
/// </summary>
private static (Number.Rational Base, Number.Rational Exponent)? AsAnUnbuiltPower(Entity expr)
=> expr is Powf(Number.Rational { IsZero: false } @base, Number.Rational exponent) && Powf.IsTooLargeToBuild(@base, exponent)
? (@base, exponent) : null;

/// <summary>
/// The sign of a real power of an exact real base that is not zero, where its exponent decides it: positive
/// for a positive base, and for a negative one by the parity of a whole exponent.
/// </summary>
private static int? SignOfAnUnbuiltPower((Number.Rational Base, Number.Rational Exponent) power)
=> power.Base.ERational.Sign > 0 ? 1
: power.Exponent is Number.Integer { EInteger: var whole } ? (whole.IsEven ? 1 : -1)
: null;

/// <summary>
/// The sign of <c>left - right</c> where either side is an exact power too large to build and the other is a
/// real number or another such power, decided without building either: two powers of one positive base by
/// their exponents, exactly, and otherwise by the signs and then by the logarithms of the magnitudes, where
/// those differ by more than the error of computing them. <see langword="null"/> where nothing decides it,
/// which leaves the comparison written; in particular, two magnitudes that may be equal are never told
/// apart. As the decimals, these powers were <c>+oo</c> and <c>0</c>, and <c>2^(10^9) &gt; 2^(10^9 - 1)</c>
/// was False. https://github.com/asc-community/AngouriMath/issues/1448
/// </summary>
private static int? OrderOfUnbuiltPowers(Entity left, Entity right)
{
var (powerLeft, powerRight) = (AsAnUnbuiltPower(left), AsAnUnbuiltPower(right));
if (powerLeft is null && powerRight is null)
return null;
if (powerLeft is { } l && powerRight is { } r && l.Base.ERational.Equals(r.Base.ERational) && l.Base.ERational.Sign > 0)
{
var byExponent = l.Exponent.ERational.CompareTo(r.Exponent.ERational);
return l.Base.ERational.CompareTo(ERational.One) > 0 ? byExponent : -byExponent;
}
if (SignAndLogarithm(left, powerLeft) is not var (signLeft, logLeft) || SignAndLogarithm(right, powerRight) is not var (signRight, logRight))
return null;
// A power is not zero, so a side of the other sign, or zero, is decided by the signs alone.
if (signLeft != signRight || logLeft is null || logRight is null)
return signLeft.CompareTo(signRight);
// Each logarithm is within a relative 1e-99 of its value: a decimal side is rounded to a hundred digits,
// and the rest is computed to OrderContext's. So a difference past 1e-90 of their size is not rounding.
var difference = logLeft.Subtract(logRight, OrderContext);
var bound = logLeft.Abs().Add(logRight.Abs()).Add(EDecimal.One).Multiply(OrderTolerance);
if (difference.Abs().CompareTo(bound) <= 0)
return null;
return signLeft * difference.Sign;
}

/// <summary>
/// The sign of an exact power too large to build, or of a finite real number, and the natural logarithm of
/// its magnitude, <see langword="null"/> for zero. <see langword="null"/> for anything else, and for a power
/// whose sign its exponent does not settle.
/// </summary>
private static (int Sign, EDecimal? Logarithm)? SignAndLogarithm(Entity side, (Number.Rational Base, Number.Rational Exponent)? power)
{
if (power is { } p)
return SignOfAnUnbuiltPower(p) is { } sign
? (sign, p.Exponent.ERational.ToEDecimal(OrderContext)
.Multiply(p.Base.ERational.Abs().ToEDecimal(OrderContext).Log(OrderContext), OrderContext))
: null;
if (side is Number number && AsAReal(number) is { EDecimal: { IsFinite: true } real })
return real.IsZero ? (0, null) : (real.Sign, real.Abs().Log(OrderContext));
return null;
}

/// <summary>The working precision of <see cref="SignAndLogarithm"/>: twenty digits past the decimals'.</summary>
[Core.ConstantField] private static readonly EContext OrderContext = EContext.ForPrecision(120);

/// <summary>The relative difference of two logarithms below which <see cref="OrderOfUnbuiltPowers"/> leaves them undecided.</summary>
[Core.ConstantField] private static readonly EDecimal OrderTolerance = EDecimal.FromString("1e-90");

partial record Greaterf
{
// Inequality comparisons are only defined for real numbers.
Expand All @@ -361,6 +439,7 @@ protected override Entity InnerSimplify(bool isExact)
(Number numLeft, Number numRight) when AsAReal(numLeft) is { } reLeft && AsAReal(numRight) is { } reRight => reLeft > reRight,
(Number numLeft, Number numRight) => MathS.NaN,
_ when AngouriMath.Core.Sets.Cardinality.IsSize(a) || AngouriMath.Core.Sets.Cardinality.IsSize(b) => AngouriMath.Core.Sets.Cardinality.Compare(a, b, static sign => sign > 0, isExact),
_ when OrderOfUnbuiltPowers(a, b) is { } sign => sign > 0,
_ => null
},
(@this, a, b) => ((Greaterf)@this).New(a, b), isExact);
Expand All @@ -381,6 +460,7 @@ protected override Entity InnerSimplify(bool isExact)
(Number numLeft, Number numRight) when AsAReal(numLeft) is { } reLeft && AsAReal(numRight) is { } reRight => reLeft >= reRight,
(Number numLeft, Number numRight) => MathS.NaN,
_ when AngouriMath.Core.Sets.Cardinality.IsSize(a) || AngouriMath.Core.Sets.Cardinality.IsSize(b) => AngouriMath.Core.Sets.Cardinality.Compare(a, b, static sign => sign >= 0, isExact),
_ when OrderOfUnbuiltPowers(a, b) is { } sign => sign >= 0,
_ => null
},
(@this, a, b) => ((GreaterOrEqualf)@this).New(a, b), isExact);
Expand All @@ -401,6 +481,7 @@ protected override Entity InnerSimplify(bool isExact)
(Number numLeft, Number numRight) when AsAReal(numLeft) is { } reLeft && AsAReal(numRight) is { } reRight => reLeft < reRight,
(Number numLeft, Number numRight) => MathS.NaN,
_ when AngouriMath.Core.Sets.Cardinality.IsSize(a) || AngouriMath.Core.Sets.Cardinality.IsSize(b) => AngouriMath.Core.Sets.Cardinality.Compare(a, b, static sign => sign < 0, isExact),
_ when OrderOfUnbuiltPowers(a, b) is { } sign => sign < 0,
_ => null
},
(@this, a, b) => ((Lessf)@this).New(a, b), isExact);
Expand All @@ -420,6 +501,7 @@ protected override Entity InnerSimplify(bool isExact)
(Number numLeft, Number numRight) when AsAReal(numLeft) is { } reLeft && AsAReal(numRight) is { } reRight => reLeft <= reRight,
(Number numLeft, Number numRight) => MathS.NaN,
_ when AngouriMath.Core.Sets.Cardinality.IsSize(a) || AngouriMath.Core.Sets.Cardinality.IsSize(b) => AngouriMath.Core.Sets.Cardinality.Compare(a, b, static sign => sign <= 0, isExact),
_ when OrderOfUnbuiltPowers(a, b) is { } sign => sign <= 0,
_ => null
},
(@this, a, b) => ((LessOrEqualf)@this).New(a, b), isExact);
Expand Down
Loading
Loading