github.com/mitranim/gg@v0.1.17/math_test.go (about)

     1  package gg_test
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"testing"
     7  
     8  	"github.com/mitranim/gg"
     9  	"github.com/mitranim/gg/gtest"
    10  )
    11  
    12  type (
    13  	Byte       uint8
    14  	Uint16     uint16
    15  	Uint32     uint32
    16  	Uint64     uint64
    17  	Uint       uint
    18  	Int8       int8
    19  	Int16      int16
    20  	Int32      int32
    21  	Int64      int64
    22  	Int        int
    23  	Float32    float32
    24  	Float64    float64
    25  	Complex64  complex64
    26  	Complex128 complex128
    27  )
    28  
    29  /*
    30  TODO consider making public. This should really be provided by "math" instead.
    31  Also TODO better names.
    32  */
    33  const (
    34  	MinSafeFloat32 = -(2 << 24)
    35  	MaxSafeFloat32 = 2 << 24
    36  	MinSafeFloat64 = -(2 << 53)
    37  	MaxSafeFloat64 = 2 << 53
    38  )
    39  
    40  func TestIsFin(t *testing.T) {
    41  	defer gtest.Catch(t)
    42  
    43  	gtest.False(gg.IsFin(math.NaN()))
    44  	gtest.False(gg.IsFin(math.Inf(1)))
    45  	gtest.False(gg.IsFin(math.Inf(-1)))
    46  	gtest.True(gg.IsFin(0.0))
    47  }
    48  
    49  func TestIsDivisibleBy(t *testing.T) {
    50  	defer gtest.Catch(t)
    51  
    52  	gtest.False(gg.IsDivisibleBy(0, 0))
    53  	gtest.False(gg.IsDivisibleBy(1, 0))
    54  	gtest.False(gg.IsDivisibleBy(2, 0))
    55  	gtest.False(gg.IsDivisibleBy(-1, 0))
    56  	gtest.False(gg.IsDivisibleBy(-2, 0))
    57  
    58  	gtest.True(gg.IsDivisibleBy(0, 1))
    59  	gtest.True(gg.IsDivisibleBy(0, 2))
    60  	gtest.True(gg.IsDivisibleBy(0, -1))
    61  	gtest.True(gg.IsDivisibleBy(0, -2))
    62  
    63  	gtest.True(gg.IsDivisibleBy(1, 1))
    64  	gtest.True(gg.IsDivisibleBy(2, 1))
    65  	gtest.True(gg.IsDivisibleBy(3, 1))
    66  	gtest.True(gg.IsDivisibleBy(-1, 1))
    67  	gtest.True(gg.IsDivisibleBy(-2, 1))
    68  	gtest.True(gg.IsDivisibleBy(-3, 1))
    69  
    70  	gtest.True(gg.IsDivisibleBy(1, -1))
    71  	gtest.True(gg.IsDivisibleBy(2, -1))
    72  	gtest.True(gg.IsDivisibleBy(3, -1))
    73  	gtest.True(gg.IsDivisibleBy(-1, -1))
    74  	gtest.True(gg.IsDivisibleBy(-2, -1))
    75  	gtest.True(gg.IsDivisibleBy(-3, -1))
    76  
    77  	gtest.False(gg.IsDivisibleBy(1, -2))
    78  	gtest.False(gg.IsDivisibleBy(1, -3))
    79  	gtest.False(gg.IsDivisibleBy(1, 2))
    80  	gtest.False(gg.IsDivisibleBy(1, 3))
    81  
    82  	gtest.False(gg.IsDivisibleBy(4, 0))
    83  	gtest.True(gg.IsDivisibleBy(4, 1))
    84  	gtest.True(gg.IsDivisibleBy(4, 2))
    85  	gtest.False(gg.IsDivisibleBy(4, 3))
    86  	gtest.True(gg.IsDivisibleBy(4, 4))
    87  	gtest.False(gg.IsDivisibleBy(4, 5))
    88  	gtest.False(gg.IsDivisibleBy(4, 6))
    89  	gtest.False(gg.IsDivisibleBy(4, 7))
    90  	gtest.False(gg.IsDivisibleBy(4, 8))
    91  	gtest.False(gg.IsDivisibleBy(4, 9))
    92  	gtest.False(gg.IsDivisibleBy(4, 10))
    93  	gtest.False(gg.IsDivisibleBy(4, 11))
    94  	gtest.False(gg.IsDivisibleBy(4, 12))
    95  	gtest.False(gg.IsDivisibleBy(4, 13))
    96  	gtest.False(gg.IsDivisibleBy(4, 14))
    97  	gtest.False(gg.IsDivisibleBy(4, 15))
    98  	gtest.False(gg.IsDivisibleBy(4, 16))
    99  
   100  	gtest.False(gg.IsDivisibleBy(-4, 0))
   101  	gtest.True(gg.IsDivisibleBy(-4, 1))
   102  	gtest.True(gg.IsDivisibleBy(-4, 2))
   103  	gtest.False(gg.IsDivisibleBy(-4, 3))
   104  	gtest.True(gg.IsDivisibleBy(-4, 4))
   105  	gtest.False(gg.IsDivisibleBy(-4, 5))
   106  	gtest.False(gg.IsDivisibleBy(-4, 6))
   107  	gtest.False(gg.IsDivisibleBy(-4, 7))
   108  	gtest.False(gg.IsDivisibleBy(-4, 8))
   109  	gtest.False(gg.IsDivisibleBy(-4, 9))
   110  	gtest.False(gg.IsDivisibleBy(-4, 10))
   111  	gtest.False(gg.IsDivisibleBy(-4, 11))
   112  	gtest.False(gg.IsDivisibleBy(-4, 12))
   113  	gtest.False(gg.IsDivisibleBy(-4, 13))
   114  	gtest.False(gg.IsDivisibleBy(-4, 14))
   115  	gtest.False(gg.IsDivisibleBy(-4, 15))
   116  	gtest.False(gg.IsDivisibleBy(-4, 16))
   117  
   118  	gtest.True(gg.IsDivisibleBy(4, -1))
   119  	gtest.True(gg.IsDivisibleBy(4, -2))
   120  	gtest.False(gg.IsDivisibleBy(4, -3))
   121  	gtest.True(gg.IsDivisibleBy(4, -4))
   122  	gtest.False(gg.IsDivisibleBy(4, -5))
   123  	gtest.False(gg.IsDivisibleBy(4, -6))
   124  	gtest.False(gg.IsDivisibleBy(4, -7))
   125  	gtest.False(gg.IsDivisibleBy(4, -8))
   126  	gtest.False(gg.IsDivisibleBy(4, -9))
   127  	gtest.False(gg.IsDivisibleBy(4, -10))
   128  	gtest.False(gg.IsDivisibleBy(4, -11))
   129  	gtest.False(gg.IsDivisibleBy(4, -12))
   130  	gtest.False(gg.IsDivisibleBy(4, -13))
   131  	gtest.False(gg.IsDivisibleBy(4, -14))
   132  	gtest.False(gg.IsDivisibleBy(4, -15))
   133  	gtest.False(gg.IsDivisibleBy(4, -16))
   134  
   135  	gtest.True(gg.IsDivisibleBy(-4, -1))
   136  	gtest.True(gg.IsDivisibleBy(-4, -2))
   137  	gtest.False(gg.IsDivisibleBy(-4, -3))
   138  	gtest.True(gg.IsDivisibleBy(-4, -4))
   139  	gtest.False(gg.IsDivisibleBy(-4, -5))
   140  	gtest.False(gg.IsDivisibleBy(-4, -6))
   141  	gtest.False(gg.IsDivisibleBy(-4, -7))
   142  	gtest.False(gg.IsDivisibleBy(-4, -8))
   143  	gtest.False(gg.IsDivisibleBy(-4, -9))
   144  	gtest.False(gg.IsDivisibleBy(-4, -10))
   145  	gtest.False(gg.IsDivisibleBy(-4, -11))
   146  	gtest.False(gg.IsDivisibleBy(-4, -12))
   147  	gtest.False(gg.IsDivisibleBy(-4, -13))
   148  	gtest.False(gg.IsDivisibleBy(-4, -14))
   149  	gtest.False(gg.IsDivisibleBy(-4, -15))
   150  	gtest.False(gg.IsDivisibleBy(-4, -16))
   151  }
   152  
   153  func TestInc(t *testing.T) {
   154  	defer gtest.Catch(t)
   155  
   156  	gtest.Eq(gg.Inc(-3), -2)
   157  	gtest.Eq(gg.Inc(-2), -1)
   158  	gtest.Eq(gg.Inc(-1), 0)
   159  	gtest.Eq(gg.Inc(0), 1)
   160  	gtest.Eq(gg.Inc(1), 2)
   161  	gtest.Eq(gg.Inc(2), 3)
   162  	gtest.Eq(gg.Inc(3), 4)
   163  
   164  	gtest.PanicStr(
   165  		`addition overflow for uint8: 255 + 1 = 0`,
   166  		func() { gg.Inc[uint8](math.MaxUint8) },
   167  	)
   168  
   169  	gtest.PanicStr(
   170  		`addition overflow for int8: 127 + 1 = -128`,
   171  		func() { gg.Inc[int8](math.MaxInt8) },
   172  	)
   173  
   174  	/**
   175  	TODO restore support for floats.
   176  
   177  	gtest.Eq(gg.Inc(-3.5), -2.5)
   178  	gtest.Eq(gg.Inc(-2.5), -1.5)
   179  	gtest.Eq(gg.Inc(-1.5), -0.5)
   180  	gtest.Eq(gg.Inc(-0.5), 0.5)
   181  	gtest.Eq(gg.Inc(0.0), 1)
   182  	gtest.Eq(gg.Inc(0.5), 1.5)
   183  	gtest.Eq(gg.Inc(1.5), 2.5)
   184  	gtest.Eq(gg.Inc(2.5), 3.5)
   185  	gtest.Eq(gg.Inc(3.5), 4.5)
   186  
   187  	gtest.PanicStr(...)
   188  	*/
   189  }
   190  
   191  func TestDec(t *testing.T) {
   192  	defer gtest.Catch(t)
   193  
   194  	gtest.Eq(gg.Dec(-3), -4)
   195  	gtest.Eq(gg.Dec(-2), -3)
   196  	gtest.Eq(gg.Dec(-1), -2)
   197  	gtest.Eq(gg.Dec(0), -1)
   198  	gtest.Eq(gg.Dec(1), 0)
   199  	gtest.Eq(gg.Dec(2), 1)
   200  	gtest.Eq(gg.Dec(3), 2)
   201  
   202  	gtest.PanicStr(
   203  		`subtraction overflow for uint8: 0 - 1 = 255`,
   204  		func() { gg.Dec[uint8](0) },
   205  	)
   206  
   207  	gtest.PanicStr(
   208  		`subtraction overflow for int8: -128 - 1 = 127`,
   209  		func() { gg.Dec[int8](math.MinInt8) },
   210  	)
   211  
   212  	/**
   213  	TODO restore support for floats.
   214  
   215  	gtest.Eq(gg.Dec(-3.5), -4.5)
   216  	gtest.Eq(gg.Dec(-2.5), -3.5)
   217  	gtest.Eq(gg.Dec(-1.5), -2.5)
   218  	gtest.Eq(gg.Dec(-0.5), -1.5)
   219  	gtest.Eq(gg.Dec(0.0), -1.0)
   220  	gtest.Eq(gg.Dec(0.5), -0.5)
   221  	gtest.Eq(gg.Dec(1.5), 0.5)
   222  	gtest.Eq(gg.Dec(2.5), 1.5)
   223  	gtest.Eq(gg.Dec(3.5), 2.5)
   224  
   225  	gtest.PanicStr(...)
   226  	*/
   227  }
   228  
   229  func TestPow(t *testing.T) {
   230  	defer gtest.Catch(t)
   231  
   232  	testPowInt(gg.Pow[int, int])
   233  	testPowFloat(gg.Pow[float64, float64])
   234  
   235  	gtest.PanicStr(
   236  		`unable to safely convert float64 1162261467 to uint8 219`,
   237  		func() { gg.Pow[uint8](3, 19) },
   238  	)
   239  
   240  	gtest.PanicStr(
   241  		`unable to safely convert float64 1162261467 to int8 -37`,
   242  		func() { gg.Pow[int8](3, 19) },
   243  	)
   244  }
   245  
   246  /*
   247  TODO test fractional and negative powers.
   248  We're simply calling `math.Pow`, but we do need a sanity check.
   249  */
   250  func testPowInt(fun func(int, int) int) {
   251  	src := []int{-12, -1, 0, 1, 12}
   252  	for _, val := range src {
   253  		testPow0(val, fun)
   254  		testPow1(val, fun)
   255  		testPow2(val, fun)
   256  		testPow3(val, fun)
   257  	}
   258  }
   259  
   260  func testPowFloat(fun func(float64, float64) float64) {
   261  	src := []float64{-12.23, -1.2, -1.0, 0.0, 1.0, 1.2, 12.23}
   262  	for _, val := range src {
   263  		testPow0(val, fun)
   264  		testPow1(val, fun)
   265  		testPow2(val, fun)
   266  		testPow3(val, fun)
   267  	}
   268  }
   269  
   270  func testPow0[A gg.Num](src A, fun func(A, A) A) { gtest.Eq(fun(src, 0), 1) }
   271  func testPow1[A gg.Num](src A, fun func(A, A) A) { gtest.Eq(fun(src, 1), src) }
   272  func testPow2[A gg.Num](src A, fun func(A, A) A) { gtest.Eq(fun(src, 2), src*src) }
   273  func testPow3[A gg.Num](src A, fun func(A, A) A) { gtest.Eq(fun(src, 3), src*src*src) }
   274  
   275  func BenchmarkPow(b *testing.B) {
   276  	defer gtest.Catch(b)
   277  
   278  	for ind := 0; ind < b.N; ind++ {
   279  		gg.Pow(79, 7)
   280  	}
   281  }
   282  
   283  func TestPowUncheck(t *testing.T) {
   284  	defer gtest.Catch(t)
   285  
   286  	testPowInt(gg.PowUncheck[int, int])
   287  	testPowFloat(gg.PowUncheck[float64, float64])
   288  }
   289  
   290  func BenchmarkPowUncheck(b *testing.B) {
   291  	defer gtest.Catch(b)
   292  
   293  	for ind := 0; ind < b.N; ind++ {
   294  		gg.PowUncheck(79, 7)
   295  	}
   296  }
   297  
   298  func TestFac(t *testing.T) {
   299  	defer gtest.Catch(t)
   300  
   301  	test := func(src, exp uint64) { gtest.Eq(gg.Fac(src), exp) }
   302  
   303  	test(0, 1)
   304  	test(1, 1)
   305  	test(2, 2)
   306  	test(3, 6)
   307  	test(4, 24)
   308  	test(5, 120)
   309  	test(6, 720)
   310  	test(7, 5040)
   311  	test(8, 40320)
   312  	test(9, 362880)
   313  	test(10, 3628800)
   314  	test(11, 39916800)
   315  	test(12, 479001600)
   316  	test(13, 6227020800)
   317  	test(14, 87178291200)
   318  	test(15, 1307674368000)
   319  	test(16, 20922789888000)
   320  	test(17, 355687428096000)
   321  	test(18, 6402373705728000)
   322  	test(19, 121645100408832000)
   323  	test(20, 2432902008176640000)
   324  
   325  	gtest.PanicStr(
   326  		`unable to safely convert float64 720 to uint8 208`,
   327  		func() { gg.Fac[uint8](6) },
   328  	)
   329  }
   330  
   331  func BenchmarkFac(b *testing.B) {
   332  	defer gtest.Catch(b)
   333  
   334  	for ind := 0; ind < b.N; ind++ {
   335  		gg.Fac[uint64](19)
   336  	}
   337  }
   338  
   339  func TestFacUncheck(t *testing.T) {
   340  	defer gtest.Catch(t)
   341  
   342  	test := func(src, exp uint64) { gtest.Eq(gg.FacUncheck(src), exp) }
   343  
   344  	test(0, 1)
   345  	test(1, 1)
   346  	test(2, 2)
   347  	test(3, 6)
   348  	test(4, 24)
   349  	test(5, 120)
   350  	test(6, 720)
   351  	test(7, 5040)
   352  	test(8, 40320)
   353  	test(9, 362880)
   354  	test(10, 3628800)
   355  	test(11, 39916800)
   356  	test(12, 479001600)
   357  	test(13, 6227020800)
   358  	test(14, 87178291200)
   359  	test(15, 1307674368000)
   360  	test(16, 20922789888000)
   361  	test(17, 355687428096000)
   362  	test(18, 6402373705728000)
   363  	test(19, 121645100408832000)
   364  	test(20, 2432902008176640000)
   365  
   366  	gtest.Eq(gg.FacUncheck[uint8](6), 208, `expecting overflow`)
   367  }
   368  
   369  func BenchmarkFacUncheck(b *testing.B) {
   370  	defer gtest.Catch(b)
   371  
   372  	for ind := 0; ind < b.N; ind++ {
   373  		gg.FacUncheck[uint64](19)
   374  	}
   375  }
   376  
   377  func TestNumConv_width_decrease_within_bounds(t *testing.T) {
   378  	defer gtest.Catch(t)
   379  
   380  	gtest.Eq(gg.NumConv[uint8](int16(0)), 0)
   381  	gtest.Eq(gg.NumConv[uint8](int16(128)), 128)
   382  	gtest.Eq(gg.NumConv[uint8](int16(255)), 255)
   383  
   384  	gtest.Eq(gg.NumConv[uint8](float32(0)), 0)
   385  	gtest.Eq(gg.NumConv[uint8](float32(128)), 128)
   386  	gtest.Eq(gg.NumConv[uint8](float32(255)), 255)
   387  
   388  	gtest.Eq(gg.NumConv[uint8](float64(0)), 0)
   389  	gtest.Eq(gg.NumConv[uint8](float64(128)), 128)
   390  	gtest.Eq(gg.NumConv[uint8](float64(255)), 255)
   391  
   392  	gtest.Eq(gg.NumConv[int8](int16(0)), 0)
   393  	gtest.Eq(gg.NumConv[int8](int16(127)), 127)
   394  	gtest.Eq(gg.NumConv[int8](int16(-128)), -128)
   395  
   396  	gtest.Eq(gg.NumConv[int8](float32(0)), 0)
   397  	gtest.Eq(gg.NumConv[int8](float32(127)), 127)
   398  	gtest.Eq(gg.NumConv[int8](float32(-128)), -128)
   399  
   400  	gtest.Eq(gg.NumConv[int8](float64(0)), 0)
   401  	gtest.Eq(gg.NumConv[int8](float64(127)), 127)
   402  	gtest.Eq(gg.NumConv[int8](float64(-128)), -128)
   403  
   404  	gtest.Eq(gg.NumConv[float32](float64(0)), 0)
   405  	gtest.Eq(gg.NumConv[float32](float64(math.MaxFloat32)), math.MaxFloat32)
   406  	gtest.Eq(gg.NumConv[float32](float64(-math.MaxFloat32)), -math.MaxFloat32)
   407  }
   408  
   409  func TestNumConv_width_decrease_sign_mismatch(t *testing.T) {
   410  	defer gtest.Catch(t)
   411  
   412  	gtest.PanicStr(
   413  		`unable to safely convert int16 -1 to uint8 255`,
   414  		func() { gg.NumConv[uint8](int16(-1)) },
   415  	)
   416  
   417  	gtest.PanicStr(
   418  		`unable to safely convert int16 -128 to uint8 128`,
   419  		func() { gg.NumConv[uint8](int16(-128)) },
   420  	)
   421  
   422  	gtest.PanicStr(
   423  		`unable to safely convert float32 -128 to uint8 128`,
   424  		func() { gg.NumConv[uint8](float32(-128)) },
   425  	)
   426  }
   427  
   428  func TestNumConv_width_decrease_out_of_bounds(t *testing.T) {
   429  	defer gtest.Catch(t)
   430  
   431  	gtest.PanicStr(
   432  		`unable to safely convert int16 256 to uint8 0`,
   433  		func() { gg.NumConv[uint8](int16(256)) },
   434  	)
   435  
   436  	gtest.PanicStr(
   437  		`unable to safely convert float32 256 to uint8 0`,
   438  		func() { gg.NumConv[uint8](float32(256)) },
   439  	)
   440  
   441  	gtest.PanicStr(
   442  		`unable to safely convert int16 128 to int8 -128`,
   443  		func() { gg.NumConv[int8](int16(128)) },
   444  	)
   445  
   446  	gtest.PanicStr(
   447  		`unable to safely convert float32 -170141173319264430000000000000000000000 to int16 0`,
   448  		func() { gg.NumConv[int16](float32(-math.MaxFloat32 / 2)) },
   449  	)
   450  
   451  	gtest.PanicStr(
   452  		`unable to safely convert float32 170141173319264430000000000000000000000 to int16 0`,
   453  		func() { gg.NumConv[int16](float32(math.MaxFloat32 / 2)) },
   454  	)
   455  
   456  	gtest.PanicStr(
   457  		`unable to safely convert float64 680564693277057700000000000000000000000 to float32 +Inf`,
   458  		func() { gg.NumConv[float32](float64(math.MaxFloat32 * 2)) },
   459  	)
   460  
   461  	gtest.PanicStr(
   462  		`unable to safely convert float64 -680564693277057700000000000000000000000 to float32 -Inf`,
   463  		func() { gg.NumConv[float32](float64(-math.MaxFloat32 * 2)) },
   464  	)
   465  
   466  	gtest.PanicStr(
   467  		`unable to safely convert float32 128 to int8 -128`,
   468  		func() { gg.NumConv[int8](float32(128)) },
   469  	)
   470  
   471  	gtest.PanicStr(
   472  		`unable to safely convert float32 NaN to int16 0`,
   473  		func() { gg.NumConv[int16](float32(math.NaN())) },
   474  	)
   475  
   476  	gtest.PanicStr(
   477  		`unable to safely convert float32 +Inf to int16 0`,
   478  		func() { gg.NumConv[int16](float32(math.Inf(1))) },
   479  	)
   480  
   481  	gtest.PanicStr(
   482  		`unable to safely convert float32 -Inf to int16 0`,
   483  		func() { gg.NumConv[int16](float32(math.Inf(-1))) },
   484  	)
   485  }
   486  
   487  func TestNumConv_width_decrease_imprecision(t *testing.T) {
   488  	defer gtest.Catch(t)
   489  
   490  	gtest.PanicStr(
   491  		`unable to safely convert float32 10.5 to int16 10`,
   492  		func() { gg.NumConv[int16](float32(10.5)) },
   493  	)
   494  
   495  	gtest.PanicStr(
   496  		`unable to safely convert float32 -10.5 to int16 -10`,
   497  		func() { gg.NumConv[int16](float32(-10.5)) },
   498  	)
   499  }
   500  
   501  func TestNumConv_width_match_within_bounds(t *testing.T) {
   502  	defer gtest.Catch(t)
   503  
   504  	gtest.Eq(gg.NumConv[uint8](int8(0)), 0)
   505  	gtest.Eq(gg.NumConv[int8](uint8(0)), 0)
   506  
   507  	gtest.Eq(gg.NumConv[uint8](int8(127)), 127)
   508  	gtest.Eq(gg.NumConv[int8](uint8(127)), 127)
   509  
   510  	gtest.Eq(gg.NumConv[int](uint(math.MaxInt)), math.MaxInt)
   511  	gtest.Eq(gg.NumConv[uint](int(math.MaxInt)), math.MaxInt)
   512  
   513  	gtest.Eq(gg.NumConv[int32](float32(0)), 0)
   514  	gtest.Eq(gg.NumConv[int32](float32(0)), 0)
   515  
   516  	gtest.Eq(gg.NumConv[int32](float32(MinSafeFloat32)), MinSafeFloat32)
   517  	gtest.Eq(gg.NumConv[int32](float32(MaxSafeFloat32)), MaxSafeFloat32)
   518  
   519  	gtest.Eq(gg.NumConv[float32](int32(MinSafeFloat32)), MinSafeFloat32)
   520  	gtest.Eq(gg.NumConv[float32](int32(MaxSafeFloat32)), MaxSafeFloat32)
   521  
   522  	/**
   523  	This is technically out of the safe range, but the value seems to match by
   524  	accident. TODO: consider forbidding conversion from integers to floats
   525  	when the output is outside of its safe range.
   526  	*/
   527  	gtest.Eq(gg.NumConv[float32](int32(math.MinInt32)), math.MinInt32)
   528  	gtest.Eq(int32(gg.NumConv[float32](int32(math.MinInt32))), int32(math.MinInt32))
   529  
   530  	// Same as above.
   531  	gtest.Eq(gg.NumConv[float64](int64(math.MinInt64)), math.MinInt64)
   532  	gtest.Eq(int64(gg.NumConv[float64](int64(math.MinInt64))), int64(math.MinInt64))
   533  }
   534  
   535  func TestNumConv_width_match_sign_mismatch(t *testing.T) {
   536  	defer gtest.Catch(t)
   537  
   538  	gtest.PanicStr(
   539  		`unable to safely convert int8 -1 to uint8 255`,
   540  		func() { gg.NumConv[uint8](int8(-1)) },
   541  	)
   542  
   543  	gtest.PanicStr(
   544  		`unable to safely convert int8 -128 to uint8 128`,
   545  		func() { gg.NumConv[uint8](int8(-128)) },
   546  	)
   547  
   548  	gtest.PanicStr(
   549  		`unable to safely convert uint8 128 to int8 -128`,
   550  		func() { gg.NumConv[int8](uint8(128)) },
   551  	)
   552  
   553  	gtest.PanicStr(
   554  		`unable to safely convert uint8 255 to int8 -1`,
   555  		func() { gg.NumConv[int8](uint8(255)) },
   556  	)
   557  
   558  	gtest.PanicStr(
   559  		`unable to safely convert float32 -1 to uint32 4294967295`,
   560  		func() { gg.NumConv[uint32](float32(-1)) },
   561  	)
   562  
   563  	gtest.PanicStr(
   564  		`unable to safely convert float64 -1 to uint64 18446744073709551615`,
   565  		func() { gg.NumConv[uint64](float64(-1)) },
   566  	)
   567  }
   568  
   569  func TestNumConv_width_match_out_of_bounds(t *testing.T) {
   570  	defer gtest.Catch(t)
   571  
   572  	gtest.PanicStr(
   573  		`unable to safely convert uint 9223372036854775808 to int -9223372036854775808`,
   574  		func() { gg.NumConv[int](uint(math.MaxInt + 1)) },
   575  	)
   576  
   577  	gtest.PanicStr(
   578  		`unable to safely convert float32 -170141173319264430000000000000000000000 to int32 -2147483648`,
   579  		func() { gg.NumConv[int32](float32(-math.MaxFloat32 / 2)) },
   580  	)
   581  
   582  	gtest.PanicStr(
   583  		`unable to safely convert float32 170141173319264430000000000000000000000 to int32 -2147483648`,
   584  		func() { gg.NumConv[int32](float32(math.MaxFloat32 / 2)) },
   585  	)
   586  
   587  	gtest.PanicStr(
   588  		`unable to safely convert float32 NaN to int32 -2147483648`,
   589  		func() { gg.NumConv[int32](float32(math.NaN())) },
   590  	)
   591  
   592  	gtest.PanicStr(
   593  		`unable to safely convert float32 +Inf to int32 -2147483648`,
   594  		func() { gg.NumConv[int32](float32(math.Inf(1))) },
   595  	)
   596  
   597  	gtest.PanicStr(
   598  		`unable to safely convert float32 -Inf to int32 -2147483648`,
   599  		func() { gg.NumConv[int32](float32(math.Inf(-1))) },
   600  	)
   601  }
   602  
   603  func TestNumConv_width_match_imprecision(t *testing.T) {
   604  	defer gtest.Catch(t)
   605  
   606  	gtest.PanicStr(
   607  		`unable to safely convert float32 10.5 to int32 10`,
   608  		func() { gg.NumConv[int32](float32(10.5)) },
   609  	)
   610  
   611  	gtest.PanicStr(
   612  		`unable to safely convert float32 -10.5 to int32 -10`,
   613  		func() { gg.NumConv[int32](float32(-10.5)) },
   614  	)
   615  
   616  	gtest.PanicStr(
   617  		`unable to safely convert int32 33554433 to float32 33554432`,
   618  		func() { gg.NumConv[float32](int32(MaxSafeFloat32 + 1)) },
   619  	)
   620  
   621  	gtest.PanicStr(
   622  		`unable to safely convert int32 -33554433 to float32 -33554432`,
   623  		func() { gg.NumConv[float32](int32(MinSafeFloat32 - 1)) },
   624  	)
   625  
   626  	gtest.PanicStr(
   627  		`unable to safely convert int32 2147483647 to float32 2147483648`,
   628  		func() { gg.NumConv[float32](int32(math.MaxInt32)) },
   629  	)
   630  
   631  	gtest.PanicStr(
   632  		`unable to safely convert int64 18014398509481985 to float64 18014398509481984`,
   633  		func() { gg.NumConv[float64](int64(MaxSafeFloat64 + 1)) },
   634  	)
   635  
   636  	gtest.PanicStr(
   637  		`unable to safely convert int64 -18014398509481985 to float64 -18014398509481984`,
   638  		func() { gg.NumConv[float64](int64(MinSafeFloat64 - 1)) },
   639  	)
   640  
   641  	gtest.PanicStr(
   642  		`unable to safely convert int64 9223372036854775807 to float64 9223372036854776000`,
   643  		func() { gg.NumConv[float64](int64(math.MaxInt64)) },
   644  	)
   645  }
   646  
   647  func TestNumConv_width_increase_within_bounds(t *testing.T) {
   648  	defer gtest.Catch(t)
   649  
   650  	gtest.Eq(gg.NumConv[uint16](uint8(0)), 0)
   651  	gtest.Eq(gg.NumConv[uint16](uint8(128)), 128)
   652  	gtest.Eq(gg.NumConv[uint16](uint8(255)), 255)
   653  
   654  	gtest.Eq(gg.NumConv[int16](uint8(0)), 0)
   655  	gtest.Eq(gg.NumConv[int16](uint8(128)), 128)
   656  	gtest.Eq(gg.NumConv[int16](uint8(255)), 255)
   657  
   658  	gtest.Eq(gg.NumConv[int16](int8(0)), 0)
   659  	gtest.Eq(gg.NumConv[int16](int8(127)), 127)
   660  	gtest.Eq(gg.NumConv[int16](int8(-128)), -128)
   661  
   662  	gtest.Eq(gg.NumConv[float64](int32(math.MaxInt32)), math.MaxInt32)
   663  	gtest.Eq(gg.NumConv[int64](float32(MaxSafeFloat32)), MaxSafeFloat32)
   664  
   665  	gtest.Eq(gg.NumConv[float64](int32(math.MinInt32)), math.MinInt32)
   666  	gtest.Eq(gg.NumConv[int64](float32(MinSafeFloat32)), MinSafeFloat32)
   667  
   668  	gtest.Eq(gg.NumConv[float64](float32(0)), 0)
   669  	gtest.Eq(gg.NumConv[float64](float32(math.MaxFloat32)), math.MaxFloat32)
   670  	gtest.Eq(gg.NumConv[float64](float32(-math.MaxFloat32)), -math.MaxFloat32)
   671  }
   672  
   673  func TestNumConv_width_increase_sign_mismatch(t *testing.T) {
   674  	defer gtest.Catch(t)
   675  
   676  	gtest.PanicStr(
   677  		`unable to safely convert int8 -1 to uint16 65535`,
   678  		func() { gg.NumConv[uint16](int8(-1)) },
   679  	)
   680  
   681  	gtest.PanicStr(
   682  		`unable to safely convert int8 -128 to uint16 65408`,
   683  		func() { gg.NumConv[uint16](int8(-128)) },
   684  	)
   685  
   686  	gtest.PanicStr(
   687  		`unable to safely convert float32 -1 to uint64 18446744073709551615`,
   688  		func() { gg.NumConv[uint64](float32(-1)) },
   689  	)
   690  }
   691  
   692  func TestNumConv_width_increase_out_of_bounds(t *testing.T) {
   693  	defer gtest.Catch(t)
   694  
   695  	gtest.PanicStr(
   696  		`unable to safely convert float32 -170141173319264430000000000000000000000 to int64 -9223372036854775808`,
   697  		func() { gg.NumConv[int64](float32(-math.MaxFloat32 / 2)) },
   698  	)
   699  
   700  	gtest.PanicStr(
   701  		`unable to safely convert float32 170141173319264430000000000000000000000 to int64 -9223372036854775808`,
   702  		func() { gg.NumConv[int64](float32(math.MaxFloat32 / 2)) },
   703  	)
   704  }
   705  
   706  func TestNumConv_width_increase_imprecision(t *testing.T) {
   707  	defer gtest.Catch(t)
   708  
   709  	gtest.PanicStr(
   710  		`unable to safely convert float32 10.5 to int64 10`,
   711  		func() { gg.NumConv[int64](float32(10.5)) },
   712  	)
   713  
   714  	gtest.PanicStr(
   715  		`unable to safely convert float32 -10.5 to int64 -10`,
   716  		func() { gg.NumConv[int64](float32(-10.5)) },
   717  	)
   718  
   719  	gtest.PanicStr(
   720  		`unable to safely convert float32 NaN to int64 -9223372036854775808`,
   721  		func() { gg.NumConv[int64](float32(math.NaN())) },
   722  	)
   723  
   724  	gtest.PanicStr(
   725  		`unable to safely convert float32 +Inf to int64 -9223372036854775808`,
   726  		func() { gg.NumConv[int64](float32(math.Inf(1))) },
   727  	)
   728  
   729  	gtest.PanicStr(
   730  		`unable to safely convert float32 -Inf to int64 -9223372036854775808`,
   731  		func() { gg.NumConv[int64](float32(math.Inf(-1))) },
   732  	)
   733  }
   734  
   735  /*
   736  This behavior is an artefact of the implementation.
   737  We might want to fix it, but without incurring overheads.
   738  */
   739  func TestNumConv_NaN(t *testing.T) {
   740  	defer gtest.Catch(t)
   741  
   742  	gtest.PanicStr(
   743  		`unable to safely convert float64 NaN to float32 NaN`,
   744  		func() { gg.NumConv[float32](math.NaN()) },
   745  	)
   746  
   747  	gtest.PanicStr(
   748  		`unable to safely convert float64 NaN to gg_test.Float32 NaN`,
   749  		func() { gg.NumConv[Float32](math.NaN()) },
   750  	)
   751  
   752  	gtest.PanicStr(
   753  		`unable to safely convert float64 NaN to float64 NaN`,
   754  		func() { gg.NumConv[float64](math.NaN()) },
   755  	)
   756  
   757  	gtest.PanicStr(
   758  		`unable to safely convert float64 NaN to gg_test.Float64 NaN`,
   759  		func() { gg.NumConv[Float64](math.NaN()) },
   760  	)
   761  }
   762  
   763  //go:noinline
   764  func makeInt32() int32 { return MaxSafeFloat32 }
   765  
   766  //go:noinline
   767  func makeFloat32() float32 { return MaxSafeFloat32 }
   768  
   769  //go:noinline
   770  func numConvNativeIntToFloat(src int32) float64 { return float64(src) }
   771  
   772  //go:noinline
   773  func numConvNativeFloatToInt(src float32) int64 { return int64(src) }
   774  
   775  //go:noinline
   776  func numConvOursIntToFloat(src int32) float64 { return gg.NumConv[float64](src) }
   777  
   778  //go:noinline
   779  func numConvOursFloatToInt(src float32) int64 { return gg.NumConv[int64](src) }
   780  
   781  func Benchmark_NumConv_int_to_float_native(b *testing.B) {
   782  	defer gtest.Catch(b)
   783  	src := makeInt32()
   784  
   785  	for ind := 0; ind < b.N; ind++ {
   786  		gg.Nop1(numConvNativeIntToFloat(src))
   787  	}
   788  }
   789  
   790  func Benchmark_NumConv_int_to_float_ours(b *testing.B) {
   791  	defer gtest.Catch(b)
   792  	src := makeInt32()
   793  
   794  	for ind := 0; ind < b.N; ind++ {
   795  		gg.Nop1(numConvOursIntToFloat(src))
   796  	}
   797  }
   798  
   799  func Benchmark_NumConv_float_to_int_native(b *testing.B) {
   800  	defer gtest.Catch(b)
   801  	src := makeFloat32()
   802  
   803  	for ind := 0; ind < b.N; ind++ {
   804  		gg.Nop1(numConvNativeFloatToInt(src))
   805  	}
   806  }
   807  
   808  func Benchmark_NumConv_float_to_int_ours(b *testing.B) {
   809  	defer gtest.Catch(b)
   810  	src := makeFloat32()
   811  
   812  	for ind := 0; ind < b.N; ind++ {
   813  		gg.Nop1(numConvOursFloatToInt(src))
   814  	}
   815  }
   816  
   817  /*
   818  Supplementary for `gg.Add`.
   819  
   820  Definitions:
   821  
   822  	A = addend
   823  	B = addend
   824  	V = valid output
   825  	O = overflow output
   826  
   827  States for unsigned integers:
   828  
   829  			        -->
   830  	0+++++++++++ | 0+++++
   831  	AB           | V
   832  	A     B      |    V
   833  	B     A      |    V
   834  	      AB     | O  OV
   835  	   A     B   |    OV
   836  	   B     A   |    OV
   837  
   838  States for signed integers:
   839  
   840  			               -->
   841  	---------0+++++++++ | ---------0+++++++++
   842  	         AB         |          V
   843  	    A    B          |     V
   844  	    B    A          |     V
   845  	         A    B     |               V
   846  	         B    A     |               V
   847  	    A         B     |     V    V    V
   848  	    B         A     |     V    V    V
   849  	  AB                |     V    O    O
   850  	  A   B             |     V         O
   851  	  B   A             |     V         O
   852  	            AB      |     O         V
   853  	            A   B   |     O         V
   854  	            B   A   |     O         V
   855  */
   856  
   857  func TestAdd_uint8(t *testing.T) {
   858  	defer gtest.Catch(t)
   859  
   860  	type Type = uint8
   861  	fun := gg.Add[Type]
   862  	enum := gg.RangeIncl[Type](0, math.MaxUint8)
   863  
   864  	/**
   865  	This should cover all possible cases. The hardcoded assertions below serve as
   866  	a sanity check and documentation.
   867  	*/
   868  	for _, one := range enum {
   869  		for _, two := range enum {
   870  			if int(one+two) == int(one)+int(two) {
   871  				gtest.Eq(fun(one, two), one+two, one, two)
   872  				continue
   873  			}
   874  
   875  			gtest.PanicStr(
   876  				fmt.Sprintf(`addition overflow for uint8: %v + %v`, one, two),
   877  				func() { fun(one, two) },
   878  				one, two,
   879  			)
   880  		}
   881  	}
   882  
   883  	gtest.Eq(fun(0, 0), 0)
   884  	gtest.Eq(fun(3, 5), 8)
   885  	gtest.Eq(fun(13, 7), 20)
   886  	gtest.Eq(fun(103, 152), 255)
   887  
   888  	gtest.PanicStr(
   889  		`addition overflow for uint8: 255 + 255 = 254`,
   890  		func() { fun(255, 255) },
   891  	)
   892  
   893  	gtest.PanicStr(
   894  		`addition overflow for uint8: 103 + 153 = 0`,
   895  		func() { fun(103, 153) },
   896  	)
   897  
   898  	gtest.PanicStr(
   899  		`addition overflow for uint8: 199 + 239 = 182`,
   900  		func() { fun(199, 239) },
   901  	)
   902  }
   903  
   904  func TestAdd_int8(t *testing.T) {
   905  	defer gtest.Catch(t)
   906  
   907  	type Type = int8
   908  	fun := gg.Add[Type]
   909  	enum := gg.RangeIncl[Type](math.MinInt8, math.MaxInt8)
   910  
   911  	/**
   912  	This should cover all possible cases. The hardcoded assertions below serve as
   913  	a sanity check and documentation.
   914  	*/
   915  	for _, one := range enum {
   916  		for _, two := range enum {
   917  			if int(one+two) == int(one)+int(two) {
   918  				gtest.Eq(fun(one, two), one+two, one, two)
   919  				continue
   920  			}
   921  
   922  			gtest.PanicStr(
   923  				fmt.Sprintf(`addition overflow for int8: %v + %v`, one, two),
   924  				func() { fun(one, two) },
   925  				one, two,
   926  			)
   927  		}
   928  	}
   929  
   930  	gtest.Eq(fun(0, 0), 0)
   931  
   932  	gtest.Eq(fun(3, 5), 8)
   933  	gtest.Eq(fun(13, 7), 20)
   934  	gtest.Eq(fun(79, 48), 127)
   935  
   936  	gtest.Eq(fun(-3, 5), 2)
   937  	gtest.Eq(fun(-13, 7), -6)
   938  	gtest.Eq(fun(-79, 48), -31)
   939  
   940  	gtest.Eq(fun(3, -5), -2)
   941  	gtest.Eq(fun(13, -7), 6)
   942  	gtest.Eq(fun(79, -48), 31)
   943  
   944  	gtest.Eq(fun(-3, -5), -8)
   945  	gtest.Eq(fun(-13, -7), -20)
   946  	gtest.Eq(fun(-79, -49), -128)
   947  
   948  	gtest.Eq(fun(127, -128), -1)
   949  	gtest.Eq(fun(-128, 127), -1)
   950  
   951  	gtest.PanicStr(
   952  		`addition overflow for int8: 127 + 127 = -2`,
   953  		func() { fun(127, 127) },
   954  	)
   955  
   956  	gtest.PanicStr(
   957  		`addition overflow for int8: -128 + -128 = 0`,
   958  		func() { fun(-128, -128) },
   959  	)
   960  
   961  	gtest.PanicStr(
   962  		`addition overflow for int8: 79 + 97 = -80`,
   963  		func() { fun(79, 97) },
   964  	)
   965  
   966  	gtest.PanicStr(
   967  		`addition overflow for int8: -79 + -97 = 80`,
   968  		func() { fun(-79, -97) },
   969  	)
   970  }
   971  
   972  func TestAdd_uint16(t *testing.T) {
   973  	defer gtest.Catch(t)
   974  
   975  	type Type = uint16
   976  	fun := gg.Add[Type]
   977  
   978  	gtest.Eq(fun(0, 0), 0)
   979  	gtest.Eq(fun(3, 5), 8)
   980  	gtest.Eq(fun(13, 7), 20)
   981  	gtest.Eq(fun(21963, 43572), 65535)
   982  
   983  	gtest.PanicStr(
   984  		`addition overflow for uint16: 65535 + 65535 = 65534`,
   985  		func() { fun(65535, 65535) },
   986  	)
   987  
   988  	gtest.PanicStr(
   989  		`addition overflow for uint16: 21963 + 43573 = 0`,
   990  		func() { fun(21963, 43573) },
   991  	)
   992  
   993  	gtest.PanicStr(
   994  		`addition overflow for uint16: 43573 + 39571 = 17608`,
   995  		func() { fun(43573, 39571) },
   996  	)
   997  }
   998  
   999  func TestAdd_int16(t *testing.T) {
  1000  	defer gtest.Catch(t)
  1001  
  1002  	type Type = int16
  1003  	fun := gg.Add[Type]
  1004  
  1005  	gtest.Eq(fun(0, 0), 0)
  1006  
  1007  	gtest.Eq(fun(3, 5), 8)
  1008  	gtest.Eq(fun(13, 7), 20)
  1009  	gtest.Eq(fun(79, 48), 127)
  1010  	gtest.Eq(fun(21963, 10804), 32767)
  1011  
  1012  	gtest.Eq(fun(-3, 5), 2)
  1013  	gtest.Eq(fun(-13, 7), -6)
  1014  	gtest.Eq(fun(-79, 48), -31)
  1015  	gtest.Eq(fun(-21963, 10804), -11159)
  1016  
  1017  	gtest.Eq(fun(3, -5), -2)
  1018  	gtest.Eq(fun(13, -7), 6)
  1019  	gtest.Eq(fun(79, -48), 31)
  1020  	gtest.Eq(fun(21963, -10804), 11159)
  1021  
  1022  	gtest.Eq(fun(-3, -5), -8)
  1023  	gtest.Eq(fun(-13, -7), -20)
  1024  	gtest.Eq(fun(-79, -49), -128)
  1025  	gtest.Eq(fun(-21963, -10804), -32767)
  1026  
  1027  	gtest.Eq(fun(32767, -32768), -1)
  1028  	gtest.Eq(fun(-32768, 32767), -1)
  1029  
  1030  	gtest.PanicStr(
  1031  		`addition overflow for int16: 32767 + 32767 = -2`,
  1032  		func() { fun(32767, 32767) },
  1033  	)
  1034  
  1035  	gtest.PanicStr(
  1036  		`addition overflow for int16: -32768 + -32768 = 0`,
  1037  		func() { fun(-32768, -32768) },
  1038  	)
  1039  
  1040  	gtest.PanicStr(
  1041  		`addition overflow for int16: 21963 + 28436 = -15137`,
  1042  		func() { fun(21963, 28436) },
  1043  	)
  1044  
  1045  	gtest.PanicStr(
  1046  		`addition overflow for int16: -21963 + -28436 = 15137`,
  1047  		func() { fun(-21963, -28436) },
  1048  	)
  1049  }
  1050  
  1051  /*
  1052  Supplementary for `gg.Sub`.
  1053  
  1054  Definitions:
  1055  
  1056  	A = minuend
  1057  	B = subtrahend
  1058  	V = valid output
  1059  	O = overflow output
  1060  
  1061  States for unsigned integers:
  1062  
  1063  			        -->
  1064  	0+++++++++++ | 0+++++
  1065  	AB           | V
  1066  	A     B      |    O
  1067  	B     A      |    V
  1068  	      AB     | V
  1069  	   A     B   |    O
  1070  	   B     A   |    V
  1071  
  1072  States for signed integers:
  1073  
  1074  			               -->
  1075  	---------0+++++++++ | ---------0+++++++++
  1076  	         AB         |          V
  1077  	    A    B          |     V
  1078  	    B    A          |     O         V
  1079  	         A    B     |     V
  1080  	         B    A     |               V
  1081  	    A         B     |     V         O
  1082  	    B         A     |     O         V
  1083  	  AB                |          V
  1084  	  A   B             |     V
  1085  	  B   A             |               V
  1086  	            AB      |          V
  1087  	            A   B   |     V
  1088  	            B   A   |               V
  1089  */
  1090  
  1091  // TODO tests for wider types.
  1092  func TestSub_uint8(t *testing.T) {
  1093  	defer gtest.Catch(t)
  1094  
  1095  	type Type = uint8
  1096  	fun := gg.Sub[Type]
  1097  	enum := gg.RangeIncl[Type](0, math.MaxUint8)
  1098  
  1099  	/**
  1100  	This should cover all possible cases. The hardcoded assertions below serve as
  1101  	a sanity check and documentation.
  1102  	*/
  1103  	for _, one := range enum {
  1104  		for _, two := range enum {
  1105  			if int(one-two) == int(one)-int(two) {
  1106  				gtest.Eq(fun(one, two), one-two, one, two)
  1107  				continue
  1108  			}
  1109  
  1110  			gtest.PanicStr(
  1111  				fmt.Sprintf(`subtraction overflow for uint8: %v - %v`, one, two),
  1112  				func() { fun(one, two) },
  1113  				one, two,
  1114  			)
  1115  		}
  1116  	}
  1117  
  1118  	gtest.Eq(fun(0, 0), 0)
  1119  	gtest.Eq(fun(1, 1), 0)
  1120  	gtest.Eq(fun(1, 0), 1)
  1121  	gtest.Eq(fun(5, 3), 2)
  1122  	gtest.Eq(fun(13, 7), 6)
  1123  	gtest.Eq(fun(152, 103), 49)
  1124  	gtest.Eq(fun(255, 0), 255)
  1125  	gtest.Eq(fun(255, 1), 254)
  1126  	gtest.Eq(fun(255, 254), 1)
  1127  	gtest.Eq(fun(255, 255), 0)
  1128  
  1129  	gtest.PanicStr(
  1130  		`subtraction overflow for uint8: 0 - 1 = 255`,
  1131  		func() { fun(0, 1) },
  1132  	)
  1133  
  1134  	gtest.PanicStr(
  1135  		`subtraction overflow for uint8: 0 - 255 = 1`,
  1136  		func() { fun(0, 255) },
  1137  	)
  1138  
  1139  	gtest.PanicStr(
  1140  		`subtraction overflow for uint8: 103 - 153 = 206`,
  1141  		func() { fun(103, 153) },
  1142  	)
  1143  
  1144  	gtest.PanicStr(
  1145  		`subtraction overflow for uint8: 79 - 255 = 80`,
  1146  		func() { fun(79, 255) },
  1147  	)
  1148  }
  1149  
  1150  // TODO tests for wider types.
  1151  func TestSub_int8(t *testing.T) {
  1152  	defer gtest.Catch(t)
  1153  
  1154  	type Type = int8
  1155  	fun := gg.Sub[Type]
  1156  	enum := gg.RangeIncl[Type](math.MinInt8, math.MaxInt8)
  1157  
  1158  	/**
  1159  	This should cover all possible cases. The hardcoded assertions below serve as
  1160  	a sanity check and documentation.
  1161  	*/
  1162  	for _, one := range enum {
  1163  		for _, two := range enum {
  1164  			if int(one-two) == int(one)-int(two) {
  1165  				gtest.Eq(fun(one, two), one-two, one, two)
  1166  				continue
  1167  			}
  1168  
  1169  			gtest.PanicStr(
  1170  				fmt.Sprintf(`subtraction overflow for int8: %v - %v`, one, two),
  1171  				func() { fun(one, two) },
  1172  				one, two,
  1173  			)
  1174  		}
  1175  	}
  1176  
  1177  	gtest.Eq(fun(0, 0), 0)
  1178  
  1179  	gtest.Eq(fun(3, 5), -2)
  1180  	gtest.Eq(fun(13, 7), 6)
  1181  	gtest.Eq(fun(79, 48), 31)
  1182  
  1183  	gtest.Eq(fun(-3, 5), -8)
  1184  	gtest.Eq(fun(-13, 7), -20)
  1185  	gtest.Eq(fun(-79, 48), -127)
  1186  	gtest.Eq(fun(-79, 49), -128)
  1187  
  1188  	gtest.Eq(fun(3, -5), 8)
  1189  	gtest.Eq(fun(13, -7), 20)
  1190  	gtest.Eq(fun(79, -48), 127)
  1191  
  1192  	gtest.Eq(fun(-3, -5), 2)
  1193  	gtest.Eq(fun(-13, -7), -6)
  1194  	gtest.Eq(fun(-79, -49), -30)
  1195  
  1196  	gtest.Eq(fun(127, 0), 127)
  1197  	gtest.Eq(fun(127, 1), 126)
  1198  	gtest.Eq(fun(127, 126), 1)
  1199  	gtest.Eq(fun(127, 127), 0)
  1200  
  1201  	gtest.Eq(fun(-128, 0), -128)
  1202  	gtest.Eq(fun(-128, -1), -127)
  1203  	gtest.Eq(fun(-128, -127), -1)
  1204  	gtest.Eq(fun(-128, -128), 0)
  1205  
  1206  	gtest.PanicStr(
  1207  		`subtraction overflow for int8: -128 - 1 = 127`,
  1208  		func() { fun(-128, 1) },
  1209  	)
  1210  
  1211  	gtest.PanicStr(
  1212  		`subtraction overflow for int8: -128 - 2 = 126`,
  1213  		func() { fun(-128, 2) },
  1214  	)
  1215  
  1216  	gtest.PanicStr(
  1217  		`subtraction overflow for int8: -128 - 127 = 1`,
  1218  		func() { fun(-128, 127) },
  1219  	)
  1220  
  1221  	gtest.PanicStr(
  1222  		`subtraction overflow for int8: 127 - -1 = -128`,
  1223  		func() { fun(127, -1) },
  1224  	)
  1225  
  1226  	gtest.PanicStr(
  1227  		`subtraction overflow for int8: 127 - -2 = -127`,
  1228  		func() { fun(127, -2) },
  1229  	)
  1230  
  1231  	gtest.PanicStr(
  1232  		`subtraction overflow for int8: 127 - -128 = -1`,
  1233  		func() { fun(127, -128) },
  1234  	)
  1235  
  1236  	gtest.PanicStr(
  1237  		`subtraction overflow for int8: -79 - 50 = 127`,
  1238  		func() { fun(-79, 50) },
  1239  	)
  1240  
  1241  	gtest.PanicStr(
  1242  		`subtraction overflow for int8: 79 - -49 = -128`,
  1243  		func() { fun(79, -49) },
  1244  	)
  1245  }
  1246  
  1247  /*
  1248  Supplementary for `gg.Mul`.
  1249  
  1250  Definitions:
  1251  
  1252  	A = multiplicand
  1253  	B = multiplicand
  1254  	V = valid output
  1255  	O = overflow output
  1256  
  1257  States for unsigned integers:
  1258  
  1259  			        -->
  1260  	0+++++++++++ | 0++++++
  1261  	AB           | V
  1262  	A     B      | V
  1263  	B     A      | V
  1264  	      AB     | O  OV
  1265  	   A     B   | O  OV
  1266  	   B     A   | O  OV
  1267  
  1268  States for signed integers:
  1269  
  1270  			               -->
  1271  	---------0+++++++++ | ---------0+++++++++
  1272  	         AB         |          V
  1273  	    A    B          |          V
  1274  	    B    A          |          V
  1275  	         A    B     |          V
  1276  	         B    A     |          V
  1277  	    A         B     |     OV   O    O
  1278  	    B         A     |     OV   O    O
  1279  	  AB                |     OV   O    O
  1280  	  A   B             |     O    O    OV
  1281  	  B   A             |     O    O    OV
  1282  	            AB      |               OV
  1283  	            A   B   |     O    O    OV
  1284  	            B   A   |     O    O    OV
  1285  */
  1286  
  1287  // TODO tests for wider types.
  1288  func TestMul_uint8(t *testing.T) {
  1289  	defer gtest.Catch(t)
  1290  
  1291  	type Type = uint8
  1292  	fun := gg.Mul[Type]
  1293  	enum := gg.RangeIncl[Type](0, math.MaxUint8)
  1294  
  1295  	/**
  1296  	This should cover all possible cases. The hardcoded assertions below serve as
  1297  	a sanity check and documentation.
  1298  	*/
  1299  	for _, one := range enum {
  1300  		for _, two := range enum {
  1301  			if int(one*two) == int(one)*int(two) {
  1302  				gtest.Eq(fun(one, two), one*two, one, two)
  1303  				continue
  1304  			}
  1305  
  1306  			gtest.PanicStr(
  1307  				fmt.Sprintf(`multiplication overflow for uint8: %v * %v`, one, two),
  1308  				func() { fun(one, two) },
  1309  				one, two,
  1310  			)
  1311  		}
  1312  	}
  1313  
  1314  	gtest.Eq(fun(3, 5), 15)
  1315  	gtest.Eq(fun(5, 3), 15)
  1316  
  1317  	gtest.Eq(fun(5, 7), 35)
  1318  	gtest.Eq(fun(7, 5), 35)
  1319  
  1320  	gtest.Eq(fun(7, 11), 77)
  1321  	gtest.Eq(fun(11, 7), 77)
  1322  
  1323  	gtest.Eq(fun(11, 13), 143)
  1324  	gtest.Eq(fun(13, 11), 143)
  1325  
  1326  	gtest.Eq(fun(13, 17), 221)
  1327  	gtest.Eq(fun(17, 13), 221)
  1328  
  1329  	gtest.Eq(fun(17, 15), 255)
  1330  	gtest.Eq(fun(15, 17), 255)
  1331  
  1332  	gtest.PanicStr(
  1333  		`multiplication overflow for uint8: 255 * 255 = 1`,
  1334  		func() { fun(255, 255) },
  1335  	)
  1336  
  1337  	gtest.PanicStr(
  1338  		`multiplication overflow for uint8: 17 * 19 = 67`,
  1339  		func() { fun(17, 19) },
  1340  	)
  1341  
  1342  	gtest.PanicStr(
  1343  		`multiplication overflow for uint8: 19 * 17 = 67`,
  1344  		func() { fun(19, 17) },
  1345  	)
  1346  
  1347  	gtest.PanicStr(
  1348  		`multiplication overflow for uint8: 2 * 128 = 0`,
  1349  		func() { fun(2, 128) },
  1350  	)
  1351  
  1352  	gtest.PanicStr(
  1353  		`multiplication overflow for uint8: 128 * 2 = 0`,
  1354  		func() { fun(128, 2) },
  1355  	)
  1356  }
  1357  
  1358  // TODO tests for wider types.
  1359  func TestMul_int8(t *testing.T) {
  1360  	defer gtest.Catch(t)
  1361  
  1362  	type Type = int8
  1363  	fun := gg.Mul[Type]
  1364  	enum := gg.RangeIncl[Type](math.MinInt8, math.MaxInt8)
  1365  
  1366  	/**
  1367  	This should cover all possible cases. The hardcoded assertions below serve as
  1368  	a sanity check and documentation.
  1369  	*/
  1370  	for _, one := range enum {
  1371  		for _, two := range enum {
  1372  			if int(one*two) == int(one)*int(two) {
  1373  				gtest.Eq(fun(one, two), one*two, one, two)
  1374  				continue
  1375  			}
  1376  
  1377  			gtest.PanicStr(
  1378  				fmt.Sprintf(`multiplication overflow for int8: %v * %v`, one, two),
  1379  				func() { fun(one, two) },
  1380  				one, two,
  1381  			)
  1382  		}
  1383  	}
  1384  
  1385  	gtest.Eq(fun(0, 0), 0)
  1386  
  1387  	gtest.Eq(fun(0, 3), 0)
  1388  	gtest.Eq(fun(3, 0), 0)
  1389  
  1390  	gtest.Eq(fun(0, -3), 0)
  1391  	gtest.Eq(fun(-3, 0), 0)
  1392  
  1393  	gtest.Eq(fun(1, 3), 3)
  1394  	gtest.Eq(fun(3, 1), 3)
  1395  
  1396  	gtest.Eq(fun(1, 127), 127)
  1397  	gtest.Eq(fun(127, 1), 127)
  1398  
  1399  	gtest.Eq(fun(1, -128), -128)
  1400  	gtest.Eq(fun(-128, 1), -128)
  1401  
  1402  	gtest.Eq(fun(1, -3), -3)
  1403  	gtest.Eq(fun(-3, 1), -3)
  1404  
  1405  	gtest.Eq(fun(-1, 3), -3)
  1406  	gtest.Eq(fun(3, -1), -3)
  1407  
  1408  	gtest.Eq(fun(-1, 127), -127)
  1409  	gtest.Eq(fun(127, -1), -127)
  1410  
  1411  	gtest.Eq(fun(-1, -3), 3)
  1412  	gtest.Eq(fun(-3, -1), 3)
  1413  
  1414  	gtest.Eq(fun(3, 5), 15)
  1415  	gtest.Eq(fun(5, 3), 15)
  1416  
  1417  	gtest.Eq(fun(3, -5), -15)
  1418  	gtest.Eq(fun(-5, 3), -15)
  1419  
  1420  	gtest.Eq(fun(-3, 5), -15)
  1421  	gtest.Eq(fun(5, -3), -15)
  1422  
  1423  	gtest.Eq(fun(-3, -5), 15)
  1424  	gtest.Eq(fun(-5, -3), 15)
  1425  
  1426  	gtest.Eq(fun(9, 14), 126)
  1427  	gtest.Eq(fun(14, 9), 126)
  1428  
  1429  	gtest.Eq(fun(9, -14), -126)
  1430  	gtest.Eq(fun(-14, 9), -126)
  1431  
  1432  	gtest.Eq(fun(-9, 14), -126)
  1433  	gtest.Eq(fun(14, -9), -126)
  1434  
  1435  	gtest.Eq(fun(-9, -14), 126)
  1436  	gtest.Eq(fun(-14, -9), 126)
  1437  
  1438  	gtest.PanicStr(
  1439  		`multiplication overflow for int8: 127 * 127 = 1`,
  1440  		func() { fun(127, 127) },
  1441  	)
  1442  
  1443  	gtest.PanicStr(
  1444  		`multiplication overflow for int8: 127 * 126 = -126`,
  1445  		func() { fun(127, 126) },
  1446  	)
  1447  
  1448  	gtest.PanicStr(
  1449  		`multiplication overflow for int8: 126 * 127 = -126`,
  1450  		func() { fun(126, 127) },
  1451  	)
  1452  
  1453  	gtest.PanicStr(
  1454  		`multiplication overflow for int8: 126 * 126 = 4`,
  1455  		func() { fun(126, 126) },
  1456  	)
  1457  
  1458  	gtest.PanicStr(
  1459  		`multiplication overflow for int8: 126 * 126 = 4`,
  1460  		func() { fun(126, 126) },
  1461  	)
  1462  
  1463  	gtest.PanicStr(
  1464  		`multiplication overflow for int8: -128 * -128 = 0`,
  1465  		func() { fun(-128, -128) },
  1466  	)
  1467  
  1468  	gtest.PanicStr(
  1469  		`multiplication overflow for int8: -128 * -127 = -128`,
  1470  		func() { fun(-128, -127) },
  1471  	)
  1472  
  1473  	gtest.PanicStr(
  1474  		`multiplication overflow for int8: -127 * -128 = -128`,
  1475  		func() { fun(-127, -128) },
  1476  	)
  1477  
  1478  	gtest.PanicStr(
  1479  		`multiplication overflow for int8: -127 * -127 = 1`,
  1480  		func() { fun(-127, -127) },
  1481  	)
  1482  
  1483  	gtest.PanicStr(
  1484  		`multiplication overflow for int8: 127 * -128 = -128`,
  1485  		func() { fun(127, -128) },
  1486  	)
  1487  
  1488  	gtest.PanicStr(
  1489  		`multiplication overflow for int8: -128 * 127 = -128`,
  1490  		func() { fun(-128, 127) },
  1491  	)
  1492  
  1493  	gtest.PanicStr(
  1494  		`multiplication overflow for int8: -127 * 127 = -1`,
  1495  		func() { fun(-127, 127) },
  1496  	)
  1497  
  1498  	gtest.PanicStr(
  1499  		`multiplication overflow for int8: -126 * 127 = 126`,
  1500  		func() { fun(-126, 127) },
  1501  	)
  1502  
  1503  	gtest.PanicStr(
  1504  		`multiplication overflow for int8: -1 * -128 = -128`,
  1505  		func() { fun(-1, -128) },
  1506  	)
  1507  
  1508  	gtest.PanicStr(
  1509  		`multiplication overflow for int8: -128 * -1 = -128`,
  1510  		func() { fun(-128, -1) },
  1511  	)
  1512  
  1513  	gtest.PanicStr(
  1514  		`multiplication overflow for int8: 11 * 13 = -113`,
  1515  		func() { fun(11, 13) },
  1516  	)
  1517  
  1518  	gtest.PanicStr(
  1519  		`multiplication overflow for int8: 13 * 11 = -113`,
  1520  		func() { fun(13, 11) },
  1521  	)
  1522  
  1523  	gtest.PanicStr(
  1524  		`multiplication overflow for int8: -11 * -13 = -113`,
  1525  		func() { fun(-11, -13) },
  1526  	)
  1527  
  1528  	gtest.PanicStr(
  1529  		`multiplication overflow for int8: -13 * -11 = -113`,
  1530  		func() { fun(-13, -11) },
  1531  	)
  1532  
  1533  	gtest.PanicStr(
  1534  		`multiplication overflow for int8: 2 * 127 = -2`,
  1535  		func() { fun(2, 127) },
  1536  	)
  1537  
  1538  	gtest.PanicStr(
  1539  		`multiplication overflow for int8: 127 * 2 = -2`,
  1540  		func() { fun(127, 2) },
  1541  	)
  1542  }
  1543  
  1544  //go:noinline
  1545  func safePairInt8() (int8, int8) { return 5, 7 }
  1546  
  1547  func Benchmark_mul_int8_native(b *testing.B) {
  1548  	defer gtest.Catch(b)
  1549  	one, two := safePairInt8()
  1550  	b.ResetTimer()
  1551  
  1552  	for ind := 0; ind < b.N; ind++ {
  1553  		gg.Nop1(one * two)
  1554  	}
  1555  }
  1556  
  1557  func Benchmark_mul_int8_ours(b *testing.B) {
  1558  	defer gtest.Catch(b)
  1559  	one, two := safePairInt8()
  1560  	b.ResetTimer()
  1561  
  1562  	for ind := 0; ind < b.N; ind++ {
  1563  		gg.Mul[int8](one, two)
  1564  	}
  1565  }