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 @@ -194,6 +194,18 @@ and `Simplify` passed that on as `not x = 0`, though at `x = 0` the input is `-y
| `"sgn(NaN)".ToEntity().Evaled` | `1` | `NaN` |
| `"sgn(-1 provided false)".ToEntity().Evaled` | `1` | `NaN` |

### A negation of a signed term is printed in brackets

**Silent: `-(-2/3)` prints as `-(-2/3)`, where it printed as `--2/3`.** A negation prints as a bare `-` in front of what
it negates, and a negative integer was bracketed by its priority, but a negative rational, a negation, and `+oo` were
not. `--5/4` reads back as `-(-5) / 4`, which has the same value as a different expression, and is easy to misread
([#2264](https://github.com/asc-community/AngouriMath/issues/2264)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"sinh(-5/4)".ToEntity().ToString()` | `(e ^ (-5/4) - e ^ (--5/4)) / 2` | `(e ^ (-5/4) - e ^ (-(-5/4))) / 2` |
| `new Entity.Mulf(-1, new Entity.Mulf(-1, x)).ToString()` | `--x` | `-(-x)` |

### A matrix is divided, raised to a power and compared as a matrix

**Silent: wrong answers about matrices become right, or stay written.** Where an operation had no answer of its own
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Convenience/RealAndImaginaryPartTest.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/NegationOfASignedTermPrintsTest.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/DoubleFactorialTest.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 @@ -34,10 +34,16 @@ public partial record Mulf
// unbracketed -- but `mod` shares this precedence level and is not associative, so a
// `mod` on the right must be bracketed or it is re-read as the outer operator:
// `2 * (3 mod 2)` is 2 and `(2 * 3) mod 2` is 0.
private protected override string StringizeNode() =>
(Multiplier is Integer(-1) && !MathS.Diagnostic.OutputExplicit ? "-"
: Multiplier.Stringize(Multiplier.Priority < Priority) + " * ")
+ Multiplicand.Stringize(Multiplicand.Priority < Priority || Multiplicand is Modf);
private protected override string StringizeNode()
{
var multiplicand = Multiplicand.Stringize(Multiplicand.Priority < Priority || Multiplicand is Modf);
if (Multiplier is not Integer(-1) || MathS.Diagnostic.OutputExplicit)
return Multiplier.Stringize(Multiplier.Priority < Priority) + " * " + multiplicand;
// A negation of something printed with a sign of its own is bracketed: -(-2/3), not --2/3, which is
// read back as -(-2) / 3; and -(+oo), not -+oo. A negative rational is not bracketed by its priority,
// which is a quotient's, as a negative integer is. https://github.com/asc-community/AngouriMath/issues/2264
return multiplicand.StartsWith("-") || multiplicand.StartsWith("+") ? "-(" + multiplicand + ")" : "-" + multiplicand;
}
/// <inheritdoc/>
public override string ToString() => Stringize();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// 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;
using AngouriMath.Extensions;
using Xunit;
using static AngouriMath.Entity;
using static AngouriMath.Entity.Number;

namespace AngouriMath.Tests.Convenience
{
/// <summary>
/// A negation of something printed with a sign of its own is bracketed, so it reads back as itself: <c>-(-2/3)</c>
/// printed as <c>--2/3</c>, read back as <c>-(-2) / 3</c>, and <c>-(+oo)</c> as <c>-+oo</c>.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2264">#2264</a>
/// </summary>
[Trait("Area", "Convenience")]
public sealed class NegationOfASignedTermPrintsTest
{
private static Entity Negated(Entity e) => new Mulf(Integer.Create(-1), e);

public static TheoryData<Entity, string> Negations => new()
{
{ Negated(Rational.Create(-2, 3)), "-(-2/3)" },
{ Negated(Real.PositiveInfinity), "-(+oo)" },
{ Negated(Negated(MathS.Var("x"))), "-(-x)" },
{ Negated(Integer.Create(-3)), "-(-3)" },
{ Negated(MathS.Var("x")), "-x" },
{ Negated(Rational.Create(2, 3)), "-2/3" },
};

[Theory]
[MemberData(nameof(Negations))]
public void ItPrintsWithOneSignAndReadsBackToItsValue(Entity negation, string printed)
{
Assert.Equal(printed, negation.Stringize());
// The parser folds a negated number into the number, so -(-2/3) reads back as 2/3: the same value, and for
// a symbol the same tree.
var x = "7/10".ToEntity();
Assert.Equal(negation.Substitute("x", x).Evaled, negation.Stringize().ToEntity().Substitute("x", x).Evaled);
}

[Fact]
public void AHyperbolicSineOfANegativeRationalHasNoDoubleSign()
=> Assert.DoesNotContain("--", "sinh(-5/4)".ToEntity().Stringize());
}
}
Loading