Browse Source

Bring Complex & BigRational from F# PowerPack, and DistributionMonad from FSharpx

v2
Gustavo Guerra 14 years ago
parent
commit
94677c6548
  1. 1
      packages/repositories.config
  2. 98
      src/FSharp/DistributionMonad.fs
  3. 5
      src/FSharp/FSharp.fsproj
  4. 133
      src/FSharp/complex.fs
  5. 139
      src/FSharp/complex.fsi
  6. 309
      src/FSharp/q.fs
  7. 96
      src/FSharp/q.fsi
  8. 614
      src/FSharpUnitTests/BigRationalTests.fs
  9. 79
      src/FSharpUnitTests/DistributionTest.fs
  10. 9
      src/FSharpUnitTests/FSharpUnitTests.fsproj
  11. 96
      src/FSharpUnitTests/PokerDistributionTest.fs
  12. 128
      src/FSharpUnitTests/Utilities.fs
  13. 4
      src/FSharpUnitTests/packages.config

1
packages/repositories.config

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\src\FSharpUnitTests\packages.config" />
<repository path="..\src\Numerics.IO\packages.config" />
<repository path="..\src\UnitTests\packages.config" />
</repositories>

98
src/FSharp/DistributionMonad.fs

@ -0,0 +1,98 @@
namespace MathNet.Numerics
#nowarn "40"
open System
open System.Collections
open System.Collections.Generic
module Distribution =
type 'a Outcome = {
Value: 'a
Probability : BigRational }
type 'a Distribution = 'a Outcome seq
// P(A AND B) = P(A | B) * P(B)
let bind (f: 'a -> 'b Distribution) (dist:'a Distribution) =
dist
|> Seq.map (fun p1 ->
f p1.Value
|> Seq.map (fun p2 ->
{ Value = p2.Value;
Probability =
p1.Probability * p2.Probability}))
|> Seq.concat : 'b Distribution
/// Sequentially compose two actions, passing any value produced by the first as an argument to the second.
let inline (>>=) dist f = bind f dist
/// Flipped >>=
let inline (=<<) f dist = bind f dist
/// Inject a value into the Distribution type
let returnM (value:'a) =
Seq.singleton { Value = value ; Probability = 1N/1N }
: 'a Distribution
type DistributionMonadBuilder() =
member this.Bind (r, f) = bind f r
member this.Return x = returnM x
member this.ReturnFrom x = x
let distribution = DistributionMonadBuilder()
// Create some helpers
let toUniformDistribution seq : 'a Distribution =
let l = Seq.length seq
seq
|> Seq.map (fun e ->
{ Value = e;
Probability = 1N / bignum.FromInt l })
let probability (dist:'a Distribution) =
dist
|> Seq.map (fun o -> o.Probability)
|> Seq.sum
let certainly = returnM
let impossible<'a> :'a Distribution = toUniformDistribution []
let fairDice sides = toUniformDistribution [1..sides]
type CoinSide =
| Heads
| Tails
let fairCoin = toUniformDistribution [Heads; Tails]
let filter predicate (dist:'a Distribution) : 'a Distribution =
dist |> Seq.filter (fun o -> predicate o.Value)
let filterInAnyOrder items dist =
items
|> Seq.fold (fun d item -> filter (Seq.exists ((=) (item))) d) dist
/// Transforms a Distribution value by using a specified mapping function.
let map f (dist:'a Distribution) : 'b Distribution =
dist
|> Seq.map (fun o -> { Value = f o.Value; Probability = o.Probability })
let selectOne values =
[for e in values -> e,values |> Seq.filter ((<>) e)]
|> toUniformDistribution
let rec selectMany n values =
match n with
| 0 -> certainly ([],values)
| _ ->
distribution {
let! (x,c1) = selectOne values
let! (xs,c2) = selectMany (n-1) c1
return x::xs,c2}
let select n values =
selectMany n values
|> map (fst >> List.rev)
let remove items = Seq.filter (fun v -> Seq.forall ((<>) v) items)

5
src/FSharp/FSharp.fsproj

@ -57,6 +57,11 @@
<Compile Include="LinearAlgebra.Double.Vector.fs" />
<Compile Include="LinearAlgebra.Double.Matrix.fs" />
<Compile Include="LinearAlgebra.Double.fs" />
<Compile Include="complex.fsi" />
<Compile Include="complex.fs" />
<Compile Include="q.fsi" />
<Compile Include="q.fs" />
<Compile Include="DistributionMonad.fs" />
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.Core" />

133
src/FSharp/complex.fs

@ -0,0 +1,133 @@
// First version copied from the F# Power Pack
// https://raw.github.com/fsharp/powerpack/master/src/FSharp.PowerPack/math/complex.fs
// (c) Microsoft Corporation 2005-2009.
#nowarn "52" // defensive copy of structs warning
namespace MathNet.Numerics
open Microsoft.FSharp.Math
open System
open System.Globalization
[<Struct>]
[<CustomEquality; CustomComparison>]
type Complex(real: float, imaginary: float) =
//new() = new Complex(0.0,0.0)
member x.r = real
member x.i = imaginary
override x.ToString() = x.ToString("g")
member x.ToString(fmt) = x.ToString(fmt,CultureInfo.InvariantCulture)
member x.ToString(fmt,fmtprovider:IFormatProvider) =
x.r.ToString(fmt,fmtprovider)+"r"+(if x.i < 0.0 then "-" else "+")+(System.Math.Abs x.i).ToString(fmt,fmtprovider)+"i"
interface IComparable with
member x.CompareTo(obj) =
match obj with
| :? Complex as y ->
let c = compare x.r y.r
if c <> 0 then c else compare x.i y.i
| _ -> invalidArg "obj" "not a Complex number"
override x.Equals(obj) =
match obj with
| :? Complex as y -> x.r = y.r && x.i = y.i
| _ -> false
override x.GetHashCode() =
(hash x.r >>> 5) ^^^ (hash x.r <<< 3) ^^^ (((hash x.i >>> 4) ^^^ (hash x.i <<< 4)) + 0x9e3779b9)
type complex = Complex
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Complex =
let mkRect(a,b) = new Complex(a,b)
let conjugate (c:complex) = mkRect (c.r, -c.i)
let mkPolar(a,b) = mkRect (a * Math.Cos(b), a * Math.Sin(b))
let cis b = mkPolar(1.0,b)
let zero = mkRect(0.,0.)
let one = mkRect(1.,0.)
let onei = mkRect(0.,1.)
let magnitude (c:complex) = sqrt(c.r*c.r + c.i*c.i)
let phase (c:complex) = Math.Atan2(c.i,c.r)
let realPart (c:complex) = c.r
let imagPart (c:complex) = c.i
let abs (a:complex) = sqrt (a.r**2.0 + a.i**2.0)
let add (a:complex) (b:complex) = mkRect(a.r + b.r, a.i+b.i)
let sub (a:complex) (b:complex) = mkRect(a.r - b.r, a.i-b.i)
let mul (a:complex) (b:complex) = mkRect(a.r * b.r - a.i * b.i, a.i*b.r + b.i*a.r)
let div (x:complex) (y:complex) =
let a = x.r in let b = x.i in
let c = y.r in let d = y.i in
//(a+ib)/(c+id)=(ac+bd+i(bc-ad))/(c2+d2)
let q = c*c + d*d in
mkRect((a*c+b*d)/q, (b*c - a*d)/q)
let neg (a:complex) = mkRect(-a.r,-a.i)
let smul (a:float)(b:complex) = mkRect(a * b.r, a*b.i)
let muls (a:complex) (b:float) = mkRect(a.r *b, a.i*b)
let fmt_of_string numstyle fmtprovider (s:string) =
mkRect (System.Double.Parse(s,numstyle,fmtprovider),0.0)
let of_string s = fmt_of_string NumberStyles.Any CultureInfo.InvariantCulture s
// ik.(r + i.th) = -k.th + i.k.r
let iscale k (x:complex) = mkRect (-k * x.i , k * x.r)
// LogN : 'a * 'a -> 'a
// Asin : 'a -> 'a
// Acos : 'a -> 'a
// Atan : 'a -> 'a
// Atan2 : 'a * 'a -> 'a
// Sinh : 'a -> 'a
// Cosh : 'a -> 'a
// Tanh : 'a -> 'a
let pi = mkRect (Math.PI,0.0)
// exp(r+it) = exp(r).(cos(t)+i.sin(t)) - De Moivre Theorem
let exp (x:complex) = smul (exp(x.r)) (mkRect(cos(x.i), sin(x.i)))
// x = mag.e^(i.th) = e^ln(mag).e^(i.th) = e^(ln(mag) + i.th)
let log x = mkRect (log(magnitude(x)),phase(x))
let sqrt x = mkPolar (sqrt(magnitude x),phase x / 2.0)
// cos(x) = (exp(i.x) + exp(-i.x))/2
let cos x = smul 0.5 (add (exp(iscale 1.0 x)) (exp(iscale -1.0 x)))
// sin(x) = (exp(i.x) - exp(-i.x))/2 . (-i)
let sin x = smul 0.5 (sub (exp(iscale 1.0 x)) (exp(iscale -1.0 x))) |> iscale (-1.0)
// tan(x) = (exp(i.x) - exp(-i.x)) . (-i) / (exp(i.x) + exp(-i.x))
// = (exp(2i.x) - 1.0) . (-i) / (exp(2i.x) + 1.0)
let tan x = let exp2ix = exp(iscale 2.0 x) in
(div (sub exp2ix one) (add exp2ix one)) |> iscale -1.0
type Complex with
static member Create(a,b) = Complex.mkRect (a,b)
static member CreatePolar(a,b) = Complex.mkPolar (a,b)
member x.Magnitude = Complex.magnitude x
member x.Phase = Complex.phase x
member x.RealPart = x.r
member x.ImaginaryPart = x.i
member x.Conjugate = Complex.conjugate x
static member Sin(x) = Complex.sin(x)
static member Cos(x) = Complex.cos(x)
static member Abs(x) = Complex.abs(x)
static member Tan(x) = Complex.tan(x)
static member Log(x) = Complex.log(x)
static member Exp(x) = Complex.exp(x)
static member Sqrt(x) = Complex.sqrt(x)
static member Zero = Complex.zero
static member One = Complex.one
static member OneI = Complex.onei
static member ( + ) (a,b) = Complex.add a b
static member ( - ) (a,b) = Complex.sub a b
static member ( * ) (a,b) = Complex.mul a b
static member ( / ) (a,b) = Complex.div a b
static member ( ~- ) a = Complex.neg a
static member ( * ) (a,b) = Complex.smul a b
static member ( * ) (a,b) = Complex.muls a b
module ComplexTopLevelOperators =
let complex x y = Complex.mkRect (x,y)

139
src/FSharp/complex.fsi

@ -0,0 +1,139 @@
// First version copied from the F# Power Pack
// https://raw.github.com/fsharp/powerpack/master/src/FSharp.PowerPack/math/complex.fsi
// (c) Microsoft Corporation 2005-2009.
namespace MathNet.Numerics
open System
/// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates
[<Struct>]
[<CustomEquality; CustomComparison>]
type Complex =
/// The real part of a complex number
member r: float
/// The imaginary part of a complex number
member i: float
/// The polar-coordinate magnitude of a complex number
member Magnitude: float
/// The polar-coordinate phase of a complex number
member Phase: float
/// The real part of a complex number
member RealPart: float
/// The imaginary part of a complex number
member ImaginaryPart: float
/// The conjugate of a complex number, i.e. x-yi
member Conjugate: Complex
/// Create a complex number x+ij using rectangular coordinates
static member Create : float * float -> Complex
/// Create a complex number using magnitude/phase polar coordinates
static member CreatePolar : float * float -> Complex
/// The complex number 0+0i
static member Zero : Complex
/// The complex number 1+0i
static member One : Complex
/// The complex number 0+1i
static member OneI : Complex
/// Add two complex numbers
static member ( + ) : Complex * Complex -> Complex
/// Subtract one complex number from another
static member ( - ) : Complex * Complex -> Complex
/// Multiply two complex numbers
static member ( * ) : Complex * Complex -> Complex
/// Complex division of two complex numbers
static member ( / ) : Complex * Complex -> Complex
/// Unary negation of a complex number
static member ( ~- ) : Complex -> Complex
/// Multiply a scalar by a complex number
static member ( * ) : float * Complex -> Complex
/// Multiply a complex number by a scalar
static member ( * ) : Complex * float -> Complex
static member Sin : Complex -> Complex
static member Cos : Complex -> Complex
/// Computes the absolute value of a complex number: e.g. Abs x+iy = sqrt(x**2.0 + y**2.0.)
/// Note: Complex.Abs(z) is the same as z.Magnitude
static member Abs : Complex -> float
static member Tan : Complex -> Complex
static member Log : Complex -> Complex
static member Exp : Complex -> Complex
static member Sqrt : Complex -> Complex
override ToString : unit -> string
override Equals : obj -> bool
interface System.IComparable
member ToString : format:string -> string
member ToString : format:string * provider:System.IFormatProvider -> string
/// The type of complex numbers
type complex = Complex
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<RequireQualifiedAccess>]
module Complex =
val mkRect: float * float -> complex
/// The polar-coordinate magnitude of a complex number
val magnitude: complex -> float
/// The polar-coordinate phase of a complex number
val phase : complex -> float
/// The real part of a complex number
val realPart : complex -> float
/// The imaginary part of a complex number
val imagPart : complex -> float
/// Create a complex number using magnitude/phase polar coordinates
val mkPolar : float * float -> complex
/// A complex of magnitude 1 and the given phase and , i.e. cis x = mkPolar 1.0 x
val cis : float -> complex
/// The conjugate of a complex number, i.e. x-yi
val conjugate : complex -> complex
/// The complex number 0+0i
val zero : complex
/// The complex number 1+0i
val one : complex
/// The complex number 0+1i
val onei : complex
/// Add two complex numbers
val add : complex -> complex -> complex
/// Subtract one complex number from another
val sub : complex -> complex -> complex
/// Multiply two complex numbers
val mul : complex -> complex -> complex
/// Complex division of two complex numbers
val div : complex -> complex -> complex
/// Unary negation of a complex number
val neg : complex -> complex
/// Multiply a scalar by a complex number
val smul : float -> complex -> complex
/// Multiply a complex number by a scalar
val muls : complex -> float -> complex
/// pi
val pi : Complex
/// exp(x) = e^x
val exp : Complex -> Complex
/// log(x) is natural log (base e)
val log : Complex -> Complex
/// sqrt(x) and 0 <= phase(x) < pi
val sqrt : Complex -> Complex
/// Sine
val sin : Complex -> Complex
/// Cosine
val cos : Complex -> Complex
/// Tagent
val tan : Complex -> Complex
[<AutoOpen>]
module ComplexTopLevelOperators =
/// Constructs a complex number from both the real and imaginary part.
val complex : float -> float -> complex

309
src/FSharp/q.fs

@ -0,0 +1,309 @@
// First version copied from the F# Power Pack
// https://raw.github.com/fsharp/powerpack/master/src/FSharp.PowerPack/math/q.fs
// (c) Microsoft Corporation. All rights reserved
#nowarn "44" // OK to use the "compiler only" function RangeGeneric
#nowarn "52" // The value has been copied to ensure the original is not mutated by this operation
namespace MathNet.Numerics
open System
open System.Numerics
open System.Globalization
module BigRationalLargeImpl =
let ZeroI = new BigInteger(0)
let OneI = new BigInteger(1)
let bigint (x:int) = new BigInteger(x)
let ToDoubleI (x:BigInteger) = double x
let ToInt32I (x:BigInteger) = int32 x
open BigRationalLargeImpl
[<CustomEquality; CustomComparison>]
type BigRationalLarge =
| Q of BigInteger * BigInteger // invariants: (p,q) in lowest form, q >= 0
override n.ToString() =
let (Q(p,q)) = n
if q.IsOne then 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()
override x.GetHashCode() = BigRationalLarge.Hash(x)
static member Equals(Q(ap,aq), Q(bp,bq)) =
BigInteger.(=) (ap,bp) && BigInteger.(=) (aq,bq) // normal form, so structural equality
static member LessThan(Q(ap,aq), Q(bp,bq)) =
BigInteger.(<) (ap * bq,bp * aq)
// note: 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
interface System.IComparable with
member this.CompareTo(obj:obj) =
match obj with
| :? BigRationalLarge as that -> BigRationalLarge.Compare(this,that)
| _ -> invalidArg "obj" "the object does not have the correct type"
override this.Equals(that:obj) =
match that with
| :? BigRationalLarge as that -> BigRationalLarge.Equals(this,that)
| _ -> false
member x.IsNegative = let (Q(ap,_)) = x in sign ap < 0
member x.IsPositive = let (Q(ap,_)) = x in sign ap > 0
member x.Numerator = let (Q(p,_)) = x in p
member x.Denominator = let (Q(_,q)) = x in q
member x.Sign = (let (Q(p,_)) = x in sign p)
static member ToDouble (Q(p,q)) =
ToDoubleI p / ToDoubleI q
static member Normalize (p:BigInteger,q:BigInteger) =
if q.IsZero then
raise (System.DivideByZeroException()) (* throw for any x/0 *)
elif q.IsOne then
Q(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)
static member Rational (p:int,q:int) = BigRationalLarge.Normalize (bigint p,bigint q)
static member RationalZ (p,q) = BigRationalLarge.Normalize (p,q)
static member Parse (str:string) =
let len = str.Length
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)
else
let p = BigInteger.Parse str
BigRationalLarge.RationalZ (p,OneI)
static member (~-) (Q(bp,bq)) = Q(-bp,bq) // still coprime, bq >= 0
static member (+) (Q(ap,aq),Q(bp,bq)) = BigRationalLarge.Normalize ((ap * bq) + (bp * aq),aq * bq)
static member (-) (Q(ap,aq),Q(bp,bq)) = BigRationalLarge.Normalize ((ap * bq) - (bp * aq),aq * bq)
static member (*) (Q(ap,aq),Q(bp,bq)) = BigRationalLarge.Normalize (ap * bp,aq * bq)
static member (/) (Q(ap,aq),Q(bp,bq)) = BigRationalLarge.Normalize (ap * bq,aq * bp)
static member ( ~+ )(n1:BigRationalLarge) = n1
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module BigRationalLarge =
open System.Numerics
let inv (Q(ap,aq)) = BigRationalLarge.Normalize(aq,ap)
let pown (Q(p,q)) (n:int) = Q(BigInteger.Pow(p,n),BigInteger.Pow (q,n)) // p,q powers still coprime
let equal (Q(ap,aq)) (Q(bp,bq)) = ap=bp && aq=bq // normal form, so structural equality
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)
let d = BigInteger.DivRem (p,q,&r) // have p = d.q + r, |r| < |q|
if r < ZeroI
then d - OneI // p = (d-1).q + (r+q)
else d // p = d.q + r
//----------------------------------------------------------------------------
// BigRational
//--------------------------------------------------------------------------
[<CustomEquality; CustomComparison>]
[<StructuredFormatDisplay("{StructuredDisplayString}N")>]
type BigRational =
| Z of BigInteger
| Q of BigRationalLarge
static member ( + )(n1,n2) =
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 q ,Z zz -> Q (q + BigRationalLarge.of_bigint zz)
static member ( * )(n1,n2) =
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 q ,Z zz -> Q (q * BigRationalLarge.of_bigint zz)
static member ( - )(n1,n2) =
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 q ,Z zz -> Q (q - BigRationalLarge.of_bigint zz)
static member ( / )(n1,n2) =
match n1,n2 with
| Z z ,Z zz -> Q (BigRationalLarge.RationalZ(z,zz))
| Q q ,Q qq -> Q (q / qq)
| Z z ,Q qq -> Q (BigRationalLarge.of_bigint z / qq)
| Q q ,Z zz -> Q (q / BigRationalLarge.of_bigint zz)
static member ( ~- )(n1) =
match n1 with
| Z z -> Z (-z)
| Q q -> Q (-q)
static member ( ~+ )(n1:BigRational) = n1
// nb. Q and Z hash codes must match up - see notes above
override n.GetHashCode() =
match n with
| Z z -> z.GetHashCode()
| Q q -> q.GetHashCode()
override this.Equals(obj:obj) =
match obj with
| :? BigRational as that -> BigRational.(=)(this, that)
| _ -> false
interface System.IComparable with
member n1.CompareTo(obj:obj) =
match obj with
| :? BigRational as n2 ->
if BigRational.(<)(n1, n2) then -1 elif BigRational.(=)(n1, n2) then 0 else 1
| _ -> invalidArg "obj" "the objects are not comparable"
static member FromInt (x:int) = Z (bigint x)
static member FromBigInt x = Z x
static member Zero = BigRational.FromInt(0)
static member One = BigRational.FromInt(1)
static member PowN (n,i:int) =
match n with
| Z z -> Z (BigInteger.Pow (z,i))
| Q q -> Q (BigRationalLarge.pown q i)
static member op_Equality (n,nn) =
match n,nn with
| Z z ,Z zz -> BigInteger.(=) (z,zz)
| Q q ,Q qq -> (BigRationalLarge.equal q qq)
| Z z ,Q qq -> (BigRationalLarge.equal (BigRationalLarge.of_bigint z) qq)
| Q q ,Z zz -> (BigRationalLarge.equal q (BigRationalLarge.of_bigint zz))
static member op_Inequality (n,nn) = not (BigRational.op_Equality(n,nn))
static member op_LessThan (n,nn) =
match n,nn with
| Z z ,Z zz -> BigInteger.(<) (z,zz)
| Q q ,Q qq -> (BigRationalLarge.lt q qq)
| Z z ,Q qq -> (BigRationalLarge.lt (BigRationalLarge.of_bigint z) qq)
| Q q ,Z zz -> (BigRationalLarge.lt q (BigRationalLarge.of_bigint zz))
static member op_GreaterThan (n,nn) =
match n,nn with
| Z z ,Z zz -> BigInteger.(>) (z,zz)
| Q q ,Q qq -> (BigRationalLarge.gt q qq)
| Z z ,Q qq -> (BigRationalLarge.gt (BigRationalLarge.of_bigint z) qq)
| Q q ,Z zz -> (BigRationalLarge.gt q (BigRationalLarge.of_bigint zz))
static member op_LessThanOrEqual (n,nn) =
match n,nn with
| Z z ,Z zz -> BigInteger.(<=) (z,zz)
| Q q ,Q qq -> (BigRationalLarge.lte q qq)
| Z z ,Q qq -> (BigRationalLarge.lte (BigRationalLarge.of_bigint z) qq)
| Q q ,Z zz -> (BigRationalLarge.lte q (BigRationalLarge.of_bigint zz))
static member op_GreaterThanOrEqual (n,nn) =
match n,nn with
| Z z ,Z zz -> BigInteger.(>=) (z,zz)
| Q q ,Q qq -> (BigRationalLarge.gte q qq)
| Z z ,Q qq -> (BigRationalLarge.gte (BigRationalLarge.of_bigint z) qq)
| Q q ,Z zz -> (BigRationalLarge.gte q (BigRationalLarge.of_bigint zz))
member n.IsNegative =
match n with
| Z z -> sign z < 0
| Q q -> q.IsNegative
member n.IsPositive =
match n with
| Z z -> sign z > 0
| Q q -> q.IsPositive
member n.Numerator =
match n with
| Z z -> z
| Q q -> q.Numerator
member n.Denominator =
match n with
| Z _ -> OneI
| Q q -> q.Denominator
member n.Sign =
if n.IsNegative then -1
elif n.IsPositive then 1
else 0
static member Abs(n:BigRational) =
if n.IsNegative then -n else n
static member ToDouble(n:BigRational) =
match n with
| Z z -> ToDoubleI z
| Q q -> BigRationalLarge.ToDouble q
static member ToBigInt(n:BigRational) =
match n with
| Z z -> z
| Q q -> BigRationalLarge.integer q
static member ToInt32(n:BigRational) =
match n with
| Z z -> ToInt32I(z)
| Q q -> ToInt32I(BigRationalLarge.integer q )
static member op_Explicit (n:BigRational) = BigRational.ToInt32 n
static member op_Explicit (n:BigRational) = BigRational.ToDouble n
static member op_Explicit (n:BigRational) = BigRational.ToBigInt n
override n.ToString() =
match n with
| Z z -> z.ToString()
| Q q -> q.ToString()
member x.StructuredDisplayString = x.ToString()
static member Parse(s:string) = Q (BigRationalLarge.Parse s)
type BigNum = BigRational
type bignum = BigNum
module NumericLiteralN =
let FromZero () = BigRational.Zero
let FromOne () = BigRational.One
let FromInt32 i = BigRational.FromInt i
let FromInt64 (i64:int64) = BigRational.FromBigInt (new BigInteger(i64))
let FromString s = BigRational.Parse s

96
src/FSharp/q.fsi

@ -0,0 +1,96 @@
// First version copied from the F# Power Pack
// https://raw.github.com/fsharp/powerpack/master/src/FSharp.PowerPack/math/q.fsi
// (c) Microsoft Corporation 2005-2009.
namespace MathNet.Numerics
open System
open System.Numerics
/// The type of arbitrary-sized rational numbers
[<Sealed>]
type BigRational =
/// Return the sum of two rational numbers
static member ( + ) : BigRational * BigRational -> BigRational
/// Return the product of two rational numbers
static member ( * ) : BigRational * BigRational -> BigRational
/// Return the difference of two rational numbers
static member ( - ) : BigRational * BigRational -> BigRational
/// Return the ratio of two rational numbers
static member ( / ) : BigRational * BigRational -> BigRational
/// Return the negation of a rational number
static member ( ~- ): BigRational -> BigRational
/// Return the given rational number
static member ( ~+ ): BigRational -> BigRational
override ToString: unit -> string
override GetHashCode: unit -> int
interface System.IComparable
/// Get zero as a rational number
static member Zero : BigRational
/// Get one as a rational number
static member One : BigRational
/// This operator is for use from other .NET languages
static member op_Equality : BigRational * BigRational -> bool
/// This operator is for use from other .NET languages
static member op_Inequality : BigRational * BigRational -> bool
/// This operator is for use from other .NET languages
static member op_LessThan: BigRational * BigRational -> bool
/// This operator is for use from other .NET languages
static member op_GreaterThan: BigRational * BigRational -> bool
/// This operator is for use from other .NET languages
static member op_LessThanOrEqual: BigRational * BigRational -> bool
/// This operator is for use from other .NET languages
static member op_GreaterThanOrEqual: BigRational * BigRational -> bool
/// Return a boolean indicating if this rational number is strictly negative
member IsNegative: bool
/// Return a boolean indicating if this rational number is strictly positive
member IsPositive: bool
/// Return the numerator of the normalized rational number
member Numerator: BigInteger
/// Return the denominator of the normalized rational number
member Denominator: BigInteger
member StructuredDisplayString : string
/// Return the absolute value of a rational number
static member Abs : BigRational -> BigRational
/// Return the sign of a rational number; 0, +1 or -1
member Sign : int
/// Return the result of raising the given rational number to the given power
static member PowN : BigRational * int -> BigRational
/// Return the result of converting the given integer to a rational number
static member FromInt : int -> BigRational
/// Return the result of converting the given big integer to a rational number
static member FromBigInt : BigInteger -> BigRational
/// Return the result of converting the given rational number to a floating point number
static member ToDouble: BigRational -> float
/// Return the result of converting the given rational number to a big integer
static member ToBigInt: BigRational -> BigInteger
/// Return the result of converting the given rational number to an integer
static member ToInt32 : BigRational -> int
/// Return the result of converting the given rational number to a floating point number
static member op_Explicit : BigRational -> float
/// Return the result of converting the given rational number to a big integer
static member op_Explicit : BigRational -> BigInteger
/// Return the result of converting the given rational number to an integer
static member op_Explicit : BigRational -> int
/// Return the result of converting the string to a rational number
static member Parse: string -> BigRational
type BigNum = BigRational
type bignum = BigRational
[<RequireQualifiedAccess>]
module NumericLiteralN =
val FromZero : unit -> BigRational
val FromOne : unit -> BigRational
val FromInt32 : int32 -> BigRational
val FromInt64 : int64 -> BigRational
val FromString : string -> BigRational

614
src/FSharpUnitTests/BigRationalTests.fs

@ -0,0 +1,614 @@
// First version copied from the F# Power Pack
// https://raw.github.com/fsharp/powerpack/master/src/FSharp.PowerPack.Unittests/BigRationalTests.fs
namespace MathNet.Numerics.Tests
open MathNet.Numerics
open NUnit.Framework
open System
open System.Collections
open System.Collections.Generic
open System.Numerics
[<TestFixture>]
type public BigRationalTests() =
// BigRational Tests
// =================
// Notes: What cases to consider?
// For (p,q) cases q=0, q=1, q<>1. [UPDATE: remove (x,0)]
// For (p,q) when q=1 there could be 2 internal representations, either Z or Q.
// For (p,0) this value can be signed, corresponds to +/- infinity point. [Update: remove it]
// Hashes on (p,1) for both representations must agree.
// For binary operators, try for result with and without HCF (normalisation).
// Also: 0/0 is an acceptable representation. See normalisation code. [Update: remove it].
// Overrides to test:
// .ToString()
// .GetHashCode()
// .Equals()
// IComparable.CompareTo()
// Misc construction.
let natA n = BigRational.FromInt n // internally Z
let natB n = (natA n / natA 7) * natA 7 // internally Q
let ratio p q = BigRational.FromInt p / BigRational.FromInt q
let (/%) b c = BigRational.FromBigInt b / BigRational.FromBigInt c
// Misc test values
let q0 = natA 0
let q1 = natA 1
let q2 = natA 2
let q3 = natA 3
let q4 = natA 4
let q5 = natA 5
let minIntI = bigint System.Int32.MinValue
let maxIntI = bigint System.Int32.MaxValue
let ran = System.Random()
let nextZ n = bigint (ran.Next(n))
// A selection of test points.
let points =
// A selection of integer and reciprical points
let points =
[for i in -13I .. 13I -> i,1I] @
[for i in -13I .. 13I -> 1I,i]
// Exclude x/0
let points = [for p,q in points do if q <> 0I then yield p,q ] // PROPOSE: (q,0) never a valid Q value, filter them out of tests...
// Scale by various values, including into BigInt range
let scale (kp,kq) (p,q) = (p*kp,q*kq)
let scales k pqs = List.map (scale k) pqs
let points = List.concat [points;
scales (10000I,1I) points;
scales (1I,10000I) points;
scales (maxIntI,1I) points;
scales (1I,maxIntI) points;
]
points
let pointsNonZero = [for p,q in points do if p<>0I then yield p,q] // non zero points
let makeQs p q =
if q = 1I && minIntI <= p && p <= maxIntI then
// (p,1) where p is int32
let p32 = int32 p
[natA p32;natB p32;BigRational.FromBigInt p] // two reprs for int32
else
[BigRational.FromBigInt p / BigRational.FromBigInt q]
let miscQs = [for p,q in points do yield! makeQs p q]
let product xs ys = [for x in xs do for y in ys do yield x,y]
let vector1s = [for z in points -> z]
let vector2s = product points points
[<Test>]
member this.BasicTests1() =
check "generic format h" "1N" (sprintf "%A" 1N)
check "generic format q" "-1N" (sprintf "%A" (-1N))
test "vliwe98" (id -2N = - 2N)
test "d3oc002" (LanguagePrimitives.GenericZero<bignum> = 0N)
test "d3oc112w" (LanguagePrimitives.GenericOne<bignum> = 1N)
check "weioj3h" (sprintf "%O" 3N) "3"
check "weioj3k" (sprintf "%O" (3N / 4N)) "3/4"
check "weioj3k" (sprintf "%O" (3N / 400000000N)) "3/400000000"
check "weioj3l" (sprintf "%O" (3N / 3N)) "1"
check "weioj3q" (sprintf "%O" (-3N)) "-3"
//check "weioj3w" (sprintf "%O" -3N) "-3"
check "weioj3e" (sprintf "%O" (-3N / -3N)) "1"
// The reason why we do not use hardcoded values is the the representation may change based on the NetFx we are targeting.
// For example, when targeting NetFx4.0, the result is "-3E+61" instead of "-3000....0N"
let v = -30000000000000000000000000000000000000000000000000000000000000N
check "weioj3r" (sprintf "%O" v) ((box v).ToString())
[<Test>]
member this.BasicTests2() =
// Test arithmetic ops: tests
let test2One name f check ((p,q),(pp,qq)) =
// There may be several ways to construct the test rationals
let zs = makeQs p q
let zzs = makeQs pp qq
let results = [for z in zs do for zz in zzs do yield f (z,zz)]
let refP,refQ = check (p,q) (pp,qq)
let refResult = BigRational.FromBigInt refP / BigRational.FromBigInt refQ
let resOK (result:BigRational) =
result.Numerator * refQ = refP * result.Denominator &&
BigRational.Equals(refResult,result)
match List.tryFind (fun result -> not (resOK result)) results with
| None -> () // ok
| Some result -> printf "Test failed. %s (%A,%A) (%A,%A). Expected %A. Observed %A\n" name p q pp qq refResult result
reportFailure "cejkew09"
let test2All name f check vectors = List.iter (test2One name f check) vectors
// Test arithmetic ops: call
test2All "add" (BigRational.(+)) (fun (p,q) (pp,qq) -> (p*qq + q*pp,q*qq)) vector2s
test2All "sub" (BigRational.(-)) (fun (p,q) (pp,qq) -> (p*qq - q*pp,q*qq)) vector2s
test2All "mul" (BigRational.(*)) (fun (p,q) (pp,qq) -> (p*pp,q*qq)) vector2s // *) <-- for EMACS
test2All "div" (BigRational.(/)) (fun (p,q) (pp,qq) -> (p*qq,q*pp)) (product points pointsNonZero)
[<Test>]
member this.RangeTests() =
// Test x0 .. dx .. x1
let checkRange3 (x0:BigRational) dx x1 k =
let f (x:BigRational) = x * BigRational.FromBigInt k |> BigRational.ToBigInt
let rangeA = {x0 .. dx .. x1} |> Seq.map f
let rangeB = {f x0 .. f dx .. f x1}
//printf "Length=%d\n" (Seq.length rangeA)
let same = Seq.forall2 (=) rangeA rangeB
check (sprintf "Range3 %A .. %A .. %A scaled to %A" x0 dx x1 k) same true
checkRange3 (0I /% 1I) (1I /% 7I) (100I /% 1I) (7I*1I)
checkRange3 (0I /% 1I) (1I /% 7I) (100I /% 11I) (7I*11I)
checkRange3 (1I /% 13I) (1I /% 7I) (100I /% 11I) (7I*11I*13I)
for i = 0 to 1000 do
let m = 1000 // max steps is -m to m in steps of 1/m i.e. 2.m^2
let p0,q0 = nextZ m ,nextZ m + 1I
let p1,q1 = nextZ m ,nextZ m + 1I
let pd,qd = nextZ m + 1I,nextZ m + 1I
checkRange3 (p0 /% q0) (pd /% qd) (p1 /% q1) (q0 * q1 * qd)
// Test x0 .. x1
let checkRange2 (x0:BigRational) x1 =
let z0 = BigRational.ToBigInt x0
let z01 = BigRational.ToBigInt (x1 - x0)
let f (x:BigRational) = x |> BigRational.ToBigInt
let rangeA = [x0 .. x1] |> List.map f // range with each item rounded down
let rangeB = [z0 .. z0 + z01] // range of same length from the round down start point
check (sprintf "Range2: %A .. %A" x0 x1) rangeA rangeB
checkRange2 (0I /% 1I) (100I /% 1I)
checkRange2 (0I /% 1I) (100I /% 11I)
checkRange2 (1I /% 13I) (100I /% 11I)
for i = 0 to 1000 do
let m = 10000 // max steps is -m to m in steps of 1 i.e. 2.m
let p0,q0 = nextZ m ,nextZ m + 1I
let p1,q1 = nextZ m ,nextZ m + 1I
checkRange2 (p0 /% q0) (p1 /% q1) //(q0 * q1 * qd)
// ToString()
// Cases: integer, computed integer, rational<1, rational>1, +/-infinity, nan
(natA 1).ToString() |> check "ToString" "1"
(natA 0).ToString() |> check "ToString" "0"
(natA (-12)).ToString() |> check "ToString" "-12"
(natB 1).ToString() |> check "ToString" "1"
(natB 0).ToString() |> check "ToString" "0"
(natB (-12)).ToString() |> check "ToString" "-12"
(1I /% 3I).ToString() |> check "ToString" "1/3"
(12I /% 5I).ToString() |> check "ToString" "12/5"
//(13I /% 0I).ToString() |> check "ToString" "1/0" // + 1/0. Plan to make this invalid value
//(-13I /% 0I).ToString() |> check "ToString" "1/0" // - 1/0. Plan to make this invalid value
//(0I /% 0I).ToString() |> check "ToString" "0/0" // 0/0. Plan to make this invalid value
// GetHashCode
// Cases: zero, integer, computed integer, computed by multiple routes.
let checkSameHashGeneric a b = check (sprintf "GenericHash %A %A" a b) (a.GetHashCode()) (b.GetHashCode())
let checkSameHash (a:BigRational) (b:BigRational) = check (sprintf "BigRationalHash %A %A" a b) (a.GetHashCode()) (b.GetHashCode()); checkSameHashGeneric a b
List.iter (fun n -> checkSameHash (natA n) (natB n)) [-10 .. 10]
List.iter (fun n -> checkSameHash n ((n * q3 + n * q2) / q5)) miscQs
// bug 3488: should non-finite values be supported?
//let x = BigRational.FromBigInt (-1I) / BigRational.FromBigInt 0I
//let q2,q3,q5 = BigRational.FromInt 2,BigRational.FromInt 3,BigRational.FromInt 5
//let x2 = (x * q2 + x * q3) / q5
//x,x2,x = x2
// Test: Zero,One?
check "ZeroA" BigRational.Zero (natA 0)
check "ZeroA" BigRational.Zero (natA 0)
check "OneA" BigRational.One (natB 1)
check "OneB" BigRational.One (natB 1)
[<Test>]
member this.BinaryAndUnaryOperators() =
// Test: generic bop
let testR2One name f check ((p,q),(pp,qq)) =
// There may be several ways to construct the test rationals
let zs = makeQs p q
let zzs = makeQs pp qq
let resultRef = check (p,q) (pp,qq) // : bool
let args = [for z in zs do for zz in zzs do yield (z,zz)]
match List.tryFind (fun (z,zz) -> resultRef <> f (z,zz)) args with
| None -> () // ok
| Some (z,zz) -> printf "Test failed. %s (%A,%A) (%A,%A) = %s %A %A. Expected %A.\n" name p q pp qq name z zz resultRef
reportFailure "cknwe9"
// Test: generic uop
let testR1One name f check (p,q) =
// There may be several ways to construct the test rationals
let zs = makeQs p q
let resultRef = check (p,q) //: bool
match List.tryFind (fun z -> resultRef <> f z) zs with
| None -> () // ok
| Some z -> printf "Test failed. %s (%A,%A) = %s %A. Expected %A.\n" name p q name z resultRef
reportFailure "vekjkrejvre0"
let testR2All name f check vectors = List.iter (testR2One name f check) vectors
let testR1All name f check vectors = List.iter (testR1One name f check) vectors
// Test: relations
let sign (i:BigInteger) = BigInteger(i.Sign)
testR2All "=" BigRational.(=) (fun (p,q) (pp,qq) -> (p*qq = q*pp)) vector2s
testR2All "=" BigRational.op_Equality (fun (p,q) (pp,qq) -> (p*qq = q*pp)) vector2s
testR2All "!=" BigRational.op_Inequality (fun (p,q) (pp,qq) -> (p*qq <> q*pp)) vector2s
// p/q < pp/qq
// iff (p * sign q) / (q * sign q) < (pp * sign qq) / (qq * sign qq)
// iff (p * sign q) * (qq * sign qq) < (pp * sign qq) * (q * sign q) since q*sign q is always +ve.
testR2All "<" BigRational.(<) (fun (p,q) (pp,qq) -> (p * sign q) * (qq * sign qq) < (pp * sign qq) * (q * sign q)) vector2s
testR2All ">" BigRational.(>) (fun (p,q) (pp,qq) -> (p * sign q) * (qq * sign qq) > (pp * sign qq) * (q * sign q)) vector2s
testR2All "<=" BigRational.(<=) (fun (p,q) (pp,qq) -> (p * sign q) * (qq * sign qq) <= (pp * sign qq) * (q * sign q)) vector2s
testR2All ">=" BigRational.(>=) (fun (p,q) (pp,qq) -> (p * sign q) * (qq * sign qq) >= (pp * sign qq) * (q * sign q)) vector2s
// System.IComparable tests
let BigRationalCompareTo (p:BigRational,q:BigRational) = (p :> System.IComparable).CompareTo(q)
testR2All "IComparable.CompareTo" BigRationalCompareTo (fun (p,q) (pp,qq) -> compare ((p * sign q) * (qq * sign qq)) ((pp * sign qq) * (q * sign q))) vector2s
// Test: is negative, is positive
testR1All "IsNegative" (fun (x:BigRational) -> x.IsNegative) (fun (p,q) -> sign p * sign q = -1I) vector1s
testR1All "IsPositive" (fun (x:BigRational) -> x.IsPositive) (fun (p,q) -> sign p * sign q = 1I) vector1s
testR1All "IsZero" (fun (x:BigRational) -> x = q0) (fun (p,q) -> sign p = 0I) vector1s
let test1One name f check (p,q) =
// There may be several ways to construct the test rationals
let zs = makeQs p q
let results = [for z in zs -> f z]
let refP,refQ = check (p,q)
let refResult = BigRational.FromBigInt refP / BigRational.FromBigInt refQ
let resOK (result:BigRational) =
result.Numerator * refQ = refP * result.Denominator &&
BigRational.Equals(refResult,result)
match List.tryFind (fun result -> not (resOK result)) results with
| None -> () // ok
| Some result -> printf "Test failed. %s (%A,%A). Expected %A. Observed %A\n" name p q refResult result
reportFailure "klcwe09wek"
let test1All name f check vectors = List.iter (test1One name f check) vectors
test1All "neg" (BigRational.(~-)) (fun (p,q) -> (-p,q)) vector1s
test1All "pos" (BigRational.(~+)) (fun (p,q) -> (p,q)) vector1s // why have ~+ ???
// Test: Abs,Sign
test1All "Abs" (BigRational.Abs) (fun (p,q) -> (abs p,abs q)) vector1s
testR1All "Sign" (fun (x:BigRational) -> x.Sign) (fun (p,q) -> check "NonZeroDenom" (sign q <> 0I) true; (sign p * sign q) |> int32) vector1s
// Test: PowN
test1All "PowN(x,2)" (fun x -> BigRational.PowN(x,2)) (fun (p,q) -> (p*p,q*q)) vector1s
test1All "PowN(x,1)" (fun x -> BigRational.PowN(x,1)) (fun (p,q) -> (p,q)) vector1s
test1All "PowN(x,0)" (fun x -> BigRational.PowN(x,0)) (fun (p,q) -> (1I,1I)) vector1s
// MatteoT: moved to numbersVS2008\test.ml
//test1All "PowN(x,200)" (fun x -> BigRational.PowN(x,200)) (fun (p,q) -> (BigInteger.Pow(p,200I),BigInteger.Pow(q,200I))) vector1s
// MatteoT: moved to numbersVS2008\test.ml
//let powers = [0I .. 100I]
//powers |> List.iter (fun i -> test1All "PowN(x,i)" (fun x -> BigRational.PowN(x,int i)) (fun (p,q) -> (BigInteger.Pow(p,i),BigInteger.Pow(q,i))) vector1s)
// Test: PowN with negative powers - expect exception
testR1All "PowN(x,-1)" (fun x -> throws (fun () -> BigRational.PowN(x,-1))) (fun (p,q) -> true) vector1s
testR1All "PowN(x,-4)" (fun x -> throws (fun () -> BigRational.PowN(x,-4))) (fun (p,q) -> true) vector1s
[<TestFixture>]
type BigNumType() =
let g_positive1 = 1000000000000000000000000000000000018N
let g_positive2 = 1000000000000000000000000000000000000N
let g_negative1 = -1000000000000000000000000000000000018N
let g_negative2 = -1000000000000000000000000000000000000N
let g_negative3 = -1000000000000000000000000000000000036N
let g_zero = 0N
let g_normal = 88N
let g_bigintpositive = 1000000000000000000000000000000000018I
let g_bigintnegative = -1000000000000000000000000000000000018I
// Interfaces
[<Test>]
member this.IComparable() =
// Legit IC
let ic = g_positive1 :> IComparable
Assert.AreEqual(ic.CompareTo(g_positive1),0)
checkThrowsArgumentException( fun () -> ic.CompareTo(g_bigintpositive) |> ignore)
// Base class methods
[<Test>]
member this.ObjectToString() =
// Currently the CLR 4.0 and CLR 2.0 behavior of BigInt.ToString is different, causing this test to fail.
Assert.AreEqual(g_positive1.ToString(),
"1000000000000000000000000000000000018")
Assert.AreEqual(g_zero.ToString(),"0")
Assert.AreEqual(g_normal.ToString(),"88")
[<Test; Ignore("Bug 5286 - Differences between CLR 2.0 BigInt and CLR 4.0 BigInt")>]
member this.System_Object_GetHashCode() =
Assert.AreEqual(g_negative1.GetHashCode(),1210897093)
Assert.AreEqual(g_normal.GetHashCode(),89)
Assert.AreEqual(g_zero.GetHashCode(),1)
()
// Static methods
[<Test>]
member this.Abs() =
Assert.AreEqual(bignum.Abs(g_negative1), g_positive1)
Assert.AreEqual(bignum.Abs(g_negative2), g_positive2)
Assert.AreEqual(bignum.Abs(g_positive1), g_positive1)
Assert.AreEqual(bignum.Abs(g_normal), g_normal)
Assert.AreEqual(bignum.Abs(g_zero), g_zero)
()
[<Test>]
member this.FromBigInt() =
Assert.AreEqual(bignum.FromBigInt(g_bigintpositive),
g_positive1)
Assert.AreEqual(bignum.FromBigInt(g_bigintnegative),
g_negative1)
Assert.AreEqual(bignum.FromBigInt(0I),g_zero)
Assert.AreEqual(bignum.FromBigInt(88I),g_normal)
()
[<Test>]
member this.FromInt() =
Assert.AreEqual(bignum.FromInt(2147483647), 2147483647N)
Assert.AreEqual(bignum.FromInt(-2147483648), -2147483648N)
Assert.AreEqual(bignum.FromInt(0), 0N)
Assert.AreEqual(bignum.FromInt(88), 88N)
()
[<Test>]
member this.One() =
Assert.AreEqual(bignum.One,1N)
()
[<Test>]
member this.Parse() =
Assert.AreEqual(bignum.Parse("100"), 100N)
Assert.AreEqual(bignum.Parse("-100"), -100N)
Assert.AreEqual(bignum.Parse("0"), g_zero)
Assert.AreEqual(bignum.Parse("88"), g_normal)
()
[<Test>]
member this.PowN() =
Assert.AreEqual(bignum.PowN(100N,2), 10000N)
Assert.AreEqual(bignum.PowN(-3N,3), -27N)
Assert.AreEqual(bignum.PowN(g_zero,2147483647), 0N)
Assert.AreEqual(bignum.PowN(g_normal,0), 1N)
()
[<Test>]
member this.Sign() =
Assert.AreEqual(g_positive1.Sign, 1)
Assert.AreEqual(g_negative1.Sign, -1)
Assert.AreEqual(g_zero.Sign, 0)
Assert.AreEqual(g_normal.Sign, 1)
()
[<Test>]
member this.ToBigInt() =
Assert.AreEqual(bignum.ToBigInt(g_positive1), g_bigintpositive)
Assert.AreEqual(bignum.ToBigInt(g_negative1), g_bigintnegative)
Assert.AreEqual(bignum.ToBigInt(g_zero), 0I)
Assert.AreEqual(bignum.ToBigInt(g_normal), 88I)
()
[<Test>]
member this.ToDouble() =
Assert.AreEqual(bignum.ToDouble(179769N*1000000000000000N), 1.79769E+20)
Assert.AreEqual(bignum.ToDouble(-179769N*1000000000000000N), -1.79769E+20)
Assert.AreEqual(bignum.ToDouble(0N),0.0)
Assert.AreEqual(bignum.ToDouble(88N),88.0)
Assert.AreEqual(double(179769N*1000000000000000N), 1.79769E+20)
Assert.AreEqual(double(-179769N*1000000000000000N), -1.79769E+20)
Assert.AreEqual(double(0N),0.0)
Assert.AreEqual(double(88N),88.0)
()
[<Test>]
member this.ToInt32() =
Assert.AreEqual(bignum.ToInt32(2147483647N), 2147483647)
Assert.AreEqual(bignum.ToInt32(-2147483648N), -2147483648)
Assert.AreEqual(bignum.ToInt32(0N), 0)
Assert.AreEqual(bignum.ToInt32(88N), 88)
Assert.AreEqual(int32(2147483647N), 2147483647)
Assert.AreEqual(int32(-2147483648N), -2147483648)
Assert.AreEqual(int32(0N), 0)
Assert.AreEqual(int32(88N), 88)
[<Test>]
member this.Zero() =
Assert.AreEqual(bignum.Zero,0N)
()
// operator methods
[<Test>]
member this.test_op_Addition() =
Assert.AreEqual(100N + 200N, 300N)
Assert.AreEqual((-100N) + (-200N), -300N)
Assert.AreEqual(g_positive1 + g_negative1, 0N)
Assert.AreEqual(g_zero + g_zero,0N)
Assert.AreEqual(g_normal + g_normal, 176N)
Assert.AreEqual(g_normal + g_normal, 176N)
()
[<Test>]
member this.test_op_Division() =
Assert.AreEqual(g_positive1 / g_positive1, 1N)
Assert.AreEqual(-100N / 2N, -50N)
Assert.AreEqual(g_zero / g_positive1, 0N)
()
[<Test>]
member this.test_op_Equality() =
Assert.IsTrue((g_positive1 = g_positive1))
Assert.IsTrue((g_negative1 = g_negative1))
Assert.IsTrue((g_zero = g_zero))
Assert.IsTrue((g_normal = g_normal))
()
[<Test>]
member this.test_op_GreaterThan() =
Assert.AreEqual((g_positive1 > g_positive2), true)
Assert.AreEqual((g_negative1 > g_negative2), false)
Assert.AreEqual((g_zero > g_zero), false)
Assert.AreEqual((g_normal > g_normal), false)
()
[<Test>]
member this.test_op_GreaterThanOrEqual() =
Assert.AreEqual((g_positive1 >= g_positive2), true)
Assert.AreEqual((g_positive2 >= g_positive1), false)
Assert.AreEqual((g_negative1 >= g_negative1), true)
Assert.AreEqual((0N >= g_zero), true)
()
[<Test>]
member this.test_op_LessThan() =
Assert.AreEqual((g_positive1 < g_positive2), false)
Assert.AreEqual((g_negative1 < g_negative3), false)
Assert.AreEqual((0N < g_zero), false)
()
[<Test>]
member this.test_op_LessThanOrEqual() =
Assert.AreEqual((g_positive1 <= g_positive2), false)
Assert.AreEqual((g_positive2 <= g_positive1), true)
Assert.AreEqual((g_negative1 <= g_negative1), true)
Assert.AreEqual((0N <= g_zero), true)
()
[<Test>]
member this.test_op_Multiply() =
Assert.AreEqual(3N * 5N, 15N)
Assert.AreEqual((-3N) * (-5N), 15N)
Assert.AreEqual((-3N) * 5N, -15N)
Assert.AreEqual(0N * 5N, 0N)
()
[<Test>]
member this.test_op_Range() =
let resultPos = [0N .. 2N]
let seqPos = [0N; 1N; 2N]
verifySeqsEqual resultPos seqPos
let resultNeg = [-2N .. 0N]
let seqNeg = [-2N; -1N; 0N]
verifySeqsEqual resultNeg seqNeg
let resultSmall = [0N ..5N]
let seqSmall = [0N; 1N; 2N; 3N; 4N; 5N]
verifySeqsEqual resultSmall seqSmall
()
[<Test>]
member this.test_op_RangeStep() =
let resultPos = [0N .. 3N .. 6N]
let seqPos = [0N; 3N; 6N]
verifySeqsEqual resultPos seqPos
let resultNeg = [-6N .. 3N .. 0N]
let seqNeg = [-6N; -3N; 0N]
verifySeqsEqual resultNeg seqNeg
let resultSmall = [0N .. 3N .. 9N]
let seqSmall = [0N; 3N; 6N; 9N]
verifySeqsEqual resultSmall seqSmall
()
[<Test>]
member this.test_op_Subtraction() =
Assert.AreEqual(g_positive1 - g_positive2,18N)
Assert.AreEqual(g_negative1 - g_negative3,18N)
Assert.AreEqual(0N-g_positive1, g_negative1)
()
[<Test>]
member this.test_op_UnaryNegation() =
Assert.AreEqual(-g_positive1, g_negative1)
Assert.AreEqual(-g_negative1, g_positive1)
Assert.AreEqual(-0N,0N)
()
[<Test>]
member this.test_op_UnaryPlus() =
Assert.AreEqual(+g_positive1,g_positive1)
Assert.AreEqual(+g_negative1,g_negative1)
Assert.AreEqual(+0N, 0N)
()
// instance methods
[<Test>]
member this.Denominator() =
Assert.AreEqual(g_positive1.Denominator, 1I)
Assert.AreEqual(g_negative1.Denominator, 1I)
Assert.AreEqual(0N.Denominator, 1I)
()
[<Test>]
member this.IsNegative() =
Assert.IsFalse(g_positive1.IsNegative)
Assert.IsTrue(g_negative1.IsNegative)
Assert.IsFalse( 0N.IsNegative)
Assert.IsFalse(-0N.IsNegative)
()
[<Test>]
member this.IsPositive() =
Assert.IsTrue(g_positive1.IsPositive)
Assert.IsFalse(g_negative1.IsPositive)
Assert.IsFalse( 0N.IsPositive)
Assert.IsFalse(-0N.IsPositive)
()
[<Test>]
member this.Numerator() =
Assert.AreEqual(g_positive1.Numerator, g_bigintpositive)
Assert.AreEqual(g_negative1.Numerator, g_bigintnegative)
Assert.AreEqual(0N.Numerator, 0I)
()

79
src/FSharpUnitTests/DistributionTest.fs

@ -0,0 +1,79 @@
module MathNet.Numerics.Tests.DistributionTest
open MathNet.Numerics
open MathNet.Numerics.Distribution
open NUnit.Framework
open FsUnit
[<Test>]
let ``When creating a empty distribution, then the probability should be 1``() =
let actual = distribution { return () }
probability actual |> should equal (1N/1N)
let sumOfTwoFairDices = distribution {
let! d1 = fairDice 6
let! d2 = fairDice 6
return d1 + d2 }
[<Test>]
let ``When creating two fair dices, then P(Sum of 2 dices = 7) should be 1/6``() =
sumOfTwoFairDices
|> filter ((=) 7)
|> probability
|> should equal (1N/6N)
let fairCoinAndDice = distribution {
let! d = fairDice 6
let! c = fairCoin
return d,c }
[<Test>]
let ``When creating a fair coin and a fair dice, then P(Heads) should be 1/2``() =
fairCoinAndDice
|> filter (fun (_,c) -> c = Heads)
|> probability
|> should equal (1N/2N)
[<Test>]
let ``When creating a fair coin and a fair dice, then P(Heads and dice > 3) should be 1/4``() =
fairCoinAndDice
|> filter (fun (d,c) -> c = Heads && d > 3)
|> probability
|> should equal (1N/4N)
// MontyHall Problem
// See Martin Erwig and Steve Kollmansberger's paper
// "Functional Pearls: Probabilistic functional programming in Haskell"
type Outcome =
| Car
| Goat
let firstChoice = toUniformDistribution [Car; Goat; Goat]
let switch firstCoice =
match firstCoice with
| Car ->
// If you had the car and you switch ==> you lose since there are only goats left
certainly Goat
| Goat ->
// If you had the goat, the host has to take out another goat ==> you win
certainly Car
[<Test>]
let ``When making the first choice in a MontyHall situation, the chances to win should be 1/3``() =
firstChoice
|> filter ((=) Car)
|> probability
|> should equal (1N/3N)
let montyHallWithSwitch = distribution {
let! firstDoor = firstChoice
return! switch firstDoor }
[<Test>]
let ``When switching in a MontyHall situation, the chances to win should be 2/3``() =
montyHallWithSwitch
|> filter ((=) Car)
|> probability
|> should equal (2N/3N)

9
src/FSharpUnitTests/FSharpUnitTests.fsproj

@ -46,14 +46,23 @@
<Import Project="$(MSBuildExtensionsPath32)\FSharp\1.0\Microsoft.FSharp.Targets" Condition="(!Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')) And (!Exists('$(MSBuildExtensionsPath32)\..\Microsoft F#\v4.0\Microsoft.FSharp.Targets')) And (Exists('$(MSBuildExtensionsPath32)\FSharp\1.0\Microsoft.FSharp.Targets'))" />
<ItemGroup>
<Compile Include="FsUnit.fs" />
<Compile Include="Utilities.fs" />
<Compile Include="BigRationalTests.fs" />
<Compile Include="DistributionTest.fs" />
<Compile Include="PokerDistributionTest.fs" />
<Compile Include="Program.fs" />
<None Include="App.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Reference Include="FSharp.Core" />
<Reference Include="mscorlib" />
<Reference Include="nunit.framework">
<HintPath>..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>

96
src/FSharpUnitTests/PokerDistributionTest.fs

@ -0,0 +1,96 @@
module MathNet.Numerics.Tests.PokerDistributionTest
open MathNet.Numerics
open MathNet.Numerics.Distribution
open NUnit.Framework
open FsUnit
type Rank = int
type Suit = | Spades | Hearts | Diamonds | Clubs
type Card = Rank * Suit
let value = fst
let suit = snd
let A,K,Q,J,T = 14,13,12,11,10
let allRanksInSuit suit = [2..A] |> List.map (fun rank -> rank,suit)
let completeDeck =
[Spades; Hearts ; Diamonds; Clubs]
|> List.map allRanksInSuit
|> List.concat
let isPair c1 c2 = value c1 = value c2
let isSuited c1 c2 = suit c1 = suit c2
let isConnected c1 c2 =
let v1,v2 = value c1,value c2
(v1 - v2 |> abs |> (=) 1) ||
(v1 = A && v2 = 2) ||
(v1 = 2 && v2 = A)
[<Test>]
let ``When drawing from a full deck, then the probability for an Ace should equal 4/52``() =
completeDeck
|> selectOne |> map fst
|> filter (fun card -> value card = A)
|> probability
|> should equal (4N/52N)
[<Test>]
let ``When drawing from a full deck, then the probability should equal 1/52``() =
completeDeck
|> selectOne |> map fst
|> filter ((=) (A,Spades))
|> probability
|> should equal (1N/52N)
[<Test>]
let ``When drawing from a full deck, then the probability for the Ace of Clubs and Ace of Spaces (in order) should equal 1/52 * 1/51``() =
completeDeck
|> select 2
|> filter ((=) [A,Clubs; A,Spades])
|> probability
|> should equal (1N/52N * 1N/51N)
[<Test>]
let ``When drawing from a full deck, then the probability for the Ace of Clubs and Ace of Spaces (in any order) should equal (1/52 * 1/51) * 2``() =
completeDeck
|> select 2
|> filterInAnyOrder [A,Clubs; A,Spades]
|> probability
|> should equal ((1N/52N * 1N/51N) * 2N)
[<Test>]
let ``When drawing the Ace of Spades and the Ace of Clubs, then the probability for drawing another Ace should equal 2/50``() =
completeDeck
|> remove [A,Clubs; A,Spades]
|> toUniformDistribution
|> filter (fun card -> value card = A)
|> probability
|> should equal (2N/50N)
[<Test>]
let ``When drawing from the full deck, then the probability for drawing a Pair preflop should equal 1/17``() =
completeDeck
|> select 2
|> filter (fun (c1::c2::_) -> isPair c1 c2)
|> probability
|> should equal (1N/17N)
[<Test>]
let ``When drawing from the full deck, then the probability for drawing Suited Connectors should equal 1/25``() =
completeDeck
|> select 2
|> filter (fun (c1::c2::_) -> isSuited c1 c2 && isConnected c1 c2)
|> probability
|> should equal (2N/51N)
[<Test>]
let ``When holding 3 Spades after the flop, than the probability for drawing a flush should equal 10/47*9/46``() =
completeDeck
|> remove [A,Clubs; A,Spades] // preflop
|> remove [2,Clubs; 3,Spades; 7,Spades] // flop
|> select 2
|> filter (fun (c1::c2::_) -> suit c1 = Spades && suit c2 = Spades)
|> probability
|> should equal (10N/47N*9N/46N)

128
src/FSharpUnitTests/Utilities.fs

@ -0,0 +1,128 @@
// First version copied from the F# Power Pack
// https://raw.github.com/fsharp/powerpack/master/src/FSharp.PowerPack.Unittests/Utilities.fs
namespace MathNet.Numerics.Tests
open NUnit.Framework
open System
open System.Collections.Generic
[<AutoOpen>]
module Utilities =
let test msg b = Assert.IsTrue(b, "MiniTest '" + msg + "'")
let logMessage msg =
System.Console.WriteLine("LOG:" + msg)
// System.Diagnostics.Trace.WriteLine("LOG:" + msg)
let check msg v1 v2 = test msg (v1 = v2)
let reportFailure msg = Assert.Fail msg
let numActiveEnumerators = ref 0
let throws f = try f() |> ignore; false with e -> true
let countEnumeratorsAndCheckedDisposedAtMostOnceAtEnd (seq: seq<'a>) =
let enumerator() =
numActiveEnumerators := !numActiveEnumerators + 1;
let disposed = ref false in
let endReached = ref false in
let ie = seq.GetEnumerator() in
{ new System.Collections.Generic.IEnumerator<'a> with
member x.Current =
test "rvlrve0" (not !endReached);
test "rvlrve1" (not !disposed);
ie.Current
member x.Dispose() =
test "rvlrve2" !endReached;
test "rvlrve4" (not !disposed);
numActiveEnumerators := !numActiveEnumerators - 1;
disposed := true;
ie.Dispose()
interface System.Collections.IEnumerator with
member x.MoveNext() =
test "rvlrve0" (not !endReached);
test "rvlrve3" (not !disposed);
endReached := not (ie.MoveNext());
not !endReached
member x.Current =
test "qrvlrve0" (not !endReached);
test "qrvlrve1" (not !disposed);
box ie.Current
member x.Reset() =
ie.Reset()
} in
{ new seq<'a> with
member x.GetEnumerator() = enumerator()
interface System.Collections.IEnumerable with
member x.GetEnumerator() = (enumerator() :> _) }
let countEnumeratorsAndCheckedDisposedAtMostOnce (seq: seq<'a>) =
let enumerator() =
let disposed = ref false in
let endReached = ref false in
let ie = seq.GetEnumerator() in
numActiveEnumerators := !numActiveEnumerators + 1;
{ new System.Collections.Generic.IEnumerator<'a> with
member x.Current =
test "qrvlrve0" (not !endReached);
test "qrvlrve1" (not !disposed);
ie.Current
member x.Dispose() =
test "qrvlrve4" (not !disposed);
numActiveEnumerators := !numActiveEnumerators - 1;
disposed := true;
ie.Dispose()
interface System.Collections.IEnumerator with
member x.MoveNext() =
test "qrvlrve0" (not !endReached);
test "qrvlrve3" (not !disposed);
endReached := not (ie.MoveNext());
not !endReached
member x.Current =
test "qrvlrve0" (not !endReached);
test "qrvlrve1" (not !disposed);
box ie.Current
member x.Reset() =
ie.Reset()
} in
{ new seq<'a> with
member x.GetEnumerator() = enumerator()
interface System.Collections.IEnumerable with
member x.GetEnumerator() = (enumerator() :> _) }
// Verifies two sequences are equal (same length, equiv elements)
let verifySeqsEqual seq1 seq2 =
if Seq.length seq1 <> Seq.length seq2 then Assert.Fail()
let zippedElements = Seq.zip seq1 seq2
if zippedElements |> Seq.forall (fun (a, b) -> a = b)
then ()
else Assert.Fail()
/// Check that the lamda throws an exception of the given type. Otherwise
/// calls Assert.Fail()
let private checkThrowsExn<'a when 'a :> exn> (f : unit -> unit) =
let funcThrowsAsExpected =
try
let _ = f ()
false // Did not throw!
with
| :? 'a
-> true // Thew null ref, OK
| _ -> false // Did now throw a null ref exception!
if funcThrowsAsExpected
then ()
else Assert.Fail()
// Illegitimate exceptions. Once we've scrubbed the library, we should add an
// attribute to flag these exception's usage as a bug.
let checkThrowsNullRefException f = checkThrowsExn<NullReferenceException> f
let checkThrowsIndexOutRangException f = checkThrowsExn<IndexOutOfRangeException> f
// Legit exceptions
let checkThrowsNotSupportedException f = checkThrowsExn<NotSupportedException> f
let checkThrowsArgumentException f = checkThrowsExn<ArgumentException> f
let checkThrowsArgumentNullException f = checkThrowsExn<ArgumentNullException> f
let checkThrowsKeyNotFoundException f = checkThrowsExn<KeyNotFoundException> f
let checkThrowsDivideByZeroException f = checkThrowsExn<DivideByZeroException> f
let checkThrowsInvalidOperationExn f = checkThrowsExn<InvalidOperationException> f

4
src/FSharpUnitTests/packages.config

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NUnit" version="2.6.2" targetFramework="net40" />
</packages>
Loading…
Cancel
Save