Source file src/simd/internal/test_helpers/checkslices.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build goexperiment.simd
     6  
     7  package test_helpers
     8  
     9  import (
    10  	"math"
    11  	"strings"
    12  	"testing"
    13  )
    14  
    15  type signed interface {
    16  	~int | ~int8 | ~int16 | ~int32 | ~int64
    17  }
    18  
    19  type integer interface {
    20  	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
    21  }
    22  
    23  type float interface {
    24  	~float32 | ~float64
    25  }
    26  
    27  type number interface {
    28  	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64
    29  }
    30  
    31  func CheckSlices[T number](t *testing.T, got, want []T) bool {
    32  	t.Helper()
    33  	return CheckSlicesLogInput[T](t, got, want, 0.0, nil)
    34  }
    35  
    36  func isSameFloat(ia, ib any, flakiness float64) bool {
    37  	switch x := ia.(type) {
    38  	case float32:
    39  		y := ib.(float32)
    40  		if math.IsNaN(float64(x)) && math.IsNaN(float64(y)) {
    41  			return true
    42  		}
    43  		if flakiness > 0 {
    44  			if y == 0 {
    45  				if math.Abs(float64(x)) < flakiness {
    46  					return true
    47  				}
    48  			} else {
    49  				if math.Abs(float64((x-y)/y)) < flakiness {
    50  					return true
    51  				}
    52  			}
    53  		}
    54  	case float64:
    55  		y := ib.(float64)
    56  		if math.IsNaN(x) && math.IsNaN(y) {
    57  			return true
    58  		}
    59  		if flakiness > 0 {
    60  			if y == 0 {
    61  				if math.Abs(x) < flakiness {
    62  					return true
    63  				}
    64  			} else if math.Abs((x-y)/y) < flakiness {
    65  				return true
    66  			}
    67  		}
    68  
    69  	default:
    70  	}
    71  	return false
    72  }
    73  
    74  func isDifferentSignZero(ia, ib any) bool {
    75  	switch x := ia.(type) {
    76  	case float32:
    77  		y := ib.(float32)
    78  		if math.Float32bits(x) != math.Float32bits(y) {
    79  			return true
    80  		}
    81  	case float64:
    82  		y := ib.(float64)
    83  		if math.Float64bits(x) != math.Float64bits(y) {
    84  			return true
    85  		}
    86  	default:
    87  	}
    88  	return false
    89  }
    90  
    91  // CheckSlicesLogInput compares two slices for equality,
    92  // reporting a test error if there is a problem,
    93  // and also consumes the two slices so that a
    94  // test/benchmark won't be dead-code eliminated.
    95  func CheckSlicesLogInput[T number](t *testing.T, got, want []T, flakiness float64, logInput func()) bool {
    96  	t.Helper()
    97  	var z T
    98  	for i := range want {
    99  		if got[i] != want[i] {
   100  			var ia any = got[i]
   101  			var ib any = want[i]
   102  			if isSameFloat(ia, ib, flakiness) {
   103  				continue
   104  			}
   105  
   106  			t.Logf("For %T vector elements:", z)
   107  			t.Logf("got =%v", got)
   108  			t.Logf("want=%v", want)
   109  			if logInput != nil {
   110  				logInput()
   111  			}
   112  			t.Errorf("at index %d, got=%v, want=%v", i, got[i], want[i])
   113  			return false
   114  		} else if got[i] == 0 { // for floating point, 0.0 == -0.0 but a bitwise check can see the difference
   115  			var ia any = got[i]
   116  			var ib any = want[i]
   117  			if isDifferentSignZero(ia, ib) {
   118  				t.Logf("For %T vector elements:", z)
   119  				t.Logf("got =%v", got)
   120  				t.Logf("want=%v", want)
   121  				if logInput != nil {
   122  					logInput()
   123  				}
   124  				t.Errorf("at index %d, different signs of zero", i)
   125  				return false
   126  			}
   127  		}
   128  	}
   129  	return true
   130  }
   131  
   132  // CheckScalarsLogInput compares two values for equality,
   133  // reporting a test error if there is a problem,
   134  // and also consumes the two values so that a
   135  // test/benchmark won't be dead-code eliminated.
   136  // The goal is to match the behavior and output for
   137  // CheckSlicesLogInput.
   138  func CheckScalarsLogInput[T number](t *testing.T, got, want T, flakiness float64, logInput func()) bool {
   139  	t.Helper()
   140  	var z T
   141  	if got != want {
   142  		var ia any = got
   143  		var ib any = want
   144  		if isSameFloat(ia, ib, flakiness) {
   145  			return true
   146  		}
   147  
   148  		t.Logf("For %T scalar:", z)
   149  		t.Logf("got =%v", got)
   150  		t.Logf("want=%v", want)
   151  		if logInput != nil {
   152  			logInput()
   153  		}
   154  		t.Errorf("got=%v, want=%v", got, want)
   155  		return false
   156  
   157  	} else if got == 0 { // for floating point, 0.0 == -0.0 but a bitwise check can see the difference
   158  		var ia any = got
   159  		var ib any = want
   160  		if isDifferentSignZero(ia, ib) {
   161  			t.Logf("For %T vector elements:", z)
   162  			t.Logf("got =%v", got)
   163  			t.Logf("want=%v", want)
   164  			if logInput != nil {
   165  				logInput()
   166  			}
   167  			t.Errorf("different signs of zero")
   168  			return false
   169  		}
   170  	}
   171  	return true
   172  }
   173  
   174  // CheckStringsLogInput compares two strings for equality,
   175  // reporting a test error if there is a problem,
   176  // and also consumes the two values so that a
   177  // test/benchmark won't be dead-code eliminated.
   178  // The goal is to match the behavior and output for
   179  // CheckSlicesLogInput.
   180  func CheckStringsLogInput(t *testing.T, got, want string, flakiness float64, logInput func()) bool {
   181  	t.Helper()
   182  	if got != want {
   183  		t.Logf("For string:")
   184  		t.Logf("got =%v", got)
   185  		t.Logf("want=%v", want)
   186  		if logInput != nil {
   187  			logInput()
   188  		}
   189  		t.Errorf("got=%v, want=%v", got, want)
   190  		return false
   191  	}
   192  	return true
   193  }
   194  
   195  // nOf returns a slice of length n whose elements are taken
   196  // from input slice s.
   197  func nOf[T any](n int, s []T) []T {
   198  	if len(s) >= n {
   199  		return s
   200  	}
   201  	r := make([]T, n)
   202  	for i := range r {
   203  		r[i] = s[i%len(s)]
   204  	}
   205  	return r
   206  }
   207  
   208  const (
   209  	PN22  = 1.0 / 1024 / 1024 / 4
   210  	PN24  = 1.0 / 1024 / 1024 / 16
   211  	PN53  = PN24 * PN24 / 32
   212  	F0    = float32(1.0 + 513*PN22/2)
   213  	F1    = float32(1.0 + 511*PN22*8)
   214  	Aeasy = float32(2046 * PN53)
   215  	Ahard = float32(2047 * PN53) // 2047 provokes a 2-rounding in 64-bit FMA rounded to 32-bit
   216  )
   217  
   218  var zero = 0.0
   219  var nzero = -zero
   220  var inf = 1 / zero
   221  var ninf = -1 / zero
   222  var nan = math.NaN()
   223  var snan32 = math.Float32frombits(0x7f800001)
   224  var snan64 = math.Float64frombits(0x7ff0000000000001)
   225  
   226  // N controls how large the test vectors are
   227  const N = 144
   228  
   229  var float32s = nOf(N, []float32{float32(inf), float32(ninf), 1, float32(nan), snan32, -float32(nan), -snan32, float32(zero), 2, float32(nan), float32(zero), 3, float32(-zero), float32(1.0 / zero), float32(-1.0 / zero), 1.0 / 2, 1.0 / 4, 1.0 / 8, 1.0 / 1000, 1.0 / 1000000, 1, -1, 0, 2, -2, 3, -3, math.MaxFloat32, 1 / math.MaxFloat32, 10, -10, 100, 20, -20, 300, -300, -4000, -80, -160, -3200, -64, -4, -8, -16, -32, -64})
   230  var float64s = nOf(N, []float64{inf, ninf, nan, snan64, -nan, -snan64, zero, -zero, 1 / zero, -1 / zero, 0.0001, 0.0000001, 1, -1, 0, 2, -2, 3, -3, math.MaxFloat64, 1.0 / math.MaxFloat64, 10, -10, 100, 20, -20, 300, -300, -4000, -80, -16, -32, -64})
   231  
   232  var int32s = nOf(N, []int32{1, -1, 0, 2, 4, 8, 1024, 0xffffff, -0xffffff, 0x55555, 0x77777, 0xccccc, -0x55555, -0x77777, -0xccccc, -4, -8, -16, -32, -64})
   233  var uint32s = nOf(N, []uint32{1, 0, 2, 4, 8, 1024, 0xffffff, ^uint32(0xffffff), 0x55555, 0x77777, 0xccccc, ^uint32(0x55555), ^uint32(0x77777), ^uint32(0xccccc)})
   234  
   235  var int64s = nOf(N, []int64{1, -1, 0, 2, 4, 8, 1024, 0xffffff, -0xffffff, 0x55555, 0x77777, 0xccccc, -0x55555, -0x77777, -0xccccc, -4, -8, -16, -32, -64})
   236  var uint64s = nOf(N, []uint64{1, 0, 2, 4, 8, 1024, 0xffffff, ^uint64(0xffffff), 0x55555, 0x77777, 0xccccc, ^uint64(0x55555), ^uint64(0x77777), ^uint64(0xccccc)})
   237  
   238  var int16s = nOf(N, []int16{1, -1, 0, 2, 4, 8, 1024, 3, 5, 7, 11, 13, 3000, 5555, 7777, 11111, 32767, 32766, -32767, -32768, -11111, -4, -8, -16, -32, -64})
   239  var uint16s = nOf(N, []uint16{1, 0, 2, 4, 8, 1024, 3, 5, 7, 11, 13, 3000, 5555, 7777, 11111, 32767, 32766, 32768, 65535, 45678, 56789})
   240  
   241  var int8s = nOf(N, []int8{0, 1, 2, 3, 5, 7, 11, 22, 33, 55, 77, 121, 127, -1, -2, -3, -5, -7, -11, -77, -121, -127, -128, 4, 8, 16, 32, 64, -4, -8, -16, -32, -64})
   242  var uint8s = nOf(N, []uint8{0, 1, 2, 3, 5, 7, 11, 22, 33, 55, 77, 121, 127, 128, 255, 233, 211, 177, 144, 4, 8, 16, 32, 64})
   243  
   244  var bools = nOf(N, []bool{
   245  	true, false, true, true, false, false, true, true, true, false, false, false, true, true, true, true, false, false, false, false})
   246  
   247  func Float64s() []float64 {
   248  	return float64s
   249  }
   250  func Int64s() []int64 {
   251  	return int64s
   252  }
   253  func Uint64s() []uint64 {
   254  	return uint64s
   255  }
   256  
   257  func Float32s() []float32 {
   258  	return float32s
   259  }
   260  func Int32s() []int32 {
   261  	return int32s
   262  }
   263  func Uint32s() []uint32 {
   264  	return uint32s
   265  }
   266  
   267  func Int16s() []int16 {
   268  	return int16s
   269  }
   270  func Uint16s() []uint16 {
   271  	return uint16s
   272  }
   273  
   274  func Int8s() []int8 {
   275  	return int8s
   276  }
   277  func Uint8s() []uint8 {
   278  	return uint8s
   279  }
   280  func Bools() []bool {
   281  	return bools
   282  }
   283  
   284  var s8s = [...]uint64{0, 1, 3, 7, 8, 9, 16, 9999999999}
   285  var s16s = [...]uint64{0, 1, 3, 15, 16, 17, 32, 9999999999}
   286  var s32s = [...]uint64{0, 1, 3, 31, 32, 33, 64, 9999999999}
   287  var s64s = [...]uint64{0, 1, 3, 63, 64, 65, 128, 9999999999}
   288  
   289  func Shift8s() []uint64 {
   290  	return s8s[:]
   291  }
   292  func Shift16s() []uint64 {
   293  	return s16s[:]
   294  }
   295  func Shift32s() []uint64 {
   296  	return s32s[:]
   297  }
   298  func Shift64s() []uint64 {
   299  	return s64s[:]
   300  }
   301  
   302  // ImpliedDo returns a slice T with length n and each element i
   303  // initialized to val(i).
   304  func ImpliedDo[T number](n int, val func(i int) T) []T {
   305  	s := make([]T, n)
   306  	for i := range s {
   307  		s[i] = val(i)
   308  	}
   309  	return s
   310  }
   311  
   312  // ImpliedBools returns a string
   313  func ImpliedBools(n int, val func(i int) bool) string {
   314  	// TODO if we cared about time and maybe we do,
   315  	// the string will be 2n+2 long with even elements
   316  	// equal to left-curly, commas, right-curly and
   317  	// odd elements equal to '0' or '1'.
   318  	var s strings.Builder
   319  	s.WriteString("{")
   320  	for i := range n {
   321  		if i > 0 {
   322  			s.WriteString(",")
   323  		}
   324  		if val(i) {
   325  			s.WriteString("1")
   326  		} else {
   327  			s.WriteString("0")
   328  		}
   329  	}
   330  	s.WriteString("}")
   331  	return s.String()
   332  }
   333  
   334  func forSlice[T number](t *testing.T, s []T, n int, f func(a []T) bool) {
   335  	t.Helper()
   336  	for i := 0; i < len(s)-n; i++ {
   337  		if !f(s[i : i+n]) {
   338  			return
   339  		}
   340  	}
   341  }
   342  
   343  func forSlicePair[T number](t *testing.T, s []T, n int, f func(a, b []T) bool) {
   344  	t.Helper()
   345  	for i := 0; i < len(s)-n; i++ {
   346  		for j := 0; j < len(s)-n; j++ {
   347  			if !f(s[i:i+n], s[j:j+n]) {
   348  				return
   349  			}
   350  		}
   351  	}
   352  }
   353  
   354  func forBoolSlice(t *testing.T, s []bool, n int, f func(a []bool) bool) {
   355  	t.Helper()
   356  	for i := 0; i < len(s)-n; i++ {
   357  		if !f(s[i : i+n]) {
   358  			return
   359  		}
   360  	}
   361  }
   362  
   363  func forBoolSlicePair(t *testing.T, s []bool, n int, f func(a, b []bool) bool) {
   364  	t.Helper()
   365  	for i := 0; i < len(s)-n; i++ {
   366  		for j := 0; j < len(s)-n; j++ {
   367  			if !f(s[i:i+n], s[j:j+n]) {
   368  				return
   369  			}
   370  		}
   371  	}
   372  }
   373  

View as plain text