namespace Quotients
open System
module Checked =
open Microsoft.FSharp.Core.Operators
let pown x y = pown x y
type Rational64 (numerator:Int64,denominator:Int64) =
do if denominator = 0L && numerator <> 0L then
raise (new DivideByZeroException())
let rec gcd x y =
match y with
| 0L -> x
| _ -> gcd y (x % y)
let norm =
let u = int64 (sign denominator) * numerator
let v = abs denominator
let d = gcd (abs u) v
if denominator <> 0L then u / d, v / d
else numerator, denominator
let numerator, denominator = norm
static let zero = Rational64(0L,1L)
static let Op f (a:Rational64,b:Rational64) =
let x,y = a.Numerator, a.Denominator
let u,v = b.Numerator, b.Denominator
f x y u v
static let Add = Op (fun x y u v -> new Rational64(x * v + u * y, y * v))
static let Sub = Op (fun x y u v -> new Rational64(x * v - u * y, y * v))
static let Mul = Op (fun x y u v -> new Rational64(x * u, y * v))
static let Div = Op (fun x y u v -> new Rational64(x * v, y * u))
static let Eq = Op (fun x y u v -> x * v = u * y)
static let Lt = Op (fun x y u v -> x * v < u * y)
static let Le = Op (fun x y u v -> x * v <= u * y)
static let Gt = Op (fun x y u v -> x * v > u * y)
static let Ge = Op (fun x y u v -> x * v >= u * y)
static let Compare (a:Rational64,b:Rational64) =
if Lt (a,b) then -1
else if Gt (a,b) then 1
else 0
new(numerator:Int64) = Rational64(numerator,1L)
new(value:Decimal) =
let xs = Decimal.GetBits(value)
let lo, mid, hi, flags = xs.[0], xs.[1], xs.[2], xs.[3]
if hi <> 0 then raise (new OverflowException())
let sign = if flags &&& (1 <<< 31) = 0 then 1 else -1
let numerator = int64 lo ||| ((int64 mid) <<< 32)
let denominator = (flags >>> 16) &&& 127
let pow x y = Checked.pown x y
Rational64(numerator * int64 sign, pow 10L denominator)
member this.Numerator = numerator
member this.Denominator = denominator
static member Zero = zero
member this.ToDecimal() =
decimal this.Numerator / decimal this.Denominator
member this.ToDouble() =
double this.Numerator / double this.Denominator
override this.ToString() =
match numerator, denominator with
| n, 1L -> n.ToString()
| n, d -> sprintf "%d/%d" n d
static member ( + ) (a,b) = Add (a,b)
static member ( - ) (a,b) = Sub (a,b)
static member ( * ) (a,b) = Mul (a,b)
static member ( / ) (a,b) = Div (a,b)
static member op_Equality (a,b) = Eq (a,b)
static member op_Inequality (a,b) = not (Eq (a,b))
static member op_LessThan (a,b) = Lt (a,b)
static member op_LessThanOrEqual (a,b) = Le (a,b)
static member op_GreaterThan (a,b) = Gt (a,b)
static member op_GreaterThanOrEqual (a,b) = Ge (a,b)
interface IComparable with
member this.CompareTo (x) = Compare (this, x :?> Rational64)
static member Equals(a,b) = Eq(a,b)
override this.Equals x = this = (x :?> Rational64)
override this.GetHashCode() = ((this.Numerator * 65536L) / this.Denominator) |> int
interface IConvertible with
member this.GetTypeCode() = TypeCode.Object
member this.ToBoolean(_) = raise (new InvalidCastException())
member this.ToByte(_) = raise (new InvalidCastException())
member this.ToChar(_) = raise (new InvalidCastException())
member this.ToDateTime(_) = raise (new InvalidCastException())
member this.ToDecimal(_) = this.ToDecimal()
member this.ToDouble(_) = this.ToDouble()
member this.ToInt16(_) = raise (new InvalidCastException())
member this.ToInt32(_) = raise (new InvalidCastException())
member this.ToInt64(_) = raise (new InvalidCastException())
member this.ToSByte(_) = raise (new InvalidCastException())
member this.ToSingle(_) = raise (new InvalidCastException())
member this.ToString(_) = this.ToString()
member this.ToType(t,p) =
if t = typeof then this.ToDecimal() |> box
else if t = typeof then this.ToDouble() |> box
else raise (new InvalidCastException())
member this.ToUInt16(_) = raise (new InvalidCastException())
member this.ToUInt32(_) = raise (new InvalidCastException())
member this.ToUInt64(_) = raise (new InvalidCastException())
module NumericLiteralQ =
let FromZero () = Rational64(0L)
let FromOne () = Rational64(1L)
let FromInt32 (x) = Rational64(int64 x)
let FromInt64 (x:Int64) = Rational64(x)
module Test =
let x = 1Q/3Q