diff --git a/math.go b/math.go index 8e9aff9..486bbe6 100644 --- a/math.go +++ b/math.go @@ -102,6 +102,16 @@ func SaturatingAddU64(a, b U64) U64 { return U64(sum) } +func SaturatingAddU128(a, b U128) U128 { + sumLow, carry := bits.Add64(uint64(a[0]), uint64(b[0]), 0) + sumHigh, overflow := bits.Add64(uint64(a[1]), uint64(b[1]), carry) + // check for overflow + if overflow == 1 || (carry == 1 && sumHigh <= uint64(a[1]) && sumHigh <= uint64(b[1])) { + return MaxU128() + } + return U128{U64(sumLow), U64(sumHigh)} +} + func SaturatingSubU64(a, b U64) U64 { diff, borrow := bits.Sub64(uint64(a), uint64(b), 0) if borrow != 0 { @@ -110,6 +120,16 @@ func SaturatingSubU64(a, b U64) U64 { return U64(diff) } +func SaturatingSubU128(a, b U128) U128 { + low, borrow := bits.Sub64(uint64(a[0]), uint64(b[0]), 0) + high, _ := bits.Sub64(uint64(a[1]), uint64(b[1]), borrow) + // check for underflow + if borrow == 1 || high > uint64(a[1]) { + return U128{0, 0} + } + return U128{U64(low), U64(high)} +} + func SaturatingMulU64(a, b U64) U64 { if a == 0 || b == 0 { return U64(0) diff --git a/math_test.go b/math_test.go index df07a06..04606af 100644 --- a/math_test.go +++ b/math_test.go @@ -173,6 +173,26 @@ func Test_SaturatingAddU64(t *testing.T) { } } +func Test_SaturatingAddU128(t *testing.T) { + testExamples := []struct { + label string + a U128 + b U128 + expect U128 + }{ + {"2 + 1", NewU128(2), NewU128(1), NewU128(3)}, + {"MaxU128+1", MaxU128(), NewU128(1), MaxU128()}, + {"MaxU128+MaxU128", MaxU128(), MaxU128(), MaxU128()}, + } + + for _, testExample := range testExamples { + t.Run(testExample.label, func(t *testing.T) { + result := SaturatingAddU128(testExample.a, testExample.b) + assert.Equal(t, testExample.expect, result) + }) + } +} + func Test_SaturatingSubU64(t *testing.T) { testExamples := []struct { label string @@ -192,6 +212,26 @@ func Test_SaturatingSubU64(t *testing.T) { } } +func Test_SaturatingSubU128(t *testing.T) { + testExamples := []struct { + label string + a U128 + b U128 + expect U128 + }{ + {"2-1", NewU128(2), NewU128(1), NewU128(1)}, + {"0-1", NewU128(0), NewU128(1), NewU128(0)}, + {"0-MaxU128", NewU128(0), MaxU128(), NewU128(0)}, + } + + for _, testExample := range testExamples { + t.Run(testExample.label, func(t *testing.T) { + result := SaturatingSubU128(testExample.a, testExample.b) + assert.Equal(t, testExample.expect, result) + }) + } +} + func Test_SaturatingMul(t *testing.T) { testExamples := []struct { label string diff --git a/u128.go b/u128.go index 286b0b3..fb3aa65 100644 --- a/u128.go +++ b/u128.go @@ -101,26 +101,6 @@ func (n U128) Gte(other U128) bool { return n.ToBigInt().Cmp(other.ToBigInt()) >= 0 } -//func (n U128) SaturatingAdd(other U128) U128 { -// sumLow, carry := bits.Add64(uint64(n[0]), uint64(other[0]), 0) -// sumHigh, overflow := bits.Add64(uint64(n[1]), uint64(other[1]), carry) -// // check for overflow -// if overflow == 1 || (carry == 1 && sumHigh <= uint64(n[1]) && sumHigh <= uint64(other[1])) { -// return MaxU128() -// } -// return U128{U64(sumLow), U64(sumHigh)} -//} -// -//func (n U128) SaturatingSub(other U128) U128 { -// low, borrow := bits.Sub64(uint64(n[0]), uint64(other[0]), 0) -// high, _ := bits.Sub64(uint64(n[1]), uint64(other[1]), borrow) -// // check for underflow -// if borrow == 1 || high > uint64(n[1]) { -// return U128{0, 0} -// } -// return U128{U64(low), U64(high)} -//} -// //func (n U128) SaturatingMul(other U128) U128 { // result := new(big.Int).Mul(n.ToBigInt(), other.ToBigInt()) // // check for overflow