/* Numeric types for XEmacs using the MP library.
Copyright (C) 2004 Jerry James.
This file is part of XEmacs.
XEmacs 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 3 of the License, or (at your
option) any later version.
XEmacs 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 XEmacs. If not, see . */
/* Synched up with: Not in FSF. */
#include
#include
#include
#include
#include "lisp.h"
static MINT *bignum_bytesize, *bignum_one, *bignum_two;
MINT *bignum_zero, *intern_bignum;
MINT *bignum_min_int, *bignum_max_int, *bignum_max_uint;
MINT *bignum_min_long, *bignum_max_long, *bignum_max_ulong;
MINT *bignum_min_llong, *bignum_max_llong, *bignum_max_ullong;
short div_rem;
char *
bignum_to_string (bignum b, int base)
{
REGISTER unsigned int i;
unsigned int bufsize = 128U, index = 0U;
int sign;
char *buffer = xnew_array (char, 128), *retval;
MINT *quo = MP_ITOM (0);
short rem;
/* FIXME: signal something if base is < 2 or doesn't fit into a short. */
/* Save the sign for later */
sign = bignum_sign (b);
if (sign == 0)
{
XREALLOC_ARRAY (buffer, char, 2);
buffer[0] = '0';
buffer[1] = '\0';
return buffer;
}
/* Copy abs(b) into quo for destructive modification */
else if (sign < 0)
MP_MSUB (bignum_zero, b, quo);
else
MP_MOVE (b, quo);
/* Loop over the digits of b (in BASE) and place each one into buffer */
for (i = 0U; MP_MCMP(quo, bignum_zero) > 0; i++)
{
MP_SDIV (quo, base, quo, &rem);
if (index == bufsize)
{
bufsize <<= 1;
XREALLOC_ARRAY (buffer, char, bufsize);
}
buffer[index++] = rem < 10 ? rem + '0' : rem - 10 + 'a';
}
MP_MFREE (quo);
/* Reverse the digits, maybe add a minus sign, and add a null terminator */
bufsize = index + (sign < 0 ? 1 : 0) + 1;
retval = xnew_array (char, bufsize);
if (sign < 0)
{
retval[0] = '-';
i = 1;
}
else
i = 0;
for (; i < bufsize - 1; i++)
retval[i] = buffer[--index];
retval[bufsize - 1] = '\0';
xfree (buffer);
return retval;
}
#define BIGNUM_TO_TYPE(type,accumtype) do { \
if (0 == sign) \
{ \
return (type)0; \
} \
\
bignum_init (quo); \
\
if (sign < 0) \
{ \
MP_MSUB (bignum_zero, b, quo); \
} \
else \
{ \
MP_MOVE (b, quo); \
} \
\
for (i = 0U; i < sizeof(type); i++) \
{ \
MP_SDIV (quo, 256, quo, &rem); \
retval |= ((accumtype) rem) << (8 * i); \
} \
bignum_fini (quo); \
} while (0)
int
bignum_to_int (bignum b)
{
short rem, sign;
unsigned int retval = 0;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
BIGNUM_TO_TYPE (int, unsigned int);
return ((int) retval) * sign;
}
unsigned int
bignum_to_uint (bignum b)
{
short rem, sign;
unsigned int retval = 0U;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
BIGNUM_TO_TYPE (unsigned int, unsigned int);
return retval;
}
long
bignum_to_long (bignum b)
{
short rem, sign;
unsigned long retval = 0L;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
BIGNUM_TO_TYPE (long, unsigned long);
return ((long) retval) * sign;
}
unsigned long
bignum_to_ulong (bignum b)
{
short rem, sign;
unsigned long retval = 0UL;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
BIGNUM_TO_TYPE (unsigned long, unsigned long);
return retval;
}
long long
bignum_to_llong (bignum b)
{
short rem, sign;
unsigned long long retval = 0LL;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
BIGNUM_TO_TYPE (long long, unsigned long long);
return ((long long) retval) * sign;
}
unsigned long long
bignum_to_ullong (bignum b)
{
short rem, sign;
unsigned long long retval = 0UL;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
BIGNUM_TO_TYPE (unsigned long long, unsigned long long);
return retval;
}
double
bignum_to_double (bignum b)
{
short rem, sign;
double retval = 0.0, factor = 1.0;
REGISTER unsigned int i;
MINT *quo;
sign = bignum_sign (b);
bignum_init (quo);
if (sign < 0)
{
MP_MSUB (bignum_zero, b, quo);
}
else
{
MP_MOVE (b, quo);
}
for (i = 0U; MP_MCMP (quo, bignum_zero) > 0; i++)
{
MP_SDIV (quo, 256, quo, &rem);
retval += rem * factor;
factor *= 256.0;
}
MP_MFREE (quo);
return retval * sign;
}
static short
char_to_number (char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'z')
return c - 'a' + 10;
if (c >= 'A' && c <= 'Z')
return c - 'A' + 10;
return -1;
}
int
bignum_set_string (bignum b, const char *s, int base)
{
MINT *mbase;
short digit;
int neg = 0;
if (base == 0)
{
if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
{
base = 16;
s += 2;
}
else if (*s == '0')
{
base = 8;
s++;
}
else
base = 10;
}
/* FIXME: signal something if base is < 2 or doesn't fit into a short. */
if (*s == '-')
{
s++;
neg = 1;
}
mbase = MP_ITOM ((short) base);
MP_MOVE (bignum_zero, b);
for (digit = char_to_number (*s); digit >= 0 && digit < base;
digit = char_to_number (*++s))
{
MINT *temp;
MP_MULT (b, mbase, b);
temp = MP_ITOM (digit);
MP_MADD (b, temp, b);
MP_MFREE (temp);
}
MP_MFREE (mbase);
if (neg)
MP_MSUB (bignum_zero, b, b);
return (digit >= 0) ? -1 : 0;
}
void
bignum_set_long (bignum b, long l)
{
char hex[SIZEOF_LONG * 2U + 2U];
MINT *temp;
int neg = l < 0L;
snprintf (hex, SIZEOF_LONG * 2U + 2U, "%lx",
neg ? (unsigned long) -l : (unsigned long) l);
temp = MP_XTOM (hex);
if (neg)
MP_MSUB (bignum_zero, temp, b);
else
MP_MOVE (temp, b);
MP_MFREE (temp);
}
void
bignum_set_ulong (bignum b, unsigned long l)
{
char hex[SIZEOF_LONG * 2U + 2U];
MINT *temp;
snprintf (hex, SIZEOF_LONG * 2U + 2U, "%lx", l);
temp = MP_XTOM (hex);
MP_MOVE (temp, b);
MP_MFREE (temp);
}
void
bignum_set_llong (bignum b, long long l)
{
char hex[SIZEOF_LONG_LONG * 2U + 2U];
MINT *temp;
int neg = l < 0LL;
snprintf (hex, SIZEOF_LONG_LONG * 2U + 2U, "%llx",
neg ? (unsigned long long) -l : (unsigned long long) l);
temp = MP_XTOM (hex);
if (neg)
MP_MSUB (bignum_zero, temp, b);
else
MP_MOVE (temp, b);
MP_MFREE (temp);
}
void
bignum_set_ullong (bignum b, unsigned long long l)
{
char hex[SIZEOF_LONG_LONG * 2U + 2U];
MINT *temp;
snprintf (hex, SIZEOF_LONG_LONG * 2U + 2U, "%llx", l);
temp = MP_XTOM (hex);
MP_MOVE (temp, b);
MP_MFREE (temp);
}
void
bignum_set_double (bignum b, double d)
{
REGISTER unsigned int i;
int negative = (d < 0) ? 1 : 0;
MINT *multiplier = MP_ITOM (1);
MP_MOVE (bignum_zero, b);
if (negative)
d = -d;
for (i = 0UL; d > 0.0; d /= 256, i++)
{
MINT *temp = MP_ITOM ((short) fmod (d, 256.0));
MP_MULT (multiplier, temp, temp);
MP_MADD (b, temp, b);
MP_MULT (multiplier, bignum_bytesize, multiplier);
MP_MFREE (temp);
}
MP_MFREE (multiplier);
if (negative)
MP_MSUB (bignum_zero, b, b);
}
/* Return nonzero if b1 is exactly divisible by b2 */
int
bignum_divisible_p (bignum b1, bignum b2)
{
int retval;
MINT *rem = MP_ITOM (0);
MP_MDIV (b1, b2, intern_bignum, rem);
retval = (MP_MCMP (rem, bignum_zero) == 0);
MP_MFREE (rem);
return retval;
}
void bignum_ceil (bignum quotient, bignum N, bignum D)
{
MP_MDIV (N, D, quotient, intern_bignum);
if (MP_MCMP (intern_bignum, bignum_zero) != 0)
{
short signN = MP_MCMP (N, bignum_zero);
short signD = MP_MCMP (D, bignum_zero);
/* If the quotient is positive, add one, since we're rounding to
positive infinity. */
if (signD < 0)
{
if (signN <= 0)
{
MP_MADD (quotient, bignum_one, quotient);
}
}
else if (signN >= 0)
{
MP_MADD (quotient, bignum_one, quotient);
}
}
}
void bignum_floor (bignum quotient, bignum N, bignum D)
{
MP_MDIV (N, D, quotient, intern_bignum);
if (MP_MCMP (intern_bignum, bignum_zero) != 0)
{
short signN = MP_MCMP (N, bignum_zero);
short signD = MP_MCMP (D, bignum_zero);
/* If the quotient is negative, subtract one, we're rounding to minus
infinity. */
if (signD < 0)
{
if (signN >= 0)
{
MP_MSUB (quotient, bignum_one, quotient);
}
}
else if (signN < 0)
{
MP_MSUB (quotient, bignum_one, quotient);
}
}
}
/* RESULT = N to the POWth power */
void
bignum_pow (bignum result, bignum n, unsigned long pow)
{
MP_MOVE (bignum_one, result);
for ( ; pow > 0UL; pow--)
MP_MULT (result, n, result);
}
/* lcm(b1,b2) = b1 * b2 / gcd(b1, b2) */
void
bignum_lcm (bignum result, bignum b1, bignum b2)
{
MP_MULT (b1, b2, result);
MP_GCD (b1, b2, intern_bignum);
MP_MDIV (result, intern_bignum, result, intern_bignum);
}
/* FIXME: We can't handle negative args, so right now we just make them
positive before doing anything else. How should we really handle negative
args? */
#define bignum_bit_op(result, b1, b2, op) \
REGISTER unsigned int i; \
MINT *multiplier = MP_ITOM (1), *n1 = MP_ITOM (0), *n2 = MP_ITOM (0); \
\
if (MP_MCMP (bignum_zero, b1) > 0) \
MP_MSUB (bignum_zero, b1, n1); \
else \
MP_MOVE (b1, n1); \
if (MP_MCMP (bignum_zero, b2) > 0) \
MP_MSUB (bignum_zero, b2, n2); \
else \
MP_MOVE (b2, n2); \
\
MP_MOVE (bignum_zero, result); \
\
for (i = 0UL; MP_MCMP (bignum_zero, n1) < 0 && \
MP_MCMP (bignum_zero, n2) < 0; i++) \
{ \
short byte1, byte2; \
MINT *temp; \
\
MP_SDIV (n1, 256, n1, &byte1); \
MP_SDIV (n2, 256, n2, &byte2); \
temp = MP_ITOM (byte1 op byte2); \
MP_MULT (multiplier, temp, temp); \
MP_MADD (result, temp, result); \
MP_MULT (multiplier, bignum_bytesize, multiplier); \
MP_MFREE (temp); \
} \
MP_MFREE (n2); \
MP_MFREE (n1); \
MP_MFREE (multiplier)
void
bignum_and (bignum result, bignum b1, bignum b2)
{
bignum_bit_op (result, b1, b2, &);
}
void
bignum_ior (bignum result, bignum b1, bignum b2)
{
bignum_bit_op (result, b1, b2, |);
}
void
bignum_xor (bignum result, bignum b1, bignum b2)
{
bignum_bit_op (result, b1, b2, ^);
}
/* NOT is not well-defined for bignums ... where do you stop flipping bits?
We just flip until we see the last one. This is probably a bad idea. */
void
bignum_not (bignum result, bignum b)
{
REGISTER unsigned int i;
MINT *multiplier = MP_ITOM (1), *n = MP_ITOM (0);
if (MP_MCMP (bignum_zero, b) > 0)
MP_MSUB (bignum_zero, b, n);
else
MP_MOVE (b, n);
MP_MOVE (bignum_zero, result);
for (i = 0UL; MP_MCMP (bignum_zero, n) < 0; i++)
{
short byte;
MINT *temp;
MP_SDIV (n, 256, n, &byte);
temp = MP_ITOM (~byte);
MP_MULT (multiplier, temp, temp);
MP_MADD (result, temp, result);
MP_MULT (multiplier, bignum_bytesize, multiplier);
MP_MFREE (temp);
}
MP_MFREE (n);
MP_MFREE (multiplier);
}
void
bignum_setbit (bignum b, unsigned long bit)
{
bignum_pow (intern_bignum, bignum_two, bit);
bignum_ior (b, b, intern_bignum);
}
/* This is so evil, even I feel queasy. */
void
bignum_clrbit (bignum b, unsigned long bit)
{
MINT *num = MP_ITOM (0);
/* See if the bit is set, and subtract it off if so */
MP_MOVE (b, intern_bignum);
bignum_pow (num, bignum_two, bit);
bignum_ior (intern_bignum, intern_bignum, num);
if (MP_MCMP (b, intern_bignum) == 0)
MP_MSUB (b, num, b);
MP_MFREE (num);
}
int
bignum_testbit (bignum b, unsigned long bit)
{
bignum_pow (intern_bignum, bignum_two, bit);
bignum_and (intern_bignum, b, intern_bignum);
return MP_MCMP (intern_bignum, bignum_zero);
}
void
bignum_lshift (bignum result, bignum b, unsigned long bits)
{
bignum_pow (intern_bignum, bignum_two, bits);
MP_MULT (b, intern_bignum, result);
}
void
bignum_rshift (bignum result, bignum b, unsigned long bits)
{
bignum_pow (intern_bignum, bignum_two, bits);
MP_MDIV (b, intern_bignum, result, intern_bignum);
}
void
bignum_random (bignum result, bignum limit)
{
MINT *denominator = MP_ITOM (0), *divisor = MP_ITOM (0);
bignum_set_long (denominator, RAND_MAX);
MP_MADD (denominator, bignum_one, denominator);
MP_MADD (limit, bignum_one, divisor);
MP_MDIV (denominator, divisor, denominator, intern_bignum);
MP_MFREE (divisor);
do
{
MINT *limitcmp = MP_ITOM (1);
/* Accumulate at least as many random bits as in LIMIT */
MP_MOVE (bignum_zero, result);
do
{
bignum_lshift (limitcmp, limitcmp, FIXNUM_VALBITS);
bignum_lshift (result, result, FIXNUM_VALBITS);
bignum_set_long (intern_bignum, get_random ());
MP_MADD (intern_bignum, result, result);
}
while (MP_MCMP (limitcmp, limit) <= 0);
MP_MDIV (result, denominator, result, intern_bignum);
MP_MFREE (limitcmp);
}
while (MP_MCMP (limit, result) <= 0);
MP_MFREE (denominator);
}
#ifdef HAVE_MP_SET_MEMORY_FUNCTIONS
/* We need the next two functions due to the extra parameter. */
static void *
mp_realloc (void *ptr, size_t UNUSED (old_size), size_t new_size)
{
return xrealloc (ptr, new_size);
}
static void
mp_free (void *ptr, size_t UNUSED (size))
{
xfree (ptr);
}
#endif
void
init_number_mp (void)
{
#ifdef HAVE_MP_SET_MEMORY_FUNCTIONS
mp_set_memory_functions ((void *(*) (size_t)) xmalloc, mp_realloc, mp_free);
#endif
bignum_zero = MP_ITOM (0);
bignum_one = MP_ITOM (1);
bignum_two = MP_ITOM (2);
/* intern_bignum holds throwaway values from macro expansions in
number-mp.h. Its value is immaterial. */
intern_bignum = MP_ITOM (0);
/* The multiplier used to shift a number left by one byte's worth of bits */
bignum_bytesize = MP_ITOM (256);
/* The MP interface only supports turning short ints into MINTs, so we have
to set these the hard way. */
bignum_min_int = MP_ITOM (0);
bignum_set_long (bignum_min_int, INT_MIN);
bignum_max_int = MP_ITOM (0);
bignum_set_long (bignum_max_int, INT_MAX);
bignum_max_uint = MP_ITOM (0);
bignum_set_ulong (bignum_max_uint, UINT_MAX);
bignum_min_long = MP_ITOM (0);
bignum_set_long (bignum_min_long, LONG_MIN);
bignum_max_long = MP_ITOM (0);
bignum_set_long (bignum_max_long, LONG_MAX);
bignum_max_ulong = MP_ITOM (0);
bignum_set_ulong (bignum_max_ulong, ULONG_MAX);
bignum_min_llong = MP_ITOM (0);
bignum_set_llong (bignum_min_llong, LLONG_MIN);
bignum_max_llong = MP_ITOM (0);
bignum_set_llong (bignum_max_llong, LLONG_MAX);
bignum_max_ullong = MP_ITOM (0);
bignum_set_ullong (bignum_max_ullong, ULLONG_MAX);
}