diff --git a/Sources/AngouriMath/Convenience/MathS.cs b/Sources/AngouriMath/Convenience/MathS.cs index 25e3952e6..0b08e8deb 100644 --- a/Sources/AngouriMath/Convenience/MathS.cs +++ b/Sources/AngouriMath/Convenience/MathS.cs @@ -5166,10 +5166,11 @@ public static class Matrices /// public enum Direction { -#pragma warning disable CS1591 + /// Side by side, so the columns of the second follow the first. Horizontal, + + /// One above the other, so the rows of the second follow the first. Vertical -#pragma warning restore CS1591 } /// diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs index e75e8cd24..3fbe93d9c 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.AbsSignum.Classes.cs @@ -11,12 +11,12 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of signum /// public sealed partial record Signumf(Entity Argument) : Function, IUnaryNode { + /// public Entity NodeChild => Argument; private Signumf New(Entity arg) => @@ -32,6 +32,7 @@ private Signumf New(Entity arg) => /// public sealed partial record Absf(Entity Argument) : Function, IUnaryNode { + /// public Entity NodeChild => Argument; private Absf New(Entity arg) => @@ -41,6 +42,5 @@ private Absf New(Entity arg) => /// protected override Entity[] InitDirectChildren() => new[] { Argument }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs index cbfdfdb5e..239903e16 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.ArcTrigonometry.Classes.cs @@ -11,12 +11,12 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of arcsine /// public sealed partial record Arcsinf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -32,6 +32,7 @@ public sealed partial record Arcsinf(Entity Argument) : TrigonometricFunction, I /// public sealed partial record Arccosf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -47,6 +48,7 @@ public sealed partial record Arccosf(Entity Argument) : TrigonometricFunction, I /// public sealed partial record Arctanf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -62,6 +64,7 @@ public sealed partial record Arctanf(Entity Argument) : TrigonometricFunction, I /// public sealed partial record Arccotanf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -77,6 +80,7 @@ public sealed partial record Arccotanf(Entity Argument) : TrigonometricFunction, /// public sealed partial record Arcsecantf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -92,6 +96,7 @@ public sealed partial record Arcsecantf(Entity Argument) : TrigonometricFunction /// public sealed partial record Arccosecantf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -101,6 +106,5 @@ public sealed partial record Arccosecantf(Entity Argument) : TrigonometricFuncti /// protected override Entity[] InitDirectChildren() => new[] { Argument }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs index ff1dffcf7..08896cf82 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs @@ -11,7 +11,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of derivative /// @@ -64,6 +63,5 @@ public override Entity Replace(Func func) => /// protected override Entity[] InitDirectChildren() => new[] { Expression, Var, Destination }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs index d0e75fd01..2137e6c09 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Complex.Definition.cs @@ -195,55 +195,136 @@ public static bool TryParse(string source, public System.Numerics.Complex ToNumerics() => new System.Numerics.Complex(RealPart.EDecimal.ToDouble(), ImaginaryPart.EDecimal.ToDouble()); -#pragma warning disable CS1591 + /// + /// Narrows to , which is + /// and loses precision the same way. + /// public static explicit operator System.Numerics.Complex(Complex it) => it.ToNumerics(); + /// Their sum. public static Complex operator +(Complex a, Complex b) => OpSum(a, b); + + /// Their difference. public static Complex operator -(Complex a, Complex b) => OpSub(a, b); + + /// Their product. public static Complex operator *(Complex a, Complex b) => OpMul(a, b); + + /// Their quotient. public static Complex operator /(Complex a, Complex b) => OpDiv(a, b); + + /// The operand itself; unary plus changes nothing. public static Complex operator +(Complex a) => a; + + /// Its negation. public static Complex operator -(Complex a) => OpMul(Integer.MinusOne, a); + + // The conversions below share one surprise, stated once here and referred to + // rather than repeated: where MathS.Settings.DowncastingEnabled is on, which is + // the default, an exact whole value arrives as an Integer and an exact ratio as + // a Rational. The declared type is Complex either way, so the difference shows + // up in the runtime type and in what the number prints as, not in the signature. + + /// The number as a . public static implicit operator Complex(sbyte value) => (long)value; + + /// The number as a . public static implicit operator Complex(byte value) => (ulong)value; + + /// The number as a . public static implicit operator Complex(short value) => (long)value; + + /// The number as a . public static implicit operator Complex(ushort value) => (ulong)value; + + /// The number as a . public static implicit operator Complex(int value) => (long)value; + + /// The number as a . public static implicit operator Complex(uint value) => (ulong)value; + + /// + /// The number as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Complex(long value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : Create(value, 0); + /// + /// The number as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Complex(ulong value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : Create(value, 0); + + /// + /// The integer as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Complex(EInteger value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : Create(value, 0); + + /// + /// The ratio as a — a at runtime + /// while downcasting is enabled, and so still exact. + /// public static implicit operator Complex(ERational value) => MathS.Settings.DowncastingEnabled ? Rational.Create(value) : Create(value, 0); + + /// The decimal as a with no imaginary part. public static implicit operator Complex(EDecimal value) => Create(value, 0); + + /// + /// The value as a with no imaginary part. It is read as the + /// binary it is, so a literal such as 0.1f arrives + /// as the value that literal actually holds and not as one tenth. + /// public static implicit operator Complex(float value) => Create(EDecimal.FromSingle(value), 0); + + /// + /// The value as a with no imaginary part, read as the + /// binary it is rather than as the decimal it was + /// written as. + /// public static implicit operator Complex(double value) => Create(EDecimal.FromDouble(value), 0); + + /// + /// The value as a with no imaginary part. A + /// is decimal already, so this one keeps the digits + /// that were written. + /// public static implicit operator Complex(decimal value) => Create(EDecimal.FromDecimal(value), 0); + + /// + /// The .NET complex number as this one, through its two + /// parts and their precision. + /// public static implicit operator Complex(System.Numerics.Complex value) => Create(EDecimal.FromDouble(value.Real), EDecimal.FromDouble(value.Imaginary)); + + /// The pair read as real and imaginary parts. public static implicit operator Complex((int re, int im) v) => Create(v.re, v.im); + + /// The pair read as real and imaginary parts. public static implicit operator Complex((float re, float im) v) => Create(v.re, v.im); - public static implicit operator Complex((decimal re, decimal im) v) => Create(v.re, v.im); - public static implicit operator Complex((double re, double im) v) => Create(v.re, v.im); -#pragma warning restore CS1591 + /// The pair read as real and imaginary parts. + public static implicit operator Complex((decimal re, decimal im) v) => Create(v.re, v.im); + /// The pair read as real and imaginary parts. + public static implicit operator Complex((double re, double im) v) => Create(v.re, v.im); } } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs index 7537e1b85..08e5081ec 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Exponential.Classes.cs @@ -11,7 +11,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of exponential (power) /// @@ -22,8 +21,10 @@ private Powf New(Entity @base, Entity exponent) => ReferenceEquals(Base, @base) && ReferenceEquals(Exponent, exponent) ? this : new(@base, exponent); internal override Priority Priority => Priority.Pow; + /// public Entity NodeFirstChild => Base; + /// public Entity NodeSecondChild => Exponent; /// @@ -41,8 +42,10 @@ public sealed partial record Logf(Entity Base, Entity Antilogarithm) : Function, private Logf New(Entity @base, Entity antilogarithm) => ReferenceEquals(Base, @base) && ReferenceEquals(Antilogarithm, antilogarithm) ? this : new(@base, antilogarithm); + /// public Entity NodeFirstChild => Base; + /// public Entity NodeSecondChild => Antilogarithm; /// @@ -51,6 +54,5 @@ private Logf New(Entity @base, Entity antilogarithm) => protected override Entity[] InitDirectChildren() => new[] { Base, Antilogarithm }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs index 8bddb0629..4d4b9280f 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Factorial.Classes.cs @@ -11,7 +11,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of factorial /// @@ -22,6 +21,7 @@ public sealed partial record Factorialf(Entity Argument) : Function, IUnaryNode // This is still a function for pattern replacement internal override Priority Priority => Priority.Factorial; + /// public Entity NodeChild => Argument; /// @@ -29,6 +29,5 @@ public sealed partial record Factorialf(Entity Argument) : Function, IUnaryNode /// protected override Entity[] InitDirectChildren() => new[] { Argument }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Floors.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Floors.Classes.cs index 05f94f543..de146b873 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Floors.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Floors.Classes.cs @@ -11,7 +11,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of floor: the greatest integer not above the argument. /// @@ -23,6 +22,7 @@ partial record Entity /// public sealed partial record Floorf(Entity Argument) : Function, IUnaryNode { + /// public Entity NodeChild => Argument; private Floorf New(Entity arg) => @@ -42,6 +42,7 @@ private Floorf New(Entity arg) => /// public sealed partial record Ceilf(Entity Argument) : Function, IUnaryNode { + /// public Entity NodeChild => Argument; private Ceilf New(Entity arg) => @@ -51,6 +52,5 @@ private Ceilf New(Entity arg) => /// protected override Entity[] InitDirectChildren() => new[] { Argument }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs index bbd1795cd..2a7bb9229 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Integer.Definition.cs @@ -121,15 +121,47 @@ internal static bool TryParse(string s, /// public Integer IntegerDiv(Integer a) => EInteger.Divide(a.EInteger); -#pragma warning disable CS1591 + // Comparison here is on the value and answers a bool, where the same operators + // on Entity build an inequality node instead. + + /// Whether the first is strictly greater. public static bool operator >(Integer a, Integer b) => a.EInteger.CompareTo(b.EInteger) > 0; + + /// Whether the first is greater or they are equal. public static bool operator >=(Integer a, Integer b) => a.EInteger.CompareTo(b.EInteger) >= 0; + + /// Whether the first is strictly less. public static bool operator <(Integer a, Integer b) => a.EInteger.CompareTo(b.EInteger) < 0; + + /// Whether the first is less or they are equal. public static bool operator <=(Integer a, Integer b) => a.EInteger.CompareTo(b.EInteger) <= 0; + + /// + /// Negative, zero or positive as this is less than, equal to or greater than + /// , which is what sorting wants. + /// + /// + /// Thrown where is , rather than + /// sorting it first as usually would. + /// public int CompareTo(Integer? other) => other is null ? throw new System.ArgumentNullException() : EInteger.CompareTo(other.EInteger); + + /// Their sum, exactly and at any size. public static Integer operator +(Integer a, Integer b) => OpSum(a, b); + + /// Their difference, exactly and at any size. public static Integer operator -(Integer a, Integer b) => OpSub(a, b); + + /// Their product, exactly and at any size. public static Integer operator *(Integer a, Integer b) => OpMul(a, b); + + /// + /// Their quotient — not integer division. 1 / 2 is a half and not + /// zero, which is why the result is a : it is a + /// wherever the division is not exact, and + /// where the divisor is zero. For the truncating kind, + /// see . + /// public static Real operator /(Integer a, Integer b) => (Real)OpDiv(a, b); /// /// The floored remainder, which takes the sign of the divisor: -7 % 3 is 2 and @@ -145,18 +177,44 @@ internal static bool TryParse(string s, .IsZero || truncated.Sign == b.EInteger.Sign ? truncated : truncated.Add(b.EInteger); + /// The operand itself; unary plus changes nothing. public static Integer operator +(Integer a) => a; + + /// Its negation. public static Integer operator -(Integer a) => OpMul(MinusOne, a); + + // Nothing to downcast to here, so unlike the conversions on Real and Rational + // these give exactly what they say. + + /// The number as an . public static implicit operator Integer(sbyte value) => Create(value); + + /// The number as an . public static implicit operator Integer(byte value) => Create(value); + + /// The number as an . public static implicit operator Integer(short value) => Create(value); + + /// The number as an . public static implicit operator Integer(ushort value) => Create(value); + + /// The number as an . public static implicit operator Integer(int value) => Create(value); + + /// The number as an . public static implicit operator Integer(uint value) => Create(value); + + /// The number as an . public static implicit operator Integer(long value) => Create(value); + + /// The number as an . public static implicit operator Integer(ulong value) => Create(value); + + /// + /// The integer as an , of any size — this is the conversion + /// to reach for where a value will not fit in a . + /// public static implicit operator Integer(EInteger value) => Create(value); -#pragma warning restore CS1591 } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs index fe46c5258..fa216a635 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Operators.Classes.cs @@ -11,7 +11,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of sum /// @@ -34,8 +33,10 @@ private Sumf New(Entity augend, Entity addend) => ReferenceEquals(Augend, augend) && ReferenceEquals(Addend, addend) ? this : new(augend, addend); internal override Priority Priority => Priority.Sum; + /// public Entity NodeFirstChild => Augend; + /// public Entity NodeSecondChild => Addend; /// @@ -67,8 +68,10 @@ private Minusf New(Entity minuend, Entity subtrahend) => ReferenceEquals(Minuend, minuend) && ReferenceEquals(Subtrahend, subtrahend) ? this : new(minuend, subtrahend); internal override Priority Priority => Priority.Minus; + /// public Entity NodeFirstChild => Minuend; + /// public Entity NodeSecondChild => Subtrahend; /// @@ -99,8 +102,10 @@ private Mulf New(Entity multiplier, Entity multiplicand) => ReferenceEquals(Multiplier, multiplier) && ReferenceEquals(Multiplicand, multiplicand) ? this : new(multiplier, multiplicand); internal override Priority Priority => Priority.Mul; + /// public Entity NodeFirstChild => Multiplier; + /// public Entity NodeSecondChild => Multiplicand; /// @@ -132,8 +137,10 @@ internal Divf New(Entity dividend, Entity divisor) => ReferenceEquals(Dividend, dividend) && ReferenceEquals(Divisor, divisor) ? this : new(dividend, divisor); internal override Priority Priority => Priority.Div; + /// public Entity NodeFirstChild => Dividend; + /// public Entity NodeSecondChild => Divisor; /// @@ -155,8 +162,10 @@ internal Modf New(Entity dividend, Entity divisor) => ReferenceEquals(Dividend, dividend) && ReferenceEquals(Divisor, divisor) ? this : new(dividend, divisor); internal override Priority Priority => Priority.Mul; + /// public Entity NodeFirstChild => Dividend; + /// public Entity NodeSecondChild => Divisor; /// @@ -164,6 +173,5 @@ internal Modf New(Entity dividend, Entity divisor) => /// protected override Entity[] InitDirectChildren() => new[] { Dividend, Divisor }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs index 92cd286db..b83d2aef3 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rational.Definition.cs @@ -48,18 +48,23 @@ private protected Rational(ERational value) public Integer Denominator => denominator.GetValue(static @this => @this.ERational.Denominator, this); private LazyPropertyA denominator; -#pragma warning disable CS1591 - + /// Takes the ratio out, so that var (r) = rational; works. + /// The value as an . public void Deconstruct(out ERational rational) => rational = ERational; + /// + /// Takes the two parts out, so that var (num, den) = rational; works. + /// They are in lowest terms and the denominator is positive, since that is the + /// only form a is ever built in. + /// + /// The numerator. + /// The denominator. public void Deconstruct(out Integer numerator, out Integer denominator) { numerator = Numerator; denominator = Denominator; } -#pragma warning restore CS1591 - /// public override bool IsExact => true; @@ -161,17 +166,52 @@ internal static bool TryParse(string s, } } -#pragma warning disable CS1591 + // Comparison here is on the value and answers a bool, where the same operators + // on Entity build an inequality node instead. + + /// Whether the first is strictly greater. public static bool operator >(Rational a, Rational b) => a.ERational.CompareTo(b.ERational) > 0; + + /// Whether the first is greater or they are equal. public static bool operator >=(Rational a, Rational b) => a.ERational.CompareTo(b.ERational) >= 0; + + /// Whether the first is strictly less. public static bool operator <(Rational a, Rational b) => a.ERational.CompareTo(b.ERational) < 0; + + /// Whether the first is less or they are equal. public static bool operator <=(Rational a, Rational b) => a.ERational.CompareTo(b.ERational) <= 0; + + /// + /// Negative, zero or positive as this is less than, equal to or greater than + /// , which is what sorting wants. + /// + /// + /// Thrown where is , rather than + /// sorting it first as usually would. + /// public int CompareTo(Rational? other) => other is null ? throw new System.ArgumentNullException() : ERational.CompareTo(other.ERational); + + /// Their sum, exactly. public static Rational operator +(Rational a, Rational b) => OpSum(a, b); + + /// Their difference, exactly. public static Rational operator -(Rational a, Rational b) => OpSub(a, b); + + /// Their product, exactly. public static Rational operator *(Rational a, Rational b) => OpMul(a, b); + + /// + /// Their quotient. rather than , + /// because dividing by zero has to go somewhere and the answer is + /// , which is not a ratio. Every other quotient of two + /// ratios is a ratio and arrives as one. + /// public static Real operator /(Rational a, Rational b) => (Real)OpDiv(a, b); + + /// The operand itself; unary plus changes nothing. public static Rational operator +(Rational a) => a; + + /// Its negation. public static Rational operator -(Rational a) => OpMul(Integer.MinusOne, a); /// @@ -191,26 +231,60 @@ internal static bool TryParse(string s, .IsZero || truncated.IsNegative == b.ERational.IsNegative ? truncated : truncated + b; + // As elsewhere, downcasting is on by default and a whole value arrives as an + // Integer, so the runtime type is narrower than the declared one. + + /// The number as a . public static implicit operator Rational(sbyte value) => (long)value; + + /// The number as a . public static implicit operator Rational(byte value) => (ulong)value; + + /// The number as a . public static implicit operator Rational(short value) => (long)value; + + /// The number as a . public static implicit operator Rational(ushort value) => (ulong)value; + + /// The number as a . public static implicit operator Rational(int value) => (long)value; + + /// The number as a . public static implicit operator Rational(uint value) => (ulong)value; + + /// + /// The number as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Rational(long value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : new Rational(value); + + /// + /// The number as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Rational(ulong value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : new Rational(value); + + /// + /// The integer as a over one — an + /// at runtime while downcasting is enabled. + /// public static implicit operator Rational(EInteger value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : new Rational(ERational.FromEInteger(value)); + + /// + /// The ratio as a , in lowest terms and with a positive + /// denominator: 2/4 arrives as 1/2 and 1/(-2) as + /// (-1)/2. + /// public static implicit operator Rational(ERational value) => Create(value); -#pragma warning restore CS1591 } } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs index d2b4c33ab..58a99a734 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Real.Definition.cs @@ -110,17 +110,63 @@ internal static bool TryParse(string s, /// public double AsDouble() => EDecimal.ToDouble(); -#pragma warning disable CS1591 + // Comparison here is on the value and answers a bool, unlike the operators on + // Entity, which build an inequality node to be solved or evaluated later. + // + // These order NaN rather than refusing to: NaN sorts above every number, so + // NaN > 1 and NaN >= 1 are both true and 1 < NaN is true as well. That is a + // total order and it is not what double does, where every comparison against + // NaN is false. Measured at +1, -1 and NaN rather than assumed, because the + // IEEE habit is the one a reader arrives with. + + /// + /// Whether the first is strictly greater. counts as greater + /// than every number, so NaN > x holds for any real x. + /// public static bool operator >(Real a, Real b) => a.EDecimal.GreaterThan(b.EDecimal); + + /// Whether the first is greater or they are equal. public static bool operator >=(Real a, Real b) => a.EDecimal.GreaterThanOrEquals(b.EDecimal); + + /// + /// Whether the first is strictly less. is less than nothing, + /// and every number is less than it. + /// public static bool operator <(Real a, Real b) => a.EDecimal.LessThan(b.EDecimal); + + /// Whether the first is less or they are equal. public static bool operator <=(Real a, Real b) => a.EDecimal.LessThanOrEquals(b.EDecimal); + + /// + /// Negative, zero or positive as this is less than, equal to or greater than + /// , which is what sorting wants. + /// + /// + /// Thrown where is . Unlike + /// 's usual contract, null does not sort first + /// here; it is refused. + /// public int CompareTo(Real? other) => other is null ? throw new System.ArgumentNullException() : EDecimal.CompareTo(other.EDecimal); + + /// Their sum, computed. public static Real operator +(Real a, Real b) => OpSum(a, b); + + /// Their difference, computed. public static Real operator -(Real a, Real b) => OpSub(a, b); + + /// Their product, computed. public static Real operator *(Real a, Real b) => OpMul(a, b); + + /// + /// Their quotient, computed. Division by zero gives rather + /// than throwing, so a quotient always has an answer of some kind. + /// public static Real operator /(Real a, Real b) => OpDiv(a, b).Downcast(); + + /// The operand itself; unary plus changes nothing. public static Real operator +(Real a) => a; + + /// Its negation, computed. public static Real operator -(Real a) => OpMul(Integer.MinusOne, a); /// /// The floored remainder, which takes the sign of the divisor: -7 % 3 is 2 and @@ -138,22 +184,74 @@ internal static bool TryParse(string s, .IsZero || truncated.IsNegative == b.EDecimal.IsNegative ? truncated : truncated.Add(b.EDecimal, MathS.Settings.DecimalPrecisionContext); + // With MathS.Settings.DowncastingEnabled, which is the default, a value that is + // exactly whole arrives as an Integer and one that is exactly a ratio as a + // Rational. The declared type is Real either way, so the difference is in the + // runtime type and in how the number prints, not in the signature. + + /// The number as a . public static implicit operator Real(sbyte value) => (long)value; + + /// The number as a . public static implicit operator Real(byte value) => (ulong)value; + + /// The number as a . public static implicit operator Real(short value) => (long)value; + + /// The number as a . public static implicit operator Real(ushort value) => (ulong)value; + + /// The number as a . public static implicit operator Real(int value) => (long)value; + + /// The number as a . public static implicit operator Real(uint value) => (ulong)value; + + /// + /// The number as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Real(long value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : new Real(value); + + /// + /// The number as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Real(ulong value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : new Real(value); + + /// + /// The integer as a — an at runtime + /// while downcasting is enabled. + /// public static implicit operator Real(EInteger value) => MathS.Settings.DowncastingEnabled ? Integer.Create(value) : new Real(value); + + /// + /// The ratio as a . While downcasting is enabled it stays a + /// and so stays exact; with it off the ratio is evaluated + /// to the working precision and a third becomes a finite decimal. + /// public static implicit operator Real(ERational value) => MathS.Settings.DowncastingEnabled ? Rational.Create(value) : Create(value.ToEDecimal(MathS.Settings.DecimalPrecisionContext)); + + /// The decimal as a . public static implicit operator Real(EDecimal value) => Create(value); + + /// + /// The value as a , read as the binary + /// it is — so 0.1f becomes the value that literal holds, not one tenth. + /// public static implicit operator Real(float value) => Create(EDecimal.FromSingle(value)); + + /// + /// The value as a , read as the binary + /// it is rather than as the decimal it was written as. + /// public static implicit operator Real(double value) => Create(EDecimal.FromDouble(value)); - public static implicit operator Real(decimal value) => Create(EDecimal.FromDecimal(value)); -#pragma warning restore CS1591 + /// + /// The value as a . A is decimal + /// already, so this one keeps the digits that were written. + /// + public static implicit operator Real(decimal value) => Create(EDecimal.FromDecimal(value)); } } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rounding.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rounding.Classes.cs index 147dcb921..b6376a30d 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rounding.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Rounding.Classes.cs @@ -11,7 +11,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of round: the nearest integer, with a tie going to the even one. /// @@ -23,6 +22,7 @@ partial record Entity /// public sealed partial record Roundf(Entity Argument) : Function, IUnaryNode { + /// public Entity NodeChild => Argument; private Roundf New(Entity arg) => @@ -46,8 +46,10 @@ private Roundf New(Entity arg) => /// public sealed partial record Minf(Entity Left, Entity Right) : Function, IBinaryNode { + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; private Minf New(Entity left, Entity right) => @@ -64,8 +66,10 @@ private Minf New(Entity left, Entity right) => /// See for why this is a node. public sealed partial record Maxf(Entity Left, Entity Right) : Function, IBinaryNode { + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; private Maxf New(Entity left, Entity right) => @@ -88,8 +92,10 @@ private Maxf New(Entity left, Entity right) => /// public sealed partial record Gcdf(Entity Left, Entity Right) : Function, IBinaryNode { + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; private Gcdf New(Entity left, Entity right) => @@ -99,6 +105,5 @@ private Gcdf New(Entity left, Entity right) => /// protected override Entity[] InitDirectChildren() => new[] { Left, Right }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs index 16f5296d8..49b8b2ea7 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Trigonometry.Classes.cs @@ -11,12 +11,12 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // only while records' parameters cannot be documented /// /// A node of sine /// public sealed partial record Sinf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -32,6 +32,7 @@ public sealed partial record Sinf(Entity Argument) : TrigonometricFunction, IUna /// public sealed partial record Cosf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -47,6 +48,7 @@ public sealed partial record Cosf(Entity Argument) : TrigonometricFunction, IUna /// public sealed partial record Tanf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -62,6 +64,7 @@ public sealed partial record Tanf(Entity Argument) : TrigonometricFunction, IUna /// public sealed partial record Cotanf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -77,6 +80,7 @@ public sealed partial record Cotanf(Entity Argument) : TrigonometricFunction, IU /// public sealed partial record Secantf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -92,6 +96,7 @@ public sealed partial record Secantf(Entity Argument) : TrigonometricFunction, I /// public sealed partial record Cosecantf(Entity Argument) : TrigonometricFunction, IUnaryNode { + /// public Entity NodeChild => Argument; /// Reuse the cache by returning the same object if possible @@ -101,6 +106,5 @@ public sealed partial record Cosecantf(Entity Argument) : TrigonometricFunction, /// protected override Entity[] InitDirectChildren() => new[] { Argument }; } -#pragma warning restore CS1591 // only while records' parameters cannot be documented } } diff --git a/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs b/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs index 353474c3b..1244db27f 100644 --- a/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs +++ b/Sources/AngouriMath/Core/Entity/Continuous/Number/Operators.cs @@ -177,14 +177,27 @@ internal static bool AreEqual(T a, T b) where T : Number => (a, b) => AreEqual(a.RealPart, b.RealPart) && AreEqual(a.ImaginaryPart, b.ImaginaryPart) ); -#pragma warning disable CS1591 + // These compute rather than build: unlike the operators on Entity, which assemble a + // Sumf or a Mulf to be evaluated later, these are arithmetic on numbers and hand back + // the number that results. + + /// Their sum, computed. public static Number operator +(Number a, Number b) => OpSum(a, b); + + /// Their difference, computed. public static Number operator -(Number a, Number b) => OpSub(a, b); + + /// Their product, computed. public static Number operator *(Number a, Number b) => OpMul(a, b); + + /// Their quotient, computed. public static Number operator /(Number a, Number b) => OpDiv(a, b); + + /// The operand itself; unary plus changes nothing. public static Number operator +(Number a) => a; + + /// Its negation, computed. public static Number operator -(Number a) => OpMul(-1, a); -#pragma warning restore CS1591 /// /// Gets all n-th roots of a number, diff --git a/Sources/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs b/Sources/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs index acbe1ce2b..e5651f2d8 100644 --- a/Sources/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Discrete/Entity.Discrete.Classes.cs @@ -12,7 +12,6 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // TODO: it's only for records' parameters! Remove it once you can document records parameters /// /// This node represents all possible values a boolean node might be of @@ -95,6 +94,7 @@ public sealed partial record Notf(Entity Argument) : Statement, IUnaryNode { internal override Priority Priority => Priority.Negation; + /// public Entity NodeChild => Argument; private Notf New(Entity negated) => @@ -113,8 +113,10 @@ public sealed partial record Andf(Entity Left, Entity Right) : Statement, IBinar { internal override Priority Priority => Priority.Conjunction; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; private Andf New(Entity left, Entity right) => @@ -133,8 +135,10 @@ public sealed partial record Orf(Entity Left, Entity Right) : Statement, IBinary { internal override Priority Priority => Priority.Disjunction; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; private Orf New(Entity left, Entity right) => @@ -153,8 +157,10 @@ public sealed partial record Xorf(Entity Left, Entity Right) : Statement, IBinar { internal override Priority Priority => Priority.XDisjunction; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; private Xorf New(Entity left, Entity right) => @@ -173,8 +179,10 @@ public sealed partial record Impliesf(Entity Assumption, Entity Conclusion) : St { internal override Priority Priority => Priority.Implication; + /// public Entity NodeFirstChild => Assumption; + /// public Entity NodeSecondChild => Conclusion; private Impliesf New(Entity assumption, Entity conclusion) => @@ -197,8 +205,10 @@ public sealed partial record Equalsf(Entity Left, Entity Right) : ComparisonSign { internal override Priority Priority => Priority.Equal; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; internal Equalsf New(Entity left, Entity right) @@ -221,8 +231,10 @@ public sealed partial record Greaterf(Entity Left, Entity Right) : ComparisonSig { internal override Priority Priority => Priority.GreaterThan; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; internal Greaterf New(Entity left, Entity right) @@ -245,8 +257,10 @@ public sealed partial record GreaterOrEqualf(Entity Left, Entity Right) : Compar { internal override Priority Priority => Priority.GreaterThan; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; internal GreaterOrEqualf New(Entity left, Entity right) @@ -269,8 +283,10 @@ public sealed partial record Lessf(Entity Left, Entity Right) : ComparisonSign, { internal override Priority Priority => Priority.GreaterThan; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; internal Lessf New(Entity left, Entity right) @@ -293,8 +309,10 @@ public sealed partial record LessOrEqualf(Entity Left, Entity Right) : Compariso { internal override Priority Priority => Priority.GreaterThan; + /// public Entity NodeFirstChild => Left; + /// public Entity NodeSecondChild => Right; internal LessOrEqualf New(Entity left, Entity right) @@ -318,8 +336,10 @@ public sealed partial record Inf(Entity Element, Entity SupSet) : Statement, IBi { internal override Priority Priority => Priority.ContainsIn; + /// public Entity NodeFirstChild => Element; + /// public Entity NodeSecondChild => SupSet; internal Inf New(Entity element, Entity supSet) @@ -339,6 +359,7 @@ public override Entity Replace(Func func) /// public sealed partial record Phif(Entity Argument) : Function, IUnaryNode { + /// public Entity NodeChild => Argument; internal Phif New(Entity argument) @@ -352,6 +373,5 @@ internal Phif New(Entity argument) } #endregion -#pragma warning restore CS1591 // TODO: it's only for records' parameters! Remove it once you can document records parameters } } diff --git a/Sources/AngouriMath/Core/Entity/Entity.ImplicitOperators.cs b/Sources/AngouriMath/Core/Entity/Entity.ImplicitOperators.cs index 9f78dcc67..d0c44e4b6 100644 --- a/Sources/AngouriMath/Core/Entity/Entity.ImplicitOperators.cs +++ b/Sources/AngouriMath/Core/Entity/Entity.ImplicitOperators.cs @@ -11,25 +11,76 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 + // A number written where an expression is expected becomes a literal node of the + // narrowest kind that holds it exactly: the integral types become Integer, a ratio + // becomes Rational, and the rest become Real. This is what lets `x + 1` and + // `MathS.Sin(2)` be written at all. + + /// The number as an literal. public static implicit operator Entity(sbyte value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(byte value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(short value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(ushort value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(int value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(uint value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(long value) => Number.Integer.Create(value); + + /// The number as an literal. public static implicit operator Entity(ulong value) => Number.Integer.Create(value); + + /// + /// The integer as an literal, of any size — this is the + /// conversion to reach for where a value will not fit in a . + /// public static implicit operator Entity(EInteger value) => Number.Integer.Create(value); + + /// The ratio as a literal, exactly. public static implicit operator Entity(ERational value) => Number.Rational.Create(value); + + /// The decimal as a literal. public static implicit operator Entity(EDecimal value) => Number.Real.Create(value); + + /// + /// The value as a literal, read as the binary + /// it is — so 0.1f becomes the value that literal holds + /// and not one tenth. Write 0.1m, or a , for the exact + /// number. + /// public static implicit operator Entity(float value) => Number.Real.Create(EDecimal.FromSingle(value)); + + /// + /// The value as a literal, read as the binary + /// it is rather than as the decimal it was written as. + /// public static implicit operator Entity(double value) => Number.Real.Create(EDecimal.FromDouble(value)); + + /// + /// The value as a literal. A is + /// decimal already, so this one keeps the digits that were written. + /// public static implicit operator Entity(decimal value) => Number.Real.Create(EDecimal.FromDecimal(value)); + + /// + /// The .NET complex number as a literal, through its two + /// parts and their precision. + /// public static implicit operator Entity(System.Numerics.Complex value) => Number.Complex.Create(EDecimal.FromDouble(value.Real), EDecimal.FromDouble(value.Imaginary)); + + /// The big integer as an literal, exactly. public static implicit operator Entity(System.Numerics.BigInteger bigInt) => Number.Integer.Create(EInteger.FromBytes(bigInt.ToByteArray(), littleEndian: true)); -#pragma warning restore CS1591 } } diff --git a/Sources/AngouriMath/Core/Entity/Omni/Entity.Matrix.cs b/Sources/AngouriMath/Core/Entity/Omni/Entity.Matrix.cs index 5fd80908f..c8f2ec06a 100644 --- a/Sources/AngouriMath/Core/Entity/Omni/Entity.Matrix.cs +++ b/Sources/AngouriMath/Core/Entity/Omni/Entity.Matrix.cs @@ -71,35 +71,83 @@ internal Matrix Elementwise(Matrix other, Func operation /// protected override Entity[] InitDirectChildren() => InnerMatrix.Iterate().Select(tup => tup.Value).ToArray(); -#pragma warning disable CS1591 + /// + /// Arithmetic on as the tensor library wants it, so that a + /// matrix can hold expressions rather than numbers. + /// + /// + /// Every operation here normalises its result with . + /// Without it an entry would accumulate the whole history of the operations that + /// built it — a determinant over a 4×4 of symbols is a sum of products of sums, and + /// left unnormalised it grows past being readable or comparable. + /// public readonly struct EntityTensorWrapperOperations : IOperations { + /// Their sum, normalised. public Entity Add(Entity a, Entity b) => (a + b).InnerSimplified; + + /// Their difference, normalised. public Entity Subtract(Entity a, Entity b) => (a - b).InnerSimplified; + + /// Their product, normalised. public Entity Multiply(Entity a, Entity b) => (a * b).InnerSimplified; + + /// Its negation, normalised. public Entity Negate(Entity a) => (-a).InnerSimplified; + + /// Their quotient, normalised. public Entity Divide(Entity a, Entity b) => (a / b).InnerSimplified; + + /// The multiplicative identity, which is the integer one. public Entity CreateOne() => Number.Integer.One; + + /// The additive identity, which is the integer zero. public Entity CreateZero() => Number.Integer.Zero; + + /// + /// The entity itself. An is immutable, so there is nothing + /// to copy and sharing it is safe. + /// public Entity Copy(Entity a) => a; + #pragma warning disable CA1822 // Mark members as static + /// The entity itself, as the interface requires it be passed through. public Entity Forward(Entity a) => a; #pragma warning restore CA1822 // Mark members as static + + /// + /// Whether the two are the same expression. This is structural equality and not + /// mathematical: x + x and 2 * x are not equal here. + /// public bool AreEqual(Entity a, Entity b) => a == b; + + /// + /// Whether the entity is the literal zero — again structurally, so an expression + /// that happens to be zero everywhere is not recognised as one. + /// public bool IsZero(Entity a) => a == 0; + + /// The entity written out, as writes it. public string ToString(Entity a) => a.Stringize(); + /// + /// Not supported: an expression has no byte form here. + /// + /// Always. public byte[] Serialize(Entity a) { throw new NotSufficientlySupportedException("Serializing a matrix is not supported"); } + /// + /// Not supported: an expression has no byte form here. + /// + /// Always. public Entity Deserialize(byte[] data) { throw new NotSufficientlySupportedException("Deserializing a matrix is not supported"); } } -#pragma warning restore CS1591 /// /// The number of columns of a matrix. It is 1 for vectors. /// diff --git a/Sources/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs b/Sources/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs index aa8b53c21..e5ae575be 100644 --- a/Sources/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs +++ b/Sources/AngouriMath/Core/Entity/Omni/Entity.Omni.Classes.cs @@ -222,7 +222,6 @@ public bool TryIsSubsetOf(FiniteSet superSet, out bool isSub) } #endregion -#pragma warning disable CS1591 // TODO: it's only for records' parameters! Remove it once you can document records parameters #region Interval /// @@ -810,7 +809,6 @@ public override Set Filter(Entity predicate, Variable over) } #endregion } -#pragma warning restore CS1591 // TODO: it's only for records' parameters! Remove it once you can document records parameters /// /// Application of arguments to the given expression diff --git a/Sources/AngouriMath/Core/Entity/Omni/Entity.Piecewise.cs b/Sources/AngouriMath/Core/Entity/Omni/Entity.Piecewise.cs index aef56b10b..94cbd5c3e 100644 --- a/Sources/AngouriMath/Core/Entity/Omni/Entity.Piecewise.cs +++ b/Sources/AngouriMath/Core/Entity/Omni/Entity.Piecewise.cs @@ -14,13 +14,14 @@ namespace AngouriMath { partial record Entity { -#pragma warning disable CS1591 // TODO: add docs for records' arguments /// /// That is a node which equals Expression if Predicate is true, otherwise /// public sealed partial record Providedf(Entity Expression, Entity Predicate) : Entity, IBinaryNode { + /// public Entity NodeFirstChild => Expression; + /// public Entity NodeSecondChild => Predicate; internal Providedf New(Entity expression, Entity predicate) @@ -50,6 +51,10 @@ public override Entity Replace(Func func) /// public sealed partial record Piecewise : Entity, IEquatable { + /// + /// The cases, in the order they are considered: the value of the piecewise is that + /// of the first one whose condition holds. + /// public IEnumerable Cases => cases; private readonly IEnumerable cases = Enumerable.Empty(); @@ -113,7 +118,6 @@ public Piecewise ApplyToValues(Func transformation) => Cases.Select(c => c.New(transformation(c.Expression), c.Predicate)).ToPiecewise(); } -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } diff --git a/Sources/AngouriMath/Core/Entity/Omni/Entity.Set.cs b/Sources/AngouriMath/Core/Entity/Omni/Entity.Set.cs index 0b52c47cb..377a4c073 100644 --- a/Sources/AngouriMath/Core/Entity/Omni/Entity.Set.cs +++ b/Sources/AngouriMath/Core/Entity/Omni/Entity.Set.cs @@ -79,9 +79,11 @@ public bool Contains(Entity entity) /// A new node public Set SetSubtract(Entity anotherSet) => new SetMinusf(this, anotherSet); -#pragma warning disable CS1591 + /// + /// The domain as the set of all its values — becomes the set + /// of reals — so a domain can be written wherever an expression is expected. + /// public static implicit operator Entity(Domain domain) => Set.SpecialSet.Create(domain); -#pragma warning restore CS1591 } } diff --git a/Sources/Tests/UnitTests/Core/RealComparisonTest.cs b/Sources/Tests/UnitTests/Core/RealComparisonTest.cs new file mode 100644 index 000000000..afb1656b8 --- /dev/null +++ b/Sources/Tests/UnitTests/Core/RealComparisonTest.cs @@ -0,0 +1,66 @@ +// +// 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 Xunit; +using static AngouriMath.Entity.Number; + +namespace AngouriMath.Tests.Core +{ + /// + /// How 's comparison operators order , which is not + /// how does and is now written into their documentation. + /// + /// + /// Pinned because it is surprising and because the documentation asserts it. A reader + /// arrives with the IEEE habit, where every comparison against NaN is false; here the + /// comparison is a total order and NaN sits at the top of it. + /// + [Trait("Area", "Core")] + public sealed class RealComparisonTest + { + [Theory] + [InlineData(1)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(100000)] + public void NaNIsAboveEveryNumber(int number) + { + Real value = number; + Assert.True(Real.NaN > value); + Assert.True(Real.NaN >= value); + Assert.False(Real.NaN < value); + Assert.False(Real.NaN <= value); + + Assert.True(value < Real.NaN); + Assert.False(value > Real.NaN); + } + + /// And the ordinary order is the ordinary one. + [Fact] + public void AndTheRestOrdersAsExpected() + { + Real one = 1, two = 2; + Assert.True(two > one); + Assert.True(one < two); + Assert.True(one <= one); + Assert.True(one >= one); + Assert.False(one > one); + } + + /// + /// Division by zero answers rather than throwing, so a quotient + /// always has an answer of some kind. + /// + [Fact] + public void DivisionByZeroIsNaNRatherThanAThrow() + { + Real one = 1, zero = 0; + Assert.True((one / zero).IsNaN); + } + } +}