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