The library *System.Numerics.dll*, who was introduced in .Net 4.0, contains a `System.Numerics.BigInteger`

structure. `BigInteger`

represents a whole number of arbitrary size (or precision). Before .Net 4.0, the largest number that could be represented out of the box was `System.Double.MaxValue`

. Written in decimal notation, this would be a whole number with 309 digits (more than 179 thousand centillion). However, using `Double`

for whole number calculations is error-prone. Double is internally stored with floating point logic; the larger the number, the less exact it becomes. For instance, you cannot exactly represent the number ten quadrillion (i.e., ten million billion):

```
let x = 1.0e+16
let y = x - 1.0
printf "%b" (x = y)
// Prints true!
```

Thanks to the introduction of `System.Numerics.BigInteger`

, it is now possible for any .Net Framework language to exactly represent any whole number regardless of its size. In the following example, we will use this type to implement a function that calculates a range of arbitrarily large prime numbers. We will design it as a static library method which can be called from any .Net 4.0 client application. The signature, seen from C#, will be as follows:

```
using System.Collections.Generic;
using System.Numerics;
namespace MyCompany.MyProduct{
public static class BigMath{
public static IEnumerable<BigInteger>
PrimesBetween(BigInteger i, BigInteger j){
// etc.
```

To implement the library method, proceed as follows:

- (If not done yet: Install the F# Power Pack)
- Create a new
*F# class library*project - Set
*.Net Framework 4 client profile*as the compilation target - Add references to
*System.Numerics.dll*and*FSharp.PowerPack.dll* - Add a new source file
*BigMath.fs*with the following code:

```
/// Provides static methods for mathematical functions involving
/// arbitrarily large numbers.
module MyCompany.MyProduct.BigMath
/// <summary>Gets a lazily evaluated sequence of prime numbers.</summary>
/// <param name="i">The beginning of the interval for the prime numbers.</param>
/// <param name="j">The end of the interval for the prime numbers.</param>
/// <returns>A sequence with the prime numbers between <paramref name="i"/> and <paramref name="j"/>.</returns>
let PrimesBetween(i, j) =
/// Approximates the square root of a BigInteger with a
/// BigRational, according to the "babylonian method".
let sqrtI i =
let iN = BigRational.FromBigInt i
let half n = n / 2N
let rec approachWith n =
let diff = BigRational.PowN(n, 2) - iN
if diff >= 0N && diff < 1N then n
else approachWith(half(n + iN / n))
approachWith(half iN)
/// Gets true if i can be divided by a number >= 2,
/// where i is a non-negative BigInteger.
let hasDivisor i =
let maxDiv =
let sr = sqrtI i
min (sr.Numerator / sr.Denominator + 1I) (i - 1I)
let rec hasDiv div =
if div > maxDiv then false
elif i % div = 0I then true
else hasDiv(div + 1I)
hasDiv 2I
/// Gets true if i is a prime number.
let isPrime i =
match i with
| _ when i < 2I -> false
| _ when i = 2I -> true
| _ -> not (hasDivisor i)
/// The sequence of numbers between i and j.
let range =
let step = i |> compare j
match step with
| 0 -> Seq.singleton i
| _ -> seq {i .. bigint step .. j}
/// The resulting sequence of prime numbers between i and j.
range |> Seq.filter isPrime
```

The compiled F# library can be used in C# as follows:

```
// using System;
// using MyCompany.MyProduct;
var primes = BigMath.PrimesBetween(10000200, 10000150);
foreach (var p in primes)
Console.WriteLine(p.ToString("#,#"));
// Produces:
// 10,000,189
// 10,000,169
```

The parameters of the `PrimesBetween`

function are declared in tupled (not curried) form, which conforms to the *F# Component Design Guidelines*' subsection *Avoid the use of currying of parameters*.

All recursive functions are implemented with *tail recursion*, using accumulator arguments. As a consequence, the compiled IL code can never produce a stack overflow.

The result is a strongly typed generic enumerable of `BigInteger`

. At any given time, no more than one prime number is held in memory, regardless of the size of the interval between `i`

and `j`

.

The square root calculation in the `sqrtI`

function is an optimization. To know whether a number has a divisor, it is sufficient to try division by all numbers between 2 and the square root. For the calculation of the square root, we use the type `Microsoft.FSharp.Math.BigRational`

from *FSharp.PowerPack.dll*.

To calculate extremely large prime numbers in a productive situation, we would have to further speed up the function using a cache.