/* mpz_cdiv_r_2exp, mpz_fdiv_r_2exp -- remainder from mpz divided by 2^n. Copyright 2001, 2002, 2004 Free Software Foundation, Inc. This file is part of the GNU MP Library. The GNU MP Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The GNU MP Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */ #include "gmp.h" #include "gmp-impl.h" /* Bit mask of "n" least significant bits of a limb. */ #define LOW_MASK(n) ((CNST_LIMB(1) << (n)) - 1) /* dir==1 for ceil, dir==-1 for floor */ static void __gmpz_cfdiv_r_2exp __GMP_PROTO ((REGPARM_3_1 (mpz_ptr w, mpz_srcptr u, unsigned long cnt, int dir))) REGPARM_ATTR (1); #define cfdiv_r_2exp(w,u,cnt,dir) __gmpz_cfdiv_r_2exp (REGPARM_3_1 (w, u, cnt, dir)) REGPARM_ATTR (1) static void cfdiv_r_2exp (mpz_ptr w, mpz_srcptr u, unsigned long cnt, int dir) { mp_size_t usize, abs_usize, limb_cnt, i; mp_srcptr up; mp_ptr wp; mp_limb_t high; usize = SIZ(u); if (usize == 0) { SIZ(w) = 0; return; } limb_cnt = cnt / GMP_NUMB_BITS; cnt %= GMP_NUMB_BITS; abs_usize = ABS (usize); /* MPZ_REALLOC(w) below is only when w!=u, so we can fetch PTR(u) here nice and early */ up = PTR(u); if ((usize ^ dir) < 0) { /* Round towards zero, means just truncate */ if (w == u) { /* if already smaller than limb_cnt then do nothing */ if (abs_usize <= limb_cnt) return; wp = PTR(w); } else { i = MIN (abs_usize, limb_cnt+1); MPZ_REALLOC (w, i); wp = PTR(w); MPN_COPY (wp, up, i); /* if smaller than limb_cnt then only the copy is needed */ if (abs_usize <= limb_cnt) { SIZ(w) = usize; return; } } } else { /* Round away from zero, means twos complement if non-zero */ /* if u!=0 and smaller than divisor, then must negate */ if (abs_usize <= limb_cnt) goto negate; /* if non-zero low limb, then must negate */ for (i = 0; i < limb_cnt; i++) if (up[i] != 0) goto negate; /* if non-zero partial limb, then must negate */ if ((up[limb_cnt] & LOW_MASK (cnt)) != 0) goto negate; /* otherwise low bits of u are zero, so that's the result */ SIZ(w) = 0; return; negate: /* twos complement negation to get 2**cnt-u */ MPZ_REALLOC (w, limb_cnt+1); up = PTR(u); wp = PTR(w); /* Ones complement */ i = MIN (abs_usize, limb_cnt+1); mpn_com_n (wp, up, i); for ( ; i <= limb_cnt; i++) wp[i] = GMP_NUMB_MAX; /* Twos complement. Since u!=0 in the relevant part, the twos complement never gives 0 and a carry, so can use MPN_INCR_U. */ MPN_INCR_U (wp, limb_cnt+1, CNST_LIMB(1)); usize = -usize; } /* Mask the high limb */ high = wp[limb_cnt]; high &= LOW_MASK (cnt); wp[limb_cnt] = high; /* Strip any consequent high zeros */ while (high == 0) { limb_cnt--; if (limb_cnt < 0) { SIZ(w) = 0; return; } high = wp[limb_cnt]; } limb_cnt++; SIZ(w) = (usize >= 0 ? limb_cnt : -limb_cnt); } void mpz_cdiv_r_2exp (mpz_ptr w, mpz_srcptr u, unsigned long cnt) { cfdiv_r_2exp (w, u, cnt, 1); } void mpz_fdiv_r_2exp (mpz_ptr w, mpz_srcptr u, unsigned long cnt) { cfdiv_r_2exp (w, u, cnt, -1); }