This library comes with some additional numeric functions and constants.
These functions work over many numeric types
let qr0 = divRem 7 3 //val qr0 : int * int = (2, 1)
let qr1 = divRem 7I 3I //val qr1 : System.Numerics.BigInteger * System.Numerics.BigInteger = (2, 1)
let qr2 = divRem 7. 3. //val qr2 : float * float = (2.333333333, 0.0) -> using default method.
Apart from typical math constants, bounded types comes with minValue
and maxValue
constants.
Here's an example how can this be used to implement an efficient findMin
function
let inline findMin (lst: 'a list) =
let rec loop acc = function
| [] -> acc
| x::_ when x = minValue -> x
| x::xs -> loop (if x < acc then x else acc) xs
loop maxValue lst
let minInt = findMin [1;0;12;2]
let minUInt = findMin [1u;0u;12u;2u] // loops only twice
Writing code that is generic over different numeric types can be really tedious in F#.
Using this library it becomes an easy task, but it's important to understand the numeric abstractions and its limitations.
In order to have a reasonable type inference over generic types we need strict operations.
For example the F# definition of (+)
can take 2 different types, this makes possible to interact with some .NET types that have defined the (+)
operator in a very arbitrary way.
For instance you can add a float
to a DateTime
with the (+)
operator, and that float
will be interpreted as seconds.
By opening the FSharpPlus.Math.Generic
namespace this will no longer be possible, because that's the tradeoff in order to get decent type inference.
Numbers with a G suffix are generics.
open FSharpPlus.Math.Generic
let res5Int : int = 5G
let res5UInt : uint32 = 5G
Often you need to define generic constants when defining generic functions.
Since there is no way to define generic decimal literals in F# at the moment of writing this, we can use divisions:
let inline areaOfCircle radio =
let pi =
314159265358979323846264338G
/
100000000000000000000000000G
pi * radio * radio
let area1 = areaOfCircle 5.
let area2 = areaOfCircle 5.0f
let area3 = areaOfCircle 5.0M
type Vector2d<'T> = Vector2d of 'T * 'T with
static member inline (+) (Vector2d(a:'t, b:'t), Vector2d(c:'t, d:'t)) = Vector2d (((a + c):'t), ((b + d):'t))
static member inline (-) (Vector2d(a:'t, b:'t), Vector2d(c:'t, d:'t)) = Vector2d (((a - c):'t), ((b - d):'t))
static member inline (*) (Vector2d(a:'t, b:'t), Vector2d(c:'t, d:'t)) = Vector2d (((a * c):'t), ((b * d):'t))
static member Return x = Vector2d (x, x)
static member Map(Vector2d(x, y), f) = Vector2d (f x, f y)
static member inline FromBigInt x = let y = fromBigInt x in Vector2d (y, y)
Note we don't define overloads for adding a vector to a number
Why? Apart from being tedious they will break math operators strictness
so we will have problems type inferencing generic functions.
OK, but then how to add (subtract, multiply) to a number?
Option 1, explicitely 'lift' the number.
Requires Return and ( + , - , * )
let x1 = Vector2d (32,5) + result 7
let x1' = result 7 + Vector2d (32,5)
Option 2, use Generic Numbers
Requires FromBigInt
and (+,-,*,/)
open FSharpPlus.Math.Generic
let x2 = Vector2d (32,5) + 7G
let x2' = 7G + Vector2d (32,5)
Option 3, use Applicative Math Operators
Requires only Map
open FSharpPlus.Math.Applicative
let x3 = Vector2d (32,5) .+ 7
let x3' = 7 +. Vector2d (32,5)
We may use types defined in other libraries, let's suppose we have this type Ratio defined somewhere.
type Ratio =
struct
val Numerator : bigint
val Denominator : bigint
new (numerator: bigint, denominator: bigint) = {Numerator = numerator; Denominator = denominator}
end
override this.ToString() = this.Numerator.ToString() + " % " + this.Denominator.ToString()
let ratio (a:bigint) (b:bigint) :Ratio =
if b = 0I then failwith "Ratio.%: zero denominator"
let a, b = if b < 0I then (-a, -b) else (a, b)
let gcd = gcd a b
Ratio (a / gcd, b / gcd)
let Ratio (x,y) = x </ratio/> y
type Ratio with
static member inline (/) (a:Ratio, b:Ratio) = (a.Numerator * b.Denominator) </ratio/> (a.Denominator * b.Numerator)
static member inline (+) (a:Ratio, b:Ratio) = (a.Numerator * b.Denominator + b.Numerator * a.Denominator) </ratio/> (a.Denominator * b.Denominator)
static member inline (-) (a:Ratio, b:Ratio) = (a.Numerator * b.Denominator - b.Numerator * a.Denominator) </ratio/> (a.Denominator * b.Denominator)
static member inline (*) (a:Ratio, b:Ratio) = (a.Numerator * b.Numerator) </ratio/> (a.Denominator * b.Denominator)
static member inline Abs (r:Ratio) = (abs r.Numerator) </ratio/> r.Denominator
static member inline Signum (r:Ratio) = (signum r.Numerator) </ratio/> 1I
static member inline FromBigInt (x:bigint) = fromBigInt x </ratio/> 1I
static member inline (~-) (r:Ratio) = -(r.Numerator) </ratio/> r.Denominator
Since most Rational implementations have Numerator and Denominator defined we can just use our generic functions on it:
let some3_2 = trySqrt (Ratio(9I, 4I))
The quadratic function has different results depending on which domain it operates.
For example for real numbers it can have 0 or 2 solutions (arguably also 1 that is a double solution).
But for complex numbers it always has 2 solutions.
open FSharpPlus.Math.Generic
let inline quadratic a b c =
let root1 = ( -b + sqrt ( b * b - 4G * a * c) ) / (2G * a)
let root2 = ( -b - sqrt ( b * b - 4G * a * c) ) / (2G * a)
(root1,root2)
let noRes = quadratic 2.0 3G 9G
// val noRes : float * float = (nan, nan)
let res30_15 = quadratic 2.0 -3G -9G
// val res30_15 : float * float = (3.0, -1.5)
let res30_15f = quadratic 2.0f -3G -9G
// val res30_15f : float32 * float32 = (3.0f, -1.5f)
let resCmplx:System.Numerics.Complex * _ = quadratic 2G -3G 9G
// val resCmplx : System.Numerics.Complex * System.Numerics.Complex = ((0.75, -1.98431348329844), (0.75, 1.98431348329844))
let res30_15r:Ratio * _ = quadratic 2G -3G -9G
// val res30_15r : Ratio * Ratio = (3 % 1, -3 % 2)
namespace FSharpPlus
val qr0: int * int
val divRem: dividend: 'Num -> divisor: 'Num -> 'Num * 'Num (requires member DivRem)
<summary>Divides one number by another, returns a tuple with the result and the remainder.</summary>
<category index="22">Numerics</category>
val qr1: System.Numerics.BigInteger * System.Numerics.BigInteger
val qr2: float * float
val findMin: lst: 'a list -> 'a (requires member MinValue and member MaxValue and comparison)
val lst: 'a list (requires member MinValue and member MaxValue and comparison)
'a
type 'T list = List<'T>
val loop: acc: 'a -> ('a list -> 'a) (requires member MinValue and member MaxValue and comparison)
val acc: 'a (requires member MinValue and member MaxValue and comparison)
val x: 'a (requires member MinValue and member MaxValue and comparison)
val minValue<'Num (requires member MinValue)> : 'Num (requires member MinValue)
<summary>The smallest possible value.</summary>
<category index="22">Numerics</category>
val xs: 'a list (requires member MinValue and member MaxValue and comparison)
val maxValue<'Num (requires member MaxValue)> : 'Num (requires member MaxValue)
<summary>The largest possible value.</summary>
<category index="22">Numerics</category>
val minInt: int
val minUInt: uint32
namespace FSharpPlus.Math
module Generic
from FSharpPlus.Math
<summary>
Generic numbers, functions and operators.
By opening this module some common operators become restricted, like (+) to 'T->'T->'T
</summary>
val res5Int: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> =
int
val res5UInt: uint32
Multiple items
val uint32: value: 'T -> uint32 (requires member op_Explicit)
--------------------
type uint32 = System.UInt32
--------------------
type uint32<'Measure> = uint<'Measure>
val areaOfCircle: radio: 'a -> 'a (requires member ( * ) and member (/) and member FromBigInt)
val radio: 'a (requires member ( * ) and member (/) and member FromBigInt)
val pi: 'a (requires member ( * ) and member (/) and member FromBigInt)
val area1: float
val area2: float32
val area3: decimal
'T
Multiple items
union case Vector2d.Vector2d: 'T * 'T -> Vector2d<'T>
--------------------
type Vector2d<'T> =
| Vector2d of 'T * 'T
static member ( * ) : Vector2d<'t> * Vector2d<'t> -> Vector2d<'t> (requires member ( * ))
static member (+) : Vector2d<'t> * Vector2d<'t> -> Vector2d<'t> (requires member ``+``)
static member (-) : Vector2d<'t> * Vector2d<'t> -> Vector2d<'t> (requires member (-))
static member FromBigInt: x: bigint -> Vector2d<'a> (requires member FromBigInt)
static member Map: Vector2d<'a> * f: ('a -> 'b) -> Vector2d<'b>
static member Return: x: 'a -> Vector2d<'a>
val a: 't (requires member ``+``)
't
val b: 't (requires member ``+``)
val c: 't (requires member ``+``)
val d: 't (requires member ``+``)
val a: 't (requires member (-))
val b: 't (requires member (-))
val c: 't (requires member (-))
val d: 't (requires member (-))
val a: 't (requires member ( * ))
val b: 't (requires member ( * ))
val c: 't (requires member ( * ))
val d: 't (requires member ( * ))
val x: 'a
Multiple items
module Map
from FSharpPlus
<summary>
Additional operations on Map<'Key, 'Value>
</summary>
--------------------
module Map
from Microsoft.FSharp.Collections
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IReadOnlyDictionary<'Key,'Value>
interface IReadOnlyCollection<KeyValuePair<'Key,'Value>>
interface IEnumerable
interface IStructuralEquatable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
member Add: key: 'Key * value: 'Value -> Map<'Key,'Value>
...
--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val y: 'a
val f: ('a -> 'b)
val x: bigint
val y: 'a (requires member FromBigInt)
val fromBigInt: x: bigint -> 'Num (requires member FromBigInt)
<summary>Converts from BigInteger to the inferred destination type.</summary>
<category index="22">Numerics</category>
val x1: Vector2d<int>
val result: x: 'T -> 'Functor<'T> (requires member Return)
<summary>
Lifts a value into a Functor. Same as return in Computation Expressions.
</summary>
<category index="2">Applicative</category>
val x1': Vector2d<int>
val x2: Vector2d<int>
val x2': Vector2d<int>
module Applicative
from FSharpPlus.Math
<summary>Math Operators ready to use over Applicative Functors.</summary>
val x3: Vector2d<int>
val x3': Vector2d<int>
Ratio.Numerator: bigint
type bigint = System.Numerics.BigInteger
Ratio.Denominator: bigint
val numerator: bigint
val denominator: bigint
val this: inref<Ratio>
val ratio: a: bigint -> b: bigint -> Ratio
val a: bigint
val b: bigint
Multiple items
[<Struct>]
type Ratio =
new: numerator: bigint * denominator: bigint -> Ratio
val Numerator: bigint
val Denominator: bigint
override ToString: unit -> string
static member ( * ) : a: Ratio * b: Ratio -> Ratio
static member (+) : a: Ratio * b: Ratio -> Ratio
static member (-) : a: Ratio * b: Ratio -> Ratio
static member (/) : a: Ratio * b: Ratio -> Ratio
static member Abs: r: Ratio -> Ratio
static member FromBigInt: x: bigint -> Ratio
...
--------------------
Ratio ()
new: numerator: bigint * denominator: bigint -> Ratio
val failwith: message: string -> 'T
val gcd: bigint
Multiple items
val Ratio: x: bigint * y: bigint -> Ratio
--------------------
[<Struct>]
type Ratio =
new: numerator: bigint * denominator: bigint -> Ratio
val Numerator: bigint
val Denominator: bigint
override ToString: unit -> string
static member ( * ) : a: Ratio * b: Ratio -> Ratio
static member (+) : a: Ratio * b: Ratio -> Ratio
static member (-) : a: Ratio * b: Ratio -> Ratio
static member (/) : a: Ratio * b: Ratio -> Ratio
static member Abs: r: Ratio -> Ratio
static member FromBigInt: x: bigint -> Ratio
...
--------------------
Ratio ()
new: numerator: bigint * denominator: bigint -> Ratio
val y: bigint
val a: Ratio
val b: Ratio
val r: Ratio
val abs: value: 'Num -> 'Num (requires member Abs)
<summary> Gets the absolute value of the given number.
<para /> Rule: signum x * abs x = x </summary>
<category index="22">Numerics</category>
<param name="value">The input value.</param>
<returns>The absolute value of the input.</returns>
val signum: value: 'Num -> 'Num (requires member Signum)
<summary>Sign of the given number
<para /> Rule: signum x * abs x = x </summary>
<category index="22">Numerics</category>
<param name="value">The input value.</param>
<returns>-1, 0, or 1 depending on the sign of the input.</returns>
val some3_2: Ratio option
val trySqrt: x: 'a -> 'a option (requires member TrySqrt)
<summary>Square root of a number of any type. Returns None if there is no square root.</summary>
<category index="22">Numerics</category>
val quadratic: a: 'a -> b: 'a -> c: 'a -> 'a * 'a (requires member FromInt32 and member ( * ) and member Sqrt and member (~-) and member (/) and member ``+`` and member (-))
val a: 'a (requires member FromInt32 and member ( * ) and member Sqrt and member (~-) and member (/) and member ``+`` and member (-))
val b: 'a (requires member FromInt32 and member ( * ) and member Sqrt and member (~-) and member (/) and member ``+`` and member (-))
val c: 'a (requires member FromInt32 and member ( * ) and member Sqrt and member (~-) and member (/) and member ``+`` and member (-))
val root1: 'a (requires member FromInt32 and member ( * ) and member Sqrt and member (~-) and member (/) and member ``+`` and member (-))
val sqrt: x: 'b -> 'b (requires member Sqrt)
<summary>Square root of a number of any type. Throws an exception if there is no square root.</summary>
<category index="22">Numerics</category>
val root2: 'a (requires member FromInt32 and member ( * ) and member Sqrt and member (~-) and member (/) and member ``+`` and member (-))
val noRes: float * float
val res30_15: float * float
val res30_15f: float32 * float32
val resCmplx: System.Numerics.Complex * System.Numerics.Complex
namespace System
namespace System.Numerics
Multiple items
[<Struct>]
type Complex =
new: real: float * imaginary: float -> unit
member Equals: value: Complex -> bool + 1 overload
member GetHashCode: unit -> int
member ToString: unit -> string + 3 overloads
static member ( * ) : left: float * right: Complex -> Complex + 2 overloads
static member (+) : left: float * right: Complex -> Complex + 2 overloads
static member (-) : left: float * right: Complex -> Complex + 2 overloads
static member (/) : left: float * right: Complex -> Complex + 2 overloads
static member (<>) : left: Complex * right: Complex -> bool
static member (=) : left: Complex * right: Complex -> bool
...
<summary>Represents a complex number.</summary>
--------------------
System.Numerics.Complex ()
System.Numerics.Complex(real: float, imaginary: float) : System.Numerics.Complex
val res30_15r: Ratio * Ratio