[BACK]Return to n_pow_ui.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gmp / mpz

Annotation of OpenXM_contrib/gmp/mpz/n_pow_ui.c, Revision 1.1.1.1

1.1       ohara       1: /* mpz_n_pow_ui -- mpn raised to ulong.
                      2:
                      3: Copyright 2001, 2002 Free Software Foundation, Inc.
                      4:
                      5: This file is part of the GNU MP Library.
                      6:
                      7: The GNU MP Library is free software; you can redistribute it and/or modify
                      8: it under the terms of the GNU Lesser General Public License as published by
                      9: the Free Software Foundation; either version 2.1 of the License, or (at your
                     10: option) any later version.
                     11:
                     12: The GNU MP Library is distributed in the hope that it will be useful, but
                     13: WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
                     14: or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
                     15: License for more details.
                     16:
                     17: You should have received a copy of the GNU Lesser General Public License
                     18: along with the GNU MP Library; see the file COPYING.LIB.  If not, write to
                     19: the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
                     20: MA 02111-1307, USA. */
                     21:
                     22: #include "gmp.h"
                     23: #include "gmp-impl.h"
                     24: #include "longlong.h"
                     25:
                     26:
                     27: /* Change this to "#define TRACE(x) x" for some traces. */
                     28: #define TRACE(x)
                     29:
                     30:
                     31: /* Use this to test the mul_2 code on a CPU without a native version of that
                     32:    routine.  */
                     33: #if 0
                     34: #define mpn_mul_2  refmpn_mul_2
                     35: #define HAVE_NATIVE_mpn_mul_2  1
                     36: #endif
                     37:
                     38:
                     39: /* mpz_pow_ui and mpz_ui_pow_ui want to share almost all of this code.
                     40:    ui_pow_ui doesn't need the mpn_mul based powering loop or the tests on
                     41:    bsize==2 or >2, but separating that isn't easy because there's shared
                     42:    code both before and after (the size calculations and the powers of 2
                     43:    handling).
                     44:
                     45:    Alternatives:
                     46:
                     47:    It would work to just use the mpn_mul powering loop for 1 and 2 limb
                     48:    bases, but the current separate loop allows mul_1 and mul_2 to be done
                     49:    in-place, which might help cache locality a bit.  If mpn_mul was relaxed
                     50:    to allow source==dest when vn==1 or 2 then some pointer twiddling might
                     51:    let us get the same effect in one loop.
                     52:
                     53:    The initial powering for bsize==1 into blimb or blimb:blimb_low doesn't
                     54:    form the biggest possible power of b that fits, only the biggest power of
                     55:    2 power, ie. b^(2^n).  It'd be possible to choose a bigger power, perhaps
                     56:    using __mp_bases[b].big_base for small b, and thereby get better value
                     57:    from mpn_mul_1 or mpn_mul_2 in the bignum powering.  It's felt that doing
                     58:    so would be more complicated than it's worth, and could well end up being
                     59:    a slowdown for small e.  For big e on the other hand the algorithm is
                     60:    dominated by mpn_sqr_n so there wouldn't much of a saving.  The current
                     61:    code can be viewed as simply doing the first few steps of the powering in
                     62:    a single or double limb where possible.
                     63:
                     64:    If r==b, and blow_twos==0, and r must be realloc'ed, then the temporary
                     65:    copy made of b is unnecessary.  We could just use the old alloc'ed block
                     66:    and free it at the end.  But arranging this seems like a lot more trouble
                     67:    than it's worth.  */
                     68:
                     69:
                     70: /* floor(sqrt(GMP_NUMB_MAX)), ie. the biggest value that can be squared in
                     71:    a limb without overflowing.
                     72:    FIXME: This formula is an underestimate when GMP_NUMB_BITS is odd. */
                     73:
                     74: #define GMP_NUMB_HALFMAX  (((mp_limb_t) 1 << GMP_NUMB_BITS/2) - 1)
                     75:
                     76:
                     77: /* The following are for convenience, they update the size and check the
                     78:    alloc.  */
                     79:
                     80: #define MPN_SQR_N(dst, alloc, src, size)        \
                     81:   do {                                          \
                     82:     ASSERT (2*(size) <= (alloc));               \
                     83:     mpn_sqr_n (dst, src, size);                 \
                     84:     (size) *= 2;                                \
                     85:     (size) -= ((dst)[(size)-1] == 0);           \
                     86:   } while (0)
                     87:
                     88: #define MPN_MUL(dst, alloc, src, size, src2, size2)     \
                     89:   do {                                                  \
                     90:     mp_limb_t  cy;                                      \
                     91:     ASSERT ((size) + (size2) <= (alloc));               \
                     92:     cy = mpn_mul (dst, src, size, src2, size2);         \
                     93:     (size) += (size2) - (cy == 0);                      \
                     94:   } while (0)
                     95:
                     96: #define MPN_MUL_2(ptr, size, alloc, mult)       \
                     97:   do {                                          \
                     98:     mp_limb_t  cy;                              \
                     99:     ASSERT ((size)+2 <= (alloc));               \
                    100:     cy = mpn_mul_2 (ptr, ptr, size, mult);      \
                    101:     (size)++;                                   \
                    102:     (ptr)[(size)] = cy;                         \
                    103:     (size) += (cy != 0);                        \
                    104:   } while (0)
                    105:
                    106: #define MPN_MUL_1(ptr, size, alloc, limb)       \
                    107:   do {                                          \
                    108:     mp_limb_t  cy;                              \
                    109:     ASSERT ((size)+1 <= (alloc));               \
                    110:     cy = mpn_mul_1 (ptr, ptr, size, limb);      \
                    111:     (ptr)[size] = cy;                           \
                    112:     (size) += (cy != 0);                        \
                    113:   } while (0)
                    114:
                    115: #define MPN_LSHIFT(ptr, size, alloc, shift)     \
                    116:   do {                                          \
                    117:     mp_limb_t  cy;                              \
                    118:     ASSERT ((size)+1 <= (alloc));               \
                    119:     cy = mpn_lshift (ptr, ptr, size, shift);    \
                    120:     (ptr)[size] = cy;                           \
                    121:     (size) += (cy != 0);                        \
                    122:   } while (0)
                    123:
                    124: #define MPN_RSHIFT_OR_COPY(dst, src, size, shift)       \
                    125:   do {                                                  \
                    126:     if ((shift) == 0)                                   \
                    127:       MPN_COPY (dst, src, size);                        \
                    128:     else                                                \
                    129:       {                                                 \
                    130:         mpn_rshift (dst, src, size, shift);             \
                    131:         (size) -= ((dst)[(size)-1] == 0);               \
                    132:       }                                                 \
                    133:   } while (0)
                    134:
                    135:
                    136: /* ralloc and talloc are only wanted for ASSERTs, after the initial space
                    137:    allocations.  Avoid writing values to them in a normal build, to ensure
                    138:    the compiler lets them go dead.  gcc already figures this out itself
                    139:    actually.  */
                    140:
                    141: #define SWAP_RP_TP                                      \
                    142:   do {                                                  \
                    143:     MP_PTR_SWAP (rp, tp);                               \
                    144:     ASSERT_CODE (MP_SIZE_T_SWAP (ralloc, talloc));      \
                    145:   } while (0)
                    146:
                    147:
                    148: void
                    149: mpz_n_pow_ui (mpz_ptr r, mp_srcptr bp, mp_size_t bsize, unsigned long int e)
                    150: {
                    151:   mp_ptr         rp;
                    152:   mp_size_t      rtwos_limbs, ralloc, rsize;
                    153:   int            rneg, i, cnt, btwos, r_bp_overlap;
                    154:   mp_limb_t      blimb, rl;
                    155:   unsigned long  rtwos_bits;
                    156: #if HAVE_NATIVE_mpn_mul_2
                    157:   mp_limb_t      blimb_low, rl_high;
                    158: #else
                    159:   mp_limb_t      b_twolimbs[2];
                    160: #endif
                    161:   TMP_DECL (marker);
                    162:
                    163:   TRACE (printf ("mpz_n_pow_ui rp=0x%lX bp=0x%lX bsize=%ld e=%lu (0x%lX)\n",
                    164:                  PTR(r), bp, bsize, e, e);
                    165:          mpn_trace ("b", bp, bsize));
                    166:
                    167:   ASSERT (bsize == 0 || bp[ABS(bsize)-1] != 0);
                    168:   ASSERT (MPN_SAME_OR_SEPARATE2_P (PTR(r), ABSIZ(r), bp, bsize));
                    169:
                    170:   /* b^0 == 1, including 0^0 == 1 */
                    171:   if (e == 0)
                    172:     {
                    173:       PTR(r)[0] = 1;
                    174:       SIZ(r) = 1;
                    175:       return;
                    176:     }
                    177:
                    178:   /* 0^e == 0 apart from 0^0 above */
                    179:   if (bsize == 0)
                    180:     {
                    181:       SIZ(r) = 0;
                    182:       return;
                    183:     }
                    184:
                    185:   /* Sign of the final result. */
                    186:   rneg = (bsize < 0 && (e & 1) != 0);
                    187:   bsize = ABS (bsize);
                    188:   TRACE (printf ("rneg %d\n", rneg));
                    189:
                    190:   r_bp_overlap = (PTR(r) == bp);
                    191:
                    192:   /* Strip low zero limbs from b. */
                    193:   rtwos_limbs = 0;
                    194:   for (blimb = *bp; blimb == 0; blimb = *++bp)
                    195:     {
                    196:       rtwos_limbs += e;
                    197:       bsize--; ASSERT (bsize >= 1);
                    198:     }
                    199:   TRACE (printf ("trailing zero rtwos_limbs=%ld\n", rtwos_limbs));
                    200:
                    201:   /* Strip low zero bits from b. */
                    202:   count_trailing_zeros (btwos, blimb);
                    203:   blimb >>= btwos;
                    204:   rtwos_bits = e * btwos;
                    205:   rtwos_limbs += rtwos_bits / GMP_NUMB_BITS;
                    206:   rtwos_bits %= GMP_NUMB_BITS;
                    207:   TRACE (printf ("trailing zero btwos=%d rtwos_limbs=%ld rtwos_bits=%lu\n",
                    208:                  btwos, rtwos_limbs, rtwos_bits));
                    209:
                    210:   TMP_MARK (marker);
                    211:
                    212:   rl = 1;
                    213: #if HAVE_NATIVE_mpn_mul_2
                    214:   rl_high = 0;
                    215: #endif
                    216:
                    217:   if (bsize == 1)
                    218:     {
                    219:     bsize_1:
                    220:       /* Power up as far as possible within blimb.  We start here with e!=0,
                    221:          but if e is small then we might reach e==0 and the whole b^e in rl.
                    222:          Notice this code works when blimb==1 too, reaching e==0.  */
                    223:
                    224:       while (blimb <= GMP_NUMB_HALFMAX)
                    225:         {
                    226:           TRACE (printf ("small e=0x%lX blimb=0x%lX rl=0x%lX\n",
                    227:                          e, blimb, rl));
                    228:           ASSERT (e != 0);
                    229:           if ((e & 1) != 0)
                    230:             rl *= blimb;
                    231:           e >>= 1;
                    232:           if (e == 0)
                    233:             goto got_rl;
                    234:           blimb *= blimb;
                    235:         }
                    236:
                    237: #if HAVE_NATIVE_mpn_mul_2
                    238:       TRACE (printf ("single power, e=0x%lX b=0x%lX rl=0x%lX\n",
                    239:                      e, blimb, rl));
                    240:
                    241:       /* Can power b once more into blimb:blimb_low */
                    242:       bsize = 2;
                    243:       ASSERT (e != 0);
                    244:       if ((e & 1) != 0)
                    245:        {
                    246:          umul_ppmm (rl_high, rl, rl, blimb << GMP_NAIL_BITS);
                    247:          rl >>= GMP_NAIL_BITS;
                    248:        }
                    249:       e >>= 1;
                    250:       umul_ppmm (blimb, blimb_low, blimb, blimb << GMP_NAIL_BITS);
                    251:       blimb_low >>= GMP_NAIL_BITS;
                    252:
                    253:     got_rl:
                    254:       TRACE (printf ("double power e=0x%lX blimb=0x%lX:0x%lX rl=0x%lX:%lX\n",
                    255:                      e, blimb, blimb_low, rl_high, rl));
                    256:
                    257:       /* Combine left-over rtwos_bits into rl_high:rl to be handled by the
                    258:          final mul_1 or mul_2 rather than a separate lshift.
                    259:          - rl_high:rl mustn't be 1 (since then there's no final mul)
                    260:          - rl_high mustn't overflow
                    261:          - rl_high mustn't change to non-zero, since mul_1+lshift is
                    262:          probably faster than mul_2 (FIXME: is this true?)  */
                    263:
                    264:       if (rtwos_bits != 0
                    265:           && ! (rl_high == 0 && rl == 1)
                    266:           && (rl_high >> (GMP_NUMB_BITS-rtwos_bits)) == 0)
                    267:         {
                    268:           mp_limb_t  new_rl_high = (rl_high << rtwos_bits)
                    269:             | (rl >> (GMP_NUMB_BITS-rtwos_bits));
                    270:           if (! (rl_high == 0 && new_rl_high != 0))
                    271:             {
                    272:               rl_high = new_rl_high;
                    273:               rl <<= rtwos_bits;
                    274:               rtwos_bits = 0;
                    275:               TRACE (printf ("merged rtwos_bits, rl=0x%lX:%lX\n",
                    276:                              rl_high, rl));
                    277:             }
                    278:         }
                    279: #else
                    280:     got_rl:
                    281:       TRACE (printf ("small power e=0x%lX blimb=0x%lX rl=0x%lX\n",
                    282:                      e, blimb, rl));
                    283:
                    284:       /* Combine left-over rtwos_bits into rl to be handled by the final
                    285:          mul_1 rather than a separate lshift.
                    286:          - rl mustn't be 1 (since then there's no final mul)
                    287:          - rl mustn't overflow  */
                    288:
                    289:       if (rtwos_bits != 0
                    290:           && rl != 1
                    291:           && (rl >> (GMP_NUMB_BITS-rtwos_bits)) == 0)
                    292:         {
                    293:           rl <<= rtwos_bits;
                    294:           rtwos_bits = 0;
                    295:           TRACE (printf ("merged rtwos_bits, rl=0x%lX\n", rl));
                    296:         }
                    297: #endif
                    298:     }
                    299:   else if (bsize == 2)
                    300:     {
                    301:       mp_limb_t  bsecond = bp[1];
                    302:       if (btwos != 0)
                    303:         blimb |= (bsecond << (GMP_NUMB_BITS - btwos)) & GMP_NUMB_MASK;
                    304:       bsecond >>= btwos;
                    305:       if (bsecond == 0)
                    306:         {
                    307:           /* Two limbs became one after rshift. */
                    308:           bsize = 1;
                    309:           goto bsize_1;
                    310:         }
                    311:
                    312:       TRACE (printf ("bsize==2 using b=0x%lX:%lX", bsecond, blimb));
                    313: #if HAVE_NATIVE_mpn_mul_2
                    314:       blimb_low = blimb;
                    315: #else
                    316:       bp = b_twolimbs;
                    317:       b_twolimbs[0] = blimb;
                    318:       b_twolimbs[1] = bsecond;
                    319: #endif
                    320:       blimb = bsecond;
                    321:     }
                    322:   else
                    323:     {
                    324:       if (r_bp_overlap || btwos != 0)
                    325:         {
                    326:           mp_ptr tp = TMP_ALLOC_LIMBS (bsize);
                    327:           MPN_RSHIFT_OR_COPY (tp, bp, bsize, btwos);
                    328:           bp = tp;
                    329:           TRACE (printf ("rshift or copy bp,bsize, new bsize=%ld\n", bsize));
                    330:         }
                    331: #if HAVE_NATIVE_mpn_mul_2
                    332:       /* in case 3 limbs rshift to 2 and hence use the mul_2 loop below */
                    333:       blimb_low = bp[0];
                    334: #endif
                    335:       blimb = bp[bsize-1];
                    336:
                    337:       TRACE (printf ("big bsize=%ld  ", bsize);
                    338:              mpn_trace ("b", bp, bsize));
                    339:     }
                    340:
                    341:   /* At this point blimb is the most significant limb of the base to use.
                    342:
                    343:      Each factor of b takes (bsize*BPML-cnt) bits and there's e of them; +1
                    344:      limb to round up the division; +1 for multiplies all using an extra
                    345:      limb over the true size; +2 for rl at the end; +1 for lshift at the
                    346:      end.
                    347:
                    348:      The size calculation here is reasonably accurate.  The base is at least
                    349:      half a limb, so in 32 bits the worst case is 2^16+1 treated as 17 bits
                    350:      when it will power up as just over 16, an overestimate of 17/16 =
                    351:      6.25%.  For a 64-bit limb it's half that.
                    352:
                    353:      If e==0 then blimb won't be anything useful (though it will be
                    354:      non-zero), but that doesn't matter since we just end up with ralloc==5,
                    355:      and that's fine for 2 limbs of rl and 1 of lshift.  */
                    356:
                    357:   ASSERT (blimb != 0);
                    358:   count_leading_zeros (cnt, blimb);
                    359:   ralloc = (bsize*GMP_NUMB_BITS - cnt + GMP_NAIL_BITS) * e / GMP_NUMB_BITS + 5;
                    360:   TRACE (printf ("ralloc %ld, from bsize=%ld blimb=0x%lX cnt=%d\n",
                    361:                  ralloc, bsize, blimb, cnt));
                    362:   MPZ_REALLOC (r, ralloc + rtwos_limbs);
                    363:   rp = PTR(r);
                    364:
                    365:   /* Low zero limbs resulting from powers of 2. */
                    366:   MPN_ZERO (rp, rtwos_limbs);
                    367:   rp += rtwos_limbs;
                    368:
                    369:   if (e == 0)
                    370:     {
                    371:       /* Any e==0 other than via bsize==1 or bsize==2 is covered at the
                    372:          start. */
                    373:       rp[0] = rl;
                    374:       rsize = 1;
                    375: #if HAVE_NATIVE_mpn_mul_2
                    376:       rp[1] = rl_high;
                    377:       rsize += (rl_high != 0);
                    378: #endif
                    379:       ASSERT (rp[rsize-1] != 0);
                    380:     }
                    381:   else
                    382:     {
                    383:       mp_ptr     tp;
                    384:       mp_size_t  talloc;
                    385:
                    386:       /* In the mpn_mul_1 or mpn_mul_2 loops or in the mpn_mul loop when the
                    387:          low bit of e is zero, tp only has to hold the second last power
                    388:          step, which is half the size of the final result.  There's no need
                    389:          to round up the divide by 2, since ralloc includes a +2 for rl
                    390:          which not needed by tp.  In the mpn_mul loop when the low bit of e
                    391:          is 1, tp must hold nearly the full result, so just size it the same
                    392:          as rp.  */
                    393:
                    394:       talloc = ralloc;
                    395: #if HAVE_NATIVE_mpn_mul_2
                    396:       if (bsize <= 2 || (e & 1) == 0)
                    397:         talloc /= 2;
                    398: #else
                    399:       if (bsize <= 1 || (e & 1) == 0)
                    400:         talloc /= 2;
                    401: #endif
                    402:       TRACE (printf ("talloc %ld\n", talloc));
                    403:       tp = TMP_ALLOC_LIMBS (talloc);
                    404:
                    405:       /* Go from high to low over the bits of e, starting with i pointing at
                    406:          the bit below the highest 1 (which will mean i==-1 if e==1).  */
                    407:       count_leading_zeros (cnt, e);
                    408:       i = GMP_LIMB_BITS - cnt - 2;
                    409:
                    410: #if HAVE_NATIVE_mpn_mul_2
                    411:       if (bsize <= 2)
                    412:         {
                    413:           mp_limb_t  mult[2];
                    414:
                    415:           /* Any bsize==1 will have been powered above to be two limbs. */
                    416:           ASSERT (bsize == 2);
                    417:           ASSERT (blimb != 0);
                    418:
                    419:           /* Arrange the final result ends up in r, not in the temp space */
                    420:           if ((i & 1) == 0)
                    421:             SWAP_RP_TP;
                    422:
                    423:           rp[0] = blimb_low;
                    424:           rp[1] = blimb;
                    425:           rsize = 2;
                    426:
                    427:           mult[0] = blimb_low;
                    428:           mult[1] = blimb;
                    429:
                    430:           for ( ; i >= 0; i--)
                    431:             {
                    432:               TRACE (printf ("mul_2 loop i=%d e=0x%lX, rsize=%ld ralloc=%ld talloc=%ld\n",
                    433:                              i, e, rsize, ralloc, talloc);
                    434:                      mpn_trace ("r", rp, rsize));
                    435:
                    436:               MPN_SQR_N (tp, talloc, rp, rsize);
                    437:               SWAP_RP_TP;
                    438:               if ((e & (1L << i)) != 0)
                    439:                 MPN_MUL_2 (rp, rsize, ralloc, mult);
                    440:             }
                    441:
                    442:           TRACE (mpn_trace ("mul_2 before rl, r", rp, rsize));
                    443:           if (rl_high != 0)
                    444:             {
                    445:               mult[0] = rl;
                    446:               mult[1] = rl_high;
                    447:               MPN_MUL_2 (rp, rsize, ralloc, mult);
                    448:             }
                    449:           else if (rl != 1)
                    450:             MPN_MUL_1 (rp, rsize, ralloc, rl);
                    451:         }
                    452: #else
                    453:       if (bsize == 1)
                    454:         {
                    455:           /* Arrange the final result ends up in r, not in the temp space */
                    456:           if ((i & 1) == 0)
                    457:             SWAP_RP_TP;
                    458:
                    459:           rp[0] = blimb;
                    460:           rsize = 1;
                    461:
                    462:           for ( ; i >= 0; i--)
                    463:             {
                    464:               TRACE (printf ("mul_1 loop i=%d e=0x%lX, rsize=%ld ralloc=%ld talloc=%ld\n",
                    465:                              i, e, rsize, ralloc, talloc);
                    466:                      mpn_trace ("r", rp, rsize));
                    467:
                    468:               MPN_SQR_N (tp, talloc, rp, rsize);
                    469:               SWAP_RP_TP;
                    470:               if ((e & (1L << i)) != 0)
                    471:                 MPN_MUL_1 (rp, rsize, ralloc, blimb);
                    472:             }
                    473:
                    474:           TRACE (mpn_trace ("mul_1 before rl, r", rp, rsize));
                    475:           if (rl != 1)
                    476:             MPN_MUL_1 (rp, rsize, ralloc, rl);
                    477:         }
                    478: #endif
                    479:       else
                    480:         {
                    481:           int  parity;
                    482:
                    483:           /* Arrange the final result ends up in r, not in the temp space */
                    484:           ULONG_PARITY (parity, e);
                    485:           if (((parity ^ i) & 1) != 0)
                    486:             SWAP_RP_TP;
                    487:
                    488:           MPN_COPY (rp, bp, bsize);
                    489:           rsize = bsize;
                    490:
                    491:           for ( ; i >= 0; i--)
                    492:             {
                    493:               TRACE (printf ("mul loop i=%d e=0x%lX, rsize=%ld ralloc=%ld talloc=%ld\n",
                    494:                              i, e, rsize, ralloc, talloc);
                    495:                      mpn_trace ("r", rp, rsize));
                    496:
                    497:               MPN_SQR_N (tp, talloc, rp, rsize);
                    498:               SWAP_RP_TP;
                    499:               if ((e & (1L << i)) != 0)
                    500:                 {
                    501:                   MPN_MUL (tp, talloc, rp, rsize, bp, bsize);
                    502:                   SWAP_RP_TP;
                    503:                 }
                    504:             }
                    505:         }
                    506:     }
                    507:
                    508:   ASSERT (rp == PTR(r) + rtwos_limbs);
                    509:   TRACE (mpn_trace ("end loop r", rp, rsize));
                    510:   TMP_FREE (marker);
                    511:
                    512:   /* Apply any partial limb factors of 2. */
                    513:   if (rtwos_bits != 0)
                    514:     {
                    515:       MPN_LSHIFT (rp, rsize, ralloc, (unsigned) rtwos_bits);
                    516:       TRACE (mpn_trace ("lshift r", rp, rsize));
                    517:     }
                    518:
                    519:   rsize += rtwos_limbs;
                    520:   SIZ(r) = (rneg ? -rsize : rsize);
                    521: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>