From 4a801064085e0d430e4c6b7301f350832bfa19d4 Mon Sep 17 00:00:00 2001 From: Jack Pappas Date: Mon, 20 Jan 2014 19:50:10 -0500 Subject: [PATCH] Changed the BigRationalLarge type to a normal class instead of a single-case discriminated union because the F# compiler doesn't (yet) optimize single-case DU types as well as it could. Removed the largely-unused BigRationalLargeImpl and BigRationalLarge modules; functions that were actually used were moved into members of the BigRationalLarge type. --- src/FSharp/BigRational.fs | 297 +++++++++++++++++--------------------- 1 file changed, 131 insertions(+), 166 deletions(-) diff --git a/src/FSharp/BigRational.fs b/src/FSharp/BigRational.fs index 1e8032c5..52de3b7e 100644 --- a/src/FSharp/BigRational.fs +++ b/src/FSharp/BigRational.fs @@ -15,131 +15,147 @@ open System.Numerics open System.Globalization -[] -module private BigRationalLargeImpl = - let ZeroI = BigInteger (0) - let OneI = BigInteger (1) - let bigint (x : int) = BigInteger (x) - let ToDoubleI (x : BigInteger) = float x - let ToInt32I (x : BigInteger) = int32 x - - -[] -type BigRationalLarge = - // invariants: (p,q) in lowest form, q >= 0 - | Q of BigInteger * BigInteger - - member x.IsNegative = - let (Q (ap, _)) = x - sign ap < 0 +// invariants: (p,q) in lowest form, q >= 0 +[] +type BigRationalLarge (p : BigInteger, q : BigInteger) = + // + member __.IsNegative = + sign p < 0 - member x.IsPositive = - let (Q (ap, _)) = x - sign ap > 0 + // + member __.IsPositive = + sign p > 0 - member x.Numerator = - let (Q (p, _)) = x in p + // + member __.Numerator = p - member x.Denominator = - let (Q (_, q)) = x in q + // + member __.Denominator = q - member x.Sign = - let (Q (p,_) ) = x + // + member __.Sign = sign p - override this.GetHashCode () = - BigRationalLarge.Hash this + override __.GetHashCode () = + // This hash code must be identical to the hash for BigInteger when the numbers coincide. + if q.IsOne then p.GetHashCode () + else (p.GetHashCode () <<< 3) + q.GetHashCode () - override this.ToString () = - let (Q (p, q)) = this + override __.ToString () = if q.IsOne then - p.ToString() + p.ToString () else - p.ToString() + "/" + q.ToString() - - static member Hash (Q (ap, aq)) = - // This hash code must be identical to the hash for BigInteger when the numbers coincide. - if aq.IsOne then ap.GetHashCode () - else (ap.GetHashCode () <<< 3) + aq.GetHashCode () + p.ToString () + "/" + q.ToString () - static member Equals(Q (ap, aq), Q (bp, bq)) = + // + static member Equals (x : BigRationalLarge, y : BigRationalLarge) = // normal form, so structural equality - BigInteger.(=) (ap, bp) && BigInteger.(=) (aq, bq) - - static member LessThan (Q (ap, aq), Q (bp, bq)) = - BigInteger.(<) (ap * bq, bp * aq) + x.Numerator = y.Numerator && x.Denominator = y.Denominator - // TODO: performance improvement possible here - static member Compare (p, q) = - if BigRationalLarge.LessThan (p, q) then -1 - elif BigRationalLarge.LessThan (q, p)then 1 - else 0 + // + static member Compare (x : BigRationalLarge, y : BigRationalLarge) = + compare (x.Numerator * y.Denominator) (y.Numerator * x.Denominator) - static member ToDouble (Q (p, q)) = - ToDoubleI p / ToDoubleI q + // + static member ToDouble (num : BigRationalLarge) = + float num.Numerator / float num.Denominator + // static member Normalize (p : BigInteger, q : BigInteger) = if q.IsZero then (* throw for any x/0 *) raise <| System.DivideByZeroException () elif q.IsOne then - Q (p, q) + BigRationalLarge (p, q) else let k = BigInteger.GreatestCommonDivisor (p, q) let p = p / k let q = q / k if sign q < 0 then - Q (-p, -q) - else Q (p, q) + BigRationalLarge (-p, -q) + else + BigRationalLarge (p, q) - static member Rational (p : int, q : int) = + // + static member Create (p : int, q : int) = BigRationalLarge.Normalize (bigint p, bigint q) - // TODO : Rename to Rational? It doesn't seem like we need to force the overload resolution here with a separate name... - static member RationalZ (p, q) = + // + static member Create (p, q) = BigRationalLarge.Normalize (p, q) + /// Return the given rational number + static member (~+) (n1 : BigRationalLarge) = n1 + /// Return the negation of a rational number - static member (~-) (Q (bp, bq)) = + static member (~-) (num : BigRationalLarge) = // still coprime, bq >= 0 - Q(-bp, bq) + BigRationalLarge (-num.Numerator, num.Denominator) /// Return the sum of two rational numbers - static member (+) (Q (ap, aq), Q (bp, bq)) = - BigRationalLarge.Normalize ((ap * bq) + (bp * aq), aq * bq) + static member (+) (x : BigRationalLarge, y : BigRationalLarge) = + BigRationalLarge.Normalize ((x.Numerator * y.Denominator) + (y.Numerator * x.Denominator), x.Denominator * y.Denominator) /// Return the difference of two rational numbers - static member (-) (Q (ap, aq), Q (bp, bq)) = - BigRationalLarge.Normalize ((ap * bq) - (bp * aq), aq * bq) + static member (-) (x : BigRationalLarge, y : BigRationalLarge) = + BigRationalLarge.Normalize ((x.Numerator * y.Denominator) - (y.Numerator * x.Denominator), x.Denominator * y.Denominator) /// Return the product of two rational numbers - static member (*) (Q (ap, aq), Q (bp, bq)) = - BigRationalLarge.Normalize (ap * bp, aq * bq) + static member (*) (x : BigRationalLarge, y : BigRationalLarge) = + BigRationalLarge.Normalize (x.Numerator * y.Numerator, x.Denominator * y.Denominator) /// Return the ratio of two rational numbers - static member (/) (Q (ap, aq), Q (bp, bq)) = - BigRationalLarge.Normalize (ap * bq, aq * bp) + static member (/) (x : BigRationalLarge, y : BigRationalLarge) = + BigRationalLarge.Normalize (x.Numerator * y.Denominator, x.Denominator * y.Numerator) - /// Return the given rational number - static member ( ~+ ) (n1 : BigRationalLarge) = n1 + // + static member Reciprocal (num : BigRationalLarge) = + BigRationalLarge.Normalize (num.Denominator, num.Numerator) + + // + static member PowN (num : BigRationalLarge, n : int) = + // p,q powers still coprime + BigRationalLarge (BigInteger.Pow (num.Numerator, n), BigInteger.Pow (num.Denominator, n)) + + // + static member FromBigInteger z = + BigRationalLarge.Create (z, BigInteger.One) + + // + static member FromInt32 n = + BigRationalLarge.Create (n, 1) + + /// Returns the integer part of a rational number. + static member ToBigInteger (num : BigRationalLarge) = + // have p = d.q + r, |r| < |q| + let d, r = BigInteger.DivRem (num.Numerator, num.Denominator) + + if r < BigInteger.Zero then + // p = (d-1).q + (r+q) + d - BigInteger.One + else + // p = d.q + r + d // static member Parse (str : string) = let len = str.Length - if len=0 then invalidArg "str" "empty string"; + if len = 0 then + invalidArg "str" "empty string" + let j = str.IndexOf '/' if j >= 0 then - let p = BigInteger.Parse (str.Substring(0,j)) - let q = BigInteger.Parse (str.Substring(j+1,len-j-1)) - BigRationalLarge.RationalZ (p,q) + let p = BigInteger.Parse (str.Substring (0, j)) + let q = BigInteger.Parse (str.Substring (j + 1, len - j - 1)) + BigRationalLarge.Create (p, q) else let p = BigInteger.Parse str - BigRationalLarge.RationalZ (p,OneI) + BigRationalLarge.Create (p, BigInteger.One) - override this.Equals(that : obj) = + override this.Equals (that : obj) = match that with | :? BigRationalLarge as that -> - BigRationalLarge.Equals(this,that) + BigRationalLarge.Equals (this, that) | _ -> false interface System.IComparable with @@ -150,67 +166,15 @@ type BigRationalLarge = | _ -> invalidArg "obj" "the object does not have the correct type" - -// -[] -module private BigRationalLarge = - // - let inv (Q (ap, aq)) = - BigRationalLarge.Normalize (aq, ap) - - // - let pown (Q (p, q)) (n:int) = - // p,q powers still coprime - Q (BigInteger.Pow (p, n), BigInteger.Pow (q, n)) - - // - let equal (Q (ap, aq)) (Q (bp, bq)) = - // normal form, so structural equality - ap = bp && aq = bq - - // - let lt a b = - BigRationalLarge.LessThan (a, b) - - // - let gt a b = - BigRationalLarge.LessThan (b, a) - - // - let lte (Q(ap, aq)) (Q(bp, bq)) = - BigInteger.(<=) (ap * bq,bp * aq) - - // - let gte (Q(ap, aq)) (Q(bp, bq)) = - BigInteger.(>=) (ap * bq, bp * aq) - - // - let of_bigint z = - BigRationalLarge.RationalZ(z,OneI) - - // - let of_int n = - BigRationalLarge.Rational(n,1) - - // integer part - let integer (Q (p, q)) = - let mutable r = BigInteger(0) - - // have p = d.q + r, |r| < |q| - let d = BigInteger.DivRem (p, q, &r) - if r < ZeroI then - // p = (d-1).q + (r+q) - d - OneI - else - // p = d.q + r - d + interface System.IComparable with + member this.CompareTo other = + BigRationalLarge.Compare (this, other) /// The type of arbitrary-sized rational numbers. [] [] type BigRational = - private // | Z of BigInteger // @@ -225,7 +189,7 @@ type BigRational = /// Return the denominator of the normalized rational number member this.Denominator = match this with - | Z _ -> OneI + | Z _ -> BigInteger.One | Q q -> q.Denominator /// Return a boolean indicating if this rational number is strictly negative @@ -261,9 +225,9 @@ type BigRational = override this.ToString () = match this with | Z z -> - z.ToString() + z.ToString () | Q q -> - q.ToString() + q.ToString () member this.StructuredDisplayString = this.ToString () @@ -272,6 +236,7 @@ type BigRational = static member Parse (str : string) = Q (BigRationalLarge.Parse str) + // TODO : Optimize this by implementing a proper comparison function (so we only do one comparison instead of two). interface System.IComparable with member this.CompareTo (obj : obj) = match obj with @@ -305,9 +270,9 @@ type BigRational = | Q q, Q qq -> Q (q + qq) | Z z, Q qq -> - Q (BigRationalLarge.of_bigint z + qq) + Q (BigRationalLarge.FromBigInteger z + qq) | Q q, Z zz -> - Q (q + BigRationalLarge.of_bigint zz) + Q (q + BigRationalLarge.FromBigInteger zz) /// Return the difference of two rational numbers static member ( - ) (n1, n2) = @@ -317,33 +282,33 @@ type BigRational = | Q q, Q qq -> Q (q - qq) | Z z, Q qq -> - Q (BigRationalLarge.of_bigint z - qq) + Q (BigRationalLarge.FromBigInteger z - qq) | Q q, Z zz -> - Q (q - BigRationalLarge.of_bigint zz) + Q (q - BigRationalLarge.FromBigInteger zz) /// Return the product of two rational numbers static member ( * ) (n1, n2) = - match n1,n2 with + match n1, n2 with | Z z, Z zz -> Z (z * zz) | Q q, Q qq -> Q (q * qq) | Z z, Q qq -> - Q (BigRationalLarge.of_bigint z * qq) + Q (BigRationalLarge.FromBigInteger z * qq) | Q q, Z zz -> - Q (q * BigRationalLarge.of_bigint zz) + Q (q * BigRationalLarge.FromBigInteger zz) /// Return the ratio of two rational numbers static member ( / ) (n1, n2) = match n1, n2 with | Z z, Z zz -> - Q (BigRationalLarge.RationalZ(z,zz)) + Q (BigRationalLarge.Create (z, zz)) | Q q, Q qq -> Q (q / qq) | Z z, Q qq -> - Q (BigRationalLarge.of_bigint z / qq) + Q (BigRationalLarge.FromBigInteger z / qq) | Q q, Z zz -> - Q (q / BigRationalLarge.of_bigint zz) + Q (q / BigRationalLarge.FromBigInteger zz) /// Return the negation of a rational number static member ( ~- ) n = @@ -360,11 +325,11 @@ type BigRational = | Z z, Z zz -> BigInteger.(=) (z,zz) | Q q, Q qq -> - (BigRationalLarge.equal q qq) + BigRationalLarge.Equals (q, qq) | Z z, Q qq -> - (BigRationalLarge.equal (BigRationalLarge.of_bigint z) qq) + BigRationalLarge.Equals (BigRationalLarge.FromBigInteger z, qq) | Q q, Z zz -> - (BigRationalLarge.equal q (BigRationalLarge.of_bigint zz)) + BigRationalLarge.Equals (q, BigRationalLarge.FromBigInteger zz) /// This operator is for use from other .NET languages static member op_Inequality (n, nn) = @@ -374,49 +339,49 @@ type BigRational = static member op_LessThan (n, nn) = match n, nn with | Z z, Z zz -> - BigInteger.(<) (z,zz) + z < zz | Q q, Q qq -> - (BigRationalLarge.lt q qq) + q < qq | Z z, Q qq -> - (BigRationalLarge.lt (BigRationalLarge.of_bigint z) qq) + BigRationalLarge.FromBigInteger z < qq | Q q, Z zz -> - (BigRationalLarge.lt q (BigRationalLarge.of_bigint zz)) + q < BigRationalLarge.FromBigInteger zz /// This operator is for use from other .NET languages static member op_LessThanOrEqual (n, nn) = match n, nn with | Z z, Z zz -> - BigInteger.(<=) (z,zz) + z <= zz | Q q, Q qq -> - (BigRationalLarge.lte q qq) + q <= qq | Z z, Q qq -> - (BigRationalLarge.lte (BigRationalLarge.of_bigint z) qq) + BigRationalLarge.FromBigInteger z <= qq | Q q, Z zz -> - (BigRationalLarge.lte q (BigRationalLarge.of_bigint zz)) + q <= BigRationalLarge.FromBigInteger zz /// This operator is for use from other .NET languages static member op_GreaterThan (n, nn) = match n, nn with | Z z, Z zz -> - BigInteger.(>) (z,zz) + z > zz | Q q, Q qq -> - (BigRationalLarge.gt q qq) + q > qq | Z z, Q qq -> - (BigRationalLarge.gt (BigRationalLarge.of_bigint z) qq) + BigRationalLarge.FromBigInteger z > qq | Q q, Z zz -> - (BigRationalLarge.gt q (BigRationalLarge.of_bigint zz)) + q > BigRationalLarge.FromBigInteger zz /// This operator is for use from other .NET languages static member op_GreaterThanOrEqual (n, nn) = match n, nn with | Z z, Z zz -> - BigInteger.(>=) (z,zz) + z >= zz | Q q, Q qq -> - (BigRationalLarge.gte q qq) + q >= qq | Z z, Q qq -> - (BigRationalLarge.gte (BigRationalLarge.of_bigint z) qq) + BigRationalLarge.FromBigInteger z >= qq | Q q, Z zz -> - (BigRationalLarge.gte q (BigRationalLarge.of_bigint zz)) + q >= BigRationalLarge.FromBigInteger zz /// Return the absolute value of a rational number static member Abs (n : BigRational) = @@ -428,13 +393,13 @@ type BigRational = | Z z -> Z (BigInteger.Pow (z, i)) | Q q -> - Q (BigRationalLarge.pown q i) + Q (BigRationalLarge.PowN (q, i)) /// Return the result of converting the given rational number to a floating point number static member ToDouble (n : BigRational) = match n with | Z z -> - ToDoubleI z + float z | Q q -> BigRationalLarge.ToDouble q @@ -443,15 +408,15 @@ type BigRational = match n with | Z z -> z | Q q -> - BigRationalLarge.integer q + BigRationalLarge.ToBigInteger q /// Return the result of converting the given rational number to an integer static member ToInt32 (n : BigRational) = match n with | Z z -> - ToInt32I z + int z | Q q -> - ToInt32I (BigRationalLarge.integer q) + int (BigRationalLarge.ToBigInteger q) /// Return the result of converting the given rational number to an integer static member op_Explicit (n : BigRational) =