Skip to content

Commit bd23d73

Browse files
committed
Add fromRadixString and fromRadixSubstring methods to BigInteger
1 parent f47e0a6 commit bd23d73

File tree

3 files changed

+374
-69
lines changed

3 files changed

+374
-69
lines changed

src/main/java/com/upokecenter/util/BigInteger.java

Lines changed: 193 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
* checks if each side of the operator is the same instance).</p>
2121
*/
2222
public final class BigInteger implements Comparable<BigInteger> {
23-
2423
private static int CountWords(short[] array, int n) {
2524
while (n != 0 && array[n - 1] == 0) {
2625
--n;
@@ -2108,8 +2107,8 @@ private static short Divide32By16(
21082107
}
21092108
}
21102109
}
2111-
return returnRemainder ? ((short)(((int)dividendHigh) &
2112-
0xffff)) : ((short)(((int)dividendLow) & 0xffff));
2110+
return returnRemainder ? ((short)(((int)dividendHigh) &
2111+
0xffff)) : ((short)(((int)dividendLow) & 0xffff));
21132112
}
21142113

21152114
private static short DivideUnsigned(int x, short y) {
@@ -2232,8 +2231,8 @@ private static void AtomicMultiplyOpt(
22322231
int a0b0high = (valueA0B0 >> 16) & 0xffff;
22332232
int valueA1B1 = valueA1 * valueB1;
22342233
int tempInt;
2235-
tempInt = a0b0high + (((int)valueA0B0) & 0xffff) + (((int)d) &
2236-
0xffff) + (((int)valueA1B1) & 0xffff);
2234+
tempInt = a0b0high + (((int)valueA0B0) & 0xffff) + (((int)d) &
2235+
0xffff) + (((int)valueA1B1) & 0xffff);
22372236
c[csi + 1] = (short)(((int)tempInt) & 0xffff);
22382237

22392238
tempInt = valueA1B1 + (((int)(tempInt >> 16)) & 0xffff) +
@@ -2262,8 +2261,8 @@ private static void AtomicMultiplyOpt(
22622261

22632262
int valueA1B1 = valueA1 * valueB1;
22642263
int tempInt;
2265-
tempInt = a0b0high + (((int)valueA0B0) & 0xffff) + (((int)d) &
2266-
0xffff) + (((int)valueA1B1) & 0xffff);
2264+
tempInt = a0b0high + (((int)valueA0B0) & 0xffff) + (((int)d) &
2265+
0xffff) + (((int)valueA1B1) & 0xffff);
22672266
c[csi + 1] = (short)(((int)tempInt) & 0xffff);
22682267

22692268
tempInt = valueA1B1 + (((int)(tempInt >> 16)) & 0xffff) +
@@ -3668,13 +3667,31 @@ public static BigInteger fromString(String str) {
36683667
if (str == null) {
36693668
throw new NullPointerException("str");
36703669
}
3671-
return fromSubstring(str, 0, str.length());
3670+
return fromRadixSubstring(str, 10, 0, str.length());
36723671
}
36733672

3674-
private static final int MaxSafeInt = 214748363;
3673+
/**
3674+
* Converts a string to an arbitrary-precision integer in a given radix.
3675+
* @param str A string containing only digits, except that it may start with a
3676+
* minus sign.
3677+
* @param radix A base from 2 to 36. The possible digits start from 0 to 9,
3678+
* then from A to Z in base 36, and the possible digits start from 0 to
3679+
* 9, then from A to F in base 16.
3680+
* @return A BigInteger object with the same value as given in the string.
3681+
* @throws NullPointerException The parameter {@code str} is null.
3682+
* @throws NumberFormatException The parameter {@code str} is in an invalid format.
3683+
*/
3684+
public static BigInteger fromRadixString(String str, int radix) {
3685+
if (str == null) {
3686+
throw new NullPointerException("str");
3687+
}
3688+
return fromRadixSubstring(str, radix, 0, str.length());
3689+
}
36753690

36763691
/**
3677-
* Converts a portion of a string to an arbitrary-precision integer.
3692+
* Converts a portion of a string to an arbitrary-precision integer. The string
3693+
* portion can begin with a minus sign ('-') to indicate that it's
3694+
* negative.
36783695
* @param str A string object.
36793696
* @param index The index of the string that starts the string portion.
36803697
* @param endIndex The index of the string that ends the string portion. The
@@ -3694,6 +3711,65 @@ public static BigInteger fromSubstring(
36943711
if (str == null) {
36953712
throw new NullPointerException("str");
36963713
}
3714+
return fromRadixSubstring(str, 10, index, endIndex);
3715+
}
3716+
3717+
private static int[] valueMaxSafeInts = { 1073741823, 715827881,
3718+
536870911, 429496728, 357913940, 306783377, 268435455, 238609293,
3719+
214748363, 195225785, 178956969, 165191048, 153391688, 143165575,
3720+
134217727, 126322566, 119304646, 113025454, 107374181, 102261125,
3721+
97612892, 93368853, 89478484, 85899344, 82595523, 79536430, 76695843,
3722+
74051159, 71582787, 69273665, 67108863, 65075261, 63161282, 61356674,
3723+
59652322 };
3724+
3725+
private static int[] valueCharToDigit = { 36, 36, 36, 36, 36, 36, 36,
3726+
36,
3727+
36, 36, 36, 36, 36, 36, 36, 36,
3728+
36, 36, 36, 36, 36, 36, 36, 36,
3729+
36, 36, 36, 36, 36, 36, 36, 36,
3730+
36, 36, 36, 36, 36, 36, 36, 36,
3731+
36, 36, 36, 36, 36, 36, 36, 36,
3732+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 36, 36, 36, 36, 36, 36,
3733+
36, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
3734+
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 36,
3735+
36, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
3736+
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36, 36, 36 };
3737+
3738+
/**
3739+
* Converts a portion of a string to an arbitrary-precision integer in a given
3740+
* radix. The string portion can begin with a minus sign ('-') to
3741+
* indicate that it's negative.
3742+
* @param str A string object.
3743+
* @param radix A base from 2 to 36. The possible digits start from 0 to 9,
3744+
* then from A to Z in base 36, and the possible digits start from 0 to
3745+
* 9, then from A to F in base 16.
3746+
* @param index The index of the string that starts the string portion.
3747+
* @param endIndex The index of the string that ends the string portion. The
3748+
* length will be index + endIndex - 1.
3749+
* @return A BigInteger object with the same value as given in the string
3750+
* portion.
3751+
* @throws NullPointerException The parameter {@code str} is null.
3752+
* @throws IllegalArgumentException The parameter {@code index} is less than 0, {@code
3753+
* endIndex} is less than 0, or either is greater than the string's
3754+
* length, or {@code endIndex} is less than {@code index} .
3755+
* @throws NumberFormatException The string portion is empty or in an invalid format.
3756+
*/
3757+
public static BigInteger fromRadixSubstring(
3758+
String str,
3759+
int radix,
3760+
int index,
3761+
int endIndex) {
3762+
if (str == null) {
3763+
throw new NullPointerException("str");
3764+
}
3765+
if (radix < 2) {
3766+
throw new IllegalArgumentException("radix (" + radix +
3767+
") is less than 2");
3768+
}
3769+
if (radix > 36) {
3770+
throw new IllegalArgumentException("radix (" + radix +
3771+
") is more than 36");
3772+
}
36973773
if (index < 0) {
36983774
throw new IllegalArgumentException("index (" + index + ") is less than " +
36993775
"0");
@@ -3703,8 +3779,8 @@ public static BigInteger fromSubstring(
37033779
str.length());
37043780
}
37053781
if (endIndex < 0) {
3706-
throw new IllegalArgumentException("endIndex (" + endIndex +
3707-
") is less than " + "0");
3782+
throw new IllegalArgumentException("endIndex (" + endIndex +
3783+
") is less than " + "0");
37083784
}
37093785
if (endIndex > str.length()) {
37103786
throw new IllegalArgumentException("endIndex (" + endIndex +
@@ -3718,62 +3794,121 @@ public static BigInteger fromSubstring(
37183794
throw new NumberFormatException("No digits");
37193795
}
37203796
boolean negative = false;
3721-
if (str.charAt(0) == '-') {
3797+
if (str.charAt(index) == '-') {
37223798
++index;
3799+
if (index == endIndex) {
3800+
throw new NumberFormatException("No digits");
3801+
}
37233802
negative = true;
37243803
}
3725-
short[] bigint = new short[4];
3726-
boolean haveDigits = false;
3727-
boolean haveSmallInt = true;
3728-
int smallInt = 0;
3729-
for (int i = index; i < endIndex; ++i) {
3730-
char c = str.charAt(i);
3731-
if (c < '0' || c > '9') {
3732-
throw new NumberFormatException("Illegal character found");
3804+
// Skip leading zeros
3805+
for (; index < endIndex; ++index) {
3806+
char c = str.charAt(index);
3807+
if (c != 0x30) {
3808+
break;
37333809
}
3734-
haveDigits = true;
3735-
int digit = (int)(c - '0');
3736-
if (haveSmallInt && smallInt < MaxSafeInt) {
3737-
smallInt *= 10;
3738-
smallInt += digit;
3739-
} else {
3740-
if (haveSmallInt) {
3741-
bigint[0] = ((short)(smallInt & 0xffff));
3742-
bigint[1] = ((short)((smallInt >> 16) & 0xffff));
3743-
haveSmallInt = false;
3744-
}
3745-
// Multiply by 10
3746-
short carry = 0;
3747-
int n = bigint.length;
3748-
for (int j = 0; j < n; ++j) {
3749-
int p;
3750-
{
3751-
p = (((int)bigint[j]) & 0xffff) * 10;
3752-
p += ((int)carry) & 0xffff;
3753-
bigint[j] = (short)p;
3754-
carry = (short)(p >> 16);
3810+
}
3811+
int effectiveLength = endIndex - index;
3812+
if (effectiveLength == 0) {
3813+
return BigInteger.ZERO;
3814+
}
3815+
short[] bigint;
3816+
if (radix == 16) {
3817+
// Special case for hexadecimal radix
3818+
int leftover = effectiveLength & 3;
3819+
int wordCount = effectiveLength >> 2;
3820+
if (leftover != 0) {
3821+
++wordCount;
3822+
}
3823+
bigint = new short[wordCount];
3824+
int currentDigit = wordCount - 1;
3825+
// Get most significant digits if effective
3826+
// length is not divisible by 4
3827+
if (leftover != 0) {
3828+
int extraWord = 0;
3829+
for (int i = 0; i < leftover; ++i) {
3830+
extraWord <<= 4;
3831+
char c = str.charAt(index + i);
3832+
int digit = (c >= 0x80) ? 36 : valueCharToDigit[(int)c];
3833+
if (digit >= 16) {
3834+
throw new NumberFormatException("Illegal character found");
37553835
}
3836+
extraWord |= digit;
37563837
}
3757-
if (carry != 0) {
3758-
bigint = GrowForCarry(bigint, carry);
3838+
bigint[currentDigit] = ((short)extraWord);
3839+
--currentDigit;
3840+
index += leftover;
3841+
}
3842+
3843+
while (index < endIndex) {
3844+
char c = str.charAt(index + 3);
3845+
int digit = (c >= 0x80) ? 36 : valueCharToDigit[(int)c];
3846+
int word = digit;
3847+
c = str.charAt(index + 2);
3848+
digit = (c >= 0x80) ? 36 : valueCharToDigit[(int)c];
3849+
word |= digit << 4;
3850+
c = str.charAt(index + 1);
3851+
digit = (c >= 0x80) ? 36 : valueCharToDigit[(int)c];
3852+
word |= digit << 8;
3853+
c = str.charAt(index);
3854+
digit = (c >= 0x80) ? 36 : valueCharToDigit[(int)c];
3855+
word |= digit << 12;
3856+
index += 4;
3857+
bigint[currentDigit] = ((short)word);
3858+
--currentDigit;
3859+
}
3860+
} else {
3861+
bigint = new short[4];
3862+
boolean haveSmallInt = true;
3863+
int maxSafeInt = valueMaxSafeInts[radix - 2];
3864+
int maxShortPlusOneMinusRadix = 65536 - radix;
3865+
int smallInt = 0;
3866+
for (int i = index; i < endIndex; ++i) {
3867+
char c = str.charAt(i);
3868+
int digit = (c >= 0x80) ? 36 : valueCharToDigit[(int)c];
3869+
if (digit >= radix) {
3870+
throw new NumberFormatException("Illegal character found");
37593871
}
3760-
// Add the parsed digit
3761-
if (digit != 0) {
3762-
int d = bigint[0] & 0xffff;
3763-
if (d <= 65526) {
3764-
bigint[0] = ((short)(d + digit));
3765-
} else if (Increment(bigint, 0, bigint.length, (short)digit) != 0) {
3766-
bigint = GrowForCarry(bigint, (short)1);
3872+
if (haveSmallInt && smallInt < maxSafeInt) {
3873+
smallInt *= radix;
3874+
smallInt += digit;
3875+
} else {
3876+
if (haveSmallInt) {
3877+
bigint[0] = ((short)(smallInt & 0xffff));
3878+
bigint[1] = ((short)((smallInt >> 16) & 0xffff));
3879+
haveSmallInt = false;
3880+
}
3881+
// Multiply by the radix
3882+
short carry = 0;
3883+
int n = bigint.length;
3884+
for (int j = 0; j < n; ++j) {
3885+
int p;
3886+
{
3887+
p = (((int)bigint[j]) & 0xffff) * radix;
3888+
p += ((int)carry) & 0xffff;
3889+
bigint[j] = (short)p;
3890+
carry = (short)(p >> 16);
3891+
}
3892+
}
3893+
if (carry != 0) {
3894+
bigint = GrowForCarry(bigint, carry);
3895+
}
3896+
// Add the parsed digit
3897+
if (digit != 0) {
3898+
int d = bigint[0] & 0xffff;
3899+
if (d <= maxShortPlusOneMinusRadix) {
3900+
bigint[0] = ((short)(d + digit));
3901+
} else if (Increment(bigint, 0, bigint.length, (short)digit) !=
3902+
0) {
3903+
bigint = GrowForCarry(bigint, (short)1);
3904+
}
37673905
}
37683906
}
37693907
}
3770-
}
3771-
if (!haveDigits) {
3772-
throw new NumberFormatException("No digits");
3773-
}
3774-
if (haveSmallInt) {
3775-
bigint[0] = ((short)(smallInt & 0xffff));
3776-
bigint[1] = ((short)((smallInt >> 16) & 0xffff));
3908+
if (haveSmallInt) {
3909+
bigint[0] = ((short)(smallInt & 0xffff));
3910+
bigint[1] = ((short)((smallInt >> 16) & 0xffff));
3911+
}
37773912
}
37783913
int count = CountWords(bigint, bigint.length);
37793914
return (count == 0) ? BigInteger.ZERO : new BigInteger(

0 commit comments

Comments
 (0)