# F# Math (III.) - Defining custom numeric types

In this article, we define an F# numeric type for calculating in the modular arithmetic (also called clock arithmetic) [1]. Modular arithmetic is used for calculations where we want to keep a value within a specified range by counting in cycles. For example, a maximal value on clock is 12 hours. When we add 11 hours and 3 hours, the value overflows and the result is 2 hours. Aside from clocks, this numeric system is also essential in cryptography or, for example, in music.

This tutorial shows several techniques that are essential when defining any new numeric type in F#. Most importantly, you’ll learn how to:

• Define a numeric type with overloaded operators
• Define a numeric literal for constructing numbers of our new type
• Enable calculating with our type in F# lists and matrices
• Hide implementation details of a numeric type

We define type `IntegerZ5` that implements modular arithmetic with modulus 5, meaning that valid values are in the range from 0 to 4 and we equip the type with operations such as addition and multiplication. When an operation produces a value that would be outside of the range, we adjust it by adding or subtracting the modulus (in our case 5). Here are some examples of calculations that we’ll be able to write:

```2 + 1 = 3 (mod 5)
4 * 2 = 3 (mod 5)
List.sum [ 0; 1; 2; 3 ] = 1 (mod 5)
```

In the first case, we can perform the operation without any adjustments. In the second case, we multiply 4 by 2 and get 8 as the result, which is out of the required range. To correct it, we calculate the remainder after a division by 5 (written as `8 % 5` in F#), which gives us 3. Finally, the last example shows that we’d also like to be able to use our type with lists. If we add values 0, 1, 2 and 3, we get 6 which is adjusted to 1.

This article is a part of a series that covers some F# and F# PowerPack features for numerical computing. Other articles in this series discuss matrices, defining custom numeric types and writing generic code. For links to other parts, see F# Math - Overview of F# PowerPack.

## Defining the IntegerZ5 type

We’ll create an F# library containing our numeric type, so that it can be easily used from multiple applications. The approach we’ll use will follow the usual F# best practices for designing this kind of libraries, so you should follow similar steps when creating any other numeric type.

To start, we create a new F# Library project in Visual Studio. Once the project is created, remove the existing files and add a new file named `IntegerZ5.fs`. This file will contain the implementation of our type. We start by adding a type `IntegerZ5` into a namespace `FSharp.Numeric`:

``` 1: namespace FSharp.Numerics
2:
3: type IntegerZ5 =
4:   | Z5 of int
5:   member z.ToInt32() =
6:     let (Z5 n) = z in n
7:   override z.ToString() =
8:     sprintf "%d (mod 5)" (z.ToInt32())
9:
10:   static member Create(n) =
11:     let z5 = n % 5
12:     Z5(max ((z5 + 5) % 5) z5)```

The type is implemented as a discriminated union with a single case labeled `Z5`. The case carries a value of type `int`, but the operations that we’ll provide for creating and calculating with `IntegerZ5` will guarantee that the actual value will be within the required range (0 to 4, inclusively). The fact that we use single-case discriminated union is merely an implementation detail that allows us to implement operations with the type in a convenient way, but we'll later see how to hide this implementation detail.

The first two members convert an `IntegerZ5` value to other types. To get an integer, we simply return the underlying value. When formatting the value as a string, we append the "(mod 5)" string after the number, which is the usual way of denoting that the number is in the modular arithmetic.

The last static members provide ways for constructing `IntegerZ5` values. It takes an arbitrary integer as an argument and adjusts it, so that it is within the specified range. This is done by calculating the remainder of a division by 5 and adding 5 in case the result is negative (for example, `-13 % 5` produces -3, and the result of `max -3 2` will give us 2).

So far, we created a simple type that can be created and printed, but that’s all we can do with it so far. In the next section, we’ll equip our type with basic operators for calculating with it. As we’ll see, that’s all we need to use the type with some standard F# library functions.

## Creating and calculating with IntegerZ5

Next, let's take the `IntegerZ5` type and add operators for multiplying, adding and subtracting values of this type. We’ll also add two members that provide easy access to frequently used constants 0 and 1 and we’ll provide a simple function for constructing values of this type. The following listing adds several static members to the type:

```1: type IntegerZ5 =
2:   (Omitted)
3:   static member (+) (Z5 a, Z5 b) = IntegerZ5.Create(a + b)
4:   static member (-) (Z5 a, Z5 b) = IntegerZ5.Create(a - b)
5:   static member (*) (Z5 a, Z5 b) = IntegerZ5.Create(a * b)
6:   static member Zero = Z5 0
7:   static member One  = Z5 1```

The three overloaded operators are implemented as static members with a special name. They take two parameters of type `IntegerZ5` and we use pattern matching (using the discriminated union case label `Z5`) to immediately extract the actual integer used to represent the number. After performing the numerical operation on integers, the result may be out of the specified range, so we construct the result using the `Create` member, which adjusts the integer. The `Zero` and `One` members simply construct value using the `Z5` union case, because 0 and 1 are always valid values (so there is no need for calling the `Create` member).

Most of the standard F# numeric types can be created from other numeric types by calling a conversion function of the same name as the name of the type. For example `float 42` returns a floating point number 42.0 and `byte 42.0` converts a floating point number to a byte value `42uy`.

Numeric type conversion functions are written in a way so that they work with any numeric type that supports the conversion in some way. This means that we can write `byte 42` as well as `byte 42.0`, but not for example `byte (new System.Random())`. This behavior is achieved using static member constraints, which allow the function to specify that the argument needs to be of any type that provides a specific method.

To write a conversion function for `IntegerZ5` type, we could either use static member constraints explicitly, or we can use some existing function (such as `int`) and let the F# type inference deduce the constraints automatically. The following listing uses the second approach:

```1: [<AutoOpen>]
2: module IntegerZ5TopLevelOperations =
3:   let inline z5 a = IntegerZ5.Create(int a)```

The function `z5` needs to be marked as `inline`, which allows us to use static member constraints. Inside the body, we first convert the argument to an integer using the int function and then construct `IntegerZ5` from the returned integer. The static member constraints are propagated automatically and we’ll look at them in more details later.

Since functions cannot be declared inside a namespace, we need to enclose our function in a module. We want to make it available automatically whenever the user of our library opens the `FSharp.Numerics` namespace, which can be done by adding the `AutoOpen` attribute to the module declaration.

## Testing numeric type interactively

As a next step, we’ll use F# interactive to verify that the code we wrote so far works as expected. At this point, we didn't even build the library. We just wrote some F# source code and want to test the source code without building the library. We'll use interactive tests written in an F# script file. Once you build the library, it is quite easy to turn those into proper unit tests using NUnit, xUnit or other testing frameworks.

To test the type we implemented at the source code level, we first add a new F# Script File (Add -> New Item... -> F# Script File) named `Tests.fsx`. In this file, we load the content of `IntegerZ5.fs` and register a printer to specify how the F# Interactive tool should output values of our type:

```1: #load "IntegerZ5.fs"
2: open FSharp.Numerics
3:

The first command loads an F# source code and evaluates it in the F# Interactive tool. Next, we open the namespace `FSharp.Numerics`, which contains our type. Finally, we call the `AddPrinter` member of the global `fsi` value. The parameter is a lambda function that takes value of our `IntegerZ5` type and returns a string by simply calling the `ToString` member we provided. Note that we need to add the type annotation, because `AddPrinter` is a generic member. The type annotation allows it to determine a type for which we’re specifying the printer.

Now, we can test our function for creating `IntegerZ5` values and operators for working with them:

```1: let a = z5 3.0
2: let b = z5 7
3: val a : IntegerZ5 = "3 (mod 5)"
4: val b : IntegerZ5 = "2 (mod 5)"
5:
6: a * b;
7: val it : IntegerZ5 = "1 (mod 5)"
8: a + b
9: val it : IntegerZ5 = "0 (mod 5)"```

As we can see, when the F# Interactive outputs a value of our type, it uses the printer we specified. The output such as “3 (mod 5)” makes it clear that the value is calculated in modulus arithmetic of base 5. The first two `let` bindings show that the `z5` function works with any numeric types (including floating point numbers and integers) and that it automatically calculates the modulus of the number. The last two examples show that overloaded operators we provided also follow the rules of modulus arithmetic.

So far, we’ve been using the operators directly. The way we wrote the `IntegerZ5` type also allows us to use it with some functions from the F# core libraries. For example, if we create a list of `IntegerZ5` values, we can sum the list using the overloaded operators we provided simply like this:

```1: List.sum [ z5 0; z5 1; z5 2; z5 3; z5 4 ]
2: val it : IntegerZ5 = "0 (mod 5)"```

How does the `List.sum` function know that it needs to use the overloaded operator of `IntegerZ5` type? The function is implemented using static member constraints and requires that the type of the elements provides a + operator and also a `Zero` member. The compiler knows that our type provides the required members, so it allows us to use `List.sum` for our type. We'll look at implementing functions like this in the next article of the series.

When creating values of `IntegerZ5` in this step, we used the `z5` conversion function. Alternatively, we could use the `IntegerZ5.Create` member, but that leads to a more heavyweight notation. In the next step, we’ll see how to use numeric literals to make the syntax more convenient.

## Providing numeric literals for IntegerZ5

Values of built-in numeric types such as `int64`, `byte` or `float32` can be created using special numeric literals `-30L`, `127uy` and `42.0f`. The F# makes it possible to provide a similar literal for our own numeric types. There are several limitations to what kind of literals we can define. Briefly, the literal must end with one of characters that are reserved for this purpose by the F# language. Additionally, the literal must consist only of numeric symbols (so for example `12#3Z` isn’t allowed).

To define a literal, we need to write a module with a special name. Inside the module, we implement several functions that are used automatically in the F# compiler whenever a literal is used:

```1: module NumericLiteralZ =
2:   let FromZero () = Z5 0
3:   let FromOne  () = Z5 1
4:   let FromInt32 a = IntegerZ5.Create(a%5)
5:   let FromInt64 a = IntegerZ5.Create(int(a%5L))```

The name of the module consists of the special name `NumericLiteral` followed by a symbol `Z` which we’ll use for writing our literals. This means that we’ll be able to write literals such as `0Z`, `1Z` and `42Z`. The module may provide several functions to enable several sizes of literals.

If we included only the first two functions (`FromZero` and `FromOne`) we would be able to write only `0Z` and `1Z`. The next two functions enable writing literals for other numbers that fit into integer values. To support numeric literals that are larger than `int64`, we could also provide the `FromString` method. Note that this wouldn’t allow writing literals consisting of non-numeric characters. The main use of this method is to enable arbitrarily sized integer values, so we don’t provide it in the above example.

The following listing shows a few examples of using our newly defined numeric literals:

```1: 6Z
2: val it : IntegerZ5 = "1 (mod 5)"
3: 3Z * (4Z + 2Z)
4: val it : IntegerZ5 = "3 (mod 5)"```

As we can see, numeric literals make working with custom numeric data types easier. Since our numeric type is almost finished now, we’ll look at compiling it into a redistributable library in the next step.

Signature files can be added to a project to specify which of the members and values from an implementation file (such as `IntegerZ5.fs`) should be publicly exported by the compiled library. A signature file lists members and functions together with their type signatures, which also makes it a useful source of information about our library.

The name of the file should be the same as the name of the implementation file, but it should have the fsi extension. In our case, we’ll add a file named `IntegerZ5.fsi`. The file needs to be passed to the compiler before the implementation file. In Visual Studio, this means that you need to move the file before the implementation file in the Solution Explorer. This can be done by right-clicking on the file and selecting the “Move Up” command.

The listing below shows the source code of the signature file. Most of the type signatures that you can see in the listing can be inferred automatically by the F# compiler. An easy way of extracting the first version of F# signature file from the source code is selecting the entire content of the implementation file and evaluating it interactively. The F# Interactive prints all the inferred type signatures and we can easily copy them to the signature file.

Nevertheless, we’ll need to make one notable addition to the generated type signature. The listing also shows the type signature of the `z5` function, which is quite interesting:

``` 1: namespace FSharp.Numerics
2:
3: [<Sealed>]
4: type IntegerZ5 =
5:   member ToInt32 : unit -> int
6:   override ToString : unit -> string
7:   static member Create : int -> IntegerZ5
8:   static member One : IntegerZ5
9:   static member Zero : IntegerZ5
10:   static member ( + ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
11:   static member ( * ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
12:   static member ( - ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
13:
14: module NumericLiteralZ =
15:   val FromZero  : unit -> IntegerZ5
16:   val FromOne   : unit -> IntegerZ5
17:   val FromInt32 : int -> IntegerZ5
18:   val FromInt64 : int64 -> IntegerZ5
19:
20: module IntegerZ5TopLevelOperations =
21:   val inline z5 : ^a -> IntegerZ5 when
22:         ^a : (static member op_Explicit :  ^a -> int)```

The signature file starts with a namespace declaration, which is the same as the declaration in the implementation file. Next, it contains a single type signature and two module signatures. Note that the `IntegerZ5` type is marked with the `Sealed` attribute. This is required, because the signature file needs to reveal what kind of file we are declaring. For F# types such as records and discriminated unions, we use `Sealed` meaning that the type is not extensible.

Type signatures of the members of our numeric type as well as functions in the `NumericLiteralZ` module are mostly straightforward. The function `z5` in the last module is far more interesting. As noted earlier, the function is implemented so that it works on any argument that can be converted into an integer. In practice, this means that the type of the argument needs to provide a static member named `op_Explicit` taking the type of the argument and returning integer.

Determining whether a type of the argument has a certain method cannot be expressed in .NET generics, so we use F# specific feature named static member constraints. This works only for so-called hat types (such as `^a`). Hat types are inlined during the compilation, so the function also needs to be marked as `inline`.

Finally, it is also worth noting that if we added `op_Explicit` member to our `IntegerZ5` type, it would be possible to use our type as parameter of the `int` function (so we could write for example `int 3Z`).

## Supporting matrices using global associations

Our numeric type can be used with some standard library functions such as `List.sum`, because they implement certain members that are required by static member constraints of `List.sum`. However, this approach doesn't work with complex data types such as generic matrices. If we try to write any matrix computation with our type, it will not work, because generic Matrix type isn’t automatically able to use the overloaded operators and members we provided.

In order to support matrices we need to provide a so-called numeric association. This is a simple interface containing numeric operations that can be performed on our type. The interface needs to be registered in a global table maintained by the F# PowerPack library, so that any F# code can get the interface from the table and use it for performing operations with our type.

The F# PowerPack library contains a hierarchy of interfaces that represent various sets of numeric operations that we may be able to provide. We’ll implement the most basic interface called `INumeric<'T>` and represent a simple numeric type. Other interfaces available are for example `IIntegral<'T>`, which provides operations such as `DivRem` (division with a remainder) or `IFloating<'T>`, which additionally includes floating point operations such as `Sqrt`.

The simplest way to implement the interface is to use object expressions and create a singleton value implementing it. We also need to provide an operation that registers the instance in the `GlobalAssociations` table:

``` 1: module IntegerZ5Associations =
2:   let IntegerZ5Numerics =
3:     { new INumeric<IntegerZ5> with
4:          member z.Zero = IntegerZ5.Zero
5:          member z.One = IntegerZ5.One
6:          member z.Add(a,b) = a + b
7:          member z.Subtract(a,b) = a - b
8:          member z.Multiply(a,b) = a * b
9:          member z.Equals(Z5 a, Z5 b) = (a = b)
10:          member z.Compare(Z5 a, Z5 b) = compare a b
11:          member z.Negate(a) = 0Z - a
12:          member z.Abs(a) = a
13:          member z.Sign(Z5 a) = Math.Sign(a)
14:          member z.ToString(Z5 n,fmt,fmtprovider) =
15:            n.ToString(fmt,fmtprovider) + " (mod 5)"
16:          member z.Parse(s,numstyle,fmtprovider) =
17:            z5 (System.Int32.Parse(s,numstyle,fmtprovider)) }
18:
19:   let Init() =
20:     GlobalAssociations.RegisterNumericAssociation IntegerZ5Numerics```

Most of the operations can be implemented directly in terms of operators that we already provided in the `IntegerZ5` type, so the purpose of the `INumeric<'T>` interface is simply to make these operations accessible indirectly. Some other operations such as `Compare`, `Sign` or `ToString` can be implemented using the underlying integer (which we can extract by writing the pattern `Z5` a in place of parameter).

The module also provides `Init` function, which registers our instance in the `GlobalAssociations` table. Ideally, the registration would be done automatically when the `IntegerZ5` type is used for the first time, but this is unfortunately not easy to achieve. Instead, we'll write a function that needs to be called explicitly (to make this automatic, you could move the code to a static constructor of the `IntegerZ5` type).

To make the module visible in the compiled library, we also need to include its type signature in the F# Signature file that we added earlier. This can be done by adding the code from the following listing:

```1: module IntegerZ5Associations =
2:   val IntegerZ5Numerics : INumeric<IntegerZ5>
3:   val Init : unit -> unit```

Now that our library is complete, we can compile and use the created assembly in other F# projects. In this tutorial, we didn’t aim to create a C# compatible type, however, the type would be also usable from C# (most importantly, all the overloaded operators and members of the type would work in C#).

As a final step, we’ll look how to load the compiled library in F# interactive and use our type to do a simple computation using generic `Matrix` type:

``` 1: #r "FSharp.PowerPack.dll"
2: #r "bin\Debug\FSharp.Numerics.dll"
3:
4: open FSharp.Numerics
5: module MatrixG = Matrix.Generic
6:
7: IntegerZ5Associations.Init()
8:
9: let ys = MatrixG.init 5 5 (fun a b -> (z5 a))
10: let xs = MatrixG.init 5 5 (fun a b -> (z5 b))
11: val ys : Matrix<IntegerZ5> = (...)
12: val xs : Matrix<IntegerZ5> = (...)
13:
14: xs .* ys
15: val it : Matrix<IntegerZ5> =
16:   matrix [[0 (mod 5); 0 (mod 5); 0 (mod 5); 0 (mod 5); 0 (mod 5)]
17:           [0 (mod 5); 1 (mod 5); 2 (mod 5); 3 (mod 5); 4 (mod 5)]
18:           [0 (mod 5); 2 (mod 5); 4 (mod 5); 1 (mod 5); 3 (mod 5)]
19:           [0 (mod 5); 3 (mod 5); 1 (mod 5); 4 (mod 5); 2 (mod 5)]
20:           [0 (mod 5); 4 (mod 5); 3 (mod 5); 2 (mod 5); 1 (mod 5)]]```

After referencing F# PowerPack and our library, we open the namespace `FSharp.Numerics`, which contains our type and define a module alias `MatrixG` to simplify access to generic `Matrix` type. Next we call the `Init` function to register the global numeric associations for our type.

Finally, we construct two matrices of the size `5x5` – the first one contains increasing values of the `IntegerZ5` type along the first dimension and the values in the second matrix increase along the second dimension. We use point-wise multiplication of these two matrices to produce a simple table that shows results of multiplying all possible combinations of `IntegerZ5` values.

## Summary

In this article, we looked at implementing a custom numeric type. Although you probably won't need to implement a custom numeric type very often, it may be useful. Aside from modular arithmetic, you may want to implement a type that represents quadruples of values (to represent colors) or a type to represent 3D vectors. The F# PowerPack library uses the techniques described in this article to implement `BigRational` and `complex` types that I introduced in an earlier article.

When implementing a numeric type, there is quite a few features that you may want to support. In this article, we defined overloaded operators and members that are required by static member constraints, so that we could use functions like `List.sum`. Then we also defined numeric literals to be able to write `1Z` instead of `z5 1`. Finally, we implemented the `INumeric<'T>` interface, which makes it possible to use our type with generic matrices from F# PowerPack, which I covered in the previous article of the series.

namespace FSharp
namespace FSharp.Numerics
type IntegerZ5 =
| Z5 of int
with
member ToInt32 : unit -> int
override ToString : unit -> string
static member Create : n:int -> IntegerZ5
end

Full name: FSharp.Numerics.Version1.IntegerZ5

type: IntegerZ5
implements: System.IEquatable<IntegerZ5>
implements: System.Collections.IStructuralEquatable
implements: System.IComparable<IntegerZ5>
implements: System.IComparable
implements: System.Collections.IStructuralComparable
union case IntegerZ5.Z5: int -> IntegerZ5
Multiple items
val int : 'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------

type int = int32

Full name: Microsoft.FSharp.Core.int

type: int
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int>
implements: System.IEquatable<int>
inherits: System.ValueType

--------------------

type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>

type: int<'Measure>
implements: System.IComparable
implements: System.IConvertible
implements: System.IFormattable
implements: System.IComparable<int<'Measure>>
implements: System.IEquatable<int<'Measure>>
inherits: System.ValueType
val z : IntegerZ5

type: IntegerZ5
implements: System.IEquatable<IntegerZ5>
implements: System.Collections.IStructuralEquatable
implements: System.IComparable<IntegerZ5>
implements: System.IComparable
implements: System.Collections.IStructuralComparable
member IntegerZ5.ToInt32 : unit -> int

Full name: FSharp.Numerics.Version1.IntegerZ5.ToInt32
val n : int

type: int
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int>
implements: System.IEquatable<int>
inherits: System.ValueType
override IntegerZ5.ToString : unit -> string

Full name: FSharp.Numerics.Version1.IntegerZ5.ToString
val sprintf : Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
member IntegerZ5.ToInt32 : unit -> int
static member IntegerZ5.Create : n:int -> IntegerZ5

Full name: FSharp.Numerics.Version1.IntegerZ5.Create
val z5 : int

type: int
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int>
implements: System.IEquatable<int>
inherits: System.ValueType
val max : 'T -> 'T -> 'T (requires comparison)

Full name: Microsoft.FSharp.Core.Operators.max
type IntegerZ5 =
| Z5 of int
with
member ToInt32 : unit -> int
override ToString : unit -> string
static member Create : n:int -> IntegerZ5
static member One : IntegerZ5
static member Zero : IntegerZ5
static member ( + ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
static member ( * ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
static member ( - ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
end

Full name: FSharp.Numerics.IntegerZ5

type: IntegerZ5
implements: System.IEquatable<IntegerZ5>
implements: System.Collections.IStructuralEquatable
implements: System.IComparable<IntegerZ5>
implements: System.IComparable
implements: System.Collections.IStructuralComparable
| Z5 of int
member z.ToInt32() =
let (Z5 n) = z in n
override z.ToString() =
sprintf "%d (mod 5)" (z.ToInt32())

static member Create(n) =
let z5 = n % 5
Z5(max ((z5 + 5) % 5) z5)

val a : int

type: int
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int>
implements: System.IEquatable<int>
inherits: System.ValueType
val b : int

type: int
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int>
implements: System.IEquatable<int>
inherits: System.ValueType
static member IntegerZ5.Create : n:int -> IntegerZ5
static member IntegerZ5.Zero : IntegerZ5

Full name: FSharp.Numerics.IntegerZ5.Zero
static member IntegerZ5.One : IntegerZ5

Full name: FSharp.Numerics.IntegerZ5.One
Multiple items
type AutoOpenAttribute =
class
inherit System.Attribute
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
member Path : string
end

Full name: Microsoft.FSharp.Core.AutoOpenAttribute

type: AutoOpenAttribute
implements: System.Runtime.InteropServices._Attribute
inherits: System.Attribute

--------------------

new : unit -> AutoOpenAttribute

--------------------

new : path:string -> AutoOpenAttribute
module IntegerZ5TopLevelOperations

from FSharp.Numerics
val z5 : 'a -> IntegerZ5 (requires member op_Explicit)

Full name: FSharp.Numerics.IntegerZ5TopLevelOperations.z5
val a : 'a (requires member op_Explicit)
Directives inside modules are ignored
val fsi : Compiler.Interactive.InteractiveSession

Full name: Microsoft.FSharp.Compiler.Interactive.Settings.fsi
member Compiler.Interactive.InteractiveSession.AddPrinter : ('T -> string) -> unit
System.Object.ToString() : string
val a : IntegerZ5

Full name: Script.Testing.a

type: IntegerZ5
implements: System.IEquatable<IntegerZ5>
implements: System.Collections.IStructuralEquatable
implements: System.IComparable<IntegerZ5>
implements: System.IComparable
implements: System.Collections.IStructuralComparable
val b : IntegerZ5

Full name: Script.Testing.b

type: IntegerZ5
implements: System.IEquatable<IntegerZ5>
implements: System.Collections.IStructuralEquatable
implements: System.IComparable<IntegerZ5>
implements: System.IComparable
implements: System.Collections.IStructuralComparable
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------

type List<'T> =
| ( [] )
| ( :: ) of 'T * 'T list
with
interface System.Collections.IEnumerable
interface System.Collections.Generic.IEnumerable<'T>
member IsEmpty : bool
member Item : index:int -> 'T with get
member Length : int
member Tail : 'T list
static member Cons : head:'T * tail:'T list -> 'T list
static member Empty : 'T list
end

Full name: Microsoft.FSharp.Collections.List<_>

type: List<'T>
implements: System.Collections.IStructuralEquatable
implements: System.IComparable<List<'T>>
implements: System.IComparable
implements: System.Collections.IStructuralComparable
implements: System.Collections.Generic.IEnumerable<'T>
implements: System.Collections.IEnumerable
val sum : 'T list -> 'T (requires member ( + ) and member get_Zero)

Full name: Microsoft.FSharp.Collections.List.sum
val FromZero : unit -> IntegerZ5

Full name: Script.Testing.NumericLiteralZ.FromZero
val FromOne : unit -> IntegerZ5

Full name: Script.Testing.NumericLiteralZ.FromOne
val FromInt32 : int -> IntegerZ5

Full name: Script.Testing.NumericLiteralZ.FromInt32
val FromInt64 : int64 -> IntegerZ5

Full name: Script.Testing.NumericLiteralZ.FromInt64
val a : int64

type: int64
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int64>
implements: System.IEquatable<int64>
inherits: System.ValueType
val IntegerZ5Numerics : INumeric<IntegerZ5>

Full name: Script.IntegerZ5Associations.IntegerZ5Numerics
Multiple items
type INumeric<'T> =
interface
abstract member Abs : 'T -> 'T
abstract member Add : 'T * 'T -> 'T
abstract member Compare : 'T * 'T -> int
abstract member Equals : 'T * 'T -> bool
abstract member Multiply : 'T * 'T -> 'T
abstract member Negate : 'T -> 'T
abstract member Parse : string * Globalization.NumberStyles * IFormatProvider -> 'T
abstract member Sign : 'T -> int
abstract member Subtract : 'T * 'T -> 'T
abstract member ToString : 'T * string * IFormatProvider -> string
abstract member One : 'T
abstract member Zero : 'T
end

Full name: Microsoft.FSharp.Math.INumeric<_>

--------------------

INumeric
type IntegerZ5 =
| Z5 of int
with
member ToInt32 : unit -> int
override ToString : unit -> string
static member Create : n:int -> IntegerZ5
static member One : IntegerZ5
static member Zero : IntegerZ5
static member ( + ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
static member ( * ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
static member ( - ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
end

Full name: FSharp.Numerics.IntegerZ5

type: IntegerZ5
implements: IEquatable<IntegerZ5>
implements: Collections.IStructuralEquatable
implements: IComparable<IntegerZ5>
implements: IComparable
implements: Collections.IStructuralComparable
val z : INumeric<IntegerZ5>
property INumeric.Zero: IntegerZ5
property IntegerZ5.Zero: IntegerZ5
property INumeric.One: IntegerZ5
property IntegerZ5.One: IntegerZ5
abstract member INumeric.Add : 'T * 'T -> 'T
val a : IntegerZ5

type: IntegerZ5
implements: IEquatable<IntegerZ5>
implements: Collections.IStructuralEquatable
implements: IComparable<IntegerZ5>
implements: IComparable
implements: Collections.IStructuralComparable
val b : IntegerZ5

type: IntegerZ5
implements: IEquatable<IntegerZ5>
implements: Collections.IStructuralEquatable
implements: IComparable<IntegerZ5>
implements: IComparable
implements: Collections.IStructuralComparable
abstract member INumeric.Subtract : 'T * 'T -> 'T
abstract member INumeric.Multiply : 'T * 'T -> 'T
Object.Equals(obj: obj) : bool
abstract member INumeric.Equals : 'T * 'T -> bool
val a : int

type: int
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType
val b : int

type: int
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType
abstract member INumeric.Compare : 'T * 'T -> int
val compare : 'T -> 'T -> int (requires comparison)

Full name: Microsoft.FSharp.Core.Operators.compare
abstract member INumeric.Negate : 'T -> 'T
abstract member INumeric.Abs : 'T -> 'T
abstract member INumeric.Sign : 'T -> int
type Math =
class
static val PI : float
static val E : float
static member Abs : System.SByte -> System.SByte
static member Abs : int16 -> int16
static member Abs : int -> int
static member Abs : int64 -> int64
static member Abs : float32 -> float32
static member Abs : float -> float
static member Abs : decimal -> decimal
static member Acos : float -> float
static member Asin : float -> float
static member Atan : float -> float
static member Atan2 : float * float -> float
static member BigMul : int * int -> int64
static member Ceiling : decimal -> decimal
static member Ceiling : float -> float
static member Cos : float -> float
static member Cosh : float -> float
static member DivRem : int * int * int -> int
static member DivRem : int64 * int64 * int64 -> int64
static member Exp : float -> float
static member Floor : decimal -> decimal
static member Floor : float -> float
static member IEEERemainder : float * float -> float
static member Log : float -> float
static member Log : float * float -> float
static member Log10 : float -> float
static member Max : System.SByte * System.SByte -> System.SByte
static member Max : System.Byte * System.Byte -> System.Byte
static member Max : int16 * int16 -> int16
static member Max : uint16 * uint16 -> uint16
static member Max : int * int -> int
static member Max : uint32 * uint32 -> uint32
static member Max : int64 * int64 -> int64
static member Max : uint64 * uint64 -> uint64
static member Max : float32 * float32 -> float32
static member Max : float * float -> float
static member Max : decimal * decimal -> decimal
static member Min : System.SByte * System.SByte -> System.SByte
static member Min : System.Byte * System.Byte -> System.Byte
static member Min : int16 * int16 -> int16
static member Min : uint16 * uint16 -> uint16
static member Min : int * int -> int
static member Min : uint32 * uint32 -> uint32
static member Min : int64 * int64 -> int64
static member Min : uint64 * uint64 -> uint64
static member Min : float32 * float32 -> float32
static member Min : float * float -> float
static member Min : decimal * decimal -> decimal
static member Pow : float * float -> float
static member Round : float -> float
static member Round : decimal -> decimal
static member Round : float * int -> float
static member Round : float * System.MidpointRounding -> float
static member Round : decimal * int -> decimal
static member Round : decimal * System.MidpointRounding -> decimal
static member Round : float * int * System.MidpointRounding -> float
static member Round : decimal * int * System.MidpointRounding -> decimal
static member Sign : System.SByte -> int
static member Sign : int16 -> int
static member Sign : int -> int
static member Sign : int64 -> int
static member Sign : float32 -> int
static member Sign : float -> int
static member Sign : decimal -> int
static member Sin : float -> float
static member Sinh : float -> float
static member Sqrt : float -> float
static member Tan : float -> float
static member Tanh : float -> float
static member Truncate : decimal -> decimal
static member Truncate : float -> float
end

Full name: System.Math
Math.Sign(value: decimal) : int
Math.Sign(value: float) : int
Math.Sign(value: float32) : int
Math.Sign(value: int64) : int
Math.Sign(value: int) : int
Math.Sign(value: int16) : int
Math.Sign(value: sbyte) : int
Object.ToString() : string
abstract member INumeric.ToString : 'T * string * IFormatProvider -> string
val n : int

type: int
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType
val fmt : string

type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
val fmtprovider : IFormatProvider
abstract member INumeric.Parse : string * Globalization.NumberStyles * IFormatProvider -> 'T
val s : string

type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
val numstyle : Globalization.NumberStyles

type: Globalization.NumberStyles
inherits: Enum
inherits: ValueType
namespace System
Multiple items
type Int32 =
struct
member CompareTo : obj -> int
member CompareTo : int -> int
member Equals : obj -> bool
member Equals : int -> bool
member GetHashCode : unit -> int
member GetTypeCode : unit -> System.TypeCode
member ToString : unit -> string
member ToString : string -> string
member ToString : System.IFormatProvider -> string
member ToString : string * System.IFormatProvider -> string
static val MaxValue : int
static val MinValue : int
static member Parse : string -> int
static member Parse : string * System.Globalization.NumberStyles -> int
static member Parse : string * System.IFormatProvider -> int
static member Parse : string * System.Globalization.NumberStyles * System.IFormatProvider -> int
static member TryParse : string * int -> bool
static member TryParse : string * System.Globalization.NumberStyles * System.IFormatProvider * int -> bool
end

Full name: System.Int32

type: Int32
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType

--------------------

Int32()
Int32.Parse(s: string) : int
Int32.Parse(s: string, provider: IFormatProvider) : int
Int32.Parse(s: string, style: Globalization.NumberStyles) : int
Int32.Parse(s: string, style: Globalization.NumberStyles, provider: IFormatProvider) : int
val Init : unit -> unit

Full name: Script.IntegerZ5Associations.Init
module GlobalAssociations

from Microsoft.FSharp.Math
val RegisterNumericAssociation : INumeric<'a> -> unit

Full name: Microsoft.FSharp.Math.GlobalAssociations.RegisterNumericAssociation
Multiple items
module Matrix

from Microsoft.FSharp.Math

--------------------

type Matrix<'T>
with
interface Collections.Generic.IEnumerable<'T>
interface Collections.IStructuralEquatable
interface Collections.IStructuralComparable
interface IComparable
member Column : index:int -> Vector<'T>
member Columns : start:int * length:int -> Matrix<'T>
member Copy : unit -> Matrix<'T>
override Equals : obj -> bool
member GetDiagonal : int -> Vector<'T>
override GetHashCode : unit -> int
member GetSlice : start1:int option * finish1:int option * start2:int option * finish2:int option -> Matrix<'T>
member PermuteColumns : permutation:(int -> int) -> Matrix<'T>
member PermuteRows : permutation:(int -> int) -> Matrix<'T>
member Region : starti:int * startj:int * lengthi:int * lengthj:int -> Matrix<'T>
member Row : index:int -> RowVector<'T>
member Rows : start:int * length:int -> Matrix<'T>
member SetSlice : start1:int option * finish1:int option * start2:int option * finish2:int option * source:Matrix<'T> -> unit
member ToArray2 : unit -> 'T [,]
member ToArray2D : unit -> 'T [,]
member ToRowVector : unit -> RowVector<'T>
member ToVector : unit -> Vector<'T>
member Diagonal : Vector<'T>
member Dimensions : int * int
member ElementOps : INumeric<'T>
member InternalDenseValues : 'T [,]
member InternalSparseColumnValues : int []
member InternalSparseRowOffsets : int []
member InternalSparseValues : 'T []
member IsDense : bool
member IsSparse : bool
member Item : int * int -> 'T with get
member NonZeroEntries : seq<int * int * 'T>
member Norm : float
member NumCols : int
member NumRows : int
member StructuredDisplayAsArray : obj
member Transpose : Matrix<'T>
member Item : int * int -> 'T with set
static member ( + ) : Matrix<'T> * Matrix<'T> -> Matrix<'T>
static member ( .* ) : Matrix<'T> * Matrix<'T> -> Matrix<'T>
static member ( * ) : 'T * Matrix<'T> -> Matrix<'T>
static member ( * ) : Matrix<'T> * 'T -> Matrix<'T>
static member ( * ) : Matrix<'T> * Vector<'T> -> Vector<'T>
static member ( * ) : Matrix<'T> * Matrix<'T> -> Matrix<'T>
static member ( - ) : Matrix<'T> * Matrix<'T> -> Matrix<'T>
static member ( ~- ) : Matrix<'T> -> Matrix<'T>
static member ( ~+ ) : Matrix<'T> -> Matrix<'T>
end

Full name: Microsoft.FSharp.Math.Matrix<_>

type: Matrix<'T>
implements: Collections.Generic.IEnumerable<'T>
implements: Collections.IEnumerable
implements: Collections.IStructuralEquatable
implements: Collections.IStructuralComparable
implements: IComparable
module Generic

from Microsoft.FSharp.Math.MatrixModule
module IntegerZ5Associations

from Script
val ys : Matrix<IntegerZ5>

Full name: Script.Script2.ys

type: Matrix<IntegerZ5>
implements: Collections.Generic.IEnumerable<IntegerZ5>
implements: Collections.IEnumerable
implements: Collections.IStructuralEquatable
implements: Collections.IStructuralComparable
implements: IComparable
val init : int -> int -> (int -> int -> 'T) -> Matrix<'T>

Full name: Microsoft.FSharp.Math.MatrixModule.Generic.init
val xs : Matrix<IntegerZ5>

Full name: Script.Script2.xs

type: Matrix<IntegerZ5>
implements: Collections.Generic.IEnumerable<IntegerZ5>
implements: Collections.IEnumerable
implements: Collections.IStructuralEquatable
implements: Collections.IStructuralComparable
implements: IComparable
namespace FSharp
namespace FSharp.Numerics
Multiple items
type SealedAttribute =
class
inherit System.Attribute
new : unit -> SealedAttribute
new : value:bool -> SealedAttribute
member Value : bool
end

Full name: Microsoft.FSharp.Core.SealedAttribute

type: SealedAttribute
implements: System.Runtime.InteropServices._Attribute
inherits: System.Attribute

--------------------

new : unit -> SealedAttribute

--------------------

new : value:bool -> SealedAttribute
type IntegerZ5
with
member ToInt32 : unit -> int
override ToString : unit -> string
static member Create : int -> IntegerZ5
static member One : IntegerZ5
static member Zero : IntegerZ5
static member ( + ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
static member ( * ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
static member ( - ) : IntegerZ5 * IntegerZ5 -> IntegerZ5
end

Full name: FSharp.Numerics.IntegerZ5
member IntegerZ5.ToInt32 : unit -> int

Full name: FSharp.Numerics.IntegerZ5.ToInt32
type unit = Unit

Full name: Microsoft.FSharp.Core.unit

type: unit
implements: System.IComparable
Multiple items
val int : 'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------

type int = int32

Full name: Microsoft.FSharp.Core.int

type: int
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int>
implements: System.IEquatable<int>
inherits: System.ValueType

--------------------

type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>

type: int<'Measure>
implements: System.IComparable
implements: System.IConvertible
implements: System.IFormattable
implements: System.IComparable<int<'Measure>>
implements: System.IEquatable<int<'Measure>>
inherits: System.ValueType
override IntegerZ5.ToString : unit -> string

Full name: FSharp.Numerics.IntegerZ5.ToString
Multiple items
val string : 'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------

type string = System.String

Full name: Microsoft.FSharp.Core.string

type: string
implements: System.IComparable
implements: System.ICloneable
implements: System.IConvertible
implements: System.IComparable<string>
implements: seq<char>
implements: System.Collections.IEnumerable
implements: System.IEquatable<string>
static member IntegerZ5.Create : int -> IntegerZ5

Full name: FSharp.Numerics.IntegerZ5.Create
static member IntegerZ5.One : IntegerZ5

Full name: FSharp.Numerics.IntegerZ5.One
static member IntegerZ5.Zero : IntegerZ5

Full name: FSharp.Numerics.IntegerZ5.Zero
module NumericLiteralZ

from FSharp.Numerics
val FromZero : unit -> IntegerZ5

Full name: FSharp.Numerics.NumericLiteralZ.FromZero
val FromOne : unit -> IntegerZ5

Full name: FSharp.Numerics.NumericLiteralZ.FromOne
val FromInt32 : int -> IntegerZ5

Full name: FSharp.Numerics.NumericLiteralZ.FromInt32
val FromInt64 : int64 -> IntegerZ5

Full name: FSharp.Numerics.NumericLiteralZ.FromInt64
Multiple items
val int64 : 'T -> int64 (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int64

--------------------

type int64 = System.Int64

Full name: Microsoft.FSharp.Core.int64

type: int64
implements: System.IComparable
implements: System.IFormattable
implements: System.IConvertible
implements: System.IComparable<int64>
implements: System.IEquatable<int64>
inherits: System.ValueType

--------------------

type int64<'Measure> = int64

Full name: Microsoft.FSharp.Core.int64<_>

type: int64<'Measure>
implements: System.IComparable
implements: System.IConvertible
implements: System.IFormattable
implements: System.IComparable<int64<'Measure>>
implements: System.IEquatable<int64<'Measure>>
inherits: System.ValueType
module IntegerZ5TopLevelOperations

from FSharp.Numerics
val z5 : 'a -> IntegerZ5 (requires member op_Explicit)

Full name: FSharp.Numerics.IntegerZ5TopLevelOperations.z5
module IntegerZ5Associations

from FSharp.Numerics
val IntegerZ5Numerics : INumeric<IntegerZ5>

Full name: FSharp.Numerics.IntegerZ5Associations.IntegerZ5Numerics
Multiple items
type INumeric<'T> =
interface
abstract member Abs : 'T -> 'T
abstract member Add : 'T * 'T -> 'T
abstract member Compare : 'T * 'T -> int
abstract member Equals : 'T * 'T -> bool
abstract member Multiply : 'T * 'T -> 'T
abstract member Negate : 'T -> 'T
abstract member Parse : string * System.Globalization.NumberStyles * System.IFormatProvider -> 'T
abstract member Sign : 'T -> int
abstract member Subtract : 'T * 'T -> 'T
abstract member ToString : 'T * string * System.IFormatProvider -> string
abstract member One : 'T
abstract member Zero : 'T
end

Full name: Microsoft.FSharp.Math.INumeric<_>

--------------------

INumeric
val Init : unit -> unit

Full name: FSharp.Numerics.IntegerZ5Associations.Init

Published: November 24, 2011 19:21
Tags: Functional Programming in .NET | F# language | Math and Numerics

Share:

• Two errors by Petr “svick” Onderka (11/24/2011 9:26:20 PM)

There are two errors in the code, I think.

First, the way Create() tries to deal with negative numbers is wrong. The way it's written (with min) IntegerZ5.Create(-1) returns -1 (mod 5). And replacing min with max (which is mentioned in the text) doesn't work either. I think the correct code is:

Z5(max ((z5 + 5) % 5) z5)

And similarly with NumericLiteralZ.FromInt32 (and 64): -1Z returns -1 (mod 5).

Other than that, interesting article.

• RE: F# Math (III.) - Defining custom numeric types by Tomas (11/24/2011 11:43:14 PM)

@Petr - Thanks for your comments! You're right, using 'min' as it is currently in the code is definitely wrong.

Maybe it would be clearer to just use 'if' for handling of negative numbers, but your version looks correct as well (I'll modify the code to use that). The functions in 'NumericLiteralZ' should probably call IntegerZ5.Create instead of duplicating the complex behaviour.

(Arguably, it would be better if the type had a constructor that would do all the normalization, but that wouldn't be as nice for demonstrating how to hide implementation details using F# Interafe file.)