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
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,19 @@ its rows in the order it finds them, which can differ from the eliminator's
| `MathS.Equations("x*y", "x + y^2 - 1").Solve("x", "y")` | `[[1, 0]]` | `[[1, 0], [0, 1], [0, -1]]` |
| `MathS.Equations("x*y + 3*x - 5*y - 15", "x^2 - 3*x - 4*y - 10").Solve("x", "y")` | `[[1, -3], [2, -3]]` | `[[5, 0], [1, -3], [2, -3]]` |

### `round` is exact, and an infinity is its own nearest integer

**Silent for a long rational, not silent for an infinity.** `round` of a rational with more digits than the precision was
taken through its decimal, which had already rounded it onto the integer beside it, and `round` of an infinity threw
`OverflowException`. `floor` and `ceil` were fixed for both before; `round` now does as they do
([#2282](https://github.com/asc-community/AngouriMath/issues/2282)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"round(10^120 + 2/3)".ToEntity().Evaled` | `10^120` | `10^120 + 1` |
| `"round(10^120 + 3/2)".ToEntity().Evaled` | `10^120` | `10^120 + 2` |
| `"round(+oo)".ToEntity().Evaled` | throws `OverflowException` | `+oo` |

### A squared sine and cosine with one coefficient are found anywhere in a sum

**Silent: an identity reaches its value however the sum is written.** `k sin(x)^2 + k cos(x)^2` was reduced to `k`
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Convenience/SpellingsAtRiskTest.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/RoundIsExactTest.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/Core/FactorialCacheIsThreadSafeTest.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 @@ -1291,6 +1291,19 @@ private static PeterO.Numbers.EInteger RoundedQuotient(PeterO.Numbers.ERational
: (value.Numerator.Sign < 0 ? quotient - PeterO.Numbers.EInteger.One : quotient);
}

/// <summary>
/// The integer nearest to a rational, exactly, and the even one of the two at a tie: its floor, or
/// the integer above where the rational is more than a half above it, or a half above it and the
/// floor is odd.
/// </summary>
private static PeterO.Numbers.EInteger RoundedHalfEven(PeterO.Numbers.ERational value)
{
var floor = RoundedQuotient(value, up: false);
var aboveTheFloor = value.Subtract(PeterO.Numbers.ERational.FromEInteger(floor))
.CompareTo(PeterO.Numbers.ERational.Create(1, 2));
return aboveTheFloor > 0 || aboveTheFloor == 0 && !floor.IsEven ? floor + PeterO.Numbers.EInteger.One : floor;
}

public partial record Ceilf
{
// Defined everywhere in the complex plane, taken componentwise.
Expand Down Expand Up @@ -1325,7 +1338,13 @@ protected override Entity InnerSimplify(bool isExact)
a => a switch
{
Integer n => n,
Rational n => Integer.Create(n.EDecimal.RoundHalfEven().ToEInteger()),
// As in Floorf: an infinity is its own nearest integer and NaN propagates,
// where EInteger refused both and round(+oo) threw, and a rational is
// rounded from its numerator and denominator, where through EDecimal
// round(10^120 + 2/3) came out 10^120.
// https://github.com/asc-community/AngouriMath/issues/2282
Real { IsFinite: false } n => n,
Rational n => Integer.Create(RoundedHalfEven(n.ERational)),
Real n when !isExact => Integer.Create(n.EDecimal.RoundHalfEven().ToEInteger()),
Complex n when !isExact => Complex.Create(
n.RealPart.EDecimal.RoundHalfEven(), n.ImaginaryPart.EDecimal.RoundHalfEven()),
Expand Down
43 changes: 43 additions & 0 deletions Sources/Tests/UnitTests/Convenience/RoundIsExactTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// 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;

namespace AngouriMath.Tests.Convenience
{
/// <summary>
/// round of a rational is the nearest integer, exactly, and the even one at a tie, and an infinity is its own: through
/// EDecimal a rational of more digits than the precision was rounded onto the integer beside it first, and
/// round(10^120 + 2/3) came out 10^120, while round(+oo) threw OverflowException. floor and ceil had both defects and
/// were fixed in #830 and #1807.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2282">#2282</a>
/// </summary>
[Trait("Area", "Convenience")]
public sealed class RoundIsExactTest
{
[Theory]
[InlineData("round(10^120 + 2/3)", "10^120 + 1")]
[InlineData("round(10^120 - 2/3)", "10^120 - 1")]
[InlineData("round(10^120 + 3/2)", "10^120 + 2")]
[InlineData("round(10^120 + 1/2)", "10^120")]
[InlineData("round(-10^120 - 2/3)", "-10^120 - 1")]
[InlineData("round(-10^400 + 1/3)", "-10^400")]
[InlineData("round(2/3)", "1")]
[InlineData("round(5/2)", "2")]
[InlineData("round(7/2)", "4")]
[InlineData("round(-5/2)", "-2")]
[InlineData("round(-7/2)", "-4")]
[InlineData("round(+oo)", "+oo")]
[InlineData("round(-oo)", "-oo")]
public void ItIsTheNearestInteger(string input, string expected)
{
Assert.Equal(expected.ToEntity().InnerSimplified, input.ToEntity().InnerSimplified);
Assert.Equal(expected.ToEntity().InnerSimplified, input.ToEntity().Evaled);
}
}
}
Loading