Category talk:Wren-i64

From Rosetta Code
Revision as of 20:15, 23 February 2022 by PureFox (talk | contribs) (Added blurb and source code for new 'Wren-i64' module.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

64-bit integer arithmetic

Although this is already supported in an easy to use way by the Wren-long 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. Moreover, it only supports unsigned 64-bit arithmetic.

I have therefore written a wrapper for the C99 fixed length types, int64_t and uint64, to deal with such cases which are represented by the Wren classes, I64 and U64, respectively. There are also I64s and U64s classes which contain some static methods for dealing with lists of such objects.

These types are designed to behave the same as the C types regards rounding, underflow and overflow.

As it is not possible to create user-defined value types in Wren, all these classes are reference types and so the first two require all objects to be allocated on the heap and are subject therefore to automatic garbage collection when they are no longer needed.

As well as methods for basic arithmetic, I have also included some additional 'convenience' or utility methods which can be coded in a few lines of Wren code using the former.

This module is written to be as fast as possible within the confines of the Wren VM and therefore follows the model of Wren-GMP in recycling memory to minimize the number of variables and garbage collections needed in a typical application. This means that there is an inevitable trade-off between maximizing efficiency/performance and easy of use which is an important consideration for a simple language such as Wren. 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 Wren-i64 types and built-in numbers (Num).

2. The two classes: I64 and U64 both 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.

How fast?

Early results suggest Wren-i64 will be at least 4 times faster than Wren-long for scripts requiring a lot of 64-bit integer 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 if maximizing performance is not a major concern.

As they are optimized for dealing with much larger numbers, Wren-i64 is also an order of magnitude quicker than both the BigInt and Mpz (in Wren-GMP) classes.

However, it appears to be around 50% slower than normal 53-bit integer arithmetic in Wren and so will there will still be a considerable speed differential between Wren and statically typed compiled or other languages which have native support for 64 integer arithmetic.

Source code (Wren)

<lang ecmascript>/* Module "i64.wren" */

import "./trait" for Comparable

/*

   I64 represents a signed 64 bit integer which mirrors the int64_t type in C99.
   Consequently, foreign methods behave the same as regards rounding, underflow and overflow as int64_t does.
   Where Num arguments are allowed, they will be converted to integers if they are not already by the C code.
  • /

foreign class I64 is Comparable {

   // Creates small commonly used I64 objects.
   static minusOne { from(-1) }
   static zero     { from(0)  }
   static one      { from(1)  }
   static two      { from(2)  }
   static three    { from(3)  }
   static four     { from(4)  }
   static five     { from(5)  }
   static ten      { from(10) }
   foreign static largest
   foreign static smallest
   static maxSafe { from(9007199254740991)  }
   static minSafe { from(-9007199254740991) }
   // Returns an I64 object equal in value to 'x' raised to the power of 'n'.
   // The arguments can be either I64 objects or Nums.
   // If 'n' is less than 0, returns zero. O.pow(0) returns one.
   // Returns zero if the calculation would otherwise overflow or underflow.
   static pow(x, n) {
       if (!(x is I64)) x = from(x)
       if (!(n is I64)) n = from(n)
       if (n < 0) return zero
       if (n.isZero) return one
       if (n.isOne) return x.copy()
       if (x.isZero) return zero
       if (x.isOne) return one
       if (x == minusOne) return n.isEven ? one : minusOne
       var neg = x < 0
       var value = neg ? -(x.toNum) : x.toNum
       value = value.pow(n.toNum)
       if (value <= 9007199254740991) return neg ? from(-value) : from(value)
       if (value >= 2.pow(63)) return zero
       x = x.copy()
       var y = one
       var z = n
       while (true) {
           if (z.isOdd) {
               y.mul(x)
               z.dec
           }
           if (z.isZero) break
           z.rsh(1)
           x.square
       }
       return y
   }
   // Returns an I64 object equal in value to the integer n'th root of 'x'
   // i.e. the precise n'th root truncated towards zero if not an integer.
   // 'x' can either be an I64 object or a Num but 'n' must be a positive integer.
   // Throws an error if 'x' is negative and 'n' is even.
   static root(x, n) {
       if (!(x is I64)) x = from(x)
       if (!((n is Num) && n.isInteger && n > 0)) {
           Fiber.abort("'n' must be a positive integer.")
       }
       if (n == 1) return x.copy()
       var t = x.copy()
       var neg = t < 0
       if (neg) {
           if (n % 2 == 0) {
               Fiber.abort("Cannot take the %(n)th root of a negative number.")
           } else {
               t.neg
           }
       }
       n = n - 1
       var s = t + 1
       var u = from(t)
       while (u < s) {
           s.set(u)
           u.set(((u * n) + t / pow(u, n)) / (n + 1))
       }
       return (neg) ? -s : s
   }
   // Returns an I64 object equal in value to 'x' to the power 'exp' modulo 'mod'.
   // The arguments can either be I64 objects or Nums but 'mod' must be non-zero.
   static modPow(x, exp, mod) {
       if (!(x is I64)) x = from(x)
       if (!(exp is I64)) exp = from(exp)
       if (!(mod is I64)) mod = from(mod)
       if (mod.isZero) Fiber.abort("Cannot take modPow with modulus 0.")
       var r = one
       var base = x % mod
       if (exp < 0) {
           exp.mul(minusOne)
           base.set(modInv(base, mod))
       }
       while (exp > 0) {
           if (base.isZero) return zero
           if (exp.isOdd) r.set((r * base) % mod)
           exp.rsh(1)
           base.square.rem(mod)
       }
       return r
   }
   // Returns the multiplicative inverse of 'x' modulo 'n'.
   // The arguments can either be I64 objects or Nums but must be co-prime.
   static modInv(x, n) {
       if (!(x is I64)) x = from(x)
       if (!(n is I64)) n = from(n)
       var r = from(n)
       var newR = from(x).abs
       var t = zero
       var newT = one
       while (!newR.isZero) {
           var q = r / newR
           var lastT = from(t)
           var lastR = from(r)
           t.set(newT)
           r.set(newR)
           newT.sub(lastT, q*newT)
           newR.sub(lastR, q*newR)
       }
       if (!r.abs.isOne) Fiber.abort("%(this) and %(n) are not co-prime.")
       if (t < 0) t.add(n)
       if (x < 0) return t.neg
       return t
   }
   // Returns the smaller of two I64 objects.
   static min(x, y) {
       if (x < y) return x
       return y
   }
   // Returns the greater of two I64 objects.
   static max(x, y) {
       if (x > y) return x
       return y
   }
   // Returns the positive difference of two I64 objects.
   static dim(x, y) {
       if (x >= y) return x - y
       return zero
   }
   // Returns the greatest common divisor of two I64 objects.
   static gcd(x, y) {
       while (y != zero) {
           var t = y
           y = x % y
           x = t
       }
       return x.abs
   }
   // Returns the least common multiple of two I64 objects.
   static lcm(x, y) {
       if (x.isZero && y.isZero) return zero
       return (x*y).abs / gcd(x, y)
   }
   // Returns whether or not 'x' is an instance of I64.
   static isInstance(x) { x is I64 }
   // 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 I64 object with a value of zero.
   construct new() {}
   // Creates a new I64 object from:
   foreign static from(x)       // another I64 object or from a Num
   foreign static fromU64(x)    // a U64 object
   foreign static fromStr(s)    // a base 10 string
   foreign static fromStr(s, b) // a base 'b' string
   // Assigns a new value to the current instance using :
   foreign set(x)        // another I64 object or a Num
   foreign setU64(x)     // a U64 object
   foreign setStr(s)     // a base 10 string
   foreign setStr(s, b)  // a base 'b' string
   // Converts the current instance to:
   foreign toNum       // a Num
   foreign toU64       // a U64 object
   foreign toString    // a base 10 string
   toString(b) {       // a base 'b' string
       if (b < 2 || b > 36) Fiber.abort("Base must be between 2 and 36.")
       if (this.isZero) return "0"
       var digits = "0123456789abcdefghijklmnopqrstuvwxyz"
       var neg = (this < 0)
       var n = this.copy()
       if (neg) n.neg
       var res = ""
       while (n > 0) {
           res = res + "%(digits[(n%b).toNum])"
           n.div(b)
       }
       return ((neg) ? "-" : "") + res[-1..0]
   }
   /* Methods which assign their result to the current instance ('this').
      Unless otherwise noted, arguments can be either I64 objects or Nums. */
   foreign add(op1, op2)        // adds two objects
   foreign sub(op1, op2)        // subtracts one object from another
   foreign mul(op1, op2)        // multiplies two objects
   foreign addMul(op1, op2)     // multiplies two objects and adds the result to this
   foreign subMul(op1, op2)     // multiplies two objects and subtracts the result from this
   foreign div(n, d)            // divides one object by another (rounds towards zero)
   foreign rem(n, d)            // sets the remainder after dividing one object by another
   foreign and(op1, op2)        // bitwise 'and' of two objects
   foreign ior(op1, op2)        // bitwise 'inclusive or' of two objects
   foreign xor(op1, op2)        // bitwise 'exclusive or' of two objects
   foreign lsh(n, b)            // shifts n (I64 or Num) by b (Num) bits to the left
   foreign rsh(n, b)            // shifts n (I64 or Num) by b (Num) bits to the right
   foreign neg(op)              // sets to -op
   foreign abs(op)              // sets to the absolute value of op
   foreign inc(op)              // adds one to op
   foreign dec(op)              // subtracts one from op
   foreign com(op)              // one's complement of an object
   pow(op, n)  { set(I64.pow (op, n)) }  // raises op to the power n
   square(op)  { set(I64.pow (op, 2)) }  // squares op
   cube(op)    { set(I64.pow (op, 3)) }  // cubes op
   root(op, n) { set(I64.root(op, n)) }  // takes the nth integral root of op
   sqrt(op)    { set(I64.root(op, 2)) }  // takes the square integral root of op
   cbrt(op)    { set(I64.root(op, 3)) }  // takes the cube integral root of op
   /* Convenience versions of the above methods where the first (or only) argument is 'this'.
      Unless otherwise noted, any other argument must be either another I64 object or a Num. */
   foreign add(op)
   foreign sub(op)
   foreign mul(op)
   foreign addMul(op)
   foreign subMul(op)
   foreign div(op)
   foreign rem(op)
   foreign and(op)
   foreign ior(op)
   foreign xor(op)
   foreign lsh(b)    // b must be a Num
   foreign rsh(b)    // b must be a Num
   foreign neg
   foreign abs
   foreign inc
   foreign dec
   foreign com
   pow(n)   { set(I64.pow (this, n)) }
   square   { set(I64.pow (this, 2)) }
   cube     { set(I64.pow (this, 3)) }
   root(n)  { set(I64.root(this, n)) }
   sqrt     { set(I64.root(this, 2)) } 
   cbrt     { set(I64.root(this, 3)) }
   /* As above methods where the first (or only) argument is 'this'
      but return a new I64 object rather than mutating 'this'. */
   // Operators corresponding to the above foreign methods.
   +(op)  { copy().add(op) }
   -(op)  { copy().sub(op) }
   *(op)  { copy().mul(op) }
   /(op)  { copy().div(op) }
   %(op)  { copy().rem(op) }
   &(op)  { copy().and(op) }
   |(op)  { copy().ior(op) }
   ^(op)  { copy().xor(op) }
   <<(b)  { copy().lsh(b)  }
   >>(b)  { copy().rsh(b)  }
   - { copy().neg }
   ~ { copy().com }
   /* Comparison methods which return -1, 0, 1
      if this < op, this == op or this > op respectively. */
   // Needed to satisfy Comparable trait and allow relational operators to be used.
   // Argument can be either another I64 object or a Num.
   foreign compare(op)
   foreign cmpU64(op)  // compare this to a U64 object

   /* Miscellaneous methods. */
   min(op)   { (op < this) ? op : this }    // returns the minimum of this and another I64 object
   max(op)   { (op > this) ? op : this }    // returns the maximum of this and another I64 object
   clamp(min, max) {            // clamps this to the interval [min, max]
       if (this < min) set(min)
       if (this > max) set(max)
   }
   copy() { I64.from(this) }    // copies 'this' to a new I64 object
   isOdd  { this % I64.two == I64.one }                  // true if 'this' is odd
   isEven { this % I64.two == I64.zero }                 // true if 'this' is even
   isZero { this == I64.zero }                           // true if 'this' is zero
   isOne  { this == I64.one  }                           // true if 'this' is one
   isSafe { this >= I64.minSafe && this <= I64.maxSafe } // true if 'this' is safe
   isDivisible(d) { this % d == I64.zero }   // true if 'this' is divisible by d
   isRoot(n) {                      // true if 'this' is an exact nth root of another I64 object
       var r = I64.root(this, n)
       return I64.pow(r, n) == this
   }
   isSquare { isRoot(2) }           // true if 'this' is an exact square root of another I64 object
   isCube   { isRoot(3) }           // true if 'this' is an exact cube root of another I64 object
   sign {                           // the sign of 'this'
       if (this < I64.zero) return -1
       if (this > I64.zero) return 1
       return 0
   }

}

/*

   U64 represents an unsigned 64 bit integer which mirrors the uint64_t type in C99.
   Consequently, foreign methods behave the same as regards rounding, underflow and overflow as uint64_t does.
   Where Num arguments are allowed, they will be converted to integers if they are not already by the C code.
  • /

foreign class U64 is Comparable {

   // Creates small commonly used U64 objects.
   static zero     { from(0)  }
   static one      { from(1)  }
   static two      { from(2)  }
   static three    { from(3)  }
   static four     { from(4)  }
   static five     { from(5)  }
   static ten      { from(10) }
   foreign static largest
   static maxSafe { from(9007199254740991) }
   static maxU32  { from(4294967295) }
   // Returns a U64 object equal in value to 'x' raised to the power of 'n'.
   // The arguments can be either U64 objects or Nums.
   // If 'n' is less than 0, returns zero. O.pow(0) returns one.
   // Returns zero if the calculation would otherwise overflow.
   static pow(x, n) {
       if (!(x is U64)) x = from(x)
       if (!(n is U64)) n = from(n)
       if (n.isZero) return one
       if (n.isOne) return x.copy()
       if (x.isZero) return zero
       if (x.isOne) return one
       var value = x.toNum.pow(n.toNum)
       if (value <= 9007199254740991) return from(value)
       if (value >= 2.pow(64)) return zero
       x = x.copy()
       var y = one
       var z = n
       while (true) {
           if (z.isOdd) {
               y.mul(x)
               z.dec
           }
           if (z.isZero) break
           z.rsh(1)
           x.square
       }
       return y
   }
   // Returns a U64 object equal in value to the integer n'th root of 'x'
   // i.e. the precise n'th root truncated towards zero if not an integer.
   // 'x' can either be a U64 object or a Num but 'n' must be a positive integer.
   static root(x, n) {
       if (!(x is U64)) x = from(x)
       if (!((n is Num) && n.isInteger && n > 0)) {
           Fiber.abort("'n' must be a positive integer.")
       }
       if (n == 1) return x.copy()
       var t = x.copy()
       n = n - 1
       var s = t + 1
       var u = from(t)
       while (u < s) {
           s.set(u)
           u.set(((u * n) + t / pow(u, n)) / (n + 1))
       }
       return s
   }
   // Returns a U64 object equal in value to 'x' to the power 'exp' modulo 'mod'.
   // The arguments can either be U64 objects or Nums but 'mod' must be non-zero.
   static modPow(x, exp, mod) {
       if (!(x is U64)) x = from(x)
       if (!(exp is U64)) exp = from(exp)
       if (!(mod is U64)) mod = from(mod)
       if (mod.isZero) Fiber.abort("Cannot take modPow with modulus 0.")
       var r = one
       var base = x % mod
       while (exp > 0) {
           if (base.isZero) return zero
           if (exp.isOdd) r.set(modmul(r, base, mod))
           exp.rsh(1)
           base.set(modMul(base, base, mod))
       }
       return r
   }
   // Returns a U64 object equal in value to 'x' multiplied by 'n' modulo 'mod'.
   // The arguments can either be U64 objects or Nums.
   static modMul(x, n, mod) {
       if (!(x is U64)) x = from(x)
       if (!(n is U64)) n = from(n)
       if (!(mod is U64)) mod = from(mod)
       var z = zero
       var y = x.copy()
       while (n > 0) {
           if (n.isOdd) x.set((x + y) % mod)
           y.lsh(1).rem(mod)
           n.rsh(1)
       }
       return x
   }
   // Returns the smaller of two U64 objects.
   static min(x, y) {
       if (x < y) return x
       return y
   }
   // Returns the greater of two U64 objects.
   static max(x, y) {
       if (x > y) return x
       return y
   }
   // Returns the positive difference of two U64 objects.
   static dim(x, y) {
       if (x >= y) return x - y
       return zero
   }
   // Returns the greatest common divisor of two U64 objects.
   static gcd(x, y) {
       while (y != zero) {
           var t = y
           y = x % y
           x = t
       }
       return x
   }
   // Returns the least common multiple of two U64 objects.
   static lcm(x, y) {
       if (x.isZero && y.isZero) return zero
       return x * y / gcd(x, y)
   }
   // Returns the factorial of 'n'. Can only be used for n <= 20.
   static factorial(n) {
       if (!((n is Num) && n >= 0 && n <= 20)) {
           Fiber.abort("Argument must be a non-negative integer no larger than 20.")
       }
       if (n < 2) return one
       var fact = one
       var i = two
       while (i <= n) {
           fact.mul(i)
           i.inc
       }
       return fact
   }
   // Determines whether 'x' is prime using a wheel with basis [2, 3].
   static isPrime(x) {
       if (!(x is U64)) x = from(x)
       if (x < 2) return false
       if (x%2 == 0) return x == 2
       if (x%3 == 0) return x == 3
       var d = U64.five
       while (d*d <= x) {
           if (x%d == 0) return false
           d.add(2)
           if (x%d == 0) return false
           d.add(4)
       }
       return true
   }
   // Returns whether or not 'x' is an instance of U64.
   static isInstance(x) { x is U64 }
   // Creates a new U64 object with a value of zero.
   construct new() {}
   // Creates a new U64 object from:
   foreign static from(x)       // another U64 object or from a Num
   foreign static fromI64(x)    // an I64 object
   foreign static fromStr(s, b) // a base 'b' string
   foreign static fromStr(s)    // a base 10 string
   // Assigns a new value to the current instance using:
   foreign set(x)        // another U64 object or a Num
   foreign setI64(x)     // an I64 object
   foreign setStr(s, b)  // a base 'b' string
   foreign setStr(s)     // a base 10 string
   // Converts the current instance to:
   foreign toNum       // a Num
   foreign toI64       // an I64 object
   foreign toString    // a base 10 string
   toString(b) {       // a base 'b' string
       if (b < 2 || b > 36) Fiber.abort("Base must be between 2 and 36.")
       if (this.isZero) return "0"
       var digits = "0123456789abcdefghijklmnopqrstuvwxyz"
       var n = this.copy()
       var res = ""
       while (n > 0) {
           res = res + "%(digits[(n%b).toNum])"
           n.div(b)
       }
       return res[-1..0]
   }
   /* Methods which assign their result to the current instance ('this').
      Unless otherwise noted, arguments can be either U64 objects or Nums. */
   foreign add(op1, op2)        // adds two objects
   foreign sub(op1, op2)        // subtracts one object from another
   foreign mul(op1, op2)        // multiplies two objects
   foreign addMul(op1, op2)     // multiplies two objects and adds the result to this
   foreign subMul(op1, op2)     // multiplies two objects and subtracts the result from this
   foreign div(n, d)            // divides one object by another (rounds towards zero)
   foreign rem(n, d)            // sets the remainder after dividing one object by another
   foreign and(op1, op2)        // bitwise 'and' of two objects
   foreign ior(op1, op2)        // bitwise 'inclusive or' of two objects
   foreign xor(op1, op2)        // bitwise 'exclusive or' of two objects
   foreign lsh(n, b)            // shifts n (U64 or Num) by b (Num) bits to the left
   foreign rsh(n, b)            // shifts n (U64 or Num) by b (Num) bits to the right
   foreign neg(op)              // sets to -op
   foreign inc(op)              // adds one to op
   foreign dec(op)              // subtracts one from op
   foreign com(op)              // one's complement of an object
   pow(op, n)  { set(U64.pow (op, n)) }  // raises op to the power n
   square(op)  { set(U64.pow (op, 2)) }  // squares op
   cube(op)    { set(U64.pow (op, 3)) }  // cubes op
   root(op, n) { set(U64.root(op, n)) }  // takes the nth integral root of op
   sqrt(op)    { set(U64.root(op, 2)) }  // takes the square integral root of op
   cbrt(op)    { set(U64.root(op, 3)) }  // takes the cube integral root of op
   /* Convenience versions of the above methods where the first (or only) argument is 'this'.
      Unless otherwise noted, any other argument must be either another U64 object or a Num. */
   foreign add(op)
   foreign sub(op)
   foreign mul(op)
   foreign addMul(op)
   foreign subMul(op)
   foreign div(op)
   foreign rem(op)
   foreign and(op)
   foreign ior(op)
   foreign xor(op)
   foreign lsh(b)    // b must be a Num
   foreign rsh(b)    // b must be a Num
   foreign neg
   foreign inc
   foreign dec
   foreign com
   pow(n)  { set(U64.pow (this, n)) }
   square  { set(U64.pow (this, 2)) }
   cube    { set(U64.pow (this, 3)) }
   root(n) { set(U64.root(this, n)) }
   sqrt    { set(U64.root(this, 2)) } 
   cbrt    { set(U64.root(this, 3)) }
   /* As above methods where the first (or only) argument is 'this'
      but return a new U64 object rather than mutating 'this'. */
   // Operators corresponding to the above foreign methods.
   +(op)  { copy().add(op) }
   -(op)  { copy().sub(op) }
   *(op)  { copy().mul(op) }
   /(op)  { copy().div(op) }
   %(op)  { copy().rem(op) }
   &(op)  { copy().and(op) }
   |(op)  { copy().ior(op) }
   ^(op)  { copy().xor(op) }
   <<(b)  { copy().lsh(b)  }
   >>(b)  { copy().rsh(b)  }
   - { copy().neg }
   ~ { copy().com }
   /* Comparison methods which return -1, 0, 1
      if this < op, this == op or this > op respectively. */
   // Needed to satisfy Comparable trait and allow relational operators to be used.
   // Argument can be either another U64 object or a Num.
   foreign compare(op)
   foreign cmpI64(op)  // compare this to an I64 object
   /* Miscellaneous methods. */
   min(op)   { (op < this) ? op : this }    // returns the minimum of this and another U64 object
   max(op)   { (op > this) ? op : this }    // returns the maximum of this and another U64 object
   clamp(min, max) {           // clamps this to the interval [min, max]
       if (this < min) set(min)
       if (this > max) set(max)
   }
   copy() { U64.from(this) }   // copies 'this' to a new U64 object
   isOdd  { this % U64.two == 1 }      // true if 'this' is odd
   isEven { this % U64.two == 0 }      // true if 'this' is even
   isZero { this == 0 }                // true if 'this' is zero
   isOne  { this == 1 }                // true if 'this' is one
   isSafe { this <= U64.maxSafe  }     // true if 'this' is safe
   isDivisible(d) { this % d == U64.zero } // true if 'this' is divisible by d
   isRoot(n) {                       // true if 'this' is an exact nth root of another U64 object
       var r = U64.root(this, n)
       return U64.pow(r, n) == this
   }
   isSquare { isRoot(2) }            // true if 'this' is an exact square root of another U64 object
   isCube   { isRoot(3) }            // true if 'this' is an exact cube root of another U64 object
   sign {                            // the sign of 'this'
       if (this > U64.zero) return 1
       return 0
   }
   digits   { toString.map { |d| Num.fromString(d) }.toList }  // a list of the current instance's base 10 digits
   digitSum { digits.reduce(0) { |acc, d| acc = acc + d } }    // the sum of those digits

}

/* I64s contains various routines applicable to a sequence of I64 objects. */ class I64s {

   static sum(sz)  { sz.reduce(I64.zero) { |acc, x| acc.add(x) } }
   static prod(sz) { sz.reduce(I64.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  } }

}

/* U64s contains various routines applicable to a sequence of U64 objects. */ class U64s {

   static sum(sz)  { sz.reduce(U64.zero) { |acc, x| acc.add(x) } }
   static prod(sz) { sz.reduce(U64.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  } }

}</lang>

Source code (C)

<lang c>/* gcc -O3 wren-i64.c -o wren-i64 -lwren -lm */

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <inttypes.h>
  4. include <string.h>
  5. include "wren.h"

/* I64 functions */

void I64_allocate(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pi = 0;

}

void I64_largest(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pi = INT64_MAX;

}

void I64_smallest(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pi = INT64_MIN;

}

void I64_from(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);
   *pi = val;

}

void I64_fromU64(WrenVM* vm) {

   int64_t *pi  = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   uint64_t val = *(uint64_t*)wrenGetSlotForeign(vm, 1);
   *pi = (int64_t)val;

}

void I64_fromStr(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   const char *str = wrenGetSlotString(vm, 1);
   int64_t val = (int64_t)strtoll(str, NULL, 10);
   *pi = val;

}

void I64_fromStr2(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   const char *str = wrenGetSlotString(vm, 1);
   int base = (int)wrenGetSlotDouble(vm, 2);
   int64_t val = (int64_t)strtoll(str, NULL, base);
   *pi = val;

}

void I64_set(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);
   *pi = val;

}

void I64_setU64(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   uint64_t val = *(uint64_t*)wrenGetSlotForeign(vm, 1);
   *pi = (int64_t)val;

}

void I64_setStr(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   const char *str = wrenGetSlotString(vm, 1);
   int64_t val = (int64_t)strtoll(str, NULL, 10);
   *pi = val;

}

void I64_setStr2(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   const char *str = wrenGetSlotString(vm, 1);
   int base = (int)wrenGetSlotDouble(vm, 2);
   int64_t val = (int64_t)strtoll(str, NULL, base);
   *pi = val;

}

void I64_toNum(WrenVM* vm) {

   int64_t val = *(int64_t*)wrenGetSlotForeign(vm, 0);
   wrenSetSlotDouble(vm, 0, (double)val);

}

void I64_toU64(WrenVM* vm) {

   int64_t val = *(int64_t*)wrenGetSlotForeign(vm, 0);
   wrenGetVariable(vm, "./i64", "U64", 0);
   uint64_t *pu = wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pu = (uint64_t)val;

}

void I64_toString(WrenVM* vm) {

   int64_t val = *(int64_t*)wrenGetSlotForeign(vm, 0);
   char buf[21];
   snprintf(buf, 21, "%lld", (long long int)val);
   wrenSetSlotString(vm, 0, (const char*)buf);

}

void I64_add2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 + v3;

}

void I64_sub2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 - v3;

}

void I64_mul2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 * v3;

}

void I64_addMul2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);
   *pv1 += v2 * v3;

}

void I64_subMul2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 -= v2 * v3;

}

void I64_div2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 / v3;

}

void I64_rem2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 % v3;

}

void I64_and2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 & v3;

}

void I64_ior2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 | v3;

}

void I64_xor2(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   int64_t v3 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 2) : (int64_t)wrenGetSlotDouble(vm, 2);
   *pv1 = v2 ^ v3;

}

void I64_lsh2(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t n = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   int b = (int)wrenGetSlotDouble(vm, 2);
   *pi = n << b;

}

void I64_rsh2(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t n = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);
   int b = (int)wrenGetSlotDouble(vm, 2);
   *pi = n >> b;

}

void I64_neg(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   *pi = -val;

}

void I64_abs(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   if (val < 0) *pi = -val;

}

void I64_inc(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   *pi = ++val;

}

void I64_dec(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1); 
   *pi = --val;

}

void I64_com(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t val = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);
   *pi = ~val;

}

void I64_add(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 += v2;

}

void I64_sub(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 -= v2;

}

void I64_mul(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 *= v2;

}

void I64_addMul(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 += *pv1 * v2;

}

void I64_subMul(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 -= *pv1 * v2;

}

void I64_div(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 /= v2;

}

void I64_rem(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 %= v2;

}

void I64_and(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 &= v2;

}

void I64_ior(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 |= v2;

}

void I64_xor(WrenVM* vm) {

   int64_t *pv1 = (int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 ^= v2;

}

void I64_lsh(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int b = (int)wrenGetSlotDouble(vm, 1);
   *pi <<= b;

}

void I64_rsh(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   int b = (int)wrenGetSlotDouble(vm, 1);
   *pi >>= b;

}

void I64_neg0(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   *pi = -(*pi);

}

void I64_abs0(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   if (*pi < 0) *pi = -(*pi);

}

void I64_inc0(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   *pi += 1;

}

void I64_dec0(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   *pi -= 1;

}

void I64_com0(WrenVM* vm) {

   int64_t *pi = (int64_t*)wrenGetSlotForeign(vm, 0);
   *pi = ~(*pi);

}

void I64_compare(WrenVM* vm) {

   int64_t v1 = *(int64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   int64_t v2 = (wt == 2) ? *(int64_t*)wrenGetSlotForeign(vm, 1) : (int64_t)wrenGetSlotDouble(vm, 1);
   int res = (v1 < v2) ? -1 : (v1 > v2) ? 1 : 0;
   wrenSetSlotDouble(vm, 0, (double)res);

}

void I64_cmpU64(WrenVM* vm) {

   int64_t v1 = *(int64_t*)wrenGetSlotForeign(vm, 0);
   uint64_t v2 = *(uint64_t*)wrenGetSlotForeign(vm, 1);
   int64_t v3 = (int64_t)v2;
   int res = (v1 < v3) ? -1 : (v1 > v3) ? 1 : 0;
   wrenSetSlotDouble(vm, 0, (double)res);

}

/* U64 functions */

void U64_allocate(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pu = 0;

}

void U64_largest(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pu = UINT64_MAX;

}

void U64_from(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t val = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pu = val;

}

void U64_fromI64(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   int64_t val = *(int64_t*)wrenGetSlotForeign(vm, 1);
   *pu = (uint64_t)val;

}

void U64_fromStr(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   const char *str = wrenGetSlotString(vm, 1);
   uint64_t val = (uint64_t)strtoull(str, NULL, 10);
   *pu = val;

}

void U64_fromStr2(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenSetSlotNewForeign(vm, 0, 0, 8);
   const char *str = wrenGetSlotString(vm, 1);
   int base = (int)wrenGetSlotDouble(vm, 2);
   uint64_t val = (uint64_t)strtoull(str, NULL, base);
   *pu = val;

}

void U64_set(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t val = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pu = val;

}

void U64_setI64(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int64_t val  = *(int64_t*)wrenGetSlotForeign(vm, 1);
   *pu = (uint64_t)val;

}

void U64_setStr(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   const char *str = wrenGetSlotString(vm, 1);
   uint64_t val = (uint64_t)strtoll(str, NULL, 10);
   *pu = val;

}

void U64_setStr2(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   const char *str = wrenGetSlotString(vm, 1);
   int base = (int)wrenGetSlotDouble(vm, 2);
   uint64_t val = (uint64_t)strtoll(str, NULL, base);
   *pu = val;

}

void U64_toNum(WrenVM* vm) {

   uint64_t val = *(uint64_t*)wrenGetSlotForeign(vm, 0);
   wrenSetSlotDouble(vm, 0, (double)val);

}

void U64_toI64(WrenVM* vm) {

   uint64_t val = *(uint64_t*)wrenGetSlotForeign(vm, 0);
   wrenGetVariable(vm, "./i64", "I64", 0);
   int64_t *pi = wrenSetSlotNewForeign(vm, 0, 0, 8);
   *pi = (int64_t)val;

}

void U64_toString(WrenVM* vm) {

   uint64_t val = *(uint64_t*)wrenGetSlotForeign(vm, 0);
   char buf[21];
   snprintf(buf, 21, "%llu", (unsigned long long int)val);
   wrenSetSlotString(vm, 0, (const char*)buf);

}

void U64_add2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 + v3;

}

void U64_sub2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 - v3;

}

void U64_mul2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 * v3;

}

void U64_addMul2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 += v2 * v3;

}

void U64_subMul2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 -= v2 * v3;

}

void U64_div2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 / v3;

}

void U64_rem2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 % v3;

}

void U64_and2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);
   *pv1 = v2 & v3;

}

void U64_ior2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);  
   *pv1 = v2 | v3;

}

void U64_xor2(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   wt = wrenGetSlotType(vm, 2);
   uint64_t v3 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 2) : (uint64_t)wrenGetSlotDouble(vm, 2);
   *pv1 = v2 ^ v3;

}

void U64_lsh2(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t n = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   int b = (int)wrenGetSlotDouble(vm, 2);
   *pu = n << b;

}

void U64_rsh2(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t n = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   int b = (int)wrenGetSlotDouble(vm, 2);
   *pu = n >> b;

}

void U64_neg(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t val = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1); 
   *pu = -val;

}

void U64_inc(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t val = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pu = ++val;

}

void U64_dec(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t val = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pu = --val;

}

void U64_com(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t val = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pu = ~val;

}

void U64_add(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 += v2;

}

void U64_sub(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pv1 -= v2;

}

void U64_mul(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 *= v2;

}

void U64_addMul(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 += *pv1 * v2;

}

void U64_subMul(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 -= *pv1 * v2;

}

void U64_div(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 /= v2;

}

void U64_rem(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 %= v2;

}

void U64_and(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pv1 &= v2;

}

void U64_ior(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);  
   *pv1 |= v2;

}

void U64_xor(WrenVM* vm) {

   uint64_t *pv1 = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   *pv1 ^= v2;

}

void U64_lsh(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int b = (int)wrenGetSlotDouble(vm, 1);
   *pu <<= b;

}

void U64_rsh(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   int b = (int)wrenGetSlotDouble(vm, 1);
   *pu >>= b;

}

void U64_neg0(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   *pu = -(*pu);

}

void U64_inc0(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   *pu += 1;

}

void U64_dec0(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   *pu -= 1;

}

void U64_com0(WrenVM* vm) {

   uint64_t *pu = (uint64_t*)wrenGetSlotForeign(vm, 0);
   *pu = ~(*pu);

}

void U64_compare(WrenVM* vm) {

   uint64_t v1 = *(uint64_t*)wrenGetSlotForeign(vm, 0);
   int wt = wrenGetSlotType(vm, 1);
   uint64_t v2 = (wt == 2) ? *(uint64_t*)wrenGetSlotForeign(vm, 1) : (uint64_t)wrenGetSlotDouble(vm, 1);
   int res = (v1 < v2) ? -1 : (v1 > v2) ? 1 : 0;
   wrenSetSlotDouble(vm, 0, (double)res);

}

void U64_cmpI64(WrenVM* vm) {

   uint64_t v1 = *(uint64_t*)wrenGetSlotForeign(vm, 0);
   int64_t  v2 = *(int64_t*)wrenGetSlotForeign(vm, 1);
   uint64_t v3 = (uint64_t)v2;
   int res = (v1 < v3) ? -1 : (v1 > v3) ? 1 : 0;
   wrenSetSlotDouble(vm, 0, (double)res);

}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {

   WrenForeignClassMethods methods;
   methods.allocate = NULL;
   methods.finalize = NULL;
   if (strcmp(module, "./i64") == 0) {
       if (strcmp(className, "I64") == 0) {
           methods.allocate = I64_allocate;
       } else if (strcmp(className, "U64") == 0) {
           methods.allocate = U64_allocate;
       }
   }
   return methods;

}

WrenForeignMethodFn bindForeignMethod(

   WrenVM* vm,
   const char* module,
   const char* className,
   bool isStatic,
   const char* signature) {
   if (strcmp(module, "./i64") == 0) {
       if (strcmp(className, "I64") == 0) {
           if( isStatic && strcmp(signature, "largest") == 0)      return I64_largest;
           if( isStatic && strcmp(signature, "smallest") == 0)     return I64_smallest;
           if( isStatic && strcmp(signature, "from(_)") == 0)      return I64_from;
           if( isStatic && strcmp(signature, "fromU64(_)") == 0)   return I64_fromU64;
           if( isStatic && strcmp(signature, "fromStr(_)") == 0)   return I64_fromStr;
           if( isStatic && strcmp(signature, "fromStr(_,_)") == 0) return I64_fromStr2;
           if(!isStatic && strcmp(signature, "set(_)") == 0)       return I64_set;
           if(!isStatic && strcmp(signature, "setU64(_)") == 0)    return I64_setU64;
           if(!isStatic && strcmp(signature, "setStr(_)") == 0)    return I64_setStr;
           if(!isStatic && strcmp(signature, "setStr(_,_)") == 0)  return I64_setStr2;
           if(!isStatic && strcmp(signature, "toNum") == 0)        return I64_toNum;
           if(!isStatic && strcmp(signature, "toU64") == 0)        return I64_toU64;
           if(!isStatic && strcmp(signature, "toString") == 0)     return I64_toString;
           if(!isStatic && strcmp(signature, "add(_,_)") == 0)     return I64_add2;
           if(!isStatic && strcmp(signature, "sub(_,_)") == 0)     return I64_sub2;
           if(!isStatic && strcmp(signature, "mul(_,_)") == 0)     return I64_mul2;
           if(!isStatic && strcmp(signature, "addMul(_,_)") == 0)  return I64_addMul2;
           if(!isStatic && strcmp(signature, "subMul(_,_)") == 0)  return I64_subMul2;
           if(!isStatic && strcmp(signature, "div(_,_)") == 0)     return I64_div2;
           if(!isStatic && strcmp(signature, "rem(_,_)") == 0)     return I64_rem2;
           if(!isStatic && strcmp(signature, "and(_,_)") == 0)     return I64_and2;
           if(!isStatic && strcmp(signature, "ior(_,_)") == 0)     return I64_ior2;
           if(!isStatic && strcmp(signature, "xor(_,_)") == 0)     return I64_xor2;
           if(!isStatic && strcmp(signature, "lsh(_,_)") == 0)     return I64_lsh2;
           if(!isStatic && strcmp(signature, "rsh(_,_)") == 0)     return I64_rsh2;
           if(!isStatic && strcmp(signature, "neg(_)") == 0)       return I64_neg;
           if(!isStatic && strcmp(signature, "abs(_)") == 0)       return I64_abs;
           if(!isStatic && strcmp(signature, "inc(_)") == 0)       return I64_inc;
           if(!isStatic && strcmp(signature, "dec(_)") == 0)       return I64_dec;
           if(!isStatic && strcmp(signature, "com(_)") == 0)       return I64_com;
           if(!isStatic && strcmp(signature, "add(_)") == 0)       return I64_add;
           if(!isStatic && strcmp(signature, "sub(_)") == 0)       return I64_sub;
           if(!isStatic && strcmp(signature, "mul(_)") == 0)       return I64_mul;
           if(!isStatic && strcmp(signature, "addMul(_)") == 0)    return I64_addMul;
           if(!isStatic && strcmp(signature, "subMul(_)") == 0)    return I64_subMul;
           if(!isStatic && strcmp(signature, "div(_)") == 0)       return I64_div;
           if(!isStatic && strcmp(signature, "rem(_)") == 0)       return I64_rem;
           if(!isStatic && strcmp(signature, "and(_)") == 0)       return I64_and;
           if(!isStatic && strcmp(signature, "ior(_)") == 0)       return I64_ior;
           if(!isStatic && strcmp(signature, "xor(_)") == 0)       return I64_xor;
           if(!isStatic && strcmp(signature, "lsh(_)") == 0)       return I64_lsh;
           if(!isStatic && strcmp(signature, "rsh(_)") == 0)       return I64_rsh;
           if(!isStatic && strcmp(signature, "neg") == 0)          return I64_neg0;
           if(!isStatic && strcmp(signature, "abs") == 0)          return I64_abs0;
           if(!isStatic && strcmp(signature, "inc") == 0)          return I64_inc0;
           if(!isStatic && strcmp(signature, "dec") == 0)          return I64_dec0;
           if(!isStatic && strcmp(signature, "com") == 0)          return I64_com0;
           if(!isStatic && strcmp(signature, "compare(_)") == 0)   return I64_compare;
           if(!isStatic && strcmp(signature, "cmpU64(_)") == 0)    return I64_cmpU64;
       } else if (strcmp(className, "U64") == 0) {
           if( isStatic && strcmp(signature, "largest") == 0)      return U64_largest;
           if( isStatic && strcmp(signature, "from(_)") == 0)      return U64_from;
           if( isStatic && strcmp(signature, "fromI64(_)") == 0)   return U64_fromI64;
           if( isStatic && strcmp(signature, "fromStr(_)") == 0)   return U64_fromStr;
           if( isStatic && strcmp(signature, "fromStr(_,_)") == 0) return U64_fromStr2;
           if(!isStatic && strcmp(signature, "set(_)") == 0)       return U64_set;
           if(!isStatic && strcmp(signature, "setI64(_)") == 0)    return U64_setI64;
           if(!isStatic && strcmp(signature, "setStr(_)") == 0)    return U64_setStr;
           if(!isStatic && strcmp(signature, "setStr(_,_)") == 0)  return U64_setStr2;
           if(!isStatic && strcmp(signature, "toNum") == 0)        return U64_toNum;
           if(!isStatic && strcmp(signature, "toI64") == 0)        return U64_toI64;
           if(!isStatic && strcmp(signature, "toString") == 0)     return U64_toString;
           if(!isStatic && strcmp(signature, "add(_,_)") == 0)     return U64_add2;
           if(!isStatic && strcmp(signature, "sub(_,_)") == 0)     return U64_sub2;
           if(!isStatic && strcmp(signature, "mul(_,_)") == 0)     return U64_mul2;
           if(!isStatic && strcmp(signature, "addMul(_,_)") == 0)  return U64_addMul2;
           if(!isStatic && strcmp(signature, "subMul(_,_)") == 0)  return U64_subMul2;
           if(!isStatic && strcmp(signature, "div(_,_)") == 0)     return U64_div2;
           if(!isStatic && strcmp(signature, "rem(_,_)") == 0)     return U64_rem2;
           if(!isStatic && strcmp(signature, "and(_,_)") == 0)     return U64_and2;
           if(!isStatic && strcmp(signature, "ior(_,_)") == 0)     return U64_ior2;
           if(!isStatic && strcmp(signature, "xor(_,_)") == 0)     return U64_xor2;
           if(!isStatic && strcmp(signature, "lsh(_,_)") == 0)     return U64_lsh2;
           if(!isStatic && strcmp(signature, "rsh(_,_)") == 0)     return U64_rsh2;
           if(!isStatic && strcmp(signature, "neg(_)") == 0)       return U64_neg;
           if(!isStatic && strcmp(signature, "inc(_)") == 0)       return U64_inc;
           if(!isStatic && strcmp(signature, "dec(_)") == 0)       return U64_dec;
           if(!isStatic && strcmp(signature, "com(_)") == 0)       return U64_com;
           if(!isStatic && strcmp(signature, "add(_)") == 0)       return U64_add;
           if(!isStatic && strcmp(signature, "sub(_)") == 0)       return U64_sub;
           if(!isStatic && strcmp(signature, "mul(_)") == 0)       return U64_mul;
           if(!isStatic && strcmp(signature, "addMul(_)") == 0)    return U64_addMul;
           if(!isStatic && strcmp(signature, "subMul(_)") == 0)    return U64_subMul;
           if(!isStatic && strcmp(signature, "div(_)") == 0)       return U64_div;
           if(!isStatic && strcmp(signature, "rem(_)") == 0)       return U64_rem;
           if(!isStatic && strcmp(signature, "and(_)") == 0)       return U64_and;
           if(!isStatic && strcmp(signature, "ior(_)") == 0)       return U64_ior;
           if(!isStatic && strcmp(signature, "xor(_)") == 0)       return U64_xor;
           if(!isStatic && strcmp(signature, "lsh(_)") == 0)       return U64_lsh;
           if(!isStatic && strcmp(signature, "rsh(_)") == 0)       return U64_rsh;
           if(!isStatic && strcmp(signature, "neg") == 0)          return U64_neg0;
           if(!isStatic && strcmp(signature, "inc") == 0)          return U64_inc0;
           if(!isStatic && strcmp(signature, "dec") == 0)          return U64_dec0;
           if(!isStatic && strcmp(signature, "com") == 0)          return U64_com0;
           if(!isStatic && strcmp(signature, "compare(_)") == 0)   return U64_compare;
           if(!isStatic && strcmp(signature, "cmpI64(_)") == 0)    return U64_cmpI64;
       }
   }
   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);
   return 0;

}</lang>