commit
21c6099775
@ -0,0 +1,21 @@ |
|||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
) |
||||||
|
|
||||||
|
type ( |
||||||
|
// Commission defines a commission parameters for a given validator.
|
||||||
|
Commission struct { |
||||||
|
CommissionRates `json:"commission_rates" yaml:"commission_rates"` |
||||||
|
UpdateHeight big.Int `json:"update_time" yaml:"update_time"` // the block height the commission rate was last changed
|
||||||
|
} |
||||||
|
|
||||||
|
// CommissionRates defines the initial commission rates to be used for creating a
|
||||||
|
// validator.
|
||||||
|
CommissionRates struct { |
||||||
|
Rate Dec `json:"rate" yaml:"rate"` // the commission rate charged to delegators, as a fraction
|
||||||
|
MaxRate Dec `json:"max_rate" yaml:"max_rate"` // maximum commission rate which validator can ever charge, as a fraction
|
||||||
|
MaxChangeRate Dec `json:"max_change_rate" yaml:"max_change_rate"` // maximum increase of the validator commission every epoch, as a fraction
|
||||||
|
} |
||||||
|
) |
@ -0,0 +1,651 @@ |
|||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
// Dec represent a decimal. NOTE: never use new(Dec) or else we will panic unmarshalling into the
|
||||||
|
// nil embedded big.Int
|
||||||
|
type Dec struct { |
||||||
|
*big.Int `json:"int"` |
||||||
|
} |
||||||
|
|
||||||
|
// number of decimal places
|
||||||
|
const ( |
||||||
|
Precision = 18 |
||||||
|
|
||||||
|
// bytes required to represent the above precision
|
||||||
|
// Ceiling[Log2[999 999 999 999 999 999]]
|
||||||
|
DecimalPrecisionBits = 60 |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
precisionReuse = new(big.Int).Exp(big.NewInt(10), big.NewInt(Precision), nil) |
||||||
|
fivePrecision = new(big.Int).Quo(precisionReuse, big.NewInt(2)) |
||||||
|
precisionMultipliers []*big.Int |
||||||
|
zeroInt = big.NewInt(0) |
||||||
|
oneInt = big.NewInt(1) |
||||||
|
tenInt = big.NewInt(10) |
||||||
|
) |
||||||
|
|
||||||
|
// Set precision multipliers
|
||||||
|
func init() { |
||||||
|
precisionMultipliers = make([]*big.Int, Precision+1) |
||||||
|
for i := 0; i <= Precision; i++ { |
||||||
|
precisionMultipliers[i] = calcPrecisionMultiplier(int64(i)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func precisionInt() *big.Int { |
||||||
|
return new(big.Int).Set(precisionReuse) |
||||||
|
} |
||||||
|
|
||||||
|
// ZeroDec ...
|
||||||
|
func ZeroDec() Dec { return Dec{new(big.Int).Set(zeroInt)} } |
||||||
|
|
||||||
|
// OneDec ...
|
||||||
|
func OneDec() Dec { return Dec{precisionInt()} } |
||||||
|
|
||||||
|
// SmallestDec ...
|
||||||
|
func SmallestDec() Dec { return Dec{new(big.Int).Set(oneInt)} } |
||||||
|
|
||||||
|
// calculate the precision multiplier
|
||||||
|
func calcPrecisionMultiplier(prec int64) *big.Int { |
||||||
|
if prec > Precision { |
||||||
|
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) |
||||||
|
} |
||||||
|
zerosToAdd := Precision - prec |
||||||
|
multiplier := new(big.Int).Exp(tenInt, big.NewInt(zerosToAdd), nil) |
||||||
|
return multiplier |
||||||
|
} |
||||||
|
|
||||||
|
// get the precision multiplier, do not mutate result
|
||||||
|
func precisionMultiplier(prec int64) *big.Int { |
||||||
|
if prec > Precision { |
||||||
|
panic(fmt.Sprintf("too much precision, maximum %v, provided %v", Precision, prec)) |
||||||
|
} |
||||||
|
return precisionMultipliers[prec] |
||||||
|
} |
||||||
|
|
||||||
|
//______________________________________________________________________________________________
|
||||||
|
|
||||||
|
// NewDec creates a new Dec from integer assuming whole number
|
||||||
|
func NewDec(i int64) Dec { |
||||||
|
return NewDecWithPrec(i, 0) |
||||||
|
} |
||||||
|
|
||||||
|
// NewDecWithPrec creates a new Dec from integer with decimal place at prec
|
||||||
|
// CONTRACT: prec <= Precision
|
||||||
|
func NewDecWithPrec(i, prec int64) Dec { |
||||||
|
return Dec{ |
||||||
|
new(big.Int).Mul(big.NewInt(i), precisionMultiplier(prec)), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// NewDecFromBigInt creates a new Dec from big integer assuming whole numbers
|
||||||
|
// CONTRACT: prec <= Precision
|
||||||
|
func NewDecFromBigInt(i *big.Int) Dec { |
||||||
|
return NewDecFromBigIntWithPrec(i, 0) |
||||||
|
} |
||||||
|
|
||||||
|
// NewDecFromBigIntWithPrec creates a new Dec from big integer assuming whole numbers
|
||||||
|
// CONTRACT: prec <= Precision
|
||||||
|
func NewDecFromBigIntWithPrec(i *big.Int, prec int64) Dec { |
||||||
|
return Dec{ |
||||||
|
new(big.Int).Mul(i, precisionMultiplier(prec)), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// NewDecFromInt creates a new Dec from big integer assuming whole numbers
|
||||||
|
// CONTRACT: prec <= Precision
|
||||||
|
func NewDecFromInt(i *big.Int) Dec { |
||||||
|
return NewDecFromIntWithPrec(i, 0) |
||||||
|
} |
||||||
|
|
||||||
|
// NewDecFromIntWithPrec creates a new Dec from big integer with decimal place at prec
|
||||||
|
// CONTRACT: prec <= Precision
|
||||||
|
func NewDecFromIntWithPrec(i *big.Int, prec int64) Dec { |
||||||
|
return Dec{ |
||||||
|
new(big.Int).Mul(i, precisionMultiplier(prec)), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// NewDecFromStr creates a decimal from an input decimal string.
|
||||||
|
// valid must come in the form:
|
||||||
|
// (-) whole integers (.) decimal integers
|
||||||
|
// examples of acceptable input include:
|
||||||
|
// -123.456
|
||||||
|
// 456.7890
|
||||||
|
// 345
|
||||||
|
// -456789
|
||||||
|
//
|
||||||
|
// NOTE - An error will return if more decimal places
|
||||||
|
// are provided in the string than the constant Precision.
|
||||||
|
//
|
||||||
|
// CONTRACT - This function does not mutate the input str.
|
||||||
|
func NewDecFromStr(str string) (d Dec, err error) { |
||||||
|
if len(str) == 0 { |
||||||
|
return d, errors.New("decimal string is empty") |
||||||
|
} |
||||||
|
|
||||||
|
// first extract any negative symbol
|
||||||
|
neg := false |
||||||
|
if str[0] == '-' { |
||||||
|
neg = true |
||||||
|
str = str[1:] |
||||||
|
} |
||||||
|
|
||||||
|
if len(str) == 0 { |
||||||
|
return d, errors.New("decimal string is empty") |
||||||
|
} |
||||||
|
|
||||||
|
strs := strings.Split(str, ".") |
||||||
|
lenDecs := 0 |
||||||
|
combinedStr := strs[0] |
||||||
|
|
||||||
|
if len(strs) == 2 { // has a decimal place
|
||||||
|
lenDecs = len(strs[1]) |
||||||
|
if lenDecs == 0 || len(combinedStr) == 0 { |
||||||
|
return d, errors.New("bad decimal length") |
||||||
|
} |
||||||
|
combinedStr += strs[1] |
||||||
|
|
||||||
|
} else if len(strs) > 2 { |
||||||
|
return d, errors.New("too many periods to be a decimal string") |
||||||
|
} |
||||||
|
|
||||||
|
if lenDecs > Precision { |
||||||
|
return d, fmt.Errorf("too much precision, maximum %v, len decimal %v", Precision, lenDecs) |
||||||
|
} |
||||||
|
|
||||||
|
// add some extra zero's to correct to the Precision factor
|
||||||
|
zerosToAdd := Precision - lenDecs |
||||||
|
zeros := fmt.Sprintf(`%0`+strconv.Itoa(zerosToAdd)+`s`, "") |
||||||
|
combinedStr += zeros |
||||||
|
|
||||||
|
combined, ok := new(big.Int).SetString(combinedStr, 10) // base 10
|
||||||
|
if !ok { |
||||||
|
return d, fmt.Errorf("bad string to integer conversion, combinedStr: %v", combinedStr) |
||||||
|
} |
||||||
|
if neg { |
||||||
|
combined = new(big.Int).Neg(combined) |
||||||
|
} |
||||||
|
return Dec{combined}, nil |
||||||
|
} |
||||||
|
|
||||||
|
// MustNewDecFromStr Decimal from string, panic on error
|
||||||
|
func MustNewDecFromStr(s string) Dec { |
||||||
|
dec, err := NewDecFromStr(s) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
return dec |
||||||
|
} |
||||||
|
|
||||||
|
// IsNil ...
|
||||||
|
func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil
|
||||||
|
// IsZero ...
|
||||||
|
func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // is equal to zero
|
||||||
|
// IsNegative ...
|
||||||
|
func (d Dec) IsNegative() bool { return (d.Int).Sign() == -1 } // is negative
|
||||||
|
// IsPositive ...
|
||||||
|
func (d Dec) IsPositive() bool { return (d.Int).Sign() == 1 } // is positive
|
||||||
|
// Equal ...
|
||||||
|
func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } // equal decimals
|
||||||
|
// GT ...
|
||||||
|
func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than
|
||||||
|
// GTE ...
|
||||||
|
func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal
|
||||||
|
// LT ...
|
||||||
|
func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than
|
||||||
|
// LTE ...
|
||||||
|
func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal
|
||||||
|
// Neg ...
|
||||||
|
func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign
|
||||||
|
// Abs ...
|
||||||
|
func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.Int)} } // absolute value
|
||||||
|
|
||||||
|
// Add addition
|
||||||
|
func (d Dec) Add(d2 Dec) Dec { |
||||||
|
res := new(big.Int).Add(d.Int, d2.Int) |
||||||
|
|
||||||
|
if res.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{res} |
||||||
|
} |
||||||
|
|
||||||
|
// Sub subtraction
|
||||||
|
func (d Dec) Sub(d2 Dec) Dec { |
||||||
|
res := new(big.Int).Sub(d.Int, d2.Int) |
||||||
|
|
||||||
|
if res.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{res} |
||||||
|
} |
||||||
|
|
||||||
|
// Mul multiplication
|
||||||
|
func (d Dec) Mul(d2 Dec) Dec { |
||||||
|
mul := new(big.Int).Mul(d.Int, d2.Int) |
||||||
|
chopped := chopPrecisionAndRound(mul) |
||||||
|
|
||||||
|
if chopped.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{chopped} |
||||||
|
} |
||||||
|
|
||||||
|
// MulTruncate multiplication truncate
|
||||||
|
func (d Dec) MulTruncate(d2 Dec) Dec { |
||||||
|
mul := new(big.Int).Mul(d.Int, d2.Int) |
||||||
|
chopped := chopPrecisionAndTruncate(mul) |
||||||
|
|
||||||
|
if chopped.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{chopped} |
||||||
|
} |
||||||
|
|
||||||
|
// MulInt multiplication
|
||||||
|
func (d Dec) MulInt(i *big.Int) Dec { |
||||||
|
mul := new(big.Int).Mul(d.Int, i) |
||||||
|
|
||||||
|
if mul.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{mul} |
||||||
|
} |
||||||
|
|
||||||
|
// MulInt64 - multiplication with int64
|
||||||
|
func (d Dec) MulInt64(i int64) Dec { |
||||||
|
mul := new(big.Int).Mul(d.Int, big.NewInt(i)) |
||||||
|
|
||||||
|
if mul.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{mul} |
||||||
|
} |
||||||
|
|
||||||
|
// Quo quotient
|
||||||
|
func (d Dec) Quo(d2 Dec) Dec { |
||||||
|
|
||||||
|
// multiply precision twice
|
||||||
|
mul := new(big.Int).Mul(d.Int, precisionReuse) |
||||||
|
mul.Mul(mul, precisionReuse) |
||||||
|
|
||||||
|
quo := new(big.Int).Quo(mul, d2.Int) |
||||||
|
chopped := chopPrecisionAndRound(quo) |
||||||
|
|
||||||
|
if chopped.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{chopped} |
||||||
|
} |
||||||
|
|
||||||
|
// QuoTruncate quotient truncate
|
||||||
|
func (d Dec) QuoTruncate(d2 Dec) Dec { |
||||||
|
|
||||||
|
// multiply precision twice
|
||||||
|
mul := new(big.Int).Mul(d.Int, precisionReuse) |
||||||
|
mul.Mul(mul, precisionReuse) |
||||||
|
|
||||||
|
quo := new(big.Int).Quo(mul, d2.Int) |
||||||
|
chopped := chopPrecisionAndTruncate(quo) |
||||||
|
|
||||||
|
if chopped.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{chopped} |
||||||
|
} |
||||||
|
|
||||||
|
// QuoRoundUp quotient, round up
|
||||||
|
func (d Dec) QuoRoundUp(d2 Dec) Dec { |
||||||
|
// multiply precision twice
|
||||||
|
mul := new(big.Int).Mul(d.Int, precisionReuse) |
||||||
|
mul.Mul(mul, precisionReuse) |
||||||
|
|
||||||
|
quo := new(big.Int).Quo(mul, d2.Int) |
||||||
|
chopped := chopPrecisionAndRoundUp(quo) |
||||||
|
|
||||||
|
if chopped.BitLen() > 255+DecimalPrecisionBits { |
||||||
|
panic("Int overflow") |
||||||
|
} |
||||||
|
return Dec{chopped} |
||||||
|
} |
||||||
|
|
||||||
|
// QuoInt quotient
|
||||||
|
func (d Dec) QuoInt(i *big.Int) Dec { |
||||||
|
mul := new(big.Int).Quo(d.Int, i) |
||||||
|
return Dec{mul} |
||||||
|
} |
||||||
|
|
||||||
|
// QuoInt64 - quotient with int64
|
||||||
|
func (d Dec) QuoInt64(i int64) Dec { |
||||||
|
mul := new(big.Int).Quo(d.Int, big.NewInt(i)) |
||||||
|
return Dec{mul} |
||||||
|
} |
||||||
|
|
||||||
|
// IsInteger is integer, e.g. decimals are zero
|
||||||
|
func (d Dec) IsInteger() bool { |
||||||
|
return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 |
||||||
|
} |
||||||
|
|
||||||
|
// Format decimal state
|
||||||
|
func (d Dec) Format(s fmt.State, verb rune) { |
||||||
|
_, err := s.Write([]byte(d.String())) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (d Dec) String() string { |
||||||
|
if d.Int == nil { |
||||||
|
return d.Int.String() |
||||||
|
} |
||||||
|
|
||||||
|
isNeg := d.IsNegative() |
||||||
|
if d.IsNegative() { |
||||||
|
d = d.Neg() |
||||||
|
} |
||||||
|
|
||||||
|
bzInt, err := d.Int.MarshalText() |
||||||
|
if err != nil { |
||||||
|
return "" |
||||||
|
} |
||||||
|
inputSize := len(bzInt) |
||||||
|
|
||||||
|
var bzStr []byte |
||||||
|
|
||||||
|
// TODO: Remove trailing zeros
|
||||||
|
// case 1, purely decimal
|
||||||
|
if inputSize <= Precision { |
||||||
|
bzStr = make([]byte, Precision+2) |
||||||
|
|
||||||
|
// 0. prefix
|
||||||
|
bzStr[0] = byte('0') |
||||||
|
bzStr[1] = byte('.') |
||||||
|
|
||||||
|
// set relevant digits to 0
|
||||||
|
for i := 0; i < Precision-inputSize; i++ { |
||||||
|
bzStr[i+2] = byte('0') |
||||||
|
} |
||||||
|
|
||||||
|
// set final digits
|
||||||
|
copy(bzStr[2+(Precision-inputSize):], bzInt) |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
// inputSize + 1 to account for the decimal point that is being added
|
||||||
|
bzStr = make([]byte, inputSize+1) |
||||||
|
decPointPlace := inputSize - Precision |
||||||
|
|
||||||
|
copy(bzStr, bzInt[:decPointPlace]) // pre-decimal digits
|
||||||
|
bzStr[decPointPlace] = byte('.') // decimal point
|
||||||
|
copy(bzStr[decPointPlace+1:], bzInt[decPointPlace:]) // post-decimal digits
|
||||||
|
} |
||||||
|
|
||||||
|
if isNeg { |
||||||
|
return "-" + string(bzStr) |
||||||
|
} |
||||||
|
|
||||||
|
return string(bzStr) |
||||||
|
} |
||||||
|
|
||||||
|
// ____
|
||||||
|
// __| |__ "chop 'em
|
||||||
|
// ` \ round!"
|
||||||
|
// ___|| ~ _ -bankers
|
||||||
|
// | | __
|
||||||
|
// | | | __|__|__
|
||||||
|
// |_____: / | $$$ |
|
||||||
|
// |________|
|
||||||
|
|
||||||
|
// nolint - go-cyclo
|
||||||
|
// Remove a Precision amount of rightmost digits and perform bankers rounding
|
||||||
|
// on the remainder (gaussian rounding) on the digits which have been removed.
|
||||||
|
//
|
||||||
|
// Mutates the input. Use the non-mutative version if that is undesired
|
||||||
|
func chopPrecisionAndRound(d *big.Int) *big.Int { |
||||||
|
|
||||||
|
// remove the negative and add it back when returning
|
||||||
|
if d.Sign() == -1 { |
||||||
|
// make d positive, compute chopped value, and then un-mutate d
|
||||||
|
d = d.Neg(d) |
||||||
|
d = chopPrecisionAndRound(d) |
||||||
|
d = d.Neg(d) |
||||||
|
return d |
||||||
|
} |
||||||
|
|
||||||
|
// get the truncated quotient and remainder
|
||||||
|
quo, rem := d, big.NewInt(0) |
||||||
|
quo, rem = quo.QuoRem(d, precisionReuse, rem) |
||||||
|
|
||||||
|
if rem.Sign() == 0 { // remainder is zero
|
||||||
|
return quo |
||||||
|
} |
||||||
|
|
||||||
|
switch rem.Cmp(fivePrecision) { |
||||||
|
case -1: |
||||||
|
return quo |
||||||
|
case 1: |
||||||
|
return quo.Add(quo, oneInt) |
||||||
|
default: // bankers rounding must take place
|
||||||
|
// always round to an even number
|
||||||
|
if quo.Bit(0) == 0 { |
||||||
|
return quo |
||||||
|
} |
||||||
|
return quo.Add(quo, oneInt) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func chopPrecisionAndRoundUp(d *big.Int) *big.Int { |
||||||
|
|
||||||
|
// remove the negative and add it back when returning
|
||||||
|
if d.Sign() == -1 { |
||||||
|
// make d positive, compute chopped value, and then un-mutate d
|
||||||
|
d = d.Neg(d) |
||||||
|
// truncate since d is negative...
|
||||||
|
d = chopPrecisionAndTruncate(d) |
||||||
|
d = d.Neg(d) |
||||||
|
return d |
||||||
|
} |
||||||
|
|
||||||
|
// get the truncated quotient and remainder
|
||||||
|
quo, rem := d, big.NewInt(0) |
||||||
|
quo, rem = quo.QuoRem(d, precisionReuse, rem) |
||||||
|
|
||||||
|
if rem.Sign() == 0 { // remainder is zero
|
||||||
|
return quo |
||||||
|
} |
||||||
|
|
||||||
|
return quo.Add(quo, oneInt) |
||||||
|
} |
||||||
|
|
||||||
|
func chopPrecisionAndRoundNonMutative(d *big.Int) *big.Int { |
||||||
|
tmp := new(big.Int).Set(d) |
||||||
|
return chopPrecisionAndRound(tmp) |
||||||
|
} |
||||||
|
|
||||||
|
// RoundInt64 rounds the decimal using bankers rounding
|
||||||
|
func (d Dec) RoundInt64() int64 { |
||||||
|
chopped := chopPrecisionAndRoundNonMutative(d.Int) |
||||||
|
if !chopped.IsInt64() { |
||||||
|
panic("Int64() out of bound") |
||||||
|
} |
||||||
|
return chopped.Int64() |
||||||
|
} |
||||||
|
|
||||||
|
// RoundInt round the decimal using bankers rounding
|
||||||
|
//func (d Dec) RoundInt() big.Int {
|
||||||
|
// return NewIntFromBigInt(chopPrecisionAndRoundNonMutative(d.Int))
|
||||||
|
//}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
|
||||||
|
// similar to chopPrecisionAndRound, but always rounds down
|
||||||
|
func chopPrecisionAndTruncate(d *big.Int) *big.Int { |
||||||
|
return d.Quo(d, precisionReuse) |
||||||
|
} |
||||||
|
|
||||||
|
func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int { |
||||||
|
tmp := new(big.Int).Set(d) |
||||||
|
return chopPrecisionAndTruncate(tmp) |
||||||
|
} |
||||||
|
|
||||||
|
// TruncateInt64 truncates the decimals from the number and returns an int64
|
||||||
|
func (d Dec) TruncateInt64() int64 { |
||||||
|
chopped := chopPrecisionAndTruncateNonMutative(d.Int) |
||||||
|
if !chopped.IsInt64() { |
||||||
|
panic("Int64() out of bound") |
||||||
|
} |
||||||
|
return chopped.Int64() |
||||||
|
} |
||||||
|
|
||||||
|
// TruncateInt truncates the decimals from the number and returns an Int
|
||||||
|
//func (d Dec) TruncateInt() big.Int {
|
||||||
|
// return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int))
|
||||||
|
//}
|
||||||
|
|
||||||
|
// TruncateDec truncates the decimals from the number and returns a Dec
|
||||||
|
func (d Dec) TruncateDec() Dec { |
||||||
|
return NewDecFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) |
||||||
|
} |
||||||
|
|
||||||
|
// Ceil returns the smallest interger value (as a decimal) that is greater than
|
||||||
|
// or equal to the given decimal.
|
||||||
|
func (d Dec) Ceil() Dec { |
||||||
|
tmp := new(big.Int).Set(d.Int) |
||||||
|
|
||||||
|
quo, rem := tmp, big.NewInt(0) |
||||||
|
quo, rem = quo.QuoRem(tmp, precisionReuse, rem) |
||||||
|
|
||||||
|
// no need to round with a zero remainder regardless of sign
|
||||||
|
if rem.Cmp(zeroInt) == 0 { |
||||||
|
return NewDecFromBigInt(quo) |
||||||
|
} |
||||||
|
|
||||||
|
if rem.Sign() == -1 { |
||||||
|
return NewDecFromBigInt(quo) |
||||||
|
} |
||||||
|
|
||||||
|
return NewDecFromBigInt(quo.Add(quo, oneInt)) |
||||||
|
} |
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
|
||||||
|
// reuse nil values
|
||||||
|
var ( |
||||||
|
nilAmino string |
||||||
|
nilJSON []byte |
||||||
|
) |
||||||
|
|
||||||
|
func init() { |
||||||
|
empty := new(big.Int) |
||||||
|
bz, err := empty.MarshalText() |
||||||
|
if err != nil { |
||||||
|
panic("bad nil amino init") |
||||||
|
} |
||||||
|
nilAmino = string(bz) |
||||||
|
|
||||||
|
nilJSON, err = json.Marshal(string(bz)) |
||||||
|
if err != nil { |
||||||
|
panic("bad nil json init") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// MarshalAmino wraps d.MarshalText()
|
||||||
|
func (d Dec) MarshalAmino() (string, error) { |
||||||
|
if d.Int == nil { |
||||||
|
return nilAmino, nil |
||||||
|
} |
||||||
|
bz, err := d.Int.MarshalText() |
||||||
|
return string(bz), err |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalAmino requires a valid JSON string - strings quotes and calls UnmarshalText
|
||||||
|
func (d *Dec) UnmarshalAmino(text string) (err error) { |
||||||
|
tempInt := new(big.Int) |
||||||
|
err = tempInt.UnmarshalText([]byte(text)) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
d.Int = tempInt |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// MarshalJSON marshals the decimal
|
||||||
|
func (d Dec) MarshalJSON() ([]byte, error) { |
||||||
|
if d.Int == nil { |
||||||
|
return nilJSON, nil |
||||||
|
} |
||||||
|
|
||||||
|
return json.Marshal(d.String()) |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalJSON defines custom decoding scheme
|
||||||
|
func (d *Dec) UnmarshalJSON(bz []byte) error { |
||||||
|
if d.Int == nil { |
||||||
|
d.Int = new(big.Int) |
||||||
|
} |
||||||
|
|
||||||
|
var text string |
||||||
|
err := json.Unmarshal(bz, &text) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
// TODO: Reuse dec allocation
|
||||||
|
newDec, err := NewDecFromStr(text) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
d.Int = newDec.Int |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// MarshalYAML returns Ythe AML representation.
|
||||||
|
func (d Dec) MarshalYAML() (interface{}, error) { return d.String(), nil } |
||||||
|
|
||||||
|
//___________________________________________________________________________________
|
||||||
|
// helpers
|
||||||
|
|
||||||
|
// DecsEqual test if two decimal arrays are equal
|
||||||
|
func DecsEqual(d1s, d2s []Dec) bool { |
||||||
|
if len(d1s) != len(d2s) { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
for i, d1 := range d1s { |
||||||
|
if !d1.Equal(d2s[i]) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// MinDec minimum decimal between two
|
||||||
|
func MinDec(d1, d2 Dec) Dec { |
||||||
|
if d1.LT(d2) { |
||||||
|
return d1 |
||||||
|
} |
||||||
|
return d2 |
||||||
|
} |
||||||
|
|
||||||
|
// MaxDec maximum decimal between two
|
||||||
|
func MaxDec(d1, d2 Dec) Dec { |
||||||
|
if d1.LT(d2) { |
||||||
|
return d2 |
||||||
|
} |
||||||
|
return d1 |
||||||
|
} |
||||||
|
|
||||||
|
// DecEq intended to be used with require/assert: require.True(DecEq(...))
|
||||||
|
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) { |
||||||
|
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() |
||||||
|
} |
@ -0,0 +1,356 @@ |
|||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
) |
||||||
|
|
||||||
|
// create a decimal from a decimal string (ex. "1234.5678")
|
||||||
|
func mustNewDecFromStr(t *testing.T, str string) (d Dec) { |
||||||
|
d, err := NewDecFromStr(str) |
||||||
|
require.NoError(t, err) |
||||||
|
return d |
||||||
|
} |
||||||
|
|
||||||
|
//_______________________________________
|
||||||
|
|
||||||
|
func TestPrecisionMultiplier(t *testing.T) { |
||||||
|
res := precisionMultiplier(5) |
||||||
|
exp := big.NewInt(10000000000000) |
||||||
|
require.Equal(t, 0, res.Cmp(exp), "equality was incorrect, res %v, exp %v", res, exp) |
||||||
|
} |
||||||
|
|
||||||
|
func TestNewDecFromStr(t *testing.T) { |
||||||
|
largeBigInt, success := new(big.Int).SetString("3144605511029693144278234343371835", 10) |
||||||
|
require.True(t, success) |
||||||
|
tests := []struct { |
||||||
|
decimalStr string |
||||||
|
expErr bool |
||||||
|
exp Dec |
||||||
|
}{ |
||||||
|
{"", true, Dec{}}, |
||||||
|
{"0.-75", true, Dec{}}, |
||||||
|
{"0", false, NewDec(0)}, |
||||||
|
{"1", false, NewDec(1)}, |
||||||
|
{"1.1", false, NewDecWithPrec(11, 1)}, |
||||||
|
{"0.75", false, NewDecWithPrec(75, 2)}, |
||||||
|
{"0.8", false, NewDecWithPrec(8, 1)}, |
||||||
|
{"0.11111", false, NewDecWithPrec(11111, 5)}, |
||||||
|
{"314460551102969.3144278234343371835", true, NewDec(3141203149163817869)}, |
||||||
|
{"314460551102969314427823434337.1835718092488231350", |
||||||
|
true, NewDecFromBigIntWithPrec(largeBigInt, 4)}, |
||||||
|
{"314460551102969314427823434337.1835", |
||||||
|
false, NewDecFromBigIntWithPrec(largeBigInt, 4)}, |
||||||
|
{".", true, Dec{}}, |
||||||
|
{".0", true, NewDec(0)}, |
||||||
|
{"1.", true, NewDec(1)}, |
||||||
|
{"foobar", true, Dec{}}, |
||||||
|
{"0.foobar", true, Dec{}}, |
||||||
|
{"0.foobar.", true, Dec{}}, |
||||||
|
} |
||||||
|
|
||||||
|
for tcIndex, tc := range tests { |
||||||
|
res, err := NewDecFromStr(tc.decimalStr) |
||||||
|
if tc.expErr { |
||||||
|
require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) |
||||||
|
} else { |
||||||
|
require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) |
||||||
|
require.True(t, res.Equal(tc.exp), "equality was incorrect, res %v, exp %v, tc %v", res, tc.exp, tcIndex) |
||||||
|
} |
||||||
|
|
||||||
|
// negative tc
|
||||||
|
res, err = NewDecFromStr("-" + tc.decimalStr) |
||||||
|
if tc.expErr { |
||||||
|
require.NotNil(t, err, "error expected, decimalStr %v, tc %v", tc.decimalStr, tcIndex) |
||||||
|
} else { |
||||||
|
require.Nil(t, err, "unexpected error, decimalStr %v, tc %v", tc.decimalStr, tcIndex) |
||||||
|
exp := tc.exp.Mul(NewDec(-1)) |
||||||
|
require.True(t, res.Equal(exp), "equality was incorrect, res %v, exp %v, tc %v", res, exp, tcIndex) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestDecString(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
d Dec |
||||||
|
want string |
||||||
|
}{ |
||||||
|
{NewDec(0), "0.000000000000000000"}, |
||||||
|
{NewDec(1), "1.000000000000000000"}, |
||||||
|
{NewDec(10), "10.000000000000000000"}, |
||||||
|
{NewDec(12340), "12340.000000000000000000"}, |
||||||
|
{NewDecWithPrec(12340, 4), "1.234000000000000000"}, |
||||||
|
{NewDecWithPrec(12340, 5), "0.123400000000000000"}, |
||||||
|
{NewDecWithPrec(12340, 8), "0.000123400000000000"}, |
||||||
|
{NewDecWithPrec(1009009009009009009, 17), "10.090090090090090090"}, |
||||||
|
} |
||||||
|
for tcIndex, tc := range tests { |
||||||
|
assert.Equal(t, tc.want, tc.d.String(), "bad String(), index: %v", tcIndex) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestEqualities(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
d1, d2 Dec |
||||||
|
gt, lt, eq bool |
||||||
|
}{ |
||||||
|
{NewDec(0), NewDec(0), false, false, true}, |
||||||
|
{NewDecWithPrec(0, 2), NewDecWithPrec(0, 4), false, false, true}, |
||||||
|
{NewDecWithPrec(100, 0), NewDecWithPrec(100, 0), false, false, true}, |
||||||
|
{NewDecWithPrec(-100, 0), NewDecWithPrec(-100, 0), false, false, true}, |
||||||
|
{NewDecWithPrec(-1, 1), NewDecWithPrec(-1, 1), false, false, true}, |
||||||
|
{NewDecWithPrec(3333, 3), NewDecWithPrec(3333, 3), false, false, true}, |
||||||
|
|
||||||
|
{NewDecWithPrec(0, 0), NewDecWithPrec(3333, 3), false, true, false}, |
||||||
|
{NewDecWithPrec(0, 0), NewDecWithPrec(100, 0), false, true, false}, |
||||||
|
{NewDecWithPrec(-1, 0), NewDecWithPrec(3333, 3), false, true, false}, |
||||||
|
{NewDecWithPrec(-1, 0), NewDecWithPrec(100, 0), false, true, false}, |
||||||
|
{NewDecWithPrec(1111, 3), NewDecWithPrec(100, 0), false, true, false}, |
||||||
|
{NewDecWithPrec(1111, 3), NewDecWithPrec(3333, 3), false, true, false}, |
||||||
|
{NewDecWithPrec(-3333, 3), NewDecWithPrec(-1111, 3), false, true, false}, |
||||||
|
|
||||||
|
{NewDecWithPrec(3333, 3), NewDecWithPrec(0, 0), true, false, false}, |
||||||
|
{NewDecWithPrec(100, 0), NewDecWithPrec(0, 0), true, false, false}, |
||||||
|
{NewDecWithPrec(3333, 3), NewDecWithPrec(-1, 0), true, false, false}, |
||||||
|
{NewDecWithPrec(100, 0), NewDecWithPrec(-1, 0), true, false, false}, |
||||||
|
{NewDecWithPrec(100, 0), NewDecWithPrec(1111, 3), true, false, false}, |
||||||
|
{NewDecWithPrec(3333, 3), NewDecWithPrec(1111, 3), true, false, false}, |
||||||
|
{NewDecWithPrec(-1111, 3), NewDecWithPrec(-3333, 3), true, false, false}, |
||||||
|
} |
||||||
|
|
||||||
|
for tcIndex, tc := range tests { |
||||||
|
require.Equal(t, tc.gt, tc.d1.GT(tc.d2), "GT result is incorrect, tc %d", tcIndex) |
||||||
|
require.Equal(t, tc.lt, tc.d1.LT(tc.d2), "LT result is incorrect, tc %d", tcIndex) |
||||||
|
require.Equal(t, tc.eq, tc.d1.Equal(tc.d2), "equality result is incorrect, tc %d", tcIndex) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
func TestDecsEqual(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
d1s, d2s []Dec |
||||||
|
eq bool |
||||||
|
}{ |
||||||
|
{[]Dec{NewDec(0)}, []Dec{NewDec(0)}, true}, |
||||||
|
{[]Dec{NewDec(0)}, []Dec{NewDec(1)}, false}, |
||||||
|
{[]Dec{NewDec(0)}, []Dec{}, false}, |
||||||
|
{[]Dec{NewDec(0), NewDec(1)}, []Dec{NewDec(0), NewDec(1)}, true}, |
||||||
|
{[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1), NewDec(0)}, true}, |
||||||
|
{[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(0), NewDec(1)}, false}, |
||||||
|
{[]Dec{NewDec(1), NewDec(0)}, []Dec{NewDec(1)}, false}, |
||||||
|
{[]Dec{NewDec(1), NewDec(2)}, []Dec{NewDec(2), NewDec(4)}, false}, |
||||||
|
{[]Dec{NewDec(3), NewDec(18)}, []Dec{NewDec(1), NewDec(6)}, false}, |
||||||
|
} |
||||||
|
|
||||||
|
for tcIndex, tc := range tests { |
||||||
|
require.Equal(t, tc.eq, DecsEqual(tc.d1s, tc.d2s), "equality of decional arrays is incorrect, tc %d", tcIndex) |
||||||
|
require.Equal(t, tc.eq, DecsEqual(tc.d2s, tc.d1s), "equality of decional arrays is incorrect (converse), tc %d", tcIndex) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestArithmetic(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
d1, d2 Dec |
||||||
|
expMul, expMulTruncate Dec |
||||||
|
expQuo, expQuoRoundUp, expQuoTruncate Dec |
||||||
|
expAdd, expSub Dec |
||||||
|
}{ |
||||||
|
// d1 d2 MUL MulTruncate QUO QUORoundUp QUOTrunctate ADD SUB
|
||||||
|
{NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0)}, |
||||||
|
{NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(1)}, |
||||||
|
{NewDec(0), NewDec(1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(1), NewDec(-1)}, |
||||||
|
{NewDec(0), NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(1)}, |
||||||
|
{NewDec(-1), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(0), NewDec(-1), NewDec(-1)}, |
||||||
|
|
||||||
|
{NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(2), NewDec(0)}, |
||||||
|
{NewDec(-1), NewDec(-1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(1), NewDec(-2), NewDec(0)}, |
||||||
|
{NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(2)}, |
||||||
|
{NewDec(-1), NewDec(1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(-1), NewDec(0), NewDec(-2)}, |
||||||
|
|
||||||
|
{NewDec(3), NewDec(7), NewDec(21), NewDec(21), |
||||||
|
NewDecWithPrec(428571428571428571, 18), NewDecWithPrec(428571428571428572, 18), NewDecWithPrec(428571428571428571, 18), |
||||||
|
NewDec(10), NewDec(-4)}, |
||||||
|
{NewDec(2), NewDec(4), NewDec(8), NewDec(8), NewDecWithPrec(5, 1), NewDecWithPrec(5, 1), NewDecWithPrec(5, 1), |
||||||
|
NewDec(6), NewDec(-2)}, |
||||||
|
|
||||||
|
{NewDec(100), NewDec(100), NewDec(10000), NewDec(10000), NewDec(1), NewDec(1), NewDec(1), NewDec(200), NewDec(0)}, |
||||||
|
|
||||||
|
{NewDecWithPrec(15, 1), NewDecWithPrec(15, 1), NewDecWithPrec(225, 2), NewDecWithPrec(225, 2), |
||||||
|
NewDec(1), NewDec(1), NewDec(1), NewDec(3), NewDec(0)}, |
||||||
|
{NewDecWithPrec(3333, 4), NewDecWithPrec(333, 4), NewDecWithPrec(1109889, 8), NewDecWithPrec(1109889, 8), |
||||||
|
MustNewDecFromStr("10.009009009009009009"), MustNewDecFromStr("10.009009009009009010"), MustNewDecFromStr("10.009009009009009009"), |
||||||
|
NewDecWithPrec(3666, 4), NewDecWithPrec(3, 1)}, |
||||||
|
} |
||||||
|
|
||||||
|
for tcIndex, tc := range tests { |
||||||
|
resAdd := tc.d1.Add(tc.d2) |
||||||
|
resSub := tc.d1.Sub(tc.d2) |
||||||
|
resMul := tc.d1.Mul(tc.d2) |
||||||
|
resMulTruncate := tc.d1.MulTruncate(tc.d2) |
||||||
|
require.True(t, tc.expAdd.Equal(resAdd), "exp %v, res %v, tc %d", tc.expAdd, resAdd, tcIndex) |
||||||
|
require.True(t, tc.expSub.Equal(resSub), "exp %v, res %v, tc %d", tc.expSub, resSub, tcIndex) |
||||||
|
require.True(t, tc.expMul.Equal(resMul), "exp %v, res %v, tc %d", tc.expMul, resMul, tcIndex) |
||||||
|
require.True(t, tc.expMulTruncate.Equal(resMulTruncate), "exp %v, res %v, tc %d", tc.expMulTruncate, resMulTruncate, tcIndex) |
||||||
|
|
||||||
|
if tc.d2.IsZero() { // panic for divide by zero
|
||||||
|
require.Panics(t, func() { tc.d1.Quo(tc.d2) }) |
||||||
|
} else { |
||||||
|
resQuo := tc.d1.Quo(tc.d2) |
||||||
|
require.True(t, tc.expQuo.Equal(resQuo), "exp %v, res %v, tc %d", tc.expQuo.String(), resQuo.String(), tcIndex) |
||||||
|
|
||||||
|
resQuoRoundUp := tc.d1.QuoRoundUp(tc.d2) |
||||||
|
require.True(t, tc.expQuoRoundUp.Equal(resQuoRoundUp), "exp %v, res %v, tc %d", |
||||||
|
tc.expQuoRoundUp.String(), resQuoRoundUp.String(), tcIndex) |
||||||
|
|
||||||
|
resQuoTruncate := tc.d1.QuoTruncate(tc.d2) |
||||||
|
require.True(t, tc.expQuoTruncate.Equal(resQuoTruncate), "exp %v, res %v, tc %d", |
||||||
|
tc.expQuoTruncate.String(), resQuoTruncate.String(), tcIndex) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestBankerRoundChop(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
d1 Dec |
||||||
|
exp int64 |
||||||
|
}{ |
||||||
|
{mustNewDecFromStr(t, "0.25"), 0}, |
||||||
|
{mustNewDecFromStr(t, "0"), 0}, |
||||||
|
{mustNewDecFromStr(t, "1"), 1}, |
||||||
|
{mustNewDecFromStr(t, "0.75"), 1}, |
||||||
|
{mustNewDecFromStr(t, "0.5"), 0}, |
||||||
|
{mustNewDecFromStr(t, "7.5"), 8}, |
||||||
|
{mustNewDecFromStr(t, "1.5"), 2}, |
||||||
|
{mustNewDecFromStr(t, "2.5"), 2}, |
||||||
|
{mustNewDecFromStr(t, "0.545"), 1}, // 0.545-> 1 even though 5 is first decimal and 1 not even
|
||||||
|
{mustNewDecFromStr(t, "1.545"), 2}, |
||||||
|
} |
||||||
|
|
||||||
|
for tcIndex, tc := range tests { |
||||||
|
resNeg := tc.d1.Neg().RoundInt64() |
||||||
|
require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) |
||||||
|
|
||||||
|
resPos := tc.d1.RoundInt64() |
||||||
|
require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestTruncate(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
d1 Dec |
||||||
|
exp int64 |
||||||
|
}{ |
||||||
|
{mustNewDecFromStr(t, "0"), 0}, |
||||||
|
{mustNewDecFromStr(t, "0.25"), 0}, |
||||||
|
{mustNewDecFromStr(t, "0.75"), 0}, |
||||||
|
{mustNewDecFromStr(t, "1"), 1}, |
||||||
|
{mustNewDecFromStr(t, "1.5"), 1}, |
||||||
|
{mustNewDecFromStr(t, "7.5"), 7}, |
||||||
|
{mustNewDecFromStr(t, "7.6"), 7}, |
||||||
|
{mustNewDecFromStr(t, "7.4"), 7}, |
||||||
|
{mustNewDecFromStr(t, "100.1"), 100}, |
||||||
|
{mustNewDecFromStr(t, "1000.1"), 1000}, |
||||||
|
} |
||||||
|
|
||||||
|
for tcIndex, tc := range tests { |
||||||
|
resNeg := tc.d1.Neg().TruncateInt64() |
||||||
|
require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) |
||||||
|
|
||||||
|
resPos := tc.d1.TruncateInt64() |
||||||
|
require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestDecMarshalJSON(t *testing.T) { |
||||||
|
decimal := func(i int64) Dec { |
||||||
|
d := NewDec(0) |
||||||
|
d.Int = new(big.Int).SetInt64(i) |
||||||
|
return d |
||||||
|
} |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
d Dec |
||||||
|
want string |
||||||
|
wantErr bool // if wantErr = false, will also attempt unmarshaling
|
||||||
|
}{ |
||||||
|
{"zero", decimal(0), "\"0.000000000000000000\"", false}, |
||||||
|
{"one", decimal(1), "\"0.000000000000000001\"", false}, |
||||||
|
{"ten", decimal(10), "\"0.000000000000000010\"", false}, |
||||||
|
{"12340", decimal(12340), "\"0.000000000000012340\"", false}, |
||||||
|
{"zeroInt", NewDec(0), "\"0.000000000000000000\"", false}, |
||||||
|
{"oneInt", NewDec(1), "\"1.000000000000000000\"", false}, |
||||||
|
{"tenInt", NewDec(10), "\"10.000000000000000000\"", false}, |
||||||
|
{"12340Int", NewDec(12340), "\"12340.000000000000000000\"", false}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
got, err := tt.d.MarshalJSON() |
||||||
|
if (err != nil) != tt.wantErr { |
||||||
|
t.Errorf("Dec.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) |
||||||
|
return |
||||||
|
} |
||||||
|
if !tt.wantErr { |
||||||
|
assert.Equal(t, tt.want, string(got), "incorrect marshalled value") |
||||||
|
unmarshalledDec := NewDec(0) |
||||||
|
unmarshalledDec.UnmarshalJSON(got) |
||||||
|
assert.Equal(t, tt.d, unmarshalledDec, "incorrect unmarshalled value") |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestSerializationText(t *testing.T) { |
||||||
|
d := mustNewDecFromStr(t, "0.333") |
||||||
|
|
||||||
|
bz, err := d.MarshalText() |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
d2 := Dec{new(big.Int)} |
||||||
|
err = d2.UnmarshalText(bz) |
||||||
|
require.NoError(t, err) |
||||||
|
require.True(t, d.Equal(d2), "original: %v, unmarshalled: %v", d, d2) |
||||||
|
} |
||||||
|
|
||||||
|
type testDEmbedStruct struct { |
||||||
|
Field1 string `json:"f1"` |
||||||
|
Field2 int `json:"f2"` |
||||||
|
Field3 Dec `json:"f3"` |
||||||
|
} |
||||||
|
|
||||||
|
func TestStringOverflow(t *testing.T) { |
||||||
|
// two random 64 bit primes
|
||||||
|
dec1, err := NewDecFromStr("51643150036226787134389711697696177267") |
||||||
|
require.NoError(t, err) |
||||||
|
dec2, err := NewDecFromStr("-31798496660535729618459429845579852627") |
||||||
|
require.NoError(t, err) |
||||||
|
dec3 := dec1.Add(dec2) |
||||||
|
require.Equal(t, |
||||||
|
"19844653375691057515930281852116324640.000000000000000000", |
||||||
|
dec3.String(), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
func TestDecCeil(t *testing.T) { |
||||||
|
testCases := []struct { |
||||||
|
input Dec |
||||||
|
expected Dec |
||||||
|
}{ |
||||||
|
{NewDecWithPrec(1000000000000000, Precision), NewDec(1)}, // 0.001 => 1.0
|
||||||
|
{NewDecWithPrec(-1000000000000000, Precision), ZeroDec()}, // -0.001 => 0.0
|
||||||
|
{ZeroDec(), ZeroDec()}, // 0.0 => 0.0
|
||||||
|
{NewDecWithPrec(900000000000000000, Precision), NewDec(1)}, // 0.9 => 1.0
|
||||||
|
{NewDecWithPrec(4001000000000000000, Precision), NewDec(5)}, // 4.001 => 5.0
|
||||||
|
{NewDecWithPrec(-4001000000000000000, Precision), NewDec(-4)}, // -4.001 => -4.0
|
||||||
|
{NewDecWithPrec(4700000000000000000, Precision), NewDec(5)}, // 4.7 => 5.0
|
||||||
|
{NewDecWithPrec(-4700000000000000000, Precision), NewDec(-4)}, // -4.7 => -4.0
|
||||||
|
} |
||||||
|
|
||||||
|
for i, tc := range testCases { |
||||||
|
res := tc.input.Ceil() |
||||||
|
require.Equal(t, tc.expected, res, "unexpected result for test case %d, input: %v", i, tc.input) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/harmony-one/bls/ffi/go/bls" |
||||||
|
"github.com/harmony-one/harmony/internal/common" |
||||||
|
) |
||||||
|
|
||||||
|
// Validator - data fields for a validator
|
||||||
|
type Validator struct { |
||||||
|
StakingAddress common.Address `json:"staking_address" yaml:"staking_address"` // ECDSA address of the validator
|
||||||
|
ValidatingPubKey bls.PublicKey `json:"validating_pub_key" yaml:"validating_pub_key"` // The BLS public key of the validator for consensus
|
||||||
|
Description Description `json:"description" yaml:"description"` // description for the validator
|
||||||
|
Active bool `json:"active" yaml:"active"` // Is the validator active in the validating process or not
|
||||||
|
Stake big.Int `json:"stake" yaml:"stake"` // The stake put by the validator itself
|
||||||
|
UnbondingHeight big.Int `json:"unbonding_height" yaml:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||||
|
Commission Commission `json:"commission" yaml:"commission"` // commission parameters
|
||||||
|
MinSelfDelegation big.Int `json:"min_self_delegation" yaml:"min_self_delegation"` // validator's self declared minimum self delegation
|
||||||
|
} |
||||||
|
|
||||||
|
// Description - description fields for a validator
|
||||||
|
type Description struct { |
||||||
|
Name string `json:"name" yaml:"name"` // name
|
||||||
|
Identity string `json:"identity" yaml:"identity"` // optional identity signature (ex. UPort or Keybase)
|
||||||
|
Website string `json:"website" yaml:"website"` // optional website link
|
||||||
|
SecurityContact string `json:"security_contact" yaml:"security_contact"` // optional security contact info
|
||||||
|
Details string `json:"details" yaml:"details"` // optional details
|
||||||
|
} |
Loading…
Reference in new issue