UNPKG

pasm

Version:

Piston X86-64 Assembler

727 lines (619 loc) 15.9 kB
<html> <head><title>Pi calculator</title> <embed type="application/x-gmplib" id="lib" width="0" height="0"> <script> // Translation of gmp-chudnovsky.c by John Tobey. function onload() { lib = document.getElementById("lib"); arg = document.getElementById("arg"); result = document.getElementById("result"); fn = (function() { var sqrt = Math.sqrt, min = Math.min, max = Math.max, mpf = lib.mpf, mpf_init = mpf.init, mpf_get_prec = mpf.get_prec, mpf_set_d = mpf.set_d, mpf_set_prec_raw = mpf.set_prec_raw, mpf_mul = mpf.mul, mpf_mul_ui = mpf.mul_ui, mpf_ui_sub = mpf.ui_sub, mpf_div_2exp = mpf.div_2exp, my_div = mpf.div, mpf_add = mpf.add, mpz = lib.mpz, mpz_set_ui = mpz.set_ui, mpz_mul_ui = mpz.mul_ui, mpz_add_ui = mpz.add_ui, mpz_mul = mpz.mul, mpz_neg = mpz.neg, mpz_add = mpz.add, mpz_divexact = mpz.divexact, mpz_clear = mpz.clear, mpf_set_default_prec= mpf.set_default_prec, mpz_sizeinbase = mpz.sizeinbase, mpz_addmul_ui = mpz.addmul_ui, mpf_set_z = mpf.set_z; /* Pi computation using Chudnovsky's algortithm. * Copyright 2002, 2005 Hanhong Xue (macroxue at yahoo dot com) * Slightly modified 2005 by Torbjorn Granlund to allow more than 2G digits to be computed. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ var A = 13591409; var B = 545140134; var C = 640320; var D = 12; var BITS_PER_DIGIT = 3.32192809488736234787; var DIGITS_PER_ITER = 14.1816474627254776555; var DOUBLE_PREC = 53; var t1 = mpf(), t2 = mpf(); /* r = sqrt(x) */ function my_sqrt_ui(r, x) { var prec, bits, prec0; prec0 = mpf_get_prec(r); if (prec0<=DOUBLE_PREC) { mpf_set_d(r, sqrt(x)); return; } bits = 0; for (prec=prec0; prec>DOUBLE_PREC;) { var bit = prec&1; prec = (prec+bit)>>1; bits = bits*2+bit; } mpf_set_prec_raw(t1, DOUBLE_PREC); mpf_set_d(t1, 1/sqrt(x)); while (prec<prec0) { prec *=2; if (prec<prec0) { /* t1 = t1+t1*(1-x*t1*t1)/2; */ mpf_set_prec_raw(t2, prec); mpf_mul(t2, t1, t1); /* half x half -> full */ mpf_mul_ui(t2, t2, x); mpf_ui_sub(t2, 1, t2); mpf_set_prec_raw(t2, prec>>1); mpf_div_2exp(t2, t2, 1); mpf_mul(t2, t2, t1); /* half x half -> half */ mpf_set_prec_raw(t1, prec); mpf_add(t1, t1, t2); } else { break; } prec -= (bits&1); bits /=2; } /* t2=x*t1, t1 = t2+t1*(x-t2*t2)/2; */ mpf_set_prec_raw(t2, prec0>>1); mpf_mul_ui(t2, t1, x); mpf_mul(r, t2, t2); /* half x half -> full */ mpf_ui_sub(r, x, r); mpf_mul(t1, t1, r); /* half x half -> half */ mpf_div_2exp(t1, t1, 1); mpf_add(r, t1, t2); } var sieve = []; var sieve_size; var ftmp = {}, fmul = {}; var INIT_FACS = 32; function fac_toString(f) { var i, ret = ""; for (i=0; i<f.num_facs; i++) if (f.pow[i]==1) ret += f.fac[i] + " "; else ret += f.fac[i] + "^" + f.pow[i] + " "; return ret; } function fac_show(f) { my_log(fac_toString(f)); } function fac_reset(f) { f.num_facs = 0; } function fac_init_size(f, s) { if (s<INIT_FACS) s=INIT_FACS; f.fac = Array(s); f.pow = Array(s); f.max_facs = s; fac_reset(f); } function fac_init(f) { fac_init_size(f, INIT_FACS); } function fac() { var ret = {}; fac_init(ret); return ret; } function fac_clear(f) { delete f.fac; delete f.pow; } function fac_resize(f, s) { if (f.max_facs < s) { fac_clear(f); fac_init_size(f, s); } } /* f = base^pow */ function fac_set_bp(f, base, pow) { var i; //assert(base<sieve_size); for (i=0, base>>=1; base>0; i++, base = sieve[base].nxt) { f.fac[i] = sieve[base].fac; f.pow[i] = sieve[base].pow*pow; } f.num_facs = i; //assert(i<=f.max_facs); } /* r = f*g */ function fac_mul2(r, f, g) { var i, j, k; for (i=j=k=0; i<f.num_facs && j<g.num_facs; k++) { if (f.fac[i] == g.fac[j]) { r.fac[k] = f.fac[i]; r.pow[k] = f.pow[i] + g.pow[j]; i++; j++; } else if (f.fac[i] < g.fac[j]) { r.fac[k] = f.fac[i]; r.pow[k] = f.pow[i]; i++; } else { r.fac[k] = g.fac[j]; r.pow[k] = g.pow[j]; j++; } } for (; i<f.num_facs; i++, k++) { r.fac[k] = f.fac[i]; r.pow[k] = f.pow[i]; } for (; j<g.num_facs; j++, k++) { r.fac[k] = g.fac[j]; r.pow[k] = g.pow[j]; } r.num_facs = k; //assert(k<=r.max_facs); } /* r = f */ function fac_set(r, f) { r.num_facs = f.num_facs; r.max_facs = f.max_facs; r.fac = f.fac; r.pow = f.pow; } /* f *= g */ function fac_mul(f, g) { var tmp = fac(); fac_resize(fmul, f.num_facs + g.num_facs); fac_mul2(fmul, f, g); fac_set(tmp, f); fac_set(f, fmul); fac_set(fmul, tmp); } /* f *= base^pow */ function fac_mul_bp(f, base, pow) { fac_set_bp(ftmp, base, pow); fac_mul(f, ftmp); } /* remove factors of power 0 */ function fac_compact(f) { var i, j; for (i=0, j=0; i<f.num_facs; i++) { if (f.pow[i]>0) { if (j<i) { f.fac[j] = f.fac[i]; f.pow[j] = f.pow[i]; } j++; } } f.num_facs = j; } /* convert factorized form to number */ function bs_mul(r, a, b) { var i, j; if (b-a<=32) { mpz_set_ui(r, 1); for (i=a; i<b; i++) for (j=0; j<fmul.pow[i]; j++) mpz_mul_ui(r, r, fmul.fac[i]); } else { var r2 = mpz(); //mpz_init(r2); bs_mul(r2, a, (a+b)>>1); bs_mul(r, (a+b)>>1, b); mpz_mul(r, r, r2); //mpz_clear(r2); } } var gcd = mpz(), mgcd = mpz(); /* f /= gcd(f,g), g /= gcd(f,g) */ function fac_remove_gcd(p, fp, g, fg) { var i, j, k, c; fac_resize(fmul, min(fp.num_facs, fg.num_facs)); for (i=j=k=0; i<fp.num_facs && j<fg.num_facs; ) { if (fp.fac[i] == fg.fac[j]) { c = min(fp.pow[i], fg.pow[j]); fp.pow[i] -= c; fg.pow[j] -= c; fmul.fac[k] = fp.fac[i]; fmul.pow[k] = c; i++; j++; k++; } else if (fp.fac[i] < fg.fac[j]) { i++; } else { j++; } } fmul.num_facs = k; //assert(k <= fmul.max_facs); if (fmul.num_facs) { bs_mul(gcd, 0, fmul.num_facs); mpz_divexact(p, p, gcd); mpz_divexact(g, g, gcd); fac_compact(fp); fac_compact(fg); } } /*///////////////////////////////////////////////////////////////////////////*/ var out=0; var pstack=[], qstack=[], gstack=[]; var fpstack=[], fgstack=[]; var top = 0; var progress=0, percent; /* binary splitting */ function bs(a, b, gflag, level) { var i, mid; var ccc; var p1 = pstack[top]; var q1 = qstack[top]; var g1 = gstack[top]; var fp1 = fpstack[top]; var fg1 = fgstack[top]; var p2 = pstack[top+1]; var q2 = qstack[top+1]; var g2 = gstack[top+1]; var fp2 = fpstack[top+1]; var fg2 = fgstack[top+1]; if (b-a==1) { /* g(b-1,b) = (6b-5)(2b-1)(6b-1) p(b-1,b) = b^3 * C^3 / 24 q(b-1,b) = (-1)^b*g(b-1,b)*(A+Bb). */ mpz_set_ui(p1, b); mpz_mul_ui(p1, p1, b); mpz_mul_ui(p1, p1, b); mpz_mul_ui(p1, p1, Math.floor(C/24)*Math.floor(C/24)); mpz_mul_ui(p1, p1, Math.floor(C*24)); mpz_set_ui(g1, 2*b-1); mpz_mul_ui(g1, g1, 6*b-1); mpz_mul_ui(g1, g1, 6*b-5); mpz_set_ui(q1, b); mpz_mul_ui(q1, q1, B); mpz_add_ui(q1, q1, A); mpz_mul (q1, q1, g1); if (b%2) mpz_neg(q1, q1); i=b; while ((i&1)==0) i>>=1; fac_set_bp(fp1, i, 3); /* b^3 */ fac_mul_bp(fp1, 3*5*23*29, 3); fp1.pow[0]--; fac_set_bp(fg1, 2*b-1, 1); /* 2b-1 */ fac_mul_bp(fg1, 6*b-1, 1); /* 6b-1 */ fac_mul_bp(fg1, 6*b-5, 1); /* 6b-5 */ if (b>progress) { //printf("."); fflush(stdout); progress += percent*2; } } else { /* p(a,b) = p(a,m) * p(m,b) g(a,b) = g(a,m) * g(m,b) q(a,b) = q(a,m) * p(m,b) + q(m,b) * g(a,m) */ mid = Math.floor(a+(b-a)*0.5224); /* tuning parameter */ bs(a, mid, 1, level+1); top++; bs(mid, b, gflag, level+1); top--; //if (level == 0) //puts (""); ccc = level == 0; //if (ccc) CHECK_MEMUSAGE; if (level>=4) { /* tuning parameter */ fac_remove_gcd(p2, fp2, g1, fg1); } //if (ccc) CHECK_MEMUSAGE; mpz_mul(p1, p1, p2); //if (ccc) CHECK_MEMUSAGE; mpz_mul(q1, q1, p2); //if (ccc) CHECK_MEMUSAGE; mpz_mul(q2, q2, g1); //if (ccc) CHECK_MEMUSAGE; mpz_add(q1, q1, q2); //if (ccc) CHECK_MEMUSAGE; fac_mul(fp1, fp2); if (gflag) { mpz_mul(g1, g1, g2); fac_mul(fg1, fg2); } } if (out&2) { my_log("p(" + a + "," + b + ")=" + fac_toString(fp1)); if (gflag) my_log("g(" + a + "," + b + ")=" + fac_toString(fg1)); } } function build_sieve(n, s) { var m, i, j, k; sieve_size = n; m = Math.floor(sqrt(n)); for (i = 0; i < n>>1; i++) s[i] = { fac: 0, pow: 0, nxt: 0 }; s[1>>1].fac = 1; s[1>>1].pow = 1; for (i=3; i<=n; i+=2) { if (s[i>>1].fac == 0) { s[i>>1].fac = i; s[i>>1].pow = 1; if (i<=m) { for (j=i*i, k=i>>1; j<=n; j+=i+i, k++) { if (s[j>>1].fac==0) { s[j>>1].fac = i; if (s[k].fac == i) { s[j>>1].pow = s[k].pow + 1; s[j>>1].nxt = s[k].nxt; } else { s[j>>1].pow = 1; s[j>>1].nxt = k; } } } } } } } function main(d, outval) { var pi = mpf(), qi = mpf(); d = d || 100; out = outval || 0; var i, depth=1, terms; var psize, qsize; var begin, mid0, mid1, mid2, mid3, mid4, end; terms = Math.floor(d/DIGITS_PER_ITER); while ((1<<depth)<terms) depth++; depth++; percent = terms/100.0; //printf("#terms=%ld, depth=%ld\n", terms, depth); //begin = cputime(); //printf("sieve "); fflush(stdout); sieve_size = max(3*5*23*29+1, terms*6); sieve = Array(sieve_size>>1); build_sieve(sieve_size, sieve); //mid0 = cputime(); //printf("time = %6.3f\n", (double)(mid0-begin)/1000); /* allocate stacks */ pstack = Array(depth); qstack = Array(depth); gstack = Array(depth); fpstack = Array(depth); fgstack = Array(depth); for (i=0; i<depth; i++) { pstack[i] = mpz(); qstack[i] = mpz(); gstack[i] = mpz(); fpstack[i] = fac(); fgstack[i] = fac(); } fac_init(ftmp); fac_init(fmul); var p1 = pstack[top]; var q1 = qstack[top]; var g1 = gstack[top]; var fp1 = fpstack[top]; var fg1 = fgstack[top]; var p2 = pstack[top+1]; var q2 = qstack[top+1]; var g2 = gstack[top+1]; var fp2 = fpstack[top+1]; var fg2 = fgstack[top+1]; /* begin binary splitting process */ if (terms<=0) { mpz_set_ui(p2,1); mpz_set_ui(q2,0); mpz_set_ui(g2,1); } else { bs(0,terms,0,0); } /* mid1 = cputime(); printf("\nbs time = %6.3f\n", (double)(mid1-mid0)/1000); printf(" gcd time = %6.3f\n", (double)(gcd_time)/1000); */ /* printf("misc "); fflush(stdout); */ /* free some resources */ sieve = undefined; mpz_clear(gcd); fac_clear(ftmp); fac_clear(fmul); for (i=1; i<depth; i++) { mpz_clear(pstack[i]); mpz_clear(qstack[i]); mpz_clear(gstack[i]); fac_clear(fpstack[i]); fac_clear(fgstack[i]); } mpz_clear(gstack[0]); fac_clear(fpstack[0]); fac_clear(fgstack[0]); gstack = undefined; fpstack = undefined; fgstack = undefined; /* prepare to convert integers to floats */ mpf_set_default_prec(Math.floor(d*BITS_PER_DIGIT+16)); /* p*(C/D)*sqrt(C) pi = ----------------- (q+A*p) */ psize = mpz_sizeinbase(p1,10); qsize = mpz_sizeinbase(q1,10); mpz_addmul_ui(q1, p1, A); mpz_mul_ui(p1, p1, Math.floor(C/D)); mpf_init(pi); mpf_set_z(pi, p1); mpz_clear(p1); mpf_init(qi); mpf_set_z(qi, q1); mpz_clear(q1); pstack = undefined; qstack = undefined; //mid2 = cputime(); /* printf("time = %6.3f\n", (double)(mid2-mid1)/1000); */ /* initialize temp float variables for sqrt & div */ mpf_init(t1); mpf_init(t2); mpf_set_prec_raw(t1, mpf_get_prec(pi)); /* final step */ //printf("div "); fflush(stdout); my_div(qi, pi, qi); //mid3 = cputime(); //printf("time = %6.3f\n", (double)(mid3-mid2)/1000); //printf("sqrt "); fflush(stdout); my_sqrt_ui(pi, C); //mid4 = cputime(); //printf("time = %6.3f\n", (double)(mid4-mid3)/1000); //printf("mul "); fflush(stdout); mpf_mul(qi, qi, pi); //end = cputime(); //printf("time = %6.3f\n", (double)(end-mid4)/1000); //printf("total time = %6.3f\n", (double)(end-begin)/1000); //fflush(stdout); /* printf(" P size=%ld digits (%f)\n" " Q size=%ld digits (%f)\n", psize, (double)psize/d, qsize, (double)qsize/d); */ /* output Pi and timing statistics */ /* if (out&1) { printf("pi(0,%ld)=\n", terms); mpf_out_str(stdout, 10, d+2, qi); printf("\n"); } */ /* free float resources */ /* mpf_clear(pi); mpf_clear(qi); mpf_clear(t1); mpf_clear(t2); exit (0); */ return qi.toString(10, d+2); } return main; })(); } function trace_all_calls(gmp) { // Wrap every gmp function... var ret = {}; for (var i in gmp) { var x = gmp[i]; if (typeof x === "function" && i === i.toLowerCase()) { x = (function (o, i, len) { function ret(a,b,c,d,e) { var args = Array.prototype.slice.call(arguments); my_log(i + "(" + args.join(",") + ")"); switch (len) { case 0: return o(); case 1: return o(a); case 2: return o(a,b); case 3: return o(a,b,c); case 4: return o(a,b,c,d); case 5: return o(a,b,c,d,e); default: throw "length " + len; } } return ret; })(x, i, x.length + 0); } ret[i] = x; } return ret; } var logged = 0; function my_log() { console.log.apply(console.log, Array.prototype.slice.call(arguments)); logged++; if (logged == 250) { if (!confirm("logged a lot")) throw Error("logged too much"); logged = 0; } } function doit() { try { result.innerHTML = eval(arg.value); } catch (e) { alert(e); } } </script> </head> <body onload="onload()"> <form onsubmit="return false;"> <textarea id="arg" rows="20" cols="80">(function(){ // Replace 10000 with a number of digits and click the button. return fn(10000); })()</textarea><button onclick="doit()">=</button><span id="result"></span> </form> </body> </html>