github.com/richardwilkes/toolbox@v1.121.0/xmath/num/uint128_test.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 num_test
    11  
    12  import (
    13  	"encoding/json"
    14  	"math"
    15  	"math/big"
    16  	"strconv"
    17  	"testing"
    18  
    19  	"github.com/richardwilkes/toolbox/check"
    20  	"gopkg.in/yaml.v3"
    21  
    22  	"github.com/richardwilkes/toolbox/xmath/num"
    23  )
    24  
    25  const (
    26  	maxUint64PlusOneAsStr  = "18446744073709551616"
    27  	maxUint128AsStr        = "340282366920938463463374607431768211455"
    28  	maxUint128PlusOneAsStr = "340282366920938463463374607431768211456"
    29  )
    30  
    31  var uTable = []*uInfo{
    32  	{
    33  		IsUint64:  true,
    34  		IsUint128: true,
    35  	},
    36  	{
    37  		Uint64:    1,
    38  		IsUint64:  true,
    39  		IsUint128: true,
    40  	},
    41  	{
    42  		ValueAsStr: "18446744073712590000",
    43  		IsUint128:  true,
    44  	},
    45  	{
    46  		Uint64:    math.MaxUint64,
    47  		IsUint64:  true,
    48  		IsUint128: true,
    49  	},
    50  	{
    51  		ValueAsStr: maxUint64PlusOneAsStr,
    52  		IsUint128:  true,
    53  	},
    54  	{
    55  		ValueAsStr: maxUint128AsStr,
    56  		IsUint128:  true,
    57  	},
    58  	{
    59  		ValueAsStr:              maxUint128PlusOneAsStr,
    60  		ExpectedConversionAsStr: maxUint128AsStr,
    61  	},
    62  }
    63  
    64  type uInfo struct {
    65  	ValueAsStr              string
    66  	ExpectedConversionAsStr string
    67  	Uint64                  uint64
    68  	IsUint64                bool
    69  	IsUint128               bool
    70  }
    71  
    72  func init() {
    73  	for _, d := range uTable {
    74  		if d.IsUint64 {
    75  			d.ValueAsStr = strconv.FormatUint(d.Uint64, 10)
    76  		}
    77  		if d.ExpectedConversionAsStr == "" {
    78  			d.ExpectedConversionAsStr = d.ValueAsStr
    79  		}
    80  	}
    81  }
    82  
    83  func bigUintFromStr(t *testing.T, one *uInfo, index int) *big.Int {
    84  	t.Helper()
    85  	b, ok := new(big.Int).SetString(one.ValueAsStr, 10)
    86  	check.True(t, ok, indexFmt, index)
    87  	check.Equal(t, one.ValueAsStr, b.String(), indexFmt, index)
    88  	return b
    89  }
    90  
    91  func TestUint128FromUint64(t *testing.T) {
    92  	for i, one := range uTable {
    93  		if one.IsUint64 {
    94  			check.Equal(t, one.ExpectedConversionAsStr, num.Uint128From64(one.Uint64).String(), indexFmt, i)
    95  		}
    96  	}
    97  }
    98  
    99  func TestUint128FromBigInt(t *testing.T) {
   100  	for i, one := range uTable {
   101  		check.Equal(t, one.ExpectedConversionAsStr, num.Uint128FromBigInt(bigUintFromStr(t, one, i)).String(), indexFmt, i)
   102  	}
   103  }
   104  
   105  func TestUint128AsBigInt(t *testing.T) {
   106  	for i, one := range uTable {
   107  		if one.IsUint128 {
   108  			check.Equal(t, one.ValueAsStr, num.Uint128FromBigInt(bigUintFromStr(t, one, i)).AsBigInt().String(), indexFmt, i)
   109  		}
   110  	}
   111  }
   112  
   113  func TestUint128AsUint64(t *testing.T) {
   114  	for i, one := range uTable {
   115  		if one.IsUint64 {
   116  			check.Equal(t, one.Uint64, num.Uint128From64(one.Uint64).AsUint64(), indexFmt, i)
   117  		}
   118  	}
   119  }
   120  
   121  func TestUint128IsUint64(t *testing.T) {
   122  	for i, one := range uTable {
   123  		if one.IsUint128 {
   124  			check.Equal(t, one.IsUint64, num.Uint128FromBigInt(bigUintFromStr(t, one, i)).IsUint64(), indexFmt, i)
   125  		}
   126  	}
   127  }
   128  
   129  func TestUint128Inc(t *testing.T) {
   130  	big1 := new(big.Int).SetInt64(1)
   131  	for i, one := range uTable {
   132  		if one.IsUint128 {
   133  			b := bigUintFromStr(t, one, i)
   134  			v := num.Uint128FromBigInt(b)
   135  			if v == num.MaxUint128 {
   136  				check.Equal(t, num.Uint128{}, v.Inc(), indexFmt, i)
   137  			} else {
   138  				b.Add(b, big1)
   139  				check.Equal(t, b.String(), v.Inc().AsBigInt().String(), indexFmt, i)
   140  			}
   141  		}
   142  	}
   143  }
   144  
   145  func TestUint128Dec(t *testing.T) {
   146  	big1 := new(big.Int).SetInt64(1)
   147  	for i, one := range uTable {
   148  		if one.IsUint128 {
   149  			b := bigUintFromStr(t, one, i)
   150  			v := num.Uint128FromBigInt(b)
   151  			if v.IsZero() {
   152  				check.Equal(t, num.MaxUint128, v.Dec(), indexFmt, i)
   153  			} else {
   154  				b.Sub(b, big1)
   155  				check.Equal(t, b.String(), v.Dec().AsBigInt().String(), indexFmt, i)
   156  			}
   157  		}
   158  	}
   159  }
   160  
   161  func TestUint128Add(t *testing.T) {
   162  	check.Equal(t, num.Uint128From64(0), num.Uint128From64(0).Add(num.Uint128From64(0)))
   163  	check.Equal(t, num.Uint128From64(120), num.Uint128From64(22).Add(num.Uint128From64(98)))
   164  	check.Equal(t, num.Uint128FromComponents(1, 0), num.Uint128FromComponents(0, 0xFFFFFFFFFFFFFFFF).Add(num.Uint128From64(1)))
   165  	check.Equal(t, num.Uint128From64(0), num.MaxUint128.Add(num.Uint128From64(1)))
   166  }
   167  
   168  func TestUint128Sub(t *testing.T) {
   169  	check.Equal(t, num.Uint128From64(0), num.Uint128From64(0).Sub(num.Uint128From64(0)))
   170  	check.Equal(t, num.Uint128FromComponents(0, 0xFFFFFFFFFFFFFFFF), num.Uint128FromComponents(1, 0).Sub(num.Uint128From64(1)))
   171  	check.Equal(t, num.MaxUint128, num.Uint128From64(0).Sub(num.Uint128From64(1)))
   172  }
   173  
   174  func TestUint128Cmp(t *testing.T) {
   175  	check.Equal(t, 0, num.Uint128From64(0).Cmp(num.Uint128From64(0)))
   176  	check.Equal(t, -1, num.Uint128From64(1).Cmp(num.Uint128From64(2)))
   177  	check.Equal(t, -1, num.Uint128From64(22).Cmp(num.Uint128From64(98)))
   178  	check.Equal(t, 1, num.Uint128FromComponents(1, 0).Cmp(num.Uint128From64(1)))
   179  	check.Equal(t, -1, num.Uint128From64(0).Cmp(num.MaxUint128))
   180  	check.Equal(t, 1, num.MaxUint128.Cmp(num.Uint128From64(0)))
   181  	check.Equal(t, 0, num.MaxUint128.Cmp(num.MaxUint128)) //nolint:gocritic // Yes, we meant to compare the same value
   182  }
   183  
   184  func TestUint128GreaterThan(t *testing.T) {
   185  	check.Equal(t, false, num.Uint128From64(0).GreaterThan(num.Uint128From64(0)))
   186  	check.Equal(t, false, num.Uint128From64(1).GreaterThan(num.Uint128From64(2)))
   187  	check.Equal(t, false, num.Uint128From64(22).GreaterThan(num.Uint128From64(98)))
   188  	check.Equal(t, false, num.Uint128From64(0).GreaterThan(num.MaxUint128))
   189  	check.Equal(t, false, num.MaxUint128.GreaterThan(num.MaxUint128))
   190  	check.Equal(t, true, num.Uint128FromComponents(1, 0).GreaterThan(num.Uint128From64(1)))
   191  	check.Equal(t, true, num.MaxUint128.GreaterThan(num.Uint128From64(0)))
   192  }
   193  
   194  func TestUint128GreaterOrEqualTo(t *testing.T) {
   195  	check.Equal(t, true, num.Uint128From64(0).GreaterThanOrEqual(num.Uint128From64(0)))
   196  	check.Equal(t, false, num.Uint128From64(1).GreaterThanOrEqual(num.Uint128From64(2)))
   197  	check.Equal(t, false, num.Uint128From64(22).GreaterThanOrEqual(num.Uint128From64(98)))
   198  	check.Equal(t, false, num.Uint128From64(0).GreaterThanOrEqual(num.Uint128From64(1)))
   199  	check.Equal(t, false, num.Uint128From64(0).GreaterThanOrEqual(num.MaxUint128))
   200  	check.Equal(t, true, num.MaxUint128.GreaterThanOrEqual(num.MaxUint128))
   201  	check.Equal(t, true, num.Uint128FromComponents(1, 0).GreaterThanOrEqual(num.Uint128From64(1)))
   202  	check.Equal(t, true, num.MaxUint128.GreaterThanOrEqual(num.Uint128From64(0)))
   203  }
   204  
   205  func TestUint128LessThan(t *testing.T) {
   206  	check.Equal(t, false, num.Uint128From64(0).LessThan(num.Uint128From64(0)))
   207  	check.Equal(t, true, num.Uint128From64(1).LessThan(num.Uint128From64(2)))
   208  	check.Equal(t, true, num.Uint128From64(22).LessThan(num.Uint128From64(98)))
   209  	check.Equal(t, true, num.Uint128From64(0).LessThan(num.Uint128From64(1)))
   210  	check.Equal(t, true, num.Uint128From64(0).LessThan(num.MaxUint128))
   211  	check.Equal(t, false, num.MaxUint128.LessThan(num.MaxUint128))
   212  	check.Equal(t, false, num.Uint128FromComponents(1, 0).LessThan(num.Uint128From64(1)))
   213  	check.Equal(t, false, num.MaxUint128.LessThan(num.Uint128From64(0)))
   214  }
   215  
   216  func TestUint128LessOrEqualTo(t *testing.T) {
   217  	check.Equal(t, true, num.Uint128From64(0).LessThanOrEqual(num.Uint128From64(0)))
   218  	check.Equal(t, true, num.Uint128From64(1).LessThanOrEqual(num.Uint128From64(2)))
   219  	check.Equal(t, true, num.Uint128From64(22).LessThanOrEqual(num.Uint128From64(98)))
   220  	check.Equal(t, true, num.Uint128From64(0).LessThanOrEqual(num.Uint128From64(1)))
   221  	check.Equal(t, true, num.Uint128From64(0).LessThanOrEqual(num.MaxUint128))
   222  	check.Equal(t, true, num.MaxUint128.LessThanOrEqual(num.MaxUint128))
   223  	check.Equal(t, false, num.Uint128FromComponents(1, 0).LessThanOrEqual(num.Uint128From64(1)))
   224  	check.Equal(t, false, num.MaxUint128.LessThanOrEqual(num.Uint128From64(0)))
   225  }
   226  
   227  func TestUint128Mul(t *testing.T) {
   228  	bigMax64 := new(big.Int).SetInt64(math.MaxInt64)
   229  	check.Equal(t, num.Uint128From64(0), num.Uint128From64(0).Mul(num.Uint128From64(0)))
   230  	check.Equal(t, num.Uint128From64(4), num.Uint128From64(2).Mul(num.Uint128From64(2)))
   231  	check.Equal(t, num.Uint128From64(0), num.Uint128From64(1).Mul(num.Uint128From64(0)))
   232  	check.Equal(t, num.Uint128From64(1176), num.Uint128From64(12).Mul(num.Uint128From64(98)))
   233  	check.Equal(t, num.Uint128FromBigInt(new(big.Int).Mul(bigMax64, bigMax64)), num.Uint128From64(math.MaxInt64).Mul(num.Uint128From64(math.MaxInt64)))
   234  }
   235  
   236  func TestUint128Div(t *testing.T) {
   237  	left, _ := new(big.Int).SetString("170141183460469231731687303715884105728", 10)
   238  	result, _ := new(big.Int).SetString("17014118346046923173168730371588410", 10)
   239  	check.Equal(t, num.Uint128From64(0), num.Uint128From64(1).Div(num.Uint128From64(2)))
   240  	check.Equal(t, num.Uint128From64(3), num.Uint128From64(11).Div(num.Uint128From64(3)))
   241  	check.Equal(t, num.Uint128From64(4), num.Uint128From64(12).Div(num.Uint128From64(3)))
   242  	check.Equal(t, num.Uint128From64(1), num.Uint128From64(10).Div(num.Uint128From64(10)))
   243  	check.Equal(t, num.Uint128From64(1), num.Uint128FromComponents(1, 0).Div(num.Uint128FromComponents(1, 0)))
   244  	check.Equal(t, num.Uint128From64(2), num.Uint128FromComponents(246, 0).Div(num.Uint128FromComponents(123, 0)))
   245  	check.Equal(t, num.Uint128From64(2), num.Uint128FromComponents(246, 0).Div(num.Uint128FromComponents(122, 0)))
   246  	check.Equal(t, num.Uint128FromBigInt(result), num.Uint128FromBigInt(left).Div(num.Uint128From64(10000)))
   247  }
   248  
   249  func TestUint128Json(t *testing.T) {
   250  	for i, one := range uTable {
   251  		if !one.IsUint128 {
   252  			continue
   253  		}
   254  		in := num.Uint128FromStringNoCheck(one.ValueAsStr)
   255  		data, err := json.Marshal(in)
   256  		check.NoError(t, err, indexFmt, i)
   257  		var out num.Uint128
   258  		check.NoError(t, json.Unmarshal(data, &out), indexFmt, i)
   259  		check.Equal(t, in, out, indexFmt, i)
   260  	}
   261  }
   262  
   263  func TestUint128Yaml(t *testing.T) {
   264  	for i, one := range uTable {
   265  		if !one.IsUint128 {
   266  			continue
   267  		}
   268  		in := num.Uint128FromStringNoCheck(one.ValueAsStr)
   269  		data, err := yaml.Marshal(in)
   270  		check.NoError(t, err, indexFmt, i)
   271  		var out num.Uint128
   272  		check.NoError(t, yaml.Unmarshal(data, &out), indexFmt, i)
   273  		check.Equal(t, in, out, indexFmt, i)
   274  	}
   275  }