From c1ea95d79332b436f2b89098fd64161f135aad1b Mon Sep 17 00:00:00 2001 From: leohhhn Date: Mon, 26 Feb 2024 20:54:53 +0100 Subject: [PATCH] add ctors, some helpers --- examples/gno.land/r/demo/tt/tt.gno | 3 + gnovm/stdlibs/std/coins.gno | 116 +++++++++++++++++++++++++---- gnovm/stdlibs/stdshim/coins.gno | 107 ++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 28 deletions(-) create mode 100644 examples/gno.land/r/demo/tt/tt.gno diff --git a/examples/gno.land/r/demo/tt/tt.gno b/examples/gno.land/r/demo/tt/tt.gno new file mode 100644 index 000000000000..23c6b306266c --- /dev/null +++ b/examples/gno.land/r/demo/tt/tt.gno @@ -0,0 +1,3 @@ +package tt + +func Render() diff --git a/gnovm/stdlibs/std/coins.gno b/gnovm/stdlibs/std/coins.gno index aaede81309da..2704ad36e7c6 100644 --- a/gnovm/stdlibs/std/coins.gno +++ b/gnovm/stdlibs/std/coins.gno @@ -1,6 +1,9 @@ package std -import "strconv" +import ( + "sort" + "strconv" +) // NOTE: this is selectly copied over from pkgs/std/coin.go // TODO: import all functionality(?). @@ -12,23 +15,62 @@ type Coin struct { Amount int64 `json:"amount"` } -func (c Coin) String() string { - return strconv.Itoa(int(c.Amount)) + c.Denom +// NewCoin returns a new coin with a denomination and amount. +func NewCoin(denom string, amount int64) Coin { + validate(amount) + 1 + return Coin{ + Denom: denom, + Amount: amount, + } +} + +func (coin Coin) String() string { + return strconv.Itoa(int(coin.Amount)) + coin.Denom +} + +func (coin Coin) IsGTE(other Coin) bool { + if coin.Denom != other.Denom { + panic("invalid coin denominations: " + coin.Denom) + } + return coin.Amount >= other.Amount +} + +// IsZero returns if this represents no money +func (coin Coin) IsZero() bool { + return coin.Amount == 0 } -func (c Coin) IsGTE(other Coin) bool { - if c.Denom != other.Denom { - panic("invalid coin denominations: " + c.Denom) +// validate returns an error if the Coin has a negative amount +func validate(amount int64) { + if amount < 0 { + panic("negative coin amount when constructing coin") } - return c.Amount >= other.Amount } // Coins is a set of Coin, one per currency type Coins []Coin -func (cz Coins) String() string { +// NewCoins constructs a new coin set. +func NewCoins(coins ...Coin) Coins { + // remove zeroes + newCoins := removeZeroCoins(Coins(coins)) + if len(newCoins) == 0 { + return Coins{} + } + + newCoins.Sort() + + // detect duplicate Denoms + if dupIndex := findDup(newCoins); dupIndex != -1 { + panic("found duplicate denom when constructing coins set") + } + return newCoins +} + +func (coins Coins) String() string { res := "" - for i, c := range cz { + for i, c := range coins { if i > 0 { res += "," } @@ -37,8 +79,8 @@ func (cz Coins) String() string { return res } -func (cz Coins) AmountOf(denom string) int64 { - for _, c := range cz { +func (coins Coins) AmountOf(denom string) int64 { + for _, c := range coins { if c.Denom == denom { return c.Amount } @@ -46,9 +88,9 @@ func (cz Coins) AmountOf(denom string) int64 { return 0 } -func (a Coins) Add(b Coins) Coins { +func (coins Coins) Add(b Coins) Coins { c := Coins{} - for _, ac := range a { + for _, ac := range coins { bc := b.AmountOf(ac.Denom) ac.Amount += bc c = append(c, ac) @@ -62,4 +104,50 @@ func (a Coins) Add(b Coins) Coins { return c } -// TODO implement Coin/Coins constructors. +// removeZeroCoins removes all zero coins from the given coin set in-place. +func removeZeroCoins(coins Coins) Coins { + i, l := 0, len(coins) + for i < l { + if coins[i].IsZero() { + // remove coin + coins = append(coins[:i], coins[i+1:]...) + l-- + } else { + i++ + } + } + + return coins[:i] +} + +// findDup works on the assumption that coins is sorted +func findDup(coins Coins) int { + if len(coins) <= 1 { + return -1 + } + + prevDenom := coins[0].Denom + for i := 1; i < len(coins); i++ { + if coins[i].Denom == prevDenom { + return i + } + prevDenom = coins[i].Denom + } + + return -1 +} + +// ----------------------------------------------------------------------------- +// Sort interface + +func (coins Coins) Len() int { return len(coins) } +func (coins Coins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom } +func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] } + +var _ sort.Interface = Coins{} + +// Sort is a helper function to sort the set of coins inplace +func (coins Coins) Sort() Coins { + sort.Sort(coins) + return coins +} diff --git a/gnovm/stdlibs/stdshim/coins.gno b/gnovm/stdlibs/stdshim/coins.gno index aaede81309da..3d166f57190a 100644 --- a/gnovm/stdlibs/stdshim/coins.gno +++ b/gnovm/stdlibs/stdshim/coins.gno @@ -1,6 +1,9 @@ package std -import "strconv" +import ( + "sort" + "strconv" +) // NOTE: this is selectly copied over from pkgs/std/coin.go // TODO: import all functionality(?). @@ -12,23 +15,53 @@ type Coin struct { Amount int64 `json:"amount"` } -func (c Coin) String() string { - return strconv.Itoa(int(c.Amount)) + c.Denom +// NewCoin returns a new coin with a denomination and amount. +func NewCoin(denom string, amount int64) Coin { + return Coin{ + Denom: denom, + Amount: amount, + } +} + +func (coin Coin) String() string { + return strconv.Itoa(int(coin.Amount)) + coin.Denom } -func (c Coin) IsGTE(other Coin) bool { - if c.Denom != other.Denom { - panic("invalid coin denominations: " + c.Denom) +func (coin Coin) IsGTE(other Coin) bool { + if coin.Denom != other.Denom { + panic("invalid coin denominations: " + coin.Denom) } - return c.Amount >= other.Amount + return coin.Amount >= other.Amount +} + +// IsZero returns if this represents no money +func (coin Coin) IsZero() bool { + return coin.Amount == 0 } // Coins is a set of Coin, one per currency type Coins []Coin -func (cz Coins) String() string { +// NewCoins constructs a new coin set. +func NewCoins(coins ...Coin) Coins { + // remove zeroes + newCoins := removeZeroCoins(Coins(coins)) + if len(newCoins) == 0 { + return Coins{} + } + + newCoins.Sort() + + // detect duplicate Denoms + if dupIndex := findDup(newCoins); dupIndex != -1 { + panic("found duplicate denom when constructing coins set") + } + return newCoins +} + +func (coins Coins) String() string { res := "" - for i, c := range cz { + for i, c := range coins { if i > 0 { res += "," } @@ -37,8 +70,8 @@ func (cz Coins) String() string { return res } -func (cz Coins) AmountOf(denom string) int64 { - for _, c := range cz { +func (coins Coins) AmountOf(denom string) int64 { + for _, c := range coins { if c.Denom == denom { return c.Amount } @@ -46,9 +79,9 @@ func (cz Coins) AmountOf(denom string) int64 { return 0 } -func (a Coins) Add(b Coins) Coins { +func (coins Coins) Add(b Coins) Coins { c := Coins{} - for _, ac := range a { + for _, ac := range coins { bc := b.AmountOf(ac.Denom) ac.Amount += bc c = append(c, ac) @@ -62,4 +95,50 @@ func (a Coins) Add(b Coins) Coins { return c } -// TODO implement Coin/Coins constructors. +// removeZeroCoins removes all zero coins from the given coin set in-place. +func removeZeroCoins(coins Coins) Coins { + i, l := 0, len(coins) + for i < l { + if coins[i].IsZero() { + // remove coin + coins = append(coins[:i], coins[i+1:]...) + l-- + } else { + i++ + } + } + + return coins[:i] +} + +// findDup works on the assumption that coins is sorted +func findDup(coins Coins) int { + if len(coins) <= 1 { + return -1 + } + + prevDenom := coins[0].Denom + for i := 1; i < len(coins); i++ { + if coins[i].Denom == prevDenom { + return i + } + prevDenom = coins[i].Denom + } + + return -1 +} + +// ----------------------------------------------------------------------------- +// Sort interface + +func (coins Coins) Len() int { return len(coins) } +func (coins Coins) Less(i, j int) bool { return coins[i].Denom < coins[j].Denom } +func (coins Coins) Swap(i, j int) { coins[i], coins[j] = coins[j], coins[i] } + +var _ sort.Interface = Coins{} + +// Sort is a helper function to sort the set of coins inplace +func (coins Coins) Sort() Coins { + sort.Sort(coins) + return coins +}