| // Copyright 2022 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 slices |
| |
| import "golang.org/x/exp/constraints" |
| |
| // Sort sorts a slice of any ordered type in ascending order. |
| func Sort[Elem constraints.Ordered](x []Elem) { |
| n := len(x) |
| quickSortOrdered(x, 0, n, maxDepth(n)) |
| } |
| |
| // Sort sorts the slice x in ascending order as determined by the less function. |
| // This sort is not guaranteed to be stable. |
| func SortFunc[Elem any](x []Elem, less func(a, b Elem) bool) { |
| n := len(x) |
| quickSortLessFunc(x, 0, n, maxDepth(n), less) |
| } |
| |
| // SortStable sorts the slice x while keeping the original order of equal |
| // elements, using less to compare elements. |
| func SortStableFunc[Elem any](x []Elem, less func(a, b Elem) bool) { |
| stableLessFunc(x, len(x), less) |
| } |
| |
| // IsSorted reports whether x is sorted in ascending order. |
| func IsSorted[Elem constraints.Ordered](x []Elem) bool { |
| for i := len(x) - 1; i > 0; i-- { |
| if x[i] < x[i-1] { |
| return false |
| } |
| } |
| return true |
| } |
| |
| // IsSortedFunc reports whether x is sorted in ascending order, with less as the |
| // comparison function. |
| func IsSortedFunc[Elem any](x []Elem, less func(a, b Elem) bool) bool { |
| for i := len(x) - 1; i > 0; i-- { |
| if less(x[i], x[i-1]) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| // BinarySearch searches for target in a sorted slice and returns the smallest |
| // index at which target is found. If the target is not found, the index at |
| // which it could be inserted into the slice is returned; therefore, if the |
| // intention is to find target itself a separate check for equality with the |
| // element at the returned index is required. |
| func BinarySearch[Elem constraints.Ordered](x []Elem, target Elem) int { |
| return search(len(x), func(i int) bool { return x[i] >= target }) |
| } |
| |
| // BinarySearchFunc uses binary search to find and return the smallest index i |
| // in [0, n) at which ok(i) is true, assuming that on the range [0, n), |
| // ok(i) == true implies ok(i+1) == true. That is, BinarySearchFunc requires |
| // that ok is false for some (possibly empty) prefix of the input range [0, n) |
| // and then true for the (possibly empty) remainder; BinarySearchFunc returns |
| // the first true index. If there is no such index, BinarySearchFunc returns n. |
| // (Note that the "not found" return value is not -1 as in, for instance, |
| // strings.Index.) Search calls ok(i) only for i in the range [0, n). |
| func BinarySearchFunc[Elem any](x []Elem, ok func(Elem) bool) int { |
| return search(len(x), func(i int) bool { return ok(x[i]) }) |
| } |
| |
| // maxDepth returns a threshold at which quicksort should switch |
| // to heapsort. It returns 2*ceil(lg(n+1)). |
| func maxDepth(n int) int { |
| var depth int |
| for i := n; i > 0; i >>= 1 { |
| depth++ |
| } |
| return depth * 2 |
| } |
| |
| 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 := int(uint(i+j) >> 1) // 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 |
| } |