Nautical Pirate Moustache

    gmp-wasm
    TypeScript icon, indicating that this package has built-in type declarations

    1.1.0 • Public • Published

    GMP-WASM

    npm package codecov Build status JSDelivr downloads

    Arbitrary-precision Integer, Rational and Float types based on the GMP and MPFR libraries.

    Features

    • Supports all modern browsers, web workers, Node.js and Deno
    • Includes an easy-to-use, high-level wrapper, but low-level functions are also exposed
    • Has a lot more features, and in some cases, it's faster than the built-in BigInt type
    • The WASM binary is bundled as a compressed base64 string (no problems with linking)
    • Works even without Webpack or other bundlers
    • Includes TypeScript type definitions, check API here.
    • Zero dependencies
    • Full minified and gzipped bundle has a size of Bundle size
    • It also packages a mini bundle without Float/MPFR operations Bundle size
    • 100% open source & transparent build process

    Installation

    npm i gmp-wasm
    

    It can also be used directly from HTML (via jsDelivr):

    <!-- loads the full, minified library into the global `gmp` variable -->
    <script src="https://cdn.jsdelivr.net/npm/gmp-wasm"></script>
    
    <!-- or loads the non-minified library -->
    <script src="https://cdn.jsdelivr.net/npm/gmp-wasm/dist/index.umd.js"></script>
    
    <!-- or loads the minified library without Float/MPFR functions -->
    <script src="https://cdn.jsdelivr.net/npm/gmp-wasm/dist/mini.umd.min.js"></script>

    Usage

    gmp-wasm also provides a high-level wrapper over the GMP functions. There are three major components:

    • g.Integer() - Wraps integers (MPZ)
    • g.Rational() - Wraps rational numbers (MPQ)
    • g.Float() - Wraps floating-point numbers (MPFR)
    const gmp = require('gmp-wasm');
    
    gmp.init().then(({ calculate }) => {
      // calculate() automatically deallocates all objects created within the callback function
      const result = calculate((g) => {
        const six = g.Float(1).add(5);
        return g.Pi().div(six).sin(); // sin(Pi/6) = 0.5
      });
      console.log(result);
    });

    It is also possible to delay deallocation through the getContext() API:

    const gmp = require('gmp-wasm');
    
    gmp.init().then(({ getContext }) => {
      const ctx = getContext();
      let x = ctx.Integer(1);
      for (let i = 2; i < 16; i++) {
        x = x.add(i);
      }
      console.log(x.toString());
      setTimeout(() => ctx.destroy(), 50);
    });

    The precision and the rounding modes can be set by passing a parameter to the context or to the Float constructor.

    const roundingMode = gmp.FloatRoundingMode.ROUND_DOWN;
    const options = { precisionBits: 10, roundingMode };
    
    const result = calculate(g => g.Float(1).div(3), options);
    // or
    const result2 = calculate(g => g.Float(1, options).div(3));
    // or
    const ctx = getContext(options);
    const result3 = ctx.Float(1).div(3).toString();

    Predefined constants

    • Pi
    • EulerConstant
    • EulerNumber
    • Log2
    • Catalan

    Advanced usage

    High-level wrapper can be combined with low-level functions:

    const sum = calculate((g) => {
      const a = g.Float(1);
      const b = g.Float(2);
      const c = g.Float(0);
      // c = a + b
      binding.mpfr_add(c.mpfr_t, a.mpfr_t, b.mpfr_t, 0);
      return c;
    });

    If you want more control and performance you can use the original GMP / MPFR functions even without high-level wrappers.

    const gmp = require('gmp-wasm');
    
    gmp.init().then(({ binding }) => {
      // Create first number and initialize it to 30
      const num1Ptr = binding.mpz_t();
      binding.mpz_init_set_si(num1Ptr, 30);
      // Create second number from string. The string needs to be copied into WASM memory
      const num2Ptr = binding.mpz_t();
      const strPtr = binding.malloc_cstr('40');
      binding.mpz_init_set_str(num2Ptr, strPtr, 10);
      // Calculate num1Ptr + num2Ptr, store the result in num1Ptr
      binding.mpz_add(num1Ptr, num1Ptr, num2Ptr);
      // Get result as integer
      console.log(binding.mpz_get_si(num1Ptr));
      // Deallocate memory
      binding.free(strPtr);
      binding.mpz_clears(num1Ptr, num2Ptr);
      binding.mpz_t_frees(num1Ptr, num2Ptr);
    });

    Sometimes, it's easier and faster to deallocate everything by reinitializing the WASM bindings:

    // Deallocate all memory objects created by gmp-wasm
    await binding.reset();

    Performance

    In some cases, this library can provide better performance than the built-in BigInt type.

    For example, calculating 8000 digits of Pi using the following formula provides better results:

    PI = 3
      + 3 * (1/2) * (1/3) * (1/4)
      + 3 * ((1 * 3)/(2 * 4)) * (1/5) * (1 / (4^2))
      + 3 * ((1 * 3 * 5) / (2 * 4 * 6)) * (1/7) * (1 / (4^3))
      + ...
    
    Test Avg. time Speedup
    With JS built-in BigInt type 129 ms 1x
    gmp-wasm Integer() high-level wrapper 88 ms 1.47x
    Same as previous with delayed memory deallocation 78 ms 1.65x
    gmp-wasm MPZ low-level functions 53 ms 2.43x
    decimal.js 10.3.1 with integer division 443 ms 0.29x
    big-integer 1.6.51 129 ms 1x
    ---------------------------- -------- --------
    gmp-wasm Float() high-level wrapper 175 ms 0.74x
    Same as previous with delayed memory deallocation 169 ms 0.76x
    gmp-wasm MPFR low-level functions 118 ms 1.09x
    decimal.js 10.3.1 with float division 785 ms 0.16x
    ---------------------------- -------- --------
    gmp-wasm Float(1).atan().mul(4) 0.6 ms 215x
    gmp-wasm Float('0.5').asin().mul(6) 17 ms 7.59x

    * These measurements were made with Node.js v16.14 on an Intel Kaby Lake desktop CPU. Source code is here.

    Install

    npm i gmp-wasm

    DownloadsWeekly Downloads

    1

    Version

    1.1.0

    License

    LGPL-3.0-only

    Unpacked Size

    3.7 MB

    Total Files

    53

    Last publish

    Collaborators

    • daninet