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  }