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);
|
||||
}
|
||||
|
||||
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
|
||||
* 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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* 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