GithubHelp home page GithubHelp logo

daninet / gmp-wasm Goto Github PK

View Code? Open in Web Editor NEW
19.0 2.0 3.0 1.22 MB

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

License: GNU Lesser General Public License v3.0

Shell 0.24% Dockerfile 0.57% C 43.03% JavaScript 4.09% TypeScript 52.06%
arbitrary-precision integer rational floating-point gmp mpfr webassembly bindings javascript fast

gmp-wasm's Introduction

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.

gmp-wasm's People

Contributors

daninet avatar yuristuken avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

gmp-wasm's Issues

TypeScript "declaration file"?

For what it's worth, I want to call this library from Kotlin, which apparently would be easier if there was a declaration file (*.d.ts). Not that I have any idea what that is, but I'm curious if it's on your agenda to make such a thing.

Memory management

I'm looking at the way you're managing memory, by keeping mpz_t_arr for all the temporary values and then blowing all of them away when the surrounding context finishes. I'm curious if you've looked at the possibility of leveraging finalizers, to allow the JavaScript garbage collector call back to you when it's time to ultimately call mpz_t_free.

This would allow higher-level logic to write arithmetic expressions as normal, without having to worry about object lifetimes.

crashy behavior with Kotlin/JS

I've been trying on and off to get gmp-wasm to work for me, and I'm getting a variety of weird behaviors. I suspect one of my bugs can be blamed on Kotlin/JS and another might be related to gmp-wasm. I've created a minimal repository that allows the bugs to be reproduced:

https://github.com/danwallach/gmp-wasm-kotlin

The README.md documents how to run the code and has the outputs. In short, I built a Kotlin BigInteger class that speaks directly to the mpz functions, and tries to manage gmp-wasm memory using FinalizationRegistry (so when the JavaScript wrapper object is garbage collected, it will tell gmp-wasm to free the corresponding mpz object).

I'd appreciate if you could take a look at this.

Feature request: xmpz's bit range feature, byte arrays

First, thank you for this library. It's exactly what I need.

GnuMP has a very useful feature for selecting bit ranges from an xmpz. For my particular application, this allows for an optimization worth about 9x (precompute to optimize modular exponentiation for known bases).

Python binding: https://gmpy2.readthedocs.io/en/latest/advmpz.html

I'd love something analogous to the "bit slice" read feature. I don't actually need anything else, but I imagine others might want the full suite of xmpz mutability features.

Also, in terms of general purpose features, it would be handy to have input and output as byte arrays. Java BigInteger defines this to be big endian, twos complement, which seems as good as anything else you might do.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.