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  }