added code to shift a large negative integer when STIX_LIMIT_OBJ_SIZE is not defined

This commit is contained in:
hyunghwan.chung 2015-12-24 14:11:04 +00:00
parent 2022ac965b
commit 1e7be310fc
3 changed files with 443 additions and 227 deletions

View File

@ -374,6 +374,9 @@ PROCESS TESTING
((-2r11111111111111111111111111111111111111111111111111111111111111111111110001 bitShift: -3) printStringRadix: 2) dump.
((-2r11111111111111111111111111111111111111111111111111111111111111111111110001 bitShift: -200) printStringRadix: 2) dump.
((-2r1000000000000000000000000000100000000000000000000000000000000000000000000000 bitShift: -5) printStringRadix: 2) dump.
((-2r1000000000000000000000000000100000000000000000000000000000000000000000000000 bitShift: -16rFFFFFFFFFFFFFFFF) printStringRadix: 2) dump.
((-2r1000000000000000000000000000100000000000000000000000000000000000000000000000 bitShift: 13) printStringRadix: 2) dump.
((2r1000000000000000000000000000100000000000000000000000000000000000000000000000 bitShift: 13) printStringRadix: 2) dump.
"

View File

@ -165,26 +165,73 @@ static STIX_INLINE int liw_mul_overflow (stix_liw_t a, stix_liw_t b, stix_liw_t*
}
#endif
static STIX_INLINE int is_integer (stix_t* stix, stix_oop_t oop)
static int is_normalized_integer (stix_t* stix, stix_oop_t oop)
{
if (STIX_OOP_IS_SMOOI(oop)) return 1;
if (STIX_OOP_IS_POINTER(oop))
{
stix_oop_t c;
c = STIX_CLASSOF(stix,oop);
/* TODO: is it better to introduce a special integer mark into the class itself */
/* TODO: or should it check if it's a subclass, subsubclass, subsubsubclass, etc of a large_integer as well? */
return c == stix->_small_integer ||
c == stix->_large_positive_integer ||
c == stix->_large_negative_integer;
c = STIX_OBJ_GET_CLASS(oop);
if (c == stix->_large_positive_integer ||
c == stix->_large_negative_integer)
{
stix_oow_t sz;
sz = STIX_OBJ_GET_SIZE(oop);
STIX_ASSERT (sz >= 1);
return ((stix_oop_liword_t)oop)->slot[sz - 1] == 0? 0: 1;
}
}
return 0;
}
static STIX_INLINE int bigint_to_oow (stix_t* stix, stix_oop_t num, stix_oow_t* w)
static STIX_INLINE int is_integer (stix_t* stix, stix_oop_t oop)
{
if (STIX_OOP_IS_SMOOI(oop)) return 1;
if (STIX_OOP_IS_POINTER(oop))
{
stix_oop_t c;
/* TODO: is it better to introduce a special integer mark into the class itself */
/* TODO: or should it check if it's a subclass, subsubclass, subsubsubclass, etc of a large_integer as well? */
c = STIX_OBJ_GET_CLASS(oop);
if (c == stix->_large_positive_integer ||
c == stix->_large_negative_integer) return 1;
}
return 0;
}
static STIX_INLINE int integer_to_oow (stix_t* stix, stix_oop_t num, stix_oow_t* w)
{
/* return value
* 1 - a positive number including 0 that can fit into stix_oow_t
* -1 - a negative number whose absolute value can fit into stix_oow_t
* 0 - number too large or too small
*/
if (STIX_OOP_IS_SMOOI(num))
{
stix_ooi_t v;
v = STIX_OOP_TO_SMOOI(num);
if (v < 0)
{
*w = -v;
return -1;
}
else
{
*w = v;
return 1;
}
}
#if (STIX_LIW_BITS == STIX_OOW_BITS)
STIX_ASSERT (STIX_OBJ_GET_SIZE(num) >= 1);
if (STIX_OBJ_GET_SIZE(num) == 1)
@ -194,6 +241,11 @@ static STIX_INLINE int bigint_to_oow (stix_t* stix, stix_oop_t num, stix_oow_t*
}
#elif (STIX_LIW_BITS == STIX_OOHW_BITS)
/* this function must be called with a real large integer.
* a real large integer is at least 2 half-word long.
* you must not call this function with an unnormalized
* large integer. */
STIX_ASSERT (STIX_OBJ_GET_SIZE(num) >= 2);
if (STIX_OBJ_GET_SIZE(num) == 2)
{
@ -2264,6 +2316,260 @@ oops_einval:
return STIX_NULL;
}
static STIX_INLINE stix_oop_t rshift_negative_bigint (stix_t* stix, stix_oop_t x, stix_oow_t shift)
{
stix_oop_t z;
stix_lidw_t w;
stix_lidw_t carry;
stix_oow_t i, xs;
STIX_ASSERT (STIX_OBJ_GET_CLASS(x) == stix->_large_negative_integer);
xs = STIX_OBJ_GET_SIZE(x);
stix_pushtmp (stix, &x);
/* +1 for the second inversion below */
z = stix_instantiate (stix, stix->_large_negative_integer, STIX_NULL, xs + 1);
stix_poptmp (stix);
if (!z) return STIX_NULL;
/* the following lines roughly for 'z = stix_bitinv (stix, x)' */
carry = 1;
for (i = 0; i < xs; i++)
{
w = (stix_lidw_t)((stix_liw_t)~((stix_oop_liword_t)x)->slot[i]) + carry;
carry = w >> STIX_LIW_BITS;
((stix_oop_liword_t)z)->slot[i] = ~(stix_liw_t)w;
}
STIX_ASSERT (carry == 0);
/* shift to the right */
rshift_unsigned_array (((stix_oop_liword_t)z)->slot, xs, shift);
/* the following lines roughly for 'z = stix_bitinv (stix, z)' */
#if 0
for (i = 0; i < xs; i++)
{
((stix_oop_liword_t)z)->slot[i] = ~((stix_oop_liword_t)z)->slot[i];
}
((stix_oop_liword_t)z)->slot[xs] = ~(stix_liw_t)0;
carry = 1;
for (i = 0; i <= xs; i++)
{
w = (stix_lidw_t)((stix_liw_t)~((stix_oop_liword_t)z)->slot[i]) + carry;
carry = w >> STIX_LIW_BITS;
((stix_oop_liword_t)z)->slot[i] = (stix_liw_t)w;
}
STIX_ASSERT (carry == 0);
#else
carry = 1;
for (i = 0; i < xs; i++)
{
w = (stix_lidw_t)(((stix_oop_liword_t)z)->slot[i]) + carry;
carry = w >> STIX_LIW_BITS;
((stix_oop_liword_t)z)->slot[i] = (stix_liw_t)w;
}
((stix_oop_liword_t)z)->slot[i] = (stix_liw_t)carry;
STIX_ASSERT ((carry >> STIX_LIW_BITS) == 0);
#endif
return z; /* z is not normalized */
}
#if defined(STIX_LIMIT_OBJ_SIZE)
/* nothing */
#else
static STIX_INLINE stix_oop_t rshift_negative_bigint_and_normalize (stix_t* stix, stix_oop_t x, stix_oop_t y)
{
stix_oop_t z;
stix_oow_t shift;
int sign;
STIX_ASSERT (STIX_OBJ_GET_CLASS(x) == stix->_large_negative_integer);
STIX_ASSERT (STIX_OBJ_GET_CLASS(y) == stix->_large_negative_integer);
/* for convenience in subtraction below.
* it could be STIX_TYPE_MAX(stix_oow_t)
* if make_bigint_with_intmax() or something
* similar were used instead of STIX_SMOOI_TO_OOP().*/
shift = STIX_SMOOI_MAX;
do
{
stix_pushtmp (stix, &y);
z = rshift_negative_bigint (stix, x, shift);
stix_poptmp (stix);
if (!z) return STIX_NULL;
/* y is a negative number. use stix_addints() until it becomes 0 */
stix_pushtmp (stix, &z);
y = stix_addints (stix, y, STIX_SMOOI_TO_OOP(shift));
stix_poptmp (stix);
if (!y) return STIX_NULL;
sign = integer_to_oow (stix, y, &shift);
if (sign == 0) shift = STIX_SMOOI_MAX;
else
{
if (shift == 0)
{
/* no more shift */
return normalize_bigint (stix, z);
}
STIX_ASSERT (sign <= -1);
}
stix_pushtmp (stix, &y);
x = normalize_bigint (stix, z);
stix_poptmp (stix);
if (!x) return STIX_NULL;
if (STIX_OOP_IS_SMOOI(x))
{
/* for normaization above, x can become a small integer */
stix_ooi_t v;
v = STIX_OOP_TO_SMOOI(x);
STIX_ASSERT (v < 0);
/* normal right shift of a small negative integer */
if (shift >= STIX_OOI_BITS - 1)
{
/* when y is still a large integer, this condition is met
* met as STIX_SMOOI_MAX > STIX_OOI_BITS. so i can simly
* terminate the loop after this */
return STIX_SMOOI_TO_OOP(-1);
}
else
{
v = (stix_ooi_t)(((stix_oow_t)v >> shift) | STIX_HBMASK(stix_oow_t, shift));
if (STIX_IN_SMOOI_RANGE(v))
return STIX_SMOOI_TO_OOP(v);
else
return make_bigint_with_ooi (stix, v);
}
}
}
while (1);
/* this part must not be reached */
STIX_ASSERT (!"internal error - must not happen");
stix->errnum = STIX_EINTERN;
return STIX_NULL;
}
static STIX_INLINE stix_oop_t rshift_positive_bigint_and_normalize (stix_t* stix, stix_oop_t x, stix_oop_t y)
{
stix_oop_t z;
stix_oow_t zs, shift;
int sign;
STIX_ASSERT (STIX_OBJ_GET_CLASS(x) == stix->_large_positive_integer);
STIX_ASSERT (STIX_OBJ_GET_CLASS(y) == stix->_large_negative_integer);
zs = STIX_OBJ_GET_SIZE(x);
stix_pushtmp (stix, &y);
z = clone_bigint (stix, x, zs);
stix_poptmp (stix);
if (!z) return STIX_NULL;
/* for convenience in subtraction below.
* it could be STIX_TYPE_MAX(stix_oow_t)
* if make_bigint_with_intmax() or something
* similar were used instead of STIX_SMOOI_TO_OOP().*/
shift = STIX_SMOOI_MAX;
do
{
rshift_unsigned_array (((stix_oop_liword_t)z)->slot, zs, shift);
if (count_effective (((stix_oop_liword_t)z)->slot, zs) == 1 &&
((stix_oop_liword_t)z)->slot[0] == 0)
{
/* if z is 0, i don't have to go on */
break;
}
/* y is a negative number. use stix_addints() until it becomes 0 */
stix_pushtmp (stix, &z);
y = stix_addints (stix, y, STIX_SMOOI_TO_OOP(shift));
stix_poptmp (stix);
if (!y) return STIX_NULL;
sign = integer_to_oow (stix, y, &shift);
if (sign == 0) shift = STIX_SMOOI_MAX;
else
{
if (shift == 0) break;
STIX_ASSERT (sign <= -1);
}
}
while (1);
return normalize_bigint(stix, z);
}
static STIX_INLINE stix_oop_t lshift_bigint_and_normalize (stix_t* stix, stix_oop_t x, stix_oop_t y)
{
stix_oop_t z;
stix_oow_t wshift, shift;
int sign;
STIX_ASSERT (STIX_OBJ_GET_CLASS(y) == stix->_large_positive_integer);
/* this loop is very inefficient as shifting is repeated
* with lshift_unsigned_array(). however, this part of the
* code is not likey to be useful because the amount of
* memory available is certainly not enough to support
* huge shifts greater than STIX_TYPE_MAX(stix_oow_t) */
shift = STIX_SMOOI_MAX;
do
{
/* for convenience only in subtraction below.
* should it be between STIX_SMOOI_MAX and STIX_TYPE_MAX(stix_oow_t),
* the second parameter to stix_subints() can't be composed
* using STIX_SMOOI_TO_OOP() */
wshift = shift / STIX_LIW_BITS;
if (shift > wshift * STIX_LIW_BITS) wshift++;
stix_pushtmp (stix, &y);
z = expand_bigint (stix, x, wshift);
stix_poptmp (stix);
if (!z) return STIX_NULL;
lshift_unsigned_array (((stix_oop_liword_t)z)->slot, STIX_OBJ_GET_SIZE(z), shift);
stix_pushtmp (stix, &y);
x = normalize_bigint (stix, z);
stix_poptmp (stix);
if (!x) return STIX_NULL;
stix_pushtmp (stix, &x);
y = stix_subints (stix, y, STIX_SMOOI_TO_OOP(shift));
stix_poptmp (stix);
if (!y) return STIX_NULL;
sign = integer_to_oow (stix, y, &shift);
if (sign == 0) shift = STIX_SMOOI_MAX;
else
{
if (shift == 0)
{
STIX_ASSERT (is_normalized_integer (stix, x));
return x;
}
STIX_ASSERT (sign >= 1);
}
}
while (1);
/* this part must not be reached */
STIX_ASSERT (!"internal error - must not happen");
stix->errnum = STIX_EINTERN;
return STIX_NULL;
}
#endif
stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
{
/* left shift if y is positive,
@ -2338,7 +2644,12 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
return make_bigint_with_ooi (stix, v);
}
}
else if (STIX_OOP_IS_SMOOI(x))
else
{
int sign, negx, negy;
stix_oow_t shift;
if (STIX_OOP_IS_SMOOI(x))
{
stix_ooi_t v;
@ -2372,18 +2683,25 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
v = STIX_OOP_TO_SMOOI(y);
if (v == 0) return clone_bigint (stix, x, STIX_OBJ_GET_SIZE(x));
stix_pushtmp (stix, &x);
y = make_bigint_with_ooi (stix, v);
stix_poptmp (stix);
if (!y) return STIX_NULL;
goto bigint_and_bigint;
negx = (STIX_OBJ_GET_CLASS(x) == stix->_large_negative_integer)? 1: 0;
if (v > 0)
{
sign = 1;
negy = 0;
shift = v;
goto bigint_and_positive_oow;
}
else
{
sign = -1;
negy = 1;
shift = -v;
goto bigint_and_negative_oow;
}
}
else
{
stix_oop_t z;
int sign, negx, negy;
stix_oow_t shift;
if (!is_integer(stix,x) || !is_integer(stix, y)) goto oops_einval;
@ -2391,7 +2709,7 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
negx = (STIX_OBJ_GET_CLASS(x) == stix->_large_negative_integer)? 1: 0;
negy = (STIX_OBJ_GET_CLASS(y) == stix->_large_negative_integer)? 1: 0;
sign = bigint_to_oow (stix, y, &shift);
sign = integer_to_oow (stix, y, &shift);
if (sign == 0)
{
/* y is too big or too small */
@ -2405,9 +2723,10 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
STIX_ASSERT (STIX_TYPE_MAX(stix_oow_t) >= STIX_OBJ_SIZE_MAX * 8);
return (negx)? STIX_SMOOI_TO_OOP(-1): STIX_SMOOI_TO_OOP(0);
#else
/* TODO: */
/* TODO: */
/* TODO: */
if (negx)
return rshift_negative_bigint_and_normalize (stix, x, y);
else
return rshift_positive_bigint_and_normalize (stix, x, y);
#endif
}
else
@ -2422,55 +2741,16 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
stix->errnum = STIX_EOOMEM; /* is it a soft failure or a hard failure? is this error code proper? */
return STIX_NULL;
#else
/* this loop is very inefficient as shifting is repeated
* with lshift_unsigned_array(). however, this part of the
* code is not likey to be useful because the amount of
* memory available is certainly not enough to support
* huge shifts greater than STIX_TYPE_MAX(stix_oow_t) */
do
{
/* for convenience only in subtraction below.
* should it be between STIX_SMOOI_MAX and STIX_TYPE_MAX(stix_oow_t),
* the second parameter to stix_subints() can't be composed
* using STIX_SMOOI_TO_OOP() */
shift = STIX_SMOOI_MAX;
wshift = shift / STIX_LIW_BITS;
if (shift > wshift * STIX_LIW_BITS) wshift++;
stix_pushtmp (stix, &y);
z = expand_bigint (stix, x, wshift);
stix_poptmp (stix);
if (!z) return STIX_NULL;
lshift_unsigned_array (((stix_oop_liword_t)z)->slot, STIX_OBJ_GET_SIZE(z), shift);
stix_pushtmp (stix, &y);
x = normalize_bigint (stix, z);
stix_poptmp (stix);
if (!x) return STIX_NULL;
stix_pushtmp (stix, &x);
y = stix_subints (stix, y, STIX_SMOOI_TO_OOP(shift));
stix_poptmp (stix);
if (!y) return STIX_NULL;
sign = bigint_to_oow (stix, y, &shift);
}
while (sign == 0);
STIX_ASSERT (sign >= 1);
if (shift)
goto left_shift_last;
else
z = x;
return lshift_bigint_and_normalize (stix, x, y);
#endif
}
}
else if (sign >= 1)
{
/* left shift */
stix_oow_t wshift;
left_shift_last:
bigint_and_positive_oow:
wshift = shift / STIX_LIW_BITS;
if (shift > wshift * STIX_LIW_BITS) wshift++;
@ -2482,63 +2762,14 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
else
{
/* right shift */
bigint_and_negative_oow:
STIX_ASSERT (sign <= -1);
if (negx)
{
stix_lidw_t w;
stix_lidw_t carry;
stix_oow_t i, xs;
xs = STIX_OBJ_GET_SIZE(x);
stix_pushtmp (stix, &x);
stix_pushtmp (stix, &y);
/* +1 for the second inversion below */
z = stix_instantiate (stix, stix->_large_negative_integer, STIX_NULL, xs + 1);
stix_poptmps (stix, 2);
z = rshift_negative_bigint (stix, x, shift);
if (!z) return STIX_NULL;
/* the following lines roughly for 'z = stix_bitinv (stix, x)' */
carry = 1;
for (i = 0; i < xs; i++)
{
w = (stix_lidw_t)((stix_liw_t)~((stix_oop_liword_t)x)->slot[i]) + carry;
carry = w >> STIX_LIW_BITS;
((stix_oop_liword_t)z)->slot[i] = ~(stix_liw_t)w;
}
STIX_ASSERT (carry == 0);
/* shift to the right */
rshift_unsigned_array (((stix_oop_liword_t)z)->slot, xs, shift);
/* the following lines roughly for 'z = stix_bitinv (stix, z)' */
#if 0
for (i = 0; i < xs; i++)
{
((stix_oop_liword_t)z)->slot[i] = ~((stix_oop_liword_t)z)->slot[i];
}
((stix_oop_liword_t)z)->slot[xs] = ~(stix_liw_t)0;
carry = 1;
for (i = 0; i <= xs; i++)
{
w = (stix_lidw_t)((stix_liw_t)~((stix_oop_liword_t)z)->slot[i]) + carry;
carry = w >> STIX_LIW_BITS;
((stix_oop_liword_t)z)->slot[i] = (stix_liw_t)w;
}
STIX_ASSERT (carry == 0);
#else
carry = 1;
for (i = 0; i < xs; i++)
{
w = (stix_lidw_t)(((stix_oop_liword_t)z)->slot[i]) + carry;
carry = w >> STIX_LIW_BITS;
((stix_oop_liword_t)z)->slot[i] = (stix_liw_t)w;
}
((stix_oop_liword_t)z)->slot[i] = (stix_liw_t)carry;
STIX_ASSERT ((carry >> STIX_LIW_BITS) == 0);
#endif
}
else
{
@ -2550,6 +2781,7 @@ stix_oop_t stix_bitshiftint (stix_t* stix, stix_oop_t x, stix_oop_t y)
return normalize_bigint(stix, z);
}
}
oops_einval:
stix->errnum = STIX_EINVAL;
@ -2848,28 +3080,8 @@ stix_oop_t stix_inttostr (stix_t* stix, stix_oop_t num, int radix)
STIX_ASSERT (radix >= 2 && radix <= 36);
if (STIX_OOP_IS_SMOOI(num))
{
v = STIX_OOP_TO_SMOOI(num);
if (v < 0)
{
w = -v;
v = -1;
}
else
{
w = v;
v = 1;
}
}
else if (!is_integer(stix,num))
{
goto oops_einval;
}
else
{
v = bigint_to_oow (stix, num, &w);
}
if (!is_integer(stix,num)) goto oops_einval;
v = integer_to_oow (stix, num, &w);
if (v)
{

View File

@ -57,7 +57,8 @@
/* limit the maximum object size such that:
* 1. an index to an object field can be represented in a small integer.
* 2. the maximum number of bit shifts can be represented in the stix_oow_t type.
* 2. the maximum number of bits including bit-shifts can be represented
* in the stix_oow_t type.
*/
#define STIX_LIMIT_OBJ_SIZE