/**
* Infinite Precision Javascript Library
*
* Author: Kyle W T Sherman
*/
/**
* Infinite Precision Number Class
*/
var IpNum = function() {
// right of decimal point
this.base = new Array();
// left of decimal point
this.radix = new Array();
// sign is 1 for positive or -1 for negative
this.sign = 1;
// remove any leading or trailing zeros
this.removeZeros = function() {
while (this.base.length > 1 && this.base[0] == 0) this.base.shift();
while (this.radix.length > 0 && this.radix[this.radix.length-1] == 0) this.radix.pop();
}
// get this ip number as an array of base, radix, sign
this.getArray = function() {
var r = new Array();
r[0] = new Array();
r[1] = new Array();
r[2] = new Array();
for (var i=0; i<this.base.length; i++) {
r[0][i] = this.base[i];
}
for (var i=0; i<this.radix.length; i++) {
r[1][i] = this.radix[i];
}
r[2] = this.sign;
return r;
}
// get this ip number from an array of base, radix, sign
this.setArray = function(parts) {
this.base = parts[0];
this.radix = parts[1];
this.sign = parts[2];
this.removeZeros();
}
// get this ip number as string
this.getString = function() {
var r = "";
if (this.sign == -1) r += "-";
if (this.base.length > 0) {
for (var i=0; i<this.base.length; i++) {
r += this.base[i];
}
}
if (this.radix.length > 0) {
r += ".";
for (var i=0; i<this.radix.length; i++) {
r += this.radix[i];
}
}
return r;
}
// set this ip number from string
this.setString = function (str) {
this.base = new Array();
this.radix = new Array();
this.sign = 1;
var loc = 0;
var len = str.length;
if (len > 0 && str.charAt(loc) == "-") {
this.sign = -1;
loc++;
}
while (loc < len && str.charAt(loc) !== ".") {
this.base.push(parseInt(str.charAt(loc)));
loc++;
}
if (loc < len) {
loc++;
while (loc < len) {
this.radix.push(parseInt(str.charAt(loc)));
loc++;
}
}
this.removeZeros();
}
// return true if this is equal to NUM
this.equals = function (num) {
if (this.sign !== num.sign) return false;
if (this.base.length !== num.base.length) return false;
if (this.radix.length !== num.radix.length) return false;
for (var i=0; i<this.base.length; i++) {
if (this.base[i] !== num.base[i]) return false;
}
for (var i=0; i<this.radix.length; i++) {
if (this.radix[i] !== num.radix[i]) return false;
}
return true;
}
// return absolute value of this
this.abs = function () {
var r = new IpNum();
r.base = this.base;
r.radix = this.radix;
r.sign = 1;
return r;
}
// return true if this is greater than NUM
this.gt = function (num) {
if (this.sign !== num.sign) {
if (this.sign == 1) return true;
else return false;
} else if (this.sign == -1) {
if (this.equals(num)) return false;
else if (this.abs().gt(num.abs())) return false;
else return true;
} else {
if (this.base.length > num.base.length) return true;
if (this.base.length < num.base.length) return false;
for (var i=0; i<this.base.length; i++) {
if (this.base[i] > num.base[i]) return true;
if (this.base[i] < num.base[i]) return false;
}
for (var i=0; i<this.radix.length; i++) {
if ((this.radix[i] || 0) > (num.radix[i] || 0)) return true;
if ((this.radix[i] || 0) < (num.radix[i] || 0)) return false;
}
return false;
}
}
// return true if this is less than NUM
this.lt = function (num) {
return num.gt(this);
}
// return true if this is greater than or equal to NUM
this.gte = function (num) {
if (this.gt(num)) return true;
if (this.equals(num)) return true;
return false;
}
// return true if this is less than or equal to NUM
this.lte = function (num) {
if (this.lt(num)) return true;
if (this.equals(num)) return true;
return false;
}
// add this to NUM
this.add = function (num) {
var result = new IpNum();
var r = new Array(3);
r[0] = new Array();
r[1] = new Array();
var n1;
var n2;
var c = 0;
var len = 0;
var sign = 1;
if (this.sign !== num.sign) {
// one number is negative and one is positive
// subtract the larger from the smaller and keep the sign of the larger
if (this.abs().gt(num.abs())) {
sign = this.sign;
n1 = this.getArray();
n2 = num.getArray();
} else {
sign = num.sign;
n1 = num.getArray();
n2 = this.getArray();
}
// subtract radix part
while (n1[1].length > n2[1].length) {
n2[1].push(0);
}
while (n2[1].length > n1[1].length) {
n1[1].push(0);
}
len = n1[1].length;
for (var i=len-1; i>=0; i--) {
var d = n1[1][i]-n2[1][i]-c;
if (d < 0) {
d += 10;
c = 1;
} else {
c = 0;
}
r[1].unshift(d);
}
// add base part
while (n1[0].length > n2[0].length) {
n2[0].unshift(0);
}
while (n2[0].length > n1[0].length) {
n1[0].unshift(0);
}
len = n1[0].length;
for (var i=len-1; i>=0; i--) {
var d = n1[0][i]-n2[0][i]-c;
if (d < 0) {
d = d+10;
c = 1;
} else {
c = 0;
}
r[0].unshift(d);
}
} else {
// both numbers have the same sign
// add them and set the result sign appropriately
if (this.sign == 1) sign = 1;
else sign = -1;
n1 = this.getArray();
n2 = num.getArray();
// add radix part
while (n1[1].length > n2[1].length) {
n2[1].push(0);
}
while (n2[1].length > n1[1].length) {
n1[1].push(0);
}
len = n1[1].length;
for (var i=len-1; i>=0; i--) {
var d = n1[1][i]+n2[1][i]+c;
if (d < 10) {
c = 0;
} else {
d = d-10;
c = 1;
}
r[1].unshift(d);
}
// add base part
while (n1[0].length > n2[0].length) {
n2[0].unshift(0);
}
while (n2[0].length > n1[0].length) {
n1[0].unshift(0);
}
len = n1[0].length;
for (var i=len-1; i>=0; i--) {
var d = n1[0][i]+n2[0][i]+c;
if (d < 10) {
c = 0;
} else {
d = d-10;
c = 1;
}
r[0].unshift(d);
}
if (c !== 0) r[0].unshift(c);
}
r[2] = sign;
// return ip number
result.setArray(r);
return result;
}
// subtract NUM from this
this.sub = function (num) {
// set negate NUM then add
var n = new IpNum();
n.setArray(num.getArray());
if (n.sign == 1) n.sign = -1;
else n.sign = 1;
return this.add(n);
}
// add two arrays together
var mulAdd = function (num1, num2) {
var r = new Array();
var c = 0;
while (num1.length > num2.length) {
num2.unshift(0);
}
while (num2.length > num1.length) {
num1.unshift(0);
}
for (var i=num1.length-1; i>=0; i--) {
var d = num1[i]+num2[i]+c;
if (d < 10) {
c = 0;
} else {
d = d-10;
c = 1;
}
r.unshift(d);
}
return r;
}
// multiply this and NUM
this.mul = function (num) {
var result = new IpNum();
var r = new Array(3);
r[0] = new Array();
r[1] = new Array();
var n1 = this.getArray();
var n2 = num.getArray();
var sign = 1;
// if the numbers have different signs, then result is negative, otherwise it is positive
if (n1[2] !== n2[2]) sign = -1;
var m1 = n1[0];
m1 = m1.concat(n1[1]);
var m2 = n2[0];
m2 = m2.concat(n2[1]);
var d = n1[1].length+n2[1].length;
var m3 = new Array();
for (var i=m1.length-1; i>=0; i--) {
var c = 0;
// offset (used to line up numbers)
var os = m1.length-1-i;
var m = new Array();
for (var j=m2.length-1; j>=0; j--) {
var n = m1[i]*m2[j]+c;
c = (n-(n%10))/10;
n = n-(c*10);
m.unshift(n);
}
if (c !== 0) m.unshift(c);
for (var j=0; j<os; j++) {
m.push(0);
}
m3 = mulAdd(m3, m);
}
for (i=0; i<d; i++) r[1].unshift(m3.pop());
var len = m3.length;
for (i=0; i<len; i++) r[0].unshift(m3.pop());
r[2] = sign;
// return ip number
result.setArray(r);
return result;
}
// return factorial of this
this.fact = function () {
var r;
var num = this.getArray();
var one = new IpNum();
one.setString("1");
if (num[2] == 1 && num[1].length == 0) {
r = new IpNum();
r.setString("1");
while (num[0].length > 0) {
r = r.mul(num);
num = num.sub(one);
}
}
return r;
}
// return this with radix cleared
this.floor = function () {
var r = new IpNum();
r.setArray(this.getArray());
r.radix = new Array();
return r;
}
// return this with radix cleared and base incremented if radix was not 0
this.ceil = function () {
var r = new IpNum();
r.setArray(this.getArray());
if (r.radix.length > 0) {
var one = new IpNum();
if (r.sign == 1) {
one.setString("1");
} else {
one.setString("-1");
}
r = r.add(one);
}
r.radix = new Array();
return r;
}
// return this rounded up if radix >= .5 or down otherwise
this.round = function() {
if (this.radix.length > 0 && this.radix[0] >= 5) return this.ceil();
else return this.floor();
}
}
// end of file