diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2015-04-22 19:55:19 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2015-04-22 19:55:19 -0700 |
commit | 150e147b77199684b32c327e4d3e2715fd2d7a0f (patch) | |
tree | 171ebf24ae26a036d04f04b0a6a0bfd2d56d9c87 /mpi | |
parent | 5f4b704f40f30d19468078ace5f7624c1225faa6 (diff) | |
download | txr-150e147b77199684b32c327e4d3e2715fd2d7a0f.tar.gz txr-150e147b77199684b32c327e4d3e2715fd2d7a0f.tar.bz2 txr-150e147b77199684b32c327e4d3e2715fd2d7a0f.zip |
add-bitops patch
Adding bit operations to MPI.
* mpi/mpi.c (MAX, MIN): New macros.
(mp_2comp, mp_and, mp_or, mp_xor, mp_comp, mp_trunc, mp_shift,
mp_bit): New functions.
* mpi/mpi.h (mp_2comp, mp_and, mp_or, mp_xor, mp_comp, mp_trunc,
mp_shift, mp_bit): Declared.
Diffstat (limited to 'mpi')
-rw-r--r-- | mpi/mpi.c | 428 | ||||
-rw-r--r-- | mpi/mpi.h | 13 |
2 files changed, 441 insertions, 0 deletions
@@ -16,6 +16,9 @@ #include <ctype.h> #include <math.h> +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + typedef unsigned char mem_t; extern mem_t *chk_calloc(size_t n, size_t size); @@ -157,6 +160,7 @@ static const char *s_dmap_2 = mp_err s_mp_grow(mp_int *mp, mp_size min); /* increase allocated size */ mp_err s_mp_pad(mp_int *mp, mp_size min); /* left pad with zeroes */ +static int s_highest_bit(mp_digit n); int s_highest_bit_mp(mp_int *a); mp_err s_mp_set_bit(mp_int *a, int bit); @@ -2334,6 +2338,430 @@ X: /* }}} */ +/* + * Convert a's bit vector to its two's complement, up to the + * number of words that it contains, storing result in b. The numeric value of + * this result depends on the size of mpi_digit. This is a building block for + * handling negative operands in the bit operations. + */ +mp_err mp_2comp(mp_int *a, mp_int *b, mp_size dig) +{ + mp_err res; + mp_size ix, adig = USED(a); + mp_digit *pa, *pb; + mp_digit padding = ISNEG(a) ? MP_DIGIT_MAX : 0; + mp_word w; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if (a != b) { + if ((res = mp_init_size(b, dig)) != MP_OKAY) + return res; + SIGN(b) = SIGN(a); + } else { + if((res = s_mp_pad(b, dig)) != MP_OKAY) + return res; + } + + for (pa = DIGITS(a), pb = DIGITS(b), w = 0, ix = 0; ix < dig; ix++) { + w += (ix == 0); + w += (ix < adig) ? ~pa[ix] : padding; + pb[ix] = ACCUM(w); + w = CARRYOUT(w); + } + + USED(b) = dig; + + return MP_OKAY; +} + +mp_err mp_and(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res = MP_OKAY; + mp_size ix, extent = 0; + mp_digit *pa, *pb, *pc; + mp_int tmp_a, tmp_b; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if (a == b) + return mp_copy(a, c); + + if (ISNEG(a)) { + extent = USED(b); + mp_init(&tmp_a); + if ((res = mp_2comp(a, &tmp_a, extent)) != MP_OKAY) + goto out; + a = &tmp_a; + } + + if (ISNEG(b)) { + extent = USED(a); + mp_init(&tmp_b); + if ((res = mp_2comp(b, &tmp_b, extent)) != MP_OKAY) + goto out; + b = &tmp_b; + } + + if (!extent) + extent = MIN(USED(a), USED(b)); + + if (c != a && c != b) { + if ((res = mp_init_size(c, extent)) != MP_OKAY) + goto out; + } + + for (pa = DIGITS(a), pb = DIGITS(b), pc = DIGITS(c), ix = 0; + ix < extent; ix++) + { + pc[ix] = pa[ix] & pb[ix]; + } + + USED(c) = extent; + + if (ISNEG(a) && ISNEG(b)) { + mp_2comp(c, c, extent); + SIGN(c) = MP_NEG; + } + + s_mp_clamp(c); + +out: + if (ISNEG(a)) + mp_clear(&tmp_a); + + if (ISNEG(b)) + mp_clear(&tmp_b); + + return res; +} + +mp_err mp_or(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_size ix, extent = 0; + mp_digit *pa, *pb, *pc; + mp_int tmp_a, tmp_b; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + extent = MAX(USED(a), USED(b)); + + if (a == b) + return mp_copy(a, c); + + if (ISNEG(a)) { + mp_init(&tmp_a); + if ((res = mp_2comp(a, &tmp_a, extent)) != MP_OKAY) + goto out; + a = &tmp_a; + } + + if (ISNEG(b)) { + mp_init(&tmp_b); + if ((res = mp_2comp(b, &tmp_b, extent)) != MP_OKAY) + goto out; + b = &tmp_b; + } + + + if (c != a && c != b) + res = mp_init_size(c, extent); + else + res = s_mp_pad(c, extent); + + if (res != MP_OKAY) + goto out; + + for (pa = DIGITS(a), pb = DIGITS(b), pc = DIGITS(c), ix = 0; + ix < extent; ix++) + { + pc[ix] = pa[ix] | pb[ix]; + } + + USED(c) = extent; + + if (ISNEG(a) || ISNEG(b)) { + mp_2comp(c, c, extent); + SIGN(c) = MP_NEG; + } + + s_mp_clamp(c); + +out: + if (ISNEG(a)) + mp_clear(&tmp_a); + + if (ISNEG(b)) + mp_clear(&tmp_b); + + return res; +} + +mp_err mp_xor(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_size ix, extent = 0; + mp_digit *pa, *pb, *pc; + mp_int tmp_a, tmp_b; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if (a == b) { + mp_zero(c); + return MP_OKAY; + } + + extent = MAX(USED(a), USED(b)); + + if (ISNEG(a)) { + mp_init(&tmp_a); + if ((res = mp_2comp(a, &tmp_a, extent)) != MP_OKAY) + goto out; + a = &tmp_a; + } + + if (ISNEG(b)) { + mp_init(&tmp_b); + if ((res = mp_2comp(b, &tmp_b, extent)) != MP_OKAY) + goto out; + b = &tmp_b; + } + + + if (c != a && c != b) + res = mp_init_size(c, extent); + else + res = s_mp_pad(c, extent); + + if (res != MP_OKAY) + goto out; + + for (pa = DIGITS(a), pb = DIGITS(b), pc = DIGITS(c), ix = 0; + ix < extent; ix++) + { + pc[ix] = pa[ix] ^ pb[ix]; + } + + USED(c) = extent; + + if (ISNEG(a) ^ ISNEG(b)) { + mp_2comp(c, c, extent); + SIGN(c) = MP_NEG; + } + + s_mp_clamp(c); + +out: + if (ISNEG(a)) + mp_clear(&tmp_a); + + if (ISNEG(b)) + mp_clear(&tmp_b); + + return res; +} + +mp_err mp_comp(mp_int *a, mp_int *b) +{ + mp_err res; + mp_size ix, dig = USED(a); + mp_digit *pa, *pb; + mp_int tmp; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if (a != b) + res = mp_init_size(b, dig); + else + res = s_mp_pad(b, dig); + + if (res != MP_OKAY) + return res; + + if (ISNEG(a)) { + mp_init(&tmp); + if ((res = mp_2comp(a, &tmp, dig)) != MP_OKAY) + return res; + a = &tmp; + } + + for (pa = DIGITS(a), pb = DIGITS(b), ix = 0; ix < dig; ix++) + pb[ix] = ~pa[ix]; + + USED(b) = dig; + + if (ISNEG(a)) { + mp_clear(&tmp); + } else { + if ((res = mp_2comp(b, b, dig)) != MP_OKAY) + return res; + SIGN(b) = MP_NEG; + } + + s_mp_clamp(b); + return MP_OKAY; +} + +mp_err mp_trunc_comp(mp_int *a, mp_int *b, mp_digit bits) +{ + mp_err res; + mp_size ix, dig = bits / DIGIT_BIT, rembits = bits % DIGIT_BIT; + mp_size adig = USED(a); + mp_digit padding = ISNEG(a) ? MP_DIGIT_MAX : 0; + int extra = (rembits != 0); + mp_digit *pa, *pb; + mp_int tmp; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if (a != b) + res = mp_init_size(b, dig + extra); + else + res = s_mp_pad(b, dig + extra); + + if (res != MP_OKAY) + return res; + + if (ISNEG(a)) { + mp_init(&tmp); + if ((res = mp_2comp(a, &tmp, dig + extra)) != MP_OKAY) + return res; + a = &tmp; + } + + for (pa = DIGITS(a), pb = DIGITS(b), ix = 0; ix < dig; ix++) + pb[ix] = (ix < adig) ? ~pa[ix] : ~padding; + + if (rembits) { + mp_digit mask = (MP_DIGIT_MAX >> (DIGIT_BIT - rembits)); + pb[ix] = (((ix < adig) ? pa[ix] : padding) & mask) ^ mask; + } + + USED(b) = dig + extra; + + if (ISNEG(a)) + mp_clear(&tmp); + + s_mp_clamp(b); + return MP_OKAY; +} + +mp_err mp_trunc(mp_int *a, mp_int *b, mp_digit bits) +{ + mp_err res; + mp_size ix, dig = bits / DIGIT_BIT, rembits = bits % DIGIT_BIT; + mp_size adig = USED(a); + mp_digit padding = ISNEG(a) ? MP_DIGIT_MAX : 0; + int extra = (rembits != 0); + mp_digit *pa, *pb; + mp_int tmp; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if (a != b) + res = mp_init_size(b, dig + extra); + else + res = s_mp_pad(b, dig + extra); + + if (res != MP_OKAY) + return res; + + if (ISNEG(a)) { + mp_init(&tmp); + if ((res = mp_2comp(a, &tmp, dig + extra)) != MP_OKAY) + return res; + a = &tmp; + } + + for (pa = DIGITS(a), pb = DIGITS(b), ix = 0; ix < dig; ix++) + pb[ix] = (ix < adig) ? pa[ix] : padding; + + if (rembits) { + mp_digit mask = (MP_DIGIT_MAX >> (DIGIT_BIT - rembits)); + pb[ix] = ((ix < adig) ? pa[ix] : padding) & mask; + } + + USED(b) = dig + extra; + + if (ISNEG(a)) + mp_clear(&tmp); + + s_mp_clamp(b); + return MP_OKAY; +} + +mp_err mp_shift(mp_int *a, mp_int *b, int bits) +{ + mp_int tmp; + mp_err res; + int a_neg = ISNEG(a); + + if (bits == 0) + return mp_copy(a, b); + + if (a_neg) { + mp_size ua = USED(a); + mp_init(&tmp); + if ((res = mp_2comp(a, &tmp, ua)) != MP_OKAY) + return res; + SIGN(&tmp) = MP_ZPOS; + a = &tmp; + } + + if (bits > 0) + res = mp_mul_2d(a, bits, b); + else + res = mp_div_2d(a, -bits, b, NULL); + + if (res != MP_OKAY) { + if (a_neg) + mp_clear(&tmp); + return res; + } + + if (a_neg) { + int hb, msd; + mp_digit *db; + + mp_clear(&tmp); + + msd = USED(b)-1; + db = DIGITS(b); + hb = s_highest_bit(db[msd]); + + if (hb < DIGIT_BIT) + db[msd] |= MP_DIGIT_MAX << hb; + + if ((res = mp_2comp(b, b, USED(b))) != MP_OKAY) + return res; + + SIGN(b) = MP_NEG; + s_mp_clamp(b); + } + + return MP_OKAY; +} + +mp_err mp_bit(mp_int *a, mp_digit bit) +{ + mp_int tmp; + mp_err res; + int a_neg = ISNEG(a); + int digit = bit / MP_DIGIT_BIT; + mp_digit mask = ((mp_digit) 1 << (bit % MP_DIGIT_BIT)); + + if (a_neg) { + mp_init(&tmp); + if ((res = mp_2comp(a, &tmp, bit + 1)) != MP_OKAY) + return res; + SIGN(&tmp) = MP_ZPOS; + a = &tmp; + } + + return (DIGITS(a)[digit] & mask) != 0 ? MP_YES : MP_NO; +} + mp_err mp_to_double(mp_int *mp, double *d) { int ix; @@ -54,6 +54,7 @@ /* Macros for accessing the mp_int internals */ #define SIGN(MP) ((MP)->sign) +#define ISNEG(MP) ((MP)->sign == MP_NEG) #define USED(MP) ((MP)->used) #define ALLOC(MP) ((MP)->alloc) #define DIGITS(MP) ((MP)->dp) @@ -187,6 +188,18 @@ mp_err mp_invmod(mp_int *a, mp_int *m, mp_int *c); #endif /* end MP_NUMTH */ /*------------------------------------------------------------------------*/ +/* Bit ops */ +mp_err mp_2comp(mp_int *a, mp_int *b, mp_size dig); /* peculiar semantics */ +mp_err mp_and(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_or(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_xor(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_comp(mp_int *a, mp_int *b); +mp_err mp_trunc_comp(mp_int *a, mp_int *b, mp_digit bits); +mp_err mp_trunc(mp_int *a, mp_int *b, mp_digit bits); +mp_err mp_shift(mp_int *a, mp_int *b, int bits); /* + left, - right */ +mp_err mp_bit(mp_int *a, mp_digit bit); + +/*------------------------------------------------------------------------*/ /* Conversions */ mp_err mp_to_double(mp_int *mp, double *d); |