void mpmovefixflt(Mpflt *a, Mpint *b) { a->val = *b; a->exp = 0; mpnorm(a); }
int mpmovefltfix(Mpint *a, Mpflt *b) { Mpflt f; int i; if(mpexactfltfix(a, b) == 0) return 0; // try rounding down a little f = *b; f.val.a[0] = 0; if(mpexactfltfix(a, &f) == 0) return 0; // try rounding up a little for(i=1; i<Mpprec; i++) { f.val.a[i]++; if(f.val.a[i] != Mpbase) break; f.val.a[i] = 0; } mpnorm(&f); if(mpexactfltfix(a, &f) == 0) return 0; return -1; }
// sum = abs(b1) + abs(b2), i.e., add the magnitudes void mpmagadd(mpint *b1, mpint *b2, mpint *sum) { int m, n; mpint *t; // get the sizes right if(b2->top > b1->top){ t = b1; b1 = b2; b2 = t; } n = b1->top; m = b2->top; if(n == 0){ mpassign(mpzero, sum); return; } if(m == 0){ mpassign(b1, sum); return; } mpbits(sum, (n+1)*Dbits); sum->top = n+1; mpvecadd(b1->p, n, b2->p, m, sum->p); sum->sign = 1; mpnorm(sum); }
vlong mptov(mpint *b) { uvlong v; int s; if(b->top == 0) return (vlong) 0; mpnorm(b); if(b->top > VLDIGITS){ if(b->sign > 0) return (vlong)MAXVLONG; else return (vlong)MINVLONG; } v = (uvlong) 0; for(s = 0; s < b->top; s++) v |= b->p[s]<<(s*sizeof(mpdigit)*8); if(b->sign > 0){ if(v > MAXVLONG) v = MAXVLONG; } else { if(v > MINVLONG) v = MINVLONG; else v = -(vlong)v; } return (vlong)v; }
// convert (truncate) b to a. // return -1 (but still convert) if b was non-integer. int mpmovefltfix(Mpint *a, Mpflt *b) { Mpflt f; *a = b->val; mpshiftfix(a, b->exp); if(b->exp < 0) { f.val = *a; f.exp = 0; mpnorm(&f); if(mpcmpfltflt(b, &f) != 0) return -1; } return 0; }
/* * mpbmu_w * computes the Barrett 'mu' coefficient * needs workspace of (6*size+4) words */ void mpbmu_w(mpbarrett* b, mpw* wksp) { register size_t size = b->size; register size_t shift; register mpw* divmod = wksp; register mpw* dividend = divmod+(size*2+2); register mpw* workspace = dividend+(size*2+1); /* normalize modulus before division */ shift = mpnorm(size, b->modl); /* make the dividend, initialize first word to 1 (shifted); the rest is zero */ *dividend = ((mpw) MP_LSBMASK << shift); mpzero(size*2, dividend+1); mpndivmod(divmod, size*2+1, dividend, size, b->modl, workspace); mpcopy(size+1, b->mu, divmod+1); /* de-normalize */ mprshift(size, b->modl, shift); }
uint64_t mptouv(mpint *b) { uint64_t v; int s; if(b->top == 0) return 0LL; mpnorm(b); if(b->top > VLDIGITS) return MAXVLONG; v = 0ULL; for(s = 0; s < b->top; s++) v |= (uint64_t)b->p[s]<<(s*sizeof(mpdigit)*8); return v; }
void mpmul(mpint *b1, mpint *b2, mpint *prod) { mpint *oprod; oprod = nil; if(prod == b1 || prod == b2){ oprod = prod; prod = mpnew(0); } prod->top = 0; mpbits(prod, (b1->top+b2->top+1)*Dbits); mpvecmul(b1->p, b1->top, b2->p, b2->top, prod->p); prod->top = b1->top+b2->top+1; prod->sign = b1->sign*b2->sign; mpnorm(prod); if(oprod != nil){ mpassign(prod, oprod); mpfree(prod); } }
// // floating point input // required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*] // void mpatoflt(Mpflt *a, char *as) { Mpflt b; int dp, c, f, ef, ex, eb, base; char *s, *start; while(*as == ' ' || *as == '\t') as++; /* determine base */ s = as; base = -1; while(base == -1) { switch(*s++) { case '-': case '+': break; case '0': if(*s == 'x') base = 16; else base = 10; break; default: base = 10; } } s = as; dp = 0; /* digits after decimal point */ f = 0; /* sign */ ex = 0; /* exponent */ eb = 0; /* binary point */ mpmovecflt(a, 0.0); if(base == 16) { start = nil; for(;;) { c = *s; if(c == '-') { f = 1; s++; } else if(c == '+') { s++; } else if(c == '0' && s[1] == 'x') { s += 2; start = s; } else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { s++; } else { break; } } if(start == nil) goto bad; mphextofix(&a->val, start, s-start); if(a->val.ovf) goto bad; a->exp = 0; mpnorm(a); } for(;;) { switch(c = *s++) { default: goto bad; case '-': f = 1; case ' ': case '\t': case '+': continue; case '.': if(base == 16) goto bad; dp = 1; continue; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '0': mpmulcflt(a, 10); mpaddcflt(a, c-'0'); if(dp) dp++; continue; case 'P': case 'p': eb = 1; case 'E': case 'e': ex = 0; ef = 0; for(;;) { c = *s++; if(c == '+' || c == ' ' || c == '\t') continue; if(c == '-') { ef = 1; continue; } if(c >= '0' && c <= '9') { ex = ex*10 + (c-'0'); if(ex > 1e8) { yyerror("constant exponent out of range: %s", as); errorexit(); } continue; } break; } if(ef) ex = -ex; case 0: break; } break; } if(eb) { if(dp) goto bad; mpsetexp(a, a->exp+ex); goto out; } if(dp) dp--; if(mpcmpfltc(a, 0.0) != 0) { if(ex >= dp) { mppow10flt(&b, ex-dp); mpmulfltflt(a, &b); } else { // 4 approximates least_upper_bound(log2(10)). if(dp-ex >= (1<<(8*sizeof(dp)-3)) || (short)(4*(dp-ex)) != 4*(dp-ex)) { mpmovecflt(a, 0.0); } else { mppow10flt(&b, dp-ex); mpdivfltflt(a, &b); } } } out: if(f) mpnegflt(a); return; bad: yyerror("constant too large: %s", as); mpmovecflt(a, 0.0); }
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; }
void mp_real::mpadd(const mp_real &a, const mp_real &b, mp_real& c, int prec_words) { /** * This routine adds MP numbers A and B to yield the MP sum C. It attempts * to include all significance of A and B in the result, up to the maximum * mantissa length MPNW. Debug output starts with debug_level = 9. * This is a new simplified version. * * D contains the intermediate results of addition before normalization. * The first 3 words of D have special meanings: * d[0] : not accessed * d[1] : sign and number of words in D * d[2] : exponent * Before normalization, each word of D may have up to 53 bits and may be * negative. After normalization, each word is in [0, 2^mpnbt-1] */ double db; int i, ia, ib, ish, ixa, ixb, ixd, na, nb; int m1, m2, m3, m4, m5, nsh; double *d; int nd; // number of actual words in d[] if (error_no != 0) { zero(c); return; } if (debug_level >= 9) { print_mpreal("MPADD a ", a); print_mpreal("MPADD b ", b); } ia = a[1] >= 0 ? 1 : -1; ib = b[1] >= 0 ? 1 : -1; na = std::min (static_cast<int>(std::abs(a[1])), prec_words); // number of words in A nb = std::min (static_cast<int>(std::abs(b[1])), prec_words); // number of words in B /* Check for zero inputs. */ if (na == 0) { /* A is zero -- the result is B. */ int num_words = std::min(nb, int(c[0])-FST_M); c[1] = ib > 0 ? num_words : -num_words; for (i = 2; i < num_words + FST_M; ++i) c[i] = b[i]; return; } else if (nb == 0) { /* B is zero -- the result is A. */ int num_words = std::min(na, int(c[0])-FST_M); c[1] = ia >= 0 ? num_words : -num_words; for (i = 2; i < num_words + FST_M; ++i) c[i] = a[i]; return; } // get ready for main part of routine. d = new double[prec_words+7]; if (ia == ib) db = 1.0; //same signs - add else db = -1.0; // different signs - subtract ixa = static_cast<int>(a[2]); ixb = static_cast<int>(b[2]); ish = ixa - ixb; d[1] = 0.0; d[2] = 0.0; if (ish >= 0) { // |A| >= |B| // A has greater exponent than B, so B must be shifted to the right // to line up the radix point. m1 = std::min (na, ish); m2 = std::min (na, nb + ish); m3 = na; m4 = std::min (std::max (na, ish), prec_words + 1); m5 = std::min (std::max (na, nb + ish), prec_words + 1); //assert(m1<=m2 && m2<=m3 && m3<=m4 && m4<=m5); for (i = FST_M; i < m1 + FST_M; ++i) d[i] = a[i]; if(db > 0) {//Addition for (i = m1 + FST_M; i < m2 + FST_M; ++i) d[i] = a[i] + b[i-ish]; for (i = m2 + FST_M; i < m3 + FST_M; ++i) d[i] = a[i]; for (i = m3 + FST_M; i < m4 + FST_M; ++i) d[i] = 0.0; for (i = m4 + FST_M; i < m5 + FST_M; ++i) d[i] = b[i-ish]; } else {//Subtraction for (i = m1 + FST_M; i < m2 + FST_M; ++i) d[i] = a[i] - b[i-ish]; for (i = m2 + FST_M; i < m3 + FST_M; ++i) d[i] = a[i]; for (i = m3 + FST_M; i < m4 + FST_M; ++i) d[i] = 0.0; for (i = m4 + FST_M; i < m5 + FST_M; ++i) d[i] = - b[i-ish]; } nd = m5; ixd = ixa; d[nd+3] = 0.0; d[nd+4] = 0.0; } else { // B has greater exponent than A, so A must be shifted to the right // to line up the radix point. nsh = -ish; m1 = std::min (nb, nsh); m2 = std::min (nb, na + nsh); m3 = nb; m4 = std::min (std::max (nb, nsh), prec_words + 1); m5 = std::min (std::max (nb, na + nsh), prec_words + 1); //assert(m1<=m2 && m2<=m3 && m3<=m4 && m4<=m5); if(db > 0) {//Addition for (i = FST_M; i < m1 + FST_M; ++i) d[i] = b[i]; for (i = m1 + FST_M; i < m2 + FST_M; ++i) d[i] = a[i-nsh] + b[i]; for (i = m2 + FST_M; i < m3 + FST_M; ++i) d[i] = b[i]; } else {//Subtraction for (i = FST_M; i < m1 + FST_M; ++i) d[i] = - b[i]; for (i = m1 + FST_M; i < m2 + FST_M; ++i) d[i] = a[i-nsh] - b[i]; for (i = m2 + FST_M; i < m3 + FST_M; ++i) d[i] = - b[i]; } for (i = m3 + FST_M; i < m4 + FST_M; ++i) d[i] = 0.0; for (i = m4 + FST_M; i < m5 + FST_M; ++i) d[i] = a[i-nsh]; nd = m5; ixd = ixb; d[nd+3] = 0.0; d[nd+4] = 0.0; } // Call mpnorm to fix up result and store in c. d[1] = ia >= 0 ? nd : -nd; d[2] = ixd; mpnorm(d, c, prec_words); delete [] (d); if (debug_level >= 9) print_mpreal("MPADD O: c ", c); return; }
void mpdiv(mpint *dividend, mpint *divisor, mpint *quotient, mpint *remainder) { int j, s, vn, sign; mpdigit qd, *up, *vp, *qp; mpint *u, *v, *t; // divide bv zero if(divisor->top == 0) sysfatal("mpdiv: divide by zero"); // quick check if(mpmagcmp(dividend, divisor) < 0){ if(remainder != nil) mpassign(dividend, remainder); if(quotient != nil) mpassign(mpzero, quotient); return; } // D1: shift until divisor, v, has hi bit set (needed to make trial // divisor accurate) qd = divisor->p[divisor->top-1]; for(s = 0; (qd & mpdighi) == 0; s++) qd <<= 1; u = mpnew((dividend->top+2)*Dbits + s); if(s == 0 && divisor != quotient && divisor != remainder) { mpassign(dividend, u); v = divisor; } else { mpleft(dividend, s, u); v = mpnew(divisor->top*Dbits); mpleft(divisor, s, v); } up = u->p+u->top-1; vp = v->p+v->top-1; vn = v->top; // D1a: make sure high digit of dividend is less than high digit of divisor if(*up >= *vp){ *++up = 0; u->top++; } // storage for multiplies t = mpnew(4*Dbits); qp = nil; if(quotient != nil){ mpbits(quotient, (u->top - v->top)*Dbits); quotient->top = u->top - v->top; qp = quotient->p+quotient->top-1; } // D2, D7: loop on length of dividend for(j = u->top; j > vn; j--){ // D3: calculate trial divisor mpdigdiv(up-1, *vp, &qd); // D3a: rule out trial divisors 2 greater than real divisor if(vn > 1) for(;;){ memset(t->p, 0, 3*Dbytes); // mpvecdigmuladd adds to what's there mpvecdigmuladd(vp-1, 2, qd, t->p); if(mpveccmp(t->p, 3, up-2, 3) > 0) qd--; else break; } // D4: u -= v*qd << j*Dbits sign = mpvecdigmulsub(v->p, vn, qd, up-vn); if(sign < 0){ // D6: trial divisor was too high, add back borrowed // value and decrease divisor mpvecadd(up-vn, vn+1, v->p, vn, up-vn); qd--; } // D5: save quotient digit if(qp != nil) *qp-- = qd; // push top of u down one u->top--; *up-- = 0; } if(qp != nil){ mpnorm(quotient); if(dividend->sign != divisor->sign) quotient->sign = -1; } if(remainder != nil){ mpright(u, s, remainder); // u is the remainder shifted remainder->sign = dividend->sign; } mpfree(t); mpfree(u); if(v != divisor) mpfree(v); }