From 94677c65486659824b7cc5553cff69b9b85a08dd Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Fri, 30 Nov 2012 22:28:44 +0000 Subject: [PATCH 01/15] Bring Complex & BigRational from F# PowerPack, and DistributionMonad from FSharpx --- packages/repositories.config | 1 + src/FSharp/DistributionMonad.fs | 98 +++ src/FSharp/FSharp.fsproj | 5 + src/FSharp/complex.fs | 133 ++++ src/FSharp/complex.fsi | 139 +++++ src/FSharp/q.fs | 309 ++++++++++ src/FSharp/q.fsi | 96 +++ src/FSharpUnitTests/BigRationalTests.fs | 614 +++++++++++++++++++ src/FSharpUnitTests/DistributionTest.fs | 79 +++ src/FSharpUnitTests/FSharpUnitTests.fsproj | 9 + src/FSharpUnitTests/PokerDistributionTest.fs | 96 +++ src/FSharpUnitTests/Utilities.fs | 128 ++++ src/FSharpUnitTests/packages.config | 4 + 13 files changed, 1711 insertions(+) create mode 100644 src/FSharp/DistributionMonad.fs create mode 100644 src/FSharp/complex.fs create mode 100644 src/FSharp/complex.fsi create mode 100644 src/FSharp/q.fs create mode 100644 src/FSharp/q.fsi create mode 100644 src/FSharpUnitTests/BigRationalTests.fs create mode 100644 src/FSharpUnitTests/DistributionTest.fs create mode 100644 src/FSharpUnitTests/PokerDistributionTest.fs create mode 100644 src/FSharpUnitTests/Utilities.fs create mode 100644 src/FSharpUnitTests/packages.config diff --git a/packages/repositories.config b/packages/repositories.config index 2cfb26c2..f8ef82c6 100644 --- a/packages/repositories.config +++ b/packages/repositories.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/src/FSharp/DistributionMonad.fs b/src/FSharp/DistributionMonad.fs new file mode 100644 index 00000000..eb32c91b --- /dev/null +++ b/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) diff --git a/src/FSharp/FSharp.fsproj b/src/FSharp/FSharp.fsproj index c8092380..26d2f50d 100644 --- a/src/FSharp/FSharp.fsproj +++ b/src/FSharp/FSharp.fsproj @@ -57,6 +57,11 @@ + + + + + diff --git a/src/FSharp/complex.fs b/src/FSharp/complex.fs new file mode 100644 index 00000000..65707075 --- /dev/null +++ b/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 + + [] + [] + 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 + + [] + 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) + diff --git a/src/FSharp/complex.fsi b/src/FSharp/complex.fsi new file mode 100644 index 00000000..185d9c5e --- /dev/null +++ b/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 + [] + [] + 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 + + + [] + [] + 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 + + + [] + module ComplexTopLevelOperators = + /// Constructs a complex number from both the real and imaginary part. + val complex : float -> float -> complex + + + diff --git a/src/FSharp/q.fs b/src/FSharp/q.fs new file mode 100644 index 00000000..2cb24129 --- /dev/null +++ b/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 + + [] + 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 + + + [] + 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 + //-------------------------------------------------------------------------- + + [] + [] + 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 diff --git a/src/FSharp/q.fsi b/src/FSharp/q.fsi new file mode 100644 index 00000000..ffcf84e2 --- /dev/null +++ b/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 + [] + 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 + + [] + module NumericLiteralN = + val FromZero : unit -> BigRational + val FromOne : unit -> BigRational + val FromInt32 : int32 -> BigRational + val FromInt64 : int64 -> BigRational + val FromString : string -> BigRational \ No newline at end of file diff --git a/src/FSharpUnitTests/BigRationalTests.fs b/src/FSharpUnitTests/BigRationalTests.fs new file mode 100644 index 00000000..1173e196 --- /dev/null +++ b/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 + + +[] +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 + + [] + 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 = 0N) + test "d3oc112w" (LanguagePrimitives.GenericOne = 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()) + + + [] + 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) + + + + [] + 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) + + [] + 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 + + + +[] +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 + [] + 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 + [] + 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") + + + [] + 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 + [] + 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) + () + + [] + 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) + () + + [] + 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) + () + + [] + member this.One() = + Assert.AreEqual(bignum.One,1N) + () + + [] + 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) + () + + [] + 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) + () + + + [] + 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) + () + + + + [] + 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) + () + + + + [] + 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) + () + + + [] + 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) + + + + [] + member this.Zero() = + Assert.AreEqual(bignum.Zero,0N) + () + + // operator methods + [] + 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) + () + + + + [] + member this.test_op_Division() = + Assert.AreEqual(g_positive1 / g_positive1, 1N) + Assert.AreEqual(-100N / 2N, -50N) + Assert.AreEqual(g_zero / g_positive1, 0N) + () + + [] + 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)) + () + + [] + 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) + + + () + [] + 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) + + () + [] + 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) + + () + [] + 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) + + () + + [] + 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) + + () + + [] + 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 + + () + + + [] + 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 + + () + + [] + 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) + () + + [] + member this.test_op_UnaryNegation() = + Assert.AreEqual(-g_positive1, g_negative1) + Assert.AreEqual(-g_negative1, g_positive1) + Assert.AreEqual(-0N,0N) + + () + + [] + member this.test_op_UnaryPlus() = + Assert.AreEqual(+g_positive1,g_positive1) + Assert.AreEqual(+g_negative1,g_negative1) + Assert.AreEqual(+0N, 0N) + + () + + // instance methods + [] + member this.Denominator() = + Assert.AreEqual(g_positive1.Denominator, 1I) + Assert.AreEqual(g_negative1.Denominator, 1I) + Assert.AreEqual(0N.Denominator, 1I) + + () + + [] + member this.IsNegative() = + Assert.IsFalse(g_positive1.IsNegative) + Assert.IsTrue(g_negative1.IsNegative) + + Assert.IsFalse( 0N.IsNegative) + Assert.IsFalse(-0N.IsNegative) + + () + + + [] + member this.IsPositive() = + + Assert.IsTrue(g_positive1.IsPositive) + Assert.IsFalse(g_negative1.IsPositive) + + Assert.IsFalse( 0N.IsPositive) + Assert.IsFalse(-0N.IsPositive) + + () + + [] + member this.Numerator() = + Assert.AreEqual(g_positive1.Numerator, g_bigintpositive) + Assert.AreEqual(g_negative1.Numerator, g_bigintnegative) + Assert.AreEqual(0N.Numerator, 0I) + + () + + + + + diff --git a/src/FSharpUnitTests/DistributionTest.fs b/src/FSharpUnitTests/DistributionTest.fs new file mode 100644 index 00000000..b511f18d --- /dev/null +++ b/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 + +[] +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 } + +[] +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 } + +[] +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) + +[] +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 + +[] +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 } + +[] +let ``When switching in a MontyHall situation, the chances to win should be 2/3``() = + montyHallWithSwitch + |> filter ((=) Car) + |> probability + |> should equal (2N/3N) \ No newline at end of file diff --git a/src/FSharpUnitTests/FSharpUnitTests.fsproj b/src/FSharpUnitTests/FSharpUnitTests.fsproj index d53cf840..4eefb688 100644 --- a/src/FSharpUnitTests/FSharpUnitTests.fsproj +++ b/src/FSharpUnitTests/FSharpUnitTests.fsproj @@ -46,14 +46,23 @@ + + + + Always + + + ..\..\packages\NUnit.2.6.2\lib\nunit.framework.dll + True + 3.5 diff --git a/src/FSharpUnitTests/PokerDistributionTest.fs b/src/FSharpUnitTests/PokerDistributionTest.fs new file mode 100644 index 00000000..b2702a71 --- /dev/null +++ b/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) + +[] +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) + +[] +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) + +[] +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) + +[] +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) + +[] +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) + + +[] +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) + +[] +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) + +[] +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) \ No newline at end of file diff --git a/src/FSharpUnitTests/Utilities.fs b/src/FSharpUnitTests/Utilities.fs new file mode 100644 index 00000000..77975580 --- /dev/null +++ b/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 + +[] +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 f + let checkThrowsIndexOutRangException f = checkThrowsExn f + + // Legit exceptions + let checkThrowsNotSupportedException f = checkThrowsExn f + let checkThrowsArgumentException f = checkThrowsExn f + let checkThrowsArgumentNullException f = checkThrowsExn f + let checkThrowsKeyNotFoundException f = checkThrowsExn f + let checkThrowsDivideByZeroException f = checkThrowsExn f + let checkThrowsInvalidOperationExn f = checkThrowsExn f + + \ No newline at end of file diff --git a/src/FSharpUnitTests/packages.config b/src/FSharpUnitTests/packages.config new file mode 100644 index 00000000..5c3ca54d --- /dev/null +++ b/src/FSharpUnitTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 7dcb6e80b4093d61392ed43a3844724c1d744445 Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Sat, 1 Dec 2012 09:33:34 +0000 Subject: [PATCH 02/15] Add N and Z implementation from FSharp.Core to portable build --- src/FSharpPortable/FSharpPortable.fsproj | 23 +- src/FSharpPortable/n.fs | 1611 ++++++++++++++++++++++ src/FSharpPortable/n.fsi | 71 + src/FSharpPortable/z.fs | 411 ++++++ src/FSharpPortable/z.fsi | 124 ++ 5 files changed, 2238 insertions(+), 2 deletions(-) create mode 100644 src/FSharpPortable/n.fs create mode 100644 src/FSharpPortable/n.fsi create mode 100644 src/FSharpPortable/z.fs create mode 100644 src/FSharpPortable/z.fsi diff --git a/src/FSharpPortable/FSharpPortable.fsproj b/src/FSharpPortable/FSharpPortable.fsproj index 67038619..420908c3 100644 --- a/src/FSharpPortable/FSharpPortable.fsproj +++ b/src/FSharpPortable/FSharpPortable.fsproj @@ -19,7 +19,7 @@ false false ..\..\out\debug\Portable\ - TRACE;DEBUG;PORTABLE + TRACE;DEBUG;PORTABLE;FX_NO_BIGINT 3 @@ -29,7 +29,7 @@ true true ..\..\out\lib\Portable\ - TRACE;PORTABLE + TRACE;PORTABLE;FX_NO_BIGINT 3 ..\..\out\lib\Portable\MathNet.Numerics.FSharp.XML @@ -62,6 +62,25 @@ LinearAlgebra.Double.fs + + + + + + complex.fsi + + + complex.fs + + + q.fsi + + + q.fs + + + DistributionMonad.fs + diff --git a/src/FSharpPortable/n.fs b/src/FSharpPortable/n.fs new file mode 100644 index 00000000..a5c97799 --- /dev/null +++ b/src/FSharpPortable/n.fs @@ -0,0 +1,1611 @@ +// First version copied from the F# compiler sources +// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/n.fs + +//---------------------------------------------------------------------------- +// Copyright (c) 2002-2012 Microsoft Corporation. +// +// This source code is subject to terms and conditions of the Apache License, Version 2.0. A +// copy of the license can be found in the License.html file at the root of this distribution. +// By using this source code in any fashion, you are agreeing to be bound +// by the terms of the Apache License, Version 2.0. +// +// You must not remove this notice, or any other, from this software. +//---------------------------------------------------------------------------- + +namespace Microsoft.FSharp.Math + +#if FX_NO_BIGINT +open System +open System.Diagnostics.CodeAnalysis +open Microsoft.FSharp.Core +open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators +open Microsoft.FSharp.Core.Operators +open Microsoft.FSharp.Collections +open Microsoft.FSharp.Primitives.Basics + +type ints = int array + +[] +type internal BigNat = + + // Have n = sum (from i=0 to bound) a.[i] * baseN ^ i + // Have 0 <= a.[i] < baseN. + //------ + // Invariant: bound is least such, i.e. bound=0 or (a.[bound-1] is highest coeff). + // Zero is {bound=0,a=...}. + // Naturals are a normal form, + // but not structurally so, + // since arrays may have non-contributing cells at a.[bound] and beyond. + // + { mutable bound : int; // non-zero coeff must be 0...(bound-1) + digits : ints // must have at least elts 0...(bound-1), + // maybe more (which should be zero!). + // Actually, the "zero" condition may be relaxed. + // + } + + +module internal BigNatModule = + + //------------------------------------------------------------------------- + // misc + //----------------------------------------------------------------------- + + #if SELFTEST + let check b = if not b then failwith "assertion failwith" + #endif + + module FFT = + let rec pow32 x n = + if n=0 then 1 + elif n % 2 = 0 then pow32 (x*x) (n / 2) + else x* pow32 (x*x) (n / 2) + + let leastBounding2Power b = + let rec findBounding2Power b tp i = if b<=tp then tp,i else findBounding2Power b (tp*2) (i+1) in + findBounding2Power b 1 0 + + //------------------------------------------------------------------------- + // p = 2^k.m + 1 prime and w primitive 2^k root of 1 mod p + //----------------------------------------------------------------------- + + // Given p = 2^k.m + 1 prime and w a primitive 2^k root of unity (mod p). + // Required to define arithmetic ops for Fp = field modulo p. + // The following are possible choices for p. + + // p, k, m, g, w + // let p,k,m,g,w = 97L, 4, 6, 5, 8 // p is 7 bit + // let p,k,m,g,w = 769L, 8, 3, 7, 7 // p is 10 bit + // let p,k,m,g,w = 7681L, 8, 30, 13, 198 // p is 13 bit + // let p,k,m,g,w = 12289L, 10, 12, 11, 49 // p is 14 bit + // let p,k,m,g,w = 167772161L, 25, 5, 557092, 39162105 // p is 28 bit + // let p,k,m,g,w = 469762049L, 26, 7, 1226571, 288772249 // p is 29 bit + + + let p,k,m,g,w = 2013265921L, 27, 15, 31, 440564289 // p is 31 bit + let primeP = p + + let maxBitsInsideFp = 30 + + + //------------------------------------------------------------------------- + // Fp = finite field mod p - rep is uint32 + //----------------------------------------------------------------------- + + + type fp = uint32 + // operations in Fp (finite field size p) + module Fp = + //module I = UInt32 + let p = 2013265921ul : fp + let p64 = 2013265921UL : uint64 + let toInt (x:fp) : int = int32 x + let ofInt32 (x:int) : fp = uint32 x + + let mzero : fp = 0ul + let mone : fp = 1ul + let mtwo : fp = 2ul + let inline madd (x:fp) (y:fp) : fp = (x + y) % p + let inline msub (x:fp) (y:fp) : fp = (x + p - y) % p + let inline mmul (x:fp) (y:fp) : fp = uint32 ((uint64 x * uint64 y) % p64) + + let rec mpow x n = + if n=0 then mone + elif n % 2=0 then mpow (mmul x x) (n / 2) + else mmul x (mpow (mmul x x) (n / 2)) + + let rec mpowL x n = + if n = 0L then mone + elif n % 2L = 0L then mpowL (mmul x x) (n / 2L) + else mmul x (mpowL (mmul x x) (n / 2L)) + + // Have the w is primitive 2^kth root of 1 in Zp + let m2PowNthRoot n = + // Find x s.t. x is (2^n)th root of unity. + // + // pow w (pow 2 k) = 1 primitively. + // = pow w (pow 2 ((k-n)+n)) + // = pow w (pow 2 (k-n) * pow 2 n) + // = pow (pow w (pow 2 (k-n))) (pow 2 n) + // + // Take wn = pow (pow w (pow 2 (k-n))) + + mpow (uint32 w) (pow32 2 (k-n)) + + let minv x = mpowL x (primeP - 2L) + + + //------------------------------------------------------------------------- + // FFT - in place low garbage + //----------------------------------------------------------------------- + + open Fp + let rec computeFFT lambda mu n w (u: _[]) (res: _[]) offset = + // Given n a 2-power, + // w an nth root of 1 in Fp, and + // lambda, mu and u(x) defining + // poly(lambda,mu,x) = sum(i pow32 2 i) + + let computeFftPaddedPolynomialProduct bigK k u v = + // REQUIRES: bigK = 2^k + // REQUIRES: Array lengths of u and v = bigK. + // REQUIRES: degree(uv) <= bigK-1 + // --- + // Given u,v polynomials. + // Computes the product polynomial by FFT. + // For correctness, + // require the result coeff to be in range [0,p-1], for p defining Fp above. + + #if SELFTEST + check ( k <= maxTwoPower ); + check ( bigK = twoPowerTable.[k] ); + check ( u.Length = bigK ); + check ( v.Length = bigK ); + #endif + // Find 2^k primitive root of 1 + let w = m2PowNthRoot k + // FFT + let n = bigK + let uT = computFftInPlace n w u + let vT = computFftInPlace n w v + // Evaluate + let rT = Array.init n (fun i -> mmul uT.[i] vT.[i]) + // INV FFT + let r = computeInverseFftInPlace n w rT + r + + let padTo n (u: _ array) = + let uBound = u.Length + Array.init n (fun i -> if i mmul uT.[i] vT.[i]) + // INV FFT + let r = computeInverseFftInPlace n w rT + Array.map Fp.toInt r + + + //------------------------------------------------------------------------- + // fp exports + //----------------------------------------------------------------------- + + open Fp + let mzero = mzero + let mone = mone + let maxFp = msub Fp.p mone + + //------------------------------------------------------------------------- + // FFT - reference implementation + //----------------------------------------------------------------------- + + #if SELFTEST + open Fp + let rec computeFftReference n w u = + // Given n a 2-power, + // w an nth root of 1 in Fp, and + // u(x) = sum(i u.[2*i]) + let uodd = Array.init (n/2) (fun i -> u.[2*i+1]) + let uevenFT = computeFftReference (n/2) (mmul w w) ueven + let uoddFT = computeFftReference (n/2) (mmul w w) uodd + Array.init n + (fun j -> + if j < n/2 then + madd + (uevenFT.[j]) + (mmul + (mpow w j) + (uoddFT.[j])) + else + let j = j - (n/2) + msub + (uevenFT.[j]) + (mmul + (mpow w j) + (uoddFT.[j]))) + #endif + + open FFT + + type n = BigNat + + let bound (n: n) = n.bound + let setBound (n: n) (v:int32) = n.bound <- v + let coeff (n:n) i = n.digits.[i] + let coeff64 (n:n) i = int64 (coeff n i) + let setCoeff (n:n) i v = n.digits.[i] <- v + + let rec pow64 x n = + if n=0 then 1L + elif n % 2 = 0 then pow64 (x * x) (n / 2) + else x * (pow64 (x * x) (n / 2)) + + let rec pow32 x n = + if n=0 then 1 + elif n % 2 = 0 then pow32 (x*x) (n / 2) + else x* pow32 (x*x) (n / 2) + + let hash(n) = + let mutable res = 0 + for i = 0 to n.bound - 1 do // could stop soon, it's "hash" + res <- n.digits.[i] + (res <<< 3) + done; + res + + //---------------------------------------------------------------------------- + // misc + //-------------------------------------------------------------------------- + +#if CHECKED + let check b str = if not b then failwith ("check failed: " + str) +#endif + let maxInt a b = if a int32 + let inline div64base (x:int64) = int64 (uint64 x >>> baseBits) + + let divbase x = int32 (uint32 x >>> baseBits) + let modbase x = (x &&& baseMask) + + let inline index z i = if i < z.bound then z.digits.[i] else 0 + + let createN b = { bound = b; + digits = Array.zeroCreate b } + let copyN x = { bound = x.bound; + digits = Array.copy x.digits } // could copy just enough... + + let normN n = + // normalises bound + let rec findLeastBound (na:ints) i = if i = -1 || na.[i]<>0 then i+1 else findLeastBound na (i-1) + let bound = findLeastBound n.digits (n.bound-1) + n.bound <- bound; + n + + let boundInt = 2 // int will fit with bound=2 + let boundInt64 = 3 // int64 will fit with bound=3 + let boundBase = 1 // base will fit with bound=1 - obviously! + +//---------------------------------------------------------------------------- +// base, coefficients, poly +//-------------------------------------------------------------------------- + + let embed x = + let x = if x<0 then 0 else x // no -ve naturals + if x < baseN then + let r = createN 1 + r.digits.[0] <- x; + normN r + else + let r = createN boundInt + for i = 0 to boundInt - 1 do + r.digits.[i] <- (x / pow32 baseN i) % baseN + done; + normN r + + let embed64 x = + let x = if x<0L then 0L else x // no -ve naturals + let r = createN boundInt64 + for i = 0 to boundInt64-1 do + r.digits.[i] <- int32 ( (x / pow64 baseNi64 i) % baseNi64) + done; + normN r + + let eval n = + if n.bound = 1 + then n.digits.[0] + else + let mutable acc = 0 + for i = n.bound-1 downto 0 do + acc <- n.digits.[i] + baseN * acc + done; + acc + + let eval64 n = + if n.bound = 1 + then int64 n.digits.[0] + else + let mutable acc = 0L + for i = n.bound-1 downto 0 do + acc <- int64 (n.digits.[i]) + baseNi64 * acc + done; + acc + + let one = embed 1 + let zero = embed 0 + + let restrictTo d n = + { bound = minInt d n.bound; digits = n.digits} + + let shiftUp d n = + let m = createN (n.bound+d) + for i = 0 to n.bound-1 do + m.digits.[i+d] <- n.digits.[i] + done; + m + + let shiftDown d n = + if n.bound-d<=0 then + zero + else + let m = createN (n.bound-d) + for i = 0 to m.bound-1 do + m.digits.[i] <- n.digits.[i+d] + done; + m + + let degree n = n.bound-1 + + +//---------------------------------------------------------------------------- +// add, sub +//-------------------------------------------------------------------------- + + // addition + let rec addP i n c p q r = // p+q + c + if i0 then + r.digits.[i] <- modbase x; + let c = divbase x + // if p (or q) exhausted and c zero could switch to copying mode + subP (i+1) n c p q r + else + let x = x + baseN // add baseN + r.digits.[i] <- modbase x; + let c = divbase x - 1 // sub baseN + // if p (or q) exhausted and c zero could switch to copying mode + subP (i+1) n c p q r + else + let underflow = c<>0 + underflow + + let sub p q = + // NOTE: x-y=0 when x<=y, it is natural subtraction + let rbound = maxInt p.bound q.bound + let r = createN rbound + let carry = 0 + let underflow = subP 0 rbound carry p q r + if underflow then + embed 0 + else + normN r + + +//---------------------------------------------------------------------------- +// isZero, equal, ordering, sign, min, max +//-------------------------------------------------------------------------- + + let isZero p = p.bound=0 + let IsZero p = isZero p + let isOne p = p.bound=1 && p.digits.[0] = 1 + + let equal p q = + (p.bound = q.bound) && + (let rec check (pa:ints) (qa:ints) i = + // HAVE: pa.[j] = qa.[j] for i < j < p.bound + (i = -1) || (pa.[i]=qa.[i] && check pa qa (i-1)) + + check p.digits q.digits (p.bound-1)) + + let shiftCompare p pn q qn = + if p.bound + pn < q.bound + qn then -1 + elif p.bound + pn > q.bound + pn then 1 + else + let rec check (pa:ints) (qa:ints) i = + // HAVE: pa.[j-pn] = qa.[j-qn] for i < j < p.bound + // Looking for most significant differing coeffs to determine ordering + if i = -1 then + 0 + else + let pai = if i < pn then 0 else pa.[i-pn] + let qai = if i < qn then 0 else qa.[i-qn] + if pai = qai then check pa qa (i-1) + elif pai < qai then -1 + else 1 + + check p.digits q.digits (p.bound + pn - 1) + + let compare p q = + if p.bound < q.bound then -1 + elif p.bound > q.bound then 1 + else + let rec check (pa:ints) (qa:ints) i = + // HAVE: pa.[j] = qa.[j] for i < j < p.bound + // Looking for most significant differing coeffs to determine ordering + if i = -1 then 0 + elif pa.[i]=qa.[i] then check pa qa (i-1) + elif pa.[i]] + let lt p q = compare p q = -1 + [] + let gt p q = compare p q = 1 + [] + let lte p q = compare p q <> 1 + [] + let gte p q = compare p q <> -1 + + [] + let min a b = if lt a b then a else b + [] + let max a b = if lt a b then b else a + + +//---------------------------------------------------------------------------- +// scale +//-------------------------------------------------------------------------- + + // REQUIRE: baseN + baseN.2^32 < Int64.maxInt + let rec contributeArr (a:ints) i (c:int64) = + // Given c and require c < baseN.2^32 + // Compute: r <- r + c . B^i + // via r.digits.[i] <- r.digits.[i] + c and normalised + let x = int64 a.[i] + c + // HAVE: x < baseN + baseN.2^32 + let c = div64base x + let x = mod64base x + // HAVE: c < 1 + 2^32 < baseN.2^32, recursive call ok + // HAVE: x < baseN + a.[i] <- x; // store residue x + if c>0L then + contributeArr a (i+1) c // contribute carry next position + + let inline contribute r i c = contributeArr r.digits i c + + // REQUIRE: maxInt < 2^32 + [] + let rec scale (k:int) (p:n) = + // Given k and p and require k < 2^32 + // Computes "scalar" product k.p + // + let rbound = p.bound + boundInt + let r = createN rbound + let k = int64 k + for i = 0 to p.bound-1 do + let kpi = k * int64 p.digits.[i] + // HAVE: kpi < 2^32 * baseN which meets "contribute" requirement + contribute r i kpi + done; + normN r + + +//---------------------------------------------------------------------------- +// mulSchoolBook +//-------------------------------------------------------------------------- + + // multiplication: naively O(n^2) +(* + let mulSchoolBook' p q = + let rbound = p.bound + q.bound + boundBase*2 + let r = createN rbound + let pa = p.digits + let qa = q.digits + for i = 0 to p.bound-1 do + for j = 0 to q.bound-1 do + contribute r (i+j) (int64 pa.[i] * int64 qa.[j]) + done + done; + normN r +*) + + let mulSchoolBookBothSmall p q = + let r = createN 2 + let rak = int64 p * int64 q + setCoeff r 0 (mod64base rak); + setCoeff r 1 (int32 (div64base rak)) + normN r + + let rec mulSchoolBookCarry r c k = + if ( c > 0L ) then + // ToAdd = c.B^k + let rak = (coeff64 r k) + c + setCoeff r k (mod64base rak); + mulSchoolBookCarry r (div64base rak) (k + 1) + + let mulSchoolBookOneSmall p q = + let bp = bound(p) + let rbound = bp + 1 + let r = createN rbound + let q = int64 q + let mutable c = 0L + for i = 0 to bp-1 do + let rak = c + (coeff64 r i) + (coeff64 p i) * q + setCoeff r i (mod64base rak); + c <- div64base rak; + mulSchoolBookCarry r c bp + normN r + + + // multiplication: naively O(n^2) -- this version - unchecked - is faster + let mulSchoolBookNeitherSmall p q = + let rbound = p.bound + q.bound + let r = createN rbound + let ra = r.digits + let pa = p.digits + let qa = q.digits + // ToAdd p*q + for i = 0 to p.bound-1 do + // ToAdd p.[i] * q * B^i + let pai = int64 pa.[i] + let mutable c = 0L + let mutable k = i // k = i + j + // ToAdd = pi.qj.B^(i+j) for j = 0,j+1... + for j = 0 to q.bound-1 do + // ToAdd = c.B^k + pi.qj.B^(i+j) for j = j,j+1... and k = i+j + let qaj = int64 qa.[j] + let rak = int64 ra.[k] + c + pai * qaj + ra.[k] <- int32 (mod64base rak); + c <- div64base rak; + k <- k + 1; + mulSchoolBookCarry r c k + normN r + + let mulSchoolBook p q = + let pSmall = (bound(p) = 1) + let qSmall = (bound(q) = 1) + if (pSmall && qSmall) then mulSchoolBookBothSmall (coeff p 0) (coeff q 0) + elif pSmall then mulSchoolBookOneSmall q (coeff p 0) + elif qSmall then mulSchoolBookOneSmall p (coeff q 0) + else mulSchoolBookNeitherSmall p q + + +//---------------------------------------------------------------------------- +// quickMulUsingFft +//-------------------------------------------------------------------------- + + // The FFT polynomial multiplier requires the result coeffs fit inside Fp. + // + // OVERVIEW: + // The numbers are recoded as polynomials to be evaluated at (x=2^bigL). + // The polynomials are FFT multiplied, requiring result coeff to fit Fp. + // The result product is recovered by evaluating the poly at (x=2^bigL). + // + // REF: + // QuickMul: Practical FFT-base Integer Multiplication, + // Chee Yap and Chen Yi. + // + // There is choice of how to encode the nats polynomials. + // The choice is the (2^bigL) base to use. + // For bigL=1, the FFT will cater for a product of upto 256M bits. + // Larger bigL have less reach, but compute faster. + // So plan to choose bigL depending on the number of bits product. + // + // DETERMINING THE K,L BOUNDS. + // + // Given representing using K-vectors, K a power of 2, K=2^k, and + // If choosing inputs to have L-bit coefficients. + // + // The result coeff are: + // + // res(i) = sum (j] + type encoding = + { bigL : int; // bits per input coeff + twoToBigL : int; // 2^bigL + k : int; + bigK : int; // bigK = 2^k, number of terms polynomials + bigN : int; // bits result (under-estimate of limit) + split : int; // baseBits / bigL + splits : int array; + } + +#if CHECKED + let _ = check (baseBits=24) "24bit" +#endif + // Requiring baseN mod 2^bigL = 0 gave quick encoding, but... + // also a terrible drop performance when the bigK jumped by more than needed! + // Below, it choose a minimal bigK to hold the product. + + let mkEncoding (bigL,k,bigK,bigN) = +#if CHECKED + check (bigK = pow32 2 k) "bigK"; + check (bigN = bigK * bigL) "bigN"; + check (2 * bigL + k <= maxBitsInsideFp) "constraint"; +#endif + { bigL = bigL; + twoToBigL = pow32 2 bigL; + k = k; + bigK = bigK; + bigN = bigN; + split = baseBits/bigL; // should divide exactly + splits = Array.init (baseBits/bigL) (fun i -> pow32 2 (bigL*i)) + } + + let table = + [| // bigL , k , bigK , bigN // + mkEncoding ( 1 , 28 , 268435456 , 268435456 ) ; + mkEncoding ( 2 , 26 , 67108864 , 134217728 ) ; + mkEncoding ( 3 , 24 , 16777216 , 50331648 ) ; + mkEncoding ( 4 , 22 , 4194304 , 16777216 ) ; + mkEncoding ( 5 , 20 , 1048576 , 5242880 ) ; + mkEncoding ( 6 , 18 , 262144 , 1572864 ) ; + mkEncoding ( 7 , 16 , 65536 , 458752 ) ; + mkEncoding ( 8 , 14 , 16384 , 131072 ) ; + mkEncoding ( 9 , 12 , 4096 , 36864 ) ; + mkEncoding ( 10 , 10 , 1024 , 10240 ) ; + mkEncoding ( 11 , 8 , 256 , 2816 ) ; + mkEncoding ( 12 , 6 , 64 , 768 ) ; + mkEncoding ( 13 , 4 , 16 , 208 ) ; + |] + + let calculateTableTow bigL = + // Given L. + // Have L via "log2 K <= maxBitsInsideFp - 2L". + // Have N via "N = K.L" + // + let k = maxBitsInsideFp - 2*bigL + let bigK = pow64 2L k + let N = bigK * int64 bigL + bigL,k,bigK,N + + let encodingGivenResultBits bitsRes = + // choose maximum bigL s.t. bitsRes < bigN + // EXCEPTION: fails is bitsRes exceeds 2^28 (largest bigN table) + let rec selectFrom i = + if i+1 < table.Length && bitsRes < table.[i+1].bigN then + selectFrom (i+1) + else + table.[i] + + if bitsRes >= table.[0].bigN then + failwith "Product is huge, around 268435456 bits, beyond quickmul" + else + selectFrom 0 + + let bitmask = Array.init baseBits (fun i -> (pow32 2 i - 1)) + let twopowers = Array.init baseBits (fun i -> (pow32 2 i)) + let twopowersI64 = Array.init baseBits (fun i -> (pow64 2L i)) + // bitmask(k) = 2^k - 1 + // twopowers(k) = 2^k // + + let wordBits word = + let rec hi k = + if k=0 then 0 + elif (word &&& twopowers.[k-1]) <> 0 then k + else hi (k-1) + + hi baseBits + + let bits u = + if u.bound=0 then 0 + else degree u * baseBits + wordBits u.digits.[degree u] + + let extractBits n enc bi = + let bj = bi + enc.bigL - 1 // the last bit (inclusive) + let biw = bi / baseBits // first bit is this index pos + let bjw = bj / baseBits // last bit is this index pos + if biw <> bjw then + // two words + let x = index n biw + let y = index n bjw // bjw = biw+1 + let xbit = bi % baseBits // start bit x + let nxbits = baseBits - xbit // number of bitsin x + let x = x >>> xbit // shift down x so bit0 is first + let y = y <<< nxbits // shift up y so it starts where x finished + let x = x ||| y // combine them + let x = x &&& bitmask.[enc.bigL] // mask out (high y bits) to get required bits + x + else + // one word + let x = index n biw + let xbit = bi % baseBits // start bit x + let x = x >>> xbit + let x = x &&& bitmask.[enc.bigL] + x + + let encodePoly enc n = + // Find poly s.t. n = poly evaluated at x=2^bigL + // with 0 <= pi < 2^bigL. + // + let poly = Array.create enc.bigK (Fp.ofInt32 0) + let biMax = n.bound * baseBits + let rec encoder i bi = + // bi = i * bigL + if i=enc.bigK || bi > biMax then + () // done + else + ( let pi = extractBits n enc bi + poly.[i] <- Fp.ofInt32 pi; + let i = i + 1 + let bi = bi + enc.bigL + encoder i bi + ) + + encoder 0 0; + poly + + let decodeResultBits enc (poly : fp array) = + // Decoding evaluates poly(x) (coeff Fp) at X = 2^bigL. + // A bound on number of result bits is "enc.bigN + boundInt", but that takes HUGE STEPS. + // Garbage has a cost, so we minimize it by working out a tight bound. + // + // poly(X) = sum i=0..n coeff_i * X^i where n is highest non-zero coeff. + // <= 2^maxBitsInsideFp * (1 + X + ... X^n) + // <= 2^maxBitsInsideFp * (X^(n+1) - 1) / (X - 1) + // <= 2^maxBitsInsideFp * X^(n+1) / (X - 1) + // <= 2^maxBitsInsideFp * X^(n+1) / (X/2) provided X/2 <= X-1 + // <= 2^maxBitsInsideFp * X^n * 2 + // <= 2^maxBitsInsideFp * (2^bigL)^n * 2 + // <= 2^(maxBitsInsideFp + bigL.n + 1) + // + let mutable n = 0 + for i = 0 to poly.Length-1 do + if poly.[i] <> mzero then n <- i + done; + let rbits = maxBitsInsideFp + enc.bigL * n + 1 + rbits + 1 // +1 since 2^1 requires 2 bits not 1 + + // REQUIRE: bigL <= baseBits + let decodePoly enc poly = + // Find n = poly evaluated at x=2^bigL + // Note, 0 <= pi < maxFp. + // + let rbound = (decodeResultBits enc poly) / baseBits + 1 + let r = createN rbound + let rec evaluate i j d = + // HAVE: bigL.i = j * baseBits + d and d= rbound then +#if CHECKED + check (poly.[i] = mzero) "decodePoly"; +#endif + () + else ( + let x = int64 (Fp.toInt poly.[i]) * twopowersI64.[d] + // HAVE: x < 2^32 . 2^baseBits = 2^32.baseN + contribute r j x + ); + let i = i + 1 + let d = d + enc.bigL + let j,d = if d >= baseBits then j+1 , d-baseBits else j,d + // HAVE: d < baseBits, note: bigL minDigitsKaratsuba then + let k = bmax / 2 + let a0 = restrictTo k p + let a1 = shiftDown k p + let b0 = restrictTo k q + let b1 = shiftDown k q + let q0 = mul a0 b0 + let q1 = mul (add a0 a1) (add b0 b1) + let q2 = mul a1 b1 + let p0 = q0 + let p1 = sub q1 (add q0 q2) + let p2 = q2 + let r = add p0 (shiftUp k (add p1 (shiftUp k p2))) + r + else + mulSchoolBook p q + + let rec mulKaratsuba x y = recMulKaratsuba mulKaratsuba x y + + +//---------------------------------------------------------------------------- +// mul - composite +//-------------------------------------------------------------------------- + + let productDigitsUpperSchoolBook = (64000 / baseBits) + // When is it worth switching away from SchoolBook? + // SchoolBook overhead is low, so although it's O(n^2) it remains competitive. + // + // 28/3/2006: + // The FFT can take over from SchoolBook at around 64000 bits. + // Note, FFT performance is stepwise, according to enc from table. + // The steps are big steps (meaning sudden jumps/drops perf). + // + + let singleDigitForceSchoolBook = (32000 / baseBits) + // If either argument is "small" then stay with SchoolBook. + // + + let productDigitsUpperFft = (table.[0].bigN / baseBits) + // QuickMul is good upto a finite (but huge) limit: + // Limit 268,435,456 bits product. + // + // From the code: + // let bitsRes = bits u + bits v + // fails when bitsRes >= table.[0].bigN + // So, not applicable when: + // P1: table.[0].bigN <= bits(u) + bits(v) + // P2: table.[0].bigN <= .. <= baseBits * (u.bound + v.bound) + // P3: table.[0].bigN <= .. <= baseBits * (u.bound + v.bound) + // P4: table.[0].bigN / baseBits <= u.bound + v.bound + // + + // Summary of mul algorithm choice: + // 0 <= uv_bound < upper_school_book - Schoolbook + // upper_school_book <= uv_bound < upper_fft - QuickMul + // upper_fft <= uv_bound < ... - Karatsuba + // + // NOTE: + // - Karatsuba current implementation has high garbage cost. + // - However, a linear space cost is possible... + // - Meantime, switch to Karatsuba only beyond FFT range. + // + + let rec mul p q = + let pqBound = p.bound + q.bound + if pqBound < productDigitsUpperSchoolBook || + p.bound < singleDigitForceSchoolBook || + q.bound < singleDigitForceSchoolBook + then + // Within school-book initial range: + mulSchoolBook p q + else + if pqBound < productDigitsUpperFft then + // Inside QuickMul FFT range: + quickMulUsingFft p q + else + // Beyond QuickMul FFT range, or maybe between Schoolbook and QuickMul (no!): + // Use karatsuba method, with "mul" as recursive multiplier, + // so will reduce sizes of products on recursive calls, + // and QuickMul will take over if they fall within it's range. + // + recMulKaratsuba mul p q + + +//---------------------------------------------------------------------------- +// division - scaleSubInPlace +//-------------------------------------------------------------------------- + + let scaleSubInPlace x f a n = + // Have x = sumR 0 xd (\i.xi.B^i) where xd = degree x + // a = sumR 0 ad (\i.digitsi.B^i) where ad = degree a + // f < B + // n < xd + // Assumes "f.digits.B^n < x". + // Required to remove f.digits.B^n from x place. + //------ + // Result = x_initial - f.digits.B^n + // = x_initial - f.[sumR 0 ad (\i.digitsi.B^(i+n))] + // State: j = 0 + // z = f * a0 + // Invariant(x,z,j,n): + // P1: x_result = x - z.B^(j+n) - f.[sumR (j+1) ad (\i.digitsi.B^i+n)] + // P2: z < B^2 - 1, and so has form z = zHi.B + zLo for zHi,zLo < B. + // Base: Invariant holds initially. + // Step: (a) Remove zLo from x: + // If zLo <= x_(j+n) then zLo <- 0 + // x_(j+n) <- x_(j+n) - zLo + // else zLo <- 0 + // x_(j+n) <- x_(j+n) + (B - zLo) + // = x_(j+n) - zLo + B + // zHi <- zHi + 1 + // Here, invariant P1 still holds, P2 may break. + // (b) Advance j: + // Have z = zHi.B since zLo = 0. + // j <- j + 1 + // z <- zHi + f * a_(j+1) + // P2 holds: + // Have z <= B + (B-1)*(B-1) = B + B^2 - 2B + 1 = B^2 - B + 1 + // Have z <= B^2 - 1 when B >= 2 which is required for B being a base. + // P1 holds, + // moved f.digits_(j+1).B^(j+1+n) factor over. + // + // Once j+1 exceeds ad, summation is zero and it contributes no more terms (b). + // Continue until z = 0, which happens since z decreases towards 0. + // Done. + // + let invariant (_,_,_) = () + #if CHECKED + let x_initial = copyN x + let x_result = sub x_initial (shiftUp n (scale f a)) + let invariant (z,j,n) = + let P1 = + equal + x_result + (sub x (add (shiftUp (j+n) (embed64 z)) + (mul (embed f) + (shiftUp (j+1+n) (shiftDown (j+1) a))))) + let P2 = z < baseNi64 * baseNi64 - 1L + check P1 "P1"; + check P2 "P2" + + #endif + let xres = x + let x,xd = x.digits,degree x + let a,ad = a.digits,degree a + let f = int64 f + let mutable j = 0 + let mutable z = f * int64 a.[0] + while( z > 0L || j < ad ) do + if j > xd then failwith "scaleSubInPlace: pre-condition did not apply, result would be -ve"; + invariant(z,j,n); // P1,P2 hold + let mutable zLo = mod64base z |> int32 + let mutable zHi = div64base z + if zLo <= x.[j+n] then + x.[j+n] <- x.[j+n] - zLo + else ( + x.[j+n] <- x.[j+n] + (baseN - zLo); + zHi <- zHi + 1L + ); + // P1 holds + if j < ad then + z <- zHi + f * int64 a.[j+1] + else + z <- zHi; + j <- j + 1; + // P1,P2 hold + done; + ignore (normN xres) + + // + let scaleSub x f a n = + let freshx = add x zero + scaleSubInPlace freshx f a n; + normN freshx +(* + + let scaleSub2 x f a n = sub x (shiftUp n (mul (embed f) a)) + + let x = (mul (embed 234234234) (pow (embed 10) (embed 20))) + let f = 2 + let a = (embed 1231231231) + let n = 2 + let res = scaleSub x f a n + let res2 = scaleSub2 x f a n + + let x, xd, f, a, ad, n = freshx.digits, freshx.bound, f, a.digits, a.bound, n + *) + + +//---------------------------------------------------------------------------- +// division - scaleAddInPlace +//-------------------------------------------------------------------------- + + let scaleAddInPlace x f a n = + // Have x = sumR 0 xd (\i.xi.B^i) + // a = sumR 0 ad (\i.digitsi.B^i) + // f < B + // n < xd + // Required to add f.digits.B^n to x place. + // Assumes result will fit with x (0...xd). + //------ + // Result = x_initial + f.digits.B^n + // = x_initial + f.[sumR 0 ad (\i.digitsi.B^i+n)] + // State: j = 0 + // z = f * a0 + // Invariant(x,z,j,n): + // P1: x_result = x + z.B^(j+n) + f.[sumR (j+1) ad (\i.digitsi.B^i+n)] + // P2: z < B^2 - 1, and so has form z = zHi.B + zLo for zHi,zLo < B. + // Base: Invariant holds initially. + // Step: (a) Add zLo to x: + // If zLo < B - x_(j+n) then zLo <- 0 + // x_(j+n) <- x_(j+n) + zLo + // else zLo <- 0 + // x_(j+n) <- zLo - (B - x_(j+n)) + // = x_(j+n) + zLo - B + // zHi <- zHi + 1 + // Here, invariant P1 still holds, P2 may break. + // (b) Advance j: + // Have z = zHi.B since zLo = 0. + // j <- j + 1 + // z <- zHi + f * a_(j+1) + // P2 holds: + // Have z <= B + (B-1)*(B-1) = B + B^2 - 2B + 1 = B^2 - B + 1 + // Have z <= B^2 - 1 when B >= 2 which is required for B being a base. + // P1 holds, + // moved f.digits_(j+1).B^(j+1+n) factor over. + // + // Once j+1 exceeds ad, summation is zero and it contributes no more terms (b). + // Continue until z = 0, which happens since z decreases towards 0. + // Done. + // + let invariant (_,_,_) = () +#if CHECKED + let x_initial = copyN x + let x_result = add x_initial (shiftUp n (scale f a)) + let invariant (z,j,n) = + let P1 = + equal + x_result + (add x (add (shiftUp (j+n) (embed64 z)) + (mul (embed f) + (shiftUp (j+1+n) (shiftDown (j+1) a))))) + let P2 = z < baseNi64 * baseNi64 - 1L + check P1 "P1"; + check P2 "P2" + +#endif + let xres = x + let x,xd = x.digits,degree x + let a,ad = a.digits,degree a + let f = int64 f + let mutable j = 0 + let mutable z = f * int64 a.[0] + while( z > 0L || j < ad ) do + if j > xd then failwith "scaleSubInPlace: pre-condition did not apply, result would be -ve"; + invariant(z,j,n); // P1,P2 hold + let mutable zLo = mod64base z |> int32 + let mutable zHi = div64base z + if zLo < baseN - x.[j+n] then + x.[j+n] <- x.[j+n] + zLo + else ( + x.[j+n] <- zLo - (baseN - x.[j+n]); + zHi <- zHi + 1L + ); + // P1 holds + if j < ad then + z <- zHi + f * int64 a.[j+1] + else + z <- zHi; + j <- j + 1; + // P1,P2 hold + done; + ignore (normN xres) + + // + let scaleAdd x f a n = + let freshx = add x zero + scaleAddInPlace freshx f a n; + normN freshx + +(* + let scaleAdd2 x f a n = add x (shiftUp n (mul (embed f) a)) + + let x = (mul (embed 234234234) (pow (embed 10) (embed 20))) + let f = 2 + let a = (embed 1231231231) + let n = 2 + let res = scaleAdd x f a n + let res2 = scaleAdd2 x f a n + + let x, xd, f, a, ad, n = freshx.digits, freshx.bound, f, a.digits, a.bound, n +*) + +//---------------------------------------------------------------------------- +// division - removeFactor +//-------------------------------------------------------------------------- + + (* + let removeFactorReference x a n = + let ff = div x (shiftUp n a) + toInt ff + *) + + let removeFactor x a n = + // Assumes x < a.B^(n+1) + // Choose f s.t. + // (a) f.digits.B^n <= x + // (b) f=0 iff x < a.B^n + // + let dega,degx = degree a,degree x + if degx < dega + n then 0 else // possible with "normalisation" + let aa,xa = a.digits,x.digits + let f = + if dega = 0 then // a = a0 + if degx = n then + xa.[n] / aa.[0] + else ( +#if CHECKED + check (degx = n+1) "removeFactor degx#1"; +#endif + let f64 = (int64 xa.[degx] * baseNi64 + int64 xa.[degx-1]) / int64 aa.[0] + int32 f64 + ) + else // a = sumR 0 dega (\i.digitsi.B^i) + if degx = dega + n then + xa.[degx] / (aa.[dega] + 1) // +1 to bound above a + else ( +#if CHECKED + check (degx = dega+n+1) "removeFactor degx#2"; +#endif + let f64 = (int64 xa.[degx] * baseNi64 + int64 xa.[degx-1]) + / (int64 aa.[dega] + 1L) // +1 to bound above a + int32 f64 + ) + + if f = 0 then + let lte = (shiftCompare a n x 0) <> 1 + if lte then 1 else 0 + else + f + + +//---------------------------------------------------------------------------- +// division - divmod +//-------------------------------------------------------------------------- + + let divmod b a = + // Returns d,r where b = d.digits + r and r0 then + scaleSubInPlace x f a n; + scaleAddInPlace d f one n; + Invariant(d,x,n,p) + else + finished <- f=0 && n=0; + if not finished then + if p = m+n then + Invariant(d,x,n-1,p); + n <- n-1 + else + Invariant(d,x,n-1,p-1); + n <- n-1; + p <- p-1 + // Have: "b = d.digits + x" return d,x + normN d,normN x + + //---------------------------------------------------------------------------- + // div, mod + //-------------------------------------------------------------------------- + + [] + let div b a = fst (divmod b a) + [] + let rem b a = snd (divmod b a) + // rem b a, for small a can do (base mod a) trick - O(N) + + + //---------------------------------------------------------------------------- + // hcf + //-------------------------------------------------------------------------- + + let hcf a b = + // Have: 0 <= a,b since naturals + let rec hcfloop a b = // Require: 0 <= a <= b + if equal zero a then b + else + // Have: 0 < a <= b + let _,r = divmod b a + // Have: r < a from divmod + hcfloop r a // Have: 0 <= r < a + + if lt a b then hcfloop a b // Have: 0 <= a < b + else hcfloop b a // Have: 0 <= b <= a + + //---------------------------------------------------------------------------- + // pow + //-------------------------------------------------------------------------- + + let two = embed 2 + let powi x n = + let rec power acc x n = + if n=0 then acc + elif n % 2=0 then power acc (mul x x) (n / 2) + else power (mul x acc) (mul x x) (n / 2) + + power one x n + + let pow x n = + let rec power acc x n = + if isZero n then acc + else + let ndiv2,nmod2 = divmod n two // use: intdivmod when available + if isZero nmod2 then power acc (mul x x) ndiv2 + else power (mul x acc) (mul x x) ndiv2 + + power one x n + +//---------------------------------------------------------------------------- +// float n +//-------------------------------------------------------------------------- + + let toFloat n = + let basef = float baseN + let rec evalFloat acc k i = + if i = n.bound then + acc + else + evalFloat (acc + k * float n.digits.[i]) (k * basef) (i+1) + evalFloat 0.0 1.0 0 + +//---------------------------------------------------------------------------- +// n <-> int +//-------------------------------------------------------------------------- + + let ofInt32 n = embed n + let ofInt64 n = embed64 n + + /// Convert BigNat to uint32 otherwise OverflowException. + let toUInt32 n = + match n.bound with + | 0 -> 0u + | 1 -> n.digits.[0] |> uint32 + | 2 -> let xA,xB = n.digits.[0],n.digits.[1] + if xB > baseMask32B then raise (System.OverflowException()) + ( uint32 (xA &&& baseMask32A)) + + ((uint32 (xB &&& baseMask32B)) <<< baseShift32B) + | _ -> raise (System.OverflowException()) + + /// Convert BigNat to uint64 otherwise OverflowException. + let toUInt64 n = + match n.bound with + | 0 -> 0UL + | 1 -> n.digits.[0] |> uint64 + | 2 -> let xA,xB = n.digits.[0],n.digits.[1] + ( uint64 (xA &&& baseMask64A)) + + ((uint64 (xB &&& baseMask64B)) <<< baseShift64B) + | 3 -> let xA,xB,xC = n.digits.[0],n.digits.[1],n.digits.[2] + if xC > baseMask64C then raise (System.OverflowException()) + ( uint64 (xA &&& baseMask64A)) + + ((uint64 (xB &&& baseMask64B)) <<< baseShift64B) + + ((uint64 (xC &&& baseMask64C)) <<< baseShift64C) + | _ -> raise (System.OverflowException()) + + +//---------------------------------------------------------------------------- +// n -> string +//-------------------------------------------------------------------------- + + +#if CHECKED + let checks = false +#endif + let toString n = + // Much better complexity than naive_string_of_z. + // It still does "nDigit" calls to (int)divmod, + // but the degree on which it is called halves (not decrements) each time. + // + let degn = degree n + let rec route prior k ten2k = + if degree ten2k > degn + then (k,ten2k)::prior + else route ((k,ten2k)::prior) (k+1) (mul ten2k ten2k) + let kten2ks = route [] 0 (embed 10) + let rec collect isLeading digits n = function + | [] -> + // Have 0 <= n < 10^1, so collect a single digit (if needed) + let n = eval n +#if CHECKED + if checks then check (0 <= n) "toString: digit0"; + if checks then check (n <= 9) "toString: digit9"; +#endif + if isLeading && n=0 then digits // suppress leading 0 + else string n :: digits + | (_,ten2k)::prior -> +#if CHECKED + if checks then check (lt n (mul ten2k ten2k)) "string_of_int: bound n"; +#endif + // Have 0 <= n < (ten2k)^2 and ten2k = 10^(2^k) + let nH,nL = divmod n ten2k +#if CHECKED + if checks then check (lt nH ten2k) "string_of_int: bound nH"; + if checks then check (lt nL ten2k) "string_of_int: bound nL"; +#endif + // Have 0 <= nH,nL < (ten2k) and ten2k = 10^(2^k) + if isLeading && isZero nH then + // suppress leading 0s + let digits = collect isLeading digits nL prior + digits + else + let digits = collect false digits nL prior + let digits = collect isLeading digits nH prior + digits + + let prior = kten2ks + let digits = collect true [] n prior + match digits with + | [] -> "0" + | _ -> digits |> Array.ofList |> System.String.Concat + +//---------------------------------------------------------------------------- +// n <- string +//-------------------------------------------------------------------------- + + let ofString (str:string) = + // Would it be better to split string half and combine results? + let len = str.Length + if System.String.IsNullOrEmpty str then invalidArg "str" "empty string"; + let ten = embed 10 + let rec build acc i = + if i=len then + acc + else + let c = str.[i] + let d = int c - int '0' + if 0 <= d && d <= 9 then + build (add (mul ten acc) (embed d)) (i+1) + else + raise (new System.FormatException("The value could not be parsed")) + + build (embed 0) 0 + + let isSmall n = (n.bound <= 1) + let getSmall n = index n 0 + + //---------------------------------------------------------------------------- + // factorial + //-------------------------------------------------------------------------- + + let factorial n = + //***** + // Factorial(n) = 1.2.3.....(n-1).n + // + // Factorial is sometimes used as a test for multiplication. + // The QuickMul FFT multiplier takes over only when both operands reach a given size. + // How to compute factorial? + // + // (a) Factorial(n) = factorial(n-1).n + // This is unlikely to make use of the FFT (n never large enough). + // (b) Factorial(n) = (1.2.3.4....k) . (k.[k+1]...(n-1).n) + // Applied recursively QuickMul FFT will take over on large products. + // + //**** + let rec productR a b = + if equal a b then a + else + let m = div (add a b) (ofInt32 2) + mul (productR a m) (productR (add m one) b) + + productR one n + + +#endif diff --git a/src/FSharpPortable/n.fsi b/src/FSharpPortable/n.fsi new file mode 100644 index 00000000..bf01cbba --- /dev/null +++ b/src/FSharpPortable/n.fsi @@ -0,0 +1,71 @@ +// First version copied from the F# compiler sources +// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/n.fsi + +//---------------------------------------------------------------------------- +// Copyright (c) 2002-2012 Microsoft Corporation. +// +// This source code is subject to terms and conditions of the Apache License, Version 2.0. A +// copy of the license can be found in the License.html file at the root of this distribution. +// By using this source code in any fashion, you are agreeing to be bound +// by the terms of the Apache License, Version 2.0. +// +// You must not remove this notice, or any other, from this software. +//---------------------------------------------------------------------------- + +namespace Microsoft.FSharp.Math + +#if FX_NO_BIGINT +open Microsoft.FSharp.Collections +open Microsoft.FSharp.Core + +/// Abstract internal type +[] +type internal BigNat + +module internal BigNatModule = + + val zero : BigNat + val one : BigNat + val two : BigNat + + val add : BigNat -> BigNat -> BigNat + val sub : BigNat -> BigNat -> BigNat + val mul : BigNat -> BigNat -> BigNat + val divmod : BigNat -> BigNat -> BigNat * BigNat + val div : BigNat -> BigNat -> BigNat + val rem : BigNat -> BigNat -> BigNat + val hcf : BigNat -> BigNat -> BigNat + + val min : BigNat -> BigNat -> BigNat + val max : BigNat -> BigNat -> BigNat + val scale : int -> BigNat -> BigNat + val powi : BigNat -> int -> BigNat + val pow : BigNat -> BigNat -> BigNat + + val IsZero : BigNat -> bool + val isZero : BigNat -> bool + val isOne : BigNat -> bool + val equal : BigNat -> BigNat -> bool + val compare : BigNat -> BigNat -> int + val lt : BigNat -> BigNat -> bool + val gt : BigNat -> BigNat -> bool + val lte : BigNat -> BigNat -> bool + val gte : BigNat -> BigNat -> bool + + val hash : BigNat -> int + val toFloat : BigNat -> float + val ofInt32 : int -> BigNat + val ofInt64 : int64 -> BigNat + val toString : BigNat -> string + val ofString : string -> BigNat + + val toUInt32 : BigNat -> uint32 + val toUInt64 : BigNat -> uint64 + + val factorial : BigNat -> BigNat + // val randomBits : int -> BigNat + val bits : BigNat -> int + val isSmall : BigNat -> bool (* will fit in int32 (but not nec all int32) *) + val getSmall : BigNat -> int32 (* get the value, if it satisfies isSmall *) + +#endif diff --git a/src/FSharpPortable/z.fs b/src/FSharpPortable/z.fs new file mode 100644 index 00000000..b2d56594 --- /dev/null +++ b/src/FSharpPortable/z.fs @@ -0,0 +1,411 @@ +// First version copied from the F# compiler sources +// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/z.fs + +//---------------------------------------------------------------------------- +// Copyright (c) 2002-2012 Microsoft Corporation. +// +// This source code is subject to terms and conditions of the Apache License, Version 2.0. A +// copy of the license can be found in the License.html file at the root of this distribution. +// By using this source code in any fashion, you are agreeing to be bound +// by the terms of the Apache License, Version 2.0. +// +// You must not remove this notice, or any other, from this software. +//---------------------------------------------------------------------------- + +#nowarn "44" // This construct is deprecated. This function is for use by compiled F# code and should not be used directly +namespace System.Numerics + +#if FX_NO_BIGINT + open Microsoft.FSharp.Collections + open Microsoft.FSharp.Core + open Microsoft.FSharp.Core.Operators + open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators + open Microsoft.FSharp.Primitives.Basics + open Microsoft.FSharp.Math + open System + open System.Globalization + + + // INVARIANT: signInt = 1 or -1 + // value(z) = signInt * v + // NOTE: 0 has two repns (+1,0) or (-1,0). + [] + [] + [] + type BigInteger(signInt:int, v : BigNat) = + + static let smallLim = 4096 + static let smallPosTab = Array.init smallLim BigNatModule.ofInt32 + static let one = BigInteger(1) + static let zero = BigInteger(0) + + static member internal nat n = + if BigNatModule.isSmall n && BigNatModule.getSmall n < smallLim + then smallPosTab.[BigNatModule.getSmall n] + else n + static member internal create (s,n) = BigInteger(s,BigInteger.nat n) + static member internal posn n = BigInteger(1,BigInteger.nat n) + static member internal negn n = BigInteger(-1,BigInteger.nat n) + + + member x.Sign = if x.IsZero then 0 else signInt + member x.SignInt = signInt + member internal x.V = v + + static member op_Equality (x:BigInteger, y:BigInteger) = + //System.Console.WriteLine("x = {0}",box x) + //System.Console.WriteLine("y = {0}",box y) + match x.SignInt,y.SignInt with + | 1, 1 -> BigNatModule.equal x.V y.V // +1.xv = +1.yv iff xv = yv + | -1, -1 -> BigNatModule.equal x.V y.V // -1.xv = -1.yv iff xv = yv + | 1,-1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V // 1.xv = -1.yv iff xv=0 and yv=0 + | -1, 1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V // -1.xv = 1.yv iff xv=0 and yv=0 + | _ -> invalidArg "x" "signs should be +/- 1" + + static member op_Inequality (x:BigInteger, y:BigInteger) = not (BigInteger.op_Equality(x,y)) // CA2226: OperatorsShouldHaveSymmetricalOverloads + + static member op_LessThan (x:BigInteger, y:BigInteger) = + match x.SignInt,y.SignInt with + | 1, 1 -> BigNatModule.lt x.V y.V // 1.xv < 1.yv iff xv < yv + | -1,-1 -> BigNatModule.lt y.V x.V // -1.xv < -1.yv iff yv < xv + | 1,-1 -> false // 1.xv < -1.yv iff 0 <= 1.xv < -1.yv <= 0 iff false + | -1, 1 -> not (BigNatModule.isZero x.V) || not (BigNatModule.isZero y.V) + // -1.xv < 1.yv + // (a) xv=0 and yv=0, then false + // (b) xv<>0, -1.xv < 0 <= 1.yv, so true + // (c) yv<>0, -1.xv <= 0 < 1.yv, so true + | _ -> invalidArg "x" "signs should be +/- 1" + + static member op_GreaterThan (x:BigInteger, y:BigInteger) = // Follow lt by +/- symmetry + match x.SignInt,y.SignInt with + | 1, 1 -> BigNatModule.gt x.V y.V + | -1,-1 -> BigNatModule.gt y.V x.V + | 1,-1 -> not (BigNatModule.isZero x.V) || not (BigNatModule.isZero y.V) + | -1, 1 -> false + | _ -> invalidArg "x" "signs should be +/- 1" + + static member internal compare(n,nn) = if BigInteger.op_LessThan(n,nn) then -1 elif BigInteger.op_Equality(n,nn) then 0 else 1 + static member internal hash (z:BigInteger) = z.SignInt + BigNatModule.hash(z.V) + + override x.ToString() = + match x.SignInt with + | 1 -> BigNatModule.toString x.V // positive + | -1 -> + if BigNatModule.isZero x.V + then "0" // not negative infact, but zero. + else "-" + BigNatModule.toString x.V // negative + | _ -> invalidOp "signs should be +/- 1" + + member x.StructuredDisplayString = x.ToString() + + interface System.IComparable with + member this.CompareTo(obj:obj) = + match obj with + | :? BigInteger as that -> BigInteger.compare(this,that) + | _ -> invalidArg "obj" "the objects are not comparable" + + override this.Equals(obj) = + match obj with + | :? BigInteger as that -> BigInteger.op_Equality(this, that) + | _ -> false + + override x.GetHashCode() = BigInteger.hash(x) + + + new (n:int) = + if n>=0 + then BigInteger (1,BigInteger.nat(BigNatModule.ofInt32 n)) + elif (n = System.Int32.MinValue) + then BigInteger(-1,BigInteger.nat(BigNatModule.ofInt64 (-(int64 n)))) + else BigInteger(-1,BigInteger.nat(BigNatModule.ofInt32 (-n))) + + + new (n:int64) = + if n>=0L + then BigInteger(1,BigInteger.nat (BigNatModule.ofInt64 n)) + elif (n = System.Int64.MinValue) + then BigInteger(-1,BigInteger.nat (BigNatModule.add (BigNatModule.ofInt64 System.Int64.MaxValue) BigNatModule.one) ) + else BigInteger(-1,BigInteger.nat (BigNatModule.ofInt64 (-n))) + + static member One = one + static member Zero = zero + static member (~-) (z:BigInteger) = BigInteger.create(-1 * z.SignInt,z.V) + static member Scale(k,z:BigInteger) = + if k<0 + then BigInteger.create(-z.SignInt, (BigNatModule.scale (-k) z.V)) // k.zsign.zv = -zsign.(-k.zv) + else BigInteger.create(z.SignInt, (BigNatModule.scale k z.V)) // k.zsign.zv = zsign.k.zv + + // Result: 1.nx - 1.ny (integer subtraction) + static member internal subnn (nx,ny) = + if BigNatModule.gte nx ny + then BigInteger.posn (BigNatModule.sub nx ny) // nx >= ny, result +ve, +1.(nx - ny) + else BigInteger.negn (BigNatModule.sub ny nx) // nx < ny, result -ve, -1.(ny - nx) + + static member internal addnn (nx,ny) = + BigInteger.posn (BigNatModule.add nx ny) // Compute "nx + ny" to be integer + + member x.IsZero = BigNatModule.isZero x.V // signx.xv = 0 iff xv=0, since signx is +1,-1 + member x.IsOne = (x.SignInt = 1) && BigNatModule.isOne x.V // signx.xv = 1 iff signx = +1 and xv = 1 + static member (+) (x:BigInteger,y:BigInteger) = + if y.IsZero then x else + if x.IsZero then y else + match x.SignInt,y.SignInt with + | 1, 1 -> BigInteger.addnn(x.V,y.V) // 1.xv + 1.yv = (xv + yv) + | -1,-1 -> -(BigInteger.addnn(x.V,y.V)) // -1.xv + -1.yv = -(xv + yv) + | 1,-1 -> BigInteger.subnn (x.V,y.V) // 1.xv + -1.yv = (xv - yv) + | -1, 1 -> BigInteger.subnn(y.V,x.V) // -1.xv + 1.yv = (yv - xv) + | _ -> invalidArg "x" "signs should be +/- 1" + + static member (-) (x:BigInteger,y:BigInteger) = + if y.IsZero then x else + match x.SignInt,y.SignInt with + | 1, 1 -> BigInteger.subnn(x.V,y.V) // 1.xv - 1.yv = (xv - yv) + | -1,-1 -> BigInteger.subnn(y.V,x.V) // -1.xv - -1.yv = (yv - xv) + | 1,-1 -> BigInteger.addnn(x.V,y.V) // 1.xv - -1.yv = (xv + yv) + | -1, 1 -> -(BigInteger.addnn(x.V,y.V)) // -1.xv - 1.yv = -(xv + yv) + | _ -> invalidArg "x" "signs should be +/- 1" + + static member ( * ) (x:BigInteger,y:BigInteger) = + if x.IsZero then x + elif y.IsZero then y + elif x.IsOne then y + elif y.IsOne then x + else + let m = (BigNatModule.mul x.V y.V) + BigInteger.create (x.SignInt * y.SignInt,m) // xsign.xv * ysign.yv = (xsign.ysign).(xv.yv) + + static member DivRem (x:BigInteger,y:BigInteger,rem:BigInteger byref) = + let d,r = BigNatModule.divmod x.V y.V + // HAVE: |x| = d.|y| + r and 0 <= r < |y| + // HAVE: xv = d.yv + r and 0 <= r < yv + match x.SignInt,y.SignInt with + | 1, 1 -> rem <- BigInteger.posn r ; BigInteger.posn d // 1.xv = 1.d.( 1.yv) + ( 1.r) + | -1,-1 -> rem <- BigInteger.negn r ; BigInteger.posn d // -1.xv = 1.d.(-1.yv) + (-1.r) + | 1,-1 -> rem <- BigInteger.posn r ; BigInteger.negn d // 1.xv = -1.d.(-1.yv) + ( 1.r) + | -1, 1 -> rem <- BigInteger.negn r ; BigInteger.negn d // -1.xv = -1.d.( 1.yv) + (-1.r) + | _ -> invalidArg "x" "signs should be +/- 1" + + static member (/) (x:BigInteger,y:BigInteger) = + let mutable rem = new BigInteger(0) + BigInteger.DivRem(x,y,&rem) + static member (%) (x:BigInteger,y:BigInteger) = + let mutable rem = new BigInteger(0) + BigInteger.DivRem(x,y,&rem) |> ignore ; rem + static member GreatestCommonDivisor (x:BigInteger,y:BigInteger) = BigInteger.posn (BigNatModule.hcf x.V y.V) // hcf (xsign.xv,ysign.yv) = hcf (xv,yv) + + member x.IsNegative = x.SignInt = -1 && not (x.IsZero) // signx.xv < 0 iff signx = -1 and xv<>0 + member x.IsPositive = x.SignInt = 1 && not (x.IsZero) // signx.xv > 0 iff signx = +1 and xv<>0 + static member Abs (x:BigInteger) = if x.SignInt = -1 then -x else x + + static member op_LessThanOrEqual (x:BigInteger,y:BigInteger) = + match x.SignInt,y.SignInt with + | 1, 1 -> BigNatModule.lte x.V y.V // 1.xv <= 1.yv iff xv <= yv + | -1,-1 -> BigNatModule.lte y.V x.V // -1.xv <= -1.yv iff yv <= xv + | 1,-1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V // 1.xv <= -1.yv, + // (a) if xv=0 and yv=0 then true + // (b) otherwise false, only meet at zero. + + | -1, 1 -> true // -1.xv <= 1.yv, true + | _ -> invalidArg "x" "signs should be +/- 1" + + static member op_GreaterThanOrEqual (x:BigInteger,y:BigInteger) = // Follow lte by +/- symmetry + match x.SignInt,y.SignInt with + | 1, 1 -> BigNatModule.gte x.V y.V + | -1,-1 -> BigNatModule.gte y.V x.V + | 1,-1 -> true + | -1, 1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V + | _ -> invalidArg "x" "signs should be +/- 1" + + + static member Pow (x:BigInteger,y:int32) = + if y < 0 then invalidArg "y" "The input must be non-negative." + let yval = BigInteger(y) + BigInteger.create ((if BigNatModule.isZero (BigNatModule.rem yval.V BigNatModule.two) then 1 else x.SignInt), BigNatModule.pow x.V yval.V) + + static member op_Explicit (x:BigInteger) = + let u = BigNatModule.toUInt32 x.V + if u <= uint32 System.Int32.MaxValue then + // Handle range [-MaxValue,MaxValue] + x.SignInt * int32 u + elif x.SignInt = -1 && u = uint32 (System.Int32.MaxValue + 1) then + //assert(System.Int32.MinValue = 0 - System.Int32.MaxValue - 1) + // Handle MinValue = -(MaxValue+1) special case not covered by the above + System.Int32.MinValue + else + raise (System.OverflowException()) + + static member op_Explicit (x:BigInteger) = + let u = BigNatModule.toUInt64 x.V + if u <= uint64 System.Int64.MaxValue then + (* Handle range [-MaxValue,MaxValue] *) + int64 x.SignInt * int64 u + elif x.SignInt = -1 && u = uint64 (System.Int64.MaxValue + 1L) then + //assert(System.Int64.MinValue = 0 - System.Int64.MaxValue - 1L) + (* Handle MinValue = -(MaxValue+1) special case not covered by the above *) + System.Int64.MinValue + else + raise (System.OverflowException()) + + static member op_Explicit (x:BigInteger) = + match x.SignInt with + | 1 -> BigNatModule.toFloat x.V // float (1.xv) = float (xv) + | -1 -> - (BigNatModule.toFloat x.V) // float (-1.xv) = - float (xv) + | _ -> invalidArg "x" "signs should be +/- 1" + + static member Parse(text:string) = + let len = text.Length + if len = 0 then raise (new System.FormatException("The value could not be parsed")) + if text.[0..0] = "-" then + BigInteger.negn (BigNatModule.ofString text.[1..len-1]) + else + BigInteger.posn (BigNatModule.ofString text) + + member internal x.IsSmall = BigNatModule.isSmall (x.V) + static member Factorial (x:BigInteger) = + if x.IsNegative then invalidArg "x" "The input must be non-negative." + if x.IsPositive then BigInteger.posn (BigNatModule.factorial x.V) + else BigInteger.One + + static member ( ~+ )(n1:BigInteger) = n1 + + static member FromInt64(x:int64) = new BigInteger(x) + static member FromInt32(x:int32) = new BigInteger(x) +#endif + +namespace Microsoft.FSharp.Core + + + type bigint = System.Numerics.BigInteger + + open System + open System.Diagnostics.CodeAnalysis + open System.Globalization + open Microsoft.FSharp.Core.Operators + open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators + open System.Numerics + +#if FX_NO_BIGINT + // FxCop suppressions + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + [] + do() +#endif + + [] + module NumericLiterals = + + module NumericLiteralI = + + let tab64 = new System.Collections.Generic.Dictionary() + let tabParse = new System.Collections.Generic.Dictionary() + + let FromInt64Dynamic (x64:int64) : obj = + lock tab64 (fun () -> + let mutable res = Unchecked.defaultof<_> + let ok = tab64.TryGetValue(x64,&res) + if ok then res else + res <- BigInteger(x64) + tab64.[x64] <- res + res) + + let inline get32 (x32:int32) = FromInt64Dynamic (int64 x32) + + let inline isOX s = not (System.String.IsNullOrEmpty(s)) && s.Length > 2 && s.[0] = '0' && s.[1] = 'x' + + let FromZero () : 'T = + (get32 0 :?> 'T) + + let FromOne () : 'T = + (get32 1 :?> 'T) + + let FromInt32 (i:int32): 'T = + (get32 i :?> 'T) + + let FromInt64 (i:int64): 'T = + (FromInt64Dynamic i :?> 'T) + + let getParse s = + lock tabParse (fun () -> + let mutable res = Unchecked.defaultof<_> + let ok = tabParse.TryGetValue(s,&res) + if ok then + res + else +#if FSHARP_CORE_PORTABLE + // SL5 (and therefore Portable Profile47) does not have Parse, so make our own simple implementation + let parse(s : string) = + // ws* sign? digits+ ws* + let mutable i = 0 + // leading whitespace + while i < s.Length && System.Char.IsWhiteSpace(s.[i]) do + i <- i + 1 + if i = s.Length then + raise <| new System.ArgumentException() + // optional sign + let mutable isNegative = false + if s.[i] = '+' then + i <- i + 1 + elif s.[i] = '-' then + isNegative <- true + i <- i + 1 + if i = s.Length then + raise <| new System.ArgumentException() + // digits + let startDigits = i + while i < s.Length && System.Char.IsDigit(s.[i]) do + i <- i + 1 + let endDigits = i + let len = endDigits - startDigits + if len = 0 then + raise <| new System.ArgumentException() + // trailing whitespace + while i < s.Length && System.Char.IsWhiteSpace(s.[i]) do + i <- i + 1 + if i <> s.Length then + raise <| new System.ArgumentException() + // text is now valid, parse it + let mutable r = new System.Numerics.BigInteger(int(s.[startDigits]) - int('0')) + let ten = new System.Numerics.BigInteger(10) + for j in startDigits+1 .. endDigits-1 do + r <- r * ten + r <- r + new System.Numerics.BigInteger(int(s.[j]) - int('0')) + if isNegative then + r <- new System.Numerics.BigInteger(0) - r + r + let v = parse s +#else + let v = +#if FX_NO_BIGINT + BigInteger.Parse s +#else +#if FX_NO_BIGINT_CULTURE_PARSE + BigInteger.Parse s +#else + if isOX s then + BigInteger.Parse (s.[2..],NumberStyles.AllowHexSpecifier,CultureInfo.InvariantCulture) + else + BigInteger.Parse (s,NumberStyles.AllowLeadingSign,CultureInfo.InvariantCulture) +#endif +#endif +#endif + res <- v + tabParse.[s] <- res + res) + + let FromStringDynamic (s:string) : obj = + getParse s + + let FromString (s:string) : 'T = + (FromStringDynamic s :?> 'T) + + + diff --git a/src/FSharpPortable/z.fsi b/src/FSharpPortable/z.fsi new file mode 100644 index 00000000..cbc66872 --- /dev/null +++ b/src/FSharpPortable/z.fsi @@ -0,0 +1,124 @@ +// First version copied from the F# compiler sources +// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/z.fsi + +//---------------------------------------------------------------------------- +// Copyright (c) 2002-2012 Microsoft Corporation. +// +// This source code is subject to terms and conditions of the Apache License, Version 2.0. A +// copy of the license can be found in the License.html file at the root of this distribution. +// By using this source code in any fashion, you are agreeing to be bound +// by the terms of the Apache License, Version 2.0. +// +// You must not remove this notice, or any other, from this software. +//---------------------------------------------------------------------------- + +namespace System.Numerics +#if FX_NO_BIGINT + + open System + open Microsoft.FSharp.Collections + open Microsoft.FSharp.Core + + /// The type of arbitrary-sized integers + [] + [] + type BigInteger = + /// Return the sum of two big integers + static member ( + ) : x:BigInteger * y:BigInteger -> BigInteger + /// Return the modulus of big integers + static member ( % ) : x:BigInteger * y:BigInteger -> BigInteger + /// Return the product of big integers + static member ( * ) : x:BigInteger * y:BigInteger -> BigInteger + /// Return the difference of two big integers + static member ( - ) : x:BigInteger * y:BigInteger -> BigInteger + /// Return the ratio of two big integers + static member ( / ) : x:BigInteger * y:BigInteger -> BigInteger + /// Return the negation of a big integer + static member (~-) : x:BigInteger -> BigInteger + /// Return the given big integer + static member (~+) : x:BigInteger -> BigInteger + /// Convert a big integer to a floating point number + static member op_Explicit : x:BigInteger -> float + /// Convert a big integer to a 64-bit signed integer + static member op_Explicit : x:BigInteger -> int64 + /// Convert a big integer to a 32-bit signed integer + static member op_Explicit : x:BigInteger -> int32 + /// Parse a big integer from a string format + static member Parse : text:string -> BigInteger + /// Return the sign of a big integer: 0, +1 or -1 + member Sign : int + /// Compute the ratio and remainder of two big integers + static member DivRem : x:BigInteger * y:BigInteger * rem:BigInteger byref -> BigInteger + + /// This operator is for consistency when this type be used from other CLI languages + static member op_LessThan : x:BigInteger * y:BigInteger -> bool + /// This operator is for consistency when this type be used from other CLI languages + static member op_LessThanOrEqual : x:BigInteger * y:BigInteger -> bool + /// This operator is for consistency when this type be used from other CLI languages + static member op_GreaterThan : x:BigInteger * y:BigInteger -> bool + /// This operator is for consistency when this type be used from other CLI languages + static member op_GreaterThanOrEqual : x:BigInteger * y:BigInteger -> bool + /// This operator is for consistency when this type be used from other CLI languages + static member op_Equality : x:BigInteger * y:BigInteger -> bool + /// This operator is for consistency when this type be used from other CLI languages + static member op_Inequality : x:BigInteger * y:BigInteger -> bool + + /// Return the greatest common divisor of two big integers + static member GreatestCommonDivisor : x:BigInteger * y:BigInteger -> BigInteger + /// Return n^m for two big integers + static member Pow : x:BigInteger * y:int32 -> BigInteger + /// Compute the absolute value of a big integer + static member Abs : x:BigInteger -> BigInteger + /// Get the big integer for zero + static member Zero : BigInteger + /// Get the big integer for one + static member One : BigInteger + + /// Return true if a big integer is 'zero' + member IsZero : bool + /// Return true if a big integer is 'one' + member IsOne : bool + interface System.IComparable + override Equals : obj -> bool + override GetHashCode : unit -> int + override ToString : unit -> string + + /// Construct a BigInteger value for the given integer + new : x:int -> BigInteger + /// Construct a BigInteger value for the given 64-bit integer + new : x:int64 -> BigInteger +#if SILVERLIGHT + /// Provides custom formatting for BigInteger values + member StructuredDisplayString : string // This needs to be accessible in order for formatting code to invoke it via reflection in Silverlight. +#endif +#endif + + +namespace Microsoft.FSharp.Core + + type bigint = System.Numerics.BigInteger + + [] + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + module NumericLiterals = + + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + module NumericLiteralI = + open System.Numerics + + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromZero : value:unit -> 'T + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromOne : value:unit -> 'T + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromInt32 : value:int32 -> 'T + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromInt64 : value:int64 -> 'T + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromString : text:string -> 'T + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromInt64Dynamic : value:int64 -> obj + /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' + val FromStringDynamic : text:string -> obj + + From 67291410844d85b72d91b61d5cd16494b368cdbc Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Sat, 1 Dec 2012 10:04:55 +0000 Subject: [PATCH 03/15] Wrap the existing Complex type instead of redefining it --- src/FSharp/complex.fs | 60 ++++++++++---------- src/FSharp/complex.fsi | 121 +++++++++++++++++++++-------------------- 2 files changed, 95 insertions(+), 86 deletions(-) diff --git a/src/FSharp/complex.fs b/src/FSharp/complex.fs index 65707075..949bf9d7 100644 --- a/src/FSharp/complex.fs +++ b/src/FSharp/complex.fs @@ -10,7 +10,9 @@ namespace MathNet.Numerics open Microsoft.FSharp.Math open System open System.Globalization + open System.Numerics + (* [] [] type Complex(real: float, imaginary: float) = @@ -34,10 +36,16 @@ namespace MathNet.Numerics | _ -> false override x.GetHashCode() = (hash x.r >>> 5) ^^^ (hash x.r <<< 3) ^^^ (((hash x.i >>> 4) ^^^ (hash x.i <<< 4)) + 0x9e3779b9) - + *) type complex = Complex + [] + module private ComplexExtensionsBasic = + type Complex with + member x.r = x.Real + member x.i = x.Imaginary + [] module Complex = let mkRect(a,b) = new Complex(a,b) @@ -98,34 +106,30 @@ namespace MathNet.Numerics 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) + [] + module ComplexExtensions = + type Complex with + member x.r = x.Real + member x.i = x.Imaginary + 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 + static member Zero = Complex.zero + static member One = Complex.one + static member OneI = Complex.onei module ComplexTopLevelOperators = diff --git a/src/FSharp/complex.fsi b/src/FSharp/complex.fsi index 185d9c5e..fd916ef3 100644 --- a/src/FSharp/complex.fsi +++ b/src/FSharp/complex.fsi @@ -6,66 +6,71 @@ namespace MathNet.Numerics open System - - /// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates - [] - [] - 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 + open System.Numerics + + [] + module ComplexExtensions = + /// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates + type Complex with + /// 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 + /// 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 + (* + 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 From 205c770c5cae1bd028f7bfeeed8316bb23915c64 Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Sat, 1 Dec 2012 10:12:59 +0000 Subject: [PATCH 04/15] Rename Distribution to RandomVariable --- src/FSharp/FSharp.fsproj | 2 +- ...ibutionMonad.fs => RandomVariableMonad.fs} | 30 +++++++++---------- src/FSharpPortable/FSharpPortable.fsproj | 4 +-- src/FSharpUnitTests/FSharpUnitTests.fsproj | 4 +-- ...PokerDistributionTest.fs => PokerTests.fs} | 4 +-- ...ributionTest.fs => RandomVariableTests.fs} | 14 ++++----- 6 files changed, 29 insertions(+), 29 deletions(-) rename src/FSharp/{DistributionMonad.fs => RandomVariableMonad.fs} (74%) rename src/FSharpUnitTests/{PokerDistributionTest.fs => PokerTests.fs} (95%) rename src/FSharpUnitTests/{DistributionTest.fs => RandomVariableTests.fs} (83%) diff --git a/src/FSharp/FSharp.fsproj b/src/FSharp/FSharp.fsproj index 26d2f50d..b1b485eb 100644 --- a/src/FSharp/FSharp.fsproj +++ b/src/FSharp/FSharp.fsproj @@ -61,7 +61,7 @@ - + diff --git a/src/FSharp/DistributionMonad.fs b/src/FSharp/RandomVariableMonad.fs similarity index 74% rename from src/FSharp/DistributionMonad.fs rename to src/FSharp/RandomVariableMonad.fs index eb32c91b..da2842ec 100644 --- a/src/FSharp/DistributionMonad.fs +++ b/src/FSharp/RandomVariableMonad.fs @@ -6,16 +6,16 @@ open System open System.Collections open System.Collections.Generic -module Distribution = +module RandomVariable = type 'a Outcome = { Value: 'a Probability : BigRational } - type 'a Distribution = 'a Outcome seq + type 'a RandomVariable = 'a Outcome seq // P(A AND B) = P(A | B) * P(B) - let bind (f: 'a -> 'b Distribution) (dist:'a Distribution) = + let bind (f: 'a -> 'b RandomVariable) (dist:'a RandomVariable) = dist |> Seq.map (fun p1 -> f p1.Value @@ -23,40 +23,40 @@ module Distribution = { Value = p2.Value; Probability = p1.Probability * p2.Probability})) - |> Seq.concat : 'b Distribution + |> Seq.concat : 'b RandomVariable /// 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 + /// Inject a value into the RandomVariable type let returnM (value:'a) = Seq.singleton { Value = value ; Probability = 1N/1N } - : 'a Distribution + : 'a RandomVariable - type DistributionMonadBuilder() = + type RandomVariableMonadBuilder() = member this.Bind (r, f) = bind f r member this.Return x = returnM x member this.ReturnFrom x = x - let distribution = DistributionMonadBuilder() + let randomVariable = RandomVariableMonadBuilder() // Create some helpers - let toUniformDistribution seq : 'a Distribution = + let toUniformDistribution seq : 'a RandomVariable = let l = Seq.length seq seq |> Seq.map (fun e -> { Value = e; Probability = 1N / bignum.FromInt l }) - let probability (dist:'a Distribution) = + let probability (dist:'a RandomVariable) = dist |> Seq.map (fun o -> o.Probability) |> Seq.sum let certainly = returnM - let impossible<'a> :'a Distribution = toUniformDistribution [] + let impossible<'a> :'a RandomVariable = toUniformDistribution [] let fairDice sides = toUniformDistribution [1..sides] @@ -66,15 +66,15 @@ module Distribution = let fairCoin = toUniformDistribution [Heads; Tails] - let filter predicate (dist:'a Distribution) : 'a Distribution = + let filter predicate (dist:'a RandomVariable) : 'a RandomVariable = 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 = + /// Transforms a RandomVariable value by using a specified mapping function. + let map f (dist:'a RandomVariable) : 'b RandomVariable = dist |> Seq.map (fun o -> { Value = f o.Value; Probability = o.Probability }) @@ -86,7 +86,7 @@ module Distribution = match n with | 0 -> certainly ([],values) | _ -> - distribution { + randomVariable { let! (x,c1) = selectOne values let! (xs,c2) = selectMany (n-1) c1 return x::xs,c2} diff --git a/src/FSharpPortable/FSharpPortable.fsproj b/src/FSharpPortable/FSharpPortable.fsproj index 420908c3..481ad7d9 100644 --- a/src/FSharpPortable/FSharpPortable.fsproj +++ b/src/FSharpPortable/FSharpPortable.fsproj @@ -78,8 +78,8 @@ q.fs - - DistributionMonad.fs + + RandomVariableMonad.fs diff --git a/src/FSharpUnitTests/FSharpUnitTests.fsproj b/src/FSharpUnitTests/FSharpUnitTests.fsproj index 4eefb688..6a51226a 100644 --- a/src/FSharpUnitTests/FSharpUnitTests.fsproj +++ b/src/FSharpUnitTests/FSharpUnitTests.fsproj @@ -48,8 +48,8 @@ - - + + Always diff --git a/src/FSharpUnitTests/PokerDistributionTest.fs b/src/FSharpUnitTests/PokerTests.fs similarity index 95% rename from src/FSharpUnitTests/PokerDistributionTest.fs rename to src/FSharpUnitTests/PokerTests.fs index b2702a71..4276d06b 100644 --- a/src/FSharpUnitTests/PokerDistributionTest.fs +++ b/src/FSharpUnitTests/PokerTests.fs @@ -1,7 +1,7 @@ -module MathNet.Numerics.Tests.PokerDistributionTest +module MathNet.Numerics.Tests.PokerTests open MathNet.Numerics -open MathNet.Numerics.Distribution +open MathNet.Numerics.RandomVariable open NUnit.Framework open FsUnit diff --git a/src/FSharpUnitTests/DistributionTest.fs b/src/FSharpUnitTests/RandomVariableTests.fs similarity index 83% rename from src/FSharpUnitTests/DistributionTest.fs rename to src/FSharpUnitTests/RandomVariableTests.fs index b511f18d..e9a80f20 100644 --- a/src/FSharpUnitTests/DistributionTest.fs +++ b/src/FSharpUnitTests/RandomVariableTests.fs @@ -1,16 +1,16 @@ -module MathNet.Numerics.Tests.DistributionTest +module MathNet.Numerics.Tests.RandomVariableTests open MathNet.Numerics -open MathNet.Numerics.Distribution +open MathNet.Numerics.RandomVariable open NUnit.Framework open FsUnit [] -let ``When creating a empty distribution, then the probability should be 1``() = - let actual = distribution { return () } +let ``When creating a empty randomVariable, then the probability should be 1``() = + let actual = randomVariable { return () } probability actual |> should equal (1N/1N) -let sumOfTwoFairDices = distribution { +let sumOfTwoFairDices = randomVariable { let! d1 = fairDice 6 let! d2 = fairDice 6 return d1 + d2 } @@ -22,7 +22,7 @@ let ``When creating two fair dices, then P(Sum of 2 dices = 7) should be 1/6``() |> probability |> should equal (1N/6N) -let fairCoinAndDice = distribution { +let fairCoinAndDice = randomVariable { let! d = fairDice 6 let! c = fairCoin return d,c } @@ -67,7 +67,7 @@ let ``When making the first choice in a MontyHall situation, the chances to win |> probability |> should equal (1N/3N) -let montyHallWithSwitch = distribution { +let montyHallWithSwitch = randomVariable { let! firstDoor = firstChoice return! switch firstDoor } From 1e7c60cb8e6bbb22271c0e09cf8bceec3e0582aa Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Sat, 1 Dec 2012 12:24:32 +0000 Subject: [PATCH 05/15] Convert FSharpUnitTests to NUnit + latest FSUnit --- src/FSharpUnitTests/App.config | 6 - src/FSharpUnitTests/BigRationalTests.fs | 10 +- src/FSharpUnitTests/DenseMatrixTests.fs | 51 ++++ src/FSharpUnitTests/DenseVectorTests.fs | 35 +++ src/FSharpUnitTests/FSharpUnitTests.fsproj | 12 +- src/FSharpUnitTests/FsUnit.fs | 310 +++++---------------- src/FSharpUnitTests/MatrixTests.fs | 112 ++++++++ src/FSharpUnitTests/Program.fs | 223 --------------- src/FSharpUnitTests/SparseMatrixTests.fs | 27 ++ src/FSharpUnitTests/SparseVectorTests.fs | 21 ++ src/FSharpUnitTests/VectorTests.fs | 103 +++++++ 11 files changed, 425 insertions(+), 485 deletions(-) delete mode 100644 src/FSharpUnitTests/App.config create mode 100644 src/FSharpUnitTests/DenseMatrixTests.fs create mode 100644 src/FSharpUnitTests/DenseVectorTests.fs create mode 100644 src/FSharpUnitTests/MatrixTests.fs delete mode 100644 src/FSharpUnitTests/Program.fs create mode 100644 src/FSharpUnitTests/SparseMatrixTests.fs create mode 100644 src/FSharpUnitTests/SparseVectorTests.fs create mode 100644 src/FSharpUnitTests/VectorTests.fs diff --git a/src/FSharpUnitTests/App.config b/src/FSharpUnitTests/App.config deleted file mode 100644 index 6c301d33..00000000 --- a/src/FSharpUnitTests/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/FSharpUnitTests/BigRationalTests.fs b/src/FSharpUnitTests/BigRationalTests.fs index 1173e196..65915416 100644 --- a/src/FSharpUnitTests/BigRationalTests.fs +++ b/src/FSharpUnitTests/BigRationalTests.fs @@ -331,15 +331,7 @@ type BigNumType() = "1000000000000000000000000000000000018") Assert.AreEqual(g_zero.ToString(),"0") Assert.AreEqual(g_normal.ToString(),"88") - - - [] - 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 [] member this.Abs() = diff --git a/src/FSharpUnitTests/DenseMatrixTests.fs b/src/FSharpUnitTests/DenseMatrixTests.fs new file mode 100644 index 00000000..b06107d9 --- /dev/null +++ b/src/FSharpUnitTests/DenseMatrixTests.fs @@ -0,0 +1,51 @@ +namespace MathNet.Numerics.Tests + +open NUnit.Framework +open FsUnit +open MathNet.Numerics.LinearAlgebra.Generic +open MathNet.Numerics.LinearAlgebra.Double + +/// Unit tests for the dense matrix type. +module DenseMatrixTests = + + /// A small uniform vector. + let smallM = new DenseMatrix( Array2D.create 2 2 0.3 ) + + /// A large vector with increasingly large entries + let largeM = new DenseMatrix( Array2D.init 100 100 (fun i j -> float i * 100.0 + float j) ) + + [] + let ``DenseMatrix.init`` () = + DenseMatrix.init 100 100 (fun i j -> float i * 100.0 + float j) |> should equal largeM + + [] + let ``DenseMatrix.ofList`` () = + DenseMatrix.ofList [[0.3;0.3];[0.3;0.3]] |> should equal smallM + + [] + let ``DenseMatrix.ofSeq`` () = + DenseMatrix.ofSeq (Seq.ofList [[0.3;0.3];[0.3;0.3]]) |> should equal smallM + + [] + let ``DenseMatrix.ofArray2`` () = + DenseMatrix.ofArray2 (Array2D.create 2 2 0.3) |> should equal smallM + + [] + let ``DenseMatrix.initDense`` () = + DenseMatrix.initDense 100 100 (seq { for i in 0 .. 99 do + for j in 0 .. 99 -> (i,j, float i * 100.0 + float j)}) |> should equal largeM + [] + let ``DenseMatrix.constDiag`` () = + DenseMatrix.constDiag 100 2.0 |> should equal (2.0 * (DenseMatrix.Identity 100)) + + [] + let ``DenseMatrix.diag`` () = + DenseMatrix.diag (new DenseVector(100, 2.0)) |> should equal (2.0 * (DenseMatrix.Identity 100)) + + [] + let ``DenseMatrix.init_row`` () = + DenseMatrix.initRow 100 100 (fun i -> (DenseVector.init 100 (fun j -> float i * 100.0 + float j))) |> should equal largeM + + [] + let ``DenseMatrix.init_col`` () = + DenseMatrix.initCol 100 100 (fun j -> (DenseVector.init 100 (fun i -> float i * 100.0 + float j))) |> should equal largeM diff --git a/src/FSharpUnitTests/DenseVectorTests.fs b/src/FSharpUnitTests/DenseVectorTests.fs new file mode 100644 index 00000000..346e55a6 --- /dev/null +++ b/src/FSharpUnitTests/DenseVectorTests.fs @@ -0,0 +1,35 @@ +namespace MathNet.Numerics.Tests + +open NUnit.Framework +open FsUnit +open MathNet.Numerics.LinearAlgebra.Generic +open MathNet.Numerics.LinearAlgebra.Double + +/// Unit tests for the dense vector type. +module DenseVectorTests = + + /// A small uniform vector. + let smallv = new DenseVector(5, 0.3 ) + + /// A large vector with increasingly large entries + let largev = new DenseVector( Array.init 100 (fun i -> float i / 100.0) ) + + [] + let ``DenseVector.init`` () = + DenseVector.init 100 (fun i -> float i / 100.0) |> should equal largev + + [] + let ``DenseVector.ofList`` () = + DenseVector.ofList [ for i in 0 .. 99 -> float i / 100.0 ] |> should equal largev + + [] + let ``DenseVector.ofSeq`` () = + DenseVector.ofSeq (seq { for i in 0 .. 99 -> float i / 100.0 }) |> should equal largev + + [] + let ``DenseVector.rangef`` () = + DenseVector.rangef 0.0 0.01 0.99 |> should equal (new DenseVector( [| for i in 0 .. 99 -> 0.01 * float i |] ) ) + + [] + let ``DenseVector.range`` () = + DenseVector.range 0 99 |> should equal (new DenseVector( [| for i in 0 .. 99 -> float i |] ) ) diff --git a/src/FSharpUnitTests/FSharpUnitTests.fsproj b/src/FSharpUnitTests/FSharpUnitTests.fsproj index 6a51226a..9859d024 100644 --- a/src/FSharpUnitTests/FSharpUnitTests.fsproj +++ b/src/FSharpUnitTests/FSharpUnitTests.fsproj @@ -6,7 +6,7 @@ 8.0.30703 2.0 {f2f8032b-a31d-4e33-a05e-f2cdcbfaa75d} - Exe + Library FSharpUnitTests MathNet.Numerics.FSharp.UnitTests v4.0 @@ -46,14 +46,16 @@ + + + + + + - - - Always - diff --git a/src/FSharpUnitTests/FsUnit.fs b/src/FSharpUnitTests/FsUnit.fs index a4f7b89c..d5c8a7a7 100644 --- a/src/FSharpUnitTests/FsUnit.fs +++ b/src/FSharpUnitTests/FsUnit.fs @@ -1,244 +1,70 @@ -(* -Copyright (c) 2008, Raymond W. Vernagus (R.Vernagus@gmail.com) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of Raymond W. Vernagus nor the names of FsUnit's - contributors may be used to endorse or promote products derived from this - software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*) -namespace FsUnit - -open MathNet.Numerics -open MathNet.Numerics.LinearAlgebra.Double -open MathNet.Numerics.LinearAlgebra.Generic - -type Result = - | Pass - | Fail of string - | Error of string - -type Expectation = - | True - | False - | Empty - | NullOrEmpty - | Null - | SameAs of obj - -type Spec = - abstract Check : unit -> Result - abstract NegatedMessage : string - -[] -module SpecOps = - let internal safeCheck f = - try - f() - with ex -> Error (ex.ToString()) - - let make f nmsg = - { new Spec with - member this.Check() = safeCheck f - member this.NegatedMessage = nmsg } - - let check (s: Spec) = s.Check() - - let not' f x = - let s = f x - match check s with - | Pass -> make (fun () -> Fail s.NegatedMessage) "" - | Fail msg -> make (fun () -> Pass) msg - | Error msg -> make (fun () -> Error msg) msg - - let equal expected actual = - make (fun () -> - if actual.Equals(expected) - then Pass - else Fail (sprintf "Expected: %A\nActual: %A" expected actual)) - (sprintf "NOT Expected: %A\nActual: %A" expected actual) - - let have n lbl s = - make (fun() -> - let len = Seq.length s - if len = n - then Pass - else Fail (sprintf "Expected: %d %s\nActual: %d %s" n lbl len lbl)) - (sprintf "NOT Expected: %d %s\nActual: %d %s" n lbl n lbl) - - let contain x s = - make (fun () -> - let exists = s |> Seq.exists (fun x' -> x' = x) - if exists - then Pass - else Fail (sprintf "Expected %A to contain %A" s x)) - (sprintf "Did NOT expect %A to contain %A" s x) - - let raise'<'a when 'a :> System.Exception> f = - let expType = typeof<'a> - make (fun () -> - try - f() - Fail (sprintf "Expected %s but no exception was raised" expType.FullName) - with ex -> - let actualExType = ex.GetType() - if expType = actualExType - then Pass - else Fail (sprintf "Expected: %s\nActual: %s" expType.FullName actualExType.FullName)) - (sprintf "Did NOT expect %s to be raised" expType.FullName) - - let be expectation x = - let x = box x - let msg = sprintf "Expected: %A\nActual: %A" expectation x - let negmsg = sprintf "NOT Expected: %A\nActual: %A" expectation x - match expectation with - | True -> - make (fun () -> - if x = box true - then Pass - else Fail msg) - negmsg - | False -> - make (fun () -> - if x = box false - then Pass - else Fail msg) - negmsg - | Empty -> - make (fun () -> - if x = box System.String.Empty - then Pass - else Fail msg) - negmsg - | NullOrEmpty -> - make (fun () -> - if System.String.IsNullOrEmpty(x :?> string) - then Pass - else Fail msg) - negmsg - | Null -> - make (fun () -> - if x = null - then Pass - else Fail msg) - negmsg - | SameAs other -> - make (fun () -> - if System.Object.ReferenceEquals(x, other) - then Pass - else Fail (sprintf "Expected actual to be same reference as expected %A" other)) - (sprintf "Expected %A to have different reference than %A" x other) - - let array_equal (expected: float []) (actual: float []) = - make (fun () -> - let mutable f = true - for i=0 to expected.Length-1 do - f <- f && (expected.[i] = actual.[i]) - if f - then Pass - else Fail (sprintf "Expected: %A\nActual: %A" expected actual)) - (sprintf "NOT Expected: %A\nActual: %A" expected actual) - - let array2_equal (expected: float [,]) (actual: float [,]) = - make (fun () -> - let mutable f = true - for i=0 to expected.GetLength(0)-1 do - for j=0 to expected.GetLength(1)-1 do - f <- f && (expected.[i,j] = actual.[i,j]) - if f - then Pass - else Fail (sprintf "Expected: %A\nActual: %A" expected actual)) - (sprintf "NOT Expected: %A\nActual: %A" expected actual) - - let approximately_equal (places : int) (expected: float) (actual: float) = - make (fun () -> - if Precision.AlmostEqualInDecimalPlaces(actual, expected, places) - then Pass - else Fail (sprintf "Expected: %A\nActual: %A" expected actual)) - (sprintf "NOT Expected: %A\nActual: %A" expected actual) - - let approximately_vector_equal (places : int) (expected: #Vector) (actual: #Vector) = - make (fun () -> - let mutable f = true - for i=0 to expected.Count-1 do - f <- f && Precision.AlmostEqualInDecimalPlaces(expected.[i], actual.[i], places) - if f - then Pass - else Fail (sprintf "Expected: %A\nActual: %A" expected actual)) - (sprintf "NOT Expected: %A\nActual: %A" expected actual) - - let approximately_matrix_equal (places : int) (expected: #Matrix) (actual: #Matrix) = - make (fun () -> - let mutable f = true - for i=0 to expected.RowCount-1 do - for j=0 to expected.ColumnCount-1 do - f <- f && Precision.AlmostEqualInDecimalPlaces(expected.[i,j], actual.[i,j], places) - if f - then Pass - else Fail (sprintf "Expected: %A\nActual: %A" expected actual)) - (sprintf "NOT Expected: %A\nActual: %A" expected actual) - -module Results = - open Microsoft.FSharp.Text - - let internal currentResults = new ResizeArray() - - let add = currentResults.Add - - let passedCount () = - currentResults - |> Seq.filter (function _,Pass -> true | _ -> false) - |> Seq.length - - let failed () = - currentResults - |> Seq.filter (function _,Fail _ -> true | _ -> false) - - let failedCount () = - failed() - |> Seq.length - - let erred () = - currentResults - |> Seq.filter (function _,Error _ -> true | _ -> false) - - let erredCount () = - erred() - |> Seq.length - - let summary () = - let buff = new System.Text.StringBuilder() - buff.AppendFormat("{0} passed.\n{1} failed.\n{2} erred.", passedCount(), failedCount(), erredCount()) |> ignore - failed() - |> Seq.iter (function (lbl,Fail msg) -> buff.AppendFormat("\n----\nFailed: {0}\n{1}", lbl, msg) |> ignore | _ -> ()) - erred() - |> Seq.iter (function (lbl,Error msg) -> buff.AppendFormat("\n----\nErred: {0}\n{1}", lbl, msg) |> ignore | _ -> ()) - buff.ToString() +module FsUnit +open NUnit.Framework +open NUnit.Framework.Constraints + +let should (f : 'a -> #Constraint) x (y : obj) = + let c = f x + let y = + match y with + | :? (unit -> unit) -> box (TestDelegate(y :?> unit -> unit)) + | _ -> y + Assert.That(y, c) +let equal x = EqualConstraint(x) -[] -module SpecHelpers = - let spec lbl s = (lbl, check s) - - let should f x = f x - - let specs lbl (results: seq) = - results |> Seq.iter (fun x -> Results.add x) +let equalWithin tolerance x = equal(x).Within tolerance + +let contain x = ContainsConstraint(x) + +let haveLength n = Has.Length.EqualTo(n) + +let haveCount n = Has.Count.EqualTo(n) + +let be = id + +let Null = NullConstraint() + +let Empty = EmptyConstraint() + +let EmptyString = EmptyStringConstraint() + +let NullOrEmptyString = NullOrEmptyStringConstraint() + +let True = TrueConstraint() + +let False = FalseConstraint() + +let sameAs x = SameAsConstraint(x) + +let throw = Throws.TypeOf + +let greaterThan x = GreaterThanConstraint(x) + +let greaterThanOrEqualTo x = GreaterThanOrEqualConstraint(x) + +let lessThan x = LessThanConstraint(x) + +let lessThanOrEqualTo x = LessThanOrEqualConstraint(x) + +let shouldFail (f : unit -> unit) = + TestDelegate(f) |> should throw typeof + +let endWith (s:string) = EndsWithConstraint s + +let startWith (s:string) = StartsWithConstraint s + +let ofExactType<'a> = ExactTypeConstraint(typeof<'a>) + +let instanceOfType<'a> = InstanceOfTypeConstraint(typeof<'a>) + +let NaN = NaNConstraint() + +let unique = UniqueItemsConstraint() + +let not' x = NotConstraint(x) + +let array_equal = equal +let array2_equal = equal +let approximately_equal tolerance = equalWithin (10.0 ** (float -tolerance)) +let approximately_vector_equal = approximately_equal +let approximately_matrix_equal = approximately_equal diff --git a/src/FSharpUnitTests/MatrixTests.fs b/src/FSharpUnitTests/MatrixTests.fs new file mode 100644 index 00000000..bea3a6c9 --- /dev/null +++ b/src/FSharpUnitTests/MatrixTests.fs @@ -0,0 +1,112 @@ +namespace MathNet.Numerics.Tests + +open NUnit.Framework +open FsUnit +open MathNet.Numerics.LinearAlgebra.Generic +open MathNet.Numerics.LinearAlgebra.Double + +/// Unit tests for the matrix type. +module MatrixTests = + + /// A small uniform vector. + let smallM = new DenseMatrix( Array2D.create 2 2 0.3 ) + let failingFoldBackM = DenseMatrix.init 2 3 (fun i j -> 1.0) + + /// A large vector with increasingly large entries + let largeM = new DenseMatrix( Array2D.init 100 100 (fun i j -> float i * 100.0 + float j) ) + + [] + let ``Matrix.fold`` () = + Matrix.fold (fun a b -> a - b) 0.0 smallM |> should equal -1.2 + + [] + let ``Matrix.foldBack`` () = + Matrix.foldBack (fun a b -> a - b) 0.0 smallM |> should equal 0.0 + + [] + let ``Matrix.foldBackSummation`` () = + Matrix.foldBack( fun a b -> a + b) 0.0 failingFoldBackM |> should equal 6.0 + + [] + let ``Matrix.foldi`` () = + Matrix.foldi (fun i j acc x -> acc + x + float (i+j)) 0.0 smallM |> should equal 5.2 + + [] + let ``Matrix.toArray2`` () = + Matrix.toArray2 smallM |> should array2_equal (Array2D.create 2 2 0.3) + + [] + let ``Matrix.forall`` () = + Matrix.forall (fun x -> x = 0.3) smallM |> should equal true + + [] + let ``Matrix.exists`` () = + Matrix.exists (fun x -> x = 0.5) smallM |> should equal false + + [] + let ``Matrix.foralli`` () = + Matrix.foralli (fun i j x -> x = float i * 100.0 + float j) largeM |> should equal true + + [] + let ``Matrix.existsi`` () = + Matrix.existsi (fun i j x -> x = float i * 100.0 + float j) largeM |> should equal true + + [] + let ``Matrix.map`` () = + Matrix.map (fun x -> 2.0 * x) smallM |> should equal (2.0 * smallM) + + [] + let ``Matrix.mapi`` () = + Matrix.mapi (fun i j x -> float i * 100.0 + float j + x) largeM |> should equal (2.0 * largeM) + + [] + let ``Matrix.mapCols`` () = + Matrix.mapCols (fun j col -> col.Add(float j)) smallM |> should (approximately_matrix_equal 14) (matrix [[0.3;1.3];[0.3;1.3]]) + + [] + let ``Matrix.mapRows`` () = + Matrix.mapRows (fun i row -> row.Add(float i)) smallM |> should (approximately_matrix_equal 14) (matrix [[0.3;0.3];[1.3;1.3]]) + + [] + let ``Matrix.inplaceAssign`` () = + let N = smallM.Clone() + Matrix.inplaceAssign (fun i j -> 0.0) N + N |> should equal (0.0 * smallM) + + [] + let ``Matrix.inplaceMapi`` () = + let N = largeM.Clone() + Matrix.inplaceMapi (fun i j x -> 2.0 * (float i * 100.0 + float j) + x) N + N |> should equal (3.0 * largeM) + + [] + let ``Matrix.nonZeroEntries`` () = + Seq.length (Matrix.nonZeroEntries smallM) |> should equal 4 + + [] + let ``Matrix.sum`` () = + Matrix.sum smallM |> should equal 1.2 + + [] + let ``Matrix.sumColsBy`` () = + Matrix.sumColsBy (fun j col -> col.[0] * col.[1]) (matrix [[1.0; 2.0]; [3.0; 4.0]]) |> should equal 11.0 + + [] + let ``Matrix.sumRowsBy`` () = + Matrix.sumRowsBy (fun i row -> row.[0] * row.[1]) (matrix [[1.0; 2.0]; [3.0; 4.0]]) |> should equal 14.0 + + [] + let ``Matrix.foldCol`` () = + Matrix.foldCol (+) 0.0 largeM 0 |> should equal 495000.0 + + [] + let ``Matrix.foldRow`` () = + Matrix.foldRow (+) 0.0 largeM 0 |> should equal 4950.0 + + [] + let ``Matrix.foldByCol`` () = + Matrix.foldByCol (+) 0.0 smallM |> should equal (DenseVector.ofList [0.6;0.6] :> Vector) + + [] + let ``Matrix.foldByRow`` () = + Matrix.foldByRow (+) 0.0 smallM |> should equal (DenseVector.ofList [0.6;0.6] :> Vector) diff --git a/src/FSharpUnitTests/Program.fs b/src/FSharpUnitTests/Program.fs deleted file mode 100644 index 79780a5a..00000000 --- a/src/FSharpUnitTests/Program.fs +++ /dev/null @@ -1,223 +0,0 @@ -open FsUnit -open MathNet.Numerics.LinearAlgebra.Generic -open MathNet.Numerics.LinearAlgebra.Double - -/// Unit tests for the dense vector type. -let DenseVectorTests = - - /// A small uniform vector. - let smallv = new DenseVector(5, 0.3 ) - - /// A large vector with increasingly large entries - let largev = new DenseVector( Array.init 100 (fun i -> float i / 100.0) ) - - specs "DenseVector" [ - spec "DenseVector.init" - (DenseVector.init 100 (fun i -> float i / 100.0) |> should equal largev) - spec "DenseVector.ofList" - (DenseVector.ofList [ for i in 0 .. 99 -> float i / 100.0 ] |> should equal largev) - spec "DenseVector.ofSeq" - (DenseVector.ofSeq (seq { for i in 0 .. 99 -> float i / 100.0 }) |> should equal largev) - spec "DenseVector.rangef" - (DenseVector.rangef 0.0 0.01 0.99 |> should equal (new DenseVector( [| for i in 0 .. 99 -> 0.01 * float i |] ) )) - spec "DenseVector.range" - (DenseVector.range 0 99 |> should equal (new DenseVector( [| for i in 0 .. 99 -> float i |] ) )) - ] - - -/// Unit tests for the sparse vector type. -let SparseVectorTests = - - /// A small uniform vector. - let smallv = new DenseVector( [|0.0;0.3;0.0;0.0;0.0|] ) :> Vector - - specs "SparseVector" [ - spec "SparseVector.ofList" - ((SparseVector.ofList 5 [ (1,0.3) ] :> Vector) |> should equal smallv) - spec "SparseVector.ofSeq" - ((SparseVector.ofSeq 5 (List.toSeq [ (1,0.3) ]) :> Vector) |> should equal smallv) - ] - - -/// Unit tests for the vector type. -let VectorTests = - - /// A small uniform vector. - let smallv = new DenseVector( [|0.3;0.3;0.3;0.3;0.3|] ) :> Vector - - /// A large vector with increasingly large entries - let largev = new DenseVector( Array.init 100 (fun i -> float i / 100.0) ) :> Vector - - specs "Vector" [ - spec "Vector.toArray" - (Vector.toArray smallv |> should array_equal [|0.3;0.3;0.3;0.3;0.3|]) - spec "Vector.toList" - (Vector.toList smallv |> should equal [0.3;0.3;0.3;0.3;0.3]) - spec "Vector.mapInPlace" - ( let w = smallv.Clone() - Vector.mapInPlace (fun x -> 2.0 * x) w - w |> should equal (2.0 * smallv)) - spec "Vector.mapiInPlace" - ( let w = largev.Clone() - Vector.mapiInPlace (fun i x -> float i / 100.0) w - w |> should equal (largev)) - spec "Vector.addInPlace" - ( let w = largev.Clone() - Vector.addInPlace w largev - w |> should equal (2.0 * largev)) - spec "Vector.subInPlace" - ( let w = largev.Clone() - Vector.subInPlace w largev - w |> should equal (0.0 * largev)) - spec "Vector.map" - (Vector.map (fun x -> 2.0 * x) largev |> should equal (2.0 * largev)) - spec "Vector.mapi" - (Vector.mapi (fun i x -> float i / 100.0) largev |> should equal largev) - spec "Vector.fold" - (Vector.fold (fun a b -> a - b) 0.0 smallv |> should equal -1.5) - spec "Vector.foldBack" - (Vector.foldBack (fun a b -> a - b) 0.0 smallv |> should equal 0.0) - spec "Vector.foldi" - (Vector.foldi (fun i a b -> a + b) 0.0 smallv |> should equal 1.5) - spec "Vector.forall" - (Vector.forall (fun x -> x = 0.3) smallv |> should equal true) - spec "Vector.exists" - (Vector.exists (fun x -> x = 0.3) smallv |> should equal true) - spec "Vector.foralli" - (Vector.foralli (fun i x -> x = 0.3 && i < 5) smallv |> should equal true) - spec "Vector.existsi" - (Vector.existsi (fun i x -> x = 0.3 && i = 2) smallv |> should equal true) - spec "Vector.scan" - (Vector.scan (fun acc x -> acc + x) smallv |> should approximately_vector_equal 14 (new DenseVector( [|0.3;0.6;0.9;1.2;1.5|] ) :> Vector) ) - spec "Vector.scanBack" - (Vector.scanBack (fun x acc -> acc + x) smallv |> should approximately_vector_equal 14 (new DenseVector( [|1.5;1.2;0.9;0.6;0.3|] ) :> Vector) ) - spec "Vector.reduce" - (Vector.reduce (fun acc x -> acc ** x) smallv |> should approximately_equal 14 0.990295218585507) - spec "Vector.reduceBack" - (Vector.reduceBack (fun x acc -> x ** acc) smallv |> should approximately_equal 14 0.488911287726319) - spec "Vector.insert" - (Vector.insert 2 0.5 smallv |> should approximately_vector_equal 14 (new DenseVector ( [|0.3;0.3;0.5;0.3;0.3;0.3|] ) :> Vector) ) - ] - - -/// Unit tests for the matrix type. -let MatrixTests = - - /// A small uniform vector. - let smallM = new DenseMatrix( Array2D.create 2 2 0.3 ) - let failingFoldBackM = DenseMatrix.init 2 3 (fun i j -> 1.0) - - - /// A large vector with increasingly large entries - let largeM = new DenseMatrix( Array2D.init 100 100 (fun i j -> float i * 100.0 + float j) ) - - specs "Matrix" [ - spec "Matrix.fold" - (Matrix.fold (fun a b -> a - b) 0.0 smallM |> should equal -1.2) - spec "Matrix.foldBack" - (Matrix.foldBack (fun a b -> a - b) 0.0 smallM |> should equal 0.0) - spec "Matrix.foldBackSummation" - (Matrix.foldBack( fun a b -> a + b) 0.0 failingFoldBackM |> should equal 6.0) - spec "Matrix.foldi" - (Matrix.foldi (fun i j acc x -> acc + x + float (i+j)) 0.0 smallM |> should equal 5.2) - spec "Matrix.toArray2" - (Matrix.toArray2 smallM |> should array2_equal (Array2D.create 2 2 0.3)) - spec "Matrix.forall" - (Matrix.forall (fun x -> x = 0.3) smallM |> should equal true) - spec "Matrix.exists" - (Matrix.exists (fun x -> x = 0.5) smallM |> should equal false) - spec "Matrix.foralli" - (Matrix.foralli (fun i j x -> x = float i * 100.0 + float j) largeM |> should equal true) - spec "Matrix.existsi" - (Matrix.existsi (fun i j x -> x = float i * 100.0 + float j) largeM |> should equal true) - spec "Matrix.map" - (Matrix.map (fun x -> 2.0 * x) smallM |> should equal (2.0 * smallM)) - spec "Matrix.mapi" - (Matrix.mapi (fun i j x -> float i * 100.0 + float j + x) largeM |> should equal (2.0 * largeM)) - spec "Matrix.mapCols" - (Matrix.mapCols (fun j col -> col.Add(float j)) smallM |> should approximately_matrix_equal 14 (matrix [[0.3;1.3];[0.3;1.3]])) - spec "Matrix.mapRows" - (Matrix.mapRows (fun i row -> row.Add(float i)) smallM |> should approximately_matrix_equal 14 (matrix [[0.3;0.3];[1.3;1.3]])) - spec "Matrix.inplaceAssign" - ( let N = smallM.Clone() - Matrix.inplaceAssign (fun i j -> 0.0) N - N |> should equal (0.0 * smallM)) - spec "Matrix.inplaceMapi" - ( let N = largeM.Clone() - Matrix.inplaceMapi (fun i j x -> 2.0 * (float i * 100.0 + float j) + x) N - N |> should equal (3.0 * largeM)) - spec "Matrix.nonZeroEntries" - (Seq.length (Matrix.nonZeroEntries smallM) |> should equal 4) - spec "Matrix.sum" - (Matrix.sum smallM |> should equal 1.2) - spec "Matrix.sumColsBy" - (Matrix.sumColsBy (fun j col -> col.[0] * col.[1]) (matrix [[1.0; 2.0]; [3.0; 4.0]]) |> should equal 11.0) - spec "Matrix.sumRowsBy" - (Matrix.sumRowsBy (fun i row -> row.[0] * row.[1]) (matrix [[1.0; 2.0]; [3.0; 4.0]]) |> should equal 14.0) - spec "Matrix.foldCol" - (Matrix.foldCol (+) 0.0 largeM 0 |> should equal 495000.0) - spec "Matrix.foldRow" - (Matrix.foldRow (+) 0.0 largeM 0 |> should equal 4950.0) - spec "Matrix.foldByCol" - (Matrix.foldByCol (+) 0.0 smallM |> should equal (DenseVector.ofList [0.6;0.6] :> Vector)) - spec "Matrix.foldByRow" - (Matrix.foldByRow (+) 0.0 smallM |> should equal (DenseVector.ofList [0.6;0.6] :> Vector)) - ] - - -/// Unit tests for the dense matrix type. -let DenseMatrixTests = - - /// A small uniform vector. - let smallM = new DenseMatrix( Array2D.create 2 2 0.3 ) - - /// A large vector with increasingly large entries - let largeM = new DenseMatrix( Array2D.init 100 100 (fun i j -> float i * 100.0 + float j) ) - - specs "DenseMatrix" [ - spec "DenseMatrix.init" - (DenseMatrix.init 100 100 (fun i j -> float i * 100.0 + float j) |> should equal largeM) - spec "DenseMatrix.ofList" - (DenseMatrix.ofList [[0.3;0.3];[0.3;0.3]] |> should equal smallM) - spec "DenseMatrix.ofSeq" - (DenseMatrix.ofSeq (Seq.ofList [[0.3;0.3];[0.3;0.3]]) |> should equal smallM) - spec "DenseMatrix.ofArray2" - (DenseMatrix.ofArray2 (Array2D.create 2 2 0.3) |> should equal smallM) - spec "DenseMatrix.initDense" - (DenseMatrix.initDense 100 100 (seq { for i in 0 .. 99 do - for j in 0 .. 99 -> (i,j, float i * 100.0 + float j)}) |> should equal largeM) - spec "DenseMatrix.constDiag" - (DenseMatrix.constDiag 100 2.0 |> should equal (2.0 * (DenseMatrix.Identity 100))) - spec "DenseMatrix.diag" - (DenseMatrix.diag (new DenseVector(100, 2.0)) |> should equal (2.0 * (DenseMatrix.Identity 100))) - spec "DenseMatrix.init_row" - (DenseMatrix.initRow 100 100 (fun i -> (DenseVector.init 100 (fun j -> float i * 100.0 + float j))) |> should equal largeM) - spec "DenseMatrix.init_col" - (DenseMatrix.initCol 100 100 (fun j -> (DenseVector.init 100 (fun i -> float i * 100.0 + float j))) |> should equal largeM) - ] - - -/// Unit tests for the sparse matrix type. -let SparseMatrixTests = - - /// A small uniform vector. - let smallM = DenseMatrix.init 4 4 (fun i j -> if i = 1 && j = 2 then 1.0 else 0.0) :> Matrix - - specs "SparseMatrix" [ - spec "SparseMatrix.ofList" - ((SparseMatrix.ofList 4 4 [(1,2,1.0)] :> Matrix) |> should equal smallM) - spec "SparseMatrix.ofSeq" - ((SparseMatrix.ofSeq 4 4 (Seq.ofList [(1,2,1.0)]) :> Matrix) |> should equal smallM) - spec "SparseMatrix.constDiag" - (SparseMatrix.constDiag 100 2.0 |> should equal (2.0 * (SparseMatrix.Identity 100))) - spec "SparseMatrix.diag" - (SparseMatrix.diag (new DenseVector(100, 2.0)) |> should equal (2.0 * (SparseMatrix.Identity 100))) - ] - - -/// Report on errors and success and exit. -printfn "F# Test Results:" -printfn "%s" (Results.summary()) - -let code = if Results.erredCount() > 0 || Results.failedCount() > 0 then -1 else 0;; -exit code;; \ No newline at end of file diff --git a/src/FSharpUnitTests/SparseMatrixTests.fs b/src/FSharpUnitTests/SparseMatrixTests.fs new file mode 100644 index 00000000..c572a26e --- /dev/null +++ b/src/FSharpUnitTests/SparseMatrixTests.fs @@ -0,0 +1,27 @@ +namespace MathNet.Numerics.Tests + +open NUnit.Framework +open FsUnit +open MathNet.Numerics.LinearAlgebra.Generic +open MathNet.Numerics.LinearAlgebra.Double + +/// Unit tests for the sparse matrix type. +module SparseMatrixTests = + + /// A small uniform vector. + let smallM = DenseMatrix.init 4 4 (fun i j -> if i = 1 && j = 2 then 1.0 else 0.0) :> Matrix + + [] + let ``SparseMatrix.ofList`` () = + (SparseMatrix.ofList 4 4 [(1,2,1.0)] :> Matrix) |> should equal smallM + + [] + let ``SparseMatrix.ofSeq`` () = + (SparseMatrix.ofSeq 4 4 (Seq.ofList [(1,2,1.0)]) :> Matrix) |> should equal smallM + + [] + let ``SparseMatrix.constDiag`` () = + SparseMatrix.constDiag 100 2.0 |> should equal (2.0 * (SparseMatrix.Identity 100)) + + [] let ``SparseMatrix.diag`` () = + SparseMatrix.diag (new DenseVector(100, 2.0)) |> should equal (2.0 * (SparseMatrix.Identity 100)) diff --git a/src/FSharpUnitTests/SparseVectorTests.fs b/src/FSharpUnitTests/SparseVectorTests.fs new file mode 100644 index 00000000..9526695d --- /dev/null +++ b/src/FSharpUnitTests/SparseVectorTests.fs @@ -0,0 +1,21 @@ +namespace MathNet.Numerics.Tests + +open NUnit.Framework +open FsUnit +open MathNet.Numerics.LinearAlgebra.Generic +open MathNet.Numerics.LinearAlgebra.Double + +/// Unit tests for the sparse vector type. +module SparseVectorTests = + + /// A small uniform vector. + let smallv = new DenseVector( [|0.0;0.3;0.0;0.0;0.0|] ) :> Vector + + [] + let ``SparseVector.ofList`` () = + (SparseVector.ofList 5 [ (1,0.3) ] :> Vector) |> should equal smallv + + [] + let ``SparseVector.ofSeq`` () = + (SparseVector.ofSeq 5 (List.toSeq [ (1,0.3) ]) :> Vector) |> should equal smallv + diff --git a/src/FSharpUnitTests/VectorTests.fs b/src/FSharpUnitTests/VectorTests.fs new file mode 100644 index 00000000..b0f0294b --- /dev/null +++ b/src/FSharpUnitTests/VectorTests.fs @@ -0,0 +1,103 @@ +namespace MathNet.Numerics.Tests + +open NUnit.Framework +open FsUnit +open MathNet.Numerics.LinearAlgebra.Generic +open MathNet.Numerics.LinearAlgebra.Double + +/// Unit tests for the vector type. +module VectorTests = + + /// A small uniform vector. + let smallv = new DenseVector([|0.3;0.3;0.3;0.3;0.3|]) :> Vector + + /// A large vector with increasingly large entries + let largev = new DenseVector(Array.init 100 (fun i -> float i / 100.0)) :> Vector + + [] + let ``Vector.toArray`` () = + Vector.toArray smallv |> should array_equal [|0.3;0.3;0.3;0.3;0.3|] + + [] + let ``Vector.toList`` () = + Vector.toList smallv |> should equal [0.3;0.3;0.3;0.3;0.3] + + [] + let ``Vector.mapInPlace`` () = + let w = smallv.Clone() + Vector.mapInPlace (fun x -> 2.0 * x) w + w |> should equal (2.0 * smallv) + + [] + let ``Vector.mapiInPlace`` () = + let w = largev.Clone() + Vector.mapiInPlace (fun i x -> float i / 100.0) w + w |> should equal (largev) + + [] + let ``Vector.addInPlace`` () = + let w = largev.Clone() + Vector.addInPlace w largev + w |> should equal (2.0 * largev) + + [] + let ``Vector.subInPlace`` () = + let w = largev.Clone() + Vector.subInPlace w largev + w |> should equal (0.0 * largev) + + [] + let ``Vector.map`` () = + Vector.map (fun x -> 2.0 * x) largev |> should equal (2.0 * largev) + + [] + let ``Vector.mapi`` () = + Vector.mapi (fun i x -> float i / 100.0) largev |> should equal largev + + [] + let ``Vector.fold`` () = + Vector.fold (fun a b -> a - b) 0.0 smallv |> should equal -1.5 + + [] + let ``Vector.foldBack`` () = + Vector.foldBack (fun a b -> a - b) 0.0 smallv |> should equal 0.0 + + [] + let ``Vector.foldi`` () = + Vector.foldi (fun i a b -> a + b) 0.0 smallv |> should equal 1.5 + + [] + let ``Vector.forall`` () = + Vector.forall (fun x -> x = 0.3) smallv |> should equal true + + [] + let ``Vector.exists`` () = + Vector.exists (fun x -> x = 0.3) smallv |> should equal true + + [] + let ``Vector.foralli`` () = + Vector.foralli (fun i x -> x = 0.3 && i < 5) smallv |> should equal true + + [] + let ``Vector.existsi`` () = + Vector.existsi (fun i x -> x = 0.3 && i = 2) smallv |> should equal true + + [] + let ``Vector.scan`` () = + Vector.scan (fun acc x -> acc + x) smallv |> should (approximately_vector_equal 14) (new DenseVector( [|0.3;0.6;0.9;1.2;1.5|] ) :> Vector) + + [] + let ``Vector.scanBack`` () = + Vector.scanBack (fun x acc -> acc + x) smallv |> should (approximately_vector_equal 14) (new DenseVector( [|1.5;1.2;0.9;0.6;0.3|] ) :> Vector) + + [] + let ``Vector.reduce`` () = + Vector.reduce (fun acc x -> acc ** x) smallv |> should (approximately_equal 14) 0.990295218585507 + + [] + let ``Vector.reduceBack`` () = + Vector.reduceBack (fun x acc -> x ** acc) smallv |> should (approximately_equal 14) 0.488911287726319 + + [] + let ``Vector.insert`` () = + Vector.insert 2 0.5 smallv |> should (approximately_vector_equal 14) (new DenseVector ( [|0.3;0.3;0.5;0.3;0.3;0.3|] ) :> Vector) From 0aa78338f94579367d13d5c651836c810c7aee4d Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Sat, 1 Dec 2012 14:56:40 +0000 Subject: [PATCH 06/15] Make RandomVariable more idiomatic and move it out of the global Math.Numerics namespace --- src/FSharp/FSharp.fsproj | 2 +- ...ndomVariableMonad.fs => RandomVariable.fs} | 87 +++++++++---------- src/FSharpPortable/FSharpPortable.fsproj | 4 +- src/FSharpUnitTests/PokerTests.fs | 58 +++++++------ src/FSharpUnitTests/RandomVariableTests.fs | 38 ++++---- 5 files changed, 94 insertions(+), 95 deletions(-) rename src/FSharp/{RandomVariableMonad.fs => RandomVariable.fs} (50%) diff --git a/src/FSharp/FSharp.fsproj b/src/FSharp/FSharp.fsproj index b1b485eb..7711a025 100644 --- a/src/FSharp/FSharp.fsproj +++ b/src/FSharp/FSharp.fsproj @@ -61,7 +61,7 @@ - + diff --git a/src/FSharp/RandomVariableMonad.fs b/src/FSharp/RandomVariable.fs similarity index 50% rename from src/FSharp/RandomVariableMonad.fs rename to src/FSharp/RandomVariable.fs index da2842ec..9bbd1e09 100644 --- a/src/FSharp/RandomVariableMonad.fs +++ b/src/FSharp/RandomVariable.fs @@ -1,56 +1,57 @@ -namespace MathNet.Numerics +module MathNet.Numerics.Probability #nowarn "40" open System open System.Collections open System.Collections.Generic +open MathNet.Numerics -module RandomVariable = - - type 'a Outcome = { - Value: 'a - Probability : BigRational } - - type 'a RandomVariable = 'a Outcome seq - - // P(A AND B) = P(A | B) * P(B) - let bind (f: 'a -> 'b RandomVariable) (dist:'a RandomVariable) = - dist - |> Seq.map (fun p1 -> - f p1.Value - |> Seq.map (fun p2 -> - { Value = p2.Value; - Probability = - p1.Probability * p2.Probability})) - |> Seq.concat : 'b RandomVariable - - /// 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 RandomVariable type - let returnM (value:'a) = - Seq.singleton { Value = value ; Probability = 1N/1N } - : 'a RandomVariable - - type RandomVariableMonadBuilder() = - member this.Bind (r, f) = bind f r - member this.Return x = returnM x - member this.ReturnFrom x = x - - let randomVariable = RandomVariableMonadBuilder() - +type Outcome<'T> = { + Value: 'T + Probability : BigRational } + +type RandomVariable<'T> = Outcome<'T> seq + +// P(A AND B) = P(A | B) * P(B) +let private bind f dist = + dist + |> Seq.map (fun p1 -> + f p1.Value + |> Seq.map (fun p2 -> + { Value = p2.Value; + Probability = + p1.Probability * p2.Probability})) + |> Seq.concat + +/// Inject a value into the RandomVariable type +let private returnM value = + Seq.singleton { Value = value ; Probability = 1N/1N } + +type RandomVariableBuilder() = + member this.Bind (r, f) = bind f r + member this.Return x = returnM x + member this.ReturnFrom x = x + +let randomVariable = RandomVariableBuilder() + +type CoinSide = + | Heads + | Tails + +[] +[] +module RandomVariable = + // Create some helpers - let toUniformDistribution seq : 'a RandomVariable = + let toUniformDistribution seq = let l = Seq.length seq seq |> Seq.map (fun e -> { Value = e; Probability = 1N / bignum.FromInt l }) - let probability (dist:'a RandomVariable) = + let probability dist = dist |> Seq.map (fun o -> o.Probability) |> Seq.sum @@ -60,13 +61,9 @@ module RandomVariable = let fairDice sides = toUniformDistribution [1..sides] - type CoinSide = - | Heads - | Tails - let fairCoin = toUniformDistribution [Heads; Tails] - let filter predicate (dist:'a RandomVariable) : 'a RandomVariable = + let filter predicate dist = dist |> Seq.filter (fun o -> predicate o.Value) let filterInAnyOrder items dist = @@ -74,7 +71,7 @@ module RandomVariable = |> Seq.fold (fun d item -> filter (Seq.exists ((=) (item))) d) dist /// Transforms a RandomVariable value by using a specified mapping function. - let map f (dist:'a RandomVariable) : 'b RandomVariable = + let map f dist = dist |> Seq.map (fun o -> { Value = f o.Value; Probability = o.Probability }) diff --git a/src/FSharpPortable/FSharpPortable.fsproj b/src/FSharpPortable/FSharpPortable.fsproj index 481ad7d9..63aaba14 100644 --- a/src/FSharpPortable/FSharpPortable.fsproj +++ b/src/FSharpPortable/FSharpPortable.fsproj @@ -78,8 +78,8 @@ q.fs - - RandomVariableMonad.fs + + RandomVariable.fs diff --git a/src/FSharpUnitTests/PokerTests.fs b/src/FSharpUnitTests/PokerTests.fs index 4276d06b..48382db9 100644 --- a/src/FSharpUnitTests/PokerTests.fs +++ b/src/FSharpUnitTests/PokerTests.fs @@ -1,7 +1,7 @@ module MathNet.Numerics.Tests.PokerTests open MathNet.Numerics -open MathNet.Numerics.RandomVariable +open MathNet.Numerics.Probability open NUnit.Framework open FsUnit @@ -30,67 +30,69 @@ let isConnected c1 c2 = [] 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 + |> RandomVariable.selectOne + |> RandomVariable.map fst + |> RandomVariable.filter (fun card -> value card = A) + |> RandomVariable.probability |> should equal (4N/52N) [] let ``When drawing from a full deck, then the probability should equal 1/52``() = completeDeck - |> selectOne |> map fst - |> filter ((=) (A,Spades)) - |> probability + |> RandomVariable.selectOne + |> RandomVariable.map fst + |> RandomVariable.filter ((=) (A,Spades)) + |> RandomVariable.probability |> should equal (1N/52N) [] 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 + |> RandomVariable.select 2 + |> RandomVariable.filter ((=) [A,Clubs; A,Spades]) + |> RandomVariable.probability |> should equal (1N/52N * 1N/51N) [] 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 + |> RandomVariable.select 2 + |> RandomVariable.filterInAnyOrder [A,Clubs; A,Spades] + |> RandomVariable.probability |> should equal ((1N/52N * 1N/51N) * 2N) [] 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 + |> RandomVariable.remove [A,Clubs; A,Spades] + |> RandomVariable.toUniformDistribution + |> RandomVariable.filter (fun card -> value card = A) + |> RandomVariable.probability |> should equal (2N/50N) [] 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 + |> RandomVariable.select 2 + |> RandomVariable.filter (fun (c1::c2::_) -> isPair c1 c2) + |> RandomVariable.probability |> should equal (1N/17N) [] 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 + |> RandomVariable.select 2 + |> RandomVariable.filter (fun (c1::c2::_) -> isSuited c1 c2 && isConnected c1 c2) + |> RandomVariable.probability |> should equal (2N/51N) [] 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 + |> RandomVariable.remove [A,Clubs; A,Spades] // preflop + |> RandomVariable.remove [2,Clubs; 3,Spades; 7,Spades] // flop + |> RandomVariable.select 2 + |> RandomVariable.filter (fun (c1::c2::_) -> suit c1 = Spades && suit c2 = Spades) + |> RandomVariable.probability |> should equal (10N/47N*9N/46N) \ No newline at end of file diff --git a/src/FSharpUnitTests/RandomVariableTests.fs b/src/FSharpUnitTests/RandomVariableTests.fs index e9a80f20..93305aab 100644 --- a/src/FSharpUnitTests/RandomVariableTests.fs +++ b/src/FSharpUnitTests/RandomVariableTests.fs @@ -1,44 +1,44 @@ module MathNet.Numerics.Tests.RandomVariableTests open MathNet.Numerics -open MathNet.Numerics.RandomVariable +open MathNet.Numerics.Probability open NUnit.Framework open FsUnit [] let ``When creating a empty randomVariable, then the probability should be 1``() = let actual = randomVariable { return () } - probability actual |> should equal (1N/1N) + RandomVariable.probability actual |> should equal (1N/1N) let sumOfTwoFairDices = randomVariable { - let! d1 = fairDice 6 - let! d2 = fairDice 6 + let! d1 = RandomVariable.fairDice 6 + let! d2 = RandomVariable.fairDice 6 return d1 + d2 } [] let ``When creating two fair dices, then P(Sum of 2 dices = 7) should be 1/6``() = sumOfTwoFairDices - |> filter ((=) 7) - |> probability + |> RandomVariable.filter ((=) 7) + |> RandomVariable.probability |> should equal (1N/6N) let fairCoinAndDice = randomVariable { - let! d = fairDice 6 - let! c = fairCoin + let! d = RandomVariable.fairDice 6 + let! c = RandomVariable.fairCoin return d,c } [] let ``When creating a fair coin and a fair dice, then P(Heads) should be 1/2``() = fairCoinAndDice - |> filter (fun (_,c) -> c = Heads) - |> probability + |> RandomVariable.filter (fun (_,c) -> c = Heads) + |> RandomVariable.probability |> should equal (1N/2N) [] 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 + |> RandomVariable.filter (fun (d,c) -> c = Heads && d > 3) + |> RandomVariable.probability |> should equal (1N/4N) // MontyHall Problem @@ -49,22 +49,22 @@ type Outcome = | Car | Goat -let firstChoice = toUniformDistribution [Car; Goat; Goat] +let firstChoice = RandomVariable.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 + RandomVariable.certainly Goat | Goat -> // If you had the goat, the host has to take out another goat ==> you win - certainly Car + RandomVariable.certainly Car [] let ``When making the first choice in a MontyHall situation, the chances to win should be 1/3``() = firstChoice - |> filter ((=) Car) - |> probability + |> RandomVariable.filter ((=) Car) + |> RandomVariable.probability |> should equal (1N/3N) let montyHallWithSwitch = randomVariable { @@ -74,6 +74,6 @@ let montyHallWithSwitch = randomVariable { [] let ``When switching in a MontyHall situation, the chances to win should be 2/3``() = montyHallWithSwitch - |> filter ((=) Car) - |> probability + |> RandomVariable.filter ((=) Car) + |> RandomVariable.probability |> should equal (2N/3N) \ No newline at end of file From d44ac6f7a2a9f6ec36096fa3be599d4a07a29838 Mon Sep 17 00:00:00 2001 From: Gustavo Guerra Date: Sat, 1 Dec 2012 15:45:50 +0000 Subject: [PATCH 07/15] Replace full BigInteger implementation with extension to add the Parse method in Portable version --- src/FSharpPortable/BigIntegerExtensions.fs | 30 + src/FSharpPortable/FSharpPortable.fsproj | 5 +- src/FSharpPortable/n.fs | 1611 -------------------- src/FSharpPortable/n.fsi | 71 - src/FSharpPortable/z.fs | 411 ----- src/FSharpPortable/z.fsi | 124 -- 6 files changed, 31 insertions(+), 2221 deletions(-) create mode 100644 src/FSharpPortable/BigIntegerExtensions.fs delete mode 100644 src/FSharpPortable/n.fs delete mode 100644 src/FSharpPortable/n.fsi delete mode 100644 src/FSharpPortable/z.fs delete mode 100644 src/FSharpPortable/z.fsi diff --git a/src/FSharpPortable/BigIntegerExtensions.fs b/src/FSharpPortable/BigIntegerExtensions.fs new file mode 100644 index 00000000..706a3567 --- /dev/null +++ b/src/FSharpPortable/BigIntegerExtensions.fs @@ -0,0 +1,30 @@ +namespace System.Numerics + +open System + +[] +module BigIntegerExtensions = + + let private parse str = + let len = String.length str + let rec build acc i = + if i = len then + acc + else + let c = str.[i] + let d = int c - int '0' + if 0 <= d && d <= 9 then + build (10I * acc + (bigint d)) (i+1) + else + raise (new FormatException("The value could not be parsed")) + build 0I 0 + + type BigInteger with + + static member Parse(text: string) = + let len = text.Length + if len = 0 then raise (new FormatException("The value could not be parsed")) + if text.[0..0] = "-" then + parse text.[1..len-1] |> bigint.Negate + else + parse text diff --git a/src/FSharpPortable/FSharpPortable.fsproj b/src/FSharpPortable/FSharpPortable.fsproj index 63aaba14..f6c88e25 100644 --- a/src/FSharpPortable/FSharpPortable.fsproj +++ b/src/FSharpPortable/FSharpPortable.fsproj @@ -62,10 +62,7 @@ LinearAlgebra.Double.fs - - - - + complex.fsi diff --git a/src/FSharpPortable/n.fs b/src/FSharpPortable/n.fs deleted file mode 100644 index a5c97799..00000000 --- a/src/FSharpPortable/n.fs +++ /dev/null @@ -1,1611 +0,0 @@ -// First version copied from the F# compiler sources -// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/n.fs - -//---------------------------------------------------------------------------- -// Copyright (c) 2002-2012 Microsoft Corporation. -// -// This source code is subject to terms and conditions of the Apache License, Version 2.0. A -// copy of the license can be found in the License.html file at the root of this distribution. -// By using this source code in any fashion, you are agreeing to be bound -// by the terms of the Apache License, Version 2.0. -// -// You must not remove this notice, or any other, from this software. -//---------------------------------------------------------------------------- - -namespace Microsoft.FSharp.Math - -#if FX_NO_BIGINT -open System -open System.Diagnostics.CodeAnalysis -open Microsoft.FSharp.Core -open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators -open Microsoft.FSharp.Core.Operators -open Microsoft.FSharp.Collections -open Microsoft.FSharp.Primitives.Basics - -type ints = int array - -[] -type internal BigNat = - - // Have n = sum (from i=0 to bound) a.[i] * baseN ^ i - // Have 0 <= a.[i] < baseN. - //------ - // Invariant: bound is least such, i.e. bound=0 or (a.[bound-1] is highest coeff). - // Zero is {bound=0,a=...}. - // Naturals are a normal form, - // but not structurally so, - // since arrays may have non-contributing cells at a.[bound] and beyond. - // - { mutable bound : int; // non-zero coeff must be 0...(bound-1) - digits : ints // must have at least elts 0...(bound-1), - // maybe more (which should be zero!). - // Actually, the "zero" condition may be relaxed. - // - } - - -module internal BigNatModule = - - //------------------------------------------------------------------------- - // misc - //----------------------------------------------------------------------- - - #if SELFTEST - let check b = if not b then failwith "assertion failwith" - #endif - - module FFT = - let rec pow32 x n = - if n=0 then 1 - elif n % 2 = 0 then pow32 (x*x) (n / 2) - else x* pow32 (x*x) (n / 2) - - let leastBounding2Power b = - let rec findBounding2Power b tp i = if b<=tp then tp,i else findBounding2Power b (tp*2) (i+1) in - findBounding2Power b 1 0 - - //------------------------------------------------------------------------- - // p = 2^k.m + 1 prime and w primitive 2^k root of 1 mod p - //----------------------------------------------------------------------- - - // Given p = 2^k.m + 1 prime and w a primitive 2^k root of unity (mod p). - // Required to define arithmetic ops for Fp = field modulo p. - // The following are possible choices for p. - - // p, k, m, g, w - // let p,k,m,g,w = 97L, 4, 6, 5, 8 // p is 7 bit - // let p,k,m,g,w = 769L, 8, 3, 7, 7 // p is 10 bit - // let p,k,m,g,w = 7681L, 8, 30, 13, 198 // p is 13 bit - // let p,k,m,g,w = 12289L, 10, 12, 11, 49 // p is 14 bit - // let p,k,m,g,w = 167772161L, 25, 5, 557092, 39162105 // p is 28 bit - // let p,k,m,g,w = 469762049L, 26, 7, 1226571, 288772249 // p is 29 bit - - - let p,k,m,g,w = 2013265921L, 27, 15, 31, 440564289 // p is 31 bit - let primeP = p - - let maxBitsInsideFp = 30 - - - //------------------------------------------------------------------------- - // Fp = finite field mod p - rep is uint32 - //----------------------------------------------------------------------- - - - type fp = uint32 - // operations in Fp (finite field size p) - module Fp = - //module I = UInt32 - let p = 2013265921ul : fp - let p64 = 2013265921UL : uint64 - let toInt (x:fp) : int = int32 x - let ofInt32 (x:int) : fp = uint32 x - - let mzero : fp = 0ul - let mone : fp = 1ul - let mtwo : fp = 2ul - let inline madd (x:fp) (y:fp) : fp = (x + y) % p - let inline msub (x:fp) (y:fp) : fp = (x + p - y) % p - let inline mmul (x:fp) (y:fp) : fp = uint32 ((uint64 x * uint64 y) % p64) - - let rec mpow x n = - if n=0 then mone - elif n % 2=0 then mpow (mmul x x) (n / 2) - else mmul x (mpow (mmul x x) (n / 2)) - - let rec mpowL x n = - if n = 0L then mone - elif n % 2L = 0L then mpowL (mmul x x) (n / 2L) - else mmul x (mpowL (mmul x x) (n / 2L)) - - // Have the w is primitive 2^kth root of 1 in Zp - let m2PowNthRoot n = - // Find x s.t. x is (2^n)th root of unity. - // - // pow w (pow 2 k) = 1 primitively. - // = pow w (pow 2 ((k-n)+n)) - // = pow w (pow 2 (k-n) * pow 2 n) - // = pow (pow w (pow 2 (k-n))) (pow 2 n) - // - // Take wn = pow (pow w (pow 2 (k-n))) - - mpow (uint32 w) (pow32 2 (k-n)) - - let minv x = mpowL x (primeP - 2L) - - - //------------------------------------------------------------------------- - // FFT - in place low garbage - //----------------------------------------------------------------------- - - open Fp - let rec computeFFT lambda mu n w (u: _[]) (res: _[]) offset = - // Given n a 2-power, - // w an nth root of 1 in Fp, and - // lambda, mu and u(x) defining - // poly(lambda,mu,x) = sum(i pow32 2 i) - - let computeFftPaddedPolynomialProduct bigK k u v = - // REQUIRES: bigK = 2^k - // REQUIRES: Array lengths of u and v = bigK. - // REQUIRES: degree(uv) <= bigK-1 - // --- - // Given u,v polynomials. - // Computes the product polynomial by FFT. - // For correctness, - // require the result coeff to be in range [0,p-1], for p defining Fp above. - - #if SELFTEST - check ( k <= maxTwoPower ); - check ( bigK = twoPowerTable.[k] ); - check ( u.Length = bigK ); - check ( v.Length = bigK ); - #endif - // Find 2^k primitive root of 1 - let w = m2PowNthRoot k - // FFT - let n = bigK - let uT = computFftInPlace n w u - let vT = computFftInPlace n w v - // Evaluate - let rT = Array.init n (fun i -> mmul uT.[i] vT.[i]) - // INV FFT - let r = computeInverseFftInPlace n w rT - r - - let padTo n (u: _ array) = - let uBound = u.Length - Array.init n (fun i -> if i mmul uT.[i] vT.[i]) - // INV FFT - let r = computeInverseFftInPlace n w rT - Array.map Fp.toInt r - - - //------------------------------------------------------------------------- - // fp exports - //----------------------------------------------------------------------- - - open Fp - let mzero = mzero - let mone = mone - let maxFp = msub Fp.p mone - - //------------------------------------------------------------------------- - // FFT - reference implementation - //----------------------------------------------------------------------- - - #if SELFTEST - open Fp - let rec computeFftReference n w u = - // Given n a 2-power, - // w an nth root of 1 in Fp, and - // u(x) = sum(i u.[2*i]) - let uodd = Array.init (n/2) (fun i -> u.[2*i+1]) - let uevenFT = computeFftReference (n/2) (mmul w w) ueven - let uoddFT = computeFftReference (n/2) (mmul w w) uodd - Array.init n - (fun j -> - if j < n/2 then - madd - (uevenFT.[j]) - (mmul - (mpow w j) - (uoddFT.[j])) - else - let j = j - (n/2) - msub - (uevenFT.[j]) - (mmul - (mpow w j) - (uoddFT.[j]))) - #endif - - open FFT - - type n = BigNat - - let bound (n: n) = n.bound - let setBound (n: n) (v:int32) = n.bound <- v - let coeff (n:n) i = n.digits.[i] - let coeff64 (n:n) i = int64 (coeff n i) - let setCoeff (n:n) i v = n.digits.[i] <- v - - let rec pow64 x n = - if n=0 then 1L - elif n % 2 = 0 then pow64 (x * x) (n / 2) - else x * (pow64 (x * x) (n / 2)) - - let rec pow32 x n = - if n=0 then 1 - elif n % 2 = 0 then pow32 (x*x) (n / 2) - else x* pow32 (x*x) (n / 2) - - let hash(n) = - let mutable res = 0 - for i = 0 to n.bound - 1 do // could stop soon, it's "hash" - res <- n.digits.[i] + (res <<< 3) - done; - res - - //---------------------------------------------------------------------------- - // misc - //-------------------------------------------------------------------------- - -#if CHECKED - let check b str = if not b then failwith ("check failed: " + str) -#endif - let maxInt a b = if a int32 - let inline div64base (x:int64) = int64 (uint64 x >>> baseBits) - - let divbase x = int32 (uint32 x >>> baseBits) - let modbase x = (x &&& baseMask) - - let inline index z i = if i < z.bound then z.digits.[i] else 0 - - let createN b = { bound = b; - digits = Array.zeroCreate b } - let copyN x = { bound = x.bound; - digits = Array.copy x.digits } // could copy just enough... - - let normN n = - // normalises bound - let rec findLeastBound (na:ints) i = if i = -1 || na.[i]<>0 then i+1 else findLeastBound na (i-1) - let bound = findLeastBound n.digits (n.bound-1) - n.bound <- bound; - n - - let boundInt = 2 // int will fit with bound=2 - let boundInt64 = 3 // int64 will fit with bound=3 - let boundBase = 1 // base will fit with bound=1 - obviously! - -//---------------------------------------------------------------------------- -// base, coefficients, poly -//-------------------------------------------------------------------------- - - let embed x = - let x = if x<0 then 0 else x // no -ve naturals - if x < baseN then - let r = createN 1 - r.digits.[0] <- x; - normN r - else - let r = createN boundInt - for i = 0 to boundInt - 1 do - r.digits.[i] <- (x / pow32 baseN i) % baseN - done; - normN r - - let embed64 x = - let x = if x<0L then 0L else x // no -ve naturals - let r = createN boundInt64 - for i = 0 to boundInt64-1 do - r.digits.[i] <- int32 ( (x / pow64 baseNi64 i) % baseNi64) - done; - normN r - - let eval n = - if n.bound = 1 - then n.digits.[0] - else - let mutable acc = 0 - for i = n.bound-1 downto 0 do - acc <- n.digits.[i] + baseN * acc - done; - acc - - let eval64 n = - if n.bound = 1 - then int64 n.digits.[0] - else - let mutable acc = 0L - for i = n.bound-1 downto 0 do - acc <- int64 (n.digits.[i]) + baseNi64 * acc - done; - acc - - let one = embed 1 - let zero = embed 0 - - let restrictTo d n = - { bound = minInt d n.bound; digits = n.digits} - - let shiftUp d n = - let m = createN (n.bound+d) - for i = 0 to n.bound-1 do - m.digits.[i+d] <- n.digits.[i] - done; - m - - let shiftDown d n = - if n.bound-d<=0 then - zero - else - let m = createN (n.bound-d) - for i = 0 to m.bound-1 do - m.digits.[i] <- n.digits.[i+d] - done; - m - - let degree n = n.bound-1 - - -//---------------------------------------------------------------------------- -// add, sub -//-------------------------------------------------------------------------- - - // addition - let rec addP i n c p q r = // p+q + c - if i0 then - r.digits.[i] <- modbase x; - let c = divbase x - // if p (or q) exhausted and c zero could switch to copying mode - subP (i+1) n c p q r - else - let x = x + baseN // add baseN - r.digits.[i] <- modbase x; - let c = divbase x - 1 // sub baseN - // if p (or q) exhausted and c zero could switch to copying mode - subP (i+1) n c p q r - else - let underflow = c<>0 - underflow - - let sub p q = - // NOTE: x-y=0 when x<=y, it is natural subtraction - let rbound = maxInt p.bound q.bound - let r = createN rbound - let carry = 0 - let underflow = subP 0 rbound carry p q r - if underflow then - embed 0 - else - normN r - - -//---------------------------------------------------------------------------- -// isZero, equal, ordering, sign, min, max -//-------------------------------------------------------------------------- - - let isZero p = p.bound=0 - let IsZero p = isZero p - let isOne p = p.bound=1 && p.digits.[0] = 1 - - let equal p q = - (p.bound = q.bound) && - (let rec check (pa:ints) (qa:ints) i = - // HAVE: pa.[j] = qa.[j] for i < j < p.bound - (i = -1) || (pa.[i]=qa.[i] && check pa qa (i-1)) - - check p.digits q.digits (p.bound-1)) - - let shiftCompare p pn q qn = - if p.bound + pn < q.bound + qn then -1 - elif p.bound + pn > q.bound + pn then 1 - else - let rec check (pa:ints) (qa:ints) i = - // HAVE: pa.[j-pn] = qa.[j-qn] for i < j < p.bound - // Looking for most significant differing coeffs to determine ordering - if i = -1 then - 0 - else - let pai = if i < pn then 0 else pa.[i-pn] - let qai = if i < qn then 0 else qa.[i-qn] - if pai = qai then check pa qa (i-1) - elif pai < qai then -1 - else 1 - - check p.digits q.digits (p.bound + pn - 1) - - let compare p q = - if p.bound < q.bound then -1 - elif p.bound > q.bound then 1 - else - let rec check (pa:ints) (qa:ints) i = - // HAVE: pa.[j] = qa.[j] for i < j < p.bound - // Looking for most significant differing coeffs to determine ordering - if i = -1 then 0 - elif pa.[i]=qa.[i] then check pa qa (i-1) - elif pa.[i]] - let lt p q = compare p q = -1 - [] - let gt p q = compare p q = 1 - [] - let lte p q = compare p q <> 1 - [] - let gte p q = compare p q <> -1 - - [] - let min a b = if lt a b then a else b - [] - let max a b = if lt a b then b else a - - -//---------------------------------------------------------------------------- -// scale -//-------------------------------------------------------------------------- - - // REQUIRE: baseN + baseN.2^32 < Int64.maxInt - let rec contributeArr (a:ints) i (c:int64) = - // Given c and require c < baseN.2^32 - // Compute: r <- r + c . B^i - // via r.digits.[i] <- r.digits.[i] + c and normalised - let x = int64 a.[i] + c - // HAVE: x < baseN + baseN.2^32 - let c = div64base x - let x = mod64base x - // HAVE: c < 1 + 2^32 < baseN.2^32, recursive call ok - // HAVE: x < baseN - a.[i] <- x; // store residue x - if c>0L then - contributeArr a (i+1) c // contribute carry next position - - let inline contribute r i c = contributeArr r.digits i c - - // REQUIRE: maxInt < 2^32 - [] - let rec scale (k:int) (p:n) = - // Given k and p and require k < 2^32 - // Computes "scalar" product k.p - // - let rbound = p.bound + boundInt - let r = createN rbound - let k = int64 k - for i = 0 to p.bound-1 do - let kpi = k * int64 p.digits.[i] - // HAVE: kpi < 2^32 * baseN which meets "contribute" requirement - contribute r i kpi - done; - normN r - - -//---------------------------------------------------------------------------- -// mulSchoolBook -//-------------------------------------------------------------------------- - - // multiplication: naively O(n^2) -(* - let mulSchoolBook' p q = - let rbound = p.bound + q.bound + boundBase*2 - let r = createN rbound - let pa = p.digits - let qa = q.digits - for i = 0 to p.bound-1 do - for j = 0 to q.bound-1 do - contribute r (i+j) (int64 pa.[i] * int64 qa.[j]) - done - done; - normN r -*) - - let mulSchoolBookBothSmall p q = - let r = createN 2 - let rak = int64 p * int64 q - setCoeff r 0 (mod64base rak); - setCoeff r 1 (int32 (div64base rak)) - normN r - - let rec mulSchoolBookCarry r c k = - if ( c > 0L ) then - // ToAdd = c.B^k - let rak = (coeff64 r k) + c - setCoeff r k (mod64base rak); - mulSchoolBookCarry r (div64base rak) (k + 1) - - let mulSchoolBookOneSmall p q = - let bp = bound(p) - let rbound = bp + 1 - let r = createN rbound - let q = int64 q - let mutable c = 0L - for i = 0 to bp-1 do - let rak = c + (coeff64 r i) + (coeff64 p i) * q - setCoeff r i (mod64base rak); - c <- div64base rak; - mulSchoolBookCarry r c bp - normN r - - - // multiplication: naively O(n^2) -- this version - unchecked - is faster - let mulSchoolBookNeitherSmall p q = - let rbound = p.bound + q.bound - let r = createN rbound - let ra = r.digits - let pa = p.digits - let qa = q.digits - // ToAdd p*q - for i = 0 to p.bound-1 do - // ToAdd p.[i] * q * B^i - let pai = int64 pa.[i] - let mutable c = 0L - let mutable k = i // k = i + j - // ToAdd = pi.qj.B^(i+j) for j = 0,j+1... - for j = 0 to q.bound-1 do - // ToAdd = c.B^k + pi.qj.B^(i+j) for j = j,j+1... and k = i+j - let qaj = int64 qa.[j] - let rak = int64 ra.[k] + c + pai * qaj - ra.[k] <- int32 (mod64base rak); - c <- div64base rak; - k <- k + 1; - mulSchoolBookCarry r c k - normN r - - let mulSchoolBook p q = - let pSmall = (bound(p) = 1) - let qSmall = (bound(q) = 1) - if (pSmall && qSmall) then mulSchoolBookBothSmall (coeff p 0) (coeff q 0) - elif pSmall then mulSchoolBookOneSmall q (coeff p 0) - elif qSmall then mulSchoolBookOneSmall p (coeff q 0) - else mulSchoolBookNeitherSmall p q - - -//---------------------------------------------------------------------------- -// quickMulUsingFft -//-------------------------------------------------------------------------- - - // The FFT polynomial multiplier requires the result coeffs fit inside Fp. - // - // OVERVIEW: - // The numbers are recoded as polynomials to be evaluated at (x=2^bigL). - // The polynomials are FFT multiplied, requiring result coeff to fit Fp. - // The result product is recovered by evaluating the poly at (x=2^bigL). - // - // REF: - // QuickMul: Practical FFT-base Integer Multiplication, - // Chee Yap and Chen Yi. - // - // There is choice of how to encode the nats polynomials. - // The choice is the (2^bigL) base to use. - // For bigL=1, the FFT will cater for a product of upto 256M bits. - // Larger bigL have less reach, but compute faster. - // So plan to choose bigL depending on the number of bits product. - // - // DETERMINING THE K,L BOUNDS. - // - // Given representing using K-vectors, K a power of 2, K=2^k, and - // If choosing inputs to have L-bit coefficients. - // - // The result coeff are: - // - // res(i) = sum (j] - type encoding = - { bigL : int; // bits per input coeff - twoToBigL : int; // 2^bigL - k : int; - bigK : int; // bigK = 2^k, number of terms polynomials - bigN : int; // bits result (under-estimate of limit) - split : int; // baseBits / bigL - splits : int array; - } - -#if CHECKED - let _ = check (baseBits=24) "24bit" -#endif - // Requiring baseN mod 2^bigL = 0 gave quick encoding, but... - // also a terrible drop performance when the bigK jumped by more than needed! - // Below, it choose a minimal bigK to hold the product. - - let mkEncoding (bigL,k,bigK,bigN) = -#if CHECKED - check (bigK = pow32 2 k) "bigK"; - check (bigN = bigK * bigL) "bigN"; - check (2 * bigL + k <= maxBitsInsideFp) "constraint"; -#endif - { bigL = bigL; - twoToBigL = pow32 2 bigL; - k = k; - bigK = bigK; - bigN = bigN; - split = baseBits/bigL; // should divide exactly - splits = Array.init (baseBits/bigL) (fun i -> pow32 2 (bigL*i)) - } - - let table = - [| // bigL , k , bigK , bigN // - mkEncoding ( 1 , 28 , 268435456 , 268435456 ) ; - mkEncoding ( 2 , 26 , 67108864 , 134217728 ) ; - mkEncoding ( 3 , 24 , 16777216 , 50331648 ) ; - mkEncoding ( 4 , 22 , 4194304 , 16777216 ) ; - mkEncoding ( 5 , 20 , 1048576 , 5242880 ) ; - mkEncoding ( 6 , 18 , 262144 , 1572864 ) ; - mkEncoding ( 7 , 16 , 65536 , 458752 ) ; - mkEncoding ( 8 , 14 , 16384 , 131072 ) ; - mkEncoding ( 9 , 12 , 4096 , 36864 ) ; - mkEncoding ( 10 , 10 , 1024 , 10240 ) ; - mkEncoding ( 11 , 8 , 256 , 2816 ) ; - mkEncoding ( 12 , 6 , 64 , 768 ) ; - mkEncoding ( 13 , 4 , 16 , 208 ) ; - |] - - let calculateTableTow bigL = - // Given L. - // Have L via "log2 K <= maxBitsInsideFp - 2L". - // Have N via "N = K.L" - // - let k = maxBitsInsideFp - 2*bigL - let bigK = pow64 2L k - let N = bigK * int64 bigL - bigL,k,bigK,N - - let encodingGivenResultBits bitsRes = - // choose maximum bigL s.t. bitsRes < bigN - // EXCEPTION: fails is bitsRes exceeds 2^28 (largest bigN table) - let rec selectFrom i = - if i+1 < table.Length && bitsRes < table.[i+1].bigN then - selectFrom (i+1) - else - table.[i] - - if bitsRes >= table.[0].bigN then - failwith "Product is huge, around 268435456 bits, beyond quickmul" - else - selectFrom 0 - - let bitmask = Array.init baseBits (fun i -> (pow32 2 i - 1)) - let twopowers = Array.init baseBits (fun i -> (pow32 2 i)) - let twopowersI64 = Array.init baseBits (fun i -> (pow64 2L i)) - // bitmask(k) = 2^k - 1 - // twopowers(k) = 2^k // - - let wordBits word = - let rec hi k = - if k=0 then 0 - elif (word &&& twopowers.[k-1]) <> 0 then k - else hi (k-1) - - hi baseBits - - let bits u = - if u.bound=0 then 0 - else degree u * baseBits + wordBits u.digits.[degree u] - - let extractBits n enc bi = - let bj = bi + enc.bigL - 1 // the last bit (inclusive) - let biw = bi / baseBits // first bit is this index pos - let bjw = bj / baseBits // last bit is this index pos - if biw <> bjw then - // two words - let x = index n biw - let y = index n bjw // bjw = biw+1 - let xbit = bi % baseBits // start bit x - let nxbits = baseBits - xbit // number of bitsin x - let x = x >>> xbit // shift down x so bit0 is first - let y = y <<< nxbits // shift up y so it starts where x finished - let x = x ||| y // combine them - let x = x &&& bitmask.[enc.bigL] // mask out (high y bits) to get required bits - x - else - // one word - let x = index n biw - let xbit = bi % baseBits // start bit x - let x = x >>> xbit - let x = x &&& bitmask.[enc.bigL] - x - - let encodePoly enc n = - // Find poly s.t. n = poly evaluated at x=2^bigL - // with 0 <= pi < 2^bigL. - // - let poly = Array.create enc.bigK (Fp.ofInt32 0) - let biMax = n.bound * baseBits - let rec encoder i bi = - // bi = i * bigL - if i=enc.bigK || bi > biMax then - () // done - else - ( let pi = extractBits n enc bi - poly.[i] <- Fp.ofInt32 pi; - let i = i + 1 - let bi = bi + enc.bigL - encoder i bi - ) - - encoder 0 0; - poly - - let decodeResultBits enc (poly : fp array) = - // Decoding evaluates poly(x) (coeff Fp) at X = 2^bigL. - // A bound on number of result bits is "enc.bigN + boundInt", but that takes HUGE STEPS. - // Garbage has a cost, so we minimize it by working out a tight bound. - // - // poly(X) = sum i=0..n coeff_i * X^i where n is highest non-zero coeff. - // <= 2^maxBitsInsideFp * (1 + X + ... X^n) - // <= 2^maxBitsInsideFp * (X^(n+1) - 1) / (X - 1) - // <= 2^maxBitsInsideFp * X^(n+1) / (X - 1) - // <= 2^maxBitsInsideFp * X^(n+1) / (X/2) provided X/2 <= X-1 - // <= 2^maxBitsInsideFp * X^n * 2 - // <= 2^maxBitsInsideFp * (2^bigL)^n * 2 - // <= 2^(maxBitsInsideFp + bigL.n + 1) - // - let mutable n = 0 - for i = 0 to poly.Length-1 do - if poly.[i] <> mzero then n <- i - done; - let rbits = maxBitsInsideFp + enc.bigL * n + 1 - rbits + 1 // +1 since 2^1 requires 2 bits not 1 - - // REQUIRE: bigL <= baseBits - let decodePoly enc poly = - // Find n = poly evaluated at x=2^bigL - // Note, 0 <= pi < maxFp. - // - let rbound = (decodeResultBits enc poly) / baseBits + 1 - let r = createN rbound - let rec evaluate i j d = - // HAVE: bigL.i = j * baseBits + d and d= rbound then -#if CHECKED - check (poly.[i] = mzero) "decodePoly"; -#endif - () - else ( - let x = int64 (Fp.toInt poly.[i]) * twopowersI64.[d] - // HAVE: x < 2^32 . 2^baseBits = 2^32.baseN - contribute r j x - ); - let i = i + 1 - let d = d + enc.bigL - let j,d = if d >= baseBits then j+1 , d-baseBits else j,d - // HAVE: d < baseBits, note: bigL minDigitsKaratsuba then - let k = bmax / 2 - let a0 = restrictTo k p - let a1 = shiftDown k p - let b0 = restrictTo k q - let b1 = shiftDown k q - let q0 = mul a0 b0 - let q1 = mul (add a0 a1) (add b0 b1) - let q2 = mul a1 b1 - let p0 = q0 - let p1 = sub q1 (add q0 q2) - let p2 = q2 - let r = add p0 (shiftUp k (add p1 (shiftUp k p2))) - r - else - mulSchoolBook p q - - let rec mulKaratsuba x y = recMulKaratsuba mulKaratsuba x y - - -//---------------------------------------------------------------------------- -// mul - composite -//-------------------------------------------------------------------------- - - let productDigitsUpperSchoolBook = (64000 / baseBits) - // When is it worth switching away from SchoolBook? - // SchoolBook overhead is low, so although it's O(n^2) it remains competitive. - // - // 28/3/2006: - // The FFT can take over from SchoolBook at around 64000 bits. - // Note, FFT performance is stepwise, according to enc from table. - // The steps are big steps (meaning sudden jumps/drops perf). - // - - let singleDigitForceSchoolBook = (32000 / baseBits) - // If either argument is "small" then stay with SchoolBook. - // - - let productDigitsUpperFft = (table.[0].bigN / baseBits) - // QuickMul is good upto a finite (but huge) limit: - // Limit 268,435,456 bits product. - // - // From the code: - // let bitsRes = bits u + bits v - // fails when bitsRes >= table.[0].bigN - // So, not applicable when: - // P1: table.[0].bigN <= bits(u) + bits(v) - // P2: table.[0].bigN <= .. <= baseBits * (u.bound + v.bound) - // P3: table.[0].bigN <= .. <= baseBits * (u.bound + v.bound) - // P4: table.[0].bigN / baseBits <= u.bound + v.bound - // - - // Summary of mul algorithm choice: - // 0 <= uv_bound < upper_school_book - Schoolbook - // upper_school_book <= uv_bound < upper_fft - QuickMul - // upper_fft <= uv_bound < ... - Karatsuba - // - // NOTE: - // - Karatsuba current implementation has high garbage cost. - // - However, a linear space cost is possible... - // - Meantime, switch to Karatsuba only beyond FFT range. - // - - let rec mul p q = - let pqBound = p.bound + q.bound - if pqBound < productDigitsUpperSchoolBook || - p.bound < singleDigitForceSchoolBook || - q.bound < singleDigitForceSchoolBook - then - // Within school-book initial range: - mulSchoolBook p q - else - if pqBound < productDigitsUpperFft then - // Inside QuickMul FFT range: - quickMulUsingFft p q - else - // Beyond QuickMul FFT range, or maybe between Schoolbook and QuickMul (no!): - // Use karatsuba method, with "mul" as recursive multiplier, - // so will reduce sizes of products on recursive calls, - // and QuickMul will take over if they fall within it's range. - // - recMulKaratsuba mul p q - - -//---------------------------------------------------------------------------- -// division - scaleSubInPlace -//-------------------------------------------------------------------------- - - let scaleSubInPlace x f a n = - // Have x = sumR 0 xd (\i.xi.B^i) where xd = degree x - // a = sumR 0 ad (\i.digitsi.B^i) where ad = degree a - // f < B - // n < xd - // Assumes "f.digits.B^n < x". - // Required to remove f.digits.B^n from x place. - //------ - // Result = x_initial - f.digits.B^n - // = x_initial - f.[sumR 0 ad (\i.digitsi.B^(i+n))] - // State: j = 0 - // z = f * a0 - // Invariant(x,z,j,n): - // P1: x_result = x - z.B^(j+n) - f.[sumR (j+1) ad (\i.digitsi.B^i+n)] - // P2: z < B^2 - 1, and so has form z = zHi.B + zLo for zHi,zLo < B. - // Base: Invariant holds initially. - // Step: (a) Remove zLo from x: - // If zLo <= x_(j+n) then zLo <- 0 - // x_(j+n) <- x_(j+n) - zLo - // else zLo <- 0 - // x_(j+n) <- x_(j+n) + (B - zLo) - // = x_(j+n) - zLo + B - // zHi <- zHi + 1 - // Here, invariant P1 still holds, P2 may break. - // (b) Advance j: - // Have z = zHi.B since zLo = 0. - // j <- j + 1 - // z <- zHi + f * a_(j+1) - // P2 holds: - // Have z <= B + (B-1)*(B-1) = B + B^2 - 2B + 1 = B^2 - B + 1 - // Have z <= B^2 - 1 when B >= 2 which is required for B being a base. - // P1 holds, - // moved f.digits_(j+1).B^(j+1+n) factor over. - // - // Once j+1 exceeds ad, summation is zero and it contributes no more terms (b). - // Continue until z = 0, which happens since z decreases towards 0. - // Done. - // - let invariant (_,_,_) = () - #if CHECKED - let x_initial = copyN x - let x_result = sub x_initial (shiftUp n (scale f a)) - let invariant (z,j,n) = - let P1 = - equal - x_result - (sub x (add (shiftUp (j+n) (embed64 z)) - (mul (embed f) - (shiftUp (j+1+n) (shiftDown (j+1) a))))) - let P2 = z < baseNi64 * baseNi64 - 1L - check P1 "P1"; - check P2 "P2" - - #endif - let xres = x - let x,xd = x.digits,degree x - let a,ad = a.digits,degree a - let f = int64 f - let mutable j = 0 - let mutable z = f * int64 a.[0] - while( z > 0L || j < ad ) do - if j > xd then failwith "scaleSubInPlace: pre-condition did not apply, result would be -ve"; - invariant(z,j,n); // P1,P2 hold - let mutable zLo = mod64base z |> int32 - let mutable zHi = div64base z - if zLo <= x.[j+n] then - x.[j+n] <- x.[j+n] - zLo - else ( - x.[j+n] <- x.[j+n] + (baseN - zLo); - zHi <- zHi + 1L - ); - // P1 holds - if j < ad then - z <- zHi + f * int64 a.[j+1] - else - z <- zHi; - j <- j + 1; - // P1,P2 hold - done; - ignore (normN xres) - - // - let scaleSub x f a n = - let freshx = add x zero - scaleSubInPlace freshx f a n; - normN freshx -(* - - let scaleSub2 x f a n = sub x (shiftUp n (mul (embed f) a)) - - let x = (mul (embed 234234234) (pow (embed 10) (embed 20))) - let f = 2 - let a = (embed 1231231231) - let n = 2 - let res = scaleSub x f a n - let res2 = scaleSub2 x f a n - - let x, xd, f, a, ad, n = freshx.digits, freshx.bound, f, a.digits, a.bound, n - *) - - -//---------------------------------------------------------------------------- -// division - scaleAddInPlace -//-------------------------------------------------------------------------- - - let scaleAddInPlace x f a n = - // Have x = sumR 0 xd (\i.xi.B^i) - // a = sumR 0 ad (\i.digitsi.B^i) - // f < B - // n < xd - // Required to add f.digits.B^n to x place. - // Assumes result will fit with x (0...xd). - //------ - // Result = x_initial + f.digits.B^n - // = x_initial + f.[sumR 0 ad (\i.digitsi.B^i+n)] - // State: j = 0 - // z = f * a0 - // Invariant(x,z,j,n): - // P1: x_result = x + z.B^(j+n) + f.[sumR (j+1) ad (\i.digitsi.B^i+n)] - // P2: z < B^2 - 1, and so has form z = zHi.B + zLo for zHi,zLo < B. - // Base: Invariant holds initially. - // Step: (a) Add zLo to x: - // If zLo < B - x_(j+n) then zLo <- 0 - // x_(j+n) <- x_(j+n) + zLo - // else zLo <- 0 - // x_(j+n) <- zLo - (B - x_(j+n)) - // = x_(j+n) + zLo - B - // zHi <- zHi + 1 - // Here, invariant P1 still holds, P2 may break. - // (b) Advance j: - // Have z = zHi.B since zLo = 0. - // j <- j + 1 - // z <- zHi + f * a_(j+1) - // P2 holds: - // Have z <= B + (B-1)*(B-1) = B + B^2 - 2B + 1 = B^2 - B + 1 - // Have z <= B^2 - 1 when B >= 2 which is required for B being a base. - // P1 holds, - // moved f.digits_(j+1).B^(j+1+n) factor over. - // - // Once j+1 exceeds ad, summation is zero and it contributes no more terms (b). - // Continue until z = 0, which happens since z decreases towards 0. - // Done. - // - let invariant (_,_,_) = () -#if CHECKED - let x_initial = copyN x - let x_result = add x_initial (shiftUp n (scale f a)) - let invariant (z,j,n) = - let P1 = - equal - x_result - (add x (add (shiftUp (j+n) (embed64 z)) - (mul (embed f) - (shiftUp (j+1+n) (shiftDown (j+1) a))))) - let P2 = z < baseNi64 * baseNi64 - 1L - check P1 "P1"; - check P2 "P2" - -#endif - let xres = x - let x,xd = x.digits,degree x - let a,ad = a.digits,degree a - let f = int64 f - let mutable j = 0 - let mutable z = f * int64 a.[0] - while( z > 0L || j < ad ) do - if j > xd then failwith "scaleSubInPlace: pre-condition did not apply, result would be -ve"; - invariant(z,j,n); // P1,P2 hold - let mutable zLo = mod64base z |> int32 - let mutable zHi = div64base z - if zLo < baseN - x.[j+n] then - x.[j+n] <- x.[j+n] + zLo - else ( - x.[j+n] <- zLo - (baseN - x.[j+n]); - zHi <- zHi + 1L - ); - // P1 holds - if j < ad then - z <- zHi + f * int64 a.[j+1] - else - z <- zHi; - j <- j + 1; - // P1,P2 hold - done; - ignore (normN xres) - - // - let scaleAdd x f a n = - let freshx = add x zero - scaleAddInPlace freshx f a n; - normN freshx - -(* - let scaleAdd2 x f a n = add x (shiftUp n (mul (embed f) a)) - - let x = (mul (embed 234234234) (pow (embed 10) (embed 20))) - let f = 2 - let a = (embed 1231231231) - let n = 2 - let res = scaleAdd x f a n - let res2 = scaleAdd2 x f a n - - let x, xd, f, a, ad, n = freshx.digits, freshx.bound, f, a.digits, a.bound, n -*) - -//---------------------------------------------------------------------------- -// division - removeFactor -//-------------------------------------------------------------------------- - - (* - let removeFactorReference x a n = - let ff = div x (shiftUp n a) - toInt ff - *) - - let removeFactor x a n = - // Assumes x < a.B^(n+1) - // Choose f s.t. - // (a) f.digits.B^n <= x - // (b) f=0 iff x < a.B^n - // - let dega,degx = degree a,degree x - if degx < dega + n then 0 else // possible with "normalisation" - let aa,xa = a.digits,x.digits - let f = - if dega = 0 then // a = a0 - if degx = n then - xa.[n] / aa.[0] - else ( -#if CHECKED - check (degx = n+1) "removeFactor degx#1"; -#endif - let f64 = (int64 xa.[degx] * baseNi64 + int64 xa.[degx-1]) / int64 aa.[0] - int32 f64 - ) - else // a = sumR 0 dega (\i.digitsi.B^i) - if degx = dega + n then - xa.[degx] / (aa.[dega] + 1) // +1 to bound above a - else ( -#if CHECKED - check (degx = dega+n+1) "removeFactor degx#2"; -#endif - let f64 = (int64 xa.[degx] * baseNi64 + int64 xa.[degx-1]) - / (int64 aa.[dega] + 1L) // +1 to bound above a - int32 f64 - ) - - if f = 0 then - let lte = (shiftCompare a n x 0) <> 1 - if lte then 1 else 0 - else - f - - -//---------------------------------------------------------------------------- -// division - divmod -//-------------------------------------------------------------------------- - - let divmod b a = - // Returns d,r where b = d.digits + r and r0 then - scaleSubInPlace x f a n; - scaleAddInPlace d f one n; - Invariant(d,x,n,p) - else - finished <- f=0 && n=0; - if not finished then - if p = m+n then - Invariant(d,x,n-1,p); - n <- n-1 - else - Invariant(d,x,n-1,p-1); - n <- n-1; - p <- p-1 - // Have: "b = d.digits + x" return d,x - normN d,normN x - - //---------------------------------------------------------------------------- - // div, mod - //-------------------------------------------------------------------------- - - [] - let div b a = fst (divmod b a) - [] - let rem b a = snd (divmod b a) - // rem b a, for small a can do (base mod a) trick - O(N) - - - //---------------------------------------------------------------------------- - // hcf - //-------------------------------------------------------------------------- - - let hcf a b = - // Have: 0 <= a,b since naturals - let rec hcfloop a b = // Require: 0 <= a <= b - if equal zero a then b - else - // Have: 0 < a <= b - let _,r = divmod b a - // Have: r < a from divmod - hcfloop r a // Have: 0 <= r < a - - if lt a b then hcfloop a b // Have: 0 <= a < b - else hcfloop b a // Have: 0 <= b <= a - - //---------------------------------------------------------------------------- - // pow - //-------------------------------------------------------------------------- - - let two = embed 2 - let powi x n = - let rec power acc x n = - if n=0 then acc - elif n % 2=0 then power acc (mul x x) (n / 2) - else power (mul x acc) (mul x x) (n / 2) - - power one x n - - let pow x n = - let rec power acc x n = - if isZero n then acc - else - let ndiv2,nmod2 = divmod n two // use: intdivmod when available - if isZero nmod2 then power acc (mul x x) ndiv2 - else power (mul x acc) (mul x x) ndiv2 - - power one x n - -//---------------------------------------------------------------------------- -// float n -//-------------------------------------------------------------------------- - - let toFloat n = - let basef = float baseN - let rec evalFloat acc k i = - if i = n.bound then - acc - else - evalFloat (acc + k * float n.digits.[i]) (k * basef) (i+1) - evalFloat 0.0 1.0 0 - -//---------------------------------------------------------------------------- -// n <-> int -//-------------------------------------------------------------------------- - - let ofInt32 n = embed n - let ofInt64 n = embed64 n - - /// Convert BigNat to uint32 otherwise OverflowException. - let toUInt32 n = - match n.bound with - | 0 -> 0u - | 1 -> n.digits.[0] |> uint32 - | 2 -> let xA,xB = n.digits.[0],n.digits.[1] - if xB > baseMask32B then raise (System.OverflowException()) - ( uint32 (xA &&& baseMask32A)) + - ((uint32 (xB &&& baseMask32B)) <<< baseShift32B) - | _ -> raise (System.OverflowException()) - - /// Convert BigNat to uint64 otherwise OverflowException. - let toUInt64 n = - match n.bound with - | 0 -> 0UL - | 1 -> n.digits.[0] |> uint64 - | 2 -> let xA,xB = n.digits.[0],n.digits.[1] - ( uint64 (xA &&& baseMask64A)) + - ((uint64 (xB &&& baseMask64B)) <<< baseShift64B) - | 3 -> let xA,xB,xC = n.digits.[0],n.digits.[1],n.digits.[2] - if xC > baseMask64C then raise (System.OverflowException()) - ( uint64 (xA &&& baseMask64A)) + - ((uint64 (xB &&& baseMask64B)) <<< baseShift64B) + - ((uint64 (xC &&& baseMask64C)) <<< baseShift64C) - | _ -> raise (System.OverflowException()) - - -//---------------------------------------------------------------------------- -// n -> string -//-------------------------------------------------------------------------- - - -#if CHECKED - let checks = false -#endif - let toString n = - // Much better complexity than naive_string_of_z. - // It still does "nDigit" calls to (int)divmod, - // but the degree on which it is called halves (not decrements) each time. - // - let degn = degree n - let rec route prior k ten2k = - if degree ten2k > degn - then (k,ten2k)::prior - else route ((k,ten2k)::prior) (k+1) (mul ten2k ten2k) - let kten2ks = route [] 0 (embed 10) - let rec collect isLeading digits n = function - | [] -> - // Have 0 <= n < 10^1, so collect a single digit (if needed) - let n = eval n -#if CHECKED - if checks then check (0 <= n) "toString: digit0"; - if checks then check (n <= 9) "toString: digit9"; -#endif - if isLeading && n=0 then digits // suppress leading 0 - else string n :: digits - | (_,ten2k)::prior -> -#if CHECKED - if checks then check (lt n (mul ten2k ten2k)) "string_of_int: bound n"; -#endif - // Have 0 <= n < (ten2k)^2 and ten2k = 10^(2^k) - let nH,nL = divmod n ten2k -#if CHECKED - if checks then check (lt nH ten2k) "string_of_int: bound nH"; - if checks then check (lt nL ten2k) "string_of_int: bound nL"; -#endif - // Have 0 <= nH,nL < (ten2k) and ten2k = 10^(2^k) - if isLeading && isZero nH then - // suppress leading 0s - let digits = collect isLeading digits nL prior - digits - else - let digits = collect false digits nL prior - let digits = collect isLeading digits nH prior - digits - - let prior = kten2ks - let digits = collect true [] n prior - match digits with - | [] -> "0" - | _ -> digits |> Array.ofList |> System.String.Concat - -//---------------------------------------------------------------------------- -// n <- string -//-------------------------------------------------------------------------- - - let ofString (str:string) = - // Would it be better to split string half and combine results? - let len = str.Length - if System.String.IsNullOrEmpty str then invalidArg "str" "empty string"; - let ten = embed 10 - let rec build acc i = - if i=len then - acc - else - let c = str.[i] - let d = int c - int '0' - if 0 <= d && d <= 9 then - build (add (mul ten acc) (embed d)) (i+1) - else - raise (new System.FormatException("The value could not be parsed")) - - build (embed 0) 0 - - let isSmall n = (n.bound <= 1) - let getSmall n = index n 0 - - //---------------------------------------------------------------------------- - // factorial - //-------------------------------------------------------------------------- - - let factorial n = - //***** - // Factorial(n) = 1.2.3.....(n-1).n - // - // Factorial is sometimes used as a test for multiplication. - // The QuickMul FFT multiplier takes over only when both operands reach a given size. - // How to compute factorial? - // - // (a) Factorial(n) = factorial(n-1).n - // This is unlikely to make use of the FFT (n never large enough). - // (b) Factorial(n) = (1.2.3.4....k) . (k.[k+1]...(n-1).n) - // Applied recursively QuickMul FFT will take over on large products. - // - //**** - let rec productR a b = - if equal a b then a - else - let m = div (add a b) (ofInt32 2) - mul (productR a m) (productR (add m one) b) - - productR one n - - -#endif diff --git a/src/FSharpPortable/n.fsi b/src/FSharpPortable/n.fsi deleted file mode 100644 index bf01cbba..00000000 --- a/src/FSharpPortable/n.fsi +++ /dev/null @@ -1,71 +0,0 @@ -// First version copied from the F# compiler sources -// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/n.fsi - -//---------------------------------------------------------------------------- -// Copyright (c) 2002-2012 Microsoft Corporation. -// -// This source code is subject to terms and conditions of the Apache License, Version 2.0. A -// copy of the license can be found in the License.html file at the root of this distribution. -// By using this source code in any fashion, you are agreeing to be bound -// by the terms of the Apache License, Version 2.0. -// -// You must not remove this notice, or any other, from this software. -//---------------------------------------------------------------------------- - -namespace Microsoft.FSharp.Math - -#if FX_NO_BIGINT -open Microsoft.FSharp.Collections -open Microsoft.FSharp.Core - -/// Abstract internal type -[] -type internal BigNat - -module internal BigNatModule = - - val zero : BigNat - val one : BigNat - val two : BigNat - - val add : BigNat -> BigNat -> BigNat - val sub : BigNat -> BigNat -> BigNat - val mul : BigNat -> BigNat -> BigNat - val divmod : BigNat -> BigNat -> BigNat * BigNat - val div : BigNat -> BigNat -> BigNat - val rem : BigNat -> BigNat -> BigNat - val hcf : BigNat -> BigNat -> BigNat - - val min : BigNat -> BigNat -> BigNat - val max : BigNat -> BigNat -> BigNat - val scale : int -> BigNat -> BigNat - val powi : BigNat -> int -> BigNat - val pow : BigNat -> BigNat -> BigNat - - val IsZero : BigNat -> bool - val isZero : BigNat -> bool - val isOne : BigNat -> bool - val equal : BigNat -> BigNat -> bool - val compare : BigNat -> BigNat -> int - val lt : BigNat -> BigNat -> bool - val gt : BigNat -> BigNat -> bool - val lte : BigNat -> BigNat -> bool - val gte : BigNat -> BigNat -> bool - - val hash : BigNat -> int - val toFloat : BigNat -> float - val ofInt32 : int -> BigNat - val ofInt64 : int64 -> BigNat - val toString : BigNat -> string - val ofString : string -> BigNat - - val toUInt32 : BigNat -> uint32 - val toUInt64 : BigNat -> uint64 - - val factorial : BigNat -> BigNat - // val randomBits : int -> BigNat - val bits : BigNat -> int - val isSmall : BigNat -> bool (* will fit in int32 (but not nec all int32) *) - val getSmall : BigNat -> int32 (* get the value, if it satisfies isSmall *) - -#endif diff --git a/src/FSharpPortable/z.fs b/src/FSharpPortable/z.fs deleted file mode 100644 index b2d56594..00000000 --- a/src/FSharpPortable/z.fs +++ /dev/null @@ -1,411 +0,0 @@ -// First version copied from the F# compiler sources -// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/z.fs - -//---------------------------------------------------------------------------- -// Copyright (c) 2002-2012 Microsoft Corporation. -// -// This source code is subject to terms and conditions of the Apache License, Version 2.0. A -// copy of the license can be found in the License.html file at the root of this distribution. -// By using this source code in any fashion, you are agreeing to be bound -// by the terms of the Apache License, Version 2.0. -// -// You must not remove this notice, or any other, from this software. -//---------------------------------------------------------------------------- - -#nowarn "44" // This construct is deprecated. This function is for use by compiled F# code and should not be used directly -namespace System.Numerics - -#if FX_NO_BIGINT - open Microsoft.FSharp.Collections - open Microsoft.FSharp.Core - open Microsoft.FSharp.Core.Operators - open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators - open Microsoft.FSharp.Primitives.Basics - open Microsoft.FSharp.Math - open System - open System.Globalization - - - // INVARIANT: signInt = 1 or -1 - // value(z) = signInt * v - // NOTE: 0 has two repns (+1,0) or (-1,0). - [] - [] - [] - type BigInteger(signInt:int, v : BigNat) = - - static let smallLim = 4096 - static let smallPosTab = Array.init smallLim BigNatModule.ofInt32 - static let one = BigInteger(1) - static let zero = BigInteger(0) - - static member internal nat n = - if BigNatModule.isSmall n && BigNatModule.getSmall n < smallLim - then smallPosTab.[BigNatModule.getSmall n] - else n - static member internal create (s,n) = BigInteger(s,BigInteger.nat n) - static member internal posn n = BigInteger(1,BigInteger.nat n) - static member internal negn n = BigInteger(-1,BigInteger.nat n) - - - member x.Sign = if x.IsZero then 0 else signInt - member x.SignInt = signInt - member internal x.V = v - - static member op_Equality (x:BigInteger, y:BigInteger) = - //System.Console.WriteLine("x = {0}",box x) - //System.Console.WriteLine("y = {0}",box y) - match x.SignInt,y.SignInt with - | 1, 1 -> BigNatModule.equal x.V y.V // +1.xv = +1.yv iff xv = yv - | -1, -1 -> BigNatModule.equal x.V y.V // -1.xv = -1.yv iff xv = yv - | 1,-1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V // 1.xv = -1.yv iff xv=0 and yv=0 - | -1, 1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V // -1.xv = 1.yv iff xv=0 and yv=0 - | _ -> invalidArg "x" "signs should be +/- 1" - - static member op_Inequality (x:BigInteger, y:BigInteger) = not (BigInteger.op_Equality(x,y)) // CA2226: OperatorsShouldHaveSymmetricalOverloads - - static member op_LessThan (x:BigInteger, y:BigInteger) = - match x.SignInt,y.SignInt with - | 1, 1 -> BigNatModule.lt x.V y.V // 1.xv < 1.yv iff xv < yv - | -1,-1 -> BigNatModule.lt y.V x.V // -1.xv < -1.yv iff yv < xv - | 1,-1 -> false // 1.xv < -1.yv iff 0 <= 1.xv < -1.yv <= 0 iff false - | -1, 1 -> not (BigNatModule.isZero x.V) || not (BigNatModule.isZero y.V) - // -1.xv < 1.yv - // (a) xv=0 and yv=0, then false - // (b) xv<>0, -1.xv < 0 <= 1.yv, so true - // (c) yv<>0, -1.xv <= 0 < 1.yv, so true - | _ -> invalidArg "x" "signs should be +/- 1" - - static member op_GreaterThan (x:BigInteger, y:BigInteger) = // Follow lt by +/- symmetry - match x.SignInt,y.SignInt with - | 1, 1 -> BigNatModule.gt x.V y.V - | -1,-1 -> BigNatModule.gt y.V x.V - | 1,-1 -> not (BigNatModule.isZero x.V) || not (BigNatModule.isZero y.V) - | -1, 1 -> false - | _ -> invalidArg "x" "signs should be +/- 1" - - static member internal compare(n,nn) = if BigInteger.op_LessThan(n,nn) then -1 elif BigInteger.op_Equality(n,nn) then 0 else 1 - static member internal hash (z:BigInteger) = z.SignInt + BigNatModule.hash(z.V) - - override x.ToString() = - match x.SignInt with - | 1 -> BigNatModule.toString x.V // positive - | -1 -> - if BigNatModule.isZero x.V - then "0" // not negative infact, but zero. - else "-" + BigNatModule.toString x.V // negative - | _ -> invalidOp "signs should be +/- 1" - - member x.StructuredDisplayString = x.ToString() - - interface System.IComparable with - member this.CompareTo(obj:obj) = - match obj with - | :? BigInteger as that -> BigInteger.compare(this,that) - | _ -> invalidArg "obj" "the objects are not comparable" - - override this.Equals(obj) = - match obj with - | :? BigInteger as that -> BigInteger.op_Equality(this, that) - | _ -> false - - override x.GetHashCode() = BigInteger.hash(x) - - - new (n:int) = - if n>=0 - then BigInteger (1,BigInteger.nat(BigNatModule.ofInt32 n)) - elif (n = System.Int32.MinValue) - then BigInteger(-1,BigInteger.nat(BigNatModule.ofInt64 (-(int64 n)))) - else BigInteger(-1,BigInteger.nat(BigNatModule.ofInt32 (-n))) - - - new (n:int64) = - if n>=0L - then BigInteger(1,BigInteger.nat (BigNatModule.ofInt64 n)) - elif (n = System.Int64.MinValue) - then BigInteger(-1,BigInteger.nat (BigNatModule.add (BigNatModule.ofInt64 System.Int64.MaxValue) BigNatModule.one) ) - else BigInteger(-1,BigInteger.nat (BigNatModule.ofInt64 (-n))) - - static member One = one - static member Zero = zero - static member (~-) (z:BigInteger) = BigInteger.create(-1 * z.SignInt,z.V) - static member Scale(k,z:BigInteger) = - if k<0 - then BigInteger.create(-z.SignInt, (BigNatModule.scale (-k) z.V)) // k.zsign.zv = -zsign.(-k.zv) - else BigInteger.create(z.SignInt, (BigNatModule.scale k z.V)) // k.zsign.zv = zsign.k.zv - - // Result: 1.nx - 1.ny (integer subtraction) - static member internal subnn (nx,ny) = - if BigNatModule.gte nx ny - then BigInteger.posn (BigNatModule.sub nx ny) // nx >= ny, result +ve, +1.(nx - ny) - else BigInteger.negn (BigNatModule.sub ny nx) // nx < ny, result -ve, -1.(ny - nx) - - static member internal addnn (nx,ny) = - BigInteger.posn (BigNatModule.add nx ny) // Compute "nx + ny" to be integer - - member x.IsZero = BigNatModule.isZero x.V // signx.xv = 0 iff xv=0, since signx is +1,-1 - member x.IsOne = (x.SignInt = 1) && BigNatModule.isOne x.V // signx.xv = 1 iff signx = +1 and xv = 1 - static member (+) (x:BigInteger,y:BigInteger) = - if y.IsZero then x else - if x.IsZero then y else - match x.SignInt,y.SignInt with - | 1, 1 -> BigInteger.addnn(x.V,y.V) // 1.xv + 1.yv = (xv + yv) - | -1,-1 -> -(BigInteger.addnn(x.V,y.V)) // -1.xv + -1.yv = -(xv + yv) - | 1,-1 -> BigInteger.subnn (x.V,y.V) // 1.xv + -1.yv = (xv - yv) - | -1, 1 -> BigInteger.subnn(y.V,x.V) // -1.xv + 1.yv = (yv - xv) - | _ -> invalidArg "x" "signs should be +/- 1" - - static member (-) (x:BigInteger,y:BigInteger) = - if y.IsZero then x else - match x.SignInt,y.SignInt with - | 1, 1 -> BigInteger.subnn(x.V,y.V) // 1.xv - 1.yv = (xv - yv) - | -1,-1 -> BigInteger.subnn(y.V,x.V) // -1.xv - -1.yv = (yv - xv) - | 1,-1 -> BigInteger.addnn(x.V,y.V) // 1.xv - -1.yv = (xv + yv) - | -1, 1 -> -(BigInteger.addnn(x.V,y.V)) // -1.xv - 1.yv = -(xv + yv) - | _ -> invalidArg "x" "signs should be +/- 1" - - static member ( * ) (x:BigInteger,y:BigInteger) = - if x.IsZero then x - elif y.IsZero then y - elif x.IsOne then y - elif y.IsOne then x - else - let m = (BigNatModule.mul x.V y.V) - BigInteger.create (x.SignInt * y.SignInt,m) // xsign.xv * ysign.yv = (xsign.ysign).(xv.yv) - - static member DivRem (x:BigInteger,y:BigInteger,rem:BigInteger byref) = - let d,r = BigNatModule.divmod x.V y.V - // HAVE: |x| = d.|y| + r and 0 <= r < |y| - // HAVE: xv = d.yv + r and 0 <= r < yv - match x.SignInt,y.SignInt with - | 1, 1 -> rem <- BigInteger.posn r ; BigInteger.posn d // 1.xv = 1.d.( 1.yv) + ( 1.r) - | -1,-1 -> rem <- BigInteger.negn r ; BigInteger.posn d // -1.xv = 1.d.(-1.yv) + (-1.r) - | 1,-1 -> rem <- BigInteger.posn r ; BigInteger.negn d // 1.xv = -1.d.(-1.yv) + ( 1.r) - | -1, 1 -> rem <- BigInteger.negn r ; BigInteger.negn d // -1.xv = -1.d.( 1.yv) + (-1.r) - | _ -> invalidArg "x" "signs should be +/- 1" - - static member (/) (x:BigInteger,y:BigInteger) = - let mutable rem = new BigInteger(0) - BigInteger.DivRem(x,y,&rem) - static member (%) (x:BigInteger,y:BigInteger) = - let mutable rem = new BigInteger(0) - BigInteger.DivRem(x,y,&rem) |> ignore ; rem - static member GreatestCommonDivisor (x:BigInteger,y:BigInteger) = BigInteger.posn (BigNatModule.hcf x.V y.V) // hcf (xsign.xv,ysign.yv) = hcf (xv,yv) - - member x.IsNegative = x.SignInt = -1 && not (x.IsZero) // signx.xv < 0 iff signx = -1 and xv<>0 - member x.IsPositive = x.SignInt = 1 && not (x.IsZero) // signx.xv > 0 iff signx = +1 and xv<>0 - static member Abs (x:BigInteger) = if x.SignInt = -1 then -x else x - - static member op_LessThanOrEqual (x:BigInteger,y:BigInteger) = - match x.SignInt,y.SignInt with - | 1, 1 -> BigNatModule.lte x.V y.V // 1.xv <= 1.yv iff xv <= yv - | -1,-1 -> BigNatModule.lte y.V x.V // -1.xv <= -1.yv iff yv <= xv - | 1,-1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V // 1.xv <= -1.yv, - // (a) if xv=0 and yv=0 then true - // (b) otherwise false, only meet at zero. - - | -1, 1 -> true // -1.xv <= 1.yv, true - | _ -> invalidArg "x" "signs should be +/- 1" - - static member op_GreaterThanOrEqual (x:BigInteger,y:BigInteger) = // Follow lte by +/- symmetry - match x.SignInt,y.SignInt with - | 1, 1 -> BigNatModule.gte x.V y.V - | -1,-1 -> BigNatModule.gte y.V x.V - | 1,-1 -> true - | -1, 1 -> BigNatModule.isZero x.V && BigNatModule.isZero y.V - | _ -> invalidArg "x" "signs should be +/- 1" - - - static member Pow (x:BigInteger,y:int32) = - if y < 0 then invalidArg "y" "The input must be non-negative." - let yval = BigInteger(y) - BigInteger.create ((if BigNatModule.isZero (BigNatModule.rem yval.V BigNatModule.two) then 1 else x.SignInt), BigNatModule.pow x.V yval.V) - - static member op_Explicit (x:BigInteger) = - let u = BigNatModule.toUInt32 x.V - if u <= uint32 System.Int32.MaxValue then - // Handle range [-MaxValue,MaxValue] - x.SignInt * int32 u - elif x.SignInt = -1 && u = uint32 (System.Int32.MaxValue + 1) then - //assert(System.Int32.MinValue = 0 - System.Int32.MaxValue - 1) - // Handle MinValue = -(MaxValue+1) special case not covered by the above - System.Int32.MinValue - else - raise (System.OverflowException()) - - static member op_Explicit (x:BigInteger) = - let u = BigNatModule.toUInt64 x.V - if u <= uint64 System.Int64.MaxValue then - (* Handle range [-MaxValue,MaxValue] *) - int64 x.SignInt * int64 u - elif x.SignInt = -1 && u = uint64 (System.Int64.MaxValue + 1L) then - //assert(System.Int64.MinValue = 0 - System.Int64.MaxValue - 1L) - (* Handle MinValue = -(MaxValue+1) special case not covered by the above *) - System.Int64.MinValue - else - raise (System.OverflowException()) - - static member op_Explicit (x:BigInteger) = - match x.SignInt with - | 1 -> BigNatModule.toFloat x.V // float (1.xv) = float (xv) - | -1 -> - (BigNatModule.toFloat x.V) // float (-1.xv) = - float (xv) - | _ -> invalidArg "x" "signs should be +/- 1" - - static member Parse(text:string) = - let len = text.Length - if len = 0 then raise (new System.FormatException("The value could not be parsed")) - if text.[0..0] = "-" then - BigInteger.negn (BigNatModule.ofString text.[1..len-1]) - else - BigInteger.posn (BigNatModule.ofString text) - - member internal x.IsSmall = BigNatModule.isSmall (x.V) - static member Factorial (x:BigInteger) = - if x.IsNegative then invalidArg "x" "The input must be non-negative." - if x.IsPositive then BigInteger.posn (BigNatModule.factorial x.V) - else BigInteger.One - - static member ( ~+ )(n1:BigInteger) = n1 - - static member FromInt64(x:int64) = new BigInteger(x) - static member FromInt32(x:int32) = new BigInteger(x) -#endif - -namespace Microsoft.FSharp.Core - - - type bigint = System.Numerics.BigInteger - - open System - open System.Diagnostics.CodeAnalysis - open System.Globalization - open Microsoft.FSharp.Core.Operators - open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators - open System.Numerics - -#if FX_NO_BIGINT - // FxCop suppressions - [] - [] - [] - [] - [] - [] - [] - [] - [] - [] - [] - do() -#endif - - [] - module NumericLiterals = - - module NumericLiteralI = - - let tab64 = new System.Collections.Generic.Dictionary() - let tabParse = new System.Collections.Generic.Dictionary() - - let FromInt64Dynamic (x64:int64) : obj = - lock tab64 (fun () -> - let mutable res = Unchecked.defaultof<_> - let ok = tab64.TryGetValue(x64,&res) - if ok then res else - res <- BigInteger(x64) - tab64.[x64] <- res - res) - - let inline get32 (x32:int32) = FromInt64Dynamic (int64 x32) - - let inline isOX s = not (System.String.IsNullOrEmpty(s)) && s.Length > 2 && s.[0] = '0' && s.[1] = 'x' - - let FromZero () : 'T = - (get32 0 :?> 'T) - - let FromOne () : 'T = - (get32 1 :?> 'T) - - let FromInt32 (i:int32): 'T = - (get32 i :?> 'T) - - let FromInt64 (i:int64): 'T = - (FromInt64Dynamic i :?> 'T) - - let getParse s = - lock tabParse (fun () -> - let mutable res = Unchecked.defaultof<_> - let ok = tabParse.TryGetValue(s,&res) - if ok then - res - else -#if FSHARP_CORE_PORTABLE - // SL5 (and therefore Portable Profile47) does not have Parse, so make our own simple implementation - let parse(s : string) = - // ws* sign? digits+ ws* - let mutable i = 0 - // leading whitespace - while i < s.Length && System.Char.IsWhiteSpace(s.[i]) do - i <- i + 1 - if i = s.Length then - raise <| new System.ArgumentException() - // optional sign - let mutable isNegative = false - if s.[i] = '+' then - i <- i + 1 - elif s.[i] = '-' then - isNegative <- true - i <- i + 1 - if i = s.Length then - raise <| new System.ArgumentException() - // digits - let startDigits = i - while i < s.Length && System.Char.IsDigit(s.[i]) do - i <- i + 1 - let endDigits = i - let len = endDigits - startDigits - if len = 0 then - raise <| new System.ArgumentException() - // trailing whitespace - while i < s.Length && System.Char.IsWhiteSpace(s.[i]) do - i <- i + 1 - if i <> s.Length then - raise <| new System.ArgumentException() - // text is now valid, parse it - let mutable r = new System.Numerics.BigInteger(int(s.[startDigits]) - int('0')) - let ten = new System.Numerics.BigInteger(10) - for j in startDigits+1 .. endDigits-1 do - r <- r * ten - r <- r + new System.Numerics.BigInteger(int(s.[j]) - int('0')) - if isNegative then - r <- new System.Numerics.BigInteger(0) - r - r - let v = parse s -#else - let v = -#if FX_NO_BIGINT - BigInteger.Parse s -#else -#if FX_NO_BIGINT_CULTURE_PARSE - BigInteger.Parse s -#else - if isOX s then - BigInteger.Parse (s.[2..],NumberStyles.AllowHexSpecifier,CultureInfo.InvariantCulture) - else - BigInteger.Parse (s,NumberStyles.AllowLeadingSign,CultureInfo.InvariantCulture) -#endif -#endif -#endif - res <- v - tabParse.[s] <- res - res) - - let FromStringDynamic (s:string) : obj = - getParse s - - let FromString (s:string) : 'T = - (FromStringDynamic s :?> 'T) - - - diff --git a/src/FSharpPortable/z.fsi b/src/FSharpPortable/z.fsi deleted file mode 100644 index cbc66872..00000000 --- a/src/FSharpPortable/z.fsi +++ /dev/null @@ -1,124 +0,0 @@ -// First version copied from the F# compiler sources -// https://raw.github.com/fsharp/fsharp/master/src/fsharp/FSharp.Core/math/z.fsi - -//---------------------------------------------------------------------------- -// Copyright (c) 2002-2012 Microsoft Corporation. -// -// This source code is subject to terms and conditions of the Apache License, Version 2.0. A -// copy of the license can be found in the License.html file at the root of this distribution. -// By using this source code in any fashion, you are agreeing to be bound -// by the terms of the Apache License, Version 2.0. -// -// You must not remove this notice, or any other, from this software. -//---------------------------------------------------------------------------- - -namespace System.Numerics -#if FX_NO_BIGINT - - open System - open Microsoft.FSharp.Collections - open Microsoft.FSharp.Core - - /// The type of arbitrary-sized integers - [] - [] - type BigInteger = - /// Return the sum of two big integers - static member ( + ) : x:BigInteger * y:BigInteger -> BigInteger - /// Return the modulus of big integers - static member ( % ) : x:BigInteger * y:BigInteger -> BigInteger - /// Return the product of big integers - static member ( * ) : x:BigInteger * y:BigInteger -> BigInteger - /// Return the difference of two big integers - static member ( - ) : x:BigInteger * y:BigInteger -> BigInteger - /// Return the ratio of two big integers - static member ( / ) : x:BigInteger * y:BigInteger -> BigInteger - /// Return the negation of a big integer - static member (~-) : x:BigInteger -> BigInteger - /// Return the given big integer - static member (~+) : x:BigInteger -> BigInteger - /// Convert a big integer to a floating point number - static member op_Explicit : x:BigInteger -> float - /// Convert a big integer to a 64-bit signed integer - static member op_Explicit : x:BigInteger -> int64 - /// Convert a big integer to a 32-bit signed integer - static member op_Explicit : x:BigInteger -> int32 - /// Parse a big integer from a string format - static member Parse : text:string -> BigInteger - /// Return the sign of a big integer: 0, +1 or -1 - member Sign : int - /// Compute the ratio and remainder of two big integers - static member DivRem : x:BigInteger * y:BigInteger * rem:BigInteger byref -> BigInteger - - /// This operator is for consistency when this type be used from other CLI languages - static member op_LessThan : x:BigInteger * y:BigInteger -> bool - /// This operator is for consistency when this type be used from other CLI languages - static member op_LessThanOrEqual : x:BigInteger * y:BigInteger -> bool - /// This operator is for consistency when this type be used from other CLI languages - static member op_GreaterThan : x:BigInteger * y:BigInteger -> bool - /// This operator is for consistency when this type be used from other CLI languages - static member op_GreaterThanOrEqual : x:BigInteger * y:BigInteger -> bool - /// This operator is for consistency when this type be used from other CLI languages - static member op_Equality : x:BigInteger * y:BigInteger -> bool - /// This operator is for consistency when this type be used from other CLI languages - static member op_Inequality : x:BigInteger * y:BigInteger -> bool - - /// Return the greatest common divisor of two big integers - static member GreatestCommonDivisor : x:BigInteger * y:BigInteger -> BigInteger - /// Return n^m for two big integers - static member Pow : x:BigInteger * y:int32 -> BigInteger - /// Compute the absolute value of a big integer - static member Abs : x:BigInteger -> BigInteger - /// Get the big integer for zero - static member Zero : BigInteger - /// Get the big integer for one - static member One : BigInteger - - /// Return true if a big integer is 'zero' - member IsZero : bool - /// Return true if a big integer is 'one' - member IsOne : bool - interface System.IComparable - override Equals : obj -> bool - override GetHashCode : unit -> int - override ToString : unit -> string - - /// Construct a BigInteger value for the given integer - new : x:int -> BigInteger - /// Construct a BigInteger value for the given 64-bit integer - new : x:int64 -> BigInteger -#if SILVERLIGHT - /// Provides custom formatting for BigInteger values - member StructuredDisplayString : string // This needs to be accessible in order for formatting code to invoke it via reflection in Silverlight. -#endif -#endif - - -namespace Microsoft.FSharp.Core - - type bigint = System.Numerics.BigInteger - - [] - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - module NumericLiterals = - - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - module NumericLiteralI = - open System.Numerics - - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromZero : value:unit -> 'T - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromOne : value:unit -> 'T - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromInt32 : value:int32 -> 'T - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromInt64 : value:int64 -> 'T - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromString : text:string -> 'T - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromInt64Dynamic : value:int64 -> obj - /// Provides a default implementations of F# numeric literal syntax for literals fo the form 'dddI' - val FromStringDynamic : text:string -> obj - - From f5b6500668419f8f2bc8894a6949102dbfe71150 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 4 Dec 2012 09:48:36 +0100 Subject: [PATCH 08/15] Realign complex types with System.Numerics --- MathNet.Numerics.sln.DotSettings | 2 + src/FSharpUnitTests/SparseMatrixTests.fs | 9 +- src/Numerics/Complex32.cs | 1049 ++++++--------- src/Numerics/Complex64.cs | 1197 ++++++----------- src/Numerics/ComplexExtensions.cs | 281 ++-- src/Numerics/Numerics.csproj | 1 + .../TargetedPatchingOptOutAttribute.cs | 16 + src/Portable/Portable.csproj | 3 + .../Complex32Test.TextHandling.cs | 4 +- src/UnitTests/ComplexTests/Complex32Test.cs | 82 +- src/UnitTests/ComplexTests/ComplexTest.cs | 20 +- .../Complex32/VectorTests.cs | 2 +- 12 files changed, 1060 insertions(+), 1606 deletions(-) create mode 100644 src/Numerics/TargetedPatchingOptOutAttribute.cs diff --git a/MathNet.Numerics.sln.DotSettings b/MathNet.Numerics.sln.DotSettings index 1cf7b657..75f0e0a2 100644 --- a/MathNet.Numerics.sln.DotSettings +++ b/MathNet.Numerics.sln.DotSettings @@ -11,5 +11,7 @@ False False True + False + False True False \ No newline at end of file diff --git a/src/FSharpUnitTests/SparseMatrixTests.fs b/src/FSharpUnitTests/SparseMatrixTests.fs index c572a26e..0ebfc195 100644 --- a/src/FSharpUnitTests/SparseMatrixTests.fs +++ b/src/FSharpUnitTests/SparseMatrixTests.fs @@ -12,16 +12,17 @@ module SparseMatrixTests = let smallM = DenseMatrix.init 4 4 (fun i j -> if i = 1 && j = 2 then 1.0 else 0.0) :> Matrix [] - let ``SparseMatrix.ofList`` () = + let ``SparseMatrix.ofList`` () = (SparseMatrix.ofList 4 4 [(1,2,1.0)] :> Matrix) |> should equal smallM [] - let ``SparseMatrix.ofSeq`` () = + let ``SparseMatrix.ofSeq`` () = (SparseMatrix.ofSeq 4 4 (Seq.ofList [(1,2,1.0)]) :> Matrix) |> should equal smallM [] - let ``SparseMatrix.constDiag`` () = + let ``SparseMatrix.constDiag`` () = SparseMatrix.constDiag 100 2.0 |> should equal (2.0 * (SparseMatrix.Identity 100)) - [] let ``SparseMatrix.diag`` () = + [] + let ``SparseMatrix.diag`` () = SparseMatrix.diag (new DenseVector(100, 2.0)) |> should equal (2.0 * (SparseMatrix.Identity 100)) diff --git a/src/Numerics/Complex32.cs b/src/Numerics/Complex32.cs index 85a94285..97b73042 100644 --- a/src/Numerics/Complex32.cs +++ b/src/Numerics/Complex32.cs @@ -28,18 +28,19 @@ // OTHER DEALINGS IN THE SOFTWARE. // + namespace MathNet.Numerics { using System; using System.Collections.Generic; + using System.Globalization; using System.Numerics; using System.Runtime; using System.Runtime.InteropServices; - using System.Text; using Properties; /// - /// 32-bit Complex32 numbers class. + /// 32-bit single precision complex numbers class. /// /// /// @@ -49,18 +50,12 @@ namespace MathNet.Numerics /// canonical way. Additional complex trigonometric functions /// are also provided. Note that the Complex32 structures /// has two special constant values and - /// . - /// - /// - /// In order to avoid possible ambiguities resulting from a - /// Complex32(float, float) constructor, the static methods - /// and - /// are provided instead. + /// . /// /// /// - /// Complex32 x = Complex32.FromRealImaginary(1d, 2d); - /// Complex32 y = Complex32.FromModulusArgument(1d, Math.Pi); + /// Complex32 x = new Complex32(1f,2f); + /// Complex32 y = Complex32.FromPolarCoordinates(1f, Math.Pi); /// Complex32 z = (x + y) / (x - y); /// /// @@ -74,33 +69,6 @@ namespace MathNet.Numerics [StructLayout(LayoutKind.Sequential)] public struct Complex32 : IFormattable, IEquatable, IPrecisionSupport { - #region fields - - /// - /// Represents imaginary unit number. - /// - private static readonly Complex32 _i = new Complex32(0, 1); - - /// - /// Represents a infinite complex number - /// - private static readonly Complex32 _infinity = new Complex32(float.PositiveInfinity, float.PositiveInfinity); - - /// - /// Represents not-a-number. - /// - private static readonly Complex32 _nan = new Complex32(float.NaN, float.NaN); - - /// - /// Representing the one value. - /// - private static readonly Complex32 _one = new Complex32(1.0f, 0.0f); - - /// - /// Representing the zero value. - /// - private static readonly Complex32 _zero = new Complex32(0.0f, 0.0f); - /// /// The real component of the complex number. /// @@ -111,102 +79,79 @@ namespace MathNet.Numerics /// private readonly float _imag; - #endregion fields - - #region Constructor - /// /// Initializes a new instance of the Complex32 structure with the given real /// and imaginary parts. /// - /// - /// The value for the real component. - /// - /// - /// The value for the imaginary component. - /// -#if !PORTABLE + /// The value for the real component. + /// The value for the imaginary component. [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif public Complex32(float real, float imaginary) { _real = real; _imag = imaginary; } - #endregion - - #region Properties - /// - /// Gets a value representing the infinity value. This field is constant. + /// Creates a complex number from a point's polar coordinates. /// - /// The infinity. - /// - /// The semantic associated to this value is a Complex32 of - /// infinite real and imaginary part. If you need more formal complex - /// number handling (according to the Riemann Sphere and the extended - /// complex plane C*, or using directed infinity) please check out the - /// alternative Math.NET symbolics packages instead. - /// - /// A value representing the infinity value. - public static Complex32 Infinity + /// A complex number. + /// The magnitude, which is the distance from the origin (the intersection of the x-axis and the y-axis) to the number. + /// The phase, which is the angle from the line to the horizontal axis, measured in radians. + public static Complex32 FromPolarCoordinates(float magnitude, float phase) { - get - { - return _infinity; - } + return new Complex32(magnitude * (float)Math.Cos(phase), magnitude * (float)Math.Sin(phase)); } - /// - /// Gets a value representing not-a-number. This field is constant. - /// - /// A value representing not-a-number. - public static Complex32 NaN + [Obsolete("Use the public constructor instead.")] + public static Complex32 WithRealImaginary(float real, float imaginary) { - get - { - return _nan; - } + return new Complex32(real, imaginary); } - /// - /// Gets a value representing the imaginary unit number. This field is constant. - /// - /// A value representing the imaginary unit number. - public static Complex32 ImaginaryOne + [Obsolete("Use static FromPolarCoordinates instead.")] + public static Complex32 WithModulusArgument(float modulus, float argument) { - get + if (modulus < 0.0f) { - return _i; + throw new ArgumentOutOfRangeException("modulus", Resources.ArgumentNotNegative); } + + return new Complex32(modulus * (float)Math.Cos(argument), modulus * (float)Math.Sin(argument)); } /// - /// Gets a value representing the zero value. This field is constant. + /// Returns a new instance + /// with a real number equal to zero and an imaginary number equal to zero. /// - /// A value representing the zero value. - public static Complex32 Zero - { - get - { - return new Complex32(0.0f, 0.0f); - } - } + public static readonly Complex32 Zero = new Complex32(0.0f, 0.0f); /// - /// Gets a value representing the 1 value. This field is constant. + /// Returns a new instance + /// with a real number equal to one and an imaginary number equal to zero. /// - /// A value representing the 1 value. - public static Complex32 One - { - get - { - return _one; - } - } + public static readonly Complex32 One = new Complex32(1.0f, 0.0f); + + /// + /// Returns a new instance + /// with a real number equal to zero and an imaginary number equal to one. + /// + public static readonly Complex32 ImaginaryOne = new Complex32(0, 1); + + /// + /// Returns a new instance + /// with real and imaginary numbers positive infinite. + /// + public static readonly Complex32 PositiveInfinity = new Complex32(float.PositiveInfinity, float.PositiveInfinity); + + [Obsolete("Use PositiveInfinity instead")] + public static readonly Complex32 Infinity = PositiveInfinity; - #endregion Properties + /// + /// Returns a new instance + /// with real and imaginary numbers not a number. + /// + public static readonly Complex32 NaN = new Complex32(float.NaN, float.NaN); /// /// Gets the real component of the complex number. @@ -214,13 +159,8 @@ namespace MathNet.Numerics /// The real component of the complex number. public float Real { -#if !PORTABLE [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - get - { - return _real; - } + get { return _real; } } /// @@ -229,12 +169,80 @@ namespace MathNet.Numerics /// The real imaginary component of the complex number. public float Imaginary { -#if !PORTABLE [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - get + get { return _imag; } + } + + /// + /// Gets the phase or argument of this Complex32. + /// + /// + /// Phase always returns a value bigger than negative Pi and + /// smaller or equal to Pi. If this Complex32 is zero, the Complex32 + /// is assumed to be positive real with an argument of zero. + /// + /// The phase or argument of this Complex32 + public float Phase + { + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + get { return (float)Math.Atan2(_imag, _real); } + } + + /// + /// Gets the magnitude (or absolute value) of a complex number. + /// + /// The magnitude of the current instance. + public float Magnitude + { + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + get { return (float)Math.Sqrt((_real * _real) + (_imag * _imag)); } + } + + /// + /// Gets the squared magnitude (or squared absolute value) of a complex number. + /// + /// The squared magnitude of the current instance. + public float MagnitudeSquared + { + get { return (_real * _real) + (_imag * _imag); } + } + + /// + /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg)) + /// + /// The unity of this Complex32. + public Complex32 Sign + { + get { - return _imag; + if (float.IsPositiveInfinity(_real) && float.IsPositiveInfinity(_imag)) + { + return new Complex32((float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2); + } + + if (float.IsPositiveInfinity(_real) && float.IsNegativeInfinity(_imag)) + { + return new Complex32((float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2); + } + + if (float.IsNegativeInfinity(_real) && float.IsPositiveInfinity(_imag)) + { + return new Complex32(-(float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2); + } + + if (float.IsNegativeInfinity(_real) && float.IsNegativeInfinity(_imag)) + { + return new Complex32(-(float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2); + } + + // don't replace this with "Magnitude"! + var mod = SpecialFunctions.Hypotenuse(_real, _imag); + if (mod == 0.0f) + { + return Zero; + } + + return new Complex32(_real / mod, _imag / mod); } } @@ -314,115 +322,6 @@ namespace MathNet.Numerics return _imag == 0.0f && _real >= 0; } - /// - /// Gets the conjugate of this Complex32. - /// - /// - /// The semantic of setting the conjugate is such that - /// - /// // a, b of type Complex32 - /// a.Conjugate = b; - /// - /// is equivalent to - /// - /// // a, b of type Complex32 - /// a = b.Conjugate - /// - /// - /// The conjugate of this Complex32 - public Complex32 Conjugate() - { - return new Complex32(_real, -_imag); - } - - /// - /// Gets the magnitude or modulus of this Complex32. - /// - /// The magnitude or modulus of this Complex32 - /// - public float Magnitude - { - get - { - return (float)Math.Sqrt((_real * _real) + (_imag * _imag)); - } - } - - /// - /// Gets the squared magnitude of this Complex32. - /// - /// The squared magnitude of this Complex32 - public float MagnitudeSquared - { - get - { - return (_real * _real) + (_imag * _imag); - } - } - - /// - /// Gets the phase or argument of this Complex32. - /// - /// - /// Phase always returns a value bigger than negative Pi and - /// smaller or equal to Pi. If this Complex32 is zero, the Complex32 - /// is assumed to be positive real with an argument of zero. - /// - /// The phase or argument of this Complex32 - public float Phase - { - get - { - if (IsReal() && _real < 0) - { - return (float)Math.PI; - } - - return IsRealNonNegative() ? 0.0f : (float)Math.Atan2(_imag, _real); - } - } - - /// - /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg)) - /// - /// The unity of this Complex32. - public Complex32 Sign - { - get - { - if (float.IsPositiveInfinity(_real) && float.IsPositiveInfinity(_imag)) - { - return new Complex32((float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2); - } - - if (float.IsPositiveInfinity(_real) && float.IsNegativeInfinity(_imag)) - { - return new Complex32((float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2); - } - - if (float.IsNegativeInfinity(_real) && float.IsPositiveInfinity(_imag)) - { - return new Complex32(-(float)Constants.Sqrt1Over2, -(float)Constants.Sqrt1Over2); - } - - if (float.IsNegativeInfinity(_real) && float.IsNegativeInfinity(_imag)) - { - return new Complex32(-(float)Constants.Sqrt1Over2, (float)Constants.Sqrt1Over2); - } - - // don't replace this with "Magnitude"! - var mod = SpecialFunctions.Hypotenuse(_real, _imag); - if (mod == 0.0f) - { - return Zero; - } - - return new Complex32(_real / mod, _imag / mod); - } - } - - #region Exponential Functions - /// /// Exponential of this Complex32 (exp(x), E^x). /// @@ -437,15 +336,13 @@ namespace MathNet.Numerics return new Complex32(exp, 0.0f); } - return new Complex32(exp * (float)Trig.Cosine(_imag), exp * (float)Trig.Sine(_imag)); + return new Complex32(exp * (float)Math.Cos(_imag), exp * (float)Math.Sin(_imag)); } /// /// Natural Logarithm of this Complex32 (Base E). /// - /// - /// The natural logarithm of this complex number. - /// + /// The natural logarithm of this complex number. public Complex32 NaturalLogarithm() { if (IsRealNonNegative()) @@ -456,6 +353,24 @@ namespace MathNet.Numerics return new Complex32(0.5f * (float)Math.Log(MagnitudeSquared), Phase); } + /// + /// Common Logarithm of this Complex32 (Base 10). + /// + /// The common logarithm of this complex number. + public Complex32 CommonLogarithm() + { + return NaturalLogarithm() / (float)Constants.Ln10; + } + + /// + /// Logarithm of this Complex32 with custom base. + /// + /// The logarithm of this complex number. + public Complex32 Logarithm(float baseValue) + { + return NaturalLogarithm() / (float)Math.Log(baseValue); + } + /// /// Raise this Complex32 to the given value. /// @@ -474,19 +389,16 @@ namespace MathNet.Numerics return One; } - if (exponent.Real > 0.0f) + if (exponent.Real > 0f) { return Zero; } - if (exponent.Real < 0) + if (exponent.Real < 0f) { - if (exponent.Imaginary == 0.0f) - { - return new Complex32(float.PositiveInfinity, 0.0f); - } - - return new Complex32(float.PositiveInfinity, float.PositiveInfinity); + return exponent.Imaginary == 0f + ? new Complex32(float.PositiveInfinity, 0f) + : new Complex32(float.PositiveInfinity, float.PositiveInfinity); } return NaN; @@ -564,192 +476,11 @@ namespace MathNet.Numerics } else { - result = new Complex32((float)(absImag / (2.0 * w)), (float)-w); - } - - return result; - } - - #endregion - - #region Static Initializers - - /// - /// Constructs a Complex32 from its real - /// and imaginary parts. - /// - /// - /// The value for the real component. - /// - /// - /// The value for the imaginary component. - /// - /// - /// A new Complex32 with the given values. - /// - public static Complex32 WithRealImaginary(float real, float imaginary) - { - return new Complex32(real, imaginary); - } - - /// - /// Constructs a Complex32 from its modulus and - /// argument. - /// - /// - /// Must be non-negative. - /// - /// - /// Real number. - /// - /// - /// A new Complex32 from the given values. - /// - public static Complex32 WithModulusArgument(float modulus, float argument) - { - if (modulus < 0.0f) - { - throw new ArgumentOutOfRangeException("modulus", Resources.ArgumentNotNegative); - } - - return new Complex32(modulus * (float)Math.Cos(argument), modulus * (float)Math.Sin(argument)); - } - - #endregion - - #region IFormattable Members - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number. - /// - public override string ToString() - { - return ToString(null, null); - } - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number formatted as specified by the - /// format string. - /// - /// - /// A format specification. - /// - public string ToString(string format) - { - return ToString(format, null); - } - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number formatted as specified by the - /// format provider. - /// - /// - /// An that supplies culture-specific formatting information. - /// - public string ToString(IFormatProvider formatProvider) - { - return ToString(null, formatProvider); - } - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number formatted as specified by the - /// format string and format provider. - /// - /// - /// if the n, is not a number. - /// - /// - /// if s, is . - /// - /// - /// A format specification. - /// - /// - /// An that supplies culture-specific formatting information. - /// - public string ToString(string format, IFormatProvider formatProvider) - { - var ret = new StringBuilder(); - ret.Append("(").Append(_real.ToString(format, formatProvider)).Append(", ").Append(_imag.ToString(format, formatProvider)).Append(")"); - return ret.ToString(); - } - - #endregion - - #region IEquatable Members - - /// - /// Checks if two complex numbers are equal. Two complex numbers are equal if their - /// corresponding real and imaginary components are equal. - /// - /// - /// Returns true if the two objects are the same object, or if their corresponding - /// real and imaginary components are equal, false otherwise. - /// - /// - /// The complex number to compare to with. - /// - public bool Equals(Complex32 other) - { - if (IsNaN() || other.IsNaN()) - { - return false; - } - - if (IsInfinity() && other.IsInfinity()) - { - return true; - } - - return _real.AlmostEqual(other._real) && _imag.AlmostEqual(other._imag); - } - - /// - /// The hash code for the complex number. - /// - /// - /// The hash code of the complex number. - /// - /// - /// The hash code is calculated as - /// System.Math.Exp(ComplexMath.Absolute(complexNumber)). - /// - public override int GetHashCode() - { - return _real.GetHashCode() ^ (-_imag.GetHashCode()); - } - - /// - /// Checks if two complex numbers are equal. Two complex numbers are equal if their - /// corresponding real and imaginary components are equal. - /// - /// - /// Returns true if the two objects are the same object, or if their corresponding - /// real and imaginary components are equal, false otherwise. - /// - /// - /// The complex number to compare to with. - /// - public override bool Equals(object obj) - { - return (obj is Complex32) && Equals((Complex32)obj); - } - - #endregion + result = new Complex32((float)(absImag / (2.0 * w)), (float)-w); + } - #region Operators + return result; + } /// /// Equality test. @@ -854,7 +585,7 @@ namespace MathNet.Numerics public static Complex32 operator *(Complex32 multiplicand, Complex32 multiplier) { return new Complex32( - (multiplicand._real * multiplier._real) - (multiplicand._imag * multiplier._imag), + (multiplicand._real * multiplier._real) - (multiplicand._imag * multiplier._imag), (multiplicand._real * multiplier._imag) + (multiplicand._imag * multiplier._real)); } @@ -889,12 +620,12 @@ namespace MathNet.Numerics if (divisor.IsZero()) { - return Infinity; + return PositiveInfinity; } var modSquared = divisor.MagnitudeSquared; return new Complex32( - ((dividend._real * divisor._real) + (dividend._imag * divisor._imag)) / modSquared, + ((dividend._real * divisor._real) + (dividend._imag * divisor._imag)) / modSquared, ((dividend._imag * divisor._real) - (dividend._real * divisor._imag)) / modSquared); } @@ -911,7 +642,7 @@ namespace MathNet.Numerics if (divisor.IsZero()) { - return Infinity; + return PositiveInfinity; } var zmod = divisor.MagnitudeSquared; @@ -931,106 +662,182 @@ namespace MathNet.Numerics if (divisor == 0.0f) { - return Infinity; + return PositiveInfinity; } return new Complex32(dividend._real / divisor, dividend._imag / divisor); } - /// - /// Unary addition. - /// - /// - /// Returns the same complex number. - /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif + [Obsolete("No Operation")] public Complex32 Plus() { return this; } - /// - /// Unary minus. - /// - /// - /// The negated value of this complex number. - /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif + [Obsolete("Use static Complex32.Negate or the unary - operator instead.")] public Complex32 Negate() { return -this; } /// - /// Adds a complex number to this one. + /// Computes the conjugate of a complex number and returns the result. /// - /// - /// The result of the addition. - /// - /// - /// The other complex number to add. - /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif + public Complex32 Conjugate() + { + return new Complex32(_real, -_imag); + } + + /// + /// Returns the multiplicative inverse of a complex number. + /// + public Complex32 Reciprocal() + { + if (IsZero()) + { + return Zero; + } + + return 1.0f / this; + } + + [Obsolete("Use static Complex32.Add or the + operator instead.")] public Complex32 Add(Complex32 other) { return this + other; } + [Obsolete("Use static Complex32.Subtract or the - operator instead.")] + public Complex32 Subtract(Complex32 other) + { + return this - other; + } + + [Obsolete("Use static Complex32.Multiply or the * operator instead.")] + public Complex32 Multiply(Complex32 multiplier) + { + return this * multiplier; + } + + [Obsolete("Use static Complex32.Divide or the / operator instead.")] + public Complex32 Divide(Complex32 divisor) + { + return this / divisor; + } + + #region IFormattable Members + + /// + /// Converts the value of the current complex number to its equivalent string representation in Cartesian form. + /// + /// The string representation of the current instance in Cartesian form. + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "({0}, {1})", _real, _imag); + } + + /// + /// Converts the value of the current complex number to its equivalent string representation + /// in Cartesian form by using the specified format for its real and imaginary parts. + /// + /// The string representation of the current instance in Cartesian form. + /// A standard or custom numeric format string. + /// + /// is not a valid format string. + public string ToString(string format) + { + return string.Format(CultureInfo.CurrentCulture, "({0}, {1})", + _real.ToString(format, CultureInfo.CurrentCulture), + _imag.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Converts the value of the current complex number to its equivalent string representation + /// in Cartesian form by using the specified culture-specific formatting information. + /// + /// The string representation of the current instance in Cartesian form, as specified by . + /// An object that supplies culture-specific formatting information. + public string ToString(IFormatProvider provider) + { + return string.Format(provider, "({0}, {1})", _real, _imag); + } + + /// Converts the value of the current complex number to its equivalent string representation + /// in Cartesian form by using the specified format and culture-specific format information for its real and imaginary parts. + /// The string representation of the current instance in Cartesian form, as specified by and . + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// + /// is not a valid format string. + public string ToString(string format, IFormatProvider provider) + { + return string.Format(provider, "({0}, {1})", + _real.ToString(format, provider), + _imag.ToString(format, provider)); + } + + #endregion + + #region IEquatable Members + /// - /// Subtracts a complex number from this one. + /// Checks if two complex numbers are equal. Two complex numbers are equal if their + /// corresponding real and imaginary components are equal. /// /// - /// The result of the subtraction. + /// Returns true if the two objects are the same object, or if their corresponding + /// real and imaginary components are equal, false otherwise. /// /// - /// The other complex number to subtract from this one. + /// The complex number to compare to with. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex32 Subtract(Complex32 other) + public bool Equals(Complex32 other) { - return this - other; + if (IsNaN() || other.IsNaN()) + { + return false; + } + + if (IsInfinity() && other.IsInfinity()) + { + return true; + } + + return _real.AlmostEqual(other._real) && _imag.AlmostEqual(other._imag); } /// - /// Multiplies this complex number with this one. + /// The hash code for the complex number. /// /// - /// The result of the multiplication. + /// The hash code of the complex number. /// - /// - /// The complex number to multiply. - /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex32 Multiply(Complex32 multiplier) + /// + /// The hash code is calculated as + /// System.Math.Exp(ComplexMath.Absolute(complexNumber)). + /// + public override int GetHashCode() { - return this * multiplier; + int hash = 27; + hash = (13 * hash) + _real.GetHashCode(); + hash = (13 * hash) + _imag.GetHashCode(); + return hash; } /// - /// Divides this complex number by another. + /// Checks if two complex numbers are equal. Two complex numbers are equal if their + /// corresponding real and imaginary components are equal. /// /// - /// The result of the division. + /// Returns true if the two objects are the same object, or if their corresponding + /// real and imaginary components are equal, false otherwise. /// - /// - /// The divisor. + /// + /// The complex number to compare to with. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex32 Divide(Complex32 divisor) + public override bool Equals(object obj) { - return this / divisor; + return (obj is Complex32) && Equals((Complex32)obj); } #endregion @@ -1304,12 +1111,12 @@ namespace MathNet.Numerics } catch (ArgumentNullException) { - result = _zero; + result = Zero; ret = false; } catch (FormatException) { - result = _zero; + result = Zero; ret = false; } @@ -1468,243 +1275,265 @@ namespace MathNet.Numerics #endregion /// - /// Gets the absolute value (or magnitude) of a complex number. + /// Returns the additive inverse of a specified complex number. /// + /// The result of the and components of the parameter multiplied by -1. /// A complex number. - /// The absolute value (or magnitude) of a complex number. - public static double Abs(Complex32 value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Negate(Complex32 value) { - return value.Magnitude; + return -value; } /// - /// Trigonometric Arc Cosine of a Complex number. + /// Computes the conjugate of a complex number and returns the result. /// + /// The conjugate of . /// A complex number. - /// - /// The arc cosine of a complex number. - /// - public static Complex32 Acos(Complex32 value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Conjugate(Complex32 value) { - return (Complex32)value.ToComplex().InverseCosine(); + return value.Conjugate(); } /// - /// Trigonometric Arc Sine of a Complex number. + /// Adds two complex numbers and returns the result. /// - /// A complex number. - /// - /// The arc sine of a complex number. - /// - public static Complex32 Asin(Complex32 value) + /// The sum of and . + /// The first complex number to add. + /// The second complex number to add. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Add(Complex32 left, Complex32 right) { - return (Complex32)value.ToComplex().InverseSine(); + return left + right; } /// - /// Trigonometric Arc Tangent of a Complex number. + /// Subtracts one complex number from another and returns the result. /// - /// A complex number. - /// - /// The arc tangent of a complex number. - /// - public static Complex32 Atan(Complex32 value) + /// The result of subtracting from . + /// The value to subtract from (the minuend). + /// The value to subtract (the subtrahend). + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Subtract(Complex32 left, Complex32 right) { - return (Complex32)value.ToComplex().InverseTangent(); + return left - right; } /// - /// Trigonometric Cosine of a Complex number. + /// Returns the product of two complex numbers. /// - /// A complex number. - /// - /// The cosine of a complex number. - /// - public static Complex32 Cos(Complex32 value) + /// The product of the and parameters. + /// The first complex number to multiply. + /// The second complex number to multiply. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Multiply(Complex32 left, Complex32 right) { - return (Complex32)value.ToComplex().Cosine(); + return left * right; } /// - /// Trigonometric Sine of a Complex number. + /// Divides one complex number by another and returns the result. /// - /// A complex number. - /// - /// The Sine of a complex number. - /// - public static Complex32 Sin(Complex32 value) + /// The quotient of the division. + /// The complex number to be divided. + /// The complex number to divide by. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Divide(Complex32 dividend, Complex32 divisor) { - return (Complex32)value.ToComplex().Sine(); + return dividend / divisor; } /// - /// Trigonometric Tangent of a Complex number. + /// Returns the multiplicative inverse of a complex number. /// + /// The reciprocal of . /// A complex number. - /// - /// The tangent of a complex number. - /// - public static Complex32 Tan(Complex32 value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Reciprocal(Complex32 value) { - return (Complex32)value.ToComplex().Tangent(); + return value.Reciprocal(); } /// - /// Trigonometric Hyperbolic Cosine of a Complex number. + /// Returns the square root of a specified complex number. /// + /// The square root of . /// A complex number. - /// - /// The hyperbolic cosine of a complex number. - /// - public static Complex32 Cosh(Complex32 value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Sqrt(Complex32 value) { - return (Complex32)value.ToComplex().HyperbolicCosine(); + return value.SquareRoot(); } /// - /// Trigonometric Hyperbolic Sine of a Complex number. + /// Gets the absolute value (or magnitude) of a complex number. /// + /// The absolute value of . /// A complex number. - /// - /// The hyperbolic sine of a complex number. - /// - public static Complex32 Sinh(Complex32 value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static double Abs(Complex32 value) { - return (Complex32)value.ToComplex().HyperbolicSine(); + return value.Magnitude; } /// - /// Trigonometric Hyperbolic Tangent of a Complex number. + /// Returns e raised to the power specified by a complex number. /// - /// A complex number. - /// - /// The hyperbolic tangent of a complex number. - /// - public static Complex32 Tanh(Complex32 value) + /// The number e raised to the power . + /// A complex number that specifies a power. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Exp(Complex32 value) { - return (Complex32)value.ToComplex().HyperbolicTangent(); + return value.Exponential(); } /// - /// Exponential of a Complex number (exp(x), E^x). + /// Returns a specified complex number raised to a power specified by a complex number. /// - /// A complex number. - /// - /// The exponential of a complex number. - /// - public static Complex32 Exp(Complex32 value) + /// The complex number raised to the power . + /// A complex number to be raised to a power. + /// A complex number that specifies a power. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Pow(Complex32 value, Complex32 power) { - return (Complex32)value.ToComplex().Exponential(); + return value.Power(power); } /// - /// Constructs a Complex from its magnitude and phase. + /// Returns a specified complex number raised to a power specified by a single-precision floating-point number. /// - /// - /// Must be non-negative. - /// - /// - /// Real number. - /// - /// - /// A new Complex from the given values. - /// - /// - public static Complex32 FromPolarCoordinates(float magnitude, float phase) + /// The complex number raised to the power . + /// A complex number to be raised to a power. + /// A single-precision floating-point number that specifies a power. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex32 Pow(Complex32 value, float power) { - return WithModulusArgument(magnitude, phase); + return value.Power(power); } /// - /// Natural Logarithm of a Complex number (exp(x), E^x). + /// Returns the natural (base e) logarithm of a specified complex number. /// + /// The natural (base e) logarithm of . /// A complex number. - /// - /// The natural logarithm of a complex number. - /// + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex32 Log(Complex32 value) { - return (Complex32)value.ToComplex().NaturalLogarithm(); + return value.NaturalLogarithm(); } /// - /// Returns the logarithm of a specified complex number in a specified base + /// Returns the logarithm of a specified complex number in a specified base. /// + /// The logarithm of in base . /// A complex number. /// The base of the logarithm. - /// The logarithm of value in base baseValue. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex32 Log(Complex32 value, float baseValue) { - if (baseValue == 1.0) - { - return float.NaN; - } - - return (Complex32)(value.ToComplex().NaturalLogarithm() / Math.Log(baseValue, Math.E)); + return value.Logarithm(baseValue); } /// - /// Returns the base-10 logarithm of a specified complex number in a specified base + /// Returns the base-10 logarithm of a specified complex number. /// + /// The base-10 logarithm of . /// A complex number. - /// The base-10 logarithm of the complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex32 Log10(Complex32 value) { - return Log(value, 10); + return value.CommonLogarithm(); } /// - /// Raise this a Complexnumber to the given value. + /// Returns the sine of the specified complex number. /// + /// The sine of . /// A complex number. - /// The exponent. - /// - /// The complex number raised to the given exponent. - /// - public static Complex32 Pow(Complex32 value, Complex32 power) + public static Complex32 Sin(Complex32 value) { - return value.Power(power); + return (Complex32)Trig.Sine(value.ToComplex()); } /// - /// Raise this a Complexnumber to the given value. + /// Returns the cosine of the specified complex number. /// + /// The cosine of . /// A complex number. - /// The exponent. - /// - /// The complex number raised to the given exponent. - /// - public static Complex32 Pow(Complex32 value, float power) + public static Complex32 Cos(Complex32 value) { - return value.Power(power); + return (Complex32)Trig.Cosine(value.ToComplex()); } /// - /// Returns the multiplicative inverse of a complex number. + /// Returns the tangent of the specified complex number. /// + /// The tangent of . /// A complex number. - /// The reciprocal of value. - /// If value is , the method returns . Otherwise, it returns the result of the expression / value. - public static Complex32 Reciprocal(Complex32 value) + public static Complex32 Tan(Complex32 value) { - if (value.IsZero()) - { - return _zero; - } + return (Complex32)Trig.Tangent(value.ToComplex()); + } + + /// + /// Returns the angle that is the arc sine of the specified complex number. + /// + /// The angle which is the arc sine of . + /// A complex number. + public static Complex32 Asin(Complex32 value) + { + return (Complex32)Trig.InverseSine(value.ToComplex()); + } - return 1.0f / value; + /// + /// Returns the angle that is the arc cosine of the specified complex number. + /// + /// The angle, measured in radians, which is the arc cosine of . + /// A complex number that represents a cosine. + public static Complex32 Acos(Complex32 value) + { + return (Complex32)Trig.InverseCosine(value.ToComplex()); } /// - /// The Square Root (power 1/2) of a Complex number. + /// Returns the angle that is the arc tangent of the specified complex number. /// + /// The angle that is the arc tangent of . /// A complex number. - /// - /// The square root of a complex number. - /// - public static Complex32 Sqrt(Complex32 value) + public static Complex32 Atan(Complex32 value) { - return value.SquareRoot(); + return (Complex32)Trig.InverseTangent(value.ToComplex()); + } + + /// + /// Returns the hyperbolic sine of the specified complex number. + /// + /// The hyperbolic sine of . + /// A complex number. + public static Complex32 Sinh(Complex32 value) + { + return (Complex32)Trig.HyperbolicSine(value.ToComplex()); + } + + /// + /// Returns the hyperbolic cosine of the specified complex number. + /// + /// The hyperbolic cosine of . + /// A complex number. + public static Complex32 Cosh(Complex32 value) + { + return (Complex32)Trig.HyperbolicCosine(value.ToComplex()); + } + + /// + /// Returns the hyperbolic tangent of the specified complex number. + /// + /// The hyperbolic tangent of . + /// A complex number. + public static Complex32 Tanh(Complex32 value) + { + return (Complex32)Trig.HyperbolicTangent(value.ToComplex()); } } } \ No newline at end of file diff --git a/src/Numerics/Complex64.cs b/src/Numerics/Complex64.cs index ec16c13f..14f4f2c4 100644 --- a/src/Numerics/Complex64.cs +++ b/src/Numerics/Complex64.cs @@ -29,19 +29,21 @@ // #if !SYSNUMERICS -using MathNet.Numerics; - namespace System.Numerics { - using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; + using MathNet.Numerics; using MathNet.Numerics.Properties; +#if !PORTABLE + using System.Runtime; +#endif + /// - /// 64-bit Complex numbers class. + /// 64-bit double precision complex numbers class. /// /// /// @@ -51,18 +53,12 @@ namespace System.Numerics /// canonical way. Additional complex trigonometric functions /// are also provided. Note that the Complex structures /// has two special constant values and - /// . - /// - /// - /// In order to avoid possible ambiguities resulting from a - /// Complex(double, double) constructor, the static methods - /// and - /// are provided instead. + /// . /// /// /// - /// Complex x = Complex.FromRealImaginary(1d, 2d); - /// Complex y = Complex.FromModulusArgument(1d, Math.Pi); + /// Complex x = new Complex(1d, 2d); + /// Complex y = Complex.FromPolarCoordinates(1d, Math.Pi); /// Complex z = (x + y) / (x - y); /// /// @@ -76,33 +72,6 @@ namespace System.Numerics [StructLayout(LayoutKind.Sequential)] public struct Complex : IFormattable, IEquatable, IPrecisionSupport { - #region fields - - /// - /// Represents imaginary unit number. - /// - private static readonly Complex _i = new Complex(0, 1); - - /// - /// Represents a infinite complex number - /// - private static readonly Complex _infinity = new Complex(double.PositiveInfinity, double.PositiveInfinity); - - /// - /// Represents not-a-number. - /// - private static readonly Complex _nan = new Complex(double.NaN, double.NaN); - - /// - /// Representing the one value. - /// - private static readonly Complex _one = new Complex(1.0f, 0.0d); - - /// - /// Representing the zero value. - /// - private static readonly Complex _zero = new Complex(0.0d, 0.0d); - /// /// The real component of the complex number. /// @@ -113,253 +82,98 @@ namespace System.Numerics /// private readonly double _imag; - #endregion fields - - #region Constructor - /// /// Initializes a new instance of the Complex structure with the given real /// and imaginary parts. /// - /// - /// The value for the real component. - /// - /// - /// The value for the imaginary component. - /// -#if !PORTABLE + /// The value for the real component. + /// The value for the imaginary component. [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif public Complex(double real, double imaginary) { _real = real; _imag = imaginary; } - #endregion - - #region Properties - /// - /// Gets a value representing the infinity value. This field is constant. + /// Creates a complex number from a point's polar coordinates. /// - /// The infinity. - /// - /// The semantic associated to this value is a Complex of - /// infinite real and imaginary part. If you need more formal complex - /// number handling (according to the Riemann Sphere and the extended - /// complex plane C*, or using directed infinity) please check out the - /// alternative Math.NET symbolics packages instead. - /// - /// A value representing the infinity value. - public static Complex Infinity - { - get - { - return _infinity; - } - } - - /// - /// Gets a value representing not-a-number. This field is constant. - /// - /// A value representing not-a-number. - public static Complex NaN - { - get - { - return _nan; - } - } - - /// - /// Gets a value representing the imaginary unit number. This field is constant. - /// - /// A value representing the imaginary unit number. - public static Complex ImaginaryOne - { - get - { - return _i; - } - } - - /// - /// Gets a value representing the zero value. This field is constant. - /// - /// A value representing the zero value. - public static Complex Zero + /// A complex number. + /// The magnitude, which is the distance from the origin (the intersection of the x-axis and the y-axis) to the number. + /// The phase, which is the angle from the line to the horizontal axis, measured in radians. + public static Complex FromPolarCoordinates(double magnitude, double phase) { - get - { - return new Complex(0.0d, 0.0d); - } + return new Complex(magnitude * Math.Cos(phase), magnitude * Math.Sin(phase)); } - /// - /// Gets a value representing the 1 value. This field is constant. - /// - /// A value representing the 1 value. - public static Complex One + [Obsolete("Use the public constructor instead.")] + public static Complex WithRealImaginary(double real, double imaginary) { - get - { - return _one; - } + return new Complex(real, imaginary); } - #endregion Properties - - /// - /// Gets the real component of the complex number. - /// - /// The real component of the complex number. - public double Real + [Obsolete("Use static FromPolarCoordinates instead.")] + public static Complex WithModulusArgument(double modulus, double argument) { -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - get + if (modulus < 0.0d) { - return _real; + throw new ArgumentOutOfRangeException("modulus", Resources.ArgumentNotNegative); } - } - /// - /// Gets the real imaginary component of the complex number. - /// - /// The real imaginary component of the complex number. - public double Imaginary - { -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - get - { - return _imag; - } + return new Complex(modulus * Math.Cos(argument), modulus * Math.Sin(argument)); } /// - /// Gets a value indicating whether the Complex is zero. + /// Returns a new instance + /// with a real number equal to zero and an imaginary number equal to zero. /// - /// true if this instance is zero; otherwise, false. - public bool IsZero() - { - return _real == 0.0d && _imag == 0.0d; - } + public static readonly Complex Zero = new Complex(0.0f, 0.0f); /// - /// Gets a value indicating whether the Complex is one. + /// Returns a new instance + /// with a real number equal to one and an imaginary number equal to zero. /// - /// true if this instance is one; otherwise, false. - public bool IsOne() - { - return _real == 1.0d && _imag == 0.0d; - } + public static readonly Complex One = new Complex(1.0f, 0.0f); /// - /// Gets a value indicating whether the Complex is the imaginary unit. + /// Returns a new instance + /// with a real number equal to zero and an imaginary number equal to one. /// - /// true if this instance is ImaginaryOne; otherwise, false. - public bool IsImaginaryOne() - { - return _real == 0.0d && _imag == 1.0d; - } + public static readonly Complex ImaginaryOne = new Complex(0, 1); /// - /// Gets a value indicating whether the provided Complexevaluates - /// to a value that is not a number. + /// Returns a new instance + /// with real and imaginary numbers positive infinite. /// - /// - /// true if this instance is ; otherwise, - /// false. - /// - public bool IsNaN() - { - return double.IsNaN(_real) || double.IsNaN(_imag); - } + public static readonly Complex PositiveInfinity = new Complex(float.PositiveInfinity, float.PositiveInfinity); - /// - /// Gets a value indicating whether the provided Complex evaluates to an - /// infinite value. - /// - /// - /// true if this instance is infinite; otherwise, false. - /// - /// - /// True if it either evaluates to a complex infinity - /// or to a directed infinity. - /// - public bool IsInfinity() - { - return double.IsInfinity(_real) || double.IsInfinity(_imag); - } + [Obsolete("Use PositiveInfinity instead")] + public static readonly Complex Infinity = PositiveInfinity; /// - /// Gets a value indicating whether the provided Complex is real. + /// Returns a new instance + /// with real and imaginary numbers not a number. /// - /// true if this instance is a real number; otherwise, false. - public bool IsReal() - { - return _imag == 0.0d; - } + public static readonly Complex NaN = new Complex(float.NaN, float.NaN); /// - /// Gets a value indicating whether the provided Complex is real and not negative, that is >= 0. - /// - /// - /// true if this instance is real nonnegative number; otherwise, false. - /// - public bool IsRealNonNegative() - { - return _imag == 0.0d && _real >= 0; - } - - /// - /// Gets the conjugate of this Complex. - /// - /// - /// The semantic of setting the conjugate is such that - /// - /// // a, b of type Complex - /// a.Conjugate = b; - /// - /// is equivalent to - /// - /// // a, b of type Complex - /// a = b.Conjugate - /// - /// - /// The conjugate of this Complex - public Complex Conjugate() - { - return new Complex(_real, -_imag); - } - - /// - /// Gets the magnitude or modulus of this Complex. + /// Gets the real component of the complex number. /// - /// The magnitude or modulus of this Complex - /// - public double Magnitude + /// The real component of the complex number. + public double Real { - get - { - return Math.Sqrt((_real * _real) + (_imag * _imag)); - } + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + get { return _real; } } /// - /// Gets the squared magnitude of this Complex. + /// Gets the real imaginary component of the complex number. /// - /// The squared magnitude of this Complex - public double MagnitudeSquared + /// The real imaginary component of the complex number. + public double Imaginary { - get - { - return (_real * _real) + (_imag * _imag); - } + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + get { return _imag; } } /// @@ -373,386 +187,20 @@ namespace System.Numerics /// The phase or argument of this Complex public double Phase { - get - { - if (IsReal() && _real < 0) - { - return Math.PI; - } - - return IsRealNonNegative() ? 0.0d : Math.Atan2(_imag, _real); - } - } - - /// - /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg)) - /// - /// The unity of this Complex. - public Complex Sign - { - get - { - if (double.IsPositiveInfinity(_real) && double.IsPositiveInfinity(_imag)) - { - return new Complex(Constants.Sqrt1Over2, Constants.Sqrt1Over2); - } - - if (double.IsPositiveInfinity(_real) && double.IsNegativeInfinity(_imag)) - { - return new Complex(Constants.Sqrt1Over2, -Constants.Sqrt1Over2); - } - - if (double.IsNegativeInfinity(_real) && double.IsPositiveInfinity(_imag)) - { - return new Complex(-Constants.Sqrt1Over2, -Constants.Sqrt1Over2); - } - - if (double.IsNegativeInfinity(_real) && double.IsNegativeInfinity(_imag)) - { - return new Complex(-Constants.Sqrt1Over2, Constants.Sqrt1Over2); - } - - // don't replace this with "Magnitude"! - var mod = SpecialFunctions.Hypotenuse(_real, _imag); - if (mod == 0.0d) - { - return Zero; - } - - return new Complex(_real / mod, _imag / mod); - } - } - - #region Exponential Functions - - /// - /// Exponential of this Complex (exp(x), E^x). - /// - /// - /// The exponential of this complex number. - /// - public Complex Exponential() - { - var exp = Math.Exp(_real); - if (IsReal()) - { - return new Complex(exp, 0.0d); - } - - return new Complex(exp * Trig.Cosine(_imag), exp * Trig.Sine(_imag)); - } - - /// - /// Natural Logarithm of this Complex (Base E). - /// - /// - /// The natural logarithm of this complex number. - /// - public Complex NaturalLogarithm() - { - if (IsRealNonNegative()) - { - return new Complex(Math.Log(_real), 0.0d); - } - - return new Complex(0.5d * Math.Log(MagnitudeSquared), Phase); - } - - /// - /// Raise this Complex to the given value. - /// - /// - /// The exponent. - /// - /// - /// The complex number raised to the given exponent. - /// - public Complex Power(Complex exponent) - { - if (IsZero()) - { - if (exponent.IsZero()) - { - return One; - } - - if (exponent.Real > 0.0d) - { - return Zero; - } - - if (exponent.Real < 0) - { - if (exponent.Imaginary == 0.0d) - { - return new Complex(double.PositiveInfinity, 0.0d); - } - - return new Complex(double.PositiveInfinity, double.PositiveInfinity); - } - - return NaN; - } - - return (exponent * NaturalLogarithm()).Exponential(); - } - - /// - /// Raise this Complex to the inverse of the given value. - /// - /// - /// The root exponent. - /// - /// - /// The complex raised to the inverse of the given exponent. - /// - public Complex Root(Complex rootExponent) - { - return Power(1 / rootExponent); - } - - /// - /// The Square (power 2) of this Complex - /// - /// - /// The square of this complex number. - /// - public Complex Square() - { - if (IsReal()) - { - return new Complex(_real * _real, 0.0d); - } - - return new Complex((_real * _real) - (_imag * _imag), 2 * _real * _imag); - } - - /// - /// The Square Root (power 1/2) of this Complex - /// - /// - /// The square root of this complex number. - /// - public Complex SquareRoot() - { - if (IsRealNonNegative()) - { - return new Complex(Math.Sqrt(_real), 0.0d); - } - - Complex result; - - var absReal = Math.Abs(Real); - var absImag = Math.Abs(Imaginary); - double w; - if (absReal >= absImag) - { - var ratio = Imaginary / Real; - w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0d + Math.Sqrt(1.0d + (ratio * ratio)))); - } - else - { - var ratio = Real / Imaginary; - w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0d + (ratio * ratio)))); - } - - if (Real >= 0.0d) - { - result = new Complex(w, Imaginary / (2.0d * w)); - } - else if (Imaginary >= 0.0d) - { - result = new Complex(absImag / (2.0 * w), w); - } - else - { - result = new Complex(absImag / (2.0 * w), -w); - } - - return result; - } - - #endregion - - #region Static Initializers - - /// - /// Constructs a Complex from its real - /// and imaginary parts. - /// - /// - /// The value for the real component. - /// - /// - /// The value for the imaginary component. - /// - /// - /// A new Complex with the given values. - /// - public static Complex WithRealImaginary(double real, double imaginary) - { - return new Complex(real, imaginary); - } - - /// - /// Constructs a Complex from its modulus and - /// argument. - /// - /// - /// Must be non-negative. - /// - /// - /// Real number. - /// - /// - /// A new Complex from the given values. - /// - public static Complex WithModulusArgument(double modulus, double argument) - { - if (modulus < 0.0d) - { - throw new ArgumentOutOfRangeException("modulus", Resources.ArgumentNotNegative); - } - - return new Complex(modulus * Math.Cos(argument), modulus * Math.Sin(argument)); - } - - #endregion - - #region IFormattable Members - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number. - /// - public override string ToString() - { - return ToString(null, null); - } - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number formatted as specified by the - /// format string. - /// - /// - /// A format specification. - /// - public string ToString(string format) - { - return ToString(format, null); - } - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number formatted as specified by the - /// format provider. - /// - /// - /// An that supplies culture-specific formatting information. - /// - public string ToString(IFormatProvider formatProvider) - { - return ToString(null, formatProvider); - } - - /// - /// A string representation of this complex number. - /// - /// - /// The string representation of this complex number formatted as specified by the - /// format string and format provider. - /// - /// - /// if the n, is not a number. - /// - /// - /// if s, is . - /// - /// - /// A format specification. - /// - /// - /// An that supplies culture-specific formatting information. - /// - public string ToString(string format, IFormatProvider formatProvider) - { - var ret = new StringBuilder(); - ret.Append("(").Append(_real.ToString(format, formatProvider)).Append(", ").Append(_imag.ToString(format, formatProvider)).Append(")"); - return ret.ToString(); - } - - #endregion - - #region IEquatable Members - - /// - /// Checks if two complex numbers are equal. Two complex numbers are equal if their - /// corresponding real and imaginary components are equal. - /// - /// - /// Returns true if the two objects are the same object, or if their corresponding - /// real and imaginary components are equal, false otherwise. - /// - /// - /// The complex number to compare to with. - /// - public bool Equals(Complex other) - { - if (IsNaN() || other.IsNaN()) - { - return false; - } - - if (IsInfinity() && other.IsInfinity()) - { - return true; - } - - return _real.AlmostEqual(other._real) && _imag.AlmostEqual(other._imag); - } - - /// - /// The hash code for the complex number. - /// - /// - /// The hash code of the complex number. - /// - /// - /// The hash code is calculated as - /// System.Math.Exp(ComplexMath.Absolute(complexNumber)). - /// - public override int GetHashCode() - { - return _real.GetHashCode() ^ (-_imag.GetHashCode()); + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + get { return Math.Atan2(_imag, _real); } } /// - /// Checks if two complex numbers are equal. Two complex numbers are equal if their - /// corresponding real and imaginary components are equal. + /// Gets the magnitude (or absolute value) of a complex number. /// - /// - /// Returns true if the two objects are the same object, or if their corresponding - /// real and imaginary components are equal, false otherwise. - /// - /// - /// The complex number to compare to with. - /// - public override bool Equals(object obj) + /// The magnitude of the current instance. + public double Magnitude { - return (obj is Complex) && Equals((Complex)obj); + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + get { return Math.Sqrt((_real * _real) + (_imag * _imag)); } } - #endregion - - #region Operators - /// /// Equality test. /// @@ -891,10 +339,10 @@ namespace System.Numerics if (divisor.IsZero()) { - return Infinity; + return PositiveInfinity; } - var modSquared = divisor.MagnitudeSquared; + var modSquared = divisor.MagnitudeSquared(); return new Complex( ((dividend._real * divisor._real) + (dividend._imag * divisor._imag)) / modSquared, ((dividend._imag * divisor._real) - (dividend._real * divisor._imag)) / modSquared); @@ -913,10 +361,10 @@ namespace System.Numerics if (divisor.IsZero()) { - return Infinity; + return PositiveInfinity; } - var zmod = divisor.MagnitudeSquared; + var zmod = divisor.MagnitudeSquared(); return new Complex(dividend * divisor._real / zmod, -dividend * divisor._imag / zmod); } @@ -933,111 +381,148 @@ namespace System.Numerics if (divisor == 0.0d) { - return Infinity; + return PositiveInfinity; } return new Complex(dividend._real / divisor, dividend._imag / divisor); } + #region IFormattable Members + /// - /// Unary addition. + /// A string representation of this complex number. /// /// - /// Returns the same complex number. + /// The string representation of this complex number. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex Plus() + public override string ToString() { - return this; + return ToString(null, null); } /// - /// Unary minus. + /// A string representation of this complex number. /// /// - /// The negated value of this complex number. + /// The string representation of this complex number formatted as specified by the + /// format string. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex Negate() + /// + /// A format specification. + /// + public string ToString(string format) + { + return ToString(format, null); + } + + /// + /// A string representation of this complex number. + /// + /// + /// The string representation of this complex number formatted as specified by the + /// format provider. + /// + /// + /// An that supplies culture-specific formatting information. + /// + public string ToString(IFormatProvider formatProvider) { - return -this; + return ToString(null, formatProvider); } /// - /// Adds a complex number to this one. + /// A string representation of this complex number. /// /// - /// The result of the addition. + /// The string representation of this complex number formatted as specified by the + /// format string and format provider. /// - /// - /// The other complex number to add. + /// + /// if the n, is not a number. + /// + /// + /// if s, is . + /// + /// + /// A format specification. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex Add(Complex other) + /// + /// An that supplies culture-specific formatting information. + /// + public string ToString(string format, IFormatProvider formatProvider) { - return this + other; + var ret = new StringBuilder(); + ret.Append("(").Append(_real.ToString(format, formatProvider)).Append(", ").Append(_imag.ToString(format, formatProvider)).Append(")"); + return ret.ToString(); } + #endregion + + #region IEquatable Members + /// - /// Subtracts a complex number from this one. + /// Checks if two complex numbers are equal. Two complex numbers are equal if their + /// corresponding real and imaginary components are equal. /// /// - /// The result of the subtraction. + /// Returns true if the two objects are the same object, or if their corresponding + /// real and imaginary components are equal, false otherwise. /// /// - /// The other complex number to subtract from this one. + /// The complex number to compare to with. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex Subtract(Complex other) + public bool Equals(Complex other) { - return this - other; + if (this.IsNaN() || other.IsNaN()) + { + return false; + } + + if (this.IsInfinity() && other.IsInfinity()) + { + return true; + } + + return _real.AlmostEqual(other._real) && _imag.AlmostEqual(other._imag); } /// - /// Multiplies this complex number with this one. + /// The hash code for the complex number. /// /// - /// The result of the multiplication. + /// The hash code of the complex number. /// - /// - /// The complex number to multiply. - /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex Multiply(Complex multiplier) + /// + /// The hash code is calculated as + /// System.Math.Exp(ComplexMath.Absolute(complexNumber)). + /// + public override int GetHashCode() { - return this * multiplier; + int hash = 27; + hash = (13 * hash) + _real.GetHashCode(); + hash = (13 * hash) + _imag.GetHashCode(); + return hash; } /// - /// Divides this complex number by another. + /// Checks if two complex numbers are equal. Two complex numbers are equal if their + /// corresponding real and imaginary components are equal. /// /// - /// The result of the division. + /// Returns true if the two objects are the same object, or if their corresponding + /// real and imaginary components are equal, false otherwise. /// - /// - /// The divisor. + /// + /// The complex number to compare to with. /// -#if !PORTABLE - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] -#endif - public Complex Divide(Complex divisor) + public override bool Equals(object obj) { - return this / divisor; + return (obj is Complex) && Equals((Complex)obj); } #endregion - #region IPrecisionSupport + #region IPrecisionSupport /// /// Returns a Norm of a value of this type, which is appropriate for measuring how @@ -1048,7 +533,7 @@ namespace System.Numerics /// double IPrecisionSupport.Norm() { - return MagnitudeSquared; + return this.MagnitudeSquared(); } /// @@ -1063,12 +548,12 @@ namespace System.Numerics /// double IPrecisionSupport.NormOfDifference(Complex otherValue) { - return (this - otherValue).MagnitudeSquared; + return (this - otherValue).MagnitudeSquared(); } #endregion - #region Parse Functions + #region Parse Functions /// /// Creates a complex number based on a string. The string can be in the @@ -1306,12 +791,12 @@ namespace System.Numerics } catch (ArgumentNullException) { - result = _zero; + result = Zero; ret = false; } catch (FormatException) { - result = _zero; + result = Zero; ret = false; } @@ -1320,7 +805,7 @@ namespace System.Numerics #endregion - #region Conversion + #region Conversion /// /// Explicit conversion of a real decimal to a Complex. @@ -1458,243 +943,353 @@ namespace System.Numerics #endregion /// - /// Gets the absolute value (or magnitude) of a complex number. + /// Returns the additive inverse of a specified complex number. /// + /// The result of the and components of the parameter multiplied by -1. /// A complex number. - /// The absolute value (or magnitude) of a complex number. - public static double Abs(Complex value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Negate(Complex value) { - return value.Magnitude; + return -value; } /// - /// Trigonometric Arc Cosine of a Complex number. + /// Computes the conjugate of a complex number and returns the result. /// + /// The conjugate of . /// A complex number. - /// - /// The arc cosine of a complex number. - /// - public static Complex Acos(Complex value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Conjugate(Complex value) { - return (Complex)value.ToComplex().InverseCosine(); + return new Complex(value._real, -value._imag); } /// - /// Trigonometric Arc Sine of a Complex number. + /// Adds two complex numbers and returns the result. /// - /// A complex number. - /// - /// The arc sine of a complex number. - /// - public static Complex Asin(Complex value) + /// The sum of and . + /// The first complex number to add. + /// The second complex number to add. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Add(Complex left, Complex right) { - return (Complex)value.ToComplex().InverseSine(); + return left + right; } /// - /// Trigonometric Arc Tangent of a Complex number. + /// Subtracts one complex number from another and returns the result. /// - /// A complex number. - /// - /// The arc tangent of a complex number. - /// - public static Complex Atan(Complex value) + /// The result of subtracting from . + /// The value to subtract from (the minuend). + /// The value to subtract (the subtrahend). + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Subtract(Complex left, Complex right) { - return (Complex)value.ToComplex().InverseTangent(); + return left - right; } /// - /// Trigonometric Cosine of a Complex number. + /// Returns the product of two complex numbers. /// - /// A complex number. - /// - /// The cosine of a complex number. - /// - public static Complex Cos(Complex value) + /// The product of the and parameters. + /// The first complex number to multiply. + /// The second complex number to multiply. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Multiply(Complex left, Complex right) { - return (Complex)value.ToComplex().Cosine(); + return left * right; } /// - /// Trigonometric Sine of a Complex number. + /// Divides one complex number by another and returns the result. /// - /// A complex number. - /// - /// The Sine of a complex number. - /// - public static Complex Sin(Complex value) + /// The quotient of the division. + /// The complex number to be divided. + /// The complex number to divide by. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Divide(Complex dividend, Complex divisor) { - return (Complex)value.ToComplex().Sine(); + return dividend / divisor; } /// - /// Trigonometric Tangent of a Complex number. + /// Returns the multiplicative inverse of a complex number. /// + /// The reciprocal of . /// A complex number. - /// - /// The tangent of a complex number. - /// - public static Complex Tan(Complex value) + public static Complex Reciprocal(Complex value) { - return (Complex)value.ToComplex().Tangent(); + if (value.IsZero()) + { + return Zero; + } + + return 1.0d / value; } /// - /// Trigonometric Hyperbolic Cosine of a Complex number. + /// Returns the square root of a specified complex number. /// + /// The square root of . /// A complex number. - /// - /// The hyperbolic cosine of a complex number. - /// - public static Complex Cosh(Complex value) + public static Complex Sqrt(Complex value) { - return (Complex)value.ToComplex().HyperbolicCosine(); + if (value.IsRealNonNegative()) + { + return new Complex(Math.Sqrt(value.Real), 0.0); + } + + Complex result; + + var absReal = Math.Abs(value.Real); + var absImag = Math.Abs(value.Imaginary); + double w; + if (absReal >= absImag) + { + var ratio = value.Imaginary / value.Real; + w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0 + Math.Sqrt(1.0 + (ratio * ratio)))); + } + else + { + var ratio = value.Real / value.Imaginary; + w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0 + (ratio * ratio)))); + } + + if (value.Real >= 0.0) + { + result = new Complex(w, value.Imaginary / (2.0 * w)); + } + else if (value.Imaginary >= 0.0) + { + result = new Complex(absImag / (2.0 * w), w); + } + else + { + result = new Complex(absImag / (2.0 * w), -w); + } + + return result; } /// - /// Trigonometric Hyperbolic Sine of a Complex number. + /// Gets the absolute value (or magnitude) of a complex number. /// /// A complex number. - /// - /// The hyperbolic sine of a complex number. - /// - public static Complex Sinh(Complex value) + /// The absolute value (or magnitude) of a complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static double Abs(Complex value) { - return (Complex)value.ToComplex().HyperbolicSine(); + return value.Magnitude; } /// - /// Trigonometric Hyperbolic Tangent of a Complex number. + /// Returns e raised to the power specified by a complex number. /// - /// A complex number. - /// - /// The hyperbolic tangent of a complex number. - /// - public static Complex Tanh(Complex value) + /// The number e raised to the power . + /// A complex number that specifies a power. + public static Complex Exp(Complex value) { - return (Complex)value.ToComplex().HyperbolicTangent(); + var exp = Math.Exp(value.Real); + if (value.IsReal()) + { + return new Complex(exp, 0.0); + } + + return new Complex(exp * Trig.Cosine(value.Imaginary), exp * Trig.Sine(value.Imaginary)); } /// - /// Exponential of a Complex number (exp(x), E^x). + /// Returns a specified complex number raised to a power specified by a complex number. /// - /// A complex number. - /// - /// The exponential of a complex number. - /// - public static Complex Exp(Complex value) + /// The complex number raised to the power . + /// A complex number to be raised to a power. + /// A complex number that specifies a power. + public static Complex Pow(Complex value, Complex power) { - return (Complex)value.ToComplex().Exponential(); + if (value.IsZero()) + { + if (power.IsZero()) + { + return One; + } + + if (power.Real > 0.0) + { + return Zero; + } + + if (power.Real < 0) + { + if (power.Imaginary == 0.0) + { + return new Complex(double.PositiveInfinity, 0.0); + } + + return new Complex(double.PositiveInfinity, double.PositiveInfinity); + } + + return double.NaN; + } + + return Exp(power * Log(value)); } /// - /// Constructs a Complex from its magnitude and phase. + /// Returns a specified complex number raised to a power specified by a double-precision floating-point number. /// - /// - /// Must be non-negative. - /// - /// - /// Real number. - /// - /// - /// A new Complex from the given values. - /// - /// - public static Complex FromPolarCoordinates(double magnitude, double phase) + /// The complex number raised to the power . + /// A complex number to be raised to a power. + /// A double-precision floating-point number that specifies a power. + public static Complex Pow(Complex value, double power) { - return WithModulusArgument(magnitude, phase); + if (value.IsZero()) + { + if (power == 0d) + { + return One; + } + + return power > 0d + ? Zero + : new Complex(double.PositiveInfinity, 0.0); + } + + return Exp(power * Log(value)); } /// - /// Natural Logarithm of a Complex number (exp(x), E^x). + /// Returns the natural (base e) logarithm of a specified complex number. /// + /// The natural (base e) logarithm of . /// A complex number. - /// - /// The natural logarithm of a complex number. - /// public static Complex Log(Complex value) { - return (Complex)value.ToComplex().NaturalLogarithm(); + if (value.IsRealNonNegative()) + { + return new Complex(Math.Log(value.Real), 0.0); + } + + return new Complex(0.5 * Math.Log(value.MagnitudeSquared()), value.Phase); } /// - /// Returns the logarithm of a specified complex number in a specified base + /// Returns the logarithm of a specified complex number in a specified base. /// + /// The logarithm of in base . /// A complex number. /// The base of the logarithm. - /// The logarithm of value in base baseValue. public static Complex Log(Complex value, double baseValue) { - if (baseValue == 1.0) - { - return double.NaN; - } - - return (Complex)(value.ToComplex().NaturalLogarithm() / Math.Log(baseValue, Math.E)); + return Log(value) / Math.Log(baseValue); } /// - /// Returns the base-10 logarithm of a specified complex number in a specified base + /// Returns the base-10 logarithm of a specified complex number. /// + /// The base-10 logarithm of . /// A complex number. - /// The base-10 logarithm of the complex number. public static Complex Log10(Complex value) { - return Log(value, 10); + return Log(value) / Constants.Ln10; } /// - /// Raise this a Complexnumber to the given value. + /// Returns the sine of the specified complex number. /// + /// The sine of . /// A complex number. - /// The exponent. - /// - /// The complex number raised to the given exponent. - /// - public static Complex Pow(Complex value, Complex power) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Sin(Complex value) { - return value.Power(power); + return Trig.Sine(value); } /// - /// Raise this a Complexnumber to the given value. + /// Returns the cosine of the specified complex number. /// + /// The cosine of . /// A complex number. - /// The exponent. - /// - /// The complex number raised to the given exponent. - /// - public static Complex Pow(Complex value, double power) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Cos(Complex value) { - return value.Power(power); + return Trig.Cosine(value); } /// - /// Returns the multiplicative inverse of a complex number. + /// Returns the tangent of the specified complex number. /// + /// The tangent of . /// A complex number. - /// The reciprocal of value. - /// If value is , the method returns . Otherwise, it returns the result of the expression / value. - public static Complex Reciprocal(Complex value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Tan(Complex value) { - if (value.IsZero()) - { - return _zero; - } + return Trig.Tangent(value); + } - return 1.0d / value; + /// + /// Returns the angle that is the arc sine of the specified complex number. + /// + /// The angle which is the arc sine of . + /// A complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Asin(Complex value) + { + return Trig.InverseSine(value); + } + + /// + /// Returns the angle that is the arc cosine of the specified complex number. + /// + /// The angle, measured in radians, which is the arc cosine of . + /// A complex number that represents a cosine. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Acos(Complex value) + { + return Trig.InverseCosine(value); } /// - /// The Square Root (power 1/2) of a Complex number. + /// Returns the angle that is the arc tangent of the specified complex number. /// + /// The angle that is the arc tangent of . /// A complex number. - /// - /// The square root of a complex number. - /// - public static Complex Sqrt(Complex value) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Atan(Complex value) + { + return Trig.InverseTangent(value); + } + + /// + /// Returns the hyperbolic sine of the specified complex number. + /// + /// The hyperbolic sine of . + /// A complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Sinh(Complex value) + { + return Trig.HyperbolicSine(value); + } + + /// + /// Returns the hyperbolic cosine of the specified complex number. + /// + /// The hyperbolic cosine of . + /// A complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Cosh(Complex value) + { + return Trig.HyperbolicCosine(value); + } + + /// + /// Returns the hyperbolic tangent of the specified complex number. + /// + /// The hyperbolic tangent of . + /// A complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Tanh(Complex value) { - return value.SquareRoot(); + return Trig.HyperbolicTangent(value); } } } diff --git a/src/Numerics/ComplexExtensions.cs b/src/Numerics/ComplexExtensions.cs index 27db348b..4d8f8487 100644 --- a/src/Numerics/ComplexExtensions.cs +++ b/src/Numerics/ComplexExtensions.cs @@ -34,92 +34,59 @@ namespace MathNet.Numerics using System.Collections.Generic; using System.Numerics; +#if !PORTABLE + using System.Runtime; +#endif + /// - /// Extension methods + /// Extension methods for the Complex type provided by System.Numerics /// public static class ComplexExtensions { /// - /// Gets a value indicating whether the Complex32 is zero. + /// Gets the squared magnitude of the Complex number. /// /// The number to perfom this operation on. - /// true if this instance is zero; otherwise, false. - public static bool IsZero(this Complex complex) + /// The squared magnitude of the Complex number. + public static double MagnitudeSquared(this Complex complex) { - return complex.Real == 0.0 && complex.Imaginary == 0.0; + return (complex.Real * complex.Real) + (complex.Imaginary * complex.Imaginary); } /// - /// Gets a value indicating whether the Complex32 is one. + /// Gets the unity of this complex (same argument, but on the unit circle; exp(I*arg)) /// - /// The number to perfom this operation on. - /// true if this instance is one; otherwise, false. - public static bool IsOne(this Complex complex) + /// The unity of this Complex. + public static Complex Sign(this Complex complex) { - return complex.Real == 1.0 && complex.Imaginary == 0.0; - } + if (double.IsPositiveInfinity(complex.Real) && double.IsPositiveInfinity(complex.Imaginary)) + { + return new Complex(Constants.Sqrt1Over2, Constants.Sqrt1Over2); + } - /// - /// Gets a value indicating whether the Complex32 is the imaginary unit. - /// - /// true if this instance is ImaginaryOne; otherwise, false. - /// The number to perfom this operation on. - public static bool IsImaginaryOne(this Complex complex) - { - return complex.Real == 0.0 && complex.Imaginary == 1.0; - } + if (double.IsPositiveInfinity(complex.Real) && double.IsNegativeInfinity(complex.Imaginary)) + { + return new Complex(Constants.Sqrt1Over2, -Constants.Sqrt1Over2); + } - /// - /// Gets a value indicating whether the provided Complex32evaluates - /// to a value that is not a number. - /// - /// The number to perfom this operation on. - /// - /// true if this instance is NaN; otherwise, - /// false. - /// - public static bool IsNaN(this Complex complex) - { - return double.IsNaN(complex.Real) || double.IsNaN(complex.Imaginary); - } + if (double.IsNegativeInfinity(complex.Real) && double.IsPositiveInfinity(complex.Imaginary)) + { + return new Complex(-Constants.Sqrt1Over2, -Constants.Sqrt1Over2); + } - /// - /// Gets a value indicating whether the provided Complex32 evaluates to an - /// infinite value. - /// - /// The number to perfom this operation on. - /// - /// true if this instance is infinite; otherwise, false. - /// - /// - /// True if it either evaluates to a complex infinity - /// or to a directed infinity. - /// - public static bool IsInfinity(this Complex complex) - { - return double.IsInfinity(complex.Real) || double.IsInfinity(complex.Imaginary); - } + if (double.IsNegativeInfinity(complex.Real) && double.IsNegativeInfinity(complex.Imaginary)) + { + return new Complex(-Constants.Sqrt1Over2, Constants.Sqrt1Over2); + } - /// - /// Gets a value indicating whether the provided Complex32 is real. - /// - /// The number to perfom this operation on. - /// true if this instance is a real number; otherwise, false. - public static bool IsReal(this Complex complex) - { - return complex.Imaginary == 0.0; - } + // don't replace this with "Magnitude"! + var mod = SpecialFunctions.Hypotenuse(complex.Real, complex.Imaginary); + if (mod == 0.0d) + { + return Complex.Zero; + } - /// - /// Gets a value indicating whether the provided Complex32 is real and not negative, that is >= 0. - /// - /// The number to perfom this operation on. - /// - /// true if this instance is real nonnegative number; otherwise, false. - /// - public static bool IsRealNonNegative(this Complex complex) - { - return complex.Imaginary == 0.0f && complex.Real >= 0; + return new Complex(complex.Real / mod, complex.Imaginary / mod); } /// @@ -139,19 +106,19 @@ namespace MathNet.Numerics /// /// /// The conjugate of the number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex Conjugate(this Complex complex) { - return new Complex(complex.Real, -complex.Imaginary); + return Complex.Conjugate(complex); } /// - /// Gets the squared magnitude of the Complex number. + /// Returns the multiplicative inverse of a complex number. /// - /// The number to perfom this operation on. - /// The squared magnitude of the Complex number. - public static double MagnitudeSquared(this Complex complex) + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Reciprocal(this Complex complex) { - return (complex.Real * complex.Real) + (complex.Imaginary * complex.Imaginary); + return Complex.Reciprocal(complex); } /// @@ -161,15 +128,10 @@ namespace MathNet.Numerics /// /// The exponential of this complex number. /// + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex Exponential(this Complex complex) { - var exp = Math.Exp(complex.Real); - if (complex.IsReal()) - { - return new Complex(exp, 0.0); - } - - return new Complex(exp * Trig.Cosine(complex.Imaginary), exp * Trig.Sine(complex.Imaginary)); + return Complex.Exp(complex); } /// @@ -179,14 +141,30 @@ namespace MathNet.Numerics /// /// The natural logarithm of this complex number. /// + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex NaturalLogarithm(this Complex complex) { - if (complex.IsRealNonNegative()) - { - return new Complex(Math.Log(complex.Real), 0.0); - } + return Complex.Log(complex); + } - return new Complex(0.5 * Math.Log(complex.MagnitudeSquared()), complex.Phase); + /// + /// Common Logarithm of this Complex (Base 10). + /// + /// The common logarithm of this complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex CommonLogarithm(this Complex complex) + { + return Complex.Log10(complex); + } + + /// + /// Logarithm of this Complex with custom base. + /// + /// The logarithm of this complex number. + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] + public static Complex Logarithm(this Complex complex, double baseValue) + { + return Complex.Log(complex, baseValue); } /// @@ -208,25 +186,22 @@ namespace MathNet.Numerics return Complex.One; } - if (exponent.Real > 0.0) + if (exponent.Real > 0d) { return Complex.Zero; } - if (exponent.Real < 0) + if (exponent.Real < 0d) { - if (exponent.Imaginary == 0.0) - { - return new Complex(double.PositiveInfinity, 0.0); - } - - return new Complex(double.PositiveInfinity, double.PositiveInfinity); + return exponent.Imaginary == 0d + ? new Complex(double.PositiveInfinity, 0d) + : new Complex(double.PositiveInfinity, double.PositiveInfinity); } - return double.NaN; + return new Complex(double.NaN, double.NaN); } - return (exponent * complex.NaturalLogarithm()).Exponential(); + return Complex.Pow(complex, exponent); } /// @@ -241,7 +216,7 @@ namespace MathNet.Numerics /// public static Complex Root(this Complex complex, Complex rootExponent) { - return Power(complex, 1 / rootExponent); + return Complex.Pow(complex, 1 / rootExponent); } /// @@ -268,43 +243,93 @@ namespace MathNet.Numerics /// /// The square root of this complex number. /// + [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex SquareRoot(this Complex complex) { - if (complex.IsRealNonNegative()) - { - return new Complex(Math.Sqrt(complex.Real), 0.0); - } + return Complex.Sqrt(complex); + } - Complex result; + /// + /// Gets a value indicating whether the Complex32 is zero. + /// + /// The number to perfom this operation on. + /// true if this instance is zero; otherwise, false. + public static bool IsZero(this Complex complex) + { + return complex.Real == 0.0 && complex.Imaginary == 0.0; + } - var absReal = Math.Abs(complex.Real); - var absImag = Math.Abs(complex.Imaginary); - double w; - if (absReal >= absImag) - { - var ratio = complex.Imaginary / complex.Real; - w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0 + Math.Sqrt(1.0 + (ratio * ratio)))); - } - else - { - var ratio = complex.Real / complex.Imaginary; - w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0 + (ratio * ratio)))); - } + /// + /// Gets a value indicating whether the Complex32 is one. + /// + /// The number to perfom this operation on. + /// true if this instance is one; otherwise, false. + public static bool IsOne(this Complex complex) + { + return complex.Real == 1.0 && complex.Imaginary == 0.0; + } - if (complex.Real >= 0.0) - { - result = new Complex(w, complex.Imaginary / (2.0 * w)); - } - else if (complex.Imaginary >= 0.0) - { - result = new Complex(absImag / (2.0 * w), w); - } - else - { - result = new Complex(absImag / (2.0 * w), -w); - } + /// + /// Gets a value indicating whether the Complex32 is the imaginary unit. + /// + /// true if this instance is ImaginaryOne; otherwise, false. + /// The number to perfom this operation on. + public static bool IsImaginaryOne(this Complex complex) + { + return complex.Real == 0.0 && complex.Imaginary == 1.0; + } + + /// + /// Gets a value indicating whether the provided Complex32evaluates + /// to a value that is not a number. + /// + /// The number to perfom this operation on. + /// + /// true if this instance is NaN; otherwise, + /// false. + /// + public static bool IsNaN(this Complex complex) + { + return double.IsNaN(complex.Real) || double.IsNaN(complex.Imaginary); + } - return result; + /// + /// Gets a value indicating whether the provided Complex32 evaluates to an + /// infinite value. + /// + /// The number to perfom this operation on. + /// + /// true if this instance is infinite; otherwise, false. + /// + /// + /// True if it either evaluates to a complex infinity + /// or to a directed infinity. + /// + public static bool IsInfinity(this Complex complex) + { + return double.IsInfinity(complex.Real) || double.IsInfinity(complex.Imaginary); + } + + /// + /// Gets a value indicating whether the provided Complex32 is real. + /// + /// The number to perfom this operation on. + /// true if this instance is a real number; otherwise, false. + public static bool IsReal(this Complex complex) + { + return complex.Imaginary == 0.0; + } + + /// + /// Gets a value indicating whether the provided Complex32 is real and not negative, that is >= 0. + /// + /// The number to perfom this operation on. + /// + /// true if this instance is real nonnegative number; otherwise, false. + /// + public static bool IsRealNonNegative(this Complex complex) + { + return complex.Imaginary == 0.0f && complex.Real >= 0; } /// diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 8a6a9de7..6c590857 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -104,6 +104,7 @@ + diff --git a/src/Numerics/TargetedPatchingOptOutAttribute.cs b/src/Numerics/TargetedPatchingOptOutAttribute.cs new file mode 100644 index 00000000..0408687b --- /dev/null +++ b/src/Numerics/TargetedPatchingOptOutAttribute.cs @@ -0,0 +1,16 @@ +#if PORTABLE +using System; + +namespace MathNet.Numerics +{ + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + public class TargetedPatchingOptOutAttribute : Attribute + { + public string Reason { get; private set; } + public TargetedPatchingOptOutAttribute(string reason) + { + Reason = reason; + } + } +} +#endif diff --git a/src/Portable/Portable.csproj b/src/Portable/Portable.csproj index cb907d41..98d20e2e 100644 --- a/src/Portable/Portable.csproj +++ b/src/Portable/Portable.csproj @@ -1032,6 +1032,9 @@ Statistics\Statistics.cs + + TargetedPatchingOptOutAttribute.cs + Threading\CommonParallel.cs diff --git a/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs b/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs index 19e55c39..155118c6 100644 --- a/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs +++ b/src/UnitTests/ComplexTests/Complex32Test.TextHandling.cs @@ -90,7 +90,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests var infinity = double.PositiveInfinity.ToString(provider); Assert.AreEqual("(" + nan + ", " + nan + ")", Complex32.NaN.ToString(provider)); - Assert.AreEqual("(" + infinity + ", " + infinity + ")", Complex32.Infinity.ToString(provider)); + Assert.AreEqual("(" + infinity + ", " + infinity + ")", Complex32.PositiveInfinity.ToString(provider)); Assert.AreEqual("(0, 0)", Complex32.Zero.ToString(provider)); Assert.AreEqual("(" + String.Format("{0}", number) + ", 0)", new Complex32(1.1f, 0.0f).ToString(provider)); Assert.AreEqual("(" + String.Format("-{0}", number) + ", 0)", new Complex32(-1.1f, 0f).ToString(provider)); @@ -133,7 +133,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests Assert.AreEqual("(1.100, 1.100)", String.Format(culture, "{0:.000}", new Complex32(1.1f, 1.1f))); Assert.AreEqual("(NaN, NaN)", Complex32.NaN.ToString("#.000", culture)); - Assert.AreEqual("(Infinity, Infinity)", Complex32.Infinity.ToString("#.000", culture)); + Assert.AreEqual("(Infinity, Infinity)", Complex32.PositiveInfinity.ToString("#.000", culture)); Assert.AreEqual("(.000, .000)", Complex32.Zero.ToString("#.000", culture)); Assert.AreEqual("(1.100, .000)", new Complex32(1.1f, 0.0f).ToString("#.000", culture)); Assert.AreEqual("(.000, -1.100)", new Complex32(0.0f, -1.1f).ToString("#.000", culture)); diff --git a/src/UnitTests/ComplexTests/Complex32Test.cs b/src/UnitTests/ComplexTests/Complex32Test.cs index 09ce3097..4d133295 100644 --- a/src/UnitTests/ComplexTests/Complex32Test.cs +++ b/src/UnitTests/ComplexTests/Complex32Test.cs @@ -45,7 +45,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests Assert.That((Complex32.NaN + float.NaN).IsNaN()); Assert.That((float.NaN + Complex32.NaN).IsNaN()); Assert.That((float.PositiveInfinity + Complex32.One).IsInfinity()); - Assert.That((Complex32.Infinity + 1.0f).IsInfinity()); + Assert.That((Complex32.PositiveInfinity + 1.0f).IsInfinity()); Assert.That((Complex32.One + 0.0f) == Complex32.One); Assert.That((0.0f + Complex32.One) == Complex32.One); Assert.That(new Complex32(1.1f, -2.2f) + 1.1f == new Complex32(2.2f, -2.2f)); @@ -59,7 +59,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests public void CanAddSubtractComplexNumbersUsingOperator() { Assert.That((Complex32.NaN - Complex32.NaN).IsNaN()); - Assert.That((Complex32.Infinity - Complex32.One).IsInfinity()); + Assert.That((Complex32.PositiveInfinity - Complex32.One).IsInfinity()); Assert.That((Complex32.One - Complex32.Zero) == Complex32.One); Assert.That((new Complex32(1.1f, -2.2f) - new Complex32(1.1f, -2.2f)) == Complex32.Zero); } @@ -70,10 +70,10 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanAddTwoComplexNumbers() { - Assert.That(Complex32.NaN.Add(Complex32.NaN).IsNaN()); - Assert.That(Complex32.Infinity.Add(Complex32.One).IsInfinity()); - Assert.That(Complex32.One.Add(Complex32.Zero) == Complex32.One); - Assert.That(new Complex32(1.1f, -2.2f).Add(new Complex32(-1.1f, 2.2f)) == Complex32.Zero); + Assert.That(Complex32.Add(Complex32.NaN, (Complex32.NaN)).IsNaN()); + Assert.That(Complex32.Add(Complex32.PositiveInfinity, Complex32.One).IsInfinity()); + Assert.That(Complex32.Add(Complex32.One, Complex32.Zero) == Complex32.One); + Assert.That(Complex32.Add(new Complex32(1.1f, -2.2f), new Complex32(-1.1f, 2.2f)) == Complex32.Zero); } /// @@ -83,7 +83,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests public void CanAddTwoComplexNumbersUsingOperator() { Assert.That((Complex32.NaN + Complex32.NaN).IsNaN()); - Assert.That((Complex32.Infinity + Complex32.One).IsInfinity()); + Assert.That((Complex32.PositiveInfinity + Complex32.One).IsInfinity()); Assert.That((Complex32.One + Complex32.Zero) == Complex32.One); Assert.That((new Complex32(1.1f, -2.2f) + new Complex32(-1.1f, 2.2f)) == Complex32.Zero); } @@ -94,12 +94,11 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanCalculateHashCode() { - var complex = new Complex32(1, 0); - Assert.AreEqual(1065353216, complex.GetHashCode()); - complex = new Complex32(0, 1); - Assert.AreEqual(-1065353216, complex.GetHashCode()); - complex = new Complex32(1, 1); - Assert.AreEqual(-16777216, complex.GetHashCode()); + Assert.AreEqual(new Complex32(1, 2).GetHashCode(), new Complex32(1, 2).GetHashCode()); + Assert.AreNotEqual(new Complex32(1, 0).GetHashCode(), new Complex32(0, 1).GetHashCode()); + Assert.AreNotEqual(new Complex32(1, 1).GetHashCode(), new Complex32(2, 2).GetHashCode()); + Assert.AreNotEqual(new Complex32(1, 0).GetHashCode(), new Complex32(-1, 0).GetHashCode()); + Assert.AreNotEqual(new Complex32(0, 1).GetHashCode(), new Complex32(0, -1).GetHashCode()); } /// @@ -285,7 +284,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanCreateComplexNumberWithModulusArgument() { - var complex = Complex32.WithModulusArgument(2, (float)-Math.PI / 6); + var complex = Complex32.FromPolarCoordinates(2, (float)-Math.PI / 6); Assert.AreEqual((float)Math.Sqrt(3), complex.Real, 1e-7f, "Real part is Sqrt(3)."); Assert.AreEqual(-1.0f, complex.Imaginary, 1e-7f, "Imaginary part is -1."); } @@ -296,7 +295,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanCreateComplexNumberWithRealImaginaryInitializer() { - var complex = Complex32.WithRealImaginary(1.1f, -2.2f); + var complex = new Complex32(1.1f, -2.2f); Assert.AreEqual(1.1f, complex.Real, "Real part is 1.1f."); Assert.AreEqual(-2.2f, complex.Imaginary, "Imaginary part is -2.2f."); } @@ -388,8 +387,8 @@ namespace MathNet.Numerics.UnitTests.ComplexTests Assert.That((Complex32.NaN * 1.0f).IsNaN()); Assert.AreEqual(new Complex32(-2, 2), new Complex32(4, -4) / -2); Assert.AreEqual(new Complex32(0.25f, 0.25f), 2 / new Complex32(4, -4)); - Assert.AreEqual(Complex32.Infinity, 2.0f / Complex32.Zero); - Assert.AreEqual(Complex32.Infinity, Complex32.One / 0); + Assert.AreEqual(Complex32.PositiveInfinity, 2.0f / Complex32.Zero); + Assert.AreEqual(Complex32.PositiveInfinity, Complex32.One / 0); } /// @@ -398,9 +397,9 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanDivideTwoComplexNumbers() { - Assert.That(Complex32.NaN.Multiply(Complex32.One).IsNaN()); - Assert.AreEqual(new Complex32(-2, 0), new Complex32(4, -4).Divide(new Complex32(-2, 2))); - Assert.AreEqual(Complex32.Infinity, Complex32.One.Divide(Complex32.Zero)); + Assert.That(Complex32.Divide(Complex32.NaN, Complex32.One).IsNaN()); + Assert.AreEqual(new Complex32(-2, 0), Complex32.Divide(new Complex32(4, -4), new Complex32(-2, 2))); + Assert.AreEqual(Complex32.PositiveInfinity, Complex32.Divide(Complex32.One, Complex32.Zero)); } /// @@ -411,7 +410,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests { Assert.That((Complex32.NaN / Complex32.One).IsNaN()); Assert.AreEqual(new Complex32(-2, 0), new Complex32(4, -4) / new Complex32(-2, 2)); - Assert.AreEqual(Complex32.Infinity, Complex32.One / Complex32.Zero); + Assert.AreEqual(Complex32.PositiveInfinity, Complex32.One / Complex32.Zero); } /// @@ -431,8 +430,8 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanMultipleTwoComplexNumbers() { - Assert.That(Complex32.NaN.Multiply(Complex32.One).IsNaN()); - Assert.AreEqual(new Complex32(0, 16), new Complex32(4, -4).Multiply(new Complex32(-2, 2))); + Assert.That(Complex32.Multiply(Complex32.NaN, Complex32.One).IsNaN()); + Assert.AreEqual(new Complex32(0, 16), Complex32.Multiply(new Complex32(4, -4), new Complex32(-2, 2))); } /// @@ -452,7 +451,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests public void CanNegateValue() { var complex = new Complex32(1.1f, -2.2f); - Assert.AreEqual(new Complex32(-1.1f, 2.2f), complex.Negate()); + Assert.AreEqual(new Complex32(-1.1f, 2.2f), Complex32.Negate(complex)); } /// @@ -474,7 +473,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests Assert.That((Complex32.NaN - float.NaN).IsNaN()); Assert.That((float.NaN - Complex32.NaN).IsNaN()); Assert.That((float.PositiveInfinity - Complex32.One).IsInfinity()); - Assert.That((Complex32.Infinity - 1.0f).IsInfinity()); + Assert.That((Complex32.PositiveInfinity - 1.0f).IsInfinity()); Assert.That((Complex32.One - 0.0f) == Complex32.One); Assert.That((0.0f - Complex32.One) == -Complex32.One); Assert.That(new Complex32(1.1f, -2.2f) - 1.1f == new Complex32(0.0f, -2.2f)); @@ -487,10 +486,10 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanSubtractTwoComplexNumbers() { - Assert.That(Complex32.NaN.Subtract(Complex32.NaN).IsNaN()); - Assert.That(Complex32.Infinity.Subtract(Complex32.One).IsInfinity()); - Assert.That(Complex32.One.Subtract(Complex32.Zero) == Complex32.One); - Assert.That(new Complex32(1.1f, -2.2f).Subtract(new Complex32(1.1f, -2.2f)) == Complex32.Zero); + Assert.That(Complex32.Subtract(Complex32.NaN, Complex32.NaN).IsNaN()); + Assert.That(Complex32.Subtract(Complex32.PositiveInfinity, Complex32.One).IsInfinity()); + Assert.That(Complex32.Subtract(Complex32.One, Complex32.Zero) == Complex32.One); + Assert.That(Complex32.Subtract(new Complex32(1.1f, -2.2f), new Complex32(1.1f, -2.2f)) == Complex32.Zero); } /// @@ -500,7 +499,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests public void CanTestForEquality() { Assert.AreNotEqual(Complex32.NaN, Complex32.NaN); - Assert.AreEqual(Complex32.Infinity, Complex32.Infinity); + Assert.AreEqual(Complex32.PositiveInfinity, Complex32.PositiveInfinity); Assert.AreEqual(new Complex32(1.1f, -2.2f), new Complex32(1.1f, -2.2f)); Assert.AreNotEqual(new Complex32(-1.1f, 2.2f), new Complex32(1.1f, -2.2f)); } @@ -512,23 +511,13 @@ namespace MathNet.Numerics.UnitTests.ComplexTests public void CanTestForEqualityUsingOperators() { Assert.That(Complex32.NaN != Complex32.NaN); - Assert.That(Complex32.Infinity == Complex32.Infinity); + Assert.That(Complex32.PositiveInfinity == Complex32.PositiveInfinity); Assert.That(new Complex32(1.1f, -2.2f) == new Complex32(1.1f, -2.2f)); Assert.That(new Complex32(-1.1f, 2.2f) != new Complex32(1.1f, -2.2f)); } /// - /// Can use Plus. - /// - [Test] - public void CanUsePlus() - { - var complex = new Complex32(1.1f, -2.2f); - Assert.AreEqual(complex, complex.Plus()); - } - - /// - /// Can use "+" operator. + /// Can use unary "+" operator. /// [Test] public void CanUsePlusOperator() @@ -537,15 +526,6 @@ namespace MathNet.Numerics.UnitTests.ComplexTests Assert.AreEqual(complex, +complex); } - /// - /// With negative modulus argument throws ArgumentOutOfRangeException. - /// - [Test] - public void WithNegativeModulusArgumentThrowsArgumentOutOfRangeException() - { - Assert.Throws(() => Complex32.WithModulusArgument(-1, 1), "Throws exception because modulus is negative."); - } - /// /// Can compute magnitude. /// diff --git a/src/UnitTests/ComplexTests/ComplexTest.cs b/src/UnitTests/ComplexTests/ComplexTest.cs index 862f88b2..faadf3e6 100644 --- a/src/UnitTests/ComplexTests/ComplexTest.cs +++ b/src/UnitTests/ComplexTests/ComplexTest.cs @@ -106,6 +106,7 @@ namespace MathNet.Numerics.UnitTests.ComplexTests a = new Complex(0.0, 0.0); b = new Complex(1.0, 0.0); AssertHelpers.AlmostEqual(new Complex(0.0, 0.0), a.Power(b), 15); + a = new Complex(0.0, 0.0); b = new Complex(-1.0, 0.0); AssertHelpers.AlmostEqual(new Complex(double.PositiveInfinity, 0.0), a.Power(b), 15); @@ -123,21 +124,22 @@ namespace MathNet.Numerics.UnitTests.ComplexTests [Test] public void CanComputeRoot() { - var a = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); - var b = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); - AssertHelpers.AlmostEqual(new Complex(0.0, 0.0), a.Root(b), 15); - a = new Complex(0.0, -1.19209289550780998537e-7); - b = new Complex(0.0, 0.5); - AssertHelpers.AlmostEqual(new Complex(0.038550761943650161, 0.019526430428319544), a.Root(b), 15); + var a = new Complex(0.0, -1.19209289550780998537e-7); + var b = new Complex(0.0, 0.5); + AssertHelpers.AlmostEqual(new Complex(0.038550761943650161, 0.019526430428319544), a.Root(b), 14); a = new Complex(0.0, 0.5); b = new Complex(0.0, -0.5); - AssertHelpers.AlmostEqual(new Complex(0.007927894711475968, -0.042480480425152213), a.Root(b), 15); + AssertHelpers.AlmostEqual(new Complex(0.007927894711475968, -0.042480480425152213), a.Root(b), 14); a = new Complex(0.0, -0.5); b = new Complex(0.0, 1.0); - AssertHelpers.AlmostEqual(new Complex(0.15990905692806806, 0.13282699942462053), a.Root(b), 15); + AssertHelpers.AlmostEqual(new Complex(0.15990905692806806, 0.13282699942462053), a.Root(b), 14); a = new Complex(0.0, 2.0); b = new Complex(0.0, -2.0); - AssertHelpers.AlmostEqual(new Complex(0.42882900629436788, 0.15487175246424678), a.Root(b), 15); + AssertHelpers.AlmostEqual(new Complex(0.42882900629436788, 0.15487175246424678), a.Root(b), 14); + + //a = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); + //b = new Complex(1.19209289550780998537e-7, 1.19209289550780998537e-7); + //AssertHelpers.AlmostEqual(new Complex(0.0, 0.0), a.Root(b), 15); a = new Complex(0.0, -8.388608e6); b = new Complex(1.19209289550780998537e-7, 0.0); AssertHelpers.AlmostEqual(new Complex(double.PositiveInfinity, double.NegativeInfinity), a.Root(b), 15); diff --git a/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs b/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs index ac0b98f4..7aa015ef 100644 --- a/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs +++ b/src/UnitTests/LinearAlgebraTests/Complex32/VectorTests.cs @@ -230,7 +230,7 @@ namespace MathNet.Numerics.UnitTests.LinearAlgebraTests.Complex32 public void CanGetHashCode() { var vector = CreateVector(new[] { new Complex32(1, 1), new Complex32(2, 1), new Complex32(3, 1), new Complex32(4, 1), new Complex32(5, 1) }); - Assert.AreEqual(-1051736064, vector.GetHashCode()); + Assert.AreEqual(-1042380805, vector.GetHashCode()); } /// From c266045b326f8400ee7fea916f77d5c95dd276d1 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 4 Dec 2012 21:02:04 +0100 Subject: [PATCH 09/15] Complex: Work around numeric instability in System.Numeric Complex.Sqrt --- src/Numerics/ComplexExtensions.cs | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Numerics/ComplexExtensions.cs b/src/Numerics/ComplexExtensions.cs index 4d8f8487..8050e43d 100644 --- a/src/Numerics/ComplexExtensions.cs +++ b/src/Numerics/ComplexExtensions.cs @@ -243,10 +243,47 @@ namespace MathNet.Numerics /// /// The square root of this complex number. /// - [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public static Complex SquareRoot(this Complex complex) { - return Complex.Sqrt(complex); + // Note: the following code should be equivalent to Complex.Sqrt(complex), + // but it turns out that is implemented poorly in System.Numerics, + // hence we provide our own implementation here. Do not replace. + + if (complex.IsRealNonNegative()) + { + return new Complex(Math.Sqrt(complex.Real), 0.0); + } + + Complex result; + + var absReal = Math.Abs(complex.Real); + var absImag = Math.Abs(complex.Imaginary); + double w; + if (absReal >= absImag) + { + var ratio = complex.Imaginary / complex.Real; + w = Math.Sqrt(absReal) * Math.Sqrt(0.5 * (1.0 + Math.Sqrt(1.0 + (ratio * ratio)))); + } + else + { + var ratio = complex.Real / complex.Imaginary; + w = Math.Sqrt(absImag) * Math.Sqrt(0.5 * (Math.Abs(ratio) + Math.Sqrt(1.0 + (ratio * ratio)))); + } + + if (complex.Real >= 0.0) + { + result = new Complex(w, complex.Imaginary / (2.0 * w)); + } + else if (complex.Imaginary >= 0.0) + { + result = new Complex(absImag / (2.0 * w), w); + } + else + { + result = new Complex(absImag / (2.0 * w), -w); + } + + return result; } /// From e24c1c25ff33f5d276b14a64ecc597ab2971b387 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 4 Dec 2012 21:37:13 +0100 Subject: [PATCH 10/15] Cleanup: file names, kill eol whitespace --- src/FSharp/{q.fs => BigRational.fs} | 204 +++++++++--------- src/FSharp/{q.fsi => BigRational.fsi} | 38 ++-- src/FSharp/{complex.fs => Complex.fs} | 69 +++--- src/FSharp/{complex.fsi => Complex.fsi} | 27 ++- src/FSharp/FSharp.fsproj | 8 +- src/FSharpPortable/FSharpPortable.fsproj | 18 +- src/FSharpUnitTests/PokerTests.fs | 34 +-- src/Numerics/Complex32.cs | 18 +- src/Numerics/Complex64.cs | 18 +- src/Numerics/ComplexExtensions.cs | 16 +- .../LinearAlgebra/Complex32/Matrix.cs | 6 +- .../Storage/DiagonalMatrixStorage.cs | 2 +- src/Numerics/SpecialFunctions/Stability.cs | 2 +- src/Numerics/Trigonometry.cs | 22 +- src/UnitTests/TrigonometryTest.cs | 8 +- 15 files changed, 243 insertions(+), 247 deletions(-) rename src/FSharp/{q.fs => BigRational.fs} (73%) rename src/FSharp/{q.fsi => BigRational.fsi} (86%) rename src/FSharp/{complex.fs => Complex.fs} (79%) rename src/FSharp/{complex.fsi => Complex.fsi} (93%) diff --git a/src/FSharp/q.fs b/src/FSharp/BigRational.fs similarity index 73% rename from src/FSharp/q.fs rename to src/FSharp/BigRational.fs index 2cb24129..221970e2 100644 --- a/src/FSharp/q.fs +++ b/src/FSharp/BigRational.fs @@ -1,8 +1,8 @@ -// First version copied from the F# Power Pack +// 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 +// (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 @@ -13,7 +13,7 @@ namespace MathNet.Numerics open System.Numerics open System.Globalization - module BigRationalLargeImpl = + module BigRationalLargeImpl = let ZeroI = new BigInteger(0) let OneI = new BigInteger(1) let bigint (x:int) = new BigInteger(x) @@ -21,44 +21,44 @@ namespace MathNet.Numerics let ToInt32I (x:BigInteger) = int32 x open BigRationalLargeImpl - + [] - type BigRationalLarge = - | Q of BigInteger * BigInteger // invariants: (p,q) in lowest form, q >= 0 + 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() + let (Q(p,q)) = n + if q.IsOne then p.ToString() else p.ToString() + "/" + q.ToString() - static member Hash (Q(ap,aq)) = + 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)) = + + 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 + 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 + override this.Equals(that:obj) = + match that with | :? BigRationalLarge as that -> BigRationalLarge.Equals(this,that) | _ -> false @@ -69,7 +69,7 @@ namespace MathNet.Numerics 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)) = + static member ToDouble (Q(p,q)) = ToDoubleI p / ToDoubleI q static member Normalize (p:BigInteger,q:BigInteger) = @@ -79,59 +79,59 @@ namespace MathNet.Numerics Q(p,q) else let k = BigInteger.GreatestCommonDivisor(p,q) - let p = p / k - let q = q / k + 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 + 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)) + 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 + let p = BigInteger.Parse str BigRationalLarge.RationalZ (p,OneI) - - static member (~-) (Q(bp,bq)) = Q(-bp,bq) // still coprime, bq >= 0 + + 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 - - + + [] - module BigRationalLarge = + module BigRationalLarge = open System.Numerics - - let inv (Q(ap,aq)) = BigRationalLarge.Normalize(aq,ap) + + 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 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_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| + 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 - - + then d - OneI // p = (d-1).q + (r+q) + else d // p = d.q + r + + //---------------------------------------------------------------------------- // BigRational //-------------------------------------------------------------------------- @@ -142,35 +142,35 @@ namespace MathNet.Numerics | Z of BigInteger | Q of BigRationalLarge - static member ( + )(n1,n2) = + 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) = + 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) = + 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) = + 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) = + static member ( ~- )(n1) = match n1 with | Z z -> Z (-z) | Q q -> Q (-q) @@ -178,28 +178,28 @@ namespace MathNet.Numerics static member ( ~+ )(n1:BigRational) = n1 // nb. Q and Z hash codes must match up - see notes above - override n.GetHashCode() = - match n with + override n.GetHashCode() = + match n with | Z z -> z.GetHashCode() - | Q q -> q.GetHashCode() + | Q q -> q.GetHashCode() - override this.Equals(obj:obj) = - match obj with + 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 -> + 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 Zero = BigRational.FromInt(0) + static member One = BigRational.FromInt(1) static member PowN (n,i:int) = @@ -207,103 +207,103 @@ namespace MathNet.Numerics | Z z -> Z (BigInteger.Pow (z,i)) | Q q -> Q (BigRationalLarge.pown q i) - static member op_Equality (n,nn) = + 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) = + + 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) = + 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) = + 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) = + 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 + + member n.IsNegative = + match n with + | Z z -> sign z < 0 | Q q -> q.IsNegative - member n.IsPositive = - match n with + member n.IsPositive = + match n with | Z z -> sign z > 0 | Q q -> q.IsPositive - - member n.Numerator = - match n with + + member n.Numerator = + match n with | Z z -> z | Q q -> q.Numerator - member n.Denominator = - match n with + 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 + member n.Sign = + if n.IsNegative then -1 + elif n.IsPositive then 1 else 0 - static member Abs(n:BigRational) = + static member Abs(n:BigRational) = if n.IsNegative then -n else n - static member ToDouble(n:BigRational) = + 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 + static member ToBigInt(n:BigRational) = + match n with | Z z -> z - | Q q -> BigRationalLarge.integer q + | Q q -> BigRationalLarge.integer q - static member ToInt32(n:BigRational) = - match n with + static member ToInt32(n:BigRational) = + match n with | Z z -> ToInt32I(z) - | Q q -> ToInt32I(BigRationalLarge.integer q ) + | 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 + 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 + 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 diff --git a/src/FSharp/q.fsi b/src/FSharp/BigRational.fsi similarity index 86% rename from src/FSharp/q.fsi rename to src/FSharp/BigRational.fsi index ffcf84e2..d6c7d422 100644 --- a/src/FSharp/q.fsi +++ b/src/FSharp/BigRational.fsi @@ -1,14 +1,14 @@ -// First version copied from the F# Power Pack +// 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. +// (c) Microsoft Corporation 2005-2009. namespace MathNet.Numerics open System open System.Numerics - + /// The type of arbitrary-sized rational numbers [] type BigRational = @@ -30,26 +30,26 @@ namespace MathNet.Numerics interface System.IComparable /// Get zero as a rational number - static member Zero : BigRational + static member Zero : BigRational /// Get one as a rational number - static member One : BigRational + 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 + static member op_LessThan: BigRational * BigRational -> bool /// This operator is for use from other .NET languages - static member op_GreaterThan: BigRational * BigRational -> bool + static member op_GreaterThan: BigRational * BigRational -> bool /// This operator is for use from other .NET languages - static member op_LessThanOrEqual: BigRational * BigRational -> bool + 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 + member IsNegative: bool /// Return a boolean indicating if this rational number is strictly positive - member IsPositive: bool + member IsPositive: bool /// Return the numerator of the normalized rational number member Numerator: BigInteger @@ -58,29 +58,29 @@ namespace MathNet.Numerics member StructuredDisplayString : string - /// Return the absolute value of a rational number + /// 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 + 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 + static member FromInt : int -> BigRational /// Return the result of converting the given big integer to a rational number - static member FromBigInt : BigInteger -> BigRational + static member FromBigInt : BigInteger -> BigRational /// Return the result of converting the given rational number to a floating point number - static member ToDouble: BigRational -> float + 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 + 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 + /// Return the result of converting the string to a rational number static member Parse: string -> BigRational type BigNum = BigRational @@ -88,7 +88,7 @@ namespace MathNet.Numerics type bignum = BigRational [] - module NumericLiteralN = + module NumericLiteralN = val FromZero : unit -> BigRational val FromOne : unit -> BigRational val FromInt32 : int32 -> BigRational diff --git a/src/FSharp/complex.fs b/src/FSharp/Complex.fs similarity index 79% rename from src/FSharp/complex.fs rename to src/FSharp/Complex.fs index 949bf9d7..3a3c91af 100644 --- a/src/FSharp/complex.fs +++ b/src/FSharp/Complex.fs @@ -1,7 +1,7 @@ -// First version copied from the F# Power Pack +// 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. +// (c) Microsoft Corporation 2005-2009. #nowarn "52" // defensive copy of structs warning @@ -21,62 +21,62 @@ namespace MathNet.Numerics 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) = + 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 -> + 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 + override x.Equals(obj) = + match obj with | :? Complex as y -> x.r = y.r && x.i = y.i | _ -> false - override x.GetHashCode() = + override x.GetHashCode() = (hash x.r >>> 5) ^^^ (hash x.r <<< 3) ^^^ (((hash x.i >>> 4) ^^^ (hash x.i <<< 4)) + 0x9e3779b9) *) - + type complex = Complex [] module private ComplexExtensionsBasic = - type Complex with + type Complex with member x.r = x.Real member x.i = x.Imaginary [] - module Complex = + 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 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 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 + 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) + 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 + // 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 @@ -90,25 +90,25 @@ namespace MathNet.Numerics let pi = mkRect (Math.PI,0.0) - // exp(r+it) = exp(r).(cos(t)+i.sin(t)) - De Moivre Theorem + // 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) + // 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 + // 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) + // 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) + // 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 [] module ComplexExtensions = - type Complex with + type Complex with member x.r = x.Real member x.i = x.Imaginary static member Create(a,b) = Complex.mkRect (a,b) @@ -126,12 +126,11 @@ namespace MathNet.Numerics 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 One = Complex.one + static member OneI = Complex.onei - module ComplexTopLevelOperators = + module ComplexTopLevelOperators = let complex x y = Complex.mkRect (x,y) - diff --git a/src/FSharp/complex.fsi b/src/FSharp/Complex.fsi similarity index 93% rename from src/FSharp/complex.fsi rename to src/FSharp/Complex.fsi index fd916ef3..29308e16 100644 --- a/src/FSharp/complex.fsi +++ b/src/FSharp/Complex.fsi @@ -1,7 +1,7 @@ -// First version copied from the F# Power Pack +// 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. +// (c) Microsoft Corporation 2005-2009. namespace MathNet.Numerics @@ -9,7 +9,7 @@ namespace MathNet.Numerics open System.Numerics [] - module ComplexExtensions = + module ComplexExtensions = /// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates type Complex with /// The real part of a complex number @@ -47,7 +47,7 @@ namespace MathNet.Numerics static member ( / ) : Complex * Complex -> Complex /// Unary negation of a complex number static member ( ~- ) : Complex -> Complex - /// Multiply a scalar by a complex number + /// Multiply a scalar by a complex number static member ( * ) : float * Complex -> Complex /// Multiply a complex number by a scalar static member ( * ) : Complex * float -> Complex @@ -55,7 +55,7 @@ namespace MathNet.Numerics 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 @@ -63,7 +63,7 @@ namespace MathNet.Numerics static member Log : Complex -> Complex static member Exp : Complex -> Complex static member Sqrt : Complex -> Complex - + (* override ToString : unit -> string override Equals : obj -> bool @@ -72,7 +72,7 @@ namespace MathNet.Numerics member ToString : format:string * provider:System.IFormatProvider -> string *) - /// The type of complex numbers + /// The type of complex numbers type complex = Complex @@ -94,7 +94,7 @@ namespace MathNet.Numerics 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 @@ -114,7 +114,7 @@ namespace MathNet.Numerics val div : complex -> complex -> complex /// Unary negation of a complex number val neg : complex -> complex - /// Multiply a scalar by a complex number + /// Multiply a scalar by a complex number val smul : float -> complex -> complex /// Multiply a complex number by a scalar val muls : complex -> float -> complex @@ -128,17 +128,14 @@ namespace MathNet.Numerics /// sqrt(x) and 0 <= phase(x) < pi val sqrt : Complex -> Complex /// Sine - val sin : Complex -> Complex + val sin : Complex -> Complex /// Cosine val cos : Complex -> Complex /// Tagent val tan : Complex -> Complex - + [] - module ComplexTopLevelOperators = + module ComplexTopLevelOperators = /// Constructs a complex number from both the real and imaginary part. val complex : float -> float -> complex - - - diff --git a/src/FSharp/FSharp.fsproj b/src/FSharp/FSharp.fsproj index 7711a025..e0f6d1bb 100644 --- a/src/FSharp/FSharp.fsproj +++ b/src/FSharp/FSharp.fsproj @@ -57,10 +57,10 @@ - - - - + + + + diff --git a/src/FSharpPortable/FSharpPortable.fsproj b/src/FSharpPortable/FSharpPortable.fsproj index f6c88e25..890b4b2d 100644 --- a/src/FSharpPortable/FSharpPortable.fsproj +++ b/src/FSharpPortable/FSharpPortable.fsproj @@ -62,18 +62,18 @@ LinearAlgebra.Double.fs - - - complex.fsi + + Complex.fsi - - complex.fs + + Complex.fs - - q.fsi + + + BigRational.fsi - - q.fs + + BigRational.fs RandomVariable.fs diff --git a/src/FSharpUnitTests/PokerTests.fs b/src/FSharpUnitTests/PokerTests.fs index 48382db9..86703f26 100644 --- a/src/FSharpUnitTests/PokerTests.fs +++ b/src/FSharpUnitTests/PokerTests.fs @@ -14,32 +14,32 @@ 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 +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 isConnected c1 c2 = let v1,v2 = value c1,value c2 (v1 - v2 |> abs |> (=) 1) || (v1 = A && v2 = 2) || (v1 = 2 && v2 = A) - + [] let ``When drawing from a full deck, then the probability for an Ace should equal 4/52``() = completeDeck - |> RandomVariable.selectOne + |> RandomVariable.selectOne |> RandomVariable.map fst |> RandomVariable.filter (fun card -> value card = A) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (4N/52N) [] let ``When drawing from a full deck, then the probability should equal 1/52``() = completeDeck - |> RandomVariable.selectOne + |> RandomVariable.selectOne |> RandomVariable.map fst |> RandomVariable.filter ((=) (A,Spades)) |> RandomVariable.probability @@ -47,23 +47,23 @@ let ``When drawing from a full deck, then the probability should equal 1/52``() [] 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 + completeDeck |> RandomVariable.select 2 |> RandomVariable.filter ((=) [A,Clubs; A,Spades]) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (1N/52N * 1N/51N) [] 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 + completeDeck |> RandomVariable.select 2 - |> RandomVariable.filterInAnyOrder [A,Clubs; A,Spades] + |> RandomVariable.filterInAnyOrder [A,Clubs; A,Spades] |> RandomVariable.probability |> should equal ((1N/52N * 1N/51N) * 2N) [] let ``When drawing the Ace of Spades and the Ace of Clubs, then the probability for drawing another Ace should equal 2/50``() = - completeDeck + completeDeck |> RandomVariable.remove [A,Clubs; A,Spades] |> RandomVariable.toUniformDistribution |> RandomVariable.filter (fun card -> value card = A) @@ -73,7 +73,7 @@ let ``When drawing the Ace of Spades and the Ace of Clubs, then the probability [] let ``When drawing from the full deck, then the probability for drawing a Pair preflop should equal 1/17``() = - completeDeck + completeDeck |> RandomVariable.select 2 |> RandomVariable.filter (fun (c1::c2::_) -> isPair c1 c2) |> RandomVariable.probability @@ -81,7 +81,7 @@ let ``When drawing from the full deck, then the probability for drawing a Pair p [] let ``When drawing from the full deck, then the probability for drawing Suited Connectors should equal 1/25``() = - completeDeck + completeDeck |> RandomVariable.select 2 |> RandomVariable.filter (fun (c1::c2::_) -> isSuited c1 c2 && isConnected c1 c2) |> RandomVariable.probability @@ -89,10 +89,10 @@ let ``When drawing from the full deck, then the probability for drawing Suited C [] let ``When holding 3 Spades after the flop, than the probability for drawing a flush should equal 10/47*9/46``() = - completeDeck + completeDeck |> RandomVariable.remove [A,Clubs; A,Spades] // preflop |> RandomVariable.remove [2,Clubs; 3,Spades; 7,Spades] // flop |> RandomVariable.select 2 |> RandomVariable.filter (fun (c1::c2::_) -> suit c1 = Spades && suit c2 = Spades) |> RandomVariable.probability - |> should equal (10N/47N*9N/46N) \ No newline at end of file + |> should equal (10N/47N*9N/46N) diff --git a/src/Numerics/Complex32.cs b/src/Numerics/Complex32.cs index 97b73042..43fa8465 100644 --- a/src/Numerics/Complex32.cs +++ b/src/Numerics/Complex32.cs @@ -47,9 +47,9 @@ namespace MathNet.Numerics /// The class Complex32 provides all elementary operations /// on complex numbers. All the operators +, -, /// *, /, ==, != are defined in the - /// canonical way. Additional complex trigonometric functions - /// are also provided. Note that the Complex32 structures - /// has two special constant values and + /// canonical way. Additional complex trigonometric functions + /// are also provided. Note that the Complex32 structures + /// has two special constant values and /// . /// /// @@ -936,8 +936,8 @@ namespace MathNet.Numerics var keywords = new[] { - textInfo.ListSeparator, numberFormatInfo.NaNSymbol, - numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, + textInfo.ListSeparator, numberFormatInfo.NaNSymbol, + numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, "+", "-", "i", "j" }; @@ -1066,17 +1066,17 @@ namespace MathNet.Numerics } /// - /// Converts the string representation of a complex number to a single-precision complex number equivalent. + /// Converts the string representation of a complex number to a single-precision complex number equivalent. /// A return value indicates whether the conversion succeeded or failed. /// /// - /// A string containing a complex number to convert. + /// A string containing a complex number to convert. /// /// /// The parsed value. /// /// - /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// If the conversion succeeds, the result will contain a complex number equivalent to value. /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized /// public static bool TryParse(string value, out Complex32 result) @@ -1536,4 +1536,4 @@ namespace MathNet.Numerics return (Complex32)Trig.HyperbolicTangent(value.ToComplex()); } } -} \ No newline at end of file +} diff --git a/src/Numerics/Complex64.cs b/src/Numerics/Complex64.cs index 14f4f2c4..72d55df6 100644 --- a/src/Numerics/Complex64.cs +++ b/src/Numerics/Complex64.cs @@ -50,9 +50,9 @@ namespace System.Numerics /// The class Complex provides all elementary operations /// on complex numbers. All the operators +, -, /// *, /, ==, != are defined in the - /// canonical way. Additional complex trigonometric functions - /// are also provided. Note that the Complex structures - /// has two special constant values and + /// canonical way. Additional complex trigonometric functions + /// are also provided. Note that the Complex structures + /// has two special constant values and /// . /// /// @@ -616,8 +616,8 @@ namespace System.Numerics var keywords = new[] { - textInfo.ListSeparator, numberFormatInfo.NaNSymbol, - numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, + textInfo.ListSeparator, numberFormatInfo.NaNSymbol, + numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, "+", "-", "i", "j" }; @@ -746,17 +746,17 @@ namespace System.Numerics } /// - /// Converts the string representation of a complex number to a single-precision complex number equivalent. + /// Converts the string representation of a complex number to a single-precision complex number equivalent. /// A return value indicates whether the conversion succeeded or failed. /// /// - /// A string containing a complex number to convert. + /// A string containing a complex number to convert. /// /// /// The parsed value. /// /// - /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// If the conversion succeeds, the result will contain a complex number equivalent to value. /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized /// public static bool TryParse(string value, out Complex result) @@ -1293,4 +1293,4 @@ namespace System.Numerics } } } -#endif \ No newline at end of file +#endif diff --git a/src/Numerics/ComplexExtensions.cs b/src/Numerics/ComplexExtensions.cs index 8050e43d..19c8e03f 100644 --- a/src/Numerics/ComplexExtensions.cs +++ b/src/Numerics/ComplexExtensions.cs @@ -453,8 +453,8 @@ namespace MathNet.Numerics var keywords = new[] { - textInfo.ListSeparator, numberFormatInfo.NaNSymbol, - numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, + textInfo.ListSeparator, numberFormatInfo.NaNSymbol, + numberFormatInfo.NegativeInfinitySymbol, numberFormatInfo.PositiveInfinitySymbol, "+", "-", "i", "j" }; @@ -583,17 +583,17 @@ namespace MathNet.Numerics } /// - /// Converts the string representation of a complex number to a double-precision complex number equivalent. + /// Converts the string representation of a complex number to a double-precision complex number equivalent. /// A return value indicates whether the conversion succeeded or failed. /// /// - /// A string containing a complex number to convert. + /// A string containing a complex number to convert. /// /// /// The parsed value. /// /// - /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// If the conversion succeeds, the result will contain a complex number equivalent to value. /// Otherwise the result will contain Complex.Zero. This parameter is passed uninitialized. /// public static bool TryToComplex(this string value, out Complex result) @@ -677,17 +677,17 @@ namespace MathNet.Numerics } /// - /// Converts the string representation of a complex number to a single-precision complex number equivalent. + /// Converts the string representation of a complex number to a single-precision complex number equivalent. /// A return value indicates whether the conversion succeeded or failed. /// /// - /// A string containing a complex number to convert. + /// A string containing a complex number to convert. /// /// /// The parsed value. /// /// - /// If the conversion succeeds, the result will contain a complex number equivalent to value. + /// If the conversion succeeds, the result will contain a complex number equivalent to value. /// Otherwise the result will contain complex32.Zero. This parameter is passed uninitialized. /// public static bool TryToComplex32(this string value, out Complex32 result) diff --git a/src/Numerics/LinearAlgebra/Complex32/Matrix.cs b/src/Numerics/LinearAlgebra/Complex32/Matrix.cs index 8adaf587..37cafee1 100644 --- a/src/Numerics/LinearAlgebra/Complex32/Matrix.cs +++ b/src/Numerics/LinearAlgebra/Complex32/Matrix.cs @@ -37,7 +37,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// [Serializable] public abstract class Matrix : Matrix - { + { /// /// Initializes a new instance of the Matrix class. /// @@ -67,7 +67,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 /// /// Returns the conjugate transpose of this matrix. - /// + /// /// The conjugate transpose of this matrix. public override Matrix ConjugateTranspose() { @@ -102,7 +102,7 @@ namespace MathNet.Numerics.LinearAlgebra.Complex32 } /// Calculates the infinity norm of this matrix. - /// The infinity norm of this matrix. + /// The infinity norm of this matrix. public override Complex32 InfinityNorm() { var norm = 0.0f; diff --git a/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs b/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs index 662dcac9..ab01ad4a 100644 --- a/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs +++ b/src/Numerics/LinearAlgebra/Storage/DiagonalMatrixStorage.cs @@ -127,7 +127,7 @@ namespace MathNet.Numerics.LinearAlgebra.Storage /// Returns a hash code for this instance. /// /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { diff --git a/src/Numerics/SpecialFunctions/Stability.cs b/src/Numerics/SpecialFunctions/Stability.cs index b8c27be7..15a858ba 100644 --- a/src/Numerics/SpecialFunctions/Stability.cs +++ b/src/Numerics/SpecialFunctions/Stability.cs @@ -192,4 +192,4 @@ namespace MathNet.Numerics return sum; } } -} \ No newline at end of file +} diff --git a/src/Numerics/Trigonometry.cs b/src/Numerics/Trigonometry.cs index d856dfc7..dff560ab 100644 --- a/src/Numerics/Trigonometry.cs +++ b/src/Numerics/Trigonometry.cs @@ -111,7 +111,7 @@ namespace MathNet.Numerics } return new Complex( - Cosine(value.Real) * HyperbolicCosine(value.Imaginary), + Cosine(value.Real) * HyperbolicCosine(value.Imaginary), -Sine(value.Real) * HyperbolicSine(value.Imaginary)); } @@ -209,7 +209,7 @@ namespace MathNet.Numerics } /// - /// Trigonometric Hyperbolic Cosecant + /// Trigonometric Hyperbolic Cosecant /// /// /// The angle in radian. @@ -279,12 +279,12 @@ namespace MathNet.Numerics } return new Complex( - HyperbolicCosine(value.Real) * Cosine(value.Imaginary), + HyperbolicCosine(value.Real) * Cosine(value.Imaginary), HyperbolicSine(value.Real) * Sine(value.Imaginary)); } /// - /// Trigonometric Hyperbolic Cotangent + /// Trigonometric Hyperbolic Cotangent /// /// /// The angle in radian angle. @@ -345,7 +345,7 @@ namespace MathNet.Numerics /// The angle in radian angle. /// /// - /// The hyperbolic secant of the radian angle. + /// The hyperbolic secant of the radian angle. /// public static double HyperbolicSecant(double radian) { @@ -409,7 +409,7 @@ namespace MathNet.Numerics } return new Complex( - HyperbolicSine(value.Real) * Cosine(value.Imaginary), + HyperbolicSine(value.Real) * Cosine(value.Imaginary), HyperbolicCosine(value.Real) * Sine(value.Imaginary)); } @@ -566,7 +566,7 @@ namespace MathNet.Numerics } /// - /// Trigonometric Hyperbolic Arc Cosecant + /// Trigonometric Hyperbolic Arc Cosecant /// /// /// The angle in radian angle. @@ -595,7 +595,7 @@ namespace MathNet.Numerics } /// - /// Trigonometric Hyperbolic Area Cosine + /// Trigonometric Hyperbolic Area Cosine /// /// /// The angle in radian angle. @@ -652,7 +652,7 @@ namespace MathNet.Numerics } /// - /// Trigonometric Hyperbolic Area Secant + /// Trigonometric Hyperbolic Area Secant /// /// /// The angle in radian angle. @@ -681,7 +681,7 @@ namespace MathNet.Numerics } /// - /// Trigonometric Hyperbolic Area Sine + /// Trigonometric Hyperbolic Area Sine /// /// /// The angle in radian angle. @@ -918,7 +918,7 @@ namespace MathNet.Numerics } return new Complex( - Sine(value.Real) * HyperbolicCosine(value.Imaginary), + Sine(value.Real) * HyperbolicCosine(value.Imaginary), Cosine(value.Real) * HyperbolicSine(value.Imaginary)); } diff --git a/src/UnitTests/TrigonometryTest.cs b/src/UnitTests/TrigonometryTest.cs index ee4c7b08..46720cef 100644 --- a/src/UnitTests/TrigonometryTest.cs +++ b/src/UnitTests/TrigonometryTest.cs @@ -116,7 +116,7 @@ namespace MathNet.Numerics.UnitTests } /// - /// Can compute cosine. + /// Can compute cosine. /// /// Input value. /// Expected value. @@ -164,7 +164,7 @@ namespace MathNet.Numerics.UnitTests } /// - /// Can compute hyperbolic cosine. + /// Can compute hyperbolic cosine. /// /// Input value. /// Expected value. @@ -379,7 +379,7 @@ namespace MathNet.Numerics.UnitTests } /// - /// Can compute inverse secant. + /// Can compute inverse secant. /// /// Input value. /// Expected value. @@ -501,7 +501,7 @@ namespace MathNet.Numerics.UnitTests } /// - /// Can convert grad to radian. + /// Can convert grad to radian. /// [Test] public void CanConvertGradToRadian() From 618a4b64981a2b24559dd6fb60f5f5323e025792 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 4 Dec 2012 22:29:08 +0100 Subject: [PATCH 11/15] F#: Complex: cleanup, no need to reimplement everything, module more thorough --- src/FSharp/Complex.fs | 178 ++++++++++++--------------------- src/FSharp/Complex.fsi | 216 ++++++++++++++++++++--------------------- 2 files changed, 168 insertions(+), 226 deletions(-) diff --git a/src/FSharp/Complex.fs b/src/FSharp/Complex.fs index 3a3c91af..53d69416 100644 --- a/src/FSharp/Complex.fs +++ b/src/FSharp/Complex.fs @@ -1,10 +1,7 @@ // 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 @@ -12,125 +9,78 @@ namespace MathNet.Numerics open System.Globalization open System.Numerics - (* - [] - [] - 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 - [] - module private ComplexExtensionsBasic = - type Complex with - member x.r = x.Real - member x.i = x.Imaginary - [] + [] 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 + + let mkRect(a,b) = new Complex(a,b) + let mkPolar(a,b) = Complex.FromPolarCoordinates(a,b) + let cis b = mkPolar(1.0,b) + + let zero = Complex.Zero + let one = Complex.One + let onei = Complex.ImaginaryOne + let pi = mkRect (Math.PI,0.0) + + let realPart (c:complex) = c.Real + let imagPart (c:complex) = c.Imaginary + let magnitude (c:complex) = c.Magnitude + let phase (c:complex) = c.Phase + + let neg (a:complex) = -a + let conjugate (c:complex) = c.Conjugate() + + let add (a:complex) (b:complex) = a + b + let sub (a:complex) (b:complex) = a - b + let mul (a:complex) (b:complex) = a * b + let div (x:complex) (y:complex) = x / y + + let smul (a:float) (b:complex) = new Complex(a * b.Real, a * b.Imaginary) + let muls (a:complex) (b:float) = new Complex(a.Real * b, a.Imaginary * b) + + let exp (x:complex) = Complex.Exp(x) + let ln x = Complex.Log(x) + let log10 x = Complex.Log10(x) + let log b x = Complex.Log(x,b) + let pow (power:Complex) x = Complex.Pow(x,power) + let powf (power:float) x = Complex.Pow(x,power) + let sqr (x:Complex) = x.Square() + let sqrt (x:Complex) = x.SquareRoot() // numerically more stable than Complex.Sqrt + + let sin x = Complex.Sin(x) + let cos x = Complex.Cos(x) + let tan x = Complex.Tan(x) + let asin x = Complex.Asin(x) + let acos x = Complex.Acos(x) + let atan x = Complex.Atan(x) + let sinh x = Complex.Sinh(x) + let cosh x = Complex.Cosh(x) + let tanh x = Complex.Tanh(x) + + let sec (x:Complex) = Trig.Secant(x) + let csc (x:Complex) = Trig.Cosecant(x) + let cot (x:Complex) = Trig.Cotangent(x) + let asec (x:Complex) = Trig.InverseSecant(x) + let acsc (x:Complex) = Trig.InverseCosecant(x) + let acot (x:Complex) = Trig.InverseCotangent(x) + let sech (x:Complex) = Trig.HyperbolicSecant(x) + let csch (x:Complex) = Trig.HyperbolicCosecant(x) + let coth (x:Complex) = Trig.HyperbolicCotangent(x) + + 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 [] module ComplexExtensions = + + let complex x y = Complex.mkRect (x,y) + type Complex with member x.r = x.Real member x.i = x.Imaginary + 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 - - - module ComplexTopLevelOperators = - let complex x y = Complex.mkRect (x,y) + static member CreatePolar(a,b) = Complex.mkPolar (a,b) \ No newline at end of file diff --git a/src/FSharp/Complex.fsi b/src/FSharp/Complex.fsi index 29308e16..4df15c17 100644 --- a/src/FSharp/Complex.fsi +++ b/src/FSharp/Complex.fsi @@ -1,6 +1,5 @@ // 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 @@ -8,134 +7,127 @@ namespace MathNet.Numerics open System open System.Numerics - [] - module ComplexExtensions = - /// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates - type Complex with - /// 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 - [] [] 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 + /// Create a complex number using real and imaginary parts + val mkRect : float * float -> complex + /// 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 cis : float -> 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 + /// pi + val pi : Complex + + /// The real part of a complex number + val realPart : complex -> float + /// The imaginary part of a complex number + val imagPart : complex -> float + /// The polar-coordinate magnitude of a complex number + val magnitude : complex -> float + /// The polar-coordinate phase of a complex number + val phase : complex -> float + + /// Unary negation of a complex number + val neg : complex -> 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 + /// 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 + + /// Multiply a scalar by a complex number + val smul : float -> complex -> complex + /// Multiply a complex number by a scalar + val muls : complex -> float -> 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 + /// ln(x) is natural log (base e) + val ln : Complex -> Complex + /// log10(x) is common log (base 10) + val log10 : Complex -> Complex + /// log(base,x) is log with custom base + val log : float -> Complex -> Complex + /// pow(power,x) is the complex power + val pow : Complex -> Complex -> Complex + /// pow(power,x) is the float power + val powf : float -> Complex -> Complex + /// sqr(x) is the square (power 2) + val sqr : Complex -> Complex + /// sqrt(x) and 0 <= phase(x) < pi val sqrt : Complex -> Complex - /// Sine + + /// Sine val sin : Complex -> Complex - /// Cosine + /// Cosine val cos : Complex -> Complex - /// Tagent + /// Tagent val tan : Complex -> Complex - + /// Arc Sine + val asin : Complex -> Complex + /// Arc Cosine + val acos : Complex -> Complex + /// Arc Tagent + val atan : Complex -> Complex + /// Hyperbolic Sine + val sinh : Complex -> Complex + /// Hyperbolic Cosine + val cosh : Complex -> Complex + /// Hyperbolic Tagent + val tanh : Complex -> Complex + + /// Secant + val sec : Complex -> Complex + /// Cosecant + val csc : Complex -> Complex + /// Cotangent + val cot : Complex -> Complex + /// Arc Secant + val asec : Complex -> Complex + /// Arc Cosecant + val acsc : Complex -> Complex + /// Arc Cotangent + val acot : Complex -> Complex + /// Hyperbolic Secant + val sech : Complex -> Complex + /// Hyperbolic Cosecant + val csch : Complex -> Complex + /// Hyperbolic Cotangent + val coth : Complex -> Complex [] - module ComplexTopLevelOperators = + module ComplexExtensions = + /// Constructs a complex number from both the real and imaginary part. val complex : float -> float -> complex + + /// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates + type Complex with + + /// 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 real part of a complex number + member r: float + /// The imaginary part of a complex number + member i: float \ No newline at end of file From b4da72e6f594be878c41c85f6edac943869f6fae Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 4 Dec 2012 22:51:24 +0100 Subject: [PATCH 12/15] F#: single precision complex32 type, in addition to double precision complex --- src/FSharp/Complex.fs | 98 +++++++++++++++++++---- src/FSharp/Complex.fsi | 178 ++++++++++++++++++++++++++++++++++------- 2 files changed, 231 insertions(+), 45 deletions(-) diff --git a/src/FSharp/Complex.fs b/src/FSharp/Complex.fs index 53d69416..cfe96299 100644 --- a/src/FSharp/Complex.fs +++ b/src/FSharp/Complex.fs @@ -10,6 +10,7 @@ namespace MathNet.Numerics open System.Numerics type complex = Complex + type complex32 = Complex32 [] [] @@ -18,6 +19,7 @@ namespace MathNet.Numerics let mkRect(a,b) = new Complex(a,b) let mkPolar(a,b) = Complex.FromPolarCoordinates(a,b) let cis b = mkPolar(1.0,b) + let ofComplex32 (x:complex32) = new Complex(float x.Real, float x.Imaginary) let zero = Complex.Zero let one = Complex.One @@ -44,10 +46,10 @@ namespace MathNet.Numerics let ln x = Complex.Log(x) let log10 x = Complex.Log10(x) let log b x = Complex.Log(x,b) - let pow (power:Complex) x = Complex.Pow(x,power) + let pow (power:complex) x = Complex.Pow(x,power) let powf (power:float) x = Complex.Pow(x,power) - let sqr (x:Complex) = x.Square() - let sqrt (x:Complex) = x.SquareRoot() // numerically more stable than Complex.Sqrt + let sqr (x:complex) = x.Square() + let sqrt (x:complex) = x.SquareRoot() // numerically more stable than Complex.Sqrt let sin x = Complex.Sin(x) let cos x = Complex.Cos(x) @@ -59,28 +61,92 @@ namespace MathNet.Numerics let cosh x = Complex.Cosh(x) let tanh x = Complex.Tanh(x) - let sec (x:Complex) = Trig.Secant(x) - let csc (x:Complex) = Trig.Cosecant(x) - let cot (x:Complex) = Trig.Cotangent(x) - let asec (x:Complex) = Trig.InverseSecant(x) - let acsc (x:Complex) = Trig.InverseCosecant(x) - let acot (x:Complex) = Trig.InverseCotangent(x) - let sech (x:Complex) = Trig.HyperbolicSecant(x) - let csch (x:Complex) = Trig.HyperbolicCosecant(x) - let coth (x:Complex) = Trig.HyperbolicCotangent(x) + let sec (x:complex) = Trig.Secant(x) + let csc (x:complex) = Trig.Cosecant(x) + let cot (x:complex) = Trig.Cotangent(x) + let asec (x:complex) = Trig.InverseSecant(x) + let acsc (x:complex) = Trig.InverseCosecant(x) + let acot (x:complex) = Trig.InverseCotangent(x) + let sech (x:complex) = Trig.HyperbolicSecant(x) + let csch (x:complex) = Trig.HyperbolicCosecant(x) + let coth (x:complex) = Trig.HyperbolicCotangent(x) - 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 + [] + [] + module Complex32 = + + let mkRect(a,b) = new Complex32(a,b) + let mkPolar(a,b) = Complex32.FromPolarCoordinates(a,b) + let cis b = mkPolar(1.0f,b) + let ofComplex (x:complex) = new Complex32(float32 x.Real, float32 x.Imaginary) + + let zero = Complex32.Zero + let one = Complex32.One + let onei = Complex32.ImaginaryOne + let pi = mkRect (float32 Math.PI,0.0f) + + let realPart (c:complex32) = c.Real + let imagPart (c:complex32) = c.Imaginary + let magnitude (c:complex32) = c.Magnitude + let phase (c:complex32) = c.Phase + + let neg (a:complex32) = -a + let conjugate (c:complex32) = c.Conjugate() + + let add (a:complex32) (b:complex32) = a + b + let sub (a:complex32) (b:complex32) = a - b + let mul (a:complex32) (b:complex32) = a * b + let div (x:complex32) (y:complex32) = x / y + + let smul (a:float32) (b:complex32) = new Complex32(a * b.Real, a * b.Imaginary) + let muls (a:complex32) (b:float32) = new Complex32(a.Real * b, a.Imaginary * b) + + let exp (x:complex32) = Complex32.Exp(x) + let ln x = Complex32.Log(x) + let log10 x = Complex32.Log10(x) + let log b x = Complex32.Log(x,b) + let pow (power:complex32) x = Complex32.Pow(x,power) + let powf (power:float32) x = Complex32.Pow(x,power) + let sqr (x:complex32) = x.Square() + let sqrt (x:complex32) = x.SquareRoot() // numerically more stable than Complex.Sqrt + + let sin x = Complex32.Sin(x) + let cos x = Complex32.Cos(x) + let tan x = Complex32.Tan(x) + let asin x = Complex32.Asin(x) + let acos x = Complex32.Acos(x) + let atan x = Complex32.Atan(x) + let sinh x = Complex32.Sinh(x) + let cosh x = Complex32.Cosh(x) + let tanh x = Complex32.Tanh(x) + + // no complex32 implementations available yet, fix once available + let sec (x:complex32) = ofComplex <| Trig.Secant(x.ToComplex()) + let csc (x:complex32) = ofComplex <| Trig.Cosecant(x.ToComplex()) + let cot (x:complex32) = ofComplex <| Trig.Cotangent(x.ToComplex()) + let asec (x:complex32) = ofComplex <| Trig.InverseSecant(x.ToComplex()) + let acsc (x:complex32) = ofComplex <| Trig.InverseCosecant(x.ToComplex()) + let acot (x:complex32) = ofComplex <| Trig.InverseCotangent(x.ToComplex()) + let sech (x:complex32) = ofComplex <| Trig.HyperbolicSecant(x.ToComplex()) + let csch (x:complex32) = ofComplex <| Trig.HyperbolicCosecant(x.ToComplex()) + let coth (x:complex32) = ofComplex <| Trig.HyperbolicCotangent(x.ToComplex()) [] module ComplexExtensions = let complex x y = Complex.mkRect (x,y) + let complex32 x y = Complex32.mkRect (x,y) type Complex with member x.r = x.Real member x.i = x.Imaginary static member Create(a,b) = Complex.mkRect (a,b) - static member CreatePolar(a,b) = Complex.mkPolar (a,b) \ No newline at end of file + static member CreatePolar(a,b) = Complex.mkPolar (a,b) + + type Complex32 with + member x.r = x.Real + member x.i = x.Imaginary + + static member Create(a,b) = Complex32.mkRect (a,b) + static member CreatePolar(a,b) = Complex32.mkPolar (a,b) diff --git a/src/FSharp/Complex.fsi b/src/FSharp/Complex.fsi index 4df15c17..605cb4cb 100644 --- a/src/FSharp/Complex.fsi +++ b/src/FSharp/Complex.fsi @@ -9,6 +9,7 @@ namespace MathNet.Numerics /// The type of complex numbers type complex = Complex + type complex32 = Complex32 [] [] @@ -28,7 +29,7 @@ namespace MathNet.Numerics /// The complex number 0+1i val onei : complex /// pi - val pi : Complex + val pi : complex /// The real part of a complex number val realPart : complex -> float @@ -59,65 +60,171 @@ namespace MathNet.Numerics val muls : complex -> float -> complex /// exp(x) = e^x - val exp : Complex -> Complex + val exp : complex -> complex /// ln(x) is natural log (base e) - val ln : Complex -> Complex + val ln : complex -> complex /// log10(x) is common log (base 10) - val log10 : Complex -> Complex + val log10 : complex -> complex /// log(base,x) is log with custom base - val log : float -> Complex -> Complex + val log : float -> complex -> complex /// pow(power,x) is the complex power - val pow : Complex -> Complex -> Complex + val pow : complex -> complex -> complex /// pow(power,x) is the float power - val powf : float -> Complex -> Complex + val powf : float -> complex -> complex /// sqr(x) is the square (power 2) - val sqr : Complex -> Complex + val sqr : complex -> complex /// sqrt(x) and 0 <= phase(x) < pi - val sqrt : Complex -> Complex + val sqrt : complex -> complex /// Sine - val sin : Complex -> Complex + val sin : complex -> complex /// Cosine - val cos : Complex -> Complex + val cos : complex -> complex /// Tagent - val tan : Complex -> Complex + val tan : complex -> complex /// Arc Sine - val asin : Complex -> Complex + val asin : complex -> complex /// Arc Cosine - val acos : Complex -> Complex + val acos : complex -> complex /// Arc Tagent - val atan : Complex -> Complex + val atan : complex -> complex /// Hyperbolic Sine - val sinh : Complex -> Complex + val sinh : complex -> complex /// Hyperbolic Cosine - val cosh : Complex -> Complex + val cosh : complex -> complex /// Hyperbolic Tagent - val tanh : Complex -> Complex + val tanh : complex -> complex /// Secant - val sec : Complex -> Complex + val sec : complex -> complex /// Cosecant - val csc : Complex -> Complex + val csc : complex -> complex /// Cotangent - val cot : Complex -> Complex + val cot : complex -> complex /// Arc Secant - val asec : Complex -> Complex + val asec : complex -> complex /// Arc Cosecant - val acsc : Complex -> Complex + val acsc : complex -> complex /// Arc Cotangent - val acot : Complex -> Complex + val acot : complex -> complex /// Hyperbolic Secant - val sech : Complex -> Complex + val sech : complex -> complex /// Hyperbolic Cosecant - val csch : Complex -> Complex + val csch : complex -> complex /// Hyperbolic Cotangent - val coth : Complex -> Complex + val coth : complex -> complex + + [] + [] + module Complex32 = + + /// Create a complex number using real and imaginary parts + val mkRect : float32 * float32 -> complex32 + /// Create a complex number using magnitude/phase polar coordinates + val mkPolar : float32 * float32 -> complex32 + /// A complex of magnitude 1 and the given phase and , i.e. cis x = mkPolar 1.0 x + val cis : float32 -> complex32 + + /// The complex number 0+0i + val zero : complex32 + /// The complex number 1+0i + val one : complex32 + /// The complex number 0+1i + val onei : complex32 + /// pi + val pi : complex32 + + /// The real part of a complex number + val realPart : complex32 -> float32 + /// The imaginary part of a complex number + val imagPart : complex32 -> float32 + /// The polar-coordinate magnitude of a complex number + val magnitude : complex32 -> float32 + /// The polar-coordinate phase of a complex number + val phase : complex32 -> float32 + + /// Unary negation of a complex number + val neg : complex32 -> complex32 + /// The conjugate of a complex number, i.e. x-yi + val conjugate : complex32 -> complex32 + + /// Add two complex numbers + val add : complex32 -> complex32 -> complex32 + /// Subtract one complex number from another + val sub : complex32 -> complex32 -> complex32 + /// Multiply two complex numbers + val mul : complex32 -> complex32 -> complex32 + /// Complex division of two complex numbers + val div : complex32 -> complex32 -> complex32 + + /// Multiply a scalar by a complex number + val smul : float32 -> complex32 -> complex32 + /// Multiply a complex number by a scalar + val muls : complex32 -> float32 -> complex32 + + /// exp(x) = e^x + val exp : complex32 -> complex32 + /// ln(x) is natural log (base e) + val ln : complex32 -> complex32 + /// log10(x) is common log (base 10) + val log10 : complex32 -> complex32 + /// log(base,x) is log with custom base + val log : float32 -> complex32 -> complex32 + /// pow(power,x) is the complex power + val pow : complex32 -> complex32 -> complex32 + /// pow(power,x) is the float power + val powf : float32 -> complex32 -> complex32 + /// sqr(x) is the square (power 2) + val sqr : complex32 -> complex32 + /// sqrt(x) and 0 <= phase(x) < pi + val sqrt : complex32 -> complex32 + + /// Sine + val sin : complex32 -> complex32 + /// Cosine + val cos : complex32 -> complex32 + /// Tagent + val tan : complex32 -> complex32 + /// Arc Sine + val asin : complex32 -> complex32 + /// Arc Cosine + val acos : complex32 -> complex32 + /// Arc Tagent + val atan : complex32 -> complex32 + /// Hyperbolic Sine + val sinh : complex32 -> complex32 + /// Hyperbolic Cosine + val cosh : complex32 -> complex32 + /// Hyperbolic Tagent + val tanh : complex32 -> complex32 + + /// Secant + val sec : complex32 -> complex32 + /// Cosecant + val csc : complex32 -> complex32 + /// Cotangent + val cot : complex32 -> complex32 + /// Arc Secant + val asec : complex32 -> complex32 + /// Arc Cosecant + val acsc : complex32 -> complex32 + /// Arc Cotangent + val acot : complex32 -> complex32 + /// Hyperbolic Secant + val sech : complex32 -> complex32 + /// Hyperbolic Cosecant + val csch : complex32 -> complex32 + /// Hyperbolic Cotangent + val coth : complex32 -> complex32 [] module ComplexExtensions = - /// Constructs a complex number from both the real and imaginary part. + /// Constructs a double precision complex number from both the real and imaginary part. val complex : float -> float -> complex + + /// Constructs a single precision complex number from both the real and imaginary part. + val complex32 : float32 -> float32 -> complex32 /// The type of complex numbers stored as pairs of 64-bit floating point numbers in rectangular coordinates type Complex with @@ -130,4 +237,17 @@ namespace MathNet.Numerics /// The real part of a complex number member r: float /// The imaginary part of a complex number - member i: float \ No newline at end of file + member i: float + + /// The type of complex numbers stored as pairs of 32-bit floating point numbers in rectangular coordinates + type Complex32 with + + /// Create a complex number x+ij using rectangular coordinates + static member Create : float32 * float32 -> Complex32 + /// Create a complex number using magnitude/phase polar coordinates + static member CreatePolar : float32 * float32 -> Complex32 + + /// The real part of a complex number + member r: float32 + /// The imaginary part of a complex number + member i: float32 From c65f6e952c94f5b892e4ac98adc70f25b6d92ddf Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Tue, 4 Dec 2012 22:59:32 +0100 Subject: [PATCH 13/15] Cosmetics, more eol whitespace --- src/FSharp/BigRational.fs | 2 - src/FSharp/BigRational.fsi | 4 +- src/FSharp/Complex.fsi | 6 +- src/FSharp/LinearAlgebra.Double.Matrix.fs | 52 ++-- src/FSharp/LinearAlgebra.Double.Vector.fs | 50 ++-- src/FSharp/LinearAlgebra.fs | 18 +- src/FSharp/RandomVariable.fs | 68 ++--- src/FSharpExamples/Apply.fs | 2 +- src/FSharpExamples/MCMC.fs | 20 +- src/FSharpUnitTests/BigRationalTests.fs | 274 ++++++++++----------- src/FSharpUnitTests/DenseMatrixTests.fs | 42 ++-- src/FSharpUnitTests/DenseVectorTests.fs | 30 +-- src/FSharpUnitTests/FsUnit.fs | 2 +- src/FSharpUnitTests/MatrixTests.fs | 128 +++++----- src/FSharpUnitTests/RandomVariableTests.fs | 32 +-- src/FSharpUnitTests/SparseMatrixTests.fs | 10 +- src/FSharpUnitTests/SparseVectorTests.fs | 12 +- src/FSharpUnitTests/Utilities.fs | 56 ++--- src/FSharpUnitTests/VectorTests.fs | 118 ++++----- 19 files changed, 461 insertions(+), 465 deletions(-) diff --git a/src/FSharp/BigRational.fs b/src/FSharp/BigRational.fs index 221970e2..3f78da44 100644 --- a/src/FSharp/BigRational.fs +++ b/src/FSharp/BigRational.fs @@ -1,7 +1,5 @@ // 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 diff --git a/src/FSharp/BigRational.fsi b/src/FSharp/BigRational.fsi index d6c7d422..2b863431 100644 --- a/src/FSharp/BigRational.fsi +++ b/src/FSharp/BigRational.fsi @@ -1,7 +1,5 @@ // 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 @@ -93,4 +91,4 @@ namespace MathNet.Numerics val FromOne : unit -> BigRational val FromInt32 : int32 -> BigRational val FromInt64 : int64 -> BigRational - val FromString : string -> BigRational \ No newline at end of file + val FromString : string -> BigRational diff --git a/src/FSharp/Complex.fsi b/src/FSharp/Complex.fsi index 605cb4cb..c2ffadb7 100644 --- a/src/FSharp/Complex.fsi +++ b/src/FSharp/Complex.fsi @@ -53,7 +53,7 @@ namespace MathNet.Numerics val mul : complex -> complex -> complex /// Complex division of two complex numbers val div : complex -> complex -> complex - + /// Multiply a scalar by a complex number val smul : float -> complex -> complex /// Multiply a complex number by a scalar @@ -156,7 +156,7 @@ namespace MathNet.Numerics val mul : complex32 -> complex32 -> complex32 /// Complex division of two complex numbers val div : complex32 -> complex32 -> complex32 - + /// Multiply a scalar by a complex number val smul : float32 -> complex32 -> complex32 /// Multiply a complex number by a scalar @@ -222,7 +222,7 @@ namespace MathNet.Numerics /// Constructs a double precision complex number from both the real and imaginary part. val complex : float -> float -> complex - + /// Constructs a single precision complex number from both the real and imaginary part. val complex32 : float32 -> float32 -> complex32 diff --git a/src/FSharp/LinearAlgebra.Double.Matrix.fs b/src/FSharp/LinearAlgebra.Double.Matrix.fs index 81542be8..aaecc1bf 100644 --- a/src/FSharp/LinearAlgebra.Double.Matrix.fs +++ b/src/FSharp/LinearAlgebra.Double.Matrix.fs @@ -35,7 +35,7 @@ open MathNet.Numerics.LinearAlgebra.Generic /// A module which implements functional matrix operations. [] module Matrix = - + /// Fold a function over all matrix elements. let inline fold (f: 'a -> float -> 'a) (acc0: 'a) (A: #Matrix) = let n = A.RowCount @@ -71,8 +71,8 @@ module Matrix = let n = A.RowCount let m = A.ColumnCount Array2D.init n m (fun i j -> (A.Item(i,j))) - - /// Checks whether a predicate holds for all elements of a matrix. + + /// Checks whether a predicate holds for all elements of a matrix. let inline forall (p: float -> bool) (A: #Matrix) = let mutable b = true let mutable i = 0 @@ -82,7 +82,7 @@ module Matrix = j <- j+1 if j = A.ColumnCount then i <- i+1; j <- 0 b - + /// Chechks whether a predicate holds for at least one element of a matrix. let inline exists (p: float -> bool) (A: #Matrix) = let mutable b = false @@ -93,7 +93,7 @@ module Matrix = j <- j+1 if j = A.ColumnCount then i <- i+1; j <- 0 b - + /// Checks whether a position dependent predicate holds for all elements of a matrix. let inline foralli (p: int -> int -> float -> bool) (A: #Matrix) = let mutable b = true @@ -104,7 +104,7 @@ module Matrix = j <- j+1 if j = A.ColumnCount then i <- i+1; j <- 0 b - + /// Checks whether a position dependent predicate holds for at least one element of a matrix. let inline existsi (p: int -> int -> float -> bool) (A: #Matrix) = let mutable b = false @@ -115,7 +115,7 @@ module Matrix = j <- j+1 if j = A.ColumnCount then i <- i+1; j <- 0 b - + /// Map every matrix element using the given function. let inline map (f: float -> float) (A: #Matrix) = let N = A.RowCount @@ -125,7 +125,7 @@ module Matrix = for j=0 to M-1 do C.[i,j] <- f (C.Item(i,j)) C - + /// Map every matrix element using the given position dependent function. let inline mapi (f: int -> int -> float -> float) (A: #Matrix) = let N = A.RowCount @@ -135,7 +135,7 @@ module Matrix = for j=0 to M-1 do C.[i,j] <- f i j (C.Item(i,j)) C - + /// In-place map every matrix column using the given position dependent function. let inline inplaceMapCols (f: int -> Vector -> Vector) (A: #Matrix) = for j = 0 to A.ColumnCount-1 do @@ -165,19 +165,19 @@ module Matrix = for i=0 to A.RowCount-1 do for j=0 to A.ColumnCount-1 do A.Item(i,j) <- f i j - + /// In-place map of every matrix element using a position dependent function. let inline inplaceMapi (f: int -> int -> float -> float) (A: #Matrix) = for i=0 to A.RowCount-1 do for j=0 to A.ColumnCount-1 do A.Item(i,j) <- f i j (A.Item(i,j)) - + /// Creates a sequence that iterates the non-zero entries in the matrix. let inline nonZeroEntries (A: #Matrix) = seq { for i in 0 .. A.RowCount-1 do for j in 0 .. A.ColumnCount-1 do if A.Item(i,j) <> 0.0 then yield (i,j, A.Item(i,j)) } - + /// Returns the sum of all elements of a matrix. let inline sum (A: #Matrix) = let mutable f = 0.0 @@ -189,7 +189,7 @@ module Matrix = /// Returns the sum of the results generated by applying a position dependent function to each column of the matrix. let inline sumColsBy (f: int -> Vector -> 'a) (A: #Matrix) = A.ColumnEnumerator() |> Seq.map (fun (j,col) -> f j col) |> Seq.reduce (+) - + /// Returns the sum of the results generated by applying a position dependent function to each row of the matrix. let inline sumRowsBy (f: int -> Vector -> 'a) (A: #Matrix) = A.RowEnumerator() |> Seq.map (fun (i,row) -> f i row) |> Seq.reduce (+) @@ -200,21 +200,21 @@ module Matrix = for j=0 to A.ColumnCount-1 do f (A.Item(i,j)) () - + /// Iterates over all elements of a matrix using the element indices. let inline iteri (f: int -> int -> float -> unit) (A: #Matrix) = for i=0 to A.RowCount-1 do for j=0 to A.ColumnCount-1 do f i j (A.Item(i,j)) () - + /// Fold one column. let inline foldCol (f: 'a -> float -> 'a) acc (A: #Matrix) k = let mutable macc = acc for i=0 to A.RowCount-1 do macc <- f macc (A.Item(i,k)) macc - + /// Fold one row. let inline foldRow (f: 'a -> float -> 'a) acc (A: #Matrix) k = let mutable macc = acc @@ -231,7 +231,7 @@ module Matrix = macc <- f macc (A.Item(i,k)) v.[k] <- macc v :> Vector - + /// Fold all rows into one column vector. let inline foldByRow (f: float -> float -> float) acc (A: #Matrix) = let v = new DenseVector(A.RowCount) @@ -253,7 +253,7 @@ module DenseMatrix = for j=0 to m-1 do A.[i,j] <- f i j A - + /// Create a matrix from a list of float lists. Every list in the master list specifies a row. let inline ofList (fll: float list list) = let n = List.length fll @@ -263,7 +263,7 @@ module DenseMatrix = if (List.length fl) <> m then failwith "Each subrow must be of the same length." else List.iteri (fun j f -> A.[i,j] <- f) fl) A - + /// Create a matrix from a list of sequences. Every sequence in the master sequence specifies a row. let inline ofSeq (fss: #seq<#seq>) = let n = Seq.length fss @@ -276,20 +276,20 @@ module DenseMatrix = /// Create a matrix from a 2D array of floating point numbers. let inline ofArray2 (arr: float[,]) = new DenseMatrix(arr) - + /// Create a matrix with the given entries. let inline initDense (n: int) (m: int) (es: #seq) = let A = new DenseMatrix(n,m) Seq.iter (fun (i,j,f) -> A.[i,j] <- f) es A - + /// Create a square matrix with constant diagonal entries. let inline constDiag (n: int) (f: float) = let A = new DenseMatrix(n,n) for i=0 to n-1 do A.[i,i] <- f A - + /// Create a square matrix with the vector elements on the diagonal. let inline diag (v: #Vector) = let n = v.Count @@ -313,26 +313,26 @@ module DenseMatrix = /// A module which implements functional sparse vector operations. [] module SparseMatrix = - + /// Create a matrix from a list of float lists. Every list in the master list specifies a row. let inline ofList (rows: int) (cols: int) (fll: list) = let A = new SparseMatrix(rows, cols) fll |> List.iter (fun (i, j, x) -> A.[i,j] <- x) A - + /// Create a matrix from a list of sequences. Every sequence in the master sequence specifies a row. let inline ofSeq (rows: int) (cols: int) (fss: #seq) = let A = new SparseMatrix(rows, cols) fss |> Seq.iter (fun (i, j, x) -> A.[i,j] <- x) A - + /// Create a square matrix with constant diagonal entries. let inline constDiag (n: int) (f: float) = let A = new SparseMatrix(n,n) for i=0 to n-1 do A.[i,i] <- f A - + /// Create a square matrix with the vector elements on the diagonal. let inline diag (v: #Vector) = let n = v.Count diff --git a/src/FSharp/LinearAlgebra.Double.Vector.fs b/src/FSharp/LinearAlgebra.Double.Vector.fs index cdd2c0a5..4a076426 100644 --- a/src/FSharp/LinearAlgebra.Double.Vector.fs +++ b/src/FSharp/LinearAlgebra.Double.Vector.fs @@ -35,12 +35,12 @@ open MathNet.Numerics.LinearAlgebra.Generic /// A module which implements functional vector operations. [] module Vector = - + /// Transform a vector into an array. let inline toArray (v: #Vector) = let n = v.Count Array.init n (fun i -> v.Item(i)) - + /// Transform a vector into an array. let inline toList (v: #Vector) = let n = v.Count @@ -57,30 +57,30 @@ module Vector = for i=0 to v.Count-1 do v.Item(i) <- f i (v.Item(i)) () - + /// In-place vector addition. let inline addInPlace (v: #Vector) (w: #Vector) = v.Add(w, v) - + /// In place vector subtraction. let inline subInPlace (v: #Vector) (w: #Vector) = v.Subtract(w, v) - + /// Functional map operator for vectors. - /// + /// let inline map f (v: #Vector) = let w = v.Clone() mapInPlace (fun x -> f x) w w - + /// Applies a function to all elements of the vector. let inline iter (f: float -> unit) (v: #Vector) = for i=0 to v.Count-1 do f (v.Item i) - + /// Applies a function to all elements of the vector. let inline iteri (f: int -> float -> unit) (v: #Vector) = for i=0 to v.Count-1 do f i (v.Item i) - + /// Maps a vector to a new vector by applying a function to every element. let inline mapi (f: int -> float -> float) (v: #Vector) = let w = v.Clone() @@ -107,7 +107,7 @@ module Vector = for i=0 to v.Count-1 do acc <- f i acc (v.Item(i)) acc - + /// Checks whether a predicate is satisfied for every element in the vector. let inline forall (p: float -> bool) (v: #Vector) = let mutable b = true @@ -116,7 +116,7 @@ module Vector = b <- b && (p (v.Item(i))) i <- i+1 b - + /// Checks whether there is an entry in the vector that satisfies a given predicate. let inline exists (p: float -> bool) (v: #Vector) = let mutable b = false @@ -125,7 +125,7 @@ module Vector = b <- b || (p (v.Item(i))) i <- i+1 b - + /// Checks whether a predicate is true for all entries in a vector. let inline foralli (p: int -> float -> bool) (v: #Vector) = let mutable b = true @@ -134,7 +134,7 @@ module Vector = b <- b && (p i (v.Item(i))) i <- i+1 b - + /// Checks whether there is an entry in the vector that satisfies a given position dependent predicate. let inline existsi (p: int -> float -> bool) (v: #Vector) = let mutable b = false @@ -177,13 +177,13 @@ module Vector = p /// Creates a new vector and inserts the given value at the given index. - let inline insert index value (v: #Vector) = - let newV = new DenseVector(v.Count + 1) - for i = 0 to index - 1 do - newV.Item(i) <- v.Item(i) - newV.Item(index) <- value - for i = index + 1 to v.Count do - newV.Item(i) <- v.Item(i - 1) + let inline insert index value (v: #Vector) = + let newV = new DenseVector(v.Count + 1) + for i = 0 to index - 1 do + newV.Item(i) <- v.Item(i) + newV.Item(index) <- value + for i = index + 1 to v.Count do + newV.Item(i) <- v.Item(i - 1) newV /// A module which implements functional dense vector operations. @@ -203,14 +203,14 @@ module DenseVector = let v = DenseVector(n) fl |> List.iteri (fun i f -> v.[i] <- f) v - + /// Create a vector from a sequences. let inline ofSeq (fs: #seq) = let n = Seq.length fs let v = DenseVector(n) fs |> Seq.iteri (fun i f -> v.[i] <- f) v - + /// Create a vector with evenly spaced entries: e.g. rangef -1.0 0.5 1.0 = [-1.0 -0.5 0.0 0.5 1.0] let inline rangef (start: float) (step: float) (stop: float) = let n = (int ((stop - start) / step)) + 1 @@ -218,7 +218,7 @@ module DenseVector = for i=0 to n-1 do v.[i] <- (float i) * step + start v - + /// Create a vector with integer entries in the given range. let inline range (start: int) (stop: int) = new DenseVector([| for i in [start .. stop] -> float i |]) @@ -226,13 +226,13 @@ module DenseVector = /// A module which implements functional sparse vector operations. [] module SparseVector = - + /// Create a sparse vector with a given dimension from a list of entry, value pairs. let inline ofList (dim: int) (fl: list) = let v = new SparseVector(dim) fl |> List.iter (fun (i, f) -> v.[i] <- f) v - + /// Create a sparse vector with a given dimension from a sequence of entry, value pairs. let inline ofSeq (dim: int) (fs: #seq) = let v = new SparseVector(dim) diff --git a/src/FSharp/LinearAlgebra.fs b/src/FSharp/LinearAlgebra.fs index f81ed35e..366b8505 100644 --- a/src/FSharp/LinearAlgebra.fs +++ b/src/FSharp/LinearAlgebra.fs @@ -33,19 +33,19 @@ namespace MathNet.Numerics.LinearAlgebra.Generic // Module that contains implementation of useful F#-specific // extension members for generic Matrix and Vector types [] -module FSharpExtensions = +module FSharpExtensions = - // A type extension for the generic vector type that + // A type extension for the generic vector type that // adds the 'GetSlice' method to allow vec.[a .. b] syntax type MathNet.Numerics.LinearAlgebra.Generic. - Vector<'T when 'T : struct and 'T : (new : unit -> 'T) - and 'T :> System.IEquatable<'T> and 'T :> System.IFormattable + Vector<'T when 'T : struct and 'T : (new : unit -> 'T) + and 'T :> System.IEquatable<'T> and 'T :> System.IFormattable and 'T :> System.ValueType> with /// Gets a slice of a vector starting at a specified index /// and ending at a specified index (both indices are optional) /// This method can be used via the x.[start .. finish] syntax - member x.GetSlice(start, finish) = + member x.GetSlice(start, finish) = let start = defaultArg start 0 let finish = defaultArg finish (x.Count - 1) x.SubVector(start, finish - start + 1) @@ -53,14 +53,14 @@ module FSharpExtensions = // A type extension for the generic matrix type that // adds the 'GetSlice' method to allow m.[r1 .. r2, c1 .. c2] syntax type MathNet.Numerics.LinearAlgebra.Generic. - Matrix<'T when 'T : struct and 'T : (new : unit -> 'T) - and 'T :> System.IEquatable<'T> and 'T :> System.IFormattable + Matrix<'T when 'T : struct and 'T : (new : unit -> 'T) + and 'T :> System.IEquatable<'T> and 'T :> System.IFormattable and 'T :> System.ValueType> with - /// Gets a submatrix using a specified column range and + /// Gets a submatrix using a specified column range and /// row range (all indices are optional) /// This method can be used via the x.[r1 .. r2, c1 .. c2 ] syntax - member x.GetSlice(rstart, rfinish, cstart, cfinish) = + member x.GetSlice(rstart, rfinish, cstart, cfinish) = let cstart = defaultArg cstart 0 let rstart = defaultArg rstart 0 let cfinish = defaultArg cfinish (x.ColumnCount - 1) diff --git a/src/FSharp/RandomVariable.fs b/src/FSharp/RandomVariable.fs index 9bbd1e09..352b7ff4 100644 --- a/src/FSharp/RandomVariable.fs +++ b/src/FSharp/RandomVariable.fs @@ -10,22 +10,22 @@ open MathNet.Numerics type Outcome<'T> = { Value: 'T Probability : BigRational } - + type RandomVariable<'T> = Outcome<'T> seq - + // P(A AND B) = P(A | B) * P(B) let private bind f dist = - dist - |> Seq.map (fun p1 -> + dist + |> Seq.map (fun p1 -> f p1.Value - |> Seq.map (fun p2 -> - { Value = p2.Value; - Probability = + |> Seq.map (fun p2 -> + { Value = p2.Value; + Probability = p1.Probability * p2.Probability})) |> Seq.concat - + /// Inject a value into the RandomVariable type -let private returnM value = +let private returnM value = Seq.singleton { Value = value ; Probability = 1N/1N } type RandomVariableBuilder() = @@ -35,61 +35,61 @@ type RandomVariableBuilder() = let randomVariable = RandomVariableBuilder() -type CoinSide = - | Heads +type CoinSide = + | Heads | Tails - + [] [] -module RandomVariable = - +module RandomVariable = + // Create some helpers let toUniformDistribution seq = let l = Seq.length seq - seq + seq |> Seq.map (fun e -> - { Value = e; + { Value = e; Probability = 1N / bignum.FromInt l }) - - let probability dist = + + let probability dist = dist |> Seq.map (fun o -> o.Probability) |> Seq.sum - + let certainly = returnM let impossible<'a> :'a RandomVariable = toUniformDistribution [] - + let fairDice sides = toUniformDistribution [1..sides] - + let fairCoin = toUniformDistribution [Heads; Tails] - + let filter predicate dist = 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 RandomVariable value by using a specified mapping function. - let map f dist = - dist + let map f dist = + dist |> Seq.map (fun o -> { Value = f o.Value; Probability = o.Probability }) - + let selectOne values = - [for e in values -> e,values |> Seq.filter ((<>) e)] + [for e in values -> e,values |> Seq.filter ((<>) e)] |> toUniformDistribution - + let rec selectMany n values = - match n with + match n with | 0 -> certainly ([],values) - | _ -> + | _ -> randomVariable { let! (x,c1) = selectOne values let! (xs,c2) = selectMany (n-1) c1 return x::xs,c2} - - let select n values = - selectMany n values + + let select n values = + selectMany n values |> map (fst >> List.rev) - + let remove items = Seq.filter (fun v -> Seq.forall ((<>) v) items) diff --git a/src/FSharpExamples/Apply.fs b/src/FSharpExamples/Apply.fs index 155b195e..16475939 100644 --- a/src/FSharpExamples/Apply.fs +++ b/src/FSharpExamples/Apply.fs @@ -90,5 +90,5 @@ for (name, fs, dotnet) in FunctionList do if prettyPrint then printfn "\tApply.Map (MKL): %d milliseconds." sw.ElapsedMilliseconds else printf "\t%d" sw.ElapsedMilliseconds sw.Reset()*) - + printfn "" \ No newline at end of file diff --git a/src/FSharpExamples/MCMC.fs b/src/FSharpExamples/MCMC.fs index d0ee5337..e48ac373 100644 --- a/src/FSharpExamples/MCMC.fs +++ b/src/FSharpExamples/MCMC.fs @@ -45,7 +45,7 @@ let rnd = new MersenneTwister() // ----------------------------------------------------------------------------- do printfn "Rejection Sampling Example" - + /// The target distribution. let beta = new Beta(2.7, 6.3) @@ -76,7 +76,7 @@ do // ----------------------------------------------------------------------------- do printfn "Metropolis Sampling Example" - + let mean, stddev = 1.0, 3.5 let normal = new Normal(mean, stddev) @@ -93,7 +93,7 @@ do printfn "\tEmpirical StdDev = %f (should be %f)" (Statistics.StandardDeviation(arr)) normal.StdDev printfn "\tAcceptance rate = %f" ms.AcceptanceRate printfn "" - + // @@ -107,12 +107,12 @@ do printfn "Metropolis Hastings Sampling Example (Symmetric Proposal)" let mean, stddev = 1.0, 3.5 let normal = new Normal(mean, stddev) - + /// Evaluates the log normal distribution. let npdf x m s = -0.5*(x-m)*(x-m)/(s*s) - 0.5 * log(2.0 * System.Math.PI * s * s) /// Implements the rejection sampling procedure. - let ms = new MetropolisHastingsSampler( 0.1, (fun x -> log(normal.Density(x))), + let ms = new MetropolisHastingsSampler( 0.1, (fun x -> log(normal.Density(x))), (fun x y -> npdf x y 0.3), (fun x -> Normal.Sample(rnd, x, 0.3)), 10, RandomSource = rnd ) @@ -138,22 +138,22 @@ do printfn "Metropolis Hastings Sampling Example (Assymetric Proposal)" let mean, stddev = 1.0, 3.5 let normal = new Normal(mean, stddev) - + /// Evaluates the logarithm of the normal distribution function. let npdf x m s = -0.5*(x-m)*(x-m)/(s*s) - 0.5 * log(2.0 * System.Math.PI * s * s) - + /// Samples from a mixture that is biased towards samples larger than x. let mixSample x = if Bernoulli.Sample(rnd, 0.5) = 1 then Normal.Sample(rnd, x, 0.3) else Normal.Sample(rnd, x + 0.1, 0.3) - + /// The transition kernel for the proposal above. let krnl xnew x = log (0.5 * exp(npdf xnew x 0.3) + 0.5 * exp(npdf xnew (x+0.1) 0.3)) /// Implements the rejection sampling procedure. - let ms = new MetropolisHastingsSampler( 0.1, (fun x -> log(normal.Density(x))), + let ms = new MetropolisHastingsSampler( 0.1, (fun x -> log(normal.Density(x))), (fun xnew x -> krnl xnew x), (fun x -> mixSample x), 10, RandomSource = rnd ) @@ -178,7 +178,7 @@ do printfn "Slice Sampling Example" let mean, stddev = 1.0, 3.5 let normal = new Normal(mean, stddev) - + /// Evaluates the unnormalized logarithm of the normal distribution function. let npdf x m s = -0.5*(x-m)*(x-m)/(s*s) diff --git a/src/FSharpUnitTests/BigRationalTests.fs b/src/FSharpUnitTests/BigRationalTests.fs index 65915416..d6f2bd5c 100644 --- a/src/FSharpUnitTests/BigRationalTests.fs +++ b/src/FSharpUnitTests/BigRationalTests.fs @@ -1,4 +1,4 @@ -// First version copied from the F# Power Pack +// 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 @@ -69,14 +69,14 @@ type public BigRationalTests() = points let pointsNonZero = [for p,q in points do if p<>0I then yield p,q] // non zero points - let makeQs p q = + 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 + [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] @@ -84,7 +84,7 @@ type public BigRationalTests() = let vector2s = product points points [] - member this.BasicTests1() = + member this.BasicTests1() = check "generic format h" "1N" (sprintf "%A" 1N) check "generic format q" "-1N" (sprintf "%A" (-1N)) @@ -92,7 +92,7 @@ type public BigRationalTests() = test "d3oc002" (LanguagePrimitives.GenericZero = 0N) test "d3oc112w" (LanguagePrimitives.GenericOne = 1N) - check "weioj3h" (sprintf "%O" 3N) "3" + 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" @@ -105,21 +105,21 @@ type public BigRationalTests() = let v = -30000000000000000000000000000000000000000000000000000000000000N check "weioj3r" (sprintf "%O" v) ((box v).ToString()) - + [] - member this.BasicTests2() = + member this.BasicTests2() = // Test arithmetic ops: tests - let test2One name f check ((p,q),(pp,qq)) = + 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 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 && + 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 @@ -135,9 +135,9 @@ type public BigRationalTests() = test2All "div" (BigRational.(/)) (fun (p,q) (pp,qq) -> (p*qq,q*pp)) (product points pointsNonZero) - + [] - member this.RangeTests() = + member this.RangeTests() = // Test x0 .. dx .. x1 let checkRange3 (x0:BigRational) dx x1 k = let f (x:BigRational) = x * BigRational.FromBigInt k |> BigRational.ToBigInt @@ -146,7 +146,7 @@ type public BigRationalTests() = //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) @@ -161,15 +161,15 @@ type public BigRationalTests() = // Test x0 .. x1 let checkRange2 (x0:BigRational) x1 = let z0 = BigRational.ToBigInt x0 - let z01 = BigRational.ToBigInt (x1 - 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) + 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 @@ -178,7 +178,7 @@ type public BigRationalTests() = // ToString() // Cases: integer, computed integer, rational<1, rational>1, +/-infinity, nan - (natA 1).ToString() |> check "ToString" "1" + (natA 1).ToString() |> check "ToString" "1" (natA 0).ToString() |> check "ToString" "0" (natA (-12)).ToString() |> check "ToString" "-12" (natB 1).ToString() |> check "ToString" "1" @@ -211,29 +211,29 @@ type public BigRationalTests() = check "OneB" BigRational.One (natB 1) [] - member this.BinaryAndUnaryOperators() = + member this.BinaryAndUnaryOperators() = // Test: generic bop - let testR2One name f check ((p,q),(pp,qq)) = + 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)] + 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) = + 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 + 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 @@ -260,14 +260,14 @@ type public BigRationalTests() = testR1All "IsZero" (fun (x:BigRational) -> x = q0) (fun (p,q) -> sign p = 0I) vector1s - let test1One name f check (p,q) = + let test1One name f check (p,q) = // There may be several ways to construct the test rationals - let zs = makeQs p q + let zs = makeQs p q let results = [for z in zs -> f z] - let refP,refQ = check (p,q) + let refP,refQ = check (p,q) let refResult = BigRational.FromBigInt refP / BigRational.FromBigInt refQ - let resOK (result:BigRational) = - result.Numerator * refQ = refP * result.Denominator && + 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 @@ -275,7 +275,7 @@ type public BigRationalTests() = 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 ~+ ??? @@ -312,27 +312,27 @@ type BigNumType() = let g_normal = 88N let g_bigintpositive = 1000000000000000000000000000000000018I let g_bigintnegative = -1000000000000000000000000000000000018I - + // Interfaces [] - member this.IComparable() = + member this.IComparable() = // Legit IC - let ic = g_positive1 :> IComparable - Assert.AreEqual(ic.CompareTo(g_positive1),0) + let ic = g_positive1 :> IComparable + Assert.AreEqual(ic.CompareTo(g_positive1),0) checkThrowsArgumentException( fun () -> ic.CompareTo(g_bigintpositive) |> ignore) - + // Base class methods [] 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_zero.ToString(),"0") Assert.AreEqual(g_normal.ToString(),"88") - - // Static methods + + // Static methods [] member this.Abs() = Assert.AreEqual(bignum.Abs(g_negative1), g_positive1) @@ -341,7 +341,7 @@ type BigNumType() = Assert.AreEqual(bignum.Abs(g_normal), g_normal) Assert.AreEqual(bignum.Abs(g_zero), g_zero) () - + [] member this.FromBigInt() = Assert.AreEqual(bignum.FromBigInt(g_bigintpositive), @@ -351,7 +351,7 @@ type BigNumType() = Assert.AreEqual(bignum.FromBigInt(0I),g_zero) Assert.AreEqual(bignum.FromBigInt(88I),g_normal) () - + [] member this.FromInt() = Assert.AreEqual(bignum.FromInt(2147483647), 2147483647N) @@ -359,12 +359,12 @@ type BigNumType() = Assert.AreEqual(bignum.FromInt(0), 0N) Assert.AreEqual(bignum.FromInt(88), 88N) () - + [] member this.One() = Assert.AreEqual(bignum.One,1N) () - + [] member this.Parse() = Assert.AreEqual(bignum.Parse("100"), 100N) @@ -372,7 +372,7 @@ type BigNumType() = Assert.AreEqual(bignum.Parse("0"), g_zero) Assert.AreEqual(bignum.Parse("88"), g_normal) () - + [] member this.PowN() = Assert.AreEqual(bignum.PowN(100N,2), 10000N) @@ -380,8 +380,8 @@ type BigNumType() = Assert.AreEqual(bignum.PowN(g_zero,2147483647), 0N) Assert.AreEqual(bignum.PowN(g_normal,0), 1N) () - - + + [] member this.Sign() = Assert.AreEqual(g_positive1.Sign, 1) @@ -389,9 +389,9 @@ type BigNumType() = Assert.AreEqual(g_zero.Sign, 0) Assert.AreEqual(g_normal.Sign, 1) () - - - + + + [] member this.ToBigInt() = Assert.AreEqual(bignum.ToBigInt(g_positive1), g_bigintpositive) @@ -399,9 +399,9 @@ type BigNumType() = Assert.AreEqual(bignum.ToBigInt(g_zero), 0I) Assert.AreEqual(bignum.ToBigInt(g_normal), 88I) () - - - + + + [] member this.ToDouble() = Assert.AreEqual(bignum.ToDouble(179769N*1000000000000000N), 1.79769E+20) @@ -413,8 +413,8 @@ type BigNumType() = Assert.AreEqual(double(0N),0.0) Assert.AreEqual(double(88N),88.0) () - - + + [] member this.ToInt32() = Assert.AreEqual(bignum.ToInt32(2147483647N), 2147483647) @@ -425,18 +425,18 @@ type BigNumType() = Assert.AreEqual(int32(-2147483648N), -2147483648) Assert.AreEqual(int32(0N), 0) Assert.AreEqual(int32(88N), 88) - - - + + + [] member this.Zero() = Assert.AreEqual(bignum.Zero,0N) () - - // operator methods + + // operator methods [] member this.test_op_Addition() = - + Assert.AreEqual(100N + 200N, 300N) Assert.AreEqual((-100N) + (-200N), -300N) Assert.AreEqual(g_positive1 + g_negative1, 0N) @@ -444,163 +444,163 @@ type BigNumType() = Assert.AreEqual(g_normal + g_normal, 176N) Assert.AreEqual(g_normal + g_normal, 176N) () - - - + + + [] member this.test_op_Division() = Assert.AreEqual(g_positive1 / g_positive1, 1N) Assert.AreEqual(-100N / 2N, -50N) Assert.AreEqual(g_zero / g_positive1, 0N) () - + [] 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)) () - + [] - member this.test_op_GreaterThan() = + 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) - - + + () [] - member this.test_op_GreaterThanOrEqual() = + member this.test_op_GreaterThanOrEqual() = Assert.AreEqual((g_positive1 >= g_positive2), true) - Assert.AreEqual((g_positive2 >= g_positive1), false) + Assert.AreEqual((g_positive2 >= g_positive1), false) Assert.AreEqual((g_negative1 >= g_negative1), true) Assert.AreEqual((0N >= g_zero), true) - + () - [] - member this.test_op_LessThan() = + [] + 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) - + () [] - member this.test_op_LessThanOrEqual() = + member this.test_op_LessThanOrEqual() = Assert.AreEqual((g_positive1 <= g_positive2), false) - Assert.AreEqual((g_positive2 <= g_positive1), true) + Assert.AreEqual((g_positive2 <= g_positive1), true) Assert.AreEqual((g_negative1 <= g_negative1), true) Assert.AreEqual((0N <= g_zero), true) - + () - + [] - member this.test_op_Multiply() = + 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) - + () - + [] - member this.test_op_Range() = + member this.test_op_Range() = let resultPos = [0N .. 2N] - let seqPos = [0N; 1N; 2N] + let seqPos = [0N; 1N; 2N] verifySeqsEqual resultPos seqPos - - let resultNeg = [-2N .. 0N] - let seqNeg = [-2N; -1N; 0N] + + let resultNeg = [-2N .. 0N] + let seqNeg = [-2N; -1N; 0N] verifySeqsEqual resultNeg seqNeg - + let resultSmall = [0N ..5N] - let seqSmall = [0N; 1N; 2N; 3N; 4N; 5N] + let seqSmall = [0N; 1N; 2N; 3N; 4N; 5N] verifySeqsEqual resultSmall seqSmall - + () - - + + [] - member this.test_op_RangeStep() = + member this.test_op_RangeStep() = let resultPos = [0N .. 3N .. 6N] - let seqPos = [0N; 3N; 6N] + let seqPos = [0N; 3N; 6N] verifySeqsEqual resultPos seqPos - - let resultNeg = [-6N .. 3N .. 0N] - let seqNeg = [-6N; -3N; 0N] + + let resultNeg = [-6N .. 3N .. 0N] + let seqNeg = [-6N; -3N; 0N] verifySeqsEqual resultNeg seqNeg - + let resultSmall = [0N .. 3N .. 9N] - let seqSmall = [0N; 3N; 6N; 9N] + let seqSmall = [0N; 3N; 6N; 9N] verifySeqsEqual resultSmall seqSmall - + () - + [] - member this.test_op_Subtraction() = + 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) () - + [] - member this.test_op_UnaryNegation() = + member this.test_op_UnaryNegation() = Assert.AreEqual(-g_positive1, g_negative1) Assert.AreEqual(-g_negative1, g_positive1) - Assert.AreEqual(-0N,0N) - + Assert.AreEqual(-0N,0N) + () - + [] - member this.test_op_UnaryPlus() = + member this.test_op_UnaryPlus() = Assert.AreEqual(+g_positive1,g_positive1) Assert.AreEqual(+g_negative1,g_negative1) Assert.AreEqual(+0N, 0N) - + () - + // instance methods [] - member this.Denominator() = + member this.Denominator() = Assert.AreEqual(g_positive1.Denominator, 1I) Assert.AreEqual(g_negative1.Denominator, 1I) Assert.AreEqual(0N.Denominator, 1I) - - () - + + () + [] - member this.IsNegative() = + member this.IsNegative() = Assert.IsFalse(g_positive1.IsNegative) Assert.IsTrue(g_negative1.IsNegative) Assert.IsFalse( 0N.IsNegative) Assert.IsFalse(-0N.IsNegative) - - () - - + + () + + [] - member this.IsPositive() = + member this.IsPositive() = Assert.IsTrue(g_positive1.IsPositive) Assert.IsFalse(g_negative1.IsPositive) Assert.IsFalse( 0N.IsPositive) Assert.IsFalse(-0N.IsPositive) - - () - + + () + [] - member this.Numerator() = + member this.Numerator() = Assert.AreEqual(g_positive1.Numerator, g_bigintpositive) Assert.AreEqual(g_negative1.Numerator, g_bigintnegative) Assert.AreEqual(0N.Numerator, 0I) - - () - - - - - + + () + + + + + diff --git a/src/FSharpUnitTests/DenseMatrixTests.fs b/src/FSharpUnitTests/DenseMatrixTests.fs index b06107d9..992fefc8 100644 --- a/src/FSharpUnitTests/DenseMatrixTests.fs +++ b/src/FSharpUnitTests/DenseMatrixTests.fs @@ -13,39 +13,39 @@ module DenseMatrixTests = /// A large vector with increasingly large entries let largeM = new DenseMatrix( Array2D.init 100 100 (fun i j -> float i * 100.0 + float j) ) - - [] - let ``DenseMatrix.init`` () = + + [] + let ``DenseMatrix.init`` () = DenseMatrix.init 100 100 (fun i j -> float i * 100.0 + float j) |> should equal largeM - [] - let ``DenseMatrix.ofList`` () = + [] + let ``DenseMatrix.ofList`` () = DenseMatrix.ofList [[0.3;0.3];[0.3;0.3]] |> should equal smallM - [] - let ``DenseMatrix.ofSeq`` () = + [] + let ``DenseMatrix.ofSeq`` () = DenseMatrix.ofSeq (Seq.ofList [[0.3;0.3];[0.3;0.3]]) |> should equal smallM - [] - let ``DenseMatrix.ofArray2`` () = + [] + let ``DenseMatrix.ofArray2`` () = DenseMatrix.ofArray2 (Array2D.create 2 2 0.3) |> should equal smallM - [] - let ``DenseMatrix.initDense`` () = + [] + let ``DenseMatrix.initDense`` () = DenseMatrix.initDense 100 100 (seq { for i in 0 .. 99 do for j in 0 .. 99 -> (i,j, float i * 100.0 + float j)}) |> should equal largeM - [] - let ``DenseMatrix.constDiag`` () = + [] + let ``DenseMatrix.constDiag`` () = DenseMatrix.constDiag 100 2.0 |> should equal (2.0 * (DenseMatrix.Identity 100)) - [] - let ``DenseMatrix.diag`` () = + [] + let ``DenseMatrix.diag`` () = DenseMatrix.diag (new DenseVector(100, 2.0)) |> should equal (2.0 * (DenseMatrix.Identity 100)) - - [] - let ``DenseMatrix.init_row`` () = + + [] + let ``DenseMatrix.init_row`` () = DenseMatrix.initRow 100 100 (fun i -> (DenseVector.init 100 (fun j -> float i * 100.0 + float j))) |> should equal largeM - - [] - let ``DenseMatrix.init_col`` () = + + [] + let ``DenseMatrix.init_col`` () = DenseMatrix.initCol 100 100 (fun j -> (DenseVector.init 100 (fun i -> float i * 100.0 + float j))) |> should equal largeM diff --git a/src/FSharpUnitTests/DenseVectorTests.fs b/src/FSharpUnitTests/DenseVectorTests.fs index 346e55a6..e179c0a8 100644 --- a/src/FSharpUnitTests/DenseVectorTests.fs +++ b/src/FSharpUnitTests/DenseVectorTests.fs @@ -13,23 +13,23 @@ module DenseVectorTests = /// A large vector with increasingly large entries let largev = new DenseVector( Array.init 100 (fun i -> float i / 100.0) ) - - [] - let ``DenseVector.init`` () = + + [] + let ``DenseVector.init`` () = DenseVector.init 100 (fun i -> float i / 100.0) |> should equal largev - - [] - let ``DenseVector.ofList`` () = + + [] + let ``DenseVector.ofList`` () = DenseVector.ofList [ for i in 0 .. 99 -> float i / 100.0 ] |> should equal largev - - [] - let ``DenseVector.ofSeq`` () = + + [] + let ``DenseVector.ofSeq`` () = DenseVector.ofSeq (seq { for i in 0 .. 99 -> float i / 100.0 }) |> should equal largev - - [] - let ``DenseVector.rangef`` () = + + [] + let ``DenseVector.rangef`` () = DenseVector.rangef 0.0 0.01 0.99 |> should equal (new DenseVector( [| for i in 0 .. 99 -> 0.01 * float i |] ) ) - - [] - let ``DenseVector.range`` () = + + [] + let ``DenseVector.range`` () = DenseVector.range 0 99 |> should equal (new DenseVector( [| for i in 0 .. 99 -> float i |] ) ) diff --git a/src/FSharpUnitTests/FsUnit.fs b/src/FSharpUnitTests/FsUnit.fs index d5c8a7a7..a0a37243 100644 --- a/src/FSharpUnitTests/FsUnit.fs +++ b/src/FSharpUnitTests/FsUnit.fs @@ -9,7 +9,7 @@ let should (f : 'a -> #Constraint) x (y : obj) = | :? (unit -> unit) -> box (TestDelegate(y :?> unit -> unit)) | _ -> y Assert.That(y, c) - + let equal x = EqualConstraint(x) let equalWithin tolerance x = equal(x).Within tolerance diff --git a/src/FSharpUnitTests/MatrixTests.fs b/src/FSharpUnitTests/MatrixTests.fs index bea3a6c9..60526fe4 100644 --- a/src/FSharpUnitTests/MatrixTests.fs +++ b/src/FSharpUnitTests/MatrixTests.fs @@ -14,99 +14,99 @@ module MatrixTests = /// A large vector with increasingly large entries let largeM = new DenseMatrix( Array2D.init 100 100 (fun i j -> float i * 100.0 + float j) ) - - [] - let ``Matrix.fold`` () = + + [] + let ``Matrix.fold`` () = Matrix.fold (fun a b -> a - b) 0.0 smallM |> should equal -1.2 - [] - let ``Matrix.foldBack`` () = + [] + let ``Matrix.foldBack`` () = Matrix.foldBack (fun a b -> a - b) 0.0 smallM |> should equal 0.0 - [] - let ``Matrix.foldBackSummation`` () = + [] + let ``Matrix.foldBackSummation`` () = Matrix.foldBack( fun a b -> a + b) 0.0 failingFoldBackM |> should equal 6.0 - [] - let ``Matrix.foldi`` () = + [] + let ``Matrix.foldi`` () = Matrix.foldi (fun i j acc x -> acc + x + float (i+j)) 0.0 smallM |> should equal 5.2 - - [] - let ``Matrix.toArray2`` () = + + [] + let ``Matrix.toArray2`` () = Matrix.toArray2 smallM |> should array2_equal (Array2D.create 2 2 0.3) - - [] - let ``Matrix.forall`` () = + + [] + let ``Matrix.forall`` () = Matrix.forall (fun x -> x = 0.3) smallM |> should equal true - - [] - let ``Matrix.exists`` () = + + [] + let ``Matrix.exists`` () = Matrix.exists (fun x -> x = 0.5) smallM |> should equal false - - [] - let ``Matrix.foralli`` () = + + [] + let ``Matrix.foralli`` () = Matrix.foralli (fun i j x -> x = float i * 100.0 + float j) largeM |> should equal true - - [] - let ``Matrix.existsi`` () = + + [] + let ``Matrix.existsi`` () = Matrix.existsi (fun i j x -> x = float i * 100.0 + float j) largeM |> should equal true - - [] - let ``Matrix.map`` () = + + [] + let ``Matrix.map`` () = Matrix.map (fun x -> 2.0 * x) smallM |> should equal (2.0 * smallM) - - [] - let ``Matrix.mapi`` () = + + [] + let ``Matrix.mapi`` () = Matrix.mapi (fun i j x -> float i * 100.0 + float j + x) largeM |> should equal (2.0 * largeM) - - [] - let ``Matrix.mapCols`` () = + + [] + let ``Matrix.mapCols`` () = Matrix.mapCols (fun j col -> col.Add(float j)) smallM |> should (approximately_matrix_equal 14) (matrix [[0.3;1.3];[0.3;1.3]]) - - [] - let ``Matrix.mapRows`` () = + + [] + let ``Matrix.mapRows`` () = Matrix.mapRows (fun i row -> row.Add(float i)) smallM |> should (approximately_matrix_equal 14) (matrix [[0.3;0.3];[1.3;1.3]]) - - [] - let ``Matrix.inplaceAssign`` () = + + [] + let ``Matrix.inplaceAssign`` () = let N = smallM.Clone() Matrix.inplaceAssign (fun i j -> 0.0) N N |> should equal (0.0 * smallM) - - [] - let ``Matrix.inplaceMapi`` () = + + [] + let ``Matrix.inplaceMapi`` () = let N = largeM.Clone() Matrix.inplaceMapi (fun i j x -> 2.0 * (float i * 100.0 + float j) + x) N N |> should equal (3.0 * largeM) - - [] - let ``Matrix.nonZeroEntries`` () = + + [] + let ``Matrix.nonZeroEntries`` () = Seq.length (Matrix.nonZeroEntries smallM) |> should equal 4 - - [] - let ``Matrix.sum`` () = + + [] + let ``Matrix.sum`` () = Matrix.sum smallM |> should equal 1.2 - - [] - let ``Matrix.sumColsBy`` () = + + [] + let ``Matrix.sumColsBy`` () = Matrix.sumColsBy (fun j col -> col.[0] * col.[1]) (matrix [[1.0; 2.0]; [3.0; 4.0]]) |> should equal 11.0 - - [] - let ``Matrix.sumRowsBy`` () = + + [] + let ``Matrix.sumRowsBy`` () = Matrix.sumRowsBy (fun i row -> row.[0] * row.[1]) (matrix [[1.0; 2.0]; [3.0; 4.0]]) |> should equal 14.0 - [] - let ``Matrix.foldCol`` () = + [] + let ``Matrix.foldCol`` () = Matrix.foldCol (+) 0.0 largeM 0 |> should equal 495000.0 - - [] - let ``Matrix.foldRow`` () = + + [] + let ``Matrix.foldRow`` () = Matrix.foldRow (+) 0.0 largeM 0 |> should equal 4950.0 - [] - let ``Matrix.foldByCol`` () = + [] + let ``Matrix.foldByCol`` () = Matrix.foldByCol (+) 0.0 smallM |> should equal (DenseVector.ofList [0.6;0.6] :> Vector) - - [] - let ``Matrix.foldByRow`` () = + + [] + let ``Matrix.foldByRow`` () = Matrix.foldByRow (+) 0.0 smallM |> should equal (DenseVector.ofList [0.6;0.6] :> Vector) diff --git a/src/FSharpUnitTests/RandomVariableTests.fs b/src/FSharpUnitTests/RandomVariableTests.fs index 93305aab..3d4267a0 100644 --- a/src/FSharpUnitTests/RandomVariableTests.fs +++ b/src/FSharpUnitTests/RandomVariableTests.fs @@ -17,9 +17,9 @@ let sumOfTwoFairDices = randomVariable { [] let ``When creating two fair dices, then P(Sum of 2 dices = 7) should be 1/6``() = - sumOfTwoFairDices + sumOfTwoFairDices |> RandomVariable.filter ((=) 7) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (1N/6N) let fairCoinAndDice = randomVariable { @@ -29,23 +29,23 @@ let fairCoinAndDice = randomVariable { [] let ``When creating a fair coin and a fair dice, then P(Heads) should be 1/2``() = - fairCoinAndDice + fairCoinAndDice |> RandomVariable.filter (fun (_,c) -> c = Heads) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (1N/2N) [] let ``When creating a fair coin and a fair dice, then P(Heads and dice > 3) should be 1/4``() = - fairCoinAndDice + fairCoinAndDice |> RandomVariable.filter (fun (d,c) -> c = Heads && d > 3) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (1N/4N) // MontyHall Problem -// See Martin Erwig and Steve Kollmansberger's paper +// See Martin Erwig and Steve Kollmansberger's paper // "Functional Pearls: Probabilistic functional programming in Haskell" -type Outcome = +type Outcome = | Car | Goat @@ -53,18 +53,18 @@ let firstChoice = RandomVariable.toUniformDistribution [Car; Goat; Goat] let switch firstCoice = match firstCoice with - | Car -> + | Car -> // If you had the car and you switch ==> you lose since there are only goats left - RandomVariable.certainly Goat - | Goat -> + RandomVariable.certainly Goat + | Goat -> // If you had the goat, the host has to take out another goat ==> you win - RandomVariable.certainly Car - + RandomVariable.certainly Car + [] let ``When making the first choice in a MontyHall situation, the chances to win should be 1/3``() = - firstChoice + firstChoice |> RandomVariable.filter ((=) Car) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (1N/3N) let montyHallWithSwitch = randomVariable { @@ -75,5 +75,5 @@ let montyHallWithSwitch = randomVariable { let ``When switching in a MontyHall situation, the chances to win should be 2/3``() = montyHallWithSwitch |> RandomVariable.filter ((=) Car) - |> RandomVariable.probability + |> RandomVariable.probability |> should equal (2N/3N) \ No newline at end of file diff --git a/src/FSharpUnitTests/SparseMatrixTests.fs b/src/FSharpUnitTests/SparseMatrixTests.fs index 0ebfc195..3650d35a 100644 --- a/src/FSharpUnitTests/SparseMatrixTests.fs +++ b/src/FSharpUnitTests/SparseMatrixTests.fs @@ -3,23 +3,23 @@ open NUnit.Framework open FsUnit open MathNet.Numerics.LinearAlgebra.Generic -open MathNet.Numerics.LinearAlgebra.Double +open MathNet.Numerics.LinearAlgebra.Double /// Unit tests for the sparse matrix type. module SparseMatrixTests = /// A small uniform vector. let smallM = DenseMatrix.init 4 4 (fun i j -> if i = 1 && j = 2 then 1.0 else 0.0) :> Matrix - - [] + + [] let ``SparseMatrix.ofList`` () = (SparseMatrix.ofList 4 4 [(1,2,1.0)] :> Matrix) |> should equal smallM - [] + [] let ``SparseMatrix.ofSeq`` () = (SparseMatrix.ofSeq 4 4 (Seq.ofList [(1,2,1.0)]) :> Matrix) |> should equal smallM - [] + [] let ``SparseMatrix.constDiag`` () = SparseMatrix.constDiag 100 2.0 |> should equal (2.0 * (SparseMatrix.Identity 100)) diff --git a/src/FSharpUnitTests/SparseVectorTests.fs b/src/FSharpUnitTests/SparseVectorTests.fs index 9526695d..d6753894 100644 --- a/src/FSharpUnitTests/SparseVectorTests.fs +++ b/src/FSharpUnitTests/SparseVectorTests.fs @@ -10,12 +10,12 @@ module SparseVectorTests = /// A small uniform vector. let smallv = new DenseVector( [|0.0;0.3;0.0;0.0;0.0|] ) :> Vector - - [] - let ``SparseVector.ofList`` () = + + [] + let ``SparseVector.ofList`` () = (SparseVector.ofList 5 [ (1,0.3) ] :> Vector) |> should equal smallv - - [] - let ``SparseVector.ofSeq`` () = + + [] + let ``SparseVector.ofSeq`` () = (SparseVector.ofSeq 5 (List.toSeq [ (1,0.3) ]) :> Vector) |> should equal smallv diff --git a/src/FSharpUnitTests/Utilities.fs b/src/FSharpUnitTests/Utilities.fs index 77975580..29d06bc0 100644 --- a/src/FSharpUnitTests/Utilities.fs +++ b/src/FSharpUnitTests/Utilities.fs @@ -1,4 +1,4 @@ -// First version copied from the F# Power Pack +// 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 @@ -7,9 +7,9 @@ open System open System.Collections.Generic [] -module Utilities = +module Utilities = let test msg b = Assert.IsTrue(b, "MiniTest '" + msg + "'") - let logMessage msg = + let logMessage msg = System.Console.WriteLine("LOG:" + msg) // System.Diagnostics.Trace.WriteLine("LOG:" + msg) let check msg v1 v2 = test msg (v1 = v2) @@ -18,85 +18,85 @@ module Utilities = let throws f = try f() |> ignore; false with e -> true let countEnumeratorsAndCheckedDisposedAtMostOnceAtEnd (seq: seq<'a>) = - let enumerator() = + 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 + { new System.Collections.Generic.IEnumerator<'a> with member x.Current = test "rvlrve0" (not !endReached); test "rvlrve1" (not !disposed); ie.Current - member x.Dispose() = + 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() = + 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 = + member x.Current = test "qrvlrve0" (not !endReached); test "qrvlrve1" (not !disposed); box ie.Current - member x.Reset() = + member x.Reset() = ie.Reset() } in - { new seq<'a> with - member x.GetEnumerator() = enumerator() - interface System.Collections.IEnumerable with + { 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 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 + { new System.Collections.Generic.IEnumerator<'a> with member x.Current = test "qrvlrve0" (not !endReached); test "qrvlrve1" (not !disposed); ie.Current - member x.Dispose() = + member x.Dispose() = test "qrvlrve4" (not !disposed); numActiveEnumerators := !numActiveEnumerators - 1; disposed := true; - ie.Dispose() - interface System.Collections.IEnumerator with - member x.MoveNext() = + 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 = + member x.Current = test "qrvlrve0" (not !endReached); test "qrvlrve1" (not !disposed); box ie.Current - member x.Reset() = + member x.Reset() = ie.Reset() } in - { new seq<'a> with - member x.GetEnumerator() = enumerator() - interface System.Collections.IEnumerable with + { 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) + 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) = diff --git a/src/FSharpUnitTests/VectorTests.fs b/src/FSharpUnitTests/VectorTests.fs index b0f0294b..b24f5d00 100644 --- a/src/FSharpUnitTests/VectorTests.fs +++ b/src/FSharpUnitTests/VectorTests.fs @@ -13,91 +13,91 @@ module VectorTests = /// A large vector with increasingly large entries let largev = new DenseVector(Array.init 100 (fun i -> float i / 100.0)) :> Vector - - [] - let ``Vector.toArray`` () = + + [] + let ``Vector.toArray`` () = Vector.toArray smallv |> should array_equal [|0.3;0.3;0.3;0.3;0.3|] - [] - let ``Vector.toList`` () = + [] + let ``Vector.toList`` () = Vector.toList smallv |> should equal [0.3;0.3;0.3;0.3;0.3] - - [] - let ``Vector.mapInPlace`` () = + + [] + let ``Vector.mapInPlace`` () = let w = smallv.Clone() Vector.mapInPlace (fun x -> 2.0 * x) w w |> should equal (2.0 * smallv) - - [] - let ``Vector.mapiInPlace`` () = + + [] + let ``Vector.mapiInPlace`` () = let w = largev.Clone() Vector.mapiInPlace (fun i x -> float i / 100.0) w w |> should equal (largev) - - [] - let ``Vector.addInPlace`` () = + + [] + let ``Vector.addInPlace`` () = let w = largev.Clone() Vector.addInPlace w largev w |> should equal (2.0 * largev) - - [] - let ``Vector.subInPlace`` () = + + [] + let ``Vector.subInPlace`` () = let w = largev.Clone() Vector.subInPlace w largev w |> should equal (0.0 * largev) - [] - let ``Vector.map`` () = + [] + let ``Vector.map`` () = Vector.map (fun x -> 2.0 * x) largev |> should equal (2.0 * largev) - - [] - let ``Vector.mapi`` () = + + [] + let ``Vector.mapi`` () = Vector.mapi (fun i x -> float i / 100.0) largev |> should equal largev - [] - let ``Vector.fold`` () = + [] + let ``Vector.fold`` () = Vector.fold (fun a b -> a - b) 0.0 smallv |> should equal -1.5 - - [] - let ``Vector.foldBack`` () = + + [] + let ``Vector.foldBack`` () = Vector.foldBack (fun a b -> a - b) 0.0 smallv |> should equal 0.0 - - [] - let ``Vector.foldi`` () = + + [] + let ``Vector.foldi`` () = Vector.foldi (fun i a b -> a + b) 0.0 smallv |> should equal 1.5 - - [] - let ``Vector.forall`` () = + + [] + let ``Vector.forall`` () = Vector.forall (fun x -> x = 0.3) smallv |> should equal true - - [] - let ``Vector.exists`` () = + + [] + let ``Vector.exists`` () = Vector.exists (fun x -> x = 0.3) smallv |> should equal true - - [] - let ``Vector.foralli`` () = + + [] + let ``Vector.foralli`` () = Vector.foralli (fun i x -> x = 0.3 && i < 5) smallv |> should equal true - - [] - let ``Vector.existsi`` () = + + [] + let ``Vector.existsi`` () = Vector.existsi (fun i x -> x = 0.3 && i = 2) smallv |> should equal true - - [] - let ``Vector.scan`` () = - Vector.scan (fun acc x -> acc + x) smallv |> should (approximately_vector_equal 14) (new DenseVector( [|0.3;0.6;0.9;1.2;1.5|] ) :> Vector) - - [] - let ``Vector.scanBack`` () = - Vector.scanBack (fun x acc -> acc + x) smallv |> should (approximately_vector_equal 14) (new DenseVector( [|1.5;1.2;0.9;0.6;0.3|] ) :> Vector) - - [] - let ``Vector.reduce`` () = + + [] + let ``Vector.scan`` () = + Vector.scan (fun acc x -> acc + x) smallv |> should (approximately_vector_equal 14) (new DenseVector( [|0.3;0.6;0.9;1.2;1.5|] ) :> Vector) + + [] + let ``Vector.scanBack`` () = + Vector.scanBack (fun x acc -> acc + x) smallv |> should (approximately_vector_equal 14) (new DenseVector( [|1.5;1.2;0.9;0.6;0.3|] ) :> Vector) + + [] + let ``Vector.reduce`` () = Vector.reduce (fun acc x -> acc ** x) smallv |> should (approximately_equal 14) 0.990295218585507 - - [] - let ``Vector.reduceBack`` () = + + [] + let ``Vector.reduceBack`` () = Vector.reduceBack (fun x acc -> x ** acc) smallv |> should (approximately_equal 14) 0.488911287726319 - - [] - let ``Vector.insert`` () = + + [] + let ``Vector.insert`` () = Vector.insert 2 0.5 smallv |> should (approximately_vector_equal 14) (new DenseVector ( [|0.3;0.3;0.5;0.3;0.3;0.3|] ) :> Vector) From 513c33c90bcdd766676718782f1acee9b3e827fc Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 5 Dec 2012 11:49:53 +0100 Subject: [PATCH 14/15] Build: simplify compilation symbols --- .../BigIntegerExtensions.fs | 12 ++++++++---- src/FSharp/FSharp.fsproj | 5 +++-- src/FSharpPortable/FSharpPortable.fsproj | 8 +++++--- src/Numerics/Complex32.cs | 2 +- src/Numerics/Complex64.cs | 10 +--------- .../NumberTheory/IntegerTheory.Euclid.Big.cs | 2 +- src/Numerics/Numerics.csproj | 6 +++--- 7 files changed, 22 insertions(+), 23 deletions(-) rename src/{FSharpPortable => FSharp}/BigIntegerExtensions.fs (83%) diff --git a/src/FSharpPortable/BigIntegerExtensions.fs b/src/FSharp/BigIntegerExtensions.fs similarity index 83% rename from src/FSharpPortable/BigIntegerExtensions.fs rename to src/FSharp/BigIntegerExtensions.fs index 706a3567..16525cfa 100644 --- a/src/FSharpPortable/BigIntegerExtensions.fs +++ b/src/FSharp/BigIntegerExtensions.fs @@ -2,8 +2,10 @@ open System +#if PORTABLE + [] -module BigIntegerExtensions = +module BigIntegerExtensions = let private parse str = let len = String.length str @@ -12,19 +14,21 @@ module BigIntegerExtensions = acc else let c = str.[i] - let d = int c - int '0' + let d = int c - int '0' if 0 <= d && d <= 9 then build (10I * acc + (bigint d)) (i+1) else - raise (new FormatException("The value could not be parsed")) + raise (new FormatException("The value could not be parsed")) build 0I 0 type BigInteger with static member Parse(text: string) = - let len = text.Length + let len = text.Length if len = 0 then raise (new FormatException("The value could not be parsed")) if text.[0..0] = "-" then parse text.[1..len-1] |> bigint.Negate else parse text + +#endif diff --git a/src/FSharp/FSharp.fsproj b/src/FSharp/FSharp.fsproj index e0f6d1bb..5c9bd21a 100644 --- a/src/FSharp/FSharp.fsproj +++ b/src/FSharp/FSharp.fsproj @@ -19,7 +19,7 @@ false false ..\..\out\debug\Net40\ - DEBUG;TRACE + TRACE;DEBUG 3 @@ -38,7 +38,7 @@ pdbonly true true - TRACE + TRACE;STRONGNAME 3 ..\..\out\lib\Net40\MathNet.Numerics.FSharp.xml bin\Release-Signed\ @@ -59,6 +59,7 @@ + diff --git a/src/FSharpPortable/FSharpPortable.fsproj b/src/FSharpPortable/FSharpPortable.fsproj index 890b4b2d..6ec5ad09 100644 --- a/src/FSharpPortable/FSharpPortable.fsproj +++ b/src/FSharpPortable/FSharpPortable.fsproj @@ -19,7 +19,7 @@ false false ..\..\out\debug\Portable\ - TRACE;DEBUG;PORTABLE;FX_NO_BIGINT + TRACE;DEBUG;PORTABLE 3 @@ -29,7 +29,7 @@ true true ..\..\out\lib\Portable\ - TRACE;PORTABLE;FX_NO_BIGINT + TRACE;PORTABLE 3 ..\..\out\lib\Portable\MathNet.Numerics.FSharp.XML @@ -68,7 +68,9 @@ Complex.fs - + + BigIntegerExtensions.fs + BigRational.fsi diff --git a/src/Numerics/Complex32.cs b/src/Numerics/Complex32.cs index 43fa8465..bbeb7bad 100644 --- a/src/Numerics/Complex32.cs +++ b/src/Numerics/Complex32.cs @@ -1199,7 +1199,7 @@ namespace MathNet.Numerics return new Complex32(value, 0.0f); } -#if SYSNUMERICS +#if !PORTABLE /// /// Implicit conversion of a BigInteger int to a Complex32. /// diff --git a/src/Numerics/Complex64.cs b/src/Numerics/Complex64.cs index 72d55df6..9ef3a678 100644 --- a/src/Numerics/Complex64.cs +++ b/src/Numerics/Complex64.cs @@ -28,7 +28,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if !SYSNUMERICS +#if PORTABLE namespace System.Numerics { using System; @@ -38,10 +38,6 @@ namespace System.Numerics using MathNet.Numerics; using MathNet.Numerics.Properties; -#if !PORTABLE - using System.Runtime; -#endif - /// /// 64-bit double precision complex numbers class. /// @@ -722,11 +718,7 @@ namespace System.Numerics } } -#if PORTABLE var value = GlobalizationHelper.ParseSingle(ref token); -#else - var value = GlobalizationHelper.ParseSingle(ref token, format.GetCultureInfo()); -#endif // handle suffix imaginary symbol if (token != null && (String.Compare(token.Value, "i", StringComparison.OrdinalIgnoreCase) == 0 diff --git a/src/Numerics/NumberTheory/IntegerTheory.Euclid.Big.cs b/src/Numerics/NumberTheory/IntegerTheory.Euclid.Big.cs index aec35941..c277573b 100644 --- a/src/Numerics/NumberTheory/IntegerTheory.Euclid.Big.cs +++ b/src/Numerics/NumberTheory/IntegerTheory.Euclid.Big.cs @@ -28,7 +28,7 @@ // OTHER DEALINGS IN THE SOFTWARE. // -#if SYSNUMERICS +#if !PORTABLE namespace MathNet.Numerics.NumberTheory { using System; diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 6c590857..c008f289 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -43,7 +43,7 @@ true full false - TRACE;DEBUG;SYSNUMERICS + TRACE;DEBUG prompt 4 @@ -57,7 +57,7 @@ false pdbonly true - TRACE;SYSNUMERICS + TRACE prompt 4 AllRules.ruleset @@ -70,7 +70,7 @@ pdbonly AnyCPU true - TRACE;SYSNUMERICS;STRONGNAME + TRACE;STRONGNAME prompt 4 AllRules.ruleset From 47300d4afeb5d89c40e53dc0d46abef90a771ce1 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 5 Dec 2012 12:13:35 +0100 Subject: [PATCH 15/15] Portable: F# unit tests (targetting .Net 4.5) --- MathNet.Numerics.Portable.sln | 11 ++ packages/repositories.config | 1 + src/FSharpPortableUnitTests/App.config | 11 ++ .../FSharpPortableUnitTests.fsproj | 104 ++++++++++++++++++ src/FSharpPortableUnitTests/packages.config | 4 + 5 files changed, 131 insertions(+) create mode 100644 src/FSharpPortableUnitTests/App.config create mode 100644 src/FSharpPortableUnitTests/FSharpPortableUnitTests.fsproj create mode 100644 src/FSharpPortableUnitTests/packages.config diff --git a/MathNet.Numerics.Portable.sln b/MathNet.Numerics.Portable.sln index 9e219e96..1683609b 100644 --- a/MathNet.Numerics.Portable.sln +++ b/MathNet.Numerics.Portable.sln @@ -12,6 +12,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Portable", "src\Portable\Po EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpPortable", "src\FSharpPortable\FSharpPortable.fsproj", "{F5F14D76-428D-43D7-8431-5B885F1BA419}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{81D2CBEF-1C80-4389-A341-F010E8F86CDB}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpPortableUnitTests", "src\FSharpPortableUnitTests\FSharpPortableUnitTests.fsproj", "{90CE8E32-354E-4728-8FE6-87342F469321}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,8 +30,15 @@ Global {F5F14D76-428D-43D7-8431-5B885F1BA419}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5F14D76-428D-43D7-8431-5B885F1BA419}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5F14D76-428D-43D7-8431-5B885F1BA419}.Release|Any CPU.Build.0 = Release|Any CPU + {90CE8E32-354E-4728-8FE6-87342F469321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90CE8E32-354E-4728-8FE6-87342F469321}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90CE8E32-354E-4728-8FE6-87342F469321}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90CE8E32-354E-4728-8FE6-87342F469321}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {90CE8E32-354E-4728-8FE6-87342F469321} = {81D2CBEF-1C80-4389-A341-F010E8F86CDB} + EndGlobalSection EndGlobal diff --git a/packages/repositories.config b/packages/repositories.config index f8ef82c6..0c61614f 100644 --- a/packages/repositories.config +++ b/packages/repositories.config @@ -1,5 +1,6 @@  + diff --git a/src/FSharpPortableUnitTests/App.config b/src/FSharpPortableUnitTests/App.config new file mode 100644 index 00000000..10beb7c0 --- /dev/null +++ b/src/FSharpPortableUnitTests/App.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/FSharpPortableUnitTests/FSharpPortableUnitTests.fsproj b/src/FSharpPortableUnitTests/FSharpPortableUnitTests.fsproj new file mode 100644 index 00000000..fde0864c --- /dev/null +++ b/src/FSharpPortableUnitTests/FSharpPortableUnitTests.fsproj @@ -0,0 +1,104 @@ + + + + + Debug + AnyCPU + 2.0 + 90ce8e32-354e-4728-8fe6-87342f469321 + Library + FSharpPortableUnitTests + FSharpPortableUnitTests + v4.5 + FSharpPortableUnitTests + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + bin\Debug\FSharpPortableUnitTests.XML + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + bin\Release\FSharpPortableUnitTests.XML + + + 11 + + + + + + FsUnit.fs + + + VectorTests.fs + + + SparseVectorTests.fs + + + DenseVectorTests.fs + + + MatrixTests.fs + + + SparseMatrixTests.fs + + + DenseMatrixTests.fs + + + Utilities.fs + + + BigRationalTests.fs + + + RandomVariableTests.fs + + + PokerTests.fs + + + + + + + True + + + True + + + + + + FSharpPortable + {f5f14d76-428d-43d7-8431-5b885f1ba419} + True + + + Portable + {d877f358-a2d2-4cc3-a921-8aa5cf6187e8} + True + + + + \ No newline at end of file diff --git a/src/FSharpPortableUnitTests/packages.config b/src/FSharpPortableUnitTests/packages.config new file mode 100644 index 00000000..62adf5a5 --- /dev/null +++ b/src/FSharpPortableUnitTests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file