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
10 changes: 7 additions & 3 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,11 @@ the square root the solver takes after solving for `x^2`, where an equation has
for `0 < p < 1` its values lie in the sector `-p pi < arg <= p pi`, with `0`, and for `-1 < p < 0` in
`p pi <= arg < -p pi`, without `0`. `x^p = a` was solved to `a^(1/p)` with no condition, which is no root where `a` is
outside the sector: `sqrt(x) = a` was `{ a ^ 2 }`, and at `a = -1` that is `1`, whose square root is `1` and not `-1`. The root now carries
the sector, written with `arg`, as `abs`'s inversion carries its own condition. A number on the other side is decided
as before, and `|p| > 1` needs no condition, since every value is then a power. What the equation is provided is known
the sector, written with `arg`, as `abs`'s inversion carries its own condition. A negative real to a rational power
whose denominator is odd and below ten is the real root to the numerator, as the evaluator gives it, `(-8)^(1/3)` being
`-2`: with an odd numerator the negative reals are values as well, so `x^(1/3) = -2` is still `{ -8 }`, and with an even
one a positive value has a negative preimage too. A number on the other side is decided as before, and `|p| > 1` needs
no condition, since every value is then a power. What the equation is provided is known
while it is solved, as a provided expression is differentiated: a value known to be real, or declared real, has the
half-line for its sector, one known to lie on it needs no condition, and one known to lie off it has no root. A conjunct of a condition that
another conjunct already forces is not stated, so `a > -5 and a >= 0` is `a >= 0`, and a condition stated twice is
Expand All @@ -153,7 +156,8 @@ carried it once combined: a second derivative of `1/sqrt(x^2 + 13)` stated `not
| Input | Was (2.6.0) | Now |
|---|---|---|
| `"sqrt(x) = a".ToEntity().Solve("x")` | `{ a ^ 2 }` | `{ a ^ 2 provided a = 0 or -1/2 * pi < arg(a) and arg(a) <= 1/2 * pi }` |
| `"x^(1/3) = a".ToEntity().Solve("x")` | `{ a ^ 3 }` | `{ a ^ 3 provided a = 0 or -1/3 * pi < arg(a) and arg(a) <= 1/3 * pi }` |
| `"x^(1/3) = a".ToEntity().Solve("x")` | `{ a ^ 3 }` | `{ a ^ 3 provided a = 0 or -1/3 * pi < arg(a) and arg(a) <= 1/3 * pi or arg(a) = pi }` |
| `"x^(2/3) = 4".ToEntity().Solve("x")` | `{ 8 }` | `{ 8, -8 }` |
| `"x^(-1/2) = a".ToEntity().Solve("x")` | `{ a ^ (-2) }` | `{ a ^ (-2) provided not a = 0 and -1/2 * pi <= arg(a) and arg(a) < 1/2 * pi }` |
| `"(sqrt(x) = a) provided a in RR".ToEntity().Solve("x")` | `{ a ^ 2 provided a in RR }` | `{ a ^ 2 provided a in RR and a >= 0 }` |
| `"(sqrt(x) = a) provided a > 0".ToEntity().Solve("x")` | `{ a ^ 2 provided a > 0 }` | `{ a ^ 2 provided a > 0 }` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,16 @@ partial record Powf
return Enumerable.Empty<Entity>();
return Together(Number.GetAllRootsOf1(pow.Abs()).Select(root => Base.Invert(root * MathS.Pow(value, 1 / Exponent), x)));
}
else
return Base.Invert(MathS.Pow(value, 1 / Exponent), x)
?.Select(root => root.Provided(InTheRange(value)));
var principal = Base.Invert(MathS.Pow(value, 1 / Exponent), x)
?.Select(root => root.Provided(InTheRange(value)));
// With an even numerator a negative base has a positive power, (-8)^(2/3) = 4,
// so a positive value has the negative of its principal preimage for a
// preimage as well: x^(2/3) = 4 is 8 and -8.
if (principal is { } && TakesTheRealRootOfANegative(out var oddNumerator) && !oddNumerator)
return Base.Invert(-MathS.Pow(value, 1 / Exponent), x) is { } negative
? principal.Concat(negative.Select(root => root.Provided(new Greaterf(value, Integer.Zero))))
: null;
return principal;
}
// a ^ x = value => x = log(a, value)
// TODO: determine how we should return periodic roots as
Expand All @@ -138,12 +145,21 @@ private Entity InTheRange(Entity value)
if (Exponent.Evaled is not Real { EDecimal.IsFinite: true } p
|| p.EDecimal.Abs().CompareTo(PeterO.Numbers.EDecimal.One) >= 0)
return Boolean.True;
// A negative base with an odd numerator has a negative power, (-8)^(1/3) = -2, so
// the negative reals are in the range as well, and x^(1/3) = -2 is x = -8, as it
// was before the sector was stated.
var reachesTheNegatives = TakesTheRealRootOfANegative(out var oddNumerator) && oddNumerator;
// Where the value is known to be real -- by its codomain, or by what the equation
// it came from was provided -- arg(value) is 0 or pi, so the sector is the half-line
// value >= 0 for p > 0 and value > 0 for p < 0, and nothing is stated where that is
// known as well: sqrt(x) = a provided a > 0 is a ^ 2 provided a > 0.
Entity halfLine = p.IsPositive ? new GreaterOrEqualf(value, Integer.Zero) : new Greaterf(value, Integer.Zero);
Entity outside = p.IsPositive ? new Lessf(value, Integer.Zero) : new LessOrEqualf(value, Integer.Zero);
// known as well: sqrt(x) = a provided a > 0 is a ^ 2 provided a > 0. With the
// negatives it is every real, or every real but 0.
Entity halfLine = reachesTheNegatives
? (p.IsPositive ? Boolean.True : !value.EqualTo(Integer.Zero))
: p.IsPositive ? new GreaterOrEqualf(value, Integer.Zero) : new Greaterf(value, Integer.Zero);
Entity outside = reachesTheNegatives
? (p.IsPositive ? Boolean.False : value.EqualTo(Integer.Zero))
: p.IsPositive ? new Lessf(value, Integer.Zero) : new LessOrEqualf(value, Integer.Zero);
if (TreeAnalyzer.IsKnown(halfLine))
return Boolean.True;
if (TreeAnalyzer.IsKnown(outside))
Expand All @@ -153,9 +169,26 @@ private Entity InTheRange(Entity value)
var bound = (Exponent * MathS.pi).InnerSimplified;
var opposite = ((-Exponent).InnerSimplified * MathS.pi).InnerSimplified;
var angle = MathS.Arg(value);
return p.IsPositive
Entity sector = p.IsPositive
? value.EqualTo(Integer.Zero) | (new Lessf(opposite, angle) & new LessOrEqualf(angle, bound))
: !value.EqualTo(Integer.Zero) & new LessOrEqualf(bound, angle) & new Lessf(angle, opposite);
return reachesTheNegatives ? sector | angle.EqualTo(MathS.pi) : sector;
}

/// <summary>
/// Whether a negative real base has a real power, as the evaluator takes it: a rational
/// power whose denominator is odd and below ten is the real root of a negative real to
/// the numerator, as Number.Pow's search for a good root finds it, and any other is the
/// principal power. (-8)^(1/3) is -2, (-8)^(2/3) is 4, and (-1)^(1/11) is not real.
/// </summary>
private bool TakesTheRealRootOfANegative(out bool oddNumerator)
{
oddNumerator = false;
if (Exponent is not Rational { ERational: var ratio } || ratio.Denominator.IsEven
|| ratio.Denominator.CompareTo(PeterO.Numbers.EInteger.FromInt32(10)) >= 0)
return false;
oddNumerator = !ratio.Numerator.IsEven;
return true;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ namespace AngouriMath.Tests.Algebra
/// solution. <c>sqrt(x) = a</c> was answered <c>{ a ^ 2 }</c>, which is wrong at
/// <c>a = -1</c>: <c>sqrt((-1)^2)</c> is <c>1</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/212">#212</a>
/// <para/>
/// A negative real to a rational power with an odd denominator below ten is the real root to
/// the numerator, which is what the evaluator gives: <c>(-8)^(1/3)</c> is <c>-2</c>. So with
/// an odd numerator the negative reals are values too, and <c>x^(1/3) = -2</c> is
/// <c>x = -8</c>, which the sector alone answered <c>{ }</c>; and with an even one a positive
/// value has a negative preimage as well, <c>(-8)^(2/3) = 4</c>.
/// </summary>
[Trait("Area", "Algebra")]
public sealed class FractionalPowerRangeTest
Expand All @@ -40,7 +46,8 @@ private static Entity OnlyRoot(string equation)
[InlineData("sqrt(x) = a", "-i", false)]
[InlineData("sqrt(x) = a", "-1", false)]
[InlineData("x^(1/3) = a", "1 + i", true)]
[InlineData("x^(1/3) = a", "-1", false)]
[InlineData("x^(1/3) = a", "-1", true)]
[InlineData("x^(1/3) = a", "-i", false)]
[InlineData("x^(-1/2) = a", "-i", true)]
[InlineData("x^(-1/2) = a", "i", false)]
[InlineData("x^(-1/2) = a", "0", false)]
Expand All @@ -63,6 +70,14 @@ public void TheRootHoldsWhereItsConditionDoes(string equation, string value, boo
[InlineData("sqrt(x) = -1", "{ }")]
[InlineData("sqrt(x) = i", "{ -1 }")]
[InlineData("sqrt(x) = -i", "{ }")]
[InlineData("x^(1/3) = -2", "{ -8 }")]
[InlineData("x^(1/5) = -2", "{ -32 }")]
[InlineData("x^(-1/3) = -1/2", "{ -8 }")]
[InlineData("x^(3/5) = -8", "{ -32 }")]
[InlineData("x^(2/3) = 4", "{ 8, -8 }")]
[InlineData("x^(4/3) = 16", "{ 8, -8 }")]
[InlineData("x^(2/3) = -4", "{ }")]
[InlineData("x^(1/11) = -1", "{ }")]
public void ANumberIsDecided(string equation, string expected)
=> Assert.Equal(expected.ToEntity(), equation.ToEntity().Solve("x"));

Expand Down
Loading