Category talk:Wren-gmp

From Rosetta Code

Arbitrary precision arithmetic

Although this is already supported in an easy to use way by the Wren-big module, there are some RC tasks which that module (written entirely in Wren) struggles to complete in an acceptable time or to a satisfactory standard.

I have therefore written a Wren wrapper for the C library, GMP, to deal with such cases. This supports arithmetic not just on integers and rational numbers (as Wren-big does) but also on floating point numbers of arbitrary size. Most of GMP's functions (around 190) have been wrapped though some - notably those concerned with input/output and random numbers - have been excluded as I didn't think they would be very useful from Wren's perspective. I have also included some additional 'convenience' methods which can be coded in a few lines using the GMP functions as well as some prime factorization routines.

GMP is not an ideal fit for an object oriented language such as Wren because arithmetic operations mutate the object itself rather than producing a new object each time as 'normal' arithmetic (and Wren-big) do. However, this constant recycling of memory means that fewer variables and less garbage collection is required for a typical application and so there's an inevitable trade-off here between maximizing efficiency/performance and easy of use. In an attempt to retain as much of the latter as possible:

1. I have included versions of the main functions which operate on and always return a reference to the current object which reduces verbosity and enables method chaining. These methods also sense the type of the argument eliminating the need to have separate versions for GMP types and unsigned integers.

2. The three classes: Mpz, Mpq and Mpf all inherit from the Comparable trait which mean that the normal ordering operators (<, <=, ==, !=, >=, >) can be used for comparisons.

3. I have also included operator overloading for the basic arithmetic operations with the difference that these always produce a new object rather than mutating the current one. Consequently, these should be used sparingly in scripts which need to maximize performance but can be used more freely in other scripts to provide a more 'natural' programming experience.

4. Memory is managed automatically by Wren which calls an object's finalizer just before it is about to be garbage collected. The finalizer in turn calls the appropriate GMP 'clear' function to ensure that memory allocated to the object is fully released. So there is no need for the user to call this function directly and, to avoid 'double clear' scenarios, it is not exposed by Wren at all. If you want to reduce memory usage prior to garbage collection, then the object's 'reset' method can be called instead.

5. The wrapper also ensures that Mpf objects are always canonicalized so there is no need for the user to worry about this particular aspect.

MPF or MPFR?

I did wonder about using MPFR rather than MPF for floating-point support. MPFR is undoubtedly better not just from a technical perspective but also because it supports many more functions (notably the transcendental functions) than MPF does and even the GMP folks themselves recommend that it should be considered for new projects.

However, it's also far more complicated to wrap and doesn't integrate quite so well as MPF with the rest of GMP - as well as an additional rounding mode it uses a default value of NaN rather than zero for new objects which is not ideal from Wren's perspective.

I have therefore decided to stick with MPF for basic arithmetic, for which it is perfectly adequate, but use MPFR for the transcendental functions. There are 25 such functions which I thought it would be worthwhile supporting and, as the wrapper converts automatically between the MPF and MPFR types, this is transparent to the user.

How fast?

Early results suggest Wren-gmp will be significantly faster than Wren-big for scripts requiring a lot of 'big number' arithmetic. However, writers of Wren-cli scripts which require file handling or other stuff which embedded Wren doesn't support 'out of the box' may prefer to stick to the former rather than make one off changes to the C executable.

Of course, the speed improvement doesn't mean that Wren will be able to compete with the statically typed compiled languages which use GMP or a similar package - it won't. Even though Wren's embedding API is quite fast and GMP/MPFR are very fast, this won't compensate for the fact that Wren is interpreted and uses double precision floating point for all numeric work even when only integers are required.

Source code (Wren)

/* Module "gmp.wren" */

import "./trait" for Comparable

/*
    Mpz represents an arbitrary length integer allowing arithmetic operations on signed integers of
    unlimited size. An Mpz object is a pointer to the GMP type mpz_t and is mutable. Many methods
    map directly to the corresponding GMP functions though others are provided for convenience.
    If Num objects are passed which are not integers, they will be truncated towards zero by GMP.
*/
foreign class Mpz is Comparable {

    // Creates small commonly used Mpz objects.
    static minusOne { from(-1) }
    static zero     { new()    }
    static one      { from(1)  }
    static two      { from(2)  } 
    static three    { from(3)  }
    static four     { from(4)  }
    static five     { from(5)  }
    static six      { from(6)  }
    static seven    { from(7)  }
    static eight    { from(8)  }
    static nine     { from(9)  }
    static ten      { from(10) }

    // Swaps the values of two Mpz objects.
    foreign static swap(op1, op2)

    // Splits an Mpz into a double plus an exponent (returned as a list of two Nums).
    foreign static frexp(op)

    // Returns a list containing the quotient and remainder of (c)dividing two Mpz objects.
    foreign static cdivRem(n, d)

    // Returns a list containing the quotient and remainder of (c)dividing an Mpz object by a uint.
    foreign static cdivRemUi(n, d)

    // Returns a list containing the quotient and remainder of (f)dividing two Mpz objects.
    foreign static fdivRem(n, d)

    // Returns a list containing the quotient and remainder of (f)dividing an Mpz object by a uint.
    foreign static fdivRemUi(n, d)

    // Returns a list containing the quotient and remainder of (t)dividing two Mpz objects.
    foreign static tdivRem(n, d)

    // Returns a list containing the quotient and remainder of (t)dividing an Mpz object by a uint.
    foreign static tdivRemUi(n, d)

    // Aliases for tdivRem and tdivRemUi
    static divRem(n, d)   { tdivRem(n, d) }
    static divRemUi(n, d) { tdivRemUi(n, d) }

    // Returns a list containing the n'th (uint) root of op (Mpz) and the remainder.
    foreign static rootRem(op, n)

    // Returns a list containing the square root of op (Mpz) and the remainder.
    foreign static sqrtRem(op, n)

    // Returns the Jacobi symbol (a/b) of two Mpz objects.
    foreign static jacobi(a, b)

    // Returns the Legendre symbol (a/p) of two Mpz objects.
    foreign static legendre(a, p)

    // Returns the Kronecker symbol (a/b) of two Mpz objects.
    foreign static kronecker(a, b)

    // Returns the Hamming distance between two Mpz objects.
    foreign static hamDist(op1, op2)

    // Returns the smaller of two Mpz objects.
    static min(op1, op2) {
        if (!(op1 is Mpz)) op1 = from(op1)
        if (!(op2 is Mpz)) op2 = from(op2)
        if (op1 < op2) return op1
        return op2
    }

    // Returns the greater of two Mpz objects.
    static max(op1, op2) {
        if (!(op1 is Mpz)) op1 = from(op1)
        if (!(op2 is Mpz)) op2 = from(op2)
        if (op1 > op2) return op1
        return op2
    }

    // Returns the positive difference of two Mpz objects.
    static dim(op1, op2) {
        if (!(op1 is Mpz)) op1 = from(op1)
        if (!(op2 is Mpz)) op2 = from(op2)
        if (op1 >= op2) return op1 - op2
        return Mpz.zero
    }

    // Private method which aborts the script if an argument is of an invalid type or value.
    static abort_() { Fiber.abort("Argument type or value is invalid.") }

    // Creates a new Mpz object with a value of zero.
    construct new() {}

    // Creates a new Mpz object from:
    construct fromMpz(op) {}                // another Mpz object
    construct fromDbl(op) {}                // a double
    construct fromStr(op, b) {}             // a base 'b' string (assigns zero if invalid)
    static fromStr(op) { fromStr(op, 10) }  // a base 10 string

    // Convenience method which creates a new Mpz object from any other numeric type.
    static from(op) {
        if (op is Mpz) return fromMpz(op)
        if (op is Num) {
            if (op.isInteger) return (op >= 0) ? new().setUi(op) : new().setSi(op)
            return fromDbl(op)
        }
        if (op is Mpq) return new().setMpq(op)
        if (op is Mpf) return new().setMpf(op)
        abort_()
    }

    // Assigns a new value to the current instance using :
    foreign setMpz(op)               // another Mpz object
    foreign setMpq(op)               // an Mpq object
    foreign setMpf(op)               // an Mpf object
    foreign setDbl(op)               // a double
    foreign setUi(op)                // an unsigned integer
    foreign setSi(op)                // a signed integer
    foreign setStr(str, b)           // a base 'b' string (unchanged if invalid)
    setStr(str) { setStr(str, 10) }  // a base 10 string

    // Convenience method which assigns a new value to the current instance
    // from any other numeric type.
    set(op) {
        if (op is Mpz) return setMpz(op)
        if (op is Num) {
            if (op.isInteger) return (op >= 0) ? setUi(op) : setSi(op)
            return setDbl(op)
        }
        if (op is Mpq) return setMpq(op)
        if (op is Mpf) return setMpf(op)
        Mpz.abort_()
    }

    // Resets the value of the current instance to zero and the space allocated to 64 bits.
    reset() { setUi(0).setBits(64) }

    // Changes the space allocated for the current instance to n bits.
    // The former is reset to zero if its value no longer fits.
    foreign setBits(n)

    // Converts the current instance to:
    foreign toNum                // a number
    foreign toString(b)          // a base 'b' string
    toString { toString(10) }    // a base 10 string
    toMpq    { Mpq.from(this) }  // an Mpq object
    toMpf    { Mpf.from(this) }  // an Mpf object

    /* Methods which assign their result to the current instance ('this').
       'uint' denotes a Num which is an unsigned integer.
       'sint' denotes a Num which is a signed integer. */

    foreign add(op1, op2)            // adds two Mpz objects
    foreign addUi(op1, op2)          // adds an Mpz object and a uint

    foreign sub(op1, op2)            // subtracts one Mpz object from another
    foreign subUi(op1, op2)          // subtracts a uint from an Mpz object
    foreign uiSub(op1, op2)          // subtracts an Mpz object from a uint

    foreign mul(op1, op2)            // multiplies two Mpz objects
    foreign mulUi(op1, op2)          // multiplies an Mpz object by a uint
    foreign mulSi(op1, op2)          // multiplies an Mpz object by a sint

    foreign addMul(op1, op2)         // multiplies two Mpz objects and adds the result to this
    foreign addMulUi(op1, op2)       // multiplies an Mpz by op2 (uint) and adds the result to this

    foreign subMul(op1, op2)         // multiplies two Mpz objects and subtracts the result from this
    foreign subMulUi(op1, op2)       // multiplies an Mpz by a uint and subtracts the result from this

    foreign cdiv(n, d)               // divides one Mpz object by another (rounds towards +infinity)
    foreign cdivUi(n, d)             // divides an Mpz object by a uint (rounds towards +infinity)

    foreign fdiv(n, d)               // divides one Mpz object by another (rounds towards -infinity)
    foreign fdivUi(n, d)             // divides an Mpz object by a uint (rounds towards -infinity)

    foreign tdiv(n, d)               // divides one Mpz object by another (rounds towards zero)
    foreign tdivUi(n, d)             // divides an Mpz object by a uint (rounds towards zero)

    addSi(op1, op2) { (op2 >= 0) ? addUi(op1, op2) : subUi(op1, -op2) } // op2 is a sint
    subSi(op1, op2) { (op2 >= 0) ? subUi(op1, op2) : addUi(op1, -op2) } // op2 is a sint 

    div(n, d)   { tdiv(n, d) }       // alias for tdiv
    divUi(n, d) { tdivUi(n, d) }     // alias for tdivUi
    divSi(n, d) { (d >= 0) ? tdivUi(n, d) : tdivUi(n, -d).neg } // d is a sint

    foreign crem(n, d)               // sets the remainder after 'cdiv' by another Mpz object
    foreign cremUi(n, d)             // sets the remainder after 'cdiv' by a uint

    foreign frem(n, d)               // sets the remainder after 'fdiv' by another Mpz object
    foreign fremUi(n, d)             // sets the remainder after 'fdiv' by a uint

    foreign trem(n, d)               // sets the remainder after 'tdiv' by another Mpz object
    foreign tremUi(n, d)             // sets the remainder after 'tdiv' by a uint

    rem(n, d)  { trem(n,d) }         // alias for trem
    remUi(n, d) { tremUi(n, d) }     // alias for tremUi
    remSi(n, d) { (d >= 0) ? tremUi(n, d) : tremUi(n, -d) } // d is a sint

    foreign mod(n, d)                // sets to n (Mpz) mod d (Mpz) ignoring the sign of d 
    foreign modUi(n, d)              // sets to n (Mpz) mod d (uint) ignoring the sign of d

    foreign divExact(n, d)           // sets to n/d (both Mpz objects) when this is known to be exact
    foreign divExactUi(n, d)         // as divExact when the second argument is a uint

    foreign neg(op)                  // sets to -op (Mpz)
    foreign abs(op)                  // sets to the absolute value of op (Mpz)
    inc(op) { addUi(op, 1) }         // adds one to op
    dec(op) { subUi(op, 1) }         // subtracts one from op

    foreign pow(base, exp)           // raises base (Mpz) to the power exp (uint)
    foreign uiPow(base, exp)         // raises base (uint) to the power exp (uint)
    square(op) { mul(op, op) }       // sets to the square of op
    cube(op)   { pow(op, 3)  }       // sets to the cube of op

    foreign modPow(base, exp, mod)   // raises base (Mpz) to the power exp (Mpz) modulo mod (Mpz)
    foreign modPowUi(base, exp, mod) // raises base (Mpz) to the power exp (uint) modulo mod (Mpz)

    foreign root(op, n)              // sets to the n'th root (uint) of op (Mpz)
    foreign sqrt(op)                 // sets to the square root of op (Mpz)
    cbrt(op) { root(op, 3) }         // sets to the cube root of op (Mpz)

    foreign lsh(n, b)                // shifts n (Mpz) by b (uint) bits to the left
    foreign rsh(n, b)                // shifts n (Mpz) by b (uint) bits to the right

    foreign com(op)                  // one's complement of an Mpz object
    foreign and(op1, op2)            // bitwise 'and' of two Mpz objects
    foreign ior(op1, op2)            // bitwise 'inclusive or' of two Mpz objects
    foreign xor(op1, op2)            // bitwise 'exclusive or' of two Mpz object

    foreign gcd(op1, op2)            // greatest common denominator of two Mpz objects
    foreign gcdUi(op1, op2)          // greatest common denominator of an Mpz object and a uint

    foreign lcm(op1, op2)            // lowest common multiple of two Mpz objects
    foreign lcmUi(op1, op2)          // lowest common multiple of an Mpz object and a uint

    foreign modInv(op1, op2)         // modular inverse of op1 mod op2
    foreign remove(op, f)            // removes all factors f from op and returns number removed
    foreign nextPrime(op)            // next prime > op (Mpz)

    prevPrime(op) {                  // previous prime < op (Mpz) or aborts if no such prime
        if (op < Mpz.three) {
            Mpz.abort_()
        }
        if (op == Mpz.three) {
            this.setMpz(Mpz.two)
            return
        }
        var n = Mpz.new().setMpz(op.isEven ? op - Mpz.one : op - Mpz.two)
        while (true) {
            if (n.probPrime(15) > 0) {
                this.setMpz(n)
                return
            }
            n.sub(Mpz.two)
        }
    }

    /* Convenience versions of the above methods where the first (or only) argument is 'this'. 
       Unless otherwise noted, any other argument must either be another Mpz object or a uint.
       Other Nums are converted by GMP to uint. */

    add(op) { (op is Mpz) ? add(this, op) : ((op is Num) ? addSi(this, op) : Mpz.abort_()) } // sint
    sub(op) { (op is Mpz) ? sub(this, op) : ((op is Num) ? subSi(this, op) : Mpz.abort_()) } // sint
    mul(op) {                                                                                // sint
        if (op is Mpz) return mul(this, op)
        if (op is Num) {
            return (op >= 0) ? mulUi(this, op) : mulSi(this, op)
        }
        Mpz.abort_()
    }
    addMul(op) { (op is Mpz) ? addMul(this, op) : ((op is Num) ? addMulUi(this, op) : Mpz.abort_()) }
    subMul(op) { (op is Mpz) ? subMul(this, op) : ((op is Num) ? subMulUi(this, op) : Mpz.abort_()) }
    cdiv(d)    { (d is Mpz) ? cdiv(this, d) : ((d is Num) ? cdivUi(this, d) : Mpz.abort_()) } 
    fdiv(d)    { (d is Mpz) ? fdiv(this, d) : ((d is Num) ? fdivUi(this, d) : Mpz.abort_()) }    
    tdiv(d)    { (d is Mpz) ? tdiv(this, d) : ((d is Num) ? tdivUi(this, d) : Mpz.abort_()) }
    div(d)     { (d is Mpz) ? tdiv(this, d) : ((d is Num) ? divSi (this, d) : Mpz.abort_()) } // sint
    crem(d)    { (d is Mpz) ? crem(this, d) : ((d is Num) ? cremUi(this, d) : Mpz.abort_()) }
    frem(d)    { (d is Mpz) ? frem(this, d) : ((d is Num) ? fremUi(this, d) : Mpz.abort_()) }
    trem(d)    { (d is Mpz) ? trem(this, d) : ((d is Num) ? tremUi(this, d) : Mpz.abort_()) }
    rem(d)     { (d is Mpz) ? trem(this, d) : ((d is Num) ? remSi (this, d) : Mpz.abort_()) } // sint
    mod(d)     { (d is Mpz) ? mod (this, d) : ((d is Num) ? modUi (this, d) : Mpz.abort_()) }

    divExact(d) { (d is Mpz) ? divExact(this, d) : ((d is Num) ? divExact(this, d) : Mpz.abort_()) }

    neg       { neg(this) }
    abs       { abs(this) }
    inc       { addUi(this, 1) }
    dec       { subUi(this, 1) }

    pow(exp)  { pow(this, exp)  }  // exp is a uint
    square    { mul(this, this) }
    cube      { pow(this, 3)    }

    // Exp can either be an Mpz object or a uint, mod must be an Mpz object.
    modPow(exp, mod) {
        if (exp is Mpz) return modPow(this, exp, mod)
        if (exp is Num) return modPowUi(this, exp, mod)
        Mpz.abort_()
    }

    root(n)     { root(this, n) }  // n is a uint
    sqrt        { sqrt(this)    }
    cbrt        { root(this, 3) }

    lsh(b)      { lsh(this, b)  }  // b is a uint
    rsh(b)      { rsh(this, b)  }  // b is a uint

    com         { com(this)     }
    and(op)     { and(this, op) }
    ior(op)     { ior(this, op) }
    xor(op)     { xor(this, op) }

    gcd(op)     { (op is Mpz) ? gcd(this, op) : ((op is Num) ? gcdUi(this, op) : Mpz.abort_()) }

    lcm(op)     { (op is Mpz) ? lcm(this, op) : ((op is Num) ? lcmUi(this, op) : Mpz.abort_()) }

    modInv(op)  { modInv(this, op) } // op is an Mpz object
    remove(f)   { remove(this, f) }  // f is an Mpz object
    nextPrime   { nextPrime(this) }
    prevPrime   { prevPrime(this) }

    /* As above methods where the first (or only) argument is 'this'
       but return a new Mpz object rather than mutating 'this'. */

    - { copy().neg }
    ~ { copy().com }

    +(op)  { copy().add(op) }
    -(op)  { copy().sub(op) }
    *(op)  { copy().mul(op) }
    /(op)  { copy().div(op) }
    %(op)  { copy().rem(op) }

    <<(b)  { copy().lsh(b)  }
    >>(b)  { copy().rsh(b)  }
    &(op)  { copy().and(op) }
    |(op)  { copy().ior(op) }
    ^(op)  { copy().xor(op) }

    /* Methods which may mutate the current instance or simply return it. */

    min(op)   { (op < this) ? set(op) : this }    // minimum of this and op
    max(op)   { (op > this) ? set(op) : this }    // maximum of this and op
    clamp(min, max) {                             // clamps this to the interval [min, max]
        if (min > max) Fiber.abort("Range cannot be decreasing.")
        if (this < min) set(min) else if (this > max) set(max)
        return this.copy()
    }

    /* Comparison methods which return -1, 0, 1
       if this < op, this == op or this > op respectively. */

    foreign cmpMpz(op)             // compare this to another Mpz object
    foreign cmpDbl(op)             // compare this to a double
    foreign cmpUi(op)              // compare this to a uint
    foreign cmpSi(op)              // compare this to a sint

    foreign cmpAbsMpz(op)          // compare this to another Mpz object by absolute values
    foreign cmpAbsDbl(op)          // compare this to a double by absolute values
    foreign cmpAbsUi(op)           // compare this to a uint by absolute values

    // Needed to satisfy Comparable trait and allow relational operators to be used.
    compare(op) {
        if (op is Mpz) return cmpMpz(op)
        if (op is Num) return !op.isInteger ? cmpDbl(op) : (op < 0) ? cmpSi(op) : cmpUi(op)
        Mpz.abort_()
    }

    // Convenience method for comparison by absolute values.
    compareAbs(op) {
        if (op is Mpz) return cmpAbsMpz(op)
        if (op is Num) return !op.isInteger ? cmpAbsDbl(op) : cmpAbsUi(op)
        Mpz.abort_()
    }

    /* Miscellaneous methods. */

    copy() { Mpz.fromMpz(this) }   // copies 'this' to a new Mpz object

    foreign isOdd                  // true if 'this' is odd
    foreign isEven                 // true if 'this' is even
    isZero { cmpUi(0) == 0 }       // true if 'this' is zero

    foreign isDivisible(d)         // true if 'this' is divisible by d (Mpz)
    foreign isDivisibleUi(d)       // true if 'this' is divisible by d (uint)
    foreign isDivisible2(b)        // true if 'this' is divisible by 2^b (uint)

    foreign isCongruent(c, d)      // true if 'this' is congruent to c (Mpz) mod d (Mpz)
    foreign isCongruentUi(c, d)    // true if 'this' is congruent to c (uint) mod d (uint)
    foreign isCongruent2(c, b)     // true if 'this' is congruent to c (uint) mod 2^b(uint)

    foreign isPower                // true if 'this' = a ^ b, for some integers a and b with b > 1
    foreign isSquare               // true if 'this' = a ^ 2, for some integer a

    foreign probPrime(reps)        // 0, 1 or 2 if 'this' is definitely non-prime, probably prime
                                   // or definitely prime respectively after 'reps' repetitions

    foreign sign(op)               // the sign of an Mpz object
    sign { sign(this) }            // the sign of the current instance

    foreign factorial(n)           // the factorial of n (uint), n!
    foreign factorial2(n)          // the double factorial of n (uint), n!!
    foreign mfactorial(n, m)       // the m-multifactorial of n, n!^m, both arguments uints
    foreign primorial(n)           // the primorial of n (uint) i.e. product of all primes <= n
    foreign binomial(n, k)         // the binomial coefficent of n (Mpz) over k (uint)
    foreign binomialUi(n, k)       // the binomial coefficent of n (uint) over k (uint)
    foreign fibonacci(n)           // the n'th (uint) Fibonacci number
    foreign lucas(n)               // the n'th (uint) Lucas number

    multinomial(n, f) {            // the multinomial coefficient of n over a list f where sum(f) == n
        if (!(n is Num && n >= 0)) {  
            Fiber.abort("First argument must be a non-negative integer.")
        }
        if (!(f is List)) Fiber.abort("Second argument must be a list.")
        var sum = f.reduce { |acc, i| acc + i }
        if (n != sum) {
            Fiber.abort("The elements of the list must sum to 'n'.")
        }
        var prod = Mpz.one
        var fact = Mpz.new()
        for (e in f) {
            if (e < 0) Fiber.abort("The elements of the list must be non-negative integers.")
            if (e > 1) prod.mul(fact.factorial(e))
        }
        return factorial(n).div(prod)
    }

    foreign popCount               // number of 1 bits in this
    foreign scan0(start)           // index of first 0 bit in this starting from index 'start'
    foreign scan1(start)           // index of first 1 bit in this starting from index 'start'
    foreign setBit(index)          // set bit at 'index' in this
    foreign clrBit(index)          // clear bit at 'index' in this
    foreign comBit(index)          // complement bit at 'index' in this
    foreign tstBit(index)          // test bit at 'index' in this and return its value
    foreign sizeInBase(base)       // number of digits in the given base 2..62 ignoring any sign
                                   // if the base is not a power of 2 may be one too big
    foreign digitsInBase(base)     // as sizeInBase, always exact but slower
    sizeInBits { sizeInBase(2) }   // number of binary digits in this ignoring any sign
  
    /* Prime factorization methods. */

    // Private worker method for Pollard's Rho algorithm.
    static pollardRho_(m, seed, c) {
        var g = Fn.new { |x| (x * x).add(c).rem(m) }
        var x = Mpz.from(seed)
        var y = Mpz.from(seed)
        var z = Mpz.one
        var d = Mpz.one
        var count = 0
        while (true) {
            x = g.call(x)
            y = g.call(g.call(y))
            d = (x - y).abs.rem(m)
            z.mul(d)
            count = count + 1
            if (count == 100) {
                d.gcd(z, m)
                if (d != 1) break
                z.set(1)
                count = 0
            }
        }
        if (d == m) return Mpz.zero
        return d
    }

    // Returns a factor (Mpz) of 'm' (an Mpz object or an integral Num) using the
    // Pollard's Rho algorithm. Both the 'seed' and 'c' can be set to integers.
    // Returns Mpz.zero in the event of failure.
    static pollardRho(m, seed, c) {
        if (m < 2) return Mpz.zero
        if (m is Num) m = Mpz.from(m)
        if (m.probPrime(15) > 0) return m.copy()
        if (m.isSquare) return m.copy().sqrt
        for (p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]) {
            if (m.isDivisibleUi(p)) return Mpz.from(p)
        }
        return pollardRho_(m, seed, c)
    }

    // Convenience version of the above method which uses a seed of 2 and a value for c of 1.
    static pollardRho(m) { pollardRho(m, 2, 1) }

    // Private method for factorizing smaller numbers (Mpz) using a wheel with basis [2, 3, 5].
    static primeFactorsWheel_(m) {
        var n = m.copy()
        var inc = [4, 2, 4, 2, 4, 6, 2, 6]
        var factors = []
        var k = Mpz.from(37)
        var i = 0
        while (k * k <= n) {
            if (n.isDivisible(k)) {
                factors.add(k.copy())
                n.div(k)
            } else {
                k.add(inc[i])
                i = (i + 1) % 8
            }
        }
        if (n > 1) factors.add(n)
        return factors
    }

    // Private worker method (recursive) to obtain the prime factors of a number (Mpz).
    static primeFactors_(m, trialDivs) {
        if (m.probPrime(15) > 0) return [m.copy()]
        var n = m.copy()
        var factors = []
        var seed = 2
        var c = 1
        var checkPrime = true
        var threshold = 1e11 // from which using PR may be advantageous
        if (trialDivs) {
            for (p in [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]) {
                while (n.isDivisibleUi(p)) {
                    factors.add(Mpz.from(p))
                    n.div(p)
                }
            }
        }
        while (n > 1) {
            if (checkPrime && n.probPrime(15) > 0) {
                factors.add(n.copy())
                break
            }
            if (n >= threshold) {
                var d = pollardRho_(n, seed, c)
                if (d != 0) {
                    factors.addAll(primeFactors_(d, false))
                    n.div(d)
                    checkPrime = true
                } else if (c == 1) {
                    if (n.isSquare) {
                        n.sqrt
                        var pf = primeFactors_(n, false)
                        factors.addAll(pf)
                        factors.addAll(pf)
                        break
                    } else {
                        c = 2
                        checkPrime = false
                    }
                } else if (c < 101) {
                    c = c + 1
                } else if (seed < 101) {
                    seed = seed + 1
                } else {
                    factors.addAll(primeFactorsWheel_(n))
                    break
                }
            } else {
                factors.addAll(primeFactorsWheel_(n))
                break
            }
        }
        factors.sort()
        return factors
    }

    // Returns a list of the primes factors (Mpz) of 'm' (an Mpz object or an integral Num)
    // using the wheel based factorization and/or Pollard's Rho algorithm as appropriate.
    static primeFactors(m) {
       if (m < 2) return []
       if (m is Num) m = Mpz.from(m)
       return primeFactors_(m, true)
    }

    // Returns all the divisors of 'n' including 1 and 'n' itself.
    static divisors(n) {
        if (n < 1) return []
        if (n is Num) n = from(n)
        var divs = []
        var divs2 = []
        var i = one
        var k = n.isEven ? one : two
        var sqrt = n.copy().sqrt
        while (i <= sqrt) {
            if (n.isDivisible(i)) {
                divs.add(i.copy())
                var j = n / i
                if (j != i) divs2.add(j)
            }
            i.add(k)
        }
        if (!divs2.isEmpty) divs = divs + divs2[-1..0]
        return divs
    }

    // Returns all the divisors of 'n' excluding 'n'.
    static properDivisors(n) {
        var d = divisors(n)
        var c = d.count
        return (c <= 1) ? [] : d[0..-2]
    }

    // As 'divisors' method but uses a different algorithm.
    // Better for large numbers with a small number of prime factors.
    static divisors2(n) {
        if (n is Num) n = from(n)
        var pf = primeFactors(n)
        if (pf.count == 0) return (n == 1) ? [one] : pf
        var arr = []
        if (pf.count == 1) {
            arr.add([pf[0].copy(), 1])
        } else {
            var prevPrime = pf[0]
            var count = 1
            for (i in 1...pf.count) {
                if (pf[i] == prevPrime) {
                    count = count + 1
                } else {
                    arr.add([prevPrime.copy(), count])
                    prevPrime = pf[i]
                    count = 1
                }
            }
            arr.add([prevPrime.copy(), count])
        }
        var divisors = []
        var generateDivs
        generateDivs = Fn.new { |currIndex, currDivisor|
            if (currIndex == arr.count) {
                divisors.add(currDivisor.copy())
                return
            }
            for (i in 0..arr[currIndex][1]) {
                generateDivs.call(currIndex+1, currDivisor)
                currDivisor = currDivisor * arr[currIndex][0]
            }
        }
        generateDivs.call(0, one)
        return divisors.sort()
    }

    // As 'properDivisors' but uses 'divisors2' method.
    static properDivisors2(n) {
        var d = divisors2(n)
        var c = d.count
        return (c <= 1) ? [] : d[0..-2]
    }

    // Returns the sum of all the divisors of 'n' including 1 and 'n' itself.
    static divisorSum(n) {
        if (n < 1) return zero
        n = from(n)
        var total = one
        var power = two
        while (n.isDivisible(two)) {
            total.add(power)
            power.mul(2)
            n.div(2)
        }
        var i = three
        while (i * i <= n) {
            var sum = one
            power.set(i)
            while (n.isDivisible(i)) {
                sum.add(power)
                power.mul(i)
                n.div(i)
            }
            total.mul(sum)
            i.add(2)
        }
        if (n > 1) total.mul(n + 1)
        return total
    }

    // Returns the number of divisors of 'n' including 1 and 'n' itself.
    static divisorCount(n) {
        if (n < 1) return 0
        n = from(n)
        var count = 0
        var prod = 1
        while (n.isDivisible(two)) {
            count = count + 1
            n.div(2)
        }
        prod = prod * (1 + count)
        var i = three
        while (i * i <= n) {
            count = 0
            while (n.isDivisible(i)) {
                count = count + 1
                n.div(i)
            }
            prod = prod * (1 + count)
            i.add(2)
        }
        if (n > 2) prod = prod * 2
        return prod
    }


    /* Methods which apply to a sequence of Mpz objects. */

    static sum(sz)  { sz.reduce(Mpz.zero) { |acc, x| acc.add(x) } }
    static prod(sz) { sz.reduce(Mpz.one)  { |acc, x| acc.mul(x) } }
    static min(sz)  { sz.reduce { |acc, x| (x > acc) ? x : acc  } }
    static max(sz)  { sz.reduce { |acc, x| (x < acc) ? x : acc  } }
}

/*
    Mpq represents an arbitrary length rational number allowing arithmetic operations on such
    numbers of unlimited size. An Mpq object is a pointer to the GMP type mpq_t and is mutable. 
    Many methods map directly to the corresponding GMP functions though others are provided for
    convenience.

    Mpq objects are always automatically canonicalized (n/d in lowest form, d > 0) by constructors
    or setters. Zero is represented by 0/1. If integers are required, Nums which are not integers
    will be truncated towards zero by GMP.
*/
foreign class Mpq is Comparable {

    // Creates small commonly used Mpq objects.
    static minusOne { from(-1, 1) }
    static zero     { new()       }
    static one      { from(1, 1)  }
    static two      { from(2, 1)  } 
    static three    { from(3, 1)  }
    static four     { from(4, 1)  }
    static five     { from(5, 1)  }
    static six      { from(6, 1)  }
    static seven    { from(7, 1)  }
    static eight    { from(8, 1)  }
    static nine     { from(9, 1)  }
    static ten      { from(10, 1) }
    static half     { from(1, 2)  }
    static third    { from(1, 3)  }
    static quarter  { from(1, 4)  }
    static fifth    { from(1, 5)  }
    static sixth    { from(1, 6)  }
    static seventh  { from(1, 7)  }
    static eighth   { from(1, 8)  }
    static ninth    { from(1, 9)  }
    static tenth    { from(1, 10) }

    // Swaps the values of two Mpq objects.
    foreign static swap(op1, op2)

    // Returns the smaller of two Mpq objects.
    static min(op1, op2) {
        if (!(op1 is Mpq)) op1 = from(op1)
        if (!(op2 is Mpq)) op2 = from(op2)
        if (op1 < op2) return op1
        return op2
    }

    // Returns the greater of two Mpq objects.
    static max(op1, op2) {
        if (!(op1 is Mpq)) op1 = from(op1)
        if (!(op2 is Mpq)) op2 = from(op2)
        if (op1 > op2) return op1
        return op2
    }

    // Returns the positive difference of two Mpq objects.
    static dim(op1, op2) {
        if (!(op1 is Mpq)) op1 = from(op1)
        if (!(op2 is Mpq)) op2 = from(op2)
        if (op1 >= op2) return op1 - op2
        return Mpq.zero
    }

    // Creates a new Mpq object with a value of 0/1.
    construct new() {}

    // Convenience method which creates a new Mpq object from any other numeric type.
    static from(op) { new().set(op) }

    // Convenience method which creates a new Mpq object from two integers (second always unsigned).
    static from(op1, op2) { new().set(op1, op2) }

    // Convenience methods which create a new Mpq object from a string (zero if invalid).
    static fromStr(str, b) { new().setStr(str, b)  }  // base 'b'
    static fromStr(str)    { new().setStr(str, 10) }  // base 10

    // Assigns new values to the current instance using :
    foreign setMpq(op)               // another Mpq object
    foreign setMpz(op)               // an Mpz object
    foreign setMpf(op)               // an Mpf object
    foreign setDbl(op)               // a double
    foreign setUi(op1, op2)          // two unsigned integers op1/op2
    foreign setSi(op1, op2)          // a signed and an unsigned integer op1/op2
    foreign setStr(str, b)           // a base 'b' string (unchanged if invalid)
    setStr(str) { setStr(str, 10) }  // a base 10 string

    // Convenience method which assigns a new value to the current instance
    // from any other numeric type.
    set(op) {
        if (op is Mpq) return setMpq(op)
        if (op is Num) {
            if (op.isInteger) return (op >= 0) ? setUi(op, 1) : setSi(op, 1)
            return setDbl(op)
        }
        if (op is Mpz) return setMpz(op)
        if (op is Mpf) return setMpf(op)
        Mpz.abort_()
    }

    // Convenience method which assigns a new value to the current instance
    // from two integers (second always unsigned).
    set(op1, op2) {
        if ((op1 is Num) && (op2 is Num)) {
            if (op1.isInteger && op2.isInteger && op2 > 0) {
                return (op1 >= 0) ? setUi(op1, op2) : setSi(op1, op2)
            } else {
                return setDbl(op1/op2)
            }
        }
        Mpz.abort()
    }

    // Resets the value of the current instance to zero.
    reset() { setUi(0, 1) }

    // Properties
    foreign num      // gets the numerator of this instance as an Mpz object
    foreign den      // gets the denominator of this instance as an Mpz object

    foreign num=(op) // sets the numerator of this instance to an Mpz object
    foreign den=(op) // sets the denominator of this instance to an Mpz object

    setNum(op) { num = op }  // alias for 'num' setter
    setDen(op) { den = op }  // alias for 'den' setter

    // Converts the current instance to:
    foreign toNum                // a number
    foreign toString(b)          // a base 'b' string
    toString { toString(10) }    // a base 10 string
    toMpz    { Mpz.from(this) }  // an Mpz object
    toMpf    { Mpf.from(this) }  // an Mpf object

    /* Methods which assign their result to the current instance ('this').
       'uint' denotes a Num which is an unsigned integer. */

    foreign add(op1, op2)   // adds two Mpq objects
    foreign sub(op1, op2)   // subtracts one Mpq object from another
    foreign mul(op1, op2)   // multiplies two Mpq objects
    foreign mul2(op1, op2)  // multiplies op1 (Mpq) by 2^op2 (uint)
    foreign div(op1, op2)   // divides one Mpq object by another
    foreign div2(op1, op2)  // divides op1 (Mpq) by 2^op2 (uint)
    foreign neg(op)         // sets to -op (Mpq)
    foreign abs(op)         // sets to the absolute value of op (Mpq)
    foreign inv(op)         // sets to 1/op (Mpq)

    // rounding methods, similar to those in Mpf class
    ceil(op) { // higher integer
        if (op.den == Mpz.one) return setMpq(op)
        var div = op.num / op.den
        if (op.sign >= 0) div.inc
        return setMpz(div)
    }

    floor(op) { // lower integer
        if (op.den == Mpz.one) return setMpq(op)
        var div = op.num / op.den
        if (op.sign < 0) div.dec
        return setMpz(div)
    }

    trunc(op) { op.sign < 0  ? ceil(op) : floor(op) } // lower integer, towards zero

    round(op) { // nearer integer, 1/2 away from zero
        if (op.den == Mpz.one) return setMpq(op)
        var op2 = op.copy()
        if (op2.sign < 0) op2.sub(Mpq.half) else op2.add(Mpq.half)
        return trunc(op2)
    }

    roundUp (op) { op.sign >= 0 ? ceil(op) : floor(op) } // higher integer, away from zero

    /* As above methods where the first (or only) argument is 'this'. */

    add(op)  { add(this, op)  }  
    sub(op)  { sub(this, op)  }
    mul(op)  { mul(this, op)  }
    mul2(op) { mul2(this, op) }
    div(op)  { div(this, op)  }
    div2(op) { div2(this, op) }
    neg      { neg(this)      }
    abs      { abs(this)      }
    inv      { inv(this)      }

    ceil     { ceil(this)     }
    floor    { floor(this)    }
    trunc    { trunc(this)    }
    round    { round(this)    }
    roundUp  { roundUp(this)  }

    /* As above methods where the first (or only) argument is 'this'
       but return a new Mpq object rather than mutating 'this'. */

    -      { copy().neg      }

    +(op)  { copy().add(op)  }
    -(op)  { copy().sub(op)  }
    *(op)  { copy().mul(op)  }
    /(op)  { copy().div(op)  }

    <<(op) { copy().mul2(op) }  // equivalent to mul2(op)
    >>(op) { copy().div2(op) }  // equivalent to div2(op)

    /* Methods which may mutate the current instance or simply return it. */

    min(op)   { (op < this) ? set(op) : this }    // minimum of this and op
    max(op)   { (op > this) ? set(op) : this }    // maximum of this and op
    clamp(min, max) {                             // clamps this to the interval [min, max]
        if (min > max) Fiber.abort("Range cannot be decreasing.")
        if (this < min) set(min) else if (this > max) set(max)
        return this.copy()
    }

    /* Comparison methods which return -1, 0, 1 
       if this < op, this == op or this > op respectively. */

    foreign cmpMpq(op)       // compare this to another Mpq object
    foreign cmpMpz(op)       // compare this to an Mpz object
    foreign cmpUi(num, den)  // compare this to two uints num/den
    foreign cmpSi(num, den)  // compare this to a sint and a uint num/den

    foreign ==(op)           // true if this and op are equal (faster than cmpMpq(op))
    !=(op) { !(this == op) } // true if this and op are unequal

    // Needed to satisfy Comparable trait and allow relational operators to be used.
    compare(op) {
        if (op is Mpq) return cmpMpq(op)
        if (op is Mpz) return cmpMpz(op)
        if (op is Num) return (op < 0) ? cmpSi(op) : cmpUi(op)
        Mpz.abort_()
    }

    /* Miscellaneous methods. */

    copy() { Mpq.from(this) } // copies 'this' to a new Mpq object

    foreign sign(op)          // the sign of an Mpq object
    sign { sign(this) }       // the sign of the current instance
    isZero { cmpUi(0) == 0 }  // returns 'true' if the current instance is zero

    fraction {  // the fractional part of this with the same sign, as a new Mpq object
        var t = copy().trunc
        return t.sub(this, t)
    }

    /* Methods which apply to a sequence of Mpq objects. */

    static sum(sq)  { sq.reduce(Mpq.zero) { |acc, x| acc.add(x) } }
    static prod(sq) { sq.reduce(Mpq.one)  { |acc, x| acc.mul(x) } }
    static mean(sq) { sum(sq).div(sq.count) }
    static min(sq)  { sq.reduce { |acc, x| (x > acc) ? x : acc  } }
    static max(sq)  { sq.reduce { |acc, x| (x < acc) ? x : acc  } }
}

/*
    Mpf represents an arbitrary length floating-point number allowing arithmetic operations on such
    numbers of unlimited size. An Mpf object is a pointer to the GMP type mpf_t and is mutable. 
    Many methods map directly to the corresponding GMP functions though others are provided for
    convenience.

    The mantissa of each Mpf object has a mimimum user-selectable precision, limited only by memory,
    which can be increased or decreased at any time. In practice GMP may use a higher precision.

    If integers are required, Nums which are not integers will be truncated towards zero by GMP.
*/
foreign class Mpf is Comparable {

    // Creates small commonly used Mpf objects with default precision.
    static minusOne { from(-1)    }
    static zero     { new()       }
    static one      { from(1)     }
    static two      { from(2)     }
    static three    { from(3)     }
    static four     { from(4)     }
    static five     { from(5)     }
    static six      { from(6)     }
    static seven    { from(7)     }
    static eight    { from(8)     }
    static nine     { from(9)     }
    static ten      { from(10)    }
    static half     { from(0.5)   }
    static third    { new().inv(three) }
    static quarter  { from(0.25)  }
    static fifth    { from(0.2)   }
    static sixth    { new().inv(six)   }
    static seventh  { new().inv(seven) }
    static eighth   { from(0.125) }
    static ninth    { new().inv(nine)  }
    static tenth    { from(0.1)   }

    // Creates transcendental constants with a specified precision.
    static e(prec)     { from(1, prec).exp  }                // E
    static ln2(prec)   { from(2, prec).log  }                // natural log of 2
    static ln10(prec)  { from(10, prec).log }                // natural log of 10
    static pi(prec)    { new(prec).acos(Mpf.zero).mul(2) }   // Pi
    static tau(prec)   { new(prec).acos(Mpf.zero).mul(4) }   // Tau (twice Pi)
    static phi(prec)   { from(5, prec).sqrt.add(1).div(2) }  // Phi (golden ratio)
    static sqrt2(prec) { from(2, prec).sqrt }                // Square root of 2
    static euler(prec) { from(1, prec).digamma.neg }         // Euler's constant

    // Convenience versions of the above constants which use the default precision.
    static e     { from(1).exp  }
    static ln2   { from(2).log  }
    static ln10  { from(10).log }
    static pi    { new().acos(Mpf.zero).mul(2) }
    static tau   { new().acos(Mpf.zero).mul(4) }
    static phi   { from(5).sqrt.add(1).div(2) }
    static sqrt2 { from(2).sqrt }
    static euler { from(1).digamma.neg }

    // Swaps the values of two Mpf objects.
    foreign static swap(op1, op2)

    // Splits an Mpf into a double plus an exponent (returned as a list of two Nums).
    foreign static frexp(op)

    // Gets the string presentation of am Mpf object in base b with up to n digits.
    // Returns it as a list containing a digit string and an exponent.
    // The digit string has an implicit radix point immediately to the left of the first digit.
    foreign static getStr(op, b, n)

    // Returns the smaller of two Mpf objects.
    static min(op1, op2) {
        if (!(op1 is Mpf)) op1 = from(op1)
        if (!(op2 is Mpf)) op2 = from(op2)
        if (op1 < op2) return op1
        return op2
    }

    // Returns the greater of two Mpf objects.
    static max(op1, op2) {
        if (!(op1 is Mpf)) op1 = from(op1)
        if (!(op2 is Mpf)) op2 = from(op2)
        if (op1 > op2) return op1
        return op2
    }

    // Returns the positive difference of two Mpf objects.
    static dim(op1, op2) {
        if (!(op1 is Mpf)) op1 = from(op1)
        if (!(op2 is Mpf)) op2 = from(op2)
        if (op1 >= op2) return op1 - op2
        return Mpf.zero
    }

    foreign static defaultPrec         // returns the default precision in bits actually used
    foreign static defaultPrec=(prec)  // sets the default precision to at least 'prec' bits

    // Creates a new Mpf object with a value of 0 and precision of:
    construct new() {}       //  default
    construct new(prec) {}   //  at least 'prec' bits

    // Convenience methods which create a new Mpf object from any other applicable type
    // with default precision.
    static from(op)        { new().set(op) }         // any numeric type
    static fromStr(str, b) { new().setStr(str, b) }  // a base 'b' string (zero if invalid)
    static fromStr(str)    { new().setStr(str, 10) } // a base 10 string (zero if invalid)

    // Convenience methods which create a new Mpq object from any other applicable type
    // with a precision of at least 'prec' bits.
    static from(op, prec) { new(prec).set(op) }               // any numeric type
    static fromStr(str, b, prec) { new(prec).setStr(str, b) } // a base 'b' string (zero if invalid)

    // Assigns new values to the current instance using :
    foreign setMpf(op)              // another Mpf object
    foreign setMpq(op)              // an Mpq object
    foreign setMpz(op)              // an Mpz object
    foreign setDbl(op)              // a double
    foreign setUi(op)               // an unsigned integer
    foreign setSi(op)               // a signed integer
    foreign setStr(str, b)          // a base 'b' string (unchanged if invalid)
    setStr(str) { setStr(str, 10) } // a base 10 string

    // Convenience method which assigns a new value to the current instance
    // from any other numeric type.
    set(op) {
        if (op is Mpf) return setMpf(op)
        if (op is Num) {
            if (op.isInteger) return (op >= 0) ? setUi(op) : setSi(op)
            return setDbl(op)
        }
        if (op is Mpz) return setMpz(op)
        if (op is Mpq) return setMpq(op)
        Mpz.abort_()
    }

    // Resets the value of the current instance to zero with default precision.
    reset() { setUi(0).setPrec(Mpf.defaultPrec) }

    // Properties.
    foreign prec         // returns the current precision of this instance in bits
    foreign prec=(prec)  // sets the precision of this instance to at least 'prec' bits
    setPrec(prec) { this.prec = prec } // alias for 'prec' setter

    // Converts the current instance to:
    foreign toNum                  // a number
    foreign toSint                 // a signed integer (Num)
    foreign toUint                 // an unsigned integer (Num)

    toString(b, d) {               // a base 'b' string with up to d radix places
        var res = Mpf.getStr(this, b, d)
        var digits = res[0].toString
        if (digits == "") return "0"
        var sgn = ""
        if (digits[0] == "-") {
            sgn = "-"
            digits = digits[1..-1]
        }
        var exp = res[1]
        if (exp == 0) return sgn + "0." + digits
        if (exp < 0)  return sgn + "0." + ("0" * (-exp)) + digits
        var c = digits.count
        if (c > exp) return sgn + digits[0...exp] + "." + digits[exp..-1]
        if (c == exp) return sgn + digits
        return sgn + digits + ("0" * (exp - c))
    }

    toString(d) { toString(10, d)   }      // a base 'b' string with up to 'd' radix places
    toString    { toString(10, 64)  }      // a base 10 string with up to 64 radix places

    toDecStr(p, round) {                   // as toString but truncated to p decimal places
        if (!(round is Bool)) Mpz.abort_() // and optionally rounded
        if (!((p is Num) && p.isInteger && p >= 0)) Mpz.abort_()
        var str = toString
        var dp = str.indexOf(".")
        if (dp == -1) return str
        var str2 = str[dp+1..-1]
        if (str2.count <= p) return str
        if (!round) {
            if (p == 0) return str[0...dp]
            return str[0..dp] + str2[0...p]
        }
        var power = Mpf.ten.pow(p)
        var temp = (this * power + Mpf.half).floor.div(power)
        return temp.toDecStr(p, false)
    }

    toDecStr(p) { toDecStr(p, true) }  // convenience version of toDecStr with 'round' equals true

    toMpz       { Mpz.from(this) }     // an Mpz object
    toMpq       { Mpq.from(this) }     // an Mpq object

    /* Methods which assign their result to the current instance ('this').
        'uint' denotes a Num which is an unsigned integer. */

    foreign add(op1, op2)            // adds two Mpf objects
    foreign addUi(op1, op2)          // adds an Mpf object and a uint

    foreign sub(op1, op2)            // subtracts one Mpf object from another
    foreign subUi(op1, op2)          // subtracts a uint from an Mpf object
    foreign uiSub(op1, op2)          // subtracts an Mpf object from a uint

    foreign mul(op1, op2)            // multiplies two Mpf objects
    foreign mulUi(op1, op2)          // multiplies an Mpf object by a uint
    foreign mul2(op1, op2)           // multiplies op1 (Mpf) by 2^op2 (uint)

    foreign div(op1, op2)            // divides one Mpf object by another
    foreign divUi(op1, op2)          // divides an Mpf object by a uint
    foreign uiDiv(op1, op2)          // divides a uint by an Mpf object
    foreign div2(op1, op2)           // divides op1 (Mpf) by 2^op2 (uint)

    addSi(op1, op2) { (op2 >= 0) ? addUi(op1, op2) : subUi(op1, -op2)     } // op2 is a sint
    subSi(op1, op2) { (op2 >= 0) ? subUi(op1, op2) : addUi(op1, -op2)     } // op2 is a sint
    mulSi(op1, op2) { (op2 >= 0) ? mulUi(op1, op2) : mulUi(op1, -op2).neg } // op2 is a sint
    divSi(op1, op2) { (op2 >= 0) ? divUi(op1, op2) : divUi(op1, -op2).neg } // op2 is a sint

    foreign neg(op)                  // sets to -op (Mpf)
    foreign abs(op)                  // sets to the absolute value of op (Mpf)
    inc(op)  { addUi(op, 1) }        // adds one to op (Mpf)
    dec(op)  { subUi(op, 1) }        // subtracts one from op (Mpf)
    inv(op)  { uiDiv(1, op) }        // sets to 1/op (Mpf)

    foreign relDiff(op1, op2)        // sets to relative difference of two Mpf objects
    foreign pow(base, exp)           // raises base (Mpf) to the power exp (uint)
    foreign powz(base, exp)          // raises base (Mpf) to the power exp (Mpz)
    foreign powf(base, exp)          // raises base (Mpf) to the power exp (Mpf)
    square(op) { mul(op, op) }       // sets to the square of op (Mpf)
    cube(op)   { pow(op, 3)  }       // sets to the cube of op (Mpf)

    foreign root(op, n)              // sets to the n'th root (uint) of op (Mpf)
    foreign sqrt(op)                 // sets to the square root of op (Mpf)
    foreign sqrtUi(op)               // sets to the square root of op (uint)
    foreign cbrt(op)                 // sets to the cube root of op (Mpf)
    foreign hypot(x, y)              // sets to the Euclidean norm of x and y

    foreign ceil(op)                 // rounds to the next higher integer
    foreign floor(op)                // rounds to the next lower integer
    foreign trunc(op)                // rounds to the next integer towards zero

    round(op) {                      // rounds to the nearest integer, half away from zero
        var sgn = Mpf.from(op.sign)
        var op2 = op.copy()
        return mul(op2.abs.add(Mpf.half).floor, sgn)
    }

    roundUp(op) {                    // rounds to the next integer away from zero
        var op2 = op.copy()
        return (op2 >= 0) ? set(op2.ceil) : set(op2.floor)
    }

    // Transcendental methods which set 'this' to the:
    foreign log(op)                  // natural logarithm of op
    foreign log2(op)                 // base two logarithm of op
    foreign log10(op)                // base ten logarithm of op
    foreign exp(op)                  // exponential of op
    foreign cos(op)                  // cosine of op
    foreign sin(op)                  // sine of op
    foreign tan(op)                  // tangent of op
    foreign acos(op)                 // arc-cosine of op
    foreign asin(op)                 // arc-sine of op
    foreign atan(op)                 // arc-tangent of op
    foreign atan2(y, x)              // arc-tangent 2 of y and x
    foreign cosh(op)                 // hyperbolic cosine of op
    foreign sinh(op)                 // hyperbolic sine of op
    foreign tanh(op)                 // hyperbolic tangent of op
    foreign gamma(op)                // gamma function of op
    foreign gammaInc(op, op2)        // incomplete gamma function of op and op2
    foreign lgamma(op)               // natural logarithm of the absolute value of gamma(op)
    foreign digamma(op)              // digamma (or psi) function of op
    foreign zeta(op)                 // zeta function of op (Mpf)
    foreign zetaUi(op)               // zeta function of op (uint)

    /* As above methods where the first (or only) argument is 'this'.
       Unless otherwise noted, any other argument must either be another Mpf object or a sint. 
       Other Nums are converted by GMP to uint. */

    add(op)   { (op is Mpf) ? add(this, op) : ((op is Num) ? addSi(this, op) : Mpz.abort_()) }
    sub(op)   { (op is Mpf) ? sub(this, op) : ((op is Num) ? subSi(this, op) : Mpz.abort_()) }

    mul(op)   { (op is Mpf) ? mul(this, op) : ((op is Num) ? mulSi(this, op) : Mpz.abort_()) }
    mul2(op)  { mul2(this, op)  }

    div(op)   { (op is Mpf) ? div(this, op) : ((op is Num) ? divSi(this, op) : Mpz.abort_()) }
    div2(op)  { div2(this, op)  }

    neg { neg(this)      }
    abs { abs(this)      }
    inc { addUi(this, 1) }
    dec { subUi(this, 1) }
    inv { uiDiv(1, this) }

    relDiff(op) { relDiff(this, op) }  // op must be another Mpf object
    pow(exp)  { pow(this, exp)  }      // exp must be a uint
    powz(exp) { powz(this, exp) }      // exp must be an Mpz object
    powf(exp) { powf(this, exp) }      // exp must be an Mpf object
    square    { mul(this, this) }  
    cube      { pow(this, 3)    }

    root(n)   { root(this, n)  }
    sqrt      { sqrt(this)     }
    cbrt      { cbrt(this)     }
    hypot(y)  { hypot(this, y) }

    ceil      { ceil(this)     } 
    floor     { floor(this)    }
    trunc     { trunc(this)    }
    round     { round(this)    }
    roundUp   { roundUp(this)  }

    log       { log(this)      }
    log2      { log2(this)     }
    log10     { log10(this)    }
    exp       { exp(this)      }
    cos       { cos(this)      }
    sin       { sin(this)      }
    tan       { tan(this)      }
    acos      { acos(this)     }
    asin      { asin(this)     }
    atan      { atan(this)     }
    atan2(x)  { atan2(this, x) }
    cosh      { cosh(this)     }
    sinh      { sinh(this)     }
    tanh      { tanh(this)     }

    gamma        { gamma(this)        }
    gammaInc(op) { gammaInc(this, op) }
    lgamma       { lgamma(this)       }
    digamma      { digamma(this)      }
    zeta         { zeta(this)         }

    /* As above methods where the first (or only) argument is 'this'
       but return a new Mpf object rather than mutating 'this'.*/

    -        { copy().neg      }

    +(op)    { copy().add(op)  }
    -(op)    { copy().sub(op)  }
    *(op)    { copy().mul(op)  }
    /(op)    { copy().div(op)  }

    <<(op)   { copy().mul2(op) }  // equivalent to mul2(op)
    >>(op)   { copy().div2(op) }  // equivalent to div2(op)

    /* Methods which may mutate the current instance or simply return it. */

    min(op)   { (op < this) ? set(op) : this }    // minimum of this and op
    max(op)   { (op > this) ? set(op) : this }    // maximum of this and op
    clamp(min, max) {                             // clamps this to the interval [min, max]
        if (min > max) Fiber.abort("Range cannot be decreasing.")
        if (this < min) set(min) else if (this > max) set(max)
        return this.copy()
    }

    /* Comparison methods which return -1, 0, 1
       if this < op, this == op or this > op respectively. */

    foreign cmpMpf(op)  // compare this to another Mpf object
    foreign cmpMpz(op)  // compare this to an Mpz object
    foreign cmpDbl(op)  // compare this to a double
    foreign cmpUi(op)   // compare this to an unsigned integer
    foreign cmpSi(op)   // compare this to a signed integer

    // Needed to satisfy Comparable trait and allow relational operators to be used.
    compare(op) {
        if (op is Mpf) return cmpMpf(op)
        if (op is Mpz) return cmpMpz(op)
        if (op is Num) return !op.isInteger ? cmpDbl(op) : (op < 0) ? cmpSi(op) : cmpUi(op)
        Mpz.abort_()
    }

    /* Miscellaneous methods. */

    copy() { Mpf.from(this, prec) } // copies 'this' to a new Mpf object with the same precision

    foreign isInteger         // true if the current instance is an integer
    foreign sign(op)          // the sign of an Mpf object
    sign { sign(this) }       // the sign of the current instance
    isZero { cmpUi(0) == 0 }  // returns 'true' if the current instance is zero

    fraction {  // the fractional part of this with the same sign, as a new Mpf object
        var t = copy().trunc
        return t.sub(this, t)
    }

    /* Methods which apply to a sequence of Mpf objects. */

    static sum(sf, prec)  { sf.reduce(Mpf.from(0, prec)) { |acc, x| acc.add(x) } }
    static prod(sf, prec) { sf.reduce(Mpf.from(1, prec)) { |acc, x| acc.mul(x) } }
    static mean(sf, prec) { sum(sf, prec) / sf.count }

    // Convenience versions of above methods where the return value has default precision.
    static sum(sf)  { sf.reduce(Mpf.zero) { |acc, x| acc.add(x) } }
    static prod(sf) { sf.reduce(Mpf.one) { |acc, x| acc.mul(x) } }
    static mean(sf) { sum(sf).div(sf.count) }

    // Min/max of a sequence of Mpf objects.
    static min(sf)  { sf.reduce { |acc, x| (x > acc) ? x : acc  } }
    static max(sf)  { sf.reduce { |acc, x| (x < acc) ? x : acc  } }
}

Source code (C)

/* gcc -O3 wren-gmp.c -o wren-gmp -lmpfr -lgmp -lwren -lm  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
#include <mpfr.h>
#include "wren.h"

/* Mpz functions */

void Mpz_allocate(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(mpz_t));
    int slots = wrenGetSlotCount(vm);
    if (slots == 1) {
        mpz_init(*pz);
    } else if (slots == 2) {
        if (wrenGetSlotType(vm, 1) == WREN_TYPE_NUM) {
            double op = wrenGetSlotDouble(vm, 1);
            mpz_init_set_d(*pz, op);
        } else {
            const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
            mpz_init_set(*pz, *pop);
        }
    } else {
        const char *str = wrenGetSlotString(vm, 1);
        int base = (int)wrenGetSlotDouble(vm, 2);
        mpz_init_set_str(*pz, str, base);
    }
}

void Mpz_finalize(void* data) {
    mpz_t *pz = (mpz_t*)data;
    mpz_clear(*pz);
}

void Mpz_swap(WrenVM* vm) {
    mpz_t *pop1 = (mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_t *pop2 = (mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_swap(*pop1, *pop2);
}

void Mpz_frexp(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    signed long int exp = 0;
    double ret = mpz_get_d_2exp(&exp, *pop);
    wrenEnsureSlots(vm, 3);
    wrenSetSlotNewList(vm, 0);
    wrenSetSlotDouble(vm, 1, ret);
    wrenSetSlotDouble(vm, 2,(double)exp);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
}

void Mpz_cdivRem(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pd = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_t q, r;
    mpz_init(q);
    mpz_init(r);
    mpz_cdiv_qr(q, r, *pn, *pd);
    wrenEnsureSlots(vm, 3);
    mpz_t *pq = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *pr = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*pq, q);
    mpz_set(*pr, r);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(q);
    mpz_clear(r);
}

void Mpz_cdivRemUi(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int d = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_t q, r;
    mpz_init(q);
    mpz_init(r);
    mpz_cdiv_qr_ui(q, r, *pn, d);
    wrenEnsureSlots(vm, 3);
    mpz_t *pq = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *pr = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*pq, q);
    mpz_set(*pr, r);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(q);
    mpz_clear(r);
}

void Mpz_fdivRem(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pd = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_t q, r;
    mpz_init(q);
    mpz_init(r);
    mpz_fdiv_qr(q, r, *pn, *pd);
    wrenEnsureSlots(vm, 3);
    mpz_t *pq = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *pr = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*pq, q);
    mpz_set(*pr, r);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(q);
    mpz_clear(r);
}

void Mpz_fdivRemUi(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int d = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_t q, r;
    mpz_init(q);
    mpz_init(r);
    mpz_fdiv_qr_ui(q, r, *pn, d);
    wrenEnsureSlots(vm, 3);
    mpz_t *pq = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *pr = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*pq, q);
    mpz_set(*pr, r);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(q);
    mpz_clear(r);
}

void Mpz_tdivRem(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pd = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_t q, r;
    mpz_init(q);
    mpz_init(r);
    mpz_tdiv_qr(q, r, *pn, *pd);
    wrenEnsureSlots(vm, 3);
    mpz_t *pq = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *pr = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*pq, q);
    mpz_set(*pr, r);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(q);
    mpz_clear(r);
}

void Mpz_tdivRemUi(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int d = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_t q, r;
    mpz_init(q);
    mpz_init(r);
    mpz_tdiv_qr_ui(q, r, *pn, d);
    wrenEnsureSlots(vm, 3);
    mpz_t *pq = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *pr = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*pq, q);
    mpz_set(*pr, r);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(q);
    mpz_clear(r);
}

void Mpz_rootRem(WrenVM* vm) {
    const mpz_t *pu = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_t root, rem;
    mpz_init(root);
    mpz_init(rem);
    mpz_rootrem(root, rem, *pu, n);
    wrenEnsureSlots(vm, 3);
    mpz_t *proot = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *prem  = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*proot, root);
    mpz_set(*prem, rem);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(root);
    mpz_clear(rem);
}

void Mpz_sqrtRem(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_t rop1, rop2;
    mpz_init(rop1);
    mpz_init(rop2);
    mpz_sqrtrem(rop1, rop2, *pop);
    wrenEnsureSlots(vm, 3);
    mpz_t *prop1 = (mpz_t*)wrenSetSlotNewForeign(vm, 1, 0, sizeof(mpz_t));
    mpz_t *prop2 = (mpz_t*)wrenSetSlotNewForeign(vm, 2, 0, sizeof(mpz_t));
    mpz_set(*prop1, rop1);
    mpz_set(*prop2, rop2);
    wrenSetSlotNewList(vm, 0);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    mpz_clear(rop1);
    mpz_clear(rop2);
}

void Mpz_jacobi(WrenVM* vm) {
    const mpz_t *pa = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pb = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    int jac = mpz_jacobi(*pa, *pb);
    wrenSetSlotDouble(vm, 0, (double)jac);
}

void Mpz_legendre(WrenVM* vm) {
    const mpz_t *pa = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pp = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    int leg = mpz_legendre(*pa, *pp);
    wrenSetSlotDouble(vm, 0, (double)leg);
}

void Mpz_kronecker(WrenVM* vm) {
    const mpz_t *pa = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pb = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    int kron = mpz_kronecker(*pa, *pb);
    wrenSetSlotDouble(vm, 0, (double)kron);
}

void Mpz_hamDist(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mp_bitcnt_t hd = mpz_hamdist(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)hd);
}

void Mpz_setMpz(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_set(*pz, *pop);
}

void Mpz_setMpq(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mpz_set_q(*pz, *pop);
}

void Mpz_setMpf(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpz_set_f(*pz, *pop);
}

void Mpz_setDbl(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    double op = wrenGetSlotDouble(vm, 1);
    mpz_set_d(*pz, op);
}

void Mpz_setUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpz_set_ui(*pz, op);
}

void Mpz_setSi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    signed long int op = (signed long int)wrenGetSlotDouble(vm, 1);
    mpz_set_si(*pz, op);
}

void Mpz_setStr(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const char *str = wrenGetSlotString(vm, 1);
    int base = (int)wrenGetSlotDouble(vm, 2);
    mpz_set_str(*pz, str, base);
}

void Mpz_setBits(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t n = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mpz_realloc2(*pz, n);
}

void Mpz_toNum(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);    
    wrenSetSlotDouble(vm, 0, mpz_get_d(*pop));
}

void Mpz_toString(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int base = (int)wrenGetSlotDouble(vm, 1);
    char *ret = mpz_get_str(NULL, base, *pop);
    wrenSetSlotString(vm, 0, ret);
    free(ret);
}

void Mpz_add(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_add(*pz, *pop1, *pop2);
}

void Mpz_addUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_add_ui(*pz, *pop1, op2);
}

void Mpz_sub(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_sub(*pz, *pop1, *pop2);
}

void Mpz_subUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_sub_ui(*pz, *pop1, op2);
}

void Mpz_uiSub(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op1 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_ui_sub(*pz, op1, *pop2);
}

void Mpz_mul(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_mul(*pz, *pop1, *pop2);
}

void Mpz_mulUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_mul_ui(*pz, *pop1, op2);
}

void Mpz_mulSi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    long int op2 = (long int)wrenGetSlotDouble(vm, 2);
    mpz_mul_si(*pz, *pop1, op2);
}

void Mpz_addMul(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_addmul(*pz, *pop1, *pop2);
}

void Mpz_addMulUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_addmul_ui(*pz, *pop1, op2);
}

void Mpz_subMul(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_submul(*pz, *pop1, *pop2);
}

void Mpz_subMulUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_submul_ui(*pz, *pop1, op2);
}

void Mpz_cdiv(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_cdiv_q(*pz, *pop1, *pop2);
}

void Mpz_cdivUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_cdiv_q_ui(*pz, *pop1, op2);
}

void Mpz_fdiv(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_fdiv_q(*pz, *pop1, *pop2);
}

void Mpz_fdivUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_fdiv_q_ui(*pz, *pop1, op2);
}

void Mpz_tdiv(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_tdiv_q(*pz, *pop1, *pop2);
}

void Mpz_tdivUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_tdiv_q_ui(*pz, *pop1, op2);
}

void Mpz_crem(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_cdiv_r(*pz, *pop1, *pop2);
}

void Mpz_cremUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_cdiv_r_ui(*pz, *pop1, op2);
}

void Mpz_frem(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_fdiv_r(*pz, *pop1, *pop2);
}

void Mpz_fremUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_fdiv_r_ui(*pz, *pop1, op2);
}

void Mpz_trem(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_tdiv_r(*pz, *pop1, *pop2);
}

void Mpz_tremUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_tdiv_r_ui(*pz, *pop1, op2);
}

void Mpz_mod(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_mod(*pz, *pop1, *pop2);
}

void Mpz_modUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_mod_ui(*pz, *pop1, op2);
}

void Mpz_divExact(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_divexact(*pz, *pop1, *pop2);
}

void Mpz_divExactUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_divexact_ui(*pz, *pop1, op2);
}

void Mpz_neg(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_neg(*pz, *pop);
}

void Mpz_abs(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_abs(*pz, *pop);
}

void Mpz_pow(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pbase = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int exp = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_pow_ui(*pz, *pbase, exp);
}

void Mpz_uiPow(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int base = (unsigned long int)wrenGetSlotDouble(vm, 1);
    unsigned long int exp  = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_ui_pow_ui(*pz, base, exp);
}

void Mpz_modPow(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pbase = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pexp  = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    const mpz_t *pmod =  (const mpz_t*)wrenGetSlotForeign(vm, 3);
    mpz_powm(*pz, *pbase, *pexp, *pmod);
}

void Mpz_modPowUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pbase = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int exp = (unsigned long int)wrenGetSlotDouble(vm, 2);
    const mpz_t *pmod = (const mpz_t*)wrenGetSlotForeign(vm, 3);
    mpz_powm_ui(*pz, *pbase, exp, *pmod);
}

void Mpz_root(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_root(*pz, *pop, n);
}

void Mpz_sqrt(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_sqrt(*pz, *pop);
}

void Mpz_lsh(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t op2 = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    mpz_mul_2exp(*pz, *pop1, op2);
}

void Mpz_rsh(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t op2 = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    mpz_fdiv_q_2exp(*pz, *pop1, op2);
}

void Mpz_com(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_com(*pz, *pop);
}

void Mpz_and(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_and(*pz, *pop1, *pop2);
}

void Mpz_ior(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_ior(*pz, *pop1, *pop2);
}

void Mpz_xor(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_xor(*pz, *pop1, *pop2);
}

void Mpz_gcd(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_gcd(*pz, *pop1, *pop2);
}

void Mpz_gcdUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_gcd_ui(*pz, *pop1, op2);
}

void Mpz_lcm(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_lcm(*pz, *pop1, *pop2);
}

void Mpz_lcmUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_lcm_ui(*pz, *pop1, op2);
}

void Mpz_modInv(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mpz_invert(*pz, *pop1, *pop2);
}

void Mpz_nextPrime(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_nextprime(*pz, *pop);
}

void Mpz_remove(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    const mpz_t *pf  = (const mpz_t*)wrenGetSlotForeign(vm, 2);
    mp_bitcnt_t n = mpz_remove(*pz, *pop, *pf);
    wrenSetSlotDouble(vm, 0, (double)n);
}

void Mpz_cmpMpz(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpz_cmp(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_cmpDbl(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    double op2 = wrenGetSlotDouble(vm, 1);
    int ret = mpz_cmp_d(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_cmpUi(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    int ret = mpz_cmp_ui(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_cmpSi(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    signed long int op2 = (signed long int)wrenGetSlotDouble(vm, 1);
    int ret = mpz_cmp_si(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_cmpAbsMpz(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpz_cmpabs(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_cmpAbsDbl(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    double op2 = wrenGetSlotDouble(vm, 1);
    int ret = mpz_cmpabs_d(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_cmpAbsUi(WrenVM* vm) {
    const mpz_t *pop1 = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    int ret = mpz_cmpabs_ui(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_isOdd(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpz_odd_p(*pop);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isEven(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpz_even_p(*pop);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isDivisible(WrenVM* vm) {
    mpz_t *pn = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mpz_t *pd = (mpz_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpz_divisible_p(*pn, *pd);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isDivisibleUi(WrenVM* vm) {
    mpz_t *pn = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int d = (unsigned long int)wrenGetSlotDouble(vm, 1);
    int ret = mpz_divisible_ui_p(*pn, d);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isDivisible2(WrenVM* vm) {
    mpz_t *pn = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t b = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    int ret = mpz_divisible_2exp_p(*pn, b);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isCongruent(WrenVM* vm) {
    mpz_t *pn = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mpz_t *pc = (mpz_t*)wrenGetSlotForeign(vm, 1);
    mpz_t *pd = (mpz_t*)wrenGetSlotForeign(vm, 2);
    int ret = mpz_congruent_p(*pn, *pc, *pd);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isCongruentUi(WrenVM* vm) {
    mpz_t *pn = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int c = (unsigned long int)wrenGetSlotDouble(vm, 1);
    unsigned long int d = (unsigned long int)wrenGetSlotDouble(vm, 2);
    int ret = mpz_congruent_ui_p(*pn, c, d);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isCongruent2(WrenVM* vm) {
    mpz_t *pn = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mpz_t *pc = (mpz_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t b = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    int ret = mpz_congruent_2exp_p(*pn, *pc, b);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isPower(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpz_perfect_power_p(*pop);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_isSquare(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpz_perfect_square_p(*pop);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpz_probPrime(WrenVM* vm) {
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int reps = (int)wrenGetSlotDouble(vm, 1);
    int ret = mpz_probab_prime_p(*pn, reps);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_sign(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpz_sgn(*pop);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_factorial(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpz_fac_ui(*pz, n);
}

void Mpz_factorial2(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpz_2fac_ui(*pz, n);
}

void Mpz_mfactorial(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    unsigned long int m = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_mfac_uiui(*pz, n, m);
}

void Mpz_primorial(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpz_primorial_ui(*pz, n);
}

void Mpz_binomial(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pn = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int k = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_bin_ui(*pz, *pn, k);
}

void Mpz_binomialUi(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    unsigned long int k = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpz_bin_uiui(*pz, n, k);
}

void Mpz_fibonacci(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpz_fib_ui(*pz, n);
}

void Mpz_lucas(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpz_lucnum_ui(*pz, n);
}

void Mpz_popCount(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t ret = mpz_popcount(*pop);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_scan0(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t startingBit = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mp_bitcnt_t ret = mpz_scan0(*pop, startingBit);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_scan1(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t startingBit = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mp_bitcnt_t ret = mpz_scan1(*pop, startingBit);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_setBit(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t bitIndex = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mpz_setbit(*pz, bitIndex);
}

void Mpz_clrBit(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t bitIndex = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mpz_clrbit(*pz, bitIndex);
}

void Mpz_comBit(WrenVM* vm) {
    mpz_t *pz = (mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t bitIndex = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mpz_combit(*pz, bitIndex);
}

void Mpz_tstBit(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t bitIndex = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    int ret = mpz_tstbit(*pop, bitIndex);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_sizeInBase(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int base = (int)wrenGetSlotDouble(vm, 1);
    size_t ret = mpz_sizeinbase(*pop, base);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpz_digitsInBase(WrenVM* vm) {
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 0);
    int base = (int)wrenGetSlotDouble(vm, 1);
    char *ret = mpz_get_str(NULL, base, *pop);
    size_t digits = strlen(ret);
    if (ret[0] == '-') --digits;
    wrenSetSlotDouble(vm, 0, (double)digits);
    free(ret);
}

/* Mpq functions */

void Mpq_allocate(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(mpq_t));
    mpq_init(*pq);
}

void Mpq_finalize(void* data) {
    mpq_t *pq = (mpq_t*)data;
    mpq_clear(*pq);
}

void Mpq_swap(WrenVM* vm) {
    mpq_t *pop1 = (mpq_t*)wrenGetSlotForeign(vm, 1);
    mpq_t *pop2 = (mpq_t*)wrenGetSlotForeign(vm, 2);
    mpq_swap(*pop1, *pop2);
}

void Mpq_setMpq(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mpq_set(*pq, *pop);
}

void Mpq_setMpz(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpq_set_z(*pq, *pop);
}

void Mpq_setMpf(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpq_set_f(*pq, *pop);
}

void Mpq_setDbl(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    double op = wrenGetSlotDouble(vm, 1);
    mpq_set_d(*pq, op);
}

void Mpq_setUi(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op1 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpq_set_ui(*pq, op1, op2);
    mpq_canonicalize(*pq);
}

void Mpq_setSi(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    signed long int op1 = (signed long int)wrenGetSlotDouble(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpq_set_si(*pq, op1, op2);
    mpq_canonicalize(*pq);
}

void Mpq_setStr(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const char *str = wrenGetSlotString(vm, 1);
    int base = (int)wrenGetSlotDouble(vm, 2);
    mpq_set_str(*pq, str, base);
    mpq_canonicalize(*pq);
}

void Mpq_num(WrenVM* vm) {
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    wrenEnsureSlots(vm, 2);
    wrenGetVariable(vm, "./gmp", "Mpz", 1);
    mpz_t *pnum = (mpz_t*)wrenSetSlotNewForeign(vm, 0, 1, sizeof(mpz_t));
    mpq_get_num(*pnum, *pop);
}

void Mpq_den(WrenVM* vm) {
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    wrenEnsureSlots(vm, 2);
    wrenGetVariable(vm, "./gmp", "Mpz", 1);
    mpz_t *pden = (mpz_t*)wrenSetSlotNewForeign(vm, 0, 1, sizeof(mpz_t));
    mpq_get_den(*pden, *pop);
}

void Mpq_setNum(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pnumerator = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpq_set_num(*pq, *pnumerator);
    mpq_canonicalize(*pq);
}

void Mpq_setDen(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pdenominator = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpq_set_den(*pq, *pdenominator);
    mpq_canonicalize(*pq);
}

void Mpq_toNum(WrenVM* vm) {
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 0);    
    wrenSetSlotDouble(vm, 0, mpq_get_d(*pop));
}

void Mpq_toString(WrenVM* vm) {
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    int base = (int)wrenGetSlotDouble(vm, 1);
    char *ret = mpq_get_str(NULL, base, *pop);
    wrenSetSlotString(vm, 0, ret);
    free(ret);
}

void Mpq_add(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    const mpq_t *pop2 = (const mpq_t*)wrenGetSlotForeign(vm, 2);
    mpq_add(*pq, *pop1, *pop2);
}

void Mpq_sub(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    const mpq_t *pop2 = (const mpq_t*)wrenGetSlotForeign(vm, 2);
    mpq_sub(*pq, *pop1, *pop2);
}

void Mpq_mul(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    const mpq_t *pop2 = (const mpq_t*)wrenGetSlotForeign(vm, 2);
    mpq_mul(*pq, *pop1, *pop2);
}

void Mpq_mul2(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t op2 = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    mpq_mul_2exp(*pq, *pop1, op2);
}

void Mpq_div(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    const mpq_t *pop2 = (const mpq_t*)wrenGetSlotForeign(vm, 2);
    mpq_div(*pq, *pop1, *pop2);
}

void Mpq_div2(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t op2 = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    mpq_div_2exp(*pq, *pop1, op2);
}

void Mpq_neg(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mpq_neg(*pq, *pop);
}

void Mpq_abs(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mpq_abs(*pq, *pop);
}

void Mpq_inv(WrenVM* vm) {
    mpq_t *pq = (mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mpq_inv(*pq, *pop);
}

void Mpq_cmpMpq(WrenVM* vm) {
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop2 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpq_cmp(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpq_cmpMpz(WrenVM* vm) {
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpq_cmp_z(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpq_cmpUi(WrenVM* vm) {
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int num2 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    unsigned long int den2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    int ret = mpq_cmp_ui(*pop1, num2, den2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpq_cmpSi(WrenVM* vm) {
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    long int num2 = (long int)wrenGetSlotDouble(vm, 1);
    unsigned long int den2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    int ret = mpq_cmp_si(*pop1, num2, den2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpq_equals(WrenVM* vm) {
    const mpq_t *pop1 = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop2 = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpq_equal(*pop1, *pop2);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpq_sign(WrenVM* vm) {
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpq_sgn(*pop);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

/* Mpf functions */

void Mpf_allocate(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(mpf_t));
    int slots = wrenGetSlotCount(vm);
    if (slots == 1) {
        mpf_init(*pf);
    } else {
        mp_bitcnt_t prec = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
        mpf_init2(*pf, prec);
    }
}

void Mpf_finalize(void* data) {
    mpf_t *pf = (mpf_t*)data;
    mpf_clear(*pf);
}

void Mpf_swap(WrenVM* vm) {
    mpf_t *pop1 = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_t *pop2 = (mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_swap(*pop1, *pop2);
}

void Mpf_frexp(WrenVM* vm) {
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    signed long int exp = 0;
    double ret = mpf_get_d_2exp(&exp, *pop);
    wrenEnsureSlots(vm, 3);
    wrenSetSlotNewList(vm, 0);
    wrenSetSlotDouble(vm, 1, ret);
    wrenSetSlotDouble(vm, 2,(double)exp);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
}

void Mpf_getStr(WrenVM* vm) {
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    int base = (int)wrenGetSlotDouble(vm, 2);
    size_t nDigits = (size_t)wrenGetSlotDouble(vm, 3);
    mp_exp_t exp = 0;
    char *ret = mpf_get_str(NULL, &exp, base, nDigits, *pop);
    wrenEnsureSlots(vm, 3);
    wrenSetSlotNewList(vm, 0);
    wrenSetSlotString(vm, 1, ret);
    wrenSetSlotDouble(vm, 2,(double)exp);
    wrenInsertInList(vm, 0, 0, 1);
    wrenInsertInList(vm, 0, 1, 2);
    free(ret);
}

void Mpf_defaultPrec(WrenVM* vm) {
    mp_bitcnt_t prec = mpf_get_default_prec();
    wrenSetSlotDouble(vm, 0, (double)prec);
}

void Mpf_setDefaultPrec(WrenVM* vm) {
    mp_bitcnt_t prec = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mpf_set_default_prec(prec);
}

void Mpf_setMpf(WrenVM* vm) {
    mpf_t *pf  = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_set(*pf, *pop);
}

void Mpf_setMpq(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpq_t *pop = (const mpq_t*)wrenGetSlotForeign(vm, 1);
    mpf_set_q(*pf, *pop);
}

void Mpf_setMpz(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    mpf_set_z(*pf, *pop);
}

void Mpf_setDbl(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    double op = wrenGetSlotDouble(vm, 1);
    mpf_set_d(*pf, op);
}

void Mpf_setUi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpf_set_ui(*pf, op);
}

void Mpf_setSi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    signed long int op = (signed long int)wrenGetSlotDouble(vm, 1);
    mpf_set_si(*pf, op);
}

void Mpf_setStr(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const char *str = wrenGetSlotString(vm, 1);
    int base = (int)wrenGetSlotDouble(vm, 2);
    mpf_set_str(*pf, str, base);
}

void Mpf_prec(WrenVM* vm) {
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t ret = mpf_get_prec(*pop);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpf_setPrec(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mp_bitcnt_t prec = (mp_bitcnt_t)wrenGetSlotDouble(vm, 1);
    mpf_set_prec(*pf, prec);
}

void Mpf_toNum(WrenVM* vm) {
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 0);    
    wrenSetSlotDouble(vm, 0, mpf_get_d(*pop));
}

void Mpf_toSint(WrenVM* vm) {
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 0);    
    wrenSetSlotDouble(vm, 0, (double)mpf_get_si(*pop));
}

void Mpf_toUint(WrenVM* vm) {
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    wrenSetSlotDouble(vm, 0, (double)mpf_get_ui(*pop));
}

void Mpf_add(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_add(*pf, *pop1, *pop2);
}

void Mpf_addUi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpf_add_ui(*pf, *pop1, op2);
}

void Mpf_sub(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_sub(*pf, *pop1, *pop2);
}

void Mpf_subUi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpf_sub_ui(*pf, *pop1, op2);
}

void Mpf_uiSub(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op1 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_ui_sub(*pf, op1, *pop2);
}

void Mpf_mul(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_mul(*pf, *pop1, *pop2);
}

void Mpf_mulUi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpf_mul_ui(*pf, *pop1, op2);
}

void Mpf_mul2(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t op2 = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    mpf_mul_2exp(*pf, *pop1, op2);
}

void Mpf_div(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_div(*pf, *pop1, *pop2);
}

void Mpf_divUi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpf_div_ui(*pf, *pop1, op2);
}

void Mpf_uiDiv(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op1 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_ui_div(*pf, op1, *pop2);
}

void Mpf_div2(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mp_bitcnt_t op2 = (mp_bitcnt_t)wrenGetSlotDouble(vm, 2);
    mpf_div_2exp(*pf, *pop1, op2);
}

void Mpf_neg(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_neg(*pf, *pop);
}

void Mpf_abs(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_abs(*pf, *pop);
}

void Mpf_relDiff(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 2);
    mpf_reldiff(*pf, *pop1, *pop2);
}

void Mpf_pow(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pbase = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int exp = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpf_pow_ui(*pf, *pbase, exp);
}

void Mpf_sqrt(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_sqrt(*pf, *pop);
}

void Mpf_sqrtUi(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op = (unsigned long int)wrenGetSlotDouble(vm, 1);
    mpf_sqrt_ui(*pf, op);
}

void Mpf_ceil(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_ceil(*pf, *pop);
}

void Mpf_floor(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_floor(*pf, *pop);
}

void Mpf_trunc(WrenVM* vm) {
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_trunc(*pf, *pop);
}

void Mpf_cmpMpf(WrenVM* vm) {
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpf_t *pop2 = (const mpf_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpf_cmp(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpf_cmpMpz(WrenVM* vm) {
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    const mpz_t *pop2 = (const mpz_t*)wrenGetSlotForeign(vm, 1);
    int ret = mpf_cmp_z(*pop1, *pop2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpf_cmpDbl(WrenVM* vm) {
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    double op2 = wrenGetSlotDouble(vm, 1);
    int ret = mpf_cmp_d(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpf_cmpUi(WrenVM* vm) {
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    unsigned long int op2 = (unsigned long int)wrenGetSlotDouble(vm, 1);
    int ret = mpf_cmp_ui(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpf_cmpSi(WrenVM* vm) {
    const mpf_t *pop1 = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    signed long int op2 = (signed long int)wrenGetSlotDouble(vm, 1);
    int ret = mpf_cmp_si(*pop1, op2);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

void Mpf_isInteger(WrenVM* vm) {
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpf_integer_p(*pop);
    wrenSetSlotBool(vm, 0, (bool)ret);
}

void Mpf_sign(WrenVM* vm) {
    const mpf_t *pop = (const mpf_t*)wrenGetSlotForeign(vm, 0);
    int ret = mpf_sgn(*pop);
    wrenSetSlotDouble(vm, 0, (double)ret);
}

/* Mpf functions which require MPFR. */

void Mpf_powz(WrenVM* vm) {
    mpfr_t op1, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop1 = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpz_t *pop2 = (mpz_t*)wrenGetSlotForeign(vm, 2);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop1);
    mpfr_init2(op1, prec2);
    mpfr_set_f(op1, *pop1, MPFR_RNDN);
    mpfr_pow_z(res, op1, *pop2, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op1);
    mpfr_clear(res);
}

void Mpf_powf(WrenVM* vm) {
    mpfr_t op1, op2, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop1 = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_t *pop2 = (mpf_t*)wrenGetSlotForeign(vm, 2);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop1);
    mpfr_init2(op1, prec2);
    mpfr_set_f(op1, *pop1, MPFR_RNDN);
    mpfr_prec_t prec3 = (mpfr_prec_t)mpf_get_prec(*pop2);
    mpfr_init2(op2, prec3);
    mpfr_set_f(op2, *pop2, MPFR_RNDN);
    mpfr_pow(res, op1, op2, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op1);
    mpfr_clear(op2);
    mpfr_clear(res);
}

void Mpf_root(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    unsigned long int n = (unsigned long int)wrenGetSlotDouble(vm, 2);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_rootn_ui(res, op, n, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_cbrt(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_cbrt(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_hypot(WrenVM* vm) {
    mpfr_t rx, ry, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *px = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_t *py = (mpf_t*)wrenGetSlotForeign(vm, 2);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*px);
    mpfr_init2(rx, prec2);
    mpfr_set_f(rx, *px, MPFR_RNDN);
    mpfr_prec_t prec3 = (mpfr_prec_t)mpf_get_prec(*py);
    mpfr_init2(ry, prec3);
    mpfr_set_f(ry, *py, MPFR_RNDN);
    mpfr_hypot(res, rx, ry, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(rx);
    mpfr_clear(ry);
    mpfr_clear(res);
}

void Mpf_log(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_log(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_log2(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_log2(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_log10(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_log10(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_exp(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_exp(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_cos(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_cos(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_sin(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_sin(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_tan(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_tan(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_acos(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_acos(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_asin(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_asin(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_atan(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_atan(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_atan2(WrenVM* vm) {
    mpfr_t ry, rx, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *py = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_t *px = (mpf_t*)wrenGetSlotForeign(vm, 2);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*py);
    mpfr_init2(ry, prec2);
    mpfr_set_f(ry, *py, MPFR_RNDN);
    mpfr_prec_t prec3 = (mpfr_prec_t)mpf_get_prec(*px);
    mpfr_init2(rx, prec3);
    mpfr_set_f(rx, *px, MPFR_RNDN);
    mpfr_atan2(res, ry, rx, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(ry);
    mpfr_clear(rx);
    mpfr_clear(res);
}

void Mpf_cosh(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_cosh(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_sinh(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_sinh(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_tanh(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_tanh(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_gamma(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_gamma(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_gammaInc(WrenVM* vm) {
    mpfr_t op, op2, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpf_t *pop2 = (mpf_t*)wrenGetSlotForeign(vm, 2);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_prec_t prec3 = (mpfr_prec_t)mpf_get_prec(*pop2);
    mpfr_init2(op2, prec3);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_set_f(op2, *pop2, MPFR_RNDN);
    mpfr_gamma_inc(res, op, op2, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(op2);
    mpfr_clear(res);
}

void Mpf_lgamma(WrenVM* vm) {
    mpfr_t op, res;
    int signnp = 0;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_lgamma(res, &signnp, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_digamma(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_digamma(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_zeta(WrenVM* vm) {
    mpfr_t op, res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    mpf_t *pop = (mpf_t*)wrenGetSlotForeign(vm, 1);
    mpfr_prec_t prec1 = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec1);
    mpfr_prec_t prec2 = (mpfr_prec_t)mpf_get_prec(*pop);
    mpfr_init2(op, prec2);
    mpfr_set_f(op, *pop, MPFR_RNDN);
    mpfr_zeta(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(op);
    mpfr_clear(res);
}

void Mpf_zetaUi(WrenVM* vm) {
    mpfr_t res;
    mpf_t *pf = (mpf_t*)wrenGetSlotForeign(vm, 0);
    unsigned long op = (unsigned long)wrenGetSlotDouble(vm, 1);
    mpfr_prec_t prec = (mpfr_prec_t)mpf_get_prec(*pf);
    mpfr_init2(res, prec);
    mpfr_zeta_ui(res, op, MPFR_RNDN);
    mpfr_get_f(*pf, res, MPFR_RNDN);
    mpfr_clear(res);
}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {
    WrenForeignClassMethods methods;
    methods.allocate = NULL;
    methods.finalize = NULL;
    if (strcmp(module, "./gmp") == 0) {
        if (strcmp(className, "Mpz") == 0) {
            methods.allocate = Mpz_allocate;
            methods.finalize = Mpz_finalize;
        } else if (strcmp(className, "Mpq") == 0) {
            methods.allocate = Mpq_allocate;
            methods.finalize = Mpq_finalize;
        } else if (strcmp(className, "Mpf") == 0) {
            methods.allocate = Mpf_allocate;
            methods.finalize = Mpf_finalize;
        }
    }
    return methods;
}

WrenForeignMethodFn bindForeignMethod(
    WrenVM* vm,
    const char* module,
    const char* className,
    bool isStatic,
    const char* signature) {
    if (strcmp(module, "./gmp") == 0) {
        if (strcmp(className, "Mpz") == 0) {
            if(isStatic && strcmp(signature, "swap(_,_)") == 0) return Mpz_swap;
            if(isStatic && strcmp(signature, "frexp(_)") == 0) return Mpz_frexp;
            if(isStatic && strcmp(signature, "cdivRem(_,_)") == 0) return Mpz_cdivRem;
            if(isStatic && strcmp(signature, "cdivRemUi(_,_)") == 0) return Mpz_cdivRemUi;
            if(isStatic && strcmp(signature, "fdivRem(_,_)") == 0) return Mpz_fdivRem;
            if(isStatic && strcmp(signature, "fdivRemUi(_,_)") == 0) return Mpz_fdivRemUi;
            if(isStatic && strcmp(signature, "tdivRem(_,_)") == 0) return Mpz_tdivRem;
            if(isStatic && strcmp(signature, "tdivRemUi(_,_)") == 0) return Mpz_tdivRemUi;
            if(isStatic && strcmp(signature, "rootRem(_,_)") == 0) return Mpz_rootRem;
            if(isStatic && strcmp(signature, "sqrtRem(_,_)") == 0) return Mpz_sqrtRem;
            if(isStatic && strcmp(signature, "jacobi(_,_)") == 0) return Mpz_jacobi;
            if(isStatic && strcmp(signature, "legendre(_,_)") == 0) return Mpz_legendre;
            if(isStatic && strcmp(signature, "kronecker(_,_)") == 0) return Mpz_kronecker;
            if(isStatic && strcmp(signature, "hamDist(_,_)") == 0) return Mpz_hamDist;

            if(!isStatic && strcmp(signature, "setMpz(_)") == 0) return Mpz_setMpz;
            if(!isStatic && strcmp(signature, "setMpq(_)") == 0) return Mpz_setMpq;
            if(!isStatic && strcmp(signature, "setMpf(_)") == 0) return Mpz_setMpf;
            if(!isStatic && strcmp(signature, "setDbl(_)") == 0) return Mpz_setDbl;
            if(!isStatic && strcmp(signature, "setUi(_)") == 0) return Mpz_setUi;
            if(!isStatic && strcmp(signature, "setSi(_)") == 0) return Mpz_setSi;
            if(!isStatic && strcmp(signature, "setStr(_,_)") == 0) return Mpz_setStr;
            if(!isStatic && strcmp(signature, "setBits(_)") == 0) return Mpz_setBits;
            if(!isStatic && strcmp(signature, "toNum") == 0) return Mpz_toNum;
            if(!isStatic && strcmp(signature, "toString(_)") == 0) return Mpz_toString;
            if(!isStatic && strcmp(signature, "add(_,_)") == 0) return Mpz_add;
            if(!isStatic && strcmp(signature, "addUi(_,_)") == 0) return Mpz_addUi;
            if(!isStatic && strcmp(signature, "sub(_,_)") == 0) return Mpz_sub;
            if(!isStatic && strcmp(signature, "subUi(_,_)") == 0) return Mpz_subUi;
            if(!isStatic && strcmp(signature, "uiSub(_,_)") == 0) return Mpz_uiSub;
            if(!isStatic && strcmp(signature, "mul(_,_)") == 0) return Mpz_mul;
            if(!isStatic && strcmp(signature, "mulUi(_,_)") == 0) return Mpz_mulUi;
            if(!isStatic && strcmp(signature, "mulSi(_,_)") == 0) return Mpz_mulSi;
            if(!isStatic && strcmp(signature, "addMul(_,_)") == 0) return Mpz_addMul;
            if(!isStatic && strcmp(signature, "addMulUi(_,_)") == 0) return Mpz_addMulUi;
            if(!isStatic && strcmp(signature, "subMul(_,_)") == 0) return Mpz_subMul;
            if(!isStatic && strcmp(signature, "subMulUi(_,_)") == 0) return Mpz_subMulUi;
            if(!isStatic && strcmp(signature, "cdiv(_,_)") == 0) return Mpz_cdiv;
            if(!isStatic && strcmp(signature, "cdivUi(_,_)") == 0) return Mpz_cdivUi;
            if(!isStatic && strcmp(signature, "fdiv(_,_)") == 0) return Mpz_fdiv;
            if(!isStatic && strcmp(signature, "fdivUi(_,_)") == 0) return Mpz_fdivUi;
            if(!isStatic && strcmp(signature, "tdiv(_,_)") == 0) return Mpz_tdiv;
            if(!isStatic && strcmp(signature, "tdivUi(_,_)") == 0) return Mpz_tdivUi;
            if(!isStatic && strcmp(signature, "crem(_,_)") == 0) return Mpz_crem;
            if(!isStatic && strcmp(signature, "cremUi(_,_)") == 0) return Mpz_cremUi;
            if(!isStatic && strcmp(signature, "frem(_,_)") == 0) return Mpz_frem;
            if(!isStatic && strcmp(signature, "fremUi(_,_)") == 0) return Mpz_fremUi;
            if(!isStatic && strcmp(signature, "trem(_,_)") == 0) return Mpz_trem;
            if(!isStatic && strcmp(signature, "tremUi(_,_)") == 0) return Mpz_tremUi;
            if(!isStatic && strcmp(signature, "mod(_,_)") == 0) return Mpz_mod;
            if(!isStatic && strcmp(signature, "modUi(_,_)") == 0) return Mpz_modUi;
            if(!isStatic && strcmp(signature, "divExact(_,_)") == 0) return Mpz_divExact;
            if(!isStatic && strcmp(signature, "divExactUi(_,_)") == 0) return Mpz_divExactUi;
            if(!isStatic && strcmp(signature, "neg(_)") == 0) return Mpz_neg;
            if(!isStatic && strcmp(signature, "abs(_)") == 0) return Mpz_abs;
            if(!isStatic && strcmp(signature, "pow(_,_)") == 0) return Mpz_pow;
            if(!isStatic && strcmp(signature, "uiPow(_,_)") == 0) return Mpz_uiPow;
            if(!isStatic && strcmp(signature, "modPow(_,_,_)") == 0) return Mpz_modPow;
            if(!isStatic && strcmp(signature, "modPowUi(_,_,_)") == 0) return Mpz_modPowUi;
            if(!isStatic && strcmp(signature, "root(_,_)") == 0) return Mpz_root;
            if(!isStatic && strcmp(signature, "sqrt(_)") == 0) return Mpz_sqrt;
            if(!isStatic && strcmp(signature, "lsh(_,_)") == 0) return Mpz_lsh;
            if(!isStatic && strcmp(signature, "rsh(_,_)") == 0) return Mpz_rsh;
            if(!isStatic && strcmp(signature, "com(_)") == 0) return Mpz_com;
            if(!isStatic && strcmp(signature, "and(_,_)") == 0) return Mpz_and;
            if(!isStatic && strcmp(signature, "ior(_,_)") == 0) return Mpz_ior;
            if(!isStatic && strcmp(signature, "xor(_,_)") == 0) return Mpz_xor;
            if(!isStatic && strcmp(signature, "gcd(_,_)") == 0) return Mpz_gcd;
            if(!isStatic && strcmp(signature, "gcdUi(_,_)") == 0) return Mpz_gcdUi;
            if(!isStatic && strcmp(signature, "lcm(_,_)") == 0) return Mpz_lcm;
            if(!isStatic && strcmp(signature, "lcmUi(_,_)") == 0) return Mpz_lcmUi;
            if(!isStatic && strcmp(signature, "modInv(_,_)") == 0) return Mpz_modInv;
            if(!isStatic && strcmp(signature, "nextPrime(_)") == 0) return Mpz_nextPrime;
            if(!isStatic && strcmp(signature, "remove(_,_)") == 0) return Mpz_remove;
            if(!isStatic && strcmp(signature, "cmpMpz(_)") == 0) return Mpz_cmpMpz;
            if(!isStatic && strcmp(signature, "cmpDbl(_)") == 0) return Mpz_cmpDbl;
            if(!isStatic && strcmp(signature, "cmpUi(_)") == 0) return Mpz_cmpUi;
            if(!isStatic && strcmp(signature, "cmpSi(_)") == 0) return Mpz_cmpSi;
            if(!isStatic && strcmp(signature, "cmpAbsMpz(_)") == 0) return Mpz_cmpAbsMpz;
            if(!isStatic && strcmp(signature, "cmpAbsDbl(_)") == 0) return Mpz_cmpAbsDbl;
            if(!isStatic && strcmp(signature, "cmpAbsUi(_)") == 0) return Mpz_cmpAbsUi;
            if(!isStatic && strcmp(signature, "isOdd") == 0) return Mpz_isOdd;
            if(!isStatic && strcmp(signature, "isEven") == 0) return Mpz_isEven;
            if(!isStatic && strcmp(signature, "isDivisible(_)") == 0) return Mpz_isDivisible;
            if(!isStatic && strcmp(signature, "isDivisibleUi(_)") == 0) return Mpz_isDivisibleUi;
            if(!isStatic && strcmp(signature, "isDivisible2(_)") == 0) return Mpz_isDivisible2;
            if(!isStatic && strcmp(signature, "isCongruent(_,_)") == 0) return Mpz_isCongruent;
            if(!isStatic && strcmp(signature, "isCongruentUi(_,_)") == 0) return Mpz_isCongruentUi;
            if(!isStatic && strcmp(signature, "isCongruent2(_,_)") == 0) return Mpz_isCongruent2;
            if(!isStatic && strcmp(signature, "isPower") == 0) return Mpz_isPower;
            if(!isStatic && strcmp(signature, "isSquare") == 0) return Mpz_isSquare;
            if(!isStatic && strcmp(signature, "probPrime(_)") == 0) return Mpz_probPrime;
            if(!isStatic && strcmp(signature, "sign(_)") == 0) return Mpz_sign;
            if(!isStatic && strcmp(signature, "factorial(_)") == 0) return Mpz_factorial;
            if(!isStatic && strcmp(signature, "factorial2(_)") == 0) return Mpz_factorial2;
            if(!isStatic && strcmp(signature, "mfactorial(_,_)") == 0) return Mpz_mfactorial;
            if(!isStatic && strcmp(signature, "primorial(_)") == 0) return Mpz_primorial;
            if(!isStatic && strcmp(signature, "binomial(_,_)") == 0) return Mpz_binomial;
            if(!isStatic && strcmp(signature, "binomialUi(_,_)") == 0) return Mpz_binomialUi;
            if(!isStatic && strcmp(signature, "fibonacci(_)") == 0) return Mpz_fibonacci;
            if(!isStatic && strcmp(signature, "lucas(_)") == 0) return Mpz_lucas;
            if(!isStatic && strcmp(signature, "popCount") == 0) return Mpz_popCount;
            if(!isStatic && strcmp(signature, "scan0(_)") == 0) return Mpz_scan0;
            if(!isStatic && strcmp(signature, "scan1(_)") == 0) return Mpz_scan1;
            if(!isStatic && strcmp(signature, "setBit(_)") == 0) return Mpz_setBit;
            if(!isStatic && strcmp(signature, "clrBit(_)") == 0) return Mpz_clrBit;
            if(!isStatic && strcmp(signature, "comBit(_)") == 0) return Mpz_comBit;
            if(!isStatic && strcmp(signature, "tstBit(_)") == 0) return Mpz_tstBit;
            if(!isStatic && strcmp(signature, "sizeInBase(_)") == 0) return Mpz_sizeInBase;
            if(!isStatic && strcmp(signature, "digitsInBase(_)") == 0) return Mpz_digitsInBase;
        } else if (strcmp(className, "Mpq") == 0) {
            if(isStatic && strcmp(signature, "swap(_,_)") == 0) return Mpq_swap;

            if(!isStatic && strcmp(signature, "setMpq(_)") == 0) return Mpq_setMpq;
            if(!isStatic && strcmp(signature, "setMpz(_)") == 0) return Mpq_setMpz;
            if(!isStatic && strcmp(signature, "setMpf(_)") == 0) return Mpq_setMpf;
            if(!isStatic && strcmp(signature, "setDbl(_)") == 0) return Mpq_setDbl;
            if(!isStatic && strcmp(signature, "setUi(_,_)") == 0) return Mpq_setUi;
            if(!isStatic && strcmp(signature, "setSi(_,_)") == 0) return Mpq_setSi;
            if(!isStatic && strcmp(signature, "setStr(_,_)") == 0) return Mpq_setStr;
            if(!isStatic && strcmp(signature, "num") == 0) return Mpq_num;
            if(!isStatic && strcmp(signature, "den") == 0) return Mpq_den;
            if(!isStatic && strcmp(signature, "num=(_)") == 0) return Mpq_setNum;
            if(!isStatic && strcmp(signature, "den=(_)") == 0) return Mpq_setDen;
            if(!isStatic && strcmp(signature, "toNum") == 0) return Mpq_toNum;
            if(!isStatic && strcmp(signature, "toString(_)") == 0) return Mpq_toString;
            if(!isStatic && strcmp(signature, "add(_,_)") == 0) return Mpq_add;
            if(!isStatic && strcmp(signature, "sub(_,_)") == 0) return Mpq_sub;
            if(!isStatic && strcmp(signature, "mul(_,_)") == 0) return Mpq_mul;
            if(!isStatic && strcmp(signature, "mul2(_,_)") == 0) return Mpq_mul2;
            if(!isStatic && strcmp(signature, "div(_,_)") == 0) return Mpq_div;
            if(!isStatic && strcmp(signature, "div2(_,_)") == 0) return Mpq_div2;
            if(!isStatic && strcmp(signature, "neg(_)") == 0) return Mpq_neg;
            if(!isStatic && strcmp(signature, "abs(_)") == 0) return Mpq_abs;
            if(!isStatic && strcmp(signature, "inv(_)") == 0) return Mpq_inv;
            if(!isStatic && strcmp(signature, "cmpMpq(_)") == 0) return Mpq_cmpMpq;
            if(!isStatic && strcmp(signature, "cmpMpz(_)") == 0) return Mpq_cmpMpz;
            if(!isStatic && strcmp(signature, "cmpUi(_,_)") == 0) return Mpq_cmpUi;
            if(!isStatic && strcmp(signature, "cmpSi(_,_)") == 0) return Mpq_cmpSi;
            if(!isStatic && strcmp(signature, "==(_)") == 0) return Mpq_equals;
            if(!isStatic && strcmp(signature, "sign(_)") == 0) return Mpq_sign;
        } else if (strcmp(className, "Mpf") == 0) {
            if(isStatic && strcmp(signature, "swap(_,_)") == 0) return Mpf_swap;
            if(isStatic && strcmp(signature, "frexp(_)") == 0) return Mpf_frexp;
            if(isStatic && strcmp(signature, "getStr(_,_,_)") == 0) return Mpf_getStr;
            if(isStatic && strcmp(signature, "defaultPrec") == 0) return Mpf_defaultPrec;
            if(isStatic && strcmp(signature, "defaultPrec=(_)") == 0) return Mpf_setDefaultPrec;

            if(!isStatic && strcmp(signature, "setMpf(_)") == 0) return Mpf_setMpf;
            if(!isStatic && strcmp(signature, "setMpq(_)") == 0) return Mpf_setMpq;
            if(!isStatic && strcmp(signature, "setMpz(_)") == 0) return Mpf_setMpz;
            if(!isStatic && strcmp(signature, "setDbl(_)") == 0) return Mpf_setDbl;
            if(!isStatic && strcmp(signature, "setUi(_)") == 0) return Mpf_setUi;
            if(!isStatic && strcmp(signature, "setSi(_)") == 0) return Mpf_setSi;
            if(!isStatic && strcmp(signature, "setStr(_,_)") == 0) return Mpf_setStr;
            if(!isStatic && strcmp(signature, "prec") == 0) return Mpf_prec;
            if(!isStatic && strcmp(signature, "prec=(_)") == 0) return Mpf_setPrec;
            if(!isStatic && strcmp(signature, "toNum") == 0) return Mpf_toNum;
            if(!isStatic && strcmp(signature, "toSint") == 0) return Mpf_toSint;
            if(!isStatic && strcmp(signature, "toUint") == 0) return Mpf_toUint;
            if(!isStatic && strcmp(signature, "add(_,_)") == 0) return Mpf_add;
            if(!isStatic && strcmp(signature, "addUi(_,_)") == 0) return Mpf_addUi;
            if(!isStatic && strcmp(signature, "sub(_,_)") == 0) return Mpf_sub;
            if(!isStatic && strcmp(signature, "subUi(_,_)") == 0) return Mpf_subUi;
            if(!isStatic && strcmp(signature, "uiSub(_,_)") == 0) return Mpf_uiSub;
            if(!isStatic && strcmp(signature, "mul(_,_)") == 0) return Mpf_mul;
            if(!isStatic && strcmp(signature, "mulUi(_,_)") == 0) return Mpf_mulUi;
            if(!isStatic && strcmp(signature, "mul2(_,_)") == 0) return Mpf_mul2;
            if(!isStatic && strcmp(signature, "div(_,_)") == 0) return Mpf_div;
            if(!isStatic && strcmp(signature, "divUi(_,_)") == 0) return Mpf_divUi;
            if(!isStatic && strcmp(signature, "uiDiv(_,_)") == 0) return Mpf_uiDiv;
            if(!isStatic && strcmp(signature, "div2(_,_)") == 0) return Mpf_div2;
            if(!isStatic && strcmp(signature, "neg(_)") == 0) return Mpf_neg;
            if(!isStatic && strcmp(signature, "abs(_)") == 0) return Mpf_abs;
            if(!isStatic && strcmp(signature, "relDiff(_,_)") == 0) return Mpf_relDiff;
            if(!isStatic && strcmp(signature, "pow(_,_)") == 0) return Mpf_pow;
            if(!isStatic && strcmp(signature, "sqrt(_)") == 0) return Mpf_sqrt;
            if(!isStatic && strcmp(signature, "sqrtUi(_)") == 0) return Mpf_sqrtUi;
            if(!isStatic && strcmp(signature, "ceil(_)") == 0) return Mpf_ceil;
            if(!isStatic && strcmp(signature, "floor(_)") == 0) return Mpf_floor;
            if(!isStatic && strcmp(signature, "trunc(_)") == 0) return Mpf_trunc;
            if(!isStatic && strcmp(signature, "cmpMpf(_)") == 0) return Mpf_cmpMpf;
            if(!isStatic && strcmp(signature, "cmpMpz(_)") == 0) return Mpf_cmpMpz;
            if(!isStatic && strcmp(signature, "cmpDbl(_)") == 0) return Mpf_cmpDbl;
            if(!isStatic && strcmp(signature, "cmpUi(_)") == 0) return Mpf_cmpUi;
            if(!isStatic && strcmp(signature, "cmpSi(_)") == 0) return Mpf_cmpSi;
            if(!isStatic && strcmp(signature, "isInteger") == 0) return Mpf_isInteger;
            if(!isStatic && strcmp(signature, "sign(_)") == 0) return Mpf_sign;

            if(!isStatic && strcmp(signature, "powz(_,_)") == 0) return Mpf_powz;
            if(!isStatic && strcmp(signature, "powf(_,_)") == 0) return Mpf_powf;
            if(!isStatic && strcmp(signature, "root(_,_)") == 0) return Mpf_root;
            if(!isStatic && strcmp(signature, "cbrt(_)") == 0) return Mpf_cbrt;
            if(!isStatic && strcmp(signature, "hypot(_,_)") == 0) return Mpf_hypot;
            if(!isStatic && strcmp(signature, "log(_)") == 0) return Mpf_log;
            if(!isStatic && strcmp(signature, "log2(_)") == 0) return Mpf_log2;
            if(!isStatic && strcmp(signature, "log10(_)") == 0) return Mpf_log10;
            if(!isStatic && strcmp(signature, "exp(_)") == 0) return Mpf_exp;
            if(!isStatic && strcmp(signature, "cos(_)") == 0) return Mpf_cos;
            if(!isStatic && strcmp(signature, "sin(_)") == 0) return Mpf_sin;
            if(!isStatic && strcmp(signature, "tan(_)") == 0) return Mpf_tan;
            if(!isStatic && strcmp(signature, "acos(_)") == 0) return Mpf_acos;
            if(!isStatic && strcmp(signature, "asin(_)") == 0) return Mpf_asin;
            if(!isStatic && strcmp(signature, "atan(_)") == 0) return Mpf_atan;
            if(!isStatic && strcmp(signature, "atan2(_,_)") == 0) return Mpf_atan2;
            if(!isStatic && strcmp(signature, "cosh(_)") == 0) return Mpf_cosh;
            if(!isStatic && strcmp(signature, "sinh(_)") == 0) return Mpf_sinh;
            if(!isStatic && strcmp(signature, "tanh(_)") == 0) return Mpf_tanh;
            if(!isStatic && strcmp(signature, "gamma(_)") == 0) return Mpf_gamma;
            if(!isStatic && strcmp(signature, "gammaInc(_,_)") == 0) return Mpf_gammaInc;
            if(!isStatic && strcmp(signature, "lgamma(_)") == 0) return Mpf_lgamma;
            if(!isStatic && strcmp(signature, "digamma(_)") == 0) return Mpf_digamma;
            if(!isStatic && strcmp(signature, "zeta(_)") == 0) return Mpf_zeta;
            if(!isStatic && strcmp(signature, "zetaUi(_)") == 0) return Mpf_zetaUi;
        }
    }
    return NULL;
}

static void writeFn(WrenVM* vm, const char* text) {
    printf("%s", text);
}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
    switch (errorType) {
        case WREN_ERROR_COMPILE:
            printf("[%s line %d] [Error] %s\n", module, line, msg);
            break;
        case WREN_ERROR_STACK_TRACE:
            printf("[%s line %d] in %s\n", module, line, msg);
            break;
        case WREN_ERROR_RUNTIME:
            printf("[Runtime Error] %s\n", msg);
            break;
    }
}

char *readFile(const char *fileName) {
    FILE *f = fopen(fileName, "r");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    rewind(f);
    char *script = malloc(fsize + 1);
    size_t ret = fread(script, 1, fsize, f);
    if (ret != fsize) printf("Error reading %s\n", fileName);
    fclose(f);
    script[fsize] = 0;
    return script;
}

static void loadModuleComplete(WrenVM* vm, const char* module, WrenLoadModuleResult result) {
    if( result.source) free((void*)result.source);
}

WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) {
    WrenLoadModuleResult result = {0};
    if (strcmp(name, "random") != 0 && strcmp(name, "meta") != 0) {
        result.onComplete = loadModuleComplete;
        char fullName[strlen(name) + 6];
        strcpy(fullName, name);
        strcat(fullName, ".wren");
        result.source = readFile(fullName);
    }
    return result;
}

int main(int argc, char **argv) {
    if (argc != 2) {
        printf("Please pass the name of the Wren file to be executed.\n");
        return 1;
    }
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = &writeFn;
    config.errorFn = &errorFn;
    config.bindForeignClassFn = &bindForeignClass;
    config.bindForeignMethodFn = &bindForeignMethod;
    config.loadModuleFn = &loadModule;
    WrenVM* vm = wrenNewVM(&config);
    const char* module = "main";
    const char* fileName = argv[1];
    char *script = readFile(fileName);
    WrenInterpretResult result = wrenInterpret(vm, module, script);
    switch (result) {
        case WREN_RESULT_COMPILE_ERROR:
            printf("Compile Error!\n");
            break;
        case WREN_RESULT_RUNTIME_ERROR:
            printf("Runtime Error!\n");
            break;
        case WREN_RESULT_SUCCESS:
            break;
    }
    wrenFreeVM(vm);
    free(script);
    mpfr_free_cache();
    return 0;
}