Skip to content

Commit

Permalink
Add numbers lib
Browse files Browse the repository at this point in the history
  • Loading branch information
vikmeup committed Nov 10, 2020
1 parent bf1fa6c commit 4ecad7e
Show file tree
Hide file tree
Showing 8 changed files with 575 additions and 0 deletions.
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ module github.com/trustwallet/golibs
go 1.15

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/shopspring/decimal v1.2.0
github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.3.0
)
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand All @@ -12,9 +21,13 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
34 changes: 34 additions & 0 deletions numbers/amount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package numbers

import (
"math"
"strconv"
)

func GetAmountValue(amount string) string {
value := ParseAmount(amount)
return strconv.FormatInt(value, 10)
}

func ParseAmount(amount string) int64 {
value, err := strconv.ParseInt(amount, 10, 64)
if err == nil {
return value
}
return ToSatoshi(amount)
}

func ToSatoshi(amount string) int64 {
value, err := strconv.ParseFloat(amount, 64)
if err != nil {
return 0
}
total := value * math.Pow10(8)
return int64(total)
}

func AddAmount(left string, right string) (sum string) {
amount1 := ParseAmount(left)
amount2 := ParseAmount(right)
return strconv.FormatInt(amount1+amount2, 10)
}
94 changes: 94 additions & 0 deletions numbers/amount_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package numbers

import (
"testing"
)

func Test_addAmount(t *testing.T) {
type args struct {
left string
right string
}
tests := []struct {
name string
args args
wantSum string
}{
{"test zero + float", args{left: "0", right: "0.33333"}, "33333000"},
{"test zero + int", args{left: "0", right: "333"}, "333"},
{"test zero + zero", args{left: "0", right: "0"}, "0"},
{"test int + float", args{left: "232", right: "0.222"}, "22200232"},
{"test int + int", args{left: "661", right: "12"}, "673"},
{"test int + zero", args{left: "131", right: "0"}, "131"},
{"test float + float", args{left: "0.4141", right: "0.11211"}, "52621000"},
{"test float + int", args{left: "3.111", right: "11"}, "311100011"},
{"test float + zero", args{left: "0.455", right: "0"}, "45500000"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotSum := AddAmount(tt.args.left, tt.args.right); gotSum != tt.wantSum {
t.Errorf("AddAmount() = %v, want %v", gotSum, tt.wantSum)
}
})
}
}

func Test_ToSatoshi(t *testing.T) {
tests := []struct {
name string
amount string
want int64
}{
{"test float", "0.33333", 33333000},
{"test int", "3333", 333300000000},
{"test zero", "0", 0},
{"test error", "trust", 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToSatoshi(tt.amount); got != tt.want {
t.Errorf("ToSatoshi() = %v, want %v", got, tt.want)
}
})
}
}

func Test_getValue(t *testing.T) {
tests := []struct {
name string
amount string
want string
}{
{"test float", "0.33333", "33333000"},
{"test int", "3333", "3333"},
{"test zero", "0", "0"},
{"test error", "trust", "0"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetAmountValue(tt.amount); got != tt.want {
t.Errorf("GetAmountValue() = %v, want %v", got, tt.want)
}
})
}
}

func Test_parseAmount(t *testing.T) {
tests := []struct {
name string
amount string
want int64
}{
{"test float", "0.33333", 33333000},
{"test int", "3333", 3333},
{"test zero", "0", 0},
{"test error", "trust", 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ParseAmount(tt.amount); got != tt.want {
t.Errorf("ParseAmount() = %v, want %v", got, tt.want)
}
})
}
}
102 changes: 102 additions & 0 deletions numbers/decimal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package numbers

import (
"errors"
"math/big"
"strings"
"unicode"
)

// DecimalToSatoshis removes the comma in a decimal string
// "12.345" => "12345"
// "0.0230" => "230"
func DecimalToSatoshis(dec string) (string, error) {
out := strings.TrimLeft(dec, " ")
out = strings.TrimRight(out, " ")
out = strings.Replace(out, ".", "", 1)
// trim left 0's but keep last
if l := len(out); l >= 2 {
out = strings.TrimLeft(out[:l-1], "0") + out[l-1:l]
}
if len(out) == 0 {
return "", errors.New("Invalid empty input: " + dec)
}
for _, c := range out {
if !unicode.IsNumber(c) {
return "", errors.New("not a number: " + dec)
}
}
return out, nil
}

// DecimalExp calculates dec * 10^exp in decimal string representation
func DecimalExp(dec string, exp int) string {
// 0 * n = 0
if dec == "0" {
return "0"
}
// Get comma position
i := strings.IndexRune(dec, '.')
if i == -1 {
// Virtual comma at the end of the string
i = len(dec)
} else {
// Remove comma from underlying number
dec = strings.Replace(dec, ".", "", 1)
}
// Shift comma by exponent
i += exp
// Remove leading zeros
origSize := len(dec)
dec = strings.TrimLeft(dec, "0")
i -= origSize - len(dec)
// Fix bounds
if i <= 0 {
zeros := ""
for ; i < 0; i++ {
zeros += "0"
}
return "0." + zeros + dec
} else if i >= len(dec) {
for i > len(dec) {
dec += "0"
}
return dec
}
// No bound fix needed
return dec[:i] + "." + dec[i:]
}

// HexToDecimal converts a hexadecimal integer to a base-10 integer
// "0x1fbad5f2e25570000" => "36582000000000000000"
func HexToDecimal(hex string) (string, error) {
var i big.Int
if _, ok := i.SetString(hex, 0); !ok {
return "", errors.New("invalid hex: " + hex)
}
return i.String(), nil
}

// CutZeroFractional cuts off a decimal separator and zeros to the right.
// Fails if the fractional part contains contains other digits than zeros.
// - CutZeroFractional("123.00000") => ("123", true)
// - CutZeroFractional("123.456") => ("", false)
func CutZeroFractional(dec string) (integer string, ok bool) {
// Get comma position
comma := strings.IndexRune(dec, '.')
if comma == -1 {
return dec, true
}

for i := len(dec) - 1; i > comma; i-- {
if dec[i] != '0' {
return "", false
}
}

if comma == 0 {
return "0", true
} else {
return dec[:comma], true
}
}
Loading

0 comments on commit 4ecad7e

Please sign in to comment.