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
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ the working digits, or `MathS.Settings.PrecisionErrorZeroRange` where a caller h
still that integer, so `e^(-123.456)` is 0 as it was.
[#1338](https://github.com/asc-community/AngouriMath/issues/1338).

### A piecewise case whose condition is undefined is passed over

**A different answer.** A case whose predicate evaluates to NaN -- an order comparison of a number
off the real line, `i < 0`, the complex numbers not being ordered -- made the whole piecewise NaN,
even where another case held. It holds no more than a false predicate does now: the case is never
taken, and the cases after it are read as before. The derivative of an antiderivative written with
an arm for each sign of a quantity that is not real, `x^2 provided not x = 0` beside
`x^3 provided -13/10 i - 3/5 < 0`, came out NaN that way, where it is `2x`.

| Input | Was (2.5.0) | Now |
|---|---|---|
| `"piecewise(1 provided i < 0, 2 provided 1 > 0)".ToEntity().InnerSimplified` | `NaN` | `2` |
| `"piecewise(2 * x provided not x = 0, 3 * x ^ 2 provided i - 1 < 0)".ToEntity().InnerSimplified` | `NaN` | `2 * x` where `x` is not 0 |
| `"piecewise(1 provided i < 0, 2 provided i > 0)".ToEntity().InnerSimplified` | `NaN` | `NaN`, as before: no case holds |

### The factorial far from zero, and a quotient by a number whose modulus is nearly whole

The numerical factorial is Spouge's approximation, and it holds right of zero. Used left of zero it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,16 +470,23 @@ partial record Piecewise
/// <inheritdoc/>
protected override Entity InnerSimplify(bool isExact)
{
// A predicate that evaluates to NaN -- `i < 0`, the complex numbers not being
// ordered -- holds no more than a false one does: the case is never taken, and the
// cases after it are read as they would be. Kept, it made the whole piecewise NaN,
// so that `piecewise(2x provided not x = 0, 3x^2 provided i - 1 < 0)` had no value
// at any point, and the derivative of an antiderivative written with an arm for
// each sign of a quantity that is not real came out NaN where it is `2x`.
foreach (var oneCase in Cases)
{
if (oneCase.Predicate.Evaled.IsNaN) continue;
if (oneCase.Predicate.Evaled is not Boolean) goto notYetDecidable;
if (oneCase.Predicate.Evaled == Boolean.True) return oneCase.Expression.InnerSimplified(isExact);
}
return MathS.NaN;
notYetDecidable:
var res = new List<Providedf>();
foreach (var (@case, srcCase) in (Cases, Cases.Select(c => c.New(c.Expression.InnerSimplified(isExact), c.Predicate.InnerSimplified(isExact)))).Zip()) {
if (@case.Predicate.Evaled == Boolean.False) continue;
if (@case.Predicate.Evaled == Boolean.False || @case.Predicate.Evaled.IsNaN) continue;
var toAdd = srcCase.Expression is Providedf(var inner, var pred) ? new Providedf(inner, (srcCase.Predicate & pred).InnerSimplified(isExact)) : srcCase;

// A piecewise takes its first matching case, and both rules below follow
Expand Down
53 changes: 53 additions & 0 deletions Sources/Tests/UnitTests/Core/UndefinedPiecewiseConditionTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// 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;
using AngouriMath;
using AngouriMath.Extensions;
using Xunit;
using static AngouriMath.Entity;

namespace AngouriMath.Tests.Core
{
/// <summary>
/// A piecewise case whose predicate evaluates to NaN -- an order comparison of a number off
/// the real line, <c>i - 1 &lt; 0</c> -- is never taken, as a false one is not, and the cases
/// after it are read as they would be. Kept, it made the whole piecewise NaN.
/// </summary>
[Trait("Area", "Core")]
public sealed class UndefinedPiecewiseConditionTest
{
private static void Near(double expected, Entity value)
=> Assert.True(Math.Abs((double)((Number.Complex)value.EvalNumerical()).RealPart - expected) < 1e-12, $"{value} is not {expected}");

[Fact]
public void TheCaseIsSkippedAndTheRestKept()
{
var simplified = "piecewise(2 * x provided not x = 0, 3 * x ^ 2 provided i - 1 < 0)".ToEntity().InnerSimplified;
Assert.False(simplified.Evaled.IsNaN);
Near(0.6, simplified.Substitute("x", 0.3));
}

[Fact]
public void AnUndefinedCaseBeforeADecidedOneIsPassedOver()
=> Assert.Equal(Number.Integer.Create(2), "piecewise(1 provided i < 0, 2 provided 1 > 0)".ToEntity().InnerSimplified);

[Fact]
public void WithNoCaseThatHoldsItIsStillNaN()
=> Assert.True("piecewise(1 provided i < 0, 2 provided i > 0)".ToEntity().InnerSimplified.Evaled.IsNaN);

// How an antiderivative with an arm for each sign of a quantity off the real line is
// differentiated back: the arm for the negative sign is undefined there, and the
// derivative was NaN where it is that of the arm which holds.
[Fact]
public void TheDerivativeIsThatOfTheCaseThatHolds()
{
var derivative = "piecewise(x ^ 2 provided not x = 0, x ^ 3 provided -13/10 * i - 3/5 < 0)".ToEntity().Differentiate("x");
Near(0.6, derivative.Substitute("x", 0.3));
}
}
}
Loading