diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md index 2977ffe9a..e11efd135 100644 --- a/BREAKING-CHANGES.md +++ b/BREAKING-CHANGES.md @@ -1041,13 +1041,15 @@ list its poles beside the other points it has no value at, which says nothing ne **Silent: `max(x, x)` and `min(x, x)` simplify to `x provided x in RR`, where they were `x`.** The complex numbers are not ordered, and both nodes are read over the reals, so `max(i, i)` has no value. Their condition said they were -defined everywhere, and `max(x, x)` simplified to `x`, which has a value at `i`. A comparison of two real arguments is -unchanged ([#2256](https://github.com/asc-community/AngouriMath/issues/2256)). +defined everywhere, and `max(x, x)` simplified to `x`, which has a value at `i`. Compiled by either compiler over +`Complex`, `max(x, x)` was `x` there too, and is `NaN`. A comparison of two real arguments is unchanged +([#2256](https://github.com/asc-community/AngouriMath/issues/2256)). | Input | Was (2.6.0) | Now | |---|---|---| | `"max(x, x)".ToEntity().Simplify()` | `x` | `x provided x in RR` | | `"max(x, 2)".ToEntity().DomainCondition` | `True` | `x in RR` | +| `"max(x, x)".ToEntity().Compile("x").Call(new Complex(0.5, 1.2))` | `<0.5; 1.2>` | `` | ### Over the complex numbers, an equation in and out of a modulus is solved in the parts of `x` diff --git a/Sources/AngouriMath/Functions/Compilation/RealOnly.cs b/Sources/AngouriMath/Functions/Compilation/RealOnly.cs index b9841f1dc..2e8542681 100644 --- a/Sources/AngouriMath/Functions/Compilation/RealOnly.cs +++ b/Sources/AngouriMath/Functions/Compilation/RealOnly.cs @@ -47,9 +47,11 @@ public static NumericsComplex Mod(NumericsComplex a, NumericsComplex b) } /// - /// The larger of two numbers, or NaN where they differ and are not both real: the complex - /// numbers are not ordered, and the interpreter leaves max(2, 1 + i) unevaluated. - /// Two equal arguments are their own maximum, which the interpreter says as well. + /// The larger of two numbers, or NaN where they are not both real: the complex numbers are + /// not ordered, and the interpreter leaves max(2, 1 + i) unevaluated and gives + /// max(i, i) no value. Two equal complex arguments had themselves for a maximum + /// here after the interpreter stopped saying so, and compiled max(x, x) was + /// x off the real line. https://github.com/asc-community/AngouriMath/issues/2256 /// public static NumericsComplex Max(NumericsComplex a, NumericsComplex b) => Ordered(a, b, larger: true); @@ -59,9 +61,9 @@ public static NumericsComplex Min(NumericsComplex a, NumericsComplex b) => Ordered(a, b, larger: false); private static NumericsComplex Ordered(NumericsComplex a, NumericsComplex b, bool larger) - => a == b ? a - : a.Imaginary is not 0 || b.Imaginary is not 0 || double.IsNaN(a.Real) || double.IsNaN(b.Real) + => a.Imaginary is not 0 || b.Imaginary is not 0 || double.IsNaN(a.Real) || double.IsNaN(b.Real) ? new NumericsComplex(double.NaN, double.NaN) + : a == b ? a : new NumericsComplex(larger == a.Real >= b.Real ? a.Real : b.Real, 0); } } diff --git a/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs b/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs index 7e69ac762..0b4793036 100644 --- a/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs +++ b/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs @@ -52,5 +52,28 @@ public void ASimplificationHasNoValueWhereTheOriginalHasNone(string expression) Assert.True(simplified.Substitute("x", "i".ToEntity()).Evaled.IsNaN, $"{simplified} at i"); Assert.Equal(Entity.Number.Integer.Create(3), simplified.Substitute("x", 3).Evaled); } + + /// + /// Compiled, they have no value where evaluation has none. FastExpression and the LINQ + /// compiler over Complex answered max(x, x) at 1/2 + 6/5 i with + /// 1/2 + 6/5 i, and so max(x, x) * 2 with 1 + 12/5 i. + /// + [Theory] + [InlineData("max(x, x)")] + [InlineData("min(x, x)")] + [InlineData("max(x, x) * 2")] + public void CompiledItHasNoValueWhereEvaluationHasNone(string expression) + { + var entity = expression.ToEntity(); + var fastExpression = entity.Compile("x"); + var linq = entity.Compile("x"); + var offTheLine = new System.Numerics.Complex(0.5, 1.2); + Assert.True(double.IsNaN(fastExpression.Call(offTheLine).Real), $"FastExpression gives {fastExpression.Call(offTheLine)}"); + Assert.True(double.IsNaN(linq(offTheLine).Real), $"LINQ gives {linq(offTheLine)}"); + var onTheLine = new System.Numerics.Complex(0.5, 0); + var value = entity.Substitute("x", "1/2".ToEntity()).EvalNumerical().RealPart.EDecimal.ToDouble(); + Assert.Equal(value, fastExpression.Call(onTheLine).Real); + Assert.Equal(value, linq(onTheLine).Real); + } } }