Skip to content
Merged
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
4 changes: 3 additions & 1 deletion BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -746,11 +746,13 @@ remainder over `Complex` already did.
numerator, as evaluation has it, where both compilers took the principal power, and `Compile<double, double>` 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<double, double>("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 }` |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
11 changes: 11 additions & 0 deletions Sources/AngouriMath/Functions/Compilation/RealProducts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// The rational <paramref name="exponent"/> is, where it is one that
/// <see cref="TakesTheRealRootOfANegative"/>, or null. One with no variable is read for its value,
/// as evaluation reads it: written otherwise than as a rational, <c>x^(1/(1 + 2))</c> and
/// <c>x^((1/3) provided 2 &gt; 1)</c> compiled to the principal power, <c>1 + 1.73i</c> at -8,
/// where evaluation takes the exponent <c>1/3</c> and is -2.
/// </summary>
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;

/// <summary>
/// A whole power of a number that is not real, as a product of its factors, which is how evaluation takes it.
/// <see cref="System.Numerics.Complex.Pow(System.Numerics.Complex, System.Numerics.Complex)"/> takes <c>e^(n ln z)</c>,
Expand Down
6 changes: 6 additions & 0 deletions Sources/Tests/UnitTests/Common/CompiledOddRootTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading