/* antirez's arbitrary precision integer math library. * * $Id: sbignum.c,v 1.3 2003/10/02 08:21:42 antirez Exp $ * * This library was implemented only to joke a bit with the bignum issues, * don't expect this is very fast or well tested. * Note that in many applications you should check that the arbitrary * precision math implementation is very reliable. * * (news! actually I'm using it for hping3, so starting from * now it is something like a real project.) * * NOTE: if you need a very good bignums implementation check-out GMP * at http://swox.com/gmp/ it is very fast and reliable. * * This library API is almost GMP compatible for the subset of * functions exported. * * COPYRIGHT NOTICE * ---------------- * * Copyright(C) 2002-2003 Salvatore Sanfilippo * All rights reserved. * * This code and the documentation is released under the GPL license * version 2 of the license. You can get a copy of the license at * http://www.gnu.org/licenses/gpl.html * A copy of the license is distributed with this code, * see the file COPYING. */ /* History of important bugs: * * 28 Feb 2002: Bad casting in low-level subtraction generated bad results * for particular pairs of numbers. It was a bit hard to * discover the real origin of the bug since all started * with a strange behaviour of the Fermat little theorem. * This was since the modular reduction uses the low-level * subtraction to perform its work. Of course now it's fixed. * * 12 Sep 2003: Fixed a memory leak in mpz_tostr(). */ #include #include #include #include #include #include "sbignum.h" #include "sbignum-tables.h" /* All the function with the _raw suffix don't care about the sign * and works if the last operand, that's specified as a mpz_atom_t pointer * and a u_int32_t length is stored in statically allocated memory, while * higher level functions expect operands declared as mpz_t and initialized * with mpz_init(). */ /* Macros and functions starting with the '_' character are usually not * exported faster versions of normal functions, that do some unsane assumption * like there is enough memory to store the result and so on. * They are used to build more complex functions */ /* --------------------------- Low level functions -------------------------- */ /* For the actual list of supported functions see sbignum.h */ /* inititialization/allocation */ static int mpz_zero_realloc(mpz_ptr z, u_int32_t i); static void mpz_zero(mpz_ptr z); /* shifting */ static int mpz_lshiftword(mpz_ptr r, u_int32_t i); static int mpz_rshiftword(mpz_ptr r, u_int32_t i); /* comparision */ static int32_t mpz_cmpabsi_raw(mpz_ptr a, mpz_atom_t *d, u_int32_t l); static int32_t mpz_cmpabs(mpz_ptr a, mpz_ptr b); /* addition */ static int mpz_addi_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l); /* subtraction */ static int mpz_subi_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l); /* multiplication */ static int mpz_muli_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l); /* division */ static int mpz_divi_qr_raw(mpz_ptr q, mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l); static int mpz_divi_r_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l); /* number theoretic functions */ static int mpz_gcd_raw(mpz_ptr g, mpz_ptr a, mpz_atom_t *b, u_int32_t l); /* to/from mpz conversions */ static int mpz_tostr(mpz_ptr z, u_int32_t b, void *s, size_t l); /* random numbers */ static void sbn_rand_init(void); /* ================================== MPZ =================================== */ #define MAX(a,b) ((a)>(b)?(a):(b)) #define MIN(a,b) ((a)<(b)?(a):(b)) /* 32bit integer to mpz conversion */ #if ATOMBYTES == 4 #define u32tompz(t,u,l) \ mpz_atom_t t[1]; \ u_int32_t l = 0; \ t[0] = u; \ if (t[0]) l = 1 #elif ATOMBYTES == 2 #define u32tompz(t,u,l) \ mpz_atom_t t[2]; \ u_int32_t l = 0; \ t[0] = u & MPZ_MASK; u >>= MPZ_SHIFT; \ t[1] = u & MPZ_MASK; u >>= MPZ_SHIFT; \ if (t[1]) l = 1; \ else if (t[0]) l = 2 #elif ATOMBYTES == 1 #define u32tompz(t,u,l) \ mpz_atom_t t[4]; \ u_int32_t l = 0; \ t[0] = u & MPZ_MASK; u >>= MPZ_SHIFT; \ t[1] = u & MPZ_MASK; u >>= MPZ_SHIFT; \ t[2] = u & MPZ_MASK; u >>= MPZ_SHIFT; \ t[3] = u & MPZ_MASK; u >>= MPZ_SHIFT; \ if (t[3]) l = 4; \ else if (t[2]) l = 3; \ else if (t[1]) l = 2; \ else if (t[0]) l = 1 #endif /* shift/andmask needed to division and modulo operation for ATOMBITS: * a / ATOMBITS == A >> DIVATOMBITS_SHIFT * a % ATOMBITS == A & MODATOMBITS_MASK */ #if ATOMBYTES == 4 #define DIVATOMBITS_SHIFT 5 #elif ATOMBYTES == 2 #define DIVATOMBITS_SHIFT 4 #elif ATOMBYTES == 1 #define DIVATOMBITS_SHIFT 3 #endif #define MODATOMBITS_MASK ((1<l = l; \ (mpz)->a = l; \ (mpz)->s = 0; \ (mpz)->d = t; \ } while(0) /* Raw inizialization of mpz_t elements */ #define _mpz_raw_init(z, d, l, a, s) \ do { \ (z)->d = d; \ (z)->l = l; \ (z)->a = a; \ (z)->s = s; \ } #define _mpz_neg(z) \ do { \ (z)->s ^= 1; \ } while(0) /* ------------------------ debugging macros -------------------------------- */ #define debugprint(m,z) do { \ char *_s = mpz_get_str(NULL, 10, z); \ printf("[%d]%s\n", m, _s); \ free(_s); \ } while(0) #define debugprint2(m,z) do { \ char *_s = mpz_get_str(NULL, 2, z); \ printf("[%d]%s\n", m, _s); \ free(_s); \ } while(0) /* ---------------------- initialization/allocation ------------------------- */ /* Initialize a relative bignum. * return values: none, can't fail */ void mpz_init(mpz_ptr z) { z->d = NULL; z->a = z->l = z->s = 0; } /* This function is used every time we need to set the z->d[l] word in the * z->d array of the mpz_t type. It performs the allocation when * needed. So if you call it with l = 0, there is anyway at least * one word allocated. Warning: the normalization inside some function * relies on this behaviour. * * return values: * SBN_OK on success * SBN_MEM on out of memory * * On error the previous memory configuration and memory of 'z' * is untouched. * * The new words are initialized to zero. * Note that this function relies on an ANSI-C realloc() that * acts like free if the 'size' = 0, and return NULL in such a case, * and also acts like malloc if the ptr = NULL. */ int mpz_realloc(mpz_ptr z, u_int32_t i) { void *new; u_int32_t j; if (i < z->a) return SBN_OK; new = realloc(z->d, (i+1)*MPZ_ATOMSZ); if (new == NULL) return SBN_MEM; z->d = new; /* set the new words to zero */ for (j = z->a; j <= i; j++) z->d[j] = 0; z->a = j; /* j = i+1 here */ return SBN_OK; } /* Normalize the length of z, that's to set z->l accordly to the * most non-zero significant digit. Assume that all the storage * is initialized to zero (that's a global assuption). */ void mpz_normalize(mpz_ptr z) { int32_t j; if (!z->a) return; j = z->a-1; while(j >= 0) { if (z->d[j]) break; j--; } z->l = j+1; if (z->l == 0) z->s = 0; } /* If z == 0, make it positive */ void mpz_normalize_sign(mpz_ptr z) { if (z->l == 0) z->s = 0; } /* inline version of mpz_normalize() that assumes z->a > 0 */ #define _mpz_normalize(z) \ do { \ int32_t j = (z)->a-1; \ while(j >=0 && !(z)->d[j]) \ j--; \ (z)->l = j+1; \ } while(0) /* Free a bignum, can't fail */ void mpz_clear(mpz_ptr z) { free(z->d); } /* Free a bignum and prepare it to accept up to i+1 digits (base 256) * Note: not GMP compatible. Don't alter the sign */ int mpz_zero_realloc(mpz_ptr z, u_int32_t i) { int err; if ((err = mpz_realloc(z, i)) != SBN_OK) return err; mpz_zero(z); return SBN_OK; } /* raw z = 0 * Note: not GMP compatible. Don't alter the sign */ void mpz_zero(mpz_ptr z) { if (!z->l) return; memset(z->d, 0, z->l*MPZ_ATOMSZ); z->l = 0; } /* Create a stack-allocated clone of the bignum pointed by 'z' and make * 'z' pointing to the clone. This is used when the different operators * of some operations point to the same object. */ #define _mpz_clone_stack(z) \ do { \ mpz_ptr t = alloca(sizeof(mpz_t)); \ t->d = alloca((z)->a*MPZ_ATOMSZ); \ t->s = (z)->s; \ t->l = (z)->l; \ t->a = (z)->a; \ memcpy(t->d, (z)->d, (z)->a*MPZ_ATOMSZ); \ (z) = t; \ } while(0) /* Clone 'z' using the 'L' atoms pointed by 'D' using stack-allocated memory */ #define _mpz_rawclone_stack(z, D, L) \ do { \ (z)->d = alloca((L)*MPZ_ATOMSZ); \ (z)->l = z->a = (L); \ (z)->s = 0; \ memcpy((z)->d, (D), (L)*MPZ_ATOMSZ); \ } while(0) /* Create a stack-allocated copy of 'z' in 'r'. 'r' is an mpz_ptr type */ #define _mpz_copy_stack(r, z) \ do { \ r = alloca(sizeof(mpz_t)); \ (r)->d = alloca((z)->a*MPZ_ATOMSZ); \ (r)->s = (z)->s; \ (r)->l = (z)->l; \ (r)->a = (z)->a; \ memcpy((r)->d, (z)->d, (z)->a*MPZ_ATOMSZ); \ } while(0) /* ----------------------- basic raw operations ----------------------------- */ /* clear the sign flag, so 'z' will be ABS(z) */ #define _mpz_abs(z) \ do { \ (z)->s = 0; \ } while(0) /* ---------------------------- bits operations ----------------------------- */ /* compute the number of bits needed to rappresent the number 'z' */ u_int32_t mpz_bits(mpz_ptr z) { u_int32_t bits = (z->l-1) * ATOMBITS; mpz_atom_t x = z->d[z->l-1]; while(x) { bits++; x >>= 1; } return bits; } /* Set the bit 'i' in 'z' */ int mpz_setbit(mpz_ptr z, u_int32_t i) { u_int32_t atom = i >> DIVATOMBITS_SHIFT; u_int32_t bit = i & MODATOMBITS_MASK; int err; if ((err = mpz_realloc(z, atom)) != SBN_OK) return err; z->d[atom] |= (mpz_atom_t) 1 << bit; if (z->l < atom+1) z->l = atom+1; return SBN_OK; } /* Inline bit pusher that expects the user know what is doing. * Used in the division algorithm. */ #define _mpz_setbit(z, i) \ do { \ u_int32_t _atom = (i)>>DIVATOMBITS_SHIFT; \ (z)->d[_atom] |= (mpz_atom_t) 1<<((i)&MODATOMBITS_MASK);\ if ((z)->l < _atom+1) (z)->l = _atom+1; \ } while(0) /* Faster version without normalization */ #define __mpz_setbit(z, i) \ do { \ u_int32_t _atom = (i)>>DIVATOMBITS_SHIFT; \ (z)->d[_atom] |= (mpz_atom_t) 1<<((i)&MODATOMBITS_MASK);\ } while(0) /* Clear the bit 'i' in 'z' */ int mpz_clrbit(mpz_ptr z, u_int32_t i) { u_int32_t atom = i >> DIVATOMBITS_SHIFT; u_int32_t bit = i & MODATOMBITS_MASK; if (atom >= z->l) return SBN_OK; /* nothing to clear */ z->d[atom] &= ~((mpz_atom_t) 1 << bit); if (atom == z->l-1) mpz_normalize(z); return SBN_OK; } /* Fast clear-bit with normalization */ #define _mpz_clrbit(z, i) \ do { \ u_int32_t _atom = (i)>>DIVATOMBITS_SHIFT; \ (z)->d[_atom] &= ~((mpz_atom_t) 1<<((i)&MODATOMBITS_MASK)); \ if (_atom == z->l-1) \ _mpz_normalize(z); \ } while(0) /* Fast clear-bit without normalization */ #define __mpz_clrbit(z, i) \ do { \ u_int32_t _atom = (i)>>DIVATOMBITS_SHIFT; \ (z)->d[_atom] &= ~((mpz_atom_t) 1<<((i)&MODATOMBITS_MASK));\ } while(0) /* test the bit 'i' of 'z' and return: * 0 if the bit 'i' is not set or out of range * > 0 if the bit 'i' is set */ int mpz_testbit(mpz_ptr z, u_int32_t i) { u_int32_t atom = i >> DIVATOMBITS_SHIFT; u_int32_t bit = i & MODATOMBITS_MASK; if (atom >= z->l) return 0; return (z->d[atom] & ((mpz_atom_t) 1 << bit)); } /* inline bit tester that expects the user know what is doing. * It's used in the division algorithm. Return 0 if the bit is set, * non zero if the bit isn't zet */ #define _mpz_testbit(z, i) \ ((z)->d[(i)>>DIVATOMBITS_SHIFT] & ((mpz_atom_t)1<<((i)&MODATOMBITS_MASK))) /* Return 1 if 'z' is odd, 0 if it's even. */ #define mpz_is_odd(z) (((z)->l) ? ((z)->d[0] & 1) : 0) /* The same of mpz_odd() but assume there is at least an word allocated */ #define _mpz_is_odd(z) ((z)->d[0] & 1) #define _mpz_is_even(z) (!_mpz_is_odd(z)) /* -------------------------------- shifting -------------------------------- */ /* Left shift of 'i' words */ int mpz_lshiftword(mpz_ptr r, u_int32_t i) { int err; if (!i) return SBN_OK; if ((err = mpz_realloc(r, (r->l+i)-1)) != SBN_OK) return err; memmove(r->d+i, r->d, r->l*MPZ_ATOMSZ); memset(r->d, 0, i*MPZ_ATOMSZ); r->l += i; return SBN_OK; } /* Right shift of 'i' words */ int mpz_rshiftword(mpz_ptr r, u_int32_t i) { if (!i) return SBN_OK; if (i >= r->l) { mpz_zero(r); return SBN_OK; } memmove(r->d, r->d+i, (r->l-i)*MPZ_ATOMSZ); r->l -= i; memset(r->d+r->l, 0, i); return SBN_OK; } /* Left shift of 'i' bits */ int mpz_lshift(mpz_ptr r, mpz_ptr z, u_int32_t i) { u_int32_t rawshift = i >> DIVATOMBITS_SHIFT; u_int32_t bitshift = i & MODATOMBITS_MASK; int32_t j; mpz_carry_t x; int err; /* clone 'z' in 'r' */ if (r != z && ((err = mpz_set(r, z)) != SBN_OK)) return err; if (rawshift && ((err = mpz_lshiftword(r, rawshift)) != SBN_OK)) return err; if (!bitshift) return SBN_OK; /* We need an additional word */ if ((err = mpz_realloc(r, r->l+1)) != SBN_OK) return err; /* note that here we are sure that 'bitshift' <= ATOMBITS */ if (r->l) { for (j = r->l-1; j >= 0; j--) { x = (mpz_carry_t) r->d[j] << bitshift; r->d[j] = x & MPZ_MASK; r->d[j+1] |= x >> ATOMBITS; } if (r->d[r->l]) r->l++; } return SBN_OK; } /* Fast 'z' 1 bit left shift. Assume there is allocated space for * an additional atom. Handle normalization */ #define _mpz_self_lshift1(z) \ do { \ int32_t j; \ for (j = (z)->l-1; j >= 0; j--) { \ (z)->d[j+1] |= ((z)->d[j] & (1<<(ATOMBITS-1))) >> (ATOMBITS-1);\ (z)->d[j] <<= 1; \ } \ if ((z)->d[(z)->l]) \ (z)->l++; \ } while(0); /* Fast 'z' 1 bit left shift + set bit 0 to 'b'. Assume there is allocated * space for an additional atom. Handle normalization */ #define _mpz_self_lshift1_setbit0(z, b) \ do { \ int32_t j; \ for (j = (z)->l-1; j >= 0; j--) { \ (z)->d[j+1] |= ((z)->d[j] & (1<<(ATOMBITS-1))) >> (ATOMBITS-1);\ (z)->d[j] <<= 1; \ } \ (z)->d[0] |= b; \ if ((z)->d[(z)->l]) \ (z)->l++; \ } while(0); /* Right shift of 'i' bits */ int mpz_rshift(mpz_ptr r, mpz_ptr z, u_int32_t i) { u_int32_t rawshift = i >> DIVATOMBITS_SHIFT; u_int32_t bitshift = i & MODATOMBITS_MASK; u_int32_t j; mpz_carry_t x; int err; /* clone 'z' in 'r' */ if (r != z && ((err = mpz_set(r, z)) != SBN_OK)) return err; if (rawshift && ((err = mpz_rshiftword(r, rawshift)) != SBN_OK)) return err; if (!bitshift) return SBN_OK; /* note that here we are sure that 'bitshift' <= ATOMBITS */ if (r->l) { r->d[0] >>= bitshift; for (j = 1; j < r->l; j++) { x = (mpz_carry_t) r->d[j] << (ATOMBITS-bitshift); r->d[j] = x >> ATOMBITS; r->d[j-1] |= x & MPZ_MASK; } if (!r->d[r->l-1]) r->l--; } return SBN_OK; } /* Fast 'z' 1 bit right shift. Handle normalization. Assume z->a != 0 * (so z->d != NULL), that's: don't call it without a reallocation. */ #define _mpz_self_rshift1(z) \ do { \ u_int32_t j; \ (z)->d[0] >>= 1; \ for (j = 1; j < (z)->l; j++) { \ (z)->d[j-1] |= ((z)->d[j] & 1) << (ATOMBITS-1); \ (z)->d[j] >>= 1; \ } \ if (!(z)->d[(z)->l-1]) \ (z)->l--; \ } while(0); /* -------------------------- bitwise AND OR XOR NOT ------------------------ */ /* 'r' = 'z' bit-AND 'm' */ int mpz_and(mpz_ptr r, mpz_ptr z, mpz_ptr m) { int err; u_int32_t j; u_int32_t len; if (z == m) { /* A AND A = A */ mpz_set(r, z); return SBN_OK; } len = MIN(z->l, m->l); if ((err = mpz_realloc(r, len)) != SBN_OK) return err; for (j = 0; j < len; j++) r->d[j] = z->d[j] & m->d[j]; memset(r->d+j, 0, r->a - j); /* clear not-used words before normalize */ mpz_normalize(r); return SBN_OK; } /* -------------------------------- compare --------------------------------- */ /* The same as mpz_cmpabs() for immediate. * Relies on the fact that mpz_cmpabs() don't perform any allocation-related * operation on the second operand. */ int32_t mpz_cmpabsi_raw(mpz_ptr a, mpz_atom_t *d, u_int32_t l) { mpz_t b; b->d = d; b->l = b->a = l; b->s = 0; return mpz_cmpabs(a, b); } /* compare ABS('a') and ABS('b'), return values: * >0 if a > b * 0 if a == b * <0 if a < b * * 'a->d' and 'b->d' can point to statically allocated memory. * * Note that we can't use subtraction to return >0 or <0 if a-b != 0 * since the type for length and atom is unsigned so it may overflow. */ int32_t mpz_cmpabs(mpz_ptr a, mpz_ptr b) { int32_t i; if (a->l > b->l) return 1; if (a->l < b->l) return -1; i = a->l; while(i--) { if (a->d[i] > b->d[i]) return 1; if (a->d[i] < b->d[i]) return -1; } return 0; } /* the same as mpz_cmpabs() but 'b' is a 32bit unsigned immediate */ int32_t mpz_cmpabs_ui(mpz_ptr a, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_cmpabs(a, mpz); } /* compare 'a' and 'b'. Return values are the same as mpz_cmpabs() */ int32_t mpz_cmp(mpz_ptr a, mpz_ptr b) { if (!a->l && !b->l) /* 0 == 0 */ return 0; if (a->s == b->s) { /* same sign */ if (a->s) return mpz_cmpabs(b,a); /* both negative */ return mpz_cmpabs(a,b); /* both positive */ } /* one negative, one positive */ if (a->s) return -1; return 1; } /* The same as mpz_cmp() with unsigned 32bit immediate */ int32_t mpz_cmp_ui(mpz_ptr a, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_cmp(a, mpz); } /* signed integer version */ int32_t mpz_cmp_si(mpz_ptr a, int32_t s) { mpz_t mpz; u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); u32pack(mpz,t,l); mpz->s = s < 0; return mpz_cmp(a, mpz); } /* ---------------------------- addition ------------------------------------ */ /* Raw add of immediate, don't care about the sign since * it's up to the caller */ int mpz_addi_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l) { int err; u_int32_t maxi = MAX(z->l, l); mpz_atom_t car = 0; mpz_carry_t sum; u_int32_t j; mpz_atom_t *t = NULL; if (r->d == d) { if ((t = malloc(l*MPZ_ATOMSZ)) == NULL) return SBN_MEM; memcpy(t, d, l*MPZ_ATOMSZ); d = t; } /* two sum of a,b requires at max MAX(len(a),len(b))+1 bytes */ if (r != z && ((err = mpz_zero_realloc(r, maxi)) != SBN_OK)) return err; if ((err = mpz_realloc(z, (r == z) ? maxi : l)) != SBN_OK) return err; for(j = 0; j < l; j++) { sum = (mpz_carry_t) d[j] + z->d[j] + car; car = sum >> MPZ_SHIFT; sum &= MPZ_MASK; r->d[j] = sum; } for (j = l; j < z->l; j++) { sum = (mpz_carry_t) z->d[j] + car; car = sum >> MPZ_SHIFT; sum &= MPZ_MASK; r->d[j] = sum; } if (car) { r->d[j] = car; j++; } r->l = j; /* mpz_normalize() not needed */ if (t) free(t); return SBN_OK; } /* Add 'z' and a 32bit unsigned integer 'u' and put the result in 'r' * Relies on the ability of mpz_add() to accept the last operator * statically allocated */ int mpz_add_ui(mpz_ptr r, mpz_ptr z, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_add(r, z, mpz); } /* The same as mpz_add_ui but with signed integer */ int mpz_add_si(mpz_ptr r, mpz_ptr z, int32_t s) { mpz_t mpz; u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); u32pack(mpz,t,l); mpz->s = s < 0; return mpz_add(r, z, mpz); } /* 'r' = 'a' + 'b' * b->d can point to statically allocated data */ int mpz_add(mpz_ptr r, mpz_ptr a, mpz_ptr b) { int cmp = mpz_cmpabs(a, b); int err; /* both positive or negative */ if (a->s == b->s) { err = mpz_addi_raw(r, a, b->d, b->l); r->s = a->s; return err; } /* different signs if we are here */ if (a->s) { /* a negative, b positive */ if (cmp >= 0) { /* a >= b */ err = mpz_subi_raw(r, a, b->d, b->l); r->s = (r->l == 0) ? 0 : 1; /* negative */ return err; } else { /* a < b */ err = mpz_subi_raw(r, b, a->d, a->l); r->s = 0; /* positive */ return err; } } else { /* a positive, b negative */ if (cmp >= 0) { /* a >= b */ err = mpz_subi_raw(r, a, b->d, b->l); r->s = 0; /* positive */ return err; } else { /* a < b */ err = mpz_subi_raw(r, b, a->d, a->l); r->s = (r->l == 0) ? 0 : 1; /* negative */ return err; } } return SBN_OK; /* not reached */ } /* ---------------------------- subtraction --------------------------------- */ /* WARNING: assume z > d */ int mpz_subi_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l) { int err; mpz_scarry_t sub; mpz_atom_t car = 0; u_int32_t j; mpz_atom_t *t = NULL; if (r->d == d) { if ((t = malloc(l*MPZ_ATOMSZ)) == NULL) return SBN_MEM; memcpy(t, d, l*MPZ_ATOMSZ); d = t; } if (r != z && ((err = mpz_set(r, z)) != SBN_OK)) return err; for (j = 0; j < l; j++) { sub = (mpz_scarry_t) z->d[j] - car - d[j]; car = 0; if (sub < 0) { sub += MPZ_BASE; car = 1; } r->d[j] = sub; } for (j = l; j < z->l; j++) { sub = (mpz_scarry_t) z->d[j] - car; car = 0; if (sub < 0) { sub += MPZ_BASE; car = 1; } r->d[j] = sub; } r->l = j; mpz_normalize(r); if (t) free(t); return SBN_OK; } /* 'r' = 'a' - 'b' * b->d can be statically allocated data */ int mpz_sub(mpz_ptr r, mpz_ptr a, mpz_ptr b) { int cmp = mpz_cmpabs(a, b); int err; /* different signs? */ if (a->s != b->s) { err = mpz_addi_raw(r, a, b->d, b->l); r->s = a->s; return err; } /* both positive or negative if we are here */ if (a->s) { /* both negative */ if (cmp >= 0) { /* a >= b */ err = mpz_subi_raw(r, a, b->d, b->l); r->s = (r->l == 0) ? 0 : 1; /* negative */ return err; } else { /* a < b */ err = mpz_subi_raw(r, b, a->d, a->l); r->s = 0; /* positive */ return err; } } else { /* both positive */ if (cmp >= 0) { /* a >= b */ err = mpz_subi_raw(r, a, b->d, b->l); r->s = 0; /* positive */ return err; } else { /* a < b */ err = mpz_subi_raw(r, b, a->d, a->l); r->s = (r->l == 0) ? 0 : 1; /* negative */ return err; } } return SBN_OK; /* not reached */ } /* mpz_sub() with immediate. * Relies on the fact that mpz_sub() works if the last argument * is statically allocated */ int mpz_sub_ui(mpz_ptr r, mpz_ptr z, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_sub(r, z, mpz); } /* like mpz_sub_ui but with signed integer */ int mpz_sub_si(mpz_ptr r, mpz_ptr z, int32_t s) { mpz_t mpz; u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); u32pack(mpz,t,l); mpz->s = s < 0; return mpz_sub(r, z, mpz); } /* ------------------------------- product ---------------------------------- */ /* Raw multiplication of immediate, don't care about the sign * since it's up to the caller */ int mpz_muli_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l) { int err; u_int32_t maxi = z->l+l; mpz_atom_t car; mpz_carry_t mul; u_int32_t j, i; mpz_t t, rt; mpz_ptr rbak = NULL; int tmptarget = (r == z); mpz_atom_t *x = NULL; /* Make a copy of 'd' if it's == r */ if (r->d == d) { if ((x = malloc(l*MPZ_ATOMSZ)) == NULL) return SBN_MEM; memcpy(x, d, l*MPZ_ATOMSZ); d = x; } /* if r and z are the same we need a temp bignum target */ if (tmptarget) { rbak = r; r = rt; mpz_init(r); r->s = rbak->s; /* preserve the original sign */ } /* two product of a,b requires at max len(a)+len(b) bytes */ if ((err = mpz_zero_realloc(r, maxi)) != SBN_OK) goto error; /* initialize the temp var */ mpz_init(t); if ((err = mpz_realloc(t, maxi)) != SBN_OK) goto error; for(j = 0; j < l; j++) { car = 0; mpz_zero(t); for (i = 0; i < z->l; i++) { /* note that A = B * C + D + E * with A of N*2 bits and C,D,E of N bits * can't overflow since: * (2^N-1)*(2^N-1)+(2^N-1)+(2^N-1) == 2^(2*N)-1 */ mul = (mpz_carry_t) d[j] * z->d[i] + car + r->d[i+j]; car = mul >> MPZ_SHIFT; mul &= MPZ_MASK; r->d[i+j] = mul; } if (car) r->d[i+j] = car; } r->l = maxi; mpz_normalize(r); if (tmptarget && ((err = mpz_set(rbak, rt)) != SBN_OK)) goto error; err = SBN_OK; /* fall through */ error: mpz_clear(t); if (tmptarget) mpz_clear(rt); if (x) free(x); return err; } /* 'r' = 'z' * 'f' */ int mpz_mul(mpz_ptr r, mpz_ptr z, mpz_ptr f) { r->s = z->s^f->s; /* the sign is the xor of the two sings */ return mpz_muli_raw(r, z, f->d, f->l); } /* Mul 'z' and a 32bit unsigned integer 'u' and put the result in 'r' * We don't need to touch the sign since the factor is >= 0 */ int mpz_mul_ui(mpz_ptr r, mpz_ptr z, u_int32_t u) { u32tompz(t,u,l); r->s = z->s; return mpz_muli_raw(r, z, t, l); } /* Like mpz_mul_ui but with signed integer */ int mpz_mul_si(mpz_ptr r, mpz_ptr z, int32_t s) { u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); r->s = z->s^(s<0); return mpz_muli_raw(r, z, t, l); } /* 'r' = i! */ int mpz_fac_ui(mpz_ptr r, u_int32_t i) { u_int32_t j; int err; if (!i) { mpz_setzero(r); return SBN_OK; } if ((err = mpz_set_ui(r, 1)) != SBN_OK) return err; for (j = 2; j <= i; j++) if ((err = mpz_mul_ui(r, r, j)) != SBN_OK) return err; return SBN_OK; } /* --------------------------- exponentialization --------------------------- */ /* compute b^e mod m. * Note that there are much faster ways to do it. * see www.nc.com for more information */ int mpz_powm(mpz_ptr r, mpz_ptr b, mpz_ptr e, mpz_ptr m) { int rs = 0, err; mpz_t B, E; if (e->s) /* can't handle negative exponents */ return SBN_INVAL; /* handle overlapping of modulo and result */ if (r == m) _mpz_clone_stack(m); /* we need to work on copies of base and exponent */ mpz_init(B); mpz_init(E); if ((err = mpz_set(B, b)) != SBN_OK) return err; if ((err = mpz_set(E, e)) != SBN_OK) { mpz_clear(B); return err; } /* make the base positive, but first compute the power sign, * that's negative only if the base is negative and exponent odd */ if (B->s && _mpz_is_odd(E)) rs = 1; _mpz_abs(B); /* compute r = b^e mod m */ mpz_set_ui(r, 1); while(mpz_cmpabs_ui(E, 1) > 0) { if (_mpz_is_odd(E)) { if ((err = mpz_mul(r, r, B)) != SBN_OK) goto error; if ((err = mpz_mod(r, r, m)) != SBN_OK) goto error; } _mpz_self_rshift1(E); /* e = e / 2 */ if ((err = mpz_mul(B, B, B)) != SBN_OK) goto error; if ((err = mpz_mod(B, B, m)) != SBN_OK) goto error; } if ((err = mpz_mul(r, r, B)) != SBN_OK) goto error; r->s = rs; /* set the pre-computed sign */ if ((err = mpz_mod(r, r, m)) != SBN_OK) goto error; err = SBN_OK; /* fall through */ error: mpz_clear(B); mpz_clear(E); return err; } /* Just b^e. The algorithm is just the one of mpz_powm() without * the modulo step. */ int mpz_pow(mpz_ptr r, mpz_ptr b, mpz_ptr e) { int rs = 0, err; mpz_t B, E; if (e->s) /* can't handle negative exponents */ return SBN_INVAL; /* we need to work on copies of base and exponent */ mpz_init(B); mpz_init(E); if ((err = mpz_set(B, b)) != SBN_OK) return err; if ((err = mpz_set(E, e)) != SBN_OK) { mpz_clear(B); return err; } /* make the base positive, but first compute the power sign, * that's negative only if the base is negative and exponent odd */ if (B->s && _mpz_is_odd(E)) rs = 1; _mpz_abs(B); /* compute r = b^e */ mpz_set_ui(r, 1); while(mpz_cmpabs_ui(E, 1) > 0) { if (_mpz_is_odd(E)) { if ((err = mpz_mul(r, r, B)) != SBN_OK) goto error; } _mpz_self_rshift1(E); /* e = e / 2 */ if ((err = mpz_mul(B, B, B)) != SBN_OK) goto error; } if ((err = mpz_mul(r, r, B)) != SBN_OK) goto error; r->s = rs; /* set the pre-computed sign */ err = SBN_OK; /* fall through */ error: mpz_clear(B); mpz_clear(E); return err; } /* -------------------------- root extraction ------------------------------- */ /* r = floor(sqrt(z)). That's r*r <= z AND (r+1)*(r+1) > z. * The algorithm used is very simple but very slow. It exploits * the binary rappresentation. This should be replaced since * performances are very poor */ int mpz_sqrt(mpz_ptr r, mpz_ptr z) { int j = mpz_bits(z); /* MSB bit of 'z' */ int i = ((j-1)/2); /* MSB bit (sometimes one more) of 'r' */ int b = i*2; /* bit to set to obtain 2^i * 2^i */ int err; u_int32_t atoms = j >> DIVATOMBITS_SHIFT; mpz_t s, R, X; mpz_init(s); mpz_init(R); mpz_init(X); if (r == z) { _mpz_clone_stack(z); } if ((err = mpz_realloc(s, atoms)) != SBN_OK) return err; if ((err = mpz_realloc(R, atoms)) != SBN_OK) return err; if ((err = mpz_realloc(X, atoms)) != SBN_OK) return err; if ((err = mpz_zero_realloc(r, atoms)) != SBN_OK) return err; for(; i >= 0; i--, b -= 2) { _mpz_setbit(R, b); mpz_addi_raw(X, s, R->d, R->l); _mpz_clrbit(R, b); if (mpz_cmpabs(X, z) <= 0) { mpz_set(s, X); _mpz_setbit(r, i); _mpz_setbit(R, b+1); } _mpz_self_rshift1(R); } mpz_clear(s); mpz_clear(R); mpz_clear(X); return SBN_OK; } /* ----------------------------- division ----------------------------------- */ /* Raw division of immediate don't care about the sign * since it's up to the caller. * * compute: * 'q' = 'z' / 'd' * 'r' = 'z' % 'd' * * Assume: z >= 0, d > 0, all the arguments must not overlap. * Arguments overlapping, sign, etc, are handled in mpz_tdiv_qr(). * 'z' can be statically allocated. * * =========================================================================== * * I got this algorithm from PGP 2.6.3i (see the mp_udiv function). * Here is how it works: * * Input: N=(Nn,...,N2,N1,N0)radix2 * D=(Dn,...,D2,D1,D0)radix2 * Output: Q=(Qn,...,Q2,Q1,Q0)radix2 = N/D * R=(Rn,...,R2,R1,R0)radix2 = N%D * * Assume: N >= 0, D > 0 * * For j from 0 to n * Qj <- 0 * Rj <- 0 * For j from n down to 0 * R <- R*2 * if Nj = 1 then R0 <- 1 * if R => D then R <- (R - D), Qn <- 1 * * Note that the doubling of R is usually done leftshifting one position. * The only operations needed are bit testing, bit setting and subtraction. * * Unfortunately it is quite slow. The algoritm is not very fast * and the implementation may be smarter. The good point is that * it's very simple to implement. */ int mpz_divi_qr_raw(mpz_ptr q, mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l) { int bit = mpz_bits(z) - 1; mpz_zero_realloc(q, z->l-l+1); mpz_zero_realloc(r, l); while(bit >= 0) { _mpz_self_lshift1_setbit0(r, (_mpz_testbit(z, bit) != 0)); if (mpz_cmpabsi_raw(r, d, l) >= 0) { _mpz_normalize(r); mpz_subi_raw(r, r, d, l); __mpz_setbit(q, bit); } bit--; } _mpz_normalize(q); _mpz_normalize(r); return SBN_OK; } /* The same as mpz_divi_qr_raw() but only the remainder is computed */ int mpz_divi_r_raw(mpz_ptr r, mpz_ptr z, mpz_atom_t *d, u_int32_t l) { int bit = mpz_bits(z) - 1; mpz_zero_realloc(r, l); while(bit >= 0) { _mpz_self_lshift1_setbit0(r, (_mpz_testbit(z, bit) != 0)); if (mpz_cmpabsi_raw(r, d, l) >= 0) { _mpz_normalize(r); mpz_subi_raw(r, r, d, l); } bit--; } _mpz_normalize(r); return SBN_OK; } /* Wrapper for the real division function * 'q' = 'z' / 'd' * 'r' = 'z' % 'd' * * Assume that q and r are different pointers. * d can be statically allocated. * Relies on the fact that: * mpz_set() can accept as second argument a statically allocated operator * mpz_cmpabs() can accept as second argument a statically allocated op. * mpz_divi_qr() can accept a statically allocated divident. */ int mpz_tdiv_qr(mpz_ptr q, mpz_ptr r, mpz_ptr z, mpz_ptr d) { int cmp; int err; if (d->l == 0) /* division by zero */ return SBN_INVAL; if (z == d) { err = mpz_set_ui(q, 1); /* a/a = 1 */ if (err != SBN_OK) return err; mpz_setzero(r); /* a%a = 0 */ return SBN_OK; } cmp = mpz_cmpabs(z, d); if (cmp < 0) { /* z < d */ err = mpz_set(r, z); /* a%b = a with as = z->s^d->s; /* the sign is the xor of the two sings */ r->s = z->s; /* the sign of the remainder is the sign of the divident */ return mpz_divi_qr_raw(q, r, z, d->d, d->l); } /* The same as mpz_tdiv_qr() but the divisor is a 32bit unsigned immediate */ int mpz_tdiv_qr_ui(mpz_ptr q, mpz_ptr r, mpz_ptr z, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_tdiv_qr(q, r, z, mpz); } /* Like mpz_tdiv_qr_si but with signed integer */ int mpz_tdiv_qr_si(mpz_ptr q, mpz_ptr r, mpz_ptr z, int32_t s) { mpz_t mpz; u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); u32pack(mpz,t,l); mpz->s = s < 0; return mpz_tdiv_qr(q, r, z, mpz); } /* Like mpz_tdiv_qr but only the remainder is computed */ int mpz_tdiv_r(mpz_ptr r, mpz_ptr z, mpz_ptr d) { int cmp; if (d->l == 0) /* division by zero */ return SBN_INVAL; if (z == d) { mpz_setzero(r); /* a%a = 0 */ return SBN_OK; } cmp = mpz_cmpabs(z, d); if (cmp < 0) { /* z < d */ if (r == z) return SBN_OK; return mpz_set(r, z); /* a%b = a with as = z->s; /* the sign of the remainder is the sign of the divident */ return mpz_divi_r_raw(r, z, d->d, d->l); } /* The same as mpz_tdiv_r() but the divisor is a 32bit unsigned immediate */ int mpz_tdiv_r_ui(mpz_ptr r, mpz_ptr z, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_tdiv_r(r, z, mpz); } /* Like the above but with signed integer */ int mpz_tdiv_r_si(mpz_ptr r, mpz_ptr z, int32_t s) { mpz_t mpz; u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); u32pack(mpz,t,l); mpz->s = s < 0; return mpz_tdiv_r(r, z, mpz); } /* Like mpz_tdiv_qr but only the quotient is computed. * This is just a wrapper for mpz_tdiv_qr() */ int mpz_tdiv_q(mpz_ptr q, mpz_ptr z, mpz_ptr d) { int err; mpz_t r; mpz_init(r); err = mpz_tdiv_qr(q, r, z, d); mpz_clear(r); return err; } /* The same as mpz_tdiv_q() but the divisor is a 32bit unsigned immediate */ int mpz_tdiv_q_ui(mpz_ptr q, mpz_ptr z, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_tdiv_r(q, z, mpz); } /* Like the above but with signed integer */ int mpz_tdiv_q_si(mpz_ptr q, mpz_ptr z, int32_t s) { mpz_t mpz; u_int32_t u = (s > 0) ? s : -s; u32tompz(t,u,l); u32pack(mpz,t,l); mpz->s = s < 0; return mpz_tdiv_r(q, z, mpz); } /* Division by one-atom divident. * compute z = z / d; * The remainder is returned. * * Assume: z > 0, d > 0 * Operands overlapping is not allowed */ mpz_atom_t _mpz_selfdiv1_qr_raw(mpz_ptr z, mpz_atom_t d) { int32_t j; mpz_carry_t t; /* divide */ for (t = 0, j = z->l-1; j >= 0; j--) { t = (t << MPZ_SHIFT) + z->d[j]; z->d[j] = t / d; t %= d; } /* normalize */ if (!z->d[z->l-1]) z->l--; return t; } /* Compute z mod m (modular reduction) */ int mpz_mod(mpz_ptr r, mpz_ptr z, mpz_ptr m) { int err; if (r == m) _mpz_clone_stack(m); if ((err = mpz_tdiv_r(r, z, m)) != SBN_OK) return err; if (r->l && z->s) { if (m->s) { if ((err = mpz_sub(r, r, m)) != SBN_OK) return err; } else { if ((err = mpz_add(r, r, m)) != SBN_OK) return err; } } return SBN_OK; } /* ---------------------------- assignment ---------------------------------- */ /* Set z = 0 * Note: not GMP compatible */ int mpz_setzero(mpz_ptr z) { z->s = 0; return mpz_zero_realloc(z, 0); } /* assign 's' to 'd'. * 's' can be statically allocated */ int mpz_set(mpz_ptr d, mpz_ptr s) { int err; if ((err = mpz_zero_realloc(d, s->l)) != SBN_OK) return err; memcpy(d->d, s->d, s->l*MPZ_ATOMSZ); d->l = s->l; d->s = s->s; return SBN_OK; } /* Like mpz_set() without reallocation. Assume there is enough * space in d to get the value of s */ #define _mpz_set(D, S) \ do { \ memcpy(D->d, S->d, S->l*MPZ_ATOMSZ); \ D->l = S->l; \ D->s = S->s; \ } while(0) /* Set in 'z' the 32bit unsigned integer given as argument */ int mpz_set_ui(mpz_ptr z, u_int32_t u) { mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); return mpz_set(z, mpz); } /* Set in 'z' the double d */ int mpz_set_d(mpz_ptr z, double d) { int i = 0; u_int64_t u; z->s = (d < 0); d = (d < 0) ? -d : d; u = d; if (mpz_realloc(z, 8)) return 1; while(u) { z->d[i] = u & MPZ_MASK; u >>= MPZ_SHIFT; i++; } z->l = i; return 0; } /* Set in 'z' the 64bit unsigned integer 'u' */ int mpz_set_ui64(mpz_ptr z, u_int64_t u) { int i = 0; z->s = 0; if (mpz_realloc(z, 8)) return 1; while(u) { z->d[i] = u & MPZ_MASK; u >>= MPZ_SHIFT; i++; } z->l = i; return 0; } /* Set in 'z' the 64bit signed integer 's' */ int mpz_set_si64(mpz_ptr z, int64_t s) { u_int64_t u; int sign = s < 0, err; u = (s > 0) ? s : -s; if ((err = mpz_set_ui64(z, u)) != SBN_OK) return err; z->s = sign; return err; } /* Set in 'z' the 32bit unsigned integer given as argument */ int mpz_set_si(mpz_ptr z, int32_t s) { int neg = s < 0; int err; u_int32_t u = neg ? -s : s; mpz_t mpz; u32tompz(t,u,l); u32pack(mpz,t,l); if ((err = mpz_set(z, mpz))) return err; if (neg) _mpz_neg(z); return err; } /* set 'd' to ABS('s'). */ int mpz_abs(mpz_ptr d, mpz_ptr s) { int err; if ((d != s) && ((err = mpz_set(d, s)) != SBN_OK)) return err; _mpz_abs(d); return SBN_OK; } /* set 'd' to -'s' */ int mpz_neg(mpz_ptr d, mpz_ptr s) { int err; if ((d != s) && ((err = mpz_set(d, s)) != SBN_OK)) return err; _mpz_neg(d); return SBN_OK; } /* ----------------------- number theoretic functions ----------------------- */ /* Compute the GCD (greatest common divisor) for 'a' and 'b' using * the binary GCD algorithm. * * 'g' = GCD('|a|', '|b|') * * g, a, b can overlap (we anyway need to work on copies of a and b) * assume a > 0, b > 0. */ int mpz_gcd_raw(mpz_ptr g, mpz_ptr a, mpz_atom_t *b, u_int32_t l) { u_int32_t maxi = MAX(a->l, l); mpz_t B, t; int err; /* we need to work on copies. */ _mpz_clone_stack(a); _mpz_rawclone_stack(B, b, l); _mpz_abs(a); _mpz_abs(B); /* Reset 'g', prepare to accept up to maxi+1 atoms, set it to 1 */ if ((err = mpz_zero_realloc(g, maxi)) != SBN_OK) return err; g->d[0] = 1; /* after the realloc call there is at least 1 atom */ g->l = 1; /* The binary GCD algorithm */ mpz_init(t); /* While even(a) and even(b) -> a=a/2 b=b/2 g=g*2; */ while(_mpz_is_even(a) && _mpz_is_even(B)) { _mpz_self_rshift1(a); _mpz_self_rshift1(B); _mpz_self_lshift1(g); } /* While a > 0 */ while(_mpz_nonzero(a)) { /* While even(a) a=a/2 */ while(_mpz_is_even(a)) _mpz_self_rshift1(a); /* While even(b) b=b/2 */ while(_mpz_is_even(B)) _mpz_self_rshift1(B); /* t = abs(a-b)/2 * if (a >= b) a = t else b = t */ if (mpz_cmpabs(a, B) >= 0) { if ((err = mpz_subi_raw(t, a, B->d, B->l)) != SBN_OK) goto err; _mpz_self_rshift1(t); _mpz_set(a, t); } else { if ((err = mpz_subi_raw(t, B, a->d, a->l)) != SBN_OK) goto err; _mpz_self_rshift1(t); _mpz_set(B, t); } } /* GCD = g * b */ mpz_muli_raw(g, g, B->d, B->l); err = SBN_OK; /* fall through */ err: mpz_clear(t); return err; } /* wrapper for mpz_gcd_raw(). set GCD(a, 0) = a */ int mpz_gcd(mpz_ptr g, mpz_ptr a, mpz_ptr b) { int err; if (_mpz_iszero(a)) { if ((err = mpz_set(g, b)) != SBN_OK) return err; _mpz_abs(g); return SBN_OK; } if (_mpz_iszero(b)) { if ((err = mpz_set(g, a)) != SBN_OK) return err; _mpz_abs(g); return SBN_OK; } return mpz_gcd_raw(g, a, b->d, b->l); } /* GCD(a, b) with b unsigned 32bit integer immediate. * if 'g' is not NULL the result is stored in g. * if 'g' is NULL and the result fits inside the u_int32_t type * it is returned. If the result doesn't fit (can happen only if b = 0) * 0 is returned. */ u_int32_t mpz_gcd_ui(mpz_ptr g, mpz_ptr a, u_int32_t b) { g = g; a = a; b = b; return SBN_OK; } /* ----------------------- to/from string conversion ------------------------ */ #define sbn_chartoval(c) (r_cset[tolower(c)]) #define sbn_valtochar(v) (cset[v]) /* Extimate the number of bytes needed to store a string rappresentation * in base 'b' of the number 'z'. The length is overstimated, assuming * the precision of the C-lib log() is of 6 digits over the dot. * the length of the minus sign and the nul term are not included */ size_t mpz_sizeinbase(mpz_ptr z, u_int32_t b) { double len; if (b < SBN_MINBASE || b > SBN_MAXBASE) return SBN_INVAL; len = ((basetable[b]+0.000001) * z->l) + 1; return (size_t) len; } /* Convert an mpz_t to a string rappresentation in base 'b' * Always nul-terminate the string if l > 0. * * We use a common trick to speed-up the conversion. * Instead to perform divisions with remainder between * the bignum and the specified base, we use a base that's * the biggest power of the real base. Then we use the CPU * division to divide by the real base. This limits a lot * the number of multi-precision divisions, that are slow. * * For example converting in base 10, every 10 divisions * 9 are divisions between two mpz_atom_t vars, and only * one between a bignum and an mpz_atom_t. * * TODO: Note that this is still not very good since we should * at least handle the case of a base that's power of 2 * in a special way (i.e. performing shiftings and bitwise * andings). */ int mpz_tostr(mpz_ptr z, u_int32_t b, void *s, size_t l) { mpz_t t; char *d = s, *p; mpz_atom_t hb, hbn; if (b < SBN_MINBASE || b > SBN_MAXBASE) return SBN_INVAL; if (!l) return SBN_OK; /* Handle z = 0 */ if (_mpz_iszero(z)) { *d++ = '0'; goto done; } /* get the biggest power of 'b' that fits in an mpz_atom_t * and it's exponent from the table. */ hbn = basepowtable[b].maxexp; hb = basepowtable[b].maxpow; l--; mpz_init(t); mpz_set(t, z); while(_mpz_nonzero(t) && l) { unsigned int i; mpz_atom_t x; x = _mpz_selfdiv1_qr_raw(t, (mpz_atom_t) hb); for (i = 0; (i < hbn) && (l != 0); i++) { *d++ = sbn_valtochar(x % b); x /= b; if (x == 0 && _mpz_iszero(t)) break; } } mpz_clear(t); done: /* add the sign if needed */ if (l && z->s) *d++ = '-'; *d-- = '\0'; /* reverse the result */ p = s; while(p < d) { char t; t = *p; *p = *d; *d = t; d--; p++; } return SBN_OK; } char *mpz_get_str(char *str, int b, mpz_ptr z) { size_t len; if (b < SBN_MINBASE || b > SBN_MAXBASE) return NULL; len = mpz_sizeinbase(z, b) + 2; if (!str && ((str = malloc(len)) == NULL)) return NULL; mpz_tostr(z, b, str, len); return str; } /* set in 'z' the ascii rappresentation in 's' of the number in base 'b' * * On error the original value of 'z' is not guaranteed to be the same * as before this function is called. * * Again possible optimizations are not implemented. Most notably * the base power of 2 case. */ int mpz_set_str(mpz_ptr z, char *s, int b) { size_t len = strlen(s); char *t = s + len - 1; int neg = 0, err; mpz_t pow, toadd; /* seek the first non-blank char from the head */ while(*s && isspace(*s)) { s++; len--; } /* check if the number is negative */ if (len && *s == '-') { neg = 1; s++; len--; } /* guess the base */ if (b == 0) { b = 10; if (len && *s == '0') { b = 8; s++; len--; if (len && tolower(*s) == 'x') { b = 16; s++; len--; } else if (len && tolower(*s) == 'b') { b = 2; s++; len--; } } } if (b < SBN_MINBASE || b > SBN_MAXBASE) return SBN_INVAL; /* seek the first non-blank char from the tail */ while(t > s && isspace(*t)) t--; /* convert it */ mpz_init(pow); mpz_init(toadd); mpz_zero(z); if ((err = mpz_set_ui(pow, 1)) != SBN_OK) return err; while(t >= s) { int digit; digit = sbn_chartoval(*t); if (digit < 0 || digit >= b) { err = SBN_INVAL; goto error; } mpz_set_ui(toadd, digit); if ((err = mpz_mul(toadd, toadd, pow)) != SBN_OK) goto error; if ((err = mpz_add(z, z, toadd)) != SBN_OK) goto error; if ((err = mpz_mul_ui(pow, pow, b)) != SBN_OK) goto error; t--; } z->s = neg; err = SBN_OK; /* fall through */ error: mpz_clear(pow); mpz_clear(toadd); return err; } /* ------------------------------- random numbers --------------------------- */ /* The rc4_sbox array is static, but this doesn't mean you can't use this * library with threads. To create a real context for every random * generation session is an overkill here */ static unsigned char rc4_sbox[256]; /* We want to start every time with the same seed. This is very * important when some random number trigger multi-precision operations * bugs. This flags is used to initialize the sbox the first time */ static int rc4_seedflag = 0; /* Initialize the sbox with the numbers from 0 to 255 */ void sbn_rand_init(void) { int i; rc4_seedflag = 1; for (i = 0; i < 256; i++) rc4_sbox[i] = i; } /* Re-seed the generator with user-provided bytes */ void sbn_seed(void *seed, size_t len) { int i; unsigned char *s = (unsigned char*)seed; for (i = 0; i < len; i++) rc4_sbox[i&0xFF] ^= s[i]; /* discard the first 256 bytes of output after the reseed */ for (i = 0; i < 32; i++) (void) sbn_rand(); } /* Generates a 32bit random number using an RC4-like algorithm */ u_int32_t sbn_rand(void) { u_int32_t r = 0; unsigned char *rc = (unsigned char*) &r; static unsigned int i = 0, j = 0; unsigned int si, sj, x; /* initialization, only needed the first time */ if (!rc4_seedflag) sbn_rand_init(); /* generates 4 bytes of pseudo-random numbers using RC4 */ for (x = 0; x < 4; x++) { i = (i+1) & 0xff; si = rc4_sbox[i]; j = (j + si) & 0xff; sj = rc4_sbox[j]; rc4_sbox[i] = sj; rc4_sbox[j] = si; *rc++ = rc4_sbox[(si+sj)&0xff]; } return r; } /* Generate a random number of at most 'len' atoms length. * If 'len' is negative the number will be negative of length abs(len) */ int mpz_random(mpz_ptr z, int32_t len) { int i, err, sign = 0; if (len < 0) { sign = 1; len = -len; } if (!len) return mpz_setzero(z); if ((err = mpz_realloc(z, len-1)) != SBN_OK) return err; for (i = 0; i < len; i++) z->d[i] = sbn_rand() & MPZ_MASK; _mpz_normalize(z); z->s = sign; return SBN_OK; } /* Convert the bignum to approsimated double */ double mpz_get_d(mpz_ptr z) { double d = 0; u_int32_t l = z->l; while(l--) d = z->d[l] + d*MPZ_BASE; if (z->s) d = -d; return d; }