diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index b6c7f20f6..302c211bd 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -746,11 +746,13 @@ remainder over `Complex` already did. numerator, as evaluation has it, where both compilers took the principal power, and `Compile` gave `NaN`.** `(-8)^(1/3)` evaluates to `-2`, `(-8)^(2/3)` to `4`. Newton's method, which `Solve` falls back to, runs on the compiled function, so a root needing the real root was not found either. A base off the real line, a positive one, an -even denominator or one of ten or more is the principal power, as before. +even denominator or one of ten or more is the principal power, as before. An exponent with no variable is read for its +value, as evaluation reads it, so `x^(1/(1 + 2))` and `x^((1/3) provided 2 > 1)` are the real root as well. | Input | Was (2.6.0) | Now | |---|---|---| | `"x^(1/3)".ToEntity().Compile("x").Call(-8)` | `<1.0000000000000002; 1.7320508075688772>` | `<-2; 0>` | +| `"x^(1/(1 + 2))".ToEntity().Compile("x").Call(-8)` | `<1.0000000000000002; 1.7320508075688772>` | `<-2; 0>` | | `"x^(1/3)".ToEntity().Compile("x")(-8)` | `NaN` | `-2` | | `"(3 - 4x)^(1/3) + sqrt(4 + 4x) = 3".ToEntity().Solve("x")` | `{ (-2.466…) / 4, 2.952… / 4 }` | `{ (-2.466…) / 4, 2.952… / 4, 33.514… / 4 }` | diff --git a/Sources/AngouriMath/Functions/Compilation/IntoFE/Compiler.cs b/Sources/AngouriMath/Functions/Compilation/IntoFE/Compiler.cs index efde9d334..b9663ea75 100644 --- a/Sources/AngouriMath/Functions/Compilation/IntoFE/Compiler.cs +++ b/Sources/AngouriMath/Functions/Compilation/IntoFE/Compiler.cs @@ -173,7 +173,7 @@ private protected override void CompileNode(Compiler compiler) Exponent.InnerCompile(compiler); Base.InnerCompile(compiler); compiler.Instructions.Add( - Exponent is Rational { ERational: var ratio } && Core.Compilation.RealProducts.TakesTheRealRootOfANegative(ratio) + Core.Compilation.RealProducts.RealRootExponent(Exponent) is { } ratio ? new(InstructionType.CALL_REAL_ROOT_POW, ratio.Numerator.IsEven ? 0 : 1) : new(InstructionType.CALL_POW)); } diff --git a/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs b/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs index b60bd0c81..fce8749f2 100644 --- a/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs +++ b/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs @@ -281,7 +281,7 @@ Divf when ShouldBeAtLeastDouble(left) is var newLeft && ShouldBeAtLeastDouble(ri right), // A rational exponent with an odd denominator below ten takes the real root of a negative // real, as evaluation does: (-8)^(1/3) is -2, which was 1 + 1.73i, and NaN over double. - Powf { Exponent: Rational { ERational: var ratio } } when Core.Compilation.RealProducts.TakesTheRealRootOfANegative(ratio) + Powf { Exponent: var exponent } when Core.Compilation.RealProducts.RealRootExponent(exponent) is { } ratio && ShouldBeAtLeastDouble(left) is var newLeft && ShouldBeAtLeastDouble(right) is var newRight && (newLeft.Type == typeof(System.Numerics.Complex) || newLeft.Type == typeof(double)) diff --git a/Sources/AngouriMath/Functions/Compilation/RealProducts.cs b/Sources/AngouriMath/Functions/Compilation/RealProducts.cs index 9fa4c7004..04a389fb6 100644 --- a/Sources/AngouriMath/Functions/Compilation/RealProducts.cs +++ b/Sources/AngouriMath/Functions/Compilation/RealProducts.cs @@ -106,6 +106,17 @@ internal static bool TakesTheRealRootOfANegative(PeterO.Numbers.ERational expone => !exponent.Denominator.Equals(PeterO.Numbers.EInteger.One) && !exponent.Denominator.IsEven && exponent.Denominator.CompareTo(PeterO.Numbers.EInteger.FromInt32(10)) < 0; + /// + /// The rational is, where it is one that + /// , or null. One with no variable is read for its value, + /// as evaluation reads it: written otherwise than as a rational, x^(1/(1 + 2)) and + /// x^((1/3) provided 2 > 1) compiled to the principal power, 1 + 1.73i at -8, + /// where evaluation takes the exponent 1/3 and is -2. + /// + internal static PeterO.Numbers.ERational? RealRootExponent(Entity exponent) + => (exponent is Entity.Number.Rational || exponent.Vars.Count > 0 ? exponent : exponent.InnerSimplified) + is Entity.Number.Rational { ERational: var ratio } && TakesTheRealRootOfANegative(ratio) ? ratio : null; + /// /// A whole power of a number that is not real, as a product of its factors, which is how evaluation takes it. /// takes e^(n ln z), diff --git a/Sources/Tests/UnitTests/Common/CompiledOddRootTest.cs b/Sources/Tests/UnitTests/Common/CompiledOddRootTest.cs index c1589d580..625511222 100644 --- a/Sources/Tests/UnitTests/Common/CompiledOddRootTest.cs +++ b/Sources/Tests/UnitTests/Common/CompiledOddRootTest.cs @@ -31,6 +31,12 @@ public sealed class CompiledOddRootTest [InlineData("x^(4/3)", -8)] [InlineData("x^(3/5)", -32)] [InlineData("(1 + x)^(1/3) + 2", -9)] + // An exponent with no variable is read for its value, as evaluation reads it: written + // otherwise than as a rational, it took the principal power, 1 + 1.73i. + [InlineData("x^(1/(1 + 2))", -8)] + [InlineData("x^(3^(-1))", -8)] + [InlineData("x^(1/(3 provided 3 > 1))", -8)] + [InlineData("x^((2/3) provided 2 > 1)", -8)] public void TheCompiledFormsTakeTheRealRoot(string expression, double at) { var entity = expression.ToEntity();