123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674 |
- // 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"
- "internal/testenv"
- "math"
- "math/rand"
- . "sort"
- "strconv"
- stringspkg "strings"
- "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 TestSlice(t *testing.T) {
- data := strings
- Slice(data[:], func(i, j int) bool {
- return data[i] < data[j]
- })
- if !SliceIsSorted(data[:], func(i, j int) bool { return data[i] < data[j] }) {
- 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
- }
- }
- }
- type nonDeterministicTestingData struct {
- r *rand.Rand
- }
- func (t *nonDeterministicTestingData) Len() int {
- return 500
- }
- func (t *nonDeterministicTestingData) Less(i, j int) bool {
- if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() {
- panic("nondeterministic comparison out of bounds")
- }
- return t.r.Float32() < 0.5
- }
- func (t *nonDeterministicTestingData) Swap(i, j int) {
- if i < 0 || j < 0 || i >= t.Len() || j >= t.Len() {
- panic("nondeterministic comparison out of bounds")
- }
- }
- func TestNonDeterministicComparison(t *testing.T) {
- // Ensure that sort.Sort does not panic when Less returns inconsistent results.
- // See https://golang.org/issue/14377.
- defer func() {
- if r := recover(); r != nil {
- t.Error(r)
- }
- }()
- td := &nonDeterministicTestingData{
- r: rand.New(rand.NewSource(0)),
- }
- for i := 0; i < 10; i++ {
- Sort(td)
- }
- }
- func BenchmarkSortString1K(b *testing.B) {
- b.StopTimer()
- unsorted := make([]string, 1<<10)
- for i := range unsorted {
- unsorted[i] = strconv.Itoa(i ^ 0x2cc)
- }
- data := make([]string, len(unsorted))
- for i := 0; i < b.N; i++ {
- copy(data, unsorted)
- b.StartTimer()
- Strings(data)
- b.StopTimer()
- }
- }
- func BenchmarkSortString1K_Slice(b *testing.B) {
- b.StopTimer()
- unsorted := make([]string, 1<<10)
- for i := range unsorted {
- unsorted[i] = strconv.Itoa(i ^ 0x2cc)
- }
- data := make([]string, len(unsorted))
- for i := 0; i < b.N; i++ {
- copy(data, unsorted)
- b.StartTimer()
- Slice(data, func(i, j int) bool { return data[i] < data[j] })
- b.StopTimer()
- }
- }
- func BenchmarkStableString1K(b *testing.B) {
- b.StopTimer()
- unsorted := make([]string, 1<<10)
- for i := range unsorted {
- unsorted[i] = strconv.Itoa(i ^ 0x2cc)
- }
- data := make([]string, len(unsorted))
- for i := 0; i < b.N; i++ {
- copy(data, unsorted)
- 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()
- unsorted := make([]int, 1<<10)
- for i := range unsorted {
- unsorted[i] = i ^ 0x2cc
- }
- data := make([]int, len(unsorted))
- for i := 0; i < b.N; i++ {
- copy(data, unsorted)
- b.StartTimer()
- Stable(IntSlice(data))
- b.StopTimer()
- }
- }
- func BenchmarkStableInt1K_Slice(b *testing.B) {
- b.StopTimer()
- unsorted := make([]int, 1<<10)
- for i := range unsorted {
- unsorted[i] = i ^ 0x2cc
- }
- data := make([]int, len(unsorted))
- for i := 0; i < b.N; i++ {
- copy(data, unsorted)
- b.StartTimer()
- SliceStable(data, func(i, j int) bool { return data[i] < data[j] })
- 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 BenchmarkSortInt64K_Slice(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()
- Slice(data, func(i, j int) bool { return data[i] < data[j] })
- 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.Fatalf("%s: used %d swaps sorting slice of %d", d.desc, d.nswap, len(d.data))
- }
- 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.Fatalf("%s: ints not sorted\n\t%v", desc, mdata)
- }
- }
- }
- }
- }
- }
- 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 https://www.cs.dartmouth.edu/~doug/mdmspe.pdf for more info.
- type adversaryTestingData struct {
- t *testing.T
- data []int // item values, initialized to special gas value and changed by Less
- maxcmp int // number of comparisons allowed
- ncmp int // number of comparisons (calls to Less)
- nsolid int // number of elements that have been set to non-gas values
- candidate int // guess at current pivot
- gas int // special value for unset elements, higher than everything else
- }
- func (d *adversaryTestingData) Len() int { return len(d.data) }
- func (d *adversaryTestingData) Less(i, j int) bool {
- if d.ncmp >= d.maxcmp {
- d.t.Fatalf("used %d comparisons sorting adversary data with size %d", d.ncmp, len(d.data))
- }
- d.ncmp++
- if d.data[i] == d.gas && d.data[j] == d.gas {
- if i == d.candidate {
- // freeze i
- d.data[i] = d.nsolid
- d.nsolid++
- } else {
- // freeze j
- d.data[j] = d.nsolid
- d.nsolid++
- }
- }
- if d.data[i] == d.gas {
- d.candidate = i
- } else if d.data[j] == d.gas {
- d.candidate = j
- }
- return d.data[i] < d.data[j]
- }
- func (d *adversaryTestingData) Swap(i, j int) {
- d.data[i], d.data[j] = d.data[j], d.data[i]
- }
- func newAdversaryTestingData(t *testing.T, size int, maxcmp int) *adversaryTestingData {
- gas := size - 1
- data := make([]int, size)
- for i := 0; i < size; i++ {
- data[i] = gas
- }
- return &adversaryTestingData{t: t, data: data, maxcmp: maxcmp, gas: gas}
- }
- func TestAdversary(t *testing.T) {
- const size = 10000 // large enough to distinguish between O(n^2) and O(n*log(n))
- maxcmp := size * lg(size) * 4 // the factor 4 was found by trial and error
- d := newAdversaryTestingData(t, size, maxcmp)
- Sort(d) // This should degenerate to heapsort.
- // Check data is fully populated and sorted.
- for i, v := range d.data {
- if v != i {
- t.Fatalf("adversary data not fully sorted")
- }
- }
- }
- 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 shuffled sorted %d ints (order)", n)
- }
- if !data.inOrder() {
- t.Errorf("Stable shuffled 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) {
- if stringspkg.HasSuffix(testenv.Builder(), "-race") && size > 1e4 {
- b.Skip("skipping slow benchmark on race builder")
- }
- 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") }
|