blob: 85b5b350c4e4ca38df2cf3f441d5dcb5c094145b [file] [log] [blame]
/*
* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.misc;
/*
* A really, really simple bigint package
* tailored to the needs of floating base conversion.
*/
public class FDBigInt {
int nWords; // number of words used
int data[]; // value: data[0] is least significant
public FDBigInt( int v ){
nWords = 1;
data = new int[1];
data[0] = v;
}
public FDBigInt( long v ){
data = new int[2];
data[0] = (int)v;
data[1] = (int)(v>>>32);
nWords = (data[1]==0) ? 1 : 2;
}
public FDBigInt( FDBigInt other ){
data = new int[nWords = other.nWords];
System.arraycopy( other.data, 0, data, 0, nWords );
}
private FDBigInt( int [] d, int n ){
data = d;
nWords = n;
}
public FDBigInt( long seed, char digit[], int nd0, int nd ){
int n= (nd+8)/9; // estimate size needed.
if ( n < 2 ) n = 2;
data = new int[n]; // allocate enough space
data[0] = (int)seed; // starting value
data[1] = (int)(seed>>>32);
nWords = (data[1]==0) ? 1 : 2;
int i = nd0;
int limit = nd-5; // slurp digits 5 at a time.
int v;
while ( i < limit ){
int ilim = i+5;
v = (int)digit[i++]-(int)'0';
while( i <ilim ){
v = 10*v + (int)digit[i++]-(int)'0';
}
multaddMe( 100000, v); // ... where 100000 is 10^5.
}
int factor = 1;
v = 0;
while ( i < nd ){
v = 10*v + (int)digit[i++]-(int)'0';
factor *= 10;
}
if ( factor != 1 ){
multaddMe( factor, v );
}
}
/*
* Left shift by c bits.
* Shifts this in place.
*/
public void
lshiftMe( int c )throws IllegalArgumentException {
if ( c <= 0 ){
if ( c == 0 )
return; // silly.
else
throw new IllegalArgumentException("negative shift count");
}
int wordcount = c>>5;
int bitcount = c & 0x1f;
int anticount = 32-bitcount;
int t[] = data;
int s[] = data;
if ( nWords+wordcount+1 > t.length ){
// reallocate.
t = new int[ nWords+wordcount+1 ];
}
int target = nWords+wordcount;
int src = nWords-1;
if ( bitcount == 0 ){
// special hack, since an anticount of 32 won't go!
System.arraycopy( s, 0, t, wordcount, nWords );
target = wordcount-1;
} else {
t[target--] = s[src]>>>anticount;
while ( src >= 1 ){
t[target--] = (s[src]<<bitcount) | (s[--src]>>>anticount);
}
t[target--] = s[src]<<bitcount;
}
while( target >= 0 ){
t[target--] = 0;
}
data = t;
nWords += wordcount + 1;
// may have constructed high-order word of 0.
// if so, trim it
while ( nWords > 1 && data[nWords-1] == 0 )
nWords--;
}
/*
* normalize this number by shifting until
* the MSB of the number is at 0x08000000.
* This is in preparation for quoRemIteration, below.
* The idea is that, to make division easier, we want the
* divisor to be "normalized" -- usually this means shifting
* the MSB into the high words sign bit. But because we know that
* the quotient will be 0 < q < 10, we would like to arrange that
* the dividend not span up into another word of precision.
* (This needs to be explained more clearly!)
*/
public int
normalizeMe() throws IllegalArgumentException {
int src;
int wordcount = 0;
int bitcount = 0;
int v = 0;
for ( src= nWords-1 ; src >= 0 && (v=data[src]) == 0 ; src--){
wordcount += 1;
}
if ( src < 0 ){
// oops. Value is zero. Cannot normalize it!
throw new IllegalArgumentException("zero value");
}
/*
* In most cases, we assume that wordcount is zero. This only
* makes sense, as we try not to maintain any high-order
* words full of zeros. In fact, if there are zeros, we will
* simply SHORTEN our number at this point. Watch closely...
*/
nWords -= wordcount;
/*
* Compute how far left we have to shift v s.t. its highest-
* order bit is in the right place. Then call lshiftMe to
* do the work.
*/
if ( (v & 0xf0000000) != 0 ){
// will have to shift up into the next word.
// too bad.
for( bitcount = 32 ; (v & 0xf0000000) != 0 ; bitcount-- )
v >>>= 1;
} else {
while ( v <= 0x000fffff ){
// hack: byte-at-a-time shifting
v <<= 8;
bitcount += 8;
}
while ( v <= 0x07ffffff ){
v <<= 1;
bitcount += 1;
}
}
if ( bitcount != 0 )
lshiftMe( bitcount );
return bitcount;
}
/*
* Multiply a FDBigInt by an int.
* Result is a new FDBigInt.
*/
public FDBigInt
mult( int iv ) {
long v = iv;
int r[];
long p;
// guess adequate size of r.
r = new int[ ( v * ((long)data[nWords-1]&0xffffffffL) > 0xfffffffL ) ? nWords+1 : nWords ];
p = 0L;
for( int i=0; i < nWords; i++ ) {
p += v * ((long)data[i]&0xffffffffL);
r[i] = (int)p;
p >>>= 32;
}
if ( p == 0L){
return new FDBigInt( r, nWords );
} else {
r[nWords] = (int)p;
return new FDBigInt( r, nWords+1 );
}
}
/*
* Multiply a FDBigInt by an int and add another int.
* Result is computed in place.
* Hope it fits!
*/
public void
multaddMe( int iv, int addend ) {
long v = iv;
long p;
// unroll 0th iteration, doing addition.
p = v * ((long)data[0]&0xffffffffL) + ((long)addend&0xffffffffL);
data[0] = (int)p;
p >>>= 32;
for( int i=1; i < nWords; i++ ) {
p += v * ((long)data[i]&0xffffffffL);
data[i] = (int)p;
p >>>= 32;
}
if ( p != 0L){
data[nWords] = (int)p; // will fail noisily if illegal!
nWords++;
}
}
/*
* Multiply a FDBigInt by another FDBigInt.
* Result is a new FDBigInt.
*/
public FDBigInt
mult( FDBigInt other ){
// crudely guess adequate size for r
int r[] = new int[ nWords + other.nWords ];
int i;
// I think I am promised zeros...
for( i = 0; i < this.nWords; i++ ){
long v = (long)this.data[i] & 0xffffffffL; // UNSIGNED CONVERSION
long p = 0L;
int j;
for( j = 0; j < other.nWords; j++ ){
p += ((long)r[i+j]&0xffffffffL) + v*((long)other.data[j]&0xffffffffL); // UNSIGNED CONVERSIONS ALL 'ROUND.
r[i+j] = (int)p;
p >>>= 32;
}
r[i+j] = (int)p;
}
// compute how much of r we actually needed for all that.
for ( i = r.length-1; i> 0; i--)
if ( r[i] != 0 )
break;
return new FDBigInt( r, i+1 );
}
/*
* Add one FDBigInt to another. Return a FDBigInt
*/
public FDBigInt
add( FDBigInt other ){
int i;
int a[], b[];
int n, m;
long c = 0L;
// arrange such that a.nWords >= b.nWords;
// n = a.nWords, m = b.nWords
if ( this.nWords >= other.nWords ){
a = this.data;
n = this.nWords;
b = other.data;
m = other.nWords;
} else {
a = other.data;
n = other.nWords;
b = this.data;
m = this.nWords;
}
int r[] = new int[ n ];
for ( i = 0; i < n; i++ ){
c += (long)a[i] & 0xffffffffL;
if ( i < m ){
c += (long)b[i] & 0xffffffffL;
}
r[i] = (int) c;
c >>= 32; // signed shift.
}
if ( c != 0L ){
// oops -- carry out -- need longer result.
int s[] = new int[ r.length+1 ];
System.arraycopy( r, 0, s, 0, r.length );
s[i++] = (int)c;
return new FDBigInt( s, i );
}
return new FDBigInt( r, i );
}
/*
* Subtract one FDBigInt from another. Return a FDBigInt
* Assert that the result is positive.
*/
public FDBigInt
sub( FDBigInt other ){
int r[] = new int[ this.nWords ];
int i;
int n = this.nWords;
int m = other.nWords;
int nzeros = 0;
long c = 0L;
for ( i = 0; i < n; i++ ){
c += (long)this.data[i] & 0xffffffffL;
if ( i < m ){
c -= (long)other.data[i] & 0xffffffffL;
}
if ( ( r[i] = (int) c ) == 0 )
nzeros++;
else
nzeros = 0;
c >>= 32; // signed shift
}
assert c == 0L : c; // borrow out of subtract
assert dataInRangeIsZero(i, m, other); // negative result of subtract
return new FDBigInt( r, n-nzeros );
}
private static boolean dataInRangeIsZero(int i, int m, FDBigInt other) {
while ( i < m )
if (other.data[i++] != 0)
return false;
return true;
}
/*
* Compare FDBigInt with another FDBigInt. Return an integer
* >0: this > other
* 0: this == other
* <0: this < other
*/
public int
cmp( FDBigInt other ){
int i;
if ( this.nWords > other.nWords ){
// if any of my high-order words is non-zero,
// then the answer is evident
int j = other.nWords-1;
for ( i = this.nWords-1; i > j ; i-- )
if ( this.data[i] != 0 ) return 1;
}else if ( this.nWords < other.nWords ){
// if any of other's high-order words is non-zero,
// then the answer is evident
int j = this.nWords-1;
for ( i = other.nWords-1; i > j ; i-- )
if ( other.data[i] != 0 ) return -1;
} else{
i = this.nWords-1;
}
for ( ; i > 0 ; i-- )
if ( this.data[i] != other.data[i] )
break;
// careful! want unsigned compare!
// use brute force here.
int a = this.data[i];
int b = other.data[i];
if ( a < 0 ){
// a is really big, unsigned
if ( b < 0 ){
return a-b; // both big, negative
} else {
return 1; // b not big, answer is obvious;
}
} else {
// a is not really big
if ( b < 0 ) {
// but b is really big
return -1;
} else {
return a - b;
}
}
}
/*
* Compute
* q = (int)( this / S )
* this = 10 * ( this mod S )
* Return q.
* This is the iteration step of digit development for output.
* We assume that S has been normalized, as above, and that
* "this" has been lshift'ed accordingly.
* Also assume, of course, that the result, q, can be expressed
* as an integer, 0 <= q < 10.
*/
public int
quoRemIteration( FDBigInt S )throws IllegalArgumentException {
// ensure that this and S have the same number of
// digits. If S is properly normalized and q < 10 then
// this must be so.
if ( nWords != S.nWords ){
throw new IllegalArgumentException("disparate values");
}
// estimate q the obvious way. We will usually be
// right. If not, then we're only off by a little and
// will re-add.
int n = nWords-1;
long q = ((long)data[n]&0xffffffffL) / (long)S.data[n];
long diff = 0L;
for ( int i = 0; i <= n ; i++ ){
diff += ((long)data[i]&0xffffffffL) - q*((long)S.data[i]&0xffffffffL);
data[i] = (int)diff;
diff >>= 32; // N.B. SIGNED shift.
}
if ( diff != 0L ) {
// damn, damn, damn. q is too big.
// add S back in until this turns +. This should
// not be very many times!
long sum = 0L;
while ( sum == 0L ){
sum = 0L;
for ( int i = 0; i <= n; i++ ){
sum += ((long)data[i]&0xffffffffL) + ((long)S.data[i]&0xffffffffL);
data[i] = (int) sum;
sum >>= 32; // Signed or unsigned, answer is 0 or 1
}
/*
* Originally the following line read
* "if ( sum !=0 && sum != -1 )"
* but that would be wrong, because of the
* treatment of the two values as entirely unsigned,
* it would be impossible for a carry-out to be interpreted
* as -1 -- it would have to be a single-bit carry-out, or
* +1.
*/
assert sum == 0 || sum == 1 : sum; // carry out of division correction
q -= 1;
}
}
// finally, we can multiply this by 10.
// it cannot overflow, right, as the high-order word has
// at least 4 high-order zeros!
long p = 0L;
for ( int i = 0; i <= n; i++ ){
p += 10*((long)data[i]&0xffffffffL);
data[i] = (int)p;
p >>= 32; // SIGNED shift.
}
assert p == 0L : p; // Carry out of *10
return (int)q;
}
public long
longValue(){
// if this can be represented as a long, return the value
assert this.nWords > 0 : this.nWords; // longValue confused
if (this.nWords == 1)
return ((long)data[0]&0xffffffffL);
assert dataInRangeIsZero(2, this.nWords, this); // value too big
assert data[1] >= 0; // value too big
return ((long)(data[1]) << 32) | ((long)data[0]&0xffffffffL);
}
public String
toString() {
StringBuffer r = new StringBuffer(30);
r.append('[');
int i = Math.min( nWords-1, data.length-1) ;
if ( nWords > data.length ){
r.append( "("+data.length+"<"+nWords+"!)" );
}
for( ; i> 0 ; i-- ){
r.append( Integer.toHexString( data[i] ) );
r.append(' ');
}
r.append( Integer.toHexString( data[0] ) );
r.append(']');
return new String( r );
}
}