diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 6faf6c53c..071c74aa0 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -161,6 +161,22 @@ the division's; with it on, the rounding is the setting's own and stays
| `"(-60.5)!"` at 30 digits, downcasting off | `5.86118478907722232671451280188E-81` | `2.93059239453861116335725639905E-81` |
| `"abs(75 + 316.22776601683796i)"`, downcasting off | `325` | `325.000000000000026076735749…` |
+### A power of a constant below `1e-50` is no longer simplified to zero
+
+**Answers that were wrong.** Evaluation rounds a value within `1e-50` of an integer onto it, and
+inner simplification takes a node's value where that value is an exact number, so `1/pi^136`,
+about `2.4e-68`, became `0`, and so did whatever held it. A product, a quotient or a power of
+numbers that are not zero, or a sum of such numbers of one sign, is kept as written now. Its
+evaluation still rounds. `Simplify` raises constants to such powers on the way, and returned `0`
+for expressions that are not zero; the integrator, simplifying under a substitution, answered such
+integrals with `0` ([#1769](https://github.com/asc-community/AngouriMath/issues/1769)).
+
+| Input | Was (2.5.0) | Now |
+|---|---|---|
+| `"1/pi^136".ToEntity().InnerSimplified` | `0` | `1 / pi ^ 136` |
+| `"e^(-160)".ToEntity().InnerSimplified` | `0` | `e ^ (-160)` |
+| `"x^n*((1 - d^2)/x - x)^3*(1 + x^2 - d*x)/(x - d)/pi^2".ToEntity().Simplify()` | `0 provided not x - d = 0 and ...` | `x ^ n * ((1 - d ^ 2) / x - x) ^ 3 * (1 - d * x + x ^ 2) / (pi ^ 2 * (x - d))` |
+
### A rational function with symbols in it beside a root of a linear is split into partial fractions first
**Answers where there were none.** `1/(x (1 + x^2) sqrt(a + b x))` was declined, while
diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Classes.cs
index 857fc3595..1f86edead 100644
--- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Classes.cs
+++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Classes.cs
@@ -33,6 +33,60 @@ public partial record Constant
protected override Entity InnerSimplify(bool isExact) => isExact ? this : Value;
}
+ ///
+ /// Whether , the exact number evaluates
+ /// to, is a zero that rounding alone made: is a product, a quotient
+ /// or a power of numbers none of which is zero, or a sum of numbers of one sign, and is
+ /// not zero itself.
+ ///
+ ///
+ /// Evaluation rounds a value within of an
+ /// integer onto it, ten to the minus fifty at the default hundred digits, which is what
+ /// makes the residual of a cancellation the zero it is. A product cancels nothing, and was
+ /// rounded the same way: 1/pi^136, about 2.4e-68, evaluated to 0, and
+ /// that zero, being an exact number, was taken as its value. The common denominator
+ /// raises constants to such powers, and Simplify returned 0 for
+ /// x^n ((1 - d^2)/x - x)^3 (1 + x^2 - d x)/(x - d)/pi^2. A sum whose terms have
+ /// one sign cancels nothing either; any other sum's zero may be a cancellation's, and is
+ /// taken as it was.
+ /// https://github.com/asc-community/AngouriMath/issues/1769
+ ///
+ private static bool IsAZeroOnlyRoundingMade(Entity node, Entity value)
+ => value is Integer { IsZero: true }
+ && (node is Mulf or Divf or Powf && CannotBeZero(node) || node is Sumf or Minusf && SignOfANonzero(node) is not null);
+
+ ///
+ /// Whether is a number or a constant that is not zero, or a product,
+ /// a quotient or a power of those with a finite divisor or exponent.
+ ///
+ private static bool CannotBeZero(Entity expr) => expr switch
+ {
+ Complex number => !number.IsZero && number.IsFinite,
+ Variable { IsConstant: true } constant => constant.Evaled is Complex { IsZero: false, IsFinite: true },
+ Mulf(var left, var right) => CannotBeZero(left) && CannotBeZero(right),
+ Divf(var dividend, var divisor) => CannotBeZero(dividend) && divisor.Evaled is Complex { IsFinite: true },
+ Powf(var @base, var exponent) => CannotBeZero(@base) && exponent.Evaled is Complex { IsFinite: true },
+ _ => SignOfANonzero(expr) is not null
+ };
+
+ ///
+ /// The sign of where it is a real number or a constant that is not
+ /// zero, or a product, a quotient, a power or a sum of those whose sign the parts decide;
+ /// where they do not.
+ ///
+ private static int? SignOfANonzero(Entity expr) => expr switch
+ {
+ Real number => !number.IsZero && number.IsFinite ? (number.IsPositive ? 1 : -1) : null,
+ Variable { IsConstant: true } constant => constant.Evaled is Real { IsZero: false } value ? (value.IsPositive ? 1 : -1) : null,
+ Mulf(var left, var right) => SignOfANonzero(left) * SignOfANonzero(right),
+ Divf(var dividend, var divisor) => SignOfANonzero(dividend) * SignOfANonzero(divisor),
+ Powf(var @base, Integer power) => SignOfANonzero(@base) is { } sign ? (power.EInteger.IsEven ? 1 : sign) : null,
+ Powf(var @base, var exponent) => SignOfANonzero(@base) == 1 && exponent.Evaled is Real { IsFinite: true } ? 1 : null,
+ Sumf(var left, var right) => SignOfANonzero(left) is { } sign && SignOfANonzero(right) == sign ? sign : null,
+ Minusf(var left, var right) => SignOfANonzero(left) is { } sign && SignOfANonzero(right) == -sign ? sign : null,
+ _ => null
+ };
+
///
/// For two-argument nodes
/// Used in InnerSimplify and InnerEval
@@ -74,7 +128,7 @@ private Entity ExpandOnTwoArguments(
bool propagateSet = true,
bool settlesNaN = false)
{
- if (isExact && this.Evaled is (Number { IsExact: true } or Boolean) and var n)
+ if (isExact && this.Evaled is (Number { IsExact: true } or Boolean) and var n && !IsAZeroOnlyRoundingMade(this, n))
return n;
left = left.InnerSimplified(isExact);
right = right.InnerSimplified(isExact);
@@ -97,7 +151,7 @@ Entity ops(Entity a, Entity b)
{
if (operation(a, b) is { } res)
return res;
- if (isExact && defaultCtor(this, a, b).Evaled is Number { IsExact: true } n)
+ if (isExact && defaultCtor(this, a, b) is var built && built.Evaled is Number { IsExact: true } n && !IsAZeroOnlyRoundingMade(built, n))
return n;
return defaultCtor(this, a, b);
}
diff --git a/Sources/Tests/UnitTests/Core/PowerOfAConstantBelowTheToleranceTest.cs b/Sources/Tests/UnitTests/Core/PowerOfAConstantBelowTheToleranceTest.cs
new file mode 100644
index 000000000..0e7953984
--- /dev/null
+++ b/Sources/Tests/UnitTests/Core/PowerOfAConstantBelowTheToleranceTest.cs
@@ -0,0 +1,68 @@
+//
+// Copyright (c) 2019-2026 Angouri.
+// AngouriMath is licensed under MIT.
+// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
+// Website: https://am.angouri.org.
+//
+
+using System;
+using AngouriMath;
+using AngouriMath.Extensions;
+using Xunit;
+using static AngouriMath.Entity;
+
+namespace AngouriMath.Tests.Core
+{
+ ///
+ /// A product, a quotient or a power of nonzero numbers is not zero however small it is.
+ /// Evaluation rounds a value within 1e-50 of an integer onto it, and the exact zero
+ /// that made of 1/pi^136 was taken as its value.
+ /// https://github.com/asc-community/AngouriMath/issues/1769
+ ///
+ [Trait("Area", "Core")]
+ public sealed class PowerOfAConstantBelowTheToleranceTest
+ {
+ private static double Value(Entity expr) => (double)((Number.Complex)expr.EvalNumerical()).RealPart;
+
+ // With a rational for each constant, both sides are exact numbers.
+ private static Entity Exactly(Entity expr) => expr.Substitute(MathS.pi, 3).Substitute(MathS.e, 2).Evaled;
+
+ [Theory]
+ [InlineData("1 / pi ^ 136")]
+ [InlineData("(pi ^ 136) ^ (-1)")]
+ [InlineData("pi ^ (-136) * pi ^ (-136)")]
+ [InlineData("e ^ (-160)")]
+ [InlineData("pi ^ (-136) + pi ^ (-137)")]
+ [InlineData("-pi ^ (-136) - e ^ (-160)")]
+ public void ItIsNotRoundedToZero(string written)
+ {
+ var expr = written.ToEntity();
+ var simplified = expr.InnerSimplified;
+ Assert.NotEqual(Number.Integer.Zero, simplified);
+ Assert.Equal(Exactly(expr), Exactly(simplified));
+ }
+
+ [Fact]
+ public void NorIsAPowerOfARoot()
+ => Assert.NotEqual(Number.Integer.Zero, "sqrt(2) ^ (-400)".ToEntity().InnerSimplified);
+
+ // What the rounding is for, the residual of a cancellation, is still zero.
+ [Theory]
+ [InlineData("sqrt(2) ^ 2 - 2")]
+ [InlineData("pi ^ (-136) - pi ^ (-136)")]
+ public void ACancellationIsStillZero(string written)
+ => Assert.Equal(Number.Integer.Zero, written.ToEntity().InnerSimplified);
+
+ // The common denominator raises pi to the 136th power on the way, and the expression
+ // simplified to zero.
+ [Fact]
+ public void SimplifyKeepsTheValueOfAQuotientByAPowerOfPi()
+ {
+ var expr = "x ^ n * ((1 - d ^ 2) / x - x) ^ 3 * (1 + x ^ 2 - d * x) / (x - d) / pi ^ 2".ToEntity();
+ static Entity At(Entity e) => e.Substitute("x", 1.3).Substitute("d", 0.7).Substitute("n", 0.37);
+ var expected = Value(At(expr));
+ Assert.True(Math.Abs(expected + 0.24771026954308137) < 1e-12, $"the expression is {expected} there");
+ Assert.True(Math.Abs(Value(At(expr.Simplify())) - expected) < 1e-12);
+ }
+ }
+}