diff --git a/mpir.net/mpir.net-tests/HugeRationalTests/Comparisons.cs b/mpir.net/mpir.net-tests/HugeRationalTests/Comparisons.cs index 5d64164e..11f1c349 100644 --- a/mpir.net/mpir.net-tests/HugeRationalTests/Comparisons.cs +++ b/mpir.net/mpir.net-tests/HugeRationalTests/Comparisons.cs @@ -41,6 +41,33 @@ namespace MPIR.Tests.HugeRationalTests } } + [TestMethod] + public void RationalCompareToHugeInt() + { + using (var a = new HugeRational("-222509832503450298345029835740293845721/115756986668303657898962467957")) + using (var b = new HugeInt("115756986668303657898962467957")) + using (var c = new HugeRational("115756986668303657898962467957/1")) + using (var d = new HugeInt(1922215141)) + { + Assert.AreEqual(-1, System.Math.Sign(a.CompareTo(d))); + Assert.AreEqual(1, System.Math.Sign((-a).CompareTo(d))); + Assert.AreEqual(-1, System.Math.Sign((-a).CompareTo(d + 1))); + Assert.AreEqual(1, System.Math.Sign(d.CompareTo(a))); + Assert.AreEqual(-1, System.Math.Sign(d.CompareTo(-a))); + Assert.AreEqual(1, System.Math.Sign((d + 1).CompareTo(-a))); + + Assert.AreEqual(0, System.Math.Sign(b.CompareTo(c))); + Assert.AreEqual(0, System.Math.Sign(c.CompareTo(b))); + Assert.AreEqual(0, System.Math.Sign((-b).CompareTo(-c))); + Assert.AreEqual(0, System.Math.Sign((-c).CompareTo(-b))); + + Assert.AreEqual(1, System.Math.Sign(b.CompareTo(c - 1))); + Assert.AreEqual(1, System.Math.Sign(c.CompareTo(b - 1))); + Assert.AreEqual(-1, System.Math.Sign((-b).CompareTo(1 - c))); + Assert.AreEqual(-1, System.Math.Sign((-c).CompareTo(1 - b))); + } + } + [TestMethod] public void RationalCompareToObject() { @@ -437,6 +464,28 @@ namespace MPIR.Tests.HugeRationalTests } } + [TestMethod] + public void RationalEqualsHugeInt() + { + using (var a = new HugeRational("-222509832503450298345029835740293845721/115756986668303657898962467957")) + using (var b = new HugeInt(1922215142)) + using (var c = new HugeRational("115756986668303657898962467957/1")) + using (var d = new HugeInt("-115756986668303657898962467957")) + { + Assert.IsFalse(a.Equals(b)); + Assert.IsFalse(b.Equals(a)); + Assert.IsFalse(a.Equals(-b)); + Assert.IsFalse(b.Equals(-a)); + Assert.IsFalse(c.Equals(d)); + Assert.IsTrue(c.Equals(-d)); + Assert.IsTrue((-c).Equals(d)); + Assert.IsTrue(d.Equals(-c)); + Assert.IsTrue((-d).Equals(c)); + Assert.IsTrue(Equals(c, -d)); + Assert.IsTrue(Equals(-c, d)); + } + } + [TestMethod] public void RationalEqualsExpression() { diff --git a/mpir.net/mpir.net/HugeInt.cpp b/mpir.net/mpir.net/HugeInt.cpp index d7c3a666..e1d62f9f 100644 --- a/mpir.net/mpir.net/HugeInt.cpp +++ b/mpir.net/mpir.net/HugeInt.cpp @@ -172,40 +172,7 @@ namespace MPIR #pragma region Interface implementations - int MPEXPR_NAME::CompareTo(Object^ a, bool& valid) - { - valid = true; - - if (IS_NULL(a)) - return 1; - - MPEXPR_NAME^ expr = dynamic_cast(a); - if(!IS_NULL(expr)) - return CompareTo(expr); - - EvaluationContext context; - - if(a->GetType() == mpir_ui::typeid) - { - ASSIGN_TO(context); - return MP(cmp_ui)(CTXT(0), (mpir_ui)a); - } - - if(a->GetType() == mpir_si::typeid) - { - ASSIGN_TO(context); - return MP(cmp_si)(CTXT(0), (mpir_si)a); - } - - if(a->GetType() == double::typeid) - { - ASSIGN_TO(context); - return MP(cmp_d)(CTXT(0), (double)a); - } - - valid = false; - return 0; - } + //CompareTo has to be defined in HugeRational.cpp because it depends on HugeRational.h int MPEXPR_NAME::CompareTo(Object^ a) { diff --git a/mpir.net/mpir.net/HugeInt.h b/mpir.net/mpir.net/HugeInt.h index 12ee440e..e823486a 100644 --- a/mpir.net/mpir.net/HugeInt.h +++ b/mpir.net/mpir.net/HugeInt.h @@ -446,8 +446,11 @@ namespace MPIR /// Compares two numbers. /// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed. - /// - /// Value to compare the source with + /// Both this method and Equals() allow the argument to be a RationalExpression, however we do not define mixed equality operators, + /// because otherwise testing for a null/non-null expression would require an awkward explicit cast on the null. + /// Although this only applies to equality operators, while comparison operators could have possibly worked, we're leaving out all mixed operators for now. + /// Since comparison via CompareTo() or Equals() is possible between ints and rationals, operators would just be another way to do the same thing. + /// Value to compare the source with. This can be an integer or rational multi-precision number or expression, or a supported primitive type (long, ulong, or double). /// A positive number if the source is greater than , negative if less, and zero if they are equal. virtual int CompareTo(Object^ a) sealed; @@ -467,8 +470,11 @@ namespace MPIR /// Compares two numbers. /// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed. - /// - /// Value to compare the source with. This can be a multi-precision number, an expression, or a supported primitive type (long, ulong, or double). + /// Both this method and CompareTo() allow the argument to be a RationalExpression, however we do not define mixed equality operators, + /// because otherwise testing for a null/non-null expression would require an awkward explicit cast on the null. + /// Although this only applies to equality operators, while comparison operators could have possibly worked, we're leaving out all mixed operators for now. + /// Since comparison via CompareTo() or Equals() is possible between ints and rationals, operators would just be another way to do the same thing. + /// Value to compare the source with. This can be an integer or rational multi-precision number or expression, or a supported primitive type (long, ulong, or double). /// true if the values of the source and are equal, false otherwise. virtual bool Equals(Object^ a) override sealed; @@ -939,7 +945,7 @@ namespace MPIR MPEXPR_NAME^ NextPrimeCandidate(MpirRandom^ random); /// Computes the greatest common divisor of this number and . - /// The result is always positive even if one or both inputs are negative. + /// The result is always positive even if one or both inputs are negative (or zero if both inputs are zero). /// As with all expressions, the result is not computed until the expression is assigned to the Value property or consumed by a method. /// /// Source value to compute the GCD with @@ -1245,9 +1251,14 @@ namespace MPIR public: /// - /// Optionally computes and saves the coefficients and such that x*s + y*t = gcd(x, y). + /// Optionally computes and saves the coefficients and such that a + b = g = gcd(a, b). /// If only one of the coefficients is needed, use null for the other. - /// + /// The values and are chosen such that normally, || < |b|/(2g) and || < |a|/(2g), + /// and these relations define and uniquely. + /// There are a few exceptional cases: + /// If |a| = |b|, then = 0 and = sgn(b). + /// Otherwise, = sgn(a) if b = 0 or |b| = 2g, and = sgn(b) if a = 0 or |a| = 2g. + /// In all cases, = 0 if and only if g = |b|, i.e., if b divides a or a = b = 0. /// destination for the first coefficient. Can be null if not needed. /// destination for the second coefficient. Can be null if not needed. /// An updated expression, with its internal state updated to save the coefficients. diff --git a/mpir.net/mpir.net/HugeRational.cpp b/mpir.net/mpir.net/HugeRational.cpp index 6bb74eeb..b762a3a6 100644 --- a/mpir.net/mpir.net/HugeRational.cpp +++ b/mpir.net/mpir.net/HugeRational.cpp @@ -165,6 +165,14 @@ namespace MPIR EvaluationContext context; + IntegerExpression^ expr2 = dynamic_cast(a); + if (!IS_NULL(expr2)) + { + ASSIGN_TO(context); + expr2->AssignToInteger(context); + return MP(cmp_z)(CTXT(0), CTXTI(1)); + } + if(a->GetType() == mpir_ui::typeid) { ASSIGN_TO(context); @@ -231,6 +239,14 @@ namespace MPIR EvaluationContext context; + IntegerExpression^ expr2 = dynamic_cast(a); + if (!IS_NULL(expr2)) + { + ASSIGN_TO(context); + expr2->AssignToRational(context); + return MP(equal)(CTXT(0), CTXT(1)) != 0; + } + if(a->GetType() == mpir_ui::typeid) { ASSIGN_TO(context); @@ -437,5 +453,48 @@ namespace MPIR mpf_set_q(_value, CTXT(0)); } + int IntegerExpression::CompareTo(Object^ a, bool& valid) + { + valid = true; + + if (IS_NULL(a)) + return 1; + + IntegerExpression^ expr = dynamic_cast(a); + if (!IS_NULL(expr)) + return CompareTo(expr); + + EvaluationContext context; + + MPEXPR_NAME^ expr2 = dynamic_cast(a); + if (!IS_NULL(expr2)) + { + expr2->AssignToRational(context); + AssignToInteger(context); + return -MP(cmp_z)(CTXT(0), CTXTI(1)); + } + + if (a->GetType() == mpir_ui::typeid) + { + AssignToInteger(context); + return mpz_cmp_ui(CTXTI(0), (mpir_ui)a); + } + + if (a->GetType() == mpir_si::typeid) + { + AssignToInteger(context); + return mpz_cmp_si(CTXTI(0), (mpir_si)a); + } + + if (a->GetType() == double::typeid) + { + AssignToInteger(context); + return mpz_cmp_d(CTXTI(0), (double)a); + } + + valid = false; + return 0; + } + #pragma endregion }; \ No newline at end of file diff --git a/mpir.net/mpir.net/HugeRational.h b/mpir.net/mpir.net/HugeRational.h index b2490aad..ed6fadfa 100644 --- a/mpir.net/mpir.net/HugeRational.h +++ b/mpir.net/mpir.net/HugeRational.h @@ -376,8 +376,11 @@ namespace MPIR /// Compares two numbers. /// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed. - /// - /// Value to compare the source with + /// Both this method and Equals() allow the argument to be an IntegerExpression, however we do not define mixed equality operators, + /// because otherwise testing for a null/non-null expression would require an awkward explicit cast on the null. + /// Although this only applies to equality operators, while comparison operators could have possibly worked, we're leaving out all mixed operators for now. + /// Since comparison via CompareTo() or Equals() is possible between ints and rationals, operators would just be another way to do the same thing. + /// Value to compare the source with. This can be an integer or rational multi-precision number or expression, or a supported primitive type (long, ulong, or double). /// A positive number if the source is greater than , negative if less, and zero if they are equal. virtual int CompareTo(Object^ a) sealed; @@ -413,8 +416,11 @@ namespace MPIR /// Compares two numbers. /// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed. - /// - /// Value to compare the source with. This can be a multi-precision number, an expression, or a supported primitive type (long, ulong, or double). + /// Both this method and CompareTo() allow the argument to be an IntegerExpression, however we do not define mixed equality operators, + /// because otherwise testing for a null/non-null expression would require an awkward explicit cast on the null. + /// Although this only applies to equality operators, while comparison operators could have possibly worked, we're leaving out all mixed operators for now. + /// Since comparison via CompareTo() or Equals() is possible between ints and rationals, operators would just be another way to do the same thing. + /// Value to compare the source with. This can be an integer or rational multi-precision number or expression, or a supported primitive type (long, ulong, or double). /// true if the values of the source and are equal, false otherwise. virtual bool Equals(Object^ a) override sealed;