perf: eliminate ULong.constructor-impl NOOP calls in unsigned comparisons

Bytecode analysis revealed that every `a.toULong() < b.toULong()`
comparison generates 2 invokestatic calls to ULong.constructor-impl
(NOOPs that return the input unchanged) plus Long.compareUnsigned.
Across all secp256k1 hot paths, this produced 554 NOOP invokestatic
calls in the bytecode, translating to ~18,000 wasted calls per verify.

Replace all toULong() comparisons with an inline uLt() helper that
uses the XOR-with-MIN_VALUE trick directly:

  (a xor Long.MIN_VALUE) < (b xor Long.MIN_VALUE)

This produces pure arithmetic bytecode (lxor, lcmp, ifge) with ZERO
method calls, eliminating all ULong.constructor-impl overhead.

Before: lload, invokestatic ULong.constructor-impl, lload,
        invokestatic ULong.constructor-impl, invokestatic
        Long.compareUnsigned, ifge  (6 bytecodes, 3 method calls)

After:  lload, ldc MIN_VALUE, lxor, lload, ldc MIN_VALUE, lxor,
        lcmp, ifge  (8 bytecodes, 0 method calls)

https://claude.ai/code/session_01EMY5RnXb9rnsyU2KbXrSaY
This commit is contained in:
Claude
2026-04-08 22:27:37 +00:00
parent 5add793abe
commit ab3a9076e3
5 changed files with 162 additions and 140 deletions
@@ -110,19 +110,19 @@ internal inline fun fieldMulReduceWith(
lo = a0 * b1
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
w[1] = s
carry = umulh(a0, b1) + c1
lo = a0 * b2
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
w[2] = s
carry = umulh(a0, b2) + c1
lo = a0 * b3
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
w[3] = s
w[4] = umulh(a0, b3) + c1
@@ -131,7 +131,7 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a1, b0)
prev = w[1]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
w[1] = s
carry = hi + c1
@@ -139,9 +139,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a1, b1)
prev = w[2]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[2] = s
carry = hi + c1 + c2
@@ -149,9 +149,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a1, b2)
prev = w[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[3] = s
carry = hi + c1 + c2
@@ -159,9 +159,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a1, b3)
prev = w[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[4] = s
w[5] = hi + c1 + c2
@@ -170,7 +170,7 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a2, b0)
prev = w[2]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
w[2] = s
carry = hi + c1
@@ -178,9 +178,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a2, b1)
prev = w[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[3] = s
carry = hi + c1 + c2
@@ -188,9 +188,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a2, b2)
prev = w[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[4] = s
carry = hi + c1 + c2
@@ -198,9 +198,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a2, b3)
prev = w[5]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[5] = s
w[6] = hi + c1 + c2
@@ -209,7 +209,7 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a3, b0)
prev = w[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
w[3] = s
carry = hi + c1
@@ -217,9 +217,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a3, b1)
prev = w[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[4] = s
carry = hi + c1 + c2
@@ -227,9 +227,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a3, b2)
prev = w[5]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[5] = s
carry = hi + c1 + c2
@@ -237,9 +237,9 @@ internal inline fun fieldMulReduceWith(
hi = umulh(a3, b3)
prev = w[6]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[6] = s
w[7] = hi + c1 + c2
@@ -280,13 +280,13 @@ internal inline fun fieldSqrReduceWith(
lo = a0 * a2
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
w[2] = s
carry = umulh(a0, a2) + c1
lo = a0 * a3
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
w[3] = s
w[4] = umulh(a0, a3) + c1
@@ -294,7 +294,7 @@ internal inline fun fieldSqrReduceWith(
hi = umulh(a1, a2)
prev = w[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
w[3] = s
carry = hi + c1
@@ -302,9 +302,9 @@ internal inline fun fieldSqrReduceWith(
hi = umulh(a1, a3)
prev = w[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
w[4] = s
w[5] = hi + c1 + c2
@@ -312,7 +312,7 @@ internal inline fun fieldSqrReduceWith(
hi = umulh(a2, a3)
prev = w[5]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
w[5] = s
w[6] = hi + c1
@@ -342,44 +342,44 @@ internal inline fun fieldSqrReduceWith(
hi = umulh(a0, a0)
w[0] = lo
s = w[1] + hi
c1 = if (s.toULong() < w[1].toULong()) 1L else 0L
c1 = if (uLt(s, w[1])) 1L else 0L
w[1] = s
var dCarry = c1
lo = a1 * a1
hi = umulh(a1, a1)
s = w[2] + lo
c1 = if (s.toULong() < w[2].toULong()) 1L else 0L
c1 = if (uLt(s, w[2])) 1L else 0L
s += dCarry
c2 = if (s.toULong() < dCarry.toULong()) 1L else 0L
c2 = if (uLt(s, dCarry)) 1L else 0L
w[2] = s
prev = w[3] + hi
val c3a = if (prev.toULong() < w[3].toULong()) 1L else 0L
val c3a = if (uLt(prev, w[3])) 1L else 0L
prev += c1 + c2
val c4a = if (prev.toULong() < (c1 + c2).toULong()) 1L else 0L
val c4a = if (uLt(prev, c1 + c2)) 1L else 0L
w[3] = prev
dCarry = c3a + c4a
lo = a2 * a2
hi = umulh(a2, a2)
s = w[4] + lo
c1 = if (s.toULong() < w[4].toULong()) 1L else 0L
c1 = if (uLt(s, w[4])) 1L else 0L
s += dCarry
c2 = if (s.toULong() < dCarry.toULong()) 1L else 0L
c2 = if (uLt(s, dCarry)) 1L else 0L
w[4] = s
prev = w[5] + hi
val c3b = if (prev.toULong() < w[5].toULong()) 1L else 0L
val c3b = if (uLt(prev, w[5])) 1L else 0L
prev += c1 + c2
val c4b = if (prev.toULong() < (c1 + c2).toULong()) 1L else 0L
val c4b = if (uLt(prev, c1 + c2)) 1L else 0L
w[5] = prev
dCarry = c3b + c4b
lo = a3 * a3
hi = umulh(a3, a3)
s = w[6] + lo
c1 = if (s.toULong() < w[6].toULong()) 1L else 0L
c1 = if (uLt(s, w[6])) 1L else 0L
s += dCarry
c2 = if (s.toULong() < dCarry.toULong()) 1L else 0L
c2 = if (uLt(s, dCarry)) 1L else 0L
w[6] = s
prev = w[7] + hi
prev += c1 + c2
@@ -412,34 +412,34 @@ internal inline fun reduceWideInline(
hcLo = w[4] * c
hcHi = umulh(w[4], c)
s1 = w[0] + hcLo
c1 = if (s1.toULong() < w[0].toULong()) 1L else 0L
c1 = if (uLt(s1, w[0])) 1L else 0L
out[0] = s1
var carry = hcHi + c1
hcLo = w[5] * c
hcHi = umulh(w[5], c)
s1 = w[1] + hcLo
c1 = if (s1.toULong() < w[1].toULong()) 1L else 0L
c1 = if (uLt(s1, w[1])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[1] = s2
carry = hcHi + c1 + c2
hcLo = w[6] * c
hcHi = umulh(w[6], c)
s1 = w[2] + hcLo
c1 = if (s1.toULong() < w[2].toULong()) 1L else 0L
c1 = if (uLt(s1, w[2])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[2] = s2
carry = hcHi + c1 + c2
hcLo = w[7] * c
hcHi = umulh(w[7], c)
s1 = w[3] + hcLo
c1 = if (s1.toULong() < w[3].toULong()) 1L else 0L
c1 = if (uLt(s1, w[3])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[3] = s2
carry = hcHi + c1 + c2
@@ -448,27 +448,27 @@ internal inline fun reduceWideInline(
val ccLo = carry * c
val ccHi = umulh(carry, c)
s1 = out[0] + ccLo
c1 = if (s1.toULong() < out[0].toULong()) 1L else 0L
c1 = if (uLt(s1, out[0])) 1L else 0L
out[0] = s1
var prop = ccHi + c1
if (prop != 0L) {
s1 = out[1] + prop
prop = if (s1.toULong() < out[1].toULong()) 1L else 0L
prop = if (uLt(s1, out[1])) 1L else 0L
out[1] = s1
if (prop != 0L) {
s1 = out[2] + prop
prop = if (s1.toULong() < out[2].toULong()) 1L else 0L
prop = if (uLt(s1, out[2])) 1L else 0L
out[2] = s1
if (prop != 0L) {
s1 = out[3] + prop
prop = if (s1.toULong() < out[3].toULong()) 1L else 0L
prop = if (uLt(s1, out[3])) 1L else 0L
out[3] = s1
}
}
}
if (prop != 0L) {
s1 = out[0] + c
c1 = if (s1.toULong() < out[0].toULong()) 1L else 0L
c1 = if (uLt(s1, out[0])) 1L else 0L
out[0] = s1
if (c1 != 0L) {
out[1]++
@@ -68,7 +68,7 @@ internal object FieldP {
if (carry != 0) {
// Overflow past 2^256: add 2^256 mod p = 2^32 + 977 = 0x1000003D1
val s1 = out[0] + 4294968273L
val c1 = if (s1.toULong() < out[0].toULong()) 1L else 0L
val c1 = if (uLt(s1, out[0])) 1L else 0L
out[0] = s1
if (c1 != 0L) {
out[1]++
@@ -95,7 +95,7 @@ internal object FieldP {
if (borrow != 0) {
// Add P = [P0, -1, -1, -1].
val s0 = out[0] + P0
val c0 = if (s0.toULong() < out[0].toULong()) 1L else 0L
val c0 = if (uLt(s0, out[0])) 1L else 0L
out[0] = s0
// For limbs 1-3: adding P[i]=-1 with carry c:
// c=1 → result unchanged, carry out=1 (identity propagation)
@@ -173,7 +173,7 @@ internal object FieldP {
}
// P - a: limb 0 is P0 - a[0], limbs 1-3 are (-1) - a[i] = ~a[i]
out[0] = P0 - a[0]
val borrow = if (a[0].toULong() > P0.toULong()) 1L else 0L
val borrow = if (uLt(P0, a[0])) 1L else 0L
// ~a[i] - borrow. New borrow only if ~a[i] == 0 (i.e., a[i] == -1) and borrow == 1
out[1] = a[1].inv() - borrow
val b1 = if (a[1] == -1L && borrow != 0L) 1L else 0L
@@ -200,28 +200,28 @@ internal object FieldP {
// Conditional add: out = a + (P & mask), unrolled
// Limb 0
s1 = a[0] + p0
c1 = if (s1.toULong() < a[0].toULong()) 1L else 0L
c1 = if (uLt(s1, a[0])) 1L else 0L
out[0] = s1
var carry = c1
// Limb 1
s1 = a[1] + mask
c1 = if (s1.toULong() < a[1].toULong()) 1L else 0L
c1 = if (uLt(s1, a[1])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[1] = s2
carry = c1 + c2
// Limb 2
s1 = a[2] + mask
c1 = if (s1.toULong() < a[2].toULong()) 1L else 0L
c1 = if (uLt(s1, a[2])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[2] = s2
carry = c1 + c2
// Limb 3
s1 = a[3] + mask
c1 = if (s1.toULong() < a[3].toULong()) 1L else 0L
c1 = if (uLt(s1, a[3])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[3] = s2
carry = c1 + c2
@@ -405,7 +405,7 @@ internal object FieldP {
hcLo = w[4] * c
hcHi = unsignedMultiplyHigh(w[4], c)
s1 = w[0] + hcLo
c1 = if (s1.toULong() < w[0].toULong()) 1L else 0L
c1 = if (uLt(s1, w[0])) 1L else 0L
out[0] = s1
var carry = hcHi + c1
@@ -413,9 +413,9 @@ internal object FieldP {
hcLo = w[5] * c
hcHi = unsignedMultiplyHigh(w[5], c)
s1 = w[1] + hcLo
c1 = if (s1.toULong() < w[1].toULong()) 1L else 0L
c1 = if (uLt(s1, w[1])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[1] = s2
carry = hcHi + c1 + c2
@@ -423,9 +423,9 @@ internal object FieldP {
hcLo = w[6] * c
hcHi = unsignedMultiplyHigh(w[6], c)
s1 = w[2] + hcLo
c1 = if (s1.toULong() < w[2].toULong()) 1L else 0L
c1 = if (uLt(s1, w[2])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[2] = s2
carry = hcHi + c1 + c2
@@ -433,9 +433,9 @@ internal object FieldP {
hcLo = w[7] * c
hcHi = unsignedMultiplyHigh(w[7], c)
s1 = w[3] + hcLo
c1 = if (s1.toULong() < w[3].toULong()) 1L else 0L
c1 = if (uLt(s1, w[3])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[3] = s2
carry = hcHi + c1 + c2
@@ -444,21 +444,21 @@ internal object FieldP {
val ccLo = carry * c
val ccHi = unsignedMultiplyHigh(carry, c)
s1 = out[0] + ccLo
c1 = if (s1.toULong() < out[0].toULong()) 1L else 0L
c1 = if (uLt(s1, out[0])) 1L else 0L
out[0] = s1
// Propagate carry (unrolled, with early exit)
var prop = ccHi + c1
if (prop != 0L) {
s1 = out[1] + prop
prop = if (s1.toULong() < out[1].toULong()) 1L else 0L
prop = if (uLt(s1, out[1])) 1L else 0L
out[1] = s1
if (prop != 0L) {
s1 = out[2] + prop
prop = if (s1.toULong() < out[2].toULong()) 1L else 0L
prop = if (uLt(s1, out[2])) 1L else 0L
out[2] = s1
if (prop != 0L) {
s1 = out[3] + prop
prop = if (s1.toULong() < out[3].toULong()) 1L else 0L
prop = if (uLt(s1, out[3])) 1L else 0L
out[3] = s1
}
}
@@ -466,7 +466,7 @@ internal object FieldP {
// Overflow past 256 bits: 2^256 ≡ C (mod p)
if (prop != 0L) {
s1 = out[0] + c
c1 = if (s1.toULong() < out[0].toULong()) 1L else 0L
c1 = if (uLt(s1, out[0])) 1L else 0L
out[0] = s1
if (c1 != 0L) {
out[1]++
@@ -224,7 +224,7 @@ internal object Glv {
for (i in limb until s.size) {
val old = s[i]
s[i] = old + if (i == limb) addVal else 1L
if (s[i].toULong() >= old.toULong() || (i == limb && addVal == 0L)) break
if (!uLt(s[i], old) || (i == limb && addVal == 0L)) break
// overflowed — carry to next limb
}
}
@@ -198,9 +198,9 @@ internal object ScalarN {
0L
}
val s1 = hiTimesNC[i] + loVal
val c1 = if (s1.toULong() < hiTimesNC[i].toULong()) 1L else 0L
val c1 = if (uLt(s1, hiTimesNC[i])) 1L else 0L
val s2 = s1 + carry
val c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
val c2 = if (uLt(s2, s1)) 1L else 0L
w[i] = s2
carry = c1 + c2
}
@@ -238,9 +238,9 @@ internal object ScalarN {
saved3
}
val s1 = loVal + hi2NC[i]
val c1 = if (s1.toULong() < loVal.toULong()) 1L else 0L
val c1 = if (uLt(s1, loVal)) 1L else 0L
val s2 = s1 + c2
val cc = if (s2.toULong() < s1.toULong()) 1L else 0L
val cc = if (uLt(s2, s1)) 1L else 0L
out[i] = s2
c2 = c1 + cc
}
@@ -252,13 +252,13 @@ internal object ScalarN {
val c1lo = ov * N_COMPLEMENT[1]
val c1hi = unsignedMultiplyHigh(ov, N_COMPLEMENT[1])
val s0 = out[0] + c0lo
val carry0 = if (s0.toULong() < out[0].toULong()) 1L else 0L
val carry0 = if (uLt(s0, out[0])) 1L else 0L
out[0] = s0
val s1 = out[1] + c0hi + c1lo + carry0
val carry1 = if (s1.toULong() < out[1].toULong()) 1L else 0L
val carry1 = if (uLt(s1, out[1])) 1L else 0L
out[1] = s1
val s2 = out[2] + c1hi + ov + carry1
val carry2 = if (s2.toULong() < out[2].toULong()) 1L else 0L
val carry2 = if (uLt(s2, out[2])) 1L else 0L
out[2] = s2
out[3] += carry2
}
@@ -45,6 +45,28 @@ package com.vitorpamplona.quartz.utils.secp256k1
// Package structure: U256 → FieldP/ScalarN → Glv/KeyCodec → Point → Secp256k1
// =====================================================================================
/**
* Unsigned less-than comparison without ULong inline class overhead.
*
* Kotlin's `uLt(a, b)` generates 2 invokestatic calls to
* ULong.constructor-impl (NOOPs that return the input unchanged) plus
* Long.compareUnsigned per comparison. On ART, these extra invokestatic
* calls add ~2-3ns each × ~18,000 comparisons per verify = ~36-54μs.
*
* This inline function uses the XOR-with-MIN_VALUE trick directly,
* producing pure arithmetic bytecode with ZERO method calls:
* lload a, ldc MIN_VALUE, lxor, lload b, ldc MIN_VALUE, lxor, lcmp, ifge
*
* vs the toULong() path:
* lload a, invokestatic ULong.constructor-impl, lload b,
* invokestatic ULong.constructor-impl, invokestatic Long.compareUnsigned, ifge
*/
@Suppress("NOTHING_TO_INLINE")
internal inline fun uLt(
a: Long,
b: Long,
): Boolean = (a xor Long.MIN_VALUE) < (b xor Long.MIN_VALUE)
/**
* Raw 256-bit unsigned integer arithmetic using 4×64-bit limbs.
*/
@@ -58,7 +80,7 @@ internal object U256 {
): Int {
for (i in 3 downTo 0) {
if (a[i] != b[i]) {
return if (a[i].toULong() < b[i].toULong()) -1 else 1
return if (uLt(a[i], b[i])) -1 else 1
}
}
return 0
@@ -77,31 +99,31 @@ internal object U256 {
// Limb 0 (no carry input)
s1 = a[0] + b[0]
c1 = if (s1.toULong() < a[0].toULong()) 1L else 0L
c1 = if (uLt(s1, a[0])) 1L else 0L
out[0] = s1
var carry = c1
// Limb 1
s1 = a[1] + b[1]
c1 = if (s1.toULong() < a[1].toULong()) 1L else 0L
c1 = if (uLt(s1, a[1])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[1] = s2
carry = c1 + c2
// Limb 2
s1 = a[2] + b[2]
c1 = if (s1.toULong() < a[2].toULong()) 1L else 0L
c1 = if (uLt(s1, a[2])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[2] = s2
carry = c1 + c2
// Limb 3
s1 = a[3] + b[3]
c1 = if (s1.toULong() < a[3].toULong()) 1L else 0L
c1 = if (uLt(s1, a[3])) 1L else 0L
s2 = s1 + carry
c2 = if (s2.toULong() < s1.toULong()) 1L else 0L
c2 = if (uLt(s2, s1)) 1L else 0L
out[3] = s2
carry = c1 + c2
@@ -121,31 +143,31 @@ internal object U256 {
// Limb 0 (no borrow input)
d1 = a[0] - b[0]
c1 = if (a[0].toULong() < b[0].toULong()) 1L else 0L
c1 = if (uLt(a[0], b[0])) 1L else 0L
out[0] = d1
var borrow = c1
// Limb 1
d1 = a[1] - b[1]
c1 = if (a[1].toULong() < b[1].toULong()) 1L else 0L
c1 = if (uLt(a[1], b[1])) 1L else 0L
d2 = d1 - borrow
c2 = if (d1.toULong() < borrow.toULong()) 1L else 0L
c2 = if (uLt(d1, borrow)) 1L else 0L
out[1] = d2
borrow = c1 + c2
// Limb 2
d1 = a[2] - b[2]
c1 = if (a[2].toULong() < b[2].toULong()) 1L else 0L
c1 = if (uLt(a[2], b[2])) 1L else 0L
d2 = d1 - borrow
c2 = if (d1.toULong() < borrow.toULong()) 1L else 0L
c2 = if (uLt(d1, borrow)) 1L else 0L
out[2] = d2
borrow = c1 + c2
// Limb 3
d1 = a[3] - b[3]
c1 = if (a[3].toULong() < b[3].toULong()) 1L else 0L
c1 = if (uLt(a[3], b[3])) 1L else 0L
d2 = d1 - borrow
c2 = if (d1.toULong() < borrow.toULong()) 1L else 0L
c2 = if (uLt(d1, borrow)) 1L else 0L
out[3] = d2
borrow = c1 + c2
@@ -187,19 +209,19 @@ internal object U256 {
lo = a0 * b1
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
out[1] = s
carry = unsignedMultiplyHigh(a0, b1) + c1
lo = a0 * b2
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
out[2] = s
carry = unsignedMultiplyHigh(a0, b2) + c1
lo = a0 * b3
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
out[3] = s
out[4] = unsignedMultiplyHigh(a0, b3) + c1
@@ -208,7 +230,7 @@ internal object U256 {
hi = unsignedMultiplyHigh(a1, b0)
prev = out[1]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
out[1] = s
carry = hi + c1
@@ -216,9 +238,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a1, b1)
prev = out[2]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[2] = s
carry = hi + c1 + c2
@@ -226,9 +248,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a1, b2)
prev = out[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[3] = s
carry = hi + c1 + c2
@@ -236,9 +258,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a1, b3)
prev = out[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[4] = s
out[5] = hi + c1 + c2
@@ -247,7 +269,7 @@ internal object U256 {
hi = unsignedMultiplyHigh(a2, b0)
prev = out[2]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
out[2] = s
carry = hi + c1
@@ -255,9 +277,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a2, b1)
prev = out[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[3] = s
carry = hi + c1 + c2
@@ -265,9 +287,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a2, b2)
prev = out[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[4] = s
carry = hi + c1 + c2
@@ -275,9 +297,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a2, b3)
prev = out[5]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[5] = s
out[6] = hi + c1 + c2
@@ -286,7 +308,7 @@ internal object U256 {
hi = unsignedMultiplyHigh(a3, b0)
prev = out[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
out[3] = s
carry = hi + c1
@@ -294,9 +316,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a3, b1)
prev = out[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[4] = s
carry = hi + c1 + c2
@@ -304,9 +326,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a3, b2)
prev = out[5]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[5] = s
carry = hi + c1 + c2
@@ -314,9 +336,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a3, b3)
prev = out[6]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[6] = s
out[7] = hi + c1 + c2
}
@@ -353,13 +375,13 @@ internal object U256 {
lo = a0 * a2
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
out[2] = s
carry = unsignedMultiplyHigh(a0, a2) + c1
lo = a0 * a3
s = lo + carry
c1 = if (s.toULong() < lo.toULong()) 1L else 0L
c1 = if (uLt(s, lo)) 1L else 0L
out[3] = s
out[4] = unsignedMultiplyHigh(a0, a3) + c1
@@ -368,7 +390,7 @@ internal object U256 {
hi = unsignedMultiplyHigh(a1, a2)
prev = out[3]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
out[3] = s
carry = hi + c1
@@ -376,9 +398,9 @@ internal object U256 {
hi = unsignedMultiplyHigh(a1, a3)
prev = out[4]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
s += carry
c2 = if (s.toULong() < carry.toULong()) 1L else 0L
c2 = if (uLt(s, carry)) 1L else 0L
out[4] = s
out[5] = hi + c1 + c2
@@ -387,7 +409,7 @@ internal object U256 {
hi = unsignedMultiplyHigh(a2, a3)
prev = out[5]
s = prev + lo
c1 = if (s.toULong() < prev.toULong()) 1L else 0L
c1 = if (uLt(s, prev)) 1L else 0L
out[5] = s
out[6] = hi + c1
@@ -419,7 +441,7 @@ internal object U256 {
hi = unsignedMultiplyHigh(a0, a0)
out[0] = lo // out[0] was 0
s = out[1] + hi
c1 = if (s.toULong() < out[1].toULong()) 1L else 0L
c1 = if (uLt(s, out[1])) 1L else 0L
out[1] = s
var dCarry = c1
@@ -427,14 +449,14 @@ internal object U256 {
lo = a1 * a1
hi = unsignedMultiplyHigh(a1, a1)
s = out[2] + lo
c1 = if (s.toULong() < out[2].toULong()) 1L else 0L
c1 = if (uLt(s, out[2])) 1L else 0L
s += dCarry
c2 = if (s.toULong() < dCarry.toULong()) 1L else 0L
c2 = if (uLt(s, dCarry)) 1L else 0L
out[2] = s
prev = out[3] + hi
val c3a = if (prev.toULong() < out[3].toULong()) 1L else 0L
val c3a = if (uLt(prev, out[3])) 1L else 0L
prev += c1 + c2
val c4a = if (prev.toULong() < (c1 + c2).toULong()) 1L else 0L
val c4a = if (uLt(prev, c1 + c2)) 1L else 0L
out[3] = prev
dCarry = c3a + c4a
@@ -442,14 +464,14 @@ internal object U256 {
lo = a2 * a2
hi = unsignedMultiplyHigh(a2, a2)
s = out[4] + lo
c1 = if (s.toULong() < out[4].toULong()) 1L else 0L
c1 = if (uLt(s, out[4])) 1L else 0L
s += dCarry
c2 = if (s.toULong() < dCarry.toULong()) 1L else 0L
c2 = if (uLt(s, dCarry)) 1L else 0L
out[4] = s
prev = out[5] + hi
val c3b = if (prev.toULong() < out[5].toULong()) 1L else 0L
val c3b = if (uLt(prev, out[5])) 1L else 0L
prev += c1 + c2
val c4b = if (prev.toULong() < (c1 + c2).toULong()) 1L else 0L
val c4b = if (uLt(prev, c1 + c2)) 1L else 0L
out[5] = prev
dCarry = c3b + c4b
@@ -457,12 +479,12 @@ internal object U256 {
lo = a3 * a3
hi = unsignedMultiplyHigh(a3, a3)
s = out[6] + lo
c1 = if (s.toULong() < out[6].toULong()) 1L else 0L
c1 = if (uLt(s, out[6])) 1L else 0L
s += dCarry
c2 = if (s.toULong() < dCarry.toULong()) 1L else 0L
c2 = if (uLt(s, dCarry)) 1L else 0L
out[6] = s
prev = out[7] + hi
val c3c = if (prev.toULong() < out[7].toULong()) 1L else 0L
val c3c = if (uLt(prev, out[7])) 1L else 0L
prev += c1 + c2
out[7] = prev
}