home *** CD-ROM | disk | FTP | other *** search
- /* _mpz_get_str (string, base, mp_src) -- Convert the multiple precision
- number MP_SRC to a string STRING of base BASE. If STRING is NULL
- allocate space for the result. In any case, return a pointer to the
- result. If STRING is not NULL, the caller must ensure enough space is
- available to store the result.
-
- Copyright (C) 1991, 1993 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 General Public License as published by
- the Free Software Foundation; either version 2, 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with the GNU MP Library; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "gmp.h"
- #include "gmp-impl.h"
- #include "longlong.h"
-
- #ifndef UMUL_TIME
- #define UMUL_TIME 1
- #endif
-
- #ifndef UDIV_TIME
- #define UDIV_TIME UMUL_TIME
- #endif
-
- #define udiv_qrnndx(q, r, nh, nl, d, di) \
- do { \
- unsigned long int _q, _ql, _r; \
- unsigned long int _xh, _xl; \
- umul_ppmm (_q, _ql, (nh), (di)); \
- _q += (nh); /* DI is 2**32 too small. Compensate */\
- if (_q < (nh)) \
- { \
- /* Got carry. Propagate it in the multiplication. */ \
- umul_ppmm (_xh, _xl, (d), _q); \
- _xh += (d); \
- } \
- else \
- umul_ppmm (_xh, _xl, (d), _q); \
- sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl); \
- if (_xh != 0) \
- { \
- sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
- _q += 1; \
- if (_xh != 0) \
- { \
- sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
- _q += 1; \
- } \
- } \
- if (_r >= (d)) \
- { \
- _r -= (d); \
- _q += 1; \
- } \
- (r) = _r; \
- (q) = _q; \
- } while (0)
-
- char *
- #ifdef __STDC__
- _mpz_get_str (char *str, int base, const MP_INT *m)
- #else
- _mpz_get_str (str, base, m)
- char *str;
- int base;
- const MP_INT *m;
- #endif
- {
- mp_ptr tp;
- mp_size msize;
- mp_limb big_base;
- #if UDIV_NEEDS_NORMALIZATION || UDIV_TIME > 2 * UMUL_TIME
-
- int normalization_steps;
- #if UDIV_TIME > 2 * UMUL_TIME
- mp_limb big_base_inverted;
- #endif
- #endif
- unsigned int dig_per_u;
- mp_size out_len;
- char *s;
- char *num_to_ascii;
-
- if (base >= 0)
- {
- if (base == 0)
- base = 10;
- num_to_ascii = "0123456789abcdefghijklmnopqrstuvwxyz";
- }
- else
- {
- base = -base;
- num_to_ascii = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- }
-
- dig_per_u = __mp_bases[base].chars_per_limb;
- out_len = mpz_sizeinbase (m, base) + 1;
- big_base = __mp_bases[base].big_base;
-
- msize = m->size;
-
- if (str == NULL)
- str = (char *) (*_mp_allocate_func) (out_len + (msize < 0));
-
- if (msize < 0)
- *str++ = '-';
- s = str;
-
- msize = ABS (msize);
-
- /* Special case zero, as the code below doesn't handle it. */
- if (msize == 0)
- {
- s[0] = '0';
- s[1] = 0;
- return str;
- }
-
- if ((base & (base - 1)) == 0)
- {
- /* The base is a power of 2. Make conversion from most
- significant side. */
- mp_limb n1, n0;
- int bits_per_digit = big_base;
- int x;
- int bit_pos;
- int i;
- unsigned mask = (1 << bits_per_digit) - 1;
-
- tp = m->d;
- n1 = tp[msize - 1];
- count_leading_zeros (x, n1);
-
- /* BIT_POS should be R when input ends in least sign. nibble,
- R + bits_per_digit * n when input ends in n:th least significant
- nibble. */
-
- {
- int bits;
-
- bits = BITS_PER_MP_LIMB * msize - x;
- x = bits % bits_per_digit;
- if (x != 0)
- bits += bits_per_digit - x;
- bit_pos = bits - (msize - 1) * BITS_PER_MP_LIMB;
- }
-
- /* Fast loop for bit output. */
- i = msize - 1;
- for (;;)
- {
- bit_pos -= bits_per_digit;
- while (bit_pos >= 0)
- {
- *s++ = num_to_ascii[(n1 >> bit_pos) & mask];
- bit_pos -= bits_per_digit;
- }
- i--;
- if (i < 0)
- break;
- n0 = (n1 << -bit_pos) & mask;
- n1 = tp[i];
- bit_pos += BITS_PER_MP_LIMB;
- *s++ = num_to_ascii[n0 | (n1 >> bit_pos)];
- }
-
- *s = 0;
- }
- else
- {
- /* General case. The base is not a power of 2. Make conversion
- from least significant end. */
-
- /* If udiv_qrnnd only handles divisors with the most significant bit
- set, prepare BIG_BASE for being a divisor by shifting it to the
- left exactly enough to set the most significant bit. */
- #if UDIV_NEEDS_NORMALIZATION || UDIV_TIME > 2 * UMUL_TIME
- count_leading_zeros (normalization_steps, big_base);
- big_base <<= normalization_steps;
- #if UDIV_TIME > 2 * UMUL_TIME
- /* Get the fixed-point approximation to 1/BIG_BASE. */
- big_base_inverted = __mp_bases[base].big_base_inverted;
- #endif
- #endif
-
- out_len--; /* now not include terminating \0 */
- s += out_len;
-
- /* Allocate temporary space and move the multi prec number to
- convert there, as we need to overwrite it below, while
- computing the successive remainders. */
- tp = (mp_ptr) alloca ((msize + 1) * BYTES_PER_MP_LIMB);
- MPN_COPY (tp, m->d, msize);
-
- while (msize != 0)
- {
- int i;
- mp_limb n0, n1;
-
- #if UDIV_NEEDS_NORMALIZATION || UDIV_TIME > 2 * UMUL_TIME
- /* If we shifted BIG_BASE above, shift the dividend too, to get
- the right quotient. We need to do this every loop,
- as the intermediate quotients are OK, but the quotient from
- one turn in the loop is going to be the dividend in the
- next turn, and the dividend needs to be up-shifted. */
- if (normalization_steps != 0)
- {
- n0 = mpn_lshift (tp, tp, msize, normalization_steps);
-
- /* If the shifting gave a carry out limb, store it and
- increase the length. */
- if (n0 != 0)
- {
- tp[msize] = n0;
- msize++;
- }
- }
- #endif
-
- /* Divide the number at TP with BIG_BASE to get a quotient and a
- remainder. The remainder is our new digit in base BIG_BASE. */
- i = msize - 1;
- n1 = tp[i];
-
- if (n1 >= big_base)
- n1 = 0;
- else
- {
- msize--;
- i--;
- }
-
- for (; i >= 0; i--)
- {
- n0 = tp[i];
- #if UDIV_TIME > 2 * UMUL_TIME
- udiv_qrnndx (tp[i], n1, n1, n0, big_base, big_base_inverted);
- #else
- udiv_qrnnd (tp[i], n1, n1, n0, big_base);
- #endif
- }
-
- #if UDIV_NEEDS_NORMALIZATION || UDIV_TIME > 2 * UMUL_TIME
- /* If we shifted above (at previous UDIV_NEEDS_NORMALIZATION tests)
- the remainder will be up-shifted here. Compensate. */
- n1 >>= normalization_steps;
- #endif
-
- /* Convert N1 from BIG_BASE to a string of digits in BASE
- using single precision operations. */
- for (i = dig_per_u - 1; i >= 0; i--)
- {
- *--s = num_to_ascii[n1 % base];
- n1 /= base;
- /* Break from the loop as soon as we would only write zeros. */
- if (n1 == 0 && msize == 0)
- break;
- }
- }
-
- /* There should be no leading zeros. */
- if (*s == '0')
- abort ();
-
- if (s == str)
- {
- /* This should be the common case. */
- s[out_len] = 0;
- }
- else if (s == str + 1)
- {
- /* The string became 1 digit shorter than its maximum. */
- /* Need to copy it back one char pos. */
- out_len--;
- #ifndef HAS_MEMMOVE
- {
- size_t i;
-
- for (i = 0; i < out_len; i++)
- str[i] = s[i];
- }
- #else
- memmove (str, s, out_len);
- #endif
- str[out_len] = 0;
- }
- else
- {
- /* Hopefully never. */
- abort ();
- }
- }
-
- alloca (0);
- /* Ugly, we incremented str for negative numbers. Fix that here. */
- return str - (m->size < 0);
- }
-