On Mon, 29 Jul 2024 13:18:16 GMT, fabioromano1 <d...@openjdk.org> wrote:

>> I have implemented the Zimmermann's square root algorithm, available in 
>> works [here](https://inria.hal.science/inria-00072854/en/) and 
>> [here](https://www.researchgate.net/publication/220532560_A_proof_of_GMP_square_root).
>> 
>> The algorithm is proved to be asymptotically faster than the Newton's 
>> Method, even for small numbers. To get an idea of how much the Newton's 
>> Method is slow,  consult my article 
>> [here](https://arxiv.org/abs/2406.07751), in which I compare Newton's Method 
>> with a version of classical square root algorithm that I implemented. After 
>> implementing Zimmermann's algorithm, it turns out that it is faster than my 
>> algorithm even for small numbers.
>
> fabioromano1 has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   If the input is a square, then s0 == 0, so testing for non-zero remainder 
> is redundant

Just some quick reviews.
Expect a more definitive review in the next days.

src/java.base/share/classes/java/math/MutableBigInteger.java line 550:

> 548:      */
> 549:     void safeRightShift(int n) {
> 550:         if (n >= bitLength()) {

The commit message for this reads `More accurate condition for 
MBI.safeRightShift()`.
If the old version works, please switch back. But if this is a genuine bug, 
then it needs a separate bug issue and PR.

src/java.base/share/classes/java/math/MutableBigInteger.java line 1891:

> 1889:         int shift = Integer.numberOfLeadingZeros(x.value[x.offset]) & 
> ~1; // shift must be even
> 1890:         if ((x.intLen & 1) != 0)
> 1891:             shift += 32; // x.intLen must be even

Suggestion:

        int shift = (Integer.numberOfLeadingZeros(x.value[x.offset]) & ~1) + 
((x.intLen & 1) << 32);

src/java.base/share/classes/java/math/MutableBigInteger.java line 1922:

> 1920:     }
> 1921: 
> 1922:     private static long ulongSqrt(long x) {

Since there is a `unisgnedLongCompare` already, it would be better to name this 
`unsignedLongSqrt`.

src/java.base/share/classes/java/math/MutableBigInteger.java line 1946:

> 1944:      * @implNote The implementation is based on Zimmermann's works 
> available
> 1945:      * <a href="https://inria.hal.science/inria-00072854/en/";>  
> here</a> and
> 1946:      * <a 
> href="https://www.researchgate.net/publication/220532560_A_proof_of_GMP_square_root";>
>   here</a>

The following variant should be preferred, as it has a readable figure on p. 
21, whereas the same figure in the variant linked in the PR seems broken for 
some reason.
https://inria.hal.science/inria-00072113/document

src/java.base/share/classes/java/math/MutableBigInteger.java line 1948:

> 1946:      * <a 
> href="https://www.researchgate.net/publication/220532560_A_proof_of_GMP_square_root";>
>   here</a>
> 1947:      */
> 1948:     private MutableBigInteger[] sqrtRemZimmermann(int len, boolean 
> needRemainder) {

This should be called `sqrtRemKaratsuba()`. This is the name chosen by Paul 
Zimmermann himself.

Also, I wonder if it wouldn't be simpler for `len` to represent the `int` 
length of the square root rather than the `int` length of the argument. It 
would be more consistent with the Bertot, Magaud, Zimmermann paper and might 
slightly simplify some later computation. But I'm not sure.

src/java.base/share/classes/java/math/MutableBigInteger.java line 1968:

> 1966:         final int halfLen = len >> 1;
> 1967:         // Recursive invocation
> 1968:         MutableBigInteger[] sr = sqrtRemZimmermann((halfLen & 1) == 0 ? 
> halfLen : halfLen + 1, true);

Suggestion:

        final int halfLen = len >> 1;
        // Recursive invocation
        MutableBigInteger[] sr = sqrtRemZimmermann(halfLen + (halfLen & 1), 
true);

src/java.base/share/classes/java/math/MutableBigInteger.java line 2018:

> 2016:      * {@code this} number, ending at {@code blockIndex*blockLen} 
> (exclusive).
> 2017:      */
> 2018:     private MutableBigInteger getBlockZimmermann(int blockIndex, int 
> len, int blockLen) {

Should be named `getBlockKaratsuba()` or similar.

test/jdk/java/math/BigInteger/BigIntegerTest.java line 299:

> 297:         /* For every long value n in [0, 2^32) such that x == n * n,
> 298:          * n - 1 <= (long) Math.sqrt(x >= 0 ? x : x + 0x1p64) <= n
> 299:          * must be true.

Suggestion:

         * must be true.
         * This property is used to implement 
MutableBigInteger.unsignedLongSqrt().

test/micro/org/openjdk/bench/java/math/BigIntegerSquareRoot.java line 85:

> 83:             largeArray[i] = new BigInteger("" + (value / 1000));
> 84:             smallArray[i] = new BigInteger("" + hi);
> 85:         }

I think it would be more representative to have 5 arrays, from extra-small (XS) 
to extra-large (XL), with elements created by `new 
BigInteger(r.nextInt(maxLen), r)`, where `maxLen` is 64, 256, 1_024, 4_096, 
16_384, resp., and drop the logic with the string parsing.

-------------

PR Review: https://git.openjdk.org/jdk/pull/19710#pullrequestreview-2205484016
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695548820
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695549452
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695549943
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695550713
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695551231
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695558116
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695558368
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695558651
PR Review Comment: https://git.openjdk.org/jdk/pull/19710#discussion_r1695559743

Reply via email to