2828 *
2929 * @author Tilman Neumann
3030 */
31- // TODO Now that there are signed methods, this class needs a refactoring
32- public class Uint128 {
31+ public class Int128 {
3332 @ SuppressWarnings ("unused" )
34- private static final Logger LOG = LogManager .getLogger (Uint128 .class );
33+ private static final Logger LOG = LogManager .getLogger (Int128 .class );
3534
3635 private static final boolean DEBUG = false ;
3736
3837 private long high , low ;
3938
40- public Uint128 (long high , long low ) {
39+ public Int128 (long high , long low ) {
4140 this .high = high ;
4241 this .low = low ;
4342 }
@@ -55,19 +54,19 @@ public long getLow() {
5554 * @param b
5655 * @return this + b
5756 */
58- public Uint128 add (Uint128 b ) {
57+ public Int128 add (Int128 b ) {
5958 long r_lo = low + b .getLow ();
6059 long carry = Long .compareUnsigned (r_lo , low ) < 0 ? 1 : 0 ;
6160 long r_hi = high + b .getHigh () + carry ;
62- return new Uint128 (r_hi , r_lo );
61+ return new Int128 (r_hi , r_lo );
6362 }
6463
6564 /**
6665 * Add two 128 bit integers, return the high part.
6766 * @param b
6867 * @return high part of this + b
6968 */
70- public long add_getHigh (Uint128 b ) {
69+ public long add_getHigh (Int128 b ) {
7170 long r_lo = low + b .getLow ();
7271 long carry = Long .compareUnsigned (r_lo , low ) < 0 ? 1 : 0 ;
7372 return high + b .getHigh () + carry ;
@@ -79,14 +78,62 @@ public long add_getHigh(Uint128 b) {
7978 * @param b
8079 * @return this - b, may be negative
8180 */
82- public Uint128 subtract (Uint128 b ) {
81+ public Int128 subtract (Int128 b ) {
8382 long b_lo = b .getLow ();
8483 long r_lo = low - b_lo ;
8584 long borrow = Long .compareUnsigned (low , b_lo ) < 0 ? 1 : 0 ;
8685 long r_hi = high - b .getHigh () - borrow ;
87- return new Uint128 (r_hi , r_lo );
86+ return new Int128 (r_hi , r_lo );
8887 }
8988
89+ /**
90+ * Multiplication of two signed 64 bit integers, adapted from Henry S. Warren, Hacker's Delight, Addison-Wesley, 2nd edition, chapter 8-2.
91+ * This is more or less what Java9 does in Math.multiplyHigh(), but I think I made it a bit faster optimizing register usage.
92+ *
93+ * @param a signed long
94+ * @param b signed long
95+ * @return a*b as a signed 127 bit number
96+ */
97+ public static Int128 mul64 (long a , long b ) {
98+ final long a_hi = a >> 32 ;
99+ final long a_lo = a & 0xFFFFFFFFL ;
100+ final long b_hi = b >> 32 ;
101+ final long b_lo = b & 0xFFFFFFFFL ;
102+
103+ // use b_lo twice as first argument hoping that this optimizes register usage
104+ final long w0 = b_lo * a_lo ;
105+ final long t = b_lo * a_hi + (w0 >>> 32 );
106+ // same with t
107+ final long w2 = t >> 32 ;
108+ final long w1 = (t & 0xFFFFFFFFL ) + a_lo * b_hi ;
109+
110+ final long r_hi = a_hi * b_hi + w2 + (w1 >> 32 );
111+ final long r_lo = a * b ;
112+ return new Int128 (r_hi , r_lo );
113+ }
114+
115+ /**
116+ * Multiplication of two signed 64-bit integers using Math.multiplyHigh().
117+ * Pretty fast if supported by intrinsics, which needs newer hardware and Java 10+.<br><br>
118+ *
119+ * @param a signed long
120+ * @param b signed long
121+ * @return a*b as a signed 127 bit number
122+ */
123+ public static Int128 mul64MH (long a , long b ) {
124+ final long r_lo = a *b ;
125+ final long r_hi = Math .multiplyHigh (a , b );
126+
127+ if (DEBUG ) {
128+ // compare to pure Java implementation
129+ Int128 testResult = mul64 (a , b );
130+ Ensure .ensureEquals (testResult .high , r_hi );
131+ Ensure .ensureEquals (testResult .low , r_lo );
132+ }
133+
134+ return new Int128 (r_hi , r_lo );
135+ }
136+
90137 /**
91138 * Multiplication of unsigned 63 bit integers,
92139 * following https://stackoverflow.com/questions/18859207/high-bits-of-long-multiplication-in-java.
@@ -98,7 +145,7 @@ public Uint128 subtract(Uint128 b) {
98145 * @param b
99146 * @return a*b accurate for inputs <= 63 bit
100147 */
101- public static Uint128 mul63 (long a , long b ) {
148+ public static Int128 mul63Unsigned (long a , long b ) {
102149 final long a_hi = a >>> 32 ;
103150 final long b_hi = b >>> 32 ;
104151 final long a_lo = a & 0xFFFFFFFFL ;
@@ -108,7 +155,7 @@ public static Uint128 mul63(long a, long b) {
108155 final long hi_prod = a_hi * b_hi ;
109156 final long r_hi = (((lo_prod >>> 32 ) + med_term ) >>> 32 ) + hi_prod ;
110157 final long r_lo = ((med_term & 0xFFFFFFFFL ) << 32 ) + lo_prod ;
111- return new Uint128 (r_hi , r_lo );
158+ return new Int128 (r_hi , r_lo );
112159 }
113160
114161 /**
@@ -118,7 +165,7 @@ public static Uint128 mul63(long a, long b) {
118165 * @param b unsigned long
119166 * @return a*b
120167 */
121- public static Uint128 mul64 (long a , long b ) {
168+ public static Int128 mul64Unsigned (long a , long b ) {
122169 final long a_hi = a >>> 32 ;
123170 final long b_hi = b >>> 32 ;
124171 final long a_lo = a & 0xFFFFFFFFL ;
@@ -131,11 +178,11 @@ public static Uint128 mul64(long a, long b) {
131178 final long hi_prod = a_hi * b_hi ;
132179
133180 // the medium term could overflow
134- final long carry = ( med_term + Long .MIN_VALUE < med_prod1 + Long . MIN_VALUE ) ? 1L <<32 : 0 ;
181+ final long carry = Long .compareUnsigned ( med_term , med_prod1 ) < 0 ? 1L <<32 : 0 ;
135182 final long r_hi = (((lo_prod >>> 32 ) + med_term ) >>> 32 ) + hi_prod + carry ;
136183 final long r_lo = ((med_term & 0xFFFFFFFFL ) << 32 ) + lo_prod ;
137184
138- return new Uint128 (r_hi , r_lo );
185+ return new Int128 (r_hi , r_lo );
139186 }
140187
141188 /**
@@ -146,68 +193,20 @@ public static Uint128 mul64(long a, long b) {
146193 * @param b
147194 * @return
148195 */
149- public static Uint128 mul64_MH (long a , long b ) {
196+ public static Int128 mul64UnsignedMH (long a , long b ) {
150197 final long r_lo = a *b ;
151198 long r_hi = Math .multiplyHigh (a , b );
152199 if (a <0 ) r_hi += b ;
153200 if (b <0 ) r_hi += a ;
154201
155202 if (DEBUG ) {
156203 // compare to pure Java implementation
157- Uint128 testResult = mul64 (a , b );
158- Ensure .ensureEquals (testResult .high , r_hi );
159- Ensure .ensureEquals (testResult .low , r_lo );
160- }
161-
162- return new Uint128 (r_hi , r_lo );
163- }
164-
165- /**
166- * Multiplication of two signed 64 bit integers, adapted from Henry S. Warren, Hacker's Delight, Addison-Wesley, 2nd edition, chapter 8-2.
167- * This is more or less what Java9 does in Math.multiplyHigh(), but I think I made it a bit faster optimizing register usage.
168- *
169- * @param a signed long
170- * @param b signed long
171- * @return a*b as a signed 127 bit number
172- */
173- public static Uint128 mul64Signed (long a , long b ) {
174- final long a_hi = a >> 32 ;
175- final long a_lo = a & 0xFFFFFFFFL ;
176- final long b_hi = b >> 32 ;
177- final long b_lo = b & 0xFFFFFFFFL ;
178-
179- // use b_lo twice as first argument hoping that this optimizes register usage
180- final long w0 = b_lo * a_lo ;
181- final long t = b_lo * a_hi + (w0 >>> 32 );
182- // same with t
183- final long w2 = t >> 32 ;
184- final long w1 = (t & 0xFFFFFFFFL ) + a_lo * b_hi ;
185-
186- final long r_hi = a_hi * b_hi + w2 + (w1 >> 32 );
187- final long r_lo = a * b ;
188- return new Uint128 (r_hi , r_lo );
189- }
190-
191- /**
192- * Multiplication of two signed 64-bit integers using Math.multiplyHigh().
193- * Pretty fast if supported by intrinsics, which needs newer hardware and Java 10+.<br><br>
194- *
195- * @param a signed long
196- * @param b signed long
197- * @return a*b as a signed 127 bit number
198- */
199- public static Uint128 mul64SignedMH (long a , long b ) {
200- final long r_lo = a *b ;
201- final long r_hi = Math .multiplyHigh (a , b );
202-
203- if (DEBUG ) {
204- // compare to pure Java implementation
205- Uint128 testResult = mul64Signed (a , b );
204+ Int128 testResult = mul64Unsigned (a , b );
206205 Ensure .ensureEquals (testResult .high , r_hi );
207206 Ensure .ensureEquals (testResult .low , r_lo );
208207 }
209208
210- return new Uint128 (r_hi , r_lo );
209+ return new Int128 (r_hi , r_lo );
211210 }
212211
213212 /**
@@ -217,7 +216,7 @@ public static Uint128 mul64SignedMH(long a, long b) {
217216 * @return a^2
218217 */
219218 // XXX speed up using intrinsics like in mul64_MH() ?
220- public static Uint128 square64 (long a ) {
219+ public static Int128 square64Unsigned (long a ) {
221220 final long a_hi = a >>> 32 ;
222221 final long a_lo = a & 0xFFFFFFFFL ;
223222
@@ -230,7 +229,7 @@ public static Uint128 square64(long a) {
230229 final long carry = (med_term +Long .MIN_VALUE < med_prod +Long .MIN_VALUE ) ? 1L <<32 : 0 ;
231230 final long r_hi = (((lo_prod >>> 32 ) + med_term ) >>> 32 ) + hi_prod + carry ;
232231 final long r_lo = ((med_term & 0xFFFFFFFFL ) << 32 ) + lo_prod ;
233- return new Uint128 (r_hi , r_lo );
232+ return new Int128 (r_hi , r_lo );
234233 }
235234
236235 /**
@@ -240,34 +239,34 @@ public static Uint128 square64(long a) {
240239 * @param b Uint128
241240 * @return a*b as an array of [low, high] Uint128 objects;
242241 */
243- public static Uint128 [] mul128 /*_v2*/ ( Uint128 a , Uint128 b ) {
242+ public static Int128 [] mul128Unsigned ( Int128 a , Int128 b ) {
244243 final long a_hi = a .getHigh (); // a >>> 32;
245244 final long b_hi = b .getHigh (); // b >>> 32;
246245 final long a_lo = a .getLow (); // a & 0xFFFFFFFFL;
247246 final long b_lo = b .getLow (); // b & 0xFFFFFFFFL;
248247
249- final Uint128 lo_prod = mul64 (a_lo , b_lo ); // a_lo * b_lo;
250- final Uint128 med_prod1 = mul64 (a_hi , b_lo ); // a_hi * b_lo;
251- final Uint128 med_prod2 = mul64 (a_lo , b_hi ); // a_lo * b_hi;
252- final Uint128 med_term = med_prod1 .add (med_prod2 ); // med_prod1 + med_prod2;
253- final Uint128 hi_prod = mul64 (a_hi , b_hi ); // a_hi * b_hi;
248+ final Int128 lo_prod = mul64Unsigned (a_lo , b_lo ); // a_lo * b_lo;
249+ final Int128 med_prod1 = mul64Unsigned (a_hi , b_lo ); // a_hi * b_lo;
250+ final Int128 med_prod2 = mul64Unsigned (a_lo , b_hi ); // a_lo * b_hi;
251+ final Int128 med_term = med_prod1 .add (med_prod2 ); // med_prod1 + med_prod2;
252+ final Int128 hi_prod = mul64Unsigned (a_hi , b_hi ); // a_hi * b_hi;
254253
255254 // the medium term could overflow
256255 //final long carry = (med_term+Long.MIN_VALUE < med_prod1+Long.MIN_VALUE) ? 1L<<32 : 0;
257- final Uint128 carry = (med_term .getHigh ()+Long .MIN_VALUE < med_prod1 .getHigh ()+Long .MIN_VALUE ) ? new Uint128 (1 , 0 ) : new Uint128 (0 , 0 );
256+ final Int128 carry = (med_term .getHigh ()+Long .MIN_VALUE < med_prod1 .getHigh ()+Long .MIN_VALUE ) ? new Int128 (1 , 0 ) : new Int128 (0 , 0 );
258257
259258 //final long r_hi = (((lo_prod >>> 32) + med_term) >>> 32) + hi_prod + carry;
260259 final long lo_prod_hi = lo_prod .getHigh (); // (lo_prod >>> 32)
261- final Uint128 intermediate = new Uint128 (0 , lo_prod_hi ).add (med_term ); // ((lo_prod >>> 32) + med_term)
260+ final Int128 intermediate = new Int128 (0 , lo_prod_hi ).add (med_term ); // ((lo_prod >>> 32) + med_term)
262261 final long intermediate_hi = intermediate .getHigh (); // (((lo_prod >>> 32) + med_term) >>> 32)
263- final Uint128 r_hi = new Uint128 (0 , intermediate_hi ).add (hi_prod ).add (carry );
262+ final Int128 r_hi = new Int128 (0 , intermediate_hi ).add (hi_prod ).add (carry );
264263
265264 //final long r_lo = ((med_term & 0xFFFFFFFFL) << 32) + lo_prod;
266265 final long med_term_lo = med_term .getLow (); // (med_term & 0xFFFFFFFFL)
267- final Uint128 r_lo = new Uint128 (med_term_lo , 0 ).add (lo_prod );
266+ final Int128 r_lo = new Int128 (med_term_lo , 0 ).add (lo_prod );
268267
269268 //return new Uint128(r_hi, r_lo);
270- return new Uint128 [] {r_lo , r_hi };
269+ return new Int128 [] {r_lo , r_hi };
271270 }
272271
273272 /**
@@ -277,20 +276,20 @@ public static Uint128 square64(long a) {
277276 * @param b Uint128
278277 * @return the low Uint128 of a*b
279278 */
280- public static Uint128 mul128_getLow ( Uint128 a , Uint128 b ) { // derived from mul128_v2
279+ public static Int128 mul128Unsigned_getLow ( Int128 a , Int128 b ) {
281280 final long a_hi = a .getHigh (); // a >>> 32;
282281 final long b_hi = b .getHigh (); // b >>> 32;
283282 final long a_lo = a .getLow (); // a & 0xFFFFFFFFL;
284283 final long b_lo = b .getLow (); // b & 0xFFFFFFFFL;
285284
286- final Uint128 lo_prod = mul64 (a_lo , b_lo ); // a_lo * b_lo;
287- final Uint128 med_prod1 = mul64 (a_hi , b_lo ); // a_hi * b_lo;
288- final Uint128 med_prod2 = mul64 (a_lo , b_hi ); // a_lo * b_hi;
289- final Uint128 med_term = med_prod1 .add (med_prod2 ); // med_prod1 + med_prod2;
285+ final Int128 lo_prod = mul64Unsigned (a_lo , b_lo ); // a_lo * b_lo;
286+ final Int128 med_prod1 = mul64Unsigned (a_hi , b_lo ); // a_hi * b_lo;
287+ final Int128 med_prod2 = mul64Unsigned (a_lo , b_hi ); // a_lo * b_hi;
288+ final Int128 med_term = med_prod1 .add (med_prod2 ); // med_prod1 + med_prod2;
290289
291290 //final long r_lo = ((med_term & 0xFFFFFFFFL) << 32) + lo_prod;
292291 final long med_term_lo = med_term .getLow (); // (med_term & 0xFFFFFFFFL)
293- final Uint128 r_lo = new Uint128 (med_term_lo , 0 ).add (lo_prod );
292+ final Int128 r_lo = new Int128 (med_term_lo , 0 ).add (lo_prod );
294293
295294 //return new Uint128(r_hi, r_lo);
296295 return r_lo ;
@@ -303,20 +302,20 @@ public static Uint128 mul128_getLow(Uint128 a, Uint128 b) { // derived from mul1
303302 * @param b Uint128
304303 * @return the low Uint128 of a*b
305304 */
306- public static Uint128 mul128MH_getLow ( Uint128 a , Uint128 b ) { // derived from mul128_v2
305+ public static Int128 mul128UnsignedMH_getLow ( Int128 a , Int128 b ) {
307306 final long a_hi = a .getHigh (); // a >>> 32;
308307 final long b_hi = b .getHigh (); // b >>> 32;
309308 final long a_lo = a .getLow (); // a & 0xFFFFFFFFL;
310309 final long b_lo = b .getLow (); // b & 0xFFFFFFFFL;
311310
312- final Uint128 lo_prod = mul64_MH (a_lo , b_lo ); // a_lo * b_lo;
313- final Uint128 med_prod1 = mul64_MH (a_hi , b_lo ); // a_hi * b_lo;
314- final Uint128 med_prod2 = mul64_MH (a_lo , b_hi ); // a_lo * b_hi;
315- final Uint128 med_term = med_prod1 .add (med_prod2 ); // med_prod1 + med_prod2;
311+ final Int128 lo_prod = mul64UnsignedMH (a_lo , b_lo ); // a_lo * b_lo;
312+ final Int128 med_prod1 = mul64UnsignedMH (a_hi , b_lo ); // a_hi * b_lo;
313+ final Int128 med_prod2 = mul64UnsignedMH (a_lo , b_hi ); // a_lo * b_hi;
314+ final Int128 med_term = med_prod1 .add (med_prod2 ); // med_prod1 + med_prod2;
316315
317316 //final long r_lo = ((med_term & 0xFFFFFFFFL) << 32) + lo_prod;
318317 final long med_term_lo = med_term .getLow (); // (med_term & 0xFFFFFFFFL)
319- final Uint128 r_lo = new Uint128 (med_term_lo , 0 ).add (lo_prod );
318+ final Int128 r_lo = new Int128 (med_term_lo , 0 ).add (lo_prod );
320319
321320 //return new Uint128(r_hi, r_lo);
322321 return r_lo ;
@@ -425,27 +424,27 @@ public static long mod128by64Unsigned(long u1, long u0, long v) {
425424 * @param bits
426425 * @return this << bits
427426 */
428- public Uint128 shiftLeft (int bits ) {
427+ public Int128 shiftLeft (int bits ) {
429428 if (bits <64 ) {
430429 long rh = (high <<bits ) | (low >>>(64 -bits ));
431430 long rl = low <<bits ;
432- return new Uint128 (rh , rl );
431+ return new Int128 (rh , rl );
433432 }
434- return new Uint128 (low <<(bits -64 ), 0 );
433+ return new Int128 (low <<(bits -64 ), 0 );
435434 }
436435
437436 /**
438437 * Shift this 'bits' bits to the right.
439438 * @param bits
440439 * @return this >>> bits
441440 */
442- public Uint128 shiftRight (int bits ) {
441+ public Int128 shiftRight (int bits ) {
443442 if (bits <64 ) {
444443 long rh = high >>>bits ;
445444 long rl = (low >>>bits ) | (high <<(64 -bits ));
446- return new Uint128 (rh , rl );
445+ return new Int128 (rh , rl );
447446 }
448- return new Uint128 (0 , high >>>(bits -64 ));
447+ return new Int128 (0 , high >>>(bits -64 ));
449448 }
450449
451450 /**
0 commit comments