Skip to content

Commit

Permalink
refactor: faster Max, tests for Intersect.
Browse files Browse the repository at this point in the history
  • Loading branch information
JimLarson committed Feb 22, 2022
1 parent 56d3a13 commit 2059cd8
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 10 deletions.
66 changes: 56 additions & 10 deletions types/coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,19 +390,65 @@ func (coins Coins) SafeSub(coinsB Coins) (Coins, bool) {
return diff, diff.IsAnyNegative()
}

// Max returns the maximum of each denom of its inputs.
// The inputs should be sorted. Note that the max might
// not be equal to either of its inputs. Uses multiset
// semantics, so unmentioned denoms are implicitly zero.
// Max takes two valid Coins inputs and returns a valid Coins result
// where for every denom D, AmountOf(D) of the result is the maximum
// of AmountOf(D) of the inputs. Note that the result might be not
// be equal to either input. For any valid Coins a, b, and c, the
// following are always true:
// a.IsAllLTE(a.Max(b))
// b.IsAllLTE(a.Max(b))
// a.IsAllLTE(c) && b.IsAllLTE(c) == a.Max(b).IsAllLTE(c)
// a.Add(b...).IsEqual(a.Min(b).Add(a.Max(b)...))
//
// E.g.
// {1A, 3B, 2C}.Max({4A, 2B, 2C} == {4A, 3B, 2C})
// {2A, 3B}.Max({1B, 4C}) == {2A, 3B, 4C}
// {1A, 2B}.Max({}) == {1A, 2B}
func (coins Coins) Max(coinsB Coins) Coins {
// coins + coinsB == min(coins, coinsB) + max(coins, coinsB)
return coins.Add(coinsB...).Sub(coins.Min(coinsB))
max := make([]Coin, 0)
indexA, indexB := 0, 0
for indexA < len(coins) && indexB < len(coinsB) {
coinA, coinB := coins[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1: // denom missing from coinsB
max = append(max, coinA)
indexA++
case 0: // same denom in both
maxCoin := coinA
if coinB.Amount.GT(maxCoin.Amount) {
maxCoin = coinB
}
max = append(max, maxCoin)
indexA++
indexB++
case 1: // denom missing from coinsA
max = append(max, coinB)
indexB++
}
}
for ; indexA < len(coins); indexA++ {
max = append(max, coins[indexA])
}
for ; indexB < len(coinsB); indexB++ {
max = append(max, coinsB[indexB])
}
return NewCoins(max...)
}

// Min returns the minimum of each denom of its inputs.
// The inputs should be sorted. Note that the min might
// not be equal to either of its inputs. Uses multiset
// semantics, so unmentioned denoms are implicitly zero.
// Min takes two valid Coins inputs and returns a valid Coins result
// where for every denom D, AmountOf(D) of the result is the minimum
// of AmountOf(D) of the inputs. Note that the result might be not
// be equal to either input. For any valid Coins a, b, and c, the
// following are always true:
// a.Min(b).IsAllLTE(a)
// a.Min(b).IsAllLTE(b)
// c.IsAllLTE(a) && c.IsAllLTE(b) == c.IsAllLTE(a.Min(b))
// a.Add(b...).IsEqual(a.Min(b).Add(a.Max(b)...))
//
// E.g.
// {1A, 3B, 2C}.Min({4A, 2B, 2C} == {1A, 2B, 2C})
// {2A, 3B}.Min({1B, 4C}) == {1B}
// {1A, 2B}.Min({3C}) == empty
func (coins Coins) Min(coinsB Coins) Coins {
min := make([]Coin, 0)
for indexA, indexB := 0, 0; indexA < len(coins) && indexB < len(coinsB); {
Expand Down
2 changes: 2 additions & 0 deletions types/coin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -679,8 +679,10 @@ func (s *coinTestSuite) TestMinMax() {
for _, tc := range cases {
min := tc.input1.Min(tc.input2)
max := tc.input1.Max(tc.input2)
intersect := tc.input1.Intersect(tc.input2)
s.Require().True(min.IsEqual(tc.min), tc.name)
s.Require().True(max.IsEqual(tc.max), tc.name)
s.Require().True(intersect.IsEqual(tc.min), tc.name)
}
}

Expand Down

0 comments on commit 2059cd8

Please sign in to comment.