Browse Source

vendor: golang.org/x/exp v0.0.0-20231006140011-7918f672742d

full diff: https://github.com/golang/exp/compare/c95f2b4c22f2...7918f672742d

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn 1 year ago
parent
commit
a60fef0c41

+ 1 - 1
vendor.mod

@@ -204,7 +204,7 @@ require (
 	go.uber.org/multierr v1.8.0 // indirect
 	go.uber.org/zap v1.21.0 // indirect
 	golang.org/x/crypto v0.17.0 // indirect
-	golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect
+	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
 	golang.org/x/oauth2 v0.11.0 // indirect
 	golang.org/x/tools v0.14.0 // indirect
 	google.golang.org/api v0.126.0 // indirect

+ 2 - 2
vendor.sum

@@ -1411,8 +1411,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI=
-golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
+golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

+ 44 - 0
vendor/golang.org/x/exp/slices/cmp.go

@@ -0,0 +1,44 @@
+// Copyright 2023 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"
+
+// min is a version of the predeclared function from the Go 1.21 release.
+func min[T constraints.Ordered](a, b T) T {
+	if a < b || isNaN(a) {
+		return a
+	}
+	return b
+}
+
+// max is a version of the predeclared function from the Go 1.21 release.
+func max[T constraints.Ordered](a, b T) T {
+	if a > b || isNaN(a) {
+		return a
+	}
+	return b
+}
+
+// cmpLess is a copy of cmp.Less from the Go 1.21 release.
+func cmpLess[T constraints.Ordered](x, y T) bool {
+	return (isNaN(x) && !isNaN(y)) || x < y
+}
+
+// cmpCompare is a copy of cmp.Compare from the Go 1.21 release.
+func cmpCompare[T constraints.Ordered](x, y T) int {
+	xNaN := isNaN(x)
+	yNaN := isNaN(y)
+	if xNaN && yNaN {
+		return 0
+	}
+	if xNaN || x < y {
+		return -1
+	}
+	if yNaN || x > y {
+		return +1
+	}
+	return 0
+}

+ 311 - 70
vendor/golang.org/x/exp/slices/slices.go

@@ -3,23 +3,20 @@
 // license that can be found in the LICENSE file.
 
 // Package slices defines various functions useful with slices of any type.
-// Unless otherwise specified, these functions all apply to the elements
-// of a slice at index 0 <= i < len(s).
-//
-// Note that the less function in IsSortedFunc, SortFunc, SortStableFunc requires a
-// strict weak ordering (https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings),
-// or the sorting may fail to sort correctly. A common case is when sorting slices of
-// floating-point numbers containing NaN values.
 package slices
 
-import "golang.org/x/exp/constraints"
+import (
+	"unsafe"
+
+	"golang.org/x/exp/constraints"
+)
 
 // Equal reports whether two slices are equal: the same length and all
 // elements equal. If the lengths are different, Equal returns false.
 // Otherwise, the elements are compared in increasing index order, and the
 // comparison stops at the first unequal pair.
 // Floating point NaNs are not considered equal.
-func Equal[E comparable](s1, s2 []E) bool {
+func Equal[S ~[]E, E comparable](s1, s2 S) bool {
 	if len(s1) != len(s2) {
 		return false
 	}
@@ -31,12 +28,12 @@ func Equal[E comparable](s1, s2 []E) bool {
 	return true
 }
 
-// EqualFunc reports whether two slices are equal using a comparison
+// EqualFunc reports whether two slices are equal using an equality
 // function on each pair of elements. If the lengths are different,
 // EqualFunc returns false. Otherwise, the elements are compared in
 // increasing index order, and the comparison stops at the first index
 // for which eq returns false.
-func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
+func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool {
 	if len(s1) != len(s2) {
 		return false
 	}
@@ -49,45 +46,37 @@ func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
 	return true
 }
 
-// Compare compares the elements of s1 and s2.
-// The elements are compared sequentially, starting at index 0,
+// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair
+// of elements. The elements are compared sequentially, starting at index 0,
 // until one element is not equal to the other.
 // The result of comparing the first non-matching elements is returned.
 // If both slices are equal until one of them ends, the shorter slice is
 // considered less than the longer one.
 // The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
-// Comparisons involving floating point NaNs are ignored.
-func Compare[E constraints.Ordered](s1, s2 []E) int {
-	s2len := len(s2)
+func Compare[S ~[]E, E constraints.Ordered](s1, s2 S) int {
 	for i, v1 := range s1 {
-		if i >= s2len {
+		if i >= len(s2) {
 			return +1
 		}
 		v2 := s2[i]
-		switch {
-		case v1 < v2:
-			return -1
-		case v1 > v2:
-			return +1
+		if c := cmpCompare(v1, v2); c != 0 {
+			return c
 		}
 	}
-	if len(s1) < s2len {
+	if len(s1) < len(s2) {
 		return -1
 	}
 	return 0
 }
 
-// CompareFunc is like Compare but uses a comparison function
-// on each pair of elements. The elements are compared in increasing
-// index order, and the comparisons stop after the first time cmp
-// returns non-zero.
+// CompareFunc is like [Compare] but uses a custom comparison function on each
+// pair of elements.
 // The result is the first non-zero result of cmp; if cmp always
 // returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
 // and +1 if len(s1) > len(s2).
-func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
-	s2len := len(s2)
+func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int {
 	for i, v1 := range s1 {
-		if i >= s2len {
+		if i >= len(s2) {
 			return +1
 		}
 		v2 := s2[i]
@@ -95,7 +84,7 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
 			return c
 		}
 	}
-	if len(s1) < s2len {
+	if len(s1) < len(s2) {
 		return -1
 	}
 	return 0
@@ -103,9 +92,9 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
 
 // Index returns the index of the first occurrence of v in s,
 // or -1 if not present.
-func Index[E comparable](s []E, v E) int {
-	for i, vs := range s {
-		if v == vs {
+func Index[S ~[]E, E comparable](s S, v E) int {
+	for i := range s {
+		if v == s[i] {
 			return i
 		}
 	}
@@ -114,9 +103,9 @@ func Index[E comparable](s []E, v E) int {
 
 // IndexFunc returns the first index i satisfying f(s[i]),
 // or -1 if none do.
-func IndexFunc[E any](s []E, f func(E) bool) int {
-	for i, v := range s {
-		if f(v) {
+func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
+	for i := range s {
+		if f(s[i]) {
 			return i
 		}
 	}
@@ -124,39 +113,104 @@ func IndexFunc[E any](s []E, f func(E) bool) int {
 }
 
 // Contains reports whether v is present in s.
-func Contains[E comparable](s []E, v E) bool {
+func Contains[S ~[]E, E comparable](s S, v E) bool {
 	return Index(s, v) >= 0
 }
 
 // ContainsFunc reports whether at least one
 // element e of s satisfies f(e).
-func ContainsFunc[E any](s []E, f func(E) bool) bool {
+func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool {
 	return IndexFunc(s, f) >= 0
 }
 
 // Insert inserts the values v... into s at index i,
 // returning the modified slice.
-// In the returned slice r, r[i] == v[0].
+// The elements at s[i:] are shifted up to make room.
+// In the returned slice r, r[i] == v[0],
+// and r[i+len(v)] == value originally at r[i].
 // Insert panics if i is out of range.
 // This function is O(len(s) + len(v)).
 func Insert[S ~[]E, E any](s S, i int, v ...E) S {
-	tot := len(s) + len(v)
-	if tot <= cap(s) {
-		s2 := s[:tot]
-		copy(s2[i+len(v):], s[i:])
+	m := len(v)
+	if m == 0 {
+		return s
+	}
+	n := len(s)
+	if i == n {
+		return append(s, v...)
+	}
+	if n+m > cap(s) {
+		// Use append rather than make so that we bump the size of
+		// the slice up to the next storage class.
+		// This is what Grow does but we don't call Grow because
+		// that might copy the values twice.
+		s2 := append(s[:i], make(S, n+m-i)...)
 		copy(s2[i:], v)
+		copy(s2[i+m:], s[i:])
 		return s2
 	}
-	s2 := make(S, tot)
-	copy(s2, s[:i])
-	copy(s2[i:], v)
-	copy(s2[i+len(v):], s[i:])
-	return s2
+	s = s[:n+m]
+
+	// before:
+	// s: aaaaaaaabbbbccccccccdddd
+	//            ^   ^       ^   ^
+	//            i  i+m      n  n+m
+	// after:
+	// s: aaaaaaaavvvvbbbbcccccccc
+	//            ^   ^       ^   ^
+	//            i  i+m      n  n+m
+	//
+	// a are the values that don't move in s.
+	// v are the values copied in from v.
+	// b and c are the values from s that are shifted up in index.
+	// d are the values that get overwritten, never to be seen again.
+
+	if !overlaps(v, s[i+m:]) {
+		// Easy case - v does not overlap either the c or d regions.
+		// (It might be in some of a or b, or elsewhere entirely.)
+		// The data we copy up doesn't write to v at all, so just do it.
+
+		copy(s[i+m:], s[i:])
+
+		// Now we have
+		// s: aaaaaaaabbbbbbbbcccccccc
+		//            ^   ^       ^   ^
+		//            i  i+m      n  n+m
+		// Note the b values are duplicated.
+
+		copy(s[i:], v)
+
+		// Now we have
+		// s: aaaaaaaavvvvbbbbcccccccc
+		//            ^   ^       ^   ^
+		//            i  i+m      n  n+m
+		// That's the result we want.
+		return s
+	}
+
+	// The hard case - v overlaps c or d. We can't just shift up
+	// the data because we'd move or clobber the values we're trying
+	// to insert.
+	// So instead, write v on top of d, then rotate.
+	copy(s[n:], v)
+
+	// Now we have
+	// s: aaaaaaaabbbbccccccccvvvv
+	//            ^   ^       ^   ^
+	//            i  i+m      n  n+m
+
+	rotateRight(s[i:], m)
+
+	// Now we have
+	// s: aaaaaaaavvvvbbbbcccccccc
+	//            ^   ^       ^   ^
+	//            i  i+m      n  n+m
+	// That's the result we want.
+	return s
 }
 
 // Delete removes the elements s[i:j] from s, returning the modified slice.
 // Delete panics if s[i:j] is not a valid slice of s.
-// Delete modifies the contents of the slice s; it does not create a new slice.
 // Delete is O(len(s)-j), so if many items must be deleted, it is better to
 // make a single call deleting them all together than to delete one at a time.
 // Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those
@@ -168,22 +222,113 @@ func Delete[S ~[]E, E any](s S, i, j int) S {
 	return append(s[:i], s[j:]...)
 }
 
+// DeleteFunc removes any elements from s for which del returns true,
+// returning the modified slice.
+// When DeleteFunc removes m elements, it might not modify the elements
+// s[len(s)-m:len(s)]. If those elements contain pointers you might consider
+// zeroing those elements so that objects they reference can be garbage
+// collected.
+func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
+	i := IndexFunc(s, del)
+	if i == -1 {
+		return s
+	}
+	// Don't start copying elements until we find one to delete.
+	for j := i + 1; j < len(s); j++ {
+		if v := s[j]; !del(v) {
+			s[i] = v
+			i++
+		}
+	}
+	return s[:i]
+}
+
 // Replace replaces the elements s[i:j] by the given v, and returns the
 // modified slice. Replace panics if s[i:j] is not a valid slice of s.
 func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
 	_ = s[i:j] // verify that i:j is a valid subslice
+
+	if i == j {
+		return Insert(s, i, v...)
+	}
+	if j == len(s) {
+		return append(s[:i], v...)
+	}
+
 	tot := len(s[:i]) + len(v) + len(s[j:])
-	if tot <= cap(s) {
-		s2 := s[:tot]
-		copy(s2[i+len(v):], s[j:])
+	if tot > cap(s) {
+		// Too big to fit, allocate and copy over.
+		s2 := append(s[:i], make(S, tot-i)...) // See Insert
 		copy(s2[i:], v)
+		copy(s2[i+len(v):], s[j:])
 		return s2
 	}
-	s2 := make(S, tot)
-	copy(s2, s[:i])
-	copy(s2[i:], v)
-	copy(s2[i+len(v):], s[j:])
-	return s2
+
+	r := s[:tot]
+
+	if i+len(v) <= j {
+		// Easy, as v fits in the deleted portion.
+		copy(r[i:], v)
+		if i+len(v) != j {
+			copy(r[i+len(v):], s[j:])
+		}
+		return r
+	}
+
+	// We are expanding (v is bigger than j-i).
+	// The situation is something like this:
+	// (example has i=4,j=8,len(s)=16,len(v)=6)
+	// s: aaaaxxxxbbbbbbbbyy
+	//        ^   ^       ^ ^
+	//        i   j  len(s) tot
+	// a: prefix of s
+	// x: deleted range
+	// b: more of s
+	// y: area to expand into
+
+	if !overlaps(r[i+len(v):], v) {
+		// Easy, as v is not clobbered by the first copy.
+		copy(r[i+len(v):], s[j:])
+		copy(r[i:], v)
+		return r
+	}
+
+	// This is a situation where we don't have a single place to which
+	// we can copy v. Parts of it need to go to two different places.
+	// We want to copy the prefix of v into y and the suffix into x, then
+	// rotate |y| spots to the right.
+	//
+	//        v[2:]      v[:2]
+	//         |           |
+	// s: aaaavvvvbbbbbbbbvv
+	//        ^   ^       ^ ^
+	//        i   j  len(s) tot
+	//
+	// If either of those two destinations don't alias v, then we're good.
+	y := len(v) - (j - i) // length of y portion
+
+	if !overlaps(r[i:j], v) {
+		copy(r[i:j], v[y:])
+		copy(r[len(s):], v[:y])
+		rotateRight(r[i:], y)
+		return r
+	}
+	if !overlaps(r[len(s):], v) {
+		copy(r[len(s):], v[:y])
+		copy(r[i:j], v[y:])
+		rotateRight(r[i:], y)
+		return r
+	}
+
+	// Now we know that v overlaps both x and y.
+	// That means that the entirety of b is *inside* v.
+	// So we don't need to preserve b at all; instead we
+	// can copy v first, then copy the b part of v out of
+	// v to the right destination.
+	k := startIdx(v, s[j:])
+	copy(r[i:], v)
+	copy(r[i+len(v):], r[i+k:])
+	return r
 }
 
 // Clone returns a copy of the slice.
@@ -198,7 +343,8 @@ func Clone[S ~[]E, E any](s S) S {
 
 // Compact replaces consecutive runs of equal elements with a single copy.
 // This is like the uniq command found on Unix.
-// Compact modifies the contents of the slice s; it does not create a new slice.
+// Compact modifies the contents of the slice s and returns the modified slice,
+// which may have a smaller length.
 // When Compact discards m elements in total, it might not modify the elements
 // s[len(s)-m:len(s)]. If those elements contain pointers you might consider
 // zeroing those elements so that objects they reference can be garbage collected.
@@ -207,29 +353,30 @@ func Compact[S ~[]E, E comparable](s S) S {
 		return s
 	}
 	i := 1
-	last := s[0]
-	for _, v := range s[1:] {
-		if v != last {
-			s[i] = v
+	for k := 1; k < len(s); k++ {
+		if s[k] != s[k-1] {
+			if i != k {
+				s[i] = s[k]
+			}
 			i++
-			last = v
 		}
 	}
 	return s[:i]
 }
 
-// CompactFunc is like Compact but uses a comparison function.
+// CompactFunc is like [Compact] but uses an equality function to compare elements.
+// For runs of elements that compare equal, CompactFunc keeps the first one.
 func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
 	if len(s) < 2 {
 		return s
 	}
 	i := 1
-	last := s[0]
-	for _, v := range s[1:] {
-		if !eq(v, last) {
-			s[i] = v
+	for k := 1; k < len(s); k++ {
+		if !eq(s[k], s[k-1]) {
+			if i != k {
+				s[i] = s[k]
+			}
 			i++
-			last = v
 		}
 	}
 	return s[:i]
@@ -256,3 +403,97 @@ func Grow[S ~[]E, E any](s S, n int) S {
 func Clip[S ~[]E, E any](s S) S {
 	return s[:len(s):len(s)]
 }
+
+// Rotation algorithm explanation:
+//
+// rotate left by 2
+// start with
+//   0123456789
+// split up like this
+//   01 234567 89
+// swap first 2 and last 2
+//   89 234567 01
+// join first parts
+//   89234567 01
+// recursively rotate first left part by 2
+//   23456789 01
+// join at the end
+//   2345678901
+//
+// rotate left by 8
+// start with
+//   0123456789
+// split up like this
+//   01 234567 89
+// swap first 2 and last 2
+//   89 234567 01
+// join last parts
+//   89 23456701
+// recursively rotate second part left by 6
+//   89 01234567
+// join at the end
+//   8901234567
+
+// TODO: There are other rotate algorithms.
+// This algorithm has the desirable property that it moves each element exactly twice.
+// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes.
+// The follow-cycles algorithm can be 1-write but it is not very cache friendly.
+
+// rotateLeft rotates b left by n spaces.
+// s_final[i] = s_orig[i+r], wrapping around.
+func rotateLeft[E any](s []E, r int) {
+	for r != 0 && r != len(s) {
+		if r*2 <= len(s) {
+			swap(s[:r], s[len(s)-r:])
+			s = s[:len(s)-r]
+		} else {
+			swap(s[:len(s)-r], s[r:])
+			s, r = s[len(s)-r:], r*2-len(s)
+		}
+	}
+}
+func rotateRight[E any](s []E, r int) {
+	rotateLeft(s, len(s)-r)
+}
+
+// swap swaps the contents of x and y. x and y must be equal length and disjoint.
+func swap[E any](x, y []E) {
+	for i := 0; i < len(x); i++ {
+		x[i], y[i] = y[i], x[i]
+	}
+}
+
+// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap.
+func overlaps[E any](a, b []E) bool {
+	if len(a) == 0 || len(b) == 0 {
+		return false
+	}
+	elemSize := unsafe.Sizeof(a[0])
+	if elemSize == 0 {
+		return false
+	}
+	// TODO: use a runtime/unsafe facility once one becomes available. See issue 12445.
+	// Also see crypto/internal/alias/alias.go:AnyOverlap
+	return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) &&
+		uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1)
+}
+
+// startIdx returns the index in haystack where the needle starts.
+// prerequisite: the needle must be aliased entirely inside the haystack.
+func startIdx[E any](haystack, needle []E) int {
+	p := &needle[0]
+	for i := range haystack {
+		if p == &haystack[i] {
+			return i
+		}
+	}
+	// TODO: what if the overlap is by a non-integral number of Es?
+	panic("needle not found")
+}
+
+// Reverse reverses the elements of the slice in place.
+func Reverse[S ~[]E, E any](s S) {
+	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
+		s[i], s[j] = s[j], s[i]
+	}
+}

+ 97 - 28
vendor/golang.org/x/exp/slices/sort.go

@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+//go:generate go run $GOROOT/src/sort/gen_sort_variants.go -exp
+
 package slices
 
 import (
@@ -11,57 +13,116 @@ import (
 )
 
 // Sort sorts a slice of any ordered type in ascending order.
-// Sort may fail to sort correctly when sorting slices of floating-point
-// numbers containing Not-a-number (NaN) values.
-// Use slices.SortFunc(x, func(a, b float64) bool {return a < b || (math.IsNaN(a) && !math.IsNaN(b))})
-// instead if the input may contain NaNs.
-func Sort[E constraints.Ordered](x []E) {
+// When sorting floating-point numbers, NaNs are ordered before other values.
+func Sort[S ~[]E, E constraints.Ordered](x S) {
 	n := len(x)
 	pdqsortOrdered(x, 0, n, bits.Len(uint(n)))
 }
 
-// SortFunc sorts the slice x in ascending order as determined by the less function.
-// This sort is not guaranteed to be stable.
+// SortFunc sorts the slice x in ascending order as determined by the cmp
+// function. This sort is not guaranteed to be stable.
+// cmp(a, b) should return a negative number when a < b, a positive number when
+// a > b and zero when a == b.
 //
-// SortFunc requires that less is a strict weak ordering.
+// SortFunc requires that cmp is a strict weak ordering.
 // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
-func SortFunc[E any](x []E, less func(a, b E) bool) {
+func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
 	n := len(x)
-	pdqsortLessFunc(x, 0, n, bits.Len(uint(n)), less)
+	pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp)
 }
 
 // SortStableFunc sorts the slice x while keeping the original order of equal
-// elements, using less to compare elements.
-func SortStableFunc[E any](x []E, less func(a, b E) bool) {
-	stableLessFunc(x, len(x), less)
+// elements, using cmp to compare elements in the same way as [SortFunc].
+func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
+	stableCmpFunc(x, len(x), cmp)
 }
 
 // IsSorted reports whether x is sorted in ascending order.
-func IsSorted[E constraints.Ordered](x []E) bool {
+func IsSorted[S ~[]E, E constraints.Ordered](x S) bool {
 	for i := len(x) - 1; i > 0; i-- {
-		if x[i] < x[i-1] {
+		if cmpLess(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[E any](x []E, less func(a, b E) bool) bool {
+// IsSortedFunc reports whether x is sorted in ascending order, with cmp as the
+// comparison function as defined by [SortFunc].
+func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool {
 	for i := len(x) - 1; i > 0; i-- {
-		if less(x[i], x[i-1]) {
+		if cmp(x[i], x[i-1]) < 0 {
 			return false
 		}
 	}
 	return true
 }
 
+// Min returns the minimal value in x. It panics if x is empty.
+// For floating-point numbers, Min propagates NaNs (any NaN value in x
+// forces the output to be NaN).
+func Min[S ~[]E, E constraints.Ordered](x S) E {
+	if len(x) < 1 {
+		panic("slices.Min: empty list")
+	}
+	m := x[0]
+	for i := 1; i < len(x); i++ {
+		m = min(m, x[i])
+	}
+	return m
+}
+
+// MinFunc returns the minimal value in x, using cmp to compare elements.
+// It panics if x is empty. If there is more than one minimal element
+// according to the cmp function, MinFunc returns the first one.
+func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
+	if len(x) < 1 {
+		panic("slices.MinFunc: empty list")
+	}
+	m := x[0]
+	for i := 1; i < len(x); i++ {
+		if cmp(x[i], m) < 0 {
+			m = x[i]
+		}
+	}
+	return m
+}
+
+// Max returns the maximal value in x. It panics if x is empty.
+// For floating-point E, Max propagates NaNs (any NaN value in x
+// forces the output to be NaN).
+func Max[S ~[]E, E constraints.Ordered](x S) E {
+	if len(x) < 1 {
+		panic("slices.Max: empty list")
+	}
+	m := x[0]
+	for i := 1; i < len(x); i++ {
+		m = max(m, x[i])
+	}
+	return m
+}
+
+// MaxFunc returns the maximal value in x, using cmp to compare elements.
+// It panics if x is empty. If there is more than one maximal element
+// according to the cmp function, MaxFunc returns the first one.
+func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
+	if len(x) < 1 {
+		panic("slices.MaxFunc: empty list")
+	}
+	m := x[0]
+	for i := 1; i < len(x); i++ {
+		if cmp(x[i], m) > 0 {
+			m = x[i]
+		}
+	}
+	return m
+}
+
 // BinarySearch searches for target in a sorted slice and returns the position
 // where target is found, or the position where target would appear in the
 // sort order; it also returns a bool saying whether the target is really found
 // in the slice. The slice must be sorted in increasing order.
-func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) {
+func BinarySearch[S ~[]E, E constraints.Ordered](x S, target E) (int, bool) {
 	// Inlining is faster than calling BinarySearchFunc with a lambda.
 	n := len(x)
 	// Define x[-1] < target and x[n] >= target.
@@ -70,22 +131,24 @@ func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) {
 	for i < j {
 		h := int(uint(i+j) >> 1) // avoid overflow when computing h
 		// i ≤ h < j
-		if x[h] < target {
+		if cmpLess(x[h], target) {
 			i = h + 1 // preserves x[i-1] < target
 		} else {
 			j = h // preserves x[j] >= target
 		}
 	}
 	// i == j, x[i-1] < target, and x[j] (= x[i]) >= target  =>  answer is i.
-	return i, i < n && x[i] == target
+	return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target)))
 }
 
-// BinarySearchFunc works like BinarySearch, but uses a custom comparison
-// function. The slice must be sorted in increasing order, where "increasing" is
-// defined by cmp. cmp(a, b) is expected to return an integer comparing the two
-// parameters: 0 if a == b, a negative number if a < b and a positive number if
-// a > b.
-func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool) {
+// BinarySearchFunc works like [BinarySearch], but uses a custom comparison
+// function. The slice must be sorted in increasing order, where "increasing"
+// is defined by cmp. cmp should return 0 if the slice element matches
+// the target, a negative number if the slice element precedes the target,
+// or a positive number if the slice element follows the target.
+// cmp must implement the same ordering as the slice, such that if
+// cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice.
+func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) {
 	n := len(x)
 	// Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 .
 	// Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0.
@@ -124,3 +187,9 @@ func (r *xorshift) Next() uint64 {
 func nextPowerOfTwo(length int) uint {
 	return 1 << bits.Len(uint(length))
 }
+
+// isNaN reports whether x is a NaN without requiring the math package.
+// This will always return false if T is not floating-point.
+func isNaN[T constraints.Ordered](x T) bool {
+	return x != x
+}

+ 77 - 77
vendor/golang.org/x/exp/slices/zsortfunc.go → vendor/golang.org/x/exp/slices/zsortanyfunc.go

@@ -6,28 +6,28 @@
 
 package slices
 
-// insertionSortLessFunc sorts data[a:b] using insertion sort.
-func insertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
+// insertionSortCmpFunc sorts data[a:b] using insertion sort.
+func insertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
 	for i := a + 1; i < b; i++ {
-		for j := i; j > a && less(data[j], data[j-1]); j-- {
+		for j := i; j > a && (cmp(data[j], data[j-1]) < 0); j-- {
 			data[j], data[j-1] = data[j-1], data[j]
 		}
 	}
 }
 
-// siftDownLessFunc implements the heap property on data[lo:hi].
+// siftDownCmpFunc implements the heap property on data[lo:hi].
 // first is an offset into the array where the root of the heap lies.
-func siftDownLessFunc[E any](data []E, lo, hi, first int, less func(a, b E) bool) {
+func siftDownCmpFunc[E any](data []E, lo, hi, first int, cmp func(a, b E) int) {
 	root := lo
 	for {
 		child := 2*root + 1
 		if child >= hi {
 			break
 		}
-		if child+1 < hi && less(data[first+child], data[first+child+1]) {
+		if child+1 < hi && (cmp(data[first+child], data[first+child+1]) < 0) {
 			child++
 		}
-		if !less(data[first+root], data[first+child]) {
+		if !(cmp(data[first+root], data[first+child]) < 0) {
 			return
 		}
 		data[first+root], data[first+child] = data[first+child], data[first+root]
@@ -35,30 +35,30 @@ func siftDownLessFunc[E any](data []E, lo, hi, first int, less func(a, b E) bool
 	}
 }
 
-func heapSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
+func heapSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
 	first := a
 	lo := 0
 	hi := b - a
 
 	// Build heap with greatest element at top.
 	for i := (hi - 1) / 2; i >= 0; i-- {
-		siftDownLessFunc(data, i, hi, first, less)
+		siftDownCmpFunc(data, i, hi, first, cmp)
 	}
 
 	// Pop elements, largest first, into end of data.
 	for i := hi - 1; i >= 0; i-- {
 		data[first], data[first+i] = data[first+i], data[first]
-		siftDownLessFunc(data, lo, i, first, less)
+		siftDownCmpFunc(data, lo, i, first, cmp)
 	}
 }
 
-// pdqsortLessFunc sorts data[a:b].
+// pdqsortCmpFunc sorts data[a:b].
 // The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
 // pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
 // C++ implementation: https://github.com/orlp/pdqsort
 // Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
 // limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
-func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) {
+func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) {
 	const maxInsertion = 12
 
 	var (
@@ -70,25 +70,25 @@ func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) {
 		length := b - a
 
 		if length <= maxInsertion {
-			insertionSortLessFunc(data, a, b, less)
+			insertionSortCmpFunc(data, a, b, cmp)
 			return
 		}
 
 		// Fall back to heapsort if too many bad choices were made.
 		if limit == 0 {
-			heapSortLessFunc(data, a, b, less)
+			heapSortCmpFunc(data, a, b, cmp)
 			return
 		}
 
 		// If the last partitioning was imbalanced, we need to breaking patterns.
 		if !wasBalanced {
-			breakPatternsLessFunc(data, a, b, less)
+			breakPatternsCmpFunc(data, a, b, cmp)
 			limit--
 		}
 
-		pivot, hint := choosePivotLessFunc(data, a, b, less)
+		pivot, hint := choosePivotCmpFunc(data, a, b, cmp)
 		if hint == decreasingHint {
-			reverseRangeLessFunc(data, a, b, less)
+			reverseRangeCmpFunc(data, a, b, cmp)
 			// The chosen pivot was pivot-a elements after the start of the array.
 			// After reversing it is pivot-a elements before the end of the array.
 			// The idea came from Rust's implementation.
@@ -98,48 +98,48 @@ func pdqsortLessFunc[E any](data []E, a, b, limit int, less func(a, b E) bool) {
 
 		// The slice is likely already sorted.
 		if wasBalanced && wasPartitioned && hint == increasingHint {
-			if partialInsertionSortLessFunc(data, a, b, less) {
+			if partialInsertionSortCmpFunc(data, a, b, cmp) {
 				return
 			}
 		}
 
 		// Probably the slice contains many duplicate elements, partition the slice into
 		// elements equal to and elements greater than the pivot.
-		if a > 0 && !less(data[a-1], data[pivot]) {
-			mid := partitionEqualLessFunc(data, a, b, pivot, less)
+		if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) {
+			mid := partitionEqualCmpFunc(data, a, b, pivot, cmp)
 			a = mid
 			continue
 		}
 
-		mid, alreadyPartitioned := partitionLessFunc(data, a, b, pivot, less)
+		mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp)
 		wasPartitioned = alreadyPartitioned
 
 		leftLen, rightLen := mid-a, b-mid
 		balanceThreshold := length / 8
 		if leftLen < rightLen {
 			wasBalanced = leftLen >= balanceThreshold
-			pdqsortLessFunc(data, a, mid, limit, less)
+			pdqsortCmpFunc(data, a, mid, limit, cmp)
 			a = mid + 1
 		} else {
 			wasBalanced = rightLen >= balanceThreshold
-			pdqsortLessFunc(data, mid+1, b, limit, less)
+			pdqsortCmpFunc(data, mid+1, b, limit, cmp)
 			b = mid
 		}
 	}
 }
 
-// partitionLessFunc does one quicksort partition.
+// partitionCmpFunc does one quicksort partition.
 // Let p = data[pivot]
 // Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
 // On return, data[newpivot] = p
-func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int, alreadyPartitioned bool) {
+func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) {
 	data[a], data[pivot] = data[pivot], data[a]
 	i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
 
-	for i <= j && less(data[i], data[a]) {
+	for i <= j && (cmp(data[i], data[a]) < 0) {
 		i++
 	}
-	for i <= j && !less(data[j], data[a]) {
+	for i <= j && !(cmp(data[j], data[a]) < 0) {
 		j--
 	}
 	if i > j {
@@ -151,10 +151,10 @@ func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool)
 	j--
 
 	for {
-		for i <= j && less(data[i], data[a]) {
+		for i <= j && (cmp(data[i], data[a]) < 0) {
 			i++
 		}
-		for i <= j && !less(data[j], data[a]) {
+		for i <= j && !(cmp(data[j], data[a]) < 0) {
 			j--
 		}
 		if i > j {
@@ -168,17 +168,17 @@ func partitionLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool)
 	return j, false
 }
 
-// partitionEqualLessFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
+// partitionEqualCmpFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
 // It assumed that data[a:b] does not contain elements smaller than the data[pivot].
-func partitionEqualLessFunc[E any](data []E, a, b, pivot int, less func(a, b E) bool) (newpivot int) {
+func partitionEqualCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) {
 	data[a], data[pivot] = data[pivot], data[a]
 	i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
 
 	for {
-		for i <= j && !less(data[a], data[i]) {
+		for i <= j && !(cmp(data[a], data[i]) < 0) {
 			i++
 		}
-		for i <= j && less(data[a], data[j]) {
+		for i <= j && (cmp(data[a], data[j]) < 0) {
 			j--
 		}
 		if i > j {
@@ -191,15 +191,15 @@ func partitionEqualLessFunc[E any](data []E, a, b, pivot int, less func(a, b E)
 	return i
 }
 
-// partialInsertionSortLessFunc partially sorts a slice, returns true if the slice is sorted at the end.
-func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) bool) bool {
+// partialInsertionSortCmpFunc partially sorts a slice, returns true if the slice is sorted at the end.
+func partialInsertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) bool {
 	const (
 		maxSteps         = 5  // maximum number of adjacent out-of-order pairs that will get shifted
 		shortestShifting = 50 // don't shift any elements on short arrays
 	)
 	i := a + 1
 	for j := 0; j < maxSteps; j++ {
-		for i < b && !less(data[i], data[i-1]) {
+		for i < b && !(cmp(data[i], data[i-1]) < 0) {
 			i++
 		}
 
@@ -216,7 +216,7 @@ func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) b
 		// Shift the smaller one to the left.
 		if i-a >= 2 {
 			for j := i - 1; j >= 1; j-- {
-				if !less(data[j], data[j-1]) {
+				if !(cmp(data[j], data[j-1]) < 0) {
 					break
 				}
 				data[j], data[j-1] = data[j-1], data[j]
@@ -225,7 +225,7 @@ func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) b
 		// Shift the greater one to the right.
 		if b-i >= 2 {
 			for j := i + 1; j < b; j++ {
-				if !less(data[j], data[j-1]) {
+				if !(cmp(data[j], data[j-1]) < 0) {
 					break
 				}
 				data[j], data[j-1] = data[j-1], data[j]
@@ -235,9 +235,9 @@ func partialInsertionSortLessFunc[E any](data []E, a, b int, less func(a, b E) b
 	return false
 }
 
-// breakPatternsLessFunc scatters some elements around in an attempt to break some patterns
+// breakPatternsCmpFunc scatters some elements around in an attempt to break some patterns
 // that might cause imbalanced partitions in quicksort.
-func breakPatternsLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
+func breakPatternsCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
 	length := b - a
 	if length >= 8 {
 		random := xorshift(length)
@@ -253,12 +253,12 @@ func breakPatternsLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
 	}
 }
 
-// choosePivotLessFunc chooses a pivot in data[a:b].
+// choosePivotCmpFunc chooses a pivot in data[a:b].
 //
 // [0,8): chooses a static pivot.
 // [8,shortestNinther): uses the simple median-of-three method.
 // [shortestNinther,∞): uses the Tukey ninther method.
-func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (pivot int, hint sortedHint) {
+func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) {
 	const (
 		shortestNinther = 50
 		maxSwaps        = 4 * 3
@@ -276,12 +276,12 @@ func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (piv
 	if l >= 8 {
 		if l >= shortestNinther {
 			// Tukey ninther method, the idea came from Rust's implementation.
-			i = medianAdjacentLessFunc(data, i, &swaps, less)
-			j = medianAdjacentLessFunc(data, j, &swaps, less)
-			k = medianAdjacentLessFunc(data, k, &swaps, less)
+			i = medianAdjacentCmpFunc(data, i, &swaps, cmp)
+			j = medianAdjacentCmpFunc(data, j, &swaps, cmp)
+			k = medianAdjacentCmpFunc(data, k, &swaps, cmp)
 		}
 		// Find the median among i, j, k and stores it into j.
-		j = medianLessFunc(data, i, j, k, &swaps, less)
+		j = medianCmpFunc(data, i, j, k, &swaps, cmp)
 	}
 
 	switch swaps {
@@ -294,29 +294,29 @@ func choosePivotLessFunc[E any](data []E, a, b int, less func(a, b E) bool) (piv
 	}
 }
 
-// order2LessFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
-func order2LessFunc[E any](data []E, a, b int, swaps *int, less func(a, b E) bool) (int, int) {
-	if less(data[b], data[a]) {
+// order2CmpFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
+func order2CmpFunc[E any](data []E, a, b int, swaps *int, cmp func(a, b E) int) (int, int) {
+	if cmp(data[b], data[a]) < 0 {
 		*swaps++
 		return b, a
 	}
 	return a, b
 }
 
-// medianLessFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
-func medianLessFunc[E any](data []E, a, b, c int, swaps *int, less func(a, b E) bool) int {
-	a, b = order2LessFunc(data, a, b, swaps, less)
-	b, c = order2LessFunc(data, b, c, swaps, less)
-	a, b = order2LessFunc(data, a, b, swaps, less)
+// medianCmpFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
+func medianCmpFunc[E any](data []E, a, b, c int, swaps *int, cmp func(a, b E) int) int {
+	a, b = order2CmpFunc(data, a, b, swaps, cmp)
+	b, c = order2CmpFunc(data, b, c, swaps, cmp)
+	a, b = order2CmpFunc(data, a, b, swaps, cmp)
 	return b
 }
 
-// medianAdjacentLessFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
-func medianAdjacentLessFunc[E any](data []E, a int, swaps *int, less func(a, b E) bool) int {
-	return medianLessFunc(data, a-1, a, a+1, swaps, less)
+// medianAdjacentCmpFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
+func medianAdjacentCmpFunc[E any](data []E, a int, swaps *int, cmp func(a, b E) int) int {
+	return medianCmpFunc(data, a-1, a, a+1, swaps, cmp)
 }
 
-func reverseRangeLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
+func reverseRangeCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) {
 	i := a
 	j := b - 1
 	for i < j {
@@ -326,37 +326,37 @@ func reverseRangeLessFunc[E any](data []E, a, b int, less func(a, b E) bool) {
 	}
 }
 
-func swapRangeLessFunc[E any](data []E, a, b, n int, less func(a, b E) bool) {
+func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) {
 	for i := 0; i < n; i++ {
 		data[a+i], data[b+i] = data[b+i], data[a+i]
 	}
 }
 
-func stableLessFunc[E any](data []E, n int, less func(a, b E) bool) {
+func stableCmpFunc[E any](data []E, n int, cmp func(a, b E) int) {
 	blockSize := 20 // must be > 0
 	a, b := 0, blockSize
 	for b <= n {
-		insertionSortLessFunc(data, a, b, less)
+		insertionSortCmpFunc(data, a, b, cmp)
 		a = b
 		b += blockSize
 	}
-	insertionSortLessFunc(data, a, n, less)
+	insertionSortCmpFunc(data, a, n, cmp)
 
 	for blockSize < n {
 		a, b = 0, 2*blockSize
 		for b <= n {
-			symMergeLessFunc(data, a, a+blockSize, b, less)
+			symMergeCmpFunc(data, a, a+blockSize, b, cmp)
 			a = b
 			b += 2 * blockSize
 		}
 		if m := a + blockSize; m < n {
-			symMergeLessFunc(data, a, m, n, less)
+			symMergeCmpFunc(data, a, m, n, cmp)
 		}
 		blockSize *= 2
 	}
 }
 
-// symMergeLessFunc merges the two sorted subsequences data[a:m] and data[m:b] using
+// symMergeCmpFunc merges the two sorted subsequences data[a:m] and data[m:b] using
 // the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum
 // Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz
 // Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in
@@ -375,7 +375,7 @@ func stableLessFunc[E any](data []E, n int, less func(a, b E) bool) {
 // symMerge assumes non-degenerate arguments: a < m && m < b.
 // Having the caller check this condition eliminates many leaf recursion calls,
 // which improves performance.
-func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
+func symMergeCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
 	// Avoid unnecessary recursions of symMerge
 	// by direct insertion of data[a] into data[m:b]
 	// if data[a:m] only contains one element.
@@ -387,7 +387,7 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
 		j := b
 		for i < j {
 			h := int(uint(i+j) >> 1)
-			if less(data[h], data[a]) {
+			if cmp(data[h], data[a]) < 0 {
 				i = h + 1
 			} else {
 				j = h
@@ -411,7 +411,7 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
 		j := m
 		for i < j {
 			h := int(uint(i+j) >> 1)
-			if !less(data[m], data[h]) {
+			if !(cmp(data[m], data[h]) < 0) {
 				i = h + 1
 			} else {
 				j = h
@@ -438,7 +438,7 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
 
 	for start < r {
 		c := int(uint(start+r) >> 1)
-		if !less(data[p-c], data[c]) {
+		if !(cmp(data[p-c], data[c]) < 0) {
 			start = c + 1
 		} else {
 			r = c
@@ -447,33 +447,33 @@ func symMergeLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
 
 	end := n - start
 	if start < m && m < end {
-		rotateLessFunc(data, start, m, end, less)
+		rotateCmpFunc(data, start, m, end, cmp)
 	}
 	if a < start && start < mid {
-		symMergeLessFunc(data, a, start, mid, less)
+		symMergeCmpFunc(data, a, start, mid, cmp)
 	}
 	if mid < end && end < b {
-		symMergeLessFunc(data, mid, end, b, less)
+		symMergeCmpFunc(data, mid, end, b, cmp)
 	}
 }
 
-// rotateLessFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
+// rotateCmpFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data:
 // Data of the form 'x u v y' is changed to 'x v u y'.
 // rotate performs at most b-a many calls to data.Swap,
 // and it assumes non-degenerate arguments: a < m && m < b.
-func rotateLessFunc[E any](data []E, a, m, b int, less func(a, b E) bool) {
+func rotateCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) {
 	i := m - a
 	j := b - m
 
 	for i != j {
 		if i > j {
-			swapRangeLessFunc(data, m-i, m, j, less)
+			swapRangeCmpFunc(data, m-i, m, j, cmp)
 			i -= j
 		} else {
-			swapRangeLessFunc(data, m-i, m+j-i, i, less)
+			swapRangeCmpFunc(data, m-i, m+j-i, i, cmp)
 			j -= i
 		}
 	}
 	// i == j
-	swapRangeLessFunc(data, m-i, m, i, less)
+	swapRangeCmpFunc(data, m-i, m, i, cmp)
 }

+ 17 - 17
vendor/golang.org/x/exp/slices/zsortordered.go

@@ -11,7 +11,7 @@ import "golang.org/x/exp/constraints"
 // insertionSortOrdered sorts data[a:b] using insertion sort.
 func insertionSortOrdered[E constraints.Ordered](data []E, a, b int) {
 	for i := a + 1; i < b; i++ {
-		for j := i; j > a && (data[j] < data[j-1]); j-- {
+		for j := i; j > a && cmpLess(data[j], data[j-1]); j-- {
 			data[j], data[j-1] = data[j-1], data[j]
 		}
 	}
@@ -26,10 +26,10 @@ func siftDownOrdered[E constraints.Ordered](data []E, lo, hi, first int) {
 		if child >= hi {
 			break
 		}
-		if child+1 < hi && (data[first+child] < data[first+child+1]) {
+		if child+1 < hi && cmpLess(data[first+child], data[first+child+1]) {
 			child++
 		}
-		if !(data[first+root] < data[first+child]) {
+		if !cmpLess(data[first+root], data[first+child]) {
 			return
 		}
 		data[first+root], data[first+child] = data[first+child], data[first+root]
@@ -107,7 +107,7 @@ func pdqsortOrdered[E constraints.Ordered](data []E, a, b, limit int) {
 
 		// Probably the slice contains many duplicate elements, partition the slice into
 		// elements equal to and elements greater than the pivot.
-		if a > 0 && !(data[a-1] < data[pivot]) {
+		if a > 0 && !cmpLess(data[a-1], data[pivot]) {
 			mid := partitionEqualOrdered(data, a, b, pivot)
 			a = mid
 			continue
@@ -138,10 +138,10 @@ func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivo
 	data[a], data[pivot] = data[pivot], data[a]
 	i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
 
-	for i <= j && (data[i] < data[a]) {
+	for i <= j && cmpLess(data[i], data[a]) {
 		i++
 	}
-	for i <= j && !(data[j] < data[a]) {
+	for i <= j && !cmpLess(data[j], data[a]) {
 		j--
 	}
 	if i > j {
@@ -153,10 +153,10 @@ func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivo
 	j--
 
 	for {
-		for i <= j && (data[i] < data[a]) {
+		for i <= j && cmpLess(data[i], data[a]) {
 			i++
 		}
-		for i <= j && !(data[j] < data[a]) {
+		for i <= j && !cmpLess(data[j], data[a]) {
 			j--
 		}
 		if i > j {
@@ -177,10 +177,10 @@ func partitionEqualOrdered[E constraints.Ordered](data []E, a, b, pivot int) (ne
 	i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
 
 	for {
-		for i <= j && !(data[a] < data[i]) {
+		for i <= j && !cmpLess(data[a], data[i]) {
 			i++
 		}
-		for i <= j && (data[a] < data[j]) {
+		for i <= j && cmpLess(data[a], data[j]) {
 			j--
 		}
 		if i > j {
@@ -201,7 +201,7 @@ func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool
 	)
 	i := a + 1
 	for j := 0; j < maxSteps; j++ {
-		for i < b && !(data[i] < data[i-1]) {
+		for i < b && !cmpLess(data[i], data[i-1]) {
 			i++
 		}
 
@@ -218,7 +218,7 @@ func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool
 		// Shift the smaller one to the left.
 		if i-a >= 2 {
 			for j := i - 1; j >= 1; j-- {
-				if !(data[j] < data[j-1]) {
+				if !cmpLess(data[j], data[j-1]) {
 					break
 				}
 				data[j], data[j-1] = data[j-1], data[j]
@@ -227,7 +227,7 @@ func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool
 		// Shift the greater one to the right.
 		if b-i >= 2 {
 			for j := i + 1; j < b; j++ {
-				if !(data[j] < data[j-1]) {
+				if !cmpLess(data[j], data[j-1]) {
 					break
 				}
 				data[j], data[j-1] = data[j-1], data[j]
@@ -298,7 +298,7 @@ func choosePivotOrdered[E constraints.Ordered](data []E, a, b int) (pivot int, h
 
 // order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
 func order2Ordered[E constraints.Ordered](data []E, a, b int, swaps *int) (int, int) {
-	if data[b] < data[a] {
+	if cmpLess(data[b], data[a]) {
 		*swaps++
 		return b, a
 	}
@@ -389,7 +389,7 @@ func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) {
 		j := b
 		for i < j {
 			h := int(uint(i+j) >> 1)
-			if data[h] < data[a] {
+			if cmpLess(data[h], data[a]) {
 				i = h + 1
 			} else {
 				j = h
@@ -413,7 +413,7 @@ func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) {
 		j := m
 		for i < j {
 			h := int(uint(i+j) >> 1)
-			if !(data[m] < data[h]) {
+			if !cmpLess(data[m], data[h]) {
 				i = h + 1
 			} else {
 				j = h
@@ -440,7 +440,7 @@ func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) {
 
 	for start < r {
 		c := int(uint(start+r) >> 1)
-		if !(data[p-c] < data[c]) {
+		if !cmpLess(data[p-c], data[c]) {
 			start = c + 1
 		} else {
 			r = c

+ 2 - 2
vendor/modules.txt

@@ -1245,8 +1245,8 @@ golang.org/x/crypto/pkcs12/internal/rc2
 golang.org/x/crypto/salsa20/salsa
 golang.org/x/crypto/ssh
 golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
-# golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
-## explicit; go 1.18
+# golang.org/x/exp v0.0.0-20231006140011-7918f672742d
+## explicit; go 1.20
 golang.org/x/exp/constraints
 golang.org/x/exp/maps
 golang.org/x/exp/slices