Subversion Repositories planix.SVN

Rev

Rev 33 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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;
}