diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/sort/sort_test.go | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/sort/sort_test.go')
-rw-r--r-- | src/sort/sort_test.go | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/src/sort/sort_test.go b/src/sort/sort_test.go new file mode 100644 index 000000000..6c36f30e0 --- /dev/null +++ b/src/sort/sort_test.go @@ -0,0 +1,553 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sort_test + +import ( + "fmt" + "math" + "math/rand" + . "sort" + "strconv" + "testing" +) + +var ints = [...]int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586} +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} +var strings = [...]string{"", "Hello", "foo", "bar", "foo", "f00", "%*&^*&^&", "***"} + +func TestSortIntSlice(t *testing.T) { + data := ints + a := IntSlice(data[0:]) + Sort(a) + if !IsSorted(a) { + t.Errorf("sorted %v", ints) + t.Errorf(" got %v", data) + } +} + +func TestSortFloat64Slice(t *testing.T) { + data := float64s + a := Float64Slice(data[0:]) + Sort(a) + if !IsSorted(a) { + t.Errorf("sorted %v", float64s) + t.Errorf(" got %v", data) + } +} + +func TestSortStringSlice(t *testing.T) { + data := strings + a := StringSlice(data[0:]) + Sort(a) + if !IsSorted(a) { + t.Errorf("sorted %v", strings) + t.Errorf(" got %v", data) + } +} + +func TestInts(t *testing.T) { + data := ints + Ints(data[0:]) + if !IntsAreSorted(data[0:]) { + t.Errorf("sorted %v", ints) + t.Errorf(" got %v", data) + } +} + +func TestFloat64s(t *testing.T) { + data := float64s + Float64s(data[0:]) + if !Float64sAreSorted(data[0:]) { + t.Errorf("sorted %v", float64s) + t.Errorf(" got %v", data) + } +} + +func TestStrings(t *testing.T) { + data := strings + Strings(data[0:]) + if !StringsAreSorted(data[0:]) { + t.Errorf("sorted %v", strings) + t.Errorf(" got %v", data) + } +} + +func TestSortLarge_Random(t *testing.T) { + n := 1000000 + if testing.Short() { + n /= 100 + } + data := make([]int, n) + for i := 0; i < len(data); i++ { + data[i] = rand.Intn(100) + } + if IntsAreSorted(data) { + t.Fatalf("terrible rand.rand") + } + Ints(data) + if !IntsAreSorted(data) { + t.Errorf("sort didn't sort - 1M ints") + } +} + +func TestReverseSortIntSlice(t *testing.T) { + data := ints + data1 := ints + a := IntSlice(data[0:]) + Sort(a) + r := IntSlice(data1[0:]) + Sort(Reverse(r)) + for i := 0; i < len(data); i++ { + if a[i] != r[len(data)-1-i] { + t.Errorf("reverse sort didn't sort") + } + if i > len(data)/2 { + break + } + } +} + +func BenchmarkSortString1K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]string, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = strconv.Itoa(i ^ 0x2cc) + } + b.StartTimer() + Strings(data) + b.StopTimer() + } +} + +func BenchmarkStableString1K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]string, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = strconv.Itoa(i ^ 0x2cc) + } + b.StartTimer() + Stable(StringSlice(data)) + b.StopTimer() + } +} + +func BenchmarkSortInt1K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = i ^ 0x2cc + } + b.StartTimer() + Ints(data) + b.StopTimer() + } +} + +func BenchmarkStableInt1K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = i ^ 0x2cc + } + b.StartTimer() + Stable(IntSlice(data)) + b.StopTimer() + } +} + +func BenchmarkSortInt64K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<16) + for i := 0; i < len(data); i++ { + data[i] = i ^ 0xcccc + } + b.StartTimer() + Ints(data) + b.StopTimer() + } +} + +func BenchmarkStableInt64K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<16) + for i := 0; i < len(data); i++ { + data[i] = i ^ 0xcccc + } + b.StartTimer() + Stable(IntSlice(data)) + b.StopTimer() + } +} + +const ( + _Sawtooth = iota + _Rand + _Stagger + _Plateau + _Shuffle + _NDist +) + +const ( + _Copy = iota + _Reverse + _ReverseFirstHalf + _ReverseSecondHalf + _Sorted + _Dither + _NMode +) + +type testingData struct { + desc string + t *testing.T + data []int + maxswap int // number of swaps allowed + ncmp, nswap int +} + +func (d *testingData) Len() int { return len(d.data) } +func (d *testingData) Less(i, j int) bool { + d.ncmp++ + return d.data[i] < d.data[j] +} +func (d *testingData) Swap(i, j int) { + if d.nswap >= d.maxswap { + d.t.Errorf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data)) + d.t.FailNow() + } + d.nswap++ + d.data[i], d.data[j] = d.data[j], d.data[i] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func lg(n int) int { + i := 0 + for 1<<uint(i) < n { + i++ + } + return i +} + +func testBentleyMcIlroy(t *testing.T, sort func(Interface), maxswap func(int) int) { + sizes := []int{100, 1023, 1024, 1025} + if testing.Short() { + sizes = []int{100, 127, 128, 129} + } + dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"} + modes := []string{"copy", "reverse", "reverse1", "reverse2", "sort", "dither"} + var tmp1, tmp2 [1025]int + for _, n := range sizes { + for m := 1; m < 2*n; m *= 2 { + for dist := 0; dist < _NDist; dist++ { + j := 0 + k := 1 + data := tmp1[0:n] + for i := 0; i < n; i++ { + switch dist { + case _Sawtooth: + data[i] = i % m + case _Rand: + data[i] = rand.Intn(m) + case _Stagger: + data[i] = (i*m + i) % n + case _Plateau: + data[i] = min(i, m) + case _Shuffle: + if rand.Intn(m) != 0 { + j += 2 + data[i] = j + } else { + k += 2 + data[i] = k + } + } + } + + mdata := tmp2[0:n] + for mode := 0; mode < _NMode; mode++ { + switch mode { + case _Copy: + for i := 0; i < n; i++ { + mdata[i] = data[i] + } + case _Reverse: + for i := 0; i < n; i++ { + mdata[i] = data[n-i-1] + } + case _ReverseFirstHalf: + for i := 0; i < n/2; i++ { + mdata[i] = data[n/2-i-1] + } + for i := n / 2; i < n; i++ { + mdata[i] = data[i] + } + case _ReverseSecondHalf: + for i := 0; i < n/2; i++ { + mdata[i] = data[i] + } + for i := n / 2; i < n; i++ { + mdata[i] = data[n-(i-n/2)-1] + } + case _Sorted: + for i := 0; i < n; i++ { + mdata[i] = data[i] + } + // Ints is known to be correct + // because mode Sort runs after mode _Copy. + Ints(mdata) + case _Dither: + for i := 0; i < n; i++ { + mdata[i] = data[i] + i%5 + } + } + + desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode]) + d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: maxswap(n)} + sort(d) + // Uncomment if you are trying to improve the number of compares/swaps. + //t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap) + + // If we were testing C qsort, we'd have to make a copy + // of the slice and sort it ourselves and then compare + // x against it, to ensure that qsort was only permuting + // the data, not (for example) overwriting it with zeros. + // + // In go, we don't have to be so paranoid: since the only + // mutating method Sort can call is TestingData.swap, + // it suffices here just to check that the final slice is sorted. + if !IntsAreSorted(mdata) { + t.Errorf("%s: ints not sorted", desc) + t.Errorf("\t%v", mdata) + t.FailNow() + } + } + } + } + } +} + +func TestSortBM(t *testing.T) { + testBentleyMcIlroy(t, Sort, func(n int) int { return n * lg(n) * 12 / 10 }) +} + +func TestHeapsortBM(t *testing.T) { + testBentleyMcIlroy(t, Heapsort, func(n int) int { return n * lg(n) * 12 / 10 }) +} + +func TestStableBM(t *testing.T) { + testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 }) +} + +// This is based on the "antiquicksort" implementation by M. Douglas McIlroy. +// See http://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info. +type adversaryTestingData struct { + data []int + keys map[int]int + candidate int +} + +func (d *adversaryTestingData) Len() int { return len(d.data) } + +func (d *adversaryTestingData) Less(i, j int) bool { + if _, present := d.keys[i]; !present { + if _, present := d.keys[j]; !present { + if i == d.candidate { + d.keys[i] = len(d.keys) + } else { + d.keys[j] = len(d.keys) + } + } + } + + if _, present := d.keys[i]; !present { + d.candidate = i + return false + } + if _, present := d.keys[j]; !present { + d.candidate = j + return true + } + + return d.keys[i] >= d.keys[j] +} + +func (d *adversaryTestingData) Swap(i, j int) { + d.data[i], d.data[j] = d.data[j], d.data[i] +} + +func TestAdversary(t *testing.T) { + const size = 100 + data := make([]int, size) + for i := 0; i < size; i++ { + data[i] = i + } + + d := &adversaryTestingData{data, make(map[int]int), 0} + Sort(d) // This should degenerate to heapsort. +} + +func TestStableInts(t *testing.T) { + data := ints + Stable(IntSlice(data[0:])) + if !IntsAreSorted(data[0:]) { + t.Errorf("nsorted %v\n got %v", ints, data) + } +} + +type intPairs []struct { + a, b int +} + +// IntPairs compare on a only. +func (d intPairs) Len() int { return len(d) } +func (d intPairs) Less(i, j int) bool { return d[i].a < d[j].a } +func (d intPairs) Swap(i, j int) { d[i], d[j] = d[j], d[i] } + +// Record initial order in B. +func (d intPairs) initB() { + for i := range d { + d[i].b = i + } +} + +// InOrder checks if a-equal elements were not reordered. +func (d intPairs) inOrder() bool { + lastA, lastB := -1, 0 + for i := 0; i < len(d); i++ { + if lastA != d[i].a { + lastA = d[i].a + lastB = d[i].b + continue + } + if d[i].b <= lastB { + return false + } + lastB = d[i].b + } + return true +} + +func TestStability(t *testing.T) { + n, m := 100000, 1000 + if testing.Short() { + n, m = 1000, 100 + } + data := make(intPairs, n) + + // random distribution + for i := 0; i < len(data); i++ { + data[i].a = rand.Intn(m) + } + if IsSorted(data) { + t.Fatalf("terrible rand.rand") + } + data.initB() + Stable(data) + if !IsSorted(data) { + t.Errorf("Stable didn't sort %d ints", n) + } + if !data.inOrder() { + t.Errorf("Stable wasn't stable on %d ints", n) + } + + // already sorted + data.initB() + Stable(data) + if !IsSorted(data) { + t.Errorf("Stable shuffeled sorted %d ints (order)", n) + } + if !data.inOrder() { + t.Errorf("Stable shuffeled sorted %d ints (stability)", n) + } + + // sorted reversed + for i := 0; i < len(data); i++ { + data[i].a = len(data) - i + } + data.initB() + Stable(data) + if !IsSorted(data) { + t.Errorf("Stable didn't sort %d ints", n) + } + if !data.inOrder() { + t.Errorf("Stable wasn't stable on %d ints", n) + } +} + +var countOpsSizes = []int{1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6} + +func countOps(t *testing.T, algo func(Interface), name string) { + sizes := countOpsSizes + if testing.Short() { + sizes = sizes[:5] + } + if !testing.Verbose() { + t.Skip("Counting skipped as non-verbose mode.") + } + for _, n := range sizes { + td := testingData{ + desc: name, + t: t, + data: make([]int, n), + maxswap: 1<<31 - 1, + } + for i := 0; i < n; i++ { + td.data[i] = rand.Intn(n / 5) + } + algo(&td) + t.Logf("%s %8d elements: %11d Swap, %10d Less", name, n, td.nswap, td.ncmp) + } +} + +func TestCountStableOps(t *testing.T) { countOps(t, Stable, "Stable") } +func TestCountSortOps(t *testing.T) { countOps(t, Sort, "Sort ") } + +func bench(b *testing.B, size int, algo func(Interface), name string) { + b.StopTimer() + data := make(intPairs, size) + x := ^uint32(0) + for i := 0; i < b.N; i++ { + for n := size - 3; n <= size+3; n++ { + for i := 0; i < len(data); i++ { + x += x + x ^= 1 + if int32(x) < 0 { + x ^= 0x88888eef + } + data[i].a = int(x % uint32(n/5)) + } + data.initB() + b.StartTimer() + algo(data) + b.StopTimer() + if !IsSorted(data) { + b.Errorf("%s did not sort %d ints", name, n) + } + if name == "Stable" && !data.inOrder() { + b.Errorf("%s unstable on %d ints", name, n) + } + } + } +} + +func BenchmarkSort1e2(b *testing.B) { bench(b, 1e2, Sort, "Sort") } +func BenchmarkStable1e2(b *testing.B) { bench(b, 1e2, Stable, "Stable") } +func BenchmarkSort1e4(b *testing.B) { bench(b, 1e4, Sort, "Sort") } +func BenchmarkStable1e4(b *testing.B) { bench(b, 1e4, Stable, "Stable") } +func BenchmarkSort1e6(b *testing.B) { bench(b, 1e6, Sort, "Sort") } +func BenchmarkStable1e6(b *testing.B) { bench(b, 1e6, Stable, "Stable") } |