diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 1481f5f6a..634038644 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -628,6 +628,18 @@ conjugate as `0 - 1i`, in decimals, on the one instance that every `i` is, in ev
|---|---|---|
| `Complex.ImaginaryOne.Conjugate`, after `(1 / Complex.ImaginaryOne).Evaled` with `DowncastingEnabled` off | `0 - 1i` | `-i` |
+### `max` and `min` are defined where their arguments are real
+
+**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)).
+
+| 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` |
+
### `conjugate`, `conj` and `arg` are functions
**Not silent: these names followed by a bracket are now the complex conjugate and the principal argument, where they
diff --git a/Sources/.editorconfig b/Sources/.editorconfig
index 059d1e5ff..5cbc5b29f 100644
--- a/Sources/.editorconfig
+++ b/Sources/.editorconfig
@@ -237,6 +237,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Convenience/BinomialTest.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/MaxMinOffTheRealLineTest.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/ConjugateAndArgumentTest.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/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs
index f8a8bc6fd..10cbe23cf 100644
--- a/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs
+++ b/Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Arithmetics.Classes.cs
@@ -1327,7 +1327,10 @@ protected override Entity InnerSimplify(bool isExact)
public partial record Minf
{
- private protected override Entity IntrinsicCondition => Boolean.True;
+ // Defined where both arguments are real: the complex numbers are not ordered, and the node is
+ // read over the reals, so min(i, i) is NaN. The condition said True, and min(x, x) simplified
+ // to x, which has a value at x = i. https://github.com/asc-community/AngouriMath/issues/2256
+ private protected override Entity IntrinsicCondition => Left.In(MathS.Sets.R) & Right.In(MathS.Sets.R);
///
protected override Entity InnerSimplify(bool isExact)
@@ -1337,7 +1340,8 @@ protected override Entity InnerSimplify(bool isExact)
// Only ordered arguments compare. A complex one is left alone rather
// than guessed at, which is what SymPy's Min does too.
(Real l, Real r) => l.EDecimal.CompareTo(r.EDecimal) <= 0 ? l : r,
- var (l, r) when l == r => l,
+ // min(a, a) is a where a is real, and has no value where it is not.
+ var (l, r) when l == r => l.Provided(l.In(MathS.Sets.R)).InnerSimplified(isExact),
_ => null
},
(@this, a, b) => ((Minf)@this).New(a, b), isExact);
@@ -1345,7 +1349,8 @@ protected override Entity InnerSimplify(bool isExact)
public partial record Maxf
{
- private protected override Entity IntrinsicCondition => Boolean.True;
+ // As Minf's: defined where both arguments are real. https://github.com/asc-community/AngouriMath/issues/2256
+ private protected override Entity IntrinsicCondition => Left.In(MathS.Sets.R) & Right.In(MathS.Sets.R);
///
protected override Entity InnerSimplify(bool isExact)
@@ -1353,7 +1358,7 @@ protected override Entity InnerSimplify(bool isExact)
(a, b) => (a, b) switch
{
(Real l, Real r) => l.EDecimal.CompareTo(r.EDecimal) >= 0 ? l : r,
- var (l, r) when l == r => l,
+ var (l, r) when l == r => l.Provided(l.In(MathS.Sets.R)).InnerSimplified(isExact),
_ => null
},
(@this, a, b) => ((Maxf)@this).New(a, b), isExact);
diff --git a/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs b/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs
new file mode 100644
index 000000000..7e69ac762
--- /dev/null
+++ b/Sources/Tests/UnitTests/Convenience/MaxMinOffTheRealLineTest.cs
@@ -0,0 +1,56 @@
+//
+// 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;
+
+namespace AngouriMath.Tests.Convenience
+{
+ ///
+ /// max and min are defined where both arguments are real, as their values are: the complex numbers are not
+ /// ordered, and max(i, i) is NaN. Their condition said True, so max(x, x) simplified to
+ /// x, which has a value at x = i.
+ /// #2256
+ ///
+ [Trait("Area", "Convenience")]
+ public sealed class MaxMinOffTheRealLineTest
+ {
+ [Theory]
+ [InlineData("max(x, 2)", "i", false)]
+ [InlineData("min(x, 2)", "1/2 + 6/5 * i", false)]
+ [InlineData("max(x, 2)", "-3", true)]
+ [InlineData("min(x, x)", "pi", true)]
+ public void TheConditionIsWhereTheValueIs(string expression, string at, bool defined)
+ {
+ var condition = expression.ToEntity().DomainCondition.Substitute("x", at.ToEntity()).Evaled;
+ Assert.Equal(defined ? Entity.Boolean.True : Entity.Boolean.False, condition);
+ // Where it is not defined, evaluation gives NaN or leaves the node, as max(i, 2) is left: no number.
+ var value = expression.ToEntity().Substitute("x", at.ToEntity()).Evaled;
+ Assert.Equal(defined, value is Entity.Number && !value.IsNaN);
+ }
+
+ [Theory]
+ [InlineData("max(i, i)")]
+ [InlineData("min(1 + i, 1 + i)")]
+ public void OfAComplexNumberWithItselfItHasNoValue(string expression)
+ {
+ Assert.True(expression.ToEntity().Evaled.IsNaN, $"{expression} evaluates to {expression.ToEntity().Evaled}");
+ Assert.True(expression.ToEntity().Simplify().IsNaN, $"{expression} simplifies to {expression.ToEntity().Simplify()}");
+ }
+
+ [Theory]
+ [InlineData("max(x, x)")]
+ [InlineData("min(x, x)")]
+ public void ASimplificationHasNoValueWhereTheOriginalHasNone(string expression)
+ {
+ var simplified = expression.ToEntity().Simplify();
+ Assert.True(simplified.Substitute("x", "i".ToEntity()).Evaled.IsNaN, $"{simplified} at i");
+ Assert.Equal(Entity.Number.Integer.Create(3), simplified.Substitute("x", 3).Evaled);
+ }
+ }
+}
diff --git a/Sources/Tests/UnitTests/Convenience/RoundMinMaxGcdTest.cs b/Sources/Tests/UnitTests/Convenience/RoundMinMaxGcdTest.cs
index f52e6f0d7..10d7eb41e 100644
--- a/Sources/Tests/UnitTests/Convenience/RoundMinMaxGcdTest.cs
+++ b/Sources/Tests/UnitTests/Convenience/RoundMinMaxGcdTest.cs
@@ -68,9 +68,12 @@ public void RoundIsNotFloorOfTheArgumentPlusAHalf(string at)
public void MinAndMaxCompareWhereTheArgumentsAreOrdered(string input, int expected)
=> Assert.Equal(Entity.Number.Integer.Create(expected), input.ToEntity().Simplify());
+ // That thing where it is real: the complex numbers are not ordered, and min(i, i) has no value.
+ // https://github.com/asc-community/AngouriMath/issues/2256
[Theory]
- [InlineData("min(x, x)", "x")]
- [InlineData("max(x, x)", "x")]
+ [InlineData("min(x, x)", "x provided x in RR")]
+ [InlineData("max(x, x)", "x provided x in RR")]
+ [InlineData("min(3, 3)", "3")]
public void MinAndMaxOfOneThingWithItselfIsThatThing(string input, string expected)
=> Assert.Equal(expected.ToEntity(), input.ToEntity().Simplify());