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  }