# HG changeset patch
# User bpb
# Date 1443818818 25200
# Fri Oct 02 13:46:58 2015 -0700
# Node ID 4cdb5bc5c1800cde6be9ec579bf4d8d2f8b4ae7c
# Parent 391f8994fe9eea94f5f8543a7ca13be02cf9aafd
8032027: Add BigInteger square root methods
Summary: Add sqrt() and sqrtAndReminder() using Newton iteration
Reviewed-by: XXX
diff --git a/src/java.base/share/classes/java/math/BigInteger.java b/src/java.base/share/classes/java/math/BigInteger.java
--- a/src/java.base/share/classes/java/math/BigInteger.java
+++ b/src/java.base/share/classes/java/math/BigInteger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2015, 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
@@ -2410,6 +2410,68 @@
}
/**
+ * Implementation of the integer square root.
+ *
+ * @return the integer square root of this.
+ * @throws ArithmeticException if {@code this} is negative
+ * @since 1.9
+ */
+ private BigInteger implSqrt() {
+ if (this.signum < 0) {
+ throw new ArithmeticException("Negative BigInteger");
+ } else if (this.signum == 0) { // this is zero
+ return BigInteger.ZERO;
+ } else if (this.mag.length == 1 &&
+ (this.mag[0] & LONG_MASK) < 4) { // result is unity
+ return BigInteger.ONE;
+ }
+
+ return new MutableBigInteger(this.mag).sqrt().toBigInteger();
+ }
+
+ /**
+ * Returns the integer square root of this BigInteger. The integer square
+ * root of the corresponding mathematical integer {@code n} is the largest
+ * mathematical integer {@code s} such that {@code s*s <= n}. It is equal
+ * to the value of {@code floor(sqrt(n))}, where {@code sqrt(n)} denotes the
+ * real square root of {@code n} treated as a real. Note that the integer
+ * square root will be less than the real square root if the latter is not
+ * representable as an integral value.
+ *
+ * @return the integer square root of {@code this}
+ * @throws ArithmeticException if {@code this} is negative. (The square
+ * root of a negative integer {@code val} is
+ * {@code (i * sqrt(-val))} where i is the
+ * imaginary unit and is equal to
+ * {@code sqrt(-1)}.)
+ * @since 1.9
+ */
+ public BigInteger sqrt() {
+ return implSqrt();
+ }
+
+ /**
+ * Returns an array of two BigIntegers containing the integer square root
+ * {@code s} of {@code this} and its remainder {@code this - s*s},
+ * respectively.
+ *
+ * @return an array of two BigIntegers with the integer square root at
+ * offset 0 and the remainder at offset 1
+ * @throws ArithmeticException if {@code this} is negative. (The square
+ * root of a negative integer {@code val} is
+ * {@code (i * sqrt(-val))} where i is the
+ * imaginary unit and is equal to
+ * {@code sqrt(-1)}.)
+ * @see #sqrt()
+ * @since 1.9
+ */
+ public BigInteger[] sqrtAndRemainder() {
+ BigInteger s = implSqrt();
+ BigInteger r = this.subtract(s.square());
+ return new BigInteger[] {s, r};
+ }
+
+ /**
* Returns a BigInteger whose value is the greatest common divisor of
* {@code abs(this)} and {@code abs(val)}. Returns 0 if
* {@code this == 0 && val == 0}.
diff --git a/src/java.base/share/classes/java/math/MutableBigInteger.java b/src/java.base/share/classes/java/math/MutableBigInteger.java
--- a/src/java.base/share/classes/java/math/MutableBigInteger.java
+++ b/src/java.base/share/classes/java/math/MutableBigInteger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2015, 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
@@ -1867,6 +1867,74 @@
}
/**
+ * Calculate the integer square root {@code floor(sqrt(this))} where
+ * {@code sqrt(.)} denotes the mathematical square root. The contents of
+ * {@code this} are not changed.
+ *
+ * @implNote The implementation is based on the material in
+ * Henry S. Warren, Jr., Hacker's Delight (2nd ed.) (Addison
+ * Wesley, 2013), 279-282.
+ *
+ * @throws ArithmeticException if the value returned by {@code bitLength()}
+ * overflows the range of {@code int}.
+ * @return the integer square root of {@code this}
+ * @since 1.9
+ */
+ MutableBigInteger sqrt() {
+ // Obtain number of bits needed to represent the number.
+ int bitLength = (int)this.bitLength();
+ if (bitLength != this.bitLength()) {
+ throw new ArithmeticException("bitLength() integer overflow");
+ }
+
+ // Initial value length is one more than floor(bitLength()/2).
+ int initBits = bitLength / 2 + 1;
+
+ // The bit length of intermediate values will be no more than 2 bits
+ // larger than the initial bit length, so size the values accordingly
+ // to minimize allocations. The number of ints in the initial guess is
+ // (initBits + 2 + 31)/32.
+ int intLength = (initBits + 33) / 32;
+
+ // Fill the lower initBits bits of the value array with ones. This
+ // should provide an initial estimate greater than or equal to the
+ // actual integer square root.
+ int[] val = new int[intLength];
+ int initInts = initBits / 32;
+ Arrays.fill(val, 0, initInts, -1);
+ int excessBits = initBits % 32;
+ if (excessBits != 0) {
+ val[initInts] =
+ (int)((0xffffffff & LONG_MASK) >>> (32 - excessBits));
+ }
+
+ // Create the initial estimate from the value array.
+ MutableBigInteger xk = new MutableBigInteger(val);
+
+ // Create intermediate and next value containers and the constant two.
+ MutableBigInteger tmp = new MutableBigInteger(new int[intLength]);
+ MutableBigInteger xk1 = new MutableBigInteger(new int[intLength]);
+ MutableBigInteger two = new MutableBigInteger(2);
+
+ do {
+ // xk1 = (xk + n/xk)/2
+ this.divide(xk, tmp, false);
+ tmp.add(xk);
+ tmp.divide(two, xk1, false);
+
+ if (xk1.compare(xk) >= 0) {
+ return xk;
+ }
+
+ // xk = xk1
+ xk.copyValue(xk1);
+
+ xk1.reset();
+ tmp.reset();
+ } while (true);
+ }
+
+ /**
* Calculate GCD of this and b. This and b are changed by the computation.
*/
MutableBigInteger hybridGCD(MutableBigInteger b) {
diff --git a/test/java/math/BigInteger/BigIntegerTest.java b/test/java/math/BigInteger/BigIntegerTest.java
--- a/test/java/math/BigInteger/BigIntegerTest.java
+++ b/test/java/math/BigInteger/BigIntegerTest.java
@@ -26,7 +26,7 @@
* @library /lib/testlibrary/
* @build jdk.testlibrary.*
* @run main BigIntegerTest
- * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 8074460 8078672
+ * @bug 4181191 4161971 4227146 4194389 4823171 4624738 4812225 4837946 4026465 8074460 8078672 8032027
* @summary tests methods in BigInteger (use -Dseed=X to set PRNG seed)
* @run main/timeout=400 BigIntegerTest
* @author madbot
@@ -38,8 +38,12 @@
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Random;
+import java.util.stream.DoubleStream;
+import java.util.stream.IntStream;
+import java.util.stream.LongStream;
import jdk.testlibrary.RandomFactory;
/**
@@ -243,6 +247,173 @@
report("square for " + order + " bits", failCount1);
}
+ private static void fail(String msg) {
+ System.err.println(msg);
+ failure = true;
+ }
+
+ private static void fail(String msg, Object expected, Object actual) {
+ System.err.println(msg + " - expected: " + expected + ", actual: " + actual);
+ failure = true;
+ }
+
+ private static void squareRootSmall() {
+ // A negative value should cause an exception.
+ BigInteger n = BigInteger.ONE.negate();
+ BigInteger s;
+ try {
+ s = n.sqrt();
+ // If sqrt() does not throw an exception that is a failure.
+ fail("sqrt() of negative number did not throw an exception");
+ } catch (ArithmeticException expected) {
+ // A negative value should cause an exception and is not a failure.
+ }
+
+ // A zero value should return BigInteger.ZERO.
+ if (!BigInteger.valueOf(0).sqrt().equals(BigInteger.ZERO)) {
+ fail("sqrt(0) != BigInteger.ZERO");
+ }
+
+ // 1 <= value < 4 should return BigInteger.ONE.
+ long[] smalls = new long[] {1, 2, 3};
+ for (long small : smalls) {
+ if (!BigInteger.valueOf(small).sqrt().equals(BigInteger.ONE)) {
+ fail("sqrt("+small+") != 1");
+ }
+ }
+ }
+
+ private static void squareRootInt() {
+ IntStream ints = random.ints(SIZE, 4, Integer.MAX_VALUE);
+ ints.forEach(i -> {
+ BigInteger n = BigInteger.valueOf(i);
+ BigInteger n2 = n.pow(2);
+
+ // square root of n^2 -> n
+ BigInteger actual = n2.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(int)", n, actual);
+ }
+
+ // square root of n^2 + 1 -> n
+ BigInteger n2up = n2.add(BigInteger.ONE);
+ actual = n2up.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(int)", n, actual);
+ }
+
+ // square root of (n + 1)^2 - 1 -> n
+ BigInteger up = n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE);
+ actual = up.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(int)", n, actual);
+ }
+ });
+ }
+
+ private static void squareRootLong() {
+ LongStream longs = random.longs(SIZE, (long)Integer.MAX_VALUE + 1L,
+ Long.MAX_VALUE);
+ longs.forEach(i -> {
+ BigInteger n = BigInteger.valueOf(i);
+ BigInteger n2 = n.pow(2);
+
+ // square root of n^2 -> n
+ BigInteger actual = n2.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(long)", n, actual);
+ }
+
+ // square root of n^2 + 1 -> n
+ BigInteger n2up = n2.add(BigInteger.ONE);
+ actual = n2up.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(long)", n, actual);
+ }
+
+ // square root of (n + 1)^2 - 1 -> n
+ BigInteger up = n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE);
+ actual = up.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(long)", n, actual);
+ }
+ });
+ }
+
+ private static void squareRootDouble() {
+ DoubleStream doubles = random.doubles(SIZE,
+ (double)Long.MAX_VALUE + 1.0, Math.sqrt(Double.MAX_VALUE));
+ doubles.forEach(i -> {
+ BigInteger n = BigDecimal.valueOf(i).toBigInteger();
+ BigInteger n2 = n.pow(2);
+
+ // square root of n^2 -> n
+ BigInteger actual = n2.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(double)", n, actual);
+ }
+
+ // square root of n^2 + 1 -> n
+ BigInteger n2up = n2.add(BigInteger.ONE);
+ actual = n2up.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(double)", n, actual);
+ }
+
+ // square root of (n + 1)^2 - 1 -> n
+ BigInteger up = n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE);
+ actual = up.sqrt();
+ if (actual.compareTo(n) != 0) {
+ fail("sqrt(double)", n, actual);
+ }
+ });
+ }
+
+ public static void squareRoot() {
+ squareRootSmall();
+ squareRootInt();
+ squareRootLong();
+ squareRootDouble();
+ }
+
+ public static void squareRootAndRemainder() {
+ IntStream bits = random.ints(SIZE, 3, Short.MAX_VALUE);
+ bits.forEach(i -> {
+ BigInteger n = new BigInteger(i, random);
+ BigInteger n2 = n.pow(2);
+
+ // square root of n^2 -> n
+ BigInteger[] actual = n2.sqrtAndRemainder();
+ if (actual[0].compareTo(n) != 0) {
+ fail("sqrtAndRemainder()[0]", n, actual[0]);
+ }
+ if (actual[1].compareTo(BigInteger.ZERO) != 0) {
+ fail("sqrtAndRemainder()[1]", BigInteger.ZERO, actual[1]);
+ }
+
+ // square root of n^2 + 1 -> n
+ BigInteger n2up = n2.add(BigInteger.ONE);
+ actual = n2up.sqrtAndRemainder();
+ if (actual[0].compareTo(n) != 0) {
+ fail("sqrtAndRemainder()[0]", n, actual[0]);
+ }
+ if (actual[1].compareTo(BigInteger.ONE) != 0) {
+ fail("sqrtAndRemainder()[1]", BigInteger.ONE, actual[1]);
+ }
+
+ // square root of (n + 1)^2 - 1 -> n
+ BigInteger up = n.add(BigInteger.ONE).pow(2).subtract(BigInteger.ONE);
+ actual = up.sqrtAndRemainder();
+ if (actual[0].compareTo(n) != 0) {
+ fail("sqrt(int)", n, actual[0]);
+ }
+ BigInteger r = up.subtract(n2);
+ if (actual[1].compareTo(r) != 0) {
+ fail("sqrtAndRemainder()[1]", r, actual[1]);
+ }
+ });
+ }
+
public static void arithmetic(int order) {
int failCount = 0;
@@ -1101,6 +1272,9 @@
square(ORDER_KARATSUBA_SQUARE);
square(ORDER_TOOM_COOK_SQUARE);
+ squareRoot();
+ squareRootAndRemainder();
+
bitCount();
bitLength();
bitOps(order1);