Mpir.NET – NuGet package for high performance large integers

mpirnetlogoMpir.NET is a .NET wrapper for the high performance multi precision integer library MPIR, which in turn is a Windows friendly fork of GMP.

As noted in my previous post, High performance large integers in .NET, it would be highly desirable to have a large integer library that combines the speed of MPIR with the usability of Emil Stefanov’s “GMP for .NET”. The obvious way of doing this, would be to make a fusion of Sergey Bochkanov’s MPIR wrapper for .NET, X-MPIR, and GMP for .NET. Since both Stefanov and Bochkanov have released their code under the LGPL license, this turned out to be possible with a couple of modifications to both code bases.

Installation

To use Mpir.NET, install it with NuGet:

Install-Package Mpir.NET

Basic usage

C# example:

using Mpir.NET;
// ...
mpz_t a = new mpz_t(12345678901234567890);
mpz_t b = new mpz_t(9876543210987654321);
mpz_t c = a * b;
System.Console.WriteLine("{0}", c);

mpz_t implements IDisposable, so you have the option of immediately releasing the unmanaged memory that the underlying MPIR library uses, by wrapping your mpz_t variables in a “using” block. In F#, of course, you don’t have to bother with the ceremony of “using” blocks. Another F# perk is that you can create mpz_t objects from numeric literals suffixed by Z, further reducing ceremony (thanks for the suggestion, Lincoln Atkinson!).

F# example:

open Mpir.NET

use a = 756749075976907490175905790287846502134Z
use b = 529134916478965674697197076070175107505Z
use c = a*b
printfn "%O" c

The type representing big integers, mpz_t, can be initialized from a number of different types, using the following constructors:

mpz_t() 
mpz_t(mpz_t op)
mpz_t(uint op) 
mpz_t(int op)  
mpz_t(double op)
mpz_t(string s, uint _base)
mpz_t(string s)
mpz_t(BigInteger op)
mpz_t(long op)
mpz_t(ulong op)
mpz_t(byte[] bytes, int order) // order = -1 for little endian
                               // order = 1 for big endian

Thanks

A big thanks to Sergey Bochkanov (ALGLIB) and Emil Stefanov (Emil, I’m very sorry to say, has tragically departed)! Their efforts with X-MPIR and GMP for .NET have made Mpir.NET possible. The vast majority of the work lies in the creation of X-MPIR. My own effort with the modifications needed to make it work together with GMP for .NET is the smallest ingredient is this mix.

Download

Mpit.NET is available on NuGet.

The source code is available on GitHub.

  1. Cool stuff, I will need to play with this!

    You can make it even easier in F# by taking advantage of compiler support for custom numeric literals. e.g. you can make a ‘Z’ suffix map to mpz_t by defining a special module NumericLiteralZ:

    open Mpir.NET

    module NumericLiteralZ =
    let FromZero () = mpz_t.Zero
    let FromOne ()= mpz_t.One
    let FromInt32 (i:int32) = new mpz_t(i)
    let FromInt64 (i:int64) = new mpz_t(i)
    let FromString (s:string) = new mpz_t(s)

    use a = 0Z
    use b = 1Z
    use c = 32Z
    use d = -5291349164789656746971Z
    use e = 529134916478965674697197076070175107505Z

    a + (b*c*d*e) |> printfn “%O”

      • John
      • June 13th, 2014 3:36pm

      Thanks for the tip, Lincoln! I’m quite fond of those things that demonstrate F#’s higher signal to noise ratio. I’ve incorporated NumericLiteralZ in Mpir.NET and updated the NuGet package.

  2. “Unfortunately, you can’t instruct NuGet to place unreferenced DLLs in your project’s output directory, which is why you have to do this manually after adding the Mpir.NET NuGet package.”

    Actually, there is a way. You just have to place a small install.ps1 script in the “tools” folder of your NuGet package; see for example this script I use for my LM.NET library:

    https://bitbucket.org/frank_niemeyer/lmdotnet/src/c58a15a4571b86844c1b055b04025301b4f8bc76/nuget/install.ps1?at=default

      • John
      • June 16th, 2014 2:56pm

      Thanks, Frank! I’ve added an install script and updated the NuGet package.

    • Micheal
    • June 15th, 2015 7:47am

    Hi,

    I try to benchmark between IntX (https://github.com/devoyster/IntXLib), BigInteger (Microsoft) and mpz_t from (http://wezeku.github.io/Mpir.NET/) with this testing

    string d1 = "123456789012345678901234";
    string d2 = "454646487978798798798798";

    string n1 = "456464679879879879879848";
    string n2 = "879878465456112131464978";

    TimeGetTime.GetBeginTime();
    IntX aX = new IntX(d1);
    IntX bX = new IntX(d2);

    IntX aX1 = new IntX(n1);
    IntX bX1 = new IntX(n2);

    int num = 1000000;

    for (int i = 0; i < num; i++)
    {
    IntX c1 = aX * bX;// + aX1 *bX1;
    }
    TimeGetTime.GetEndTime();
    TimeGetTime.DisplayTime("IntX");

    TimeGetTime.GetBeginTime();
    BigInteger aI = BigInteger.Parse(d1);
    BigInteger bI = BigInteger.Parse(d2);

    BigInteger aI1 = BigInteger.Parse(n1);
    BigInteger bI1 = BigInteger.Parse(n2);

    for (int i = 0; i < num; i++)
    {
    BigInteger c1 = aI * bI;// + aI1 * bI1;
    }
    TimeGetTime.GetEndTime();
    TimeGetTime.DisplayTime("BigInteger");

    TimeGetTime.GetBeginTime();
    mpz_t a1 = new mpz_t(System.Numerics.BigInteger.Parse(d1));
    mpz_t b1 = new mpz_t(System.Numerics.BigInteger.Parse(d2));
    mpz_t a11 = new mpz_t(System.Numerics.BigInteger.Parse(n1));
    mpz_t b11 = new mpz_t(System.Numerics.BigInteger.Parse(n2));

    for (int i = 0; i < num; i++)
    {
    mpz_t c1 = a1 * b1;// +a11 * b11;
    //c1.Dispose();
    }

    TimeGetTime.GetEndTime();
    TimeGetTime.DisplayTime("gmp");

    and the output time is as follow:
    IntX 0.3390194
    BigInteger 0.1430082
    gmp 0.5780331

    Do you know why gmp is the slowest one? Isn't it is suppose to be the fastest one?

      • John
      • June 17th, 2015 8:21pm

      It seems that the creation and garbage collection of mpz_t values is quite slow. So, for smaller numbers, MPIR can’t keep up with IntX and BigInteger. If you change d1 and d1 to be 200 digit numbers, then MPIR will be the fastest.

      I tried this code which reuses the c1 result variable, just to see how much faster it would run:

      var c1 = new mpz_t();
      for (int i = 0; i < num; i++) { mpir.mpz_mul(c1, a1, b1); } This runs about twice as fast for 200 digit a1 and b1, which means that the creation and disposal of mpz_t takes as much time as the multiplication itself. I also tried wrapping c1 in a "using" statement, for (int i = 0; i < num; i++) { using (mpz_t c1 = a1 * b1) { }; } which was faster than without "using", so garbage collection definitely affects performance here. I don't know why creation/disposal takes so much time. It shouldn't really take as much time as multiplying two 200-digit numbers, but it does. Maybe it has something to do with crossing the boundaries between managed and unmanaged code.

    • John
    • June 30th, 2015 8:03pm

    New version released, updated to use MPIR 2.7.0 which was released a few days ago.

    • Roger C.
    • October 29th, 2015 5:47pm

    I had to demonstrate some liabilities of 64-bit floats the other day, and Mpir.NET came through for me.

    The only problems I ran into is that there’s a pretty steep learning curve because the syntax of the functions is fairly different from typical C#, and the examples could use some expansion. In the end, though, it works great and you can specify exactly what you need to do.

    Thanks for making this available!

    • John
    • November 20th, 2015 1:26pm

    Mpir.NET is updated with MPIR v 2.7.1.

  1. No trackbacks yet.