UNPKG

nextgen-web3-precision

Version:

NextGen precision calculation library with BigNumber.js v9 and logarithm extensions for high-precision financial calculations

726 lines (658 loc) 31.6 kB
// JS BigNumber Utilities uses BigNumber 9.0.2, // Dr Ron Knott version of 20 Aug 2023: ronknott AT mac DOT com // 于2025/7/8 引入此外部拓展库 因为 BigNumber.js 9.1.2 (当前版本)没有 ln() 方法 // 官方链接:https://r-knott.surrey.ac.uk/BigNumberLIB/bignumberLIB.js // 官方文档:https://r-knott.surrey.ac.uk/BigNumberLIB/index.html var BigNumber = require('bignumber.js'); //自行添加此行,其余部分丝毫未动 BNversion="9.1.1"; BN1=new BigNumber(1), BN0=new BigNumber(0), BNm1=new BigNumber(-1), BN2=new BigNumber(2), BN3=new BigNumber(3), BN4=new BigNumber(4), BN5=new BigNumber(5), BN10=new BigNumber(10), BNinf=new BigNumber(Infinity); BNminf=new BigNumber(-Infinity); BNpi=new BigNumber("3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196"), BNe=new BigNumber("2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011"), BNEulerGamma=new BigNumber("0.577215664901532860606512090082402431042159335939923598805767234884867726777664670936947063291746749515"), ln10=2.30258509299404568401799145, BNln10=new BigNumber("2.30258509299404568401799145468436420760110148862877297603332790096757260967735248023599720508959829834196778392"), BNPhi=new BigNumber("1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137484754088075386891752126633862223536931793180060766726354433389086595939582905638322661319928290267880675208766892501711696207032221043216269548626296313614"), MAXINT=9007199254740991; function typ(x){return ( typeof x=="number"?"num": typeof RKNS!=="undefined" &&x instanceof RKNS ?"RKNS": typeof BigNumber!=="undefined"&&x instanceof BigNumber?"BigNumber": typeof RATbn!="undefined" &&x instanceof RATbn ?"RATbn": typeof PHIC!="undefined" &&x instanceof PHIC ?"PHIC": typeof PHICBN!="undefined" &&x instanceof PHICBN ?"PHICBN": typeof BASEDIGS!="undefined" &&x instanceof BASEDIGS ?"BASEDIGS": typeof x=="undefined" ?"undefined": x instanceof Array ?"Array"+(x.slice(1).every(e=>typ(e)==typ(x[0]))?"["+typ(x[0])+"]":""): typeof(typeof x)=="string" ?typeof x :"?typ?")}; function BNdps(){return BigNumber.config().DECIMAL_PLACES}; function BNsetdps(nbdps){ if(nbdps<0)HALT("nb dps cannot be negative: "+nbdps); if(! isInt(nbdps))HALT("nb dps must be a (JS) integer: "+nbdps); BigNumber.config({ //EXPONENTIAL_AT: ndps, //Keep all output to max nb digits DECIMAL_PLACES:nbdps, POW_PRECISION:nbdps+5}); return BNdps() }; BigNumber.prototype.inc=function(){return this.plus(1)}; function fw(n,fwd){ if(arguments.length<2)HALT("Function fw needs a second argument"); if(!isInt(fwd)||fwd<=0)HALT("Function fw needs a positive whole number for the second argument"); var s=(typeof n == "string"?n:n.toString()); while(s.length<fwd)s=" "+s; return s}; function BNegcd(x,y){ if(typeof x=="number")x=new BigNumber(x); if(typeof y=="number")y=new BigNumber(y); if(x instanceof BigNumber&&y instanceof BigNumber){}else HALT("BNegcd called on non-BigNumber"); var a0=BN1,a1=BN0,b0=BN0,b1=BN1,a2=x,b2=y,q,z; while(!(b2.isZero())) {q=a2.idiv(b2); z=a0.minus(q.times(b0));a0=b0;b0=z; z=a1.minus(q.times(b1));a1=b1;b1=z; z=a2.minus(q.times(b2));a2=b2;b2=z // putmsg([a0,a1,a2]+" "+[b0,b1,b2]+" q="+q); }; if(a2.lte(0)){a1=a1.negated(),a2=a2.negated()}; // putmsg("egcd "+x+" "+y+" "+[a0,a1,a2]+" CHECK:"+(a0*x+a1*y)+"="+a2); return [a0,a1,a2] // ASSERT a0*x+a1*y==g==a2 }; function BNgcd3(a,b,c){ if(typeof a == "number")a=new BigNumber(a); if(typeof b == "number")b=new BigNumber(b); if(typeof c == "number")c=new BigNumber(c); return BNegcd(BNegcd(a.abs(),b.abs())[2],c.abs())[2]} function BNgcd(a,b){ if(typeof a == "number")a=new BigNumber(a); if(typeof b == "number")b=new BigNumber(b); if(!a.isInteger())HALT("GCD arguments must be whole numbers: "+a); if(!b.isInteger())HALT("GCD arguments must be whole numbers: "+b); return BNegcd((a.eq(0)?BN1:a.abs()),(b.eq(0)?BN1:b.abs())) [2] }; var BNGCD=BNgcd; function BNlcm(a,b){ if(typeof a == "number")a=new BigNumber(a); if(typeof b == "number")b=new BigNumber(b); return a.times(b).div(BNgcd(a,b)) }; function BNsqrfactor(n){var N=n;if(n.eq(0))return BN1; if(n.lt(0))n=n.negated(); var f=BN1,lim=n.sqrt(); //if(debug)DBG("\rsqrfactor of "+n+ " lim="+lim); //if(lim>100000) // if(!confirm("To simplify the fraction may take a while - do you still want simplification?")) // {return 1}; while(n.mod(4).eq(0)){f=f.times(4);n=n.idiv(4)}; //if(n<9)return f; for(var i=new BigNumber(3),ii=new BigNumber(9);n.gte(ii) && i.lte(lim);i=i.plus(2),ii=i.times(i)) {//if(debug)DBG("sqrf i="+i+" ii="+ii+" n="+n); while(n.gte(ii) && n.mod(ii).eq(0)){f=f.times(ii);n=n.idiv(ii) //if(debug)DBG(" ..f="+f+" n="+n) }}; //if(debug)DBG("sqrfactor of "+N+" = "+f+" *"+n); return f }; function BNrandomInt(lo,hi,width){ //all ints in range lo..hi inclusive are equally likely if(typeof lo=="number")lo=new BigNumber(lo); if(typeof hi=="number")hi=new BigNumber(hi); if(arguments.length<3)width=20; if(hi.le(lo))HALT("Random int arguments: lo must be less than hi"); do{var r=BigNumber.random(width).times(hi.minus(lo).plus(1)).plus(lo).floor()} while(r.gt(hi)); //random can return 1.00!! return r } BigNumber.prototype.ne=function(x){return ! this.eq(x)}; BigNumber.prototype.le=BigNumber.prototype.lte; BigNumber.prototype.ge=BigNumber.prototype.gte; function BNmax(BNrow){ if(arguments.length==1&& !isArray(BNrow))HALT("?? BNmax expects an array of BN"); var row=BNrow; if(row.length==0)HALT("?? BNmax given empty Array"); for(var i=1,maxi=0;i<row.length;i++) if(row[i].gt(row[maxi]))maxi=i; // putmsg("BNmax of "+BNrow+" is@"+maxi+" = "+BNrow[maxi]); return row[maxi] }; function BNmaxabs(BNrow){ //return the element with max ABS value, may be -ve if(arguments.length!=1 || !isArray(BNrow))HALT("?? BNmax expects an array of BN"); var row=(arguments.length==1?BNrow:arguments); if(row.length==0)HALT("?? BNmax given empty Array"); for(var i=1,maxi=0;i<row.length;i++) if(row[i].abs().gt(row[maxi].abs()))maxi=i; // putmsg("BNmax of "+BNrow+" is@"+maxi+" = "+BNrow[maxi]); return row[maxi] }; function BNeq(a,b,eps){ if(typeof a=="number"||typeof a=="string")a=new BigNumber(a); if(typeof b=="number"||typeof b=="string")b=new BigNumber(b); // putmsg("test "+echotypeBN(a)+" =?= "+echotypeBN(b)+" err="+nb(eps)); if(arguments.length<3)eps=BN0; if(a instanceof BigNumber) {if(b instanceof BigNumber) return a.minus(b).abs().le(eps) else HALT("BNeq given non-BN arg: "+typeof a)} else HALT("BNeq given a non BN argument: "+typeof b)}; BigNumber.prototype.sqr=function(){return this.times(this) }; //BigNumber.max(bn1,bn2, ... ) and BigNumber.min(bn1,bn2,...) are built-in //ALSO can have 1 arg which is an Array function BNln(x){ function rawln(x){// X=(x-1)/(x+1) then ln(x) = 2 Sum(x^(2i+1)/(2i+1), {i,0,inf}) for x>0 var X=x.minus(1).div(x.plus(1)); var Xsqrd=X.times(X); // write("X = "+X); var Sum=X,p=1,xpow=X,term; for(p=3,term=X;term.gt(0);p+=2) { X=X.times(Xsqrd); term=X.div(p); Sum=Sum.plus(term) }; return Sum.plus(Sum) }; if(typeof x =="number")x=new BigNumber(x); if(x.isNegative())HALT("Cannot take the log of negative numbers: "+x); var ch=x.e; var man=x.div("1e"+ch); //1<=man<10 var ln10=(BNdps()<100?BNln10:rawln(BN10)); return ln10.times(ch).plus(rawln(man)) }; BigNumber.prototype.powmod=function(Pow,Mod){ //works on larger Powers than built-in BN.pow //pow::int|BN, mod::BN if(isInteger(Pow))Pow=new BigNumber(Pow); var pm; if(Pow.lt(9007199254740991))pm=this.pow(Pow,Mod) // else if(Pow.isZero())pm= BN1 // else if(Pow.eq(1))pm= this.mod(Mod) else if(Pow.gt(1)) {var pmod2=Pow.mod(2); var s=this.powmod(Pow.idiv(2),Mod); var ss=s.sqr().mod(Mod); // if(isInteger(ss))ss=ss%Mod // else {//putmsg(s+"^2="+ss); // halt("Sorry - the numbers have become too large for this calculator")}; pm= (pmod2.isZero()? ss : this.times(ss).mod(Mod ) ) } ; //putmsg('BN powmod '+this+'^'+Pow+"%"+Mod+" <- "+pm) return pm }; BigNumber.prototype.log=function(base){ return arguments.length==0? BNln(this):BNln(this).div(BNln(base)) }; BigNumber.prototype.exp=function(){ //e^this for any value of this if(this.isInteger()) return BNe.pow(this) var sum=BN1,x=this,term=BN1,zero=new BigNumber("1e-"+(BNdps()+5)),den=0,sgn1; if(this.isZero())return BN1; if(this.isNegative())x=this.negated(); // ASSERT x is +ve, use e^x = 1+x+x^2/2!+x^3/3!+... while(term.abs().gt(zero)&&den<500) {den++;term=term.times(x).div(den); sum=sum.plus(term)}; // putmsg("nbterms="+den); if(this.isNegative())sum=BN1.div(sum); return sum }; function BNpow(b,p){//BigNumber.pow only accepts integer powers! if(typeof b=="number"||typeof b=="string")b=new BigNumber(b); if(!(b instanceof BigNumber)) HALT("To-Power has wrong type of base value "+typeof(b)); if(typeof p=="number"||typeof p=="string")p=new BigNumber(p); // if(!(p instanceof BigNumber)) HALT("To-power has wrong type of power value "+typeof(p)); var ans; if(p.isInteger())ans=b.pow(p) else ans=BNln(b).times(p).exp(); //if(p.isNegative())ans=BN1.div(ans); // putmsg(b+"^"+p+"="+Math.pow(b.toNumber(),p.toNumber())) return ans }; BigNumber.prototype.topow=function(p){ return BNpow(this,p) }; BigNumber.prototype.floor=function(){return this.integerValue(3)} BigNumber.prototype.round=function(){return this.integerValue(6)}; BigNumber.prototype.ceil=function(){return this.integerValue(2)}; BigNumber.prototype.degtorad=function(){ return this.div(45).times(BN1.arctan())}; BigNumber.prototype.radtodeg=function(){ return this.div(BN1.arctan()).times(45)}; BigNumber.prototype.sin=function(){ var x=new BigNumber(this),sign=1,piover4=BN1.arctan(); var twopi=piover4.times(8),halfpi=piover4.times(2),pi=piover4.times(4); if(x.isNegative()){sign=-1;x=x.negated()}// 0<=x since sin(-x)=-sin(x) x=x.minus(x.idiv(twopi).floor().times(twopi)); // 0<=x<=TwoPi if(x.gte(pi)){x=x.minus(pi);sign*=-1}; // 0<=x<Pi since sin(x+Pi)=-sin(x) if(x.gte(halfpi)){x=pi.minus(x)}; //0<=x<Pi/2 since sin(x+Pi/2)=sin(Pi/2-x) //if(x.gte(Piover4)){return Piover2.minus(x).cos()} // 0<x<Pi/4<1 var term=x,xsqr=x.sqr(),sum=x,i=1,zero=new BigNumber("1e-"+(BNdps()+5)); //putmsg("sin of "+x); while(term.abs().gt(zero)){term=term.times(xsqr).div((i+1)*(i+2)).negated();sum=sum.plus(term);i+=2;}; //putmsg("sin "+i+" terms, final term is "+term+"="+term.toPrecision(ndps)+" sum="+sum.toPrecision(ndps,4)+"("+ndps+")"); var ans=(sign<0?sum.negated():sum); //putmsg("SIN="+ans); return ans //.precision(BNdps(),4) }; BigNumber.prototype.sec=function(){ return BN1.div(this.cos()) }; BigNumber.prototype.cos=function(){ var x=new BigNumber(this),sign=1,piover4=BN1.arctan(); var halfpi=piover4.times(2),twopi=piover4.times(8),pi=piover4.times(4); if(x.isNegative()){x=x.negated()}; // 0<=x since cos(-x)=cos(x) x=x.minus(x.div(twopi).floor().times(twopi)); // 0<=x<=TwoPi if(x.gte(pi)){x=twopi.minus(x)} //0<=x<Pi since cos(x)=cos(2PI-x) if(x.gte(halfpi)){x=pi.minus(x);sign*=-1} //0<=x<Pi/2 since cos(x)=-cos(Pi-x) return (sign<0?halfpi.minus(x).sin().negated(): halfpi.minus(x).sin()) //since cos(x)=sin(Pi/2-x) }; BigNumber.prototype.cosec=function(){ return BN1.div(this.sin()) }; BigNumber.prototype.tan=function(){ // up to about 250 dps BNsetdps(BNdps()+9); var pi=BN1.arctan().times(4); var a=this.mod(pi),sgn; if(a.isNegative()){a=a.negated();sgn=-1}else{sgn=1}; // ASSERT 0<=a<Pi var t=BN0,i,asq=a.sqr(); for(i=181;i>1;i=i-2){t=asq.div(t.minus(i).negated())}; t= a.div(BN1.minus(t)); if(sgn<0)t=t.negated(); BNsetdps(BNdps()-9); return t }; BigNumber.prototype.cot=function(){ return BN1.div(this.tan()) }; BigNumber.prototype.arctan=function(){ //arctan(-t)=-arctan(t); arctan(t>1)=Pi/2-arctan(1/t) !!this==1??? //From Mathworld, Euler's formula: SLOW as x nears 1 so use half-angle formula! var delta=new BigNumber("1.0e-"+(BNdps()+2).toString()),sgn,x=this; if(x.isNegative()){sgn=-1;x=x.negated()}else{sgn=1}; if(x.gt(1)){s= BN1.arctan().times(2).minus(BN1.div(x).arctan())} // else if(x.gt(0.5))s=this.div(this.sqr().plus(1).sqrt().plus(1)).arctan().times(2) else{var y=x.sqr().div(x.sqr().plus(1)),s=x.div(x.sqr().plus(1)),k=1,lastt=BN0; var t=s; while(lastt.minus(t).abs().gt(delta)) {lastt=t;t=t.times(2*k).div(2*k+1).times(y);s=s.plus(t);k++} }; return (sgn<0?s.negated():s) }; BigNumber.prototype.arccot=function(){ return BN1.div(this).arctan() }; BigNumber.prototype.arcsin=function(){ if(this.abs().gt(1))HALT("arcsin was given an argument outside the range -1..1: "+this); return this.div(BN1.minus(this.sqr()).sqrt()).arctan()}; BigNumber.prototype.arccos=function(){ if(this.abs().gt(1))HALT("arccos was given an argument outside the range -1..1): "+this); var ans= BN1.minus(this.sqr()).sqrt().div(this).arctan(); return ans.isNegative()?ans.plus(BN1.arctan().times(4)):ans}; BigNumber.prototype.sinh=function(){ return (this.exp().minus(this.negated().exp())).div(2)}; BigNumber.prototype.cosh=function(){ //result is >=1 return (this.exp().plus(this.negated().exp())).div(2); // var ex=this.exp(); // return ex.minus(BN1.div(ex)).div(2) }; BigNumber.prototype.tanh=function(){ //returns a value in range -1..1 var e2=this.times(2).exp(); return e2.minus(BN1).div(e2.plus(BN1)) // var et=this.exp(),emt=this.negated().exp(); // return et.minus(emt).div(et.plus(emt)) //OR // var t=BN0,i,thissq=this.sqr(); // for(i=181;i>1;i=i-2){t=thissq.div(t.plus(i))}; // t= this.div(BN1.plus(t)); // return t }; BigNumber.prototype.arcsinh=function(){ return this.plus(this.sqr().plus(1).sqrt()).log() }; BigNumber.prototype.arccosh=function(){// 'this' must be >=1 return (this.lt(1)? HALT("arccosh was given an argument <1:"+this) : this.sqr().minus(1).sqrt().plus(this).log() ) }; BigNumber.prototype.arctanh=function(){ //'this' must be in range -1..1 if(this.lt(-1)||this.gt(1))HALT("arctanh was given an argument not in range -1..1:"+this) else if(this.eq(BN1))return BNinf else if(this.eq(BNm1))return BNminf; BNsetdps(BNdps()+5); var ans= this.plus(1).div(this.negated().plus(1)).log().div(2) ; BNsetdps(BNdps()-5); return ans}; BigNumber.prototype.factorial=function(){ if(this.isNegative())HALT("Cannot take the factorial of a negative number"); if(!this.isInteger())HALT("Factorial is only defined for whole numbers: "+this); var f=BN1; for(var i=1;this.ge(i);i++)f=f.times(i); return f }; BigNumber.prototype.factorialNbDigits=function(Base){ if(arguments.length==0)Base=10; return this.times(BNpi).times(2).sqrt().times(this.div(BNe).pow(this)).log(Base).plus(1).floor() }; BigNumber.prototype.factorialApprox=function(){ var dps=BNdps(); putmsg("dps was "+dps); BNsetdps(30); // putmsg(this.times(BNpi).times(2).sqrt()); // putmsg(this.div(BNe).pow(this)); var f= this.times(BNpi).times(2).sqrt().times(this.div(BNe).pow(this)) // putmsg(f); //this.times(BNpi).times(2).sqrt().times(this.div(BNe).pow(this)) BNsetdps(dps); putmsg("dps is now "+BNdps()); return f}; function BNbincoeff(r,c){ //n and r are int, return BN var ans=BN1, rr=(typeof r == "number"?new BigNumber(r):r), cc=(typeof c == "number"?new BigNumber(c):c); if(rr.isNegative()||cc.isNegative()||cc.gt(rr))ans= BN0 else if(cc.isZero()||cc.eq(rr))ans=BN1 else{if(r-c<c)c=r-c; //for(var i=r;i>=r-c+1;i--)ans=ans.times(i); // ans=rr.factorial().div(rr.minus(cc).factorial().times(cc.factorial()))} for(var i=0;i<c;i++)ans=ans.times(r-i).div(i+1); }; //write("bnbincoeff "+r+"C"+c+" = "+ans+" top="+rr+".."+(r-c+1)+" bot="+cc.factorial()); return ans }; var BNbinomial=BNbincoeff; // function bnbincoeffApprox(r,c){ // var rf=r.factorialApprox(), // rcf=r.minus(c).factorialApprox(), // cf=c.factorialApprox(); // putmsg([rf,rcf,cf].join("<br>")); // return rf.div(rcf).div(cf)}; function getBNdps(fld){ // reads input fld and return a JS number if(arguments.length==0)HALT("!! reading DP from input: missing field id"); var dps=document.getElementById(fld).value; if(dps==""){dps=30;document.getElementById(fld).value="30"}; try{dps=eval(dps)} catch(e){put("!! dps field error: "+e)}; if(!isInt(dps))HALT("!! dps field must be whole number: "+dps); if(dps<0)HALT("!! dps field must be a positive value: "+dps); // put("dps input "+dps); return dps }; BigNumber.prototype.toBase=function(b){ if(b instanceof BigNumber) { b=b.toNumber(); if(!isInt(b))HALT("BigNumber tobase: base is too large")}; if(!isInt(b)||b<2||b>36)HALT("The base "+b+" is not an integer in range 2..35"); return this.toString(b) }; BigNumber.prototype.toBaseDigits=function(b){ // return a ROW of ints, the digits of THIS in base b var ds=new Array(), BigNumberBaseChrs="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"; if(b instanceof BigNumber) { b=b.toNumber(); if(!isInteger(b))HALT("BigNumber tobase: base is too large")}; if(b==0||b==-1||b==1)HALT("BigNumber toBaseNbs cannot have base "+base); else if(1<b&&b<=64) ds=map(this.toString(b).split(""),function(b){return BigNumberBaseChrs.indexOf(b)}) else {var r,bn=new BigNumber(this); while(!bn.isZero()) { r=bn.mod(b);bn=bn.idiv(b); ds.unshift(r.toNumber()); //putmsg("push "+r.toNumber()); };}; return ds }; function BNstr(bn,base){ // replace "e" notation by 10^ if(bn instanceof BigNumber)bn=bn.toString(base); if(typeof bn == "string"){}else{HALT("BNstr called on non BigNumber")}; if(arguments.length==1)base=10; var s=bn.toString(base); if(s.indexOf("e")!=-1) s=s.replace("e","&times;"+base+"<sup>")+"</sup>"; return s }; // ---------- Some other utility functions -------- /* isInteger built-in for BigNumber function isInt(i){ //is a JS value an integer? if(typeof i!="number"&&typeof i!="string")return false; var s=i.toString().toLowerCase().replace(/\s/g,""); return !isNaN(i) && isFinite(i) && s!="" && s.indexOf(".")==-1 && s.indexOf("e")==-1 && i<=MAXINT } ; */ function getBN(id,descript,opts){ // OPTS O - can be empty, N = integer, + = pos only if(arguments.length<3)opts=""; var n=document.getElementById(id),N; if(!n)HALT("?bug? getBN cannot find "+id); n=n.value.replace(/\s/g,""); var x=n.replace(/[0-9\.eE\+\-]/g,""); if(x!="")HALT("Illegal characters in input for "+descript+": "+x); if(opts.indexOf("O")==-1&&n=="") HALT("Input for "+descript+" is empty!") else if(n=="")N="" else {try{N= new BigNumber(n)} catch(e){HALT("Input for "+descript+" is not a number")}; if(opts.indexOf("N")>-1&&!N.isInteger())HALT("Input for "+descript+" must be a whole number"); if((opts.indexOf("+")>-1||opts.indexOf("N")>-1)&&N.isNegative())HALT("Input for "+descript+" cannot be negative") }; if(opts.indexOf("?")>-1)putmsg("getBN "+descript+"|"+n+"| << "+N); return N //"" or BigNumber }; //BigNumber.max(bn1,bn2, ... ) and BigNumber.min(bn1,bn2,...) are built-in //ALSO can have 1 arg which is an Array function BNprimefactors(N){//SLOW with JS int it is MUCH faster var pfs=new Array(); if((typeof N=="string"||typeof N=="number")&&isInt(N))N=new BigNumber(N); //putmsg("primefactors "+N+" int:"+isInt(N)); //putmsg("primefs "+N); if(!N.isInteger())HALT("Cannot call BNprimefactors on "+N+"::"+telltype(N)); if(N.eq(BN1))return [[BN1,1]]; var i,inc,II=N,L=N.sqrt(),pct=0; var starttime=performance.now(); pct=0;while(II.mod(2).isZero()){pct++;II=II.div(2);L=II.sqrt()};if(pct>0){pfs.push([BN2,pct]); }; pct=0;while(II.mod(3).isZero()){pct++;II=II.div(3);L=II.sqrt()};if(pct>0){pfs.push([BN3,pct]); }; for(i=new BigNumber(5),inc=4; i.lte(L); i=i.plus(inc=6-inc)) {pct=0; while( II.mod(i).isZero()){pct++;II=II.div(i)}; if(pct>0){pfs.push([i,pct]);L=II.sqrt();//putmsg("found "+i+"^"+pct) }; }; if(II.gt(1))pfs.push([II,1]); var endtime=performance.now(); // putmsg("$BNprimefactors: "+N+" has "+pfs.length+" prime factors "+pfstoString(pfs)+" in "+Math.round(endtime-starttime)+"ms") return pfs }; function BNprimefactorsupto(N,maxfactor,printQ){//N::BN,maxfactor::int // <- row of [prime::int,pow::int] and last is [restOfQuotient::BN,0] //SLOW with JS int it is MUCH faster // putmsg("BNpfut "+N+"::"+(N instanceof BigNumber?"BN":typeof N)+" maxf="+maxfactor); var pfs=new Array(); if(typeof N=="string"||isInt(N))N=new BigNumber(N); //putmsg("primefactors "+N+" int:"+isInt(N)); //putmsg("primefs "+N); if(!N.isInteger())HALT("Cannot use primefactors on a non-integer "+N); maxfactor=new BigNumber(maxfactor); if(N.eq(BN1))return [[BN1,1]]; var i,inc,II=N,L=N.sqrt(),pct=0; var starttime=performance.now(); pct=0;while(II.mod(2).isZero()){pct++;II=II.div(2);L=II.sqrt()};if(pct>0){pfs.push([BN2,pct]); }; pct=0;while(II.mod(3).isZero()){pct++;II=II.div(3);L=II.sqrt()};if(pct>0){pfs.push([BN3,pct]); }; for(i=5,inc=4; maxfactor.gte(i)&&L.gte(i); i=i+(inc=6-inc)) {pct=0; while( !II.isZero()&&II.mod(i).isZero()){pct++;II=II.div(i)}; if(pct>0){pfs.push([i,pct]);L=II.sqrt();//putmsg("found "+i+"^"+pct) }; }; // putmsg("stopped at i="+i+" lim="+maxfactor+" L="+L.round(1)+" II="+II); if(II.gt(1)) {if(L.lt(i)){ pfs.push([II,1]) } else pfs.push([II,0])}; var endtime=performance.now(); //putmsg("$BNprimefactors: "+N+" has "+pfs.length+" prime factors "+pfstoString(pfs)+" in "+Math.round(endtime-starttime)+"ms") return (printQ?pfstoString(pfs,'xnonprime'):pfs) }; function BNfactors(N){ if(N.eq(1))return [BN1]; return BNfactorspfs(BNprimefactors(N)); }; function BNfactorspfs(pfs){ //given pfs list of [BNprime,INTpower], find all the factors function cpfrom(PrevAnss,Li,L,F){ //putmsg("cpfrom ")+L; if(L.length==Li)return F(PrevAnss); var ans=[],prpow=BN1,pr=L[Li][0],maxpow=L[Li][1]; for(var i=0;i<=maxpow;i++,prpow=prpow.times(pr)) {ans=ans.concat(cpfrom(PrevAnss.concat(prpow), Li+1, L, F)); }; //putmsg("cpfrom <- "+ans); return ans }; if(pfs[0][0].eq(1))pfs=pfs.slice(1); var fs= cpfrom([],0,pfs,function(L){return FoldR(BN1,L,function(bn1,bn2){return bn1.times(bn2)})}) //eval(L.join('*'))}) .sort(function(bn1,bn2){return bn1.cmp(bn2)}); // putmsg("Factors of "+ZbagtoString(pfs)+" = "+fs); return fs }; function BNEulerPhi(N,Pfs){ //the number of x's in Range[1..N] with gcd(x,N)==1 //putmsg("Eulerphi "+N); var pfs=(arguments.length>=2?Pfs:BNprimefactors(N)); //putmsg("pfs of "+N+": "+pfs); var phi=BN1,i=0; while(i<pfs.length) {var pr=pfs[i][0],po=pfs[i][1]; i++; phi=phi.times(pr.pow(po-1)).times(pr.minus(1))}; //putmsg("Eulerphi <- "+phi); if(!phi.isInteger())HALT("BN integer overflow during BNEulerPhi"); return phi }; function BNcmp(bn1,bn2){if(isInt(bn1))bn1=new BigNumber(bn1);if(isInteger(bn2))bn2=new BigNumber(bn2); return bn1.cmp(bn2)}; //bn1>bn2:1, bn1=bn2:0 bn1<bn2:-1 function BNEulerPhiPfs(N,Pfs){ //the size of { x in Range[1..N] | gcd(x,N)==1 } //N::BN, Pfs ::row [BN,int] Prime factors and powers of N // putmsg("EulerphiPfs "+ZbagtoString(Pfs)); var efs=new Array([BN1,1]),i=0; while(i<Pfs.length) {var CMP=(BNpr instanceof BigNumber?BNcmp:cmp); var BNpr=Pfs[i][0],po=Pfs[i][1]; i++; efs=(po>1?Zbagunion(efs,[[BNpr,po-1]],CMP):efs); if(isInteger(BNpr)){BNpr=new BigNumber(BNpr)}; efs=Zbagunion(efs,BNprimefactors(BNpr.minus(1)),CMP) //phi=phi*Math.pow(pr,po-1)*(pr-1) }; //putmsg("EulerphiPfs <- "+ZbagtoString(efs)); return efs }; function BNpfsmod(pfs,m){ var i,p,ans=BN1; for(var i=0;i<pfs.length;i++) {p=pfs[i][0].mod(m); ans=ans.times(p.pow(pfs[i][1],m)).mod(m) }; return ans }; function BNorderBASE(D,B,Dpfs){ //D::BN,B(ase)::int, Dpfs row of [pr::BN|int,pow::int] <- int //putmsg("BNorderBASE "+D+"::"+(isInt(D)?"N":D instanceof BigNumber?"BN":"?")+" "+echotype(B)+" "+(arguments.length>2&&Dpfs.length>50?Dpfs.length+" fctrs":Dpfs)); if(D.eq(1))return BN0; if(!D.isInteger())HALT("BNorderBASE arg 1 is not an integer: "+D); if(BNgcd(D,B).ne(1))HALT("BNorderBASE called on numbers with a common factor: "+D+" "+B) // putmsg("BNorderBASE Dpfs "+pfstoString(Dpfs)); var fs=(arguments.length==3?BNfactorspfs(BNEulerPhiPfs(D,Dpfs)):BNfactors(BNEulerPhi(D))), i,ans; //for(var i=0;i<fs.length;i++)while(B%fs[i]==0,B=Math.round(B/fs[i])); i=0;if(B instanceof BigNumber){}else{B=new BigNumber(B)}; // putmsg("$$BNorderBASE on "+D+" with "+Dpfs+" -> "+fs); var ok;i=0; while(i<fs.length&&(ans=B.powmod(fs[i],D)).ne(1)){i++}; // putmsg("BNorderbase <- B^"+BNprimefactors(fs[fs.length-1])+" = "+B.powmod(fs[fs.length-1],D)) return (i<fs.length?fs[i]:BNm1) }; BigNumber.prototype.primefactors=function(printQ){//only on JS ints and returns row of [pr::int,pow::int] var pfs; if(isInt(this.toNumber())) {if(arguments.length==1&&printQ) return pfstoString(GLOBALprimefactors(this.toNumber()),'x') else return GLOBALprimefactors(this.toNumber())} else HALT(this+" is too big to find its primefactors") }; BigNumber.prototype.isprime=function(){ var pfs=this.primefactors(); return pfs.length==1&&pfs[0][1]==1}; BigNumber.prototype.isrect=function(){ return !this.isprime()}; BigNumber.prototype.primefactorsupto=function(maxfactor,printQ){//only try factors up to maxfactor::int if(arguments.length<2)printQ=false; return BNprimefactorsupto(this,maxfactor,printQ) }; function champ(base,dps){ if(arguments.length<2)dps=BNdps(); // alert(dps+" dps"); if(base instanceof BigNumber)base=base.toNumber() else if(typeof base!="number")HALT("Champ function expected a numerical argument, Found: "+typeof base); if(base>1&&base<=10) {for(var i=1,ans="0.";ans.length<=dps;i++){ans+=i.toString(base)}; // pumsg("champ raw "+ans+" "+base); return new BigNumber(ans,base)} else HALT("Champernowne numbers only available in bases 2 to 10: found base "+b) }; var fibBNmemo=new Array(); BigNumber.prototype.fib= //function(){ // depends on getting accuracy of Phi right!! // if(!this.isInteger())HALT(".fib cannot be called on a non-integer BigNumber"); // if(this.isZero())return BN0; // if(this.eq(1)||this.eq(2))return BN1; // if(this.isNegative())return (this.mod(2).eq(0)?this.negated().fib().negated():this.negated().fib()) // var BN5root=BN5.sqrt(); // // write(BN5root); // var Phi=BN5root.plus(1).div(2); // // write(Phi); // // write(this); // // write(Phi.topow(this)); // // write(Phi.topow(this).div(BN5root)); // return Phi.topow(this).div(BN5root).round(); // }; function(){var x,z; if(this.isInteger()) { if(x=fibBNmemo[this.toNumber()])return x; if(this.isNegative()){x= this.negated().fib();if(this.negated().mod(2n).isZero())x=x.negated();return x} if(this.isZero()){x= BN0} else if(this.eq(1) || this.eq(2) ){x= BN1} else { if(this.mod(2).isZero()) {var z=this.div(2).fib(); x=z.times(this.div(2).plus(1).fib().times(2).minus(z))} else x=this.minus(1).div(2).fib().sqr().plus(this.minus(1).div(2).plus(1).fib().sqr()); fibBNmemo[this.toNumber()]=x; //putmsg("fibmemo("+this+")="+x); } return x } else { HALT("BN.fib() is only defined on integers: "+this); //var Phi=BN5.sqrt().plus(1).div(2),phi;phi=Phi.minus(1); //return BNpow(Phi,this).minus(BNpow(phi.neg(),this)).div(BN5.sqrt()) } }; function fibBN(i){return (new BigNumber(i)).fib()}; BigNumber.prototype.luc=function(){ if(this.isInteger()) return this.minus(1).fib().plus(this.plus(1).fib()) else { HALT("Luc() is only defined on integers"); //var Phi=BN5.sqrt().plus(1).div(2),phi;phi=Phi.minus(1); // return BNpow(Phi,this).plus(BNpow(phi.neg(),this)) } }; function gfib(a,b,i){ if(arguments.length<3)HALT("Gfib needs parameters a, b and i"); return (a==0 && b==0? BN0 : i.minus(1).fib().times(a).plus(i.fib().times(b))) }; function polyg(n,r){if(typeof n =="number" && isInt(n))n=new BigNumber(n); if(typeof r== "number"&&isInt(r))r=new BigNumber(r); if(!isInteger(n))HALT("Cannot find polygonal numbers with non-integer sides: "+n); if(!isInteger(r))HALT("Cannot find polygonal numbers with non-integer order: "+r); return r.times( n.minus(2).times(r).minus (n).plus(4) ).div(2) }; BigNumber.prototype.sqrfactor=function(){ if(this.isZero())return BN1; if(n.isNegative())n=n.negated(); var f=BN1,lim=n.sqrt(); //if(debug)DBG("\rsqrfactor of "+n+ " lim="+lim); //if(lim>100000) // if(!confirm("To simplify the fraction may take a while - do you still want simplification?")) // {return 1}; while(n.mod(4).isZero()){f=f.times(4);n=n.div(4)}; //if(n<9)return f; for(var i=3,ii=9;n>=ii && i<=lim;i=i+2,ii=i*i) {//if(debug)DBG("sqrf i="+i+" ii="+ii+" n="+n); while(n>=ii && n%ii==0){f=f*ii;n=Math.round(n/ii) //if(debug)DBG(" ..f="+f+" n="+n) }}; //if(debug)DBG("sqrfactor of "+N+" = "+f+" *"+n); return f }; function BNHarmonic(n) // Harmonic number approx, By summing recips for n<400 // by an good approximaator after that..see GKP Page 278 // n::string|number|BN --> [H(n)::BN, nbdps accuracy::number] {if(typeof n=="string"||typeof n=="number")n=new BigNumber(n); if(n.lt(1)||!n.isInteger())HALT("?? (BN) Harmonic Number needs a whole number argument. Found :"+n) var sum=BN0; if(n<400) {for(var i=1;i<=n;i++)sum=sum.plus(BN1.div(i)); return [sum,BNdps()]} else { var prevdps=BNdps(); BNsetdps(50); var ans=n.log().plus(BNEulerGamma).plus(BN1.div(n.times(2))).minus(BN1.div(n.sqr().times(12))); var err=BN1.div(BN10.times(12).times(n.sqr().sqr())); // putmsg("err "+err+" "+BNdps()+"dps comp"); var ndps=err.log(10).negated().plus(2).round().toNumber(); // [ln(n)+EulerGamma+1/(2*n)-1/(12*n*n),1/(120*n*n*n*n)] error <1/(120n^4) BNsetdps(prevdps); return [ans,err.toFixed(ndps),ndps-2] } };