github.com/richardwilkes/toolbox@v1.121.0/xmath/fixed/f128/f128.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package f128
    11  
    12  import (
    13  	"fmt"
    14  	"math/big"
    15  	"reflect"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/richardwilkes/toolbox/errs"
    20  	"github.com/richardwilkes/toolbox/txt"
    21  	"github.com/richardwilkes/toolbox/xmath"
    22  	"github.com/richardwilkes/toolbox/xmath/fixed"
    23  	"github.com/richardwilkes/toolbox/xmath/num"
    24  	"gopkg.in/yaml.v3"
    25  )
    26  
    27  // Int holds a fixed-point value. Values are truncated, not rounded.
    28  type Int[T fixed.Dx] struct {
    29  	data num.Int128
    30  }
    31  
    32  // Maximum returns the maximum possible value the type can hold.
    33  func Maximum[T fixed.Dx]() Int[T] {
    34  	return Int[T]{data: num.MaxInt128}
    35  }
    36  
    37  // Minimum returns the minimum possible value the type can hold.
    38  func Minimum[T fixed.Dx]() Int[T] {
    39  	return Int[T]{data: num.MinInt128}
    40  }
    41  
    42  // MaxSafeMultiply returns the maximum value that can be safely multiplied without overflow.
    43  func MaxSafeMultiply[T fixed.Dx]() Int[T] {
    44  	return Maximum[T]().Div(Multiplier[T]())
    45  }
    46  
    47  // MaxDecimalDigits returns the maximum number of digits after the decimal that will be used.
    48  func MaxDecimalDigits[T fixed.Dx]() int {
    49  	var t T
    50  	return t.Places()
    51  }
    52  
    53  // Multiplier returns the multiplier used.
    54  func Multiplier[T fixed.Dx]() Int[T] {
    55  	return Int[T]{data: multiplier[T]()}
    56  }
    57  
    58  func multiplier[T fixed.Dx]() num.Int128 {
    59  	var t T
    60  	return num.Int128From64(t.Multiplier())
    61  }
    62  
    63  // From creates a new value.
    64  func From[T fixed.Dx, FROM xmath.Numeric](value FROM) Int[T] {
    65  	switch reflect.TypeOf(value).Kind() {
    66  	case reflect.Float32, reflect.Float64:
    67  		f, _ := FromString[T](new(big.Float).SetPrec(128).SetFloat64(float64(value)).Text('f', MaxDecimalDigits[T]()+1)) //nolint:errcheck // Failure means 0
    68  		return f
    69  	default:
    70  		var t T
    71  		return Int[T]{data: num.Int128From64(int64(value)).Mul(num.Int128From64(t.Multiplier()))}
    72  	}
    73  }
    74  
    75  // FromString creates a new value from a string.
    76  func FromString[T fixed.Dx](str string) (Int[T], error) {
    77  	if str == "" {
    78  		return Int[T]{}, errs.New("empty string is not valid")
    79  	}
    80  	str = strings.ReplaceAll(str, ",", "")
    81  	if strings.ContainsAny(str, "Ee") {
    82  		// Given a floating-point value with an exponent, which technically
    83  		// isn't valid input, but we'll try to convert it anyway.
    84  		f, err := strconv.ParseFloat(str, 64)
    85  		if err != nil {
    86  			return Int[T]{}, err
    87  		}
    88  		return From[T](f), nil
    89  	}
    90  	parts := strings.SplitN(str, ".", 2)
    91  	var neg bool
    92  	value := new(big.Int)
    93  	fraction := new(big.Int)
    94  	var t T
    95  	switch parts[0] {
    96  	case "":
    97  	case "-", "-0":
    98  		neg = true
    99  	default:
   100  		if _, ok := value.SetString(parts[0], 10); !ok {
   101  			return Int[T]{}, errs.Newf("invalid value: %s", str)
   102  		}
   103  		if value.Sign() < 0 {
   104  			neg = true
   105  			value.Neg(value)
   106  		}
   107  		value.Mul(value, big.NewInt(t.Multiplier()))
   108  	}
   109  	if len(parts) > 1 {
   110  		cutoff := 1 + MaxDecimalDigits[T]()
   111  		var buffer strings.Builder
   112  		buffer.WriteString("1")
   113  		buffer.WriteString(parts[1])
   114  		for buffer.Len() < cutoff {
   115  			buffer.WriteString("0")
   116  		}
   117  		frac := buffer.String()
   118  		if len(frac) > cutoff {
   119  			frac = frac[:cutoff]
   120  		}
   121  		if _, ok := fraction.SetString(frac, 10); !ok {
   122  			return Int[T]{}, errs.Newf("invalid value: %s", str)
   123  		}
   124  		value.Add(value, fraction).Sub(value, big.NewInt(t.Multiplier()))
   125  	}
   126  	if neg {
   127  		value.Neg(value)
   128  	}
   129  	return Int[T]{data: num.Int128FromBigInt(value)}, nil
   130  }
   131  
   132  // FromStringForced creates a new value from a string.
   133  func FromStringForced[T fixed.Dx](str string) Int[T] {
   134  	f, _ := FromString[T](str) //nolint:errcheck // failure results in 0, which is acceptable here
   135  	return f
   136  }
   137  
   138  // Add adds this value to the passed-in value, returning a new value.
   139  func (f Int[T]) Add(value Int[T]) Int[T] {
   140  	return Int[T]{data: f.data.Add(value.data)}
   141  }
   142  
   143  // Sub subtracts the passed-in value from this value, returning a new value.
   144  func (f Int[T]) Sub(value Int[T]) Int[T] {
   145  	return Int[T]{data: f.data.Sub(value.data)}
   146  }
   147  
   148  // Mul multiplies this value by the passed-in value, returning a new value.
   149  func (f Int[T]) Mul(value Int[T]) Int[T] {
   150  	return Int[T]{data: f.data.Mul(value.data).Div(multiplier[T]())}
   151  }
   152  
   153  // Div divides this value by the passed-in value, returning a new value.
   154  func (f Int[T]) Div(value Int[T]) Int[T] {
   155  	return Int[T]{data: f.data.Mul(multiplier[T]()).Div(value.data)}
   156  }
   157  
   158  // Mod returns the remainder after subtracting all full multiples of the passed-in value.
   159  func (f Int[T]) Mod(value Int[T]) Int[T] {
   160  	return f.Sub(value.Mul(f.Div(value).Trunc()))
   161  }
   162  
   163  // Neg negates this value, returning a new value.
   164  func (f Int[T]) Neg() Int[T] {
   165  	return Int[T]{data: f.data.Neg()}
   166  }
   167  
   168  // Abs returns the absolute value of this value.
   169  func (f Int[T]) Abs() Int[T] {
   170  	return Int[T]{data: f.data.Abs()}
   171  }
   172  
   173  // Cmp returns 1 if i > n, 0 if i == n, and -1 if i < n.
   174  func (f Int[T]) Cmp(n Int[T]) int {
   175  	return f.data.Cmp(n.data)
   176  }
   177  
   178  // GreaterThan returns true if i > n.
   179  func (f Int[T]) GreaterThan(n Int[T]) bool {
   180  	return f.data.GreaterThan(n.data)
   181  }
   182  
   183  // GreaterThanOrEqual returns true if i >= n.
   184  func (f Int[T]) GreaterThanOrEqual(n Int[T]) bool {
   185  	return f.data.GreaterThanOrEqual(n.data)
   186  }
   187  
   188  // Equal returns true if i == n.
   189  func (f Int[T]) Equal(n Int[T]) bool {
   190  	return f.data.Equal(n.data)
   191  }
   192  
   193  // LessThan returns true if i < n.
   194  func (f Int[T]) LessThan(n Int[T]) bool {
   195  	return f.data.LessThan(n.data)
   196  }
   197  
   198  // LessThanOrEqual returns true if i <= n.
   199  func (f Int[T]) LessThanOrEqual(n Int[T]) bool {
   200  	return f.data.LessThanOrEqual(n.data)
   201  }
   202  
   203  // Trunc returns a new value which has everything to the right of the decimal place truncated.
   204  func (f Int[T]) Trunc() Int[T] {
   205  	m := multiplier[T]()
   206  	return Int[T]{data: f.data.Div(m).Mul(m)}
   207  }
   208  
   209  // Ceil returns the value rounded up to the nearest whole number.
   210  func (f Int[T]) Ceil() Int[T] {
   211  	v := f.Trunc()
   212  	if f.GreaterThan(Int[T]{}) && f != v {
   213  		v = v.Add(Multiplier[T]())
   214  	}
   215  	return v
   216  }
   217  
   218  // Round returns the nearest integer, rounding half away from zero.
   219  func (f Int[T]) Round() Int[T] {
   220  	one := Multiplier[T]()
   221  	half := Int[T]{data: one.data.Div(num.Int128From64(2))}
   222  	negHalf := half.Neg()
   223  	value := f.Trunc()
   224  	rem := f.Sub(value)
   225  	if rem.GreaterThanOrEqual(half) {
   226  		value = value.Add(one)
   227  	} else if rem.LessThan(negHalf) {
   228  		value = value.Sub(one)
   229  	}
   230  	return value
   231  }
   232  
   233  // Min returns the minimum of this value or its argument.
   234  func (f Int[T]) Min(value Int[T]) Int[T] {
   235  	if f.data.LessThan(value.data) {
   236  		return f
   237  	}
   238  	return value
   239  }
   240  
   241  // Max returns the maximum of this value or its argument.
   242  func (f Int[T]) Max(value Int[T]) Int[T] {
   243  	if f.data.GreaterThan(value.data) {
   244  		return f
   245  	}
   246  	return value
   247  }
   248  
   249  // Inc returns the value incremented by 1.
   250  func (f Int[T]) Inc() Int[T] {
   251  	return f.Add(Multiplier[T]())
   252  }
   253  
   254  // Dec returns the value decremented by 1.
   255  func (f Int[T]) Dec() Int[T] {
   256  	return f.Sub(Multiplier[T]())
   257  }
   258  
   259  // As returns the equivalent value in the destination type.
   260  func As[T fixed.Dx, TO xmath.Numeric](f Int[T]) TO {
   261  	var n TO
   262  	switch reflect.TypeOf(n).Kind() {
   263  	case reflect.Float32, reflect.Float64:
   264  		var t T
   265  		f64, _ := new(big.Float).SetPrec(128).Quo(f.data.AsBigFloat(),
   266  			new(big.Float).SetPrec(128).SetInt(big.NewInt(t.Multiplier()))).Float64()
   267  		return TO(f64)
   268  	default:
   269  		return TO(f.data.Div(multiplier[T]()).AsInt64())
   270  	}
   271  }
   272  
   273  // CheckedAs is the same as As(), except that it returns an error if the value cannot be represented exactly in the
   274  // requested destination type.
   275  func CheckedAs[T fixed.Dx, TO xmath.Numeric](f Int[T]) (TO, error) {
   276  	var n TO
   277  	switch reflect.TypeOf(n).Kind() {
   278  	case reflect.Float32, reflect.Float64:
   279  		var t T
   280  		f64, _ := new(big.Float).SetPrec(128).Quo(f.data.AsBigFloat(),
   281  			new(big.Float).SetPrec(128).SetInt(big.NewInt(t.Multiplier()))).Float64()
   282  		n = TO(f64)
   283  		if strconv.FormatFloat(float64(n), 'g', -1, reflect.TypeOf(n).Bits()) != f.String() {
   284  			return 0, fixed.ErrDoesNotFitInRequestedType
   285  		}
   286  	default:
   287  		n = TO(f.data.Div(multiplier[T]()).AsInt64())
   288  		if From[T](n) != f {
   289  			return 0, fixed.ErrDoesNotFitInRequestedType
   290  		}
   291  	}
   292  	return n, nil
   293  }
   294  
   295  // CommaWithSign returns the same as Comma(), but prefixes the value with a '+' if it is positive.
   296  func (f Int[T]) CommaWithSign() string {
   297  	if f.data.Sign() >= 0 {
   298  		return "+" + f.Comma()
   299  	}
   300  	return f.Comma()
   301  }
   302  
   303  // Comma returns the same as String(), but with commas for values of 1000 and greater.
   304  func (f Int[T]) Comma() string {
   305  	return txt.CommaFromStringNum(f.String())
   306  }
   307  
   308  // StringWithSign returns the same as String(), but prefixes the value with a '+' if it is positive.
   309  func (f Int[T]) StringWithSign() string {
   310  	if f.data.Sign() >= 0 {
   311  		return "+" + f.String()
   312  	}
   313  	return f.String()
   314  }
   315  
   316  func (f Int[T]) String() string {
   317  	mult := multiplier[T]()
   318  	integer := f.data.Div(mult)
   319  	iStr := integer.String()
   320  	fraction := f.data.Sub(integer.Mul(mult))
   321  	if fraction.IsZero() {
   322  		return iStr
   323  	}
   324  	if fraction.Sign() < 0 {
   325  		fraction = fraction.Neg()
   326  	}
   327  	fStr := fraction.Add(mult).String()
   328  	for i := len(fStr) - 1; i > 0; i-- {
   329  		if fStr[i] != '0' {
   330  			fStr = fStr[1 : i+1]
   331  			break
   332  		}
   333  	}
   334  	var neg string
   335  	if integer.IsZero() && f.data.Sign() < 0 {
   336  		neg = "-"
   337  	} else {
   338  		neg = ""
   339  	}
   340  	return fmt.Sprintf("%s%s.%s", neg, iStr, fStr)
   341  }
   342  
   343  // MarshalText implements the encoding.TextMarshaler interface.
   344  func (f Int[T]) MarshalText() ([]byte, error) {
   345  	return []byte(f.String()), nil
   346  }
   347  
   348  // UnmarshalText implements the encoding.TextUnmarshaler interface.
   349  func (f *Int[T]) UnmarshalText(text []byte) error {
   350  	f1, err := FromString[T](txt.Unquote(string(text)))
   351  	if err != nil {
   352  		return err
   353  	}
   354  	*f = f1
   355  	return nil
   356  }
   357  
   358  // MarshalJSON implements json.Marshaler.
   359  func (f Int[T]) MarshalJSON() ([]byte, error) {
   360  	return []byte(f.String()), nil
   361  }
   362  
   363  // UnmarshalJSON implements json.Unmarshaler.
   364  func (f *Int[T]) UnmarshalJSON(in []byte) error {
   365  	v, err := FromString[T](txt.Unquote(string(in)))
   366  	if err != nil {
   367  		return err
   368  	}
   369  	*f = v
   370  	return nil
   371  }
   372  
   373  // MarshalYAML implements yaml.Marshaler.
   374  func (f Int[T]) MarshalYAML() (any, error) {
   375  	return yaml.Node{
   376  		Kind:  yaml.ScalarNode,
   377  		Value: f.String(),
   378  	}, nil
   379  }
   380  
   381  // UnmarshalYAML implements yaml.Unmarshaler.
   382  func (f *Int[T]) UnmarshalYAML(unmarshal func(any) error) error {
   383  	var str string
   384  	if err := unmarshal(&str); err != nil {
   385  		return err
   386  	}
   387  	v, err := FromString[T](str)
   388  	if err != nil {
   389  		return err
   390  	}
   391  	*f = v
   392  	return nil
   393  }