Skip to content

Commit 25403e6

Browse files
committed
Iterative version of the algorithm
1 parent dc67d06 commit 25403e6

File tree

1 file changed

+57
-71
lines changed

1 file changed

+57
-71
lines changed

src/java.base/share/classes/java/math/MutableBigInteger.java

Lines changed: 57 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,24 +1913,26 @@ MutableBigInteger[] sqrtRem() {
19131913
if (bitLength != (int) bitLength)
19141914
throw new ArithmeticException("bitLength() integer overflow");
19151915

1916-
return sqrtRemZimmermann(this, this.intLen, false);
1916+
return sqrtRemZimmermann(this);
19171917
}
19181918

19191919
/**
1920-
* Assume {@code 2 <= limit <= x.intLen}
1920+
* Assume {@code x.intLen >= 2}
19211921
* @implNote The implementation is based on Zimmermann's works available
19221922
* <a href="https://inria.hal.science/inria-00072854/en/"> here</a> and
19231923
* <a href="https://www.researchgate.net/publication/220532560_A_proof_of_GMP_square_root"> here</a>
19241924
*/
1925-
private static MutableBigInteger[] sqrtRemZimmermann(MutableBigInteger x, int limit, boolean isRecursion) {
1925+
private static MutableBigInteger[] sqrtRemZimmermann(MutableBigInteger x) {
19261926
// Normalize leading int
19271927
int shift = Integer.numberOfLeadingZeros(x.value[x.offset]) & ~1; // shift must be even
19281928
if (shift != 0) {
1929-
x = new MutableBigInteger(Arrays.copyOfRange(x.value, x.offset, limit));
1929+
x = new MutableBigInteger(Arrays.copyOfRange(x.value, x.offset, x.intLen));
19301930
x.primitiveLeftShift(shift);
19311931
}
19321932

1933-
if (limit == 2) { // Base case
1933+
MutableBigInteger sqrt, rem;
1934+
{
1935+
// Base case
19341936
long hi = x.value[x.offset] & LONG_MASK, lo = x.value[x.offset + 1] & LONG_MASK;
19351937
long s = (long) Math.sqrt(hi); // Math.sqrt is exact
19361938
long r = hi - s * s;
@@ -1946,85 +1948,69 @@ private static MutableBigInteger[] sqrtRemZimmermann(MutableBigInteger x, int li
19461948
s--;
19471949
}
19481950

1949-
// Denormalize
1950-
if (shift != 0) {
1951-
s >>= (shift >> 1);
1952-
r = (((hi << 32) | lo) >>> shift) - s * s;
1951+
sqrt = new MutableBigInteger((int) s);
1952+
rem = (r & LONG_MASK) == r ? new MutableBigInteger((int) r)
1953+
: new MutableBigInteger(new int[] { 1, (int) r });
1954+
}
1955+
1956+
// Iterative step
1957+
int limit;
1958+
for (limit = 4; limit >> 1 < x.intLen; limit <<= 1) {
1959+
final int blockLength = limit >> 2;
1960+
MutableBigInteger dividend = x.getBlockZimmermann(1, limit, blockLength);
1961+
dividend.addDisjoint(rem, blockLength);
1962+
MutableBigInteger twiceSqrt = new MutableBigInteger(sqrt);
1963+
twiceSqrt.leftShift(1);
1964+
MutableBigInteger q = new MutableBigInteger();
1965+
MutableBigInteger u = dividend.divide(twiceSqrt, q);
1966+
1967+
rem = x.getBlockZimmermann(0, limit, blockLength);
1968+
rem.addDisjoint(u, blockLength);
1969+
BigInteger qBig = q.toBigInteger();
1970+
MutableBigInteger qSqr = new MutableBigInteger(qBig.multiply(qBig).mag);
1971+
int rSign = rem.subtract(qSqr);
1972+
1973+
q.addShifted(sqrt, blockLength);
1974+
sqrt = q;
1975+
1976+
if (rSign == -1) {
1977+
twiceSqrt = new MutableBigInteger(sqrt);
1978+
twiceSqrt.leftShift(1);
1979+
1980+
rem.subtract(twiceSqrt);
1981+
rem.add(ONE);
1982+
sqrt.subtract(ONE);
19531983
}
1954-
1955-
return new MutableBigInteger[] { new MutableBigInteger((int) s),
1956-
(r & LONG_MASK) == r ? new MutableBigInteger((int) r)
1957-
: new MutableBigInteger(new int[] { 1, (int) r }) };
1958-
}
1959-
1960-
// Recursive case (limit >= 3)
1961-
1962-
// Normalize limit
1963-
if ((limit & 3) != 0) { // limit must be a multiple of 4
1964-
if (isRecursion) { // In recursive invocations, limit is even
1965-
int[] xVal = new int[limit + 2];
1966-
System.arraycopy(x.value, x.offset, xVal, 0, limit);
1967-
x = new MutableBigInteger(xVal);
1968-
limit += 2;
1969-
shift += 64;
1970-
} else {
1971-
int intsToAdd = 4 - (limit & 3);
1972-
int[] xVal = new int[limit + intsToAdd];
1973-
System.arraycopy(x.value, x.offset, xVal, 0, limit);
1974-
x = new MutableBigInteger(xVal);
1975-
limit += intsToAdd;
1976-
shift += intsToAdd << 5;
1977-
}
1978-
}
1979-
1980-
MutableBigInteger[] sr = sqrtRemZimmermann(x, limit >> 1, true); // Recursive invocation
1981-
1982-
final int blockLength = limit >> 2;
1983-
MutableBigInteger dividend = x.getBlockZimmermann(1, limit, blockLength);
1984-
dividend.addDisjoint(sr[1], blockLength);
1985-
MutableBigInteger twiceS = new MutableBigInteger(sr[0]);
1986-
twiceS.leftShift(1);
1987-
MutableBigInteger q = new MutableBigInteger();
1988-
MutableBigInteger u = dividend.divide(twiceS, q);
1989-
1990-
MutableBigInteger r = x.getBlockZimmermann(0, limit, blockLength);
1991-
r.addDisjoint(u, blockLength);
1992-
BigInteger qBig = q.toBigInteger();
1993-
MutableBigInteger qSqr = new MutableBigInteger(qBig.multiply(qBig).mag);
1994-
int rSign = r.subtract(qSqr);
1995-
1996-
MutableBigInteger s = q;
1997-
s.addShifted(sr[0], blockLength);
1998-
1999-
if (rSign == -1) {
2000-
twiceS = new MutableBigInteger(s);
2001-
twiceS.leftShift(1);
2002-
2003-
r.subtract(twiceS);
2004-
r.subtract(ONE);
2005-
s.subtract(ONE);
20061984
}
1985+
limit >>= 1;
20071986

20081987
// Denormalize
1988+
shift += (limit - x.intLen) << 5;
20091989
if (shift != 0) {
20101990
final int halfShift = shift >> 1;
2011-
if (!r.isZero()) {
2012-
BigInteger s0 = s.getBlockZimmermann(0, s.intLen, (halfShift + 31) >> 5).toBigInteger();
2013-
if ((halfShift & 31) != 0)
1991+
if (!rem.isZero()) {
1992+
BigInteger s0 = sqrt.getBlockZimmermann(0, sqrt.intLen, (halfShift + 31) >> 5).toBigInteger();
1993+
if (s0.mag.length != 0 && (halfShift & 31) != 0)
20141994
s0.mag[0] &= (1 << halfShift) - 1;
20151995

2016-
r.add(new MutableBigInteger(s0.multiply(s.toBigInteger()).shiftLeft(1).mag));
2017-
r.subtract(new MutableBigInteger(s0.multiply(s0).mag));
2018-
r.rightShift(shift);
1996+
rem.add(new MutableBigInteger(s0.multiply(sqrt.toBigInteger()).shiftLeft(1).mag));
1997+
rem.subtract(new MutableBigInteger(s0.multiply(s0).mag));
1998+
rem.rightShift(shift);
20191999
}
2020-
s.rightShift(halfShift);
2000+
sqrt.rightShift(halfShift);
20212001
}
2022-
return new MutableBigInteger[] { s, r };
2002+
return new MutableBigInteger[] { sqrt, rem };
20232003
}
20242004

20252005
private MutableBigInteger getBlockZimmermann(int blockIndex, int limit, int blockLength) {
2026-
int to = offset + limit - (blockIndex * blockLength);
2027-
return new MutableBigInteger(Arrays.copyOfRange(value, to - blockLength, to));
2006+
int from = offset + limit - (blockIndex + 1) * blockLength;
2007+
int intEnd = offset + intLen;
2008+
if (from >= intEnd)
2009+
return new MutableBigInteger();
2010+
2011+
int[] block = new int[blockLength];
2012+
System.arraycopy(value, from, block, 0, Math.min(intEnd - from, blockLength));
2013+
return new MutableBigInteger(block);
20282014
}
20292015

20302016
/**

0 commit comments

Comments
 (0)