github.com/dorkamotorka/go/src@v0.0.0-20230614113921-187095f0e316/slices/sort.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package slices 6 7 import ( 8 "cmp" 9 "math/bits" 10 ) 11 12 // Sort sorts a slice of any ordered type in ascending order. 13 // When sorting floating-point numbers, NaNs are ordered before other values. 14 func Sort[E cmp.Ordered](x []E) { 15 n := len(x) 16 pdqsortOrdered(x, 0, n, bits.Len(uint(n))) 17 } 18 19 // SortFunc sorts the slice x in ascending order as determined by the cmp 20 // function. This sort is not guaranteed to be stable. 21 // cmp(a, b) should return a negative number when a < b, a positive number when 22 // a > b and zero when a == b. 23 // 24 // SortFunc requires that cmp is a strict weak ordering. 25 // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. 26 func SortFunc[E any](x []E, cmp func(a, b E) int) { 27 n := len(x) 28 pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp) 29 } 30 31 // SortStableFunc sorts the slice x while keeping the original order of equal 32 // elements, using cmp to compare elements. 33 func SortStableFunc[E any](x []E, cmp func(a, b E) int) { 34 stableCmpFunc(x, len(x), cmp) 35 } 36 37 // IsSorted reports whether x is sorted in ascending order. 38 func IsSorted[E cmp.Ordered](x []E) bool { 39 for i := len(x) - 1; i > 0; i-- { 40 if cmp.Less(x[i], x[i-1]) { 41 return false 42 } 43 } 44 return true 45 } 46 47 // IsSortedFunc reports whether x is sorted in ascending order, with cmp as the 48 // comparison function. 49 func IsSortedFunc[E any](x []E, cmp func(a, b E) int) bool { 50 for i := len(x) - 1; i > 0; i-- { 51 if cmp(x[i], x[i-1]) < 0 { 52 return false 53 } 54 } 55 return true 56 } 57 58 // Min returns the minimal value in x. It panics if x is empty. 59 // For floating-point numbers, Min propagates NaNs (any NaN value in x 60 // forces the output to be NaN). 61 func Min[E cmp.Ordered](x []E) E { 62 if len(x) < 1 { 63 panic("slices.Min: empty list") 64 } 65 m := x[0] 66 for i := 1; i < len(x); i++ { 67 m = min(m, x[i]) 68 } 69 return m 70 } 71 72 // MinFunc returns the minimal value in x, using cmp to compare elements. 73 // It panics if x is empty. 74 func MinFunc[E any](x []E, cmp func(a, b E) int) E { 75 if len(x) < 1 { 76 panic("slices.MinFunc: empty list") 77 } 78 m := x[0] 79 for i := 1; i < len(x); i++ { 80 if cmp(x[i], m) < 0 { 81 m = x[i] 82 } 83 } 84 return m 85 } 86 87 // Max returns the maximal value in x. It panics if x is empty. 88 // For floating-point E, Max propagates NaNs (any NaN value in x 89 // forces the output to be NaN). 90 func Max[E cmp.Ordered](x []E) E { 91 if len(x) < 1 { 92 panic("slices.Max: empty list") 93 } 94 m := x[0] 95 for i := 1; i < len(x); i++ { 96 m = max(m, x[i]) 97 } 98 return m 99 } 100 101 // MaxFunc returns the maximal value in x, using cmp to compare elements. 102 // It panics if x is empty. 103 func MaxFunc[E any](x []E, cmp func(a, b E) int) E { 104 if len(x) < 1 { 105 panic("slices.MaxFunc: empty list") 106 } 107 m := x[0] 108 for i := 1; i < len(x); i++ { 109 if cmp(x[i], m) > 0 { 110 m = x[i] 111 } 112 } 113 return m 114 } 115 116 // BinarySearch searches for target in a sorted slice and returns the position 117 // where target is found, or the position where target would appear in the 118 // sort order; it also returns a bool saying whether the target is really found 119 // in the slice. The slice must be sorted in increasing order. 120 func BinarySearch[E cmp.Ordered](x []E, target E) (int, bool) { 121 // Inlining is faster than calling BinarySearchFunc with a lambda. 122 n := len(x) 123 // Define x[-1] < target and x[n] >= target. 124 // Invariant: x[i-1] < target, x[j] >= target. 125 i, j := 0, n 126 for i < j { 127 h := int(uint(i+j) >> 1) // avoid overflow when computing h 128 // i ≤ h < j 129 if cmp.Less(x[h], target) { 130 i = h + 1 // preserves x[i-1] < target 131 } else { 132 j = h // preserves x[j] >= target 133 } 134 } 135 // i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i. 136 return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target))) 137 } 138 139 // BinarySearchFunc works like BinarySearch, but uses a custom comparison 140 // function. The slice must be sorted in increasing order, where "increasing" 141 // is defined by cmp. cmp should return 0 if the slice element matches 142 // the target, a negative number if the slice element precedes the target, 143 // or a positive number if the slice element follows the target. 144 // cmp must implement the same ordering as the slice, such that if 145 // cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice. 146 func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool) { 147 n := len(x) 148 // Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 . 149 // Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0. 150 i, j := 0, n 151 for i < j { 152 h := int(uint(i+j) >> 1) // avoid overflow when computing h 153 // i ≤ h < j 154 if cmp(x[h], target) < 0 { 155 i = h + 1 // preserves cmp(x[i - 1], target) < 0 156 } else { 157 j = h // preserves cmp(x[j], target) >= 0 158 } 159 } 160 // i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i. 161 return i, i < n && cmp(x[i], target) == 0 162 } 163 164 type sortedHint int // hint for pdqsort when choosing the pivot 165 166 const ( 167 unknownHint sortedHint = iota 168 increasingHint 169 decreasingHint 170 ) 171 172 // xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf 173 type xorshift uint64 174 175 func (r *xorshift) Next() uint64 { 176 *r ^= *r << 13 177 *r ^= *r >> 17 178 *r ^= *r << 5 179 return uint64(*r) 180 } 181 182 func nextPowerOfTwo(length int) uint { 183 return 1 << bits.Len(uint(length)) 184 } 185 186 // isNaN reports whether x is a NaN without requiring the math package. 187 // This will always return false if T is not floating-point. 188 func isNaN[T cmp.Ordered](x T) bool { 189 return x != x 190 }