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 }