code.vegaprotocol.io/vega@v0.79.0/libs/num/numeric.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package num 17 18 import ( 19 "errors" 20 "fmt" 21 "strings" 22 ) 23 24 type Numeric struct { 25 asInt *Int 26 asUint *Uint 27 asDecimal *Decimal 28 } 29 30 func (n *Numeric) Clone() *Numeric { 31 if n.asUint != nil { 32 nn := &Numeric{} 33 nn.SetUint(n.Uint().Clone()) 34 return nn 35 } 36 37 if n.asInt != nil { 38 nn := &Numeric{} 39 nn.SetInt(n.Int().Clone()) 40 return nn 41 } 42 43 nn := &Numeric{} 44 45 if n.asDecimal != nil { 46 decimal := *n.asDecimal 47 nn.asDecimal = &decimal 48 } 49 return nn 50 } 51 52 func (n *Numeric) String() string { 53 if n.asUint != nil { 54 return n.asUint.String() 55 } 56 if n.asDecimal != nil { 57 return n.asDecimal.String() 58 } 59 if n.asInt != nil { 60 return n.asInt.String() 61 } 62 63 return "" 64 } 65 66 func NumericToString(n *Numeric) string { 67 if n == nil { 68 return "" 69 } 70 71 return n.String() 72 } 73 74 func NumericFromString(s string) (*Numeric, error) { 75 if s == "" { 76 return nil, nil 77 } 78 79 // Check if the provided string contains a ".", because if it does not, 80 // the DecimalFromString will return it as int 81 split := strings.Split(s, ".") 82 if len(split) > 1 { 83 d, err := DecimalFromString(s) 84 if err != nil { 85 return nil, fmt.Errorf("error obtaining decimal from string: %s", err.Error()) 86 } 87 return &Numeric{ 88 asDecimal: &d, 89 }, nil 90 } 91 92 if strings.HasPrefix(s, "-") { 93 in, _ := IntFromString(s, 10) 94 return &Numeric{ 95 asInt: in, 96 }, nil 97 } 98 99 u, _ := UintFromString(s, 10) 100 101 return &Numeric{ 102 asUint: u, 103 }, nil 104 } 105 106 // ScaleTo calculates the current contained value - decimal or uint - scaled to the target decimals. 107 func (n *Numeric) ScaleTo(op, tdp int64) (*Uint, error) { 108 base := DecimalFromInt64(10) 109 if n.asDecimal != nil { 110 scaled := n.asDecimal.Mul(base.Pow(DecimalFromInt64(tdp))) 111 r, overflow := UintFromDecimal(scaled) 112 if overflow { 113 return nil, errors.New("failed to scale settlement data, overflow occurred") 114 } 115 return r, nil 116 } 117 118 if n.asUint != nil { 119 scaled := base.Pow(DecimalFromInt64(tdp - op)) 120 r, overflow := UintFromDecimal(n.asUint.ToDecimal().Mul(scaled)) 121 if overflow { 122 return nil, errors.New("failed to scale settlement data, overflow occurred") 123 } 124 return r, nil 125 } 126 127 return nil, nil 128 } 129 130 func (n *Numeric) SupportDecimalPlaces(dp int64) bool { 131 if n.IsDecimal() { 132 decimalParts := strings.Split(n.Decimal().String(), ".") 133 if len(decimalParts) > 1 { 134 if int64(len(decimalParts[1])) > dp { 135 return false 136 } 137 } 138 } 139 140 return true 141 } 142 143 func (n *Numeric) SetInt(in *Int) *Numeric { 144 n.asInt = in 145 n.asDecimal = nil 146 n.asUint = nil 147 return n 148 } 149 150 func (n *Numeric) SetUint(u *Uint) *Numeric { 151 n.asUint = u 152 n.asDecimal = nil 153 n.asInt = nil 154 return n 155 } 156 157 func (n *Numeric) SetDecimal(d *Decimal) *Numeric { 158 n.asDecimal = d 159 n.asUint = nil 160 n.asInt = nil 161 return n 162 } 163 164 func (n *Numeric) Decimal() *Decimal { 165 if n.asDecimal == nil { 166 return nil 167 } 168 d := *n.asDecimal 169 return &d 170 } 171 172 func (n *Numeric) Uint() *Uint { 173 if n.asUint == nil { 174 return nil 175 } 176 u := *n.asUint 177 return &u 178 } 179 180 func (n *Numeric) Int() *Int { 181 if n.asInt == nil { 182 return nil 183 } 184 in := *n.asInt 185 return &in 186 } 187 188 func (n *Numeric) IsDecimal() bool { 189 return n.asDecimal != nil 190 } 191 192 func (n *Numeric) IsUint() bool { 193 return n.asUint != nil 194 } 195 196 func (n *Numeric) IsInt() bool { 197 return n.asInt != nil 198 }