github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/base/bslice/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  package bslice
     6  
     7  import (
     8  	"github.com/songzhibin97/go-baseutils/base/btype"
     9  	"math/bits"
    10  )
    11  
    12  // Sort sorts a slice of any ordered type in ascending order.
    13  // Sort may fail to sort correctly when sorting slices of floating-point
    14  // numbers containing Not-a-number (NaN) values.
    15  // Use slices.SortFunc(x, func(a, b float64) bool {return a < b || (math.IsNaN(a) && !math.IsNaN(b))})
    16  // instead if the input may contain NaNs.
    17  func Sort[E btype.Ordered](x []E) {
    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 less function.
    23  // This sort is not guaranteed to be stable.
    24  //
    25  // SortFunc requires that less is a strict weak ordering.
    26  // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
    27  func SortFunc[E any](x []E, less func(a, b E) bool) {
    28  	n := len(x)
    29  	pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less)
    30  }
    31  
    32  // SortStableFunc sorts the slice x while keeping the original order of equal
    33  // elements, using less to compare elements.
    34  func SortStableFunc[E any](x []E, less func(a, b E) bool) {
    35  	stableLessFunc(x, len(x), less)
    36  }
    37  
    38  // IsSorted reports whether x is sorted in ascending order.
    39  func IsSorted[E btype.Ordered](x []E) bool {
    40  	for i := len(x) - 1; i > 0; i-- {
    41  		if x[i] < x[i-1] {
    42  			return false
    43  		}
    44  	}
    45  	return true
    46  }
    47  
    48  // IsSortedFunc reports whether x is sorted in ascending order, with less as the
    49  // comparison function.
    50  func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool {
    51  	for i := len(x) - 1; i > 0; i-- {
    52  		if less(x[i], x[i-1]) {
    53  			return false
    54  		}
    55  	}
    56  	return true
    57  }
    58  
    59  // BinarySearch searches for target in a sorted slice and returns the position
    60  // where target is found, or the position where target would appear in the
    61  // sort order; it also returns a bool saying whether the target is really found
    62  // in the slice. The slice must be sorted in increasing order.
    63  func BinarySearch[E btype.Ordered](x []E, target E) (int, bool) {
    64  	// Inlining is faster than calling BinarySearchFunc with a lambda.
    65  	n := len(x)
    66  	// Define x[-1] < target and x[n] >= target.
    67  	// Invariant: x[i-1] < target, x[j] >= target.
    68  	i, j := 0, n
    69  	for i < j {
    70  		h := int(uint(i+j) >> 1) // avoid overflow when computing h
    71  		// i ≤ h < j
    72  		if x[h] < target {
    73  			i = h + 1 // preserves x[i-1] < target
    74  		} else {
    75  			j = h // preserves x[j] >= target
    76  		}
    77  	}
    78  	// i == j, x[i-1] < target, and x[j] (= x[i]) >= target  =>  answer is i.
    79  	return i, i < n && x[i] == target
    80  }
    81  
    82  // BinarySearchFunc works like BinarySearch, but uses a custom comparison
    83  // function. The slice must be sorted in increasing order, where "increasing"
    84  // is defined by cmp. cmp should return 0 if the slice element matches
    85  // the target, a negative number if the slice element precedes the target,
    86  // or a positive number if the slice element follows the target.
    87  // cmp must implement the same ordering as the slice, such that if
    88  // cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice.
    89  func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool) {
    90  	n := len(x)
    91  	// Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
    92  	// Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
    93  	i, j := 0, n
    94  	for i < j {
    95  		h := int(uint(i+j) >> 1) // avoid overflow when computing h
    96  		// i ≤ h < j
    97  		if cmp(x[h], target) < 0 {
    98  			i = h + 1 // preserves cmp(x[i - 1], target) < 0
    99  		} else {
   100  			j = h // preserves cmp(x[j], target) >= 0
   101  		}
   102  	}
   103  	// i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0  =>  answer is i.
   104  	return i, i < n && cmp(x[i], target) == 0
   105  }
   106  
   107  type sortedHint int // hint for pdqsort when choosing the pivot
   108  
   109  const (
   110  	unknownHint sortedHint = iota
   111  	increasingHint
   112  	decreasingHint
   113  )
   114  
   115  // xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
   116  type xorshift uint64
   117  
   118  func (r *xorshift) Next() uint64 {
   119  	*r ^= *r << 13
   120  	*r ^= *r >> 17
   121  	*r ^= *r << 5
   122  	return uint64(*r)
   123  }
   124  
   125  func nextPowerOfTwo(length int) uint {
   126  	return 1 << bits.Len(uint(length))
   127  }