From 7967c1df75ff8584cd05738ff258c8ab5dee86f1 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Sun, 25 Mar 2018 19:35:45 +0200 Subject: [PATCH] coin math --- types/coin.go | 56 ++++++++++++++---- types/coin_test.go | 138 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 12 deletions(-) diff --git a/types/coin.go b/types/coin.go index 92871cd179..d19d4d8545 100644 --- a/types/coin.go +++ b/types/coin.go @@ -19,6 +19,11 @@ func (coin Coin) String() string { return fmt.Sprintf("%v%v", coin.Amount, coin.Denom) } +// SameDenomAs returns true if the two coins are the same denom +func (coin Coin) SameDenomAs(other Coin) bool { + return (coin.Denom == other.Denom) +} + // IsZero returns if this represents no money func (coin Coin) IsZero() bool { return coin.Amount == 0 @@ -27,8 +32,38 @@ func (coin Coin) IsZero() bool { // IsGTE returns true if they are the same type and the receiver is // an equal or greater value func (coin Coin) IsGTE(other Coin) bool { - return (coin.Denom == other.Denom) && - (coin.Amount >= other.Amount) + return coin.SameDenomAs(other) && (coin.Amount >= other.Amount) +} + +// IsEqual returns true if the two sets of Coins have the same value +func (coin Coin) IsEqual(other Coin) bool { + return coin.SameDenomAs(other) && (coin.Amount == other.Amount) +} + +// IsPositive returns true if coin amount is positive +func (coin Coin) IsPositive() bool { + return (coin.Amount > 0) +} + +// IsNotNegative returns true if coin amount is not negative +func (coin Coin) IsNotNegative() bool { + return (coin.Amount >= 0) +} + +// Adds amounts of two coins with same denom +func (coin Coin) Plus(coinB Coin) Coin { + if !coin.SameDenomAs(coinB) { + return coin + } + return Coin{coin.Denom, coin.Amount + coinB.Amount} +} + +// Subtracts amounts of two coins with same denom +func (coin Coin) Minus(coinB Coin) Coin { + if !coin.SameDenomAs(coinB) { + return coin + } + return Coin{coin.Denom, coin.Amount - coinB.Amount} } //---------------------------------------- @@ -55,14 +90,14 @@ func (coins Coins) IsValid() bool { case 0: return true case 1: - return coins[0].Amount != 0 + return !coins[0].IsZero() default: lowDenom := coins[0].Denom for _, coin := range coins[1:] { if coin.Denom <= lowDenom { return false } - if coin.Amount == 0 { + if coin.IsZero() { return false } // we compare each coin against the last denom @@ -96,10 +131,7 @@ func (coins Coins) Plus(coinsB Coins) Coins { if coinA.Amount+coinB.Amount == 0 { // ignore 0 sum coin type } else { - sum = append(sum, Coin{ - Denom: coinA.Denom, - Amount: coinA.Amount + coinB.Amount, - }) + sum = append(sum, coinA.Plus(coinB)) } indexA++ indexB++ @@ -168,8 +200,8 @@ func (coins Coins) IsPositive() bool { if len(coins) == 0 { return false } - for _, coinAmount := range coins { - if coinAmount.Amount <= 0 { + for _, coin := range coins { + if !coin.IsPositive() { return false } } @@ -182,8 +214,8 @@ func (coins Coins) IsNotNegative() bool { if len(coins) == 0 { return true } - for _, coinAmount := range coins { - if coinAmount.Amount < 0 { + for _, coin := range coins { + if !coin.IsNotNegative() { return false } } diff --git a/types/coin_test.go b/types/coin_test.go index b58578a25f..19929e8c79 100644 --- a/types/coin_test.go +++ b/types/coin_test.go @@ -6,6 +6,144 @@ import ( "github.com/stretchr/testify/assert" ) +func TestIsPositiveCoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + expected bool + }{ + {Coin{"A", 1}, true}, + {Coin{"A", 0}, false}, + {Coin{"a", -1}, false}, + } + + for _, tc := range cases { + res := tc.inputOne.IsPositive() + assert.Equal(tc.expected, res) + } +} + +func TestIsNotNegativeCoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + expected bool + }{ + {Coin{"A", 1}, true}, + {Coin{"A", 0}, true}, + {Coin{"a", -1}, false}, + } + + for _, tc := range cases { + res := tc.inputOne.IsNotNegative() + assert.Equal(tc.expected, res) + } +} + +func TestSameDenomAsCoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + inputTwo Coin + expected bool + }{ + {Coin{"A", 1}, Coin{"A", 1}, true}, + {Coin{"A", 1}, Coin{"a", 1}, false}, + {Coin{"a", 1}, Coin{"b", 1}, false}, + {Coin{"steak", 1}, Coin{"steak", 10}, true}, + {Coin{"steak", -11}, Coin{"steak", 10}, true}, + } + + for _, tc := range cases { + res := tc.inputOne.SameDenomAs(tc.inputTwo) + assert.Equal(tc.expected, res) + } +} + +func TestIsGTECoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + inputTwo Coin + expected bool + }{ + {Coin{"A", 1}, Coin{"A", 1}, true}, + {Coin{"A", 2}, Coin{"A", 1}, true}, + {Coin{"A", -1}, Coin{"A", 5}, false}, + {Coin{"a", 1}, Coin{"b", 1}, false}, + } + + for _, tc := range cases { + res := tc.inputOne.IsGTE(tc.inputTwo) + assert.Equal(tc.expected, res) + } +} + +func TestIsEqualCoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + inputTwo Coin + expected bool + }{ + {Coin{"A", 1}, Coin{"A", 1}, true}, + {Coin{"A", 1}, Coin{"a", 1}, false}, + {Coin{"a", 1}, Coin{"b", 1}, false}, + {Coin{"steak", 1}, Coin{"steak", 10}, false}, + {Coin{"steak", -11}, Coin{"steak", 10}, false}, + } + + for _, tc := range cases { + res := tc.inputOne.IsEqual(tc.inputTwo) + assert.Equal(tc.expected, res) + } +} + +func TestPlusCoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + inputTwo Coin + expected Coin + }{ + {Coin{"A", 1}, Coin{"A", 1}, Coin{"A", 2}}, + {Coin{"A", 1}, Coin{"B", 1}, Coin{"A", 1}}, + {Coin{"asdf", -4}, Coin{"asdf", 5}, Coin{"asdf", 1}}, + {Coin{"asdf", -1}, Coin{"asdf", 1}, Coin{"asdf", 0}}, + } + + for _, tc := range cases { + res := tc.inputOne.Plus(tc.inputTwo) + assert.Equal(tc.expected, res) + } +} + +func TestMinusCoin(t *testing.T) { + assert := assert.New(t) + + cases := []struct { + inputOne Coin + inputTwo Coin + expected Coin + }{ + {Coin{"A", 1}, Coin{"A", 1}, Coin{"A", 0}}, + {Coin{"A", 1}, Coin{"B", 1}, Coin{"A", 1}}, + {Coin{"asdf", -4}, Coin{"asdf", 5}, Coin{"asdf", -9}}, + {Coin{"asdf", 10}, Coin{"asdf", 1}, Coin{"asdf", 9}}, + } + + for _, tc := range cases { + res := tc.inputOne.Minus(tc.inputTwo) + assert.Equal(tc.expected, res) + } +} + func TestCoins(t *testing.T) { //Define the coins to be used in tests