jdk.internal.math.FDBigInteger Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qbicc-rt-java.base Show documentation
Show all versions of qbicc-rt-java.base Show documentation
The Qbicc builder for the java.base JDK module
/*
* Copyright (c) 2013, 2020, 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 jdk.internal.math;
import jdk.internal.misc.CDS;
import java.math.BigInteger;
import java.util.Arrays;
//@ model import org.jmlspecs.models.JMLMath;
/**
* A simple big integer package specifically for floating point base conversion.
*/
public /*@ spec_bigint_math @*/ class FDBigInteger {
//
// This class contains many comments that start with "/*@" mark.
// They are behavourial specification in
// the Java Modelling Language (JML):
// http://www.eecs.ucf.edu/~leavens/JML//index.shtml
//
/*@
@ public pure model static \bigint UNSIGNED(int v) {
@ return v >= 0 ? v : v + (((\bigint)1) << 32);
@ }
@
@ public pure model static \bigint UNSIGNED(long v) {
@ return v >= 0 ? v : v + (((\bigint)1) << 64);
@ }
@
@ public pure model static \bigint AP(int[] data, int len) {
@ return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32));
@ }
@
@ public pure model static \bigint pow52(int p5, int p2) {
@ ghost \bigint v = 1;
@ for (int i = 0; i < p5; i++) v *= 5;
@ return v << p2;
@ }
@
@ public pure model static \bigint pow10(int p10) {
@ return pow52(p10, p10);
@ }
@*/
static final int[] SMALL_5_POW;
static final long[] LONG_5_POW;
// Maximum size of cache of powers of 5 as FDBigIntegers.
private static final int MAX_FIVE_POW = 340;
// Cache of big powers of 5 as FDBigIntegers.
private static final FDBigInteger POW_5_CACHE[];
// Zero as an FDBigInteger.
public static final FDBigInteger ZERO;
// Archive proxy
private static Object[] archivedCaches;
// Initialize FDBigInteger cache of powers of 5.
static {
CDS.initializeFromArchive(FDBigInteger.class);
Object[] caches = archivedCaches;
if (caches == null) {
long[] long5pow = {
1L,
5L,
5L * 5,
5L * 5 * 5,
5L * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
};
int[] small5pow = {
1,
5,
5 * 5,
5 * 5 * 5,
5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
};
FDBigInteger[] pow5cache = new FDBigInteger[MAX_FIVE_POW];
int i = 0;
while (i < small5pow.length) {
FDBigInteger pow5 = new FDBigInteger(new int[] { small5pow[i] }, 0);
pow5.makeImmutable();
pow5cache[i] = pow5;
i++;
}
FDBigInteger prev = pow5cache[i - 1];
while (i < MAX_FIVE_POW) {
pow5cache[i] = prev = prev.mult(5);
prev.makeImmutable();
i++;
}
FDBigInteger zero = new FDBigInteger(new int[0], 0);
zero.makeImmutable();
archivedCaches = caches = new Object[] {small5pow, long5pow, pow5cache, zero};
}
SMALL_5_POW = (int[])caches[0];
LONG_5_POW = (long[])caches[1];
POW_5_CACHE = (FDBigInteger[])caches[2];
ZERO = (FDBigInteger)caches[3];
}
// Constant for casting an int to a long via bitwise AND.
private static final long LONG_MASK = 0xffffffffL;
//@ spec_public non_null;
private int data[]; // value: data[0] is least significant
//@ spec_public;
private int offset; // number of least significant zero padding ints
//@ spec_public;
private int nWords; // data[nWords-1]!=0, all values above are zero
// if nWords==0 -> this FDBigInteger is zero
//@ spec_public;
private boolean isImmutable = false;
/*@
@ public invariant 0 <= nWords && nWords <= data.length && offset >= 0;
@ public invariant nWords == 0 ==> offset == 0;
@ public invariant nWords > 0 ==> data[nWords - 1] != 0;
@ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0);
@ public pure model \bigint value() {
@ return AP(data, nWords) << (offset*32);
@ }
@*/
/**
* Constructs an FDBigInteger
from data and padding. The
* data
parameter has the least significant int
at
* the zeroth index. The offset
parameter gives the number of
* zero int
s to be inferred below the least significant element
* of data
.
*
* @param data An array containing all non-zero int
s of the value.
* @param offset An offset indicating the number of zero int
s to pad
* below the least significant element of data
.
*/
/*@
@ requires data != null && offset >= 0;
@ ensures this.value() == \old(AP(data, data.length) << (offset*32));
@ ensures this.data == \old(data);
@*/
private FDBigInteger(int[] data, int offset) {
this.data = data;
this.offset = offset;
this.nWords = data.length;
trimLeadingZeros();
}
/**
* Constructs an FDBigInteger
from a starting value and some
* decimal digits.
*
* @param lValue The starting value.
* @param digits The decimal digits.
* @param kDigits The initial index into digits
.
* @param nDigits The final index into digits
.
*/
/*@
@ requires digits != null;
@ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length;
@ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9');
@ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1)));
@*/
public FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits) {
int n = Math.max((nDigits + 8) / 9, 2); // estimate size needed.
data = new int[n]; // allocate enough space
data[0] = (int) lValue; // starting value
data[1] = (int) (lValue >>> 32);
offset = 0;
nWords = 2;
int i = kDigits;
int limit = nDigits - 5; // slurp digits 5 at a time.
int v;
while (i < limit) {
int ilim = i + 5;
v = (int) digits[i++] - (int) '0';
while (i < ilim) {
v = 10 * v + (int) digits[i++] - (int) '0';
}
multAddMe(100000, v); // ... where 100000 is 10^5.
}
int factor = 1;
v = 0;
while (i < nDigits) {
v = 10 * v + (int) digits[i++] - (int) '0';
factor *= 10;
}
if (factor != 1) {
multAddMe(factor, v);
}
trimLeadingZeros();
}
/**
* Returns an FDBigInteger
with the numerical value
* 5p5 * 2p2
.
*
* @param p5 The exponent of the power-of-five factor.
* @param p2 The exponent of the power-of-two factor.
* @return 5p5 * 2p2
*/
/*@
@ requires p5 >= 0 && p2 >= 0;
@ assignable \nothing;
@ ensures \result.value() == \old(pow52(p5, p2));
@*/
public static FDBigInteger valueOfPow52(int p5, int p2) {
if (p5 != 0) {
if (p2 == 0) {
return big5pow(p5);
} else if (p5 < SMALL_5_POW.length) {
int pow5 = SMALL_5_POW[p5];
int wordcount = p2 >> 5;
int bitcount = p2 & 0x1f;
if (bitcount == 0) {
return new FDBigInteger(new int[]{pow5}, wordcount);
} else {
return new FDBigInteger(new int[]{
pow5 << bitcount,
pow5 >>> (32 - bitcount)
}, wordcount);
}
} else {
return big5pow(p5).leftShift(p2);
}
} else {
return valueOfPow2(p2);
}
}
/**
* Returns an FDBigInteger
with the numerical value
* value * 5p5 * 2p2
.
*
* @param value The constant factor.
* @param p5 The exponent of the power-of-five factor.
* @param p2 The exponent of the power-of-two factor.
* @return value * 5p5 * 2p2
*/
/*@
@ requires p5 >= 0 && p2 >= 0;
@ assignable \nothing;
@ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2));
@*/
public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) {
assert p5 >= 0 : p5;
assert p2 >= 0 : p2;
int v0 = (int) value;
int v1 = (int) (value >>> 32);
int wordcount = p2 >> 5;
int bitcount = p2 & 0x1f;
if (p5 != 0) {
if (p5 < SMALL_5_POW.length) {
long pow5 = SMALL_5_POW[p5] & LONG_MASK;
long carry = (v0 & LONG_MASK) * pow5;
v0 = (int) carry;
carry >>>= 32;
carry = (v1 & LONG_MASK) * pow5 + carry;
v1 = (int) carry;
int v2 = (int) (carry >>> 32);
if (bitcount == 0) {
return new FDBigInteger(new int[]{v0, v1, v2}, wordcount);
} else {
return new FDBigInteger(new int[]{
v0 << bitcount,
(v1 << bitcount) | (v0 >>> (32 - bitcount)),
(v2 << bitcount) | (v1 >>> (32 - bitcount)),
v2 >>> (32 - bitcount)
}, wordcount);
}
} else {
FDBigInteger pow5 = big5pow(p5);
int[] r;
if (v1 == 0) {
r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)];
mult(pow5.data, pow5.nWords, v0, r);
} else {
r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)];
mult(pow5.data, pow5.nWords, v0, v1, r);
}
return (new FDBigInteger(r, pow5.offset)).leftShift(p2);
}
} else if (p2 != 0) {
if (bitcount == 0) {
return new FDBigInteger(new int[]{v0, v1}, wordcount);
} else {
return new FDBigInteger(new int[]{
v0 << bitcount,
(v1 << bitcount) | (v0 >>> (32 - bitcount)),
v1 >>> (32 - bitcount)
}, wordcount);
}
}
return new FDBigInteger(new int[]{v0, v1}, 0);
}
/**
* Returns an FDBigInteger
with the numerical value
* 2p2
.
*
* @param p2 The exponent of 2.
* @return 2p2
*/
/*@
@ requires p2 >= 0;
@ assignable \nothing;
@ ensures \result.value() == pow52(0, p2);
@*/
private static FDBigInteger valueOfPow2(int p2) {
int wordcount = p2 >> 5;
int bitcount = p2 & 0x1f;
return new FDBigInteger(new int[]{1 << bitcount}, wordcount);
}
/**
* Removes all leading zeros from this FDBigInteger
adjusting
* the offset and number of non-zero leading words accordingly.
*/
/*@
@ requires data != null;
@ requires 0 <= nWords && nWords <= data.length && offset >= 0;
@ requires nWords == 0 ==> offset == 0;
@ ensures nWords == 0 ==> offset == 0;
@ ensures nWords > 0 ==> data[nWords - 1] != 0;
@*/
private /*@ helper @*/ void trimLeadingZeros() {
int i = nWords;
if (i > 0 && (data[--i] == 0)) {
//for (; i > 0 && data[i - 1] == 0; i--) ;
while(i > 0 && data[i - 1] == 0) {
i--;
}
this.nWords = i;
if (i == 0) { // all words are zero
this.offset = 0;
}
}
}
/**
* Retrieves the normalization bias of the FDBigIntger
. The
* normalization bias is a left shift such that after it the highest word
* of the value will have the 4 highest bits equal to zero:
* {@code (highestWord & 0xf0000000) == 0}, but the next bit should be 1
* {@code (highestWord & 0x08000000) != 0}.
*
* @return The normalization bias.
*/
/*@
@ requires this.value() > 0;
@*/
public /*@ pure @*/ int getNormalizationBias() {
if (nWords == 0) {
throw new IllegalArgumentException("Zero value cannot be normalized");
}
int zeros = Integer.numberOfLeadingZeros(data[nWords - 1]);
return (zeros < 4) ? 28 + zeros : zeros - 4;
}
// TODO: Why is anticount param needed if it is always 32 - bitcount?
/**
* Left shifts the contents of one int array into another.
*
* @param src The source array.
* @param idx The initial index of the source array.
* @param result The destination array.
* @param bitcount The left shift.
* @param anticount The left anti-shift, e.g., 32-bitcount
.
* @param prev The prior source value.
*/
/*@
@ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount;
@ requires src.length >= idx && result.length > idx;
@ assignable result[*];
@ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount);
@*/
private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){
for (; idx > 0; idx--) {
int v = (prev << bitcount);
prev = src[idx - 1];
v |= (prev >>> anticount);
result[idx] = v;
}
int v = prev << bitcount;
result[0] = v;
}
/**
* Shifts this FDBigInteger
to the left. The shift is performed
* in-place unless the FDBigInteger
is immutable in which case
* a new instance of FDBigInteger
is returned.
*
* @param shift The number of bits to shift left.
* @return The shifted FDBigInteger
.
*/
/*@
@ requires this.value() == 0 || shift == 0;
@ assignable \nothing;
@ ensures \result == this;
@
@ also
@
@ requires this.value() > 0 && shift > 0 && this.isImmutable;
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() << shift);
@
@ also
@
@ requires this.value() > 0 && shift > 0 && this.isImmutable;
@ assignable \nothing;
@ ensures \result == this;
@ ensures \result.value() == \old(this.value() << shift);
@*/
public FDBigInteger leftShift(int shift) {
if (shift == 0 || nWords == 0) {
return this;
}
int wordcount = shift >> 5;
int bitcount = shift & 0x1f;
if (this.isImmutable) {
if (bitcount == 0) {
return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount);
} else {
int anticount = 32 - bitcount;
int idx = nWords - 1;
int prev = data[idx];
int hi = prev >>> anticount;
int[] result;
if (hi != 0) {
result = new int[nWords + 1];
result[nWords] = hi;
} else {
result = new int[nWords];
}
leftShift(data,idx,result,bitcount,anticount,prev);
return new FDBigInteger(result, offset + wordcount);
}
} else {
if (bitcount != 0) {
int anticount = 32 - bitcount;
if ((data[0] << bitcount) == 0) {
int idx = 0;
int prev = data[idx];
for (; idx < nWords - 1; idx++) {
int v = (prev >>> anticount);
prev = data[idx + 1];
v |= (prev << bitcount);
data[idx] = v;
}
int v = prev >>> anticount;
data[idx] = v;
if(v==0) {
nWords--;
}
offset++;
} else {
int idx = nWords - 1;
int prev = data[idx];
int hi = prev >>> anticount;
int[] result = data;
int[] src = data;
if (hi != 0) {
if(nWords == data.length) {
data = result = new int[nWords + 1];
}
result[nWords++] = hi;
}
leftShift(src,idx,result,bitcount,anticount,prev);
}
}
offset += wordcount;
return this;
}
}
/**
* Returns the number of int
s this FDBigInteger
represents.
*
* @return Number of int
s required to represent this FDBigInteger
.
*/
/*@
@ requires this.value() == 0;
@ ensures \result == 0;
@
@ also
@
@ requires this.value() > 0;
@ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result;
@*/
private /*@ pure @*/ int size() {
return nWords + offset;
}
/**
* Computes
*
* 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 left-shifted accordingly.
* Also assumed, of course, is that the result, q, can be expressed
* as an integer, {@code 0 <= q < 10}.
*
* @param S The divisor of this FDBigInteger
.
* @return q = (int)(this / S)
.
*/
/*@
@ requires !this.isImmutable;
@ requires this.size() <= S.size();
@ requires this.data.length + this.offset >= S.size();
@ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4);
@ assignable this.nWords, this.offset, this.data, this.data[*];
@ ensures \result == \old(this.value() / S.value());
@ ensures this.value() == \old(10 * (this.value() % S.value()));
@*/
public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException {
assert !this.isImmutable : "cannot modify immutable value";
// ensure that this and S have the same number of
// digits. If S is properly normalized and q < 10 then
// this must be so.
int thSize = this.size();
int sSize = S.size();
if (thSize < sSize) {
// this value is significantly less than S, result of division is zero.
// just mult this by 10.
int p = multAndCarryBy10(this.data, this.nWords, this.data);
if(p!=0) {
this.data[nWords++] = p;
} else {
trimLeadingZeros();
}
return 0;
} else if (thSize > sSize) {
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.
long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK);
long diff = multDiffMe(q, S);
if (diff != 0L) {
//@ assert q != 0;
//@ assert this.offset == \old(Math.min(this.offset, S.offset));
//@ assert this.offset <= S.offset;
// q is too big.
// add S back in until this turns +. This should
// not be very many times!
long sum = 0L;
int tStart = S.offset - this.offset;
//@ assert tStart >= 0;
int[] sd = S.data;
int[] td = this.data;
while (sum == 0L) {
for (int sIndex = 0, tIndex = tStart; tIndex < this.nWords; sIndex++, tIndex++) {
sum += (td[tIndex] & LONG_MASK) + (sd[sIndex] & LONG_MASK);
td[tIndex] = (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!
int p = multAndCarryBy10(this.data, this.nWords, this.data);
assert p == 0 : p; // Carry out of *10
trimLeadingZeros();
return (int) q;
}
/**
* Multiplies this FDBigInteger
by 10. The operation will be
* performed in place unless the FDBigInteger
is immutable in
* which case a new FDBigInteger
will be returned.
*
* @return The FDBigInteger
multiplied by 10.
*/
/*@
@ requires this.value() == 0;
@ assignable \nothing;
@ ensures \result == this;
@
@ also
@
@ requires this.value() > 0 && this.isImmutable;
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() * 10);
@
@ also
@
@ requires this.value() > 0 && !this.isImmutable;
@ assignable this.nWords, this.data, this.data[*];
@ ensures \result == this;
@ ensures \result.value() == \old(this.value() * 10);
@*/
public FDBigInteger multBy10() {
if (nWords == 0) {
return this;
}
if (isImmutable) {
int[] res = new int[nWords + 1];
res[nWords] = multAndCarryBy10(data, nWords, res);
return new FDBigInteger(res, offset);
} else {
int p = multAndCarryBy10(this.data, this.nWords, this.data);
if (p != 0) {
if (nWords == data.length) {
if (data[0] == 0) {
System.arraycopy(data, 1, data, 0, --nWords);
offset++;
} else {
data = Arrays.copyOf(data, data.length + 1);
}
}
data[nWords++] = p;
} else {
trimLeadingZeros();
}
return this;
}
}
/**
* Multiplies this FDBigInteger
by
* 5p5 * 2p2
. The operation will be
* performed in place if possible, otherwise a new FDBigInteger
* will be returned.
*
* @param p5 The exponent of the power-of-five factor.
* @param p2 The exponent of the power-of-two factor.
* @return The multiplication result.
*/
/*@
@ requires this.value() == 0 || p5 == 0 && p2 == 0;
@ assignable \nothing;
@ ensures \result == this;
@
@ also
@
@ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable);
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() * pow52(p5, p2));
@
@ also
@
@ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable;
@ assignable this.nWords, this.data, this.data[*];
@ ensures \result == this;
@ ensures \result.value() == \old(this.value() * pow52(p5, p2));
@*/
public FDBigInteger multByPow52(int p5, int p2) {
if (this.nWords == 0) {
return this;
}
FDBigInteger res = this;
if (p5 != 0) {
int[] r;
int extraSize = (p2 != 0) ? 1 : 0;
if (p5 < SMALL_5_POW.length) {
r = new int[this.nWords + 1 + extraSize];
mult(this.data, this.nWords, SMALL_5_POW[p5], r);
res = new FDBigInteger(r, this.offset);
} else {
FDBigInteger pow5 = big5pow(p5);
r = new int[this.nWords + pow5.size() + extraSize];
mult(this.data, this.nWords, pow5.data, pow5.nWords, r);
res = new FDBigInteger(r, this.offset + pow5.offset);
}
}
return res.leftShift(p2);
}
/**
* Multiplies two big integers represented as int arrays.
*
* @param s1 The first array factor.
* @param s1Len The number of elements of s1
to use.
* @param s2 The second array factor.
* @param s2Len The number of elements of s2
to use.
* @param dst The product array.
*/
/*@
@ requires s1 != dst && s2 != dst;
@ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len;
@ assignable dst[0 .. s1Len + s2Len - 1];
@ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len));
@*/
private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) {
for (int i = 0; i < s1Len; i++) {
long v = s1[i] & LONG_MASK;
long p = 0L;
for (int j = 0; j < s2Len; j++) {
p += (dst[i + j] & LONG_MASK) + v * (s2[j] & LONG_MASK);
dst[i + j] = (int) p;
p >>>= 32;
}
dst[i + s2Len] = (int) p;
}
}
/**
* Subtracts the supplied FDBigInteger
subtrahend from this
* FDBigInteger
. Assert that the result is positive.
* If the subtrahend is immutable, store the result in this(minuend).
* If this(minuend) is immutable a new FDBigInteger
is created.
*
* @param subtrahend The FDBigInteger
to be subtracted.
* @return This FDBigInteger
less the subtrahend.
*/
/*@
@ requires this.isImmutable;
@ requires this.value() >= subtrahend.value();
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() - subtrahend.value());
@
@ also
@
@ requires !subtrahend.isImmutable;
@ requires this.value() >= subtrahend.value();
@ assignable this.nWords, this.offset, this.data, this.data[*];
@ ensures \result == this;
@ ensures \result.value() == \old(this.value() - subtrahend.value());
@*/
public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) {
assert this.size() >= subtrahend.size() : "result should be positive";
FDBigInteger minuend;
if (this.isImmutable) {
minuend = new FDBigInteger(this.data.clone(), this.offset);
} else {
minuend = this;
}
int offsetDiff = subtrahend.offset - minuend.offset;
int[] sData = subtrahend.data;
int[] mData = minuend.data;
int subLen = subtrahend.nWords;
int minLen = minuend.nWords;
if (offsetDiff < 0) {
// need to expand minuend
int rLen = minLen - offsetDiff;
if (rLen < mData.length) {
System.arraycopy(mData, 0, mData, -offsetDiff, minLen);
Arrays.fill(mData, 0, -offsetDiff, 0);
} else {
int[] r = new int[rLen];
System.arraycopy(mData, 0, r, -offsetDiff, minLen);
minuend.data = mData = r;
}
minuend.offset = subtrahend.offset;
minuend.nWords = minLen = rLen;
offsetDiff = 0;
}
long borrow = 0L;
int mIndex = offsetDiff;
for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) {
long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
mData[mIndex] = (int) diff;
borrow = diff >> 32; // signed shift
}
for (; borrow != 0 && mIndex < minLen; mIndex++) {
long diff = (mData[mIndex] & LONG_MASK) + borrow;
mData[mIndex] = (int) diff;
borrow = diff >> 32; // signed shift
}
assert borrow == 0L : borrow; // borrow out of subtract,
// result should be positive
minuend.trimLeadingZeros();
return minuend;
}
/**
* Subtracts the supplied FDBigInteger
subtrahend from this
* FDBigInteger
. Assert that the result is positive.
* If the this(minuend) is immutable, store the result in subtrahend.
* If subtrahend is immutable a new FDBigInteger
is created.
*
* @param subtrahend The FDBigInteger
to be subtracted.
* @return This FDBigInteger
less the subtrahend.
*/
/*@
@ requires subtrahend.isImmutable;
@ requires this.value() >= subtrahend.value();
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() - subtrahend.value());
@
@ also
@
@ requires !subtrahend.isImmutable;
@ requires this.value() >= subtrahend.value();
@ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*];
@ ensures \result == subtrahend;
@ ensures \result.value() == \old(this.value() - subtrahend.value());
@*/
public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) {
assert this.size() >= subtrahend.size() : "result should be positive";
FDBigInteger minuend = this;
if (subtrahend.isImmutable) {
subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset);
}
int offsetDiff = minuend.offset - subtrahend.offset;
int[] sData = subtrahend.data;
int[] mData = minuend.data;
int subLen = subtrahend.nWords;
int minLen = minuend.nWords;
if (offsetDiff < 0) {
int rLen = minLen;
if (rLen < sData.length) {
System.arraycopy(sData, 0, sData, -offsetDiff, subLen);
Arrays.fill(sData, 0, -offsetDiff, 0);
} else {
int[] r = new int[rLen];
System.arraycopy(sData, 0, r, -offsetDiff, subLen);
subtrahend.data = sData = r;
}
subtrahend.offset = minuend.offset;
subLen -= offsetDiff;
offsetDiff = 0;
} else {
int rLen = minLen + offsetDiff;
if (rLen >= sData.length) {
subtrahend.data = sData = Arrays.copyOf(sData, rLen);
}
}
//@ assert minuend == this && minuend.value() == \old(this.value());
//@ assert mData == minuend.data && minLen == minuend.nWords;
//@ assert subtrahend.offset + subtrahend.data.length >= minuend.size();
//@ assert sData == subtrahend.data;
//@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value());
//@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset);
//@ assert offsetDiff == minuend.offset - subtrahend.offset;
//@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length;
int sIndex = 0;
long borrow = 0L;
for (; sIndex < offsetDiff; sIndex++) {
long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow;
sData[sIndex] = (int) diff;
borrow = diff >> 32; // signed shift
}
//@ assert sIndex == offsetDiff;
for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) {
//@ assert sIndex == offsetDiff + mIndex;
long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
sData[sIndex] = (int) diff;
borrow = diff >> 32; // signed shift
}
assert borrow == 0L : borrow; // borrow out of subtract,
// result should be positive
subtrahend.nWords = sIndex;
subtrahend.trimLeadingZeros();
return subtrahend;
}
/**
* Determines whether all elements of an array are zero for all indices less
* than a given index.
*
* @param a The array to be examined.
* @param from The index strictly below which elements are to be examined.
* @return Zero if all elements in range are zero, 1 otherwise.
*/
/*@
@ requires 0 <= from && from <= a.length;
@ ensures \result == (AP(a, from) == 0 ? 0 : 1);
@*/
private /*@ pure @*/ static int checkZeroTail(int[] a, int from) {
while (from > 0) {
if (a[--from] != 0) {
return 1;
}
}
return 0;
}
/**
* Compares the parameter with this FDBigInteger
. Returns an
* integer accordingly as:
* {@code
* > 0: this > other
* 0: this == other
* < 0: this < other
* }
*
* @param other The FDBigInteger
to compare.
* @return A negative value, zero, or a positive value according to the
* result of the comparison.
*/
/*@
@ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0);
@*/
public /*@ pure @*/ int cmp(FDBigInteger other) {
int aSize = nWords + offset;
int bSize = other.nWords + other.offset;
if (aSize > bSize) {
return 1;
} else if (aSize < bSize) {
return -1;
}
int aLen = nWords;
int bLen = other.nWords;
while (aLen > 0 && bLen > 0) {
int a = data[--aLen];
int b = other.data[--bLen];
if (a != b) {
return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
}
}
if (aLen > 0) {
return checkZeroTail(data, aLen);
}
if (bLen > 0) {
return -checkZeroTail(other.data, bLen);
}
return 0;
}
/**
* Compares this FDBigInteger
with
* 5p5 * 2p2
.
* Returns an integer accordingly as:
* {@code
* > 0: this > other
* 0: this == other
* < 0: this < other
* }
* @param p5 The exponent of the power-of-five factor.
* @param p2 The exponent of the power-of-two factor.
* @return A negative value, zero, or a positive value according to the
* result of the comparison.
*/
/*@
@ requires p5 >= 0 && p2 >= 0;
@ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0);
@*/
public /*@ pure @*/ int cmpPow52(int p5, int p2) {
if (p5 == 0) {
int wordcount = p2 >> 5;
int bitcount = p2 & 0x1f;
int size = this.nWords + this.offset;
if (size > wordcount + 1) {
return 1;
} else if (size < wordcount + 1) {
return -1;
}
int a = this.data[this.nWords -1];
int b = 1 << bitcount;
if (a != b) {
return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
}
return checkZeroTail(this.data, this.nWords - 1);
}
return this.cmp(big5pow(p5).leftShift(p2));
}
/**
* Compares this FDBigInteger
with x + y
. Returns a
* value according to the comparison as:
* {@code
* -1: this < x + y
* 0: this == x + y
* 1: this > x + y
* }
* @param x The first addend of the sum to compare.
* @param y The second addend of the sum to compare.
* @return -1, 0, or 1 according to the result of the comparison.
*/
/*@
@ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0);
@*/
public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) {
FDBigInteger big;
FDBigInteger small;
int xSize = x.size();
int ySize = y.size();
int bSize;
int sSize;
if (xSize >= ySize) {
big = x;
small = y;
bSize = xSize;
sSize = ySize;
} else {
big = y;
small = x;
bSize = ySize;
sSize = xSize;
}
int thSize = this.size();
if (bSize == 0) {
return thSize == 0 ? 0 : 1;
}
if (sSize == 0) {
return this.cmp(big);
}
if (bSize > thSize) {
return -1;
}
if (bSize + 1 < thSize) {
return 1;
}
long top = (big.data[big.nWords - 1] & LONG_MASK);
if (sSize == bSize) {
top += (small.data[small.nWords - 1] & LONG_MASK);
}
if ((top >>> 32) == 0) {
if (((top + 1) >>> 32) == 0) {
// good case - no carry extension
if (bSize < thSize) {
return 1;
}
// here sum.nWords == this.nWords
long v = (this.data[this.nWords - 1] & LONG_MASK);
if (v < top) {
return -1;
}
if (v > top + 1) {
return 1;
}
}
} else { // (top>>>32)!=0 guaranteed carry extension
if (bSize + 1 > thSize) {
return -1;
}
// here sum.nWords == this.nWords
top >>>= 32;
long v = (this.data[this.nWords - 1] & LONG_MASK);
if (v < top) {
return -1;
}
if (v > top + 1) {
return 1;
}
}
return this.cmp(big.add(small));
}
/**
* Makes this FDBigInteger
immutable.
*/
/*@
@ assignable this.isImmutable;
@ ensures this.isImmutable;
@*/
public void makeImmutable() {
this.isImmutable = true;
}
/**
* Multiplies this FDBigInteger
by an integer.
*
* @param i The factor by which to multiply this FDBigInteger
.
* @return This FDBigInteger
multiplied by an integer.
*/
/*@
@ requires this.value() == 0;
@ assignable \nothing;
@ ensures \result == this;
@
@ also
@
@ requires this.value() != 0;
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() * UNSIGNED(i));
@*/
private FDBigInteger mult(int i) {
if (this.nWords == 0) {
return this;
}
int[] r = new int[nWords + 1];
mult(data, nWords, i, r);
return new FDBigInteger(r, offset);
}
/**
* Multiplies this FDBigInteger
by another FDBigInteger
.
*
* @param other The FDBigInteger
factor by which to multiply.
* @return The product of this and the parameter FDBigInteger
s.
*/
/*@
@ requires this.value() == 0;
@ assignable \nothing;
@ ensures \result == this;
@
@ also
@
@ requires this.value() != 0 && other.value() == 0;
@ assignable \nothing;
@ ensures \result == other;
@
@ also
@
@ requires this.value() != 0 && other.value() != 0;
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() * other.value());
@*/
private FDBigInteger mult(FDBigInteger other) {
if (this.nWords == 0) {
return this;
}
if (this.size() == 1) {
return other.mult(data[0]);
}
if (other.nWords == 0) {
return other;
}
if (other.size() == 1) {
return this.mult(other.data[0]);
}
int[] r = new int[nWords + other.nWords];
mult(this.data, this.nWords, other.data, other.nWords, r);
return new FDBigInteger(r, this.offset + other.offset);
}
/**
* Adds another FDBigInteger
to this FDBigInteger
.
*
* @param other The FDBigInteger
to add.
* @return The sum of the FDBigInteger
s.
*/
/*@
@ assignable \nothing;
@ ensures \result.value() == \old(this.value() + other.value());
@*/
private FDBigInteger add(FDBigInteger other) {
FDBigInteger big, small;
int bigLen, smallLen;
int tSize = this.size();
int oSize = other.size();
if (tSize >= oSize) {
big = this;
bigLen = tSize;
small = other;
smallLen = oSize;
} else {
big = other;
bigLen = oSize;
small = this;
smallLen = tSize;
}
int[] r = new int[bigLen + 1];
int i = 0;
long carry = 0L;
for (; i < smallLen; i++) {
carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) )
+ ((i < small.offset ? 0L : (small.data[i - small.offset] & LONG_MASK)));
r[i] = (int) carry;
carry >>= 32; // signed shift.
}
for (; i < bigLen; i++) {
carry += (i < big.offset ? 0L : (big.data[i - big.offset] & LONG_MASK) );
r[i] = (int) carry;
carry >>= 32; // signed shift.
}
r[bigLen] = (int) carry;
return new FDBigInteger(r, 0);
}
/**
* Multiplies a FDBigInteger
by an int and adds another int. The
* result is computed in place. This method is intended only to be invoked
* from
*
* FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits)
*
.
*
* @param iv The factor by which to multiply this FDBigInteger
.
* @param addend The value to add to the product of this
* FDBigInteger
and iv
.
*/
/*@
@ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32);
@ assignable this.data[*];
@ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend));
@*/
private /*@ helper @*/ void multAddMe(int iv, int addend) {
long v = iv & LONG_MASK;
// unroll 0th iteration, doing addition.
long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK);
data[0] = (int) p;
p >>>= 32;
for (int i = 1; i < nWords; i++) {
p += v * (data[i] & LONG_MASK);
data[i] = (int) p;
p >>>= 32;
}
if (p != 0L) {
data[nWords++] = (int) p; // will fail noisily if illegal!
}
}
//
// original doc:
//
// do this -=q*S
// returns borrow
//
/**
* Multiplies the parameters and subtracts them from this
* FDBigInteger
.
*
* @param q The integer parameter.
* @param S The FDBigInteger
parameter.
* @return this - q*S
.
*/
/*@
@ ensures nWords == 0 ==> offset == 0;
@ ensures nWords > 0 ==> data[nWords - 1] != 0;
@*/
/*@
@ requires 0 < q && q <= (1L << 31);
@ requires data != null;
@ requires 0 <= nWords && nWords <= data.length && offset >= 0;
@ requires !this.isImmutable;
@ requires this.size() == S.size();
@ requires this != S;
@ assignable this.nWords, this.offset, this.data, this.data[*];
@ ensures -q <= \result && \result <= 0;
@ ensures this.size() == \old(this.size());
@ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value());
@ ensures this.offset == \old(Math.min(this.offset, S.offset));
@ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords);
@ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset);
@ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data);
@
@ also
@
@ requires q == 0;
@ assignable \nothing;
@ ensures \result == 0;
@*/
private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) {
long diff = 0L;
if (q != 0) {
int deltaSize = S.offset - this.offset;
if (deltaSize >= 0) {
int[] sd = S.data;
int[] td = this.data;
for (int sIndex = 0, tIndex = deltaSize; sIndex < S.nWords; sIndex++, tIndex++) {
diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK);
td[tIndex] = (int) diff;
diff >>= 32; // N.B. SIGNED shift.
}
} else {
deltaSize = -deltaSize;
int[] rd = new int[nWords + deltaSize];
int sIndex = 0;
int rIndex = 0;
int[] sd = S.data;
for (; rIndex < deltaSize && sIndex < S.nWords; sIndex++, rIndex++) {
diff -= q * (sd[sIndex] & LONG_MASK);
rd[rIndex] = (int) diff;
diff >>= 32; // N.B. SIGNED shift.
}
int tIndex = 0;
int[] td = this.data;
for (; sIndex < S.nWords; sIndex++, tIndex++, rIndex++) {
diff += (td[tIndex] & LONG_MASK) - q * (sd[sIndex] & LONG_MASK);
rd[rIndex] = (int) diff;
diff >>= 32; // N.B. SIGNED shift.
}
this.nWords += deltaSize;
this.offset -= deltaSize;
this.data = rd;
}
}
return diff;
}
/**
* Multiplies by 10 a big integer represented as an array. The final carry
* is returned.
*
* @param src The array representation of the big integer.
* @param srcLen The number of elements of src
to use.
* @param dst The product array.
* @return The final carry of the multiplication.
*/
/*@
@ requires src.length >= srcLen && dst.length >= srcLen;
@ assignable dst[0 .. srcLen - 1];
@ ensures 0 <= \result && \result < 10;
@ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10);
@*/
private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) {
long carry = 0;
for (int i = 0; i < srcLen; i++) {
long product = (src[i] & LONG_MASK) * 10L + carry;
dst[i] = (int) product;
carry = product >>> 32;
}
return (int) carry;
}
/**
* Multiplies by a constant value a big integer represented as an array.
* The constant factor is an int
.
*
* @param src The array representation of the big integer.
* @param srcLen The number of elements of src
to use.
* @param value The constant factor by which to multiply.
* @param dst The product array.
*/
/*@
@ requires src.length >= srcLen && dst.length >= srcLen + 1;
@ assignable dst[0 .. srcLen];
@ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value));
@*/
private static void mult(int[] src, int srcLen, int value, int[] dst) {
long val = value & LONG_MASK;
long carry = 0;
for (int i = 0; i < srcLen; i++) {
long product = (src[i] & LONG_MASK) * val + carry;
dst[i] = (int) product;
carry = product >>> 32;
}
dst[srcLen] = (int) carry;
}
/**
* Multiplies by a constant value a big integer represented as an array.
* The constant factor is a long represent as two int
s.
*
* @param src The array representation of the big integer.
* @param srcLen The number of elements of src
to use.
* @param v0 The lower 32 bits of the long factor.
* @param v1 The upper 32 bits of the long factor.
* @param dst The product array.
*/
/*@
@ requires src != dst;
@ requires src.length >= srcLen && dst.length >= srcLen + 2;
@ assignable dst[0 .. srcLen + 1];
@ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32)));
@*/
private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) {
long v = v0 & LONG_MASK;
long carry = 0;
for (int j = 0; j < srcLen; j++) {
long product = v * (src[j] & LONG_MASK) + carry;
dst[j] = (int) product;
carry = product >>> 32;
}
dst[srcLen] = (int) carry;
v = v1 & LONG_MASK;
carry = 0;
for (int j = 0; j < srcLen; j++) {
long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry;
dst[j + 1] = (int) product;
carry = product >>> 32;
}
dst[srcLen + 1] = (int) carry;
}
// Fails assertion for negative exponent.
/**
* Computes 5
raised to a given power.
*
* @param p The exponent of 5.
* @return 5p
.
*/
private static FDBigInteger big5pow(int p) {
assert p >= 0 : p; // negative power of 5
if (p < MAX_FIVE_POW) {
return POW_5_CACHE[p];
}
return big5powRec(p);
}
// slow path
/**
* Computes 5
raised to a given power.
*
* @param p The exponent of 5.
* @return 5p
.
*/
private static FDBigInteger big5powRec(int p) {
if (p < MAX_FIVE_POW) {
return POW_5_CACHE[p];
}
// construct the value.
// recursively.
int q, r;
// in order to compute 5^p,
// compute its square root, 5^(p/2) and square.
// or, let q = p / 2, r = p -q, then
// 5^p = 5^(q+r) = 5^q * 5^r
q = p >> 1;
r = p - q;
FDBigInteger bigq = big5powRec(q);
if (r < SMALL_5_POW.length) {
return bigq.mult(SMALL_5_POW[r]);
} else {
return bigq.mult(big5powRec(r));
}
}
// for debugging ...
/**
* Converts this FDBigInteger
to a hexadecimal string.
*
* @return The hexadecimal string representation.
*/
public String toHexString(){
if(nWords ==0) {
return "0";
}
StringBuilder sb = new StringBuilder((nWords +offset)*8);
for(int i= nWords -1; i>=0; i--) {
String subStr = Integer.toHexString(data[i]);
for(int j = subStr.length(); j<8; j++) {
sb.append('0');
}
sb.append(subStr);
}
for(int i=offset; i>0; i--) {
sb.append("00000000");
}
return sb.toString();
}
// for debugging ...
/**
* Converts this FDBigInteger
to a BigInteger
.
*
* @return The BigInteger
representation.
*/
public BigInteger toBigInteger() {
byte[] magnitude = new byte[nWords * 4 + 1];
for (int i = 0; i < nWords; i++) {
int w = data[i];
magnitude[magnitude.length - 4 * i - 1] = (byte) w;
magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8);
magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16);
magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24);
}
return new BigInteger(magnitude).shiftLeft(offset * 32);
}
// for debugging ...
/**
* Converts this FDBigInteger
to a string.
*
* @return The string representation.
*/
@Override
public String toString(){
return toBigInteger().toString();
}
}