| #include "os.h" |
| #include <mp.h> |
| #include "dat.h" |
| |
| mpint* |
| mpfactorial(ulong n) |
| { |
| int i; |
| ulong k; |
| unsigned cnt; |
| int max, mmax; |
| mpdigit p, pp[2]; |
| mpint *r, *s, *stk[31]; |
| |
| cnt = 0; |
| max = mmax = -1; |
| p = 1; |
| r = mpnew(0); |
| for(k=2; k<=n; k++){ |
| pp[0] = 0; |
| pp[1] = 0; |
| mpvecdigmuladd(&p, 1, (mpdigit)k, pp); |
| if(pp[1] == 0) /* !overflow */ |
| p = pp[0]; |
| else{ |
| cnt++; |
| if((cnt & 1) == 0){ |
| s = stk[max]; |
| mpbits(r, Dbits*(s->top+1+1)); |
| memset(r->p, 0, Dbytes*(s->top+1+1)); |
| mpvecmul(s->p, s->top, &p, 1, r->p); |
| r->sign = 1; |
| r->top = s->top+1+1; /* XXX: norm */ |
| mpassign(r, s); |
| for(i=4; (cnt & (i-1)) == 0; i=i<<1){ |
| mpmul(stk[max], stk[max-1], r); |
| mpassign(r, stk[max-1]); |
| max--; |
| } |
| }else{ |
| max++; |
| if(max > mmax){ |
| mmax++; |
| if(max > nelem(stk)) |
| abort(); |
| stk[max] = mpnew(Dbits); |
| } |
| stk[max]->top = 1; |
| stk[max]->p[0] = p; |
| } |
| p = (mpdigit)k; |
| } |
| } |
| if(max < 0){ |
| mpbits(r, Dbits); |
| r->top = 1; |
| r->sign = 1; |
| r->p[0] = p; |
| }else{ |
| s = stk[max--]; |
| mpbits(r, Dbits*(s->top+1+1)); |
| memset(r->p, 0, Dbytes*(s->top+1+1)); |
| mpvecmul(s->p, s->top, &p, 1, r->p); |
| r->sign = 1; |
| r->top = s->top+1+1; /* XXX: norm */ |
| } |
| |
| while(max >= 0) |
| mpmul(r, stk[max--], r); |
| for(max=mmax; max>=0; max--) |
| mpfree(stk[max]); |
| mpnorm(r); |
| return r; |
| } |