فهرست منبع

Add BigInteger type (#10750)

* Add BigInteger type

* Add BigInt test and allow unit package

* Add abs,pow,modPow,isProbablePrime,getLowestSetBit,bitLength methods

* Add missing tests and counting of trailing zeros

* Run tests for BigInt

* Add missing const

* Fiexes for Int32 ( javascript)

* More fixes for Int32

* Add trace for macro crash

* Fix missing ;

* Fix macro test

* Remove macrotest crash

* Fix Int32 (python)

* More fixes

* More fixes for python

* more fixes

* Fix for Python target

* Correct test for mutable bigint

* Add method to create BigInt from bytes

* Add auto convert from String

* Add greater common divisor (gcd) of two BigInt values

* Add lcm,min,max,nextprobableprime methods

* Add method to create random BigInt

* Add test for random and small fixes

* More random methods and some fixes

* Fix pow bug

* Add test for random prime and random in range + prime fix

* Remove trace(..)

* Fix test

* Fix unsigned compare for randomInRange

* Fix for randomInRange

* Hope dies last

* Fix for JS

* Better check for sign

* Thanks to cbatson

* Add modInverse method

* Add bitcount(),isPositive,isEven and isOdd methods

* Add testBit() method

* Add tests for modInverse,isPositive,isEven,isOdd, testBit, bitCount

* Fix bitcount

* Try to fix bitCount()

* Debug cpp (couldn't repeat)

* Fix debug

* Debug

* [cpp] Fix bitCount()

* Add square() and Karatsuba multiplication

* Add test for Karatsuba square

* Add divMod and hashCode functions.Auto convert to string

* Fix last commit.

* Fix overflow behaviour

* Add negative base for modInverse

* Add OR and AND (bigint) operations

* Fix for negative numbers

* Correct OR for bigint

* For OR small optimization

* Add XOR and NOT bitwise operators

* Add bit operations and powerOfTwo method

* Test bit for negative numbers

* Use enum abstract for exception. Reformat the code

* Add missing tests

* Temp fix for BigIntException

* Fix for BigIntExceptions

* Fix BigIntExceptions test

* Fix tests for String conversion

* Temp fix for flipBit (python)

* Add @:noDoc and @:noCompletion .Fix for BigIntException and tests

* Change var NEGATIVE_ONE to MINUS_ONE

* New implementation for BigIntException class

* Remove memory limit for PHP test

* Fix for php test

* Add from/to different base

* Remove trace()

* Fix for toString()

* Add Montgomery and Barrett reduction for modPow

* Add bitlen()

* Fix typo

* Add compareMonty() and subtractMonty()

* Change millerRabin().Fix compareMonty()

* Prime check for small numbers

* Debug Lua

* Always init m_data

* Trigger GitHub actions

* Debug BigIntRandomPrime [php,python]

* Fix syntax err

* Lua test - modInverse

* Is toString() the problem ?

* Debug Lua

* Debug compare()

* Is the unsigned shift the problem ?

* Test sign() Lua

* Lua clampInt32 for compareInt()

* Check compare() - Lua

* Clamp left shift - Lua

* [lua] Shift left doesn't work correctly

* Test left shift for Lua

* Sign test - Lua

* Clamp lshift too, because -1 << 1 is 4294967294, but should be -2

* Check for nil

* What is nil ?

* Check testPrimeNumber

* Lua nil debug

* Debug

* Debug Lua

* Dbg Lua

* Debug

* Remove debug ( failed)

* Last chance Lua

* Init m_count

* Set m_data to null

* Debug

* Debug

* Debug

* How num is null?

* Trigger GitHub actions

* Spooky compare call

* wheel of fortune

* Check if this it the init poblem for Lua

* Remove trace() and workaround

* Remove Lua fix

* Add test for Python

* Check why Python failed

* Check Python

* Fix Python target

* Fix for Neko

* Fix Neko II

* Remove prime test for Lua and Cppia

* Fix for Cppia (see issue #11897)

* Reorder const

* Fix typo

* Reorder static var

* Workaround Cppia

* Fix Cppia II

* Give up

* I won't never give up, no, never give up, no

* Stop more Lua tests. Run cppia tests

* Add null check and test.

* Fix syntax error

* Fix Int+BigInt operation

* Fix String conversion`

* Add rudimentary documentation
flashultra 2 ماه پیش
والد
کامیت
94dd308f39

+ 649 - 0
std/haxe/math/bigint/BigInt.hx

@@ -0,0 +1,649 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.ds.Vector;
+import haxe.io.Bytes;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+@:allow(haxe.math.bigint)
+abstract BigInt(BigInt_) {
+	//-----------------------------------------------------------------------
+	// Public constants
+	//-----------------------------------------------------------------------
+	/** The BigInt value of 0 **/
+	public static var ZERO(default, null):BigInt = new BigInt(BigInt_.fromInt(0));
+	/** The BigInt value of 1 **/
+	public static var ONE(default, null):BigInt = new BigInt(BigInt_.fromInt(1));
+	/** The BigInt value of 2 **/
+	public static var TWO(default, null):BigInt = new BigInt(BigInt_.fromInt(2));
+	/** The BigInt value of -1 **/
+	public static var MINUS_ONE(default, null):BigInt = new BigInt(BigInt_.fromInt(-1));
+	
+	//-----------------------------------------------------------------------
+	// Private constants
+	//-----------------------------------------------------------------------
+	#if !cppia
+	private static var SMALL_PRIMES_PRODUCT:BigInt = BigInt.fromString("1451887755777639901511587432083070202422614380984889313550570919659315177065956574359078912654149167643992684236991305777574330831666511589145701059710742276692757882915756220901998212975756543223550490431013061082131040808010565293748926901442915057819663730454818359472391642885328171302299245556663073719855");
+	#end
+	//-----------------------------------------------------------------------
+	// Public interface
+	//-----------------------------------------------------------------------
+
+	/**
+		Returns the sign of this `BigInt`.
+		@return -1 if negative, 1 if positive, 0 if zero.
+	**/
+	public inline function sign():Int {
+		return BigInt_.sign1(this);
+	}
+
+	/**
+		Checks if this `BigInt` is equal to zero.
+		@return `true` if the value is 0, otherwise `false`.
+	**/
+	public inline function isZero():Bool {
+		return BigInt_.isZero1(this);
+	}
+
+	/**
+		Checks if this `BigInt` is a negative number.
+		@return `true` if the value is less than 0, otherwise `false`.
+	**/
+	public inline function isNegative():Bool {
+		return BigInt_.isNegative1(this);
+	}
+
+	/**
+		Checks if this `BigInt` is a positive number.
+		@return `true` if the value is greater than 0, otherwise `false`.
+	**/
+	public inline function isPositive():Bool {
+		return BigInt_.isPositive1(this);
+	}
+
+	/**
+		Checks if this `BigInt` is an odd number.
+		@return `true` if the value is odd, otherwise `false`.
+	**/
+	public inline function isOdd():Bool {
+		return BigInt_.isOdd1(this);
+	}
+
+	/**
+		Checks if this `BigInt` is an even number.
+		@return `true` if the value is even, otherwise `false`.
+	**/
+	public inline function isEven():Bool {
+		return BigInt_.isEven1(this);
+	}
+
+	/**
+		Returns the minimum of this `BigInt` and another.
+		@param other The `BigInt` to compare with.
+		@return The smaller of the two `BigInt` values.
+	**/
+	public inline function min(other:BigInt):BigInt {
+		return new BigInt(this.min(other));
+	}
+
+	/**
+		Returns the maximum of this `BigInt` and another.
+		@param other The `BigInt` to compare with.
+		@return The larger of the two `BigInt` values.
+	**/
+	public inline function max(other:BigInt):BigInt {
+		return new BigInt(this.max(other));
+	}
+
+	/**
+		Returns the string representation of this `BigInt` in the specified base.
+		@param radix The base for the conversion (e.g., 10 for decimal, 16 for hexadecimal).
+		@return The string representation of the number.
+	**/
+	public inline function toString(radix:Int=10):String {
+		return BigInt_.toString1(this,radix);
+	}
+
+	/**
+		Returns the hexadecimal string representation of this `BigInt`.
+		This is a shorthand for `toString(16)`.
+		@return The hexadecimal string.
+	**/
+	public inline function toHex():String {
+		return BigInt_.toHex1(this);
+	}
+
+	/**
+		Converts this `BigInt` to a `Bytes` sequence (big-endian).
+		@return A `Bytes` object representing the number's magnitude.
+	**/
+	public inline function toBytes():Bytes {
+		return BigInt_.toBytes1(this);
+	}
+
+	/**
+		Converts this `BigInt` to a `Vector` of `Int`s.
+		@param output The vector to write the integer words into.
+		@return The number of words written to the vector.
+	**/
+	public inline function toInts(output:Vector<Int>):Int {
+		return BigInt_.toInts1(this, output);
+	}
+
+	/**
+		Creates a new BigInt from an Int.
+		@param value The Int value.
+	**/
+	public static inline function fromInt(value:Int):BigInt {
+		return new BigInt(BigInt_.fromInt(value));
+	}
+
+	/**
+		Creates a `BigInt` by parsing a string with a given radix.
+		@param value The string representation of the number.
+		@param radix The base of the number in the string (e.g., 10 or 16).
+		@return A new `BigInt` instance.
+	**/
+	public static inline function fromString(value:String,radix:Int=10):BigInt {
+		return new BigInt(BigInt_.fromString(value,radix));
+	}
+
+	/**
+		Creates a `BigInt` from a hexadecimal string. Assumes signed representation.
+		This is a shorthand for `fromHexSigned(value)`.
+		@param value The hexadecimal string.
+		@return A new `BigInt` instance.
+	**/
+	public static inline function fromHex(value:String):BigInt {
+		return fromHexSigned(value);
+	}
+
+	/**
+		Creates a `BigInt` from a signed hexadecimal string.
+		@param value The hexadecimal string.
+		@return A new `BigInt` instance.
+	**/
+	public static inline function fromHexSigned(value:String):BigInt {
+		return new BigInt(BigInt_.fromHexSigned(value));
+	}
+
+	/**
+		Creates a `BigInt` from an unsigned hexadecimal string.
+		@param value The hexadecimal string.
+		@return A new `BigInt` instance.
+	**/
+	public static inline function fromHexUnsigned(value:String):BigInt {
+		return new BigInt(BigInt_.fromHexUnsigned(value));
+	}
+
+	/**
+		Creates a `BigInt` from a `Vector` of unsigned `Int` words.
+		@param value A vector containing the integer words of the number.
+		@param length The number of words to use from the vector. If 0, uses the whole vector.
+		@return A new `BigInt` instance.
+	**/
+	public static inline function fromUnsignedInts(value:Vector<Int>, length:Int = 0):BigInt {
+		return new BigInt(BigInt_.fromUnsignedInts(value, length));
+	}
+
+	/**
+		Creates a `BigInt` from a `Bytes` sequence.
+		@param value The `Bytes` object to read from.
+		@param offset The starting position in the bytes.
+		@param length The number of bytes to read. If 0, reads to the end.
+		@return A new `BigInt` instance.
+	**/
+	public static inline function fromBytes(value:Bytes, offset:Int = 0, length:Int = 0):BigInt {
+		return new BigInt(BigInt_.fromBytes(value, offset, length));
+	}
+
+	/**
+		Generates a pseudo-random `BigInt` with a specified number of bits.
+		@param bits The bit-length of the random number. The result is in the range `[0, 2^bits - 1]`.
+		@return A new `BigInt` with a random value.
+	**/
+	public static inline function random(bits:Int32):BigInt {
+		return new BigInt(BigInt_.random(bits));
+	}
+
+	/**
+		Generates a pseudo-random `BigInt` within a given range.
+		@param min The inclusive minimum value.
+		@param max The inclusive maximum value.
+		@return A random `BigInt` such that `min <= result <= max`.
+	**/
+	public static function randomInRange(min:BigInt, max:BigInt):BigInt {
+		return new BigInt(BigInt_.randomInRange(min, max));
+	}
+
+	/**
+		Generates a probable prime number with a specified bit-length.
+		@param bits The desired bit-length of the prime.
+		@param tolerance The certainty level for the primality test.
+		@return A probable prime `BigInt`.
+	**/
+	public static function randomPrime(bits:Int32, tolerance:UInt):BigInt {
+		return new BigInt(BigInt_.randomPrime(bits, tolerance));
+	}
+
+	/**
+		Performs division and returns both the quotient and the remainder.
+		@param dividend The number to be divided.
+		@param divisor The number to divide by.
+		@return An object `{ quotient: BigInt, remainder: BigInt }`.
+	**/
+	public static function divMod(dividend:BigInt, divisor:BigInt):{quotient:BigInt, remainder:BigInt} {
+		var result:{quotient:BigInt_, remainder:BigInt_} = BigInt_.divMod(dividend, divisor);
+		return {quotient: (new BigInt(result.quotient)), remainder: (new BigInt(result.remainder))};
+	}
+
+	/**
+		Gets the value of a single bit at the specified index.
+		@param index The zero-based index of the bit to get.
+		@return 1 if the bit is set, otherwise 0.
+	**/
+	public inline function getBit(index:Int):Int {
+		return BigIntArithmetic.getBit(this, index);
+	}
+
+	/**
+		Returns the absolute value of this `BigInt`.
+		@return A new `BigInt` representing the absolute value.
+	**/
+	public function abs():BigInt {
+		return new BigInt(this.abs());
+	}
+
+	/**
+		Calculates the modular multiplicative inverse of this `BigInt`.
+		@param modulus The modulus for the inverse operation.
+		@return A new `BigInt` `x` such that `(this * x) % modulus == 1`.
+	**/
+	public function modInverse(modulus:BigInt_):BigInt {
+		return new BigInt(this.modInverse(modulus));
+	}
+
+	/**
+		Finds the greatest common divisor (GCD) of this `BigInt` and another.
+		@param b The other `BigInt`.
+		@return The GCD of this and `b`.
+	**/
+	public function gcd(b:BigInt):BigInt {
+		return new BigInt(this.gcd(b));
+	}
+
+	/**
+		Finds the least common multiple (LCM) of this `BigInt` and another.
+		@param b The other `BigInt`.
+		@return The LCM of this and `b`.
+	**/
+	public function lcm(b:BigInt):BigInt {
+		return new BigInt(this.lcm(b));
+	}
+
+	/**
+		Raises this `BigInt` to the power of an integer exponent.
+		@param exponent The non-negative exponent.
+		@return A new `BigInt` representing `this` raised to the power of `exponent`.
+	**/
+	public function pow(exponent:UInt):BigInt {
+		return new BigInt(this.pow(exponent));
+	}
+
+	/**
+		Calculates `(this ^ exponent) mod modulus`.
+		@param exponent The exponent.
+		@param modulus The modulus.
+		@return The result of the modular exponentiation.
+	**/
+	public function modPow(exponent:BigInt, modulus:BigInt):BigInt {
+		return new BigInt(this.modPow(exponent, modulus));
+	}
+
+	/**
+		Calculates the square of this `BigInt`.
+		@return A new `BigInt` representing `this * this`.
+	**/
+	public function square():BigInt {
+		return new BigInt(this.square());
+	}
+
+	/**
+		Performs a primality test on this `BigInt`.
+		@param tolerance The certainty level. A higher value means a more rigorous (but slower) test.
+		@return `true` if the number is probably prime, `false` if it is definitely composite.
+	**/
+	public function isProbablePrime(tolerance:UInt):Bool {
+		return this.isProbablePrime(tolerance);
+	}
+
+	/**
+		Finds the first probable prime number greater than this `BigInt`.
+		@return The next probable prime.
+	**/
+	public function nextProbablePrime():BigInt {
+		return new BigInt(this.nextProbablePrime());
+	}
+
+	/**
+		Gets the index of the lowest-set (rightmost) bit.
+		@return The index of the rightmost '1' bit, or -1 if the number is zero.
+	**/
+	public function getLowestSetBit():Int {
+		return this.getLowestSetBit();
+	}
+
+	/**
+		Returns the number of bits of this `BigInt`.
+		@return The number of bits required to represent this number.
+	**/
+	public function bitLength():Int {
+		return this.bitLength();
+	}
+
+	/**
+		Counts the number of bits that are set to 1.
+		@return The number of set bits.
+	**/
+	public function bitCount():Int {
+		return this.bitCount();
+	}
+
+	/**
+		Tests if the bit at a given index is set.
+		@param n The index of the bit to test.
+		@return `true` if the bit is 1, otherwise `false`.
+	**/
+	public function testBit(n:Int):Bool {
+		return this.testBit(n);
+	}
+
+	/**
+		Returns a new `BigInt` with the specified bit set (to 1).
+		@param n The index of the bit to set.
+		@return A new `BigInt` with the bit set.
+	**/
+	public function setBit(n:Int):BigInt {
+		return new BigInt(this.setBit(n));
+	}
+
+	/**
+		Returns a new `BigInt` with the specified bit cleared (to 0).
+		@param n The index of the bit to clear.
+		@return A new `BigInt` with the bit cleared.
+	**/
+	public function clearBit(n:Int):BigInt {
+		return new BigInt(this.clearBit(n));
+	}
+
+	/**
+		Returns a new `BigInt` with the specified bit flipped.
+		@param n The index of the bit to flip.
+		@return A new `BigInt` with the bit flipped.
+	**/
+	public function flipBit(n:Int):BigInt {
+		return new BigInt(this.flipBit(n));
+	}
+
+	/**
+		Calculates 2 raised to the power of the given exponent.
+		@param exponent The exponent.
+		@return A `BigInt` equal to `2^exponent`.
+	**/
+	public static function getPowerOfTwo(exponent:Int):BigInt {
+		return new BigInt(BigInt_.getPowerOfTwo(exponent));
+	}
+
+	/**
+		Generates a hash code for this `BigInt`.
+		@return An integer hash code.
+	**/
+	public function hashCode():Int {
+		return this.hashCode();
+	}
+
+	//-----------------------------------------------------------------------
+	// Operators
+	//-----------------------------------------------------------------------
+	// The declaration order of the operations is significant in Haxe.
+	// Recommended order is:
+	//	* BigInt <binOp> Int
+	//	* BigInt <binOp> BigInt
+	//	* BigInt <binOp> MutableBigInt
+	// Unary negation
+	@:op(-A) @:noCompletion @:noDoc public static inline function negate_(a:BigInt):BigInt {
+		return new BigInt(BigInt_.negate1(a));
+	}
+
+	// Binary equality
+	@:op(A == B) @:noCompletion @:noDoc public static inline function eqInt_(a:BigInt, b:Int):Bool {
+		return BigInt_.equals2Int(a, b);
+	}
+
+	@:op(A == B) @:noCompletion @:noDoc public static inline function eq_(a:BigInt, b:BigInt):Bool {
+		return BigInt_.equals2(a, b);
+	}
+
+	@:op(A == B) @:noCompletion @:noDoc public static inline function eqMutable_<T:MutableBigInt_>(a:BigInt, b:T):Bool {
+		return BigInt_.equals2(a, b);
+	}
+
+	// Binary inequality
+	@:op(A != B) @:noCompletion @:noDoc public static inline function ineqInt_(a:BigInt, b:Int):Bool {
+		return !BigInt_.equals2Int(a, b);
+	}
+
+	@:op(A != B) @:noCompletion @:noDoc public static inline function ineq_(a:BigInt, b:BigInt):Bool {
+		return !BigInt_.equals2(a, b);
+	}
+
+	@:op(A != B) @:noCompletion @:noDoc public static inline function ineqMutable_<T:MutableBigInt_>(a:BigInt, b:T):Bool {
+		return !BigInt_.equals2(a, b);
+	}
+
+	// Binary less than
+	@:op(A < B) @:noCompletion @:noDoc public static inline function ltInt_(a:BigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) < 0;
+	}
+
+	@:op(A < B) @:noCompletion @:noDoc public static inline function lt_(a:BigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) < 0;
+	}
+
+	@:op(A < B) @:noCompletion @:noDoc public static inline function ltMutable_(a:BigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) < 0;
+	}
+
+	// Binary less than or equal
+	@:op(A <= B) @:noCompletion @:noDoc public static inline function lteInt_(a:BigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) <= 0;
+	}
+
+	@:op(A <= B) @:noCompletion @:noDoc public static inline function lte_(a:BigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) <= 0;
+	}
+
+	@:op(A <= B) @:noCompletion @:noDoc public static inline function lteMutable_(a:BigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) <= 0;
+	}
+
+	// Binary greater than
+	@:op(A > B) @:noCompletion @:noDoc public static inline function gtInt_(a:BigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) > 0;
+	}
+
+	@:op(A > B) @:noCompletion @:noDoc public static inline function gt_(a:BigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) > 0;
+	}
+
+	@:op(A > B) @:noCompletion @:noDoc public static inline function gtMutable_(a:BigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) > 0;
+	}
+
+	// Binary greater than or equal
+	@:op(A >= B) @:noCompletion @:noDoc public static inline function gteInt_(a:BigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) >= 0;
+	}
+
+	@:op(A >= B) @:noCompletion @:noDoc public static inline function gte_(a:BigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) >= 0;
+	}
+
+	@:op(A >= B) @:noCompletion @:noDoc public static inline function gteMutable_(a:BigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) >= 0;
+	}
+
+	// Binary addition
+	@:commutative @:op(A + B) @:noCompletion @:noDoc public static inline function addInt_(a:BigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.addInt2(a, b));
+	}
+
+	@:op(A + B) @:noCompletion @:noDoc public static inline function add_<T:BigInt_>(a:BigInt, b:T):BigInt {
+		return new BigInt(BigInt_.add2(a, b));
+	}
+
+	@:op(A + B) @:noCompletion @:noDoc public static inline function addMutable_(a:BigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.add2(a, b));
+	}
+
+	// Binary subtraction
+	@:commutative @:op(A - B) @:noCompletion @:noDoc public static inline function subInt_(a:BigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.subInt2(a, b));
+	}
+
+	@:op(A - B) @:noCompletion @:noDoc public static inline function sub_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.sub2(a, b));
+	}
+
+	@:op(A - B) @:noCompletion @:noDoc public static inline function subMutable_(a:BigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.sub2(a, b));
+	}
+
+	// Binary multiplication
+	@:commutative @:op(A * B) @:noCompletion @:noDoc public static inline function mulInt_(a:BigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.multiplyInt2(a, b));
+	}
+
+	@:op(A * B) @:noCompletion @:noDoc public static inline function mul_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.multiply2(a, b));
+	}
+
+	@:op(A * B) @:noCompletion @:noDoc public static inline function mulMutable_(a:BigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.multiply2(a, b));
+	}
+
+	// Binary division
+	@:commutative @:op(A / B) @:noCompletion @:noDoc public static inline function divInt_(a:BigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.divideInt2(a, b));
+	}
+
+	@:op(A / B) @:noCompletion @:noDoc public static inline function div_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.divide2(a, b));
+	}
+
+	@:op(A / B) @:noCompletion @:noDoc public static inline function divMutable_(a:BigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.divide2(a, b));
+	}
+
+	// Binary modulus
+	@:commutative @:op(A % B) @:noCompletion @:noDoc public static inline function modInt_(a:BigInt, b:Int):Int {
+		return BigInt_.modulusInt2(a, b);
+	}
+
+	@:op(A % B) @:noCompletion @:noDoc public static inline function mod_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.modulus2(a, b));
+	}
+
+	@:op(A % B) @:noCompletion @:noDoc public static inline function modMutable_(a:BigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.modulus2(a, b));
+	}
+
+	// Binary OR
+	@:op(A | B) @:noCompletion @:noDoc public static inline function orInt_(a:BigInt, b:Int):BigInt {
+		return or_(a, b);
+	}
+
+	@:op(A | B) @:noCompletion @:noDoc public static inline function or_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigIntArithmetic.bitwiseOr(a, b));
+	}
+
+	@:op(A ^ B) @:noCompletion @:noDoc public static inline function xorInt_(a:BigInt, b:Int):BigInt {
+		return xor_(a, b);
+	}
+
+	@:op(A ^ B) @:noCompletion @:noDoc public static inline function xor_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigIntArithmetic.bitwiseXor(a, b));
+	}
+
+	@:op(~A) @:noCompletion @:noDoc public static inline function not_(a:BigInt):BigInt {
+		return new BigInt(BigIntArithmetic.bitwiseNot(a));
+	}
+
+	// Binary AND
+	@:op(A & B) @:noCompletion @:noDoc public static inline function andInt_(a:BigInt, b:Int):Int {
+		return BigIntArithmetic.bitwiseAndInt(a, b);
+	}
+
+	@:op(A & B) @:noCompletion @:noDoc public static inline function and_(a:BigInt, b:BigInt):BigInt {
+		return new BigInt(BigIntArithmetic.bitwiseAnd(a, b));
+	}
+
+	// Binary shift left
+	@:op(A << B) @:noCompletion @:noDoc public static inline function asl_(a:BigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.arithmeticShiftLeft2(a, b));
+	}
+
+	// Binary shift right
+	@:op(A >> B) @:noCompletion @:noDoc public static inline function asr_(a:BigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.arithmeticShiftRight2(a, b));
+	}
+
+	//-----------------------------------------------------------------------
+	// Automatic conversions
+	//-----------------------------------------------------------------------
+
+	@:from @:noCompletion @:noDoc public static inline function fromInt_(a:Int):BigInt {
+		return new BigInt(BigInt_.fromInt(a));
+	}
+
+	@:from @:noCompletion @:noDoc public static inline function fromString_(a:String):BigInt {
+		return new BigInt(BigInt_.fromString(a));
+	}
+
+	@:to @:noCompletion @:noDoc public inline function toBigInt_():BigInt_ {
+		return this;
+	}
+
+	@:to @:noCompletion @:noDoc public inline function toMutableBigInt():MutableBigInt {
+		return new MutableBigInt(MutableBigInt_.fromBigInt(this));
+	}
+
+	//-----------------------------------------------------------------------
+	// Private implementation
+	//-----------------------------------------------------------------------
+
+	@:noCompletion @:noDoc private inline function new(a:BigInt_) {
+		this = a;
+	}
+}

+ 797 - 0
std/haxe/math/bigint/BigIntArithmetic.hx

@@ -0,0 +1,797 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.math.bigint.BigIntException;
+import haxe.math.bigint.BigIntError;
+import haxe.math.bigint.BigIntHelper;
+import haxe.ds.Vector;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+/**
+	A collection of static helper functions for performing arithmetic
+	on `BigInt_` objects.
+**/
+class BigIntArithmetic {
+	/**
+		Compare a big integer with an Int.
+
+		Returns -1 if `a < b`; otherwise
+		returns 1 if `a > b`; otherwise
+		returns 0 (`a == b`).
+	**/
+	public static function compareInt(a:BigInt_, b:Int):Int {
+		if (a.m_count > 1) {
+			return (a.sign() << 1) + 1;
+		}
+		var x:Int = a.m_data.get(0);
+		var lt:Int = (x - b) ^ ((x ^ b) & ((x - b) ^ x)); // "Hacker's Delight" p. 23
+		var gt:Int = (b - x) ^ ((x ^ b) & ((b - x) ^ b));
+		return (lt >> 31) | (gt >>> 31);
+	}
+
+	/**
+		Compare two big integers.
+
+		Returns -1 if `a < b`; otherwise
+		returns 1 if `a > b`; otherwise
+		returns 0 (`a == b`).
+	**/
+	public static function compare(a:BigInt_, b:BigInt_):Int {
+		if (a != b) {
+			var c:Int = (a.sign() & 2) + (b.sign() & 1);
+			switch (c) {
+				case 0: // a and b are positive
+					if (a.m_count > b.m_count) {
+						return 1;
+					}
+					if (a.m_count < b.m_count) {
+						return -1;
+					}
+				case 1: // a is positive, b is negative
+					return 1;
+				case 2: // a is negative, b is positive
+					return -1;
+				case 3: // a and b are negative
+					if (a.m_count > b.m_count) {
+						return -1;
+					}
+					if (a.m_count < b.m_count) {
+						return 1;
+					}
+			}
+			return MultiwordArithmetic.compareUnsigned(a.m_data, b.m_data, a.m_count);
+		}
+		return 0;
+	}
+
+	/**
+		Perform the unary negation of big integer `operand` and put
+		the result into big integer `result`.
+
+		Ok for `result` and `operand` to be the same object.
+	**/
+	public static function negate(result:MutableBigInt_, operand:BigInt_):Void {
+		var c:Int = 1;
+		var x:Int32 = 0;
+		var z:Int = 0;
+		result.ensureCapacity(operand.m_count + 1, result == operand); // overflow may add a digit
+		for (i in 0...operand.m_count) {
+			x = ~operand.m_data.get(i);
+			z = x + c;
+			result.m_data.set(i, z);
+			c = (x & ~z) >>> 31; // "Hacker's Delight" p. 38
+		}
+		result.m_count = operand.m_count;
+		// detect overflow; intuitively, this can only occur for inputs of 2 ^ (32 * N - 1).
+		if ((~x & z) < 0) {
+			result.m_data.set(result.m_count++, 0);
+		} else {
+			// Handle compacting; intuitively, this can only occur for inputs of -[2 ^ (32 * N - 1)].
+			// TODO: good way to detect this specific scenario?
+			result.compact();
+		}
+	}
+
+	/**
+		Add big integer `operand2` to big integer `operand1` and put
+		the result into big integer `result`.
+
+		Ok for `result`, `operand1`, and `operand2` to be the same object.
+	**/
+	public static function add(result:MutableBigInt_, operand1:BigInt_, operand2:BigInt_):Void {
+		var c:Int = 0;
+		var x:Int32 = 0, y:Int32 = 0, z:Int = 0;
+		if (operand1.m_count == operand2.m_count) {
+			result.ensureCapacity(operand1.m_count + 1, (result == operand1) || (result == operand2));
+			for (i in 0...operand1.m_count) {
+				x = operand1.m_data.get(i);
+				y = operand2.m_data.get(i);
+				z = x + y + c;
+				result.m_data.set(i, z);
+				c = ((x & y) | ((x | y) & ~z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			result.m_count = operand1.m_count;
+		} else {
+			// longer operand is put into o1
+			var o1 = (operand1.m_count > operand2.m_count) ? operand1 : operand2;
+			var o2 = (operand1.m_count > operand2.m_count) ? operand2 : operand1;
+			result.ensureCapacity(o1.m_count + 1, (result == operand1) || (result == operand2));
+			var s:Int = o2.sign();
+			for (i in 0...o2.m_count) {
+				x = o1.m_data.get(i);
+				y = o2.m_data.get(i);
+				z = x + y + c;
+				result.m_data.set(i, z);
+				c = ((x & y) | ((x | y) & ~z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			y = s;
+			for (i in o2.m_count...o1.m_count) {
+				x = o1.m_data.get(i);
+				z = x + y + c;
+				result.m_data.set(i, z);
+				c = ((x & y) | ((x | y) & ~z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			result.m_count = o1.m_count;
+		}
+		var o:Int = (z ^ x) & (z ^ y); // "Hacker's Delight" p. 29
+		if (o < 0) // overflow flag is in sign bit
+		{
+			result.m_data.set(result.m_count++, ~(z >> 31));
+		} else {
+			result.compact(); // TODO: True that this will only ever eliminate at most one digit? Lighter way to detect?
+		}
+	}
+
+	/**
+		Add integer `operand2` to big integer `operand1` and put the
+		result into big integer `result`.
+
+		Ok for `result` and `operand1` to be the same object.
+	**/
+	public static function addInt(result:MutableBigInt_, operand1:BigInt_, operand2:Int):Void {
+		var c:Int = 0;
+		var x:Int32;
+		var y:Int = operand2;
+		var z:Int;
+
+		result.ensureCapacity(operand1.m_count + 1, result == operand1);
+		if (operand1.m_count > 1) {
+			x = operand1.m_data.get(0);
+			z = x + y;
+			c = ((x & y) | ((x | y) & ~z)) >>> 31; // "Hacker's Delight" p. 38
+			result.m_data.set(0, z);
+			y >>= 31;
+			for (i in 1...operand1.m_count - 1) {
+				x = operand1.m_data.get(i);
+				z = x + y + c;
+				result.m_data.set(i, z);
+				c = ((x & y) | ((x | y) & ~z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+		}
+		x = operand1.m_data.get(operand1.m_count - 1);
+		z = x + y + c;
+		result.m_data.set(operand1.m_count - 1, z);
+		result.m_count = operand1.m_count;
+		var o:Int = (z ^ x) & (z ^ y); // "Hacker's Delight" p. 29
+		if (o < 0) // overflow flag is in sign bit
+		{
+			result.m_data.set(result.m_count++, x >> 31);
+		} else if (result.m_count > 1) {
+			if (z == (result.m_data.get(result.m_count - 2) >> 31)) {
+				--result.m_count;
+			}
+		}
+	}
+
+	/**
+		Subtract big integer `operand2` from big integer `operand1`
+		and put the result into big integer `result`.
+
+		Ok for `result`, `operand1`, and `operand2` to be the same object.
+	**/
+	public static function subtract(result:MutableBigInt_, operand1:BigInt_, operand2:BigInt_):Void {
+		var c:Int32 = 0;
+		var x:Int = 0, y:Int = 0, z:Int = 0;
+		if (operand1.m_count == operand2.m_count) {
+			result.ensureCapacity(operand1.m_count + 1, (result == operand1) || (result == operand2));
+			for (i in 0...operand1.m_count) {
+				x = operand1.m_data.get(i);
+				y = operand2.m_data.get(i);
+				z = x - y - c;
+				result.m_data.set(i, z);
+				c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			result.m_count = operand1.m_count;
+		} else if (operand1.m_count > operand2.m_count) {
+			// operand1 is longer
+			result.ensureCapacity(operand1.m_count + 1, (result == operand1) || (result == operand2));
+			var s:Int = operand2.sign();
+			for (i in 0...operand2.m_count) {
+				x = operand1.m_data.get(i);
+				y = operand2.m_data.get(i);
+				z = x - y - c;
+				result.m_data.set(i, z);
+				c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			y = s;
+			for (i in operand2.m_count...operand1.m_count) {
+				x = operand1.m_data.get(i);
+				z = x - y - c;
+				result.m_data.set(i, z);
+				c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			result.m_count = operand1.m_count;
+		} else {
+			// operand2 is longer
+			result.ensureCapacity(operand2.m_count + 1, (result == operand1) || (result == operand2));
+			var s:Int = operand1.sign();
+			for (i in 0...operand1.m_count) {
+				x = operand1.m_data.get(i);
+				y = operand2.m_data.get(i);
+				z = x - y - c;
+				result.m_data.set(i, z);
+				c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			x = s;
+			for (i in operand1.m_count...operand2.m_count) {
+				y = operand2.m_data.get(i);
+				z = x - y - c;
+				result.m_data.set(i, z);
+				c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+			result.m_count = operand2.m_count;
+		}
+		var o:Int = (x ^ y) & (z ^ x); // "Hacker's Delight" p. 29
+		if (o < 0) // overflow flag is in sign bit
+		{
+			result.m_data.set(result.m_count++, ~(z >> 31));
+		} else {
+			result.compact(); // TODO: True that this will only ever eliminate at most one digit? Lighter way to detect?
+		}
+	}
+
+	/**
+		Subtract integer `operand2` from big integer `operand1` and
+		put the result into big integer `result`.
+
+		Ok for `result` and `operand1` to be the same object.
+	**/
+	public static function subtractInt(result:MutableBigInt_, operand1:BigInt_, operand2:Int):Void {
+		var c:Int = 0;
+		var x:Int;
+		var y:Int = operand2;
+		var z:Int;
+
+		result.ensureCapacity(operand1.m_count + 1, result == operand1);
+		if (operand1.m_count > 1) {
+			x = operand1.m_data.get(0);
+			z = x - y;
+			c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			result.m_data.set(0, z);
+			y >>= 31;
+			for (i in 1...operand1.m_count - 1) {
+				x = operand1.m_data.get(i);
+				z = x - y - c;
+				result.m_data.set(i, z);
+				c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+			}
+		}
+		x = operand1.m_data.get(operand1.m_count - 1);
+		z = x - y - c;
+		result.m_data.set(operand1.m_count - 1, z);
+		result.m_count = operand1.m_count;
+		var o:Int = (x ^ y) & (z ^ x); // "Hacker's Delight" p. 29
+		if (o < 0) // overflow flag is in sign bit
+		{
+			result.m_data.set(result.m_count++, x >> 31);
+		} else if (result.m_count > 1) {
+			if (z == (result.m_data.get(result.m_count - 2) >> 31)) {
+				--result.m_count;
+			}
+		}
+	}
+
+	/**
+		Multiply big integer `operand1` by integer `operand2` and put
+		the result into `result`.
+
+		`result` may not refer the same object as either `operand1`
+		or `operand2`; however, `operand1` and `operand2` may be the
+		same object.
+	**/
+	public static function multiplyInt(result:MutableBigInt_, operand1:BigInt_, operand2:Int):Void {
+		// TODO: Optimize.
+		multiply(result, operand1, BigInt_.fromInt(operand2));
+	}
+
+	/**
+		Multiply big integer `operand1` by big integer `operand2` and
+		put the result into `result`.
+
+		`result` may not refer the same object as either `operand1`
+		or `operand2`; however, `operand1` and `operand2` may be the
+		same object.
+	**/
+	public static function multiply(result:MutableBigInt_, operand1:BigInt_, operand2:BigInt_):Void {
+		// Implements Figure 8-1 (p. 172) from "Hacker's Delight", Second Edition; Henry S. Warren, Jr.; 2013.
+
+		if ((operand1 == result) || (operand2 == result)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		if (operand1.isZero() || operand2.isZero()) {
+			result.setFromInt(0);
+			return;
+		}
+
+		if ((operand1.bitLength() >= 2500) && (operand2.bitLength() >= 2500)) {
+			multiplyKaratsuba(result, operand1, operand2);
+			return;
+		}
+
+		var resultSize:Int = operand1.m_count + operand2.m_count;
+		result.ensureCapacity(resultSize, false); // always overwrite result
+		for (i in 0...resultSize) {
+			result.m_data.set(i, 0);
+		}
+		result.m_count = resultSize;
+
+		var b:Int, k:Int, t:Int;
+		var u:Int, v:Int, w:Int;
+		var m:Int = operand1.m_count << 1;
+		var n:Int = operand2.m_count << 1;
+
+		for (j in 0...n) {
+			v = operand2.getShort(j);
+			k = 0;
+			for (i in 0...m) {
+				u = operand1.getShort(i);
+				w = result.getShort(i + j);
+				t = u * v + w + k;
+				result.setShort(i + j, t);
+				k = t >>> 16;
+			}
+			result.setShort(j + m, k);
+		}
+
+		// Now result has the unsigned product.  Correct by
+		// subtracting v * 2 ^ (16m) if u < 0, and
+		// subtracting u * 2 ^ (16n) if v < 0.
+		// TODO: Do these as 32-bit operations.
+		if (operand1.isNegative()) {
+			b = 0;
+			for (j in 0...n) {
+				w = result.getShort(j + m);
+				v = operand2.getShort(j);
+				t = w - v - b;
+				result.setShort(j + m, t);
+				b = t >>> 31;
+			}
+		}
+		if (operand2.isNegative()) {
+			b = 0;
+			for (i in 0...m) {
+				w = result.getShort(i + n);
+				u = operand1.getShort(i);
+				t = w - u - b;
+				result.setShort(i + n, t);
+				b = t >>> 31;
+			}
+		}
+
+		result.compact();
+	}
+
+	/**
+		Divide the big integer `dividend` by the integer `divisor`.
+		The quotient of the division is put into `quotientOut`;
+		the remainder is the return value.
+
+		`quotientOut` may refer to `dividend`.
+
+		`work`, if supplied, must not refer to any of the inputs.
+	**/
+	public static function divideInt(dividend:BigInt_, divisor:Int, quotientOut:MutableBigInt_, work:MutableBigInt_ = null):Int {
+		// TODO: Consider optimizing this case.
+		var remainder = new MutableBigInt_();
+		var divisorBi = BigInt_.fromInt(divisor);
+		divide(dividend, divisorBi, quotientOut, remainder, work);
+		return remainder.m_data.get(0);
+	}
+
+	/**
+		Divide the big integer `dividend` by the big integer `divisor`.
+		The quotient of the division is put into `quotientOut`;
+		the remainder is put into `remainderOut`.
+
+		`remainderOut` may be `null` if the remainder value is not
+		needed.
+
+		`dividend` and `divisor` may refer to the same object.
+
+		`quotientOut` and `remainderOut` must not refer to the same
+		object; but either may refer to the inputs.
+
+		`work`, if supplied, must not refer to any of the inputs.
+	**/
+	public static function divide(dividend:BigInt_, divisor:BigInt_, quotientOut:MutableBigInt_, remainderOut:MutableBigInt_,
+			work:MutableBigInt_ = null):Void {
+		var c:Int = (dividend.sign() & 2) + (divisor.sign() & 1);
+		switch (c) {
+			case 0: // dividend positive, divisor positive
+				multiwordUnsignedDivide(dividend, divisor, quotientOut, remainderOut, work);
+			case 1: // dividend positive, divisor negative
+				negate(quotientOut, divisor);
+				multiwordUnsignedDivide(dividend, quotientOut, quotientOut, remainderOut, work);
+				negate(quotientOut, quotientOut);
+			case 2: // dividend negative, divisor positive
+				negate(quotientOut, dividend);
+				multiwordUnsignedDivide(quotientOut, divisor, quotientOut, remainderOut, work);
+				negate(quotientOut, quotientOut);
+				if (remainderOut != null) {
+					negate(remainderOut, remainderOut);
+				}
+			case 3: // dividend negative, divisor negative
+				if (remainderOut == null) {
+					// TODO: use work buffer rather than creating an object here
+					remainderOut = new MutableBigInt_();
+				}
+				negate(quotientOut, dividend);
+				negate(remainderOut, divisor);
+				multiwordUnsignedDivide(quotientOut, remainderOut, quotientOut, remainderOut, work);
+				negate(remainderOut, remainderOut);
+		}
+	}
+
+	/*
+		Unsigned division; inputs must not be negative.
+
+		`remainderOut` may be `null` if the remainder value is not
+		needed.
+
+		`dividend` and `divisor` may refer to the same object.
+
+		`quotientOut` and `remainderOut` must not refer to the same
+		object; but either may refer to the inputs.
+
+		`work`, if supplied, must not refer to any of the inputs.
+	 */
+	private static function multiwordUnsignedDivide(dividend:BigInt_, divisor:BigInt_, quotientOut:MutableBigInt_, remainderOut:MutableBigInt_,
+			work:MutableBigInt_ = null):Void {
+		if ((quotientOut == null) || (dividend == null) || (divisor == null)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((work == dividend) || (work == divisor) || (work == quotientOut)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		var dividendInts:Int = dividend.getUnsignedDigitCount();
+		var divisorInts:Int = divisor.getUnsignedDigitCount();
+
+		var quotientLength:Int = MultiwordArithmetic.getDivisionQuotientLengthUnsigned(dividendInts, divisorInts);
+
+		if (remainderOut != null) {
+			if (work == remainderOut) {
+				throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+			}
+			remainderOut.ensureCapacity(divisor.m_count, (remainderOut == dividend) || (remainderOut == divisor));
+		}
+		quotientOut.ensureCapacity(quotientLength + 1, (quotientOut == dividend) || (quotientOut == divisor)); // +1 in case we need leading 0 digit
+
+		if (work == null) {
+			work = new MutableBigInt_();
+		}
+		work.ensureCapacity(dividendInts + divisorInts + 1, false);
+
+		MultiwordArithmetic.divideUnsigned(dividend.m_data, dividendInts, divisor.m_data, divisorInts, quotientOut.m_data,
+			(remainderOut != null) ? remainderOut.m_data : null, work.m_data);
+
+		quotientOut.m_count = quotientLength;
+		if (quotientOut.isNegative()) {
+			quotientOut.m_data.set(quotientOut.m_count++, 0);
+		} else {
+			quotientOut.compact();
+		}
+
+		if (remainderOut != null) {
+			remainderOut.m_count = divisorInts;
+			if (remainderOut.isNegative()) {
+				remainderOut.m_data.set(remainderOut.m_count++, 0);
+			} else {
+				remainderOut.compact();
+			}
+		}
+	}
+
+	/**
+		Shift big integer `operand1` to the left by `operand2` bits
+		and put the result into big integer `result`.
+
+		Ok for `result` and `operand1` to be the same object.
+	**/
+	public static function arithmeticShiftLeft(result:MutableBigInt_, operand1:BigInt_, operand2:Int):Void {
+		if (operand2 < 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		if ((operand2 == 0) || operand1.isZero()) {
+			result.copyFrom(operand1);
+			return;
+		}
+
+		result.ensureCapacity(operand1.m_count + ((operand2 + 31) >> 5), result == operand1);
+
+		var whole:Int = operand2 >> 5; // whole digits portion
+		var n:Int = operand2 & 0x1f; // sub digit poortion
+		if (n > 0) {
+			asl32(result.m_data, whole, operand1.m_data, operand1.m_count, n);
+			result.m_count = operand1.m_count + whole + 1;
+			result.compact();
+		} else if (whole > 0) {
+			for (i in 0...operand1.m_count) {
+				result.m_data.set(operand1.m_count - i - 1 + whole, operand1.m_data.get(operand1.m_count - i - 1));
+			}
+			result.m_count = operand1.m_count + whole;
+		}
+		for (i in 0...whole) {
+			result.m_data.set(i, 0);
+		}
+	}
+
+	/**
+		Shift big integer `operand1` to the right by `operand2` bits
+		and put the result into big integer `result`.
+
+		Ok for `result` and `operand1` to be the same object.
+	**/
+	public static function arithmeticShiftRight(result:MutableBigInt_, operand1:BigInt_, operand2:Int):Void {
+		if (operand2 < 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		if ((operand2 == 0) || operand1.isZero()) {
+			result.copyFrom(operand1);
+			return;
+		}
+
+		result.ensureCapacity(operand1.m_count, result == operand1);
+
+		var whole:Int = operand2 >> 5; // whole digits portion
+		var n:Int = operand2 & 0x1f; // sub digit poortion
+		if (whole >= operand1.m_count) {
+			result.m_data.set(0, operand1.sign());
+			result.m_count = 1;
+		} else if (n > 0) {
+			MultiwordArithmetic._asr32(result.m_data, operand1.m_data, operand1.m_count, whole, n);
+			result.m_count = operand1.m_count - whole;
+			result.compact();
+		} else if (whole > 0) {
+			for (i in 0...operand1.m_count - whole) {
+				result.m_data.set(i, operand1.m_data.get(i + whole));
+			}
+			result.m_count = operand1.m_count - whole;
+		}
+	}
+
+	/**
+		Returns the value, 0 or 1, of the bit at 2^`index` place.
+	**/
+	public static inline function getBit(value:BigInt_, index:Int):Int {
+		return MultiwordArithmetic.getBitSigned(value.m_data, value.m_count, index);
+	}
+
+	/**
+		Returns the bitwise AND of `operand1` with `operand2`.
+	**/
+	public static inline function bitwiseAndInt(operand1:BigInt_, operand2:Int):Int {
+		return operand1.m_data.get(0) & operand2;
+	}
+
+	/**
+		Returns the bitwise AND of two big integers.
+		@return A new `BigInt_` holding the result.
+	**/
+	public static inline function bitwiseAnd(operand1:BigInt_, operand2:BigInt_):BigInt_ {
+		var result:MutableBigInt_ = new MutableBigInt_();
+		if ((operand1.m_count > operand2.m_count)) {
+			result.m_count = (operand2.sign() == 0) ? operand2.m_count : operand1.m_count;
+		} else {
+			result.m_count = (operand1.sign() == 0) ? operand1.m_count : operand2.m_count;
+		}
+		result.ensureCapacity(result.m_count, false);
+		for (i in 0...result.m_count) {
+			if (i > (operand1.m_count - 1)) {
+				result.m_data.set(i, operand2.m_data.get(i));
+			} else if (i > (operand2.m_count - 1)) {
+				result.m_data.set(i, operand1.m_data.get(i));
+			} else {
+				result.m_data.set(i, (operand1.m_data.get(i) & operand2.m_data.get(i)));
+			}
+		}
+		result.compact();
+		return result;
+	}
+
+	/**
+		Returns the bitwise OR of `operand1` with `operand2`.
+	**/
+	public static inline function bitwiseOr(operand1:BigInt_, operand2:BigInt_):BigInt_ {
+		var result:MutableBigInt_ = new MutableBigInt_();
+		result.m_count = (operand1.m_count > operand2.m_count) ? operand1.m_count : operand2.m_count;
+		result.ensureCapacity(result.m_count, false);
+		var operand1Positive:Bool = operand1.sign() == 0;
+		var operand2Positive:Bool = operand2.sign() == 0;
+		for (i in 0...result.m_count) {
+			if (i > (operand1.m_count - 1)) {
+				result.m_data.set(i, (operand1Positive ? operand2.m_data.get(i) : 0xffffffff));
+			} else if (i > (operand2.m_count - 1)) {
+				result.m_data.set(i, (operand2Positive ? operand1.m_data.get(i) : 0xffffffff));
+			} else {
+				result.m_data.set(i, (operand1.m_data.get(i) | operand2.m_data.get(i)));
+			}
+		}
+		result.compact();
+		return result;
+	}
+
+	/**
+		Returns the bitwise XOR of two big integers.
+		@return A new `BigInt_` holding the result.
+	**/
+	public static inline function bitwiseXor(operand1:BigInt_, operand2:BigInt_):BigInt_ {
+		var result:MutableBigInt_ = new MutableBigInt_();
+		result.m_count = (operand1.m_count > operand2.m_count) ? operand1.m_count : operand2.m_count;
+		result.ensureCapacity(result.m_count, false);
+		var operand1Positive:Bool = operand1.sign() == 0;
+		var operand2Positive:Bool = operand2.sign() == 0;
+		for (i in 0...result.m_count) {
+			if (i > (operand1.m_count - 1)) {
+				result.m_data.set(i, (operand1Positive ? operand2.m_data.get(i) : (operand2.m_data.get(i) ^ 0xffffffff)));
+			} else if (i > (operand2.m_count - 1)) {
+				result.m_data.set(i, (operand2Positive ? operand1.m_data.get(i) : (operand1.m_data.get(i) ^ 0xffffffff)));
+			} else {
+				result.m_data.set(i, (operand1.m_data.get(i) ^ operand2.m_data.get(i)));
+			}
+		}
+		result.compact();
+		return result;
+	}
+
+	/**
+		Returns the bitwise NOT (inversion) of a big integer.
+		@return A new `BigInt_` holding the result.
+	**/
+	public static inline function bitwiseNot(operand:BigInt_):BigInt_ {
+		var result:MutableBigInt_ = new MutableBigInt_();
+		result.copyFrom(operand);
+		for (i in 0...result.m_count) {
+			result.m_data.set(i, ~operand.m_data.get(i));
+		}
+		result.compact();
+		return result;
+	}
+
+	/**
+		Returns `floor(log2(input))`.
+		@param input The `BigInt_` operand.
+		@return The integer base-2 logarithm.
+	**/
+	public static function floorLog2(input:BigInt_):Int {
+		return (input.m_count << 5) - BigIntHelper.nlz(input.m_data.get(input.m_count - 1));
+	}
+
+	/**
+		Multiply two big integers using the Karatsuba algorithm for performance.
+		@param result The `MutableBigInt_` to store the product.
+		@param x The first operand.
+		@param y The second operand.
+	**/
+	public static function multiplyKaratsuba(result:MutableBigInt_, x:BigInt_, y:BigInt_):Void {
+		var n = (x.bitLength() > y.bitLength()) ? x.bitLength() : y.bitLength();
+		if (n < 2500) {
+			multiply(result, x, y);
+			return;
+		}
+		n = (n + 1) >> 1;
+		var b = new MutableBigInt_();
+		arithmeticShiftRight(b, x, n);
+		var a = new MutableBigInt_();
+		arithmeticShiftLeft(a, b, n);
+		subtract(a, x, a);
+		var d = new MutableBigInt_();
+		arithmeticShiftRight(d, y, n);
+		var c = new MutableBigInt_();
+		arithmeticShiftLeft(c, d, n);
+		subtract(c, y, c);
+		var ac = new MutableBigInt_();
+		multiplyKaratsuba(ac, a, c);
+		var bd = new MutableBigInt_();
+		multiplyKaratsuba(bd, b, d);
+		var abcd = new MutableBigInt_();
+		add(a, a, b);
+		add(c, c, d);
+		multiplyKaratsuba(abcd, a, c);
+		var op1 = new MutableBigInt_();
+		arithmeticShiftLeft(op1, bd, 2 * n);
+		var op2 = new MutableBigInt_();
+		subtract(op2, abcd, ac);
+		subtract(op2, op2, bd);
+		arithmeticShiftLeft(op2, op2, n);
+		add(op2, ac, op2);
+		add(result, op1, op2);
+	}
+
+	//-----------------------------------------------------------------------
+	// Private helpers
+	//-----------------------------------------------------------------------
+	// assumes 0 < shift < 32
+	// ok if output == input
+	private static inline function asl32(output:Vector<Int>, outputOffset:Int, input:Vector<Int>, inputSize:Int, shift:Int32):Void {
+		var x:Int = input.get(inputSize - 1) >> 31; // sign extend
+		var r:Int = 32 - shift;
+		var y:Int;
+		while (inputSize > 0) {
+			y = input[inputSize - 1];
+			x = (x << shift) | (y >>> r);
+			output.set(inputSize + outputOffset, x);
+			x = y;
+			--inputSize;
+		}
+		output.set(outputOffset, x << shift);
+	}
+
+	// assumes 0 < shift < 32
+	// ok if output == input
+	private static inline function lsl32(output:Vector<Int>, outputOffset:Int, input:Vector<Int>, inputSize:Int, shift:Int32):Void {
+		var x:Int = 0;
+		var r:Int = 32 - shift;
+		var y:Int;
+		while (inputSize > 0) {
+			y = input[inputSize - 1];
+			x = (x << shift) | (y >>> r);
+			output.set(inputSize + outputOffset, x);
+			x = y;
+			--inputSize;
+		}
+		output.set(outputOffset, x << shift);
+	}
+
+	// assumes 0 < shift < 32
+	// ok if output == input
+	private static inline function lsr32(output:Vector<Int>, input:Vector<Int>, inputSize:Int, inputOffset:Int, shift:Int32):Void {
+		var r:Int = 32 - shift;
+		var i:Int = 0;
+		while (i < inputSize - 1) {
+			output.set(i, (input.get(inputOffset + i) >>> shift) | (input.get(inputOffset + i + 1) << r));
+			++i;
+		}
+		output.set(i, input.get(inputOffset + i) >>> shift);
+	}
+
+	private static inline function copy(output:Vector<Int>, outputOffset:Int, input:Vector<Int>, inputOffset:Int, length:Int):Void {
+		for (i in 0...length) {
+			output.set(outputOffset + i, input.get(inputOffset + i));
+		}
+	}
+}

+ 40 - 0
std/haxe/math/bigint/BigIntError.hx

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+enum abstract BigIntError(String) to String {
+	/** An argument provided to a function was not valid. */
+	var INVALID_ARGUMENT = "Invalid argument";
+	/** An output buffer was too small to hold the result. */
+	var BUFFER_TOO_SMALL = "Buffer too small";
+	/** An attempt was made to divide by zero. */
+	var DIVISION_BY_ZERO = "Division by zero";
+	/** An exponent was negative where not supported. */
+	var NEGATIVE_EXPONENT = "Negative exponent";
+	/** An operation was performed that is not valid. */
+	var INVALID_OPERATION = "Invalid operation";
+	/** A modulus was negative where it must be positive. */
+	var NEGATIVE_MODULUS = "Modulus should be positive";
+	/** An operation required at least one odd number, but both were even. */
+	var EVEN_VALUES = "Both values are even";
+}

+ 38 - 0
std/haxe/math/bigint/BigIntException.hx

@@ -0,0 +1,38 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+/**
+	An exception thrown for errors during `BigInt` operations.
+	It wraps a `BigIntError` value to provide specific details.
+**/
+class BigIntException extends haxe.Exception {
+	/**
+		Creates a new `BigIntException`.
+		@param error The specific `BigIntError` that occurred.
+		@param previous An optional previous exception in a chain.
+	**/
+	public function new(error:BigIntError, ?previous:haxe.Exception) {
+		super( error, previous);
+	}
+}

+ 166 - 0
std/haxe/math/bigint/BigIntHelper.hx

@@ -0,0 +1,166 @@
+/*
+ * Copyright (C)2005-2022 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+/**
+	A collection of low-level, static Int manipulation helper functions.
+**/
+class BigIntHelper {
+	/**
+		"Numbler of leading zeros" - return the number of leading
+		0-value bits in the binary representation of `x`.
+		@param x The integer to inspect.
+		@return The count of leading zeros.
+	**/
+	public static function nlz(x:Int):Int {
+		// From "Hacker's Delight", Second Edition; Henry S. Warren, Jr.; 2013. Figure 5-15, p. 102.
+		var y:Int, m:Int, n:Int;
+
+		y = -(x >>> 16);
+		m = (y >> 16) & 16;
+		n = 16 - m;
+		x = x >>> m;
+
+		y = x - 0x100;
+		m = (y >> 16) & 8;
+		n = n + m;
+		x = x << m;
+
+		y = x - 0x1000;
+		m = (y >> 16) & 4;
+		n = n + m;
+		x = x << m;
+
+		y = x - 0x4000;
+		m = (y >> 16) & 2;
+		n = n + m;
+		x = x << m;
+
+		y = x >> 14;
+		m = y & (~y >> 1);
+		return n + 2 - m;
+	}
+	
+	/**
+		Calculates the bit length of a signed 32-bit integer.
+		@param x The integer value.
+		@return The number of bits required to represent `x`.
+	**/
+	public static function bitLen(x:Int):Int {
+		var sign:Int = (x<0)?-1:0;
+		return ( 32 - BigIntHelper.nlz(x^sign) );
+	}
+
+	/**
+		"Ceiling power of two" -- round up to the least power of two
+		greater than or equal to input `x`, which is interpreted as
+		unsigned.
+	**/
+	public static function clp2(x:Int32):Int {
+		// From "Hacker's Delight", Second Edition; Henry S. Warren, Jr.; 2013. Figure 3-3, p. 62.
+		x = x - 1;
+		x = x | (x >> 1);
+		x = x | (x >> 2);
+		x = x | (x >> 4);
+		x = x | (x >> 8);
+		x = x | (x >> 16);
+		return x + 1;
+	}
+
+	/**
+		Unsigned greater than comparison.
+
+		Returns `true` if `a > b` when both `a` and `b` are
+		interpreted as unsigned integers; `false` otherwise.
+	**/
+	public static inline function u32gtu32(a:Int, b:Int):Bool {
+		return (a ^ -2147483648) > (b ^ -2147483648); // unsigned comparison, see "Hacker's Delight" p. 25.
+	}
+
+	/**
+		Integer division of unsigned 32-bit integer by unsigned 16-bit integer.
+
+		Result is undefined when `divisor` <= 0 or `divisor` >= 2^16.
+	**/
+	public static function u32divu16(dividend:Int32, divisor:Int32):Int {
+		/*
+			Complicated because Haxe's division is always performed as
+			floating-point.  Here we rely on the ability to exactly represent
+			a 31-bit integer as a Float.  In other words, 64-bit floating
+			point is required.
+
+			TODO: Implement a method without this restriction.
+			TODO: Consider C++-specific optimization here.
+		 */
+
+		// From "Hacker's Delight", Second Edition; Henry S. Warren, Jr.; 2013. Section 9-3, p. 192.
+		var t:Int = divisor >> 31;
+		var nprime:Int = dividend & ~t;
+		var q:Int32 = Std.int((nprime >>> 1) / divisor) << 1;
+		var r:Int = dividend - q * divisor;
+		var c:Int = u32geu32(r, divisor) ? 1 : 0;
+		return q + c;
+	}
+
+	/**
+		Unsigned greater than or equal comparison.
+		Returns `true` if `a >= b` when both `a` and `b` are
+		interpreted as unsigned integers; `false` otherwise.
+	**/
+	public static inline function u32geu32(a:Int, b:Int):Bool {
+		return (a ^ -2147483648) >= (b ^ -2147483648); // unsigned comparison, see "Hacker's Delight" p. 25.
+	}
+
+	/**
+		Number of trailing zeros - return the number of trailing
+		0-value bits 
+	**/
+	public static function ntz(x:Int32):Int {
+		if (x == 0)
+			return 32;
+		var y:Int;
+		var n:Int = 31;
+		y = x << 16;
+		if (y != 0) {
+			n -= 16;
+			x = y;
+		}
+		y = x << 8;
+		if (y != 0) {
+			n -= 8;
+			x = y;
+		}
+		y = x << 4;
+		if (y != 0) {
+			n -= 4;
+			x = y;
+		}
+		y = x << 2;
+		if (y != 0) {
+			n -= 2;
+			x = y;
+		}
+		return (n - ((x << 1) >>> 31));
+	}
+}

+ 100 - 0
std/haxe/math/bigint/BigIntTools.hx

@@ -0,0 +1,100 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.math.bigint.BigIntException;
+import haxe.math.bigint.BigIntError;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+/**
+	A collection of high-level static utility functions for `BigInt`.
+**/
+class BigIntTools {
+	/**
+		Checks if a `BigInt` value is null.
+		@param value The `BigInt` to check.
+		@return `true` if the value is null.
+	**/
+	public static inline function isNull(value:BigInt):Bool {
+		var a:BigInt_ = value;
+		return a == null;
+	}
+
+	/**
+		Checks if a dynamic value is a `BigInt`.
+		@param value The value to check.
+		@return `true` if the value is of type `BigInt`.
+	**/
+	public static inline function isBigInt(value:Dynamic):Bool {
+		return Std.isOfType(value, BigInt_);
+	}
+
+	/**
+		Casts a dynamic value to a `BigInt`.
+		@param value The value to cast.
+		@return The value as a `BigInt`.
+	**/
+	public static inline function castFrom(value:Dynamic):BigInt {
+		return new BigInt(Std.downcast(value, BigInt_));
+	}
+
+	/**
+		Parses a dynamic value into an unsigned `BigInt`.
+		Supports `String`, `Int`, and other `BigInt` types.
+		@param value The value to parse.
+		@return A new `BigInt` instance.
+	**/
+	public static function parseValueUnsigned(value:Dynamic):BigInt {
+		var bi:BigInt;
+		if (Std.isOfType(value, String)) {
+			bi = parseStringUnsigned(cast(value, String));
+		} else if (isBigInt(value)) {
+			var t = new MutableBigInt_();
+			t.copyFrom(castFrom(value));
+			return new BigInt(t);
+		} else if (Std.isOfType(value, Int)) {
+			bi = BigInt.fromInt(cast(value, Int));
+		} else {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		return bi;
+	}
+
+	/**
+		Parses a string representing an unsigned integer into a `BigInt`.
+		This internal helper handles decimal strings, and hexadecimal strings
+		that are prefixed with "0x".
+		@param value The string to be parsed.
+		@return A new `BigInt` instance representing the unsigned value.
+	**/
+	private static function parseStringUnsigned(value:String):BigInt {
+		var result = new MutableBigInt_();
+		if (StringTools.startsWith(value, "0x")) {
+			result.setFromHexUnsigned(value.substr(2));
+		} else {
+			result.setFromString(value);
+		}
+		var result2:MutableBigInt = result;
+		return result2;
+	}
+}

+ 1635 - 0
std/haxe/math/bigint/BigInt_.hx

@@ -0,0 +1,1635 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.ds.Vector;
+import haxe.io.Bytes;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+@:noCompletion
+@:noDoc
+@:allow(unit)
+@:allow(haxe.math.bigint)
+class BigInt_ {
+
+	private var m_count:Int = 0;
+	private var m_data:Vector<Int>;
+
+	private static inline var s_firstCachedValue:Int = -16;
+	private static inline var s_lastCachedValue:Int = 16;
+	private static var s_cache:Vector<BigInt_> = null;
+	
+	//-----------------------------------------------------------------------
+	// Public interface
+	//-----------------------------------------------------------------------
+	/**
+		Returns the absolute value of this `BigInt`.
+		@return A new `BigInt` with the absolute value.
+	**/
+	public inline function abs():BigInt_ {
+		if (this.sign() < 0) {
+			return BigInt_.negate1(this);
+		}
+		var r = new MutableBigInt_();
+		r.copyFrom(this);
+		return r;
+	}
+
+	/**
+		Calculates the greatest common divisor (GCD) of this and another `BigInt`.
+		@param b The other `BigInt`.
+		@return The GCD of the two numbers.
+	**/
+	public function gcd(b:BigInt_):BigInt_ {
+		var m:BigInt_ = this.abs();
+		b = b.abs();
+		var t:BigInt_;
+		while (!equals2Int(b, 0)) {
+			t = m;
+			m = b;
+			b = modulus2(t, m);
+		}
+		return m;
+	}
+
+	/**
+		Calculates the least common multiple (LCM) of the specified big integer numbers.
+		@param b The other `BigInt`.
+		@return The LCM of the two numbers.
+	**/
+	public function lcm(b:BigInt_):BigInt_ {
+		var m:BigInt_ = this.abs();
+		var n:BigInt_ = b.abs();
+		return BigInt_.divide2(BigInt_.multiply2(m, n), m.gcd(n));
+	}
+
+	/**
+		Returns `true` if this big integer is equivalent to 0, otherwise returns `false`.
+	**/
+	public inline function isZero():Bool {
+		return (m_count == 1) && (m_data.get(0) == 0);
+	}
+
+	/**
+		Returns `true` if this big integer is less than 0, otherwise returns `false`.
+	**/
+	public inline function isNegative():Bool {
+		return m_data.get(m_count - 1) < 0;
+	}
+
+	/**
+		Returns `true` if this big integer is greater than or equal to 0.
+		@return `true` if the value is non-negative.
+	**/
+	public function isPositive():Bool {
+		return m_data.get(m_count - 1) >= 0;
+	}
+
+	/**
+		Returns `true` if this big integer is an odd number.
+		@return `true` if the last bit is 1.
+	**/
+	public function isOdd():Bool {
+		return ((m_data.get(0) & 1) == 1);
+	}
+
+	/**
+		Returns `true` if this big integer is an even number.
+		@return `true` if the last bit is 0.
+	**/
+	public function isEven():Bool {
+		return ((m_data.get(0) & 1) == 0);
+	}
+
+	/**
+		Retrieve the sign value of this big integer.
+		@return 0 if positive or zero, -1 if negative.
+	**/
+	public inline function sign():Int {
+		return (m_data.get(m_count - 1) >> 31 != 0) ? -1 : 0;
+	}
+
+	/**
+		Gets the index of the lowest-set (rightmost) '1' bit.
+		@return The index of the rightmost set bit, or -1 if the number is zero.
+	**/
+	public function getLowestSetBit():Int {
+		if (this.isZero())
+			return -1;
+		var result:Int = -1;
+		var i:Int = 0;
+		var b = m_data.get(0);
+		while (b == 0) {
+			i++;
+			b = m_data.get(i);
+		}
+		result = (i << 5) + BigIntHelper.ntz(b);
+		return result;
+	}
+
+	/**
+		Returns the number of bits in the minimal two's-complement representation.
+		@return The bit length of the number.
+	**/
+	public function bitLength():Int {
+		if (m_count <= 0)
+			return 0;
+		return (32 * m_count - BigIntHelper.nlz(m_data.get(m_count - 1) ^ sign()));
+	}
+
+	/**
+		Returns the number of bits set to 1 in the two's-complement representation.
+		@return The count of set bits (population count).
+	**/
+	public function bitCount():Int {
+		var totalBits:Int = 0;
+		var x:Int32;
+		for (n in 0...this.m_count) {
+			x = this.m_data.get(n);
+			x = x - ((x >> 1) & 0x55555555);
+			x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+			x = (x + (x >> 4)) & 0x0F0F0F0F;
+			x = x + (x >> 8);
+			x = x + (x >> 16);
+			totalBits += x & 0x0000003F;
+		}
+		return totalBits;
+	}
+
+	/**
+		Tests whether the bit at the specified index is set.
+		@param n The index of the bit to test.
+		@return `true` if the bit is 1, otherwise `false`.
+	**/
+	public function testBit(n:Int):Bool {
+		if (n < 0)
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		var chunk = n >> 5; // divide by 32
+		if (chunk >= m_count)
+			return (sign() < 0);
+		return ((m_data.get(chunk) & (1 << (n & 0x1f))) != 0);
+	}
+
+	/**
+		Returns a `BigInt` with the specified bit set (to 1).
+		@param n The index of the bit to set.
+		@return A new `BigInt` with the bit set, or `this` if it's already set.
+	**/
+	public function setBit(n:Int):BigInt_ {
+		return (testBit(n)) ? this : flipBit(n);
+	}
+
+	/**
+		Returns a `BigInt` with the specified bit cleared (to 0).
+		@param n The index of the bit to clear.
+		@return A new `BigInt` with the bit cleared, or `this` if it's already clear.
+	**/
+	public function clearBit(n:Int):BigInt_ {
+		return (testBit(n)) ? flipBit(n) : this;
+	}
+
+	/**
+		Returns a `BigInt` with the specified bit flipped.
+		@param n The index of the bit to flip.
+		@return A new `BigInt` with the bit at index `n` inverted.
+	**/
+	public function flipBit(n:Int):BigInt_ {
+		if (n < 0)
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		var isNegative:Bool = sign() < 0;
+		var chunk = (n >> 5) + 1;
+		var changeBit:Int = (n & 0x1f);
+		var r:MutableBigInt_ = new MutableBigInt_();
+		if (chunk > m_count) {
+			r.fixedSizeCopyFrom(this, ((changeBit == 0x1f) ? (chunk + 1) : chunk), isNegative ? 0xffffffff : 0x0);
+		} else {
+			if (chunk == m_count && changeBit == 0x1f) {
+				r.fixedSizeCopyFrom(this, chunk + 1, (isNegative ? 0xffffffff : 0));
+			} else {
+				r.fixedSizeCopyFrom(this, m_count, 0);
+			}
+		}
+		#if (python || php)
+		// Temp fix for issues #10995
+		if ( changeBit == 31) {
+			r.m_data.set(chunk - 1, r.m_data.get(chunk - 1) ^ (1 << 31));
+		} else {
+			r.m_data.set(chunk - 1, r.m_data.get(chunk - 1) ^ (1 << changeBit));
+		}
+		#else
+		r.m_data.set(chunk - 1, r.m_data.get(chunk - 1) ^ (1 << changeBit));
+		#end
+		r.compact();
+		return r;
+	}
+
+	/**
+		Returns a `BigInt` whose value is (2^exponent).
+		@param exponent The exponent to raise 2 to.
+		@return A new `BigInt` equal to 2 to the power of `exponent`.
+	**/
+	public static inline function getPowerOfTwo(exponent:Int):BigInt_ {
+		var num = BigInt_.fromInt(1);
+		var r = arithmeticShiftLeft2(num, exponent);
+		return r;
+	}
+
+	/**
+		Computes the hash code for this `BigInt`.
+		@return An integer hash code.
+	**/
+	public function hashCode():Int {
+		var hash:Int32 = 0;
+		for (n in 0...this.m_count) {
+			hash = 31 * hash + this.m_data.get(n);
+		}
+		return hash;
+	}
+
+	/**
+		Tests if this `BigInt` is probably prime, using the Miller-Rabin test.
+		@param tolerance The number of iterations. Higher values increase certainty.
+		@return `true` if the number is probably prime, `false` if it is definitely composite.
+	**/
+	public function isProbablePrime(tolerance:Int):Bool {
+		if (tolerance <= 0)
+			return true;
+		var b:BigInt_ = this.abs();
+		if (equals2Int(b, 1))
+			return false;
+		if (equals2Int(b, 2))
+			return true;
+		if (b.m_data.get(0) & 1 == 0)
+			return false;
+
+		var rounds:Int = 0;
+		if (b.m_count <= 4) {
+			rounds = (tolerance > 64) ? 64 : tolerance;
+		} else if (b.m_count < 8) {
+			rounds = 32;
+		} else if (b.m_count < 16) {
+			rounds = 16;
+		} else if (b.m_count < 24) {
+			rounds = 8;
+		} else if (b.m_count < 32) {
+			rounds = 4;
+		} else {
+			rounds = 2;
+		}
+		rounds = (tolerance < rounds) ? tolerance : rounds;
+		return b.millerRabin(rounds);
+	}
+
+	/**
+		Returns the first integer greater than this `BigInt` that is probably prime.
+		@return A new `BigInt` representing the next probable prime.
+	**/
+	public function nextProbablePrime():BigInt_ {
+		var r = new MutableBigInt_();
+		r.copyFrom(this);
+		if (m_data.get(0) & 1 == 0)
+			BigIntArithmetic.addInt(r, r, 1);
+		do {
+			BigIntArithmetic.addInt(r, r, 2);
+		} while (!r.isProbablePrime(1));
+		r.compact();
+		return r;
+	}
+
+	/**
+		Test for numeric equality between this big integer and another.
+		@param other The `BigInt` to compare with.
+		@return `true` if the values are equal, otherwise `false`.
+	**/
+	public function equals(other:BigInt_):Bool {
+		if (this.m_count != other.m_count) {
+			return false;
+		}
+		for (n in 0...this.m_count) {
+			if (this.m_data.get(n) != other.m_data.get(n)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+		Test for numeric equality between this big integer and a standard `Int`.
+		@param other The `Int` to compare with.
+		@return `true` if the values are equal, otherwise `false`.
+	**/
+	public function equalsInt(other:Int):Bool {
+		if (this.m_count != 1) {
+			return false;
+		}
+		return m_data.get(0) == other;
+	}
+
+	/**
+		Returns the minimum of this and another `BigInt`.
+		@param other The `BigInt` to compare with.
+		@return The smaller of the two values.
+	**/
+	public function min(other:BigInt_):BigInt_ {
+		return (BigIntArithmetic.compare(this, other) < 0) ? this : other;
+	}
+
+	/**
+		Returns the maximum of this and another `BigInt`.
+		@param other The `BigInt` to compare with.
+		@return The larger of the two values.
+	**/
+	public function max(other:BigInt_):BigInt_ {
+		return (BigIntArithmetic.compare(this, other) > 0) ? this : other;
+	}
+
+	/**
+		Get the value in decimal form.
+		@return The string representation of the number in base 10.
+	**/
+	public inline function toString():String {
+		return MultiwordArithmetic.toDecimalSigned(m_data, m_count);
+	}
+	
+	/**
+		Get the value in a specified base.
+		@param radix The base for the string conversion.
+		@return The string representation of the number in the given `radix`.
+	**/
+	public inline function toBase(radix:Int):String 
+	{
+		return MultiwordArithmetic.toBaseString(m_data, m_count,radix);
+	}
+
+	/**
+		Get the value in hexadecimal form.
+		@return The string representation of the number in base 16.
+	**/
+	public function toHex():String {
+		var sb = new StringBuf();
+		var i:Int = m_count;
+		while (--i >= 0) {
+			var v = m_data.get(i);
+			for (j in 0...8) {
+				var c:Int = (v >> 28) & 0x0f;
+				v <<= 4;
+				c = (c < 10) ? (c + 48) : (c - 10 + 97);
+				sb.addChar(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+		Get the value as bytes, in big-endian order.
+		@return A `Bytes` object containing the two's-complement representation.
+	**/
+	public function toBytes():Bytes {
+		var result = Bytes.alloc(m_count << 2);
+		for (i in 0...m_count) {
+			var v:Int = m_data.get(m_count - i - 1);
+			result.set((i << 2) + 0, (v >> 24) & 0xff);
+			result.set((i << 2) + 1, (v >> 16) & 0xff);
+			result.set((i << 2) + 2, (v >> 8) & 0xff);
+			result.set((i << 2) + 3, (v >> 0) & 0xff);
+		}
+		return result;
+	}
+
+	/**
+		Get the value as a vector of Ints.
+
+		Values go from less significant to more significant with
+		increasing index in the vector.
+
+		@param output The `Vector` to write the words into.
+		@return The number of `Int`s required to store the value.
+	**/
+	public function toInts(output:Vector<Int>):Int {
+		if (output != null) {
+			var n:Int = (m_count > output.length) ? output.length : m_count;
+			for (i in 0...n) {
+				output.set(i, m_data.get(i));
+			}
+		}
+		return m_count;
+	}
+
+	/**
+		Creates a big integer with value `value`.
+		@param value The integer value.
+		@return A new `BigInt_` instance.
+	**/
+	public static function fromInt(value:Int):BigInt_ {
+		var c = getCachedValue(value);
+		if (c == null) {
+			c = newFromInt(value);
+		}
+		return c;
+	}
+
+	/**
+		Creates a big integer with the value represented by the string `value`.
+		@param value The string to parse.
+		@param radix The base of the number in the string.
+		@return A new `BigInt_` instance.
+	**/
+	public static function fromString(value:String,radix:Int=10):BigInt_ {
+		var bi = new MutableBigInt_();
+		bi.setFromString(value,radix);
+		return bi;
+	}
+
+	/**
+		Creates a big integer with the signed value represented by the hexadecimal string `value`.
+		@param value The hexadecimal string.
+		@return A new `BigInt_` instance.
+	**/
+	public static function fromHexSigned(value:String):BigInt_ {
+		var bi = new MutableBigInt_();
+		bi.setFromHexSigned(value);
+		return bi;
+	}
+
+	/**
+		Creates a big integer with the unsigned value represented by the hexadecimal string `value`.
+		@param value The hexadecimal string.
+		@return A new `BigInt_` instance.
+	**/
+	public static function fromHexUnsigned(value:String):BigInt_ {
+		var bi = new MutableBigInt_();
+		bi.setFromHexUnsigned(value);
+		return bi;
+	}
+
+
+	/**
+		Creates a big integer from a `Bytes` sequence.
+		@param value The bytes to convert.
+		@param offset The starting offset in the bytes.
+		@param length The number of bytes to read.
+		@return A new `BigInt_` instance.
+	**/
+	public static function fromBytes(value:Bytes, offset:Int = 0, length:Int = 0):BigInt_ {
+		var bi = new MutableBigInt_();
+		bi.setFromBigEndianBytesSigned(value, offset, length);
+		return bi;
+	}
+
+	/**
+		Creates a big integer with the value represented by the integer vector `value`.
+		@param value The vector of integer words.
+		@param length The number of words to use from the vector.
+		@return A new `BigInt_` instance.
+	**/
+	public static function fromUnsignedInts(value:Vector<Int>, length:Int = 0):BigInt_ {
+		var bi = new MutableBigInt_();
+		bi.setFromUnsignedInts(value, length);
+		return bi;
+	}
+
+	/**
+		Calculates the square of this `BigInt`.
+		@return A new `BigInt` equal to `this * this`.
+	**/
+	public function square():BigInt_ {
+		var r:MutableBigInt_ = new MutableBigInt_();
+		BigIntArithmetic.multiply(r, this, this);
+		return r;
+	}
+
+	/**
+		Calculates `(this ^ exponent) mod modulus`.
+		@param exponent The exponent.
+		@param modulus The modulus.
+		@return The result of the modular exponentiation.
+	**/
+	#if neko
+	public function modPow(exponent:BigInt_, modulus:BigInt_):BigInt_ {
+		if (BigIntArithmetic.compareInt(exponent, 0) < 0)
+			throw new BigIntException(BigIntError.NEGATIVE_EXPONENT);
+		if (this.isZero())
+			return (BigIntArithmetic.compareInt(exponent, 0) == 0 ? BigInt.fromInt(1) : this);
+		var r = BigInt_.newFromInt(1);
+		var p:BigInt_ = this;
+		while (true) {
+			if (BigIntArithmetic.bitwiseAndInt(exponent, 1) == 1)
+				r = modulus2(multiply2(p, r), modulus);
+			exponent = BigInt_.arithmeticShiftRight2(exponent, 1);
+			if (BigIntArithmetic.compareInt(exponent, 0) == 0)
+				break;
+			p = modulus2(multiply2(p, p), modulus);
+		}
+		return r;
+	}
+	#else
+	public function modPow(exponent:BigInt_, modulus:BigInt_):BigInt_ {
+		if (BigIntArithmetic.compareInt(modulus, 0) < 0)
+			throw BigIntError.NEGATIVE_MODULUS;
+		if (BigIntArithmetic.compareInt(modulus, 1) == 0)
+			return BigInt.fromInt(0);
+		if (BigIntArithmetic.compareInt(exponent, 0) == 0)
+			return BigInt.fromInt(1);
+		if (this.isZero())
+			return BigInt.fromInt(0);
+		var negExponent:Bool = (BigIntArithmetic.compareInt(exponent, 0) < 0);
+		if (negExponent)
+			exponent = BigInt_.negate1(exponent);
+		var result:BigInt_ = modulus2(this, modulus);
+		if (BigIntArithmetic.compareInt(exponent, 1) != 0) {
+			if ((modulus.m_data.get(0) & 1) == 0) {
+				result = modPowBarrett(result, exponent, modulus);
+			} else {
+				result = modPowMonty(result, exponent, modulus, true);
+			}
+		}
+		if (negExponent) {
+			result = result.modInverse(modulus);
+		}
+		return result;
+	}
+	#end
+
+	/**
+		Calculates `this` raised to the power of `exponent`.
+		@param exponent The non-negative exponent.
+		@return A new `BigInt` result.
+	**/
+	public function pow(exponent:UInt):BigInt_ {
+		if (exponent < 0)
+			throw new BigIntException(BigIntError.NEGATIVE_EXPONENT);
+		if (this.isZero())
+			return (exponent == 0 ? BigInt.fromInt(1) : this);
+		var r = BigInt_.newFromInt(1);
+		var p:BigInt_ = this;
+		while (true) {
+			if ((exponent & 1) == 1)
+				r = multiply2(p, r);
+			exponent = exponent >> 1;
+			if (exponent == 0)
+				break;
+			p = multiply2(p, p);
+		}
+		return r;
+	}
+
+	/**
+		Calculates the modular multiplicative inverse of this `BigInt` modulo `modulus`.
+		@param modulus The modulus.
+		@return A new `BigInt` `x` such that `(this * x) % modulus == 1`.
+	**/
+	public function modInverse(modulus:BigInt_):BigInt_ {
+		if (modulus.sign() == -1 || modulus.isZero())
+			throw new BigIntException(BigIntError.NEGATIVE_MODULUS);
+		if (equals2Int(modulus, 1))
+			return BigInt.ZERO;
+
+		var isModulusEven:Bool = (BigIntArithmetic.bitwiseAndInt(modulus, 1) == 0);
+		if ((BigIntArithmetic.bitwiseAndInt(this, 1) == 0) && isModulusEven || modulus.isZero())
+			return BigInt.ZERO;
+
+		var x:BigInt_ = this.abs();
+		var y:BigInt_ = MutableBigInt_.fromBigInt(modulus);
+
+		if (x.sign() == -1 || BigIntArithmetic.compare(x, modulus) >= 0)
+			x = modulus2(x, modulus);
+		if (equals2Int(x, 1))
+			return BigInt.ONE;
+
+		if ((BigIntArithmetic.bitwiseAndInt(x, 1) == 0) && (BigIntArithmetic.bitwiseAndInt(y, 1) == 0))
+			throw new BigIntException(BigIntError.EVEN_VALUES);
+
+		if (!isModulusEven) {
+			// fast odd calculation
+			return modInverseOdd(x, y);
+		}
+
+		var a:BigInt_ = fromInt(1);
+		var b:BigInt_ = fromInt(0);
+		var c:BigInt_ = fromInt(0);
+		var d:BigInt_ = fromInt(1);
+		var u:BigInt_ = MutableBigInt_.fromBigInt(x);
+		var v:BigInt_ = MutableBigInt_.fromBigInt(y);
+
+		do {
+			while ((BigIntArithmetic.bitwiseAndInt(u, 1) == 0)) {
+				u = arithmeticShiftRight2(u, 1);
+				if ((BigIntArithmetic.bitwiseAndInt(a, 1) == 1) || (BigIntArithmetic.bitwiseAndInt(b, 1) == 1)) {
+					a = add2(a, y);
+					b = sub2(b, x);
+				}
+				a = arithmeticShiftRight2(a, 1);
+				b = arithmeticShiftRight2(b, 1);
+			}
+
+			while ((BigIntArithmetic.bitwiseAndInt(v, 1) == 0)) {
+				v = arithmeticShiftRight2(v, 1);
+				if ((BigIntArithmetic.bitwiseAndInt(c, 1) == 1) || (BigIntArithmetic.bitwiseAndInt(d, 1) == 1)) {
+					c = add2(c, y);
+					d = sub2(d, x);
+				}
+
+				c = arithmeticShiftRight2(c, 1);
+				d = arithmeticShiftRight2(d, 1);
+			}
+
+			if (BigIntArithmetic.compare(u, v) >= 0) {
+				u = sub2(u, v);
+				a = sub2(a, c);
+				b = sub2(b, d);
+			} else {
+				v = sub2(v, u);
+				c = sub2(c, a);
+				d = sub2(d, b);
+			}
+		} while (!u.isZero());
+
+		if (!equals2Int(v, 1)) {
+			return BigInt.ZERO;
+		}
+
+		while (BigIntArithmetic.compareInt(c, 0) < 0) {
+			c = add2(c, modulus);
+		}
+
+		while (BigIntArithmetic.compare(c, modulus) >= 0) {
+			c = sub2(c, modulus);
+		}
+
+		if (this.sign() < 0) {
+			c = sub2(modulus, c);
+		}
+
+		return c;
+	}
+
+	/**
+		Generates a probable prime number with a specified bit-length.
+		@param bits The desired bit-length.
+		@param tolerance The certainty level for the primality test.
+		@return A probable prime `BigInt`.
+	**/
+	public static function randomPrime(bits:Int32, tolerance:Int):BigInt_ {
+		if (bits < 2)
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		if (bits == 2)
+			return ((Math.random() < 0.5) ? BigInt.TWO : BigInt.fromInt(3));
+		var r = new MutableBigInt_();
+		do {
+			var bytes = randomBytes(bits);
+			var excessBits = 8 * bytes.length - bits;
+			bytes.set(0, bytes.get(0) | (1 << (7 - excessBits)));
+			bytes.set(bytes.length - 1, bytes.get(bytes.length - 1) | 1);
+			r.setFromBigEndianBytesSigned(bytes);
+			#if !cppia
+			if (bits > 10) {
+				while (!equals2Int(r.gcd(BigInt.SMALL_PRIMES_PRODUCT), 1)) {
+					BigIntArithmetic.addInt(r, r, 2);
+				}
+			}
+			#end
+		} while (!r.isProbablePrime(tolerance));
+		if (r.sign() < 0)
+			BigIntArithmetic.negate(r, r);
+		return r;
+	}
+
+	/**
+		Generates a pseudo-random `BigInt` within a given range.
+		@param min The inclusive minimum value.
+		@param max The inclusive maximum value.
+		@return A random `BigInt` between `min` and `max`.
+	**/
+	public static function randomInRange(min:BigInt_, max:BigInt_):BigInt_ {
+		min = min.abs();
+		max = max.abs();
+		var initCheck = BigIntArithmetic.compare(min, max);
+		if (initCheck == 0)
+			return min;
+		if (initCheck > 0)
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		if (min.bitLength() > (max.bitLength() >> 1))
+			return add2(randomInRange(BigInt.ZERO, sub2(max, min)), min);
+		for (i in 0...1000) {
+			var rnd = random(max.bitLength());
+			if (BigIntArithmetic.compare(rnd, min) >= 0 && BigIntArithmetic.compare(rnd, max) <= 0) {
+				return rnd;
+			}
+		}
+		return add2(random(sub2(max, min).bitLength() - 1), min);
+	}
+
+	/**
+		Generates a pseudo-random `BigInt` with a specified number of bits.
+		@param bits The bit-length of the random number.
+		@return A new `BigInt` with a random value.
+	**/
+	public static function random(bits:Int32):BigInt_ {
+		if (bits <= 0)
+			return BigInt.ZERO;
+		var r = new MutableBigInt_();
+		r.setFromBigEndianBytesSigned(randomBytes(bits));
+		r.compact();
+		return r;
+	}
+
+	/**
+		Performs division, returning both quotient and remainder.
+		@param dividend The number to be divided.
+		@param divisor The number to divide by.
+		@return An object `{quotient: BigInt_, remainder: BigInt_}`.
+	**/
+	public static function divMod(dividend:BigInt_, divisor:BigInt_):{quotient:BigInt_, remainder:BigInt_} {
+		var q = new MutableBigInt_();
+		var r = new MutableBigInt_();
+		BigIntArithmetic.divide(dividend, divisor, q, r);
+		return {quotient: q, remainder: r};
+	}
+
+	//-----------------------------------------------------------------------
+	// Private implementation
+	//-----------------------------------------------------------------------
+
+	private inline function getUnsignedDigitCount():Int {
+		if ((m_count > 1) && (m_data.get(m_count - 1) == 0)) {
+			return m_count - 1;
+		}
+		return m_count;
+	}
+
+	private inline function getShort(n:Int):Int {
+		return MultiwordArithmetic.getShort(m_data, n);
+	}
+
+	private function compact():Void {
+		if (isNegative()) {
+			while (m_count > 1) {
+				if ((m_data.get(m_count - 1) == -1) && (m_data.get(m_count - 2) < 0)) {
+					--m_count;
+				} else {
+					break;
+				}
+			}
+		} else {
+			while (m_count > 1) {
+				if ((m_data.get(m_count - 1) == 0) && (m_data.get(m_count - 2) >= 0)) {
+					--m_count;
+				} else {
+					break;
+				}
+			}
+		}
+	}
+
+	#if neko
+	private function millerRabin(rounds:UInt):Bool {
+		var minusOne:BigInt_ = subInt2(this, 1);
+		var m = subInt2(this, 1);
+		var lsb = m.getLowestSetBit();
+		if (lsb <= 0)
+			return false;
+		m = arithmeticShiftRight2(m, lsb);
+		var num:BigInt_;
+		for (i in 0...rounds) {
+			num = randomInRange(BigInt.TWO, minusOne);
+			var z:BigInt_ = num.modPow(m, this);
+			if (BigIntArithmetic.compare(z, BigInt.ONE) != 0 && BigIntArithmetic.compare(z, minusOne) != 0) {
+				var j:Int = 1;
+				while (j <= lsb && BigIntArithmetic.compare(z, minusOne) != 0) {
+					if (BigIntArithmetic.compare(z, BigInt.ONE) == 0 || j == lsb) {
+						return false;
+					}
+					z = z.modPow(BigInt.TWO, this);
+					j++;
+				}
+			}
+		}
+		return true;
+	}
+	#else
+	private function millerRabin(rounds:Int):Bool {
+		var numLists:Int = ((this.bitLength() - 1) < s_primeNumbers.length) ? (this.bitLength() - 1) : s_primeNumbers.length;
+		for (i in 0...numLists) {
+			var t:Int32 = divMod(this, BigInt_.fromInt(s_primeProduct[i])).remainder.m_data.get(0);
+			var primeNumbers = s_primeNumbers[i];
+			for (j in 0...primeNumbers.length) {
+				var prime:Int = primeNumbers[j];
+				var qRem:Int = t % prime;
+				if (qRem == 0) {
+					return (this.bitLength() < 16 && this.m_data.get(0) == prime);
+				}
+			}
+		}
+		var m = subInt2(this, 1);
+		var lsb = m.getLowestSetBit();
+		if (lsb <= 0)
+			return false;
+		m = arithmeticShiftRight2(m, lsb);
+		var montyRadix:BigInt_ = divMod(arithmeticShiftLeft2(BigInt.ONE, 32 * this.m_count), this).remainder;
+		var minusMontyRadix:BigInt_ = sub2(this, montyRadix);
+		var num:BigInt_;
+		do {
+			do {
+				num = random(this.bitLength());
+			} while (BigIntArithmetic.compare(num, BigInt.ZERO) == 0
+				|| BigIntArithmetic.compare(num, montyRadix) == 0
+				|| BigIntArithmetic.compare(num, minusMontyRadix) == 0
+				|| BigIntArithmetic.compare(num, this) >= 0);
+			var y = modPowMonty(num, m, this, false);
+			if (BigIntArithmetic.compare(y, montyRadix) != 0) {
+				var j:Int = 1;
+				while (BigIntArithmetic.compare(y, minusMontyRadix) != 0) {
+					if (j == lsb)
+						return false;
+					y = modPowMonty(y, BigInt.TWO, this, false);
+					if (BigIntArithmetic.compare(y, montyRadix) == 0)
+						return false;
+					j++;
+				}
+			}
+			rounds -= 2;
+		} while (rounds >= 0);
+		return true;
+	}
+	#end
+
+	/* hac 14.64, pp. 610 */
+	private function modInverseOdd(y:BigInt_, x:BigInt_):BigInt_ {
+		var b:BigInt_ = fromInt(0);
+		var d:BigInt_ = fromInt(1);
+		var u:BigInt_ = MutableBigInt_.fromBigInt(x);
+		var v:BigInt_ = MutableBigInt_.fromBigInt(y);
+		do {
+			while ((BigIntArithmetic.bitwiseAndInt(u, 1) == 0)) {
+				u = arithmeticShiftRight2(u, 1);
+				if (BigIntArithmetic.bitwiseAndInt(b, 1) == 1)
+					b = sub2(b, x);
+				b = arithmeticShiftRight2(b, 1);
+			}
+			while ((BigIntArithmetic.bitwiseAndInt(v, 1) == 0)) {
+				v = arithmeticShiftRight2(v, 1);
+				if (BigIntArithmetic.bitwiseAndInt(d, 1) == 1)
+					d = sub2(d, x);
+
+				d = arithmeticShiftRight2(d, 1);
+			}
+			if (BigIntArithmetic.compare(u, v) >= 0) {
+				u = sub2(u, v);
+				b = sub2(b, d);
+			} else {
+				v = sub2(v, u);
+				d = sub2(d, b);
+			}
+		} while (!u.isZero());
+
+		if (!equals2Int(v, 1)) {
+			return BigInt.ZERO;
+		}
+
+		while (BigIntArithmetic.compareInt(d, 0) < 0) {
+			d = add2(d, x);
+		}
+
+		while (BigIntArithmetic.compare(d, x) >= 0) {
+			d = sub2(d, x);
+		}
+
+		return d;
+	}
+	
+	#if !neko
+	private function modPowMonty(b:BigInt_, _e:BigInt_, _m:BigInt_, convert:Bool):BigInt_ {
+		var n:Int,
+			powR:Int,
+			extraBits:Int,
+			expLength:Int,
+			numPowers:Int32,
+			i:Int,
+			window:Int32,
+			mult:Int,
+			lastZeroes:Int,
+			windowPos:Int,
+			bits:Int,
+			j:Int;
+		var smallMontyModulus:Bool;
+		var mDash:Int32;
+		var yAccum:Vector<Int32>,
+			zVal:Vector<Int32>,
+			tmp:Vector<Int32>,
+			zSquared:Vector<Int32>,
+			windowList:Vector<Int32>,
+			yVal:Vector<Int32>;
+		var oddPowers:Vector<Vector<Int32>>;
+		var m:BigInt_ = _m, e:BigInt_ = _e;
+		n = m.m_count;
+		powR = 32 * n;
+		smallMontyModulus = (m.bitLength() + 2) <= powR;
+		mDash = m.getMQuote();
+		if (convert) {
+			b = divMod(BigInt_.arithmeticShiftLeft2(b, powR), m).remainder;
+		}
+		yAccum = new Vector<Int32>(n + 1);
+		zVal = b.m_data;
+		var zLen = b.m_count;
+		if (zLen < n) {
+			tmp = new Vector<Int32>(n);
+			Vector.blit(zVal, 0, tmp, n - zLen, zLen);
+			zVal = tmp;
+		}
+		extraBits = 0;
+		if (e.m_count > 1 || e.bitCount() > 2) {
+			expLength = e.bitLength();
+			while (expLength > expWindowThresholds[extraBits])
+				extraBits++;
+		}
+		numPowers = 1 << extraBits;
+		oddPowers = new Vector<Vector<Int32>>(numPowers);
+		oddPowers[0] = zVal;
+		zSquared = zVal.copy();
+		squareMonty(yAccum, zSquared, m.m_data, m.m_count, mDash, smallMontyModulus);
+		for (i in 1...numPowers) {
+			oddPowers[i] = oddPowers[i - 1].copy();
+			multiplyMonty(yAccum, oddPowers[i], zSquared, m.m_data, m.m_count, mDash, smallMontyModulus);
+		}
+		windowList = getWindowList(e.m_data, e.m_count, extraBits);
+		window = windowList[0];
+		mult = window & 0xFF;
+		lastZeroes = window >> 8;
+		if (mult == 1) {
+			yVal = zSquared;
+			lastZeroes--;
+		} else {
+			yVal = oddPowers[mult >> 1].copy();
+		}
+		windowPos = 1;
+		window = windowList[windowPos];
+		windowPos++;
+		while (window != -1) {
+			mult = window & 0xFF;
+			bits = lastZeroes + BitLengthTable[mult];
+			j = 0;
+			while (j < bits) {
+				squareMonty(yAccum, yVal, m.m_data, m.m_count, mDash, smallMontyModulus);
+				j++;
+			}
+			multiplyMonty(yAccum, yVal, oddPowers[mult >> 1], m.m_data, m.m_count, mDash, smallMontyModulus);
+			lastZeroes = window >> 8;
+			window = windowList[windowPos];
+			windowPos++;
+		}
+		for (i in 0...lastZeroes) {
+			squareMonty(yAccum, yVal, m.m_data, m.m_count, mDash, smallMontyModulus);
+		}
+		if (convert) {
+			montgomeryReduce(yVal, m.m_data, m.m_count, mDash);
+		} else if (smallMontyModulus && compareMonty(yVal, m.m_data) >= 0) {
+			subtractMonty(yVal,m.m_data);
+		}
+		var montResult:BigInt_ = BigInt_.fromUnsignedInts(yVal);
+		return montResult;
+	}
+
+	private function squareMonty(a:Vector<Int32>, x:Vector<Int32>, m:Vector<Int32>, mLen:Int, mDash:Int32, smallMontyModulus:Bool):Void {
+		var n:Int, aMax:Int, j:Int, i:Int;
+		var xVal:Int, a0:Int;
+		var x0:Int64, carry:Int64, t:Int64, prod1:Int64, prod2:Int64, xi:Int64, u:Int64;
+		n = mLen;
+		x0 = Int64.make(0, x[0]);
+		carry = Int64.mul(x0, x0);
+		u = Int64.make(0, Int64.mul(carry.low, mDash).low);
+		prod2 = Int64.mul(u, Int64.make(0, m[0]));
+		carry = Int64.add(carry, Int64.make(0, prod2.low));
+		carry = Int64.add(Int64.ushr(carry, 32), Int64.ushr(prod2, 32));
+		j = 1;
+		while (j < mLen) {
+			prod1 = Int64.mul(x0, Int64.make(0, x[j]));
+			prod2 = Int64.mul(u, Int64.make(0, m[j]));
+			carry = Int64.add(carry, Int64.add(Int64.make(0, Int64.shl(prod1, 1).low), Int64.make(0, prod2.low)));
+			a[j - 1] = carry.low;
+			carry = Int64.add(Int64.add(Int64.ushr(carry, 32), Int64.ushr(prod2, 32)), Int64.ushr(prod1, 31));
+			j++;
+		}
+		a[mLen] = Int64.ushr(carry, 32).low;
+		a[mLen - 1] = carry.low;
+		i = 1;
+		while (i < mLen) {
+			a0 = a[0];
+			u = Int64.make(0, Int64.mul(a0, mDash).low);
+			carry = Int64.add(Int64.mul(u, Int64.make(0, m[0])), Int64.make(0, a0));
+			carry = Int64.ushr(carry, 32);
+			j = 1;
+			while (j < i) {
+				carry = Int64.add(carry, Int64.add(Int64.mul(u, Int64.make(0, m[j])), Int64.make(0, a[j])));
+				a[j - 1] = carry.low;
+				carry = Int64.ushr(carry, 32);
+				j++;
+			}
+			xi = Int64.make(0, x[i]);
+			prod1 = Int64.mul(xi, xi);
+			prod2 = Int64.mul(u, Int64.make(0, m[i]));
+			carry += Int64.add(Int64.add(Int64.make(0, prod1.low), Int64.make(0, prod2.low)), Int64.make(0, a[i]));
+			a[i - 1] = carry.low;
+			carry = Int64.add(Int64.add(Int64.ushr(carry, 32), Int64.ushr(prod1, 32)), Int64.ushr(prod2, 32));
+			j = i + 1;
+			while (j < n) {
+				prod1 = Int64.mul(xi, Int64.make(0, x[j]));
+				prod2 = Int64.mul(u, Int64.make(0, m[j]));
+				carry = Int64.add(carry, Int64.add(Int64.add(Int64.make(0, Int64.shl(prod1, 1).low), Int64.make(0, prod2.low)), Int64.make(0, a[j])));
+				a[j - 1] = carry.low;
+				carry = Int64.add(Int64.add(Int64.ushr(carry, 32), Int64.ushr(prod1, 31)), Int64.ushr(prod2, 32));
+				j++;
+			}
+			carry = Int64.add(carry, Int64.make(0, a[n]));
+			a[n] = Int64.ushr(carry, 32).low;
+			a[n - 1] = carry.low;
+			i++;
+		}
+
+		if (!smallMontyModulus && compareMonty(a, m) >= 0) {
+			subtractMonty(a,m);
+		}
+		Vector.blit(a, 0, x, 0, n);
+	}
+
+	private function multiplyMonty(a:Vector<Int32>, x:Vector<Int32>, y:Vector<Int32>, m:Vector<Int32>, mLen:Int, mDash:Int32, smallMontyModulus:Bool):Void {
+		var n:Int, aMax:Int, j:Int, i:Int;
+		var a0:Int64, y0:Int64;
+		var carry:Int64, t:Int64, prod1:Int64, prod2:Int64, xi:Int64, u:Int64;
+		n = mLen;
+		y0 = Int64.make(0, y[0]);
+		i = 0;
+		while (i <= n) {
+			a[i] = 0;
+			i++;
+		}
+		i = 0;
+		while (i < n) {
+			a0 = Int64.make(0, a[0]);
+			xi = Int64.make(0, x[i]);
+			prod1 = Int64.mul(xi, y0);
+			carry = Int64.add(Int64.make(0, prod1.low), a0);
+			u = Int64.make(0, Int64.mul(carry.low, mDash).low);
+			prod2 = Int64.mul(u, Int64.make(0, m[0]));
+			carry = Int64.add(carry, Int64.make(0, prod2.low));
+			carry = Int64.add(Int64.add(Int64.ushr(carry, 32), Int64.ushr(prod1, 32)), Int64.ushr(prod2, 32));
+			j = 1;
+			while (j <= (n - 1)) {
+				prod1 = Int64.mul(xi, Int64.make(0, y[j]));
+				prod2 = Int64.mul(u, Int64.make(0, m[j]));
+				carry = Int64.add(Int64.add(Int64.make(0, prod1.low), Int64.make(0, prod2.low)), Int64.add(Int64.make(0, a[j]), carry));
+				a[j - 1] = carry.low;
+				carry = Int64.add(Int64.add(Int64.ushr(carry, 32), Int64.ushr(prod1, 32)), Int64.ushr(prod2, 32));
+				j++;
+			}
+			carry = Int64.add(carry, Int64.make(0, a[n]));
+			a[n] = Int64.ushr(carry, 32).low;
+			a[n - 1] = carry.low;
+			i++;
+		}
+
+		if (!smallMontyModulus && compareMonty(a, m) >= 0) {
+			subtractMonty(a,m);
+		}
+		Vector.blit(a, 0, x, 0, n);
+	}
+
+	private function montgomeryReduce(x:Vector<Int32>, m:Vector<Int32>, mLen:Int, mDash:Int32):Void {
+		var n:Int, i:Int, j:Int;
+		var x0:Int;
+		var t:Int64, carry:Int64;
+		n = mLen;
+		i = 0;
+		while (i < n) {
+			x0 = x[0];
+			t = Int64.make(0, Int64.mul(x0, mDash).low);
+			carry = Int64.add(Int64.mul(t, Int64.make(0, m[0])), Int64.make(0, x0));
+			carry = Int64.ushr(carry, 32);
+			j = 1;
+			while (j < n) {
+				carry = Int64.add(carry, Int64.add(Int64.mul(t, Int64.make(0, m[j])), Int64.make(0, x[j])));
+				x[j - 1] = carry.low;
+				carry = Int64.ushr(carry, 32);
+				j++;
+			}
+			x[n - 1] = carry.low;
+			i++;
+		}
+		if (compareMonty(x, m) >= 0) {
+			subtractMonty(x,m);
+		}
+	}
+	
+	// x = x - y - where x is >= y
+	private function subtractMonty(x:Vector<Int32>,y:Vector<Int32>):Void {
+		var yIndex:Int = y.length-1;
+		while(yIndex>=0 && y[yIndex]==0) {
+			yIndex--;
+		}
+		var xn : Int, yn : Int, i:Int = 0;
+		var c : Int32 = 0, z : Int = 0;
+		while(i<=yIndex)
+		{
+			xn = x.get(i);
+			yn = y.get(i);
+			z = xn - yn - c;
+			x.set(i, z);
+			c = ((~xn & yn) | (~(xn ^ yn) & z)) >>> 31;
+			i++;
+		}
+	}
+
+	private function compareMonty(x:Vector<Int32>, y:Vector<Int32>):Int {
+		var xIndex:Int = 0;
+		var yIndex:Int = 0;
+		var xLen:Int = x.length - 1;
+		var yLen:Int = y.length - 1;
+		while (xIndex != x.length && x[xLen - xIndex] == 0) {
+			xIndex++;
+		}
+		while (yIndex != y.length && y[yLen - yIndex] == 0) {
+			yIndex++;
+		}
+		var diff:Int = (x.length - y.length) - (xIndex - yIndex);
+		if (diff != 0) {
+			return diff < 0 ? -1 : 1;
+		}
+		var xn:Int, yn:Int;
+		while (xIndex >= 0) {
+			xn = x[xLen - xIndex];
+			xIndex--;
+			yn = y[yLen - yIndex];
+			yIndex--;
+			if (xn != yn) {
+				return (xn ^ -2147483648) < (yn ^ -2147483648) ? -1 : 1;
+			}
+		}
+		return 0;
+	}
+	
+	private function getMQuote():Int32 {
+		var d:Int = -m_data[0];
+		var mQuote:Int32 = modInverse32(d);
+		return mQuote;
+	}
+
+	private function modInverse32(d:Int):Int32 {
+		var x:Int32;
+		x = d + (((d + 1) & 4) << 1);
+		x = (2 - (d * x)) * x;
+		x = (2 - (d * x)) * x;
+		x = (2 - (d * x)) * x;
+		return x;
+	}
+
+	private function modPowBarrett(base:BigInt_, exponent:BigInt_, modulus:BigInt_):BigInt_ {
+		var i:Int, j:Int, k:Int;
+		var extraBits:Int,
+			expLength:Int,
+			numPowers:Int32,
+			window:Int32,
+			mult:Int,
+			lastZeroes:Int,
+			windowPos:Int,
+			bits:Int;
+		var mr:BigInt_, yu:BigInt_, b2:BigInt_, y:BigInt_;
+		var e:BigInt_ = exponent;
+		var m:BigInt_ = modulus;
+		var oddPowers:Vector<BigInt_>;
+		var windowList:Vector<Int32>;
+
+		k = m.m_count;
+		mr = BigInt_.arithmeticShiftLeft2(BigInt.ONE, (k + 1) << 5);
+		yu = BigInt_.divide2(BigInt_.arithmeticShiftLeft2(BigInt.ONE, k << 6), m);
+		extraBits = 0;
+		expLength = e.bitLength();
+		while (expLength > expWindowThresholds[extraBits])
+			extraBits++;
+		numPowers = 1 << extraBits;
+		oddPowers = new Vector<BigInt_>(numPowers);
+		oddPowers[0] = base;
+		b2 = reduceBarrett(base.square(), m, mr, yu);
+		for (i in 1...numPowers) {
+			oddPowers[i] = reduceBarrett(multiply2(oddPowers[i - 1], b2), m, mr, yu);
+		}
+
+		windowList = getWindowList(e.m_data, e.m_count, extraBits);
+		window = windowList[0];
+		mult = window & 0xFF;
+		lastZeroes = window >> 8;
+		if (mult == 1) {
+			y = b2;
+			lastZeroes--;
+		} else {
+			y = oddPowers[mult >> 1];
+		}
+
+		windowPos = 1;
+		window = windowList[windowPos];
+		windowPos++;
+		while (window != -1) {
+			mult = window & 0xFF;
+			bits = lastZeroes + BitLengthTable[mult];
+			j = 0;
+			while (j < bits) {
+				y = reduceBarrett(y.square(), m, mr, yu);
+				j++;
+			}
+			y = reduceBarrett(multiply2(y, (oddPowers[mult >> 1])), m, mr, yu);
+			lastZeroes = window >> 8;
+			window = windowList[windowPos];
+			windowPos++;
+		}
+		i = 0;
+		while (i < lastZeroes) {
+			y = reduceBarrett(y.square(), m, mr, yu);
+			i++;
+		}
+
+		return y;
+	}
+
+	private function getWindowList(mag:Vector<Int32>, magLen:Int, extraBits:Int):Vector<Int32> {
+		var i:Int,
+			v:Int32,
+			leadingBits:Int,
+			resultSize:Int,
+			resultPos:Int,
+			bitPos:Int,
+			mult:Int32,
+			multLimit:Int32,
+			zeroes:Int32;
+		v = mag[magLen - 1];
+		leadingBits = BigIntHelper.bitLen(v);
+		resultSize = Math.floor((((magLen - 1) << 5) + leadingBits) / (1 + extraBits) + 2);
+		var result:Vector<Int32> = new Vector<Int32>(resultSize);
+		resultPos = 0;
+		bitPos = 33 - leadingBits;
+		v = v << bitPos;
+		mult = 1;
+		multLimit = 1 << extraBits;
+		zeroes = 0;
+		i = magLen - 1;
+		while (i >= 0) {
+			while (bitPos < 32) {
+				if (mult < multLimit) {
+					mult = (mult << 1) | (v >>> 31);
+				} else if (v < 0) {
+					result[resultPos] = createWindowEntry(mult, zeroes);
+					resultPos++;
+					mult = 1;
+					zeroes = 0;
+				} else
+					zeroes++;
+
+				v = v << 1;
+				bitPos++;
+			}
+			i--;
+			if (i < 0) {
+				result[resultPos] = createWindowEntry(mult, zeroes);
+				resultPos++;
+				break;
+			}
+			v = mag[i];
+			bitPos = 0;
+		}
+		result[resultPos] = -1;
+		return result;
+	}
+
+	private function createWindowEntry(mult:Int32, zeroes:Int32):Int32 {
+		while ((mult & 1) == 0) {
+			mult >>>= 1;
+			++zeroes;
+		}
+		return mult | (zeroes << 8);
+	}
+
+	private function reduceBarrett(x:BigInt_, m:BigInt_, mr:BigInt_, yu:BigInt_):BigInt_ {
+		var timestamp:Float = Timer.stamp();
+		var xLen:Int, mLen:Int, k:Int;
+		var q1:BigInt_, q2:BigInt_, q3:BigInt_, r1:BigInt_, r2:BigInt_, r3:BigInt_;
+		xLen = x.bitLength();
+		mLen = m.bitLength();
+		if (xLen < mLen) {
+			return x;
+		}
+		if ((xLen - mLen) > 1) {
+			var k:Int = m.m_count;
+			var q1:BigInt_ = x.divideWords(k - 1);
+			var q2:BigInt_ = multiply2(yu, q1);
+			var q3:BigInt_ = q2.divideWords(k + 1);
+			var r1:BigInt_ = x.remainderWords(k + 1);
+			var r2:BigInt_ = multiply2(q3, m);
+			var r3:BigInt_ = r2.remainderWords(k + 1);
+			x = sub2(r1, r3);
+			if (x.sign() < 0) {
+				x = add2(x, mr);
+			}
+		}
+
+		while (BigIntArithmetic.compare(x, m) >= 0) {
+			x = sub2(x, m);
+		}
+		return x;
+	}
+	
+	private function divideWords(w:Int):BigInt_ {
+		var n:Int = m_count;
+		if (w >= n)
+			return BigInt.ZERO;
+		var bi = new MutableBigInt_();
+		bi.setFromVector(m_data, m_count - (n - w), n - w);
+		return bi;
+	}
+
+	private function remainderWords(w:Int):BigInt_ {
+		var n:Int = m_count;
+		if (w >= n)
+			return this;
+		var bi = new MutableBigInt_();
+		bi.setFromVector(m_data, 0, w);
+		return bi;
+	}
+	
+	#end
+
+	private static function newFromInt(value:Int):BigInt_ {
+		var bi = new MutableBigInt_();
+		bi.setFromInt(value);
+		return bi;
+	}
+
+	private static function randomBytes(bits:Int32):Bytes {
+		var countBytes:Int = Std.int((bits + 7) / 8);
+		var randomBytes = Bytes.alloc(countBytes);
+		for (j in 0...countBytes) {
+			var rndN = Math.floor(Math.random() * 256);
+			randomBytes.set(j, rndN);
+		}
+		var excessBits:Int = 8 * countBytes - bits;
+		if (excessBits > 0)
+			randomBytes.set(0, randomBytes.get(0) & (255 >>> excessBits));
+		return randomBytes;
+	}
+
+	private function new():Void {}
+
+	private static function getCachedValue(value:Int):BigInt_ {
+		if ((s_firstCachedValue <= value) && (value <= s_lastCachedValue)) {
+			initCache();
+			return s_cache[value - s_firstCachedValue];
+		}
+		return null;
+	}
+
+	private static function initCache():Void {
+		if (s_cache == null) {
+			s_cache = new Vector<BigInt_>(s_lastCachedValue + 1 - s_firstCachedValue);
+			for (i in 0...s_cache.length) {
+				s_cache[i] = newFromInt(i + s_firstCachedValue);
+			}
+		}
+	}
+
+	//-----------------------------------------------------------------------
+	// Static helpers
+	//-----------------------------------------------------------------------
+
+	@:noCompletion
+	private static inline function negate1(a:BigInt_):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.negate(r, a);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function equals2Int(a:BigInt_, b:Int):Bool {
+		return a.equalsInt(b);
+	}
+
+	@:noCompletion
+	private static inline function equals2(a:BigInt_, b:BigInt_):Bool {
+		return a.equals(b);
+	}
+
+	@:noCompletion
+	private static inline function addInt2(a:BigInt_, b:Int):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.addInt(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function add2(a:BigInt_, b:BigInt_):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.add(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function subInt2(a:BigInt_, b:Int):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.subtractInt(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function sub2(a:BigInt_, b:BigInt_):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.subtract(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function multiplyInt2(a:BigInt_, b:Int):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.multiplyInt(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function multiply2(a:BigInt_, b:BigInt_):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.multiply(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function divideInt2(a:BigInt_, b:Int):BigInt_ {
+		var q = new MutableBigInt_();
+		BigIntArithmetic.divideInt(a, b, q);
+		return q;
+	}
+
+	@:noCompletion
+	private static inline function divide2(a:BigInt_, b:BigInt_):BigInt_ {
+		var q = new MutableBigInt_();
+		BigIntArithmetic.divide(a, b, q, null);
+		return q;
+	}
+
+	@:noCompletion
+	private static inline function modulusInt2(a:BigInt_, b:Int):Int {
+		var q = new MutableBigInt_();
+		return BigIntArithmetic.divideInt(a, b, q);
+	}
+
+	@:noCompletion
+	private static inline function modulus2(a:BigInt_, b:BigInt_):BigInt_ {
+		var q = new MutableBigInt_();
+		var r = new MutableBigInt_();
+		BigIntArithmetic.divide(a, b, q, r);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function arithmeticShiftLeft2(a:BigInt_, b:Int):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.arithmeticShiftLeft(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function arithmeticShiftRight2(a:BigInt_, b:Int):BigInt_ {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.arithmeticShiftRight(r, a, b);
+		return r;
+	}
+
+	@:noCompletion
+	private static inline function sign1(a:BigInt_):Int {
+		return a.sign();
+	}
+
+	@:noCompletion
+	private static inline function isZero1(a:BigInt_):Bool {
+		return a.isZero();
+	}
+
+	@:noCompletion
+	private static inline function isNegative1(a:BigInt_):Bool {
+		return a.isNegative();
+	}
+
+	@:noCompletion
+	private static inline function isPositive1(a:BigInt_):Bool {
+		return a.isPositive();
+	}
+
+	@:noCompletion
+	private static inline function isOdd1(a:BigInt_):Bool {
+		return a.isOdd();
+	}
+
+	@:noCompletion
+	private static inline function isEven1(a:BigInt_):Bool {
+		return a.isEven();
+	}
+
+	@:noCompletion
+	private static inline function toString1(a:BigInt_, radix:Int):String {
+		if ((radix == 10 ) || ( radix <2 || radix >36 )) return a.toString();
+		if (radix == 16 ) return a.toHex();
+		return a.toBase(radix);
+	}
+
+	@:noCompletion
+	private static inline function toHex1(a:BigInt_):String {
+		return a.toHex();
+	}
+
+	@:noCompletion
+	private static inline function toBytes1(a:BigInt_):Bytes {
+		return a.toBytes();
+	}
+
+	@:noCompletion
+	private static inline function toInts1(a:BigInt_, v:Vector<Int>):Int {
+		return a.toInts(v);
+	}
+
+	static final BitLengthTable:Array<Int> = [
+		0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+		6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+		7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+		8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+		8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+		8, 8, 8, 8, 8, 8
+	];
+
+	var expWindowThresholds:Vector<Int> = Vector.fromArrayCopy([7, 25, 81, 241, 673, 1793, 4609, 2147483647]);
+	
+	private static final s_primeProduct:Array<Int> = [
+		111546435, 58642669, 600662303, 33984931, 89809099, 167375713, 371700317, 645328247, 1070560157, 1596463769, 11592209, 13420567, 16965341, 20193023,
+		23300239, 29884301, 35360399, 42749359, 49143869, 56466073, 65111573, 76027969, 84208541, 94593973, 103569859, 119319383, 133390067, 154769821,
+		178433279, 193397129, 213479407, 229580147, 250367549, 271661713, 293158127, 319512181, 357349471, 393806449, 422400701, 452366557, 507436351,
+		547978913, 575204137, 627947039, 666785731, 710381447, 777767161, 834985999, 894826021, 951747481, 1019050649, 1072651369, 1125878063, 1185362993,
+		1267745273, 1322520163, 1391119619, 1498299287, 1608372013, 1700725291, 1805418283, 1871456063, 2008071007, 2115193573, -2116537769, -2048682597,
+		-1909179209, -1702980825, -1489962503, -1372818057, -1273647213, -1176554679, -1029034995, -962574873, -771748953, -583131125, -457088133, -303174767,
+		-155320833, -61811709, 118284847, 331869553, 586295383, 772701351, 975998995, 1184460183, 1405182073, 1650949557, 1998668581, -2047251531,
+		-1927652605, -1654913601, -1315052365, -972124375, -681632865, -537896625, -294451281, 45921629, 378607295, 561165965, 906561455, 1160065935,
+		1380657927, 2004409167, -1889883587, -1580206561, -1192992209, -923130317, -627882485, -110329421, 120545641, 534097439, 738416003, 1094999525,
+		1528839403, 1959463903, -1847439821, -979396295, -644014227, -93511911, 524494345, 1087323987, 1634127285, 2004297177, -2016987781, -1667747257,
+		-1343966817, -896726901, -320897353, 297288423, 657704777, 1374982823, 1828886847, -1690887095, -1136243417, -366760067, 243359903, 969193253,
+		1709898031, -1905679739
+	];
+	
+	private static final s_primeNumbers:Array<Array<Int>> = [
+		[3, 5, 7, 11, 13, 17, 19, 23], [29, 31, 37, 41, 43],[47, 53, 59, 61, 67],[71, 73, 79, 83],
+		[89, 97, 101, 103],[107, 109, 113, 127],[131, 137, 139, 149],[151, 157, 163, 167],
+		[173, 179, 181, 191],[193, 197, 199, 211],[223, 227, 229],[233, 239, 241],
+		[251, 257, 263],[269, 271, 277],[281, 283, 293],[307, 311, 313],
+		[317, 331, 337],[347, 349, 353],[359, 367, 373],[379, 383, 389],
+		[397, 401, 409],[419, 421, 431],[433, 439, 443],[449, 457, 461],
+		[463, 467, 479],[487, 491, 499],[503, 509, 521],[523, 541, 547],
+		[557, 563, 569],[571, 577, 587],[593, 599, 601],[607, 613, 617],
+		[619, 631, 641],[643, 647, 653],[659, 661, 673],[677, 683, 691],
+		[701, 709, 719],[727, 733, 739],[743, 751, 757],[761, 769, 773],
+		[787, 797, 809],[811, 821, 823],[827, 829, 839],[853, 857, 859],
+		[863, 877, 881],[883, 887, 907],[911, 919, 929],[937, 941, 947],
+		[953, 967, 971],[977, 983, 991],[997, 1009, 1013],[1019, 1021, 1031],
+		[1033, 1039, 1049],[1051, 1061, 1063],[1069, 1087, 1091],[1093, 1097, 1103],
+		[1109, 1117, 1123],[1129, 1151, 1153],[1163, 1171, 1181],[1187, 1193, 1201],
+		[1213, 1217, 1223],[1229, 1231, 1237],[1249, 1259, 1277],[1279, 1283, 1289],
+		[1291, 1297, 1301],[1303, 1307, 1319],[1321, 1327, 1361],[1367, 1373, 1381],
+		[1399, 1409, 1423],[1427, 1429, 1433],[1439, 1447, 1451],[1453, 1459, 1471],
+		[1481, 1483, 1487],[1489, 1493, 1499],[1511, 1523, 1531],[1543, 1549, 1553],
+		[1559, 1567, 1571],[1579, 1583, 1597],[1601, 1607, 1609],[1613, 1619, 1621],
+		[1627, 1637, 1657],[1663, 1667, 1669],[1693, 1697, 1699],[1709, 1721, 1723],
+		[1733, 1741, 1747],[1753, 1759, 1777],[1783, 1787, 1789],[1801, 1811, 1823],
+		[1831, 1847, 1861],[1867, 1871, 1873],[1877, 1879, 1889],[1901, 1907, 1913],
+		[1931, 1933, 1949],[1951, 1973, 1979],[1987, 1993, 1997],[1999, 2003, 2011],
+		[2017, 2027, 2029],[2039, 2053,	2063],[2069, 2081, 2083],[2087, 2089, 2099],
+		[2111, 2113, 2129],[2131, 2137, 2141],[2143, 2153, 2161],[2179, 2203, 2207],
+		[2213, 2221, 2237],[2239, 2243, 2251],[2267, 2269, 2273],[2281, 2287, 2293],
+		[2297, 2309, 2311],[2333, 2339, 2341],[2347, 2351, 2357],[2371, 2377, 2381],
+		[2383, 2389, 2393],[2399, 2411, 2417],[2423, 2437, 2441],[2447, 2459, 2467],
+		[2473, 2477, 2503],[2521, 2531, 2539],[2543, 2549, 2551],[2557, 2579, 2591],
+		[2593, 2609, 2617],[2621, 2633, 2647],[2657, 2659, 2663],[2671, 2677, 2683],
+		[2687, 2689, 2693],[2699, 2707, 2711],[2713, 2719, 2729],[2731, 2741, 2749],
+		[2753, 2767, 2777],[2789, 2791, 2797],[2801, 2803, 2819],[2833, 2837, 2843],
+		[2851, 2857, 2861],[2879, 2887, 2897],[2903, 2909, 2917],[2927, 2939, 2953], 
+		[2957, 2963, 2969],[2971, 2999, 3001],[3011, 3019, 3023],[3037, 3041, 3049]
+	];
+}

+ 902 - 0
std/haxe/math/bigint/MultiwordArithmetic.hx

@@ -0,0 +1,902 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.math.bigint.BigIntException;
+import haxe.math.bigint.BigIntError;
+import haxe.math.bigint.BigIntHelper;
+import haxe.ds.Vector;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+/**
+	A collection of static, low-level arithmetic functions that operate directly on
+	`Vector<Int>` representations of large numbers.
+**/
+@:allow(haxe.math.bigint)
+class MultiwordArithmetic {
+	/**
+		Checks if a multi-word integer is zero.
+		@param value The vector of integer words.
+		@param length The number of words in the value.
+		@return `true` if the value is zero.
+	**/
+	public static function isZero(value:Vector<Int>, length:Int):Bool {
+		if (length < 1) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		for (i in 0...length) {
+			if (value.get(i) != 0) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+		Checks if a multi-word integer is negative.
+		@param value The vector of integer words.
+		@param length The number of words in the value.
+		@return `true` if the most significant bit is set.
+	**/
+	public static inline function isNegative(value:Vector<Int>, length:Int):Bool {
+		return value.get(length - 1) < 0;
+	}
+
+	/**
+		Gets the effective length of an unsigned multi-word integer, ignoring leading zeros.
+		@param value The vector of integer words.
+		@param length The number of words in the value.
+		@return The minimal number of words needed to represent the value.
+	**/
+	public static function getLengthUnsigned(value:Vector<Int>, length:Int):Int {
+		if (length < 1) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		while (--length > 0) {
+			if (value.get(length) != 0) {
+				break;
+			}
+		}
+		return length + 1;
+	}
+
+	/**
+		Perform unsigned (zero) extension of `input` into `result`.
+		`input` and `result` may refer to the same object.
+		@param result The destination vector.
+		@param resultLength The desired length of the result.
+		@param input The source vector.
+		@param inputLength The length of the input.
+	**/
+	public static function extendUnsigned(result:Vector<Int>, resultLength:Int, input:Vector<Int>, inputLength:Int):Void {
+		if (input == result) {
+			if (resultLength > inputLength) {
+				for (i in inputLength...resultLength) {
+					result.set(i, 0);
+				}
+			}
+		} else {
+			if (resultLength > inputLength) {
+				copy(result, input, inputLength);
+				for (i in inputLength...resultLength) {
+					result.set(i, 0);
+				}
+			} else {
+				copy(result, input, resultLength);
+			}
+		}
+	}
+
+	/**
+		Perform the unary negation of big integer `operand` and put
+		the result into big integer `result`.
+		@param result The vector to store the result.
+		@param operand The vector to negate.
+		@param length The number of words.
+		@return `true` if the operation overflowed; `false` otherwise.
+
+		Ok for `result` and `operand` to be the same object.
+	**/
+	public static function negate(result:Vector<Int>, operand:Vector<Int>, length:Int):Bool {
+		var c:Int = 1;
+		var x:Int = 0;
+		var z:Int = 0;
+		for (i in 0...length) {
+			x = ~operand.get(i);
+			z = x + c;
+			result.set(i, z);
+			c = (x & ~z) >>> 31; // "Hacker's Delight" p. 38
+		}
+		// detect overflow; intuitively, this can only occur for inputs of 2 ^ (32 * N - 1).
+		return (~x & z) < 0;
+	}
+
+	/**
+		Add big integer `operand2` to big integer `operand1` and put
+		the result into big integer `result`.
+
+		Ok for `result`, `operand1`, and `operand2` to be the same object.
+
+		Returns the "carry" value of either 0 or 1.
+	**/
+	public static function add(result:Vector<Int>, operand1:Vector<Int>, operand2:Vector<Int>, length:Int):Int {
+		if ((length < 1) || (result.length < length) || (operand1.length < length) || (operand2.length < length)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		var c:Int = 0;
+		var x:Int = 0, y:Int = 0, z:Int = 0;
+		for (i in 0...length) {
+			x = operand1.get(i);
+			y = operand2.get(i);
+			z = x + y + c;
+			result.set(i, z);
+			c = ((x & y) | ((x | y) & ~z)) >>> 31; // "Hacker's Delight" p. 38
+		}
+		return c;
+	}
+
+	/**
+		Subtract big integer `operand2` from big integer `operand1`
+		and put the result into big integer `result`.
+
+		Ok for `result`, `operand1`, and `operand2` to be the same object.
+
+		Returns the "borrow" value of either 0 or 1.
+	**/
+	public static function subtract(result:Vector<Int>, operand1:Vector<Int>, operand2:Vector<Int>, length:Int):Int {
+		if ((length < 1) || (result.length < length) || (operand1.length < length) || (operand2.length < length)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		var c:Int = 0;
+		var x:Int = 0, y:Int = 0, z:Int = 0;
+		for (i in 0...length) {
+			x = operand1.get(i);
+			y = operand2.get(i);
+			z = x - y - c;
+			result.set(i, z);
+			c = ((~x & y) | (~(x ^ y) & z)) >>> 31; // "Hacker's Delight" p. 38
+		}
+		return c;
+	}
+
+	/**
+		Multiply `operand1` by `operand2`, where both are unsigned,
+		and put the result into `result`.
+
+		`result` must have length >= `operand1Length` + 1.
+
+		`result` may not refer the same object as either `operand1`
+		or `operand2`; however, `operand1` and `operand2` may be the
+		same object.
+	**/
+	public static function multiplyIntUnsigned(result:Vector<Int>, operand1:Vector<Int>, operand1Length:Int, operand2:Int):Void {
+		// TODO: Optimize.
+		var op2 = new Vector<Int>(1);
+		op2.set(0, operand2);
+		multiplyUnsigned(result, operand1, operand1Length, op2, 1);
+	}
+
+	/**
+		Multiply `operand1` by `operand2`, where both are unsigned,
+		and put the result into `result`.
+
+		`result` must have length >= `operand1Length` +
+		`operand2Length`.
+
+		`result` may not refer the same object as either `operand1`
+		or `operand2`; however, `operand1` and `operand2` may be the
+		same object.
+	**/
+	public static function multiplyUnsigned(result:Vector<Int>, operand1:Vector<Int>, operand1Length:Int, operand2:Vector<Int>, operand2Length:Int):Void {
+		// Implements Figure 8-1 (p. 172) from "Hacker's Delight", Second Edition; Henry S. Warren, Jr.; 2013.
+
+		if ((operand1 == result) || (operand2 == result)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((operand1Length < 1) || (operand2Length < 1)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((operand1.length < operand1Length) || (operand2.length < operand2Length)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		var resultSize:Int = operand1Length + operand2Length;
+		if (result.length < resultSize) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		setZero(result, resultSize);
+
+		if (isZero(operand1, operand1Length) || isZero(operand2, operand2Length)) {
+			return;
+		}
+
+		var b:Int, k:Int, t:Int;
+		var u:Int, v:Int, w:Int;
+		var m:Int = operand1Length << 1;
+		var n:Int = operand2Length << 1;
+
+		for (j in 0...n) {
+			v = getShort(operand2, j);
+			k = 0;
+			for (i in 0...m) {
+				u = getShort(operand1, i);
+				w = getShort(result, i + j);
+				t = u * v + w + k;
+				setShort(result, i + j, t);
+				k = t >>> 16;
+			}
+			setShort(result, j + m, k);
+		}
+	}
+
+	/**
+		Calculates the required length for the quotient of an unsigned division.
+		@param dividendLength The length of the dividend.
+		@param divisorLength The length of the divisor.
+		@return The length of the quotient.
+	**/
+	public static inline function getDivisionQuotientLengthUnsigned(dividendLength:Int, divisorLength:Int):Int {
+		var max:Int = dividendLength - divisorLength + 1;
+		return ((max > 1) ? max : 1);
+	}
+
+	/**
+		Divide `dividend` by `divisor`, where both are unsigned.
+		The quotient of the division is put into `quotientOut`;
+		the remainder is the return value.
+
+		`quotientOut` must have length >= `dividendLength`.
+
+		`quotientOut` may refer to `dividend`.
+
+		`work` must not refer to any of the inputs, and must have
+		length >= `dividendLength` + 2.
+
+		`dividend` is not modified, unless it refers to `quotientOut`.
+
+		The results are unspecified if `divisor` is negative.
+	**/
+	public static function divideIntUnsigned(dividend:Vector<Int>, dividendLength:Int, divisor:Int, quotientOut:Vector<Int>, work:Vector<Int>):Int {
+		// TODO: Consider optimizing this case.
+		var remainder = new Vector<Int>(1);
+		var vDivisor = new Vector<Int>(1);
+		vDivisor.set(0, divisor);
+		divideUnsigned(dividend, dividendLength, vDivisor, 1, quotientOut, remainder, work);
+		return remainder.get(0);
+	}
+
+	/**
+		Divide `dividend` by `divisor`, where both are unsigned.
+		The quotient of the division is put into `quotientOut`;
+		the remainder is put into `remainderOut`.
+
+		`divisor` must not have any leading zeros.
+
+		`quotientOut` must have length >= `dividendLength` -
+		`divisorLength` + 1.
+
+		`remainderOut` may be `null` if the remainder value is not
+		needed. If supplied, it must be of length >= `divisorLength`.
+
+		`quotientOut` and `remainderOut` must not refer to the same
+		object; but either may refer to the inputs.
+
+		`dividend` and `divisor` may refer to the same object.
+
+		`work` must not refer to any of the inputs, and must have
+		length >= `dividendLength` + `divisorLength` + 1.
+
+		`dividend` and `divisor` are not modified, unless they
+		reference one of the outputs.
+	**/
+	public static function divideUnsigned(dividend:Vector<Int>, dividendLength:Int, divisor:Vector<Int>, divisorLength:Int, quotientOut:Vector<Int>,
+			remainderOut:Vector<Int>, work:Vector<Int>):Void {
+		if ((quotientOut == null) || (work == null) || (quotientOut == remainderOut)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((work == dividend) || (work == divisor) || (work == quotientOut) || (work == remainderOut)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((divisorLength < 1) || (dividendLength < 1)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		var quotientLength:Int = getDivisionQuotientLengthUnsigned(dividendLength, divisorLength);
+		if (quotientOut.length < quotientLength) {
+			// quotient storage too small
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((remainderOut != null) && (remainderOut.length < divisorLength)) {
+			// remainder storage too small
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (work.length < dividendLength + divisorLength + 1) {
+			// quotient storage too small
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		// special cases
+		var dh:Int32 = divisor.get(divisorLength - 1);
+		if (divisorLength < 2) {
+			switch (dh) {
+				case 0:
+					throw new BigIntException(BigIntError.DIVISION_BY_ZERO);
+				case 1:
+					copy(quotientOut, dividend, dividendLength); // quotientLength == dividendLength
+					if (remainderOut != null) {
+						setZero(remainderOut, divisorLength);
+					}
+					return;
+			}
+		} else if (dh == 0) {
+			// leading zero
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+
+		// trim leading zeros
+		/*while ((dividendLength > 1) && (dividend.get(dividendLength - 1) == 0))
+			{
+				--dividendLength;
+		}*/
+
+		if (dividendLength < 2) {
+			switch (dividend.get(0)) {
+				case 0:
+					setZero(quotientOut, quotientLength);
+					if (remainderOut != null) {
+						setZero(remainderOut, divisorLength);
+					}
+					return;
+			}
+		}
+
+		// if dividend is shorter than divisor
+		if (dividendLength < divisorLength) {
+			if (remainderOut != null) {
+				copy(remainderOut, dividend, dividendLength);
+				for (i in dividendLength...divisorLength) {
+					remainderOut.set(i, 0);
+				}
+			}
+			setZero(quotientOut, quotientLength);
+			return;
+		}
+
+		// TODO: Handle special case of dividend < divisor.
+
+		// Based on Figure 9-1 (p. 185) from "Hacker's Delight", Second Edition; Henry S. Warren, Jr.; 2013.
+
+		var j:Int, k:Int32, t:Int;
+		var m:Int = dividendLength << 1;
+		var un:Int = divisorLength << 1;
+		var n:Int = un;
+		if (getShort(divisor, n - 1) == 0) {
+			--n;
+		}
+
+		// Take care of the case of a single-digit divisor here.
+		if (n == 1) {
+			var v0:Int = divisor.get(0);
+			if (quotientOut != dividend) {
+				setZero(quotientOut, quotientLength);
+			}
+			var uj:Int;
+			k = 0;
+			j = m;
+			while (--j >= 0) {
+				uj = getShort(dividend, j);
+				t = BigIntHelper.u32divu16((k << 16) + uj, v0);
+				setShort(quotientOut, j, t);
+				k = (k << 16) + uj - t * v0;
+			}
+			if (remainderOut != null) {
+				setFromIntUnsigned(remainderOut, divisorLength, k);
+			}
+			return;
+		}
+
+		// vn is work[0] through work[divisor.m_count - 1] or shorts [0, n)
+		// un is work[divisor.m_count] through work[dividend.m_count + divisor.m_count] or shorts [n, n + m]
+
+		var s:Int = BigIntHelper.nlz(getShort(divisor, n - 1)) - 16; // 0 <= s < 16
+		if (s > 0) {
+			_lsl32x(work, 0, divisor, divisorLength, s);
+			_lsl32x(work, divisorLength, dividend, dividendLength, s);
+		} else {
+			Vector.blit(divisor, 0, work, 0, divisorLength);
+			Vector.blit(dividend, 0, work, divisorLength, dividendLength);
+			work.set(divisorLength + dividendLength, 0);
+		}
+
+		setZero(quotientOut, quotientLength);
+
+		// Main loop.
+		var qhat:Int32, rhat:Int32, p:Int32, t:Int32;
+		var vn:Int = getShort(work, n - 1);
+		j = m - n + 1;
+		while (--j >= 0) {
+			// Compute estimate qhat of q[j]
+			t = (getShort(work, j + n + un) << 16) + getShort(work, j + n + un - 1);
+			qhat = BigIntHelper.u32divu16(t, vn);
+			rhat = t - qhat * vn;
+			while ((qhat >= 65536) || BigIntHelper.u32gtu32(qhat * getShort(work, n - 2), (rhat << 16) + getShort(work, j + n + un - 2))) {
+				qhat -= 1;
+				rhat += vn;
+				if (rhat >= 65536) {
+					break;
+				}
+			}
+
+			// Multiply and subtract
+			k = 0;
+			for (i in 0...n) {
+				p = qhat * getShort(work, i);
+				t = getShort(work, i + j + un) - k - (p & 0xffff);
+				setShort(work, i + j + un, t);
+				k = (p >>> 16) - (t >> 16);
+			}
+			t = getShort(work, j + n + un) - k;
+			setShort(work, j + n + un, t);
+
+			// Store quotient digit.
+			// If we subtracted too much, add back.
+			if (t >= 0) {
+				setShort(quotientOut, j, qhat);
+			} else {
+				setShort(quotientOut, j, qhat - 1);
+				k = 0;
+				for (i in 0...n) {
+					t = getShort(work, i + j + un) + getShort(work, i) + k;
+					setShort(work, i + j + un, t);
+					k = t >> 16;
+				}
+				t = getShort(work, j + n + un) + k;
+				setShort(work, j + n + un, t);
+			}
+		}
+
+		// If the caller wants the remainder, unnormalize it and pass back.
+		if (remainderOut != null) {
+			if (s > 0) {
+				_lsr32(remainderOut, work, divisorLength, divisorLength, s);
+			} else {
+				Vector.blit(work, divisorLength, remainderOut, 0, divisorLength);
+			}
+		}
+	}
+
+	/**
+		Shift multiword integer `input` right by `shift` binary
+		places and store the result in `result`, with the high bit
+		duplicated for bits inserted from the left.
+
+		`shift` must meet the critera 0 <= `shift` < 32.
+
+		`result` and `input` may be the same object.
+	**/
+	public static function arithmeticShiftRight(result:Vector<Int>, input:Vector<Int>, length:Int, shift:Int):Void {
+		if ((length < 1) || (result.length < length) || (input.length < length)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (shift < 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		} else if (shift == 0) {
+			if (input != result) {
+				Vector.blit(input, 0, result, 0, length);
+			}
+		} else if (shift < 32) {
+			_asr32(result, input, length, 0, shift);
+		} else {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+	}
+
+	/**
+		Shift multiword integer `input` right by `shift` binary
+		places and store the result in `result`, with zeros inserted
+		from the left.
+
+		`shift` must meet the critera 0 <= `shift` < 32.
+
+		`result` and `input` may be the same object.
+	**/
+	public static function logicalShiftRight(result:Vector<Int>, input:Vector<Int>, length:Int, shift:Int):Void {
+		if ((length < 1) || (result.length < length) || (input.length < length)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (shift < 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		} else if (shift == 0) {
+			if (input != result) {
+				Vector.blit(input, 0, result, 0, length);
+			}
+		} else if (shift < 32) {
+			_lsr32(result, input, length, 0, shift);
+		} else {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+	}
+
+	/**
+		Shift multiword integer `input` left by `shift` binary
+		places and store the result in `result`, with zeros inserted
+		from the right.
+
+		`shift` must meet the critera 0 <= `shift` < 32.
+
+		`result` and `input` may be the same object.
+	**/
+	public static function shiftLeft(result:Vector<Int>, input:Vector<Int>, length:Int, shift:Int):Void {
+		if ((length < 1) || (result.length < length) || (input.length < length)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (shift < 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		} else if (shift == 0) {
+			if (input != result) {
+				Vector.blit(input, 0, result, 0, length);
+			}
+		} else if (shift < 32) {
+			_lsl32(result, 0, input, length, shift);
+		} else {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+	}
+
+	/**
+		Compare two signed multiword integers.
+
+		Returns -1 if `a < b`; otherwise
+		returns 1 if `a > b`; otherwise
+		returns 0 (`a == b`).
+	**/
+	public static function compareSigned(a:Vector<Int>, b:Vector<Int>, length:Int):Int {
+		if (a != b) {
+			var ah:Int = a.get(length - 1);
+			var bh:Int = b.get(length - 1);
+			if ((ah ^ bh) < 0) {
+				// differing signs
+				return (ah >> 30) | 1;
+			}
+			return compareUnsigned(a, b, length);
+		}
+		return 0;
+	}
+
+	/**
+		Compare two unsigned multiword integers.
+
+		Returns -1 if `a < b`; otherwise
+		returns 1 if `a > b`; otherwise
+		returns 0 (`a == b`).
+	**/
+	public static function compareUnsigned(a:Vector<Int>, b:Vector<Int>, length:Int):Int {
+		if (a != b) {
+			var an:Int, bn:Int, d:Int;
+			var x:Int32 = -2147483648;
+			while (--length >= 0) {
+				an = a.get(length) + x;
+				bn = b.get(length) + x;
+				if (an > bn)
+					return 1;
+				if (an < bn)
+					return -1;
+			}
+		}
+		return 0;
+	}
+
+	/**
+		Fills a vector with zeros.
+		@param dest The destination vector.
+		@param length The number of words to zero out.
+	**/
+	public static function setZero(dest:Vector<Int>, length:Int):Void {
+		if (dest.length < length) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		for (i in 0...length) {
+			dest.set(i, 0);
+		}
+	}
+
+	/**
+		Sets a multi-word integer from a single unsigned `Int`.
+		@param dest The destination vector.
+		@param length The total length of the destination.
+		@param value The integer value to set.
+	**/
+	public static function setFromIntUnsigned(dest:Vector<Int>, length:Int, value:Int):Void {
+		if (dest.length < length) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		dest.set(0, value);
+		for (i in 1...length) {
+			dest.set(i, 0);
+		}
+	}
+
+	/**
+		Sets a multi-word integer from an unsigned hexadecimal string.
+		@param dest The destination vector.
+		@param length The length of the destination.
+		@param value The hexadecimal string.
+		@return `true` on success.
+	**/
+	public static function setFromHexUnsigned(dest:Vector<Int>, length:Int, value:String):Bool {
+		if ((value == null) || (dest == null)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (dest.length < length) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		var index = value.length;
+		if (index <= 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (length < 1) {
+			return false;
+		}
+		var c:Int;
+		var start:Int = 0;
+		while (start < index) {
+			c = value.charCodeAt(start);
+			if ((c != 48) && (c != 32)) {
+				break;
+			}
+			++start;
+		}
+		var pos:Int = 0;
+		var bit:Int = 0;
+		var acc:Int32 = 0;
+		while (index > start) {
+			c = value.charCodeAt(--index);
+			if ((48 <= c) && (c <= 57)) {
+				c -= 48;
+			} else if ((65 <= c) && (c <= 70)) {
+				c -= 55;
+			} else if ((97 <= c) && (c <= 102)) {
+				c -= 87;
+			} else if (c == 32) {
+				continue;
+			} else {
+				throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+			}
+			acc |= c << bit;
+			bit += 4;
+			if (bit >= 32) {
+				if (pos >= length) {
+					return false;
+				}
+				dest.set(pos++, acc);
+				acc = 0;
+				bit = 0;
+			}
+		}
+		if (bit > 0) {
+			if (pos >= length) {
+				return false;
+			}
+			dest.set(pos++, acc);
+		}
+		for (c in pos...length) {
+			dest.set(c, 0);
+		}
+		return true;
+	}
+
+	/**
+		Converts a multi-word integer to a hexadecimal string.
+		@param input The source vector.
+		@param length The number of words.
+		@return The hexadecimal string representation.
+	**/
+	public static function toHex(input:Vector<Int>, length:Int):String {
+		var sb = new StringBuf();
+		while (--length >= 0) {
+			var v = input.get(length);
+			for (j in 0...8) {
+				var c:Int = (v >> 28) & 0x0f;
+				v <<= 4;
+				c = (c < 10) ? (c + 48) : (c - 10 + 97);
+				sb.addChar(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+		Get the value in decimal form.
+	**/
+	public static function toDecimalSigned(value:Vector<Int>, length:Int):String {
+		var sb = new StringBuf();
+		var work = new Vector<Int>(length);
+		if (isNegative(value, length)) {
+			negate(work, value, length);
+			sb.addChar(45); // '-'
+		} else {
+			copy(work, value, length);
+		}
+		return _toDecimal(sb, work, length);
+	}
+	
+	/**
+		Converts a signed multi-word integer to a string in the specified base.
+		@param value The source vector.
+		@param length The number of words.
+		@param radix The base for the conversion.
+		@return The string representation.
+	**/
+	public static function toBaseString(value : Vector<Int32>, length : Int, radix:Int) : String
+	{
+		var sb = new StringBuf();
+		var work = new Vector<Int32>(length);
+		if (isNegative(value, length))
+		{
+			negate(work, value, length);
+			sb.addChar(45);	// '-'
+		}
+		else
+		{
+			copy(work, value, length);
+		}
+		return _toBase(sb, work, length, radix);
+	}
+
+	/**
+		Get the value in decimal form.
+	**/
+	public static function toDecimalUnsigned(value:Vector<Int>, length:Int):String {
+		var sb = new StringBuf();
+		var work = new Vector<Int>(length);
+		copy(work, value, length);
+		return _toDecimal(sb, work, length);
+	}
+
+	/**
+		Copies words from one vector to another.
+		@param dest The destination vector.
+		@param source The source vector.
+		@param length The number of words to copy.
+	**/
+	public static function copy(dest:Vector<Int>, source:Vector<Int>, length:Int):Void {
+		if (dest.length < length) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		Vector.blit(source, 0, dest, 0, length);
+	}
+
+	/**
+		Gets the value of a single bit from a signed multi-word integer.
+		@param value The source vector.
+		@param length The number of words.
+		@param index The index of the bit to get.
+		@return 1 if the bit is set, 0 otherwise.
+	**/
+	public static function getBitSigned(value:Vector<Int>, length:Int, index:Int):Int {
+		var d:Int = index >> 5;
+		if (d >= length) {
+			return value.get(length - 1) >>> 31;
+		}
+		return (value.get(d) >> (index & 31)) & 1;
+	}
+
+	//-----------------------------------------------------------------------
+	// Private helpers
+	//-----------------------------------------------------------------------
+	// assumes 0 < shift < 32
+	// ok if output == input
+	private static function _lsl32(output:Vector<Int>, outputOffset:Int, input:Vector<Int>, inputSize:Int, shift:Int):Void {
+		var x:Int = input.get(inputSize - 1);
+		var r:Int = 32 - shift;
+		var y:Int;
+		while (--inputSize > 0) {
+			y = input.get(inputSize - 1);
+			x = (x << shift) | (y >>> r);
+			output.set(inputSize + outputOffset, x);
+			x = y;
+		}
+		output.set(outputOffset, x << shift);
+	}
+
+	// assumes 0 < shift < 32
+	// ok if output == input
+	// note this writes inputSize + 1 words to output
+	private static function _lsl32x(output:Vector<Int>, outputOffset:Int, input:Vector<Int>, inputSize:Int, shift:Int):Void {
+		var x:Int = 0;
+		var r:Int = 32 - shift;
+		var y:Int;
+		while (inputSize > 0) {
+			y = input.get(inputSize - 1);
+			x = (x << shift) | (y >>> r);
+			output.set(inputSize + outputOffset, x);
+			x = y;
+			--inputSize;
+		}
+		output.set(outputOffset, x << shift);
+	}
+
+	// assumes 0 < shiftBits < 32
+	// assumes shiftDigits < length
+	private static function _asr32(result:Vector<Int>, input:Vector<Int>, length:Int, shiftDigits:Int, shiftBits:Int32):Void {
+		var r:Int = 32 - shiftBits;
+		var i:Int = 0;
+		while (i < length - shiftDigits - 1) {
+			result.set(i, (input.get(i + shiftDigits) >>> shiftBits) | (input.get(i + shiftDigits + 1) << r));
+			++i;
+		}
+		result.set(i, input.get(i + shiftDigits) >> shiftBits);
+	}
+
+	// assumes 0 < shift < 32
+	// ok if output == input
+	private static function _lsr32(output:Vector<Int>, input:Vector<Int>, inputSize:Int, inputOffset:Int, shift:Int32):Void {
+		var r:Int = 32 - shift;
+		var i:Int = 0;
+		while (i < inputSize - 1) {
+			output.set(i, (input.get(inputOffset + i) >>> shift) | (input.get(inputOffset + i + 1) << r));
+			++i;
+		}
+		output.set(i, input.get(inputOffset + i) >>> shift);
+	}
+
+	@:noCompletion
+	private static function _toDecimal(sb:StringBuf, value:Vector<Int>, length:Int):String {
+		return _toBase(sb,value,length,10);
+	}
+	
+	@:noCompletion
+	private static function _toBase(sb : StringBuf, value : Vector<Int32>, length : Int, radix:Int) : String
+	{
+		length = getLengthUnsigned(value, length);
+		var digits = new Vector<Int32>(length * Math.ceil(Math.log(4294967296)/Math.log(radix)));
+		var work = new Vector<Int32>(length + 1 + 1);
+		var pos : Int = digits.length;
+		var r : Int;
+		do
+		{
+			r = divideIntUnsigned(value, length, radix, value, work);
+			length = getLengthUnsigned(value, length);
+			if ( r < 10) {
+				digits.set(--pos, r + 48);
+			} else {
+				digits.set(--pos, r + 87);
+			}
+		} while (!isZero(value, length));
+		for (i in pos ... digits.length)
+		{
+			sb.addChar(digits.get(i));
+		}
+		return sb.toString();
+	}
+
+	private static inline function getShort(v:Vector<Int>, n:Int):Int {
+		return (v.get(n >> 1) >> ((n & 1) << 4)) & 0xffff;
+	}
+
+	private static inline function setShort(a:Vector<Int>, n:Int, v:Int32):Void {
+		var s:Int = (n & 1) << 4;
+		var t:Int = a.get(n >> 1) & (~0xffff >>> s);
+		a.set(n >> 1, t | ((v & 0xffff) << s));
+	}
+}

+ 551 - 0
std/haxe/math/bigint/MutableBigInt.hx

@@ -0,0 +1,551 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.ds.Vector;
+import haxe.io.Bytes;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+/**
+	A mutable arbitrary-precision integer.
+	
+	This abstract type provides in-place modification of a `BigInt`'s value,
+	which can be more efficient for operations that involve many intermediate steps,
+	as it avoids repeated memory allocation.
+**/
+@:allow(haxe.math.bigint)
+abstract MutableBigInt(MutableBigInt_) {
+	//-----------------------------------------------------------------------
+	// Public interface
+	//-----------------------------------------------------------------------
+	/**
+		Returns the sign of this `MutableBigInt`.
+		@return -1 if negative, 1 if positive, 0 if zero.
+	**/
+	public inline function sign():Int {
+		return BigInt_.sign1(this);
+	}
+
+	/**
+		Checks if this `MutableBigInt` is equal to zero.
+		@return `true` if the value is 0, otherwise `false`.
+	**/
+	public inline function isZero():Bool {
+		return BigInt_.isZero1(this);
+	}
+
+	/**
+		Checks if this `MutableBigInt` is a negative number.
+		@return `true` if the value is less than 0, otherwise `false`.
+	**/
+	public inline function isNegative():Bool {
+		return BigInt_.isNegative1(this);
+	}
+
+	/**
+		Returns the string representation of this `MutableBigInt` in the specified base.
+		@param radix The base for the conversion (e.g., 10 for decimal).
+		@return The string representation of the number.
+	**/
+	public inline function toString(radix:Int=10):String {
+		return BigInt_.toString1(this,radix);
+	}
+
+	/**
+		Returns the hexadecimal string representation of this `MutableBigInt`.
+		@return The hexadecimal string.
+	**/
+	public inline function toHex():String {
+		return BigInt_.toHex1(this);
+	}
+
+	/**
+		Converts this `MutableBigInt` to a `Bytes` sequence.
+		@return A `Bytes` object representing the number.
+	**/
+	public inline function toBytes():Bytes {
+		return BigInt_.toBytes1(this);
+	}
+
+	/**
+		Converts this `MutableBigInt` to a `Vector` of `Int`s.
+		@param output The vector to write the integer words into.
+		@return The number of words written.
+	**/
+	public inline function toInts(output:Vector<Int>):Int {
+		return BigInt_.toInts1(this, output);
+	}
+
+	/**
+		Sets the value of this `MutableBigInt` from a standard `Int`.
+		@param value The new integer value.
+	**/
+	public inline function setFromInt(value:Int):Void {
+		var a:MutableBigInt_ = this;
+		a.setFromInt(value);
+	}
+
+	/**
+		Sets the value from a `Vector` of unsigned `Int` words.
+		@param value The vector of integer words.
+		@param length The number of words to use. If 0, uses the whole vector.
+	**/
+	public inline function setFromUnsignedInts(value:Vector<Int>, length:Int = 0):Void {
+		var a:MutableBigInt_ = this;
+		a.setFromUnsignedInts(value, length);
+	}
+	
+	/**
+		Sets the value from a portion of another `Vector` of `Int`s.
+		@param source The source vector.
+		@param sourcePosition The starting position in the source vector.
+		@param length The number of words to copy.
+	**/
+	public inline function setFromVector(source : Vector<Int32>, sourcePosition:Int, length : Int ) : Void
+	{
+		var a:MutableBigInt_ = this;
+		a.setFromVector(source,sourcePosition, length);
+	}
+
+	/**
+		Sets the value from a `Bytes` sequence, interpreted as unsigned big-endian.
+		@param value The source `Bytes`.
+		@param offset The starting offset.
+		@param length The number of bytes to read.
+	**/
+	public inline function setFromBigEndianBytesUnsigned(value:Bytes, offset:Int = 0, length:Int = 0):Void {
+		var a:MutableBigInt_ = this;
+		a.setFromBigEndianBytesUnsigned(value, offset, length);
+	}
+
+	/**
+		Sets the value from a `Bytes` sequence, interpreted as unsigned little-endian.
+		@param value The source `Bytes`.
+		@param offset The starting offset.
+		@param length The number of bytes to read.
+	**/
+	public inline function setFromLittleEndianBytesUnsigned(value:Bytes, offset:Int = 0, length:Int = 0):Void {
+		var a:MutableBigInt_ = this;
+		a.setFromLittleEndianBytesUnsigned(value, offset, length);
+	}
+
+	/**
+		Resets the value of this `MutableBigInt` to zero.
+	**/
+	public inline function clear():Void {
+		var a:MutableBigInt_ = this;
+		a.clear();
+	}
+
+	/**
+		Copies the value from another `BigInt` into this one.
+		@param other The `BigInt` to copy from.
+	**/
+	public inline function copyFrom(other:BigInt):Void {
+		var a:MutableBigInt_ = this;
+		a.copyFrom(other);
+	}
+
+	/**
+		Gets the value of a single bit at the specified index.
+		@param index The index of the bit to get.
+		@return 1 if the bit is set, 0 otherwise.
+	**/
+	public inline function getBit(index:Int):Int {
+		return BigIntArithmetic.getBit(this, index);
+	}
+
+	/**
+		Creates a `MutableBigInt` from unsigned, big-endian bytes.
+		@param value The `Bytes` to convert.
+		@return A new `MutableBigInt` instance.
+	**/
+	public static function fromBigEndianBytesUnsigned(value:Bytes):MutableBigInt {
+		var r = new MutableBigInt_();
+		r.setFromBigEndianBytesUnsigned(value);
+		return new MutableBigInt(r);
+	}
+
+	/**
+		Creates a `MutableBigInt` from unsigned, little-endian bytes.
+		@param value The `Bytes` to convert.
+		@return A new `MutableBigInt` instance.
+	**/
+	public static function fromLittleEndianBytesUnsigned(value:Bytes):MutableBigInt {
+		var r = new MutableBigInt_();
+		r.setFromLittleEndianBytesUnsigned(value);
+		return new MutableBigInt(r);
+	}
+
+	/**
+		Converts this number to its absolute value, in place.
+		@return This `MutableBigInt` instance.
+	**/
+	public function abs():MutableBigInt {
+		return this.abs();
+	}
+
+	/**
+		Calculates the GCD of this and another `BigInt`.
+		@param b The other `BigInt`.
+		@return A new `MutableBigInt` holding the result.
+	**/
+	public function gcd(b:BigInt):MutableBigInt {
+		return this.gcd(b);
+	}
+
+	/**
+		Raises this number to the power of `exponent`.
+		@param exponent The non-negative exponent.
+		@return A new `MutableBigInt` holding the result.
+	**/
+	public function pow(exponent:UInt):MutableBigInt {
+		return this.pow(exponent);
+	}
+
+	/**
+		Calculates `(this ^ exponent) mod modulus`.
+		@param exponent The exponent.
+		@param modulus The modulus.
+		@return A new `MutableBigInt` holding the result.
+	**/
+	public function modPow(exponent:BigInt, modulus:BigInt):MutableBigInt {
+		return this.modPow(exponent, modulus);
+	}
+
+	/**
+		Tests if this number is probably prime.
+		@param tolerance Certainty level for the Miller-Rabin test.
+		@return `true` if probably prime.
+	**/
+	public function isProbablePrime(tolerance:UInt):Bool {
+		return this.isProbablePrime(tolerance);
+	}
+
+	/**
+		Gets the index of the lowest-set (rightmost) '1' bit.
+		@return The bit index, or -1 if zero.
+	**/
+	public function getLowestSetBit():Int {
+		return this.getLowestSetBit();
+	}
+
+	/**
+		Returns the number of bits
+		@return The bit length.
+	**/
+	public function bitLength():Int {
+		return this.bitLength();
+	}
+
+	//-----------------------------------------------------------------------
+	// Operators
+	//-----------------------------------------------------------------------
+	// The declaration order of the operations is significant in Haxe.
+	// Recommended order is:
+	//	* MutableBigInt <binOp=> Int
+	//	* MutableBigInt <binOp=> BigInt
+	//	* MutableBigInt <binOp=> MutableBigInt
+	//	* MutableBigInt <binOp> Int
+	//	* MutableBigInt <binOp> BigInt
+	//	* MutableBigInt <binOp> MutableBigInt
+	// Unary negation
+	@:op(-A) @:noCompletion @:noDoc public static inline function negate_(a:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.negate1(a));
+	}
+
+	// Binary equality
+	@:op(A == B) @:noCompletion @:noDoc public static inline function eqInt_(a:MutableBigInt, b:Int):Bool {
+		return BigInt_.equals2Int(a, b);
+	}
+
+	@:op(A == B) @:noCompletion @:noDoc public static inline function eq_(a:MutableBigInt, b:BigInt):Bool {
+		return BigInt_.equals2(a, b);
+	}
+
+	@:op(A == B) @:noCompletion @:noDoc public static inline function eqMutable_(a:MutableBigInt, b:MutableBigInt):Bool {
+		return BigInt_.equals2(a, b);
+	}
+
+	// Binary inequality
+	@:op(A != B) @:noCompletion @:noDoc public static inline function ineqInt_(a:MutableBigInt, b:Int):Bool {
+		return !BigInt_.equals2Int(a, b);
+	}
+
+	@:op(A != B) @:noCompletion @:noDoc public static inline function ineq_(a:MutableBigInt, b:BigInt):Bool {
+		return !BigInt_.equals2(a, b);
+	}
+
+	@:op(A != B) @:noCompletion @:noDoc public static inline function ineqMutable_(a:MutableBigInt, b:MutableBigInt):Bool {
+		return !BigInt_.equals2(a, b);
+	}
+
+	// Binary less than
+	@:op(A < B) @:noCompletion @:noDoc public static inline function ltInt_(a:MutableBigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) < 0;
+	}
+
+	@:op(A < B) @:noCompletion @:noDoc public static inline function lt_(a:MutableBigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) < 0;
+	}
+
+	@:op(A < B) @:noCompletion @:noDoc public static inline function ltMutable_(a:MutableBigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) < 0;
+	}
+
+	// Binary less than or equal
+	@:op(A <= B) @:noCompletion @:noDoc public static inline function lteInt_(a:MutableBigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) <= 0;
+	}
+
+	@:op(A <= B) @:noCompletion @:noDoc public static inline function lte_(a:MutableBigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) <= 0;
+	}
+
+	@:op(A <= B) @:noCompletion @:noDoc public static inline function lteMutable_(a:MutableBigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) <= 0;
+	}
+
+	// Binary greater than
+	@:op(A > B) @:noCompletion @:noDoc public static inline function gtInt_(a:MutableBigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) > 0;
+	}
+
+	@:op(A > B) @:noCompletion @:noDoc public static inline function gt_(a:MutableBigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) > 0;
+	}
+
+	@:op(A > B) @:noCompletion @:noDoc public static inline function gtMutable_(a:MutableBigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) > 0;
+	}
+
+	// Binary greater than or equal
+	@:op(A >= B) @:noCompletion @:noDoc public static inline function gteInt_(a:MutableBigInt, b:Int):Bool {
+		return BigIntArithmetic.compareInt(a, b) >= 0;
+	}
+
+	@:op(A >= B) @:noCompletion @:noDoc public static inline function gte_(a:MutableBigInt, b:BigInt):Bool {
+		return BigIntArithmetic.compare(a, b) >= 0;
+	}
+
+	@:op(A >= B) @:noCompletion @:noDoc public static inline function gteMutable_(a:MutableBigInt, b:MutableBigInt):Bool {
+		return BigIntArithmetic.compare(a, b) >= 0;
+	}
+
+	// Binary addition
+	@:op(A += B) @:noCompletion @:noDoc public static inline function addAssignInt_(a:MutableBigInt, b:Int):MutableBigInt {
+		BigIntArithmetic.addInt(a, a, b);
+		return a;
+	}
+
+	@:op(A += B) @:noCompletion @:noDoc public static inline function addAssign_(a:MutableBigInt, b:BigInt):MutableBigInt {
+		BigIntArithmetic.add(a, a, b);
+		return a;
+	}
+
+	@:op(A += B) @:noCompletion @:noDoc public static inline function addAssignMutable_(a:MutableBigInt, b:MutableBigInt):MutableBigInt {
+		BigIntArithmetic.add(a, a, b);
+		return a;
+	}
+
+	@:op(A + B) @:noCompletion @:noDoc public static inline function addInt_(a:MutableBigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.addInt2(a, b));
+	}
+
+	@:op(A + B) @:noCompletion @:noDoc public static inline function add_(a:MutableBigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.add2(a, b));
+	}
+
+	@:op(A + B) @:noCompletion @:noDoc public static inline function addMutable_(a:MutableBigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.add2(a, b));
+	}
+
+	// Binary subtraction
+	@:op(A -= B) @:noCompletion @:noDoc public static inline function subAssignInt_(a:MutableBigInt, b:Int):MutableBigInt {
+		BigIntArithmetic.subtractInt(a, a, b);
+		return a;
+	}
+
+	@:op(A -= B) @:noCompletion @:noDoc public static inline function subAssign_(a:MutableBigInt, b:BigInt):MutableBigInt {
+		BigIntArithmetic.subtract(a, a, b);
+		return a;
+	}
+
+	@:op(A -= B) @:noCompletion @:noDoc public static inline function subAssignMutable_(a:MutableBigInt, b:MutableBigInt):MutableBigInt {
+		BigIntArithmetic.subtract(a, a, b);
+		return a;
+	}
+
+	@:op(A - B) @:noCompletion @:noDoc public static inline function subInt_(a:MutableBigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.subInt2(a, b));
+	}
+
+	@:op(A - B) @:noCompletion @:noDoc public static inline function sub_(a:MutableBigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.sub2(a, b));
+	}
+
+	@:op(A - B) @:noCompletion @:noDoc public static inline function subMutable_(a:MutableBigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.sub2(a, b));
+	}
+
+	// Binary multiplication
+	@:op(A *= B) @:noCompletion @:noDoc public static inline function mulAssignInt_(a:MutableBigInt, b:Int):MutableBigInt {
+		MutableBigInt_.multiplyAssignInt2(a, b);
+		return a;
+	}
+
+	@:op(A *= B) @:noCompletion @:noDoc public static inline function mulAssign_(a:MutableBigInt, b:BigInt):MutableBigInt {
+		MutableBigInt_.multiplyAssign2(a, b);
+		return a;
+	}
+
+	@:op(A *= B) @:noCompletion @:noDoc public static inline function mulAssignMutable_(a:MutableBigInt, b:MutableBigInt):MutableBigInt {
+		MutableBigInt_.multiplyAssign2(a, b);
+		return a;
+	}
+
+	@:op(A * B) @:noCompletion @:noDoc public static inline function mulInt_(a:MutableBigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.multiplyInt2(a, b));
+	}
+
+	@:op(A * B) @:noCompletion @:noDoc public static inline function mul_(a:MutableBigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.multiply2(a, b));
+	}
+
+	@:op(A * B) @:noCompletion @:noDoc public static inline function mulMutable_(a:MutableBigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.multiply2(a, b));
+	}
+
+	// Binary division
+	@:op(A /= B) @:noCompletion @:noDoc public static inline function divAssignInt_(a:MutableBigInt, b:Int):MutableBigInt {
+		MutableBigInt_.divideAssignInt2(a, b);
+		return a;
+	}
+
+	@:op(A /= B) @:noCompletion @:noDoc public static inline function divAssign_(a:MutableBigInt, b:BigInt):MutableBigInt {
+		MutableBigInt_.divideAssign2(a, b);
+		return a;
+	}
+
+	@:op(A /= B) @:noCompletion @:noDoc public static inline function divAssignMutable_(a:MutableBigInt, b:MutableBigInt):MutableBigInt {
+		MutableBigInt_.divideAssign2(a, b);
+		return a;
+	}
+
+	@:op(A / B) @:noCompletion @:noDoc public static inline function divInt_(a:MutableBigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.divideInt2(a, b));
+	}
+
+	@:op(A / B) @:noCompletion @:noDoc public static inline function div_(a:MutableBigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.divide2(a, b));
+	}
+
+	@:op(A / B) @:noCompletion @:noDoc public static inline function divMutable_(a:MutableBigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.divide2(a, b));
+	}
+
+	// Binary modulus
+	@:op(A %= B) @:noCompletion @:noDoc public static inline function modAssignInt_(a:MutableBigInt, b:Int):MutableBigInt {
+		MutableBigInt_.modulusAssignInt2(a, b);
+		return a;
+	}
+
+	@:op(A %= B) @:noCompletion @:noDoc public static inline function modAssign_(a:MutableBigInt, b:BigInt):MutableBigInt {
+		MutableBigInt_.modulusAssign2(a, b);
+		return a;
+	}
+
+	@:op(A %= B) @:noCompletion @:noDoc public static inline function modAssignMutable_(a:MutableBigInt, b:MutableBigInt):MutableBigInt {
+		MutableBigInt_.modulusAssign2(a, b);
+		return a;
+	}
+
+	@:op(A % B) @:noCompletion @:noDoc public static inline function modInt_(a:MutableBigInt, b:Int):Int {
+		return BigInt_.modulusInt2(a, b);
+	}
+
+	@:op(A % B) @:noCompletion @:noDoc public static inline function mod_(a:MutableBigInt, b:BigInt):BigInt {
+		return new BigInt(BigInt_.modulus2(a, b));
+	}
+
+	@:op(A % B) @:noCompletion @:noDoc public static inline function modMutable_(a:MutableBigInt, b:MutableBigInt):BigInt {
+		return new BigInt(BigInt_.modulus2(a, b));
+	}
+
+	// Binary AND
+	@:op(A & B) @:noCompletion @:noDoc public static inline function andInt_(a:MutableBigInt, b:Int):Int {
+		return BigIntArithmetic.bitwiseAndInt(a, b);
+	}
+
+	// Binary shift left
+	@:op(A <<= B) @:noCompletion @:noDoc public static inline function arithmeticShiftLeftAssign_(a:MutableBigInt, b:Int):MutableBigInt {
+		MutableBigInt_.arithmeticShiftLeftAssign2(a, b);
+		return a;
+	}
+
+	@:op(A << B) @:noCompletion @:noDoc public static inline function asl_(a:MutableBigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.arithmeticShiftLeft2(a, b));
+	}
+
+	// Binary shift right
+	@:op(A >>= B) @:noCompletion @:noDoc public static inline function arithmeticShiftRightAssign_(a:MutableBigInt, b:Int):MutableBigInt {
+		MutableBigInt_.arithmeticShiftRightAssign2(a, b);
+		return a;
+	}
+
+	@:op(A >> B) @:noCompletion @:noDoc public static inline function asr_(a:MutableBigInt, b:Int):BigInt {
+		return new BigInt(BigInt_.arithmeticShiftRight2(a, b));
+	}
+
+	//-----------------------------------------------------------------------
+	// Automatic conversions
+	//-----------------------------------------------------------------------
+
+	@:from @:noCompletion @:noDoc public static inline function fromInt_(a:Int):MutableBigInt {
+		return new MutableBigInt(MutableBigInt_.fromInt(a));
+	}
+
+	@:from @:noCompletion @:noDoc public static inline function fromBigInt_(a:BigInt_):MutableBigInt {
+		return new MutableBigInt(MutableBigInt_.fromBigInt(a));
+	}
+
+	@:from @:noCompletion @:noDoc public static inline function fromMutableBigInt_(a:MutableBigInt_):MutableBigInt {
+		return new MutableBigInt(MutableBigInt_.fromBigInt(a));
+	}
+
+	@:to @:noCompletion @:noDoc public inline function toMutableBigInt_():MutableBigInt_ {
+		return this;
+	}
+
+	@:to @:noCompletion @:noDoc public inline function toBigInt():BigInt {
+		return new BigInt(this);
+	}
+
+	//-----------------------------------------------------------------------
+	// Private implementation
+	//-----------------------------------------------------------------------
+
+	private inline function new(a:MutableBigInt_) {
+		this = a;
+	}
+}

+ 518 - 0
std/haxe/math/bigint/MutableBigInt_.hx

@@ -0,0 +1,518 @@
+/*
+ * Copyright (C)2005-2023 Haxe Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+package haxe.math.bigint;
+
+import haxe.math.bigint.BigIntException;
+import haxe.math.bigint.BigIntError;
+import haxe.math.bigint.BigIntHelper;
+import haxe.ds.Vector;
+import haxe.io.Bytes;
+
+/* Original code courtesy Chuck Batson (github.com/cbatson) */
+@:noCompletion
+@:noDoc
+@:allow(unit)
+@:allow(haxe.math.bigint)
+class MutableBigInt_ extends BigInt_ {
+
+	private var m_owned:Bool = false;
+
+	private static var s_testAllocation:Bool = false;
+	private static var s_debugAllocationPadding:Int = 0;
+	
+	//-----------------------------------------------------------------------
+	// Public interface
+	//-----------------------------------------------------------------------
+
+	/**
+		Set the value of this big int with an integer of value `value`.
+		@param value The new integer value for this instance.
+	**/
+	public function setFromInt(value:Int):Void {
+		ensureCapacity(1, false);
+		m_data.set(0, value);
+		m_count = 1;
+	}
+
+	/**
+		Set the value of this big integer with the signed value
+		represented by the hexadecimal string `value`.
+		@param value A string containing a hexadecimal number.
+	**/
+	public inline function setFromHexSigned(value:String):Void {
+		_setFromHex(value, true);
+	}
+
+	/**
+		Set the value of this big integer with the unsigned value
+		represented by the hexadecimal string `value`.
+		@param value A string containing a hexadecimal number.
+	**/
+	public inline function setFromHexUnsigned(value:String):Void {
+		_setFromHex(value, false);
+	}
+
+	/**
+		Set the value of this big integer by parsing the given string.
+		@param value The string representation of the number.
+		@param radix The base of the number in the string (e.g., 10, 16).
+	**/
+	public function setFromString(value:String, radix:Int = 10):Void {
+		if ((value == null) || (value.length < 1)) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		var negate = value.charCodeAt(0) == 0x2d;
+		var index = negate ? 1 : 0;
+		if (value.length <= index) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ( value.charCodeAt(index) ==  0x30) {
+			if ( value.charCodeAt(index+1) ==  0x62 ) { // binary
+				radix = 2;
+				index +=2;
+			} else if ( value.charCodeAt(index+1) ==  0x6F ) { //octal
+				radix = 8;
+				index +=2;
+			} else if ( value.charCodeAt(index+1) ==  0x78 ) { // hex
+				setFromHexSigned((negate?"-":"")+value.substr(index+2));
+				return;
+			}
+		}
+		if (radix == 16) { 
+			setFromHexSigned(value);
+			return;
+		}
+		this.setFromInt(0);
+		var t = new MutableBigInt_();
+		var endDigit:Int = 57;
+		var extraEndDigit:Int=0;
+		if (  radix <= 10 ) {
+			endDigit =  48 + radix-1;
+		} else {
+			extraEndDigit =  radix-11;
+		}
+		for (i in index...value.length) {
+			var c = value.charCodeAt(i);
+			if ( ((48 <= c) && (c <= endDigit)) || (radix > 10 && ( (65<=c && c<=(65+extraEndDigit)) || (97<=c && c<=(97+extraEndDigit)) ) ) ) {
+				BigIntArithmetic.multiplyInt(t, this, radix);
+				if ( c <= endDigit) {
+					BigIntArithmetic.addInt(this, t, c - 48);
+				} else if (c<=(65+extraEndDigit)) {
+					BigIntArithmetic.addInt(this, t, c - 55);
+				} else {
+					BigIntArithmetic.addInt(this, t, c - 87);
+				}
+			} else {
+				throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+			}
+		}
+		if (negate) {
+			BigIntArithmetic.negate(this, this);
+		}
+	}
+
+	/**
+		Set the value of this big integer from a vector of unsigned `Int`s.
+		@param value The `Vector` containing the integer words.
+		@param length The number of words to use from the vector.
+	**/
+	public function setFromUnsignedInts(value:Vector<Int>, length:Int = 0):Void {
+		if (length <= 0) {
+			length = value.length;
+		}
+		var neg = value.get(length - 1) >>> 31;
+		ensureCapacity(length + neg, false);
+		m_data.set(length + neg - 1, 0);
+		MultiwordArithmetic.copy(m_data, value, length);
+		m_count = length + neg;
+		compact();
+	}
+	
+	/**
+		Set the value from a portion of another `Vector` of `Int`s.
+		@param source The source vector.
+		@param sourcePosition The starting position in the source vector.
+		@param length The number of words to copy.
+	**/
+	public function setFromVector(source : Vector<Int32>, sourcePosition:Int, length : Int ) : Void
+	{
+		ensureCapacity(length , false);
+		Vector.blit(source, sourcePosition, m_data, 0, length);
+		m_count = length;
+	}
+
+	/**
+		Set the value from a `Bytes` sequence, interpreted as signed big-endian.
+		@param value The source `Bytes`.
+		@param offset The starting offset in the bytes.
+		@param valueLength The number of bytes to read.
+	**/
+	public function setFromBigEndianBytesSigned(value:Bytes, offset:Int = 0, valueLength:Int = 0):Void {
+		if (value == null) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if (valueLength <= 0) {
+			valueLength = value.length;
+		}
+		if (offset + valueLength > value.length) {
+			throw new BigIntException(BigIntError.BUFFER_TOO_SMALL);
+		}
+		if (valueLength < 1) {
+			setFromInt(0);
+			return;
+		}
+		var length = (valueLength + 3) >> 2;
+		ensureCapacity(length, false);
+		m_data.set(length - 1, 0);
+		var pos = 0;
+		var i = offset + valueLength;
+		while (i >= offset + 4) {
+			m_data.set(pos++, (value.get(i - 1) << 0) | (value.get(i - 2) << 8) | (value.get(i - 3) << 16) | (value.get(i - 4) << 24));
+			i -= 4;
+		}
+		if (i > offset) {
+			var x:Int = 0;
+			for (j in offset...i) {
+				x = (x << 8) | value.get(j);
+			}
+			m_data.set(pos++, x);
+		}
+		m_count = length;
+		compact();
+	}
+
+	/**
+		Set the value from a `Bytes` sequence, interpreted as unsigned big-endian.
+		@param value The source `Bytes`.
+		@param offset The starting offset in the bytes.
+		@param valueLength The number of bytes to read.
+	**/
+	public function setFromBigEndianBytesUnsigned(value:Bytes, offset:Int = 0, valueLength:Int = 0):Void {
+		if (valueLength <= 0) {
+			valueLength = value.length;
+		}
+		if (offset + valueLength > value.length) {
+			throw new BigIntException(BigIntError.BUFFER_TOO_SMALL);
+		}
+		if (valueLength < 1) {
+			setFromInt(0);
+			return;
+		}
+		var neg = ((valueLength & 3) == 0) ? (value.get(0) >> 7) : 0;
+		var length = (valueLength + 3) >> 2;
+		ensureCapacity(length + neg, false);
+		m_data.set(length + neg - 1, 0);
+		var pos = 0;
+		var i = offset + valueLength;
+		while (i >= offset + 4) {
+			m_data.set(pos++, (value.get(i - 1) << 0) | (value.get(i - 2) << 8) | (value.get(i - 3) << 16) | (value.get(i - 4) << 24));
+			i -= 4;
+		}
+		if (i > offset) {
+			var x:Int = 0;
+			for (j in offset...i) {
+				x = (x << 8) | value.get(j);
+			}
+			m_data.set(pos++, x);
+		}
+		m_count = length + neg;
+		compact();
+	}
+
+	/**
+		Set the value from a `Bytes` sequence, interpreted as unsigned little-endian.
+		@param value The source `Bytes`.
+		@param offset The starting offset in the bytes.
+		@param valueLength The number of bytes to read.
+	**/
+	public function setFromLittleEndianBytesUnsigned(value:Bytes, offset:Int = 0, valueLength:Int = 0):Void {
+		if (valueLength <= 0) {
+			valueLength = value.length;
+		}
+		if (offset + valueLength > value.length) {
+			throw new BigIntException(BigIntError.BUFFER_TOO_SMALL);
+		}
+		if (valueLength < 1) {
+			setFromInt(0);
+			return;
+		}
+		var neg = ((valueLength & 3) == 0) ? (value.get(valueLength - 1) >> 7) : 0;
+		var length = (valueLength + 3) >> 2;
+		ensureCapacity(length + neg, false);
+		m_data.set(length + neg - 1, 0);
+		var pos = 0;
+		var i = offset;
+		while (i <= offset + valueLength - 4) {
+			m_data.set(pos++, (value.get(i + 0) << 0) | (value.get(i + 1) << 8) | (value.get(i + 2) << 16) | (value.get(i + 3) << 24));
+			i += 4;
+		}
+		if (i < offset + valueLength) {
+			var x:Int = 0;
+			for (j in i...offset + valueLength) {
+				x |= value.get(j) << ((j - i) << 3);
+			}
+			m_data.set(pos++, x);
+		}
+		m_count = length + neg;
+		compact();
+	}
+
+	/**
+		Resets the value of this `MutableBigInt` to zero.
+	**/
+	public function clear():Void {
+		MultiwordArithmetic.setZero(m_data, m_data.length);
+		m_count = 1;
+	}
+
+	/**
+		Copy the value from big integer `other` into this big
+		integer.
+	**/
+	private function copyFrom(other:BigInt_):Void {
+		if (other != this) {
+			ensureCapacity(other.m_count, false);
+			for (i in 0...other.m_count) {
+				m_data.set(i, other.m_data.get(i));
+			}
+			m_count = other.m_count;
+		}
+	}
+
+	private function fixedSizeCopyFrom(other:BigInt_, size:Int, value:Int = 0):Void {
+		if (other != this) {
+			ensureCapacity(size, false);
+			var maxSize:Int = (size > other.m_count) ? other.m_count : size;
+			for (i in 0...maxSize) {
+				m_data.set(i, other.m_data.get(i));
+			}
+			var diffSize = size - maxSize;
+			while (diffSize > 0) {
+				m_data.set(maxSize++, value);
+				diffSize--;
+			}
+			m_count = size;
+		}
+	}
+
+	//-----------------------------------------------------------------------
+	// Private implementation
+	//-----------------------------------------------------------------------
+
+	private inline function setShort(n:Int32, v:Int32):Void {
+		var s:Int = (n & 1) << 4;
+		var t:Int = m_data.get(n >> 1) & (~0xffff >>> s);
+		m_data.set(n >> 1, t | ((v & 0xffff) << s));
+	}
+
+	private function copy(other:MutableBigInt_):Void {
+		this.m_data = other.m_data;
+		this.m_count = other.m_count;
+		this.m_owned = other.m_owned;
+	}
+
+	private inline function ensureCapacity(n:Int, preserve:Bool):Void {
+		#if debug
+		if (s_testAllocation) {
+			ensureCapacityDebug(n, preserve);
+			return;
+		}
+		#end
+		ensureCapacityProd(n, preserve);
+	}
+
+	@:noCompletion
+	private function ensureCapacityDebug(n:Int, preserve:Bool):Void {
+		// always allocate the minimum amount necessary, to catch
+		// bounds edge cases as well as use of stale buffer data
+		if (preserve && (m_data != null) && (m_count > 0)) {
+			n = (m_count > n) ? m_count : n;
+			n += s_debugAllocationPadding;
+			var newData = new Vector<Int>(n);
+			for (i in 0...m_count) {
+				newData.set(i, m_data.get(i));
+			}
+			for (i in m_count...n) {
+				newData.set(i, 0xdeadbeef);
+			}
+			m_data = newData;
+		} else {
+			n += s_debugAllocationPadding;
+			m_data = new Vector<Int>(n);
+			for (i in 0...n) {
+				m_data.set(i, 0xdeadbeef);
+			}
+		}
+	}
+
+	@:noCompletion
+	private function ensureCapacityProd(n:Int, preserve:Bool):Void {
+		if (n < 1) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		if ((!m_owned) || (m_data == null) || (n > m_data.length)) {
+			n = BigIntHelper.clp2(n);
+			if (preserve && (m_data != null)) {
+				var newData = new Vector<Int>(n);
+				for (i in 0...m_count) {
+					newData.set(i, m_data.get(i));
+				}
+				m_data = newData;
+			} else {
+				m_data = new Vector<Int>(n);
+				for(i in 0...n) {
+					m_data.set(i,0);
+				}
+			}
+		}
+		m_owned = true;
+	}
+
+	private function new() {
+		super();
+	}
+
+	private static function fromInt(other:Int):MutableBigInt_ {
+		var c = BigInt_.getCachedValue(other);
+		if (c != null) {
+			return fromBigInt(c);
+		}
+		var r = new MutableBigInt_();
+		r.ensureCapacity(1, false);
+		r.m_data.set(0, other);
+		r.m_count = 1;
+		return r;
+	}
+
+	private static function fromBigInt(other:BigInt_):MutableBigInt_ {
+		// TODO: this will be problematic if `other` is actually a MutableBigInt_
+		var r = new MutableBigInt_(); // unowned
+		r.m_data = other.m_data;
+		r.m_count = other.m_count;
+		return r;
+	}
+
+	//-----------------------------------------------------------------------
+	// Static helpers
+	//-----------------------------------------------------------------------
+
+	private function _setFromHex(value:String, signed:Bool):Void {
+		if (value == null) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		var index = value.length;
+		if (index <= 0) {
+			throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+		}
+		var extra:Int = signed ? 0 : 1;
+		ensureCapacity(((index + 7) >> 3) + extra, false);
+		var pos = -1;
+		var bit:Int = 32;
+		var c:Int32 = 0;
+		while (index > 0) {
+			c = value.charCodeAt(--index);
+			if ((48 <= c) && (c <= 57)) {
+				c -= 48;
+			} else if ((65 <= c) && (c <= 70)) {
+				c -= 55;
+			} else if ((97 <= c) && (c <= 102)) {
+				c -= 87;
+			} else if (c == 32) {
+				continue;
+			} else {
+				throw new BigIntException(BigIntError.INVALID_ARGUMENT);
+			}
+			if (bit >= 32) {
+				m_data.set(++pos, 0);
+				bit = 0;
+			}
+			m_data.set(pos, m_data.get(pos) | (c << bit));
+			bit += 4;
+		}
+		// Sign extend
+		m_count = pos + 1;
+		if (signed) {
+			c = ((c & 8) != 0) ? 15 : 0;
+			while (bit < 32) {
+				m_data.set(pos, m_data.get(pos) | (c << bit));
+				bit += 4;
+			}
+		} else if (m_data.get(pos) < 0) {
+			m_data.set(m_count++, 0);
+		}
+		compact();
+	}
+
+	@:noCompletion
+	private static inline function multiplyAssignInt2(a7:MutableBigInt_, b:Int):Void {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.multiplyInt(r, a7, b);
+		a7.copy(r);
+	}
+
+	@:noCompletion
+	private static inline function multiplyAssign2(a8:MutableBigInt_, b:BigInt_):Void {
+		var r = new MutableBigInt_();
+		BigIntArithmetic.multiply(r, a8, b);
+		a8.copy(r);
+	}
+
+	@:noCompletion
+	private static inline function divideAssignInt2(a9:MutableBigInt_, b:Int):Void {
+		var q = new MutableBigInt_();
+		BigIntArithmetic.divideInt(a9, b, q);
+		a9.copy(q);
+	}
+
+	@:noCompletion
+	private static inline function divideAssign2(a10:MutableBigInt_, b:BigInt_):Void {
+		var q = new MutableBigInt_();
+		BigIntArithmetic.divide(a10, b, q, null);
+		a10.copy(q);
+	}
+
+	@:noCompletion
+	private static inline function modulusAssignInt2(a11:MutableBigInt_, b:Int):Void {
+		var q = new MutableBigInt_();
+		var r = BigIntArithmetic.divideInt(a11, b, q);
+		a11.setFromInt(r);
+	}
+
+	@:noCompletion
+	private static inline function modulusAssign2(a12:MutableBigInt_, b:BigInt_):Void {
+		var q = new MutableBigInt_();
+		var r = new MutableBigInt_();
+		BigIntArithmetic.divide(a12, b, q, r);
+		a12.copy(r);
+	}
+
+	@:noCompletion
+	private static inline function arithmeticShiftLeftAssign2(a13:MutableBigInt_, b:Int):Void {
+		BigIntArithmetic.arithmeticShiftLeft(a13, a13, b);
+	}
+
+	@:noCompletion
+	private static inline function arithmeticShiftRightAssign2(a14:MutableBigInt_, b:Int):Void {
+		BigIntArithmetic.arithmeticShiftRight(a14, a14, b);
+	}
+}

+ 3 - 1
tests/runci/targets/Php.hx

@@ -22,12 +22,14 @@ class Php {
 
 	static function generateArgs(file:String) {
 		if (systemName != "Windows")
-			return [file];
+			return ["-d","memory_limit=-1",file];
 		return [
 			"-c",
 			windowsPhpIni,
 			"-d",
 			'extension_dir=$windowsPhpExtPath',
+			"-d",
+			"memory_limit=-1",
 			file
 		];
 	}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2874 - 0
tests/unit/src/unit/TestBigInt.hx


+ 1 - 0
tests/unit/src/unit/TestMain.hx

@@ -69,6 +69,7 @@ function main() {
 		#if (!php && !lua)
 		new TestHttps(),
 		#end
+		new TestBigInt(),
 		#if !no_pattern_matching
 		new TestMatch(),
 		#end

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است