diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 05b616dc3..1d90bc43c 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -437,8 +437,9 @@ one where both sides are real, and `NaN` where either is not - A constant is taken over `Complex` where the expression is compiled over it. As a `long`, a constant widened only to `double`, so `sqrt(-1)`, `ln(-1)` and `arcsin(2)` were `NaN` and `1/0` was `+oo` ([#2235](https://github.com/asc-community/AngouriMath/issues/2235)). -- An Iverson bracket, an `int?`, and a `provided` integer, a `long?`, are widened to `double` before a function takes - them. They threw `KeyNotFoundException` or `AngouriBugException` +- A `provided` integer, a `long?`, and an Iverson bracket, an `int?`, are widened to `double` before a function or a + `piecewise` takes them. A `provided` integer threw `KeyNotFoundException` or `AngouriBugException` there, and an + Iverson bracket did not compile at all in 2.6.0 ([#2237](https://github.com/asc-community/AngouriMath/issues/2237)). - `sgn(0)` is `0`, where `z / |z|` made it `NaN` ([#2238](https://github.com/asc-community/AngouriMath/issues/2238)). @@ -447,6 +448,7 @@ one where both sides are real, and `NaN` where either is not | `"x + sqrt(-1)".ToEntity().Compile("x")(0)` | `(NaN, 0)` | `(6.123233995736766E-17, 1)` | | `"x + 1/0".ToEntity().Compile("x")(0)` | `(Infinity, 0)` | `(NaN, NaN)` | | `"x + sgn(-1 provided pi > 3)".ToEntity().Compile("x")(1)` | throws `AngouriBugException` | `(0, 0)` | +| `"piecewise(piecewise(-1 provided x > 0, 3) provided x > 0, x) + x".ToEntity().Compile("x")(1)` | throws `KeyNotFoundException` | `0` | | `"sgn(x)".ToEntity().Compile("x")(0)` | `(NaN, NaN)` | `(0, 0)` | ### A compiled product of real numbers is real at an infinity diff --git a/Sources/.editorconfig b/Sources/.editorconfig index 9bcb8732a..18c8f72ec 100644 --- a/Sources/.editorconfig +++ b/Sources/.editorconfig @@ -396,6 +396,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed [Tests/UnitTests/Common/LinqCompilationValueTest.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/Common/PiecewiseOfNullableCompilationTest.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/Common/CompileMatrixTest.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 diff --git a/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs b/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs index e84a5ae7f..39e17b585 100644 --- a/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs +++ b/Sources/AngouriMath/Functions/Compilation/IntoLinq/CompilationProtocol.cs @@ -386,6 +386,10 @@ private Expression HandlePiecewise(IEnumerable en) children[i] = ConvertType(children[i], typeof(bool?)); statements++; } + // A case that is an Iverson bracket or a provided integer is widened to a double, as a function's + // operand is: MaxType has no int? or long?. https://github.com/asc-community/AngouriMath/issues/2237 + else + children[i] = NumericOperand(children[i]); if (statements > 0 && statements * 2 != children.Length) throw new UncompilableNodeException("A piecewise whose cases are statements and numbers at once has no compiled form"); diff --git a/Sources/Tests/UnitTests/Common/PiecewiseOfNullableCompilationTest.cs b/Sources/Tests/UnitTests/Common/PiecewiseOfNullableCompilationTest.cs new file mode 100644 index 000000000..5f571aa11 --- /dev/null +++ b/Sources/Tests/UnitTests/Common/PiecewiseOfNullableCompilationTest.cs @@ -0,0 +1,40 @@ +// +// 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.Numerics; +using AngouriMath; +using AngouriMath.Extensions; +using Xunit; + +namespace AngouriMath.Tests.Common +{ + /// + /// A piecewise whose case is an Iverson bracket or a provided integer compiles in the LINQ compiler, where it threw + /// KeyNotFoundException: its case types, an int? and a long?, were not in the widening table. + /// #2237 + /// + [Trait("Area", "Common")] + public sealed class PiecewiseOfNullableCompilationTest + { + [Theory] + [InlineData("sin(piecewise(iverson(x > 0) provided x > 0, sin(2)))", 2)] + [InlineData("sin(piecewise(iverson(x > 0) provided x > 0, sin(2)))", -2)] + [InlineData("exp(piecewise(2^x provided x > 0, iverson(x > 0)))", -1)] + [InlineData("abs(piecewise((-1)^2 provided x > 0, iverson(-1 > 0)))", 1)] + [InlineData("piecewise(iverson(sinh(3) > 0) provided x > 0, tan(-1))", 1)] + [InlineData("piecewise(piecewise(-1 provided x > 0, 3) provided x > 0, x) + x", 1)] + [InlineData("piecewise(piecewise(-1 provided x > 0, 3) provided x > 0, x) + x", -1)] + public void ItIsWhatEvaluationGives(string expression, double at) + { + var expected = ((Entity.Number.Complex)expression.ToEntity().Substitute("x", at).Evaled).ToNumerics(); + var linq = expression.ToEntity().Compile("x")(at); + var real = expression.ToEntity().Compile("x")(at); + Assert.True(Complex.Abs(expected - linq) < 1e-12, $"{expression} at {at}: {expected} evaluated, {linq} by LINQ"); + Assert.True(System.Math.Abs(expected.Real - real) < 1e-12, $"{expression} at {at}: {expected} evaluated, {real} over double"); + } + } +}