diff options
Diffstat (limited to 'erts/emulator/beam/big.c')
-rw-r--r-- | erts/emulator/beam/big.c | 2241 |
1 files changed, 2241 insertions, 0 deletions
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c new file mode 100644 index 0000000000..03c88da8c6 --- /dev/null +++ b/erts/emulator/beam/big.c @@ -0,0 +1,2241 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "erl_vm.h" +#include "global.h" +#include "big.h" +#include "error.h" +#include "bif.h" + +#define ZERO_DIGITS(v, sz) do { \ + dsize_t _t_sz = sz; \ + ErtsDigit* _t_v = v; \ + while(_t_sz--) *_t_v++ = 0; \ + } while(0) + +#define MOVE_DIGITS(dst, src, sz) do { \ + dsize_t _t_sz = sz; \ + ErtsDigit* _t_dst; \ + ErtsDigit* _t_src; \ + if (dst < src) { \ + _t_dst = dst; \ + _t_src = src; \ + while(_t_sz--) *_t_dst++ = *_t_src++; \ + } \ + else if (dst > src) { \ + _t_dst = (dst)+((sz)-1); \ + _t_src = (src)+((sz)-1); \ + while(_t_sz--) *_t_dst-- = *_t_src--; \ + } \ + } while(0) + +/* add a and b with carry in + out */ +#define DSUMc(a,b,c,s) do { \ + ErtsDigit ___cr = (c); \ + ErtsDigit ___xr = (a)+(___cr); \ + ErtsDigit ___yr = (b); \ + ___cr = (___xr < ___cr); \ + ___xr = ___yr + ___xr; \ + ___cr += (___xr < ___yr); \ + s = ___xr; \ + c = ___cr; \ + } while(0) + +/* add a and b with carry out */ +#define DSUM(a,b,c,s) do { \ + ErtsDigit ___xr = (a); \ + ErtsDigit ___yr = (b); \ + ___xr = ___yr + ___xr; \ + s = ___xr; \ + c = (___xr < ___yr); \ + } while(0) + +#define DSUBb(a,b,r,d) do { \ + ErtsDigit ___cr = (r); \ + ErtsDigit ___xr = (a); \ + ErtsDigit ___yr = (b)+___cr; \ + ___cr = (___yr < ___cr); \ + ___yr = ___xr - ___yr; \ + ___cr += (___yr > ___xr); \ + d = ___yr; \ + r = ___cr; \ + } while(0) + +#define DSUB(a,b,r,d) do { \ + ErtsDigit ___xr = (a); \ + ErtsDigit ___yr = (b); \ + ___yr = ___xr - ___yr; \ + r = (___yr > ___xr); \ + d = ___yr; \ + } while(0) + +/* type a constant as a ErtsDigit - to get shifts correct */ +#define DCONST(n) ((ErtsDigit)(n)) + +/* + * BIG_HAVE_DOUBLE_DIGIT is defined if we have defined + * the type ErtsDoubleDigit which MUST have + * sizeof(ErtsDoubleDigit) >= sizeof(ErtsDigit) + */ +#ifdef BIG_HAVE_DOUBLE_DIGIT + +/* ErtsDoubleDigit => ErtsDigit */ +#define DLOW(x) ((ErtsDigit)(x)) +#define DHIGH(x) ((ErtsDigit)(((ErtsDoubleDigit)(x)) >> D_EXP)) + +/* ErtsDigit => ErtsDoubleDigit */ +#define DLOW2HIGH(x) (((ErtsDoubleDigit)(x)) << D_EXP) +#define DDIGIT(a1,a0) (DLOW2HIGH(a1) + (a0)) + +#define DMULc(a,b,c,p) do { \ + ErtsDoubleDigit _t = ((ErtsDoubleDigit)(a))*(b) + (c); \ + p = DLOW(_t); \ + c = DHIGH(_t); \ + } while(0) +#define DMUL(a,b,c1,c0) do { \ + ErtsDoubleDigit _t = ((ErtsDoubleDigit)(a))*(b); \ + c0 = DLOW(_t); \ + c1 = DHIGH(_t); \ + } while(0) + +#define DDIV(a1,a0,b,q) do { \ + ErtsDoubleDigit _t = DDIGIT((a1),(a0)); \ + q = _t / (b); \ + } while(0) + +#define DDIV2(a1,a0,b1,b0,q) do { \ + ErtsDoubleDigit _t = DDIGIT((a1),(a0)); \ + q = _t / DDIGIT((b1),(b0)); \ + } while(0) + +#define DREM(a1,a0,b,r) do { \ + ErtsDoubleDigit _t = DDIGIT((a1),(a0)); \ + r = _t % (b); \ + } while(0) + +#else + +/* If we do not have double digit then we have some more work to do */ +#define H_EXP (D_EXP >> 1) +#define LO_MASK ((ErtsDigit)((DCONST(1) << H_EXP)-1)) +#define HI_MASK ((ErtsDigit)(LO_MASK << H_EXP)) + +#define DGT(a,b) ((a)>(b)) +#define DEQ(a,b) ((a)==(b)) + +#define D2GT(a1,a0,b1,b0) (DGT(a1,b1) || (((a1)==(b1)) && DGT(a0,b0))) +#define D2EQ(a1,a0,b1,b0) (DEQ(a1,b1) && DEQ(a0,b0)) +#define D2LT(a1,a0,b1,b0) D2GT(b1,b0,a1,a0) +#define D2GTE(a1,a0,b1,b0) (!D2LT(a1,a0,b1,b0)) +#define D2LTE(a1,a0,b1,b0) (!D2GT(a1,a0,b1,b0)) + +// Add (A+B), A=(a1B+a0) B=(b1B+b0) +#define D2ADD(a1,a0,b1,b0,c1,c0) do { \ + ErtsDigit __ci = 0; \ + DSUM(a0,b0,__ci,c0); \ + DSUMc(a1,b1,__ci,c1); \ + } while(0) + +// Subtract (A-B), A=(a1B+a0), B=(b1B+b0) (A>=B) +#define D2SUB(a1,a0,b1,b0,c1,c0) do { \ + ErtsDigit __bi; \ + DSUB(a0,b0,__bi,c0); \ + DSUBb(a1,b1,__bi,c1); \ + } while(0) + + +/* Left shift (multiply by 2) (A <<= 1 where A=a1*B+a0) */ +#define D2LSHIFT1(a1,a0) do { \ + a1 = ((a0) >> (D_EXP-1)) | ((a1)<<1); \ + a0 = (a0) << 1; \ + } while(0) + +/* Right shift (divide by 2) (A >>= 1 where A=a1*B+a0) */ +#define D2RSHIFT1(a1,a0) do { \ + a0 = (((a1) & 1) << (D_EXP-1)) | ((a0)>>1); \ + a1 = ((a1) >> 1); \ + } while(0) + +/* Calculate a*b + d1 and store double prec result in d1, d0 */ +#define DMULc(a,b,d1,d0) do { \ + ErtsHalfDigit __a0 = (a); \ + ErtsHalfDigit __a1 = ((a) >> H_EXP); \ + ErtsHalfDigit __b0 = (b); \ + ErtsHalfDigit __b1 = ((b) >> H_EXP); \ + ErtsDigit __a0b0 = (ErtsDigit)__a0*__b0; \ + ErtsDigit __a0b1 = (ErtsDigit)__a0*__b1; \ + ErtsDigit __a1b0 = (ErtsDigit)__a1*__b0; \ + ErtsDigit __a1b1 = (ErtsDigit)__a1*__b1; \ + ErtsDigit __p0,__p1,__p2,__c0; \ + DSUM(__a0b0,d1,__c0,__p0); \ + DSUM((__c0<<H_EXP),(__p0>>H_EXP),__p2,__p1); \ + DSUM(__p1,__a0b1,__c0,__p1); \ + __p2 += __c0; \ + DSUM(__p1,__a1b0,__c0,__p1); \ + __p2 += __c0; \ + DSUM(__p1,__a1b1<<H_EXP,__c0,__p1); \ + __p2 += __c0; \ + DSUM(__a1b1, (__p2<<H_EXP),__c0,__p2); \ + d1 = (__p2 & HI_MASK) | (__p1 >> H_EXP); \ + d0 = (__p1 << H_EXP) | (__p0 & LO_MASK); \ + } while(0) + +#define DMUL(a,b,d1,d0) do { \ + ErtsDigit _ds = 0; \ + DMULc(a,b,_ds,d0); \ + d1 = _ds; \ + } while(0) + +/* Calculate a*(Bb1 + b0) + d2 = a*b1B + a*b0 + d2 */ +#define D2MULc(a,b1,b0,d2,d1,d0) do { \ + DMULc(a, b0, d2, d0); \ + DMULc(a, b1, d2, d1); \ + } while(0) + +/* Calculate s in a = 2^s*a1 */ +/* NOTE since D2PF is used by other macros variables is prefixed bt __ */ +#if D_EXP == 64 +#define D2PF(a, s) do { \ + ErtsDigit __x = (a); \ + int __s = 0; \ + if (__x <= 0x00000000FFFFFFFF) { __s += 32; __x <<= 32; } \ + if (__x <= 0x0000FFFFFFFFFFFF) { __s += 16; __x <<= 16; } \ + if (__x <= 0x00FFFFFFFFFFFFFF) { __s += 8; __x <<= 8; } \ + if (__x <= 0x0FFFFFFFFFFFFFFF) { __s += 4; __x <<= 4; } \ + if (__x <= 0x3FFFFFFFFFFFFFFF) { __s += 2; __x <<= 2; } \ + if (__x <= 0x7FFFFFFFFFFFFFFF) { __s += 1; } \ + s = __s; \ + } while(0) +#elif D_EXP == 32 +#define D2PF(a, s) do { \ + ErtsDigit __x = (a); \ + int __s = 0; \ + if (__x <= 0x0000FFFF) { __s += 16; __x <<= 16; } \ + if (__x <= 0x00FFFFFF) { __s += 8; __x <<= 8; } \ + if (__x <= 0x0FFFFFFF) { __s += 4; __x <<= 4; } \ + if (__x <= 0x3FFFFFFF) { __s += 2; __x <<= 2; } \ + if (__x <= 0x7FFFFFFF) { __s += 1; } \ + s = __s; \ + } while(0) +#elif D_EXP == 16 +#define D2PF(a, s) do { \ + ErtsDigit __x = (a); \ + int __s = 0; \ + if (__x <= 0x00FF) { __s += 8; __x <<= 8; } \ + if (__x <= 0x0FFF) { __s += 4; __x <<= 4; } \ + if (__x <= 0x3FFF) { __s += 2; __x <<= 2; } \ + if (__x <= 0x7FFF) { __s += 1; } \ + s = __s; \ + } while(0) +#elif D_EXP == 8 +#define D2PF(a, s) do { \ + ErtsDigit __x = (a); \ + int __s = 0; \ + if (__x <= 0x0F) { __s += 4; __x <<= 4; } \ + if (__x <= 0x3F) { __s += 2; __x <<= 2; } \ + if (__x <= 0x7F) { __s += 1; } \ + s = _s; \ + } while(0) +#endif + +/* Calculate q = (a1B + a0) / b, assume a1 < b */ +#define DDIVREM(a1,a0,b,q,r) do { \ + ErtsDigit _a1 = (a1); \ + ErtsDigit _a0 = (a0); \ + ErtsDigit _b = (b); \ + ErtsHalfDigit _un1, _un0; \ + ErtsHalfDigit _vn1, _vn0; \ + ErtsDigit _q1, _q0; \ + ErtsDigit _un32, _un21, _un10; \ + ErtsDigit _rh; \ + Sint _s; \ + D2PF(_b, _s); \ + _b = _b << _s; \ + _vn1 = _b >> H_EXP; \ + _vn0 = _b & LO_MASK; \ + _un32 = (_a1 << _s) | ((_a0>>(D_EXP-_s)) & (-_s >> (D_EXP-1))); \ + _un10 = _a0 << _s; \ + _un1 = _un10 >> H_EXP; \ + _un0 = _un10 & LO_MASK; \ + _q1 = _un32/_vn1; \ + _rh = _un32 - _q1*_vn1; \ + while ((_q1 >= (DCONST(1)<<H_EXP))||(_q1*_vn0 > (_rh<<H_EXP)+_un1)) { \ + _q1--; \ + _rh += _vn1; \ + if (_rh >= (DCONST(1)<<H_EXP)) break; \ + } \ + _un21 = (_un32<<H_EXP) + _un1 - _q1*_b; \ + _q0 = _un21/_vn1; \ + _rh = _un21 - _q0*_vn1; \ + while ((_q0 >= (DCONST(1)<<H_EXP))||(_q0*_vn0 > ((_rh<<H_EXP)+_un0))) { \ + _q0--; \ + _rh += _vn1; \ + if (_rh >= (DCONST(1)<<H_EXP)) break; \ + } \ + r = ((_un21<<H_EXP) + _un0 - _q0*_b) >> _s; \ + q = (_q1<<H_EXP) + _q0; \ + } while(0) + +/* divide any a=(a1*B + a0) with b */ +#define DDIVREM2(a1,a0,b,q1,q0,r) do { \ + ErtsDigit __a1 = (a1); \ + ErtsDigit __b = (b); \ + q1 = __a1 / __b; \ + DDIVREM(__a1 % __b, (a0), __b, q0, r); \ + } while(0) + + +/* Calculate q = (a1B + a0) % b */ +#define DREM(a1,a0,b,r) do { \ + ErtsDigit __a1 = (a1); \ + ErtsDigit __b = (b); \ + ErtsDigit __q0; \ + DDIVREM((__a1 % __b), (a0), __b, __q0, r); \ + } while(0) + +#define DDIV(a1,a0,b,q) do { \ + ErtsDigit _tmp; \ + DDIVREM(a1,a0,b,q,_tmp); \ + } while(0) + + +/* Calculate q, r A = Bq+R when, assume A1 >= B */ +#if (SIZEOF_VOID_P == 8) +#define QUOT_LIM 0x7FFFFFFFFFFFFFFF +#else +#define QUOT_LIM 0x7FFFFFFF +#endif + +#define D2DIVREM(a1,a0,b1,b0,q0,r1,r0) do { \ + ErtsDigit _a1 = (a1); \ + ErtsDigit _a0 = (a0); \ + ErtsDigit _b1 = (b1); \ + ErtsDigit _b0 = (b0); \ + ErtsDigit _q = 0; \ + int _as = 1; \ + while(D2GTE(_a1,_a0,_b1,_b0)) { \ + ErtsDigit _q1; \ + ErtsDigit _t2=0, _t1, _t0; \ + if ((_b1 == 1) && (_a1 > 1)) \ + _q1 = _a1 / 2; \ + else if ((_a1 > QUOT_LIM) && (_b1 < _a1)) \ + _q1 = _a1/(_b1+1); \ + else \ + _q1 = _a1/_b1; \ + if (_as<0) \ + _q -= _q1; \ + else \ + _q += _q1; \ + D2MULc(_q1, _b1, _b0, _t2, _t1, _t0); \ + ASSERT(_t2 == 0); \ + if (D2GT(_t1,_t0,_a1,_a0)) { \ + D2SUB(_t1,_t0,_a1,_a0,_a1,_a0); \ + _as = -_as; \ + } \ + else { \ + D2SUB(_a1,_a0,_t1,_t0,_a1,_a0); \ + } \ + } \ + if (_as < 0) { \ + _q--; \ + D2SUB(_b1,_b0,_a1,_a0,_a1,_a0); \ + } \ + q0 = _q; \ + r1 = _a1; \ + r0 = _a0; \ + } while(0) + + +/* Calculate q, r A = Bq+R when assume B>0 */ +#define D2DIVREM_0(a1,a0,b1,b0,q1,q0,r1,r0) do { \ + ErtsDigit _a1 = (a1); \ + ErtsDigit _a0 = (a0); \ + ErtsDigit _b1 = (b1); \ + ErtsDigit _b0 = (b0); \ + if (D2EQ(_a1,_a0,0,0)) { \ + q1 = q0 = 0; \ + r1 = r0 = 0; \ + } \ + else { \ + ErtsDigit _res1 = 0; \ + ErtsDigit _res0 = 0; \ + ErtsDigit _d1 = 0; \ + ErtsDigit _d0 = 1; \ + ErtsDigit _e1 = (1 << (D_EXP-1)); \ + ErtsDigit _e0 = 0; \ + while(_e1 && !(_a1 & _e1)) \ + _e1 >>= 1; \ + if (_e1 == 0) { \ + _e0 = (1 << (D_EXP-1)); \ + while(_e0 && !(_a0 & _e0)) \ + _e0 >>= 1; \ + } \ + if (D2GT(_b1,_b0,0,0)) { \ + while(D2GT(_e1,_e0,_b1,_b0)) { \ + D2LSHIFT1(_b1,_b0); \ + D2LSHIFT1(_d1,_d0); \ + } \ + } \ + do { \ + if (!D2GT(_b1,_b0,_a1,_a0)) { \ + D2SUB(_a1,_a0, _b1, _b0, _a1, _a0); \ + D2ADD(_d1,_d0, _res1,_res0, _res1, _res0); \ + } \ + D2RSHIFT1(_b1,_b0); \ + D2RSHIFT1(_d1,_d0); \ + } while (!D2EQ(_d1,_d0,0,0)); \ + r1 = _a1; \ + r0 = _a0; \ + q1 = _res1; \ + q0 = _res0; \ + } \ + } while(0) + +#define DDIV2(a1,a0,b1,b0,q) do { \ + ErtsDigit _tmp_r1; \ + ErtsDigit _tmp_r0; \ + D2DIVREM(a1,a0,b1,b0,q,_tmp_r1,_tmp_r0); \ + } while(0) + +#endif + +/* +** compare two number vectors +*/ +static int I_comp(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl) +{ + if (xl < yl) + return -1; + else if (xl > yl) + return 1; + else { + if (x == y) + return 0; + x += (xl-1); + y += (yl-1); + while((xl > 0) && (*x == *y)) { + x--; + y--; + xl--; + } + if (xl == 0) + return 0; + return (*x < *y) ? -1 : 1; + } +} + +/* +** Add digits in x and y and store them in r +** assumption: (xl >= yl) +*/ +static dsize_t I_add(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, ErtsDigit* r) +{ + dsize_t sz = xl; + register ErtsDigit yr, xr; + register ErtsDigit c = 0; + + ASSERT(xl >= yl); + + xl -= yl; + do { + xr = *x++ + c; + yr = *y++; + c = (xr < c); + xr = yr + xr; + c += (xr < yr); + *r++ = xr; + } while(--yl); + + while(xl--) { + xr = *x++ + c; + c = (xr < c); + *r++ = xr; + } + if (c) { + *r = 1; + return sz+1; + } + return sz; +} +/* +** Add a digits in v1 and store result in vr +*/ +static dsize_t D_add(ErtsDigit* x, dsize_t xl, ErtsDigit c, ErtsDigit* r) +{ + dsize_t sz = xl; + register ErtsDigit xr; + + while(xl--) { + xr = *x++ + c; + c = (xr < c); + *r++ = xr; + } + if (c) { + *r = 1; + return sz+1; + } + return sz; +} + +/* +** Subtract digits v2 from v1 and store result in v3 +** Assert I_comp(x, xl, y, yl) >= 0 +** +*/ +static dsize_t I_sub(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + register ErtsDigit yr, xr; + register ErtsDigit c = 0; + + ASSERT(I_comp(x, xl, y, yl) >= 0); + + xl -= yl; + do { + yr = *y++ + c; + xr = *x++; + c = (yr < c); + yr = xr - yr; + c += (yr > xr); + *r++ = yr; + } while(--yl); + + while(xl--) { + xr = *x++; + yr = xr - c; + c = (yr > xr); + *r++ = yr; + } + do { + r--; + } while(*r == 0 && r != r0); + + return (r - r0) + 1; +} + +/* +** Subtract digit d from v1 and store result in vr +*/ +static dsize_t D_sub(ErtsDigit* x, dsize_t xl, ErtsDigit c, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + register ErtsDigit yr, xr; + + ASSERT(I_comp(x, xl, x, 1) >= 0); + + while(xl--) { + xr = *x++; + yr = xr - c; + c = (yr > xr); + *r++ = yr; + } + do { + r--; + } while(*r == 0 && r != r0); + + return (r - r0) + 1; +} + +/* +** subtract Z000...0 - y and store result in r, return new size +*/ +static dsize_t Z_sub(ErtsDigit* y, dsize_t yl, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + register ErtsDigit yr; + register ErtsDigit c = 0; + + while(yl--) { + yr = *y++ + c; + c = (yr < c); + yr = 0 - yr; + c += (yr > 0); + *r++ = yr; + } + do { + r--; + } while(*r == 0 && r != r0); + return (r - r0) + 1; +} + +/* +** Multiply digits in x with digits in y and store in r +** Assumption: digits in r must be 0 (upto the size of x) +*/ +static dsize_t I_mul(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + ErtsDigit* rt = r; + + while(xl--) { + ErtsDigit cp = 0; + ErtsDigit c = 0; + dsize_t n = yl; + ErtsDigit* yt = y; + ErtsDigit d; + ErtsDigit p; + + d = *x; + x++; + rt = r; + + switch(d) { + case 0: + rt = rt + n; + break; + case 1: + while(n--) { + DSUMc(*yt, *rt, c, p); + *rt++ = p; + yt++; + } + break; + case 2: + while(n--) { + p = *yt; + DSUMc(p, p, cp, p); + DSUMc(p, *rt, c, p); + *rt++ = p; + yt++; + } + break; + default: + while(n--) { + DMULc(d,*yt, cp, p); + DSUMc(p,*rt, c, p); + *rt++ = p; + yt++; + } + break; + } + *rt = c + cp; + r++; + } + if (*rt == 0) + return (rt - r0); + else + return (rt - r0) + 1; +} + +/* +** Square digits in x store in r (x & r may point into a common area) +** Assumption: x is destroyed if common area and digits in r are zero +** to the size of xl+1 +*/ + +static dsize_t I_sqr(ErtsDigit* x, dsize_t xl, ErtsDigit* r) +{ + ErtsDigit d_next = *x; + ErtsDigit d; + ErtsDigit* r0 = r; + ErtsDigit* s = r; + + if ((r + xl) == x) /* "Inline" operation */ + *x = 0; + x++; + + while(xl--) { + ErtsDigit* y = x; + ErtsDigit y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0; + ErtsDigit b0, b1; + ErtsDigit z0, z1, z2; + ErtsDigit t; + dsize_t y_l = xl; + + s = r; + d = d_next; + d_next = *x; + x++; + + DMUL(d, d, b1, b0); + DSUMc(*s, b0, y_3, t); + *s++ = t; + z1 = b1; + while(y_l--) { + DMUL(d, *y, b1, b0); + y++; + DSUMc(b0, b0, y_0, z0); + DSUMc(z0, z1, y_2, z2); + DSUMc(*s, z2, y_3, t); + *s++ = t; + DSUMc(b1, b1, y_1, z1); + } + z0 = y_0; + DSUMc(z0, z1, y_2, z2); + DSUMc(*s, z2, y_3, t); + *s = t; + if (xl != 0) { + s++; + t = (y_1+y_2+y_3); + *s = t; + r += 2; + } + else { + ASSERT((y_1+y_2+y_3) == 0); + } + } + if (*s == 0) + return (s - r0); + else + return (s - r0) + 1; +} + + +/* +** Multiply digits d with digits in x and store in r +*/ +static dsize_t D_mul(ErtsDigit* x, dsize_t xl, ErtsDigit d, ErtsDigit* r) +{ + ErtsDigit c = 0; + dsize_t rl = xl; + ErtsDigit p; + + switch(d) { + case 0: + ZERO_DIGITS(r, 1); + return 1; + case 1: + if (x != r) + MOVE_DIGITS(r, x, xl); + return xl; + case 2: + while(xl--) { + p = *x; + DSUMc(p, p, c, p); + *r++ = p; + x++; + } + break; + default: + while(xl--) { + DMULc(d, *x, c, p); + *r++ = p; + x++; + } + break; + } + if (c == 0) + return rl; + *r = c; + return rl+1; +} + +/* +** Multiply and subtract +** calculate r(i) = x(i) - d*y(i) +** assumption: xl = yl || xl == yl+1 +** +** Return size of r +** 0 means borrow +*/ +static dsize_t D_mulsub(ErtsDigit* x, dsize_t xl, ErtsDigit d, + ErtsDigit* y, dsize_t yl, ErtsDigit* r) +{ + ErtsDigit c = 0; + ErtsDigit b = 0; + ErtsDigit c0; + ErtsDigit* r0 = r; + ErtsDigit s; + + ASSERT(xl == yl || xl == yl+1); + + xl -= yl; + while(yl--) { + DMULc(d, *y, c, c0); + DSUBb(*x, c0, b, s); + *r++ = s; + x++; + y++; + } + if (xl == 0) { + if (c != 0 || b != 0) + return 0; + } + else { /* xl == 1 */ + DSUBb(*x, c, b, s); + *r++ = s; + } + if (b != 0) return 0; + + do { + r--; + } while(*r == 0 && r != r0); + return (r - r0) + 1; +} + +/* +** Divide digits in x with a digit, +** quotient is returned in q and remainder digit in r +** x and q may be equal +*/ +static dsize_t D_div(ErtsDigit* x, dsize_t xl, ErtsDigit d, ErtsDigit* q, ErtsDigit* r) +{ + ErtsDigit* xp = x + (xl-1); + ErtsDigit* qp = q + (xl-1); + dsize_t qsz = xl; + ErtsDigit a1; + + a1 = *xp; + xp--; + + if (d > a1) { + if (xl == 1) { + *r = a1; + *qp = 0; + return 1; + } + qsz--; + qp--; + } + + do { + ErtsDigit q0, a0, b1, b0, b; + + if (d > a1) { + a0 = *xp; + xp--; + } + else { + a0 = a1; a1 = 0; + } + DDIV(a1, a0, d, q0); + DMUL(d, q0, b1, b0); + DSUB(a0,b0, b, a1); + *qp = q0; + qp--; + } while (xp >= x); + + *r = a1; + return qsz; +} + +/* +** Divide digits in x with digits in y and return qutient in q +** and remainder in r +** assume that integer(x) > integer(y) +** Return remainder in x (length int rl) +** Return quotient size +*/ + +static dsize_t I_div(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, + ErtsDigit* q, ErtsDigit* r, dsize_t* rlp) +{ + ErtsDigit* rp; + ErtsDigit* qp; + ErtsDigit b1 = y[yl-1]; + ErtsDigit b2 = y[yl-2]; + ErtsDigit a1; + ErtsDigit a2; + int r_signed = 0; + dsize_t ql; + dsize_t rl; + + if (x != r) + MOVE_DIGITS(r, x, xl); + rp = r + (xl-yl); + rl = xl; + + ZERO_DIGITS(q, xl-yl+1); + qp = q + (xl-yl); + ql = 0; + + /* Adjust length */ + a1 = rp[yl-1]; + a2 = rp[yl-2]; + if (b1 < a1 || (b1 == a1 && b2 <= a2)) + ql = 1; + + do { + ErtsDigit q0; + dsize_t nsz = yl; + dsize_t nnsz; + + a1 = rp[yl-1]; + a2 = rp[yl-2]; + + if (b1 < a1) + DDIV2(a1,a2,b1,b2,q0); + else if (b1 > a1) { + DDIV(a1,a2,b1,q0); + nsz++; + rp--; + qp--; + ql++; + } + else { /* (b1 == a1) */ + if (b2 <= a2) + q0 = 1; + else { + q0 = D_MASK; + nsz++; + rp--; + qp--; + ql++; + } + } + + if (r_signed) + ql = D_sub(qp, ql, q0, qp); + else + ql = D_add(qp, ql, q0, qp); + + if ((nnsz = D_mulsub(rp, nsz, q0, y, yl, rp)) == 0) { + nnsz = Z_sub(r, rl, r); + if (nsz > (rl-nnsz)) + nnsz = nsz - (rl-nnsz); + else + nnsz = 1; + r_signed = !r_signed; + } + + if ((nnsz == 1) && (*rp == 0)) + nnsz = 0; + rp = rp - (yl-nnsz); + rl -= (nsz-nnsz); + qp = qp - (yl-nnsz); + ql += (yl-nnsz); + } while (I_comp(r, rl, y, yl) >= 0); + + ql -= (q - qp); + qp = q; + + if (rl == 0) + rl = 1; + + while(rl > 1 && r[rl-1] == 0) /* Remove "trailing zeroes" */ + --rl; + + if (r_signed && (rl > 1 || *r != 0)) { + rl = I_sub(y, yl, r, rl, r); + ql = D_sub(qp, ql, 1, qp); + } + + *rlp = rl; + return ql; +} + +/* +** Remainder of digits in x and a digit d +*/ +static ErtsDigit D_rem(ErtsDigit* x, dsize_t xl, ErtsDigit d) +{ + ErtsDigit rem = 0; + + x += (xl-1); + do { + if (rem != 0) + DREM(rem, *x, d, rem); + else + DREM(0, *x, d, rem); + x--; + xl--; + } while(xl > 0); + return rem; +} + +/* +** Remainder of x and y +** +** Assumtions: xl >= yl, yl > 1 +** r must contain at least xl number of digits +*/ +static dsize_t I_rem(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, ErtsDigit* r) +{ + ErtsDigit* rp; + ErtsDigit b1 = y[yl-1]; + ErtsDigit b2 = y[yl-2]; + ErtsDigit a1; + ErtsDigit a2; + int r_signed = 0; + dsize_t rl; + + if (x != r) + MOVE_DIGITS(r, x, xl); + rp = r + (xl-yl); + rl = xl; + + do { + ErtsDigit q0; + dsize_t nsz = yl; + dsize_t nnsz; + + a1 = rp[yl-1]; + a2 = rp[yl-2]; + + if (b1 < a1) + DDIV2(a1,a2,b1,b2,q0); + else if (b1 > a1) { + DDIV(a1,a2,b1,q0); + nsz++; + rp--; + } + else { /* (b1 == a1) */ + if (b2 <= a2) + q0 = 1; + else { + q0 = D_MASK; + nsz++; + rp--; + } + } + + if ((nnsz = D_mulsub(rp, nsz, q0, y, yl, rp)) == 0) { + nnsz = Z_sub(r, rl, r); + if (nsz > (rl-nnsz)) + nnsz = nsz - (rl-nnsz); + else + nnsz = 1; + r_signed = !r_signed; + } + + if (nnsz == 1 && *rp == 0) + nnsz = 0; + + rp = rp - (yl-nnsz); + rl -= (nsz-nnsz); + } while (I_comp(r, rl, y, yl) >= 0); + + if (rl == 0) + rl = 1; + + while(rl > 1 && r[rl-1] == 0) /* Remove "trailing zeroes" */ + --rl; + + if (r_signed && (rl > 1 || *r != 0)) + rl = I_sub(y, yl, r, rl, r); + return rl; +} + +/* +** Remove trailing digits from bitwise operations +*/ +static dsize_t I_btrail(ErtsDigit* r0, ErtsDigit* r, short sign) +{ + /* convert negative numbers to one complement */ + if (sign) { + dsize_t rl; + ErtsDigit d; + + /* 1 remove all 0xffff words */ + do { + r--; + } while(((d = *r) == D_MASK) && (r != r0)); + + /* 2 complement high digit */ + if (d == D_MASK) + *r = 0; + else { + ErtsDigit prev_mask = 0; + ErtsDigit mask = (DCONST(1) << (D_EXP-1)); + + while((d & mask) == mask) { + prev_mask = mask; + mask = (prev_mask >> 1) | (DCONST(1)<<(D_EXP-1)); + } + *r = ~d & ~prev_mask; + } + rl = (r - r0) + 1; + while(r != r0) { + r--; + *r = ~*r; + } + return D_add(r0, rl, 1, r0); + } + + do { + r--; + } while(*r == 0 && r != r0); + return (r - r0) + 1; +} + +/* +** Bitwise and +*/ +static dsize_t I_band(ErtsDigit* x, dsize_t xl, short xsgn, + ErtsDigit* y, dsize_t yl, short ysgn, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + short sign = xsgn && ysgn; + + ASSERT(xl >= yl); + + xl -= yl; + + if (!xsgn) { + if (!ysgn) { + while(yl--) + *r++ = *x++ & *y++; + } + else { + ErtsDigit b; + ErtsDigit c; + + DSUB(*y,1,b,c); + *r++ = *x++ & ~c; + y++; + yl--; + while(yl--) { + DSUBb(*y,0,b,c); + *r++ = *x++ & ~c; + y++; + } + while (xl--) { + *r++ = *x++; + } + } + } + else { + if (!ysgn) { + ErtsDigit b; + ErtsDigit c; + + DSUB(*x,1,b,c); + *r = ~c & *y; + x++; y++; r++; + yl--; + while(yl--) { + DSUBb(*x,0,b,c); + *r++ = ~c & *y++; + x++; + } + } + else { + ErtsDigit b1, b2; + ErtsDigit c1, c2; + + DSUB(*x,1,b1,c1); + DSUB(*y,1,b2,c2); + *r++ = ~c1 & ~c2; + x++; y++; + yl--; + while(yl--) { + DSUBb(*x,0,b1,c1); + DSUBb(*y,0,b2,c2); + *r++ = ~c1 & ~c2; + x++; y++; + } + while(xl--) + *r++ = ~*x++; + } + } + return I_btrail(r0, r, sign); +} + +/* + * Bitwise 'or'. + */ +static dsize_t +I_bor(ErtsDigit* x, dsize_t xl, short xsgn, ErtsDigit* y, + dsize_t yl, short ysgn, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + short sign = xsgn || ysgn; + + ASSERT(xl >= yl); + + xl -= yl; + + if (!xsgn) { + if (!ysgn) { + while(yl--) + *r++ = *x++ | *y++; + while(xl--) + *r++ = *x++; + } + else { + ErtsDigit b; + ErtsDigit c; + + DSUB(*y,1,b,c); + *r++ = *x++ | ~c; + y++; + yl--; + while(yl--) { + DSUBb(*y,0,b,c); + *r++ = *x++ | ~c; + y++; + } + } + } + else { + if (!ysgn) { + ErtsDigit b; + ErtsDigit c; + + DSUB(*x,1,b,c); + *r++ = ~c | *y++; + x++; + yl--; + while(yl--) { + DSUBb(*x,0,b,c); + *r++ = ~c | *y++; + x++; + } + while(xl--) { + DSUBb(*x,0,b,c); + *r++ = ~c; + x++; + } + } + else { + ErtsDigit b1, b2; + ErtsDigit c1, c2; + + DSUB(*x,1,b1,c1); + DSUB(*y,1,b2,c2); + *r++ = ~c1 | ~c2; + x++; y++; + yl--; + while(yl--) { + DSUBb(*x,0,b1,c1); + DSUBb(*y,0,b2,c2); + *r++ = ~c1 | ~c2; + x++; y++; + } + } + } + return I_btrail(r0, r, sign); +} + +/* +** Bitwise xor +*/ +static dsize_t I_bxor(ErtsDigit* x, dsize_t xl, short xsgn, + ErtsDigit* y, dsize_t yl, short ysgn, ErtsDigit* r) +{ + ErtsDigit* r0 = r; + short sign = xsgn != ysgn; + + ASSERT(xl >= yl); + + xl -= yl; + + if (!xsgn) { + if (!ysgn) { + while(yl--) + *r++ = *x++ ^ *y++; + while(xl--) + *r++ = *x++; + } + else { + ErtsDigit b; + ErtsDigit c; + + DSUB(*y,1,b,c); + *r++ = *x++ ^ ~c; + y++; + yl--; + while(yl--) { + DSUBb(*y,0,b,c); + *r++ = *x++ ^ ~c; + y++; + } + while(xl--) + *r++ = ~*x++; + } + } + else { + if (!ysgn) { + ErtsDigit b; + ErtsDigit c; + + DSUB(*x,1,b,c); + *r++ = ~c ^ *y++; + x++; + yl--; + while(yl--) { + DSUBb(*x,0,b,c); + *r++ = ~c ^ *y++; + x++; + } + while(xl--) + *r++ = ~*x++; + } + else { + ErtsDigit b1, b2; + ErtsDigit c1, c2; + + DSUB(*x,1,b1,c1); + DSUB(*y,1,b2,c2); + *r++ = ~c1 ^ ~c2; + x++; y++; + yl--; + while(yl--) { + DSUBb(*x,0,b1,c1); + DSUBb(*y,0,b2,c2); + *r++ = ~c1 ^ ~c2; + x++; y++; + } + while(xl--) { + *r++ = *x++; + } + } + } + return I_btrail(r0, r, sign); +} + +/* +** Bitwise not simulated as +** bnot -X == (X - 1) +** bnot +X == -(X + 1) +*/ +static dsize_t I_bnot(ErtsDigit* x, dsize_t xl, short xsgn, ErtsDigit* r) +{ + if (xsgn) + return D_add(x, xl, 1, r); + else + return D_sub(x, xl, 1, r); +} + +/* +** Arithmetic left shift or right +*/ +static dsize_t I_lshift(ErtsDigit* x, dsize_t xl, Sint y, + short sign, ErtsDigit* r) +{ + if (y == 0) { + MOVE_DIGITS(r, x, xl); + return xl; + } + else if (xl == 1 && *x == 0) { + *r = 0; + return 1; + } + else { + long ay = (y < 0) ? -y : y; + int bw = ay / D_EXP; + int sw = ay % D_EXP; + dsize_t rl; + ErtsDigit a1=0; + ErtsDigit a0=0; + + if (y > 0) { /* shift left */ + rl = xl + bw + 1; + + while(bw--) + *r++ = 0; + if (sw) { // NOTE! x >> 32 is not = 0! + while(xl--) { + a0 = (*x << sw) | a1; + a1 = (*x >> (D_EXP - sw)); + *r++ = a0; + x++; + } + } + else { + while(xl--) { + *r++ = *x++; + } + } + if (a1 == 0) + return rl-1; + *r = a1; + return rl; + } + else { /* shift right */ + ErtsDigit* r0 = r; + int add_one = 0; + + if (xl <= bw) { + if (sign) + *r = 1; + else + *r = 0; + return 1; + } + + if (sign) { + int zl = bw; + ErtsDigit* z = x; + + while(zl--) { + if (*z != 0) { + add_one = 1; + break; + } + z++; + } + } + + rl = xl - bw; + x += (xl-1); + r += (rl-1); + xl -= bw; + if (sw) { // NOTE! x >> 32 is not = 0! + while(xl--) { + a1 = (*x >> sw) | a0; + a0 = (*x << (D_EXP-sw)); + *r-- = a1; + x--; + } + } + else { + while(xl--) { + *r-- = *x--; + } + } + + if (sign && (a0 != 0)) + add_one = 1; + + if (r[rl] == 0) { + if (rl == 1) { + if (sign) + r[1] = 1; + return 1; + } + rl--; + } + if (add_one) + return D_add(r0, rl, 1, r0); + return rl; + } + } +} + +/* +** Return log(x)/log(2) +*/ +static int I_lg(ErtsDigit* x, dsize_t xl) +{ + dsize_t sz = xl - 1; + ErtsDigit d = x[sz]; + + sz *= D_EXP; + while(d != 0) { + d >>= 1; + sz++; + } + return sz - 1; +} + +/* +** Create bigint on heap if necessary. Like the previously existing +** make_small_or_big(), except for a HAlloc() instead of an +** ArithAlloc(). +** NOTE: Only use erts_make_integer(), when order of heap fragments is +** guaranteed to be correct. +*/ +Eterm +erts_make_integer(Uint x, Process *p) +{ + Eterm* hp; + if (IS_USMALL(0,x)) + return make_small(x); + else { + hp = HAlloc(p, BIG_UINT_HEAP_SIZE); + return uint_to_big(x,hp); + } +} + +/* +** convert Uint to bigint +** (must only be used if x is to big to be stored as a small) +*/ +Eterm uint_to_big(Uint x, Eterm *y) +{ + *y = make_pos_bignum_header(1); + BIG_DIGIT(y, 0) = x; + return make_big(y); +} + + +/* +** convert signed int to bigint +*/ +Eterm small_to_big(Sint x, Eterm *y) +{ + if (x >= 0) { + *y = make_pos_bignum_header(1); + } else { + x = -x; + *y = make_neg_bignum_header(1); + } + BIG_DIGIT(y, 0) = x; + return make_big(y); +} + + +Eterm erts_uint64_to_big(Uint64 x, Eterm **hpp) +{ + Eterm *hp = *hpp; +#ifdef ARCH_32 + if (x >= (((Uint64) 1) << 32)) { + *hp = make_pos_bignum_header(2); + BIG_DIGIT(hp, 0) = (Uint) (x & ((Uint) 0xffffffff)); + BIG_DIGIT(hp, 1) = (Uint) ((x >> 32) & ((Uint) 0xffffffff)); + *hpp += 2; + } + else +#endif + { + *hp = make_pos_bignum_header(1); + BIG_DIGIT(hp, 0) = (Uint) x; + *hpp += 1; + } + return make_big(hp); +} + +Eterm erts_sint64_to_big(Sint64 x, Eterm **hpp) +{ + Eterm *hp = *hpp; + int neg; + if (x >= 0) + neg = 0; + else { + neg = 1; + x = -x; + } +#ifdef ARCH_32 + if (x >= (((Uint64) 1) << 32)) { + if (neg) + *hp = make_neg_bignum_header(2); + else + *hp = make_pos_bignum_header(2); + BIG_DIGIT(hp, 0) = (Uint) (x & ((Uint) 0xffffffff)); + BIG_DIGIT(hp, 1) = (Uint) ((x >> 32) & ((Uint) 0xffffffff)); + *hpp += 2; + } + else +#endif + { + if (neg) + *hp = make_neg_bignum_header(1); + else + *hp = make_pos_bignum_header(1); + BIG_DIGIT(hp, 0) = (Uint) x; + *hpp += 1; + } + return make_big(hp); +} + +/* +** Convert a bignum to a double float +*/ +int +big_to_double(Eterm x, double* resp) +{ + double d = 0.0; + Eterm* xp = big_val(x); + dsize_t xl = BIG_SIZE(xp); + ErtsDigit* s = BIG_V(xp) + xl; + short xsgn = BIG_SIGN(xp); + double dbase = ((double)(D_MASK)+1); +#ifndef NO_FPE_SIGNALS + volatile unsigned long *fpexnp = erts_get_current_fp_exception(); +#endif + __ERTS_SAVE_FP_EXCEPTION(fpexnp); + + __ERTS_FP_CHECK_INIT(fpexnp); + while (xl--) { + d = d * dbase + *--s; + + __ERTS_FP_ERROR(fpexnp, d, __ERTS_RESTORE_FP_EXCEPTION(fpexnp); return -1); + } + + *resp = xsgn ? -d : d; + __ERTS_FP_ERROR(fpexnp,*resp,;); + __ERTS_RESTORE_FP_EXCEPTION(fpexnp); + return 0; +} + + +/* + ** Estimate the number of decimal digits (include sign) + */ +int big_decimal_estimate(Eterm x) +{ + Eterm* xp = big_val(x); + int lg = I_lg(BIG_V(xp), BIG_SIZE(xp)); + int lg10 = ((lg+1)*28/93)+1; + + if (BIG_SIGN(xp)) lg10++; /* add sign */ + return lg10+1; /* add null */ +} + +/* +** Convert a bignum into a string of decimal numbers +*/ + +static void write_big(Eterm x, void (*write_func)(void *, char), void *arg) +{ + Eterm* xp = big_val(x); + ErtsDigit* dx = BIG_V(xp); + dsize_t xl = BIG_SIZE(xp); + short sign = BIG_SIGN(xp); + ErtsDigit rem; + + if (xl == 1 && *dx < D_DECIMAL_BASE) { + rem = *dx; + if (rem == 0) + (*write_func)(arg, '0'); + else { + while(rem) { + (*write_func)(arg, (rem % 10) + '0'); + rem /= 10; + } + } + } + else { + ErtsDigit* tmp = (ErtsDigit*) erts_alloc(ERTS_ALC_T_TMP, + sizeof(ErtsDigit)*xl); + dsize_t tmpl = xl; + + MOVE_DIGITS(tmp, dx, xl); + + while(1) { + tmpl = D_div(tmp, tmpl, D_DECIMAL_BASE, tmp, &rem); + if (tmpl == 1 && *tmp == 0) { + while(rem) { + (*write_func)(arg, (rem % 10)+'0'); + rem /= 10; + } + break; + } + else { + int i = D_DECIMAL_EXP; + while(i--) { + (*write_func)(arg, (rem % 10)+'0'); + rem /= 10; + } + } + } + erts_free(ERTS_ALC_T_TMP, (void *) tmp); + } + + if (sign) + (*write_func)(arg, '-'); +} + +struct big_list__ { + Eterm *hp; + Eterm res; +}; + +static void +write_list(void *arg, char c) +{ + struct big_list__ *blp = (struct big_list__ *) arg; + blp->res = CONS(blp->hp, make_small(c), blp->res); + blp->hp += 2; +} + +Eterm erts_big_to_list(Eterm x, Eterm **hpp) +{ + struct big_list__ bl; + bl.hp = *hpp; + bl.res = NIL; + write_big(x, write_list, (void *) &bl); + *hpp = bl.hp; + return bl.res; +} + +static void +write_string(void *arg, char c) +{ + *(--(*((char **) arg))) = c; +} + +char *erts_big_to_string(Eterm x, char *buf, Uint buf_sz) +{ + char *big_str = buf + buf_sz - 1; + *big_str = '\0'; + write_big(x, write_string, (void *) &big_str); + ASSERT(buf <= big_str && big_str <= buf + buf_sz - 1); + return big_str; +} + +/* +** Normalize a bignum given thing pointer length in digits and a sign +** patch zero if odd length +*/ +static Eterm big_norm(Eterm *x, dsize_t xl, short sign) +{ + Uint arity; + + if (xl == 1) { + Uint y = BIG_DIGIT(x, 0); + + if (D_EXP < SMALL_BITS || IS_USMALL(sign, y)) { + if (sign) + return make_small(-((Sint)y)); + else + return make_small(y); + } + } + + /* __alpha__: This was fixed */ + if ((arity = BIG_NEED_SIZE(xl)-1) > BIG_ARITY_MAX) + return NIL; /* signal error (too big) */ + + if (sign) { + *x = make_neg_bignum_header(arity); + } + else { + *x = make_pos_bignum_header(arity); + } + return make_big(x); +} + +/* +** Compare bignums +*/ +int big_comp(Eterm x, Eterm y) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + if (BIG_SIGN(xp) == BIG_SIGN(yp)) { + int c = I_comp(BIG_V(xp), BIG_SIZE(xp), BIG_V(yp), BIG_SIZE(yp)); + if (BIG_SIGN(xp)) + return -c; + else + return c; + } + else + return BIG_SIGN(xp) ? -1 : 1; +} + +/* +** Unsigned compare +*/ +int big_ucomp(Eterm x, Eterm y) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + return I_comp(BIG_V(xp), BIG_SIZE(xp), BIG_V(yp), BIG_SIZE(yp)); +} + +/* +** Return number of bytes in the bignum +*/ +dsize_t big_bytes(Eterm x) +{ + Eterm* xp = big_val(x); + dsize_t sz = BIG_SIZE(xp); + ErtsDigit d = BIG_DIGIT(xp, sz-1); + + sz = (sz-1) * sizeof(ErtsDigit); + while (d != 0) { + ++sz; + d >>= 8; + } + return sz; +} + +/* +** Load a bignum from bytes +** xsz is the number of bytes in xp +*/ +Eterm bytes_to_big(byte *xp, dsize_t xsz, int xsgn, Eterm *r) +{ + ErtsDigit* rwp = BIG_V(r); + dsize_t rsz = 0; + ErtsDigit d; + int i; + + while(xsz >= sizeof(ErtsDigit)) { + d = 0; + for(i = sizeof(ErtsDigit); --i >= 0;) + d = (d << 8) | xp[i]; + *rwp = d; + rwp++; + xsz -= sizeof(ErtsDigit); + xp += sizeof(ErtsDigit); + rsz++; + } + + if (xsz > 0) { + d = 0; + for(i = xsz; --i >= 0;) + d = (d << 8) | xp[i]; + *rwp = d; + rwp++; + rsz++; + } + return big_norm(r, rsz, (short) xsgn); +} + +/* +** Store digits in the array of bytes pointed to by p +*/ +byte* big_to_bytes(Eterm x, byte *p) +{ + ErtsDigit* xr = big_v(x); + dsize_t xl = big_size(x); + ErtsDigit d; + int i; + + while(xl > 1) { + d = *xr; + xr++; + for(i = 0; i < sizeof(ErtsDigit); ++i) { + p[i] = d & 0xff; + d >>= 8; + } + p += sizeof(ErtsDigit); + xl--; + } + d = *xr; + do { + *p++ = d & 0xff; + d >>= 8; + } while (d != 0); + return p; +} + +/* + * Converts a positive term (small or bignum) to an Uint. + * + * Fails returning 0 if the term is neither a small nor a bignum, + * if it's negative, or the big number does not fit in an Uint; + * in addition the error reason, BADARG or SYSTEM_LIMIT, will be + * stored in *up. + * + * Otherwise returns a non-zero value and the converted number + * in *up. + */ + +int +term_to_Uint(Eterm term, Uint *up) +{ + if (is_small(term)) { + Sint i = signed_val(term); + if (i < 0) { + *up = BADARG; + return 0; + } + *up = (Uint) i; + return 1; + } else if (is_big(term)) { + ErtsDigit* xr = big_v(term); + dsize_t xl = big_size(term); + Uint uval = 0; + int n = 0; + + if (big_sign(term)) { + *up = BADARG; + return 0; + } else if (xl*D_EXP > sizeof(Uint)*8) { + *up = SYSTEM_LIMIT; + return 0; + } + while (xl-- > 0) { + uval |= ((Uint)(*xr++)) << n; + n += D_EXP; + } + *up = uval; + return 1; + } else { + *up = BADARG; + return 0; + } +} + +int term_to_Sint(Eterm term, Sint *sp) +{ + if (is_small(term)) { + *sp = signed_val(term); + return 1; + } else if (is_big(term)) { + ErtsDigit* xr = big_v(term); + dsize_t xl = big_size(term); + int sign = big_sign(term); + Uint uval = 0; + int n = 0; + + if (xl*D_EXP > sizeof(Uint)*8) { + return 0; + } + while (xl-- > 0) { + uval |= ((Uint)(*xr++)) << n; + n += D_EXP; + } + if (sign) { + uval = -uval; + if ((Sint)uval > 0) + return 0; + } else { + if ((Sint)uval < 0) + return 0; + } + *sp = uval; + return 1; + } else { + return 0; + } +} + +/* +** Add and subtract +*/ +static Eterm B_plus_minus(ErtsDigit *x, dsize_t xl, short xsgn, + ErtsDigit *y, dsize_t yl, short ysgn, Eterm *r) +{ + if (xsgn == ysgn) { + if (xl > yl) + return big_norm(r, I_add(x,xl,y,yl,BIG_V(r)), xsgn); + else + return big_norm(r, I_add(y,yl,x,xl,BIG_V(r)), xsgn); + } + else { + int comp = I_comp(x, xl, y, yl); + if (comp == 0) + return make_small(0); + else if (comp > 0) + return big_norm(r, I_sub(x,xl,y,yl,BIG_V(r)), xsgn); + else + return big_norm(r, I_sub(y,yl,x,xl,BIG_V(r)), ysgn); + } +} + +/* +** Add bignums +*/ +Eterm big_plus(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + return B_plus_minus(BIG_V(xp),BIG_SIZE(xp),(short) BIG_SIGN(xp), + BIG_V(yp),BIG_SIZE(yp),(short) BIG_SIGN(yp), r); +} + +/* +** Subtract bignums +*/ + +Eterm big_minus(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + return B_plus_minus(BIG_V(xp),BIG_SIZE(xp),(short) BIG_SIGN(xp), + BIG_V(yp),BIG_SIZE(yp),(short) !BIG_SIGN(yp), r); +} + +/* +** Subtract a digit from big number +*/ +Eterm big_minus_small(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + + if (BIG_SIGN(xp)) + return big_norm(r, D_add(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, BIG_V(r)), + (short) BIG_SIGN(xp)); + else + return big_norm(r, D_sub(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, BIG_V(r)), + (short) BIG_SIGN(xp)); +} + +/* +** Multiply smallnums +*/ + +Eterm small_times(Sint x, Sint y, Eterm *r) +{ + short sign = (x<0) != (y<0); + ErtsDigit xu = (x > 0) ? x : -x; + ErtsDigit yu = (y > 0) ? y : -y; + ErtsDigit d1=0; + ErtsDigit d0; + Uint arity; + + DMULc(xu, yu, d1, d0); + + if (!d1 && ((D_EXP < SMALL_BITS) || IS_USMALL(sign, d0))) { + if (sign) + return make_small(-((Sint)d0)); + else + return make_small(d0); + } + + BIG_DIGIT(r,0) = d0; + arity = d1 ? 2 : 1; + if (sign) + *r = make_neg_bignum_header(arity); + else + *r = make_pos_bignum_header(arity); + if (d1) + BIG_DIGIT(r,1) = d1; + return make_big(r); +} + +/* +** Multiply bignums +*/ + +Eterm big_times(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + short sign = BIG_SIGN(xp) != BIG_SIGN(yp); + dsize_t xsz = BIG_SIZE(xp); + dsize_t ysz = BIG_SIZE(yp); + dsize_t rsz; + + if (ysz == 1) + rsz = D_mul(BIG_V(xp), xsz, BIG_DIGIT(yp, 0), BIG_V(r)); + else if (xsz == 1) + rsz = D_mul(BIG_V(yp), ysz, BIG_DIGIT(xp, 0), BIG_V(r)); + else if (xp == yp) { + ZERO_DIGITS(BIG_V(r), xsz+1); + rsz = I_sqr(BIG_V(xp), xsz, BIG_V(r)); + } + else if (xsz >= ysz) { + ZERO_DIGITS(BIG_V(r), xsz); + rsz = I_mul(BIG_V(xp), xsz, BIG_V(yp), ysz, BIG_V(r)); + } + else { + ZERO_DIGITS(BIG_V(r), ysz); + rsz = I_mul(BIG_V(yp), ysz, BIG_V(xp), xsz, BIG_V(r)); + } + return big_norm(r, rsz, sign); +} + + +/* +** Divide bignums +*/ + +Eterm big_div(Eterm x, Eterm y, Eterm *q) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + short sign = BIG_SIGN(xp) != BIG_SIGN(yp); + dsize_t xsz = BIG_SIZE(xp); + dsize_t ysz = BIG_SIZE(yp); + dsize_t qsz; + + if (ysz == 1) { + ErtsDigit rem; + qsz = D_div(BIG_V(xp), xsz, BIG_DIGIT(yp,0), BIG_V(q), &rem); + } + else { + Eterm* remp; + dsize_t rem_sz; + + qsz = xsz - ysz + 1; + remp = q + BIG_NEED_SIZE(qsz); + qsz = I_div(BIG_V(xp), xsz, BIG_V(yp), ysz, BIG_V(q), BIG_V(remp), + &rem_sz); + } + return big_norm(q, qsz, sign); +} + +/* +** Remainder +*/ +Eterm big_rem(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + short sign = BIG_SIGN(xp); + dsize_t xsz = BIG_SIZE(xp); + dsize_t ysz = BIG_SIZE(yp); + + if (ysz == 1) { + ErtsDigit rem; + rem = D_rem(BIG_V(xp), xsz, BIG_DIGIT(yp,0)); + if (IS_USMALL(sign, rem)) { + if (sign) + return make_small(-(Sint)rem); + else + return make_small(rem); + } + else { + if (sign) + *r = make_neg_bignum_header(1); + else + *r = make_pos_bignum_header(1); + BIG_DIGIT(r, 0) = rem; + return make_big(r); + } + } + else { + dsize_t rsz = I_rem(BIG_V(xp), xsz, BIG_V(yp), ysz, BIG_V(r)); + return big_norm(r, rsz, sign); + } +} + +Eterm big_neg(Eterm x, Eterm *r) +{ + Eterm* xp = big_val(x); + dsize_t xsz = BIG_SIZE(xp); + short xsgn = BIG_SIGN(xp); + + MOVE_DIGITS(BIG_V(r), BIG_V(xp), xsz); + return big_norm(r, xsz, (short) !xsgn); +} + +Eterm big_band(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + + short xsgn = BIG_SIGN(xp); + short ysgn = BIG_SIGN(yp); + short sign = xsgn && ysgn; + dsize_t xsz = BIG_SIZE(xp); + dsize_t ysz = BIG_SIZE(yp); + + if (xsz >= ysz) + return big_norm(r,I_band(BIG_V(xp),xsz,xsgn, + BIG_V(yp),ysz,ysgn, + BIG_V(r)),sign); + else + return big_norm(r,I_band(BIG_V(yp),ysz,ysgn, + BIG_V(xp),xsz,xsgn, + BIG_V(r)),sign); +} + + +Eterm big_bor(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + short xsgn = BIG_SIGN(xp); + short ysgn = BIG_SIGN(yp); + short sign = (xsgn || ysgn); + dsize_t xsz = BIG_SIZE(xp); + dsize_t ysz = BIG_SIZE(yp); + + if (xsz >= ysz) + return big_norm(r,I_bor(BIG_V(xp),xsz,xsgn, + BIG_V(yp),ysz,ysgn, + BIG_V(r)),sign); + else + return big_norm(r,I_bor(BIG_V(yp),ysz,ysgn, + BIG_V(xp),xsz,xsgn, + BIG_V(r)),sign); +} + + +Eterm big_bxor(Eterm x, Eterm y, Eterm *r) +{ + Eterm* xp = big_val(x); + Eterm* yp = big_val(y); + short xsgn = BIG_SIGN(xp); + short ysgn = BIG_SIGN(yp); + short sign = (xsgn != ysgn); + dsize_t xsz = BIG_SIZE(xp); + dsize_t ysz = BIG_SIZE(yp); + + if (xsz >= ysz) + return big_norm(r,I_bxor(BIG_V(xp),xsz,xsgn, + BIG_V(yp),ysz,ysgn, + BIG_V(r)),sign); + else + return big_norm(r,I_bxor(BIG_V(yp),ysz,ysgn, + BIG_V(xp),xsz,xsgn, + BIG_V(r)),sign); +} + +Eterm big_bnot(Eterm x, Eterm *r) +{ + Eterm* xp = big_val(x); + short sign = !BIG_SIGN(xp); + dsize_t xsz = BIG_SIZE(xp); + + return big_norm(r, I_bnot(BIG_V(xp), xsz, sign, BIG_V(r)), sign); +} + +Eterm big_lshift(Eterm x, Sint y, Eterm *r) +{ + Eterm* xp = big_val(x); + short sign = BIG_SIGN(xp); + dsize_t xsz = BIG_SIZE(xp); + + return big_norm(r, I_lshift(BIG_V(xp), xsz, y, sign, BIG_V(r)), sign); +} + + +/* add unsigned small int y to x */ + +Eterm big_plus_small(Eterm x, Uint y, Eterm *r) +{ + Eterm* xp = big_val(x); + + if (BIG_SIGN(xp)) + return big_norm(r, D_sub(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, + BIG_V(r)), (short) BIG_SIGN(xp)); + else + return big_norm(r, D_add(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, + BIG_V(r)), (short) BIG_SIGN(xp)); +} + +Eterm big_times_small(Eterm x, Uint y, Eterm *r) +{ + Eterm* xp = big_val(x); + + return big_norm(r, D_mul(BIG_V(xp),BIG_SIZE(xp), (ErtsDigit) y, + BIG_V(r)), (short) BIG_SIGN(xp)); +} + +/* +** Expects the big to fit. +*/ +Uint32 big_to_uint32(Eterm b) +{ + Uint u; + if (!term_to_Uint(b, &u)) { + ASSERT(0); + return 0; + } + return u; +} + +/* + * Check if a fixnum or bignum equals 2^32. + */ +int term_equals_2pow32(Eterm x) +{ + if (sizeof(Uint) > 4) { + Uint u; + if (!term_to_Uint(x, &u)) + return 0; + return (u & 0xFFFFFFFF) == 0 && ((u >> 16) >> 16) == 1; + } else { + Eterm *bp; + if (!is_big(x)) + return 0; + bp = big_val(x); +#if D_EXP == 16 // 16 bit platfrom not really supported!!! + return (BIG_SIZE(bp) == 3) && !BIG_DIGIT(bp,0) && !BIG_DIGIT(bp,1) && + BIG_DIGIT(bp,2) == 1; +#elif D_EXP == 32 + return (BIG_SIZE(bp) == 2) && !BIG_DIGIT(bp,0) && + BIG_DIGIT(bp,1) == 1; +#elif D_EXP == 64 + return (BIG_SIZE(bp) == 1) && + ((BIG_DIGIT(bp,0) & 0xffffffff) == 0) && + ((BIG_DIGIT(bp,0) >> 32) == 1); +#endif + return 0; + } +} |