diff options
Diffstat (limited to 'polly/lib/External/isl/isl_int_sioimath.h')
| -rw-r--r-- | polly/lib/External/isl/isl_int_sioimath.h | 1216 |
1 files changed, 1216 insertions, 0 deletions
diff --git a/polly/lib/External/isl/isl_int_sioimath.h b/polly/lib/External/isl/isl_int_sioimath.h new file mode 100644 index 00000000000..91bec90594f --- /dev/null +++ b/polly/lib/External/isl/isl_int_sioimath.h @@ -0,0 +1,1216 @@ +/* + * Copyright 2015 INRIA Paris-Rocquencourt + * + * Use of this software is governed by the MIT license + * + * Written by Michael Kruse, INRIA Paris-Rocquencourt, + * Domaine de Voluceau, Rocquenqourt, B.P. 105, + * 78153 Le Chesnay Cedex France + */ +#ifndef ISL_INT_SIOIMATH_H +#define ISL_INT_SIOIMATH_H + +#include <inttypes.h> +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> + +#include <isl_imath.h> +#include <isl/hash.h> + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof(*array)) + +/* The type to represent integers optimized for small values. It is either a + * pointer to an mp_int ( = mpz_t*; big representation) or an int32_t (small + * represenation) with a discriminator at the least significant bit. In big + * representation it will be always zero because of heap alignment. It is set + * to 1 for small representation and use the 32 most significant bits for the + * int32_t. + * + * Structure on 64 bit machines, with 8-byte aligment (3 bits): + * + * Big representation: + * MSB LSB + * |------------------------------------------------------------000 + * | mpz_t* | + * | != NULL | + * + * Small representation: + * MSB 32 LSB + * |------------------------------|00000000000000000000000000000001 + * | int32_t | + * | 2147483647 ... -2147483647 | + * ^ + * | + * discriminator bit + * + * On 32 bit machines isl_sioimath type is blown up to 8 bytes, i.e. + * isl_sioimath is guaranteed to be at least 8 bytes. This is to ensure the + * int32_t can be hidden in that type without data loss. In the future we might + * optimize this to use 31 hidden bits in a 32 bit pointer. We may also use 63 + * bits on 64 bit machines, but this comes with the cost of additional overflow + * checks because there is no standardized 128 bit integer we could expand to. + * + * We use native integer types and avoid union structures to avoid assumptions + * on the machine's endianness. + * + * This implementation makes the following assumptions: + * - long can represent any int32_t + * - mp_small is signed long + * - mp_usmall is unsigned long + * - adresses returned by malloc are aligned to 2-byte boundaries (leastmost + * bit is zero) + */ +#if UINT64_MAX > UINTPTR_MAX +typedef uint64_t isl_sioimath; +#else +typedef uintptr_t isl_sioimath; +#endif + +/* The negation of the smallest possible number in int32_t, INT32_MIN + * (0x80000000u, -2147483648), cannot be represented in an int32_t, therefore + * every operation that may produce this value needs to special-case it. + * The operations are: + * abs(INT32_MIN) + * -INT32_MIN (negation) + * -1 * INT32_MIN (multiplication) + * INT32_MIN/-1 (any division: divexact, fdiv, cdiv, tdiv) + * To avoid checking these cases, we exclude INT32_MIN from small + * representation. + */ +#define ISL_SIOIMATH_SMALL_MIN (-INT32_MAX) + +/* Largest possible number in small representation */ +#define ISL_SIOIMATH_SMALL_MAX INT32_MAX + +/* Used for function parameters the function modifies. */ +typedef isl_sioimath *isl_sioimath_ptr; + +/* Used for function parameters that are read-only. */ +typedef isl_sioimath isl_sioimath_src; + +/* Return whether the argument is stored in small representation. + */ +inline int isl_sioimath_is_small(isl_sioimath val) +{ + return val & 0x00000001; +} + +/* Return whether the argument is stored in big representation. + */ +inline int isl_sioimath_is_big(isl_sioimath val) +{ + return !isl_sioimath_is_small(val); +} + +/* Get the number of an isl_int in small representation. Result is undefined if + * val is not stored in that format. + */ +inline int32_t isl_sioimath_get_small(isl_sioimath val) +{ + return val >> 32; +} + +/* Get the number of an in isl_int in big representation. Result is undefined if + * val is not stored in that format. + */ +inline mp_int isl_sioimath_get_big(isl_sioimath val) +{ + return (mp_int)(uintptr_t) val; +} + +/* Return 1 if val is stored in small representation and store its value to + * small. We rely on the compiler to optimize the isl_sioimath_get_small such + * that the shift is moved into the branch that executes in case of small + * representation. If there is no such branch, then a single shift is still + * cheaper than introducing branching code. + */ +inline int isl_sioimath_decode_small(isl_sioimath val, int32_t *small) +{ + *small = isl_sioimath_get_small(val); + return isl_sioimath_is_small(val); +} + +/* Return 1 if val is stored in big representation and store its value to big. + */ +inline int isl_sioimath_decode_big(isl_sioimath val, mp_int *big) +{ + *big = isl_sioimath_get_big(val); + return isl_sioimath_is_big(val); +} + +/* Encode a small representation into an isl_int. + */ +inline isl_sioimath isl_sioimath_encode_small(int32_t val) +{ + return ((isl_sioimath) val) << 32 | 0x00000001; +} + +/* Encode a big representation. + */ +inline isl_sioimath isl_sioimath_encode_big(mp_int val) +{ + return (isl_sioimath)(uintptr_t) val; +} + +/* A common situation is to call an IMath function with at least one argument + * that is currently in small representation or an integer parameter, i.e. a big + * representation of the same number is required. Promoting the original + * argument comes with multiple problems, such as modifying a read-only + * argument, the responsibility of deallocation and the execution cost. Instead, + * we make a copy by 'faking' the IMath internal structure. + * + * We reserve the maximum number of required digits on the stack to avoid heap + * allocations. + * + * mp_digit can be uint32_t or uint16_t. This code must work for little and big + * endian digits. The structure for an uint64_t argument and 32-bit mp_digits is + * sketched below. + * + * |----------------------------| + * uint64_t + * + * |-------------||-------------| + * mp_digit mp_digit + * digits[1] digits[0] + * Most sig digit Least sig digit + */ +typedef struct { + mpz_t big; + mp_digit digits[(sizeof(uintmax_t) + sizeof(mp_digit) - 1) / + sizeof(mp_digit)]; +} isl_sioimath_scratchspace_t; + +/* Convert a native integer to IMath's digit representation. A native integer + * might be big- or little endian, but IMath always stores the least significant + * digit in the lowest array indices. memcpy therefore is not possible. + * + * We also have to consider that long and mp_digit can be of different sizes, + * depending on the compiler (LP64, LLP64) and IMath's USE_64BIT_WORDS. This + * macro should work for all of them. + * + * "used" is set to the number of written digits. It must be minimal (IMath + * checks zeroness using the used field), but always at least one. Also note + * that the result of num>>(sizeof(num)*CHAR_BIT) is undefined. + */ +#define ISL_SIOIMATH_TO_DIGITS(num, digits, used) \ + do { \ + int i = 0; \ + do { \ + (digits)[i] = \ + ((num) >> (sizeof(mp_digit) * CHAR_BIT * i)); \ + i += 1; \ + if (i >= (sizeof(num) + sizeof(mp_digit) - 1) / \ + sizeof(mp_digit)) \ + break; \ + if (((num) >> (sizeof(mp_digit) * CHAR_BIT * i)) == 0) \ + break; \ + } while (1); \ + (used) = i; \ + } while (0) + +inline void isl_siomath_uint32_to_digits(uint32_t num, mp_digit *digits, + mp_size *used) +{ + ISL_SIOIMATH_TO_DIGITS(num, digits, *used); +} + +inline void isl_siomath_ulong_to_digits(unsigned long num, mp_digit *digits, + mp_size *used) +{ + ISL_SIOIMATH_TO_DIGITS(num, digits, *used); +} + +inline void isl_siomath_uint64_to_digits(uint64_t num, mp_digit *digits, + mp_size *used) +{ + ISL_SIOIMATH_TO_DIGITS(num, digits, *used); +} + +/* Get the IMath representation of an isl_int without modifying it. + * For the case it is not in big representation yet, pass some scratch space we + * can use to store the big representation in. + * In order to avoid requiring init and free on the scratch space, we directly + * modify the internal representation. + * + * The name derives from its indented use: getting the big representation of an + * input (src) argument. + */ +inline mp_int isl_sioimath_bigarg_src(isl_sioimath arg, + isl_sioimath_scratchspace_t *scratch) +{ + mp_int big; + int32_t small; + uint32_t num; + + if (isl_sioimath_decode_big(arg, &big)) + return big; + + small = isl_sioimath_get_small(arg); + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + if (small >= 0) { + scratch->big.sign = MP_ZPOS; + num = small; + } else { + scratch->big.sign = MP_NEG; + num = -small; + } + + isl_siomath_uint32_to_digits(num, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Create a temporary IMath mp_int for a signed long. + */ +inline mp_int isl_sioimath_siarg_src(signed long arg, + isl_sioimath_scratchspace_t *scratch) +{ + unsigned long num; + + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + if (arg >= 0) { + scratch->big.sign = MP_ZPOS; + num = arg; + } else { + scratch->big.sign = MP_NEG; + num = (arg == LONG_MIN) ? ((unsigned long) LONG_MAX) + 1 : -arg; + } + + isl_siomath_ulong_to_digits(num, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Create a temporary IMath mp_int for an int64_t. + */ +inline mp_int isl_sioimath_si64arg_src(int64_t arg, + isl_sioimath_scratchspace_t *scratch) +{ + uint64_t num; + + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + if (arg >= 0) { + scratch->big.sign = MP_ZPOS; + num = arg; + } else { + scratch->big.sign = MP_NEG; + num = (arg == INT64_MIN) ? ((uint64_t) INT64_MAX) + 1 : -arg; + } + + isl_siomath_uint64_to_digits(num, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Create a temporary IMath mp_int for an unsigned long. + */ +inline mp_int isl_sioimath_uiarg_src(unsigned long arg, + isl_sioimath_scratchspace_t *scratch) +{ + scratch->big.digits = scratch->digits; + scratch->big.alloc = ARRAY_SIZE(scratch->digits); + scratch->big.sign = MP_ZPOS; + + isl_siomath_ulong_to_digits(arg, scratch->digits, &scratch->big.used); + return &scratch->big; +} + +/* Ensure big representation. Does not preserve the current number. + * Callers may use the fact that the value _is_ preserved if the presentation + * was big before. + */ +inline mp_int isl_sioimath_reinit_big(isl_sioimath_ptr ptr) +{ + if (isl_sioimath_is_small(*ptr)) + *ptr = isl_sioimath_encode_big(mp_int_alloc()); + return isl_sioimath_get_big(*ptr); +} + +/* Set ptr to a number in small representation. + */ +inline void isl_sioimath_set_small(isl_sioimath_ptr ptr, int32_t val) +{ + if (isl_sioimath_is_big(*ptr)) + mp_int_free(isl_sioimath_get_big(*ptr)); + *ptr = isl_sioimath_encode_small(val); +} + +/* Set ptr to val, choosing small representation if possible. + */ +inline void isl_sioimath_set_int32(isl_sioimath_ptr ptr, int32_t val) +{ + if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(ptr, val); + return; + } + + mp_int_init_value(isl_sioimath_reinit_big(ptr), val); +} + +/* Assign an int64_t number using small representation if possible. + */ +inline void isl_sioimath_set_int64(isl_sioimath_ptr ptr, int64_t val) +{ + if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(ptr, val); + return; + } + + isl_sioimath_scratchspace_t scratch; + mp_int_copy(isl_sioimath_si64arg_src(val, &scratch), + isl_sioimath_reinit_big(ptr)); +} + +/* Convert to big representation while preserving the current number. + */ +inline void isl_sioimath_promote(isl_sioimath_ptr dst) +{ + int32_t small; + + if (isl_sioimath_is_big(*dst)) + return; + + small = isl_sioimath_get_small(*dst); + mp_int_set_value(isl_sioimath_reinit_big(dst), small); +} + +/* Convert to small representation while preserving the current number. Does + * nothing if dst doesn't fit small representation. + */ +inline void isl_sioimath_try_demote(isl_sioimath_ptr dst) +{ + mp_small small; + + if (isl_sioimath_is_small(*dst)) + return; + + if (mp_int_to_int(isl_sioimath_get_big(*dst), &small) != MP_OK) + return; + + if (ISL_SIOIMATH_SMALL_MIN <= small && small <= ISL_SIOIMATH_SMALL_MAX) + isl_sioimath_set_small(dst, small); +} + +/* Initialize an isl_int. The implicit value is 0 in small representation. + */ +inline void isl_sioimath_init(isl_sioimath_ptr dst) +{ + *dst = isl_sioimath_encode_small(0); +} + +/* Free the resources taken by an isl_int. + */ +inline void isl_sioimath_clear(isl_sioimath_ptr dst) +{ + if (isl_sioimath_is_small(*dst)) + return; + + mp_int_free(isl_sioimath_get_big(*dst)); +} + +/* Copy the value of one isl_int to another. + */ +inline void isl_sioimath_set(isl_sioimath_ptr dst, isl_sioimath_src val) +{ + if (isl_sioimath_is_small(val)) { + isl_sioimath_set_small(dst, isl_sioimath_get_small(val)); + return; + } + + mp_int_copy(isl_sioimath_get_big(val), isl_sioimath_reinit_big(dst)); +} + +/* Store a signed long into an isl_int. + */ +inline void isl_sioimath_set_si(isl_sioimath_ptr dst, long val) +{ + if (ISL_SIOIMATH_SMALL_MIN <= val && val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(dst, val); + return; + } + + mp_int_set_value(isl_sioimath_reinit_big(dst), val); +} + +/* Store an unsigned long into an isl_int. + */ +inline void isl_sioimath_set_ui(isl_sioimath_ptr dst, unsigned long val) +{ + if (val <= ISL_SIOIMATH_SMALL_MAX) { + isl_sioimath_set_small(dst, val); + return; + } + + mp_int_set_uvalue(isl_sioimath_reinit_big(dst), val); +} + +/* Return whether a number can be represented by a signed long. + */ +inline int isl_sioimath_fits_slong(isl_sioimath_src val) +{ + mp_small dummy; + + if (isl_sioimath_is_small(val)) + return 1; + + return mp_int_to_int(isl_sioimath_get_big(val), &dummy) == MP_OK; +} + +/* Return a number as signed long. Result is undefined if the number cannot be + * represented as long. + */ +inline long isl_sioimath_get_si(isl_sioimath_src val) +{ + mp_small result; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val); + + mp_int_to_int(isl_sioimath_get_big(val), &result); + return result; +} + +/* Return whether a number can be represented as unsigned long. + */ +inline int isl_sioimath_fits_ulong(isl_sioimath_src val) +{ + mp_usmall dummy; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val) >= 0; + + return mp_int_to_uint(isl_sioimath_get_big(val), &dummy) == MP_OK; +} + +/* Return a number as unsigned long. Result is undefined if the number cannot be + * represented as unsigned long. + */ +inline unsigned long isl_sioimath_get_ui(isl_sioimath_src val) +{ + mp_usmall result; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val); + + mp_int_to_uint(isl_sioimath_get_big(val), &result); + return result; +} + +/* Return a number as floating point value. + */ +inline double isl_sioimath_get_d(isl_sioimath_src val) +{ + mp_int big; + double result = 0; + int i; + + if (isl_sioimath_is_small(val)) + return isl_sioimath_get_small(val); + + big = isl_sioimath_get_big(val); + for (i = 0; i < big->used; ++i) + result = result * (double) ((uintmax_t) MP_DIGIT_MAX + 1) + + (double) big->digits[i]; + + if (big->sign == MP_NEG) + result = -result; + + return result; +} + +/* Format a number as decimal string. + * + * The largest possible string from small representation is 12 characters + *("-2147483647"). + */ +inline char *isl_sioimath_get_str(isl_sioimath_src val) +{ + char *result; + + if (isl_sioimath_is_small(val)) { + result = malloc(12); + snprintf(result, 12, "%" PRIi32, isl_sioimath_get_small(val)); + return result; + } + + return impz_get_str(NULL, 10, isl_sioimath_get_big(val)); +} + +/* Return the absolute value. + */ +inline void isl_sioimath_abs(isl_sioimath_ptr dst, isl_sioimath_src arg) +{ + if (isl_sioimath_is_small(arg)) { + isl_sioimath_set_small(dst, labs(isl_sioimath_get_small(arg))); + return; + } + + mp_int_abs(isl_sioimath_get_big(arg), isl_sioimath_reinit_big(dst)); +} + +/* Return the negation of a number. + */ +inline void isl_sioimath_neg(isl_sioimath_ptr dst, isl_sioimath_src arg) +{ + if (isl_sioimath_is_small(arg)) { + isl_sioimath_set_small(dst, -isl_sioimath_get_small(arg)); + return; + } + + mp_int_neg(isl_sioimath_get_big(arg), isl_sioimath_reinit_big(dst)); +} + +/* Swap two isl_ints. + * + * isl_sioimath can be copied bytewise; nothing depends on its address. It can + * also be stored in a CPU register. + */ +inline void isl_sioimath_swap(isl_sioimath_ptr lhs, isl_sioimath_ptr rhs) +{ + isl_sioimath tmp = *lhs; + *lhs = *rhs; + *rhs = tmp; +} + +/* Add an unsigned long to the number. + * + * On LP64 unsigned long exceeds the range of an int64_t, therefore we check in + * advance whether small representation possibly overflows. + */ +inline void isl_sioimath_add_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + int32_t smalllhs; + isl_sioimath_scratchspace_t lhsscratch; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + (rhs <= (uint64_t) INT64_MAX - (uint64_t) ISL_SIOIMATH_SMALL_MAX)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs + rhs); + return; + } + + impz_add_ui(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs); + isl_sioimath_try_demote(dst); +} + +/* Subtract an unsigned long. + * + * On LP64 unsigned long exceeds the range of an int64_t. If + * ISL_SIOIMATH_SMALL_MIN-rhs>=INT64_MIN we can do the calculation using int64_t + * without risking an overflow. + */ +inline void isl_sioimath_sub_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + int32_t smalllhs; + isl_sioimath_scratchspace_t lhsscratch; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + (rhs < (uint64_t) INT64_MIN - (uint64_t) ISL_SIOIMATH_SMALL_MIN)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs - rhs); + return; + } + + impz_sub_ui(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs); + isl_sioimath_try_demote(dst); +} + +/* Sum of two isl_ints. + */ +inline void isl_sioimath_add(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs, smallrhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + isl_sioimath_decode_small(rhs, &smallrhs)) { + isl_sioimath_set_int64( + dst, (int64_t) smalllhs + (int64_t) smallrhs); + return; + } + + mp_int_add(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Subtract two isl_ints. + */ +inline void isl_sioimath_sub(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs, smallrhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + isl_sioimath_decode_small(rhs, &smallrhs)) { + isl_sioimath_set_int64( + dst, (int64_t) smalllhs - (int64_t) smallrhs); + return; + } + + mp_int_sub(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Multiply two isl_ints. + */ +inline void isl_sioimath_mul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs, smallrhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && + isl_sioimath_decode_small(rhs, &smallrhs)) { + isl_sioimath_set_int64( + dst, (int64_t) smalllhs * (int64_t) smallrhs); + return; + } + + mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_bigarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Shift lhs by rhs bits to the left and store the result in dst. Effectively, + * this operation computes 'lhs * 2^rhs'. + */ +inline void isl_sioimath_mul_2exp(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs; + int32_t smalllhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs <= 32ul)) { + isl_sioimath_set_int64(dst, ((int64_t) smalllhs) << rhs); + return; + } + + mp_int_mul_pow2(isl_sioimath_bigarg_src(lhs, &scratchlhs), rhs, + isl_sioimath_reinit_big(dst)); +} + +/* Multiply an isl_int and a signed long. + */ +inline void isl_sioimath_mul_si(isl_sioimath_ptr dst, isl_sioimath lhs, + signed long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs > LONG_MIN) && + (labs(rhs) <= UINT32_MAX)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs * (int64_t) rhs); + return; + } + + mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_siarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Multiply an isl_int and an unsigned long + */ +inline void isl_sioimath_mul_ui(isl_sioimath_ptr dst, isl_sioimath lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs; + + if (isl_sioimath_decode_small(lhs, &smalllhs) && (rhs <= UINT32_MAX)) { + isl_sioimath_set_int64(dst, (int64_t) smalllhs * (int64_t) rhs); + return; + } + + mp_int_mul(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_uiarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Compute the power of an isl_int to an unsigned long. + * Always let IMath do it; the result is unlikely to be small except some + * special + * cases. + * Note: 0^0 == 1 + */ +inline void isl_sioimath_pow_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t scratchlhs, scratchrhs; + int32_t smalllhs; + + switch (rhs) { + case 0: + isl_sioimath_set_small(dst, 1); + return; + case 1: + isl_sioimath_set(dst, lhs); + return; + case 2: + isl_sioimath_mul(dst, lhs, lhs); + return; + } + + if (isl_sioimath_decode_small(lhs, &smalllhs)) { + switch (smalllhs) { + case 0: + isl_sioimath_set_small(dst, 0); + return; + case 1: + isl_sioimath_set_small(dst, 1); + return; + case 2: + isl_sioimath_set_small(dst, 1); + isl_sioimath_mul_2exp(dst, *dst, rhs); + return; + default: + if ((MP_SMALL_MIN <= rhs) && (rhs <= MP_SMALL_MAX)) { + mp_int_expt_value(smalllhs, rhs, + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); + return; + } + } + } + + mp_int_expt_full(isl_sioimath_bigarg_src(lhs, &scratchlhs), + isl_sioimath_uiarg_src(rhs, &scratchrhs), + isl_sioimath_reinit_big(dst)); + isl_sioimath_try_demote(dst); +} + +/* Fused multiply-add. + */ +inline void isl_sioimath_addmul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul(&tmp, lhs, rhs); + isl_sioimath_add(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +/* Fused multiply-add with an unsigned long. + */ +inline void isl_sioimath_addmul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul_ui(&tmp, lhs, rhs); + isl_sioimath_add(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +/* Fused multiply-subtract. + */ +inline void isl_sioimath_submul(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul(&tmp, lhs, rhs); + isl_sioimath_sub(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +/* Fused multiply-add with an unsigned long. + */ +inline void isl_sioimath_submul_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath tmp; + isl_sioimath_init(&tmp); + isl_sioimath_mul_ui(&tmp, lhs, rhs); + isl_sioimath_sub(dst, *dst, tmp); + isl_sioimath_clear(&tmp); +} + +void isl_sioimath_gcd(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); +void isl_sioimath_lcm(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs); + +/* Divide lhs by rhs, rounding to zero (Truncate). + */ +inline void isl_sioimath_tdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + isl_sioimath_set_small(dst, lhssmall / rhssmall); + return; + } + + mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch), + isl_sioimath_reinit_big(dst), NULL); + isl_sioimath_try_demote(dst); + return; +} + +/* Divide lhs by an unsigned long rhs, rounding to zero (Truncate). + */ +inline void isl_sioimath_tdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall; + + if (isl_sioimath_is_small(lhs) && (rhs <= (unsigned long) INT32_MAX)) { + lhssmall = isl_sioimath_get_small(lhs); + isl_sioimath_set_small(dst, lhssmall / (int32_t) rhs); + return; + } + + if (rhs <= MP_SMALL_MAX) { + mp_int_div_value(isl_sioimath_bigarg_src(lhs, &lhsscratch), rhs, + isl_sioimath_reinit_big(dst), NULL); + isl_sioimath_try_demote(dst); + return; + } + + mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_uiarg_src(rhs, &rhsscratch), + isl_sioimath_reinit_big(dst), NULL); + isl_sioimath_try_demote(dst); +} + +/* Divide lhs by rhs, rounding to positive infinity (Ceil). + */ +inline void isl_sioimath_cdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + int32_t lhssmall, rhssmall; + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t q; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + if ((lhssmall >= 0) && (rhssmall >= 0)) + q = ((int64_t) lhssmall + (int64_t) rhssmall - 1) / + rhssmall; + else if ((lhssmall < 0) && (rhssmall < 0)) + q = ((int64_t) lhssmall + (int64_t) rhssmall + 1) / + rhssmall; + else + q = lhssmall / rhssmall; + isl_sioimath_set_small(dst, q); + return; + } + + impz_cdiv_q(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +/* Divide lhs by rhs, rounding to negative infinity (Floor). + */ +inline void isl_sioimath_fdiv_q(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + int32_t q; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + if ((lhssmall < 0) && (rhssmall >= 0)) + q = ((int64_t) lhssmall - ((int64_t) rhssmall - 1)) / + rhssmall; + else if ((lhssmall >= 0) && (rhssmall < 0)) + q = ((int64_t) lhssmall - ((int64_t) rhssmall + 1)) / + rhssmall; + else + q = lhssmall / rhssmall; + isl_sioimath_set_small(dst, q); + return; + } + + impz_fdiv_q(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +/* Compute the division of lhs by a rhs of type unsigned long, rounding towards + * negative infinity (Floor). + */ +inline void isl_sioimath_fdiv_q_ui(isl_sioimath_ptr dst, isl_sioimath_src lhs, + unsigned long rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, q; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && (rhs <= INT32_MAX)) { + if (lhssmall >= 0) + q = (uint32_t) lhssmall / rhs; + else + q = ((int64_t) lhssmall - ((int64_t) rhs - 1)) / + (int64_t) rhs; + isl_sioimath_set_small(dst, q); + return; + } + + impz_fdiv_q(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_uiarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +/* Get the remainder of: lhs divided by rhs rounded towards negative infinite + * (Floor). + */ +inline void isl_sioimath_fdiv_r(isl_sioimath_ptr dst, isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int64_t lhssmall, rhssmall; + int32_t r; + + if (isl_sioimath_is_small(lhs) && isl_sioimath_is_small(rhs)) { + lhssmall = isl_sioimath_get_small(lhs); + rhssmall = isl_sioimath_get_small(rhs); + r = (rhssmall + lhssmall % rhssmall) % rhssmall; + isl_sioimath_set_small(dst, r); + return; + } + + impz_fdiv_r(isl_sioimath_reinit_big(dst), + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); + isl_sioimath_try_demote(dst); +} + +void isl_sioimath_read(isl_sioimath_ptr dst, const char *str); + +/* Return: + * +1 for a positive number + * -1 for a negative number + * 0 if the number is zero + */ +inline int isl_sioimath_sgn(isl_sioimath_src arg) +{ + int32_t small; + + if (isl_sioimath_decode_small(arg, &small)) + return (small > 0) - (small < 0); + + return mp_int_compare_zero(isl_sioimath_get_big(arg)); +} + +/* Return: + * +1 if lhs > rhs + * -1 if lhs < rhs + * 0 if lhs = rhs + */ +inline int isl_sioimath_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) + return (lhssmall > rhssmall) - (lhssmall < rhssmall); + + if (isl_sioimath_decode_small(rhs, &rhssmall)) + return mp_int_compare_value( + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhssmall); + + if (isl_sioimath_decode_small(lhs, &lhssmall)) + return -mp_int_compare_value( + isl_sioimath_bigarg_src(rhs, &rhsscratch), lhssmall); + + return mp_int_compare( + isl_sioimath_get_big(lhs), isl_sioimath_get_big(rhs)); +} + +/* As isl_sioimath_cmp, but with signed long rhs. + */ +inline int isl_sioimath_cmp_si(isl_sioimath_src lhs, signed long rhs) +{ + int32_t lhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall)) + return (lhssmall > rhs) - (lhssmall < rhs); + + return mp_int_compare_value(isl_sioimath_get_big(lhs), rhs); +} + +/* Return: + * +1 if |lhs| > |rhs| + * -1 if |lhs| < |rhs| + * 0 if |lhs| = |rhs| + */ +inline int isl_sioimath_abs_cmp(isl_sioimath_src lhs, isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) { + lhssmall = labs(lhssmall); + rhssmall = labs(rhssmall); + return (lhssmall > rhssmall) - (lhssmall < rhssmall); + } + + return mp_int_compare_unsigned( + isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch)); +} + +/* Return whether lhs is divisible by rhs. + */ +inline int isl_sioimath_is_divisible_by(isl_sioimath_src lhs, + isl_sioimath_src rhs) +{ + isl_sioimath_scratchspace_t lhsscratch, rhsscratch; + int32_t lhssmall, rhssmall; + mpz_t rem; + int cmp; + + if (isl_sioimath_decode_small(lhs, &lhssmall) && + isl_sioimath_decode_small(rhs, &rhssmall)) + return lhssmall % rhssmall == 0; + + if (isl_sioimath_decode_small(rhs, &rhssmall)) + return mp_int_divisible_value( + isl_sioimath_bigarg_src(lhs, &lhsscratch), rhssmall); + + mp_int_init(&rem); + mp_int_div(isl_sioimath_bigarg_src(lhs, &lhsscratch), + isl_sioimath_bigarg_src(rhs, &rhsscratch), NULL, &rem); + cmp = mp_int_compare_zero(&rem); + mp_int_clear(&rem); + return cmp == 0; +} + +/* Return a hash code of an isl_sioimath. + * The hash code for a number in small and big representation must be identical + * on the same machine because small representation if not obligatory if fits. + */ +inline uint32_t isl_sioimath_hash(isl_sioimath_src arg, uint32_t hash) +{ + int32_t small; + int i; + uint32_t num; + mp_digit digits[(sizeof(uint32_t) + sizeof(mp_digit) - 1) / + sizeof(mp_digit)]; + mp_size used; + const unsigned char *digitdata = (const unsigned char *) &digits; + + if (isl_sioimath_decode_small(arg, &small)) { + if (small < 0) + isl_hash_byte(hash, 0xFF); + num = labs(small); + + isl_siomath_uint32_to_digits(num, digits, &used); + for (i = 0; i < used * sizeof(mp_digit); i += 1) + isl_hash_byte(hash, digitdata[i]); + return hash; + } + + return isl_imath_hash(isl_sioimath_get_big(arg), hash); +} + +/* Return the number of digits in a number of the given base or more, i.e. the + * string length without sign and null terminator. + * + * Current implementation for small representation returns the maximal number + * of binary digits in that representation, which can be much larger than the + * smallest possible solution. + */ +inline size_t isl_sioimath_sizeinbase(isl_sioimath_src arg, int base) +{ + int32_t small; + + if (isl_sioimath_decode_small(arg, &small)) + return sizeof(int32_t) * CHAR_BIT - 1; + + return impz_sizeinbase(isl_sioimath_get_big(arg), base); +} + +void isl_sioimath_print(FILE *out, isl_sioimath_src i, int width); +void isl_sioimath_dump(isl_sioimath_src arg); + +typedef isl_sioimath isl_int; +#define isl_int_init(i) isl_sioimath_init(&(i)) +#define isl_int_clear(i) isl_sioimath_clear(&(i)) + +#define isl_int_set(r, i) isl_sioimath_set(&(r), i) +#define isl_int_set_si(r, i) isl_sioimath_set_si(&(r), i) +#define isl_int_set_ui(r, i) isl_sioimath_set_ui(&(r), i) +#define isl_int_fits_slong(r) isl_sioimath_fits_slong(r) +#define isl_int_get_si(r) isl_sioimath_get_si(r) +#define isl_int_fits_ulong(r) isl_sioimath_fits_ulong(r) +#define isl_int_get_ui(r) isl_sioimath_get_ui(r) +#define isl_int_get_d(r) isl_sioimath_get_d(r) +#define isl_int_get_str(r) isl_sioimath_get_str(r) +#define isl_int_abs(r, i) isl_sioimath_abs(&(r), i) +#define isl_int_neg(r, i) isl_sioimath_neg(&(r), i) +#define isl_int_swap(i, j) isl_sioimath_swap(&(i), &(j)) +#define isl_int_swap_or_set(i, j) isl_sioimath_swap(&(i), &(j)) +#define isl_int_add_ui(r, i, j) isl_sioimath_add_ui(&(r), i, j) +#define isl_int_sub_ui(r, i, j) isl_sioimath_sub_ui(&(r), i, j) + +#define isl_int_add(r, i, j) isl_sioimath_add(&(r), i, j) +#define isl_int_sub(r, i, j) isl_sioimath_sub(&(r), i, j) +#define isl_int_mul(r, i, j) isl_sioimath_mul(&(r), i, j) +#define isl_int_mul_2exp(r, i, j) isl_sioimath_mul_2exp(&(r), i, j) +#define isl_int_mul_si(r, i, j) isl_sioimath_mul_si(&(r), i, j) +#define isl_int_mul_ui(r, i, j) isl_sioimath_mul_ui(&(r), i, j) +#define isl_int_pow_ui(r, i, j) isl_sioimath_pow_ui(&(r), i, j) +#define isl_int_addmul(r, i, j) isl_sioimath_addmul(&(r), i, j) +#define isl_int_addmul_ui(r, i, j) isl_sioimath_addmul_ui(&(r), i, j) +#define isl_int_submul(r, i, j) isl_sioimath_submul(&(r), i, j) +#define isl_int_submul_ui(r, i, j) isl_sioimath_submul_ui(&(r), i, j) + +#define isl_int_gcd(r, i, j) isl_sioimath_gcd(&(r), i, j) +#define isl_int_lcm(r, i, j) isl_sioimath_lcm(&(r), i, j) +#define isl_int_divexact(r, i, j) isl_sioimath_tdiv_q(&(r), i, j) +#define isl_int_divexact_ui(r, i, j) isl_sioimath_tdiv_q_ui(&(r), i, j) +#define isl_int_tdiv_q(r, i, j) isl_sioimath_tdiv_q(&(r), i, j) +#define isl_int_cdiv_q(r, i, j) isl_sioimath_cdiv_q(&(r), i, j) +#define isl_int_fdiv_q(r, i, j) isl_sioimath_fdiv_q(&(r), i, j) +#define isl_int_fdiv_r(r, i, j) isl_sioimath_fdiv_r(&(r), i, j) +#define isl_int_fdiv_q_ui(r, i, j) isl_sioimath_fdiv_q_ui(&(r), i, j) + +#define isl_int_read(r, s) isl_sioimath_read(&(r), s) +#define isl_int_sgn(i) isl_sioimath_sgn(i) +#define isl_int_cmp(i, j) isl_sioimath_cmp(i, j) +#define isl_int_cmp_si(i, si) isl_sioimath_cmp_si(i, si) +#define isl_int_eq(i, j) (isl_sioimath_cmp(i, j) == 0) +#define isl_int_ne(i, j) (isl_sioimath_cmp(i, j) != 0) +#define isl_int_lt(i, j) (isl_sioimath_cmp(i, j) < 0) +#define isl_int_le(i, j) (isl_sioimath_cmp(i, j) <= 0) +#define isl_int_gt(i, j) (isl_sioimath_cmp(i, j) > 0) +#define isl_int_ge(i, j) (isl_sioimath_cmp(i, j) >= 0) +#define isl_int_abs_cmp(i, j) isl_sioimath_abs_cmp(i, j) +#define isl_int_abs_eq(i, j) (isl_sioimath_abs_cmp(i, j) == 0) +#define isl_int_abs_ne(i, j) (isl_sioimath_abs_cmp(i, j) != 0) +#define isl_int_abs_lt(i, j) (isl_sioimath_abs_cmp(i, j) < 0) +#define isl_int_abs_gt(i, j) (isl_sioimath_abs_cmp(i, j) > 0) +#define isl_int_abs_ge(i, j) (isl_sioimath_abs_cmp(i, j) >= 0) +#define isl_int_is_divisible_by(i, j) isl_sioimath_is_divisible_by(i, j) + +#define isl_int_hash(v, h) isl_sioimath_hash(v, h) +#define isl_int_free_str(s) free(s) +#define isl_int_print(out, i, width) isl_sioimath_print(out, i, width) + +#endif /* ISL_INT_SIOIMATH_H */ |

