Browse Source

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.
provider
Jack Pappas 13 years ago
parent
commit
4a80106408
  1. 297
      src/FSharp/BigRational.fs

297
src/FSharp/BigRational.fs

@ -15,131 +15,147 @@ open System.Numerics
open System.Globalization
[<AutoOpen>]
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
[<CustomEquality; CustomComparison>]
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
[<Sealed>]
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"
//
[<RequireQualifiedAccess; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
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<BigRationalLarge> with
member this.CompareTo other =
BigRationalLarge.Compare (this, other)
/// The type of arbitrary-sized rational numbers.
[<CustomEquality; CustomComparison>]
[<StructuredFormatDisplay("{StructuredDisplayString}N")>]
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) =

Loading…
Cancel
Save