Skip to content

Commit 4ec7a5e

Browse files
committed
Implemented Zimmermann's square root algorithm
1 parent 78cc0f9 commit 4ec7a5e

File tree

2 files changed

+151
-9
lines changed

2 files changed

+151
-9
lines changed

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2719,11 +2719,7 @@ public BigInteger pow(int exponent) {
27192719
* @since 9
27202720
*/
27212721
public BigInteger sqrt() {
2722-
if (this.signum < 0) {
2723-
throw new ArithmeticException("Negative BigInteger");
2724-
}
2725-
2726-
return new MutableBigInteger(this.mag).sqrt().toBigInteger();
2722+
return sqrtAndRemainder()[0];
27272723
}
27282724

27292725
/**
@@ -2742,10 +2738,11 @@ public BigInteger sqrt() {
27422738
* @since 9
27432739
*/
27442740
public BigInteger[] sqrtAndRemainder() {
2745-
BigInteger s = sqrt();
2746-
BigInteger r = this.subtract(s.square());
2747-
assert r.compareTo(BigInteger.ZERO) >= 0;
2748-
return new BigInteger[] {s, r};
2741+
if (this.signum < 0)
2742+
throw new ArithmeticException("Negative BigInteger");
2743+
2744+
MutableBigInteger[] sqrtRem = new MutableBigInteger(this.mag).sqrtRem();
2745+
return new BigInteger[] { sqrtRem[0].toBigInteger(), sqrtRem[1].toBigInteger() };
27492746
}
27502747

27512748
/**

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

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,151 @@ MutableBigInteger sqrt() {
18821882
}
18831883
}
18841884

1885+
/**
1886+
* Calculate the integer square root {@code floor(sqrt(this))} and the remainder,
1887+
* where {@code sqrt(.)} denotes the mathematical square root.
1888+
* The contents of {@code this} are <b>not</b> changed.
1889+
* The value of {@code this} is assumed to be non-negative.
1890+
* @throws ArithmeticException if the value returned by {@code bitLength()}
1891+
* overflows the range of {@code int}.
1892+
* @return the integer square root of {@code this}
1893+
*/
1894+
MutableBigInteger[] sqrtRem() {
1895+
// Special cases.
1896+
if (this.isZero()) {
1897+
return new MutableBigInteger[] { this, this };
1898+
} else if (this.intLen == 1) {
1899+
MutableBigInteger r = new MutableBigInteger(this);
1900+
1901+
final long val = this.value[offset] & LONG_MASK;
1902+
if (val < 4) { // result is unity
1903+
r.subtract(ONE);
1904+
return new MutableBigInteger[] { ONE, r };
1905+
}
1906+
1907+
int s = (int) Math.sqrt(val); // Math.sqrt is exact
1908+
r.subtract(new MutableBigInteger(s * s));
1909+
return new MutableBigInteger[] { new MutableBigInteger(s), r };
1910+
}
1911+
1912+
long bitLength = this.bitLength();
1913+
if (bitLength != (int) bitLength)
1914+
throw new ArithmeticException("bitLength() integer overflow");
1915+
1916+
return sqrtRemZimmermann(this, this.intLen, false);
1917+
}
1918+
1919+
/**
1920+
* Assume {@code 2 <= limit <= x.intLen}
1921+
* @implNote The implementation is based on Zimmermann's works available
1922+
* <a href="https://inria.hal.science/inria-00072854/en/"> here</a> and
1923+
* <a href="https://www.researchgate.net/publication/220532560_A_proof_of_GMP_square_root"> here</a>
1924+
*/
1925+
private static MutableBigInteger[] sqrtRemZimmermann(MutableBigInteger x, int limit, boolean isRecursion) {
1926+
// Normalize leading int
1927+
int shift = Integer.numberOfLeadingZeros(x.value[x.offset]) & ~1; // shift must be even
1928+
if (shift != 0) {
1929+
x = new MutableBigInteger(Arrays.copyOfRange(x.value, x.offset, limit));
1930+
x.primitiveLeftShift(shift);
1931+
}
1932+
1933+
if (limit == 2) { // Base case
1934+
long hi = x.value[x.offset] & LONG_MASK, lo = x.value[x.offset + 1] & LONG_MASK;
1935+
long s = (long) Math.sqrt(hi); // Math.sqrt is exact
1936+
long r = hi - s * s;
1937+
1938+
final long dividend = (r << 16) | (lo >> 16), divisor = s << 1;
1939+
final long q = dividend / divisor;
1940+
final long u = dividend % divisor;
1941+
s = (s << 16) + q;
1942+
r = ((u << 16) | (lo & 0xffffL)) - q * q;
1943+
1944+
if (r < 0) {
1945+
r += (s << 1) - 1;
1946+
s--;
1947+
}
1948+
1949+
// Denormalize
1950+
if (shift != 0) {
1951+
s >>= (shift >> 1);
1952+
r = (((hi << 32) | lo) >>> shift) - s * s;
1953+
}
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);
2006+
}
2007+
2008+
// Denormalize
2009+
if (shift != 0) {
2010+
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)
2014+
s0.mag[0] &= (1 << halfShift) - 1;
2015+
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);
2019+
}
2020+
s.rightShift(halfShift);
2021+
}
2022+
return new MutableBigInteger[] { s, r };
2023+
}
2024+
2025+
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));
2028+
}
2029+
18852030
/**
18862031
* Calculate GCD of this and b. This and b are changed by the computation.
18872032
*/

0 commit comments

Comments
 (0)