diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 698516842..9bdf3c99a 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -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
diff --git a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs
index 5acba0df4..1a37d9ec9 100644
--- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs
+++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Omni/Evaluation.Omni.Classes.cs
@@ -470,8 +470,15 @@ partial record Piecewise
///
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);
}
@@ -479,7 +486,7 @@ protected override Entity InnerSimplify(bool isExact)
notYetDecidable:
var res = new List();
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
diff --git a/Sources/Tests/UnitTests/Core/UndefinedPiecewiseConditionTest.cs b/Sources/Tests/UnitTests/Core/UndefinedPiecewiseConditionTest.cs
new file mode 100644
index 000000000..523e8e421
--- /dev/null
+++ b/Sources/Tests/UnitTests/Core/UndefinedPiecewiseConditionTest.cs
@@ -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
+{
+ ///
+ /// A piecewise case whose predicate evaluates to NaN -- an order comparison of a number off
+ /// the real line, i - 1 < 0 -- 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.
+ ///
+ [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));
+ }
+ }
+}