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
12 changes: 12 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ did, and `ln(x + 2) / ln(x + 3)` at 0 was `ln(2) / ln(3)`. With symbols it was n
| `"limit(ln(a + 1/x) / ln(b + 1/x), x, +oo)".ToEntity().Simplify()` | `a / b` | `ln(a) / ln(b)` |
| `"limit(ln(1 + x) / ln(y + x), x, 0)".ToEntity().Simplify()` | `1 / y` | `0 provided not ln(y) = 0` |

### A cube shifted is solved

**Silent: a cubic `a(x + h)^3 = k` written out, with `b^2 = 3ac`, is solved where it was answered `{ }`.** Cardano's
`C^3` is `(D1 + sqrt(D1^2 - 4 D0^3)) / 2`, and with `D0 = 0` that is `(D1 + |D1|) / 2`, which is 0 for a negative `D1`.
Every root was then taken for `-b/(3a)`, which is none, and the empty set claimed there was no root. With `D0 = 0`, `C` is a
cube root of `D1` itself.

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"(x + 1)^3 = 2".ToEntity().Solve("x")` | `{ }` | `{ -(3 + -3 * 2 ^ (1/3)) / 3, … }`, the three cube roots of 2 less 1 |
| `"3x^3 + 9x^2 + 9x + 1 = 0".ToEntity().Solve("x")` | `{ }` | `{ -(9 + -3 * 18 ^ (1/3)) / 3 / 3, … }` |

### A quadratic whose discriminant is a square is answered without the root

**Silent: the same roots, written as what they are.** The quadratic formula wrote `sqrt(b^2 - 4ac)` whatever the
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Convenience/EmptySequenceFoldTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Algebra/SolveTest/AShiftedCubeTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Convenience/FoldEnumerationTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,23 @@ internal static IEnumerable<Entity> SolveCubic(Entity a, Entity b, Entity c, Ent
var u3 = Rational.Create(-1, 2) - coeff;
var D0 = MathS.Sqr(b) - 3 * a * c;
var D1 = (2 * MathS.Pow(b, 3) - 9 * a * b * c + 27 * MathS.Sqr(a) * d).InnerSimplified;
var C = MathS.Pow((D1 + MathS.Sqrt(MathS.Sqr(D1) - 4 * MathS.Pow(D0, 3))) / 2, Rational.Create(1, 3));
// C is a cube root of either root of C^6 - D1 C^3 + D0^3 = 0, and only one that is not 0
// can be divided by. With D0 = 0 those roots are D1 and 0, and the square root of D1^2
// is |D1|, so for a negative D1 the sum below is 0: a(x + h)^3 = k, a cube shifted, had
// C = 0 and every root taken for -b/(3a), which is none, and (x + 1)^3 = 2 was answered
// { }. With D0 = 0 a cube root of D1 itself is C, and the roots are -(b + u C)/(3a),
// D0/C being 0; that is -b/(3a) three times where D1 is 0 as well. For a negative D1 the
// cube root is written as the negative of the cube root of -D1, which is a cube root of
// D1 whichever root of a negative number a power is read as.
var shifted = D0.Evaled == 0;
var C = !shifted
? MathS.Pow((D1 + MathS.Sqrt(MathS.Sqr(D1) - 4 * MathS.Pow(D0, 3))) / 2, Rational.Create(1, 3))
: D1.Evaled is Real { IsNegative: true }
? -MathS.Pow(-D1, Rational.Create(1, 3))
: MathS.Pow(D1, Rational.Create(1, 3));

return new[] { u1, u2, u3 }.Select(uk =>
C.Evaled == 0 && D0.Evaled == 0 ? -(b + uk * C) / 3 / a : -(b + uk * C + D0 / C / uk) / 3 / a);
shifted ? -(b + uk * C) / 3 / a : -(b + uk * C + D0 / C / uk) / 3 / a);
}

[ConstantField] private static readonly int[] sqrtsOf1 = { -1, 1 };
Expand Down
48 changes: 48 additions & 0 deletions Sources/Tests/UnitTests/Algebra/SolveTest/AShiftedCubeTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// 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 AngouriMath.Extensions;
using Xunit;
using static AngouriMath.Entity;
using static AngouriMath.Entity.Set;

namespace AngouriMath.Tests.Algebra
{
/// <summary>
/// A cube shifted, <c>a(x + h)^3 = k</c>, has <c>D0 = b^2 - 3ac = 0</c>, and Cardano's
/// <c>C^3 = (D1 + sqrt(D1^2 - 4 D0^3)) / 2</c> is then <c>(D1 + |D1|) / 2</c>, which is 0 for a
/// negative <c>D1</c>. Every root was taken for <c>-b/(3a)</c>, none was one, and
/// <c>(x + 1)^3 = 2</c> was answered <c>{ }</c>. Found by a cross-check of equations in
/// logarithms, one of which exponentiates to <c>3x^3 + 9x^2 + 9x + 1</c>; no issue was filed.
/// </summary>
[Trait("Area", "Algebra")]
public sealed class AShiftedCubeTest
{
[Theory]
[InlineData("(x + 1)^3 = 2", "-1", "2")]
[InlineData("x^3 + 3x^2 + 3x - 1 = 0", "-1", "2")]
[InlineData("3x^3 + 9x^2 + 9x + 1 = 0", "-1", "2/3")]
[InlineData("x^3 - 3x^2 + 3x - 3 = 0", "1", "2")]
// D1 positive, which was answered: (x + 1)^3 = -2.
[InlineData("x^3 + 3x^2 + 3x + 3 = 0", "-1", "-2")]
public void ItsRootsAreTheCubeRootsShifted(string equation, string shift, string cube)
{
var answer = Assert.IsType<FiniteSet>(equation.ToEntity().Solve("x"));
Assert.Equal(3, answer.Count);
foreach (var unity in new[] { "1", "-1/2 + sqrt(3) / 2 * i", "-1/2 - sqrt(3) / 2 * i" })
{
// The real cube root of the cube, times each cube root of unity, shifted.
var root = $"({shift}) + ({unity}) * sgn({cube}) * abs({cube})^(1/3)".ToEntity();
Assert.Contains(answer, found => Number.IsZero((found - root).EvalNumerical()));
}
}

[Fact]
public void ATripleRootIsStillOne()
=> Assert.Equal("{ -1 }".ToEntity(), "x^3 + 3x^2 + 3x + 1 = 0".ToEntity().Solve("x"));
}
}
Loading