[Gmp-commit] /var/hg/gmp-proj/mini-gmp: scan[01] copy-pasted from GMP, to be ...
mercurial at gmplib.org
mercurial at gmplib.org
Thu Jan 12 13:15:39 CET 2012
details: /var/hg/gmp-proj/mini-gmp/rev/aa34d3a5c1e6
changeset: 72:aa34d3a5c1e6
user: Marco Bodrato <bodrato at mail.dm.unipi.it>
date: Thu Jan 12 13:15:36 2012 +0100
description:
scan[01] copy-pasted from GMP, to be simplified?
diffstat:
mini-gmp.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
mini-gmp.h | 2 +
2 files changed, 212 insertions(+), 3 deletions(-)
diffs (242 lines):
diff -r 8e338a441f3b -r aa34d3a5c1e6 mini-gmp.c
--- a/mini-gmp.c Thu Jan 12 10:18:01 2012 +0100
+++ b/mini-gmp.c Thu Jan 12 13:15:36 2012 +0100
@@ -39,9 +39,7 @@
mpz_export
mpz_fdiv_q_2exp
mpz_fdiv_r_2exp
- mpz_import
- mpz_scan0
- mpz_scan1.
+ mpz_import.
*/
#include <assert.h>
@@ -3365,6 +3363,215 @@
return c;
}
+mp_bitcnt_t
+mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit)
+{
+ mp_limb_t *u_ptr;
+ mp_size_t size;
+ mp_size_t abs_size;
+ mp_limb_t *u_end;
+ mp_size_t starting_limb;
+ mp_limb_t *p;
+ mp_limb_t limb;
+ int cnt;
+
+ u_ptr = u->_mp_d;
+ size = u->_mp_size;
+ abs_size = GMP_ABS (size);
+ u_end = u_ptr + abs_size;
+ starting_limb = starting_bit / GMP_LIMB_BITS;
+ p = u_ptr + starting_limb;
+
+ /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit for u<0.
+ Notice this test picks up any u==0 too. */
+ if (starting_limb >= abs_size)
+ return (size >= 0 ? ULONG_MAX : starting_bit);
+
+ limb = *p;
+
+ if (size >= 0)
+ {
+ /* Mask to 0 all bits before starting_bit, thus ignoring them. */
+ limb &= (GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS));
+
+ if (limb == 0)
+ {
+ /* If it's the high limb which is zero after masking, then there's
+ no 1 bits after starting_bit. */
+ p++;
+ if (p == u_end)
+ return ULONG_MAX;
+
+ /* Otherwise search further for a non-zero limb. The high limb is
+ non-zero, if nothing else. */
+ for (;;)
+ {
+ limb = *p;
+ if (limb != 0)
+ break;
+ p++;
+ }
+ }
+ }
+ else
+ {
+ mp_srcptr q;
+
+ /* If there's a non-zero limb before ours then we're in the ones
+ complement region. Search from *(p-1) downwards since that might
+ give better cache locality, and since a non-zero in the middle of a
+ number is perhaps a touch more likely than at the end. */
+ q = p;
+ while (q != u_ptr)
+ {
+ q--;
+ if (*q != 0)
+ goto inverted;
+ }
+
+ if (limb == 0)
+ {
+ /* Skip zero limbs, to find the start of twos complement. The
+ high limb is non-zero, if nothing else. This search is
+ necessary so the -limb is applied at the right spot. */
+ do
+ {
+ p++;
+ limb = *p;
+ }
+ while (limb == 0);
+
+ /* Apply twos complement, and look for a 1 bit in that. Since
+ limb!=0 here, also have (-limb)!=0 so there's certainly a 1
+ bit. */
+ limb = -limb;
+ goto got_limb;
+ }
+
+ /* Adjust so ~limb implied by searching for 0 bit becomes -limb. */
+ limb--;
+
+ inverted:
+ /* Now seeking a 0 bit. */
+
+ /* Mask to 1 all bits before starting_bit, thus ignoring them. */
+ limb |= (1UL << (starting_bit % GMP_LIMB_BITS)) - 1;
+
+ /* Search for a limb which is not all ones. If the end is reached
+ then the zero immediately past the end is the result. */
+ while (limb == GMP_LIMB_MAX)
+ {
+ p++;
+ if (p == u_end)
+ return (mp_bitcnt_t) abs_size * GMP_LIMB_BITS;
+ limb = *p;
+ }
+
+ /* Now seeking low 1 bit. */
+ limb = ~limb;
+ }
+
+ got_limb:
+ gmp_ctz (cnt, limb);
+ return (mp_bitcnt_t) (p - u_ptr) * GMP_LIMB_BITS + cnt;
+}
+
+mp_bitcnt_t
+mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit)
+{
+ mp_limb_t *u_ptr;
+ mp_size_t size;
+ mp_size_t abs_size;
+ mp_limb_t *u_end;
+ mp_size_t starting_limb;
+ mp_limb_t *p;
+ mp_limb_t limb;
+ int cnt;
+
+ u_ptr = u->_mp_d;
+ size = u->_mp_size;
+ abs_size = GMP_ABS (size);
+ u_end = u_ptr + abs_size;
+ starting_limb = starting_bit / GMP_LIMB_BITS;
+ p = u_ptr + starting_limb;
+
+ /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for
+ u<0. Notice this test picks up all cases of u==0 too. */
+ if (starting_limb >= abs_size)
+ return (size >= 0 ? starting_bit : ULONG_MAX);
+
+ limb = *p;
+
+ if (size >= 0)
+ {
+ /* Mask to 1 all bits before starting_bit, thus ignoring them. */
+ limb |= (1UL << (starting_bit % GMP_LIMB_BITS)) - 1;
+
+ /* Search for a limb which isn't all ones. If the end is reached then
+ the zero bit immediately past the end is returned. */
+ while (limb == GMP_LIMB_MAX)
+ {
+ p++;
+ if (p == u_end)
+ return (mp_bitcnt_t) abs_size * GMP_LIMB_BITS;
+ limb = *p;
+ }
+
+ /* Now seek low 1 bit. */
+ limb = ~limb;
+ }
+ else
+ {
+ mp_srcptr q;
+
+ /* If there's a non-zero limb before ours then we're in the ones
+ complement region. Search from *(p-1) downwards since that might
+ give better cache locality, and since a non-zero in the middle of a
+ number is perhaps a touch more likely than at the end. */
+ q = p;
+ while (q != u_ptr)
+ {
+ q--;
+ if (*q != 0)
+ goto inverted;
+ }
+
+ /* Adjust so ~limb implied by searching for 1 bit below becomes -limb.
+ If limb==0 here then this isn't the beginning of twos complement
+ inversion, but that doesn't matter because limb==0 is a zero bit
+ immediately (-1 is all ones for below). */
+ limb--;
+
+ inverted:
+ /* Now seeking a 1 bit. */
+
+ /* Mask to 0 all bits before starting_bit, thus ignoring them. */
+ limb &= (GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS));
+
+ if (limb == 0)
+ {
+ /* If the high limb is zero after masking, then no 1 bits past
+ starting_bit. */
+ p++;
+ if (p == u_end)
+ return ULONG_MAX;
+
+ /* Search further for a non-zero limb. The high limb is non-zero,
+ if nothing else. */
+ for (;;)
+ {
+ limb = *p;
+ if (limb != 0)
+ break;
+ p++;
+ }
+ }
+ }
+
+ gmp_ctz (cnt, limb);
+ return (mp_bitcnt_t) (p - u_ptr) * GMP_LIMB_BITS + cnt;
+}
+
/* MPZ base conversion. */
diff -r 8e338a441f3b -r aa34d3a5c1e6 mini-gmp.h
--- a/mini-gmp.h Thu Jan 12 10:18:01 2012 +0100
+++ b/mini-gmp.h Thu Jan 12 13:15:36 2012 +0100
@@ -164,6 +164,8 @@
mp_bitcnt_t mpz_popcount (const mpz_t);
mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t);
+mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t);
+mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t);
int mpz_fits_slong_p (const mpz_t);
int mpz_fits_ulong_p (const mpz_t);
More information about the gmp-commit
mailing list