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