func (coins DecCoins) safeAdd(coinsB DecCoins) DecCoins {
	sum := ([]DecCoin)(nil)
	indexA, indexB := 0, 0
	lenA, lenB := len(coins), len(coinsB)
	for {
		if indexA == lenA {
			if indexB == lenB {
				return sum
			}
			return append(sum, removeZeroDecCoins(coinsB[indexB:])...)
		} else if indexB == lenB {
			return append(sum, removeZeroDecCoins(coins[indexA:])...)
		}
		coinA, coinB := coins[indexA], coinsB[indexB]
		switch strings.Compare(coinA.Denom, coinB.Denom) {
		case -1:  
			if !coinA.IsZero() {
				sum = append(sum, coinA)
			}
			indexA++
		case 0:  
			res := coinA.Add(coinB)
			if !res.IsZero() {
				sum = append(sum, res)
			}
			indexA++
			indexB++
		case 1:  
			if !coinB.IsZero() {
				sum = append(sum, coinB)
			}
			indexB++
		}
	}
}
safeAdd will perform addition of two DecCoins sets. If both coin sets are
empty, then an empty set is returned. If only a single set is empty, the
other set is returned. Otherwise, the coins are compared in order of their
denomination and addition only occurs when the denominations match, otherwise
the coin is simply added to the sum assuming it's not zero.
package coin
import (
	"fmt"
	"sort"
	"strings"
	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/pkg/errors"
)
type DecCoin struct {
	Denom  string `json:"denom"`
	Amount Dec    `json:"amount"`
}
func (d DecCoin) ToSDK() sdk.DecCoin {
	return sdk.DecCoin{
		Denom: d.Denom,
		Amount: sdk.Dec{
			d.Amount.Int,
		},
	}
}
func NewDecCoin(denom string, amount Int) DecCoin {
	if err := validate(denom, amount); err != nil {
		panic(err)
	}
	return DecCoin{
		Denom:  denom,
		Amount: amount.ToDec(),
	}
}
func NewDecCoinFromDec(denom string, amount Dec) DecCoin {
	mustValidateDenom(denom)
	if amount.IsNegative() {
		panic(fmt.Sprintf("negative decimal coin amount: %v\n", amount))
	}
	return DecCoin{
		Denom:  denom,
		Amount: amount,
	}
}
func NewDecCoinFromCoin(coin Coin) DecCoin {
	if err := validate(coin.Denom, coin.Amount); err != nil {
		panic(err)
	}
	return DecCoin{
		Denom:  coin.Denom,
		Amount: coin.Amount.ToDec(),
	}
}
func NewInt64DecCoin(denom string, amount int64) DecCoin {
	return NewDecCoin(denom, NewInt(amount))
}
func (coin DecCoin) IsZero() bool {
	return coin.Amount.IsZero()
}
func (coin DecCoin) IsGTE(other DecCoin) bool {
	if coin.Denom != other.Denom {
		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
	}
	return !coin.Amount.LT(other.Amount)
}
func (coin DecCoin) IsLT(other DecCoin) bool {
	if coin.Denom != other.Denom {
		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
	}
	return coin.Amount.LT(other.Amount)
}
func (coin DecCoin) IsEqual(other DecCoin) bool {
	if coin.Denom != other.Denom {
		panic(fmt.Sprintf("invalid coin denominations; %s, %s", coin.Denom, other.Denom))
	}
	return coin.Amount.Equal(other.Amount)
}
func (coin DecCoin) Add(coinB DecCoin) DecCoin {
	if coin.Denom != coinB.Denom {
		panic(fmt.Sprintf("coin denom different: %v %v\n", coin.Denom, coinB.Denom))
	}
	return DecCoin{coin.Denom, coin.Amount.Add(coinB.Amount)}
}
func (coin DecCoin) Sub(coinB DecCoin) DecCoin {
	if coin.Denom != coinB.Denom {
		panic(fmt.Sprintf("coin denom different: %v %v\n", coin.Denom, coinB.Denom))
	}
	res := DecCoin{coin.Denom, coin.Amount.Sub(coinB.Amount)}
	if res.IsNegative() {
		panic("negative decimal coin amount")
	}
	return res
}
func (coin DecCoin) TruncateDecimal() (Coin, DecCoin) {
	truncated := coin.Amount.TruncateInt()
	change := coin.Amount.Sub(truncated.ToDec())
	return NewCoin(coin.Denom, truncated), NewDecCoinFromDec(coin.Denom, change)
}
func (coin DecCoin) IsPositive() bool {
	return coin.Amount.IsPositive()
}
func (coin DecCoin) IsNegative() bool {
	return coin.Amount.Sign() == -1
}
func (coin DecCoin) String() string {
	return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
}
func (coin DecCoin) IsValid() bool {
	if err := ValidateDenom(coin.Denom); err != nil {
		return false
	}
	return !coin.IsNegative()
}
type DecCoins []DecCoin
func (ds DecCoins) ToSDK() sdk.DecCoins {
	res := make([]sdk.DecCoin, 0, len(ds))
	for _, d := range ds {
		res = append(res, d.ToSDK())
	}
	return res
}
func NewDecCoins(decCoins ...DecCoin) DecCoins {
	newDecCoins := removeZeroDecCoins(DecCoins(decCoins))
	if len(newDecCoins) == 0 {
		return DecCoins{}
	}
	newDecCoins.Sort()
	if dupIndex := findDup(newDecCoins); dupIndex != -1 {
		panic(fmt.Errorf("find duplicate denom: %s", newDecCoins[dupIndex]))
	}
	if !newDecCoins.IsValid() {
		panic(fmt.Errorf("invalid coin set: %s", newDecCoins))
	}
	return newDecCoins
}
func NewDecCoinsFromCoins(coins ...Coin) DecCoins {
	decCoins := make(DecCoins, len(coins))
	newCoins := NewCoins(coins...)
	for i, coin := range newCoins {
		decCoins[i] = NewDecCoinFromCoin(coin)
	}
	return decCoins
}
func (coins DecCoins) String() string {
	if len(coins) == 0 {
		return ""
	}
	out := ""
	for _, coin := range coins {
		out += fmt.Sprintf("%v,", coin.String())
	}
	return out[:len(out)-1]
}
func (coins DecCoins) TruncateDecimal() (truncatedCoins Coins, changeCoins DecCoins) {
	for _, coin := range coins {
		truncated, change := coin.TruncateDecimal()
		if !truncated.IsZero() {
			truncatedCoins = truncatedCoins.Add(truncated)
		}
		if !change.IsZero() {
			changeCoins = changeCoins.Add(change)
		}
	}
	return truncatedCoins, changeCoins
}
func (coins DecCoins) Add(coinsB ...DecCoin) DecCoins {
	return coins.safeAdd(coinsB)
}