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)
type 'T list = List<'T>
<summary>The type of immutable singly-linked lists. </summary>
<remarks>See the <see cref="T:Microsoft.FSharp.Collections.ListModule" /> module for further operations related to lists.
Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or
the notation <c>[1; 2; 3]</c>. Use the values in the <c>List</c> module to manipulate
values of this type, or pattern match against the values directly.
See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/lists">F# Language Guide - Lists</a>.
</remarks>
val loop: ('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)
<summary>Converts the argument to signed 32-bit integer. This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>Int32.Parse()</c>
with InvariantCulture settings. Otherwise the operation requires an appropriate
static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted int</returns>
<example id="int-example"><code lang="fsharp"></code></example>
--------------------
[<Struct>]
type int = int32
<summary>An abbreviation for the CLI type <see cref="T:System.Int32" />.</summary>
<category>Basic Types</category>
--------------------
type int<'Measure> =
int
<summary>The type of 32-bit signed integer numbers, annotated with a unit of measure. The unit
of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.Int32" />.</summary>
<category>Basic Types with Units of Measure</category>
val res5UInt: uint32
Multiple items
val uint32: value: 'T -> uint32 (requires member op_Explicit)
<summary>Converts the argument to unsigned 32-bit integer. This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>UInt32.Parse()</c>
with InvariantCulture settings. Otherwise the operation requires an appropriate
static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted uint32</returns>
<example id="uint32-example"><code lang="fsharp"></code></example>
--------------------
[<Struct>]
type uint32 = System.UInt32
<summary>An abbreviation for the CLI type <see cref="T:System.UInt32" />.</summary>
<category>Basic Types</category>
--------------------
type uint32<'Measure> = uint<'Measure>
<summary>The type of 32-bit unsigned integer numbers, annotated with a unit of measure.
The unit of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.UInt32" />.</summary>
<category>Basic Types with Units of Measure</category>
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
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 ``+``)
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
<summary>Contains operations for working with values of type <see cref="T:Microsoft.FSharp.Collections.FSharpMap`2" />.</summary>
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IReadOnlyDictionary<'Key,'Value>
interface IReadOnlyCollection<KeyValuePair<'Key,'Value>>
interface IEnumerable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new: elements: seq<'Key * 'Value> -> Map<'Key,'Value>
member Add: key: 'Key * value: 'Value -> Map<'Key,'Value>
member Change: key: 'Key * f: ('Value option -> 'Value option) -> Map<'Key,'Value>
...
<summary>Immutable maps based on binary trees, where keys are ordered by F# generic comparison. By default
comparison is the F# structural comparison function or uses implementations of the IComparable interface on key values.</summary>
<remarks>See the <see cref="T:Microsoft.FSharp.Collections.MapModule" /> module for further operations on maps.
All members of this class are thread-safe and may be used concurrently from multiple threads.</remarks>
--------------------
new: elements: seq<'Key * 'Value> -> 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
[<Struct>]
type bigint = System.Numerics.BigInteger
<summary>An abbreviation for <see cref="T:System.Numerics.BigInteger" />. </summary>
<category>Basic Types</category>
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
<summary>Throw a <see cref="T:System.Exception" /> exception.</summary>
<param name="message">The exception message.</param>
<returns>Never returns.</returns>
<example id="failwith-example"><code lang="fsharp">
let failingFunction() =
failwith "Oh no" // Throws an exception
true // Never reaches this
failingFunction() // Throws a System.Exception
</code></example>
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