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
22 changes: 22 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ read first.
| **silent** | `"1/(x^4 + 4)".Integrate("x")` | unevaluated | the antiderivative over its two quadratic factors |
| **silent** | `"sin(-x) + sin(x)".Simplify()` | `sin(-x) + sin(x)` | `0` |
| **silent** | `"cos(-x)".Simplify()` and `"abs(-x)".Simplify()` | unchanged | `cos(x)`, `abs(x)` |
| **silent** | `"cos(0 ^ y)".InnerSimplified` | `-(-1) provided ...` | `1 provided ...` |

### `InnerSimplified` is idempotent again

An exact trigonometric value reached through a half turn — `cos(x)` read off the table as
`-cos(pi - x)` — was built as a negation over the value it turned to and handed back unfolded. Where
the answer is then *wrapped* rather than rebuilt, nothing normalises it again:

```
"cos(0 ^ y)".InnerSimplified

was -(-1) provided y / 2 * (1 + 1 / sgn(y) ^ 2) > 0
is 1 provided y / 2 * (1 + 1 / sgn(y) ^ 2) > 0
```

Both are the same value, so nothing was wrong — but applying `InnerSimplified` twice gave a different
tree from applying it once, and a great deal of the library treats what it hands back as settled.
`Simplify` was unaffected, since it normalises again anyway.

Found by `canoncheck`, the canonical-form harness: it was the only idempotence failure in 834
generated expressions, and the count is now zero.
[#930](https://github.com/asc-community/AngouriMath/issues/930).

### The parity identities are applied

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,14 @@ private static bool OrHalfTurn(System.Func<Complex, (bool Found, Entity? Value)>
}
if (lookup(HalfTurn(arg)) is (true, { } turned))
{
res = oddOverHalfTurn ? -turned : turned;
// Folded here rather than left to whoever receives it. The negation is built
// above a value that is already in its final form, so nothing downstream is
// obliged to look at it again -- and where the answer is wrapped rather than
// rebuilt, nothing does: cos(0 ^ y) came back as `-(-1) provided ...` and
// needed a second InnerSimplified to become `1 provided ...`, which is an
// idempotence failure rather than a cosmetic one.
// https://github.com/asc-community/AngouriMath/issues/930
res = oddOverHalfTurn ? (-turned).InnerSimplified : turned;
return true;
}
res = null;
Expand Down
28 changes: 28 additions & 0 deletions Sources/Tests/UnitTests/Common/InnerSimplifyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,34 @@ public void InequalityShouldBeNaN(string expr) {
expr.ToEntity().Evaled.ShouldBe(MathS.NaN);
expr.ToEntity().InnerSimplified.ShouldBe(MathS.NaN);
}

/// <summary>
/// Normalising a normalised expression leaves it alone. `cos(0 ^ y)` did not: the
/// exact value of a cosine reached through a half turn was built as a negation over
/// the value it turned to and handed back unfolded, so the answer arrived as
/// `-(-1) provided ...` and became `1 provided ...` only if something normalised it
/// again. https://github.com/asc-community/AngouriMath/issues/930
/// </summary>
/// <remarks>
/// Compared as entities rather than as strings, deliberately. The general failure here
/// includes two trees that print alike and differ — `(x + y) + a` and `x + (y + a)` do
/// — so a comparison of printed forms would wave those through. See
/// `Docs/Contributing/CanonicalForm.md`.
/// </remarks>
[Theory]
[InlineData("cos(0 ^ y)")]
[InlineData("sin(0 ^ y)")]
[InlineData("cos(0)")]
[InlineData("cos(pi)")]
[InlineData("sin(pi / 6)")]
[InlineData("cos(pi / 3)")]
[InlineData("tan(pi / 4)")]
[InlineData("x + sin(0 ^ y)")]
public void InnerSimplifiedIsIdempotent(string expr)
{
var once = expr.ToEntity().InnerSimplified;
Assert.Equal(once, once.InnerSimplified);
}
}
}

Expand Down
Loading