blob: 6e6d2cdd7ad2d92a93e64a1c43d4417a80b3b476 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BigInt.h"
#include <decaf/lang/Double.h>
#include <decaf/lang/Float.h>
#include <decaf/internal/util/BitOps.h>
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::internal;
using namespace decaf::internal::util;
////////////////////////////////////////////////////////////////////////////////
BigInt::BigInt() {
}
////////////////////////////////////////////////////////////////////////////////
void BigInt::multiplyHighPrecision( unsigned long long* arg1,
int length1,
unsigned long long* arg2,
int length2,
unsigned long long* result,
int length ) {
// assumes result is large enough to hold product
unsigned long long *temp;
unsigned int *resultIn32;
int count, index;
if( length1 < length2 ) {
temp = arg1;
arg1 = arg2;
arg2 = temp;
count = length1;
length1 = length2;
length2 = count;
}
memset( result, 0, sizeof(unsigned long long) * length );
// length1 > length2
resultIn32 = (unsigned int*)result;
index = -1;
for( count = 0; count < length2; ++count ) {
simpleMultiplyAddHighPrecision( arg1, length1,
arg2[count] & BitOps::LONG_LO_MASK,
resultIn32 + (++index) );
simpleMultiplyAddHighPrecision( arg1, length1,
( arg2[count] >> 32 ),
resultIn32 + (++index) );
}
}
////////////////////////////////////////////////////////////////////////////////
unsigned int BigInt::simpleAppendDecimalDigitHighPrecision(
unsigned long long* arg1, int length, unsigned long long digit ) {
// assumes digit is less than 32 bits
unsigned long long arg = 0;
int index = 0;
digit <<= 32;
do {
arg = arg1[index] & BitOps::LONG_LO_MASK;
digit = ( digit >> 32 ) + BitOps::TIMES_TEN( arg );
arg1[index] = ( arg1[index] & BitOps::LONG_LO_MASK ) |
( digit & BitOps::LONG_LO_MASK );
arg = arg1[index] >> 32;
digit = ( digit >> 32 ) + BitOps::TIMES_TEN( arg );
arg1[index] = ( arg1[index] & BitOps::LONG_HI_MASK ) |
( digit & BitOps::LONG_LO_MASK );
} while( ++index < length );
return digit >> 32;
}
/*
U_32 simpleAppendDecimalDigitHighPrecision (U_64 * arg1, IDATA length, U_64 digit)
{
// assumes digit is less than 32 bits
U_64 arg;
IDATA index = 0;
digit <<= 32;
do
{
arg = LOW_IN_U64 (arg1[index]);
digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg);
LOW_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit);
arg = HIGH_IN_U64 (arg1[index]);
digit = HIGH_IN_U64 (digit) + TIMES_TEN (arg);
HIGH_U32_FROM_PTR (arg1 + index) = LOW_U32_FROM_VAR (digit);
}
while (++index < length);
return HIGH_U32_FROM_VAR (digit);
}
*/
////////////////////////////////////////////////////////////////////////////////
double BigInt::toDoubleHighPrecision( unsigned long long* arg, int length ) {
int highBit;
unsigned long long mantissa = 0;
unsigned long long test64 = 0;
unsigned int test;
long long result;
while( length > 0 && arg[length - 1] == 0 ) {
--length;
}
if( length == 0 ) {
return 0.0;
} else if( length > 16 ) {
result = BitOps::EXPONENT_MASK;
} else if( length == 1 ) {
highBit = highestSetBit (arg);
if( highBit <= 53 ) {
highBit = 53 - highBit;
mantissa = *arg << highBit;
result = BitOps::CREATE_DOUBLE_BITS( mantissa, -highBit );
} else {
highBit -= 53;
mantissa = *arg >> highBit;
result = BitOps::CREATE_DOUBLE_BITS( mantissa, highBit );
// perform rounding, round to even in case of tie
test = ( BitOps::LOW_U32_FROM_LONG64_PTR( arg ) << ( 11 - highBit ) ) & 0x7FF;
if( test > 0x400 || ((test == 0x400) && (mantissa & 1)) ) {
result = result + 1;
}
}
} else {
highBit = highestSetBit (arg + (--length));
if( highBit <= 53 ) {
highBit = 53 - highBit;
if( highBit > 0 ) {
mantissa = (arg[length] << highBit) |
(arg[length - 1] >> (64 - highBit));
} else {
mantissa = arg[length];
}
result = BitOps::CREATE_DOUBLE_BITS( mantissa, length * 64 - highBit );
// perform rounding, round to even in case of tie
test64 = arg[--length] << highBit;
if( test64 > BitOps::SIGN_MASK || ((test64 == BitOps::SIGN_MASK) && (mantissa & 1)) ) {
result = result + 1;
} else if( test64 == BitOps::SIGN_MASK ) {
while( --length >= 0 ) {
if( arg[length] != 0 ) {
result = result + 1;
break;
}
}
}
} else {
highBit -= 53;
mantissa = arg[length] >> highBit;
result = BitOps::CREATE_DOUBLE_BITS( mantissa, length * 64 + highBit );
// perform rounding, round to even in case of tie
test = ( BitOps::LOW_U32_FROM_LONG64_PTR( arg + length ) << ( 11 - highBit ) ) & 0x7FF;
if( test > 0x400 || ((test == 0x400) && (mantissa & 1)) ) {
result = result + 1;
} else if( test == 0x400 ) {
do {
if( arg[--length] != 0 ) {
result = result + 1;
break;
}
} while( length > 0 );
}
}
}
return Double::longBitsToDouble( result );
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::tenToTheEHighPrecision( unsigned long long * result,
int length, int e ) {
// size test
if( length < ((e / 19) + 1) ) {
return 0;
}
memset( result, 0, length * sizeof(unsigned long long) );
*result = 1;
if( e == 0 ) {
return 1;
}
length = 1;
length = timesTenToTheEHighPrecision( result, length, e );
return length;
}
////////////////////////////////////////////////////////////////////////////////
unsigned long long BigInt::doubleMantissa( double z ) {
unsigned long long m = Double::doubleToLongBits( z );
if ((m & BitOps::EXPONENT_MASK) != 0)
m = (m & BitOps::MANTISSA_MASK) | BitOps::NORMAL_MASK;
else
m = (m & BitOps::MANTISSA_MASK);
return m;
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::compareHighPrecision( unsigned long long* arg1,
int length1,
unsigned long long* arg2,
int length2 ) {
while( --length1 >= 0 && arg1[length1] == 0 );
while( --length2 >= 0 && arg2[length2] == 0 );
if( length1 > length2 ) {
return 1;
} else if( length1 < length2 ) {
return -1;
} else if( length1 > -1 ) {
do {
if( arg1[length1] > arg2[length1] ) {
return 1;
} else if( arg1[length1] < arg2[length1] ) {
return -1;
}
} while( --length1 >= 0 );
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::highestSetBitHighPrecision( unsigned long long* arg, int length ) {
int highBit;
while( --length >= 0 ) {
highBit = highestSetBit( arg + length );
if( highBit ) {
return highBit + 64 * length;
}
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
void BigInt::subtractHighPrecision( unsigned long long* arg1,
int length1,
unsigned long long* arg2,
int length2 ) {
// assumes arg1 > arg2
for( int index = 0; index < length1; ++index ) {
arg1[index] = ~arg1[index];
}
simpleAddHighPrecision( arg1, length1, 1 );
while( length2 > 0 && arg2[length2 - 1] == 0 ) {
--length2;
}
addHighPrecision( arg1, length1, arg2, length2 );
for( int index = 0; index < length1; ++index ) {
arg1[index] = ~arg1[index];
}
simpleAddHighPrecision( arg1, length1, 1 );
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::doubleExponent( double z ) {
// assumes positive double
int k = ( (unsigned long long )Double::doubleToLongBits( z ) >> 52 );
if( k ) {
k -= BitOps::E_OFFSET;
} else {
k = 1 - BitOps::E_OFFSET;
}
return k;
}
////////////////////////////////////////////////////////////////////////////////
unsigned int BigInt::simpleMultiplyHighPrecision(
unsigned long long* arg1, int length, unsigned long long arg2 ) {
// assumes arg2 only holds 32 bits of information
unsigned long long product = 0;
int index = 0;
do {
product = ( product >> 32 ) + arg2 * BitOps::LOW_U32_FROM_LONG64_PTR( arg1 + index );
BitOps::LOW_U32_FROM_LONG64_PTR( arg1 + index ) = BitOps::LOW_U32_FROM_LONG64( product );
product = ( product >> 32 ) + arg2 * BitOps::HIGH_U32_FROM_LONG64_PTR( arg1 + index );
BitOps::HIGH_U32_FROM_LONG64_PTR( arg1 + index ) = BitOps::LOW_U32_FROM_LONG64( product );
} while( ++index < length );
return ( product >> 32 );
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::addHighPrecision( unsigned long long* arg1,
int length1,
unsigned long long* arg2,
int length2 ) {
// addition is limited by length of arg1 as it this function is
// storing the result in arg1
//
// fix for cc (GCC) 3.2 20020903 (Red Hat Linux 8.0 3.2-7): code generated does not
// do the temp1 + temp2 + carry addition correct. carry is 64 bit because gcc has
// subtle issues when you mix 64 / 32 bit maths.
unsigned long long temp1, temp2, temp3; /* temporary variables to help the SH-4, and gcc */
unsigned long long carry;
int index;
if( length1 == 0 || length2 == 0 ) {
return 0;
} else if( length1 < length2 ) {
length2 = length1;
}
carry = 0;
index = 0;
do {
temp1 = arg1[index];
temp2 = arg2[index];
temp3 = temp1 + temp2;
arg1[index] = temp3 + carry;
if( arg2[index] < arg1[index] ) {
carry = 0;
} else if( arg2[index] != arg1[index] ) {
carry = 1;
}
} while( ++index < length2 );
if( !carry ) {
return 0;
} else if( index == length1 ) {
return 1;
}
while( ++arg1[index] == 0 && ++index < length1 );
return (int)( index == length1 );
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::lowestSetBit( unsigned long long* y ) {
unsigned int x;
int result;
if( *y == 0 ) {
return 0;
}
if( *y & 0x00000000FFFFFFFFULL ) {
x = BitOps::LOW_U32_FROM_LONG64_PTR( y );
result = 0;
} else {
x = BitOps::HIGH_U32_FROM_LONG64_PTR( y );
result = 32;
}
if( !( x & 0xFFFF ) ) {
x = BitOps::bitSection( x, 0xFFFF0000, 16 );
result += 16;
}
if( !( x & 0xFF) ) {
x = BitOps::bitSection( x, 0xFF00, 8 );
result += 8;
}
if( !( x & 0xF ) ) {
x = BitOps::bitSection( x, 0xF0, 4 );
result += 4;
}
if( x & 0x1 ) {
return result + 1;
} else if( x & 0x2 ) {
return result + 2;
} else if( x & 0x4 ) {
return result + 3;
}
return result + 4;
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::timesTenToTheEHighPrecision(
unsigned long long* result, int length, int e ) {
// assumes result can hold value
unsigned long long overflow;
int exp10 = e;
if( e == 0 ) {
return length;
}
// Replace the current implementaion which performs a
// "multiplication" by 10 e number of times with an actual
// mulitplication. 10e19 is the largest exponent to the power of ten
// that will fit in a 64-bit integer, and 10e9 is the largest exponent to
// the power of ten that will fit in a 64-bit integer. Not sure where the
// break-even point is between an actual multiplication and a
// simpleAappendDecimalDigit() so just pick 10e3 as that point for
// now.
while( exp10 >= 19 ) {
overflow = simpleMultiplyHighPrecision64( result, length, BitOps::TEN_E19 );
if( overflow ) {
result[length++] = overflow;
}
exp10 -= 19;
}
while( exp10 >= 9 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E9 );
if( overflow ) {
result[length++] = overflow;
}
exp10 -= 9;
}
if( exp10 == 0 ) {
return length;
} else if( exp10 == 1 ) {
overflow = simpleAppendDecimalDigitHighPrecision( result, length, 0 );
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 2 ) {
overflow = simpleAppendDecimalDigitHighPrecision( result, length, 0 );
if( overflow ) {
result[length++] = overflow;
}
overflow = simpleAppendDecimalDigitHighPrecision( result, length, 0 );
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 3 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E3 );
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 4 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E4 );
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 5 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E5 );
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 6 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E6);
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 7 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E7 );
if( overflow ) {
result[length++] = overflow;
}
} else if( exp10 == 8 ) {
overflow = simpleMultiplyHighPrecision( result, length, BitOps::TEN_E8 );
if( overflow ) {
result[length++] = overflow;
}
}
return length;
}
////////////////////////////////////////////////////////////////////////////////
void BigInt::simpleMultiplyAddHighPrecision(
unsigned long long* arg1, int length,
unsigned long long arg2, unsigned int* result ) {
// Assumes result can hold the product and arg2 only holds 32 bits
// of information
unsigned long long product = 0;
int index = 0;
int resultIndex = 0;
do {
product =
( product >> 32 ) + result[BitOps::at( resultIndex )] +
arg2 * BitOps::LOW_U32_FROM_LONG64_PTR( arg1 + index );
result[BitOps::at( resultIndex )] = BitOps::LOW_U32_FROM_LONG64( product );
++resultIndex;
product =
( product >> 32 ) + result[BitOps::at( resultIndex )] +
arg2 * BitOps::HIGH_U32_FROM_LONG64_PTR( arg1 + index );
result[BitOps::at( resultIndex )] = BitOps::LOW_U32_FROM_LONG64( product );
++resultIndex;
} while( ++index < length );
result[BitOps::at( resultIndex )] += BitOps::HIGH_U32_FROM_LONG64( product );
if( result[BitOps::at( resultIndex )] < BitOps::HIGH_U32_FROM_LONG64( product ) ) {
// must be careful with ++ operator and macro expansion
++resultIndex;
while( ++result[BitOps::at( resultIndex )] == 0 ) {
++resultIndex;
}
}
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::highestSetBit(unsigned long long* y ) {
unsigned int x = 0;
int result = 0;
if( *y == 0 ) {
return 0;
}
if( *y & 0xFFFFFFFF00000000LL ) {
x = BitOps::HIGH_U32_FROM_LONG64_PTR(y);
result = 32;
} else {
x = BitOps::LOW_U32_FROM_LONG64_PTR(y);
result = 0;
}
if( x & 0xFFFF0000 )
{
x = BitOps::bitSection( x, 0xFFFF0000, 16 );
result += 16;
}
if( x & 0xFF00 ) {
x = BitOps::bitSection( x, 0xFF00, 8 );
result += 8;
}
if( x & 0xF0 ) {
x = BitOps::bitSection( x, 0xF0, 4 );
result += 4;
}
if( x > 0x7 ) {
return result + 4;
} else if( x > 0x3 ) {
return result + 3;
} else if( x > 0x1 ) {
return result + 2;
} else {
return result + 1;
}
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::lowestSetBitHighPrecision( unsigned long long* arg, int length ) {
int lowBit, index = -1;
while( ++index < length ) {
lowBit = lowestSetBit( arg + index );
if( lowBit ) {
return lowBit + 64 * index;
}
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
void BigInt::simpleShiftLeftHighPrecision( unsigned long long* arg1,
int length, int arg2 ) {
// assumes length > 0
int index, offset = 0;
if( arg2 >= 64 ) {
offset = arg2 >> 6;
index = length;
while( --index - offset >= 0 ) {
arg1[index] = arg1[index - offset];
}
do {
arg1[index] = 0;
} while (--index >= 0);
arg2 &= 0x3F;
}
if( arg2 == 0 ) {
return;
}
while( --length > 0 ) {
arg1[length] = arg1[length] << arg2 | arg1[length - 1] >> (64 - arg2);
}
*arg1 <<= arg2;
}
////////////////////////////////////////////////////////////////////////////////
unsigned int BigInt::floatMantissa( float z ) {
unsigned int m = (unsigned int)Float::floatToIntBits( z );
if( ( m & BitOps::FLOAT_EXPONENT_MASK ) != 0 )
m = ( m & BitOps::FLOAT_MANTISSA_MASK ) | BitOps::FLOAT_NORMAL_MASK;
else
m = ( m & BitOps::FLOAT_MANTISSA_MASK );
return m;
}
////////////////////////////////////////////////////////////////////////////////
unsigned long long BigInt::simpleMultiplyHighPrecision64(
unsigned long long* arg1, int length, unsigned long long arg2 ) {
unsigned long long intermediate = 0;
unsigned long long* pArg1 = NULL;
unsigned long long carry1 = 0;
unsigned long long carry2 = 0;
unsigned long long prod1 = 0;
unsigned long long prod2 = 0;
unsigned long long sum = 0;
int index = 0;
unsigned int buf32 = 0;
// set the starting position
pArg1 = &( arg1[index] );
do {
if( (*pArg1 != 0) || (intermediate != 0) ) {
prod1 = (unsigned long long)BitOps::LOW_U32_FROM_LONG64(arg2) *
(unsigned long long)BitOps::LOW_U32_FROM_LONG64_PTR(pArg1);
sum = intermediate + prod1;
if( (sum < prod1) || (sum < intermediate) ) {
carry1 = 1;
} else {
carry1 = 0;
}
prod1 = (unsigned long long)BitOps::LOW_U32_FROM_LONG64(arg2) *
(unsigned long long)BitOps::HIGH_U32_FROM_LONG64_PTR(pArg1);
prod2 = (unsigned long long)BitOps::HIGH_U32_FROM_LONG64(arg2) *
(unsigned long long)BitOps::LOW_U32_FROM_LONG64_PTR(pArg1);
intermediate = carry2 + ( sum >> 32 ) + prod1 + prod2;
if( (intermediate < prod1) || (intermediate < prod2) ) {
carry2 = 1;
} else {
carry2 = 0;
}
BitOps::LOW_U32_FROM_LONG64_PTR(pArg1) = BitOps::LOW_U32_FROM_LONG64(sum);
buf32 = BitOps::HIGH_U32_FROM_LONG64_PTR (pArg1);
BitOps::HIGH_U32_FROM_LONG64_PTR(pArg1) = BitOps::LOW_U32_FROM_LONG64(intermediate);
intermediate = carry1 + ( intermediate >> 32 ) +
(unsigned long long)BitOps::HIGH_U32_FROM_LONG64 (arg2) *
(unsigned long long)buf32;
}
pArg1++;
} while( ++index < length );
return intermediate;
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::simpleAddHighPrecision( unsigned long long* arg1,
int length,
unsigned long long arg2 ) {
// assumes length > 0
int index = 1;
*arg1 += arg2;
if( arg2 <= *arg1 ) {
return 0;
} else if (length == 1) {
return 1;
}
while( ++arg1[index] == 0 && ++index < length );
return (int)( index == length );
}
////////////////////////////////////////////////////////////////////////////////
int BigInt::floatExponent( float z ) {
// assumes positive float
int k = Float::floatToIntBits( z ) >> 23;
if( k ) {
k -= BitOps::FLOAT_E_OFFSET;
} else {
k = 1 - BitOps::FLOAT_E_OFFSET;
}
return k;
}