diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index c83cb36be..bbf8384e1 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -371,6 +371,21 @@ is the canonical one: square-free, and without what the answer's own denominator | `"(a - 1)/(1 - b) - (1 - a)/(b - 1)".Simplify()` | `0 provided not (b - 1) * (1 - b) = 0` | `0 provided not b - 1 = 0` | | `"(x*y*a*b*c)/(c*b*a*x*x)".Simplify()` | `y / x provided not a * b * c * x = 0` | `y / x provided not a * b * c = 0` | +### A sine or cosine with no real radical form stays as written + +**Silent: `sin(pi/7)`, `cos(2pi/9)` and the other values whose only radical form goes through the complex numbers +simplify to themselves, where they were nested radicals with cube roots of complex numbers.** That is casus +irreducibilis: no real radical form exists, and the radical was many times longer than the input. The table's +entries for the sevenths, ninths and eighteenths are passed over, and their half angles with them. A value with a +real radical form still takes it ([#2143](https://github.com/asc-community/AngouriMath/issues/2143)). + +| Input | Was (2.6.0) | Now | +|---|---|---| +| `"sin(pi/7)".ToEntity().Simplify()` | `sqrt((1 - 1/6 * (-1 + ((7 + 21 * sqrt(-3)) / 2) ^ (1/3) + ((7 - 21 * sqrt(-3)) / 2) ^ (1/3))) / 2)` | `sin(pi / 7)` | +| `"cos(2 pi/9)".ToEntity().Simplify()` | `(((sqrt(-3) - 1) / 2) ^ (1/3) + (-(sqrt(-3) + 1) / 2) ^ (1/3)) / 2` | `cos(2/9 * pi)` | +| `"sin(pi/18)".ToEntity().Simplify()` | `sqrt((1 - 1/4 * ((4 + 4 * sqrt(-3)) ^ (1/3) + (4 - 4 * sqrt(-3)) ^ (1/3))) / 2)` | `sin(pi / 18)` | +| `"sin(pi/5)".ToEntity().Simplify()` | `1/4 * sqrt(10 - 2 * sqrt(5))` | `1/4 * sqrt(10 - 2 * sqrt(5))` | + ### A step function at a jump takes the side its argument arrives from **Silent: a limit of `floor`, `ceil` or `sgn` whose argument arrives on a jump is answered, where it was left diff --git a/Sources/.editorconfig b/Sources/.editorconfig index ddd6649cc..062284068 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -238,6 +238,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [Tests/UnitTests/Convenience/ArctangentPolesTest.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/PatternsTest/CasusIrreducibilisTest.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/ModulusEquationTest.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 diff --git a/Sources/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs b/Sources/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs index 8b78be55b..492e0e93c 100644 --- a/Sources/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs +++ b/Sources/AngouriMath/Functions/Simplification/TrigonometricTableValues.cs @@ -52,7 +52,7 @@ private static bool TryPulling(TrigTable table, Complex arg, for (var j = begin; j <= end; j++) { - if (Number.IsZero(table[j].arg - dArg)) + if (Number.IsZero(table[j].arg - dArg) && !IsWrittenThroughTheComplexNumbers(table[j].res)) { res = table[j].res; return true; @@ -62,6 +62,20 @@ private static bool TryPulling(TrigTable table, Complex arg, return false; } + /// + /// Whether a value is written through numbers that are not real: i, or a square root + /// of a negative number. Every angle here is real, so such a value is a real number whose + /// radical form needs the complex numbers, casus irreducibilis, and the function of its angle + /// is the shorter answer: cos(2pi/7) is + /// (-1 + cbrt((7 + 21 sqrt(-3))/2) + cbrt((7 - 21 sqrt(-3))/2))/6, and no real + /// radical form exists. sin(pi/7) simplified to the half-angle of it, many times + /// longer than the input. Such an entry is passed over rather than removed, so the closed form + /// stays on record. https://github.com/asc-community/AngouriMath/issues/2143 + /// + private static bool IsWrittenThroughTheComplexNumbers(Entity value) + => value.Nodes.Any(node => node is Complex and not Real + || node is Powf(Real { IsNegative: true }, Rational { ERational.Denominator.IsEven: true })); + /// /// Every angle in the tables below is written 2pi/n, so they name nothing at /// all between 2pi/5 and 2pi/3 -- the whole lower half of the circle diff --git a/Sources/Tests/UnitTests/Common/TrigonometricTableTest.cs b/Sources/Tests/UnitTests/Common/TrigonometricTableTest.cs index f71af9e82..d18da10aa 100644 --- a/Sources/Tests/UnitTests/Common/TrigonometricTableTest.cs +++ b/Sources/Tests/UnitTests/Common/TrigonometricTableTest.cs @@ -91,8 +91,10 @@ public void EveryExactValueAgreesWithTheCallItReplaces() // the 1158 calls swept: 581 of them resolved before the half turn was applied // and 690 after, and the 581 were already sound -- what was missing was missing, // not wrong. Raise this if the table gains entries; it is here to catch losing - // them silently. - Assert.True(resolved >= 690, $"only {resolved} of the swept angles resolved at all"); + // them silently. 642 since the entries written through the complex numbers, the + // sevenths, ninths and eighteenths and the half angles of them, are passed over and + // their calls left as written: https://github.com/asc-community/AngouriMath/issues/2143 + Assert.True(resolved >= 642, $"only {resolved} of the swept angles resolved at all"); } /// diff --git a/Sources/Tests/UnitTests/PatternsTest/CasusIrreducibilisTest.cs b/Sources/Tests/UnitTests/PatternsTest/CasusIrreducibilisTest.cs new file mode 100644 index 000000000..b93a4c872 --- /dev/null +++ b/Sources/Tests/UnitTests/PatternsTest/CasusIrreducibilisTest.cs @@ -0,0 +1,54 @@ +// +// 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.Linq; +using AngouriMath; +using AngouriMath.Extensions; +using Xunit; +using static AngouriMath.Entity.Number; + +namespace AngouriMath.Tests.PatternsTest +{ + /// + /// A sine or cosine whose only radical form goes through the complex numbers, casus irreducibilis, stays as the + /// function of its angle. sin(pi/7) simplified to sqrt((1 - 1/6 * (-1 + ((7 + 21 * sqrt(-3)) / 2) ^ (1/3) + + /// ((7 - 21 * sqrt(-3)) / 2) ^ (1/3))) / 2), many times longer than the input, and no real radical form exists. + /// #2143 + /// + [Trait("Area", "PatternsTest")] + public sealed class CasusIrreducibilisTest + { + [Theory] + [InlineData("sin(pi/7)")] + [InlineData("cos(pi/7)")] + [InlineData("cos(2 pi/7)")] + [InlineData("sin(4 pi/7)")] + [InlineData("sin(pi/9)")] + [InlineData("cos(2 pi/9)")] + [InlineData("sin(pi/18)")] + public void ItStaysAsWritten(string value) + { + var simplified = value.ToEntity().Simplify(); + Assert.DoesNotContain(simplified.Nodes, node => node is Complex and not Real || node is Entity.Powf(_, Rational { ERational.Denominator.IsEven: false })); + Assert.True(simplified.Nodes.Count() <= value.ToEntity().Nodes.Count() + 2, simplified.Stringize()); + var difference = (simplified - value.ToEntity()).EvalNumerical(); + Assert.True(difference.Abs() < 1e-30, $"{simplified} differs from {value} by {difference}"); + } + + /// A value with a real radical form still takes it. + [Theory] + [InlineData("sin(pi/5)", "1/4 * sqrt(10 - 2 * sqrt(5))")] + [InlineData("sin(pi/3)", "sqrt(3) / 2")] + [InlineData("cos(2 pi/5)", "(sqrt(5) - 1) / 4")] + public void ARealRadicalIsStillTaken(string value, string expected) + => Assert.Equal(expected, value.ToEntity().Simplify().Stringize()); + + [Fact] + public void TheIdentitiesStillHold() + => Assert.Equal(Integer.One, "sin(pi/7)^2 + cos(pi/7)^2".ToEntity().Simplify()); + } +}