github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/sort/search_test.go (about) 1 // Copyright 2010 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 package sort_test 6 7 import ( 8 "runtime" 9 . "sort" 10 stringspkg "strings" 11 "testing" 12 ) 13 14 func f(a []int, x int) func(int) bool { 15 return func(i int) bool { 16 return a[i] >= x 17 } 18 } 19 20 var data = []int{0: -10, 1: -5, 2: 0, 3: 1, 4: 2, 5: 3, 6: 5, 7: 7, 8: 11, 9: 100, 10: 100, 11: 100, 12: 1000, 13: 10000} 21 22 var tests = []struct { 23 name string 24 n int 25 f func(int) bool 26 i int 27 }{ 28 {"empty", 0, nil, 0}, 29 {"1 1", 1, func(i int) bool { return i >= 1 }, 1}, 30 {"1 true", 1, func(i int) bool { return true }, 0}, 31 {"1 false", 1, func(i int) bool { return false }, 1}, 32 {"1e9 991", 1e9, func(i int) bool { return i >= 991 }, 991}, 33 {"1e9 true", 1e9, func(i int) bool { return true }, 0}, 34 {"1e9 false", 1e9, func(i int) bool { return false }, 1e9}, 35 {"data -20", len(data), f(data, -20), 0}, 36 {"data -10", len(data), f(data, -10), 0}, 37 {"data -9", len(data), f(data, -9), 1}, 38 {"data -6", len(data), f(data, -6), 1}, 39 {"data -5", len(data), f(data, -5), 1}, 40 {"data 3", len(data), f(data, 3), 5}, 41 {"data 11", len(data), f(data, 11), 8}, 42 {"data 99", len(data), f(data, 99), 9}, 43 {"data 100", len(data), f(data, 100), 9}, 44 {"data 101", len(data), f(data, 101), 12}, 45 {"data 10000", len(data), f(data, 10000), 13}, 46 {"data 10001", len(data), f(data, 10001), 14}, 47 {"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] <= 7 }, 4}, 48 {"descending 7", 1e9, func(i int) bool { return 1e9-i <= 7 }, 1e9 - 7}, 49 {"overflow", 2e9, func(i int) bool { return false }, 2e9}, 50 } 51 52 func TestSearch(t *testing.T) { 53 for _, e := range tests { 54 i := Search(e.n, e.f) 55 if i != e.i { 56 t.Errorf("%s: expected index %d; got %d", e.name, e.i, i) 57 } 58 } 59 } 60 61 func TestFind(t *testing.T) { 62 str1 := []string{"foo"} 63 str2 := []string{"ab", "ca"} 64 str3 := []string{"mo", "qo", "vo"} 65 str4 := []string{"ab", "ad", "ca", "xy"} 66 67 // slice with repeating elements 68 strRepeats := []string{"ba", "ca", "da", "da", "da", "ka", "ma", "ma", "ta"} 69 70 // slice with all element equal 71 strSame := []string{"xx", "xx", "xx"} 72 73 tests := []struct { 74 data []string 75 target string 76 wantPos int 77 wantFound bool 78 }{ 79 {[]string{}, "foo", 0, false}, 80 {[]string{}, "", 0, false}, 81 82 {str1, "foo", 0, true}, 83 {str1, "bar", 0, false}, 84 {str1, "zx", 1, false}, 85 86 {str2, "aa", 0, false}, 87 {str2, "ab", 0, true}, 88 {str2, "ad", 1, false}, 89 {str2, "ca", 1, true}, 90 {str2, "ra", 2, false}, 91 92 {str3, "bb", 0, false}, 93 {str3, "mo", 0, true}, 94 {str3, "nb", 1, false}, 95 {str3, "qo", 1, true}, 96 {str3, "tr", 2, false}, 97 {str3, "vo", 2, true}, 98 {str3, "xr", 3, false}, 99 100 {str4, "aa", 0, false}, 101 {str4, "ab", 0, true}, 102 {str4, "ac", 1, false}, 103 {str4, "ad", 1, true}, 104 {str4, "ax", 2, false}, 105 {str4, "ca", 2, true}, 106 {str4, "cc", 3, false}, 107 {str4, "dd", 3, false}, 108 {str4, "xy", 3, true}, 109 {str4, "zz", 4, false}, 110 111 {strRepeats, "da", 2, true}, 112 {strRepeats, "db", 5, false}, 113 {strRepeats, "ma", 6, true}, 114 {strRepeats, "mb", 8, false}, 115 116 {strSame, "xx", 0, true}, 117 {strSame, "ab", 0, false}, 118 {strSame, "zz", 3, false}, 119 } 120 121 for _, tt := range tests { 122 t.Run(tt.target, func(t *testing.T) { 123 cmp := func(i int) int { 124 return stringspkg.Compare(tt.target, tt.data[i]) 125 } 126 127 pos, found := Find(len(tt.data), cmp) 128 if pos != tt.wantPos || found != tt.wantFound { 129 t.Errorf("Find got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) 130 } 131 }) 132 } 133 } 134 135 // log2 computes the binary logarithm of x, rounded up to the next integer. 136 // (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.) 137 func log2(x int) int { 138 n := 0 139 for p := 1; p < x; p += p { 140 // p == 2**n 141 n++ 142 } 143 // p/2 < x <= p == 2**n 144 return n 145 } 146 147 func TestSearchEfficiency(t *testing.T) { 148 n := 100 149 step := 1 150 for exp := 2; exp < 10; exp++ { 151 // n == 10**exp 152 // step == 10**(exp-2) 153 max := log2(n) 154 for x := 0; x < n; x += step { 155 count := 0 156 i := Search(n, func(i int) bool { count++; return i >= x }) 157 if i != x { 158 t.Errorf("n = %d: expected index %d; got %d", n, x, i) 159 } 160 if count > max { 161 t.Errorf("n = %d, x = %d: expected <= %d calls; got %d", n, x, max, count) 162 } 163 } 164 n *= 10 165 step *= 10 166 } 167 } 168 169 // Smoke tests for convenience wrappers - not comprehensive. 170 171 var fdata = []float64{0: -3.14, 1: 0, 2: 1, 3: 2, 4: 1000.7} 172 var sdata = []string{0: "f", 1: "foo", 2: "foobar", 3: "x"} 173 174 var wrappertests = []struct { 175 name string 176 result int 177 i int 178 }{ 179 {"SearchInts", SearchInts(data, 11), 8}, 180 {"SearchFloat64s", SearchFloat64s(fdata, 2.1), 4}, 181 {"SearchStrings", SearchStrings(sdata, ""), 0}, 182 {"IntSlice.Search", IntSlice(data).Search(0), 2}, 183 {"Float64Slice.Search", Float64Slice(fdata).Search(2.0), 3}, 184 {"StringSlice.Search", StringSlice(sdata).Search("x"), 3}, 185 } 186 187 func TestSearchWrappers(t *testing.T) { 188 for _, e := range wrappertests { 189 if e.result != e.i { 190 t.Errorf("%s: expected index %d; got %d", e.name, e.i, e.result) 191 } 192 } 193 } 194 195 func runSearchWrappers() { 196 SearchInts(data, 11) 197 SearchFloat64s(fdata, 2.1) 198 SearchStrings(sdata, "") 199 IntSlice(data).Search(0) 200 Float64Slice(fdata).Search(2.0) 201 StringSlice(sdata).Search("x") 202 } 203 204 func TestSearchWrappersDontAlloc(t *testing.T) { 205 if testing.Short() { 206 t.Skip("skipping malloc count in short mode") 207 } 208 if runtime.GOMAXPROCS(0) > 1 { 209 t.Skip("skipping; GOMAXPROCS>1") 210 } 211 allocs := testing.AllocsPerRun(100, runSearchWrappers) 212 if allocs != 0 { 213 t.Errorf("expected no allocs for runSearchWrappers, got %v", allocs) 214 } 215 } 216 217 func BenchmarkSearchWrappers(b *testing.B) { 218 for i := 0; i < b.N; i++ { 219 runSearchWrappers() 220 } 221 } 222 223 // Abstract exhaustive test: all sizes up to 100, 224 // all possible return values. If there are any small 225 // corner cases, this test exercises them. 226 func TestSearchExhaustive(t *testing.T) { 227 for size := 0; size <= 100; size++ { 228 for targ := 0; targ <= size; targ++ { 229 i := Search(size, func(i int) bool { return i >= targ }) 230 if i != targ { 231 t.Errorf("Search(%d, %d) = %d", size, targ, i) 232 } 233 } 234 } 235 } 236 237 // Abstract exhaustive test for Find. 238 func TestFindExhaustive(t *testing.T) { 239 // Test Find for different sequence sizes and search targets. 240 // For each size, we have a (unmaterialized) sequence of integers: 241 // 2,4...size*2 242 // And we're looking for every possible integer between 1 and size*2 + 1. 243 for size := 0; size <= 100; size++ { 244 for x := 1; x <= size*2+1; x++ { 245 var wantFound bool 246 var wantPos int 247 248 cmp := func(i int) int { 249 // Encodes the unmaterialized sequence with elem[i] == (i+1)*2 250 return x - (i+1)*2 251 } 252 pos, found := Find(size, cmp) 253 254 if x%2 == 0 { 255 wantPos = x/2 - 1 256 wantFound = true 257 } else { 258 wantPos = x / 2 259 wantFound = false 260 } 261 if found != wantFound || pos != wantPos { 262 t.Errorf("Find(%d, %d): got (%v, %v), want (%v, %v)", size, x, pos, found, wantPos, wantFound) 263 } 264 } 265 } 266 }