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
20 changes: 20 additions & 0 deletions Sources/AngouriMath/Functions/Simplification/Simplificator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,26 @@ not solve this issue completely and yet too slow to be accepted.
{
AddHistory(res.Expand().Simplify(-level));
AddHistory(res.Factorize().Simplify(-level));

// A multiple angle written out is worth having only where the pieces then
// cancel, so it has to be simplified in full before the metric can be
// asked -- the same reason Expand and Factorize are re-simplified above,
// and for the same reason it is a candidate rather than a step. The pass
// inside the loop offers the opened form to the trigonometric rules only,
// which settles an expression that is already one term; it cannot settle
// one whose cancellation needs a common denominator, because the passes
// that build one ran earlier in the loop, while the angles were still shut.
// That is https://github.com/asc-community/AngouriMath/issues/557: the
// reporter's second expression is 0, and reaches 0 through
// 2 sin(t) cos(t) and not through sin(2t).
var openedAngles = res
.Replace(Patterns.ExpandMultipleAngleRules)
.Replace(Patterns.NormalTrigonometricForm)
.InnerSimplified;
if (openedAngles != res)
// Expanded, and for the same reason res is expanded above: the
// cancellation only shows up once the products are multiplied out.
AddHistory(openedAngles.Expand().Simplify(-level));
}

return history.Values.SelectMany(x => x);
Expand Down
28 changes: 28 additions & 0 deletions Sources/Tests/UnitTests/PatternsTest/DoubleAngleOverSineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,34 @@ private static Entity Bare(string expression)
public void TheDoubleAngleOverTheSingleOne(string expression, string expected) =>
Assert.Equal(expected.ToEntity().Simplify(), Bare(expression));

/// <summary>
/// The second expression the reporter gives, which is the first one with a
/// quotient of sixth and eighth powers of sin(t) added in front of it. Both are 0.
/// <para/>
/// This one needs the opened angle to survive as far as the fractions: the quotient
/// only cancels once its terms are over a common denominator, and the passes that
/// build one run before the angles are opened. So the opened form was offered to the
/// complexity metric in the one shape where its payoff had not happened yet, and was
/// rightly rejected as the longer of the two. It is now simplified in full first --
/// including expanded, since the cancellation only shows up once the products are
/// multiplied out -- which is what Expand and Factorize already get.
/// </summary>
[Fact]
public void TheReportersSecondExpressionIsAlsoZero() =>
Assert.Equal(Entity.Number.Integer.Create(0), Bare(
"(cos(2 * t) * sin(t) ^ 6 * (-1) + cos(t) * sin(t) ^ 5 * sin(2 * t)"
+ " - sin(2 * t) ^ 2 * sin(t) ^ 4 / 4) / sin(t) ^ 8 - 1"
+ " + (sin(2 * t) * cosec(t)) ^ 2 / 4 - cos(2 * t) - sin(t) ^ 2"));

// The pieces it is built from, each 0 or 1 on its own. Together they say the
// cancellation is the quotient's and not only the tail's.
[Theory]
[InlineData("(cos(2 * t) * sin(t) ^ 6 * (-1) + cos(t) * sin(t) ^ 5 * sin(2 * t)"
+ " - sin(2 * t) ^ 2 * sin(t) ^ 4 / 4) / sin(t) ^ 8", "1")]
[InlineData("(sin(t) ^ 6 - sin(2 * t) ^ 2 * sin(t) ^ 4 / 4) / sin(t) ^ 8 - 1", "0")]
public void TheQuotientCancelsOnItsOwn(string expression, string expected) =>
Assert.Equal(expected.ToEntity(), Bare(expression));

/// <summary>
/// 2cos(u) is a number where sin(u) is zero and sin(2u) csc(u) is not, so the
/// cosecant's own condition has to be carried rather than dropped. It is the same
Expand Down
Loading