-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathFastIntegerMath.java
178 lines (162 loc) · 6.58 KB
/
FastIntegerMath.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
* @(#)FastIntegerMath.java
* Copyright © 2023 Werner Randelshofer, Switzerland. MIT License.
*/
package ch.randelshofer.fastdoubleparser;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
class FastIntegerMath {
public static final BigInteger FIVE = BigInteger.valueOf(5);
final static BigInteger TEN_POW_16 = BigInteger.valueOf(10_000_000_000_000_000L);
final static BigInteger FIVE_POW_16 = BigInteger.valueOf(152_587_890_625L);
private final static BigInteger[] SMALL_POWERS_OF_TEN = new BigInteger[]{
BigInteger.ONE,
BigInteger.TEN,
BigInteger.valueOf(100L),
BigInteger.valueOf(1_000L),
BigInteger.valueOf(10_000L),
BigInteger.valueOf(100_000L),
BigInteger.valueOf(1_000_000L),
BigInteger.valueOf(10_000_000L),
BigInteger.valueOf(100_000_000L),
BigInteger.valueOf(1_000_000_000L),
BigInteger.valueOf(10_000_000_000L),
BigInteger.valueOf(100_000_000_000L),
BigInteger.valueOf(1_000_000_000_000L),
BigInteger.valueOf(10_000_000_000_000L),
BigInteger.valueOf(100_000_000_000_000L),
BigInteger.valueOf(1_000_000_000_000_000L)
};
/**
* Don't let anyone instantiate this class.
*/
private FastIntegerMath() {
}
/**
* Computes the n-th power of ten.
*
* @param powersOfTen A map with pre-computed powers of ten
* @param n the power
* @return the computed power of ten
*/
static BigInteger computePowerOfTen(NavigableMap<Integer, BigInteger> powersOfTen, int n) {
if (n < SMALL_POWERS_OF_TEN.length) {
return SMALL_POWERS_OF_TEN[n];
}
if (powersOfTen != null) {
Map.Entry<Integer, BigInteger> floorEntry = powersOfTen.floorEntry(n);
Integer floorN = floorEntry.getKey();
if (floorN == n) {
return floorEntry.getValue();
} else {
return FftMultiplier.multiply(floorEntry.getValue(), computePowerOfTen(powersOfTen, n - floorN));
}
}
return FIVE.pow(n).shiftLeft(n);
}
/**
* Computes 10<sup>n&~15</sup>.
*/
static BigInteger computeTenRaisedByNFloor16Recursive(NavigableMap<Integer, BigInteger> powersOfTen, int n) {
n = n & ~15;
Map.Entry<Integer, BigInteger> floorEntry = powersOfTen.floorEntry(n);
int floorPower = floorEntry.getKey();
BigInteger floorValue = floorEntry.getValue();
if (floorPower == n) {
return floorValue;
}
int diff = n - floorPower;
BigInteger diffValue = powersOfTen.get(diff);
if (diffValue == null) {
diffValue = computeTenRaisedByNFloor16Recursive(powersOfTen, diff);
powersOfTen.put(diff, diffValue);
}
return FftMultiplier.multiply(floorValue, diffValue);
}
static NavigableMap<Integer, BigInteger> createPowersOfTenFloor16Map() {
NavigableMap<Integer, BigInteger> powersOfTen;
powersOfTen = new TreeMap<>();
powersOfTen.put(0, BigInteger.ONE);
powersOfTen.put(16, TEN_POW_16);
return powersOfTen;
}
/** Gives the number of bits necessary to store given number of decimal digits.
* According to tests, overestimation is 1 bit at most for numbers like "999...",
* as the smallest one is "100..." additional 4 bits overestimation can occur.
* @param numDecimalDigits number of decimal digits, expected to be positive
* @return estimated number of bits
*/
public static long estimateNumBits(int numDecimalDigits) {
// For the decimal digit we need log_2(10) = 3.32192809488736234787 bits.
// The following formula uses 3.32192809488736234787 * 1073741824 (=2^30) = 3566893131.8 rounded up
// and adds 1, so that we overestimate but never underestimate the number of bits.
return (((numDecimalDigits * 3_566_893_132L) >>> 30) + 1);
}
/**
* Fills a map with powers of 10 floor 16.
*
* @param from the start index of the character sequence that contains the digits
* @param to the end index of the character sequence that contains the digits
* @return the filled map
*/
static NavigableMap<Integer, BigInteger> fillPowersOf10Floor16(int from, int to) {
// Fill the map with powers of 5
NavigableMap<Integer, BigInteger> powers = new TreeMap<>();
powers.put(0, BigInteger.valueOf(5));
powers.put(16, FIVE_POW_16);
fillPowersOfNFloor16Recursive(powers, from, to);
// Shift map entries to the left to obtain powers of ten
for (Iterator<Map.Entry<Integer, BigInteger>> iterator = powers.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Integer, BigInteger> e = iterator.next();
e.setValue(e.getValue().shiftLeft(e.getKey()));
}
return powers;
}
static void fillPowersOfNFloor16Recursive(NavigableMap<Integer, BigInteger> powersOfTen, int from, int to) {
int numDigits = to - from;
// base case:
if (numDigits <= 18) {
return;
}
// recursion case:
int mid = splitFloor16(from, to);
int n = to - mid;
if (!powersOfTen.containsKey(n)) {
fillPowersOfNFloor16Recursive(powersOfTen, from, mid);
fillPowersOfNFloor16Recursive(powersOfTen, mid, to);
powersOfTen.put(n, computeTenRaisedByNFloor16Recursive(powersOfTen, n));
}
}
/**
* Computes {@code uint128 product = (uint64)x * (uint64)y}.
* <p>
* References:
* <dl>
* <dt>Getting the high part of 64 bit integer multiplication</dt>
* <dd><a href="https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication">
* stackoverflow</a></dd>
* </dl>
*
* @param x uint64 factor x
* @param y uint64 factor y
* @return uint128 product of x and y
*/
static UInt128 fullMultiplication(long x, long y) {//since Java 18
return new UInt128(Math.unsignedMultiplyHigh(x, y), x * y);
}
static int splitFloor16(int from, int to) {
int mid = (from + to) >>> 1;// split in half
mid = to - (((to - mid + 15) >> 4) << 4);// make numDigits of low a multiple of 16
return mid;
}
static class UInt128 {
final long high, low;
private UInt128(long high, long low) {
this.high = high;
this.low = low;
}
}
}