/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file implements the functions handling GMP integers. ** ** There are three integer types in GAP: 'T_INT', 'T_INTPOS' and 'T_INTNEG'. ** Each integer has a unique representation, e.g., an integer that can be ** represented as 'T_INT' is never represented as 'T_INTPOS' or 'T_INTNEG'. ** ** In the following, let 'N' be the number of bits in an UInt (so 32 or ** 64, depending on the system). 'T_INT' is the type of those integers small ** enough to fit into N-3 bits. Therefore the value range of this small ** integers is $-2^{N-4}...2^{N-4}-1$. Only these small integers can be used ** as index expression into sequences. ** ** Small integers are represented by an immediate integer handle, containing ** the value instead of pointing to it, which has the following form: ** ** +-------+-------+-------+-------+- - - -+-------+-------+-------+ ** | guard | sign | bit | bit | | bit | tag | tag | ** | bit | bit | N-5 | N-6 | | 0 | = 0 | = 1 | ** +-------+-------+-------+-------+- - - -+-------+-------+-------+ ** ** Immediate integers handles carry the tag 'T_INT', i.e. the last bit is 1. ** This distinguishes immediate integers from other handles which point to ** structures aligned on even boundaries and therefore have last bit zero. ** (The second bit is reserved as tag to allow extensions of this scheme.) ** Using immediates as pointers and dereferencing them gives address errors. ** ** To aid overflow check the most significant two bits must always be equal, ** that is to say that the sign bit of immediate integers has a guard bit. ** ** The macros 'INTOBJ_INT' and 'INT_INTOBJ' should be used to convert between ** a small integer value and its representation as immediate integer handle. ** ** 'T_INTPOS' and 'T_INTNEG' are the types of positive respectively negative ** integer values that cannot be represented by immediate integers. ** ** These large integers values are represented as low-level GMP integer ** objects, that is, in base 2^N. That means that the bag of a large integer ** has the following form, where the "digits" are also more commonly referred ** to as "limbs".: ** ** +-------+-------+-------+-------+- - - -+-------+-------+-------+ ** | digit | digit | digit | digit | | digit | digit | digit | ** | 0 | 1 | 2 | 3 | | <n>-2 | <n>-1 | <n> | ** +-------+-------+-------+-------+- - - -+-------+-------+-------+ ** ** The value of this is: $d0 + d1 2^N + d2 (2^N)^2 + ... + d_n (2^N)^n$, ** respectively the negative of this if the object type is 'T_INTNEG'. ** ** Each digit resp. limb is stored as a N bit wide unsigned integer. ** ** Note that we require that all large integers be normalized (that is, they ** must not contain leading zero limbs) and reduced (they do not fit into a ** small integer). Internally, a large integer may temporarily not be ** normalized or not be reduced, but all kernel functions must make sure ** that they eventually return normalized and reduced values. The function ** GMP_NORMALIZE can be used to ensure this.
*/
#if GMP_NAIL_BITS != 0 #error Aborting compile: GAP does not support non-zero GMP nail size #endif #if !defined(__GNU_MP_RELEASE) #if __GMP_MP_RELEASE < 50002 #error Aborting compile: GAP requires GMP 5.0.2 or newer #endif #endif
#define RequireNonzero(funcname, op, argname) \ do { \ if (op == INTOBJ_INT(0)) { \
RequireArgumentEx(funcname, op, "<" argname ">", \ "must be a nonzero integer"); \
} \
} while (0)
GAP_STATIC_ASSERT( sizeof(mp_limb_t) == sizeof(UInt), "gmp limb size incompatible with GAP word size");
/* This ensures that all memory underlying a bag is actually committed ** to physical memory and can be written to. ** This is a workaround to a bug specific to Cygwin 64-bit and bad ** interaction with GMP, so this is only needed specifically for new ** bags created in this module to hold the outputs of GMP routines. ** ** Thus, any time NewBag is called, it is also necessary to call ** ENSURE_BAG(bag) on the newly created bag if some GMP function will be ** the first place that bag's data is written to. ** ** To give a counter-example, ENSURE_BAG is *not* needed in ObjInt_Int, ** because it just creates a bag to hold a single mp_limb_t, and ** immediately assigns it a value. ** ** The bug this works around is explained more in ** https://github.com/gap-system/gap/issues/3434
*/ staticinlinevoid ENSURE_BAG(Bag bag)
{ // Note: This workaround is only required with the original GMP and not with // MPIR #ifdefined(SYS_IS_CYGWIN32) && defined(SYS_IS_64_BIT) && \
!defined(__MPIR_VERSION)
memset(PTR_BAG(bag), 0, SIZE_BAG(bag)); #endif
}
// for fallbacks to library static Obj String; static Obj IsIntFilt;
/**************************************************************************** ** *F TypeInt(<val>) . . . . . . . . . . . . . . . . . . . . . type of integer ** ** 'TypeInt' returns the type of the integer <val>. ** ** 'TypeInt' is the function in 'TypeObjFuncs' for integers.
*/ static Obj TYPE_INT_SMALL_ZERO; static Obj TYPE_INT_SMALL_POS; static Obj TYPE_INT_SMALL_NEG; static Obj TYPE_INT_LARGE_POS; static Obj TYPE_INT_LARGE_NEG;
/**************************************************************************** ** ** In order to use the high-level GMP mpz_* functions conveniently while ** retaining a low overhead, we introduce fake_mpz_t, which can be thought ** of as a "subclass" of mpz_t, extending it by temporary storage for ** single limb of data, as well as a reference to a corresponding GAP ** object. ** ** For an example on how to correctly use this, see GcdInt.
*/ typedefstruct {
mpz_t v;
mp_limb_t tmp;
Obj obj;
} fake_mpz_t[1];
/**************************************************************************** ** *F NEW_FAKEMPZ( <fake>, <size> ) ** ** Setup a fake mpz_t object for capturing the output of a GMP mpz_ function, ** with space for up to <size> limbs allocated.
*/ staticvoid NEW_FAKEMPZ( fake_mpz_t fake, UInt size )
{
fake->v->_mp_alloc = size;
fake->v->_mp_size = 0; if (size == 1) {
fake->obj = 0;
} else {
fake->obj = NewBag( T_INTPOS, size * sizeof(mp_limb_t) );
ENSURE_BAG(fake->obj);
}
}
/**************************************************************************** ** *F FAKEMPZ_GMPorINTOBJ( <fake>, <op> ) ** ** Initialize <fake> to reference the content of <op>. For this, <op> ** must be an integer, either small or large, but this is *not* checked. ** The calling code is responsible for any verification.
*/ staticvoid FAKEMPZ_GMPorINTOBJ( fake_mpz_t fake, Obj op )
{ if (IS_INTOBJ(op)) {
fake->obj = 0;
fake->v->_mp_alloc = 1; constInt i = INT_INTOBJ(op); if ( i >= 0 ) {
fake->tmp = i;
fake->v->_mp_size = i ? 1 : 0;
} else {
fake->tmp = -i;
fake->v->_mp_size = -1;
}
} else {
fake->obj = op;
fake->v->_mp_alloc = SIZE_INT(op);
fake->v->_mp_size = IS_INTPOS(op) ? SIZE_INT(op) : -SIZE_INT(op);
}
}
/**************************************************************************** ** *F GMPorINTOBJ_FAKEMPZ( <fake> ) ** ** This function converts a fake mpz_t into a GAP integer object.
*/ static Obj GMPorINTOBJ_FAKEMPZ( fake_mpz_t fake )
{
Obj obj = fake->obj; if ( fake->v->_mp_size == 0 ) {
obj = INTOBJ_INT(0);
} elseif ( obj != 0 ) { if ( fake->v->_mp_size < 0 ) { /* Warning: changing the bag type is only correct if the object was not yet visible to the outside world. Thus, it is safe to use with an fake_mpz_t initialized with NEW_FAKEMPZ, but not with
one that was setup by FAKEMPZ_GMPorINTOBJ. */
RetypeBag( obj, T_INTNEG );
}
obj = GMP_NORMALIZE( obj );
} else { if ( fake->v->_mp_size == 1 )
obj = ObjInt_UInt( fake->tmp ); else
obj = ObjInt_UIntInv( fake->tmp );
} return obj;
}
/**************************************************************************** ** *F GMPorINTOBJ_MPZ( <fake> ) ** ** This function converts an mpz_t into a GAP integer object.
*/ static Obj GMPorINTOBJ_MPZ( mpz_t v )
{ return MakeObjInt((const UInt *)v->_mp_d, v->_mp_size);
}
/**************************************************************************** ** ** MPZ_FAKEMPZ( <fake> ) ** ** This converts a fake_mpz_t into an mpz_t. As a side effect, it updates ** fake->v->_mp_d. This allows us to use SWAP on fake_mpz_t objects, and ** also protects against garbage collection moving around data.
*/ #define MPZ_FAKEMPZ(fake) (UPDATE_FAKEMPZ(fake), fake->v)
// UPDATE_FAKEMPZ is a helper function for the MPZ_FAKEMPZ macro staticinlinevoid UPDATE_FAKEMPZ( fake_mpz_t fake )
{
fake->v->_mp_d = fake->obj ? (mp_ptr)ADDR_INT(fake->obj) : &fake->tmp;
}
/**************************************************************************** ** *F GMP_NORMALIZE( <op> ) . . . . . . . remove leading zeros from a GMP bag ** ** 'GMP_NORMALIZE' removes any leading zeros from a large integer object ** and returns a small int or resizes the bag if possible. **
*/
Obj GMP_NORMALIZE(Obj op)
{
mp_size_t size; if (IS_INTOBJ(op)) { return op;
} for (size = SIZE_INT(op); size != (mp_size_t)1; size--) { if (CONST_ADDR_INT(op)[(size - 1)] != 0) { break;
}
} if (size < SIZE_INT(op)) {
ResizeBag(op, size * sizeof(mp_limb_t));
}
/**************************************************************************** ** ** This is a helper function for the CHECK_INT macro, which checks that ** the given integer object <op> is normalized and reduced. **
*/ #if DEBUG_GMP staticBOOL IS_NORMALIZED_AND_REDUCED(Obj op, constchar * func, int line)
{
mp_size_t size; if ( IS_INTOBJ( op ) ) { returnTRUE;
} if ( !IS_LARGEINT( op ) ) { // ignore non-integers returnFALSE;
} for ( size = SIZE_INT(op); size != (mp_size_t)1; size-- ) { if ( CONST_ADDR_INT(op)[(size - 1)] != 0 ) { break;
}
} if ( size < SIZE_INT(op) ) {
Pr("WARNING: non-normalized gmp value (%s:%d)\n",(Int)func,line);
} if ( SIZE_INT(op) == 1) { if ( ( VAL_LIMB0(op) <= INT_INTOBJ_MAX ) ||
( IS_INTNEG(op) && VAL_LIMB0(op) == -INT_INTOBJ_MIN ) ) { if ( IS_INTNEG(op) ) {
Pr("WARNING: non-reduced negative gmp value (%s:%d)\n",(Int)func,line); returnFALSE;
} else {
Pr("WARNING: non-reduced positive gmp value (%s:%d)\n",(Int)func,line); returnFALSE;
}
}
} returnTRUE;
} #endif
/**************************************************************************** ** *F ObjInt_Int( <cint> ) . . . . . . . . . . convert c int to integer object ** ** 'ObjInt_Int' takes the C integer <cint> and returns the equivalent ** GMP obj or int obj, according to the value of <cint>. **
*/
Obj ObjInt_Int( Int i )
{
Obj gmp;
if (INT_INTOBJ_MIN <= i && i <= INT_INTOBJ_MAX) { return INTOBJ_INT(i);
} elseif (i < 0 ) {
gmp = NewBag( T_INTNEG, sizeof(mp_limb_t) );
i = -i;
} else {
gmp = NewBag( T_INTPOS, sizeof(mp_limb_t) );
}
SET_VAL_LIMB0( gmp, i ); return gmp;
}
Obj ObjInt_UInt( UInt i )
{
Obj gmp; if (i <= INT_INTOBJ_MAX) { return INTOBJ_INT(i);
} else {
gmp = NewBag( T_INTPOS, sizeof(mp_limb_t) );
SET_VAL_LIMB0( gmp, i ); return gmp;
}
}
// initialize to -i
Obj ObjInt_UIntInv( UInt i )
{
Obj gmp; // we need to test INT_INTOBJ_MIN <= -i; to express this with unsigned // values, we must avoid all negative terms, which leads to this equivalent // check: if (i <= -INT_INTOBJ_MIN) { return INTOBJ_INT(-i);
} else {
gmp = NewBag( T_INTNEG, sizeof(mp_limb_t) );
SET_VAL_LIMB0( gmp, i ); return gmp;
}
}
Obj ObjInt_Int8( Int8 i )
{ #ifdef SYS_IS_64_BIT return ObjInt_Int(i); #else if (i == (Int4)i) { return ObjInt_Int((Int4)i);
}
// we need two limbs to store this integer
assert( sizeof(mp_limb_t) == 4 );
Obj gmp; if (i >= 0) {
gmp = NewBag( T_INTPOS, 2 * sizeof(mp_limb_t) );
} else {
gmp = NewBag( T_INTNEG, 2 * sizeof(mp_limb_t) );
i = -i;
}
Obj ObjInt_UInt8( UInt8 i )
{ #ifdef SYS_IS_64_BIT return ObjInt_UInt(i); #else if (i == (UInt4)i) { return ObjInt_UInt((UInt4)i);
}
// we need two limbs to store this integer
assert( sizeof(mp_limb_t) == 4 );
Obj gmp = NewBag( T_INTPOS, 2 * sizeof(mp_limb_t) );
UInt *ptr = ADDR_INT(gmp);
ptr[0] = (UInt4)i;
ptr[1] = ((UInt8)i) >> 32; return gmp; #endif
}
/**************************************************************************** ** ** Convert GAP Integers to various C types -- see header file
*/ Int Int_ObjInt(Obj i)
{
UInt sign = 0; if (IS_INTOBJ(i)) return INT_INTOBJ(i); // must be a single limb if (TNUM_OBJ(i) == T_INTPOS)
sign = 0; elseif (TNUM_OBJ(i) == T_INTNEG)
sign = 1; else
RequireArgument("Conversion error", i, "must be an integer"); if (SIZE_BAG(i) != sizeof(mp_limb_t))
ErrorMayQuit("Conversion error: integer too large", 0, 0);
// now check if val is small enough to fit in the signed Int type // that has a range from -2^N to 2^N-1 so we need to check both ends // Since -2^N is the same bit pattern as the UInt 2^N (N is 31 or 63) // we can do it as below which avoids some compiler warnings
UInt val = VAL_LIMB0(i); #ifdef SYS_IS_64_BIT if ((!sign && (val > INT64_MAX)) || (sign && (val > (UInt)INT64_MIN))) #else if ((!sign && (val > INT32_MAX)) || (sign && (val > (UInt)INT32_MIN))) #endif
ErrorMayQuit("Conversion error: integer too large", 0, 0); return sign ? -(Int)val : (Int)val;
}
UInt UInt_ObjInt(Obj i)
{ if (IS_NEG_INT(i))
ErrorMayQuit("Conversion error: cannot convert negative integer to unsigned type", 0, 0); if (IS_INTOBJ(i)) return (UInt)INT_INTOBJ(i); if (TNUM_OBJ(i) != T_INTPOS)
RequireArgument("Conversion error", i, "must be a non-negative integer");
// must be a single limb if (SIZE_INT(i) != 1)
ErrorMayQuit("Conversion error: integer too large", 0, 0); return VAL_LIMB0(i);
}
Int8 Int8_ObjInt(Obj i)
{ #ifdef SYS_IS_64_BIT // in this case Int8 is Int return Int_ObjInt(i); #else if (IS_INTOBJ(i)) return (Int8)INT_INTOBJ(i);
UInt sign = 0; if (TNUM_OBJ(i) == T_INTPOS)
sign = 0; elseif (TNUM_OBJ(i) == T_INTNEG)
sign = 1; else
RequireArgument("Conversion error", i, "must be an integer");
// must be at most two limbs if (SIZE_INT(i) > 2)
ErrorMayQuit("Conversion error: integer too large", 0, 0);
UInt vall = VAL_LIMB0(i);
UInt valh = (SIZE_INT(i) == 1) ? 0 : CONST_ADDR_INT(i)[1];
UInt8 val = (UInt8)vall + ((UInt8)valh << 32); // now check if val is small enough to fit in the signed Int8 type // that has a range from -2^63 to 2^63-1 so we need to check both ends // Since -2^63 is the same bit pattern as the UInt8 2^63 we can do it // this way which avoids some compiler warnings if ((!sign && (val > INT64_MAX)) || (sign && (val > (UInt8)INT64_MIN)))
ErrorMayQuit("Conversion error: integer too large", 0, 0); return sign ? -(Int8)val : (Int8)val; #endif
}
UInt8 UInt8_ObjInt(Obj i)
{ #ifdef SYS_IS_64_BIT // in this case UInt8 is UInt return UInt_ObjInt(i); #else if (IS_NEG_INT(i))
ErrorMayQuit("Conversion error: cannot convert negative integer to unsigned type", 0, 0); if (IS_INTOBJ(i)) return (UInt8)INT_INTOBJ(i); if (TNUM_OBJ(i) != T_INTPOS)
RequireArgument("Conversion error", i, "must be a non-negative integer"); if (SIZE_INT(i) > 2)
ErrorMayQuit("Conversion error: integer too large", 0, 0);
UInt vall = VAL_LIMB0(i);
UInt valh = (SIZE_INT(i) == 1) ? 0 : CONST_ADDR_INT(i)[1]; return (UInt8)vall + ((UInt8)valh << 32); #endif
}
// This function returns an immediate integer, or // an integer object with exactly one limb, and returns // its absolute value as an unsigned integer. staticinline UInt AbsOfSmallInt(Obj x)
{ if (!IS_INTOBJ(x)) {
GAP_ASSERT(SIZE_INT(x) == 1); return VAL_LIMB0(x);
} Int val = INT_INTOBJ(x); return val > 0 ? val : -val;
}
/**************************************************************************** ** *F PrintInt( <op> ) . . . . . . . . . . . . . . . . print an integer object ** ** 'PrintInt' prints the integer <op> in the usual decimal notation.
*/ void PrintInt ( Obj op )
{ // print a small integer if ( IS_INTOBJ(op) ) {
Pr("%>%d%<", INT_INTOBJ(op), 0);
}
// print a large integer elseif ( SIZE_INT(op) < 1000 ) {
CHECK_INT(op);
/* Use GMP to print the integer to a buffer. We are looking at an integer with less than 1000 limbs, hence in decimal notation, it will take up at most LogInt( 2^(1000 * GMP_LIMB_BITS), 10) digits. Since 1000*Log(2)/Log(10) = 301.03, we get the following estimate for the buffer size (the overestimate is big enough to
include space for a sign and a null terminator). */ Char buf[302 * GMP_LIMB_BITS];
mpz_t v;
v->_mp_alloc = SIZE_INT(op);
v->_mp_size = IS_INTPOS(op) ? v->_mp_alloc : -v->_mp_alloc;
v->_mp_d = (mp_ptr)ADDR_INT(op);
mpz_get_str(buf, 10, v);
// print the buffer, %> means insert '\' before a linebreak
Pr("%>%s%<",(Int)buf, 0);
} else {
Obj str = CALL_1ARGS( String, op );
Pr("%>", 0, 0);
PrintString1(str);
Pr("%<", 0, 0); /* for a long time Print of large ints did not follow the general idea * that Print should produce something that can be read back into GAP:
Pr("<<an integer too large to be printed>>", 0, 0); */
}
}
/**************************************************************************** ** *F StringIntBase( <op>, <base> ) ** ** Convert the integer <op> to a string relative to the given base <base>. ** Here, base may range from 2 to 36.
*/ static Obj StringIntBase(Obj op, int base)
{ int len;
Obj res;
fake_mpz_t v;
GAP_ASSERT(IS_INT(op));
CHECK_INT(op);
GAP_ASSERT(2 <= base && base <= 36);
// 0 is special if (op == INTOBJ_INT(0)) {
res = NEW_STRING(1);
CHARS_STRING(res)[0] = '0'; return res;
}
// convert integer to fake_mpz_t
FAKEMPZ_GMPorINTOBJ(v, op);
// allocate the result string
len = mpz_sizeinbase( MPZ_FAKEMPZ(v), base ) + 2;
res = NEW_STRING( len );
// ask GMP to perform the actual conversion
mpz_get_str( CSTR_STRING( res ), -base, MPZ_FAKEMPZ(v) );
// we may have to shrink the string int real_len = strlen( CONST_CSTR_STRING(res) ); if ( real_len != GET_LEN_STRING(res) ) {
SET_LEN_STRING(res, real_len);
}
return res;
}
/**************************************************************************** ** *F FuncHexStringInt( <self>, <n> ) . . . . . . . . . hex string for GAP int *F FuncIntHexString( <self>, <string> ) . . . . . . GAP int from hex string ** ** The function `FuncHexStringInt' constructs from a GAP integer the ** corresponding string in hexadecimal notation. It has a leading '-' ** for negative numbers and the digits 10..15 are written as A..F. ** ** The function `FuncIntHexString' does the converse, but here the ** letters a..f are also allowed in <string> instead of A..F. **
*/ static Obj FuncHexStringInt(Obj self, Obj n)
{
RequireInt(SELF_NAME, n); return StringIntBase(n, 16);
}
/**************************************************************************** ** ** This helper function for IntHexString reads <len> bytes in the string ** <p> and parses them as a hexadecimal. The resulting integer is returned. ** This function does not check for overflow, so make sure that len*4 does ** not exceed the number of bits in mp_limb_t.
**/ static mp_limb_t hexstr2int( const UInt1 *p, UInt len )
{
mp_limb_t n = 0;
UInt1 a; while (len--) {
a = *p++; if (a >= 'a')
a -= 'a' - 10; elseif ( a>= 'A')
a -= 'A' - 10; else
a -= '0'; if (a > 15)
ErrorMayQuit("IntHexString: invalid character in hex-string", 0, 0);
n = (n << 4) + a;
} return n;
}
Obj IntHexString(Obj str)
{
Obj res; Int i, len, sign, nd;
mp_limb_t n; const UInt1 *p;
UInt *limbs;
GAP_ASSERT(IS_STRING_REP(str));
len = GET_LEN_STRING(str); if (len == 0) {
res = INTOBJ_INT(0); return res;
}
p = CONST_CHARS_STRING(str); if (*p == '-') {
sign = -1;
i = 1;
} else {
sign = 1;
i = 0;
}
while (p[i] == '0' && i < len)
i++;
len -= i;
if (len*4 <= NR_SMALL_INT_BITS) {
n = hexstr2int( p + i, len );
res = INTOBJ_INT(sign * n); return res;
}
else { /* Each hex digit corresponds to 4 bits, and each GMP limb has sizeof(UInt) bytes, thus 2*sizeof(UInt) hex digits fit into one limb. We use this
to compute the number of limbs minus 1: */
nd = (len - 1) / (2*sizeof(UInt));
res = NewBag( (sign == 1) ? T_INTPOS : T_INTNEG, (nd + 1) * sizeof(mp_limb_t) );
// update pointer, in case a garbage collection happened
p = CONST_CHARS_STRING(str) + i;
limbs = ADDR_INT(res);
// if len is not divisible by 2*sizeof(UInt), then take care of the extra bytes
UInt diff = len - nd * (2*sizeof(UInt)); if ( diff ) {
n = hexstr2int( p, diff );
p += diff;
len -= diff;
limbs[nd--] = n;
}
while ( len ) {
n = hexstr2int( p, 2*sizeof(UInt) );
p += 2*sizeof(UInt);
len -= 2*sizeof(UInt);
limbs[nd--] = n;
}
res = GMP_NORMALIZE(res); return res;
}
}
/**************************************************************************** ** ** Implementation of Log2Int for C integers. ** ** When available, we try to use GCC builtins. Otherwise, fall back to code ** based on ** https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup ** On a test machine with x86 64bit, the builtins are about 4 times faster ** than the generic code. **
*/
Int res = 0;
UInt b;
b = a >> 32; if (b) { res+=32; a=b; }
b = a >> 16; if (b) { res+=16; a=b; }
b = a >> 8; if (b) { res+= 8; a=b; } return res + LogTable256[a]; #endif
}
Int CLog2Int(Int a)
{ if (a == 0) return -1; if (a < 0) a = -a; return CLog2UInt(a);
}
/**************************************************************************** ** *F FuncLog2Int( <self>, <n> ) . . . . . . . . . . . nr of bits of a GAP int ** ** Given to GAP-Level as "Log2Int".
*/ static Obj FuncLog2Int(Obj self, Obj n)
{
RequireInt(SELF_NAME, n);
if (IS_INTOBJ(n)) { return INTOBJ_INT(CLog2Int(INT_INTOBJ(n)));
}
UInt len = SIZE_INT(n) - 1;
UInt a = CLog2UInt(CONST_ADDR_INT(n)[len]);
CHECK_INT(n);
#ifdef SYS_IS_64_BIT return INTOBJ_INT(len * GMP_LIMB_BITS + a); #else /* The final result is len * GMP_LIMB_BITS - d, which may not
fit into an immediate integer (at least on a 32bit system) */ return SumInt(ProdInt(INTOBJ_INT(len), INTOBJ_INT(GMP_LIMB_BITS)),
INTOBJ_INT(a)); #endif
}
/**************************************************************************** ** *F FuncSTRING_INT( <self>, <n> ) . . . . . . convert an integer to a string ** ** `FuncSTRING_INT' returns an immutable string representing the integer <n> **
*/ static Obj FuncSTRING_INT(Obj self, Obj n)
{
RequireInt(SELF_NAME, n); return StringIntBase(n, 10);
}
Obj IntStringInternal(Obj string, constChar *str)
{
Obj val; // value = <upp> * <pow> + <low>
Obj upp; // upper part Int pow; // power Int low; // lower part Int sign; // is the integer negative
UInt i; // loop variable
// if <string> is given, then we ignore <str> if (string)
str = CONST_CSTR_STRING(string);
// get the sign, if any
sign = 1;
i = 0; if (str[i] == '-') {
sign = -sign;
i++;
}
// collect the digits in groups of 8, for improved performance // note that 2^26 < 10^8 < 2^27, so the intermediate // values always fit into an immediate integer
low = 0;
pow = 1;
upp = INTOBJ_INT(0); while (str[i] != '\0') { if (str[i] < '0' || str[i] > '9') { return Fail;
}
low = 10 * low + str[i] - '0';
pow = 10 * pow; if (pow == 100000000L) {
upp = ProdInt(upp, INTOBJ_INT(pow));
upp = SumInt(upp, INTOBJ_INT(sign*low)); // refresh 'str', in case the arithmetic operations triggered // a garbage collection if (string)
str = CONST_CSTR_STRING(string);
pow = 1;
low = 0;
}
i++;
}
// check if 0 char does not mark the end of the string if (string && i < GET_LEN_STRING(string)) return Fail;
// compose the integer value if (upp == INTOBJ_INT(0)) {
val = INTOBJ_INT(sign * low);
} elseif (pow == 1) {
val = upp;
} else {
upp = ProdInt(upp, INTOBJ_INT(pow));
val = SumInt(upp, INTOBJ_INT(sign * low));
}
// return the integer value return val;
}
/**************************************************************************** ** *F FuncINT_STRING( <self>, <string> ) . . . . convert a string to an integer ** ** `FuncINT_STRING' returns an integer representing the string, or ** fail if the string is not a valid integer. **
*/ static Obj FuncINT_STRING(Obj self, Obj string)
{ if( !IS_STRING(string) ) { return Fail;
}
/**************************************************************************** ** *F EqInt( <opL>, <opR> ) . . . . . . . . . . test if two integers are equal ** ** 'EqInt' returns 1 if the two integer arguments <opL> and <opR> are equal ** and 0 otherwise.
*/ Int EqInt(Obj opL, Obj opR)
{
CHECK_INT(opL);
CHECK_INT(opR);
// if at least one input is a small int, a naive equality test suffices if (IS_INTOBJ(opL) || IS_INTOBJ(opR)) return opL == opR;
// compare the sign and size // note: at this point we know that opL and opR are proper bags, so // we can use TNUM_BAG instead of TNUM_OBJ if (TNUM_BAG(opL) != TNUM_BAG(opR) || SIZE_INT(opL) != SIZE_INT(opR)) return 0;
/**************************************************************************** ** *F LtInt( <opL>, <opR> ) . . . . . . test if an integer is less than another ** ** 'LtInt' returns 1 if the integer <opL> is strictly less than the ** integer <opR> and 0 otherwise.
*/ Int LtInt(Obj opL, Obj opR)
{ Int res;
CHECK_INT(opL);
CHECK_INT(opR);
// compare two small integers if (ARE_INTOBJS(opL, opR)) return (Int)opL < (Int)opR;
// a small int is always less than a positive large int, // and always more than a negative large int if (IS_INTOBJ(opL)) return IS_INTPOS(opR); if (IS_INTOBJ(opR)) return IS_INTNEG(opL);
// at this point, both inputs are large integers, and we compare their // signs first if (TNUM_OBJ(opL) != TNUM_OBJ(opR)) return IS_INTNEG(opL);
// signs are equal; compare sizes and absolute values if (SIZE_INT(opL) < SIZE_INT(opR))
res = -1; elseif (SIZE_INT(opL) > SIZE_INT(opR))
res = +1; else
res = mpn_cmp((mp_srcptr)CONST_ADDR_INT(opL),
(mp_srcptr)CONST_ADDR_INT(opR), SIZE_INT(opL));
// if both arguments are negative, flip the result if (IS_INTNEG(opL))
res = -res;
return res < 0;
}
/**************************************************************************** ** *F SumOrDiffInt( <opL>, <opR>, <sign> ) . . . . sum or diff of two integers ** ** 'SumOrDiffInt' returns the sum or difference of the two integer arguments ** <opL> and <opR>, depending on whether sign is +1 or -1. ** ** 'SumOrDiffInt' is a little bit tricky since there are many different ** cases to handle: each operand can be positive or negative, small or large ** integer.
*/ static Obj SumOrDiffInt(Obj opL, Obj opR, Int sign)
{
UInt sizeL, sizeR;
fake_mpz_t mpzL, mpzR, mpzResult;
Obj result;
CHECK_INT(opL);
CHECK_INT(opR);
// handle trivial cases first if (opR == INTOBJ_INT(0)) return opL; if (opL == INTOBJ_INT(0)) { if (sign == 1) return opR; else return AInvInt(opR);
}
// add or subtract if (sign == 1)
mpz_add( MPZ_FAKEMPZ(mpzResult), MPZ_FAKEMPZ(mpzL), MPZ_FAKEMPZ(mpzR) ); else
mpz_sub( MPZ_FAKEMPZ(mpzResult), MPZ_FAKEMPZ(mpzL), MPZ_FAKEMPZ(mpzR) );
// convert result to GAP object and return it
CHECK_FAKEMPZ(mpzResult);
CHECK_FAKEMPZ(mpzL);
CHECK_FAKEMPZ(mpzR);
result = GMPorINTOBJ_FAKEMPZ( mpzResult );
CHECK_INT(result); return result;
}
// convert result to GAP object and return it
CHECK_FAKEMPZ(mpzResult);
CHECK_FAKEMPZ(mpzL);
CHECK_FAKEMPZ(mpzR);
prd = GMPorINTOBJ_FAKEMPZ( mpzResult );
CHECK_INT(prd); return prd;
}
/**************************************************************************** ** *F ProdIntObj(<n>,<op>) . . . . . . . . product of an integer and an object
*/ static Obj ProdIntObj ( Obj n, Obj op )
{
Obj res = 0; // result
UInt i, k; // loop variables
mp_limb_t l; // loop variable
CHECK_INT(n);
// if the integer is zero, return the neutral element of the operand if ( n == INTOBJ_INT(0) ) {
res = ZERO_SAMEMUT( op );
}
/* if the integer is one, return the object if immutable - if mutable, add the object to its ZeroSameMutability to
ensure correct mutability propagation */ elseif ( n == INTOBJ_INT(1) ) { if (IS_MUTABLE_OBJ(op))
res = SUM(ZERO_SAMEMUT(op),op); else
res = op;
}
// if the integer is minus one, return the inverse of the operand elseif ( n == INTOBJ_INT(-1) ) {
res = AINV_SAMEMUT( op );
}
// if the integer is negative, invert the operand and the integer elseif ( IS_NEG_INT(n) ) {
res = AINV_SAMEMUT( op ); if ( res == Fail ) {
ErrorMayQuit("Operations: must have an additive inverse", 0, 0);
}
res = PROD(AINV_SAMEMUT(n), res);
}
// if the integer is small, compute the product by repeated doubling // the loop invariant is <result> = <k>*<res> + <l>*<op>, <l> < <k> // <res> = 0 means that <res> is the neutral element elseif ( IS_INTOBJ(n) && INT_INTOBJ(n) > 1 ) {
res = 0;
k = (Int)1 << NR_SMALL_INT_BITS;
l = INT_INTOBJ(n); while ( 0 < k ) {
res = (res == 0 ? res : SUM( res, res )); if ( k <= l ) {
res = (res == 0 ? op : SUM( res, op ));
l = l - k;
}
k = k / 2;
}
}
// if the integer is large, compute the product by repeated doubling elseif ( IS_INTPOS(n) ) {
res = 0; for ( i = SIZE_INT(n); 0 < i; i-- ) {
k = 8*sizeof(mp_limb_t);
l = CONST_ADDR_INT(n)[i-1]; while ( 0 < k ) {
res = (res == 0 ? res : SUM( res, res ));
k--; if ( (l >> k) & 1 ) {
res = (res == 0 ? op : SUM( res, op ));
}
}
}
}
// power with a large exponent elseif ( ! IS_INTOBJ(opR) ) {
ErrorMayQuit("Integer operands: is too large", 0, 0);
}
// power with a negative exponent elseif ( INT_INTOBJ(opR) < 0 ) {
pow = QUO( INTOBJ_INT(1),
PowInt( opL, INTOBJ_INT( -INT_INTOBJ(opR)) ) );
}
// findme - can we use the gmp function mpz_n_pow_ui?
// power with a small positive exponent, do it by a repeated squaring else {
pow = INTOBJ_INT(1);
i = INT_INTOBJ(opR); while ( i != 0 ) { if ( i % 2 == 1 ) pow = ProdInt( pow, opL ); if ( i > 1 ) opL = ProdInt( opL, opL );
TakeInterrupt();
i = i / 2;
}
}
// return the power
CHECK_INT(pow); return pow;
}
/**************************************************************************** ** *F PowObjInt(<op>,<n>) . . . . . . . . . . power of an object and an integer
*/ static Obj PowObjInt(Obj op, Obj n)
{
Obj res = 0; // result
UInt i, k; // loop variables
mp_limb_t l; // loop variable
CHECK_INT(n);
// if the integer is zero, return the neutral element of the operand if ( n == INTOBJ_INT(0) ) { return ONE_SAMEMUT( op );
}
// if the integer is one, return a copy of the operand elseif ( n == INTOBJ_INT(1) ) {
res = CopyObj( op, 1 );
}
// if the integer is minus one, return the inverse of the operand elseif ( n == INTOBJ_INT(-1) ) {
res = INV_SAMEMUT( op );
}
// if the integer is negative, invert the operand and the integer elseif ( IS_NEG_INT(n) ) {
res = INV_SAMEMUT( op ); if ( res == Fail ) {
ErrorMayQuit("Operations: must have an inverse", 0, 0);
}
res = POW(res, AINV_SAMEMUT(n));
}
// if the integer is small, compute the power by repeated squaring // the loop invariant is <result> = <res>^<k> * <op>^<l>, <l> < <k> // <res> = 0 means that <res> is the neutral element elseif ( IS_INTOBJ(n) && INT_INTOBJ(n) > 0 ) {
res = 0;
k = (Int)1 << NR_SMALL_INT_BITS;
l = INT_INTOBJ(n); while ( 0 < k ) {
res = (res == 0 ? res : PROD( res, res )); if ( k <= l ) {
res = (res == 0 ? op : PROD( res, op ));
l = l - k;
}
k = k / 2;
}
}
// if the integer is large, compute the power by repeated squaring elseif ( IS_INTPOS(n) ) {
res = 0; for ( i = SIZE_INT(n); 0 < i; i-- ) {
k = 8*sizeof(mp_limb_t);
l = CONST_ADDR_INT(n)[i-1]; while ( 0 < k ) {
res = (res == 0 ? res : PROD( res, res ));
k--; if ( (l >> k) & 1 ) {
res = (res == 0 ? op : PROD( res, op ));
}
}
}
}
/**************************************************************************** ** *F ModInt( <opL>, <opR> ) . . representative of residue class of an integer ** ** 'ModInt' returns the smallest positive representative of the residue ** class of the integer <opL> modulo the integer <opR>.
*/
Obj ModInt(Obj opL, Obj opR)
{ Int i; // loop count, value for small int Int k; // loop count, value for small int
UInt c; // product of two digits
Obj mod; // handle of the remainder bag
Obj quo; // handle of the quotient bag
CHECK_INT(opL);
CHECK_INT(opR);
// pathological case first
RequireNonzero("Integer operations", opR, "divisor");
// compute the remainder of two small integers if ( ARE_INTOBJS( opL, opR ) ) {
// get the integer values
i = INT_INTOBJ(opL);
k = INT_INTOBJ(opR);
// compute the remainder, make sure we divide only positive numbers
i %= k; if (i < 0)
i += k > 0 ? k : -k;
mod = INTOBJ_INT(i);
}
// compute the remainder of a small integer by a large integer elseif ( IS_INTOBJ(opL) ) {
// the small int -(1<<28) mod the large int (1<<28) is 0 if ( opL == INTOBJ_MIN
&& ( IS_INTPOS(opR) )
&& ( SIZE_INT(opR) == 1 )
&& ( VAL_LIMB0(opR) == -INT_INTOBJ_MIN ) )
mod = INTOBJ_INT(0);
// in all other cases the remainder is equal the left operand elseif ( 0 <= INT_INTOBJ(opL) )
mod = opL; elseif ( IS_INTPOS(opR) )
mod = SumOrDiffInt( opL, opR, 1 ); else
mod = SumOrDiffInt( opL, opR, -1 );
}
// compute the remainder of a large integer by a small integer elseif ( IS_INTOBJ(opR) ) {
// get the integer value, make positive
i = INT_INTOBJ(opR); if ( i < 0 ) i = -i;
// check whether right operand is a small power of 2 if ( !(i & (i-1)) ) {
c = VAL_LIMB0(opL) & (i-1);
}
// otherwise use the gmp function to divide else {
c = mpn_mod_1( (mp_srcptr)CONST_ADDR_INT(opL), SIZE_INT(opL), i );
}
// now c is the absolute value of the actual result. Thus, if the left // operand is negative, and c is non-zero, we have to adjust it. if (IS_INTPOS(opL) || c == 0)
mod = INTOBJ_INT( c ); else // even if opR is INT_INTOBJ_MIN, and hence i is INT_INTOBJ_MAX+1, we // have 0 <= i-c <= INT_INTOBJ_MAX, so i-c fits into a small integer
mod = INTOBJ_INT( i - (Int)c );
}
// compute the remainder of a large integer modulo a large integer else {
// trivial case first if ( SIZE_INT(opL) < SIZE_INT(opR) ) { if ( IS_INTPOS(opL) ) return opL; elseif ( IS_INTPOS(opR) )
mod = SumOrDiffInt( opL, opR, 1 ); else
mod = SumOrDiffInt( opL, opR, -1 ); #if DEBUG_GMP
assert( !IS_NEG_INT(mod) ); #endif
CHECK_INT(mod); return mod;
}
mod = NewBag( TNUM_OBJ(opL), (SIZE_INT(opL)+1)*sizeof(mp_limb_t) );
ENSURE_BAG(mod);
quo = NewBag( T_INTPOS,
(SIZE_INT(opL)-SIZE_INT(opR)+1)*sizeof(mp_limb_t) );
ENSURE_BAG(quo);
// and let gmp do the work
mpn_tdiv_qr( (mp_ptr)ADDR_INT(quo), (mp_ptr)ADDR_INT(mod), 0,
(mp_srcptr)CONST_ADDR_INT(opL), SIZE_INT(opL),
(mp_srcptr)CONST_ADDR_INT(opR), SIZE_INT(opR) );
// reduce to small integer if possible, otherwise shrink bag
mod = GMP_NORMALIZE( mod );
// make the representative positive if ( IS_NEG_INT(mod) ) { if ( IS_INTPOS(opR) )
mod = SumOrDiffInt( mod, opR, 1 ); else
mod = SumOrDiffInt( mod, opR, -1 );
}
/**************************************************************************** ** *F QuoInt( <opL>, <opR> ) . . . . . . . . . . . . . quotient of two integers ** ** 'QuoInt' returns the integer part of the two integers <opL> and <opR>. ** ** Note that this routine is not called from 'EvalQuo', the division of two ** integers yields a rational and is therefore performed in 'QuoRat'. This ** operation is however available through the internal function 'Quo'.
*/
Obj QuoInt(Obj opL, Obj opR)
{ Int i; // loop count, value for small int Int k; // loop count, value for small int
Obj quo; // handle of the result bag
Obj rem; // handle of the remainder bag
CHECK_INT(opL);
CHECK_INT(opR);
// pathological case first
RequireNonzero("Integer operations", opR, "divisor");
// divide two small integers if ( ARE_INTOBJS( opL, opR ) ) {
// the small int -(1<<28) divided by -1 is the large int (1<<28) if ( opL == INTOBJ_MIN && opR == INTOBJ_INT(-1) ) {
quo = NewBag( T_INTPOS, sizeof(mp_limb_t) );
SET_VAL_LIMB0( quo, -INT_INTOBJ_MIN ); return quo;
}
// get the integer values
i = INT_INTOBJ(opL);
k = INT_INTOBJ(opR);
// divide, truncated towards zero; this is also what section 6.5.5 of the // C99 standard guarantees for integer division
quo = INTOBJ_INT(i / k);
}
// divide a small integer by a large one elseif ( IS_INTOBJ(opL) ) {
// the small int -(1<<28) divided by the large int (1<<28) is -1 if ( opL == INTOBJ_MIN
&& IS_INTPOS(opR) && SIZE_INT(opR) == 1
&& VAL_LIMB0(opR) == -INT_INTOBJ_MIN )
quo = INTOBJ_INT(-1);
// in all other cases the quotient is of course zero else
quo = INTOBJ_INT(0);
}
// divide a large integer by a small integer elseif ( IS_INTOBJ(opR) ) {
k = INT_INTOBJ(opR);
// allocate a bag for the result and set up the pointers if ( IS_INTNEG(opL) == ( k < 0 ) )
quo = NewBag( T_INTPOS, SIZE_OBJ(opL) ); else
quo = NewBag( T_INTNEG, SIZE_OBJ(opL) );
ENSURE_BAG(quo);
if ( k < 0 ) k = -k;
// use gmp function for dividing by a 1-limb number
mpn_divrem_1( (mp_ptr)ADDR_INT(quo), 0,
(mp_srcptr)CONST_ADDR_INT(opL), SIZE_INT(opL),
k );
}
// divide a large integer by a large integer else {
// trivial case first if ( SIZE_INT(opL) < SIZE_INT(opR) ) return INTOBJ_INT(0);
// create a new bag for the remainder
rem = NewBag( TNUM_OBJ(opL), (SIZE_INT(opL)+1)*sizeof(mp_limb_t) );
ENSURE_BAG(rem);
// allocate a bag for the quotient if ( TNUM_OBJ(opL) == TNUM_OBJ(opR) )
quo = NewBag( T_INTPOS,
(SIZE_INT(opL)-SIZE_INT(opR)+1)*sizeof(mp_limb_t) ); else
quo = NewBag( T_INTNEG,
(SIZE_INT(opL)-SIZE_INT(opR)+1)*sizeof(mp_limb_t) );
ENSURE_BAG(quo);
/**************************************************************************** ** *F FuncQUO_INT( <self>, <a>, <b> ) . . . . . . . internal function 'QuoInt' ** ** 'FuncQUO_INT' implements the internal function 'QuoInt'. ** ** 'QuoInt( <a>, <b> )' ** ** 'Quo' returns the integer part of the quotient of its integer operands. ** If <a> and <b> are positive 'Quo( <a>, <b> )' is the largest positive ** integer <q> such that '<q> * <b> \<= <a>'. If <a> or <b> or both are ** negative we define 'Abs( Quo(<a>,<b>) ) = Quo( Abs(<a>), Abs(<b>) )' and ** 'Sign( Quo(<a>,<b>) ) = Sign(<a>) * Sign(<b>)'. Dividing by 0 causes an ** error. 'Rem' (see "Rem") can be used to compute the remainder.
*/ static Obj FuncQUO_INT(Obj self, Obj a, Obj b)
{
RequireInt(SELF_NAME, a);
RequireInt(SELF_NAME, b); return QuoInt(a, b);
}
/**************************************************************************** ** *F RemInt( <opL>, <opR> ) . . . . . . . . . . . . remainder of two integers ** ** 'RemInt' returns the remainder of the quotient of the integers <opL> ** and <opR>. ** ** Note that the remainder is different from the value returned by the 'mod' ** operator which is always positive, while the result of 'RemInt' has ** the same sign as <opL>.
*/
Obj RemInt(Obj opL, Obj opR)
{ Int i; // loop count, value for small int Int k; // loop count, value for small int
UInt c;
Obj rem; // handle of the remainder bag
Obj quo; // handle of the quotient bag
CHECK_INT(opL);
CHECK_INT(opR);
// pathological case first
RequireNonzero("Integer operations", opR, "divisor");
// compute the remainder of two small integers if ( ARE_INTOBJS( opL, opR ) ) {
// get the integer values
i = INT_INTOBJ(opL);
k = INT_INTOBJ(opR);
// compute the remainder with sign matching that of the dividend i; // this matches the sign of i % k as specified in section 6.5.5 of // the C99 standard, which indicates division truncates towards zero, // and the invariant i%k == i - (i/k)*k holds
rem = INTOBJ_INT(i % k);
}
// compute the remainder of a small integer by a large integer elseif ( IS_INTOBJ(opL) ) {
// the small int -(1<<28) rem the large int (1<<28) is 0 if ( opL == INTOBJ_MIN
&& IS_INTPOS(opR) && SIZE_INT(opR) == 1
&& VAL_LIMB0(opR) == -INT_INTOBJ_MIN )
rem = INTOBJ_INT(0);
// in all other cases the remainder is equal the left operand else
rem = opL;
}
// compute the remainder of a large integer by a small integer elseif ( IS_INTOBJ(opR) ) {
// get the integer value, make positive
i = INT_INTOBJ(opR); if ( i < 0 ) i = -i;
// check whether right operand is a small power of 2 if ( !(i & (i-1)) ) {
c = VAL_LIMB0(opL) & (i-1);
}
// otherwise use the gmp function to divide else {
c = mpn_mod_1( (mp_srcptr)CONST_ADDR_INT(opL), SIZE_INT(opL), i );
}
// adjust c for the sign of the left operand if ( IS_INTPOS(opL) )
rem = INTOBJ_INT( c ); else
rem = INTOBJ_INT( -(Int)c );
}
// compute the remainder of a large integer modulo a large integer else {
// trivial case first if ( SIZE_INT(opL) < SIZE_INT(opR) ) return opL;
rem = NewBag( TNUM_OBJ(opL), (SIZE_INT(opL)+1)*sizeof(mp_limb_t) );
ENSURE_BAG(rem);
quo = NewBag( T_INTPOS,
(SIZE_INT(opL)-SIZE_INT(opR)+1)*sizeof(mp_limb_t) );
ENSURE_BAG(quo);
// and let gmp do the work
mpn_tdiv_qr( (mp_ptr)ADDR_INT(quo), (mp_ptr)ADDR_INT(rem), 0,
(mp_srcptr)CONST_ADDR_INT(opL), SIZE_INT(opL),
(mp_srcptr)CONST_ADDR_INT(opR), SIZE_INT(opR) );
// reduce to small integer if possible, otherwise shrink bag
rem = GMP_NORMALIZE( rem );
}
CHECK_INT(rem); return rem;
}
/**************************************************************************** ** *F FuncREM_INT( <self>, <a>, <b> ) . . . . . . . internal function 'RemInt' ** ** 'FuncREM_INT' implements the internal function 'RemInt'. ** ** 'RemInt( <a>, <b> )' ** ** 'RemInt' returns the remainder of its two integer operands, i.e., if <k> ** is not equal to zero 'Rem( <i>, <k> ) = <i> - <k> * Quo( <i>, <k> )'. ** Note that the rules given for 'Quo' imply that 'Rem( <i>, <k> )' has the ** same sign as <i> and its absolute value is strictly less than the ** absolute value of <k>. Dividing by 0 causes an error.
*/ static Obj FuncREM_INT(Obj self, Obj a, Obj b)
{
RequireInt(SELF_NAME, a);
RequireInt(SELF_NAME, b); return RemInt(a, b);
}
/**************************************************************************** ** *F GcdInt( <opL>, <opR> ) . . . . . . . . . . . . . . . gcd of two integers ** ** 'GcdInt' returns the gcd of the two integers <opL> and <opR>. ** ** It is called from 'FuncGCD_INT' and from the rational package.
*/
Obj GcdInt ( Obj opL, Obj opR )
{
UInt sizeL, sizeR;
fake_mpz_t mpzL, mpzR, mpzResult;
Obj result;
CHECK_INT(opL);
CHECK_INT(opR);
if (opL == INTOBJ_INT(0)) return AbsInt(opR); if (opR == INTOBJ_INT(0)) return AbsInt(opL);
// compute the gcd
mpz_gcd( MPZ_FAKEMPZ(mpzResult), MPZ_FAKEMPZ(mpzL), MPZ_FAKEMPZ(mpzR) );
// convert result to GAP object and return it
CHECK_FAKEMPZ(mpzResult);
CHECK_FAKEMPZ(mpzL);
CHECK_FAKEMPZ(mpzR);
result = GMPorINTOBJ_FAKEMPZ( mpzResult );
CHECK_INT(result); return result;
}
/**************************************************************************** ** *F FuncGCD_INT(<self>,<a>,<b>) . . . . . . . internal function 'GcdInt' ** ** 'FuncGCD_INT' implements the internal function 'GcdInt'. ** ** 'GcdInt( <a>, <b> )' ** ** 'Gcd' returns the greatest common divisor of the two integers <a> and ** <b>, i.e., the greatest integer that divides both <a> and <b>. The ** greatest common divisor is never negative, even if the arguments are. We ** define $gcd( a, 0 ) = gcd( 0, a ) = abs( a )$ and $gcd( 0, 0 ) = 0$.
*/ static Obj FuncGCD_INT(Obj self, Obj a, Obj b)
{
RequireInt(SELF_NAME, a);
RequireInt(SELF_NAME, b); return GcdInt(a, b);
}
/**************************************************************************** ** *F LcmInt( <opL>, <opR> ) . . . . . . . . . . . . . . . lcm of two integers ** ** 'LcmInt' returns the lcm of the two integers <opL> and <opR>.
*/
Obj LcmInt(Obj opL, Obj opR)
{
UInt sizeL, sizeR;
fake_mpz_t mpzL, mpzR, mpzResult;
Obj result;
CHECK_INT(opL);
CHECK_INT(opR);
if (opL == INTOBJ_INT(0) || opR == INTOBJ_INT(0)) return INTOBJ_INT(0);
// compute the gcd
mpz_lcm(MPZ_FAKEMPZ(mpzResult), MPZ_FAKEMPZ(mpzL), MPZ_FAKEMPZ(mpzR));
// convert result to GAP object and return it
CHECK_FAKEMPZ(mpzResult);
CHECK_FAKEMPZ(mpzL);
CHECK_FAKEMPZ(mpzR);
result = GMPorINTOBJ_FAKEMPZ(mpzResult);
CHECK_INT(result); return result;
}
static Obj FuncLCM_INT(Obj self, Obj a, Obj b)
{
RequireInt(SELF_NAME, a);
RequireInt(SELF_NAME, b); return LcmInt(a, b);
}
// convert mpzResult into a GAP integer object.
Obj result = GMPorINTOBJ_MPZ(mpzResult);
// free mpzResult
mpz_clear(mpzResult);
return result;
}
/**************************************************************************** **
*/
Obj BinomialInt(Obj n, Obj k)
{ Int negate_result = 0;
// deal with k <= 1 if (k == INTOBJ_INT(0)) return INTOBJ_INT(1); if (k == INTOBJ_INT(1)) return n; if (IS_NEG_INT(k)) return INTOBJ_INT(0);
// deal with n < 0 if (IS_NEG_INT(n)) { // use the identity Binomial(n,k) = (-1)^k * Binomial(-n+k-1, k)
negate_result = IS_ODD_INT(k);
n = DiffInt(DiffInt(k,n), INTOBJ_INT(1));
}
// deal with n <= k if (n == k) return negate_result ? INTOBJ_INT(-1) : INTOBJ_INT(1); if (LtInt(n, k)) return INTOBJ_INT(0);
// deal with n-k < k <=> n < 2k
Obj k2 = DiffInt(n, k); if (LtInt(k2, k))
k = k2;
// From here on, we only support single limb integers for k. Anything else // would lead to output too big for storage anyway, at least on a 64 bit // system. To be specific, in that case n >= 2k and k >= 2^60. Thus, in // the *best* case, we are trying to compute the central binomial // coefficient Binomial(2k k) for k = 2^60. This value is approximately // (4^k / sqrt(pi*k)); taking the logarithm and dividing by 8 yields that // we need about k/4 = 2^58 bytes to store this value. No computer in the // foreseeable future will be able to store the result (and much less // compute it in a reasonable time). // // On 32 bit systems, the limit is k = 2^28, and then the central binomial // coefficient Binomial(2k,k) takes up about 64 MB, so that would still be // feasible. However, GMP does not support computing binomials when k is // larger than a single limb, so we'd have to implement this on our own. // // Since GAP previously was effectively unable to compute such binomial // coefficients (unless you were willing to wait for a few days or so), we // simply do not implement this on 32bit systems, and instead return Fail, // jut as we do on 64 bit. If somebody complains about this, we can still // look into implementing this (and in the meantime, tell the user to use // the old GAP version of this function).
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.35Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.