|
|
|
@ -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) = |
|
|
|
|