var Integer = function(bits, sign) {
this.bits_ = [];
this.sign_ = sign;
var top = true;
for (var i = bits.length - 1; i >= 0; i--) {
var val = bits[i] | 0;
if (!top || val != sign) {
this.bits_[i] = val;
top = false;
}
}
};
Integer.IntCache_ = {};
var I_fromInt = function(value) {
if (-128 <= value && value < 128) {
var cachedObj = Integer.IntCache_[value];
if (cachedObj) {
return cachedObj;
}
}
var obj = new Integer([value | 0], value < 0 ? -1 : 0);
if (-128 <= value && value < 128) {
Integer.IntCache_[value] = obj;
}
return obj;
};
var I_fromNumber = function(value) {
if (isNaN(value) || !isFinite(value)) {
return Integer.ZERO;
} else if (value < 0) {
return I_negate(I_fromNumber(-value));
} else {
var bits = [];
var pow = 1;
for (var i = 0; value >= pow; i++) {
bits[i] = (value / pow) | 0;
pow *= Integer.TWO_PWR_32_DBL_;
}
return new Integer(bits, 0);
}
};
Integer.fromBits = function(bits) {
var high = bits[bits.length - 1];
return new Integer(bits, high & (1 << 31) ? -1 : 0);
};
I_fromString = function(str, opt_radix) {
if (str.length == 0) {
throw Error('number format error: empty string');
}
var radix = opt_radix || 10;
if (radix < 2 || 36 < radix) {
throw Error('radix out of range: ' + radix);
}
if (str.charAt(0) == '-') {
return I_negate(I_fromString(str.substring(1), radix));
} else if (str.indexOf('-') >= 0) {
throw Error('number format error: interior "-" character');
}
var radixToPower = I_fromNumber(Math.pow(radix, 8));
var result = Integer.ZERO;
for (var i = 0; i < str.length; i += 8) {
var size = Math.min(8, str.length - i);
var value = parseInt(str.substring(i, i + size), radix);
if (size < 8) {
var power = I_fromNumber(Math.pow(radix, size));
result = I_add(I_mul(result, power), I_fromNumber(value));
} else {
result = I_mul(result, radixToPower);
result = I_add(add, I_fromNumber(value));
}
}
return result;
};
Integer.TWO_PWR_32_DBL_ = (1 << 16) * (1 << 16);
Integer.ZERO = I_fromInt(0);
Integer.ONE = I_fromInt(1);
Integer.TWO_PWR_24_ = I_fromInt(1 << 24);
var I_toInt = function(self) {
return self.bits_.length > 0 ? self.bits_[0] : self.sign_;
};
var I_toWord = function(self) {
return I_toInt(self) >>> 0;
};
var I_toNumber = function(self) {
if (isNegative(self)) {
return -I_toNumber(I_negate(self));
} else {
var val = 0;
var pow = 1;
for (var i = 0; i < self.bits_.length; i++) {
val += getBitsUnsigned(self, i) * pow;
pow *= Integer.TWO_PWR_32_DBL_;
}
return val;
}
};
var getBits = function(self, index) {
if (index < 0) {
return 0;
} else if (index < self.bits_.length) {
return self.bits_[index];
} else {
return self.sign_;
}
};
var getBitsUnsigned = function(self, index) {
var val = getBits(self, index);
return val >= 0 ? val : Integer.TWO_PWR_32_DBL_ + val;
};
var getSign = function(self) {
return self.sign_;
};
var isZero = function(self) {
if (self.sign_ != 0) {
return false;
}
for (var i = 0; i < self.bits_.length; i++) {
if (self.bits_[i] != 0) {
return false;
}
}
return true;
};
var isNegative = function(self) {
return self.sign_ == -1;
};
var isOdd = function(self) {
return (self.bits_.length == 0) && (self.sign_ == -1) ||
(self.bits_.length > 0) && ((self.bits_[0] & 1) != 0);
};
var I_equals = function(self, other) {
if (self.sign_ != other.sign_) {
return false;
}
var len = Math.max(self.bits_.length, other.bits_.length);
for (var i = 0; i < len; i++) {
if (getBits(self, i) != getBits(other, i)) {
return false;
}
}
return true;
};
var I_notEquals = function(self, other) {
return !I_equals(self, other);
};
var greaterThan = function(self, other) {
return I_compare(self, other) > 0;
};
var greaterThanOrEqual = function(self, other) {
return I_compare(self, other) >= 0;
};
var lessThan = function(self, other) {
return I_compare(self, other) < 0;
};
var lessThanOrEqual = function(self, other) {
return I_compare(self, other) <= 0;
};
var I_compare = function(self, other) {
var diff = I_sub(self, other);
if (isNegative(diff)) {
return -1;
} else if (isZero(diff)) {
return 0;
} else {
return +1;
}
};
var I_compareInt = function(self, other) {
return I_compare(self, I_fromInt(other));
}
var shorten = function(self, numBits) {
var arr_index = (numBits - 1) >> 5;
var bit_index = (numBits - 1) % 32;
var bits = [];
for (var i = 0; i < arr_index; i++) {
bits[i] = getBits(self, i);
}
var sigBits = bit_index == 31 ? 0xFFFFFFFF : (1 << (bit_index + 1)) - 1;
var val = getBits(self, arr_index) & sigBits;
if (val & (1 << bit_index)) {
val |= 0xFFFFFFFF - sigBits;
bits[arr_index] = val;
return new Integer(bits, -1);
} else {
bits[arr_index] = val;
return new Integer(bits, 0);
}
};
var I_negate = function(self) {
return I_add(not(self), Integer.ONE);
};
var I_add = function(self, other) {
var len = Math.max(self.bits_.length, other.bits_.length);
var arr = [];
var carry = 0;
for (var i = 0; i <= len; i++) {
var a1 = getBits(self, i) >>> 16;
var a0 = getBits(self, i) & 0xFFFF;
var b1 = getBits(other, i) >>> 16;
var b0 = getBits(other, i) & 0xFFFF;
var c0 = carry + a0 + b0;
var c1 = (c0 >>> 16) + a1 + b1;
carry = c1 >>> 16;
c0 &= 0xFFFF;
c1 &= 0xFFFF;
arr[i] = (c1 << 16) | c0;
}
return Integer.fromBits(arr);
};
var I_sub = function(self, other) {
return I_add(self, I_negate(other));
};
var I_mul = function(self, other) {
if (isZero(self)) {
return Integer.ZERO;
} else if (isZero(other)) {
return Integer.ZERO;
}
if (isNegative(self)) {
if (isNegative(other)) {
return I_mul(I_negate(self), I_negate(other));
} else {
return I_negate(I_mul(I_negate(self), other));
}
} else if (isNegative(other)) {
return I_negate(I_mul(self, I_negate(other)));
}
if (lessThan(self, Integer.TWO_PWR_24_) &&
lessThan(other, Integer.TWO_PWR_24_)) {
return I_fromNumber(I_toNumber(self) * I_toNumber(other));
}
var len = self.bits_.length + other.bits_.length;
var arr = [];
for (var i = 0; i < 2 * len; i++) {
arr[i] = 0;
}
for (var i = 0; i < self.bits_.length; i++) {
for (var j = 0; j < other.bits_.length; j++) {
var a1 = getBits(self, i) >>> 16;
var a0 = getBits(self, i) & 0xFFFF;
var b1 = getBits(other, j) >>> 16;
var b0 = getBits(other, j) & 0xFFFF;
arr[2 * i + 2 * j] += a0 * b0;
Integer.carry16_(arr, 2 * i + 2 * j);
arr[2 * i + 2 * j + 1] += a1 * b0;
Integer.carry16_(arr, 2 * i + 2 * j + 1);
arr[2 * i + 2 * j + 1] += a0 * b1;
Integer.carry16_(arr, 2 * i + 2 * j + 1);
arr[2 * i + 2 * j + 2] += a1 * b1;
Integer.carry16_(arr, 2 * i + 2 * j + 2);
}
}
for (var i = 0; i < len; i++) {
arr[i] = (arr[2 * i + 1] << 16) | arr[2 * i];
}
for (var i = len; i < 2 * len; i++) {
arr[i] = 0;
}
return new Integer(arr, 0);
};
Integer.carry16_ = function(bits, index) {
while ((bits[index] & 0xFFFF) != bits[index]) {
bits[index + 1] += bits[index] >>> 16;
bits[index] &= 0xFFFF;
}
};
var I_mod = function(self, other) {
return I_rem(I_add(other, I_rem(self, other)), other);
}
var I_div = function(self, other) {
if(greaterThan(self, Integer.ZERO) != greaterThan(other, Integer.ZERO)) {
if(I_rem(self, other) != Integer.ZERO) {
return I_sub(I_quot(self, other), Integer.ONE);
}
}
return I_quot(self, other);
}
var I_quotRem = function(self, other) {
return [1, I_quot(self, other), I_rem(self, other)];
}
var I_divMod = function(self, other) {
return [1, I_div(self, other), I_mod(self, other)];
}
var I_quot = function(self, other) {
if (isZero(other)) {
throw Error('division by zero');
} else if (isZero(self)) {
return Integer.ZERO;
}
if (isNegative(self)) {
if (isNegative(other)) {
return I_quot(I_negate(self), I_negate(other));
} else {
return I_negate(I_quot(I_negate(self), other));
}
} else if (isNegative(other)) {
return I_negate(I_quot(self, I_negate(other)));
}
var res = Integer.ZERO;
var rem = self;
while (greaterThanOrEqual(rem, other)) {
var approx = Math.max(1, Math.floor(I_toNumber(rem) / I_toNumber(other)));
var log2 = Math.ceil(Math.log(approx) / Math.LN2);
var delta = (log2 <= 48) ? 1 : Math.pow(2, log2 - 48);
var approxRes = I_fromNumber(approx);
var approxRem = I_mul(approxRes, other);
while (isNegative(approxRem) || greaterThan(approxRem, rem)) {
approx -= delta;
approxRes = I_fromNumber(approx);
approxRem = I_mul(approxRes, other);
}
if (isZero(approxRes)) {
approxRes = Integer.ONE;
}
res = I_add(res, approxRes);
rem = I_sub(rem, approxRem);
}
return res;
};
var I_rem = function(self, other) {
return I_sub(self, I_mul(I_quot(self, other), other));
};
var not = function(self) {
var len = self.bits_.length;
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = ~self.bits_[i];
}
return new Integer(arr, ~self.sign_);
};
var I_and = function(self, other) {
var len = Math.max(self.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = getBits(self, i) & getBits(other, i);
}
return new Integer(arr, self.sign_ & other.sign_);
};
var I_or = function(self, other) {
var len = Math.max(self.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = getBits(self, i) | getBits(other, i);
}
return new Integer(arr, self.sign_ | other.sign_);
};
var I_xor = function(self, other) {
var len = Math.max(self.bits_.length, other.bits_.length);
var arr = [];
for (var i = 0; i < len; i++) {
arr[i] = getBits(self, i) ^ getBits(other, i);
}
return new Integer(arr, self.sign_ ^ other.sign_);
};
var I_shiftLeft = function(self, numBits) {
var arr_delta = numBits >> 5;
var bit_delta = numBits % 32;
var len = self.bits_.length + arr_delta + (bit_delta > 0 ? 1 : 0);
var arr = [];
for (var i = 0; i < len; i++) {
if (bit_delta > 0) {
arr[i] = (getBits(self, i - arr_delta) << bit_delta) |
(getBits(self, i - arr_delta - 1) >>> (32 - bit_delta));
} else {
arr[i] = getBits(self, i - arr_delta);
}
}
return new Integer(arr, self.sign_);
};
var I_shiftRight = function(self, numBits) {
var arr_delta = numBits >> 5;
var bit_delta = numBits % 32;
var len = self.bits_.length - arr_delta;
var arr = [];
for (var i = 0; i < len; i++) {
if (bit_delta > 0) {
arr[i] = (getBits(self, i + arr_delta) >>> bit_delta) |
(getBits(self, i + arr_delta + 1) << (32 - bit_delta));
} else {
arr[i] = getBits(self, i + arr_delta);
}
}
return new Integer(arr, self.sign_);
};
var I_signum = function(self) {
var cmp = I_compare(self, Integer.ZERO);
if(cmp > 0) {
return Integer.ONE
}
if(cmp < 0) {
return I_sub(Integer.ZERO, Integer.ONE);
}
return Integer.ZERO;
};
var I_abs = function(self) {
if(I_compare(self, Integer.ZERO) < 0) {
return I_sub(Integer.ZERO, self);
}
return self;
};
var I_decodeDouble = function(x) {
var dec = decodeDouble(x);
var mantissa = Integer.fromBits([dec[3], dec[2]]);
if(dec[1] < 0) {
mantissa = I_negate(mantissa);
}
return [1, dec[4], mantissa];
}
var I_toString = function(self) {
var radix = 10;
if (isZero(self)) {
return '0';
} else if (isNegative(self)) {
return '-' + I_toString(I_negate(self));
}
var radixToPower = I_fromNumber(Math.pow(radix, 6));
var rem = self;
var result = '';
while (true) {
var remDiv = I_div(rem, radixToPower);
var intval = I_toInt(I_sub(rem, I_mul(remDiv, radixToPower)));
var digits = intval.toString();
rem = remDiv;
if (isZero(rem)) {
return digits + result;
} else {
while (digits.length < 6) {
digits = '0' + digits;
}
result = '' + digits + result;
}
}
};
var I_fromRat = function(a, b) {
return I_toNumber(a) / I_toNumber(b);
}