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