From 982a0c3aece1a403eede976fadd179bcdb04fbf4 Mon Sep 17 00:00:00 2001 From: Martin Stjernholm <mast@lysator.liu.se> Date: Fri, 28 Mar 2003 16:51:40 +0100 Subject: [PATCH] More efficient conversions between mpz and integers. Moved the implementations for push_int64 and int64_from_bignum here. int64_from_bignum now properly checks for overflow. Rev: src/modules/Gmp/mpz_glue.c:1.128 Rev: src/modules/Gmp/my_gmp.h:1.16 --- src/modules/Gmp/mpz_glue.c | 318 +++++++++++++++++++++++-------------- src/modules/Gmp/my_gmp.h | 20 ++- 2 files changed, 220 insertions(+), 118 deletions(-) diff --git a/src/modules/Gmp/mpz_glue.c b/src/modules/Gmp/mpz_glue.c index d04f5e02e1..da95b8a618 100644 --- a/src/modules/Gmp/mpz_glue.c +++ b/src/modules/Gmp/mpz_glue.c @@ -2,14 +2,17 @@ || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. -|| $Id: mpz_glue.c,v 1.127 2003/03/26 15:23:39 mast Exp $ +|| $Id: mpz_glue.c,v 1.128 2003/03/28 15:51:40 mast Exp $ */ #include "global.h" -RCSID("$Id: mpz_glue.c,v 1.127 2003/03/26 15:23:39 mast Exp $"); +RCSID("$Id: mpz_glue.c,v 1.128 2003/03/28 15:51:40 mast Exp $"); #include "gmp_machine.h" #include "module.h" +/* Disable this for now to check that the fallbacks work correctly. */ +#undef HAVE_MPZ_IMPORT + #if defined(HAVE_GMP2_GMP_H) && defined(HAVE_LIBGMP2) #define USE_GMP2 #else /* !HAVE_GMP2_GMP_H || !HAVE_LIBGMP2 */ @@ -40,10 +43,6 @@ RCSID("$Id: mpz_glue.c,v 1.127 2003/03/26 15:23:39 mast Exp $"); #include <limits.h> -#if SIZEOF_INT_TYPE > SIZEOF_LONG -#define BIG_PIKE_INT -#endif - #define sp Pike_sp #define fp Pike_fp @@ -68,53 +67,49 @@ struct program *bignum_program; #endif #ifdef AUTO_BIGNUM +static mpz_t mpz_int_type_min; + void mpzmod_reduce(struct object *o) { - INT_TYPE i; + MP_INT *mpz = OBTOMPZ (o); + int neg = mpz_sgn (mpz) < 0; + INT_TYPE res = 0; + + /* Get the index of the highest limb that have bits within the range + * of the INT64. */ + size_t pos = (INT_TYPE_BITS + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS - 1; + + if (mpz_size (mpz) <= pos + 1) { +#if INT_TYPE_BITS == GMP_NUMB_BITS + res = mpz_getlimbn (mpz, 0); +#elif INT_TYPE_BITS < GMP_NUMB_BITS + mp_limb_t val = mpz_getlimbn (mpz, 0); + if (val >= (mp_limb_t) 1 << INT_TYPE_BITS) goto overflow; + res = val; +#else + for (;; pos--) { + res |= mpz_getlimbn (mpz, pos) & GMP_NUMB_MASK; + if (pos == 0) break; + if (res >= (INT_TYPE) 1 << (INT_TYPE_BITS - GMP_NUMB_BITS)) goto overflow; + res <<= GMP_NUMB_BITS; + } +#endif - i = mpz_get_si(OBTOMPZ(o)); - if(mpz_cmp_si(OBTOMPZ(o), i) == 0) - { - free_object(o); - push_int(i); + if (neg) res = -res; + free_object (o); + push_int (res); + return; } - else - { -#ifdef BIG_PIKE_INT -#define SHIFT 16 /* suggested: CHAR_BIT*sizeof(unsigned long int) */ -#define FILTER ((1<<SHIFT)-1) - int pos=0; - unsigned long int a; - INT_TYPE res=0; - mpz_t t,u; - - mpz_init_set(t,OBTOMPZ(o)); - mpz_init(u); - while (pos<(int)sizeof(INT_TYPE)*CHAR_BIT) - { - a=mpz_get_ui(t)&FILTER; - if (!a && mpz_cmp_si(t,0)==0) - { - if (mpz_get_si(OBTOMPZ(o))<0) res=-res; - mpz_clear(t); - mpz_clear(u); - free_object(o); - push_int(res); - return; - } - res|=((INT_TYPE)a)<<pos; - if ((res>>pos) != a) break; - mpz_fdiv_q_2exp(u,t,SHIFT); - mpz_set(t,u); - pos+=SHIFT; - } - mpz_clear(t); - mpz_clear(u); -#undef SHIFT -#undef FILTER -#endif - push_object(o); + +overflow: + if (neg && !mpz_cmp (mpz, mpz_int_type_min)) { + /* No overflow afterall; it's MIN_INT_TYPE, the only valid integer + * whose absolute value is INT_TYPE_BITS long. */ + free_object (o); + push_int (MIN_INT_TYPE); } + else + push_object (o); } #define PUSH_REDUCED(o) do { struct object *reducetmp__=(o); \ if(THIS_PROGRAM == bignum_program) \ @@ -123,6 +118,94 @@ void mpzmod_reduce(struct object *o) push_object(reducetmp__); \ }while(0) +#ifdef INT64 + +static void gmp_push_int64 (INT64 i) +{ + if(i == DO_NOT_WARN((INT_TYPE)i)) + { + push_int(DO_NOT_WARN((INT_TYPE)i)); + } + else + { + MP_INT *mpz; + int neg = i < 0; + if (neg) i = -i; + + push_object (fast_clone_object (bignum_program)); + mpz = OBTOMPZ (sp[-1].u.object); + +#ifdef HAVE_MPZ_IMPORT + mpz_import (mpz, 1, 1, SIZEOF_INT64, 0, 0, &i); +#else + { + size_t n = + ((SIZEOF_INT64 + SIZEOF_LONG - 1) / SIZEOF_LONG - 1) + /* The above is the position of the top unsigned long in the INT64. */ + * ULONG_BITS; + mpz_set_ui (mpz, (i >> n) & ULONG_MAX); + while (n) { + n -= ULONG_BITS; + mpz_mul_2exp (mpz, mpz, ULONG_BITS); + mpz_add_ui (mpz, mpz, (i >> n) & ULONG_MAX); + } + } +#endif + + if (neg) mpz_neg (mpz, mpz); + } +} + +static mpz_t mpz_int64_min; + +static int gmp_int64_from_bignum (INT64 *i, struct object *bignum) +{ + MP_INT *mpz = OBTOMPZ (bignum); + int neg = mpz_sgn (mpz) < 0; + INT64 res = 0; + + /* Get the index of the highest limb that have bits within the range + * of the INT64. */ + size_t pos = (INT64_BITS + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS - 1; + +#ifdef PIKE_DEBUG + if (bignum->prog != bignum_program) Pike_fatal ("Not a Gmp.bignum.\n"); +#endif + + if (mpz_size (mpz) <= pos + 1) { +#if INT64_BITS == GMP_NUMB_BITS + res = mpz_getlimbn (mpz, 0); +#elif INT64_BITS < GMP_NUMB_BITS + mp_limb_t val = mpz_getlimbn (mpz, 0); + if (val >= (mp_limb_t) 1 << INT64_BITS) goto overflow; + res = val; +#else + for (;; pos--) { + res |= mpz_getlimbn (mpz, pos) & GMP_NUMB_MASK; + if (pos == 0) break; + if (res >= (INT64) 1 << (INT64_BITS - GMP_NUMB_BITS)) goto overflow; + res <<= GMP_NUMB_BITS; + } +#endif + + if (neg) res = -res; + *i = res; + return 1; + } + +overflow: + if (neg && !mpz_cmp (mpz, mpz_int64_min)) { + /* No overflow afterall; it's MIN_INT64, the only valid integer + * whose absolute value is INT64_BITS long. */ + *i = MIN_INT64; + return 1; + } + *i = neg ? MIN_INT64 : MAX_INT64; + return 0; +} + +#endif /* INT64 */ + #else #define PUSH_REDUCED(o) push_object(o) #endif /* AUTO_BIGNUM */ @@ -174,19 +257,28 @@ void get_mpz_from_digits(MP_INT *tmp, } else if(base == 256) { - int i; - mpz_t digit; - - mpz_init(digit); - mpz_set_ui(tmp, 0); - for (i = 0; i < digits->len; i++) + if (digits->size_shift) + Pike_error("Invalid digits, cannot convert to Gmp.mpz.\n"); + +#ifdef HAVE_MPZ_IMPORT + mpz_import (tmp, digits->len, 1, 1, 0, 0, digits->str); +#else { - mpz_set_ui(digit, EXTRACT_UCHAR(digits->str + i)); - mpz_mul_2exp(digit, digit, - DO_NOT_WARN((unsigned long)(digits->len - i - 1) * 8)); - mpz_ior(tmp, tmp, digit); + int i; + mpz_t digit; + + mpz_init(digit); + mpz_set_ui(tmp, 0); + for (i = 0; i < digits->len; i++) + { + mpz_set_ui(digit, EXTRACT_UCHAR(digits->str + i)); + mpz_mul_2exp(digit, digit, + DO_NOT_WARN((unsigned long)(digits->len - i - 1) * 8)); + mpz_ior(tmp, tmp, digit); + } + mpz_clear(digit); } - mpz_clear(digit); +#endif } else { @@ -199,40 +291,23 @@ void get_new_mpz(MP_INT *tmp, struct svalue *s) switch(s->type) { case T_INT: -#ifdef BIG_PIKE_INT -/* INT_TYPE is bigger then long int */ - { - INT_TYPE x=s->u.integer; -#define SHIFT 24 -#define FILTER ((1<<SHIFT)-1) - int neg=0; - int pos=SHIFT; - if (x<0) neg=1,x=-x; - mpz_set_ui(tmp,(unsigned long int)(x&FILTER)); - while ( (x>>=SHIFT) ) - { - mpz_t t2,t1; - mpz_init_set_ui(t2,(unsigned long int)(x&FILTER)); - mpz_init(t1); - mpz_mul_2exp(t1,t2,pos); - mpz_add(t2,tmp,t1); - mpz_set(tmp,t2); - mpz_clear(t1); - mpz_clear(t2); - pos+=SHIFT; - } - if (neg) - { - mpz_t t1; - mpz_init_set(t1,tmp); - mpz_neg(tmp,t1); - mpz_clear(t1); - } - } -#undef SHIFT -#undef FILTER -#else +#if SIZEOF_INT_TYPE <= SIZEOF_LONG mpz_set_si(tmp, (signed long int) s->u.integer); +#elif defined (HAVE_MPZ_IMPORT) + mpz_import (tmp, 1, 1, SIZEOF_INT_TYPE, 0, 0, &s->u.integer); +#else + { + size_t n = + ((SIZEOF_INT_TYPE + SIZEOF_LONG - 1) / SIZEOF_LONG - 1) + /* The above is the position of the top unsigned long in the INT64. */ + * ULONG_BITS; + mpz_set_ui (mpz, (s->u.integer >> n) & ULONG_MAX); + while (n) { + n -= ULONG_BITS; + mpz_mul_2exp (mpz, mpz, ULONG_BITS); + mpz_add_ui (mpz, mpz, (s->u.integer >> n) & ULONG_MAX); + } + } #endif break; @@ -379,33 +454,18 @@ struct pike_string *low_get_mpz_digits(MP_INT *mpz, int base) else if (base == 256) { size_t i; -#if 0 - mpz_t tmp; -#endif if (mpz_sgn(mpz) < 0) Pike_error("Only non-negative numbers can be converted to base 256.\n"); -#if 0 - len = (mpz_sizeinbase(mpz, 2) + 7) / 8; - s = begin_shared_string(len); - mpz_init_set(tmp, mpz); - i = len; - while(i--) - { - s->str[i] = mpz_get_ui(tmp) & 0xff; - mpz_fdiv_q_2exp(tmp, tmp, 8); - } - mpz_clear(tmp); -#endif /* lets optimize this /Mirar & Per */ - /* len = mpz->_mp_size*sizeof(mp_limb_t); */ + /* len = mpz_size(mpz)*sizeof(mp_limb_t); */ /* This function should not return any leading zeros. /Nisse */ len = (mpz_sizeinbase(mpz, 2) + 7) / 8; s = begin_shared_string(len); - if (!mpz->_mp_size) + if (!mpz_size (mpz)) { /* Zero is a special case. There are no limbs at all, but * the size is still 1 bit, and one digit should be produced. */ @@ -413,12 +473,15 @@ struct pike_string *low_get_mpz_digits(MP_INT *mpz, int base) Pike_fatal("mpz->low_get_mpz_digits: strange mpz state!\n"); s->str[0] = 0; } else { - mp_limb_t *src = mpz->_mp_d; +#if GMP_NUMB_BITS != SIZEOF_MP_LIMB_T * CHAR_BIT +#error Cannot cope with GMP using nail bits. +#endif + size_t pos = 0; unsigned char *dst = (unsigned char *)s->str+s->len; while (len > 0) { - mp_limb_t x=*(src++); + mp_limb_t x = mpz_getlimbn (mpz, pos++); for (i=0; i<sizeof(mp_limb_t); i++) { *(--dst) = DO_NOT_WARN((unsigned char)(x & 0xff)); @@ -557,12 +620,12 @@ static void mpzmod__sprintf(INT32 args) { INT_TYPE length, neg = 0; unsigned char *dst; - mp_limb_t *src; + size_t pos; mpz_t tmp; MP_INT *n; INT_TYPE i; - length = THIS->_mp_size; + length = mpz_size (THIS); if(width_undecided) { @@ -577,7 +640,7 @@ static void mpzmod__sprintf(INT32 args) { mpz_init_set(tmp, THIS); mpz_add_ui(tmp, tmp, 1); - length = -tmp->_mp_size; + length = -mpz_size (tmp); n = tmp; neg = 1; } @@ -593,12 +656,15 @@ static void mpzmod__sprintf(INT32 args) dst = (unsigned char *)STR0(s) + width; else dst = (unsigned char *)STR0(s); - - src = n->_mp_d; + pos = 0; while(width > 0) { - mp_limb_t x = (length-->0? *(src++) : 0); +#if GMP_NUMB_BITS != SIZEOF_MP_LIMB_T * CHAR_BIT +#error Cannot cope with GMP using nail bits. +#endif + + mp_limb_t x = (length-->0? mpz_getlimbn(n, pos++) : 0); if (!flag_left) for(i = 0; i < (INT_TYPE)sizeof(mp_limb_t); i++) @@ -1575,7 +1641,9 @@ static void mpzmod_popcount(INT32 args) #endif break; default: +#ifdef PIKE_DEBUG Pike_fatal("Gmp.mpz->popcount: Unexpected sign!\n"); +#endif } #endif } @@ -1644,6 +1712,10 @@ PIKE_MODULE_EXIT free_program(bignum_program); bignum_program=0; } + mpz_clear (mpz_int_type_min); +#ifdef INT64 + mpz_clear (mpz_int64_min); +#endif #endif #endif } @@ -1788,12 +1860,24 @@ PIKE_MODULE_INIT PROGRAM_NO_WEAK_FREE | PROGRAM_NO_EXPLICIT_DESTRUCT | PROGRAM_CONSTANT ; + + mpz_init (mpz_int_type_min); + mpz_setbit (mpz_int_type_min, INT_TYPE_BITS); + mpz_neg (mpz_int_type_min, mpz_int_type_min); /* Magic hook in... */ free_svalue(&auto_bignum_program); add_ref(auto_bignum_program.u.program = bignum_program); auto_bignum_program.type = PIKE_T_PROGRAM; +#ifdef INT64 + mpz_init (mpz_int64_min); + mpz_setbit (mpz_int64_min, INT64_BITS); + mpz_neg (mpz_int64_min, mpz_int64_min); + push_int64 = gmp_push_int64; + int64_from_bignum = gmp_int64_from_bignum; +#endif + #if 0 /* magic /Hubbe * This seems to break more than it fixes though... /Hubbe diff --git a/src/modules/Gmp/my_gmp.h b/src/modules/Gmp/my_gmp.h index 8ce01191d9..95fb87a30d 100644 --- a/src/modules/Gmp/my_gmp.h +++ b/src/modules/Gmp/my_gmp.h @@ -2,7 +2,7 @@ || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. -|| $Id: my_gmp.h,v 1.15 2003/03/26 15:23:39 mast Exp $ +|| $Id: my_gmp.h,v 1.16 2003/03/28 15:51:40 mast Exp $ */ /* @@ -55,6 +55,10 @@ struct pike_string *low_get_mpz_digits(MP_INT *mpz, int base); #define mpz_xor my_mpz_xor #endif +#ifndef HAVE_MPZ_GETLIMBN +#define mpz_getlimbn(mpz, pos) ((mpz)->_mp_d[pos]) +#endif + extern struct program *mpzmod_program; extern struct program *mpq_program; extern struct program *mpf_program; @@ -75,6 +79,20 @@ extern struct program *bignum_program; #define OBTOMPQ(o) ((MP_RAT *)(o->storage)) #define OBTOMPF(o) ((MP_FLT *)(o->storage)) +#ifndef GMP_NUMB_BITS +#define GMP_NUMB_BITS (SIZEOF_MP_LIMB_T * CHAR_BIT) +#endif +#ifndef GMP_NUMB_MASK +#define GMP_NUMB_MASK ((mp_limb_t) -1) +#endif + +/* Bits excluding the sign bit, if any. */ +#define ULONG_BITS (SIZEOF_LONG * 8) +#define INT_TYPE_BITS (SIZEOF_INT_TYPE * CHAR_BIT - 1) +#ifdef INT64 +#define INT64_BITS (SIZEOF_INT64 * CHAR_BIT - 1) +#endif + /* MPQ protos */ void pike_init_mpq_module(void); void pike_exit_mpq_module(void); -- GitLab