diff options
Diffstat (limited to 'src/pkg/sort')
-rw-r--r-- | src/pkg/sort/example_interface_test.go | 44 | ||||
-rw-r--r-- | src/pkg/sort/example_keys_test.go | 96 | ||||
-rw-r--r-- | src/pkg/sort/example_multi_test.go | 131 | ||||
-rw-r--r-- | src/pkg/sort/example_test.go | 24 | ||||
-rw-r--r-- | src/pkg/sort/example_wrapper_test.go | 77 | ||||
-rw-r--r-- | src/pkg/sort/export_test.go | 9 | ||||
-rw-r--r-- | src/pkg/sort/search.go | 112 | ||||
-rw-r--r-- | src/pkg/sort/search_test.go | 161 | ||||
-rw-r--r-- | src/pkg/sort/sort.go | 474 | ||||
-rw-r--r-- | src/pkg/sort/sort_test.go | 553 |
10 files changed, 0 insertions, 1681 deletions
diff --git a/src/pkg/sort/example_interface_test.go b/src/pkg/sort/example_interface_test.go deleted file mode 100644 index 442204ea9..000000000 --- a/src/pkg/sort/example_interface_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2011 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" - "sort" -) - -type Person struct { - Name string - Age int -} - -func (p Person) String() string { - return fmt.Sprintf("%s: %d", p.Name, p.Age) -} - -// ByAge implements sort.Interface for []Person based on -// the Age field. -type ByAge []Person - -func (a ByAge) Len() int { return len(a) } -func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } - -func Example() { - people := []Person{ - {"Bob", 31}, - {"John", 42}, - {"Michael", 17}, - {"Jenny", 26}, - } - - fmt.Println(people) - sort.Sort(ByAge(people)) - fmt.Println(people) - - // Output: - // [Bob: 31 John: 42 Michael: 17 Jenny: 26] - // [Michael: 17 Jenny: 26 Bob: 31 John: 42] -} diff --git a/src/pkg/sort/example_keys_test.go b/src/pkg/sort/example_keys_test.go deleted file mode 100644 index a8e47e492..000000000 --- a/src/pkg/sort/example_keys_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2013 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" - "sort" -) - -// A couple of type definitions to make the units clear. -type earthMass float64 -type au float64 - -// A Planet defines the properties of a solar system object. -type Planet struct { - name string - mass earthMass - distance au -} - -// By is the type of a "less" function that defines the ordering of its Planet arguments. -type By func(p1, p2 *Planet) bool - -// Sort is a method on the function type, By, that sorts the argument slice according to the function. -func (by By) Sort(planets []Planet) { - ps := &planetSorter{ - planets: planets, - by: by, // The Sort method's receiver is the function (closure) that defines the sort order. - } - sort.Sort(ps) -} - -// planetSorter joins a By function and a slice of Planets to be sorted. -type planetSorter struct { - planets []Planet - by func(p1, p2 *Planet) bool // Closure used in the Less method. -} - -// Len is part of sort.Interface. -func (s *planetSorter) Len() int { - return len(s.planets) -} - -// Swap is part of sort.Interface. -func (s *planetSorter) Swap(i, j int) { - s.planets[i], s.planets[j] = s.planets[j], s.planets[i] -} - -// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. -func (s *planetSorter) Less(i, j int) bool { - return s.by(&s.planets[i], &s.planets[j]) -} - -var planets = []Planet{ - {"Mercury", 0.055, 0.4}, - {"Venus", 0.815, 0.7}, - {"Earth", 1.0, 1.0}, - {"Mars", 0.107, 1.5}, -} - -// ExampleSortKeys demonstrates a technique for sorting a struct type using programmable sort criteria. -func Example_sortKeys() { - // Closures that order the Planet structure. - name := func(p1, p2 *Planet) bool { - return p1.name < p2.name - } - mass := func(p1, p2 *Planet) bool { - return p1.mass < p2.mass - } - distance := func(p1, p2 *Planet) bool { - return p1.distance < p2.distance - } - decreasingDistance := func(p1, p2 *Planet) bool { - return !distance(p1, p2) - } - - // Sort the planets by the various criteria. - By(name).Sort(planets) - fmt.Println("By name:", planets) - - By(mass).Sort(planets) - fmt.Println("By mass:", planets) - - By(distance).Sort(planets) - fmt.Println("By distance:", planets) - - By(decreasingDistance).Sort(planets) - fmt.Println("By decreasing distance:", planets) - - // Output: By name: [{Earth 1 1} {Mars 0.107 1.5} {Mercury 0.055 0.4} {Venus 0.815 0.7}] - // By mass: [{Mercury 0.055 0.4} {Mars 0.107 1.5} {Venus 0.815 0.7} {Earth 1 1}] - // By distance: [{Mercury 0.055 0.4} {Venus 0.815 0.7} {Earth 1 1} {Mars 0.107 1.5}] - // By decreasing distance: [{Mars 0.107 1.5} {Earth 1 1} {Venus 0.815 0.7} {Mercury 0.055 0.4}] -} diff --git a/src/pkg/sort/example_multi_test.go b/src/pkg/sort/example_multi_test.go deleted file mode 100644 index ac316540f..000000000 --- a/src/pkg/sort/example_multi_test.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2013 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" - "sort" -) - -// A Change is a record of source code changes, recording user, language, and delta size. -type Change struct { - user string - language string - lines int -} - -type lessFunc func(p1, p2 *Change) bool - -// multiSorter implements the Sort interface, sorting the changes within. -type multiSorter struct { - changes []Change - less []lessFunc -} - -// Sort sorts the argument slice according to the less functions passed to OrderedBy. -func (ms *multiSorter) Sort(changes []Change) { - ms.changes = changes - sort.Sort(ms) -} - -// OrderedBy returns a Sorter that sorts using the less functions, in order. -// Call its Sort method to sort the data. -func OrderedBy(less ...lessFunc) *multiSorter { - return &multiSorter{ - less: less, - } -} - -// Len is part of sort.Interface. -func (ms *multiSorter) Len() int { - return len(ms.changes) -} - -// Swap is part of sort.Interface. -func (ms *multiSorter) Swap(i, j int) { - ms.changes[i], ms.changes[j] = ms.changes[j], ms.changes[i] -} - -// Less is part of sort.Interface. It is implemented by looping along the -// less functions until it finds a comparison that is either Less or -// !Less. Note that it can call the less functions twice per call. We -// could change the functions to return -1, 0, 1 and reduce the -// number of calls for greater efficiency: an exercise for the reader. -func (ms *multiSorter) Less(i, j int) bool { - p, q := &ms.changes[i], &ms.changes[j] - // Try all but the last comparison. - var k int - for k = 0; k < len(ms.less)-1; k++ { - less := ms.less[k] - switch { - case less(p, q): - // p < q, so we have a decision. - return true - case less(q, p): - // p > q, so we have a decision. - return false - } - // p == q; try the next comparison. - } - // All comparisons to here said "equal", so just return whatever - // the final comparison reports. - return ms.less[k](p, q) -} - -var changes = []Change{ - {"gri", "Go", 100}, - {"ken", "C", 150}, - {"glenda", "Go", 200}, - {"rsc", "Go", 200}, - {"r", "Go", 100}, - {"ken", "Go", 200}, - {"dmr", "C", 100}, - {"r", "C", 150}, - {"gri", "Smalltalk", 80}, -} - -// ExampleMultiKeys demonstrates a technique for sorting a struct type using different -// sets of multiple fields in the comparison. We chain together "Less" functions, each of -// which compares a single field. -func Example_sortMultiKeys() { - // Closures that order the Change structure. - user := func(c1, c2 *Change) bool { - return c1.user < c2.user - } - language := func(c1, c2 *Change) bool { - return c1.language < c2.language - } - increasingLines := func(c1, c2 *Change) bool { - return c1.lines < c2.lines - } - decreasingLines := func(c1, c2 *Change) bool { - return c1.lines > c2.lines // Note: > orders downwards. - } - - // Simple use: Sort by user. - OrderedBy(user).Sort(changes) - fmt.Println("By user:", changes) - - // More examples. - OrderedBy(user, increasingLines).Sort(changes) - fmt.Println("By user,<lines:", changes) - - OrderedBy(user, decreasingLines).Sort(changes) - fmt.Println("By user,>lines:", changes) - - OrderedBy(language, increasingLines).Sort(changes) - fmt.Println("By language,<lines:", changes) - - OrderedBy(language, increasingLines, user).Sort(changes) - fmt.Println("By language,<lines,user:", changes) - - // Output: - // By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}] - // By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}] - // By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}] - // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}] - // By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}] - -} diff --git a/src/pkg/sort/example_test.go b/src/pkg/sort/example_test.go deleted file mode 100644 index f7372bec3..000000000 --- a/src/pkg/sort/example_test.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2011 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" - "sort" -) - -func ExampleInts() { - s := []int{5, 2, 6, 3, 1, 4} // unsorted - sort.Ints(s) - fmt.Println(s) - // Output: [1 2 3 4 5 6] -} - -func ExampleReverse() { - s := []int{5, 2, 6, 3, 1, 4} // unsorted - sort.Sort(sort.Reverse(sort.IntSlice(s))) - fmt.Println(s) - // Output: [6 5 4 3 2 1] -} diff --git a/src/pkg/sort/example_wrapper_test.go b/src/pkg/sort/example_wrapper_test.go deleted file mode 100644 index cf6d74cf7..000000000 --- a/src/pkg/sort/example_wrapper_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2011 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" - "sort" -) - -type Grams int - -func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) } - -type Organ struct { - Name string - Weight Grams -} - -type Organs []*Organ - -func (s Organs) Len() int { return len(s) } -func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// ByName implements sort.Interface by providing Less and using the Len and -// Swap methods of the embedded Organs value. -type ByName struct{ Organs } - -func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name } - -// ByWeight implements sort.Interface by providing Less and using the Len and -// Swap methods of the embedded Organs value. -type ByWeight struct{ Organs } - -func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight } - -func Example_sortWrapper() { - s := []*Organ{ - {"brain", 1340}, - {"heart", 290}, - {"liver", 1494}, - {"pancreas", 131}, - {"prostate", 62}, - {"spleen", 162}, - } - - sort.Sort(ByWeight{s}) - fmt.Println("Organs by weight:") - printOrgans(s) - - sort.Sort(ByName{s}) - fmt.Println("Organs by name:") - printOrgans(s) - - // Output: - // Organs by weight: - // prostate (62g) - // pancreas (131g) - // spleen (162g) - // heart (290g) - // brain (1340g) - // liver (1494g) - // Organs by name: - // brain (1340g) - // heart (290g) - // liver (1494g) - // pancreas (131g) - // prostate (62g) - // spleen (162g) -} - -func printOrgans(s []*Organ) { - for _, o := range s { - fmt.Printf("%-8s (%v)\n", o.Name, o.Weight) - } -} diff --git a/src/pkg/sort/export_test.go b/src/pkg/sort/export_test.go deleted file mode 100644 index b6e30ceb5..000000000 --- a/src/pkg/sort/export_test.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2011 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 - -func Heapsort(data Interface) { - heapSort(data, 0, data.Len()) -} diff --git a/src/pkg/sort/search.go b/src/pkg/sort/search.go deleted file mode 100644 index 8a2c1c33b..000000000 --- a/src/pkg/sort/search.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2010 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. - -// This file implements binary search. - -package sort - -// Search uses binary search to find and return the smallest index i -// in [0, n) at which f(i) is true, assuming that on the range [0, n), -// f(i) == true implies f(i+1) == true. That is, Search requires that -// f is false for some (possibly empty) prefix of the input range [0, n) -// and then true for the (possibly empty) remainder; Search returns -// the first true index. If there is no such index, Search returns n. -// (Note that the "not found" return value is not -1 as in, for instance, -// strings.Index). -// Search calls f(i) only for i in the range [0, n). -// -// A common use of Search is to find the index i for a value x in -// a sorted, indexable data structure such as an array or slice. -// In this case, the argument f, typically a closure, captures the value -// to be searched for, and how the data structure is indexed and -// ordered. -// -// For instance, given a slice data sorted in ascending order, -// the call Search(len(data), func(i int) bool { return data[i] >= 23 }) -// returns the smallest index i such that data[i] >= 23. If the caller -// wants to find whether 23 is in the slice, it must test data[i] == 23 -// separately. -// -// Searching data sorted in descending order would use the <= -// operator instead of the >= operator. -// -// To complete the example above, the following code tries to find the value -// x in an integer slice data sorted in ascending order: -// -// x := 23 -// i := sort.Search(len(data), func(i int) bool { return data[i] >= x }) -// if i < len(data) && data[i] == x { -// // x is present at data[i] -// } else { -// // x is not present in data, -// // but i is the index where it would be inserted. -// } -// -// As a more whimsical example, this program guesses your number: -// -// func GuessingGame() { -// var s string -// fmt.Printf("Pick an integer from 0 to 100.\n") -// answer := sort.Search(100, func(i int) bool { -// fmt.Printf("Is your number <= %d? ", i) -// fmt.Scanf("%s", &s) -// return s != "" && s[0] == 'y' -// }) -// fmt.Printf("Your number is %d.\n", answer) -// } -// -func Search(n int, f func(int) bool) int { - // Define f(-1) == false and f(n) == true. - // Invariant: f(i-1) == false, f(j) == true. - i, j := 0, n - for i < j { - h := i + (j-i)/2 // avoid overflow when computing h - // i ≤ h < j - if !f(h) { - i = h + 1 // preserves f(i-1) == false - } else { - j = h // preserves f(j) == true - } - } - // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. - return i -} - -// Convenience wrappers for common cases. - -// SearchInts searches for x in a sorted slice of ints and returns the index -// as specified by Search. The return value is the index to insert x if x is -// not present (it could be len(a)). -// The slice must be sorted in ascending order. -// -func SearchInts(a []int, x int) int { - return Search(len(a), func(i int) bool { return a[i] >= x }) -} - -// SearchFloat64s searches for x in a sorted slice of float64s and returns the index -// as specified by Search. The return value is the index to insert x if x is not -// present (it could be len(a)). -// The slice must be sorted in ascending order. -// -func SearchFloat64s(a []float64, x float64) int { - return Search(len(a), func(i int) bool { return a[i] >= x }) -} - -// SearchStrings searches for x in a sorted slice of strings and returns the index -// as specified by Search. The return value is the index to insert x if x is not -// present (it could be len(a)). -// The slice must be sorted in ascending order. -// -func SearchStrings(a []string, x string) int { - return Search(len(a), func(i int) bool { return a[i] >= x }) -} - -// Search returns the result of applying SearchInts to the receiver and x. -func (p IntSlice) Search(x int) int { return SearchInts(p, x) } - -// Search returns the result of applying SearchFloat64s to the receiver and x. -func (p Float64Slice) Search(x float64) int { return SearchFloat64s(p, x) } - -// Search returns the result of applying SearchStrings to the receiver and x. -func (p StringSlice) Search(x string) int { return SearchStrings(p, x) } diff --git a/src/pkg/sort/search_test.go b/src/pkg/sort/search_test.go deleted file mode 100644 index 29b8d62df..000000000 --- a/src/pkg/sort/search_test.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2010 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 ( - "runtime" - . "sort" - "testing" -) - -func f(a []int, x int) func(int) bool { - return func(i int) bool { - return a[i] >= x - } -} - -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} - -var tests = []struct { - name string - n int - f func(int) bool - i int -}{ - {"empty", 0, nil, 0}, - {"1 1", 1, func(i int) bool { return i >= 1 }, 1}, - {"1 true", 1, func(i int) bool { return true }, 0}, - {"1 false", 1, func(i int) bool { return false }, 1}, - {"1e9 991", 1e9, func(i int) bool { return i >= 991 }, 991}, - {"1e9 true", 1e9, func(i int) bool { return true }, 0}, - {"1e9 false", 1e9, func(i int) bool { return false }, 1e9}, - {"data -20", len(data), f(data, -20), 0}, - {"data -10", len(data), f(data, -10), 0}, - {"data -9", len(data), f(data, -9), 1}, - {"data -6", len(data), f(data, -6), 1}, - {"data -5", len(data), f(data, -5), 1}, - {"data 3", len(data), f(data, 3), 5}, - {"data 11", len(data), f(data, 11), 8}, - {"data 99", len(data), f(data, 99), 9}, - {"data 100", len(data), f(data, 100), 9}, - {"data 101", len(data), f(data, 101), 12}, - {"data 10000", len(data), f(data, 10000), 13}, - {"data 10001", len(data), f(data, 10001), 14}, - {"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] <= 7 }, 4}, - {"descending 7", 1e9, func(i int) bool { return 1e9-i <= 7 }, 1e9 - 7}, - {"overflow", 2e9, func(i int) bool { return false }, 2e9}, -} - -func TestSearch(t *testing.T) { - for _, e := range tests { - i := Search(e.n, e.f) - if i != e.i { - t.Errorf("%s: expected index %d; got %d", e.name, e.i, i) - } - } -} - -// log2 computes the binary logarithm of x, rounded up to the next integer. -// (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.) -// -func log2(x int) int { - n := 0 - for p := 1; p < x; p += p { - // p == 2**n - n++ - } - // p/2 < x <= p == 2**n - return n -} - -func TestSearchEfficiency(t *testing.T) { - n := 100 - step := 1 - for exp := 2; exp < 10; exp++ { - // n == 10**exp - // step == 10**(exp-2) - max := log2(n) - for x := 0; x < n; x += step { - count := 0 - i := Search(n, func(i int) bool { count++; return i >= x }) - if i != x { - t.Errorf("n = %d: expected index %d; got %d", n, x, i) - } - if count > max { - t.Errorf("n = %d, x = %d: expected <= %d calls; got %d", n, x, max, count) - } - } - n *= 10 - step *= 10 - } -} - -// Smoke tests for convenience wrappers - not comprehensive. - -var fdata = []float64{0: -3.14, 1: 0, 2: 1, 3: 2, 4: 1000.7} -var sdata = []string{0: "f", 1: "foo", 2: "foobar", 3: "x"} - -var wrappertests = []struct { - name string - result int - i int -}{ - {"SearchInts", SearchInts(data, 11), 8}, - {"SearchFloat64s", SearchFloat64s(fdata, 2.1), 4}, - {"SearchStrings", SearchStrings(sdata, ""), 0}, - {"IntSlice.Search", IntSlice(data).Search(0), 2}, - {"Float64Slice.Search", Float64Slice(fdata).Search(2.0), 3}, - {"StringSlice.Search", StringSlice(sdata).Search("x"), 3}, -} - -func TestSearchWrappers(t *testing.T) { - for _, e := range wrappertests { - if e.result != e.i { - t.Errorf("%s: expected index %d; got %d", e.name, e.i, e.result) - } - } -} - -func runSearchWrappers() { - SearchInts(data, 11) - SearchFloat64s(fdata, 2.1) - SearchStrings(sdata, "") - IntSlice(data).Search(0) - Float64Slice(fdata).Search(2.0) - StringSlice(sdata).Search("x") -} - -func TestSearchWrappersDontAlloc(t *testing.T) { - if testing.Short() { - t.Skip("skipping malloc count in short mode") - } - if runtime.GOMAXPROCS(0) > 1 { - t.Skip("skipping; GOMAXPROCS>1") - } - allocs := testing.AllocsPerRun(100, runSearchWrappers) - if allocs != 0 { - t.Errorf("expected no allocs for runSearchWrappers, got %v", allocs) - } -} - -func BenchmarkSearchWrappers(b *testing.B) { - for i := 0; i < b.N; i++ { - runSearchWrappers() - } -} - -// Abstract exhaustive test: all sizes up to 100, -// all possible return values. If there are any small -// corner cases, this test exercises them. -func TestSearchExhaustive(t *testing.T) { - for size := 0; size <= 100; size++ { - for targ := 0; targ <= size; targ++ { - i := Search(size, func(i int) bool { return i >= targ }) - if i != targ { - t.Errorf("Search(%d, %d) = %d", size, targ, i) - } - } - } -} diff --git a/src/pkg/sort/sort.go b/src/pkg/sort/sort.go deleted file mode 100644 index e980c295c..000000000 --- a/src/pkg/sort/sort.go +++ /dev/null @@ -1,474 +0,0 @@ -// 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 provides primitives for sorting slices and user-defined -// collections. -package sort - -// A type, typically a collection, that satisfies sort.Interface can be -// sorted by the routines in this package. The methods require that the -// elements of the collection be enumerated by an integer index. -type Interface interface { - // Len is the number of elements in the collection. - Len() int - // Less reports whether the element with - // index i should sort before the element with index j. - Less(i, j int) bool - // Swap swaps the elements with indexes i and j. - Swap(i, j int) -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -// Insertion sort -func insertionSort(data Interface, a, b int) { - for i := a + 1; i < b; i++ { - for j := i; j > a && data.Less(j, j-1); j-- { - data.Swap(j, j-1) - } - } -} - -// siftDown implements the heap property on data[lo, hi). -// first is an offset into the array where the root of the heap lies. -func siftDown(data Interface, lo, hi, first int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && data.Less(first+child, first+child+1) { - child++ - } - if !data.Less(first+root, first+child) { - return - } - data.Swap(first+root, first+child) - root = child - } -} - -func heapSort(data Interface, a, b int) { - first := a - lo := 0 - hi := b - a - - // Build heap with greatest element at top. - for i := (hi - 1) / 2; i >= 0; i-- { - siftDown(data, i, hi, first) - } - - // Pop elements, largest first, into end of data. - for i := hi - 1; i >= 0; i-- { - data.Swap(first, first+i) - siftDown(data, lo, i, first) - } -} - -// Quicksort, following Bentley and McIlroy, -// ``Engineering a Sort Function,'' SP&E November 1993. - -// medianOfThree moves the median of the three values data[a], data[b], data[c] into data[a]. -func medianOfThree(data Interface, a, b, c int) { - m0 := b - m1 := a - m2 := c - // bubble sort on 3 elements - if data.Less(m1, m0) { - data.Swap(m1, m0) - } - if data.Less(m2, m1) { - data.Swap(m2, m1) - } - if data.Less(m1, m0) { - data.Swap(m1, m0) - } - // now data[m0] <= data[m1] <= data[m2] -} - -func swapRange(data Interface, a, b, n int) { - for i := 0; i < n; i++ { - data.Swap(a+i, b+i) - } -} - -func doPivot(data Interface, lo, hi int) (midlo, midhi int) { - m := lo + (hi-lo)/2 // Written like this to avoid integer overflow. - if hi-lo > 40 { - // Tukey's ``Ninther,'' median of three medians of three. - s := (hi - lo) / 8 - medianOfThree(data, lo, lo+s, lo+2*s) - medianOfThree(data, m, m-s, m+s) - medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) - } - medianOfThree(data, lo, m, hi-1) - - // Invariants are: - // data[lo] = pivot (set up by ChoosePivot) - // data[lo <= i < a] = pivot - // data[a <= i < b] < pivot - // data[b <= i < c] is unexamined - // data[c <= i < d] > pivot - // data[d <= i < hi] = pivot - // - // Once b meets c, can swap the "= pivot" sections - // into the middle of the slice. - pivot := lo - a, b, c, d := lo+1, lo+1, hi, hi - for { - for b < c { - if data.Less(b, pivot) { // data[b] < pivot - b++ - } else if !data.Less(pivot, b) { // data[b] = pivot - data.Swap(a, b) - a++ - b++ - } else { - break - } - } - for b < c { - if data.Less(pivot, c-1) { // data[c-1] > pivot - c-- - } else if !data.Less(c-1, pivot) { // data[c-1] = pivot - data.Swap(c-1, d-1) - c-- - d-- - } else { - break - } - } - if b >= c { - break - } - // data[b] > pivot; data[c-1] < pivot - data.Swap(b, c-1) - b++ - c-- - } - - n := min(b-a, a-lo) - swapRange(data, lo, b-n, n) - - n = min(hi-d, d-c) - swapRange(data, c, hi-n, n) - - return lo + b - a, hi - (d - c) -} - -func quickSort(data Interface, a, b, maxDepth int) { - for b-a > 7 { - if maxDepth == 0 { - heapSort(data, a, b) - return - } - maxDepth-- - mlo, mhi := doPivot(data, a, b) - // Avoiding recursion on the larger subproblem guarantees - // a stack depth of at most lg(b-a). - if mlo-a < b-mhi { - quickSort(data, a, mlo, maxDepth) - a = mhi // i.e., quickSort(data, mhi, b) - } else { - quickSort(data, mhi, b, maxDepth) - b = mlo // i.e., quickSort(data, a, mlo) - } - } - if b-a > 1 { - insertionSort(data, a, b) - } -} - -// Sort sorts data. -// It makes one call to data.Len to determine n, and O(n*log(n)) calls to -// data.Less and data.Swap. The sort is not guaranteed to be stable. -func Sort(data Interface) { - // Switch to heapsort if depth of 2*ceil(lg(n+1)) is reached. - n := data.Len() - maxDepth := 0 - for i := n; i > 0; i >>= 1 { - maxDepth++ - } - maxDepth *= 2 - quickSort(data, 0, n, maxDepth) -} - -type reverse struct { - // This embedded Interface permits Reverse to use the methods of - // another Interface implementation. - Interface -} - -// Less returns the opposite of the embedded implementation's Less method. -func (r reverse) Less(i, j int) bool { - return r.Interface.Less(j, i) -} - -// Reverse returns the reverse order for data. -func Reverse(data Interface) Interface { - return &reverse{data} -} - -// IsSorted reports whether data is sorted. -func IsSorted(data Interface) bool { - n := data.Len() - for i := n - 1; i > 0; i-- { - if data.Less(i, i-1) { - return false - } - } - return true -} - -// Convenience types for common cases - -// IntSlice attaches the methods of Interface to []int, sorting in increasing order. -type IntSlice []int - -func (p IntSlice) Len() int { return len(p) } -func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] } -func (p IntSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// Sort is a convenience method. -func (p IntSlice) Sort() { Sort(p) } - -// Float64Slice attaches the methods of Interface to []float64, sorting in increasing order. -type Float64Slice []float64 - -func (p Float64Slice) Len() int { return len(p) } -func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) } -func (p Float64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// isNaN is a copy of math.IsNaN to avoid a dependency on the math package. -func isNaN(f float64) bool { - return f != f -} - -// Sort is a convenience method. -func (p Float64Slice) Sort() { Sort(p) } - -// StringSlice attaches the methods of Interface to []string, sorting in increasing order. -type StringSlice []string - -func (p StringSlice) Len() int { return len(p) } -func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] } -func (p StringSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// Sort is a convenience method. -func (p StringSlice) Sort() { Sort(p) } - -// Convenience wrappers for common cases - -// Ints sorts a slice of ints in increasing order. -func Ints(a []int) { Sort(IntSlice(a)) } - -// Float64s sorts a slice of float64s in increasing order. -func Float64s(a []float64) { Sort(Float64Slice(a)) } - -// Strings sorts a slice of strings in increasing order. -func Strings(a []string) { Sort(StringSlice(a)) } - -// IntsAreSorted tests whether a slice of ints is sorted in increasing order. -func IntsAreSorted(a []int) bool { return IsSorted(IntSlice(a)) } - -// Float64sAreSorted tests whether a slice of float64s is sorted in increasing order. -func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) } - -// StringsAreSorted tests whether a slice of strings is sorted in increasing order. -func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) } - -// Notes on stable sorting: -// The used algorithms are simple and provable correct on all input and use -// only logarithmic additional stack space. They perform well if compared -// experimentally to other stable in-place sorting algorithms. -// -// Remarks on other algorithms evaluated: -// - GCC's 4.6.3 stable_sort with merge_without_buffer from libstdc++: -// Not faster. -// - GCC's __rotate for block rotations: Not faster. -// - "Practical in-place mergesort" from Jyrki Katajainen, Tomi A. Pasanen -// and Jukka Teuhola; Nordic Journal of Computing 3,1 (1996), 27-40: -// The given algorithms are in-place, number of Swap and Assignments -// grow as n log n but the algorithm is not stable. -// - "Fast Stable In-Plcae Sorting with O(n) Data Moves" J.I. Munro and -// V. Raman in Algorithmica (1996) 16, 115-160: -// This algorithm either needs additional 2n bits or works only if there -// are enough different elements available to encode some permutations -// which have to be undone later (so not stable an any input). -// - All the optimal in-place sorting/merging algorithms I found are either -// unstable or rely on enough different elements in each step to encode the -// performed block rearrangements. See also "In-Place Merging Algorithms", -// Denham Coates-Evely, Department of Computer Science, Kings College, -// January 2004 and the reverences in there. -// - Often "optimal" algorithms are optimal in the number of assignments -// but Interface has only Swap as operation. - -// Stable sorts data while keeping the original order of equal elements. -// -// It makes one call to data.Len to determine n, O(n*log(n)) calls to -// data.Less and O(n*log(n)*log(n)) calls to data.Swap. -func Stable(data Interface) { - n := data.Len() - blockSize := 20 - a, b := 0, blockSize - for b <= n { - insertionSort(data, a, b) - a = b - b += blockSize - } - insertionSort(data, a, n) - - for blockSize < n { - a, b = 0, 2*blockSize - for b <= n { - symMerge(data, a, a+blockSize, b) - a = b - b += 2 * blockSize - } - symMerge(data, a, a+blockSize, n) - blockSize *= 2 - } -} - -// SymMerge merges the two sorted subsequences data[a:m] and data[m:b] using -// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum -// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz -// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in -// Computer Science, pages 714-723. Springer, 2004. -// -// Let M = m-a and N = b-n. Wolog M < N. -// The recursion depth is bound by ceil(log(N+M)). -// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. -// The algorithm needs O((M+N)*log(M)) calls to data.Swap. -// -// The paper gives O((M+N)*log(M)) as the number of assignments assuming a -// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation -// in the paper carries through for Swap operations, especially as the block -// swapping rotate uses only O(M+N) Swaps. -func symMerge(data Interface, a, m, b int) { - if a >= m || m >= b { - return - } - - mid := a + (b-a)/2 - n := mid + m - start := 0 - if m > mid { - start = n - b - r, p := mid, n-1 - for start < r { - c := start + (r-start)/2 - if !data.Less(p-c, c) { - start = c + 1 - } else { - r = c - } - } - } else { - start = a - r, p := m, n-1 - for start < r { - c := start + (r-start)/2 - if !data.Less(p-c, c) { - start = c + 1 - } else { - r = c - } - } - } - end := n - start - rotate(data, start, m, end) - symMerge(data, a, start, mid) - symMerge(data, mid, end, b) -} - -// Rotate two consecutives blocks u = data[a:m] and v = data[m:b] in data: -// Data of the form 'x u v y' is changed to 'x v u y'. -// Rotate performs at most b-a many calls to data.Swap. -func rotate(data Interface, a, m, b int) { - i := m - a - if i == 0 { - return - } - j := b - m - if j == 0 { - return - } - - if i == j { - swapRange(data, a, m, i) - return - } - - p := a + i - for i != j { - if i > j { - swapRange(data, p-i, p, j) - i -= j - } else { - swapRange(data, p-i, p+j-i, i) - j -= i - } - } - swapRange(data, p-i, p, i) -} - -/* -Complexity of Stable Sorting - - -Complexity of block swapping rotation - -Each Swap puts one new element into its correct, final position. -Elements which reach their final position are no longer moved. -Thus block swapping rotation needs |u|+|v| calls to Swaps. -This is best possible as each element might need a move. - -Pay attention when comparing to other optimal algorithms which -typically count the number of assignments instead of swaps: -E.g. the optimal algorithm of Dudzinski and Dydek for in-place -rotations uses O(u + v + gcd(u,v)) assignments which is -better than our O(3 * (u+v)) as gcd(u,v) <= u. - - -Stable sorting by SymMerge and BlockSwap rotations - -SymMerg complexity for same size input M = N: -Calls to Less: O(M*log(N/M+1)) = O(N*log(2)) = O(N) -Calls to Swap: O((M+N)*log(M)) = O(2*N*log(N)) = O(N*log(N)) - -(The following argument does not fuzz over a missing -1 or -other stuff which does not impact the final result). - -Let n = data.Len(). Assume n = 2^k. - -Plain merge sort performs log(n) = k iterations. -On iteration i the algorithm merges 2^(k-i) blocks, each of size 2^i. - -Thus iteration i of merge sort performs: -Calls to Less O(2^(k-i) * 2^i) = O(2^k) = O(2^log(n)) = O(n) -Calls to Swap O(2^(k-i) * 2^i * log(2^i)) = O(2^k * i) = O(n*i) - -In total k = log(n) iterations are performed; so in total: -Calls to Less O(log(n) * n) -Calls to Swap O(n + 2*n + 3*n + ... + (k-1)*n + k*n) - = O((k/2) * k * n) = O(n * k^2) = O(n * log^2(n)) - - -Above results should generalize to arbitrary n = 2^k + p -and should not be influenced by the initial insertion sort phase: -Insertion sort is O(n^2) on Swap and Less, thus O(bs^2) per block of -size bs at n/bs blocks: O(bs*n) Swaps and Less during insertion sort. -Merge sort iterations start at i = log(bs). With t = log(bs) constant: -Calls to Less O((log(n)-t) * n + bs*n) = O(log(n)*n + (bs-t)*n) - = O(n * log(n)) -Calls to Swap O(n * log^2(n) - (t^2+t)/2*n) = O(n * log^2(n)) - -*/ diff --git a/src/pkg/sort/sort_test.go b/src/pkg/sort/sort_test.go deleted file mode 100644 index 6c36f30e0..000000000 --- a/src/pkg/sort/sort_test.go +++ /dev/null @@ -1,553 +0,0 @@ -// 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") } |