diff --git a/contract/p/gnoswap/gnsmath/sqrt_price_math_test.gno b/contract/p/gnoswap/gnsmath/sqrt_price_math_test.gno index fbcf5518b..771dd0b46 100644 --- a/contract/p/gnoswap/gnsmath/sqrt_price_math_test.gno +++ b/contract/p/gnoswap/gnsmath/sqrt_price_math_test.gno @@ -1092,7 +1092,7 @@ func TestSqrtPriceMath_GetAmount0Delta(t *testing.T) { name: "negative_liquidity", sqrtRatioAX96: u256.MustFromDecimal("1000000"), sqrtRatioBX96: u256.MustFromDecimal("2000000"), - liquidity: i256.New().Neg(i256.FromUint256(u256.MustFromDecimal("5000000"))), + liquidity: i256.Zero().Neg(i256.FromUint256(u256.MustFromDecimal("5000000"))), expected: "-198070406285660843983859875840", }, // Zero liquidity @@ -1160,14 +1160,14 @@ func TestSqrtPriceMath_GetAmount1Delta(t *testing.T) { name: "negative_liquidity", sqrtRatioAX96: u256.MustFromDecimal("1000000"), sqrtRatioBX96: u256.MustFromDecimal("2000000"), - liquidity: i256.New().Neg(i256.FromUint256(u256.MustFromDecimal("5000000"))), + liquidity: i256.Zero().Neg(i256.FromUint256(u256.MustFromDecimal("5000000"))), expected: "0", }, { name: "negative_liquidity_large", sqrtRatioAX96: encodePriceSqrt("1", "1"), sqrtRatioBX96: encodePriceSqrt("4", "1"), - liquidity: i256.New().Neg(i256.FromUint256(u256.MustFromDecimal("1000000000000000000"))), + liquidity: i256.Zero().Neg(i256.FromUint256(u256.MustFromDecimal("1000000000000000000"))), expected: "-1000000000000000000", }, { diff --git a/contract/p/gnoswap/int256/LICENSE b/contract/p/gnoswap/int256/LICENSE deleted file mode 100644 index fc7e78a48..000000000 --- a/contract/p/gnoswap/int256/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 Trịnh Đức Bảo Linh(Kevin) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/contract/p/gnoswap/int256/absolute.gno b/contract/p/gnoswap/int256/absolute.gno deleted file mode 100644 index 584be440c..000000000 --- a/contract/p/gnoswap/int256/absolute.gno +++ /dev/null @@ -1,38 +0,0 @@ -package int256 - -import ( - "gno.land/p/gnoswap/uint256" -) - -// Abs returns the absolute value of z. -func (z *Int) Abs() *uint256.Uint { - return z.abs.Clone() -} - -// AbsGt returns true if the absolute value of z is greater than x. -func (z *Int) AbsGt(x *uint256.Uint) bool { - return z.abs.Gt(x) -} - -// AbsLt returns true if the absolute value of z is less than x. -func (z *Int) AbsLt(x *uint256.Uint) bool { - return z.abs.Lt(x) -} - -// AbsOverflow sets z to the absolute value of x and returns z and whether overflow occurred. -// Overflow occurs when x is the minimum int256 value (-2^255), as its absolute value (2^255) -// cannot be represented in a signed 256-bit integer. -func (z *Int) AbsOverflow(x *Int) (*Int, bool) { - z = z.initiateAbs() - - // overflow can be happen when negating a minimum of int256 value - if x.neg && x.abs.Eq(MinInt256().abs) { - z.Set(x) // keep the original value - return z, true - } - - z.abs.Set(x.abs) - z.neg = false - - return z, false -} diff --git a/contract/p/gnoswap/int256/absolute_test.gno b/contract/p/gnoswap/int256/absolute_test.gno deleted file mode 100644 index 2162d626d..000000000 --- a/contract/p/gnoswap/int256/absolute_test.gno +++ /dev/null @@ -1,184 +0,0 @@ -package int256 - -import ( - "testing" - - "gno.land/p/gnoswap/uint256" -) - -func TestAbs(t *testing.T) { - tests := []struct { - x, want string - }{ - {"0", "0"}, - {"1", "1"}, - {"-1", "1"}, - {"-2", "2"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := x.Abs() - - if got.ToString() != tc.want { - t.Errorf("Abs(%s) = %v, want %v", tc.x, got.ToString(), tc.want) - } - } -} - -func TestAbsGt(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"0", "0", "false"}, - {"1", "0", "true"}, - {"-1", "0", "true"}, - {"-1", "1", "false"}, - {"-2", "1", "true"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "true"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "true"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "false"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := uint256.FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.AbsGt(y) - - if got != (tc.want == "true") { - t.Errorf("AbsGt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestAbsLt(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"0", "0", "false"}, - {"1", "0", "false"}, - {"-1", "0", "false"}, - {"-1", "1", "false"}, - {"-2", "1", "false"}, - {"-5", "10", "true"}, - {"31330", "31337", "true"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "false"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "false"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "false"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := uint256.FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.AbsLt(y) - - if got != (tc.want == "true") { - t.Errorf("AbsLt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestInt_AbsOverflow(t *testing.T) { - tests := []struct { - name string - x *Int - wantResult string - wantOverflow bool - }{ - { - name: "zero", - x: Zero(), - wantResult: "0", - wantOverflow: false, - }, - { - name: "positive number", - x: NewInt(100), - wantResult: "100", - wantOverflow: false, - }, - { - name: "negative number", - x: NewInt(-100), - wantResult: "100", - wantOverflow: false, - }, - { - name: "max_int256", - x: MustFromDecimal("57896044618658097711785492504343953926634992332820282019728792003956564819967"), - wantResult: "57896044618658097711785492504343953926634992332820282019728792003956564819967", - wantOverflow: false, - }, - { - name: "min_int256", - x: MustFromDecimal("-57896044618658097711785492504343953926634992332820282019728792003956564819968"), - wantResult: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", - wantOverflow: true, - }, - { - name: "min_int256 + 1", - x: MustFromDecimal("-57896044618658097711785492504343953926634992332820282019728792003956564819967"), - wantResult: "57896044618658097711785492504343953926634992332820282019728792003956564819967", - wantOverflow: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - z := new(Int) - gotResult, gotOverflow := z.AbsOverflow(tt.x) - - if gotOverflow != tt.wantOverflow { - t.Errorf("overflow = %v, want %v", gotOverflow, tt.wantOverflow) - return - } - - if gotResult == nil { - t.Error("unexpected nil result") - return - } - - if gotResult.ToString() != tt.wantResult { - t.Errorf("result = %v, want %v", gotResult.ToString(), tt.wantResult) - } - - // abs value must be GTE 0 (if there is no overflow) - if !tt.wantOverflow && gotResult.neg && !gotResult.IsZero() { - t.Error("absolute value cannot be negative") - } - - // original value must not be modified - originalValue := tt.x.ToString() - if tt.x.ToString() != originalValue { - t.Errorf("original value was modified: got %v, want %v", - tt.x.ToString(), originalValue) - } - }) - } -} diff --git a/contract/p/gnoswap/int256/arithmetic.gno b/contract/p/gnoswap/int256/arithmetic.gno index 606bd015c..471be0fa7 100644 --- a/contract/p/gnoswap/int256/arithmetic.gno +++ b/contract/p/gnoswap/int256/arithmetic.gno @@ -1,319 +1,149 @@ package int256 -import "gno.land/p/gnoswap/uint256" - -// Add sets z to the sum x+y and returns z. -func (z *Int) Add(x, y *Int) *Int { - z = z.initiateAbs() - - if x.neg == y.neg { - // If both numbers have the same sign, add their absolute values - z.abs.Add(x.abs, y.abs) - z.neg = x.neg - } else { - // If signs are different, subtract the smaller absolute value from the larger - if x.abs.Cmp(y.abs) >= 0 { - z.abs.Sub(x.abs, y.abs) - z.neg = x.neg - } else { - z.abs.Sub(y.abs, x.abs) - z.neg = y.neg +import "math/bits" + +func udivrem(quot, u []uint64, d *Int) (rem Int) { + var dLen int + for i := len(d) - 1; i >= 0; i-- { + if d[i] != 0 { + dLen = i + 1 + break } } - // Ensure zero is always positive - if z.abs.IsZero() { - z.neg = false + if dLen == 0 { + panic("integer divide by zero") } - return z -} - -// AddOverflow sets z to the sum x+y and returns z and whether overflow occurred. -// Overflow occurs when the result exceeds the int256 range [-2^255, 2^255-1]. -func (z *Int) AddOverflow(x, y *Int) (*Int, bool) { - z = z.initiateAbs() - - if x.neg == y.neg { - // same sign - var overflow bool - z.abs, overflow = z.abs.AddOverflow(x.abs, y.abs) - z.neg = x.neg - - if overflow { - return z, true - } - - // check int256 range - if z.neg { - if z.abs.Cmp(MinInt256().abs) > 0 { - return z, true - } - } else { - if z.abs.Cmp(MaxInt256().abs) > 0 { - return z, true - } - } - } else { - // handle different sign by subtracting absolute values - if x.abs.Cmp(y.abs) >= 0 { - z.abs.Sub(x.abs, y.abs) - z.neg = x.neg - } else { - z.abs.Sub(y.abs, x.abs) - z.neg = y.neg - } - } + shift := uint(bits.LeadingZeros64(d[dLen-1])) - // overflow can be happen when result is 0 - if z.abs.IsZero() { - z.neg = false + var dnStorage Int + dn := dnStorage[:dLen] + for i := dLen - 1; i > 0; i-- { + dn[i] = (d[i] << shift) | (d[i-1] >> (64 - shift)) } + dn[0] = d[0] << shift - return z, false -} - -// AddUint256 sets z to the sum x+y, where y is a uint256, and returns z. -func (z *Int) AddUint256(x *Int, y *uint256.Uint) *Int { - z = z.initiateAbs() - - if x.neg { - if x.abs.Gt(y) { - z.abs.Sub(x.abs, y) - z.neg = true - } else { - z.abs.Sub(y, x.abs) - z.neg = false + var uLen int + for i := len(u) - 1; i >= 0; i-- { + if u[i] != 0 { + uLen = i + 1 + break } - } else { - z.abs.Add(x.abs, y) - z.neg = false } - return z -} -// AddDelta adds a signed int256 value y to an unsigned uint256 value x and stores the result in z. -func AddDelta(z, x *uint256.Uint, y *Int) { - if y.neg { - z.Sub(x, y.abs) - } else { - z.Add(x, y.abs) + if uLen < dLen { + copy(rem[:], u) + return rem } -} -// AddDeltaOverflow adds a signed int256 value y to an unsigned uint256 value x, stores the result in z, -// and returns true if overflow occurs. -func AddDeltaOverflow(z, x *uint256.Uint, y *Int) bool { - var overflow bool - if y.neg { - _, overflow = z.SubOverflow(x, y.abs) - } else { - _, overflow = z.AddOverflow(x, y.abs) + var unStorage [9]uint64 + un := unStorage[:uLen+1] + un[uLen] = u[uLen-1] >> (64 - shift) + for i := uLen - 1; i > 0; i-- { + un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift)) } - return overflow -} - -// Sub sets z to the difference x-y and returns z. -func (z *Int) Sub(x, y *Int) *Int { - z = z.initiateAbs() + un[0] = u[0] << shift - if x.neg != y.neg { - // If sign are different, add the absolute values - z.abs.Add(x.abs, y.abs) - z.neg = x.neg - } else { - // If signs are the same, subtract the smaller absolute value from the larger - if x.abs.Cmp(y.abs) >= 0 { - z.abs = z.abs.Sub(x.abs, y.abs) - z.neg = x.neg - } else { - z.abs.Sub(y.abs, x.abs) - z.neg = !x.neg - } + if dLen == 1 { + r := udivremBy1(quot, un, dn[0]) + rem.SetUint64(r >> shift) + return rem } - // Ensure zero is always positive - if z.abs.IsZero() { - z.neg = false - } - return z -} + udivremKnuth(quot, un, dn) -// SubUint256 sets z to the difference x-y, where y is a uint256, and returns z. -func (z *Int) SubUint256(x *Int, y *uint256.Uint) *Int { - z = z.initiateAbs() - - if x.neg { - z.abs.Add(x.abs, y) - z.neg = true - } else { - if x.abs.Lt(y) { - z.abs.Sub(y, x.abs) - z.neg = true - } else { - z.abs.Sub(x.abs, y) - z.neg = false - } + for i := 0; i < dLen-1; i++ { + rem[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) } - return z -} - -// SubOverflow sets z to the difference x-y and returns z and whether overflow occurred. -// Overflow occurs when subtracting a positive number from the minimum int256 value -// or a negative number from the maximum int256 value. -func (z *Int) SubOverflow(x, y *Int) (*Int, bool) { - z = z.initiateAbs() - - // must keep the original value of y - negY := y.Clone() - negY.neg = !y.neg && !y.IsZero() // reverse sign if y is not zero - - // x + (-y) - return z.AddOverflow(x, negY) -} - -// Mul sets z to the product x*y and returns z. -func (z *Int) Mul(x, y *Int) *Int { - z = z.initiateAbs() + rem[dLen-1] = un[dLen-1] >> shift - z.abs = z.abs.Mul(x.abs, y.abs) - z.neg = x.neg != y.neg && !z.abs.IsZero() // 0 has no sign - return z + return rem } -// MulUint256 sets z to the product x*y, where y is a uint256, and returns z. -func (z *Int) MulUint256(x *Int, y *uint256.Uint) *Int { - z = z.initiateAbs() - - z.abs.Mul(x.abs, y) - if z.abs.IsZero() { - z.neg = false - } else { - z.neg = x.neg +func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { + reciprocal := reciprocal2by1(d) + rem = u[len(u)-1] + for j := len(u) - 2; j >= 0; j-- { + quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal) } - return z + return rem } -// MulOverflow sets z to the product x*y and returns z and whether overflow occurred. -// Multiplication frequently overflows when multiplying large numbers or when -// the product exceeds the int256 range [-2^255, 2^255-1]. -func (z *Int) MulOverflow(x, y *Int) (*Int, bool) { - z = z.initiateAbs() - - // always 0. no need to check overflow - if x.IsZero() || y.IsZero() { - z.abs.Clear() - z.neg = false - return z, false - } - - // multiply with absolute values - absResult, overflow := z.abs.MulOverflow(x.abs, y.abs) - z.abs = absResult +func udivremKnuth(quot, u, d []uint64) { + dh := d[len(d)-1] + dl := d[len(d)-2] + reciprocal := reciprocal2by1(dh) - // calculate the result's sign - z.neg = x.neg != y.neg - - if overflow { - return z, true - } + for j := len(u) - len(d) - 1; j >= 0; j-- { + u2 := u[j+len(d)] + u1 := u[j+len(d)-1] + u0 := u[j+len(d)-2] - if z.neg { - if z.abs.Cmp(MinInt256().abs) > 0 { - return z, true - } - } else { - if z.abs.Cmp(MaxInt256().abs) > 0 { - return z, true + var qhat, rhat uint64 + if u2 >= dh { + qhat = ^uint64(0) + } else { + qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal) + ph, pl := bits.Mul64(qhat, dl) + if ph > rhat || (ph == rhat && pl > u0) { + qhat-- + } } - } - return z, false -} - -// Div sets z to the quotient x/y for y != 0 and returns z. -// Panics if y == 0. -func (z *Int) Div(x, y *Int) *Int { - z = z.initiateAbs() + borrow := subMulTo(u[j:], d, qhat) + u[j+len(d)] = u2 - borrow + if u2 < borrow { + qhat-- + u[j+len(d)] += addTo(u[j:], d) + } - if y.abs.IsZero() { - panic("division by zero") + quot[j] = qhat } - - z.abs.Div(x.abs, y.abs) - z.neg = (x.neg != y.neg) && !z.abs.IsZero() // 0 has no sign - - return z } -// DivUint256 sets z to the quotient x/y, where y is a uint256, and returns z. -// If y == 0, z is set to 0. -func (z *Int) DivUint256(x *Int, y *uint256.Uint) *Int { - z = z.initiateAbs() - - z.abs.Div(x.abs, y) - if z.abs.IsZero() { - z.neg = false - } else { - z.neg = x.neg - } - return z +func reciprocal2by1(d uint64) uint64 { + reciprocal, _ := bits.Div64(^d, ^uint64(0), d) + return reciprocal } -// Quo sets z to the quotient x/y for y != 0 and returns z. -// It implements truncated division (like Go). Panics if y == 0. -// This differs from mempooler int256 which requires manual panic handling. -func (z *Int) Quo(x, y *Int) *Int { - if y.IsZero() { - panic("division by zero") - } +func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { + qh, ql := bits.Mul64(reciprocal, uh) + ql, carry := bits.Add64(ql, ul, 0) + qh += uh + carry + qh++ - z = z.initiateAbs() + r := ul - qh*d - z.abs = z.abs.Div(x.abs, y.abs) - z.neg = !(z.abs.IsZero()) && x.neg != y.neg // 0 has no sign - return z -} - -// Rem sets z to the remainder x%y for y != 0 and returns z. -// It implements truncated modulus (like Go). Panics if y == 0. -// This differs from mempooler int256 which requires manual panic handling. -func (z *Int) Rem(x, y *Int) *Int { - if y.IsZero() { - panic("division by zero") + if r > ql { + qh-- + r += d } - z = z.initiateAbs() + if r >= d { + qh++ + r -= d + } - z.abs.Mod(x.abs, y.abs) - z.neg = z.abs.Sign() > 0 && x.neg // 0 has no sign - return z + return qh, r } -// Mod sets z to the modulus x%y for y != 0 and returns z. -// If y == 0, z is set to 0 (differs from big.Int behavior). -func (z *Int) Mod(x, y *Int) *Int { - z = z.initiateAbs() - - if x.neg { - z.abs.Div(x.abs, y.abs) - z.abs.Add(z.abs, one) - z.abs.Mul(z.abs, y.abs) - z.abs.Sub(z.abs, x.abs) - z.abs.Mod(z.abs, y.abs) - } else { - z.abs.Mod(x.abs, y.abs) +func subMulTo(x, y []uint64, multiplier uint64) uint64 { + var borrow uint64 + for i := 0; i < len(y); i++ { + s, carry1 := bits.Sub64(x[i], borrow, 0) + ph, pl := bits.Mul64(y[i], multiplier) + t, carry2 := bits.Sub64(s, pl, 0) + x[i] = t + borrow = ph + carry1 + carry2 } - z.neg = false - return z + return borrow } -// MaxInt256 returns the maximum value for a 256-bit signed integer (2^255 - 1). -func MaxInt256() *Int { - return MustFromDecimal("57896044618658097711785492504343953926634992332820282019728792003956564819967") -} - -// MinInt256 returns the minimum value for a 256-bit signed integer (-2^255). -func MinInt256() *Int { - return MustFromDecimal("-57896044618658097711785492504343953926634992332820282019728792003956564819968") +func addTo(x, y []uint64) uint64 { + var carry uint64 + for i := 0; i < len(y); i++ { + x[i], carry = bits.Add64(x[i], y[i], carry) + } + return carry } diff --git a/contract/p/gnoswap/int256/arithmetic_test.gno b/contract/p/gnoswap/int256/arithmetic_test.gno index efc4fa399..a0efea9a8 100644 --- a/contract/p/gnoswap/int256/arithmetic_test.gno +++ b/contract/p/gnoswap/int256/arithmetic_test.gno @@ -1,1016 +1,392 @@ package int256 import ( + "math" "testing" - "gno.land/p/gnoswap/uint256" + "gno.land/p/nt/uassert" ) -func TestAdd(t *testing.T) { +func TestNewInt(t *testing.T) { tests := []struct { - x, y, want string + name string + v int64 }{ - {"0", "1", "1"}, - {"1", "0", "1"}, - {"1", "1", "2"}, - {"1", "2", "3"}, - // NEGATIVE - {"-1", "1", "0"}, - {"1", "-1", "0"}, - {"3", "-3", "0"}, - {"-1", "-1", "-2"}, - {"-1", "-2", "-3"}, - {"-1", "3", "2"}, - {"3", "-1", "2"}, - // OVERFLOW - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "0"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Add(x, y) - - if got.Neq(want) { - t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } -} - -func TestAddUint256(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"0", "1", "1"}, - {"1", "0", "1"}, - {"1", "1", "2"}, - {"1", "2", "3"}, - {"-1", "1", "0"}, - {"-1", "3", "2"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "1"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639934", "-1"}, - // OVERFLOW - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "0"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := uint256.FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.AddUint256(x, y) - - if got.Neq(want) { - t.Errorf("AddUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } -} - -func TestAddDelta(t *testing.T) { - tests := []struct { - z, x, y, want string - }{ - {"0", "0", "0", "0"}, - {"0", "0", "1", "1"}, - {"0", "1", "0", "1"}, - {"0", "1", "1", "2"}, - {"1", "2", "3", "5"}, - {"5", "10", "-3", "7"}, - // underflow - {"1", "2", "-3", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + { + name: "positive", + v: int64(math.MaxInt64), + }, + { + name: "negative", + v: int64(math.MinInt64), + }, + { + name: "zero", + v: int64(0), + }, } - for _, tc := range tests { - z, err := uint256.FromDecimal(tc.z) - if err != nil { - t.Error(err) - continue - } - - x, err := uint256.FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := uint256.FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - AddDelta(z, x, y) - - if z.Neq(want) { - t.Errorf("AddDelta(%s, %s, %s) = %v, want %v", tc.z, tc.x, tc.y, z.ToString(), want.ToString()) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + z := NewInt(tt.v) + uassert.True(t, z.IsInt64()) + uassert.Equal(t, tt.v, z.Int64()) + }) } } -func TestAddDeltaOverflow(t *testing.T) { +func TestGetSetInt64(t *testing.T) { tests := []struct { - z, x, y string - want bool + name string + v int64 }{ - {"0", "0", "0", false}, - // underflow - {"1", "2", "-3", true}, + { + name: "positive", + v: int64(math.MaxInt64), + }, + { + name: "negative", + v: int64(math.MinInt64), + }, + { + name: "zero", + v: int64(0), + }, } - for _, tc := range tests { - z, err := uint256.FromDecimal(tc.z) - if err != nil { - t.Error(err) - continue - } - - x, err := uint256.FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - result := AddDeltaOverflow(z, x, y) - if result != tc.want { - t.Errorf("AddDeltaOverflow(%s, %s, %s) = %v, want %v", tc.z, tc.x, tc.y, result, tc.want) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + z := new(Int).SetInt64(tt.v) + uassert.Equal(t, tt.v, z.Int64()) + }) } } -func TestAddOverflow(t *testing.T) { - maxInt256 := MaxInt256() - minInt256 := MinInt256() - +func TestIsInt64(t *testing.T) { tests := []struct { - name string - x *Int - y *Int - wantResult string - wantOverflow bool + name string + x string + expected bool }{ - // Basic cases (no overflow) - { - name: "positive + positive (no overflow)", - x: NewInt(100), - y: NewInt(200), - wantResult: "300", - wantOverflow: false, - }, - { - name: "negative + negative (no overflow)", - x: NewInt(-100), - y: NewInt(-200), - wantResult: "-300", - wantOverflow: false, - }, - // Boundary cases - near maximum value - { - name: "max_int256 + 0", - x: maxInt256, - y: Zero(), - wantResult: maxInt256.ToString(), - wantOverflow: false, - }, - { - name: "max_int256 - 1 + 1", - x: new(Int).Sub(maxInt256, One()), - y: One(), - wantResult: maxInt256.ToString(), - wantOverflow: false, - }, { - name: "max_int256 + 1", - x: maxInt256, - y: One(), - wantResult: "", // overflow - wantOverflow: true, + name: "max int64", + x: "9223372036854775807", + expected: true, }, - - // Boundary cases - near minimum value { - name: "min_int256 + 0", - x: minInt256, - y: Zero(), - wantResult: minInt256.ToString(), - wantOverflow: false, + name: "min int64", + x: "-9223372036854775808", + expected: true, }, { - name: "min_int256 + 1 - 1", - x: new(Int).Add(minInt256, One()), - y: NewInt(-1), - wantResult: minInt256.ToString(), - wantOverflow: false, + name: "max int64 + 1", + x: "9223372036854775808", + expected: false, }, { - name: "min_int256 + (-1)", - x: minInt256, - y: NewInt(-1), - wantResult: "", // overflow - wantOverflow: true, + name: "min int64 - 1", + x: "-9223372036854775809", + expected: false, }, - - // Special cases { - name: "max_int256 + min_int256", - x: maxInt256, - y: minInt256, - wantResult: "-1", - wantOverflow: false, + name: "zero", + x: "0", + expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - z := new(Int) - gotResult, gotOverflow := z.AddOverflow(tt.x, tt.y) - - if gotOverflow != tt.wantOverflow { - t.Errorf("overflow = %v, want %v", gotOverflow, tt.wantOverflow) - return - } - - if !gotOverflow { - if gotResult == nil { - t.Error("unexpected nil result for non-overflow case") - return - } - if gotResult.ToString() != tt.wantResult { - t.Errorf("result = %v, want %v", gotResult.ToString(), tt.wantResult) - } - } - - // Commutativity test only for non-overflow cases - if !tt.wantOverflow { - reverseResult, reverseOverflow := z.AddOverflow(tt.y, tt.x) - if reverseOverflow != gotOverflow { - t.Error("addition is not commutative for overflow") - } - if reverseResult.ToString() != gotResult.ToString() { - t.Error("addition is not commutative for result") - } - } + x := MustFromDecimal(tt.x) + uassert.Equal(t, tt.expected, x.IsInt64()) }) } } -func TestSub(t *testing.T) { +func TestIsUint64(t *testing.T) { tests := []struct { - x, y, want string + name string + x string + expected bool }{ - {"1", "0", "1"}, - {"1", "1", "0"}, - {"-1", "1", "-2"}, - {"1", "-1", "2"}, - {"-1", "-1", "0"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - {x: "-115792089237316195423570985008687907853269984665640564039457584007913129639935", y: "1", want: "0"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Sub(x, y) - - if got.Neq(want) { - t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } -} - -func TestSubUint256(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"0", "1", "-1"}, - {"1", "0", "1"}, - {"1", "1", "0"}, - {"1", "2", "-1"}, - {"-1", "1", "-2"}, - {"-1", "3", "-4"}, - // underflow - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "-0"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "2", "-1"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "3", "-2"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := uint256.FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.SubUint256(x, y) - - if got.Neq(want) { - t.Errorf("SubUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } -} - -func TestMul(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"5", "3", "15"}, - {"-5", "3", "-15"}, - {"5", "-3", "-15"}, - {"0", "3", "0"}, - {"3", "0", "0"}, + { + name: "max uint64", + x: "18446744073709551615", + expected: true, + }, + { + name: "max uint64 + 1", + x: "18446744073709551616", + expected: false, + }, + { + name: "zero", + x: "0", + expected: true, + }, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Mul(x, y) - - if got.Neq(want) { - t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + uassert.Equal(t, tt.expected, x.IsUint64()) + }) } } -func TestInt_SubOverflow(t *testing.T) { - maxInt256 := MaxInt256() - minInt256 := MinInt256() - +func TestRem(t *testing.T) { tests := []struct { - name string - x *Int - y *Int - wantResult string - wantOverflow bool + name string + x string + y string + expected string + shouldPanic bool }{ { - name: "positive - positive (no overflow)", - x: NewInt(200), - y: NewInt(100), - wantResult: "100", - wantOverflow: false, - }, - { - name: "negative - negative (no overflow)", - x: NewInt(-200), - y: NewInt(-300), - wantResult: "100", - wantOverflow: false, - }, - { - name: "positive - negative (no overflow)", - x: NewInt(200), - y: NewInt(-100), - wantResult: "300", - wantOverflow: false, + name: "positive % negative", + x: "3", + y: "-2", + expected: "1", }, { - name: "max_int256 - 0", - x: maxInt256, - y: Zero(), - wantResult: maxInt256.ToString(), - wantOverflow: false, + name: "min negative % positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "35719473219571942749314729421", + expected: "-34167184328512991083512640217", }, { - name: "min_int256 - 0", - x: minInt256, - y: Zero(), - wantResult: minInt256.ToString(), - wantOverflow: false, + name: "max positive % negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-35719473219571942749314729421", + expected: "34167184328512991083512640216", }, { - name: "max_int256 - (-1)", // max_int256 + 1 -> overflow - x: maxInt256, - y: NewInt(-1), - wantResult: "", - wantOverflow: true, + name: "large negative % large negative", + x: "-43179374921751324719573491471294", + y: "-43127519734921524", + expected: "-22155214380278890", }, { - name: "min_int256 - 1", // min_int256 - 1 -> overflow - x: minInt256, - y: One(), - wantResult: "", - wantOverflow: true, + name: "zero % negative", + x: "0", + y: "-2", + expected: "0", }, { - name: "0 - 0", - x: Zero(), - y: Zero(), - wantResult: "0", - wantOverflow: false, + name: "large positive % smaller positive", + x: "4372195701247205721942146816424", + y: "1974924792517421647328142", + expected: "549633341738318150337156", }, { - name: "min_int256 - min_int256", - x: minInt256, - y: minInt256, - wantResult: "0", - wantOverflow: false, + name: "same values", + x: "1974924792517421647328142", + y: "1974924792517421647328142", + expected: "0", }, { - name: "max_int256 - max_int256", - x: maxInt256, - y: maxInt256, - wantResult: "0", - wantOverflow: false, + name: "remainder by zero", + x: "1", + y: "0", + expected: "", + shouldPanic: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // z := new(Int) - z := New() - gotResult, gotOverflow := z.SubOverflow(tt.x, tt.y) - - if gotOverflow != tt.wantOverflow { - t.Errorf("overflow = %v, want %v", gotOverflow, tt.wantOverflow) - return + if tt.shouldPanic { + defer func() { + if r := recover(); r == nil { + t.Error("Rem should panic on division by zero") + } + }() } - if !gotOverflow { - if gotResult == nil { - t.Error("unexpected nil result for non-overflow case") - return - } - if gotResult.ToString() != tt.wantResult { - t.Errorf("result = %v, want %v", gotResult.ToString(), tt.wantResult) - } + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Rem(x, y) + if !tt.shouldPanic { + uassert.Equal(t, tt.expected, z.ToString()) } }) } } -func TestMulUint256(t *testing.T) { +func TestIsPositive(t *testing.T) { tests := []struct { - x, y, want string - }{ - {"0", "1", "0"}, - {"1", "0", "0"}, - {"1", "1", "1"}, - {"1", "2", "2"}, - {"-1", "1", "-1"}, - {"-1", "3", "-3"}, - {"3", "4", "12"}, - {"-3", "4", "-12"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "-115792089237316195423570985008687907853269984665640564039457584007913129639932"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "115792089237316195423570985008687907853269984665640564039457584007913129639932"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := uint256.FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.MulUint256(x, y) - - if got.Neq(want) { - t.Errorf("MulUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } -} - -func TestInt_MulOverflow(t *testing.T) { - maxInt256 := MaxInt256() - minInt256 := MinInt256() - - tests := []struct { - name string - x *Int - y *Int - wantResult string - wantOverflow bool + name string + x string + expected bool }{ { - name: "positive * positive (no overflow)", - x: NewInt(100), - y: NewInt(100), - wantResult: "10000", - wantOverflow: false, + name: "positive number", + x: "100", + expected: true, }, { - name: "negative * negative (no overflow)", - x: NewInt(-100), - y: NewInt(-100), - wantResult: "10000", - wantOverflow: false, + name: "negative number", + x: "-100", + expected: false, }, { - name: "positive * negative (no overflow)", - x: NewInt(100), - y: NewInt(-100), - wantResult: "-10000", - wantOverflow: false, - }, - { - name: "0 * positive", - x: Zero(), - y: NewInt(100), - wantResult: "0", - wantOverflow: false, - }, - { - name: "positive * 0", - x: NewInt(100), - y: Zero(), - wantResult: "0", - wantOverflow: false, - }, - { - name: "0 * 0", - x: Zero(), - y: Zero(), - wantResult: "0", - wantOverflow: false, - }, - { - name: "max_int256 * 1", - x: maxInt256, - y: One(), - wantResult: maxInt256.ToString(), - wantOverflow: false, - }, - { - name: "min_int256 * 1", - x: minInt256, - y: One(), - wantResult: minInt256.ToString(), - wantOverflow: false, - }, - { - name: "min_int256 * -1", - x: minInt256, - y: NewInt(-1), - wantResult: "", // overflow because abs(min_int256) > max_int256 - wantOverflow: true, - }, - { - name: "max_int256 * 2", - x: maxInt256, - y: NewInt(2), - wantResult: "", - wantOverflow: true, - }, - { - name: "min_int256 * 2", - x: minInt256, - y: NewInt(2), - wantResult: "", - wantOverflow: true, - }, - { - name: "half_max * 2", - x: MustFromDecimal("28948022309329048855892746252171976963317496332820282019728792003956564819983"), // (2^255-1)/2 - y: NewInt(2), - wantResult: "", - wantOverflow: true, - }, - { - name: "(half_max + 1) * 2", - x: new(Int).Add(new(Int).Div(maxInt256, NewInt(2)), One()), - y: NewInt(2), - wantResult: "", - wantOverflow: true, + name: "zero", + x: "0", + expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - z := new(Int) - gotResult, gotOverflow := z.MulOverflow(tt.x, tt.y) - - if gotOverflow != tt.wantOverflow { - t.Errorf("overflow = %v, want %v", gotOverflow, tt.wantOverflow) - return - } - - if !gotOverflow { - if gotResult == nil { - t.Error("unexpected nil result for non-overflow case") - return - } - if gotResult.ToString() != tt.wantResult { - t.Errorf("result = %v, want %v", gotResult.ToString(), tt.wantResult) - } - } - - if !tt.wantOverflow { - reverseResult, reverseOverflow := z.MulOverflow(tt.y, tt.x) - if reverseOverflow != gotOverflow { - t.Error("multiplication is not commutative for overflow") - } - if reverseResult.ToString() != gotResult.ToString() { - t.Error("multiplication is not commutative for result") - } - } + x := MustFromDecimal(tt.x) + uassert.Equal(t, tt.expected, x.IsPositive()) }) } } -func TestDiv(t *testing.T) { +func TestLte(t *testing.T) { tests := []struct { - x, y, expected string + name string + x string + y string + expected bool }{ - {"1", "1", "1"}, - {"0", "1", "0"}, - {"-1", "1", "-1"}, - {"1", "-1", "-1"}, - {"-1", "-1", "1"}, - {"-6", "3", "-2"}, - {"10", "-2", "-5"}, - {"-10", "3", "-3"}, - {"7", "3", "2"}, - {"-7", "3", "-2"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "2", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, // Max uint256 / 2 + { + name: "min negative <= max positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: true, + }, + { + name: "equal values", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: true, + }, + { + name: "max positive > min negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: false, + }, } for _, tt := range tests { - t.Run(tt.x+"/"+tt.y, func(t *testing.T) { + t.Run(tt.name, func(t *testing.T) { x := MustFromDecimal(tt.x) y := MustFromDecimal(tt.y) - result := Zero().Div(x, y) - if result.ToString() != tt.expected { - t.Errorf("Div(%s, %s) = %s, want %s", tt.x, tt.y, result.ToString(), tt.expected) - } - if result.abs.IsZero() && result.neg { - t.Errorf("Div(%s, %s) resulted in negative zero", tt.x, tt.y) - } + uassert.Equal(t, tt.expected, x.Lte(y)) }) } - - t.Run("Division by zero", func(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Errorf("Div(1, 0) did not panic") - } - }() - x := MustFromDecimal("1") - y := MustFromDecimal("0") - Zero().Div(x, y) - }) -} - -func TestDivUint256(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"0", "1", "0"}, - {"1", "0", "0"}, - {"1", "1", "1"}, - {"1", "2", "0"}, - {"-1", "1", "-1"}, - {"-1", "3", "0"}, - {"4", "3", "1"}, - {"25", "5", "5"}, - {"25", "4", "6"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "-57896044618658097711785492504343953926634992332820282019728792003956564819967"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := uint256.FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.DivUint256(x, y) - - if got.Neq(want) { - t.Errorf("DivUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } } -func TestQuo(t *testing.T) { +func TestGte(t *testing.T) { tests := []struct { - x, y, want string + name string + x string + y string + expected bool }{ - {"0", "1", "0"}, - {"0", "-1", "0"}, - {"10", "1", "10"}, - {"10", "-1", "-10"}, - {"-10", "1", "-10"}, - {"-10", "-1", "10"}, - {"10", "-3", "-3"}, - {"10", "3", "3"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Quo(x, y) - - if got.Neq(want) { - t.Errorf("Quo(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } - } -} - -func TestRem(t *testing.T) { - tests := []struct { - x, y, want string - }{ - {"0", "1", "0"}, - {"0", "-1", "0"}, - {"10", "1", "0"}, - {"10", "-1", "0"}, - {"-10", "1", "0"}, - {"-10", "-1", "0"}, - {"10", "3", "1"}, - {"10", "-3", "1"}, - {"-10", "3", "-1"}, - {"-10", "-3", "-1"}, + { + name: "min negative < max positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: false, + }, + { + name: "equal values", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: true, + }, + { + name: "max positive >= min negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: true, + }, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Rem(x, y) - - if got.Neq(want) { - t.Errorf("Rem(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + uassert.Equal(t, tt.expected, x.Gte(y)) + }) } } -func TestMod(t *testing.T) { +func TestXor(t *testing.T) { tests := []struct { - x, y, want string + name string + x string + y string + expected string }{ - {"0", "1", "0"}, - {"0", "-1", "0"}, - {"10", "0", "0"}, - {"10", "1", "0"}, - {"10", "-1", "0"}, - {"-10", "0", "0"}, - {"-10", "1", "0"}, - {"-10", "-1", "0"}, - {"10", "3", "1"}, - {"10", "-3", "1"}, - {"-10", "3", "2"}, - {"-10", "-3", "2"}, + { + name: "min negative ^ negative", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-100", + expected: "57896044618658097711785492504343953926634992332820282019728792003956564819868", + }, + { + name: "max positive ^ positive", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "41457129491534261876432718654783265437285638275", + expected: "57896044618658097711785492504302496797143458070943849301074008738519279181692", + }, + { + name: "min negative ^ max positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "-1", + }, + { + name: "min negative ^ zero", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "0", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + }, } - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := FromDecimal(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Mod(x, y) - - if got.Neq(want) { - t.Errorf("Mod(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Xor(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + }) } } -func TestNilInitialization(t *testing.T) { +func TestNot(t *testing.T) { tests := []struct { - name string - setup func() (*Int, error) - wantStr string + name string + x string + expected string }{ { - name: "AddUint256 with nil abs", - setup: func() (*Int, error) { - z := new(Int) - x := uint256.NewUint(5) - return z.AddUint256(z, x), nil - }, - wantStr: "5", - }, - { - name: "SubUint256 with nil abs", - setup: func() (*Int, error) { - z := new(Int) - x := uint256.NewUint(5) - return z.SubUint256(z, x), nil - }, - wantStr: "-5", - }, - { - name: "MulUint256 with nil abs", - setup: func() (*Int, error) { - z := new(Int) - x := uint256.NewUint(5) - return z.MulUint256(z, x), nil - }, - wantStr: "0", - }, - { - name: "DivUint256 with nil abs", - setup: func() (*Int, error) { - z := new(Int) - x := uint256.NewUint(5) - return z.DivUint256(z, x), nil - }, - wantStr: "0", - }, - { - name: "Mod with nil abs", - setup: func() (*Int, error) { - z := new(Int) - x := MustFromDecimal("5") - defer func() { - if r := recover(); r != nil { - t.Errorf("Mod with nil abs panicked: %v", r) - } - }() - return z.Mod(z, x), nil - }, - wantStr: "0", + name: "positive number", + x: "100", + expected: "-101", + }, + { + name: "negative number", + x: "-100", + expected: "99", }, { - name: "Chained operations with nil abs", - setup: func() (*Int, error) { - z := new(Int) - x := uint256.NewUint(5) - y := uint256.NewUint(3) - // (0 + 5) * 3 - return z.AddUint256(z, x).MulUint256(z, y), nil - }, - wantStr: "15", + name: "zero", + x: "0", + expected: "-1", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := tt.setup() - if err != nil { - t.Errorf("unexpected error: %v", err) - return - } - - got := result.ToString() - if got != tt.wantStr { - t.Errorf("%s: got %v, want %v", tt.name, got, tt.wantStr) - } + x := MustFromDecimal(tt.x) + z := new(Int).Not(x) + uassert.Equal(t, tt.expected, z.ToString()) }) } } diff --git a/contract/p/gnoswap/int256/bitwise.gno b/contract/p/gnoswap/int256/bitwise.gno deleted file mode 100644 index ef0bd9514..000000000 --- a/contract/p/gnoswap/int256/bitwise.gno +++ /dev/null @@ -1,101 +0,0 @@ -package int256 - -import ( - "gno.land/p/gnoswap/uint256" -) - -// Or sets z to the bitwise OR of x and y and returns z. -// The operation handles two's complement representation for negative numbers. -func (z *Int) Or(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := new(uint256.Uint).Sub(x.abs, one) - y1 := new(uint256.Uint).Sub(y.abs, one) - z.abs = z.abs.Add(z.abs.And(x1, y1), one) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x | y == x | y - z.abs = z.abs.Or(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // | is symmetric - } - - // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := new(uint256.Uint).Sub(y.abs, one) - z.abs = z.abs.Add(z.abs.AndNot(y1, x.abs), one) - z.neg = true // z cannot be zero if one of x or y is negative - - return z -} - -// And sets z to the bitwise AND of x and y and returns z. -// The operation handles two's complement representation for negative numbers. -func (z *Int) And(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := new(uint256.Uint).Sub(x.abs, one) - y1 := new(uint256.Uint).Sub(y.abs, one) - z.abs = z.abs.Add(z.abs.Or(x1, y1), one) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x & y == x & y - z.abs = z.abs.And(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - // REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1192-1202;drc=d57303e65f00b84b528ee682747dbe1fd3316d30 - if x.neg { - x, y = y, x // & is symmetric - } - - // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := new(uint256.Uint).Sub(y.abs, uint256.One()) - z.abs = z.abs.AndNot(x.abs, y1) - z.neg = false - return z -} - -// Rsh sets z to x right-shifted by n bits and returns z. -// It performs arithmetic right shift, preserving the sign bit. -// This differs from the original implementation which used math/big. -func (z *Int) Rsh(x *Int, n uint) *Int { - if !x.neg { - z.abs.Rsh(x.abs, n) - z.neg = x.neg - return z - } - - // REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1118-1126;drc=d57303e65f00b84b528ee682747dbe1fd3316d30 - t := NewInt(0).Sub(FromUint256(x.abs), NewInt(1)) - t = t.Rsh(t, n) - - _tmp := t.Add(t, NewInt(1)) - z.abs = _tmp.Abs() - z.neg = true - - return z -} - -// Lsh sets z to x left-shifted by n bits and returns z. -func (z *Int) Lsh(x *Int, n uint) *Int { - z.abs.Lsh(x.abs, n) - if z.abs.IsZero() { - z.neg = false - } else { - z.neg = x.neg - } - return z -} diff --git a/contract/p/gnoswap/int256/bitwise_test.gno b/contract/p/gnoswap/int256/bitwise_test.gno deleted file mode 100644 index 282de5b03..000000000 --- a/contract/p/gnoswap/int256/bitwise_test.gno +++ /dev/null @@ -1,204 +0,0 @@ -package int256 - -import ( - "testing" - - "gno.land/p/nt/uassert" - - "gno.land/p/gnoswap/uint256" -) - -func TestOr(t *testing.T) { - tests := []struct { - name string - x, y, want Int - }{ - { - name: "all zeroes", - x: Int{abs: uint256.Zero(), neg: false}, - y: Int{abs: uint256.Zero(), neg: false}, - want: Int{abs: uint256.Zero(), neg: false}, - }, - { - name: "all ones", - x: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - y: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - want: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - }, - { - name: "mixed", - x: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - y: Int{abs: uint256.NewUint(0), neg: false}, - want: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := New() - got.Or(&tc.x, &tc.y) - - if got.Neq(&tc.want) { - t.Errorf("Or(%v, %v) = %v, want %v", tc.x, tc.y, got, tc.want) - } - }) - } -} - -func TestAnd(t *testing.T) { - tests := []struct { - name string - x, y, want Int - }{ - { - name: "all zeroes", - x: Int{abs: uint256.Zero(), neg: false}, - y: Int{abs: uint256.Zero(), neg: false}, - want: Int{abs: uint256.Zero(), neg: false}, - }, - { - name: "all ones", - x: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - y: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - want: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - }, - { - name: "mixed", - x: Int{abs: uint256.Zero(), neg: false}, - y: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - want: Int{abs: uint256.Zero(), neg: false}, - }, - { - name: "mixed 2", - x: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - y: Int{abs: uint256.Zero(), neg: false}, - want: Int{abs: uint256.Zero(), neg: false}, - }, - { - name: "mixed 3", - x: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - y: Int{abs: uint256.Zero(), neg: false}, - want: Int{abs: uint256.Zero(), neg: false}, - }, - { - name: "one operand zero", - x: Int{abs: uint256.Zero(), neg: false}, - y: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - want: Int{abs: uint256.Zero(), neg: false}, - }, - { - name: "one operand all ones", - x: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - y: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - want: Int{abs: uint256.NewUint(0).SetAllOne(), neg: false}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := New() - got.And(&tc.x, &tc.y) - - if got.Neq(&tc.want) { - t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, got, tc.want) - } - }) - } -} - -func TestRsh(t *testing.T) { - tests := []struct { - x string - n uint - want string - }{ - {"1024", 0, "1024"}, - {"1024", 1, "512"}, - {"1024", 2, "256"}, - {"1024", 10, "1"}, - {"1024", 11, "0"}, - {"18446744073709551615", 0, "18446744073709551615"}, - {"18446744073709551615", 1, "9223372036854775807"}, - {"18446744073709551615", 62, "3"}, - {"18446744073709551615", 63, "1"}, - {"18446744073709551615", 64, "0"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 0, "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 1, "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 128, "340282366920938463463374607431768211455"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 255, "1"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 256, "0"}, - {"-1024", 0, "-1024"}, - {"-1024", 1, "-512"}, - {"-1024", 2, "-256"}, - {"-1024", 10, "-1"}, - {"-1024", 10, "-1"}, - {"-9223372036854775808", 0, "-9223372036854775808"}, - {"-9223372036854775808", 1, "-4611686018427387904"}, - {"-9223372036854775808", 62, "-2"}, - {"-9223372036854775808", 63, "-1"}, - {"-9223372036854775808", 64, "-1"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 0, "-57896044618658097711785492504343953926634992332820282019728792003956564819968"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 1, "-28948022309329048855892746252171976963317496166410141009864396001978282409984"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 253, "-4"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 254, "-2"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 255, "-1"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 256, "-1"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Rsh(x, tc.n) - - if got.ToString() != tc.want { - t.Errorf("Rsh(%s, %d) = %v, want %v", tc.x, tc.n, got.ToString(), tc.want) - } - } -} - -func TestLsh(t *testing.T) { - tests := []struct { - x string - n uint - want string - expectPanic bool - }{ - {"1", 0, "1", false}, - {"1", 1, "2", false}, - {"1", 2, "4", false}, - {"2", 0, "2", false}, - {"2", 1, "4", false}, - {"2", 2, "8", false}, - {"-2", 0, "-2", false}, - {"-4", 0, "-4", false}, - {"-8", 0, "-8", false}, - {"-1", 255, "-57896044618658097711785492504343953926634992332820282019728792003956564819968", false}, - {"-1", 256, "0", true}, - {"-2", 255, "0", true}, - {"-4", 254, "0", true}, - {"-8", 253, "0", true}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := New() - if tc.expectPanic { - uassert.PanicsWithMessage(t, "Lsh: overflow", func() { - got.Lsh(x, tc.n) - }) - } else { - got.Lsh(x, tc.n) - uassert.Equal(t, tc.want, got.ToString()) - } - } -} diff --git a/contract/p/gnoswap/int256/cmp.gno b/contract/p/gnoswap/int256/cmp.gno deleted file mode 100644 index 5a891773f..000000000 --- a/contract/p/gnoswap/int256/cmp.gno +++ /dev/null @@ -1,117 +0,0 @@ -package int256 - -// Eq returns true if z equals x. -// Panics if either z or x is nil. -func (z *Int) Eq(x *Int) bool { - if z == nil || x == nil { - panic("int256: comparing with nil") - } - return (z.neg == x.neg) && z.abs.Eq(x.abs) -} - -// Neq returns true if z does not equal x. -// Panics if either z or x is nil. -func (z *Int) Neq(x *Int) bool { - if z == nil || x == nil { - panic("int256: comparing with nil") - } - return !z.Eq(x) -} - -// Cmp compares z and x and returns -1 if z < x, 0 if z == x, or +1 if z > x. -// Panics if either z or x is nil. -func (z *Int) Cmp(x *Int) (r int) { - if z == nil || x == nil { - panic("int256: comparing with nil") - } - // x cmp y == x cmp y - // x cmp (-y) == x - // (-x) cmp y == y - // (-x) cmp (-y) == -(x cmp y) - switch { - case z == x: - // nothing to do - case z.neg == x.neg: - r = z.abs.Cmp(x.abs) - if z.neg { - r = -r - } - case z.neg: - r = -1 - default: - r = 1 - } - return -} - -// IsZero returns true if z equals 0. -func (z *Int) IsZero() bool { - return z.abs.IsZero() -} - -// IsNeg returns true if z is negative. -func (z *Int) IsNeg() bool { - return z.neg -} - -// Lt returns true if z is less than x. -// Panics if either z or x is nil. -func (z *Int) Lt(x *Int) bool { - if z == nil || x == nil { - panic("int256: comparing with nil") - } - if z.neg { - if x.neg { - return z.abs.Gt(x.abs) - } else { - return true - } - } else { - if x.neg { - return false - } else { - return z.abs.Lt(x.abs) - } - } -} - -// Lte returns true if z is less than or equal to x. -func (z *Int) Lte(x *Int) bool { - return z.Lt(x) || z.Eq(x) -} - -// Gt returns true if z is greater than x. -// Panics if either z or x is nil. -func (z *Int) Gt(x *Int) bool { - if z == nil || x == nil { - panic("int256: comparing with nil") - } - if z.neg { - if x.neg { - return z.abs.Lt(x.abs) - } else { - return false - } - } else { - if x.neg { - return true - } else { - return z.abs.Gt(x.abs) - } - } -} - -// Gte returns true if z is greater than or equal to x. -func (z *Int) Gte(x *Int) bool { - return z.Gt(x) || z.Eq(x) -} - -// Clone creates a new Int identical to z. -func (z *Int) Clone() *Int { - return &Int{z.abs.Clone(), z.neg} -} - -// IsOverflow returns true if z's absolute value has overflowed. -func (z *Int) IsOverflow() bool { - return z.abs.IsOverflow() -} diff --git a/contract/p/gnoswap/int256/cmp_test.gno b/contract/p/gnoswap/int256/cmp_test.gno deleted file mode 100644 index b6bb6a8ee..000000000 --- a/contract/p/gnoswap/int256/cmp_test.gno +++ /dev/null @@ -1,316 +0,0 @@ -package int256 - -import ( - "testing" -) - -func TestEq(t *testing.T) { - tests := []struct { - x, y string - want bool - }{ - {"0", "0", true}, - {"0", "1", false}, - {"1", "0", false}, - {"-1", "0", false}, - {"0", "-1", false}, - {"1", "1", true}, - {"-1", "-1", true}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.Eq(y) - if got != tc.want { - t.Errorf("Eq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestNeq(t *testing.T) { - tests := []struct { - x, y string - want bool - }{ - {"0", "0", false}, - {"0", "1", true}, - {"1", "0", true}, - {"-1", "0", true}, - {"0", "-1", true}, - {"1", "1", false}, - {"-1", "-1", false}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.Neq(y) - if got != tc.want { - t.Errorf("Neq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestCmp(t *testing.T) { - tests := []struct { - x, y string - want int - }{ - {"0", "0", 0}, - {"0", "1", -1}, - {"1", "0", 1}, - {"-1", "0", -1}, - {"0", "-1", 1}, - {"1", "1", 0}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", 1}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.Cmp(y) - if got != tc.want { - t.Errorf("Cmp(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestIsZero(t *testing.T) { - tests := []struct { - x string - want bool - }{ - {"0", true}, - {"-0", true}, - {"1", false}, - {"-1", false}, - {"10", false}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := x.IsZero() - if got != tc.want { - t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) - } - } -} - -func TestIsNeg(t *testing.T) { - tests := []struct { - x string - want bool - }{ - {"0", false}, - {"-0", true}, - {"1", false}, - {"-1", true}, - {"10", false}, - {"-10", true}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := x.IsNeg() - if got != tc.want { - t.Errorf("IsNeg(%s) = %v, want %v", tc.x, got, tc.want) - } - } -} - -func TestLt(t *testing.T) { - tests := []struct { - x, y string - want bool - }{ - {"0", "0", false}, - {"0", "1", true}, - {"1", "0", false}, - {"-1", "0", true}, - {"0", "-1", false}, - {"1", "1", false}, - {"-1", "-1", false}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.Lt(y) - if got != tc.want { - t.Errorf("Lt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestGt(t *testing.T) { - tests := []struct { - x, y string - want bool - }{ - {"0", "0", false}, - {"0", "1", false}, - {"1", "0", true}, - {"-1", "0", false}, - {"0", "-1", true}, - {"1", "1", false}, - {"-1", "-1", false}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := FromDecimal(tc.y) - if err != nil { - t.Error(err) - continue - } - - got := x.Gt(y) - if got != tc.want { - t.Errorf("Gt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) - } - } -} - -func TestClone(t *testing.T) { - tests := []struct { - x string - }{ - {"0"}, - {"-0"}, - {"1"}, - {"-1"}, - {"10"}, - {"-10"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - y := x.Clone() - - if x.Cmp(y) != 0 { - t.Errorf("Clone(%s) = %v, want %v", tc.x, y, x) - } - } -} - -func TestNilChecks(t *testing.T) { - validInt := NewInt(123) - - tests := []struct { - name string - fn func() - wantPanic string - }{ - { - name: "Eq with nil", - fn: func() { validInt.Eq(nil) }, - wantPanic: "int256: comparing with nil", - }, - { - name: "Neq with nil", - fn: func() { validInt.Neq(nil) }, - wantPanic: "int256: comparing with nil", - }, - { - name: "Cmp with nil", - fn: func() { validInt.Cmp(nil) }, - wantPanic: "int256: comparing with nil", - }, - { - name: "Lt with nil", - fn: func() { validInt.Lt(nil) }, - wantPanic: "int256: comparing with nil", - }, - { - name: "Gt with nil", - fn: func() { validInt.Gt(nil) }, - wantPanic: "int256: comparing with nil", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - r := recover() - if r == nil { - t.Errorf("%s: expected panic but got none", tt.name) - return - } - if r.(string) != tt.wantPanic { - t.Errorf("%s: got panic %v, want %v", tt.name, r, tt.wantPanic) - } - }() - - tt.fn() - }) - } -} diff --git a/contract/p/gnoswap/int256/conversion.gno b/contract/p/gnoswap/int256/conversion.gno index 75527da51..6fafeb889 100644 --- a/contract/p/gnoswap/int256/conversion.gno +++ b/contract/p/gnoswap/int256/conversion.gno @@ -1,120 +1,229 @@ package int256 import ( - "gno.land/p/gnoswap/uint256" + "errors" + "math/bits" + "strconv" + + u256 "gno.land/p/gnoswap/uint256" ) -// SetInt64 sets z to x and returns z. -// -// This implementation uses two's complement to handle the edge case of math.MinInt64. -// When x = math.MinInt64 (-2^63), negating it would cause an overflow since 2^63 -// cannot be represented as a positive int64. By converting to uint64 first and then -// applying two's complement (^u + 1) when negative, we correctly handle all int64 -// values including the minimum value without overflow. -func (z *Int) SetInt64(x int64) *Int { - z = z.initiateAbs() - if z.abs == nil { - panic("int256_SetInt64(): abs is nil") - } - u := uint64(x) - neg := x < 0 - if neg { - u = ^u + 1 // |x| = two's complement magnitude - } - z.abs = z.abs.SetUint64(u) - z.neg = neg && u != 0 // prevent -0 - return z -} +const ( + maxAbsI256Dec = "57896044618658097711785492504343953926634992332820282019728792003956564819968" + maxWords = 256 / bits.UintSize +) -// SetUint64 sets z to x and returns z. -func (z *Int) SetUint64(x uint64) *Int { - z = z.initiateAbs() +var ( + ErrOverflow = errors.New("int256: overflow") + ErrEmptyString = errors.New("int256: empty string") + ErrInvalidSignInMiddleOfNumber = errors.New("int256: invalid sign in middle of number") + + multipliers = [5]*Int{ + nil, + {0x8ac7230489e80000, 0, 0, 0}, + {0x98a224000000000, 0x4b3b4ca85a86c47a, 0, 0}, + {0x4a00000000000000, 0xebfdcb54864ada83, 0x28c87cb5c89a2571, 0}, + {0, 0x7775a5f171951000, 0x764b4abe8652979, 0x161bcca7119915b5}, + } +) - if z.abs == nil { - panic("int256_SetUint64(): abs is nil") +func FromDecimal(decimal string) (*Int, error) { + z, err := new(Int).SetString(decimal) + if err != nil { + return nil, err } - z.abs = z.abs.SetUint64(x) - z.neg = false - return z -} -// Uint64 returns the lower 64 bits of z as a uint64. -func (z *Int) Uint64() uint64 { - return z.abs.Uint64() + return z, nil } -// Int64 returns the lower 64 bits of z, interpreted as a signed int64 (two's complement). -// -// Since int64 already uses two's complement representation internally, -// we can simply apply two's complement to the unsigned magnitude when negative and cast -// to int64. This approach correctly handles all edge cases including when the magnitude -// equals 2^63 (which represents math.MinInt64 when negative) without special casing. -func (z *Int) Int64() int64 { - u := z.abs.Uint64() // lower 64 bits of magnitude - if z.neg { - u = ^u + 1 // apply two's complement for negative sign - } - return int64(u) // reinterpret as two's complement int64 +func MustFromDecimal(decimal string) *Int { + z, err := FromDecimal(decimal) + if err != nil { + panic(err) + } + return z } -// Neg sets z to -x and returns z. -func (z *Int) Neg(x *Int) *Int { - z.abs.Set(x.abs) - if z.abs.IsZero() { - z.neg = false +func (z *Int) ToString() string { + s := z.Sign() + if s == 0 { + return "0" + } + if z.IsInt64() { + return strconv.FormatInt(z.Int64(), 10) + } + y := new(Int) + if s > 0 { + y.Set(z) } else { - z.neg = !x.neg + y.Neg(z) } - return z + var ( + out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + divisor = new(Int).SetUint64(10000000000000000000) + pos = len(out) + buf = make([]byte, 0, 19) + ) + + for { + var quot Int + rem := udivrem(quot[:], y[:], divisor) + y.Set(") + buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) + copy(out[pos-len(buf):], buf) + if y.IsZero() { + break + } + pos -= 19 + } + + var res string + if s < 0 { + res = "-" + } + res += string(out[pos-len(buf):]) + return res } -// NegOverflow sets z to -x and returns z and whether overflow occurred. -// Overflow occurs when negating the minimum int256 value (-2^255). -func (z *Int) NegOverflow(x *Int) (*Int, bool) { - z = z.initiateAbs() +func (z *Int) SetString(s string) (*Int, error) { + if len(s) == 0 { + return nil, ErrEmptyString + } - if x.IsZero() { - z.abs.Clear() - z.neg = false - return z, false + isNeg := false + switch s[0] { + case '+': + s = s[1:] + case '-': + isNeg = true + s = s[1:] } - if x.neg && x.abs.Eq(MinInt256().abs) { - z.Set(x) // must preserve the original value - return z, true + if len(s) == 0 { + return nil, ErrEmptyString } - z.abs.Set(x.abs) - z.neg = !x.neg + // Parallel comparison technique for validation + // Process in 8-byte chunks for optimal performance + sLen := len(s) + i := 0 + + // Process 8 bytes at a time + for i+7 < sLen { + // Access up to s[i+7] is safe, then we can reduce the number of bounds checks + _ = s[i+7] + + // Convert 8 bytes into a single uint64 + // This method processes bytes directly, so no endianness issues + chunk := uint64(s[i]) | uint64(s[i+1])<<8 + chunk |= uint64(s[i+2])<<16 | uint64(s[i+3])<<24 + chunk |= uint64(s[i+4])<<32 | uint64(s[i+5])<<40 + chunk |= uint64(s[i+6])<<48 | uint64(s[i+7])<<56 + + // Check for '+' (0x2B) using SWAR technique + // Subtracting 0x2B from each byte makes '+' bytes become 0 + // Subtracting 0x01 makes bytes in ASCII range (0-127) have 0 in their highest bit + // Therefore, AND with 0x80 to check for zero bytes + plusTest := ((chunk ^ 0x2B2B2B2B2B2B2B2B) - 0x0101010101010101) & 0x8080808080808080 + + // Check for '-' (0x2D) using SWAR technique + minusTest := ((chunk ^ 0x2D2D2D2D2D2D2D2D) - 0x0101010101010101) & 0x8080808080808080 + + // If either test is non-zero, a sign character exists + if (plusTest | minusTest) != 0 { + return nil, ErrInvalidSignInMiddleOfNumber + } + + i += 8 + } - return z, false -} + // Process remaining bytes + for ; i < sLen; i++ { + if s[i] == '+' || s[i] == '-' { + return nil, ErrInvalidSignInMiddleOfNumber + } + } -// Set sets z to x and returns z. -func (z *Int) Set(x *Int) *Int { - z.abs.Set(x.abs) - z.neg = x.neg - return z -} + // Strip leading zeros + if len(s) > 0 && s[0] == '0' { + idx := 0 + for idx < len(s) && s[idx] == '0' { + idx++ + } + s = s[idx:] + // If all characters were zeros, set to "0" + if len(s) == 0 { + s = "0" + } + } -// SetUint256 sets z to the value of x and returns z. -func (z *Int) SetUint256(x *uint256.Uint) *Int { - z.abs.Set(x) - z.neg = false - return z + // Check for overflow + if len(s) > len(maxAbsI256Dec) || + (len(s) == len(maxAbsI256Dec) && s > maxAbsI256Dec) || + (s == maxAbsI256Dec && !isNeg) { + return nil, ErrOverflow + } + + if err := z.fromDecimal(s); err != nil { + return nil, err + } + + if isNeg { + z.Neg(z) + } + + return z, nil } -// ToString returns the decimal string representation of z. -// Panics if z is nil. -// This differs from the original mempooler int256 implementation. -func (z *Int) ToString() string { - if z == nil { - panic("int256: nil pointer to ToString()") +func (z *Int) fromDecimal(bs string) error { + z.Clear() + var ( + num uint64 + err error + remaining = len(bs) + ) + + if remaining == 0 { + return errors.New("EOF") } - t := z.abs.Dec() - if z.neg { - return "-" + t + for i, mult := range multipliers { + if remaining <= 0 { + return nil + } + if remaining > 19 { + num, err = strconv.ParseUint(bs[remaining-19:remaining], 10, 64) + } else { + num, err = strconv.ParseUint(bs, 10, 64) + } + if err != nil { + return err + } + if i == 0 { + z.SetUint64(num) + } else { + base := new(Int).SetUint64(num) + z.Add(z, base.Mul(base, mult)) + } + if remaining > 19 { + bs = bs[0 : remaining-19] + } + remaining -= 19 } - return t + return nil +} + +// FromUint256 converts a uint256 to int256. +// Panics if the uint256 value is greater than MaxInt256 (2^255 - 1). +func FromUint256(x *u256.Uint) *Int { + // Check overflow: if MSB of x[3] is set, value > MaxInt256 + if x[3] > 0x7fffffffffffffff { + panic("int256: overflow - uint256 value exceeds MaxInt256") + } + z := &Int{} + z[0] = x[0] + z[1] = x[1] + z[2] = x[2] + z[3] = x[3] + return z } diff --git a/contract/p/gnoswap/int256/conversion_test.gno b/contract/p/gnoswap/int256/conversion_test.gno index aae2b8226..b99bc6be7 100644 --- a/contract/p/gnoswap/int256/conversion_test.gno +++ b/contract/p/gnoswap/int256/conversion_test.gno @@ -1,540 +1,244 @@ package int256 import ( + "runtime" + "strconv" + "strings" "testing" - - "gno.land/p/nt/ufmt" - - "gno.land/p/gnoswap/uint256" + "time" ) -func TestSetInt64(t *testing.T) { +func TestFromDecimal(t *testing.T) { tests := []struct { - x int64 - want string + input string + wantErr bool + want string }{ - {0, "0"}, - {1, "1"}, - {-1, "-1"}, - {9223372036854775807, "9223372036854775807"}, - {-9223372036854775808, "-9223372036854775808"}, + {"0", false, "0"}, + {"1", false, "1"}, + {"-1", false, "-1"}, + {"123456789", false, "123456789"}, + {"-123456789", false, "-123456789"}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", false, "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", false, "-57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + // overflow cases + {"57896044618658097711785492504343953926634992332820282019728792003956564819968", true, ""}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819969", true, ""}, + // invalid inputs + {"", true, ""}, + {"abc", true, ""}, + {"12.34", true, ""}, } for _, tc := range tests { - var z Int - z.SetInt64(tc.x) - - got := z.ToString() - if got != tc.want { - t.Errorf("SetInt64(%d) = %s, want %s", tc.x, got, tc.want) + z, err := FromDecimal(tc.input) + if tc.wantErr { + if err == nil { + t.Errorf("FromDecimal(%q) expected error, got nil", tc.input) + } + continue + } + if err != nil { + t.Errorf("FromDecimal(%q) unexpected error: %v", tc.input, err) + continue + } + if z.ToString() != tc.want { + t.Errorf("FromDecimal(%q) = %s, want %s", tc.input, z.ToString(), tc.want) } } } -func TestSetInt64MinValueOverflow(t *testing.T) { - const minInt64 = -9223372036854775808 // -2^63 - const maxInt64 = 9223372036854775807 // 2^63 - 1 - - tests := []struct { - name string - x int64 - want string - }{ - { - name: "MinInt64 should not cause overflow", - x: minInt64, - want: "-9223372036854775808", - }, - { - name: "MaxInt64 works correctly", - x: maxInt64, - want: "9223372036854775807", - }, - { - name: "MinInt64 + 1", - x: minInt64 + 1, - want: "-9223372036854775807", - }, - { - name: "Negative one", - x: -1, - want: "-1", - }, - { - name: "Zero", - x: 0, - want: "0", - }, +func TestMustFromDecimal(t *testing.T) { + // Valid cases should not panic + validCases := []string{ + "0", "1", "-1", "123456789", + "57896044618658097711785492504343953926634992332820282019728792003956564819967", } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - var z Int - z.SetInt64(tc.x) - - got := z.ToString() - if got != tc.want { - t.Errorf("SetInt64(%d) = %s, want %s", tc.x, got, tc.want) - } - - // Verify the internal representation is correct - if tc.x < 0 { - if !z.neg { - t.Errorf("SetInt64(%d): expected neg=true, got neg=false", tc.x) - } - // Check magnitude for MinInt64 - if tc.x == minInt64 { - expectedMag := uint64(1 << 63) // 2^63 - gotMag := z.abs.Uint64() - if gotMag != expectedMag { - t.Errorf("SetInt64(%d): magnitude = %d, want %d", tc.x, gotMag, expectedMag) - } - } - } else if tc.x > 0 { - if z.neg { - t.Errorf("SetInt64(%d): expected neg=false, got neg=true", tc.x) - } - } else { // tc.x == 0 - if z.neg { - t.Errorf("SetInt64(0): expected neg=false (no -0), got neg=true") + for _, tc := range validCases { + func() { + defer func() { + if r := recover(); r != nil { + t.Errorf("MustFromDecimal(%q) panicked unexpectedly: %v", tc, r) } + }() + z := MustFromDecimal(tc) + if z == nil { + t.Errorf("MustFromDecimal(%q) returned nil", tc) } - }) - } -} - -func TestSetUint64(t *testing.T) { - tests := []struct { - x uint64 - want string - }{ - {0, "0"}, - {1, "1"}, - } - - for _, tc := range tests { - var z Int - z.SetUint64(tc.x) - - got := z.ToString() - if got != tc.want { - t.Errorf("SetUint64(%d) = %s, want %s", tc.x, got, tc.want) - } + }() } -} -func TestUint64(t *testing.T) { - tests := []struct { - x string - want uint64 - }{ - {"0", 0}, - {"1", 1}, - {"9223372036854775807", 9223372036854775807}, - {"9223372036854775808", 9223372036854775808}, - {"18446744073709551615", 18446744073709551615}, - {"18446744073709551616", 0}, - {"18446744073709551617", 1}, - {"-1", 1}, - {"-18446744073709551615", 18446744073709551615}, - {"-18446744073709551616", 0}, - {"-18446744073709551617", 1}, + // Invalid cases should panic + invalidCases := []string{ + "", "abc", "12.34", + "57896044618658097711785492504343953926634992332820282019728792003956564819968", } - for _, tc := range tests { - z := MustFromDecimal(tc.x) - - got := z.Uint64() - if got != tc.want { - t.Errorf("Uint64(%s) = %d, want %d", tc.x, got, tc.want) - } + for _, tc := range invalidCases { + func() { + defer func() { + if r := recover(); r == nil { + t.Errorf("MustFromDecimal(%q) should have panicked", tc) + } + }() + MustFromDecimal(tc) + }() } } -func TestInt64(t *testing.T) { - tests := []struct { - x string - want int64 - }{ - {"0", 0}, - {"1", 1}, - {"-1", -1}, - {"9223372036854775807", 9223372036854775807}, - {"-9223372036854775808", -9223372036854775808}, - {"9223372036854775808", -9223372036854775808}, - {"-9223372036854775809", 9223372036854775807}, - {"18446744073709551616", 0}, - {"18446744073709551617", 1}, - {"18446744073709551615", -1}, - {"-18446744073709551615", 1}, - } - - for _, tc := range tests { - z := MustFromDecimal(tc.x) - - got := z.Int64() - if got != tc.want { - t.Errorf("Int64(%s) = %d, want %d", tc.x, got, tc.want) +// readAllocatorBytes parses runtime.MemStats() into the current allocated bytes. +func readAllocatorBytes(t *testing.T) int64 { + t.Helper() + stats := runtime.MemStats() + if stats == "nil allocator" { + return 0 + } + if !strings.HasPrefix(stats, "Allocator{") || !strings.HasSuffix(stats, "}") { + t.Fatalf("unexpected runtime.MemStats output: %q", stats) + } + body := strings.TrimSuffix(strings.TrimPrefix(stats, "Allocator{"), "}") + parts := strings.Split(body, ", ") + if len(parts) != 2 { + t.Fatalf("unexpected runtime.MemStats content: %q", stats) + } + var ( + bytes int64 + found bool + ) + for _, part := range parts { + fields := strings.Split(part, ":") + if len(fields) != 2 { + t.Fatalf("unexpected runtime.MemStats pair %q", part) } - } -} - -func TestInt64EdgeCases(t *testing.T) { - const minInt64 = -9223372036854775808 // -2^63 - const maxInt64 = 9223372036854775807 // 2^63 - 1 - - tests := []struct { - name string - setupInt func() *Int - want int64 - description string - }{ - { - name: "MinInt64 from SetInt64", - setupInt: func() *Int { - z := new(Int) - return z.SetInt64(minInt64) - }, - want: minInt64, - description: "SetInt64(MinInt64) should round-trip correctly", - }, - { - name: "MaxInt64 from SetInt64", - setupInt: func() *Int { - z := new(Int) - return z.SetInt64(maxInt64) - }, - want: maxInt64, - description: "SetInt64(MaxInt64) should round-trip correctly", - }, - { - name: "Magnitude 2^63 with negative sign", - setupInt: func() *Int { - // Create Int with magnitude = 2^63 and neg = true - z := new(Int) - z.abs = uint256.NewUint(1 << 63) - z.neg = true - return z - }, - want: minInt64, - description: "Magnitude 2^63 with neg=true should return MinInt64", - }, - { - name: "Magnitude 2^63 with positive sign", - setupInt: func() *Int { - // Create Int with magnitude = 2^63 and neg = false - z := new(Int) - z.abs = uint256.NewUint(1 << 63) - z.neg = false - return z - }, - want: minInt64, // Wraps around due to two's complement - description: "Magnitude 2^63 with neg=false wraps to MinInt64", - }, - { - name: "Large positive value wrapping", - setupInt: func() *Int { - // 2^64 - 1 (max uint64) - z := new(Int) - z.abs = uint256.NewUint(18446744073709551615) - z.neg = false - return z - }, - want: -1, - description: "Max uint64 wraps to -1 in int64", - }, - { - name: "Negative large value", - setupInt: func() *Int { - // -(2^64 - 1) - z := new(Int) - z.abs = uint256.NewUint(18446744073709551615) - z.neg = true - return z - }, - want: 1, - description: "-(Max uint64) becomes 1 due to two's complement", - }, - { - name: "Zero", - setupInt: func() *Int { - return Zero() - }, - want: 0, - description: "Zero should return 0", - }, - { - name: "Negative zero prevention", - setupInt: func() *Int { - z := new(Int) - z.abs = uint256.NewUint(0) - z.neg = true // This should be normalized to false - return z - }, - want: 0, - description: "Negative zero should return 0", - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - z := tc.setupInt() - got := z.Int64() - - if got != tc.want { - t.Errorf("%s: got %d, want %d", tc.description, got, tc.want) + if fields[0] == "bytes" { + val, err := strconv.ParseInt(fields[1], 10, 64) + if err != nil { + t.Fatalf("failed to parse bytes from %q: %v", part, err) } - }) - } -} - -// TestInt64RoundTrip verifies that SetInt64 and Int64 work correctly together -func TestInt64RoundTrip(t *testing.T) { - // Test all interesting int64 values - values := []int64{ - 0, 1, -1, - 127, -128, // int8 boundaries - 32767, -32768, // int16 boundaries - 2147483647, -2147483648, // int32 boundaries - 9223372036854775807, -9223372036854775808, // int64 boundaries (MaxInt64, MinInt64) - 1234567890, -1234567890, + bytes = val + found = true + break + } } - - for _, v := range values { - t.Run(ufmt.Sprintf("RoundTrip_%d", v), func(t *testing.T) { - z := new(Int) - z.SetInt64(v) - - got := z.Int64() - if got != v { - t.Errorf("Round trip failed: SetInt64(%d).Int64() = %d", v, got) - } - }) + if !found { + t.Fatalf("bytes key not found in runtime.MemStats output: %q", stats) } + return bytes } -func TestNeg(t *testing.T) { - tests := []struct { - x string - want string - }{ - {"0", "0"}, - {"1", "-1"}, - {"-1", "1"}, - {"9223372036854775807", "-9223372036854775807"}, - {"-18446744073709551615", "18446744073709551615"}, - } - - for _, tc := range tests { - z := MustFromDecimal(tc.x) - z.Neg(z) - - got := z.ToString() - if got != tc.want { - t.Errorf("Neg(%s) = %s, want %s", tc.x, got, tc.want) - } - } +type MetricResult struct { + Name string + Iterations int + DurationNs int64 + AllocDelta int64 } -func TestInt_NegOverflow(t *testing.T) { - maxInt256 := MaxInt256() - minInt256 := MinInt256() - - negMaxInt256 := New().Neg(maxInt256) - - tests := []struct { - name string - x *Int - wantResult string - wantOverflow bool - }{ - { - name: "negate zero", - x: Zero(), - wantResult: "0", - wantOverflow: false, - }, - { - name: "negate positive", - x: NewInt(100), - wantResult: "-100", - wantOverflow: false, - }, - { - name: "negate negative", - x: NewInt(-100), - wantResult: "100", - wantOverflow: false, - }, - { - name: "negate max_int256", - x: maxInt256, - wantResult: negMaxInt256.ToString(), - wantOverflow: false, - }, - { - name: "negate min_int256", - x: minInt256, - wantResult: minInt256.ToString(), // must preserve the original value - wantOverflow: true, - }, - { - name: "negate (min_int256 + 1)", - x: new(Int).Add(minInt256, One()), - wantResult: new(Int).Sub(maxInt256, Zero()).ToString(), - wantOverflow: false, - }, - { - name: "negate (max_int256 - 1)", - x: MustFromDecimal("57896044618658097711785492504343953926634992332820282019728792003956564819966"), // max_int256 - 1 - wantResult: "-57896044618658097711785492504343953926634992332820282019728792003956564819966", - wantOverflow: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - z := new(Int) - gotResult, gotOverflow := z.NegOverflow(tt.x) - - if gotOverflow != tt.wantOverflow { - t.Errorf("overflow = %v, want %v", gotOverflow, tt.wantOverflow) - return - } - - if gotResult == nil { - t.Error("unexpected nil result") - return - } - - if gotResult.ToString() != tt.wantResult { - // use almost equal comparison to handle the precision issue - diff := new(Int).Sub(gotResult, MustFromDecimal(tt.wantResult)) - if diff.Abs().Cmp(uint256.NewUint(1)) > 0 { - t.Errorf("result = %v, want %v", gotResult.ToString(), tt.wantResult) - } - } +// runMetric executes fn the given number of iterations while logging elapsed time and allocation deltas. +func runMetric(t *testing.T, name string, iterations int, fn func()) MetricResult { + t.Helper() - // double negation test (only if there is no overflow) - if !tt.wantOverflow { - doubleNegResult, doubleNegOverflow := new(Int).NegOverflow(gotResult) - if doubleNegOverflow { - t.Error("unexpected overflow in double negation") - } - if doubleNegResult.ToString() != tt.x.ToString() { - t.Errorf("double negation result = %v, want %v", - doubleNegResult.ToString(), tt.x.ToString()) - } - } - }) + // Skip GC if allocator is nil to avoid panic + stats := runtime.MemStats() + if stats != "nil allocator" { + runtime.GC() } -} - -func TestSet(t *testing.T) { - tests := []struct { - x string - want string - }{ - {"0", "0"}, - {"1", "1"}, - {"-1", "-1"}, - {"9223372036854775807", "9223372036854775807"}, - {"-18446744073709551615", "-18446744073709551615"}, + beforeBytes := readAllocatorBytes(t) + start := time.Now() + for i := 0; i < iterations; i++ { + fn() } + elapsed := time.Since(start) + afterBytes := readAllocatorBytes(t) - for _, tc := range tests { - z := MustFromDecimal(tc.x) - z.Set(z) - - got := z.ToString() - if got != tc.want { - t.Errorf("set(%s) = %s, want %s", tc.x, got, tc.want) - } + return MetricResult{ + Name: name, + Iterations: iterations, + DurationNs: elapsed.Nanoseconds(), + AllocDelta: afterBytes - beforeBytes, } } -func TestSetUint256(t *testing.T) { - tests := []struct { - x string - want string - }{ - {"0", "0"}, - {"1", "1"}, - {"9223372036854775807", "9223372036854775807"}, - {"18446744073709551615", "18446744073709551615"}, - } - - for _, tc := range tests { - got := New() +func TestPublicFunctionRuntimeMetrics(t *testing.T) { + const iterations = 200 + var results []MetricResult - z := uint256.MustFromDecimal(tc.x) - got.SetUint256(z) + // Test data + commonDecimal := "1234567890123456789012345678901234567890" - if got.ToString() != tc.want { - t.Errorf("SetUint256(%s) = %s, want %s", tc.x, got.ToString(), tc.want) - } - } -} - -func TestToString(t *testing.T) { tests := []struct { - name string - setup func() *Int - expected string + name string + run func(t *testing.T) MetricResult }{ { - name: "Zero from subtraction", - setup: func() *Int { - minusThree := MustFromDecimal("-3") - three := MustFromDecimal("3") - return Zero().Add(minusThree, three) + name: "FromDecimal", + run: func(t *testing.T) MetricResult { + return runMetric(t, "FromDecimal", iterations, func() { + if _, err := FromDecimal(commonDecimal); err != nil { + t.Fatalf("FromDecimal error: %v", err) + } + }) }, - expected: "0", }, { - name: "Zero from right shift", - setup: func() *Int { - return Zero().Rsh(One(), 1234) + name: "SetString", + run: func(t *testing.T) MetricResult { + return runMetric(t, "SetString", iterations, func() { + var target Int + if _, err := target.SetString(commonDecimal); err != nil { + t.Fatalf("SetString error: %v", err) + } + }) }, - expected: "0", }, { - name: "Positive number", - setup: func() *Int { - return MustFromDecimal("42") + name: "MustFromDecimal", + run: func(t *testing.T) MetricResult { + return runMetric(t, "MustFromDecimal", iterations, func() { + MustFromDecimal(commonDecimal) + }) }, - expected: "42", }, { - name: "Negative number", - setup: func() *Int { - return MustFromDecimal("-42") + name: "MaxInt256", + run: func(t *testing.T) MetricResult { + return runMetric(t, "MaxInt256", iterations, func() { + MaxInt256() + }) }, - expected: "-42", }, { - name: "Large positive number", - setup: func() *Int { - return MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935") + name: "MinInt256", + run: func(t *testing.T) MetricResult { + return runMetric(t, "MinInt256", iterations, func() { + MinInt256() + }) }, - expected: "115792089237316195423570985008687907853269984665640564039457584007913129639935", }, { - name: "Large negative number", - setup: func() *Int { - return MustFromDecimal("-115792089237316195423570985008687907853269984665640564039457584007913129639935") + name: "ToString", + run: func(t *testing.T) MetricResult { + largeInt := MustFromDecimal("57896044618658097711785492504343953926634992332820282019728792003956564819967") // max int256 + return runMetric(t, "ToString", iterations, func() { + _ = largeInt.ToString() + }) }, - expected: "-115792089237316195423570985008687907853269984665640564039457584007913129639935", }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - z := tt.setup() - result := z.ToString() - if result != tt.expected { - t.Errorf("ToString() = %s, want %s", result, tt.expected) - } + for i := range tests { + test := tests[i] + t.Run(test.name, func(t *testing.T) { + result := test.run(t) + results = append(results, result) }) } + + // Print results as markdown table + t.Log("\n## Runtime Metrics Results\n") + t.Log("| Function | Iterations | Duration (ns) | Alloc Delta (bytes) |") + t.Log("|----------|------------|---------------|---------------------|") + for _, result := range results { + t.Logf("| %s | %d | %d | %d |", result.Name, result.Iterations, result.DurationNs, result.AllocDelta) + } } diff --git a/contract/p/gnoswap/int256/int256.gno b/contract/p/gnoswap/int256/int256.gno index 3cd68d291..29312591d 100644 --- a/contract/p/gnoswap/int256/int256.gno +++ b/contract/p/gnoswap/int256/int256.gno @@ -1,182 +1,620 @@ package int256 import ( + "encoding/binary" "errors" + "math" + "math/bits" - "gno.land/p/gnoswap/uint256" + u256 "gno.land/p/gnoswap/uint256" ) +type Int [4]uint64 + var ( - errEmptyString = errors.New("empty string") - errStringContainsOnlySign = errors.New("string contains only sign") - errInvalidSignInMiddleOfNumber = errors.New("invalid sign in middle of number") + MinI256 = &Int{0, 0, 0, 0x8000000000000000} + MaxI256 = &Int{0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff} + + ErrZeroDivision = errors.New("zero division") + ErrNegativeNum = errors.New("negative number") ) -var one = uint256.NewUint(1) +func Zero() *Int { + return &Int{} +} -// Int represents a 256-bit signed integer. -// It uses a sign-magnitude representation with abs storing the absolute value -// and neg indicating whether the number is negative. -type Int struct { - abs *uint256.Uint - neg bool +func One() *Int { + return &Int{1, 0, 0, 0} } -// Zero returns a new Int set to 0. -func Zero() *Int { - return NewInt(0) +func MinInt256() *Int { + return &Int{0, 0, 0, 0x8000000000000000} } -// One returns a new Int set to 1. -func One() *Int { - return NewInt(1) +func MaxInt256() *Int { + return &Int{0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0x7fffffffffffffff} } -// Sign returns the sign of x. -// It returns -1 if x < 0, 0 if x == 0, and +1 if x > 0. -func (z *Int) Sign() int { - z.initiateAbs() +func NewInt(val int64) *Int { + z := &Int{} + z.SetInt64(val) + return z +} + +func (z *Int) Set(x *Int) *Int { + z[0], z[1], z[2], z[3] = x[0], x[1], x[2], x[3] + return z +} + +func (z *Int) SetInt64(x int64) *Int { + if x >= 0 { + z[3], z[2], z[1], z[0] = 0, 0, 0, uint64(x) + return z + } + + z[3], z[2], z[1], z[0] = 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, uint64(x) + return z +} + +func (z *Int) IsInt64() bool { + return ((z[1]|z[2]|z[3]) == 0 && z[0] <= 0x7fffffffffffffff) || // zero or positive int64 + ((z[1]&z[2]&z[3]) == 0xffffffffffffffff && z[0] >= 0x8000000000000000) // negative int64 +} - if z.abs.IsZero() { +func (z *Int) Int64() int64 { + s := z.Sign() + if s == 0 { return 0 } - if z.neg { - return -1 + if s > 0 { + // overflow when z[0] > math.MaxInt64 + return int64(z[0]) } - return 1 + // -(2^64 - z[0]) + return -int64(math.MaxUint64 - z[0] + 1) +} + +func (z *Int) SetUint64(x uint64) *Int { + z[3], z[2], z[1], z[0] = 0, 0, 0, x + return z +} + +func (z *Int) IsUint64() bool { + return (z[1] | z[2] | z[3]) == 0 +} + +func (z *Int) Uint64() uint64 { + return z[0] +} + +func (z *Int) Abs() *u256.Uint { + if z.IsNeg() { + if z[3] == 0x8000000000000000 && z[2] == 0 && z[1] == 0 && z[0] == 0 { + panic("int256: overflow") + } + neg := new(Int).Neg(z) + return &u256.Uint{neg[0], neg[1], neg[2], neg[3]} + } + return &u256.Uint{z[0], z[1], z[2], z[3]} } -// New returns a new Int set to 0. -func New() *Int { - return &Int{ - abs: new(uint256.Uint), +func (z *Int) Sign() int { + if z.IsZero() { + return 0 } + if z[3]&0x8000000000000000 == 0 { + return 1 + } + return -1 +} + +func (z *Int) IsZero() bool { + return (z[0] | z[1] | z[2] | z[3]) == 0 +} + +func (z *Int) IsOne() bool { + return (z[0] == 1) && (z[1]|z[2]|z[3]) == 0 } -// NewInt allocates and returns a new Int set to x. -func NewInt(x int64) *Int { - return New().SetInt64(x) +func (z *Int) IsNeg() bool { + return z[3]&0x8000000000000000 != 0 } -// FromDecimal returns a new Int from a decimal string and an error if the string is not valid. -func FromDecimal(s string) (*Int, error) { - return new(Int).SetString(s) +func (z *Int) IsPositive() bool { + return (z[3]&0x8000000000000000) == 0 && (z[3]|z[2]|z[1]|z[0]) != 0 } -// MustFromDecimal returns a new Int from a decimal string. -// Panics if the string is not a valid decimal. -func MustFromDecimal(s string) *Int { - z, err := FromDecimal(s) - if err != nil { - panic(err) +func (z *Int) IsMinI256() bool { + return (z[3] == 0x8000000000000000) && ((z[2] | z[1] | z[0]) == 0) +} + +func (z *Int) Neg(x *Int) *Int { + var carry uint64 + z[0], z[1], z[2], z[3] = ^x[0], ^x[1], ^x[2], ^x[3] + z[0], carry = bits.Add64(z[0], 1, 0) + z[1], carry = bits.Add64(z[1], 0, carry) + z[2], carry = bits.Add64(z[2], 0, carry) + z[3] += carry + return z +} + +func (z *Int) Eq(x *Int) bool { + return (z[0] == x[0]) && (z[1] == x[1]) && (z[2] == x[2]) && (z[3] == x[3]) +} + +func (z *Int) Neq(x *Int) bool { + return !z.Eq(x) +} + +func (z *Int) Add(x, y *Int) *Int { + var carry uint64 + z[0], carry = bits.Add64(x[0], y[0], 0) + z[1], carry = bits.Add64(x[1], y[1], carry) + z[2], carry = bits.Add64(x[2], y[2], carry) + z[3] = x[3] + y[3] + carry + return z +} + +func (z *Int) AddOverflow(x, y *Int) (*Int, bool) { + var carry uint64 + z[0], carry = bits.Add64(x[0], y[0], 0) + z[1], carry = bits.Add64(x[1], y[1], carry) + z[2], carry = bits.Add64(x[2], y[2], carry) + z[3] = x[3] + y[3] + carry + var overflow bool + signX, signY, signZ := x.Sign(), y.Sign(), z.Sign() + if (signX == signY) && (signX != signZ) { + overflow = true } + return z, overflow +} + +func (z *Int) Sub(x, y *Int) *Int { + var carry uint64 + z[0], carry = bits.Sub64(x[0], y[0], 0) + z[1], carry = bits.Sub64(x[1], y[1], carry) + z[2], carry = bits.Sub64(x[2], y[2], carry) + z[3] = x[3] - y[3] - carry return z } -// SetString sets z to the value of s and returns z and an error. -// It uses a bit masking technique for efficient sign character detection, -// based on "Bit Twiddling Hacks" by Sean Eron Anderson. -// This approach provides significantly better performance than string scanning methods. -func (z *Int) SetString(s string) (*Int, error) { - if len(s) == 0 { - return nil, errEmptyString +func (z *Int) SubOverflow(x, y *Int) (*Int, bool) { + var carry uint64 + z[0], carry = bits.Sub64(x[0], y[0], 0) + z[1], carry = bits.Sub64(x[1], y[1], carry) + z[2], carry = bits.Sub64(x[2], y[2], carry) + z[3] = x[3] - y[3] - carry + var overflow bool + signX, signY, signZ := x.Sign(), y.Sign(), z.Sign() + if (signX == 0 && y.IsMinI256()) || ((signX != 0) && (signX != signY) && (signX != signZ)) { + overflow = true } + return z, overflow +} + +func (z *Int) Mul(x, y *Int) *Int { + var ( + res Int + carry uint64 + res1, res2, res3 uint64 + ) + + carry, res[0] = bits.Mul64(x[0], y[0]) + carry, res1 = umulHop(carry, x[1], y[0]) + carry, res2 = umulHop(carry, x[2], y[0]) + res3 = x[3]*y[0] + carry + + carry, res[1] = umulHop(res1, x[0], y[1]) + carry, res2 = umulStep(res2, x[1], y[1], carry) + res3 = res3 + x[2]*y[1] + carry + + carry, res[2] = umulHop(res2, x[0], y[2]) + res3 = res3 + x[1]*y[2] + carry + + res[3] = res3 + x[0]*y[3] - // check sign only in the first character - neg := false - switch s[0] { - case '+': - s = s[1:] - case '-': - neg = true - s = s[1:] + return z.Set(&res) +} + +func (z *Int) MulOverflow(x, y *Int) (*Int, bool) { + if (x.IsMinI256() && y.IsOne()) || (x.IsOne() && y.IsMinI256()) { + return z.Set(MinI256), false } - // check if the string is empty after removing the sign - if len(s) == 0 { - return nil, errStringContainsOnlySign + var flipSign bool + xSign, ySign := x.Sign(), y.Sign() + if xSign*ySign == -1 { + flipSign = true + } + + xCopy := x.Clone() + yCopy := y.Clone() + + if xSign < 0 { + xCopy.Neg(xCopy) + } + if ySign < 0 { + yCopy.Neg(yCopy) } - // Parallel comparison technique for sign detection - // Process in 8-byte chunks - sLen := len(s) - i := 0 + p := umul(xCopy, yCopy) + z[0], z[1], z[2], z[3] = p[0], p[1], p[2], p[3] - // Process 8 bytes at a time - for i+7 < sLen { - // Convert 8 bytes into a single uint64 - // This method processes bytes directly, so no endianness issues - // - // access up to s[i+7] is safe, then we can reduce the number of bounds checks - _ = s[i+7] - chunk := uint64(s[i]) | uint64(s[i+1])<<8 - chunk |= uint64(s[i+2])<<16 | uint64(s[i+3])<<24 - chunk |= uint64(s[i+4])<<32 | uint64(s[i+5])<<40 - chunk |= uint64(s[i+6])<<48 | uint64(s[i+7])<<56 + var overflow bool + if ((p[4] | p[5] | p[6] | p[7]) != 0) || z.IsNeg() { + overflow = true + } - // Operation to check for '+' (0x2B) - // Subtracting 0x2B from each byte makes '+' bytes become 0 - // Subtracting 0x01 makes bytes in ASCII range (0-127) have 0 in their highest bit - // Therefore, AND with 0x80 to check for zero bytes - plusTest := ((chunk ^ 0x2B2B2B2B2B2B2B2B) - 0x0101010101010101) & 0x8080808080808080 + if flipSign { + z.Neg(z) + } + + return z, overflow +} - // check for '-' (0x2D) - minusTest := ((chunk ^ 0x2D2D2D2D2D2D2D2D) - 0x0101010101010101) & 0x8080808080808080 +func umul(x, y *Int) [8]uint64 { + var ( + res [8]uint64 + carry, carry4, carry5, carry6 uint64 + res1, res2, res3, res4, res5 uint64 + ) - // If either test is non-zero, a sign character exists - if (plusTest | minusTest) != 0 { - return nil, errInvalidSignInMiddleOfNumber + carry, res[0] = bits.Mul64(x[0], y[0]) + carry, res1 = umulHop(carry, x[1], y[0]) + carry, res2 = umulHop(carry, x[2], y[0]) + carry4, res3 = umulHop(carry, x[3], y[0]) + + carry, res[1] = umulHop(res1, x[0], y[1]) + carry, res2 = umulStep(res2, x[1], y[1], carry) + carry, res3 = umulStep(res3, x[2], y[1], carry) + carry5, res4 = umulStep(carry4, x[3], y[1], carry) + + carry, res[2] = umulHop(res2, x[0], y[2]) + carry, res3 = umulStep(res3, x[1], y[2], carry) + carry, res4 = umulStep(res4, x[2], y[2], carry) + carry6, res5 = umulStep(carry5, x[3], y[2], carry) + + carry, res[3] = umulHop(res3, x[0], y[3]) + carry, res[4] = umulStep(res4, x[1], y[3], carry) + carry, res[5] = umulStep(res5, x[2], y[3], carry) + res[7], res[6] = umulStep(carry6, x[3], y[3], carry) + + return res +} + +func umulStep(z, x, y, carry uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry = bits.Add64(lo, carry, 0) + hi += carry + lo, carry = bits.Add64(lo, z, 0) + hi += carry + return hi, lo +} + +func umulHop(z, x, y uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry := bits.Add64(lo, z, 0) + hi += carry + return hi, lo +} + +func (z *Int) Clear() *Int { + z[0], z[1], z[2], z[3] = 0, 0, 0, 0 + return z +} + +func (z *Int) SetOne() *Int { + z[3], z[2], z[1], z[0] = 0, 0, 0, 1 + return z +} + +func (z *Int) SetAllBitsOne() *Int { + z[0], z[1], z[2], z[3] = 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff + return z +} + +func (z *Int) Div(x, y *Int) *Int { + if x.Sign() > 0 { + if y.Sign() > 0 { + return z.uquo(x, y) } + z.uquo(x, new(Int).Neg(y)) + return z.Neg(z) + } + if y.Sign() < 0 { + return z.uquo(new(Int).Neg(x), new(Int).Neg(y)) + } + z.uquo(new(Int).Neg(x), y) + return z.Neg(z) +} - i += 8 +func (z *Int) uquo(x, y *Int) *Int { + if y.IsZero() { + panic(ErrZeroDivision) + } + if x.IsZero() { + return z.Clear() + } + if x.Eq(y) { + return z.SetOne() } + if x.IsInt64() && y.IsInt64() { + return z.SetInt64(x.Int64() / y.Int64()) + } + quot := Int{} + udivrem(quot[:], x[:], y) + return z.Set(") +} - // Process remaining bytes - for ; i < sLen; i++ { - if s[i] == '+' || s[i] == '-' { - return nil, errInvalidSignInMiddleOfNumber +func (z *Int) Rem(x, y *Int) *Int { + if x.Sign() > 0 { + if y.Sign() > 0 { + return z.urem(x, y) } + return z.urem(x, new(Int).Neg(y)) + } + if y.Sign() < 0 { + z.urem(new(Int).Neg(x), new(Int).Neg(y)) + return z.Neg(z) } + z.urem(new(Int).Neg(x), y) + return z.Neg(z) +} - abs, err := uint256.FromDecimal(s) - if err != nil { - return nil, err +func (z *Int) urem(x, y *Int) *Int { + if y.IsZero() { + panic(ErrZeroDivision) + } + if x.IsZero() { + return z.Clear() } + if x.Eq(y) { + return z.Clear() + } + if x.IsInt64() && y.IsInt64() { + xInt64 := x.Int64() + yInt64 := y.Int64() + return z.SetInt64(xInt64 % yInt64) + } + quot := Int{} + rem := udivrem(quot[:], x[:], y) + return z.Set(&rem) +} + +func (z *Int) Lt(x *Int) bool { + return z.Cmp(x) < 0 +} - return &Int{abs, neg}, nil +func (z *Int) Lte(x *Int) bool { + return z.Cmp(x) <= 0 } -// FromUint256 creates a new Int from a uint256.Uint. -// It returns nil if the input is nil. -func FromUint256(x *uint256.Uint) *Int { - if x == nil { - return nil +func (z *Int) Gt(x *Int) bool { + return z.Cmp(x) > 0 +} + +func (z *Int) Gte(x *Int) bool { + return z.Cmp(x) >= 0 +} + +func (z *Int) Cmp(x *Int) int { + zneg := int8(z[3] >> 63) + xneg := int8(x[3] >> 63) + if zneg != xneg { + return int(xneg - zneg) } - z := Zero() + d0, carry := bits.Sub64(z[0], x[0], 0) + d1, carry := bits.Sub64(z[1], x[1], carry) + d2, carry := bits.Sub64(z[2], x[2], carry) + d3, carry := bits.Sub64(z[3], x[3], carry) + if carry == 1 { + return -1 + } + if d0|d1|d2|d3 == 0 { + return 0 + } + return 1 +} - z.SetUint256(x) +func (z *Int) Clone() *Int { + return &Int{z[0], z[1], z[2], z[3]} +} + +func (z *Int) Or(x, y *Int) *Int { + z[0] = x[0] | y[0] + z[1] = x[1] | y[1] + z[2] = x[2] | y[2] + z[3] = x[3] | y[3] return z } -// NilToZero returns z if it's non-nil, otherwise returns a new Int set to 0. -// This differs from the original mempooler int256 implementation. -func (z *Int) NilToZero() *Int { - if z == nil { - return NewInt(0) +func (z *Int) And(x, y *Int) *Int { + z[0] = x[0] & y[0] + z[1] = x[1] & y[1] + z[2] = x[2] & y[2] + z[3] = x[3] & y[3] + return z +} + +func (z *Int) Xor(x, y *Int) *Int { + z[0] = x[0] ^ y[0] + z[1] = x[1] ^ y[1] + z[2] = x[2] ^ y[2] + z[3] = x[3] ^ y[3] + return z +} + +func (z *Int) Not(x *Int) *Int { + z[0] = ^x[0] + z[1] = ^x[1] + z[2] = ^x[2] + z[3] = ^x[3] + return z +} + +func (z *Int) Lsh(x *Int, n uint) *Int { + if n == 0 { + return z.Set(x) + } + if n >= 256 { + return z.Clear() + } + // Handle exact multiples of 64 separately to avoid 64-bit shift issues + if n&0x3f == 0 { + switch n { + case 64: + z[3], z[2], z[1], z[0] = x[2], x[1], x[0], 0 + case 128: + z[3], z[2], z[1], z[0] = x[1], x[0], 0, 0 + case 192: + z[3], z[2], z[1], z[0] = x[0], 0, 0, 0 + } + return z + } + switch { + case n > 192: + n -= 192 + z[3], z[2], z[1], z[0] = x[0]< 128: + n -= 128 + z[3] = (x[1] << n) | (x[0] >> (64 - n)) + z[2] = x[0] << n + z[1], z[0] = 0, 0 + case n > 64: + n -= 64 + z[3] = (x[2] << n) | (x[1] >> (64 - n)) + z[2] = (x[1] << n) | (x[0] >> (64 - n)) + z[1] = x[0] << n + z[0] = 0 + default: + z[3] = (x[3] << n) | (x[2] >> (64 - n)) + z[2] = (x[2] << n) | (x[1] >> (64 - n)) + z[1] = (x[1] << n) | (x[0] >> (64 - n)) + z[0] = x[0] << n } return z } -// initiateAbs ensures z and z.abs are initialized. -// If z is nil, it returns a new Int set to 0. -// If z.abs is nil, it initializes it to a new uint256.Uint. -// This differs from mempooler int256 by also checking if z itself is nil. -func (z *Int) initiateAbs() *Int { - if z == nil { - return NewInt(0) +func (z *Int) Rsh(x *Int, n uint) *Int { + if n == 0 { + return z.Set(x) + } + if x.IsNeg() { + return z.negRsh(x, n) + } + return z.rsh(x, n) +} + +func (z *Int) rsh(x *Int, n uint) *Int { + if n >= 256 { + return z.Clear() + } + // Handle exact multiples of 64 separately to avoid 64-bit shift issues + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + z[3], z[2], z[1], z[0] = 0, x[3], x[2], x[1] + case 128: + z[3], z[2], z[1], z[0] = 0, 0, x[3], x[2] + case 192: + z[3], z[2], z[1], z[0] = 0, 0, 0, x[3] + } + return z } - if z.abs == nil { - z.abs = new(uint256.Uint) + switch { + case n > 192: + n -= 192 + z[3], z[2], z[1], z[0] = 0, 0, 0, x[3]>>n + case n > 128: + n -= 128 + z[3], z[2] = 0, 0 + z[1] = x[3] >> n + z[0] = (x[3] << (64 - n)) | (x[2] >> n) + case n > 64: + n -= 64 + z[3] = 0 + z[2] = x[3] >> n + z[1] = (x[3] << (64 - n)) | (x[2] >> n) + z[0] = (x[2] << (64 - n)) | (x[1] >> n) + default: + z[3] = x[3] >> n + z[2] = (x[3] << (64 - n)) | (x[2] >> n) + z[1] = (x[2] << (64 - n)) | (x[1] >> n) + z[0] = (x[1] << (64 - n)) | (x[0] >> n) + } + return z +} + +func (z *Int) negRsh(x *Int, n uint) *Int { + if n >= 256 { + return z.SetAllBitsOne() + } + var v uint64 = 0xffffffffffffffff + // Handle exact multiples of 64 separately to avoid 64-bit shift issues + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + z[3], z[2], z[1], z[0] = v, x[3], x[2], x[1] + case 128: + z[3], z[2], z[1], z[0] = v, v, x[3], x[2] + case 192: + z[3], z[2], z[1], z[0] = v, v, v, x[3] + } + return z + } + switch { + case n > 192: + n -= 192 + z[3], z[2], z[1], z[0] = v, v, v, (v<<(64-n))|(x[3]>>n) + case n > 128: + n -= 128 + z[3], z[2] = v, v + z[1] = (v << (64 - n)) | (x[3] >> n) + z[0] = (x[3] << (64 - n)) | (x[2] >> n) + case n > 64: + n -= 64 + z[3] = v + z[2] = (v << (64 - n)) | (x[3] >> n) + z[1] = (x[3] << (64 - n)) | (x[2] >> n) + z[0] = (x[2] << (64 - n)) | (x[1] >> n) + default: + z[3] = (v << (64 - n)) | (x[3] >> n) + z[2] = (x[3] << (64 - n)) | (x[2] >> n) + z[1] = (x[2] << (64 - n)) | (x[1] >> n) + z[0] = (x[1] << (64 - n)) | (x[0] >> n) + } + return z +} + +func (z *Int) BitLen() int { + switch { + case z[3] != 0: + return 192 + bits.Len64(z[3]) + case z[2] != 0: + return 128 + bits.Len64(z[2]) + case z[1] != 0: + return 64 + bits.Len64(z[1]) + default: + return bits.Len64(z[0]) + } +} + +func (z *Int) SetBytes32(in []byte) *Int { + _ = in[31] // bounds check hint to compiler; see golang.org/issue/14808 + z[3] = binary.BigEndian.Uint64(in[0:8]) + z[2] = binary.BigEndian.Uint64(in[8:16]) + z[1] = binary.BigEndian.Uint64(in[16:24]) + z[0] = binary.BigEndian.Uint64(in[24:32]) + return z +} + +func (z *Int) NilToZero() *Int { + if z == nil { + return Zero() } return z } diff --git a/contract/p/gnoswap/int256/int256_test.gno b/contract/p/gnoswap/int256/int256_test.gno index f8e3060be..b67d7f2ae 100644 --- a/contract/p/gnoswap/int256/int256_test.gno +++ b/contract/p/gnoswap/int256/int256_test.gno @@ -1,25 +1,1513 @@ -// ported from github.com/mempooler/int256 package int256 import ( "testing" + + "gno.land/p/nt/uassert" + "gno.land/p/nt/ufmt" ) -func TestSign(t *testing.T) { +func TestSign(t *testing.T) { + tests := []struct { + x string + want int + }{ + {"0", 0}, + {"1", 1}, + {"-1", -1}, + } + + for _, tt := range tests { + z := MustFromDecimal(tt.x) + got := z.Sign() + if got != tt.want { + t.Errorf("Sign(%s) = %d, want %d", tt.x, got, tt.want) + } + } +} + +func TestAbs(t *testing.T) { + tests := []struct { + x, want string + }{ + {"0", "0"}, + {"1", "1"}, + {"-1", "1"}, + {"-2", "2"}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.Abs() + + if got.ToString() != tc.want { + t.Errorf("Abs(%s) = %v, want %v", tc.x, got.ToString(), tc.want) + } + } +} + +func TestAbsWithPanicCases(t *testing.T) { + tests := []struct { + x, want string + wantErr bool + wantPanic bool + }{ + {"0", "0", false, false}, + {"1", "1", false, false}, + {"-1", "1", false, false}, + {"-2", "2", false, false}, + // Int256 min value overflow: out of range, FromDecimal should error + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "", true, false}, + // Int256 most negative value: FromDecimal succeeds, Abs() panics + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", "57896044618658097711785492504343953926634992332820282019728792003956564819968", false, true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + + // Check if FromDecimal returned expected error + if tc.wantErr { + if err == nil { + t.Errorf("FromDecimal(%s) expected error, but got nil", tc.x) + } + continue + } + + // If we expect no error, but got one + if err != nil { + t.Errorf("FromDecimal(%s) returned unexpected error: %v", tc.x, err) + continue + } + + // At this point, x should not be nil + if x == nil { + t.Errorf("FromDecimal(%s) returned nil without error", tc.x) + continue + } + + if tc.wantPanic { + uassert.PanicsContains(t, "int256: overflow", func() { + x.Abs() + }) + ufmt.Printf("Abs(%s) correctly panicked with overflow\n", tc.x) + } else { + got := x.Abs() + if got.ToString() != tc.want { + t.Errorf("Abs(%s) = %v, want %v", tc.x, got.ToString(), tc.want) + } + } + } +} + +func TestAdd(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + }{ + { + name: "large negative + small negative overflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564606844", + y: "-431294739547329532759", + expected: "57896044618658097711785492504343953926634992332820282019297497264409235500333", + }, + { + name: "min negative + min negative overflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + }, + { + name: "max positive + min negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "-1", + }, + { + name: "small positive + large negative", + x: "10000", + y: "-2000000", + expected: "-1990000", + }, + { + name: "max positive + max positive overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "-2", + }, + { + name: "large positive + large positive overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819961", + y: "431405283104328105143242031570231414", + expected: "-57896044618658097711785492504343953926634560927537177691623648761924994588561", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Add(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestAddOverflow(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + expectedOverflow bool + }{ + { + name: "large negative + small negative overflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564606844", + y: "-431294739547329532759", + expected: "57896044618658097711785492504343953926634992332820282019297497264409235500333", + expectedOverflow: true, + }, + { + name: "min negative + min negative overflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + expectedOverflow: true, + }, + { + name: "max positive + min negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "-1", + expectedOverflow: false, + }, + { + name: "small positive + large negative", + x: "10000", + y: "-2000000", + expected: "-1990000", + expectedOverflow: false, + }, + { + name: "max positive + max positive overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "-2", + expectedOverflow: true, + }, + { + name: "large positive + large positive overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819961", + y: "431405283104328105143242031570231414", + expected: "-57896044618658097711785492504343953926634560927537177691623648761924994588561", + expectedOverflow: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z, overflow := new(Int).AddOverflow(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + uassert.Equal(t, tt.expectedOverflow, overflow) + }) + } +} + +func TestSub(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + }{ + { + name: "min negative - positive underflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "100", + expected: "57896044618658097711785492504343953926634992332820282019728792003956564819868", + }, + { + name: "zero - positive", + x: "0", + y: "9999", + expected: "-9999", + }, + { + name: "max positive - min negative overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "-1", + }, + { + name: "min negative - max positive underflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "1", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Sub(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestSubOverflow(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + expectedOverflow bool + }{ + { + name: "min negative - positive underflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "100", + expected: "57896044618658097711785492504343953926634992332820282019728792003956564819868", + expectedOverflow: true, + }, + { + name: "zero - positive", + x: "0", + y: "9999", + expected: "-9999", + expectedOverflow: false, + }, + { + name: "max positive - min negative overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "-1", + expectedOverflow: true, + }, + { + name: "min negative - max positive underflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "1", + expectedOverflow: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z, overflow := new(Int).SubOverflow(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + uassert.Equal(t, tt.expectedOverflow, overflow) + }) + } +} + +func TestMulOverFlow(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + expectedOverflow bool + }{ + { + name: "positive * negative", + x: "100", + y: "-2", + expected: "-200", + expectedOverflow: false, + }, + { + name: "max positive * min negative overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expectedOverflow: true, + }, + { + name: "max positive * max positive overflow", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "1", + expectedOverflow: true, + }, + { + name: "min negative * min negative overflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + expectedOverflow: true, + }, + { + name: "zero * min negative", + x: "0", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + expectedOverflow: false, + }, + { + name: "large positive * large positive", + x: "412421424314214830214", + y: "491735014023482390148157914", + expected: "202802054868735010725494286098171881252370413596", + expectedOverflow: false, + }, + { + name: "small positive * small positive", + x: "12", + y: "12", + expected: "144", + expectedOverflow: false, + }, + { + name: "negative * positive", + x: "-500000000000000", + y: "5000000000000", + expected: "-2500000000000000000000000000", + expectedOverflow: false, + }, + { + name: "large negative * large positive", + x: "-412421424314214830214", + y: "491735014023482390148157914", + expected: "-202802054868735010725494286098171881252370413596", + expectedOverflow: false, + }, + { + name: "large positive * large negative overflow", + x: "202802054868735010725494286098171881252370413596", + y: "-202802054868735010725494286098171881252370413596", + expected: "17633904406147578101277522337283808353988667996273383726665993364273483857136", + expectedOverflow: true, + }, + { + name: "min negative * 1", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "1", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expectedOverflow: false, + }, + { + name: "min negative * -1 overflow", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-1", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expectedOverflow: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z, overflow := new(Int).MulOverflow(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + uassert.Equal(t, tt.expectedOverflow, overflow) + }) + } +} + +func TestMul(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + }{ + { + name: "positive * negative", + x: "100", + y: "-2", + expected: "-200", + }, + { + name: "max positive * min negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + }, + { + name: "max positive * max positive", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "1", + }, + { + name: "min negative * min negative", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + }, + { + name: "zero * min negative", + x: "0", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + }, + { + name: "large positive * large positive", + x: "412421424314214830214", + y: "491735014023482390148157914", + expected: "202802054868735010725494286098171881252370413596", + }, + { + name: "small positive * small positive", + x: "12", + y: "12", + expected: "144", + }, + { + name: "negative * positive", + x: "-500000000000000", + y: "5000000000000", + expected: "-2500000000000000000000000000", + }, + { + name: "large negative * large positive", + x: "-412421424314214830214", + y: "491735014023482390148157914", + expected: "-202802054868735010725494286098171881252370413596", + }, + { + name: "large positive * large negative overflow", + x: "202802054868735010725494286098171881252370413596", + y: "-202802054868735010725494286098171881252370413596", + expected: "17633904406147578101277522337283808353988667996273383726665993364273483857136", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Mul(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestDiv(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + shoudPanic bool + }{ + { + name: "positive / negative", + x: "100", + y: "-2", + expected: "-50", + }, + { + name: "min negative / positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "3194721952341", + expected: "-18122404854742851078740194629148605029554483750497671811411863963", + }, + { + name: "large positive / negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819961", + y: "-4134320184329015710493829104", + expected: "-14003764110508630918697735423914335136046266814161", + }, + { + name: "large negative / large negative", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819961", + y: "-4134320184329015710493829104", + expected: "14003764110508630918697735423914335136046266814161", + }, + { + name: "small negative / large negative", + x: "-578960446186580977117854925043439539266349923328202564819961", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: "0", + }, + { + name: "large / small", + x: "3214732915732914729142135321421", + y: "510482085320157124", + expected: "6297445117426", + }, + { + name: "same values", + x: "510482085320157124", + y: "510482085320157124", + expected: "1", + }, + { + name: "zero dividend", + x: "0", + y: "510482085320157124", + expected: "0", + }, + { + name: "division by zero", + x: "1", + y: "0", + expected: "", + shoudPanic: true, + }, + { + name: "min int256 / -1", + x: MinInt256().ToString(), + y: "-1", + expected: MinInt256().ToString(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.shoudPanic { + defer func() { + if r := recover(); r == nil { + t.Error("Div should panic on division by zero") + } + }() + } + + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Div(x, y) + if !tt.shoudPanic { + uassert.Equal(t, tt.expected, z.ToString()) + } + }) + } +} + +func TestOr(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + }{ + { + name: "min negative | negative", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-100", + expected: "-100", + }, + { + name: "max positive | positive", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "41457129491534261876432718654783265437285638275", + expected: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + }, + { + name: "min negative | max positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: "-1", + }, + { + name: "min negative | zero", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "0", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).Or(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestAnd(t *testing.T) { + tests := []struct { + name string + x string + y string + expected string + }{ + { + name: "min negative & negative", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-100", + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + }, + { + name: "max positive & positive", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "41457129491534261876432718654783265437285638275", + expected: "41457129491534261876432718654783265437285638275", + }, + { + name: "negative & small positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "7", + expected: "1", + }, + { + name: "min negative & zero", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "0", + expected: "0", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + z := new(Int).And(x, y) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestLsh(t *testing.T) { + tests := []struct { + name string + x string + n uint + expected string + }{ + { + name: "n is equal 0", + x: "100", + n: 0, + expected: "100", + }, + { + name: "n is greater than or equal 256", + x: "-4125871947195612497219427349", + n: 256, + expected: "0", + }, + { + name: "x is non-neg & n is smaller than 256 and greater than or equal 192", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 200, + expected: "-1606938044258990275541962092341162602522202993782792835301376", + }, + { + name: "x is non-neg & n is smaller than 192 and greater than or equal 128", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 150, + expected: "-1427247692705959881058285969449495136382746624", + }, + { + name: "x is non-neg & n is smaller than 128 and greater than or equal 64", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 100, + expected: "-1267650600228229401496703205376", + }, + { + name: "x is non-neg & n is smaller than 64", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 32, + expected: "-4294967296", + }, + { + name: "x is neg & n is smaller than 256 and greater than or equal 192", + x: "-11111", + n: 200, + expected: "-17854688609761640951546740808002657676624197463920611193033588736", + }, + { + name: "x is neg & n is smaller than 192 and greater than or equal 128", + x: "-11111", + n: 150, + expected: "-15858149113655920238438615406553340460348697739264", + }, + { + name: "x is neg & n is smaller than 128 and greater than or equal 64", + x: "-11111", + n: 100, + expected: "-14084865819135856880029869314932736", + }, + { + name: "x is neg & n is smaller than 64", + x: "-11111", + n: 32, + expected: "-47721381625856", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + z := new(Int).Lsh(x, tt.n) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestRsh(t *testing.T) { + tests := []struct { + name string + x string + n uint + expected string + }{ + { + name: "n is equal 0", + x: "100", + n: 0, + expected: "100", + }, + { + name: "x is non-neg & n is greater than or equal 255", + x: "431247391574329147932", + n: 255, + expected: "0", + }, + { + name: "x is non-neg & n is smaller than 256 and greater than or equal 192", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 200, + expected: "36028797018963967", + }, + { + name: "x is non-neg & n is smaller than 192 and greater than or equal 128", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 150, + expected: "40564819207303340847894502572031", + }, + { + name: "x is non-neg & n is smaller than 128 and greater than or equal 64", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 100, + expected: "45671926166590716193865151022383844364247891967", + }, + { + name: "x is non-neg & n is smaller than 64", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + n: 32, + expected: "13479973333575319897333507543509815336818572211270286240551805124607", + }, + { + name: "x is neg & n is smaller than 256 and greater than or equal 192", + x: "-11111", + n: 200, + expected: "-1", + }, + { + name: "x is neg & n is smaller than 192 and greater than or equal 128", + x: "-11111", + n: 150, + expected: "-1", + }, + { + name: "x is neg & n is smaller than 128 and greater than or equal 64", + x: "-11111", + n: 100, + expected: "-1", + }, + { + name: "x is neg & n is smaller than 64", + x: "-11111", + n: 2, + expected: "-2778", + }, + { + name: "x is neg & n is greater than or equal 255", + x: "-11111", + n: 255, + expected: "-1", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + z := new(Int).Rsh(x, tt.n) + uassert.Equal(t, tt.expected, z.ToString()) + }) + } +} + +func TestLsh_NoPanic(t *testing.T) { + x := MustFromDecimal("-12345678901234567890") + + for n := uint(0); n < 300; n++ { + uassert.NotPanics(t, func() { + _ = new(Int).Lsh(x, n) + }) + } +} + +func TestRsh_NoPanic(t *testing.T) { + x := MustFromDecimal("-12345678901234567890") + + for n := uint(0); n < 300; n++ { + uassert.NotPanics(t, func() { + _ = new(Int).Rsh(x, n) + }) + } +} + +func TestRsh_SignExtendProperty(t *testing.T) { + // For all negative integers, Rsh should satisfy: + // - Rsh(x, n) <= 0 + // - Rsh(x, n+1) == Rsh(Rsh(x,n), 1) + x := MustFromDecimal("-11111") + + for n := uint(0); n < 255; n++ { + z := new(Int).Rsh(x, n) + zz := new(Int).Rsh(z, 1) + z2 := new(Int).Rsh(x, n+1) + + uassert.Equal(t, z2.ToString(), zz.ToString()) + } +} + +func TestEq(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", true}, + {"0", "1", false}, + {"1", "0", false}, + {"-1", "0", false}, + {"0", "-1", false}, + {"1", "1", true}, + {"-1", "-1", true}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "-57896044618658097711785492504343953926634992332820282019728792003956564819967", false}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "-57896044618658097711785492504343953926634992332820282019728792003956564819967", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Eq(y) + if got != tc.want { + t.Errorf("Eq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestNeq(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", true}, + {"1", "0", true}, + {"-1", "0", true}, + {"0", "-1", true}, + {"1", "1", false}, + {"-1", "-1", false}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "-57896044618658097711785492504343953926634992332820282019728792003956564819967", true}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819967", "-57896044618658097711785492504343953926634992332820282019728792003956564819967", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Neq(y) + if got != tc.want { + t.Errorf("Neq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestCmp(t *testing.T) { + tests := []struct { + name string + x string + y string + expected int + }{ + { + name: "min negative < max positive", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: -1, + }, + { + name: "min negative == min negative", + x: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: 0, + }, + { + name: "max positive > min negative", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", + expected: 1, + }, + { + name: "max positive == max positive", + x: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: 0, + }, + { + name: "zero == zero", + x: "0", + y: "0", + expected: 0, + }, + { + name: "zero > negative", + x: "0", + y: "-10", + expected: 1, + }, + { + name: "smaller positive < larger positive", + x: "57896044618658097711785492504343953926634992332820282019728792003956564", + y: "57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: -1, + }, + { + name: "larger negative > smaller negative", + x: "-578960446186580977117854925043439539266349923328202820197287920039565648", + y: "-57896044618658097711785492504343953926634992332820282019728792003956564819967", + expected: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + x := MustFromDecimal(tt.x) + y := MustFromDecimal(tt.y) + uassert.Equal(t, tt.expected, x.Cmp(y)) + }) + } +} + +func TestIsZero(t *testing.T) { tests := []struct { x string - want int + want bool + }{ + {"0", true}, + {"-0", true}, + {"1", false}, + {"-1", false}, + {"10", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsZero() + if got != tc.want { + t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestIsNeg(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", false}, + {"-0", false}, // In two's complement, -0 is same as 0 + {"1", false}, + {"-1", true}, + {"10", false}, + {"-10", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsNeg() + if got != tc.want { + t.Errorf("IsNeg(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestLt(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", "57896044618658097711785492504343953926634992332820282019728792003956564819967", true}, + {"0", "0", false}, + {"0", "1", true}, + {"1", "0", false}, + {"-1", "0", true}, + {"0", "-1", false}, + {"1", "1", false}, + {"-1", "-1", false}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "57896044618658097711785492504343953926634992332820282019728792003956564819967", false}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "-57896044618658097711785492504343953926634992332820282019728792003956564819967", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Lt(y) + if got != tc.want { + t.Errorf("Lt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestGt(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", false}, + {"1", "0", true}, + {"-1", "0", false}, + {"0", "-1", true}, + {"1", "1", false}, + {"-1", "-1", false}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967", "-57896044618658097711785492504343953926634992332820282019728792003956564819967", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Gt(y) + if got != tc.want { + t.Errorf("Gt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestClone(t *testing.T) { + tests := []struct { + x string + }{ + {"0"}, + {"-0"}, + {"1"}, + {"-1"}, + {"10"}, + {"-10"}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + {"-57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y := x.Clone() + + if x.Cmp(y) != 0 { + t.Errorf("Clone(%s) = %v, want %v", tc.x, y, x) + } + } +} + +func TestNilChecks(t *testing.T) { + validInt := NewInt(123) + + tests := []struct { + name string + fn func() + }{ + { + name: "Eq with nil", + fn: func() { validInt.Eq(nil) }, + }, + { + name: "Neq with nil", + fn: func() { validInt.Neq(nil) }, + }, + { + name: "Cmp with nil", + fn: func() { validInt.Cmp(nil) }, + }, + { + name: "Lt with nil", + fn: func() { validInt.Lt(nil) }, + }, + { + name: "Gt with nil", + fn: func() { validInt.Gt(nil) }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + r := recover() + if r == nil { + t.Errorf("%s: expected panic but got none", tt.name) + } + // Any panic (nil pointer or custom message) is acceptable + }() + + tt.fn() + }) + } +} + +func TestSetInt64(t *testing.T) { + tests := []struct { + x int64 + want string + }{ + {0, "0"}, + {1, "1"}, + {-1, "-1"}, + {9223372036854775807, "9223372036854775807"}, + {-9223372036854775808, "-9223372036854775808"}, + } + + for _, tc := range tests { + var z Int + z.SetInt64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetInt64(%d) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSetInt64MinValueOverflow(t *testing.T) { + const minInt64 = -9223372036854775808 // -2^63 + const maxInt64 = 9223372036854775807 // 2^63 - 1 + + tests := []struct { + name string + x int64 + want string + }{ + { + name: "MinInt64 should not cause overflow", + x: minInt64, + want: "-9223372036854775808", + }, + { + name: "MaxInt64 works correctly", + x: maxInt64, + want: "9223372036854775807", + }, + { + name: "MinInt64 + 1", + x: minInt64 + 1, + want: "-9223372036854775807", + }, + { + name: "Negative one", + x: -1, + want: "-1", + }, + { + name: "Zero", + x: 0, + want: "0", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var z Int + z.SetInt64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetInt64(%d) = %s, want %s", tc.x, got, tc.want) + } + + // Verify sign is correct + if tc.x < 0 { + if !z.IsNeg() { + t.Errorf("SetInt64(%d): expected IsNegative()=true, got false", tc.x) + } + } else if tc.x > 0 { + if z.IsNeg() { + t.Errorf("SetInt64(%d): expected IsNegative()=false, got true", tc.x) + } + } else { // tc.x == 0 + if z.IsNeg() { + t.Errorf("SetInt64(0): expected IsNegative()=false (no -0), got true") + } + } + }) + } +} + +func TestSetUint64(t *testing.T) { + tests := []struct { + x uint64 + want string + }{ + {0, "0"}, + {1, "1"}, + } + + for _, tc := range tests { + var z Int + z.SetUint64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetUint64(%d) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestUint64(t *testing.T) { + tests := []struct { + x string + want uint64 + }{ + {"0", 0}, + {"1", 1}, + {"9223372036854775807", 9223372036854775807}, + {"9223372036854775808", 9223372036854775808}, + {"18446744073709551615", 18446744073709551615}, + {"18446744073709551616", 0}, + {"18446744073709551617", 1}, + // In two's complement, -1 = 0xFFFF...FFFF, so z[0] = max uint64 + {"-1", 18446744073709551615}, + {"-18446744073709551615", 1}, + {"-18446744073709551616", 0}, + {"-18446744073709551617", 18446744073709551615}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + + got := z.Uint64() + if got != tc.want { + t.Errorf("Uint64(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} + +func TestInt64(t *testing.T) { + tests := []struct { + x string + want int64 }{ {"0", 0}, {"1", 1}, {"-1", -1}, + {"9223372036854775807", 9223372036854775807}, + {"-9223372036854775808", -9223372036854775808}, + {"9223372036854775808", -9223372036854775808}, + {"-9223372036854775809", 9223372036854775807}, + {"18446744073709551616", 0}, + {"18446744073709551617", 1}, + {"18446744073709551615", -1}, + {"-18446744073709551615", 1}, } - for _, tt := range tests { - z := MustFromDecimal(tt.x) - got := z.Sign() - if got != tt.want { - t.Errorf("Sign(%s) = %d, want %d", tt.x, got, tt.want) + for _, tc := range tests { + z := MustFromDecimal(tc.x) + + got := z.Int64() + if got != tc.want { + t.Errorf("Int64(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} + +func TestInt64EdgeCases(t *testing.T) { + const minInt64 = -9223372036854775808 // -2^63 + const maxInt64 = 9223372036854775807 // 2^63 - 1 + + tests := []struct { + name string + setupInt func() *Int + want int64 + description string + }{ + { + name: "MinInt64 from SetInt64", + setupInt: func() *Int { + z := new(Int) + return z.SetInt64(minInt64) + }, + want: minInt64, + description: "SetInt64(MinInt64) should round-trip correctly", + }, + { + name: "MaxInt64 from SetInt64", + setupInt: func() *Int { + z := new(Int) + return z.SetInt64(maxInt64) + }, + want: maxInt64, + description: "SetInt64(MaxInt64) should round-trip correctly", + }, + { + name: "Zero", + setupInt: func() *Int { + return Zero() + }, + want: 0, + description: "Zero should return 0", + }, + { + name: "Large positive int256", + setupInt: func() *Int { + // Value larger than int64 max + return MustFromDecimal("18446744073709551615") + }, + want: -1, // Truncated to int64 + description: "Large positive wraps in int64", + }, + { + name: "Large negative int256", + setupInt: func() *Int { + return MustFromDecimal("-18446744073709551615") + }, + want: 1, // Truncated to int64 + description: "Large negative wraps in int64", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + z := tc.setupInt() + got := z.Int64() + + if got != tc.want { + t.Errorf("%s: got %d, want %d", tc.description, got, tc.want) + } + }) + } +} + +// TestInt64RoundTrip verifies that SetInt64 and Int64 work correctly together +func TestInt64RoundTrip(t *testing.T) { + // Test all interesting int64 values + values := []int64{ + 0, 1, -1, + 127, -128, // int8 boundaries + 32767, -32768, // int16 boundaries + 2147483647, -2147483648, // int32 boundaries + 9223372036854775807, -9223372036854775808, // int64 boundaries (MaxInt64, MinInt64) + 1234567890, -1234567890, + } + + for _, v := range values { + t.Run(ufmt.Sprintf("RoundTrip_%d", v), func(t *testing.T) { + z := new(Int) + z.SetInt64(v) + + got := z.Int64() + if got != v { + t.Errorf("Round trip failed: SetInt64(%d).Int64() = %d", v, got) + } + }) + } +} + +func TestNeg(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "-1"}, + {"-1", "1"}, + {"9223372036854775807", "-9223372036854775807"}, + {"-18446744073709551615", "18446744073709551615"}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + z.Neg(z) + + got := z.ToString() + if got != tc.want { + t.Errorf("Neg(%s) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSet(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "1"}, + {"-1", "-1"}, + {"9223372036854775807", "9223372036854775807"}, + {"-18446744073709551615", "-18446744073709551615"}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + z.Set(z) + + got := z.ToString() + if got != tc.want { + t.Errorf("set(%s) = %s, want %s", tc.x, got, tc.want) } } } @@ -72,113 +1560,79 @@ func TestSetString(t *testing.T) { } if err != nil { + println("ERROR", err.Error(), tt.input) t.Errorf("SetString(%q) returned unexpected error: %v", tt.input, err) continue } - if got := z.abs.ToString(); got != tt.wantVal { + if got := z.Abs().ToString(); got != tt.wantVal { t.Errorf("SetString(%q).abs = %s, want %s", tt.input, got, tt.wantVal) } - if got := z.neg; got != tt.wantSign { + if got := z.IsNeg(); got != tt.wantSign { t.Errorf("SetString(%q).neg = %v, want %v", tt.input, got, tt.wantSign) } } } -func TestInitiateAbs(t *testing.T) { +func TestToString(t *testing.T) { tests := []struct { name string - input *Int - wantNil bool - wantZero bool + setup func() *Int + expected string }{ { - name: "nil input returns new zero Int", - input: nil, - wantNil: false, - wantZero: true, + name: "Zero from subtraction", + setup: func() *Int { + minusThree := MustFromDecimal("-3") + three := MustFromDecimal("3") + return Zero().Add(minusThree, three) + }, + expected: "0", }, { - name: "nil abs field gets initialized", - input: &Int{abs: nil, neg: false}, - wantNil: false, - wantZero: true, + name: "Zero from right shift", + setup: func() *Int { + return Zero().Rsh(One(), 1234) + }, + expected: "0", }, { - name: "existing abs field remains unchanged", - input: NewInt(123), - wantNil: false, - wantZero: false, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - result := tc.input.initiateAbs() - - if result == nil { - t.Error("initiateAbs returned nil") - return - } - - if (result.abs == nil) != tc.wantNil { - t.Errorf("abs field nil status = %v, want %v", result.abs == nil, tc.wantNil) - } - - isZero := result.abs != nil && result.abs.IsZero() - if isZero != tc.wantZero { - t.Errorf("IsZero() = %v, want %v", isZero, tc.wantZero) - } - }) - } -} - -func TestInitiateAbsInOperation(t *testing.T) { - tests := []struct { - name string - setup func() *Int - op func(*Int) *Int - want string - }{ - { - name: "Add with nil receiver", - setup: func() *Int { return nil }, - op: func(z *Int) *Int { return z.Add(NewInt(10), NewInt(20)) }, - want: "30", + name: "Positive number", + setup: func() *Int { + return MustFromDecimal("42") + }, + expected: "42", }, { - name: "Add with nil abs field", - setup: func() *Int { return &Int{abs: nil} }, - op: func(z *Int) *Int { return z.Add(NewInt(10), NewInt(20)) }, - want: "30", + name: "Negative number", + setup: func() *Int { + return MustFromDecimal("-42") + }, + expected: "-42", }, { - name: "Sub with nil receiver", - setup: func() *Int { return nil }, - op: func(z *Int) *Int { return z.Sub(NewInt(30), NewInt(20)) }, - want: "10", + name: "Large positive number", + setup: func() *Int { + return MustFromDecimal("57896044618658097711785492504343953926634992332820282019728792003956564819967") + }, + expected: "57896044618658097711785492504343953926634992332820282019728792003956564819967", }, { - name: "Sub with nil abs field", - setup: func() *Int { return &Int{abs: nil} }, - op: func(z *Int) *Int { return z.Sub(NewInt(30), NewInt(20)) }, - want: "10", + name: "Large negative number", + setup: func() *Int { + return MustFromDecimal("-57896044618658097711785492504343953926634992332820282019728792003956564819968") + }, + expected: "-57896044618658097711785492504343953926634992332820282019728792003956564819968", }, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - z := tc.setup() - result := tc.op(z) - - if result == nil { - t.Error("operation returned nil") - return - } - - if got := result.ToString(); got != tc.want { - t.Errorf("got %v, want %v", got, tc.want) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + z := tt.setup() + result := z.ToString() + if result != tt.expected { + t.Errorf("ToString() = %s, want %s", result, tt.expected) } }) } diff --git a/contract/p/gnoswap/int256/internal_test.gno b/contract/p/gnoswap/int256/internal_test.gno new file mode 100644 index 000000000..87b47f2e0 --- /dev/null +++ b/contract/p/gnoswap/int256/internal_test.gno @@ -0,0 +1,265 @@ +package int256 + +import ( + "math/bits" + "testing" + + "gno.land/p/nt/uassert" + "gno.land/p/nt/ufmt" +) + +func pack32to64(words []uint32) []uint64 { + var out []uint64 + for i := 0; i < len(words); i += 2 { + lo := uint64(words[i]) + hi := uint64(0) + if i+1 < len(words) { + hi = uint64(words[i+1]) + } + out = append(out, (hi<<32)|lo) + } + return out +} + +func toInt(xs []uint64) Int { + var z Int + for i := 0; i < len(xs) && i < len(z); i++ { + z[i] = xs[i] + } + return z +} + +// Source: https://skanthak.hier-im-netz.de/division.html#proper (Section: Generic Implementation in Hacker’s Delight) +var tests = []uint32{ + // m, n, u..., v..., cq..., cr.... + 1, 1, 3, 0, 1, 1, // Error, divide by 0. + 1, 2, 7, 1, 3, 0, 7, 0, + 2, 2, 0, 0, 1, 0, 0, 0, 0, + 1, 1, 3, 2, 1, 1, + 1, 1, 3, 3, 1, 0, + 1, 1, 3, 4, 0, 3, + 1, 1, 0, 0xffffffff, 0, 0, + 1, 1, 0xffffffff, 1, 0xffffffff, 0, + 1, 1, 0xffffffff, 0xffffffff, 1, 0, + 1, 1, 0xffffffff, 3, 0x55555555, 0, + 2, 1, 0xffffffff, 0xffffffff, 1, 0xffffffff, 0xffffffff, 0, + 2, 1, 0xffffffff, 0xffffffff, 0xffffffff, 1, 1, 0, + 2, 1, 0xffffffff, 0xfffffffe, 0xffffffff, 0xffffffff, 0, 0xfffffffe, + 2, 1, 0x00005678, 0x00001234, 0x00009abc, 0x1e1dba76, 0, 0x6bd0, + 2, 2, 0, 0, 0, 1, 0, 0, 0, + 2, 2, 0, 7, 0, 3, 2, 0, 1, + 2, 2, 5, 7, 0, 3, 2, 5, 1, + 2, 2, 0, 6, 0, 2, 3, 0, 0, + 1, 1, 0x80000000, 0x40000001, 0x00000001, 0x3fffffff, + 2, 1, 0x00000000, 0x80000000, 0x40000001, 0xfffffff8, 0x00000001, 0x00000008, + 2, 2, 0x00000000, 0x80000000, 0x00000001, 0x40000000, 0x00000001, 0xffffffff, 0x3fffffff, + 2, 2, 0x0000789a, 0x0000bcde, 0x0000789a, 0x0000bcde, 1, 0, 0, + 2, 2, 0x0000789b, 0x0000bcde, 0x0000789a, 0x0000bcde, 1, 1, 0, + 2, 2, 0x00007899, 0x0000bcde, 0x0000789a, 0x0000bcde, 0, 0x00007899, 0x0000bcde, + 2, 2, 0x0000ffff, 0x0000ffff, 0x0000ffff, 0x0000ffff, 1, 0, 0, + 2, 2, 0x0000ffff, 0x0000ffff, 0x00000000, 0x00000001, 0x0000ffff, 0x0000ffff, 0, + 3, 2, 0x000089ab, 0x00004567, 0x00000123, 0x00000000, 0x00000001, 0x00004567, 0x00000123, 0x000089ab, 0, + 3, 2, 0x00000000, 0x0000fffe, 0x00008000, 0x0000ffff, 0x00008000, 0xffffffff, 0x00000000, 0x0000ffff, 0x00007fff, // Shows that first qhat can = b + 1. + 3, 3, 0x00000003, 0x00000000, 0x80000000, 0x00000001, 0x00000000, 0x20000000, 0x00000003, 0, 0, 0x20000000, // Adding back step req'd. + 3, 3, 0x00000003, 0x00000000, 0x00008000, 0x00000001, 0x00000000, 0x00002000, 0x00000003, 0, 0, 0x00002000, // Adding back step req'd. + 4, 3, 0, 0, 0x00008000, 0x00007fff, 1, 0, 0x00008000, 0xfffe0000, 0, 0x00020000, 0xffffffff, 0x00007fff, // Add back req'd. + 4, 3, 0, 0x0000fffe, 0, 0x00008000, 0x0000ffff, 0, 0x00008000, 0xffffffff, 0, 0x0000ffff, 0xffffffff, 0x00007fff, // Shows that mult-sub quantity cannot be treated as signed. + 4, 3, 0, 0xfffffffe, 0, 0x80000000, 0x0000ffff, 0, 0x80000000, 0x00000000, 1, 0x00000000, 0xfffeffff, 0x00000000, // Shows that mult-sub quantity cannot be treated as signed. + 4, 3, 0, 0xfffffffe, 0, 0x80000000, 0xffffffff, 0, 0x80000000, 0xffffffff, 0, 0xffffffff, 0xffffffff, 0x7fffffff, // Shows that mult-sub quantity cannot be treated as signed. +} + +func TestUdivrem(t *testing.T) { + i := 0 + caseNo := 0 + + for i < len(tests) { + caseNo++ + + m := int(tests[i]) + n := int(tests[i+1]) + + u32 := tests[i+2 : i+2+m] + v32 := tests[i+2+m : i+2+m+n] + + qlen := max(m-n+1, 1) + cq32 := tests[i+2+m+n : i+2+m+n+qlen] + cr32 := tests[i+2+m+n+qlen : i+2+m+n+qlen+n] + + // transform + u := toInt(pack32to64(u32)) + v := toInt(pack32to64(v32)) + cq := toInt(pack32to64(cq32)) + cr := toInt(pack32to64(cr32)) + + shouldError := false + + // Division by zero case + isZero := true + for i := 0; i < len(v32); i++ { + if v32[i] != 0 { + isZero = false + break + } + } + if isZero { + shouldError = true + } + + if shouldError { + mustPanic(t, func() { + var q [4]uint64 + _ = udivrem(q[:], u[:], &v) + }) + } else { + var quot [4]uint64 + + mustNotPanic(t, func() { + rem := udivrem(quot[:], u[:], &v) + q := toInt(quot[:]) + + uassert.Equal(t, cq.ToString(), q.ToString(), ufmt.Sprintf("[case %d] quotient mismatch:\n got=%v\n want=%v\n", caseNo, q, cq)) + uassert.Equal(t, cr.ToString(), rem.ToString(), ufmt.Sprintf("[case %d] remainder mismatch:\n got=%v\n want=%v\n", caseNo, rem, cr)) + }) + } + + i += 2 + m + n + qlen + n + } +} + +func mustPanic(t *testing.T, f func()) { + t.Helper() + defer func() { + if r := recover(); r == nil { + t.Fatalf("expected panic but did not panic") + } + }() + f() +} + +func mustNotPanic(t *testing.T, f func()) { + t.Helper() + defer func() { + if r := recover(); r != nil { + t.Fatalf("unexpected panic: %v", r) + } + }() + f() +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// Implementation of schoolbook multiplication +// ref: https://en.wikipedia.org/wiki/Multiplication_algorithm#Example +func referenceMul(x, y *Int) [8]uint64 { + var out [8]uint64 + for i := 0; i < 4; i++ { + var carry uint64 + for j := 0; j < 4; j++ { + hi, lo := bits.Mul64(x[j], y[i]) + + // out[i+j] += lo + lo2, c1 := bits.Add64(out[i+j], lo, 0) + out[i+j] = lo2 + hi += c1 + + // add previous carry + lo3, c2 := bits.Add64(out[i+j], carry, 0) + out[i+j] = lo3 + hi += c2 + + // update carry + carry = hi + } + out[i+4] = carry + } + return out +} + +func TestUmul(t *testing.T) { + tests := []struct { + name string + x Int + y Int + }{ + { + name: "zero * random", + x: Int{0, 0, 0, 0}, + y: Int{1, 2, 3, 4}, + }, + { + name: "random * zero", + x: Int{5, 6, 7, 8}, + y: Int{0, 0, 0, 0}, + }, + { + name: "one * value", + x: Int{1, 0, 0, 0}, + y: Int{9, 10, 11, 12}, + }, + { + name: "value * one", + x: Int{9, 10, 11, 12}, + y: Int{1, 0, 0, 0}, + }, + { + name: "maxword low 64 bits", + x: Int{^uint64(0), 0, 0, 0}, + y: Int{^uint64(0), 0, 0, 0}, + }, + { + name: "max whole 256-bit", + x: Int{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}, + y: Int{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}, + }, + { + name: "carry chain test A", + x: Int{^uint64(0), 0, 0, 0}, + y: Int{1, 1, 1, 1}, + }, + { + name: "carry chain test B", + x: Int{1, ^uint64(0), 1, ^uint64(0)}, + y: Int{^uint64(0), 1, ^uint64(0), 1}, + }, + { + name: "diagonal test 0 (res[0] only)", + x: Int{1, 0, 0, 0}, + y: Int{1, 0, 0, 0}, + }, + { + name: "diagonal test 1", + x: Int{1, 0, 0, 0}, + y: Int{0, 1, 0, 0}, + }, + { + name: "diagonal test 2", + x: Int{1, 0, 0, 0}, + y: Int{0, 0, 1, 0}, + }, + { + name: "diagonal test 3", + x: Int{0, 0, 0, 1}, + y: Int{0, 0, 1, 0}, + }, + { + name: "sparse pattern", + x: Int{1, ^uint64(0), 0, 1}, + y: Int{0, 1, 0, ^uint64(0)}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := umul(&tt.x, &tt.y) + want := referenceMul(&tt.x, &tt.y) + for i := 0; i < 8; i++ { + uassert.Equal(t, got[i], want[i], ufmt.Sprintf("umul(x,y) mismatch at index %d\nx=%v\ny=%v\ngot = %v\nwant= %v", i, tt.x, tt.y, got, want)) + } + }) + } +} diff --git a/contract/p/gnoswap/int256/runtime_metrics_test.gno b/contract/p/gnoswap/int256/runtime_metrics_test.gno deleted file mode 100644 index 4fb9cbd3e..000000000 --- a/contract/p/gnoswap/int256/runtime_metrics_test.gno +++ /dev/null @@ -1,165 +0,0 @@ -package int256 - -import ( - "runtime" - "strconv" - "strings" - "testing" - "time" -) - -// readAllocatorBytes parses runtime.MemStats() into the current allocated bytes. -func readAllocatorBytes(t *testing.T) int64 { - t.Helper() - stats := runtime.MemStats() - if stats == "nil allocator" { - return 0 - } - if !strings.HasPrefix(stats, "Allocator{") || !strings.HasSuffix(stats, "}") { - t.Fatalf("unexpected runtime.MemStats output: %q", stats) - } - body := strings.TrimSuffix(strings.TrimPrefix(stats, "Allocator{"), "}") - parts := strings.Split(body, ", ") - if len(parts) != 2 { - t.Fatalf("unexpected runtime.MemStats content: %q", stats) - } - var ( - bytes int64 - found bool - ) - for _, part := range parts { - fields := strings.Split(part, ":") - if len(fields) != 2 { - t.Fatalf("unexpected runtime.MemStats pair %q", part) - } - if fields[0] == "bytes" { - val, err := strconv.ParseInt(fields[1], 10, 64) - if err != nil { - t.Fatalf("failed to parse bytes from %q: %v", part, err) - } - bytes = val - found = true - break - } - } - if !found { - t.Fatalf("bytes key not found in runtime.MemStats output: %q", stats) - } - return bytes -} - -type MetricResult struct { - Name string - Iterations int - DurationNs int64 - AllocDelta int64 -} - -// runMetric executes fn the given number of iterations while logging elapsed time and allocation deltas. -func runMetric(t *testing.T, name string, iterations int, fn func()) MetricResult { - t.Helper() - - // Skip GC if allocator is nil to avoid panic - stats := runtime.MemStats() - if stats != "nil allocator" { - runtime.GC() - } - beforeBytes := readAllocatorBytes(t) - start := time.Now() - for i := 0; i < iterations; i++ { - fn() - } - elapsed := time.Since(start) - afterBytes := readAllocatorBytes(t) - - return MetricResult{ - Name: name, - Iterations: iterations, - DurationNs: elapsed.Nanoseconds(), - AllocDelta: afterBytes - beforeBytes, - } -} - -func TestPublicFunctionRuntimeMetrics(t *testing.T) { - const iterations = 200 - var results []MetricResult - - // Test data - commonDecimal := "1234567890123456789012345678901234567890" - - tests := []struct { - name string - run func(t *testing.T) MetricResult - }{ - { - name: "FromDecimal", - run: func(t *testing.T) MetricResult { - return runMetric(t, "FromDecimal", iterations, func() { - if _, err := FromDecimal(commonDecimal); err != nil { - t.Fatalf("FromDecimal error: %v", err) - } - }) - }, - }, - { - name: "SetString", - run: func(t *testing.T) MetricResult { - return runMetric(t, "SetString", iterations, func() { - var target Int - if _, err := target.SetString(commonDecimal); err != nil { - t.Fatalf("SetString error: %v", err) - } - }) - }, - }, - { - name: "MustFromDecimal", - run: func(t *testing.T) MetricResult { - return runMetric(t, "MustFromDecimal", iterations, func() { - MustFromDecimal(commonDecimal) - }) - }, - }, - { - name: "MaxInt256", - run: func(t *testing.T) MetricResult { - return runMetric(t, "MaxInt256", iterations, func() { - MaxInt256() - }) - }, - }, - { - name: "MinInt256", - run: func(t *testing.T) MetricResult { - return runMetric(t, "MinInt256", iterations, func() { - MinInt256() - }) - }, - }, - { - name: "ToString", - run: func(t *testing.T) MetricResult { - largeInt := MustFromDecimal("57896044618658097711785492504343953926634992332820282019728792003956564819967") // max int256 - return runMetric(t, "ToString", iterations, func() { - _ = largeInt.ToString() - }) - }, - }, - } - - for i := range tests { - test := tests[i] - t.Run(test.name, func(t *testing.T) { - result := test.run(t) - results = append(results, result) - }) - } - - // Print results as markdown table - t.Log("\n## Runtime Metrics Results\n") - t.Log("| Function | Iterations | Duration (ns) | Alloc Delta (bytes) |") - t.Log("|----------|------------|---------------|---------------------|") - for _, result := range results { - t.Logf("| %s | %d | %d | %d |", result.Name, result.Iterations, result.DurationNs, result.AllocDelta) - } -} diff --git a/contract/r/gnoswap/router/v1/router_dry_test.gno b/contract/r/gnoswap/router/v1/router_dry_test.gno index ac2a2f3f8..bb75541ff 100644 --- a/contract/r/gnoswap/router/v1/router_dry_test.gno +++ b/contract/r/gnoswap/router/v1/router_dry_test.gno @@ -211,6 +211,7 @@ func TestDrySwapRoute_Basic(t *testing.T) { } func TestDrySwapRouteOverflow(t *testing.T) { + // maxUint256 exceeds int256 max (2^255 - 1), so parsing it as int256 will overflow maxUint256 := "115792089237316195423570985008687907853269984665640564039457584007913129639935" tests := []DrySwapTestCase{ @@ -228,7 +229,8 @@ func TestDrySwapRouteOverflow(t *testing.T) { expectedAmountIn: "0", expectedAmountOut: "0", expectedSuccess: false, - expectPanic: false, + expectPanic: true, + expectedPanicMsg: "int256: overflow", }, { name: "ExactOut - Overflow in calculateSwapAmount", @@ -244,7 +246,7 @@ func TestDrySwapRouteOverflow(t *testing.T) { expectedAmountIn: "0", expectedAmountOut: "0", expectPanic: true, - expectedPanicMsg: "overflow: denominator(9985) must be greater than hi(9999)", + expectedPanicMsg: "int256: overflow", }, } diff --git a/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno b/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno new file mode 100644 index 000000000..3c3f43904 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno @@ -0,0 +1,303 @@ +package fuzz + +import ( + "testing" + + "gno.land/p/gnoswap/fuzz" + "gno.land/p/gnoswap/fuzzutils" + i256 "gno.land/p/gnoswap/int256" +) + +// TestFuzzInt256Add_ValidParams_Stateless tests Add with valid parameters +func TestFuzzInt256Add_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntAddParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Add(x, y) + }) +} + +// TestFuzzInt256Add_RandomizedParams_Stateless tests Add with randomized parameters +func TestFuzzInt256Add_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntAddParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Add(x, y) + }) +} + +// TestFuzzInt256AddOverflow_ValidParams_Stateless tests AddOverflow with valid (non-overflowing) parameters +func TestFuzzInt256AddOverflow_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntAddOverflowParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + _, overflow := z.AddOverflow(x, y) + + if overflow { + panic("IsValid returned true but overflow occurred") + } + }) +} + +// TestFuzzInt256AddOverflow_RandomizedParams_Stateless tests AddOverflow with randomized parameters +func TestFuzzInt256AddOverflow_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntAddOverflowParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + _, overflow := z.AddOverflow(x, y) + + switch { + case params.IsValid() && overflow: + panic("IsValid returned true but overflow occurred") + case !params.IsValid() && !overflow: + panic("expected overflow" + params.ToString()) + case !params.IsValid() && overflow: + // expected overflow: signal error so fuzz harness aligns with IsValid=false + panic("overflow as expected " + params.ToString()) + } + }) +} + +// TestFuzzInt256Sub_ValidParams_Stateless tests Sub with valid parameters +func TestFuzzInt256Sub_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntSubParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Sub(x, y) + }) +} + +// TestFuzzInt256Sub_RandomizedParams_Stateless tests Sub with randomized parameters +func TestFuzzInt256Sub_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntSubParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Sub(x, y) + }) +} + +// TestFuzzInt256SubOverflow_ValidParams_Stateless tests SubOverflow with valid (non-overflowing) parameters +func TestFuzzInt256SubOverflow_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntSubOverflowParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + _, overflow := z.SubOverflow(x, y) + + if overflow { + panic("IsValid returned true but overflow occurred") + } + }) +} + +// TestFuzzInt256SubOverflow_RandomizedParams_Stateless tests SubOverflow with randomized parameters +func TestFuzzInt256SubOverflow_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntSubOverflowParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + _, overflow := z.SubOverflow(x, y) + + switch { + case params.IsValid() && overflow: + panic("IsValid returned true but overflow occurred") + case !params.IsValid() && !overflow: + panic("expected underflow" + params.ToString()) + case !params.IsValid() && overflow: + // expected underflow: signal error so fuzz harness aligns with IsValid=false + panic("underflow as expected " + params.ToString()) + } + }) +} + +// TestFuzzInt256Mul_ValidParams_Stateless tests Mul with valid parameters +func TestFuzzInt256Mul_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntMulParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Mul(x, y) + }) +} + +// TestFuzzInt256Mul_RandomizedParams_Stateless tests Mul with randomized parameters +func TestFuzzInt256Mul_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntMulParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Mul(x, y) + }) +} + +// TestFuzzInt256MulOverflow_ValidParams_Stateless tests MulOverflow with valid (non-overflowing) parameters +func TestFuzzInt256MulOverflow_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntMulOverflowParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + _, overflow := z.MulOverflow(x, y) + + if overflow { + panic("IsValid returned true but overflow occurred") + } + }) +} + +// TestFuzzInt256MulOverflow_RandomizedParams_Stateless tests MulOverflow with randomized parameters +func TestFuzzInt256MulOverflow_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntMulOverflowParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + _, overflow := z.MulOverflow(x, y) + + switch { + case params.IsValid() && overflow: + panic("IsValid returned true but overflow occurred") + case !params.IsValid() && !overflow: + panic("expected overflow" + params.ToString()) + case !params.IsValid() && overflow: + // expected overflow: signal error so fuzz harness aligns with IsValid=false + panic("overflow as expected " + params.ToString()) + } + }) +} + +// TestFuzzInt256Div_ValidParams_Stateless tests Div with valid parameters +func TestFuzzInt256Div_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidIntDivParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Div(x, y) + }) +} + +// TestFuzzInt256Div_RandomizedParams_Stateless tests Div with randomized parameters +func TestFuzzInt256Div_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedIntDivParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + var z i256.Int + z.Div(x, y) + }) +} diff --git a/contract/r/gnoswap/test/fuzz/int256_params_fuzz_test.gno b/contract/r/gnoswap/test/fuzz/int256_params_fuzz_test.gno new file mode 100644 index 000000000..3709740c1 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_params_fuzz_test.gno @@ -0,0 +1,314 @@ +package fuzz + +import ( + "gno.land/p/gnoswap/fuzz" + i256 "gno.land/p/gnoswap/int256" + u256 "gno.land/p/gnoswap/uint256" + "gno.land/p/nt/ufmt" +) + +const ( + smallIntRangeMin = "-1000000000000000000" + smallIntRangeMax = "1000000000000000000" + smallMulRangeMin = "-1000000000" + smallMulRangeMax = "1000000000" +) + +var ( + maxInt256Abs = intAbs(i256.MaxInt256()) + minInt256Abs = intAbs(i256.MinInt256()) +) + +// Add Parameters +type intAddParams struct { + x string + y string +} + +func (p *intAddParams) IsValid() bool { + return true +} + +func (p *intAddParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntAddParams(t *fuzz.T) *intAddParams { + x := fuzz.Int256Range(smallIntRangeMin, smallIntRangeMax).Draw(t, "x").(*i256.Int) + y := fuzz.Int256Range(smallIntRangeMin, smallIntRangeMax).Draw(t, "y").(*i256.Int) + + return &intAddParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntAddParams(t *fuzz.T) *intAddParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intAddParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// AddOverflow Parameters +type intAddOverflowParams struct { + x string + y string +} + +func (p *intAddOverflowParams) IsValid() bool { + x := i256.MustFromDecimal(p.x) + y := i256.MustFromDecimal(p.y) + + _, overflow := new(i256.Int).AddOverflow(x, y) + return !overflow +} + +func (p *intAddOverflowParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntAddOverflowParams(t *fuzz.T) *intAddOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + var limit *u256.Uint + if x.IsNeg() { + limit = minInt256Abs + } else { + limit = maxInt256Abs + } + + remaining := new(u256.Uint).Sub(limit, intAbs(x)) + yAbs := fuzz.Uint256Range("0", remaining.ToString()).Draw(t, "yAbs").(*u256.Uint) + y := newIntWithSign(yAbs, x.IsNeg()) + + return &intAddOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntAddOverflowParams(t *fuzz.T) *intAddOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intAddOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Sub Parameters +type intSubParams struct { + x string + y string +} + +func (p *intSubParams) IsValid() bool { + return true +} + +func (p *intSubParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntSubParams(t *fuzz.T) *intSubParams { + x := fuzz.Int256Range(smallIntRangeMin, smallIntRangeMax).Draw(t, "x").(*i256.Int) + y := fuzz.Int256Range(smallIntRangeMin, smallIntRangeMax).Draw(t, "y").(*i256.Int) + + return &intSubParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntSubParams(t *fuzz.T) *intSubParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intSubParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// SubOverflow Parameters +type intSubOverflowParams struct { + x string + y string +} + +func (p *intSubOverflowParams) IsValid() bool { + x := i256.MustFromDecimal(p.x) + y := i256.MustFromDecimal(p.y) + + _, overflow := new(i256.Int).SubOverflow(x, y) + return !overflow +} + +func (p *intSubOverflowParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntSubOverflowParams(t *fuzz.T) *intSubOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + var limit *u256.Uint + if x.IsNeg() { + limit = minInt256Abs + } else { + limit = maxInt256Abs + } + + remaining := new(u256.Uint).Sub(limit, intAbs(x)) + negYAbs := fuzz.Uint256Range("0", remaining.ToString()).Draw(t, "negYAbs").(*u256.Uint) + negY := newIntWithSign(negYAbs, x.IsNeg()) + y := i256.Zero().Neg(negY) + + return &intSubOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntSubOverflowParams(t *fuzz.T) *intSubOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intSubOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Mul Parameters +type intMulParams struct { + x string + y string +} + +func (p *intMulParams) IsValid() bool { + return true +} + +func (p *intMulParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntMulParams(t *fuzz.T) *intMulParams { + x := fuzz.Int256Range(smallMulRangeMin, smallMulRangeMax).Draw(t, "x").(*i256.Int) + y := fuzz.Int256Range(smallMulRangeMin, smallMulRangeMax).Draw(t, "y").(*i256.Int) + + return &intMulParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntMulParams(t *fuzz.T) *intMulParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intMulParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// MulOverflow Parameters +type intMulOverflowParams struct { + x string + y string +} + +func (p *intMulOverflowParams) IsValid() bool { + x := i256.MustFromDecimal(p.x) + y := i256.MustFromDecimal(p.y) + + _, overflow := new(i256.Int).MulOverflow(x, y) + return !overflow +} + +func (p *intMulOverflowParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntMulOverflowParams(t *fuzz.T) *intMulOverflowParams { + x := fuzz.Int256Range(smallMulRangeMin, smallMulRangeMax).Draw(t, "x").(*i256.Int) + y := fuzz.Int256Range(smallMulRangeMin, smallMulRangeMax).Draw(t, "y").(*i256.Int) + + return &intMulOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntMulOverflowParams(t *fuzz.T) *intMulOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intMulOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Div Parameters +type intDivParams struct { + x string + y string +} + +func (p *intDivParams) IsValid() bool { + return !i256.MustFromDecimal(p.y).IsZero() +} + +func (p *intDivParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidIntDivParams(t *fuzz.T) *intDivParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + if y.IsZero() { + y = i256.One() + } + + return &intDivParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedIntDivParams(t *fuzz.T) *intDivParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &intDivParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func newIntWithSign(abs *u256.Uint, neg bool) *i256.Int { + val := i256.FromUint256(abs) + if neg && !abs.IsZero() { + val.Neg(val) + } + return val +} + +func intAbs(x *i256.Int) *u256.Uint { + if x.IsMinI256() { + // |MinInt256| is 1<<255, which does not fit in Int.Abs(). + return u256.Zero().Lsh(u256.One(), 255) + } + if x.IsNeg() { + neg := i256.Zero().Neg(x) + return &u256.Uint{neg[0], neg[1], neg[2], neg[3]} + } + return &u256.Uint{x[0], x[1], x[2], x[3]} +} diff --git a/tests/integration/testdata/base/int256_gas_measurement.txtar b/tests/integration/testdata/base/int256_gas_measurement.txtar index 3b160d870..ca2710294 100644 --- a/tests/integration/testdata/base/int256_gas_measurement.txtar +++ b/tests/integration/testdata/base/int256_gas_measurement.txtar @@ -40,10 +40,6 @@ gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAdd -insecure gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAddOverflow -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 9869996' -# AddUint256 -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAddUint256 -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4348107' - # Sub gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestSub -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 4382174' @@ -52,10 +48,6 @@ gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestSub -insecure gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestSubOverflow -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 4401211' -# SubUint256 -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestSubUint256 -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4362770' - # Mul gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestMul -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 4428494' @@ -64,30 +56,10 @@ gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestMul -insecure gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestMulOverflow -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 10107046' -# MulUint256 -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestMulUint256 -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4418751' - # Div gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestDiv -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 4386817' -# DivUint256 -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestDivUint256 -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4373178' - -# Quo -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestQuo -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4392997' - -# Rem -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestRem -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4396670' - -# Mod -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestMod -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4537413' - # Neg gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestNeg -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 3644946' @@ -139,14 +111,6 @@ gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestIsNeg -insecu gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAbs -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 3625840' -# AbsGt -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAbsGt -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4327613' - -# AbsLt -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAbsLt -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4325745' - ### Conversion Methods ### # ToString gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestToString -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 @@ -160,14 +124,6 @@ gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestClone -insecu gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestSign -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 # stdout 'GAS USED: 3628520' -### Delta Operations ### -# AddDelta -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAddDelta -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 4329302' - -# AddDeltaOverflow -gnokey maketx call -pkgpath gno.land/r/gnoswap/int256gas -func TestAddDeltaOverflow -insecure-password-stdin=true -broadcast=true -chainid=tendermint_test -gas-fee 1000000ugnot -gas-wanted 1000000000 -memo "" test1 -# stdout 'GAS USED: 7872829' -- gnomod.toml -- module = "gno.land/r/gnoswap/int256gas" @@ -222,13 +178,6 @@ func TestAddOverflow(cur realm) { z.AddOverflow(x, y) } -func TestAddUint256(cur realm) { - x := int256.NewInt(1000000) - y := uint256.NewUint(500000) - z := int256.Zero() - z.AddUint256(x, y) -} - func TestSub(cur realm) { x := int256.NewInt(1000000) y := int256.NewInt(300000) @@ -243,13 +192,6 @@ func TestSubOverflow(cur realm) { z.SubOverflow(x, y) } -func TestSubUint256(cur realm) { - x := int256.NewInt(1000000) - y := uint256.NewUint(300000) - z := int256.Zero() - z.SubUint256(x, y) -} - func TestMul(cur realm) { x := int256.NewInt(1000) y := int256.NewInt(2000) @@ -264,13 +206,6 @@ func TestMulOverflow(cur realm) { z.MulOverflow(x, y) } -func TestMulUint256(cur realm) { - x := int256.NewInt(1000) - y := uint256.NewUint(2000) - z := int256.Zero() - z.MulUint256(x, y) -} - func TestDiv(cur realm) { x := int256.NewInt(1000000) y := int256.NewInt(3) @@ -278,34 +213,6 @@ func TestDiv(cur realm) { z.Div(x, y) } -func TestDivUint256(cur realm) { - x := int256.NewInt(1000000) - y := uint256.NewUint(3) - z := int256.Zero() - z.DivUint256(x, y) -} - -func TestQuo(cur realm) { - x := int256.NewInt(-1000000) - y := int256.NewInt(3) - z := int256.Zero() - z.Quo(x, y) -} - -func TestRem(cur realm) { - x := int256.NewInt(-1000000) - y := int256.NewInt(7) - z := int256.Zero() - z.Rem(x, y) -} - -func TestMod(cur realm) { - x := int256.NewInt(-1000000) - y := int256.NewInt(7) - z := int256.Zero() - z.Mod(x, y) -} - func TestNeg(cur realm) { x := int256.NewInt(12345) z := int256.Zero() @@ -380,18 +287,6 @@ func TestAbs(cur realm) { x.Abs() } -func TestAbsGt(cur realm) { - x := int256.NewInt(-1000000) - y := uint256.NewUint(500000) - x.AbsGt(y) -} - -func TestAbsLt(cur realm) { - x := int256.NewInt(-1000000) - y := uint256.NewUint(2000000) - x.AbsLt(y) -} - // Conversion Methods func TestToString(cur realm) { x := int256.NewInt(-123456789) @@ -407,18 +302,3 @@ func TestSign(cur realm) { x := int256.NewInt(-12345) x.Sign() } - -// Delta Operations -func TestAddDelta(cur realm) { - z := uint256.NewUint(1000000) - x := uint256.NewUint(500000) - y := int256.NewInt(300000) - int256.AddDelta(z, x, y) -} - -func TestAddDeltaOverflow(cur realm) { - z := uint256.NewUint(1000000) - x := uint256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935") - y := int256.NewInt(1) - int256.AddDeltaOverflow(z, x, y) -} diff --git a/tests/integration/testdata/deploy.txtar b/tests/integration/testdata/deploy.txtar index 640522e1e..2b183e76e 100644 --- a/tests/integration/testdata/deploy.txtar +++ b/tests/integration/testdata/deploy.txtar @@ -70,8 +70,8 @@ gnokey maketx addpkg -pkgdir ../../../examples/gno.land/p/gnoswap/uint256 -pkgpa # stdout 'STORAGE FEE: (1|2)[0-9]{7}ugnot' ## deploy int256 -gnokey maketx addpkg -pkgdir ../../../examples/gno.land/p/gnoswap/int256 -pkgpath gno.land/p/gnoswap/int256 -gas-fee 14936ugnot -gas-wanted 14936000 -broadcast -chainid=tendermint_test test1 -# stdout 'GAS USED: 14[0-9]{6}' +gnokey maketx addpkg -pkgdir ../../../examples/gno.land/p/gnoswap/int256 -pkgpath gno.land/p/gnoswap/int256 -gas-fee 17000ugnot -gas-wanted 17000000 -broadcast -chainid=tendermint_test test1 +# stdout 'GAS USED: 16[0-9]{6}' # stdout 'STORAGE DELTA: (5|6)[0-9]{4} bytes' # stdout 'STORAGE FEE: (5|6)[0-9]{6}ugnot'