jehanne/sys/src/lib/mp/port/cnfield.c

115 lines
1.7 KiB
C

#include "os.h"
#include <mp.h>
#include "dat.h"
/*
* fast reduction for crandall numbers of the form: 2^n - c
*/
enum {
MAXDIG = 1024 / Dbits,
};
typedef struct CNfield CNfield;
struct CNfield
{
Mfield;
mpint m[1];
int s;
mpdigit c;
};
static int
cnreduce(Mfield *m, mpint *a, mpint *r)
{
mpdigit q[MAXDIG-1], t[MAXDIG], d;
CNfield *f = (CNfield*)m;
int qn, tn, k;
k = f->top;
if((a->top - k) >= MAXDIG)
return -1;
mpleft(a, f->s, r);
if(r->top <= k)
mpbits(r, (k+1)*Dbits);
/* q = hi(r) */
qn = r->top - k;
jehanne_memmove(q, r->p+k, qn*Dbytes);
/* r = lo(r) */
r->top = k;
r->sign = 1;
do {
/* t = q*c */
tn = qn+1;
jehanne_memset(t, 0, tn*Dbytes);
mpvecdigmuladd(q, qn, f->c, t);
/* q = hi(t) */
qn = tn - k;
if(qn <= 0) qn = 0;
else jehanne_memmove(q, t+k, qn*Dbytes);
/* r += lo(t) */
if(tn > k)
tn = k;
mpvecadd(r->p, k, t, tn, r->p);
/* if(r >= m) r -= m */
mpvecsub(r->p, k+1, f->m->p, k, t);
d = t[k];
for(tn = 0; tn < k; tn++)
r->p[tn] = (r->p[tn] & d) | (t[tn] & ~d);
} while(qn > 0);
if(f->s != 0)
mpright(r, f->s, r);
mpnorm(r);
return 0;
}
Mfield*
cnfield(mpint *N)
{
mpint *M, *C;
CNfield *f;
mpdigit d;
int s;
if(N->top <= 2 || N->top >= MAXDIG)
return nil;
f = nil;
d = N->p[N->top-1];
for(s = 0; (d & (mpdigit)1<<Dbits-1) == 0; s++)
d <<= 1;
C = mpnew(0);
M = mpcopy(N);
mpleft(N, s, M);
mpleft(mpone, M->top*Dbits, C);
mpsub(C, M, C);
if(C->top != 1)
goto out;
f = jehanne_mallocz(sizeof(CNfield) + M->top*sizeof(mpdigit), 1);
if(f == nil)
goto out;
f->s = s;
f->c = C->p[0];
f->m->size = M->top;
f->m->p = (mpdigit*)&f[1];
mpassign(M, f->m);
mpassign(N, f);
f->reduce = cnreduce;
f->flags |= MPfield;
out:
mpfree(M);
mpfree(C);
return f;
}