diff --git a/contract/p/gnoswap/fuzz/int256.gno b/contract/p/gnoswap/fuzz/int256.gno index 4a1b1e8d4..8f3e03fde 100644 --- a/contract/p/gnoswap/fuzz/int256.gno +++ b/contract/p/gnoswap/fuzz/int256.gno @@ -1,64 +1,51 @@ package fuzz import ( + "strings" + "gno.land/p/gnoswap/int256" "gno.land/p/gnoswap/uint256" - "gno.land/p/nt/ufmt" +) + +const ( + maxUint256SampleAttempts = 64 + maxUint256Dec = "115792089237316195423570985008687907853269984665640564039457584007913129639935" + maxInt256Dec = "57896044618658097711785492504343953926634992332820282019728792003956564819967" + minInt256Dec = "-57896044618658097711785492504343953926634992332820282019728792003956564819968" +) + +var ( + maxUint256Value = uint256.MustFromDecimal(maxUint256Dec) + maxInt256Value = int256.MustFromDecimal(maxInt256Dec) + minInt256Value = int256.MustFromDecimal(minInt256Dec) ) // Uint256 Generators type uint256Gen struct { - min *uint256.Uint - max *uint256.Uint + min *uint256.Uint + max *uint256.Uint + span *uint256.Uint } func (g *uint256Gen) String() string { - return ufmt.Sprintf("Uint256(%s, %s)", g.min.ToString(), g.max.ToString()) + var b strings.Builder + b.WriteString("Uint256(") + b.WriteString(g.min.ToString()) + b.WriteString(", ") + b.WriteString(g.max.ToString()) + b.WriteByte(')') + return b.String() } func (g *uint256Gen) value(t *T) any { - // If min >= max, return min - if g.min.Cmp(g.max) >= 0 { - return g.min.Clone() - } - - // Calculate range = max - min - rangeVal := new(uint256.Uint).Sub(g.max, g.min) - - // For small ranges that fit in uint64 - if rangeVal.IsUint64() { - rangeU64 := rangeVal.Uint64() - if rangeU64 == 0 { - return g.min.Clone() - } - randomOffset := Uint64Range(0, rangeU64).Draw(t, "offset").(uint64) - result := new(uint256.Uint).Add(g.min, uint256.NewUint(randomOffset)) - return result - } - - // For larger ranges, generate a random value using digit-based approach - minDigits := len(g.min.ToString()) - maxDigits := len(g.max.ToString()) - - numDigits := IntRange(minDigits, maxDigits).Draw(t, "numDigits").(int) - - // Build random decimal string - firstDigit := IntRange(1, 9).Draw(t, "firstDigit").(int) - result := ufmt.Sprintf("%d", firstDigit) - - for i := 1; i < numDigits; i++ { - digit := IntRange(0, 9).Draw(t, ufmt.Sprintf("digit%d", i)).(int) - result += ufmt.Sprintf("%d", digit) - } - - val, err := uint256.FromDecimal(result) - if err != nil || val.Cmp(g.min) < 0 || val.Cmp(g.max) > 0 { - // If out of range, return min + if g.span.IsZero() { + consumeEntropyBit(t.s) return g.min.Clone() } - return val + offset := drawUint256UpTo(t.s, g.span) + return new(uint256.Uint).Add(g.min, offset) } // Uint256 generates random uint256 values across the full uint256 range [0, 2^256-1] @@ -67,13 +54,7 @@ func (g *uint256Gen) value(t *T) any { // // Uint256() generates values from 0 to max uint256 func Uint256() *Generator { - // Max uint256: 2^256 - 1 - maxStr := "115792089237316195423570985008687907853269984665640564039457584007913129639935" - max, _ := uint256.FromDecimal(maxStr) - return newGenerator(&uint256Gen{ - min: uint256.NewUint(0), - max: max, - }) + return Uint256RangeFrom(uint256.NewUint(0), maxUint256Value) } // Uint256Max generates uint256 values from 0 to the specified max @@ -87,10 +68,7 @@ func Uint256Max(max string) *Generator { if err != nil { maxVal = uint256.NewUint(1000000) } - return newGenerator(&uint256Gen{ - min: uint256.NewUint(0), - max: maxVal, - }) + return Uint256RangeFrom(uint256.NewUint(0), maxVal) } // Uint256Range generates uint256 values within a specified range [min, max] @@ -110,10 +88,7 @@ func Uint256Range(minStr, maxStr string) *Generator { max = uint256.NewUint(1000000) } - return newGenerator(&uint256Gen{ - min: min, - max: max, - }) + return Uint256RangeFrom(min, max) } // Uint256Min generates uint256 values from the specified min to uint256 max @@ -127,124 +102,43 @@ func Uint256Min(min string) *Generator { if err != nil { minVal = uint256.NewUint(0) } - maxStr := "115792089237316195423570985008687907853269984665640564039457584007913129639935" - maxVal, _ := uint256.FromDecimal(maxStr) - return newGenerator(&uint256Gen{ - min: minVal, - max: maxVal, - }) + return Uint256RangeFrom(minVal, maxUint256Value) +} + +// Uint256RangeFrom builds a uint256 generator from already parsed bounds. +func Uint256RangeFrom(minVal *uint256.Uint, maxVal *uint256.Uint) *Generator { + assertf(minVal != nil && maxVal != nil, "Uint256RangeFrom: nil bounds") + return newGenerator(newUint256Gen(minVal, maxVal)) } // Int256 Generators type int256Gen struct { - min *int256.Int - max *int256.Int + min *int256.Int + max *int256.Int + span *int256.Int } func (g *int256Gen) String() string { - return ufmt.Sprintf("Int256(%s, %s)", g.min.ToString(), g.max.ToString()) + var b strings.Builder + b.WriteString("Int256(") + b.WriteString(g.min.ToString()) + b.WriteString(", ") + b.WriteString(g.max.ToString()) + b.WriteByte(')') + return b.String() } func (g *int256Gen) value(t *T) any { - // If min >= max, return min - if g.min.Cmp(g.max) >= 0 { - return g.min.Clone() - } - - // Calculate range = max - min - rangeVal := new(int256.Int).Sub(g.max, g.min) - - // Get absolute value for range calculation - rangeAbs := rangeVal.Abs() - - // For small ranges that fit in uint64 - if rangeAbs.IsUint64() { - rangeU64 := rangeAbs.Uint64() - if rangeU64 == 0 { - return g.min.Clone() - } - randomOffset := Uint64Range(0, rangeU64).Draw(t, "offset").(uint64) - result := new(int256.Int).Add(g.min, int256.FromUint256(uint256.NewUint(randomOffset))) - return result - } - - // For larger ranges, use digit-based approach - minStr := g.min.ToString() - maxStr := g.max.ToString() - - // Determine if result should be positive or negative - minNeg := g.min.Sign() < 0 - maxPos := g.max.Sign() > 0 - - var isNegative bool - if minNeg && maxPos { - // Range spans zero, randomly choose - isNegative = Bool().Draw(t, "isNegative").(bool) - } else { - isNegative = minNeg - } - - // Calculate digit range based on sign - var minDigits, maxDigits int - if isNegative { - // For negative, use min's digits (or 1 if positive min) - if minNeg { - minDigits = len(minStr) - 1 // subtract '-' - } else { - minDigits = 1 - } - // For max digits, use max if it's negative - if maxPos { - // Range crosses zero, so pick moderate size - maxDigits = minDigits + 10 - } else { - maxDigits = len(maxStr) - 1 // subtract '-' - } - } else { - // For positive, use min's digits (or 1 if negative min) - if maxPos { - maxDigits = len(maxStr) - } else { - maxDigits = 1 - } - if minPos := g.min.Sign() > 0; minPos { - minDigits = len(minStr) - } else { - minDigits = 1 - } - } - - // Ensure minDigits <= maxDigits - if minDigits > maxDigits { - minDigits, maxDigits = maxDigits, minDigits - } - if minDigits < 1 { - minDigits = 1 - } - - numDigits := IntRange(minDigits, maxDigits).Draw(t, "numDigits").(int) - - // Build random decimal string - firstDigit := IntRange(1, 9).Draw(t, "firstDigit").(int) - result := ufmt.Sprintf("%d", firstDigit) - - for i := 1; i < numDigits; i++ { - digit := IntRange(0, 9).Draw(t, ufmt.Sprintf("digit%d", i)).(int) - result += ufmt.Sprintf("%d", digit) - } - - if isNegative { - result = "-" + result - } - - val, err := int256.FromDecimal(result) - if err != nil || val.Cmp(g.min) < 0 || val.Cmp(g.max) > 0 { - // If out of range, return min + if g.span.IsZero() { + consumeEntropyBit(t.s) return g.min.Clone() } - return val + offset := drawInt256UpTo(t.s, g.span) + result := g.min.Clone() + result.Add(g.min, offset) + return result } // Int256 generates random int256 values across the full int256 range [-2^255, 2^255-1] @@ -253,16 +147,7 @@ func (g *int256Gen) value(t *T) any { // // Int256() generates values from min int256 to max int256 func Int256() *Generator { - // Max int256: 2^255 - 1 - // Min int256: -2^255 - minStr := "-57896044618658097711785492504343953926634992332820282019728792003956564819968" - maxStr := "57896044618658097711785492504343953926634992332820282019728792003956564819967" - min, _ := int256.FromDecimal(minStr) - max, _ := int256.FromDecimal(maxStr) - return newGenerator(&int256Gen{ - min: min, - max: max, - }) + return Int256RangeFrom(minInt256Value, maxInt256Value) } // Int256Max generates int256 values from min int256 to the specified max @@ -276,12 +161,7 @@ func Int256Max(max string) *Generator { if err != nil { maxVal = int256.NewInt(1000000) } - minStr := "-57896044618658097711785492504343953926634992332820282019728792003956564819968" - minVal, _ := int256.FromDecimal(minStr) - return newGenerator(&int256Gen{ - min: minVal, - max: maxVal, - }) + return Int256RangeFrom(minInt256Value, maxVal) } // Int256Range generates int256 values within a specified range [min, max] @@ -301,10 +181,7 @@ func Int256Range(minStr, maxStr string) *Generator { max = int256.NewInt(1000000) } - return newGenerator(&int256Gen{ - min: min, - max: max, - }) + return Int256RangeFrom(min, max) } // Int256Min generates int256 values from the specified min to max int256 @@ -318,10 +195,102 @@ func Int256Min(min string) *Generator { if err != nil { minVal = int256.NewInt(0) } - maxStr := "57896044618658097711785492504343953926634992332820282019728792003956564819967" - maxVal, _ := int256.FromDecimal(maxStr) - return newGenerator(&int256Gen{ - min: minVal, - max: maxVal, - }) + return Int256RangeFrom(minVal, maxInt256Value) +} + +// Int256RangeFrom builds an int256 generator from already parsed bounds. +func Int256RangeFrom(minVal *int256.Int, maxVal *int256.Int) *Generator { + assertf(minVal != nil && maxVal != nil, "Int256RangeFrom: nil bounds") + return newGenerator(newInt256Gen(minVal, maxVal)) +} + +func newUint256Gen(minVal *uint256.Uint, maxVal *uint256.Uint) *uint256Gen { + minClone := minVal.Clone() + maxClone := maxVal.Clone() + assertf(minClone.Cmp(maxClone) <= 0, "invalid uint256 range [%s, %s]", minClone.ToString(), maxClone.ToString()) + span := new(uint256.Uint).Sub(maxClone, minClone) + return &uint256Gen{min: minClone, max: maxClone, span: span} +} + +func newInt256Gen(minVal *int256.Int, maxVal *int256.Int) *int256Gen { + minClone := minVal.Clone() + maxClone := maxVal.Clone() + assertf(minClone.Cmp(maxClone) <= 0, "invalid int256 range [%s, %s]", minClone.ToString(), maxClone.ToString()) + + // Handle overflow when computing span for full int256 range + // When max - min would overflow (e.g., MaxInt256 - MinInt256), use MaxInt256 as span + diff, overflow := new(int256.Int).SubOverflow(maxClone, minClone) + if overflow || diff.Sign() < 0 { + // Span would exceed int256 range, use MaxInt256 as approximation + diff = int256.MaxInt256() + } + return &int256Gen{min: minClone, max: maxClone, span: diff} +} + +func drawUint256UpTo(s bitStream, max *uint256.Uint) *uint256.Uint { + if max.IsZero() { + return uint256.NewUint(0) + } + + bitlen := max.BitLen() + numBytes := (bitlen + 7) / 8 + buf := make([]byte, numBytes) + candidate := new(uint256.Uint) + + for attempt := 0; attempt < maxUint256SampleAttempts; attempt++ { + for i := 0; i < numBytes; i++ { + buf[i] = byte(s.drawBits(8)) + } + + extraBits := numBytes*8 - bitlen + if extraBits > 0 { + buf[0] &= byte(0xFF >> uint(extraBits)) + } + + candidate.SetBytes(buf) + if candidate.Lte(max) { + return candidate.Clone() + } + } + + panic(invalidData("failed to sample uint256 within range")) +} + +func drawInt256UpTo(s bitStream, max *int256.Int) *int256.Int { + if max.IsZero() { + return int256.NewInt(0) + } + + // Get absolute value for bit length calculation + absMax := max.Abs() + bitlen := absMax.BitLen() + numBytes := (bitlen + 7) / 8 + buf := make([]byte, numBytes) + candidate := new(uint256.Uint) + + for attempt := 0; attempt < maxUint256SampleAttempts; attempt++ { + for i := 0; i < numBytes; i++ { + buf[i] = byte(s.drawBits(8)) + } + + extraBits := numBytes*8 - bitlen + if extraBits > 0 { + buf[0] &= byte(0xFF >> uint(extraBits)) + } + + candidate.SetBytes(buf) + + // Check if candidate fits within max (as unsigned) + if candidate.Lte(absMax) { + // Safe to convert - value is within range + return int256.FromUint256(candidate) + } + } + + // Fallback: return 0 if we couldn't sample in range + return int256.NewInt(0) +} + +func consumeEntropyBit(s bitStream) { + s.drawBits(1) } diff --git a/contract/p/gnoswap/fuzz/int256_test.gno b/contract/p/gnoswap/fuzz/int256_test.gno index e5b76a90e..1ddc19b06 100644 --- a/contract/p/gnoswap/fuzz/int256_test.gno +++ b/contract/p/gnoswap/fuzz/int256_test.gno @@ -185,3 +185,62 @@ func TestInt256_Int256RangePositiveOnly(t *testing.T) { } }) } + +func TestUint256Range_PreservesSwapBounds(t *testing.T) { + const ( + minPrice = "4295128740" + maxPrice = "1461446703485210103287273052203988822378723970341" + ) + + gen := Uint256Range(minPrice, maxPrice) + impl, ok := gen.impl.(*uint256Gen) + if !ok { + t.Fatalf("unexpected generator implementation: %T", gen.impl) + } + + minBefore := impl.min.ToString() + maxBefore := impl.max.ToString() + + CheckN(t, 5, func(ft *T) { + val := gen.Draw(ft, "priceLimit").(*uint256.Uint) + + if val.Cmp(impl.min) < 0 || val.Cmp(impl.max) > 0 { + ft.Fatalf("value %s out of range [%s, %s]", val.ToString(), impl.min.ToString(), impl.max.ToString()) + } + if val == impl.min || val == impl.max { + ft.Fatalf("generator returned internal bound pointer") + } + }) + + if impl.min.ToString() != minBefore || impl.max.ToString() != maxBefore { + t.Fatalf("generator bounds mutated during draws") + } +} + +func TestInt256Range_SingleValueReturnsClone(t *testing.T) { + const value = "-12345678901234567890" + + gen := Int256Range(value, value) + impl, ok := gen.impl.(*int256Gen) + if !ok { + t.Fatalf("unexpected generator implementation: %T", gen.impl) + } + + minBefore := impl.min.ToString() + maxBefore := impl.max.ToString() + + CheckN(t, 3, func(ft *T) { + val := gen.Draw(ft, "singleVal").(*int256.Int) + + if val.ToString() != value { + ft.Fatalf("value %s differs from expected %s", val.ToString(), value) + } + if val == impl.min || val == impl.max { + ft.Fatalf("generator returned internal bound pointer") + } + }) + + if impl.min.ToString() != minBefore || impl.max.ToString() != maxBefore { + t.Fatalf("generator bounds mutated during draws") + } +} diff --git a/contract/p/gnoswap/fuzzutils/runner.gno b/contract/p/gnoswap/fuzzutils/runner.gno index a1b8c6797..1fab613b6 100644 --- a/contract/p/gnoswap/fuzzutils/runner.gno +++ b/contract/p/gnoswap/fuzzutils/runner.gno @@ -11,6 +11,7 @@ import ( func RunFuzzTest(t *testing.T, iterations int, fn func(*fuzz.T, *Result, int), options ...any) { result := NewFuzzResult(fuzz.BASE_SEED) isHandlePanic := true + isPrintLog := true index := 0 if len(options) > 0 { @@ -20,6 +21,13 @@ func RunFuzzTest(t *testing.T, iterations int, fn func(*fuzz.T, *Result, int), o } } + if len(options) > 1 { + isPrintLogOption, ok := options[1].(bool) + if ok { + isPrintLog = isPrintLogOption + } + } + // Use CheckN instead of CheckWithConfig fuzz.CheckN(t, iterations, func(ft *fuzz.T) { index++ @@ -45,6 +53,14 @@ func RunFuzzTest(t *testing.T, iterations int, fn func(*fuzz.T, *Result, int), o fn(ft, result, index) } + if isPrintLog { + if isError { + ufmt.Printf("[LOG-%d] %s (error: %s)\n", index, result.params[index].ToString(), errorMessage) + } else { + ufmt.Printf("[LOG-%d] %s (valid)\n", index, result.params[index].ToString()) + } + } + if isError { message := strings.TrimSpace(errorMessage) result.AddErrorMessage(index, message) diff --git a/contract/r/gnoswap/test/fuzz/int256_arithmetic_fuzz_params_test.gno b/contract/r/gnoswap/test/fuzz/int256_arithmetic_fuzz_params_test.gno new file mode 100644 index 000000000..3d0581b66 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_arithmetic_fuzz_params_test.gno @@ -0,0 +1,320 @@ +package fuzz + +import ( + "gno.land/p/gnoswap/fuzz" + i256 "gno.land/p/gnoswap/int256" + "gno.land/p/nt/ufmt" +) + +// Arithmetic Identity Laws Parameters (for associativity, commutativity, distributivity tests) +type int256IdentityLawParams struct { + a string + b string + c string +} + +func (p *int256IdentityLawParams) IsValid() bool { + // Identity laws are always valid (we test with wrap-around semantics) + return true +} + +func (p *int256IdentityLawParams) ToString() string { + return ufmt.Sprintf("a: %s, b: %s, c: %s", p.a, p.b, p.c) +} + +func NewValidInt256IdentityLawParams(t *fuzz.T) *int256IdentityLawParams { + // Use smaller values to avoid overflow in some tests + a := fuzz.Int64Range(-1<<20, 1<<20).Draw(t, "a").(int64) + b := fuzz.Int64Range(-1<<20, 1<<20).Draw(t, "b").(int64) + c := fuzz.Int64Range(-1<<20, 1<<20).Draw(t, "c").(int64) + + return &int256IdentityLawParams{ + a: i256.NewInt(a).ToString(), + b: i256.NewInt(b).ToString(), + c: i256.NewInt(c).ToString(), + } +} + +func NewRandomizedInt256IdentityLawParams(t *fuzz.T) *int256IdentityLawParams { + // Use int64 range to avoid overflow issues with full int256 range + a := fuzz.Int64().Draw(t, "a").(int64) + b := fuzz.Int64().Draw(t, "b").(int64) + c := fuzz.Int64().Draw(t, "c").(int64) + + return &int256IdentityLawParams{ + a: i256.NewInt(a).ToString(), + b: i256.NewInt(b).ToString(), + c: i256.NewInt(c).ToString(), + } +} + +// Comparison Parameters +type int256CmpParams struct { + x string + y string +} + +func (p *int256CmpParams) IsValid() bool { + // Comparison is always valid + return true +} + +func (p *int256CmpParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256CmpParams(t *fuzz.T) *int256CmpParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256CmpParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedInt256CmpParams(t *fuzz.T) *int256CmpParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256CmpParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Sign Test Parameters +type int256SignParams struct { + x string +} + +func (p *int256SignParams) IsValid() bool { + return true +} + +func (p *int256SignParams) ToString() string { + return ufmt.Sprintf("x: %s", p.x) +} + +func NewValidInt256SignParams(t *fuzz.T) *int256SignParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256SignParams{ + x: x.ToString(), + } +} + +func NewRandomizedInt256SignParams(t *fuzz.T) *int256SignParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256SignParams{ + x: x.ToString(), + } +} + +// Clone Parameters +type int256CloneParams struct { + x string +} + +func (p *int256CloneParams) IsValid() bool { + return true +} + +func (p *int256CloneParams) ToString() string { + return ufmt.Sprintf("x: %s", p.x) +} + +func NewValidInt256CloneParams(t *fuzz.T) *int256CloneParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256CloneParams{ + x: x.ToString(), + } +} + +func NewRandomizedInt256CloneParams(t *fuzz.T) *int256CloneParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256CloneParams{ + x: x.ToString(), + } +} + +// Conversion Parameters (Int64, Uint64) +type int256ConversionParams struct { + x string +} + +func (p *int256ConversionParams) IsValid() bool { + return true +} + +func (p *int256ConversionParams) ToString() string { + return ufmt.Sprintf("x: %s", p.x) +} + +func NewValidInt256ConversionParams(t *fuzz.T) *int256ConversionParams { + // Use values within int64 range for predictable conversion + x := fuzz.Int64().Draw(t, "x").(int64) + + return &int256ConversionParams{ + x: i256.NewInt(x).ToString(), + } +} + +func NewRandomizedInt256ConversionParams(t *fuzz.T) *int256ConversionParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256ConversionParams{ + x: x.ToString(), + } +} + +// SetString Parameters +type int256SetStringParams struct { + s string +} + +func (p *int256SetStringParams) IsValid() bool { + // Check if string is a valid decimal + _, err := i256.FromDecimal(p.s) + return err == nil +} + +func (p *int256SetStringParams) ToString() string { + return ufmt.Sprintf("s: %s", p.s) +} + +func NewValidInt256SetStringParams(t *fuzz.T) *int256SetStringParams { + // Generate a valid decimal string + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256SetStringParams{ + s: x.ToString(), + } +} + +func NewRandomizedInt256SetStringParams(t *fuzz.T) *int256SetStringParams { + // Generate potentially invalid strings + choice := fuzz.IntRange(0, 4).Draw(t, "choice").(int) + + var s string + switch choice { + case 0: + // Valid positive number + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + if x.Sign() < 0 { + x.Neg(x) + } + s = x.ToString() + case 1: + // Valid negative number + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + if x.Sign() > 0 { + x.Neg(x) + } + s = x.ToString() + case 2: + // Zero + s = "0" + case 3: + // Explicit positive sign + x := fuzz.Uint64Range(1, 1<<62).Draw(t, "x").(uint64) + s = ufmt.Sprintf("+%d", x) + case 4: + // Small int64 value + x := fuzz.Int64().Draw(t, "x").(int64) + s = i256.NewInt(x).ToString() + } + + return &int256SetStringParams{ + s: s, + } +} + +// DivRem Parameters (for testing Div and Rem together) +type int256DivRemParams struct { + x string + y string +} + +func (p *int256DivRemParams) IsValid() bool { + // Division by zero panics + y := i256.MustFromDecimal(p.y) + return !y.IsZero() +} + +func (p *int256DivRemParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256DivRemParams(t *fuzz.T) *int256DivRemParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int64Range(1, 1<<62).Draw(t, "y").(int64) + if fuzz.Bool().Draw(t, "yNeg").(bool) { + y = -y + } + + return &int256DivRemParams{ + x: x.ToString(), + y: i256.NewInt(y).ToString(), + } +} + +func NewRandomizedInt256DivRemParams(t *fuzz.T) *int256DivRemParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256DivRemParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Boundary Value Parameters +type int256BoundaryParams struct { + boundaryType int // 0: MinInt256, 1: MaxInt256, 2: Zero, 3: One, 4: NegOne +} + +func (p *int256BoundaryParams) IsValid() bool { + return true +} + +func (p *int256BoundaryParams) ToString() string { + return ufmt.Sprintf("boundaryType: %d", p.boundaryType) +} + +func NewValidInt256BoundaryParams(t *fuzz.T) *int256BoundaryParams { + boundaryType := fuzz.IntRange(0, 4).Draw(t, "boundaryType").(int) + + return &int256BoundaryParams{ + boundaryType: boundaryType, + } +} + +func NewRandomizedInt256BoundaryParams(t *fuzz.T) *int256BoundaryParams { + boundaryType := fuzz.IntRange(0, 4).Draw(t, "boundaryType").(int) + + return &int256BoundaryParams{ + boundaryType: boundaryType, + } +} + +// Helper function to get Int from boundary type +func (p *int256BoundaryParams) GetValue() *i256.Int { + switch p.boundaryType { + case 0: + return i256.MinInt256() + case 1: + return i256.MaxInt256() + case 2: + return i256.Zero() + case 3: + return i256.One() + case 4: + return i256.NewInt(-1) + default: + return i256.Zero() + } +} diff --git a/contract/r/gnoswap/test/fuzz/int256_arithmetic_fuzz_test.gno b/contract/r/gnoswap/test/fuzz/int256_arithmetic_fuzz_test.gno new file mode 100644 index 000000000..012216a29 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_arithmetic_fuzz_test.gno @@ -0,0 +1,607 @@ +package fuzz + +import ( + "testing" + + "gno.land/p/gnoswap/fuzz" + "gno.land/p/gnoswap/fuzzutils" + i256 "gno.land/p/gnoswap/int256" +) + +// Arithmetic Identity Laws Tests + +// TestFuzzInt256Arithmetic_AdditionAssociativity tests (a + b) + c == a + (b + c) +func TestFuzzInt256Arithmetic_AdditionAssociativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given - use valid params to avoid overflow + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + b := i256.MustFromDecimal(params.b) + c := i256.MustFromDecimal(params.c) + + // (a + b) + c + ab := new(i256.Int).Add(a, b) + left := new(i256.Int).Add(ab, c) + + // a + (b + c) + bc := new(i256.Int).Add(b, c) + right := new(i256.Int).Add(a, bc) + + if !left.Eq(right) { + panic("Addition associativity failed: (a + b) + c != a + (b + c)") + } + }) +} + +// TestFuzzInt256Arithmetic_AdditionCommutativity tests a + b == b + a +func TestFuzzInt256Arithmetic_AdditionCommutativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + b := i256.MustFromDecimal(params.b) + + ab := new(i256.Int).Add(a, b) + ba := new(i256.Int).Add(b, a) + + if !ab.Eq(ba) { + panic("Addition commutativity failed: a + b != b + a") + } + }) +} + +// TestFuzzInt256Arithmetic_MultiplicationAssociativity tests (a * b) * c == a * (b * c) +func TestFuzzInt256Arithmetic_MultiplicationAssociativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given - use small values to avoid overflow + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + b := i256.MustFromDecimal(params.b) + c := i256.MustFromDecimal(params.c) + + // (a * b) * c + ab := new(i256.Int).Mul(a, b) + left := new(i256.Int).Mul(ab, c) + + // a * (b * c) + bc := new(i256.Int).Mul(b, c) + right := new(i256.Int).Mul(a, bc) + + if !left.Eq(right) { + panic("Multiplication associativity failed: (a * b) * c != a * (b * c)") + } + }) +} + +// TestFuzzInt256Arithmetic_MultiplicationCommutativity tests a * b == b * a +func TestFuzzInt256Arithmetic_MultiplicationCommutativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + b := i256.MustFromDecimal(params.b) + + ab := new(i256.Int).Mul(a, b) + ba := new(i256.Int).Mul(b, a) + + if !ab.Eq(ba) { + panic("Multiplication commutativity failed: a * b != b * a") + } + }) +} + +// TestFuzzInt256Arithmetic_DistributiveLaw tests a * (b + c) == a*b + a*c +func TestFuzzInt256Arithmetic_DistributiveLaw(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given - use small values to avoid overflow + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + b := i256.MustFromDecimal(params.b) + c := i256.MustFromDecimal(params.c) + + // a * (b + c) + bPlusC := new(i256.Int).Add(b, c) + left := new(i256.Int).Mul(a, bPlusC) + + // a*b + a*c + ab := new(i256.Int).Mul(a, b) + ac := new(i256.Int).Mul(a, c) + right := new(i256.Int).Add(ab, ac) + + if !left.Eq(right) { + panic("Distributive law failed: a * (b + c) != a*b + a*c") + } + }) +} + +// TestFuzzInt256Arithmetic_AdditionIdentity tests a + 0 == a +func TestFuzzInt256Arithmetic_AdditionIdentity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + zero := i256.Zero() + + result := new(i256.Int).Add(a, zero) + if !result.Eq(a) { + panic("Addition identity failed: a + 0 != a") + } + }) +} + +// TestFuzzInt256Arithmetic_MultiplicationIdentity tests a * 1 == a +func TestFuzzInt256Arithmetic_MultiplicationIdentity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + one := i256.One() + + result := new(i256.Int).Mul(a, one) + if !result.Eq(a) { + panic("Multiplication identity failed: a * 1 != a") + } + }) +} + +// TestFuzzInt256Arithmetic_MultiplicationZero tests a * 0 == 0 +func TestFuzzInt256Arithmetic_MultiplicationZero(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + zero := i256.Zero() + + result := new(i256.Int).Mul(a, zero) + if !result.IsZero() { + panic("Multiplication zero failed: a * 0 != 0") + } + }) +} + +// TestFuzzInt256Arithmetic_SubtractionInverse tests a - a == 0 +func TestFuzzInt256Arithmetic_SubtractionInverse(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + + result := new(i256.Int).Sub(a, a) + if !result.IsZero() { + panic("Subtraction inverse failed: a - a != 0") + } + }) +} + +// TestFuzzInt256Arithmetic_AdditionInverse tests a + (-a) == 0 +func TestFuzzInt256Arithmetic_AdditionInverse(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + negA := new(i256.Int).Neg(a) + + result := new(i256.Int).Add(a, negA) + if !result.IsZero() { + panic("Addition inverse failed: a + (-a) != 0") + } + }) +} + +// TestFuzzInt256Arithmetic_DoubleNegation tests -(-a) == a +func TestFuzzInt256Arithmetic_DoubleNegation(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + negA := new(i256.Int).Neg(a) + result := new(i256.Int).Neg(negA) + + if !result.Eq(a) { + panic("Double negation failed: -(-a) != a") + } + }) +} + +// TestFuzzInt256Arithmetic_NegationMultiplication tests -a == a * (-1) +func TestFuzzInt256Arithmetic_NegationMultiplication(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + negOne := i256.NewInt(-1) + + negA := new(i256.Int).Neg(a) + mulResult := new(i256.Int).Mul(a, negOne) + + if !negA.Eq(mulResult) { + panic("Negation multiplication failed: -a != a * (-1)") + } + }) +} + +// TestFuzzInt256Cmp_Antisymmetry tests if a.Cmp(b) == 1 then b.Cmp(a) == -1 +func TestFuzzInt256Cmp_Antisymmetry(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256CmpParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + cmpXY := x.Cmp(y) + cmpYX := y.Cmp(x) + + if cmpXY != -cmpYX { + panic("Comparison antisymmetry failed: x.Cmp(y) != -y.Cmp(x)") + } + }) +} + +// TestFuzzInt256Cmp_Transitivity tests transitivity of comparison +func TestFuzzInt256Cmp_Transitivity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + a := i256.MustFromDecimal(params.a) + b := i256.MustFromDecimal(params.b) + c := i256.MustFromDecimal(params.c) + + // If a <= b and b <= c, then a <= c + if a.Lte(b) && b.Lte(c) { + if !a.Lte(c) { + panic("Comparison transitivity failed: a <= b and b <= c but a > c") + } + } + }) +} + +// TestFuzzInt256Cmp_EqConsistency tests Eq consistency with Cmp +func TestFuzzInt256Cmp_EqConsistency(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256CmpParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + cmpResult := x.Cmp(y) + eqResult := x.Eq(y) + + if (cmpResult == 0) != eqResult { + panic("Eq/Cmp consistency failed: (Cmp == 0) != Eq") + } + }) +} + +// TestFuzzInt256Cmp_LtGtConsistency tests Lt/Gt consistency +func TestFuzzInt256Cmp_LtGtConsistency(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256CmpParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + // x < y should be same as y > x + if x.Lt(y) != y.Gt(x) { + panic("Lt/Gt consistency failed: (x < y) != (y > x)") + } + + // x <= y should be same as y >= x + if x.Lte(y) != y.Gte(x) { + panic("Lte/Gte consistency failed: (x <= y) != (y >= x)") + } + }) +} + +// Sign Tests + +// TestFuzzInt256Sign_Consistency tests sign consistency +func TestFuzzInt256Sign_Consistency(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256SignParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + sign := x.Sign() + + // Sign should be -1, 0, or 1 + if sign != -1 && sign != 0 && sign != 1 { + panic("Sign is not -1, 0, or 1") + } + + // Sign consistency with comparison to zero + zero := i256.Zero() + if sign == 0 && !x.Eq(zero) { + panic("Sign is 0 but x != 0") + } + if sign > 0 && !x.Gt(zero) { + panic("Sign > 0 but x <= 0") + } + if sign < 0 && !x.Lt(zero) { + panic("Sign < 0 but x >= 0") + } + }) +} + +// TestFuzzInt256Sign_NegConsistency tests sign consistency with negation +func TestFuzzInt256Sign_NegConsistency(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256SignParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + negX := new(i256.Int).Neg(x) + + signX := x.Sign() + signNegX := negX.Sign() + + // Sign of -x should be opposite of x (unless x is zero) + if !x.IsZero() && signX != -signNegX { + panic("Sign of -x is not opposite of sign of x") + } + }) +} + +// Clone Tests + +// TestFuzzInt256Clone_Equality tests clone produces equal value +func TestFuzzInt256Clone_Equality(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256CloneParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + clone := x.Clone() + + if !x.Eq(clone) { + panic("Clone not equal to original") + } + }) +} + +// TestFuzzInt256Clone_Independence tests clone is independent +func TestFuzzInt256Clone_Independence(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256CloneParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + clone := x.Clone() + + // Modify clone + clone.Add(clone, i256.One()) + + // Original should not change (unless it was max which would wrap) + // Compare string representations to check independence + if x.ToString() == clone.ToString() && !x.Eq(i256.MaxInt256()) { + panic("Clone modification affected original") + } + }) +} + +// Conversion Tests + +// TestFuzzInt256Conversion_Int64RoundTrip tests Int64 round-trip for small values +func TestFuzzInt256Conversion_Int64RoundTrip(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + x := fuzz.Int64().Draw(ft, "x").(int64) + params := &int256ConversionParams{x: i256.NewInt(x).ToString()} + + // when + fuzzResult.AddParams(index, params) + + // then + val := i256.NewInt(x) + roundTrip := val.Int64() + + if roundTrip != x { + panic("Int64 round-trip failed") + } + }) +} + +// TestFuzzInt256Conversion_SetStringRoundTrip tests SetString/ToString round-trip +func TestFuzzInt256Conversion_SetStringRoundTrip(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256SetStringParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + val, err := i256.FromDecimal(params.s) + if err != nil { + panic("FromDecimal failed: " + err.Error()) + } + + roundTrip := val.ToString() + + // Parse again and compare + val2, err := i256.FromDecimal(roundTrip) + if err != nil { + panic("Second FromDecimal failed: " + err.Error()) + } + + if !val.Eq(val2) { + panic("SetString round-trip failed") + } + }) +} + +// Boundary Tests + +// TestFuzzInt256Boundary_MaxMinRelation tests MaxInt256 > MinInt256 +func TestFuzzInt256Boundary_MaxMinRelation(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256BoundaryParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + max := i256.MaxInt256() + min := i256.MinInt256() + + if !max.Gt(min) { + panic("MaxInt256 is not greater than MinInt256") + } + + if !max.Gt(i256.Zero()) { + panic("MaxInt256 is not greater than zero") + } + + if !min.Lt(i256.Zero()) { + panic("MinInt256 is not less than zero") + } + }) +} + +// TestFuzzInt256Boundary_ZeroIdentity tests zero boundary behavior +func TestFuzzInt256Boundary_ZeroIdentity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256BoundaryParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + zero := i256.Zero() + + if zero.Sign() != 0 { + panic("Zero sign is not 0") + } + + if !zero.IsZero() { + panic("Zero.IsZero() is false") + } + + if zero.IsNeg() { + panic("Zero is negative") + } + }) +} + +// TestFuzzInt256Boundary_OneIdentity tests one boundary behavior +func TestFuzzInt256Boundary_OneIdentity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256BoundaryParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + one := i256.One() + + if one.Sign() != 1 { + panic("One sign is not 1") + } + + if one.IsZero() { + panic("One.IsZero() is true") + } + + if one.IsNeg() { + panic("One is negative") + } + }) +} diff --git a/contract/r/gnoswap/test/fuzz/int256_bitwise_fuzz_params_test.gno b/contract/r/gnoswap/test/fuzz/int256_bitwise_fuzz_params_test.gno new file mode 100644 index 000000000..e18dde030 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_bitwise_fuzz_params_test.gno @@ -0,0 +1,339 @@ +package fuzz + +import ( + "gno.land/p/gnoswap/fuzz" + i256 "gno.land/p/gnoswap/int256" + "gno.land/p/nt/ufmt" +) + +// Left Shift (Lsh) Parameters +type int256LshParams struct { + x string + n uint + result *i256.Int +} + +func (p *int256LshParams) IsValid() bool { + // Check if the shift would cause overflow in uint256 + // For safety, limit shift to 255 bits max + if p.n > 255 { + return false + } + + x := i256.MustFromDecimal(p.x) + abs := x.Abs() + + // If x is 0, shift is always valid + if abs.IsZero() { + return true + } + + // Check if shift would overflow by checking bit length + bitLen := abs.BitLen() + if bitLen+int(p.n) > 256 { + return false + } + + return true +} + +func (p *int256LshParams) AddResult(result *i256.Int) { + p.result = result +} + +func (p *int256LshParams) ToString() string { + resultStr := "-" + if p.result != nil { + resultStr = p.result.ToString() + } + + return ufmt.Sprintf("x: %s, n: %d, result: %s", p.x, p.n, resultStr) +} + +func NewValidInt256LshParams(t *fuzz.T) *int256LshParams { + // Use small values to avoid overflow when shifting + x := fuzz.Int64Range(-1<<20, 1<<20).Draw(t, "x").(int64) + // Use small shift values to avoid extreme results + n := fuzz.Uint64Range(0, 32).Draw(t, "n").(uint64) + + return &int256LshParams{ + x: i256.NewInt(x).ToString(), + n: uint(n), + } +} + +func NewRandomizedInt256LshParams(t *fuzz.T) *int256LshParams { + // Use int64 range to avoid issues with full int256 generator + x := fuzz.Int64().Draw(t, "x").(int64) + // Use smaller shift values for randomized testing + n := fuzz.Uint64Range(0, 64).Draw(t, "n").(uint64) + + return &int256LshParams{ + x: i256.NewInt(x).ToString(), + n: uint(n), + } +} + +// Right Shift (Rsh) Parameters +type int256RshParams struct { + x string + n uint + result *i256.Int +} + +func (p *int256RshParams) IsValid() bool { + // Rsh is always valid + return true +} + +func (p *int256RshParams) AddResult(result *i256.Int) { + p.result = result +} + +func (p *int256RshParams) ToString() string { + resultStr := "-" + if p.result != nil { + resultStr = p.result.ToString() + } + + return ufmt.Sprintf("x: %s, n: %d, result: %s", p.x, p.n, resultStr) +} + +func NewValidInt256RshParams(t *fuzz.T) *int256RshParams { + // Use int64 range to avoid issues with full int256 generator + x := fuzz.Int64().Draw(t, "x").(int64) + // Use small shift values + n := fuzz.Uint64Range(0, 64).Draw(t, "n").(uint64) + + return &int256RshParams{ + x: i256.NewInt(x).ToString(), + n: uint(n), + } +} + +func NewRandomizedInt256RshParams(t *fuzz.T) *int256RshParams { + // Use int64 range to avoid issues with full int256 generator + x := fuzz.Int64().Draw(t, "x").(int64) + // Allow larger shift values for randomized testing + n := fuzz.Uint64Range(0, 128).Draw(t, "n").(uint64) + + return &int256RshParams{ + x: i256.NewInt(x).ToString(), + n: uint(n), + } +} + +// And Parameters +type int256AndParams struct { + x string + y string +} + +func (p *int256AndParams) IsValid() bool { + // And is always valid + return true +} + +func (p *int256AndParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256AndParams(t *fuzz.T) *int256AndParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256AndParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedInt256AndParams(t *fuzz.T) *int256AndParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256AndParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Or Parameters +type int256OrParams struct { + x string + y string +} + +func (p *int256OrParams) IsValid() bool { + // Or is always valid + return true +} + +func (p *int256OrParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256OrParams(t *fuzz.T) *int256OrParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256OrParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedInt256OrParams(t *fuzz.T) *int256OrParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256OrParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Shift Combined Parameters (for testing Lsh/Rsh together) +type int256ShiftParams struct { + x string + n uint +} + +func (p *int256ShiftParams) IsValid() bool { + return true +} + +func (p *int256ShiftParams) ToString() string { + return ufmt.Sprintf("x: %s, n: %d", p.x, p.n) +} + +func NewValidInt256ShiftParams(t *fuzz.T) *int256ShiftParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + // Use small shift values for round-trip testing + n := fuzz.Uint64Range(0, 32).Draw(t, "n").(uint64) + + return &int256ShiftParams{ + x: x.ToString(), + n: uint(n), + } +} + +func NewRandomizedInt256ShiftParams(t *fuzz.T) *int256ShiftParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + n := fuzz.Uint64Range(0, 128).Draw(t, "n").(uint64) + + return &int256ShiftParams{ + x: x.ToString(), + n: uint(n), + } +} + +// Sparse Pattern Parameters (for testing edge cases in bitwise operations) +type int256BitwiseSparseParams struct { + patternType int +} + +func (p *int256BitwiseSparseParams) IsValid() bool { + return true +} + +func (p *int256BitwiseSparseParams) ToString() string { + return ufmt.Sprintf("patternType: %d", p.patternType) +} + +func NewValidInt256BitwiseSparseParams(t *fuzz.T) *int256BitwiseSparseParams { + patternType := fuzz.IntRange(0, 7).Draw(t, "patternType").(int) + + return &int256BitwiseSparseParams{ + patternType: patternType, + } +} + +func NewRandomizedInt256BitwiseSparseParams(t *fuzz.T) *int256BitwiseSparseParams { + patternType := fuzz.IntRange(0, 7).Draw(t, "patternType").(int) + + return &int256BitwiseSparseParams{ + patternType: patternType, + } +} + +// Helper function to get Int from pattern type +func (p *int256BitwiseSparseParams) GetValue() *i256.Int { + switch p.patternType { + case 0: + // Zero + return i256.Zero() + case 1: + // One + return i256.One() + case 2: + // Negative one + return i256.NewInt(-1) + case 3: + // MaxInt256 + return i256.MaxInt256() + case 4: + // MinInt256 + return i256.MinInt256() + case 5: + // Small positive + return i256.NewInt(0x7FFFFFFF) + case 6: + // Small negative + return i256.NewInt(-0x7FFFFFFF) + case 7: + // Alternating bits pattern (positive) + return i256.MustFromDecimal("6148914691236517205") // 0x5555555555555555 + default: + return i256.Zero() + } +} + +// Combined Bitwise Operation Parameters +type int256BitwiseOpParams struct { + x string + y string + operation string // "and", "or" +} + +func (p *int256BitwiseOpParams) IsValid() bool { + return p.operation == "and" || p.operation == "or" +} + +func (p *int256BitwiseOpParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s, op: %s", p.x, p.y, p.operation) +} + +func NewValidInt256BitwiseOpParams(t *fuzz.T) *int256BitwiseOpParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + opChoice := fuzz.IntRange(0, 1).Draw(t, "op").(int) + + op := "and" + if opChoice == 1 { + op = "or" + } + + return &int256BitwiseOpParams{ + x: x.ToString(), + y: y.ToString(), + operation: op, + } +} + +func NewRandomizedInt256BitwiseOpParams(t *fuzz.T) *int256BitwiseOpParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + opChoice := fuzz.IntRange(0, 1).Draw(t, "op").(int) + + op := "and" + if opChoice == 1 { + op = "or" + } + + return &int256BitwiseOpParams{ + x: x.ToString(), + y: y.ToString(), + operation: op, + } +} diff --git a/contract/r/gnoswap/test/fuzz/int256_bitwise_fuzz_test.gno b/contract/r/gnoswap/test/fuzz/int256_bitwise_fuzz_test.gno new file mode 100644 index 000000000..4194ae6a4 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_bitwise_fuzz_test.gno @@ -0,0 +1,631 @@ +package fuzz + +import ( + "testing" + + "gno.land/p/gnoswap/fuzz" + "gno.land/p/gnoswap/fuzzutils" + i256 "gno.land/p/gnoswap/int256" +) + +// Left Shift (Lsh) Tests + +// TestFuzzInt256Lsh_ValidParams_Stateless tests Lsh with valid parameters +func TestFuzzInt256Lsh_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256LshParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Lsh(x, params.n) + params.AddResult(z) + }) +} + +// TestFuzzInt256Lsh_RandomizedParams_Stateless tests Lsh with randomized parameters +func TestFuzzInt256Lsh_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256LshParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Lsh(x, params.n) + params.AddResult(z) + }) +} + +// TestFuzzInt256Lsh_ZeroShift tests Lsh with shift by 0 returns same value +func TestFuzzInt256Lsh_ZeroShift(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256LshParams(ft) + params.n = 0 + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Lsh(x, 0) + params.AddResult(z) + + if !z.Eq(x) { + panic("Lsh by 0 should return same value") + } + }) +} + +// TestFuzzInt256Lsh_SignPreservation tests Lsh preserves sign +func TestFuzzInt256Lsh_SignPreservation(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256LshParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + // Skip zero (has no sign) + if x.IsZero() { + return + } + + z := new(i256.Int) + z.Lsh(x, params.n) + params.AddResult(z) + + // If result is not zero, sign should be preserved + if !z.IsZero() { + if x.IsNeg() != z.IsNeg() { + panic("Lsh changed sign of non-zero result") + } + } + }) +} + +// Right Shift (Rsh) Tests + +// TestFuzzInt256Rsh_ValidParams_Stateless tests Rsh with valid parameters +func TestFuzzInt256Rsh_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256RshParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Rsh(x, params.n) + + params.AddResult(z) + }) +} + +// TestFuzzInt256Rsh_RandomizedParams_Stateless tests Rsh with randomized parameters +func TestFuzzInt256Rsh_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256RshParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Rsh(x, params.n) + + params.AddResult(z) + }) +} + +// TestFuzzInt256Rsh_ZeroShift tests Rsh with shift by 0 returns same value +func TestFuzzInt256Rsh_ZeroShift(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256RshParams(ft) + params.n = 0 + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Rsh(x, 0) + + if !z.Eq(x) { + panic("Rsh by 0 should return same value") + } + }) +} + +// TestFuzzInt256Rsh_ArithmeticShift tests Rsh performs arithmetic right shift (sign extension) +func TestFuzzInt256Rsh_ArithmeticShift(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256RshParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Rsh(x, params.n) + + params.AddResult(z) + + // For negative numbers, result should remain negative (or be -1 for large shifts) + if x.IsNeg() && !z.IsNeg() && !z.IsZero() { + panic("Arithmetic right shift of negative number should preserve negativity") + } + }) +} + +// And Tests + +// TestFuzzInt256And_ValidParams_Stateless tests And with valid parameters +func TestFuzzInt256And_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256AndParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + z := new(i256.Int) + z.And(x, y) + }) +} + +// TestFuzzInt256And_RandomizedParams_Stateless tests And with randomized parameters +func TestFuzzInt256And_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256AndParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + z := new(i256.Int) + z.And(x, y) + }) +} + +// TestFuzzInt256And_Commutativity tests And commutativity: x & y == y & x +func TestFuzzInt256And_Commutativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256AndParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + xy := new(i256.Int).And(x, y) + yx := new(i256.Int).And(y, x) + + if !xy.Eq(yx) { + panic("And commutativity failed: x & y != y & x") + } + }) +} + +// TestFuzzInt256And_Idempotence tests And idempotence: x & x == x +func TestFuzzInt256And_Idempotence(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256AndParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + xx := new(i256.Int).And(x, x) + + if !xx.Eq(x) { + panic("And idempotence failed: x & x != x") + } + }) +} + +// Or Tests + +// TestFuzzInt256Or_ValidParams_Stateless tests Or with valid parameters +func TestFuzzInt256Or_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256OrParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + z := new(i256.Int) + z.Or(x, y) + }) +} + +// TestFuzzInt256Or_RandomizedParams_Stateless tests Or with randomized parameters +func TestFuzzInt256Or_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256OrParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + z := new(i256.Int) + z.Or(x, y) + }) +} + +// TestFuzzInt256Or_Commutativity tests Or commutativity: x | y == y | x +func TestFuzzInt256Or_Commutativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256OrParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + xy := new(i256.Int).Or(x, y) + yx := new(i256.Int).Or(y, x) + + if !xy.Eq(yx) { + panic("Or commutativity failed: x | y != y | x") + } + }) +} + +// TestFuzzInt256Or_Idempotence tests Or idempotence: x | x == x +func TestFuzzInt256Or_Idempotence(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256OrParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + xx := new(i256.Int).Or(x, x) + + if !xx.Eq(x) { + panic("Or idempotence failed: x | x != x") + } + }) +} + +// Bitwise Identity Tests + +// TestFuzzInt256Bitwise_AndZero tests x & 0 == 0 +func TestFuzzInt256Bitwise_AndZero(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256AndParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + zero := i256.Zero() + + result := new(i256.Int).And(x, zero) + + if !result.IsZero() { + panic("x & 0 should equal 0") + } + }) +} + +// TestFuzzInt256Bitwise_OrZero tests x | 0 == x +func TestFuzzInt256Bitwise_OrZero(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256OrParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + zero := i256.Zero() + + result := new(i256.Int).Or(x, zero) + + if !result.Eq(x) { + panic("x | 0 should equal x") + } + }) +} + +// TestFuzzInt256Bitwise_Absorption tests absorption law: x & (x | y) == x +func TestFuzzInt256Bitwise_Absorption(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256AndParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + y := i256.MustFromDecimal(params.y) + + // x & (x | y) == x + xOrY := new(i256.Int).Or(x, y) + result := new(i256.Int).And(x, xOrY) + + if !result.Eq(x) { + panic("Absorption law failed: x & (x | y) != x") + } + }) +} + +// Shift Relationship Tests + +// TestFuzzInt256Shift_LshMultiplication tests Lsh by 1 is multiplication by 2 for small positive values +func TestFuzzInt256Shift_LshMultiplication(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given - use small values to avoid overflow + x := fuzz.Int64Range(0, 1<<62).Draw(ft, "x").(int64) + params := &int256ShiftParams{ + x: i256.NewInt(x).ToString(), + n: 1, + } + + // when + fuzzResult.AddParams(index, params) + + // then + val := i256.NewInt(x) + + shifted := new(i256.Int).Lsh(val, 1) + multiplied := new(i256.Int).Mul(val, i256.NewInt(2)) + + if !shifted.Eq(multiplied) { + panic("Lsh by 1 should equal multiplication by 2") + } + }) +} + +// TestFuzzInt256Shift_RshDivision tests Rsh by 1 is division by 2 for positive values +func TestFuzzInt256Shift_RshDivision(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given - use positive values + x := fuzz.Int64Range(0, 1<<62).Draw(ft, "x").(int64) + params := &int256ShiftParams{ + x: i256.NewInt(x).ToString(), + n: 1, + } + + // when + fuzzResult.AddParams(index, params) + + // then + val := i256.NewInt(x) + + shifted := new(i256.Int).Rsh(val, 1) + divided := new(i256.Int).Div(val, i256.NewInt(2)) + + if !shifted.Eq(divided) { + panic("Rsh by 1 should equal division by 2 for positive values") + } + }) +} + +// TestFuzzInt256Shift_LargeRsh tests behavior with large right shift values +func TestFuzzInt256Shift_LargeRsh(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256ShiftParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + // Large right shift should be handled without panic + // Right shift by large amount should give 0 for positive, -1 for negative + z := new(i256.Int) + z.Rsh(x, 200) + + // If shift is very large, result approaches 0 or -1 + // Just verify no panic + }) +} + +// Sparse Pattern Tests + +// TestFuzzInt256Bitwise_SparsePatterns tests bitwise operations with sparse patterns +func TestFuzzInt256Bitwise_SparsePatterns(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params1 := NewValidInt256BitwiseSparseParams(ft) + params2 := NewValidInt256BitwiseSparseParams(ft) + + // when + fuzzResult.AddParams(index, params1) + + // then + x := params1.GetValue() + y := params2.GetValue() + + // Test And + andResult := new(i256.Int).And(x, y) + _ = andResult + + // Test Or + orResult := new(i256.Int).Or(x, y) + _ = orResult + + // Test Rsh (Rsh is always safe, Lsh can overflow for boundary values) + rshResult := new(i256.Int).Rsh(x, 1) + _ = rshResult + + // Test Lsh only for safe values (not MinInt256 or near-max values) + // Skip Lsh for extreme values that could overflow + // patternType 4 is MinInt256 which causes Abs() overflow + if params1.patternType == 4 || params1.patternType == 3 { + // Skip MinInt256 and MaxInt256 for Lsh tests + return + } + + // Safe to call Abs() now + abs := x.Abs() + bitLen := abs.BitLen() + if bitLen < 255 && !abs.IsZero() { + lshResult := new(i256.Int).Lsh(x, 1) + _ = lshResult + } + }) +} + +// Combined Operation Tests + +// TestFuzzInt256Bitwise_AndOrDistributive tests distributive law: x & (y | z) == (x & y) | (x & z) +func TestFuzzInt256Bitwise_AndOrDistributive(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.a) + y := i256.MustFromDecimal(params.b) + z := i256.MustFromDecimal(params.c) + + // x & (y | z) + yOrZ := new(i256.Int).Or(y, z) + left := new(i256.Int).And(x, yOrZ) + + // (x & y) | (x & z) + xAndY := new(i256.Int).And(x, y) + xAndZ := new(i256.Int).And(x, z) + right := new(i256.Int).Or(xAndY, xAndZ) + + if !left.Eq(right) { + panic("Distributive law failed: x & (y | z) != (x & y) | (x & z)") + } + }) +} + +// TestFuzzInt256Bitwise_OrAndDistributive tests distributive law: x | (y & z) == (x | y) & (x | z) +func TestFuzzInt256Bitwise_OrAndDistributive(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.a) + y := i256.MustFromDecimal(params.b) + z := i256.MustFromDecimal(params.c) + + // x | (y & z) + yAndZ := new(i256.Int).And(y, z) + left := new(i256.Int).Or(x, yAndZ) + + // (x | y) & (x | z) + xOrY := new(i256.Int).Or(x, y) + xOrZ := new(i256.Int).Or(x, z) + right := new(i256.Int).And(xOrY, xOrZ) + + if !left.Eq(right) { + panic("Distributive law failed: x | (y & z) != (x | y) & (x | z)") + } + }) +} + +// TestFuzzInt256Bitwise_Associativity tests associativity of And and Or +func TestFuzzInt256Bitwise_Associativity(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256IdentityLawParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.a) + y := i256.MustFromDecimal(params.b) + z := i256.MustFromDecimal(params.c) + + // Test And associativity: (x & y) & z == x & (y & z) + xy := new(i256.Int).And(x, y) + leftAnd := new(i256.Int).And(xy, z) + + yz := new(i256.Int).And(y, z) + rightAnd := new(i256.Int).And(x, yz) + + if !leftAnd.Eq(rightAnd) { + panic("And associativity failed: (x & y) & z != x & (y & z)") + } + + // Test Or associativity: (x | y) | z == x | (y | z) + xyOr := new(i256.Int).Or(x, y) + leftOr := new(i256.Int).Or(xyOr, z) + + yzOr := new(i256.Int).Or(y, z) + rightOr := new(i256.Int).Or(x, yzOr) + + if !leftOr.Eq(rightOr) { + panic("Or associativity failed: (x | y) | z != x | (y | z)") + } + }) +} diff --git a/contract/r/gnoswap/test/fuzz/int256_fuzz_params_test.gno b/contract/r/gnoswap/test/fuzz/int256_fuzz_params_test.gno new file mode 100644 index 000000000..f9e5d5d33 --- /dev/null +++ b/contract/r/gnoswap/test/fuzz/int256_fuzz_params_test.gno @@ -0,0 +1,426 @@ +package fuzz + +import ( + "gno.land/p/gnoswap/fuzz" + i256 "gno.land/p/gnoswap/int256" + "gno.land/p/nt/ufmt" +) + +// Add Parameters +type int256AddParams struct { + x string + y string + result *i256.Int +} + +func (p *int256AddParams) IsValid() bool { + // Add always succeeds (wraps on overflow) + return true +} + +func (p *int256AddParams) AddResult(result *i256.Int) { + p.result = result +} + +func (p *int256AddParams) ToString() string { + resultStr := "-" + if p.result != nil { + resultStr = p.result.ToString() + } + + return ufmt.Sprintf("x: %s, y: %s, result: %s", p.x, p.y, resultStr) +} + +func NewValidInt256AddParams(t *fuzz.T) *int256AddParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256AddParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedInt256AddParams(t *fuzz.T) *int256AddParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256AddParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// AddOverflow Parameters +type int256AddOverflowParams struct { + x string + y string + result *i256.Int + overflow bool +} + +func (p *int256AddOverflowParams) IsValid() bool { + x := i256.MustFromDecimal(p.x) + y := i256.MustFromDecimal(p.y) + + // Check if addition would overflow + // Overflow occurs when: + // - Both positive and sum > MaxInt256 + // - Both negative and sum < MinInt256 + if x.Sign() >= 0 && y.Sign() >= 0 { + // Both non-negative: check if x + y > MaxInt256 + maxInt := i256.MaxInt256() + remain := new(i256.Int).Sub(maxInt, y) + return x.Lte(remain) + } else if x.Sign() < 0 && y.Sign() < 0 { + // Both negative: check if x + y < MinInt256 + minInt := i256.MinInt256() + remain := new(i256.Int).Sub(minInt, y) + return x.Gte(remain) + } + // Different signs: no overflow possible + return true +} + +func (p *int256AddOverflowParams) AddResult(result *i256.Int, overflow bool) { + p.result = result + p.overflow = overflow +} + +func (p *int256AddOverflowParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s, result: %s, overflow: %t", p.x, p.y, p.result.ToString(), p.overflow) +} + +func NewValidInt256AddOverflowParams(t *fuzz.T) *int256AddOverflowParams { + // Generate values that won't overflow when added + x := fuzz.Int64Range(-1<<62, 1<<62).Draw(t, "x").(int64) + y := fuzz.Int64Range(-1<<62, 1<<62).Draw(t, "y").(int64) + + return &int256AddOverflowParams{ + x: i256.NewInt(x).ToString(), + y: i256.NewInt(y).ToString(), + } +} + +func NewRandomizedInt256AddOverflowParams(t *fuzz.T) *int256AddOverflowParams { + // Use int64 range to avoid issues with full int256 generator + x := fuzz.Int64().Draw(t, "x").(int64) + y := fuzz.Int64().Draw(t, "y").(int64) + + return &int256AddOverflowParams{ + x: i256.NewInt(x).ToString(), + y: i256.NewInt(y).ToString(), + } +} + +// Sub Parameters +type int256SubParams struct { + x string + y string + result *i256.Int +} + +func (p *int256SubParams) IsValid() bool { + // Sub always succeeds (wraps on underflow) + return true +} + +func (p *int256SubParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256SubParams(t *fuzz.T) *int256SubParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256SubParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedInt256SubParams(t *fuzz.T) *int256SubParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256SubParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// SubOverflow Parameters +type int256SubOverflowParams struct { + x string + y string +} + +func (p *int256SubOverflowParams) IsValid() bool { + x := i256.MustFromDecimal(p.x) + y := i256.MustFromDecimal(p.y) + + // x - y == x + (-y) + // Overflow occurs when: + // - x positive, y negative, and x - y > MaxInt256 + // - x negative, y positive, and x - y < MinInt256 + if x.Sign() >= 0 && y.Sign() < 0 { + // x - (-|y|) = x + |y|, check if > MaxInt256 + maxInt := i256.MaxInt256() + negY := new(i256.Int).Neg(y) + remain := new(i256.Int).Sub(maxInt, negY) + return x.Lte(remain) + } else if x.Sign() < 0 && y.Sign() > 0 { + // x - y where x < 0, y > 0, check if < MinInt256 + minInt := i256.MinInt256() + remain := new(i256.Int).Add(minInt, y) + return x.Gte(remain) + } + // Same signs: no overflow possible + return true +} + +func (p *int256SubOverflowParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256SubOverflowParams(t *fuzz.T) *int256SubOverflowParams { + // Generate values that won't overflow when subtracted + x := fuzz.Int64Range(-1<<62, 1<<62).Draw(t, "x").(int64) + y := fuzz.Int64Range(-1<<62, 1<<62).Draw(t, "y").(int64) + + return &int256SubOverflowParams{ + x: i256.NewInt(x).ToString(), + y: i256.NewInt(y).ToString(), + } +} + +func NewRandomizedInt256SubOverflowParams(t *fuzz.T) *int256SubOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256SubOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Mul Parameters +type int256MulParams struct { + x string + y string +} + +func (p *int256MulParams) IsValid() bool { + // Mul always succeeds (truncates overflow) + return true +} + +func (p *int256MulParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256MulParams(t *fuzz.T) *int256MulParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256MulParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +func NewRandomizedInt256MulParams(t *fuzz.T) *int256MulParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256MulParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// MulOverflow Parameters +type int256MulOverflowParams struct { + x string + y string +} + +func (p *int256MulOverflowParams) IsValid() bool { + x := i256.MustFromDecimal(p.x) + y := i256.MustFromDecimal(p.y) + + // Check for multiplication overflow + // Special case: multiplication by 0 never overflows + if x.IsZero() || y.IsZero() { + return true + } + + // Use MulOverflow to check + _, overflow := new(i256.Int).MulOverflow(x, y) + return !overflow +} + +func (p *int256MulOverflowParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256MulOverflowParams(t *fuzz.T) *int256MulOverflowParams { + // Use smaller values to avoid overflow + x := fuzz.Int64Range(-1<<31, 1<<31).Draw(t, "x").(int64) + y := fuzz.Int64Range(-1<<31, 1<<31).Draw(t, "y").(int64) + + return &int256MulOverflowParams{ + x: i256.NewInt(x).ToString(), + y: i256.NewInt(y).ToString(), + } +} + +func NewRandomizedInt256MulOverflowParams(t *fuzz.T) *int256MulOverflowParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256MulOverflowParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Div Parameters +type int256DivParams struct { + x string + y string +} + +func (p *int256DivParams) IsValid() bool { + // Division by zero panics + y := i256.MustFromDecimal(p.y) + return !y.IsZero() +} + +func (p *int256DivParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256DivParams(t *fuzz.T) *int256DivParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + // Ensure y is non-zero + y := fuzz.Int64Range(1, 1<<62).Draw(t, "y").(int64) + // Randomly make y negative + if fuzz.Bool().Draw(t, "yNeg").(bool) { + y = -y + } + + return &int256DivParams{ + x: x.ToString(), + y: i256.NewInt(y).ToString(), + } +} + +func NewRandomizedInt256DivParams(t *fuzz.T) *int256DivParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256DivParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Rem Parameters +type int256RemParams struct { + x string + y string +} + +func (p *int256RemParams) IsValid() bool { + // Remainder by zero panics + y := i256.MustFromDecimal(p.y) + return !y.IsZero() +} + +func (p *int256RemParams) ToString() string { + return ufmt.Sprintf("x: %s, y: %s", p.x, p.y) +} + +func NewValidInt256RemParams(t *fuzz.T) *int256RemParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int64Range(1, 1<<62).Draw(t, "y").(int64) + if fuzz.Bool().Draw(t, "yNeg").(bool) { + y = -y + } + + return &int256RemParams{ + x: x.ToString(), + y: i256.NewInt(y).ToString(), + } +} + +func NewRandomizedInt256RemParams(t *fuzz.T) *int256RemParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + y := fuzz.Int256().Draw(t, "y").(*i256.Int) + + return &int256RemParams{ + x: x.ToString(), + y: y.ToString(), + } +} + +// Neg Parameters +type int256NegParams struct { + x string +} + +func (p *int256NegParams) IsValid() bool { + // Neg always succeeds + return true +} + +func (p *int256NegParams) ToString() string { + return ufmt.Sprintf("x: %s", p.x) +} + +func NewValidInt256NegParams(t *fuzz.T) *int256NegParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256NegParams{ + x: x.ToString(), + } +} + +func NewRandomizedInt256NegParams(t *fuzz.T) *int256NegParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256NegParams{ + x: x.ToString(), + } +} + +// Abs Parameters +type int256AbsParams struct { + x string +} + +func (p *int256AbsParams) IsValid() bool { + // Abs always succeeds (returns uint256) + return true +} + +func (p *int256AbsParams) ToString() string { + return ufmt.Sprintf("x: %s", p.x) +} + +func NewValidInt256AbsParams(t *fuzz.T) *int256AbsParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256AbsParams{ + x: x.ToString(), + } +} + +func NewRandomizedInt256AbsParams(t *fuzz.T) *int256AbsParams { + x := fuzz.Int256().Draw(t, "x").(*i256.Int) + + return &int256AbsParams{ + x: x.ToString(), + } +} diff --git a/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno b/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno index 3c3f43904..ad1630a2e 100644 --- a/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno +++ b/contract/r/gnoswap/test/fuzz/int256_fuzz_test.gno @@ -12,7 +12,7 @@ import ( 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) + params := NewValidInt256AddParams(ft) // when fuzzResult.AddParams(index, params) @@ -21,8 +21,9 @@ func TestFuzzInt256Add_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Add(x, y) + params.AddResult(z) }) } @@ -30,7 +31,7 @@ func TestFuzzInt256Add_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256AddParams(ft) // when fuzzResult.AddParams(index, params) @@ -39,8 +40,9 @@ func TestFuzzInt256Add_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Add(x, y) + params.AddResult(z) }) } @@ -48,7 +50,7 @@ func TestFuzzInt256Add_RandomizedParams_Stateless(t *testing.T) { 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) + params := NewValidInt256AddOverflowParams(ft) // when fuzzResult.AddParams(index, params) @@ -57,9 +59,11 @@ func TestFuzzInt256AddOverflow_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) _, overflow := z.AddOverflow(x, y) + params.AddResult(z, overflow) + // Verify IsValid prediction matches actual result if overflow { panic("IsValid returned true but overflow occurred") } @@ -70,7 +74,7 @@ func TestFuzzInt256AddOverflow_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256AddOverflowParams(ft) // when fuzzResult.AddParams(index, params) @@ -79,17 +83,12 @@ func TestFuzzInt256AddOverflow_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int - _, overflow := z.AddOverflow(x, y) + z := new(i256.Int) + result, overflow := z.AddOverflow(x, y) + params.AddResult(result, overflow) - 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()) + if overflow { + panic("overflow occurred: " + params.ToString()) } }) } @@ -98,7 +97,7 @@ func TestFuzzInt256AddOverflow_RandomizedParams_Stateless(t *testing.T) { 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) + params := NewValidInt256SubParams(ft) // when fuzzResult.AddParams(index, params) @@ -107,7 +106,7 @@ func TestFuzzInt256Sub_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Sub(x, y) }) } @@ -116,7 +115,7 @@ func TestFuzzInt256Sub_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256SubParams(ft) // when fuzzResult.AddParams(index, params) @@ -125,7 +124,7 @@ func TestFuzzInt256Sub_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Sub(x, y) }) } @@ -134,7 +133,7 @@ func TestFuzzInt256Sub_RandomizedParams_Stateless(t *testing.T) { 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) + params := NewValidInt256SubOverflowParams(ft) // when fuzzResult.AddParams(index, params) @@ -143,9 +142,10 @@ func TestFuzzInt256SubOverflow_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) _, overflow := z.SubOverflow(x, y) + // Verify IsValid prediction matches actual result if overflow { panic("IsValid returned true but overflow occurred") } @@ -156,7 +156,7 @@ func TestFuzzInt256SubOverflow_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256SubOverflowParams(ft) // when fuzzResult.AddParams(index, params) @@ -165,17 +165,11 @@ func TestFuzzInt256SubOverflow_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(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()) + if overflow { + panic("overflow occurred: " + params.ToString()) } }) } @@ -184,7 +178,7 @@ func TestFuzzInt256SubOverflow_RandomizedParams_Stateless(t *testing.T) { 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) + params := NewValidInt256MulParams(ft) // when fuzzResult.AddParams(index, params) @@ -193,7 +187,7 @@ func TestFuzzInt256Mul_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Mul(x, y) }) } @@ -202,7 +196,7 @@ func TestFuzzInt256Mul_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256MulParams(ft) // when fuzzResult.AddParams(index, params) @@ -211,7 +205,7 @@ func TestFuzzInt256Mul_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Mul(x, y) }) } @@ -220,7 +214,7 @@ func TestFuzzInt256Mul_RandomizedParams_Stateless(t *testing.T) { 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) + params := NewValidInt256MulOverflowParams(ft) // when fuzzResult.AddParams(index, params) @@ -229,9 +223,10 @@ func TestFuzzInt256MulOverflow_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) _, overflow := z.MulOverflow(x, y) + // Verify IsValid prediction matches actual result if overflow { panic("IsValid returned true but overflow occurred") } @@ -242,7 +237,7 @@ func TestFuzzInt256MulOverflow_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256MulOverflowParams(ft) // when fuzzResult.AddParams(index, params) @@ -251,17 +246,11 @@ func TestFuzzInt256MulOverflow_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(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()) + if overflow { + panic("overflow occurred: " + params.ToString()) } }) } @@ -270,7 +259,7 @@ func TestFuzzInt256MulOverflow_RandomizedParams_Stateless(t *testing.T) { 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) + params := NewValidInt256DivParams(ft) // when fuzzResult.AddParams(index, params) @@ -279,7 +268,7 @@ func TestFuzzInt256Div_ValidParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + z := new(i256.Int) z.Div(x, y) }) } @@ -288,7 +277,7 @@ func TestFuzzInt256Div_ValidParams_Stateless(t *testing.T) { 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) + params := NewRandomizedInt256DivParams(ft) // when fuzzResult.AddParams(index, params) @@ -297,7 +286,71 @@ func TestFuzzInt256Div_RandomizedParams_Stateless(t *testing.T) { x := i256.MustFromDecimal(params.x) y := i256.MustFromDecimal(params.y) - var z i256.Int + // Skip if y is zero (would panic) + if y.IsZero() { + return + } + + z := new(i256.Int) z.Div(x, y) }) } + +// TestFuzzInt256Neg_RandomizedParams_Stateless tests Neg with randomized parameters +func TestFuzzInt256Neg_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256NegParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + z := new(i256.Int) + z.Neg(x) + }) +} + +// TestFuzzInt256Abs_ValidParams_Stateless tests Abs with valid parameters +func TestFuzzInt256Abs_ValidParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewValidInt256AbsParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + abs := x.Abs() + + // Verify abs is non-negative (as uint256) + if abs == nil { + panic("Abs returned nil") + } + }) +} + +// TestFuzzInt256Abs_RandomizedParams_Stateless tests Abs with randomized parameters +func TestFuzzInt256Abs_RandomizedParams_Stateless(t *testing.T) { + fuzzutils.RunFuzzTest(t, FUZZ_ITERATIONS, func(ft *fuzz.T, fuzzResult *fuzzutils.Result, index int) { + // given + params := NewRandomizedInt256AbsParams(ft) + + // when + fuzzResult.AddParams(index, params) + + // then + x := i256.MustFromDecimal(params.x) + + abs := x.Abs() + + // Verify abs is non-negative (as uint256) + if abs == nil { + panic("Abs returned nil") + } + }) +}