1// Code generated by gen_sort_variants.go; DO NOT EDIT. 2 3// Copyright 2022 The Go Authors. All rights reserved. 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file. 6 7package slices 8 9import "cmp" 10 11// insertionSortOrdered sorts data[a:b] using insertion sort. 12func insertionSortOrdered[E cmp.Ordered](data []E, a, b int) { 13 for i := a + 1; i < b; i++ { 14 for j := i; j > a && cmp.Less(data[j], data[j-1]); j-- { 15 data[j], data[j-1] = data[j-1], data[j] 16 } 17 } 18} 19 20// siftDownOrdered implements the heap property on data[lo:hi]. 21// first is an offset into the array where the root of the heap lies. 22func siftDownOrdered[E cmp.Ordered](data []E, lo, hi, first int) { 23 root := lo 24 for { 25 child := 2*root + 1 26 if child >= hi { 27 break 28 } 29 if child+1 < hi && cmp.Less(data[first+child], data[first+child+1]) { 30 child++ 31 } 32 if !cmp.Less(data[first+root], data[first+child]) { 33 return 34 } 35 data[first+root], data[first+child] = data[first+child], data[first+root] 36 root = child 37 } 38} 39 40func heapSortOrdered[E cmp.Ordered](data []E, a, b int) { 41 first := a 42 lo := 0 43 hi := b - a 44 45 // Build heap with greatest element at top. 46 for i := (hi - 1) / 2; i >= 0; i-- { 47 siftDownOrdered(data, i, hi, first) 48 } 49 50 // Pop elements, largest first, into end of data. 51 for i := hi - 1; i >= 0; i-- { 52 data[first], data[first+i] = data[first+i], data[first] 53 siftDownOrdered(data, lo, i, first) 54 } 55} 56 57// pdqsortOrdered sorts data[a:b]. 58// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. 59// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf 60// C++ implementation: https://github.com/orlp/pdqsort 61// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ 62// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. 63func pdqsortOrdered[E cmp.Ordered](data []E, a, b, limit int) { 64 const maxInsertion = 12 65 66 var ( 67 wasBalanced = true // whether the last partitioning was reasonably balanced 68 wasPartitioned = true // whether the slice was already partitioned 69 ) 70 71 for { 72 length := b - a 73 74 if length <= maxInsertion { 75 insertionSortOrdered(data, a, b) 76 return 77 } 78 79 // Fall back to heapsort if too many bad choices were made. 80 if limit == 0 { 81 heapSortOrdered(data, a, b) 82 return 83 } 84 85 // If the last partitioning was imbalanced, we need to breaking patterns. 86 if !wasBalanced { 87 breakPatternsOrdered(data, a, b) 88 limit-- 89 } 90 91 pivot, hint := choosePivotOrdered(data, a, b) 92 if hint == decreasingHint { 93 reverseRangeOrdered(data, a, b) 94 // The chosen pivot was pivot-a elements after the start of the array. 95 // After reversing it is pivot-a elements before the end of the array. 96 // The idea came from Rust's implementation. 97 pivot = (b - 1) - (pivot - a) 98 hint = increasingHint 99 } 100 101 // The slice is likely already sorted. 102 if wasBalanced && wasPartitioned && hint == increasingHint { 103 if partialInsertionSortOrdered(data, a, b) { 104 return 105 } 106 } 107 108 // Probably the slice contains many duplicate elements, partition the slice into 109 // elements equal to and elements greater than the pivot. 110 if a > 0 && !cmp.Less(data[a-1], data[pivot]) { 111 mid := partitionEqualOrdered(data, a, b, pivot) 112 a = mid 113 continue 114 } 115 116 mid, alreadyPartitioned := partitionOrdered(data, a, b, pivot) 117 wasPartitioned = alreadyPartitioned 118 119 leftLen, rightLen := mid-a, b-mid 120 balanceThreshold := length / 8 121 if leftLen < rightLen { 122 wasBalanced = leftLen >= balanceThreshold 123 pdqsortOrdered(data, a, mid, limit) 124 a = mid + 1 125 } else { 126 wasBalanced = rightLen >= balanceThreshold 127 pdqsortOrdered(data, mid+1, b, limit) 128 b = mid 129 } 130 } 131} 132 133// partitionOrdered does one quicksort partition. 134// Let p = data[pivot] 135// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot. 136// On return, data[newpivot] = p 137func partitionOrdered[E cmp.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) { 138 data[a], data[pivot] = data[pivot], data[a] 139 i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned 140 141 for i <= j && cmp.Less(data[i], data[a]) { 142 i++ 143 } 144 for i <= j && !cmp.Less(data[j], data[a]) { 145 j-- 146 } 147 if i > j { 148 data[j], data[a] = data[a], data[j] 149 return j, true 150 } 151 data[i], data[j] = data[j], data[i] 152 i++ 153 j-- 154 155 for { 156 for i <= j && cmp.Less(data[i], data[a]) { 157 i++ 158 } 159 for i <= j && !cmp.Less(data[j], data[a]) { 160 j-- 161 } 162 if i > j { 163 break 164 } 165 data[i], data[j] = data[j], data[i] 166 i++ 167 j-- 168 } 169 data[j], data[a] = data[a], data[j] 170 return j, false 171} 172 173// partitionEqualOrdered partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. 174// It assumed that data[a:b] does not contain elements smaller than the data[pivot]. 175func partitionEqualOrdered[E cmp.Ordered](data []E, a, b, pivot int) (newpivot int) { 176 data[a], data[pivot] = data[pivot], data[a] 177 i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned 178 179 for { 180 for i <= j && !cmp.Less(data[a], data[i]) { 181 i++ 182 } 183 for i <= j && cmp.Less(data[a], data[j]) { 184 j-- 185 } 186 if i > j { 187 break 188 } 189 data[i], data[j] = data[j], data[i] 190 i++ 191 j-- 192 } 193 return i 194} 195 196// partialInsertionSortOrdered partially sorts a slice, returns true if the slice is sorted at the end. 197func partialInsertionSortOrdered[E cmp.Ordered](data []E, a, b int) bool { 198 const ( 199 maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted 200 shortestShifting = 50 // don't shift any elements on short arrays 201 ) 202 i := a + 1 203 for j := 0; j < maxSteps; j++ { 204 for i < b && !cmp.Less(data[i], data[i-1]) { 205 i++ 206 } 207 208 if i == b { 209 return true 210 } 211 212 if b-a < shortestShifting { 213 return false 214 } 215 216 data[i], data[i-1] = data[i-1], data[i] 217 218 // Shift the smaller one to the left. 219 if i-a >= 2 { 220 for j := i - 1; j >= 1; j-- { 221 if !cmp.Less(data[j], data[j-1]) { 222 break 223 } 224 data[j], data[j-1] = data[j-1], data[j] 225 } 226 } 227 // Shift the greater one to the right. 228 if b-i >= 2 { 229 for j := i + 1; j < b; j++ { 230 if !cmp.Less(data[j], data[j-1]) { 231 break 232 } 233 data[j], data[j-1] = data[j-1], data[j] 234 } 235 } 236 } 237 return false 238} 239 240// breakPatternsOrdered scatters some elements around in an attempt to break some patterns 241// that might cause imbalanced partitions in quicksort. 242func breakPatternsOrdered[E cmp.Ordered](data []E, a, b int) { 243 length := b - a 244 if length >= 8 { 245 random := xorshift(length) 246 modulus := nextPowerOfTwo(length) 247 248 for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ { 249 other := int(uint(random.Next()) & (modulus - 1)) 250 if other >= length { 251 other -= length 252 } 253 data[idx], data[a+other] = data[a+other], data[idx] 254 } 255 } 256} 257 258// choosePivotOrdered chooses a pivot in data[a:b]. 259// 260// [0,8): chooses a static pivot. 261// [8,shortestNinther): uses the simple median-of-three method. 262// [shortestNinther,∞): uses the Tukey ninther method. 263func choosePivotOrdered[E cmp.Ordered](data []E, a, b int) (pivot int, hint sortedHint) { 264 const ( 265 shortestNinther = 50 266 maxSwaps = 4 * 3 267 ) 268 269 l := b - a 270 271 var ( 272 swaps int 273 i = a + l/4*1 274 j = a + l/4*2 275 k = a + l/4*3 276 ) 277 278 if l >= 8 { 279 if l >= shortestNinther { 280 // Tukey ninther method, the idea came from Rust's implementation. 281 i = medianAdjacentOrdered(data, i, &swaps) 282 j = medianAdjacentOrdered(data, j, &swaps) 283 k = medianAdjacentOrdered(data, k, &swaps) 284 } 285 // Find the median among i, j, k and stores it into j. 286 j = medianOrdered(data, i, j, k, &swaps) 287 } 288 289 switch swaps { 290 case 0: 291 return j, increasingHint 292 case maxSwaps: 293 return j, decreasingHint 294 default: 295 return j, unknownHint 296 } 297} 298 299// order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. 300func order2Ordered[E cmp.Ordered](data []E, a, b int, swaps *int) (int, int) { 301 if cmp.Less(data[b], data[a]) { 302 *swaps++ 303 return b, a 304 } 305 return a, b 306} 307 308// medianOrdered returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. 309func medianOrdered[E cmp.Ordered](data []E, a, b, c int, swaps *int) int { 310 a, b = order2Ordered(data, a, b, swaps) 311 b, c = order2Ordered(data, b, c, swaps) 312 a, b = order2Ordered(data, a, b, swaps) 313 return b 314} 315 316// medianAdjacentOrdered finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. 317func medianAdjacentOrdered[E cmp.Ordered](data []E, a int, swaps *int) int { 318 return medianOrdered(data, a-1, a, a+1, swaps) 319} 320 321func reverseRangeOrdered[E cmp.Ordered](data []E, a, b int) { 322 i := a 323 j := b - 1 324 for i < j { 325 data[i], data[j] = data[j], data[i] 326 i++ 327 j-- 328 } 329} 330 331func swapRangeOrdered[E cmp.Ordered](data []E, a, b, n int) { 332 for i := 0; i < n; i++ { 333 data[a+i], data[b+i] = data[b+i], data[a+i] 334 } 335} 336 337func stableOrdered[E cmp.Ordered](data []E, n int) { 338 blockSize := 20 // must be > 0 339 a, b := 0, blockSize 340 for b <= n { 341 insertionSortOrdered(data, a, b) 342 a = b 343 b += blockSize 344 } 345 insertionSortOrdered(data, a, n) 346 347 for blockSize < n { 348 a, b = 0, 2*blockSize 349 for b <= n { 350 symMergeOrdered(data, a, a+blockSize, b) 351 a = b 352 b += 2 * blockSize 353 } 354 if m := a + blockSize; m < n { 355 symMergeOrdered(data, a, m, n) 356 } 357 blockSize *= 2 358 } 359} 360 361// symMergeOrdered merges the two sorted subsequences data[a:m] and data[m:b] using 362// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum 363// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz 364// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in 365// Computer Science, pages 714-723. Springer, 2004. 366// 367// Let M = m-a and N = b-n. Wolog M < N. 368// The recursion depth is bound by ceil(log(N+M)). 369// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. 370// The algorithm needs O((M+N)*log(M)) calls to data.Swap. 371// 372// The paper gives O((M+N)*log(M)) as the number of assignments assuming a 373// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation 374// in the paper carries through for Swap operations, especially as the block 375// swapping rotate uses only O(M+N) Swaps. 376// 377// symMerge assumes non-degenerate arguments: a < m && m < b. 378// Having the caller check this condition eliminates many leaf recursion calls, 379// which improves performance. 380func symMergeOrdered[E cmp.Ordered](data []E, a, m, b int) { 381 // Avoid unnecessary recursions of symMerge 382 // by direct insertion of data[a] into data[m:b] 383 // if data[a:m] only contains one element. 384 if m-a == 1 { 385 // Use binary search to find the lowest index i 386 // such that data[i] >= data[a] for m <= i < b. 387 // Exit the search loop with i == b in case no such index exists. 388 i := m 389 j := b 390 for i < j { 391 h := int(uint(i+j) >> 1) 392 if cmp.Less(data[h], data[a]) { 393 i = h + 1 394 } else { 395 j = h 396 } 397 } 398 // Swap values until data[a] reaches the position before i. 399 for k := a; k < i-1; k++ { 400 data[k], data[k+1] = data[k+1], data[k] 401 } 402 return 403 } 404 405 // Avoid unnecessary recursions of symMerge 406 // by direct insertion of data[m] into data[a:m] 407 // if data[m:b] only contains one element. 408 if b-m == 1 { 409 // Use binary search to find the lowest index i 410 // such that data[i] > data[m] for a <= i < m. 411 // Exit the search loop with i == m in case no such index exists. 412 i := a 413 j := m 414 for i < j { 415 h := int(uint(i+j) >> 1) 416 if !cmp.Less(data[m], data[h]) { 417 i = h + 1 418 } else { 419 j = h 420 } 421 } 422 // Swap values until data[m] reaches the position i. 423 for k := m; k > i; k-- { 424 data[k], data[k-1] = data[k-1], data[k] 425 } 426 return 427 } 428 429 mid := int(uint(a+b) >> 1) 430 n := mid + m 431 var start, r int 432 if m > mid { 433 start = n - b 434 r = mid 435 } else { 436 start = a 437 r = m 438 } 439 p := n - 1 440 441 for start < r { 442 c := int(uint(start+r) >> 1) 443 if !cmp.Less(data[p-c], data[c]) { 444 start = c + 1 445 } else { 446 r = c 447 } 448 } 449 450 end := n - start 451 if start < m && m < end { 452 rotateOrdered(data, start, m, end) 453 } 454 if a < start && start < mid { 455 symMergeOrdered(data, a, start, mid) 456 } 457 if mid < end && end < b { 458 symMergeOrdered(data, mid, end, b) 459 } 460} 461 462// rotateOrdered rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: 463// Data of the form 'x u v y' is changed to 'x v u y'. 464// rotate performs at most b-a many calls to data.Swap, 465// and it assumes non-degenerate arguments: a < m && m < b. 466func rotateOrdered[E cmp.Ordered](data []E, a, m, b int) { 467 i := m - a 468 j := b - m 469 470 for i != j { 471 if i > j { 472 swapRangeOrdered(data, m-i, m, j) 473 i -= j 474 } else { 475 swapRangeOrdered(data, m-i, m+j-i, i) 476 j -= i 477 } 478 } 479 // i == j 480 swapRangeOrdered(data, m-i, m, i) 481} 482