Skip to content

Commit

Permalink
refactor: gnsmath
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed Jan 31, 2025
1 parent 8f0660f commit 00011ea
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 292 deletions.
39 changes: 0 additions & 39 deletions contract/p/gnoswap/gnsmath/_helper_test.gno

This file was deleted.

58 changes: 32 additions & 26 deletions contract/p/gnoswap/gnsmath/bit_math.gno
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,44 @@ import (
u256 "gno.land/p/gnoswap/uint256"
)

var (
msbShifts = []bitShift{
{u256.MustFromDecimal(Q128), 128}, // 2^128
{u256.MustFromDecimal(Q64), 64}, // 2^64
{u256.NewUint(0x100000000), 32}, // 2^32
{u256.NewUint(0x10000), 16}, // 2^16
{u256.NewUint(0x100), 8}, // 2^8
{u256.NewUint(0x10), 4}, // 2^4
{u256.NewUint(0x4), 2}, // 2^2
{u256.NewUint(0x2), 1}, // 2^1
}

lsbShifts = []bitShift{
{u256.MustFromDecimal(MAX_UINT128), 128},
{u256.MustFromDecimal(MAX_UINT64), 64},
{u256.MustFromDecimal(MAX_UINT32), 32},
{u256.MustFromDecimal(MAX_UINT16), 16},
{u256.MustFromDecimal(MAX_UINT8), 8},
{u256.NewUint(0xf), 4},
{u256.NewUint(0x3), 2},
{u256.NewUint(0x1), 1},
}
)

type bitShift struct {
bitPattern *u256.Uint
shift uint
}

// BitMathMostSignificantBit finds the highest set bit (0-based) in x.
// If x == 0, it panics.
func BitMathMostSignificantBit(x *u256.Uint) uint8 {
if x.IsZero() {
panic("BitMathMostSignificantBit: x should not be zero")
}

shifts := []bitShift{
{u256.MustFromDecimal(Q128), 128}, // 2^128
{u256.MustFromDecimal(Q64), 64}, // 2^64
{u256.NewUint(0x100000000), 32},
{u256.NewUint(0x10000), 16},
{u256.NewUint(0x100), 8},
{u256.NewUint(0x10), 4},
{u256.NewUint(0x4), 2},
{u256.NewUint(0x2), 1},
panic(errMSBZeroInput)
}

r := uint8(0)
for _, s := range shifts {
for _, s := range msbShifts {
if x.Gte(s.bitPattern) {
x = new(u256.Uint).Rsh(x, s.shift)
r += uint8(s.shift)
Expand All @@ -36,24 +51,15 @@ func BitMathMostSignificantBit(x *u256.Uint) uint8 {
return r
}

// BitMathLeastSignificantBit finds the lowest set bit (0-based) in x.
// If x == 0, it panics.
func BitMathLeastSignificantBit(x *u256.Uint) uint8 {
if x.IsZero() {
panic("BitMathLeastSignificantBit: x should not be zero")
}

shifts := []bitShift{
{u256.MustFromDecimal(MAX_UINT128), 128},
{u256.MustFromDecimal(MAX_UINT64), 64},
{u256.MustFromDecimal(MAX_UINT32), 32},
{u256.MustFromDecimal(MAX_UINT16), 16},
{u256.MustFromDecimal(MAX_UINT8), 8},
{u256.NewUint(0xf), 4},
{u256.NewUint(0x3), 2},
{u256.NewUint(0x1), 1},
panic(errLSBZeroInput)
}

r := uint8(255)
for _, s := range shifts {
for _, s := range lsbShifts {
if new(u256.Uint).And(x, s.bitPattern).Gt(u256.Zero()) {
r -= uint8(s.shift)
} else {
Expand Down
36 changes: 16 additions & 20 deletions contract/p/gnoswap/gnsmath/bit_math_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,65 @@ package gnsmath
import (
"testing"

"gno.land/p/demo/uassert"

u256 "gno.land/p/gnoswap/uint256"
)

func TestBitMathMostSignificantBit(t *testing.T) {
t.Run("0", func(t *testing.T) {
shouldPanic(
t,
func() {
BitMathMostSignificantBit(u256.Zero())
},
)
uassert.PanicsWithMessage(t, errMSBZeroInput.Error(), func() {
BitMathMostSignificantBit(u256.Zero())
})
})

t.Run("1", func(t *testing.T) {
shouldEQ(t, BitMathMostSignificantBit(u256.One()), uint8(0))
uassert.Equal(t, BitMathMostSignificantBit(u256.One()), uint8(0))
})

t.Run("2", func(t *testing.T) {
shouldEQ(t, BitMathMostSignificantBit(u256.NewUint(2)), uint8(1))
uassert.Equal(t, BitMathMostSignificantBit(u256.NewUint(2)), uint8(1))
})

t.Run("all powers of 2", func(t *testing.T) {
for i := 0; i < 256; i++ {
num := u256.Zero()
num.Lsh(u256.One(), uint(i))
shouldEQ(t, BitMathMostSignificantBit(num), uint8(i))
uassert.Equal(t, BitMathMostSignificantBit(num), uint8(i))
}
})

t.Run("uint256(-1)", func(t *testing.T) {
// BigNumber.from(2).pow(256).sub(1))
shouldEQ(t, BitMathMostSignificantBit(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")), uint8(255))
uassert.Equal(t, BitMathMostSignificantBit(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")), uint8(255))
})
}

func TestBitMathLeastSignificantBit(t *testing.T) {
t.Run("0", func(t *testing.T) {
shouldPanic(
t,
func() {
BitMathLeastSignificantBit(u256.Zero())
},
)
uassert.PanicsWithMessage(t, errLSBZeroInput.Error(), func() {
BitMathLeastSignificantBit(u256.Zero())
})
})

t.Run("1", func(t *testing.T) {
shouldEQ(t, BitMathLeastSignificantBit(u256.One()), uint8(0))
uassert.Equal(t, BitMathLeastSignificantBit(u256.One()), uint8(0))
})

t.Run("2", func(t *testing.T) {
shouldEQ(t, BitMathLeastSignificantBit(u256.NewUint(2)), uint8(1))
uassert.Equal(t, BitMathLeastSignificantBit(u256.NewUint(2)), uint8(1))
})

t.Run("all powers of 2", func(t *testing.T) {
for i := 0; i < 256; i++ {
num := u256.Zero()
num.Lsh(u256.One(), uint(i))
shouldEQ(t, BitMathLeastSignificantBit(num), uint8(i))
uassert.Equal(t, BitMathLeastSignificantBit(num), uint8(i))
}
})

t.Run("uint256(-1)", func(t *testing.T) {
// BigNumber.from(2).pow(256).sub(1))
shouldEQ(t, BitMathLeastSignificantBit(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")), uint8(0))
uassert.Equal(t, BitMathLeastSignificantBit(u256.MustFromDecimal("115792089237316195423570985008687907853269984665640564039457584007913129639935")), uint8(0))
})
}
19 changes: 19 additions & 0 deletions contract/p/gnoswap/gnsmath/erros.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package gnsmath

import (
"errors"
)

var (
errInvalidPoolSqrtPrice = errors.New("invalid pool sqrt price calculation: product/amount != sqrtPX96 or numerator1 <= product")
errNextSqrtPriceOverflow = errors.New("nextSqrtPrice overflows uint160")
errSqrtPriceQuotientOverflow = errors.New("GetNextSqrtPriceFromAmount1RoundingDown sqrtPx96 + quotient overflow uint160")
errSqrtPriceExceedsQuotient = errors.New("sqrt price exceeds calculated quotient")
errSqrtPriceZero = errors.New("sqrtPX96 should not be zero")
errLiquidityZero = errors.New("liquidity should not be zero")
errSqrtRatioAX96NotPositive = errors.New("sqrtRatioAX96 must be greater than zero")
errAmount0DeltaOverflow = errors.New("SqrtPriceMathGetAmount0DeltaStr: overflow")
errAmount1DeltaOverflow = errors.New("SqrtPriceMathGetAmount1DeltaStr: overflow")
errMSBZeroInput = errors.New("input for MSB calculation should not be zero")
errLSBZeroInput = errors.New("input for LSB calculation should not be zero")
)
Loading

0 comments on commit 00011ea

Please sign in to comment.