github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/sort/sort_test.gno (about) 1 // Copyright 2009 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 "fmt" 9 "math/rand" 10 "sort" 11 "strconv" 12 "testing" 13 ) 14 15 var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586} 16 17 // var float64s = [...]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} 18 var strings = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"} 19 20 /*a XXX removed slice due to reflect methods 21 func TestSortIntSlice(t *testing.T) { 22 data := ints 23 a := IntSlice(data[0:]) 24 Sort(a) 25 if !IsSorted(a) { 26 t.Errorf("sorted %v", ints) 27 t.Errorf(" got %v", data) 28 } 29 } 30 31 func TestSortFloat64Slice(t *testing.T) { 32 data := float64s 33 a := Float64Slice(data[0:]) 34 Sort(a) 35 if !IsSorted(a) { 36 t.Errorf("sorted %v", float64s) 37 t.Errorf(" got %v", data) 38 } 39 } 40 41 func TestSortStringSlice(t *testing.T) { 42 data := strings 43 a := StringSlice(data[0:]) 44 Sort(a) 45 if !IsSorted(a) { 46 t.Errorf("sorted %v", strings) 47 t.Errorf(" got %v", data) 48 } 49 } 50 */ 51 52 func TestInts(t *testing.T) { 53 data := ints 54 sort.Ints(data[0:]) 55 if !sort.IntsAreSorted(data[0:]) { 56 t.Errorf("sorted %v", ints) 57 t.Errorf(" got %v", data) 58 } 59 } 60 61 /* removed due to float 62 func TestFloat64s(t *testing.T) { 63 data := float64s 64 Float64s(data[0:]) 65 if !Float64sAreSorted(data[0:]) { 66 t.Errorf("sorted %v", float64s) 67 t.Errorf(" got %v", data) 68 } 69 } 70 */ 71 72 func TestStrings(t *testing.T) { 73 data := strings 74 sort.Strings(data[0:]) 75 if !sort.StringsAreSorted(data[0:]) { 76 t.Errorf("sorted %v", strings) 77 t.Errorf(" got %v", data) 78 } 79 } 80 81 /* 82 func TestSlice(t *testing.T) { 83 data := strings 84 Slice(data[:], func(i, j int) bool { 85 return data[i] < data[j] 86 }) 87 if !SliceIsSorted(data[:], func(i, j int) bool { return data[i] < data[j] }) { 88 t.Errorf("sorted %v", strings) 89 t.Errorf(" got %v", data) 90 } 91 } 92 */ 93 94 func TestSortLarge_Random(t *testing.T) { 95 n := 1000000 96 if testing.Short() { 97 n /= 100 98 } 99 data := make([]int, n) 100 for i := 0; i < len(data); i++ { 101 data[i] = rand.Intn(100) 102 } 103 if sort.IntsAreSorted(data) { 104 t.Fatalf("terrible rand.rand") 105 } 106 sort.Ints(data) 107 if !sort.IntsAreSorted(data) { 108 t.Errorf("sort didn't sort - 1M ints") 109 } 110 } 111 112 /* 113 func TestReverseSortIntSlice(t *testing.T) { 114 data := ints 115 data1 := ints 116 a := IntSlice(data[0:]) 117 Sort(a) 118 r := IntSlice(data1[0:]) 119 Sort(Reverse(r)) 120 for i := 0; i < len(data); i++ { 121 if a[i] != r[len(data)-1-i] { 122 t.Errorf("reverse sort didn't sort") 123 } 124 if i > len(data)/2 { 125 break 126 } 127 } 128 } 129 */ 130 131 type nonDeterministicTestingData struct { 132 r *rand.Rand 133 } 134 135 func (t *nonDeterministicTestingData) Len() int { 136 return 500 137 } 138 139 func (t *nonDeterministicTestingData) Less(i, j int) bool { 140 if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() { 141 panic("nondeterministic comparison out of bounds") 142 } 143 return t.r.Int()%2 == 0 144 // return t.r.Float32() < 0.5 145 } 146 147 func (t *nonDeterministicTestingData) Swap(i, j int) { 148 if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() { 149 panic("nondeterministic comparison out of bounds") 150 } 151 } 152 153 func TestNonDeterministicComparison(t *testing.T) { 154 // Ensure that sort.Sort does not panic when Less returns inconsistent results. 155 // See https://golang.org/issue/14377. 156 defer func() { 157 if r := recover(); r != nil { 158 t.Error(r) 159 } 160 }() 161 162 td := &nonDeterministicTestingData{ 163 r: rand.New(rand.NewSource(0)), 164 } 165 166 for i := 0; i < 10; i++ { 167 sort.Sort(td) 168 } 169 } 170 171 func BenchmarkSortString1K(b *testing.B) { 172 b.StopTimer() 173 unsorted := make([]string, 1<<10) 174 for i := range unsorted { 175 unsorted[i] = strconv.Itoa(i ^ 0x2cc) 176 } 177 data := make([]string, len(unsorted)) 178 179 for i := 0; i < b.N; i++ { 180 copy(data, unsorted) 181 b.StartTimer() 182 sort.Strings(data) 183 b.StopTimer() 184 } 185 } 186 187 /* 188 func BenchmarkSortString1K_Slice(b *testing.B) { 189 b.StopTimer() 190 unsorted := make([]string, 1<<10) 191 for i := range unsorted { 192 unsorted[i] = strconv.Itoa(i ^ 0x2cc) 193 } 194 data := make([]string, len(unsorted)) 195 196 for i := 0; i < b.N; i++ { 197 copy(data, unsorted) 198 b.StartTimer() 199 Slice(data, func(i, j int) bool { return data[i] < data[j] }) 200 b.StopTimer() 201 } 202 } 203 204 func BenchmarkStableString1K(b *testing.B) { 205 b.StopTimer() 206 unsorted := make([]string, 1<<10) 207 for i := range unsorted { 208 unsorted[i] = strconv.Itoa(i ^ 0x2cc) 209 } 210 data := make([]string, len(unsorted)) 211 212 for i := 0; i < b.N; i++ { 213 copy(data, unsorted) 214 b.StartTimer() 215 Stable(StringSlice(data)) 216 b.StopTimer() 217 } 218 } 219 */ 220 221 func BenchmarkSortInt1K(b *testing.B) { 222 b.StopTimer() 223 for i := 0; i < b.N; i++ { 224 data := make([]int, 1<<10) 225 for i := 0; i < len(data); i++ { 226 data[i] = i ^ 0x2cc 227 } 228 b.StartTimer() 229 sort.Ints(data) 230 b.StopTimer() 231 } 232 } 233 234 /* 235 func BenchmarkStableInt1K(b *testing.B) { 236 b.StopTimer() 237 unsorted := make([]int, 1<<10) 238 for i := range unsorted { 239 unsorted[i] = i ^ 0x2cc 240 } 241 data := make([]int, len(unsorted)) 242 for i := 0; i < b.N; i++ { 243 copy(data, unsorted) 244 b.StartTimer() 245 Stable(IntSlice(data)) 246 b.StopTimer() 247 } 248 } 249 250 func BenchmarkStableInt1K_Slice(b *testing.B) { 251 b.StopTimer() 252 unsorted := make([]int, 1<<10) 253 for i := range unsorted { 254 unsorted[i] = i ^ 0x2cc 255 } 256 data := make([]int, len(unsorted)) 257 for i := 0; i < b.N; i++ { 258 copy(data, unsorted) 259 b.StartTimer() 260 SliceStable(data, func(i, j int) bool { return data[i] < data[j] }) 261 b.StopTimer() 262 } 263 } 264 265 func BenchmarkSortInt64K(b *testing.B) { 266 b.StopTimer() 267 for i := 0; i < b.N; i++ { 268 data := make([]int, 1<<16) 269 for i := 0; i < len(data); i++ { 270 data[i] = i ^ 0xcccc 271 } 272 b.StartTimer() 273 Ints(data) 274 b.StopTimer() 275 } 276 } 277 278 func BenchmarkSortInt64K_Slice(b *testing.B) { 279 b.StopTimer() 280 for i := 0; i < b.N; i++ { 281 data := make([]int, 1<<16) 282 for i := 0; i < len(data); i++ { 283 data[i] = i ^ 0xcccc 284 } 285 b.StartTimer() 286 Slice(data, func(i, j int) bool { return data[i] < data[j] }) 287 b.StopTimer() 288 } 289 } 290 291 func BenchmarkStableInt64K(b *testing.B) { 292 b.StopTimer() 293 for i := 0; i < b.N; i++ { 294 data := make([]int, 1<<16) 295 for i := 0; i < len(data); i++ { 296 data[i] = i ^ 0xcccc 297 } 298 b.StartTimer() 299 Stable(IntSlice(data)) 300 b.StopTimer() 301 } 302 } 303 */ 304 305 const ( 306 _Sawtooth = iota 307 _Rand 308 _Stagger 309 _Plateau 310 _Shuffle 311 _NDist 312 ) 313 314 const ( 315 _Copy = iota 316 _Reverse 317 _ReverseFirstHalf 318 _ReverseSecondHalf 319 _Sorted 320 _Dither 321 _NMode 322 ) 323 324 type testingData struct { 325 desc string 326 t *testing.T 327 data []int 328 maxswap int // number of swaps allowed 329 ncmp, nswap int 330 } 331 332 func (d *testingData) Len() int { return len(d.data) } 333 func (d *testingData) Less(i, j int) bool { 334 d.ncmp++ 335 return d.data[i] < d.data[j] 336 } 337 338 func (d *testingData) Swap(i, j int) { 339 if d.nswap >= d.maxswap { 340 d.t.Fatalf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data)) 341 } 342 d.nswap++ 343 d.data[i], d.data[j] = d.data[j], d.data[i] 344 } 345 346 func min(a, b int) int { 347 if a < b { 348 return a 349 } 350 return b 351 } 352 353 func lg(n int) int { 354 i := 0 355 for 1<<uint(i) < n { 356 i++ 357 } 358 return i 359 } 360 361 func testBentleyMcIlroy(t *testing.T, sortFn func(sort.Interface), maxswap func(int) int) { 362 sizes := []int{100, 1023, 1024, 1025} 363 if testing.Short() { 364 sizes = []int{100, 127, 128, 129} 365 } 366 dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"} 367 modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"} 368 var tmp1, tmp2 [1025]int 369 for _, n := range sizes { 370 for m := 1; m < 2*n; m *= 2 { 371 for dist := 0; dist < _NDist; dist++ { 372 j := 0 373 k := 1 374 data := tmp1[0:n] 375 for i := 0; i < n; i++ { 376 switch dist { 377 case _Sawtooth: 378 data[i] = i % m 379 case _Rand: 380 data[i] = rand.Intn(m) 381 case _Stagger: 382 data[i] = (i*m + i) % n 383 case _Plateau: 384 data[i] = min(i, m) 385 case _Shuffle: 386 if rand.Intn(m) != 0 { 387 j += 2 388 data[i] = j 389 } else { 390 k += 2 391 data[i] = k 392 } 393 } 394 } 395 396 mdata := tmp2[0:n] 397 for mode := 0; mode < _NMode; mode++ { 398 switch mode { 399 case _Copy: 400 for i := 0; i < n; i++ { 401 mdata[i] = data[i] 402 } 403 case _Reverse: 404 for i := 0; i < n; i++ { 405 mdata[i] = data[n-i-1] 406 } 407 case _ReverseFirstHalf: 408 for i := 0; i < n/2; i++ { 409 mdata[i] = data[n/2-i-1] 410 } 411 for i := n / 2; i < n; i++ { 412 mdata[i] = data[i] 413 } 414 case _ReverseSecondHalf: 415 for i := 0; i < n/2; i++ { 416 mdata[i] = data[i] 417 } 418 for i := n / 2; i < n; i++ { 419 mdata[i] = data[n-(i-n/2)-1] 420 } 421 case _Sorted: 422 for i := 0; i < n; i++ { 423 mdata[i] = data[i] 424 } 425 // Ints is known to be correct 426 // because mode Sort runs after mode _Copy. 427 sort.Ints(mdata) 428 case _Dither: 429 for i := 0; i < n; i++ { 430 mdata[i] = data[i] + i%5 431 } 432 } 433 434 desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode]) 435 d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: maxswap(n)} 436 sortFn(d) 437 // Uncomment if you are trying to improve the number of compares/swaps. 438 // t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap) 439 440 // If we were testing C qsort, we'd have to make a copy 441 // of the slice and sort it ourselves and then compare 442 // x against it, to ensure that qsort was only permuting 443 // the data, not (for example) overwriting it with zeros. 444 // 445 // In go, we don't have to be so paranoid: since the only 446 // mutating method Sort can call is TestingData.swap, 447 // it suffices here just to check that the final slice is sorted. 448 if !sort.IntsAreSorted(mdata) { 449 t.Fatalf("%s: ints not sorted\n\t%v", desc, mdata) 450 } 451 } 452 } 453 } 454 } 455 } 456 457 func TestSortBM(t *testing.T) { 458 testBentleyMcIlroy(t, sort.Sort, func(n int) int { return n * lg(n) * 12 / 10 }) 459 } 460 461 /* removed because sort.Heapsort removed 462 func TestHeapsortBM(t *testing.T) { 463 testBentleyMcIlroy(t, sort.Heapsort, func(n int) int { return n * lg(n) * 12 / 10 }) 464 } 465 */ 466 467 func TestStableBM(t *testing.T) { 468 testBentleyMcIlroy(t, sort.Stable, func(n int) int { return n * lg(n) * lg(n) / 3 }) 469 } 470 471 // This is based on the "antiquicksort" implementation by M. Douglas McIlroy. 472 // See https://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info. 473 type adversaryTestingData struct { 474 t *testing.T 475 data []int // item values, initialized to special gas value and changed by Less 476 maxcmp int // number of comparisons allowed 477 ncmp int // number of comparisons (calls to Less) 478 nsolid int // number of elements that have been set to non-gas values 479 candidate int // guess at current pivot 480 gas int // special value for unset elements, higher than everything else 481 } 482 483 func (d *adversaryTestingData) Len() int { return len(d.data) } 484 485 func (d *adversaryTestingData) Less(i, j int) bool { 486 if d.ncmp >= d.maxcmp { 487 d.t.Fatalf("used %d comparisons sorting adversary data with size %d", d.ncmp, len(d.data)) 488 } 489 d.ncmp++ 490 491 if d.data[i] == d.gas && d.data[j] == d.gas { 492 if i == d.candidate { 493 // freeze i 494 d.data[i] = d.nsolid 495 d.nsolid++ 496 } else { 497 // freeze j 498 d.data[j] = d.nsolid 499 d.nsolid++ 500 } 501 } 502 503 if d.data[i] == d.gas { 504 d.candidate = i 505 } else if d.data[j] == d.gas { 506 d.candidate = j 507 } 508 509 return d.data[i] < d.data[j] 510 } 511 512 func (d *adversaryTestingData) Swap(i, j int) { 513 d.data[i], d.data[j] = d.data[j], d.data[i] 514 } 515 516 func newAdversaryTestingData(t *testing.T, size int, maxcmp int) *adversaryTestingData { 517 gas := size - 1 518 data := make([]int, size) 519 for i := 0; i < size; i++ { 520 data[i] = gas 521 } 522 return &adversaryTestingData{t: t, data: data, maxcmp: maxcmp, gas: gas} 523 } 524 525 func TestAdversary(t *testing.T) { 526 const size = 10000 // large enough to distinguish between O(n^2) and O(n*log(n)) 527 maxcmp := size * lg(size) * 4 // the factor 4 was found by trial and error 528 d := newAdversaryTestingData(t, size, maxcmp) 529 sort.Sort(d) // This should degenerate to heapsort. 530 // Check data is fully populated and sorted. 531 for i, v := range d.data { 532 if v != i { 533 t.Fatalf("adversary data not fully sorted") 534 } 535 } 536 } 537 538 /* 539 func TestStableInts(t *testing.T) { 540 data := ints 541 Stable(IntSlice(data[0:])) 542 if !IntsAreSorted(data[0:]) { 543 t.Errorf("nsorted %v\n got %v", ints, data) 544 } 545 } 546 */ 547 548 type intPairs []struct { 549 a, b int 550 } 551 552 // IntPairs compare on a only. 553 func (d intPairs) Len() int { return len(d) } 554 func (d intPairs) Less(i, j int) bool { return d[i].a < d[j].a } 555 func (d intPairs) Swap(i, j int) { d[i], d[j] = d[j], d[i] } 556 557 // Record initial order in B. 558 func (d intPairs) initB() { 559 for i := range d { 560 d[i].b = i 561 } 562 } 563 564 // InOrder checks if a-equal elements were not reordered. 565 func (d intPairs) inOrder() bool { 566 lastA, lastB := -1, 0 567 for i := 0; i < len(d); i++ { 568 if lastA != d[i].a { 569 lastA = d[i].a 570 lastB = d[i].b 571 continue 572 } 573 if d[i].b <= lastB { 574 return false 575 } 576 lastB = d[i].b 577 } 578 return true 579 } 580 581 func TestStability(t *testing.T) { 582 n, m := 100000, 1000 583 if testing.Short() { 584 n, m = 1000, 100 585 } 586 data := make(intPairs, n) 587 588 // random distribution 589 for i := 0; i < len(data); i++ { 590 data[i].a = rand.Intn(m) 591 } 592 if sort.IsSorted(data) { 593 t.Fatalf("terrible rand.rand") 594 } 595 data.initB() 596 sort.Stable(data) 597 if !sort.IsSorted(data) { 598 t.Errorf("Stable didn't sort %d ints", n) 599 } 600 if !data.inOrder() { 601 t.Errorf("Stable wasn't stable on %d ints", n) 602 } 603 604 // already sorted 605 data.initB() 606 sort.Stable(data) 607 if !sort.IsSorted(data) { 608 t.Errorf("Stable shuffled sorted %d ints (order)", n) 609 } 610 if !data.inOrder() { 611 t.Errorf("Stable shuffled sorted %d ints (stability)", n) 612 } 613 614 // sorted reversed 615 for i := 0; i < len(data); i++ { 616 data[i].a = len(data) - i 617 } 618 data.initB() 619 sort.Stable(data) 620 if !sort.IsSorted(data) { 621 t.Errorf("Stable didn't sort %d ints", n) 622 } 623 if !data.inOrder() { 624 t.Errorf("Stable wasn't stable on %d ints", n) 625 } 626 } 627 628 var countOpsSizes = []int{1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6} 629 630 func countOps(t *testing.T, algo func(sort.Interface), name string) { 631 sizes := countOpsSizes 632 if testing.Short() { 633 sizes = sizes[:5] 634 } 635 if !testing.Verbose() { 636 t.Skip("Counting skipped as non-verbose mode.") 637 } 638 for _, n := range sizes { 639 td := testingData{ 640 desc: name, 641 t: t, 642 data: make([]int, n), 643 maxswap: 1<<31 - 1, 644 } 645 for i := 0; i < n; i++ { 646 td.data[i] = rand.Intn(n / 5) 647 } 648 algo(&td) 649 t.Logf("%s %8d elements: %11d Swap, %10d Less", name, n, td.nswap, td.ncmp) 650 } 651 } 652 653 func TestCountStableOps(t *testing.T) { countOps(t, sort.Stable, "Stable") } 654 func TestCountSortOps(t *testing.T) { countOps(t, sort.Sort, "Sort ") } 655 656 /* XXX removed due to testenv 657 func bench(b *testing.B, size int, algo func(Interface), name string) { 658 if stringspkg.HasSuffix(testenv.Builder(), "-race") && size > 1e4 { 659 b.Skip("skipping slow benchmark on race builder") 660 } 661 b.StopTimer() 662 data := make(intPairs, size) 663 x := ^uint32(0) 664 for i := 0; i < b.N; i++ { 665 for n := size - 3; n <= size+3; n++ { 666 for i := 0; i < len(data); i++ { 667 x += x 668 x ^= 1 669 if int32(x) < 0 { 670 x ^= 0x88888eef 671 } 672 data[i].a = int(x % uint32(n/5)) 673 } 674 data.initB() 675 b.StartTimer() 676 algo(data) 677 b.StopTimer() 678 if !IsSorted(data) { 679 b.Errorf("%s did not sort %d ints", name, n) 680 } 681 if name == "Stable" && !data.inOrder() { 682 b.Errorf("%s unstable on %d ints", name, n) 683 } 684 } 685 } 686 } 687 688 func BenchmarkSort1e2(b *testing.B) { bench(b, 1e2, Sort, "Sort") } 689 func BenchmarkStable1e2(b *testing.B) { bench(b, 1e2, Stable, "Stable") } 690 func BenchmarkSort1e4(b *testing.B) { bench(b, 1e4, Sort, "Sort") } 691 func BenchmarkStable1e4(b *testing.B) { bench(b, 1e4, Stable, "Stable") } 692 func BenchmarkSort1e6(b *testing.B) { bench(b, 1e6, Sort, "Sort") } 693 func BenchmarkStable1e6(b *testing.B) { bench(b, 1e6, Stable, "Stable") } 694 695 */