mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: extend BigInteger implementation with xValueExact() and sqrt()
This commit is contained in:
parent
4e076a65ee
commit
83e3306071
|
@ -836,6 +836,20 @@ public class TBigInteger extends Number implements Comparable<TBigInteger>, Seri
|
||||||
return TLogical.andNot(this, val);
|
return TLogical.andNot(this, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte byteValueExact() {
|
||||||
|
if (numberLength > 1 || bitLength() > 7) {
|
||||||
|
throw new ArithmeticException("BigInteger out of byte range");
|
||||||
|
}
|
||||||
|
return byteValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public short shortValueExact() {
|
||||||
|
if (numberLength > 1 || bitLength() > 15) {
|
||||||
|
throw new ArithmeticException("BigInteger out of short range");
|
||||||
|
}
|
||||||
|
return shortValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this {@code BigInteger} as an int value. If {@code this} is too
|
* Returns this {@code BigInteger} as an int value. If {@code this} is too
|
||||||
* big to be represented as an int, then {@code this} % 2^32 is returned.
|
* big to be represented as an int, then {@code this} % 2^32 is returned.
|
||||||
|
@ -847,6 +861,21 @@ public class TBigInteger extends Number implements Comparable<TBigInteger>, Seri
|
||||||
return sign * digits[0];
|
return sign * digits[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this {@code BigInter} as an int value.
|
||||||
|
*
|
||||||
|
* @return this {@code BigInteger} as an int value.
|
||||||
|
* @see #intValue
|
||||||
|
* @throws ArithmeticException
|
||||||
|
* if {@code this} is too big to be represented as an int.
|
||||||
|
*/
|
||||||
|
public int intValueExact() {
|
||||||
|
if (numberLength > 1 || bitLength() > 31) {
|
||||||
|
throw new ArithmeticException("BigInteger out of int range");
|
||||||
|
}
|
||||||
|
return intValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this {@code BigInteger} as an long value. If {@code this} is too
|
* Returns this {@code BigInteger} as an long value. If {@code this} is too
|
||||||
* big to be represented as an long, then {@code this} % 2^64 is returned.
|
* big to be represented as an long, then {@code this} % 2^64 is returned.
|
||||||
|
@ -860,6 +889,21 @@ public class TBigInteger extends Number implements Comparable<TBigInteger>, Seri
|
||||||
return sign * value;
|
return sign * value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this {@code BigInter} as an long value.
|
||||||
|
*
|
||||||
|
* @return this {@code BigInteger} as a long value.
|
||||||
|
* @throws ArithmeticException
|
||||||
|
* if {@code this} is too big to be represented as a long.
|
||||||
|
* @see #longValue
|
||||||
|
*/
|
||||||
|
public long longValueExact() {
|
||||||
|
if (numberLength > 2 || bitLength() > 63) {
|
||||||
|
throw new ArithmeticException("BigInteger out of long range");
|
||||||
|
}
|
||||||
|
return longValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns this {@code BigInteger} as an float value. If {@code this} is too
|
* Returns this {@code BigInteger} as an float value. If {@code this} is too
|
||||||
* big to be represented as an float, then {@code Float.POSITIVE_INFINITY}
|
* big to be represented as an float, then {@code Float.POSITIVE_INFINITY}
|
||||||
|
@ -1102,6 +1146,76 @@ public class TBigInteger extends Number implements Comparable<TBigInteger>, Seri
|
||||||
return TMultiplication.pow(this, exp);
|
return TMultiplication.pow(this, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@code BigInteger} whose value is the biggest integer
|
||||||
|
* {@code n} such that {@code n * n <= this}.
|
||||||
|
*
|
||||||
|
* @implNote This implementation follows the ideas in Henry S. Warren, Jr.,
|
||||||
|
* Hacker's Delight (2nd ed.) (Addison Wesley, 2013), 279-282.
|
||||||
|
*
|
||||||
|
* @return {@code floor(sqrt(this))}
|
||||||
|
* @throws ArithmeticException if {@code this} is negative.
|
||||||
|
*/
|
||||||
|
public TBigInteger sqrt() {
|
||||||
|
if (sign < 0) {
|
||||||
|
throw new ArithmeticException("Negative BigInteger");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trivial cases
|
||||||
|
if (equals(ZERO)) {
|
||||||
|
return ZERO;
|
||||||
|
} else if (compareTo(valueOf(4)) < 0) {
|
||||||
|
return ONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigInteger fits into long, so do calculation directly
|
||||||
|
if (bitLength() < 64) {
|
||||||
|
// Estimate using existing sqrt implementation for double
|
||||||
|
long val = longValueExact();
|
||||||
|
long candidate = (long) Math.floor(Math.sqrt(val));
|
||||||
|
|
||||||
|
// Improve estimate using Newton's method
|
||||||
|
do {
|
||||||
|
long next = (candidate + val / candidate) >> 1;
|
||||||
|
if (next >= candidate) {
|
||||||
|
// found convergence candidate if stopped to decrease
|
||||||
|
return valueOf(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate = next;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift BigInteger into long range to use existing sqrt implementation
|
||||||
|
// and then shift back into the initial range for a rough estimate
|
||||||
|
|
||||||
|
long shiftCount = bitLength() - 63;
|
||||||
|
if (shiftCount % 2 == 1) {
|
||||||
|
shiftCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((shiftCount & 0xFFFFFFFF00000000L) > 0) {
|
||||||
|
throw new ArithmeticException("integer overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
double shiftedVal = shiftRight((int) shiftCount).doubleValue();
|
||||||
|
TBigInteger candidate = valueOf((long) Math.ceil(Math.sqrt(shiftedVal)));
|
||||||
|
|
||||||
|
candidate = candidate.shiftLeft((int) shiftCount >> 1);
|
||||||
|
|
||||||
|
// Improve estimate using Newton's method
|
||||||
|
do {
|
||||||
|
// next = (candidate + this/candidate) >> 1;
|
||||||
|
TBigInteger next = candidate.add(this.divide(candidate)).shiftRight(1);
|
||||||
|
if (next.compareTo(candidate) >= 0) {
|
||||||
|
// found convergence candidate if stopped to decrease
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
candidate = next;
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a {@code BigInteger} array which contains {@code this / divisor}
|
* Returns a {@code BigInteger} array which contains {@code this / divisor}
|
||||||
* at index 0 and {@code this % divisor} at index 1.
|
* at index 0 and {@code this % divisor} at index 1.
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Bernd Busse.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.teavm.classlib.java.math;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.teavm.junit.SkipPlatform;
|
||||||
|
import org.teavm.junit.TeaVMTestRunner;
|
||||||
|
import org.teavm.junit.TestPlatform;
|
||||||
|
|
||||||
|
@RunWith(TeaVMTestRunner.class)
|
||||||
|
@SkipPlatform(TestPlatform.WASI)
|
||||||
|
public class BigIntegerSquareRootTest {
|
||||||
|
/**
|
||||||
|
* sqrt: negative value
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSqrtException() {
|
||||||
|
BigInteger aNumber = new BigInteger("-8");
|
||||||
|
try {
|
||||||
|
aNumber.sqrt();
|
||||||
|
fail("ArithmeticException has not been caught");
|
||||||
|
} catch (ArithmeticException e) {
|
||||||
|
assertEquals("Improper exception message", "Negative BigInteger", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sqrt: special cases
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSpecialCases() {
|
||||||
|
BigInteger aNumber = new BigInteger("3");
|
||||||
|
|
||||||
|
assertEquals(BigInteger.ZERO, BigInteger.ZERO.sqrt());
|
||||||
|
assertEquals(BigInteger.ONE, BigInteger.ONE.sqrt());
|
||||||
|
assertEquals(BigInteger.ONE, aNumber.sqrt());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sqrt of small number
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSmallNumbers() {
|
||||||
|
byte[] aBytes = {39, -128, 127};
|
||||||
|
int aSign = 1;
|
||||||
|
byte[] rBytes = {6, 72};
|
||||||
|
BigInteger aNumber = new BigInteger(aSign, aBytes);
|
||||||
|
BigInteger result = aNumber.sqrt();
|
||||||
|
byte[] resBytes = new byte[rBytes.length];
|
||||||
|
resBytes = result.toByteArray();
|
||||||
|
for (int i = 0; i < resBytes.length; i++) {
|
||||||
|
assertTrue(resBytes[i] == rBytes[i]);
|
||||||
|
}
|
||||||
|
assertEquals("incorrect sign", 1, result.signum());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sqrt of large number
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBigNumbers() {
|
||||||
|
byte[] aBytes = {1, 100, 56, 7, 98, -1, 39, -128, 127, 5, 6, 7, 8, 9};
|
||||||
|
int aSign = 1;
|
||||||
|
byte[] rBytes = {18, -33, -82, -48, -58, 93, 37};
|
||||||
|
BigInteger aNumber = new BigInteger(aSign, aBytes);
|
||||||
|
BigInteger result = aNumber.sqrt();
|
||||||
|
byte[] resBytes = new byte[rBytes.length];
|
||||||
|
resBytes = result.toByteArray();
|
||||||
|
for (int i = 0; i < resBytes.length; i++) {
|
||||||
|
assertTrue(resBytes[i] == rBytes[i]);
|
||||||
|
}
|
||||||
|
assertEquals("incorrect sign", 1, result.signum());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user