github.com/dorkamotorka/go/src@v0.0.0-20230614113921-187095f0e316/slices/sort_test.go (about) 1 // Copyright 2023 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 slices 6 7 import ( 8 "cmp" 9 "fmt" 10 "math" 11 "math/rand" 12 "sort" 13 "strconv" 14 "strings" 15 "testing" 16 ) 17 18 var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586} 19 var float64s = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8, 74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3} 20 var float64sWithNaNs = [...]float64{74.3, 59.0, math.Inf(1), 238.2, -784.0, 2.3, math.NaN(), math.NaN(), math.Inf(-1), 9845.768, -959.7485, 905, 7.8, 7.8} 21 var strs = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"} 22 23 func TestSortIntSlice(t *testing.T) { 24 data := Clone(ints[:]) 25 Sort(data) 26 if !IsSorted(data) { 27 t.Errorf("sorted %v", ints) 28 t.Errorf(" got %v", data) 29 } 30 } 31 32 func TestSortFuncIntSlice(t *testing.T) { 33 data := Clone(ints[:]) 34 SortFunc(data, func(a, b int) int { return a - b }) 35 if !IsSorted(data) { 36 t.Errorf("sorted %v", ints) 37 t.Errorf(" got %v", data) 38 } 39 } 40 41 func TestSortFloat64Slice(t *testing.T) { 42 data := Clone(float64s[:]) 43 Sort(data) 44 if !IsSorted(data) { 45 t.Errorf("sorted %v", float64s) 46 t.Errorf(" got %v", data) 47 } 48 } 49 50 func TestSortFloat64SliceWithNaNs(t *testing.T) { 51 data := float64sWithNaNs[:] 52 data2 := Clone(data) 53 54 Sort(data) 55 sort.Float64s(data2) 56 57 if !IsSorted(data) { 58 t.Error("IsSorted indicates data isn't sorted") 59 } 60 61 // Compare for equality using cmp.Compare, which considers NaNs equal. 62 if !EqualFunc(data, data2, func(a, b float64) bool { return cmp.Compare(a, b) == 0 }) { 63 t.Errorf("mismatch between Sort and sort.Float64: got %v, want %v", data, data2) 64 } 65 } 66 67 func TestSortStringSlice(t *testing.T) { 68 data := Clone(strs[:]) 69 Sort(data) 70 if !IsSorted(data) { 71 t.Errorf("sorted %v", strs) 72 t.Errorf(" got %v", data) 73 } 74 } 75 76 func TestSortLarge_Random(t *testing.T) { 77 n := 1000000 78 if testing.Short() { 79 n /= 100 80 } 81 data := make([]int, n) 82 for i := 0; i < len(data); i++ { 83 data[i] = rand.Intn(100) 84 } 85 if IsSorted(data) { 86 t.Fatalf("terrible rand.rand") 87 } 88 Sort(data) 89 if !IsSorted(data) { 90 t.Errorf("sort didn't sort - 1M ints") 91 } 92 } 93 94 type intPair struct { 95 a, b int 96 } 97 98 type intPairs []intPair 99 100 // Pairs compare on a only. 101 func intPairCmp(x, y intPair) int { 102 return x.a - y.a 103 } 104 105 // Record initial order in B. 106 func (d intPairs) initB() { 107 for i := range d { 108 d[i].b = i 109 } 110 } 111 112 // InOrder checks if a-equal elements were not reordered. 113 func (d intPairs) inOrder() bool { 114 lastA, lastB := -1, 0 115 for i := 0; i < len(d); i++ { 116 if lastA != d[i].a { 117 lastA = d[i].a 118 lastB = d[i].b 119 continue 120 } 121 if d[i].b <= lastB { 122 return false 123 } 124 lastB = d[i].b 125 } 126 return true 127 } 128 129 func TestStability(t *testing.T) { 130 n, m := 100000, 1000 131 if testing.Short() { 132 n, m = 1000, 100 133 } 134 data := make(intPairs, n) 135 136 // random distribution 137 for i := 0; i < len(data); i++ { 138 data[i].a = rand.Intn(m) 139 } 140 if IsSortedFunc(data, intPairCmp) { 141 t.Fatalf("terrible rand.rand") 142 } 143 data.initB() 144 SortStableFunc(data, intPairCmp) 145 if !IsSortedFunc(data, intPairCmp) { 146 t.Errorf("Stable didn't sort %d ints", n) 147 } 148 if !data.inOrder() { 149 t.Errorf("Stable wasn't stable on %d ints", n) 150 } 151 152 // already sorted 153 data.initB() 154 SortStableFunc(data, intPairCmp) 155 if !IsSortedFunc(data, intPairCmp) { 156 t.Errorf("Stable shuffled sorted %d ints (order)", n) 157 } 158 if !data.inOrder() { 159 t.Errorf("Stable shuffled sorted %d ints (stability)", n) 160 } 161 162 // sorted reversed 163 for i := 0; i < len(data); i++ { 164 data[i].a = len(data) - i 165 } 166 data.initB() 167 SortStableFunc(data, intPairCmp) 168 if !IsSortedFunc(data, intPairCmp) { 169 t.Errorf("Stable didn't sort %d ints", n) 170 } 171 if !data.inOrder() { 172 t.Errorf("Stable wasn't stable on %d ints", n) 173 } 174 } 175 176 func TestMinMax(t *testing.T) { 177 intCmp := func(a, b int) int { return a - b } 178 179 tests := []struct { 180 data []int 181 wantMin int 182 wantMax int 183 }{ 184 {[]int{7}, 7, 7}, 185 {[]int{1, 2}, 1, 2}, 186 {[]int{2, 1}, 1, 2}, 187 {[]int{1, 2, 3}, 1, 3}, 188 {[]int{3, 2, 1}, 1, 3}, 189 {[]int{2, 1, 3}, 1, 3}, 190 {[]int{2, 2, 3}, 2, 3}, 191 {[]int{3, 2, 3}, 2, 3}, 192 {[]int{0, 2, -9}, -9, 2}, 193 } 194 for _, tt := range tests { 195 t.Run(fmt.Sprintf("%v", tt.data), func(t *testing.T) { 196 gotMin := Min(tt.data) 197 if gotMin != tt.wantMin { 198 t.Errorf("Min got %v, want %v", gotMin, tt.wantMin) 199 } 200 201 gotMinFunc := MinFunc(tt.data, intCmp) 202 if gotMinFunc != tt.wantMin { 203 t.Errorf("MinFunc got %v, want %v", gotMinFunc, tt.wantMin) 204 } 205 206 gotMax := Max(tt.data) 207 if gotMax != tt.wantMax { 208 t.Errorf("Max got %v, want %v", gotMax, tt.wantMax) 209 } 210 211 gotMaxFunc := MaxFunc(tt.data, intCmp) 212 if gotMaxFunc != tt.wantMax { 213 t.Errorf("MaxFunc got %v, want %v", gotMaxFunc, tt.wantMax) 214 } 215 }) 216 } 217 } 218 219 func TestMinMaxNaNs(t *testing.T) { 220 fs := []float64{1.0, 999.9, 3.14, -400.4, -5.14} 221 if Min(fs) != -400.4 { 222 t.Errorf("got min %v, want -400.4", Min(fs)) 223 } 224 if Max(fs) != 999.9 { 225 t.Errorf("got max %v, want 999.9", Max(fs)) 226 } 227 228 // No matter which element of fs is replaced with a NaN, both Min and Max 229 // should propagate the NaN to their output. 230 for i := 0; i < len(fs); i++ { 231 testfs := Clone(fs) 232 testfs[i] = math.NaN() 233 234 fmin := Min(testfs) 235 if !math.IsNaN(fmin) { 236 t.Errorf("got min %v, want NaN", fmin) 237 } 238 239 fmax := Max(testfs) 240 if !math.IsNaN(fmax) { 241 t.Errorf("got max %v, want NaN", fmax) 242 } 243 } 244 } 245 246 func TestMinMaxPanics(t *testing.T) { 247 intCmp := func(a, b int) int { return a - b } 248 emptySlice := []int{} 249 250 if !panics(func() { Min(emptySlice) }) { 251 t.Errorf("Min([]): got no panic, want panic") 252 } 253 254 if !panics(func() { Max(emptySlice) }) { 255 t.Errorf("Max([]): got no panic, want panic") 256 } 257 258 if !panics(func() { MinFunc(emptySlice, intCmp) }) { 259 t.Errorf("MinFunc([]): got no panic, want panic") 260 } 261 262 if !panics(func() { MaxFunc(emptySlice, intCmp) }) { 263 t.Errorf("MaxFunc([]): got no panic, want panic") 264 } 265 } 266 267 func TestBinarySearch(t *testing.T) { 268 str1 := []string{"foo"} 269 str2 := []string{"ab", "ca"} 270 str3 := []string{"mo", "qo", "vo"} 271 str4 := []string{"ab", "ad", "ca", "xy"} 272 273 // slice with repeating elements 274 strRepeats := []string{"ba", "ca", "da", "da", "da", "ka", "ma", "ma", "ta"} 275 276 // slice with all element equal 277 strSame := []string{"xx", "xx", "xx"} 278 279 tests := []struct { 280 data []string 281 target string 282 wantPos int 283 wantFound bool 284 }{ 285 {[]string{}, "foo", 0, false}, 286 {[]string{}, "", 0, false}, 287 288 {str1, "foo", 0, true}, 289 {str1, "bar", 0, false}, 290 {str1, "zx", 1, false}, 291 292 {str2, "aa", 0, false}, 293 {str2, "ab", 0, true}, 294 {str2, "ad", 1, false}, 295 {str2, "ca", 1, true}, 296 {str2, "ra", 2, false}, 297 298 {str3, "bb", 0, false}, 299 {str3, "mo", 0, true}, 300 {str3, "nb", 1, false}, 301 {str3, "qo", 1, true}, 302 {str3, "tr", 2, false}, 303 {str3, "vo", 2, true}, 304 {str3, "xr", 3, false}, 305 306 {str4, "aa", 0, false}, 307 {str4, "ab", 0, true}, 308 {str4, "ac", 1, false}, 309 {str4, "ad", 1, true}, 310 {str4, "ax", 2, false}, 311 {str4, "ca", 2, true}, 312 {str4, "cc", 3, false}, 313 {str4, "dd", 3, false}, 314 {str4, "xy", 3, true}, 315 {str4, "zz", 4, false}, 316 317 {strRepeats, "da", 2, true}, 318 {strRepeats, "db", 5, false}, 319 {strRepeats, "ma", 6, true}, 320 {strRepeats, "mb", 8, false}, 321 322 {strSame, "xx", 0, true}, 323 {strSame, "ab", 0, false}, 324 {strSame, "zz", 3, false}, 325 } 326 for _, tt := range tests { 327 t.Run(tt.target, func(t *testing.T) { 328 { 329 pos, found := BinarySearch(tt.data, tt.target) 330 if pos != tt.wantPos || found != tt.wantFound { 331 t.Errorf("BinarySearch got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) 332 } 333 } 334 335 { 336 pos, found := BinarySearchFunc(tt.data, tt.target, strings.Compare) 337 if pos != tt.wantPos || found != tt.wantFound { 338 t.Errorf("BinarySearchFunc got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) 339 } 340 } 341 }) 342 } 343 } 344 345 func TestBinarySearchInts(t *testing.T) { 346 data := []int{20, 30, 40, 50, 60, 70, 80, 90} 347 tests := []struct { 348 target int 349 wantPos int 350 wantFound bool 351 }{ 352 {20, 0, true}, 353 {23, 1, false}, 354 {43, 3, false}, 355 {80, 6, true}, 356 } 357 for _, tt := range tests { 358 t.Run(strconv.Itoa(tt.target), func(t *testing.T) { 359 { 360 pos, found := BinarySearch(data, tt.target) 361 if pos != tt.wantPos || found != tt.wantFound { 362 t.Errorf("BinarySearch got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) 363 } 364 } 365 366 { 367 cmp := func(a, b int) int { 368 return a - b 369 } 370 pos, found := BinarySearchFunc(data, tt.target, cmp) 371 if pos != tt.wantPos || found != tt.wantFound { 372 t.Errorf("BinarySearchFunc got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) 373 } 374 } 375 }) 376 } 377 } 378 379 func TestBinarySearchFloats(t *testing.T) { 380 data := []float64{math.NaN(), -0.25, 0.0, 1.4} 381 tests := []struct { 382 target float64 383 wantPos int 384 wantFound bool 385 }{ 386 {math.NaN(), 0, true}, 387 {math.Inf(-1), 1, false}, 388 {-0.25, 1, true}, 389 {0.0, 2, true}, 390 {1.4, 3, true}, 391 {1.5, 4, false}, 392 } 393 for _, tt := range tests { 394 t.Run(fmt.Sprintf("%v", tt.target), func(t *testing.T) { 395 { 396 pos, found := BinarySearch(data, tt.target) 397 if pos != tt.wantPos || found != tt.wantFound { 398 t.Errorf("BinarySearch got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound) 399 } 400 } 401 }) 402 } 403 } 404 405 func TestBinarySearchFunc(t *testing.T) { 406 data := []int{1, 10, 11, 2} // sorted lexicographically 407 cmp := func(a int, b string) int { 408 return strings.Compare(strconv.Itoa(a), b) 409 } 410 pos, found := BinarySearchFunc(data, "2", cmp) 411 if pos != 3 || !found { 412 t.Errorf("BinarySearchFunc(%v, %q, cmp) = %v, %v, want %v, %v", data, "2", pos, found, 3, true) 413 } 414 }