github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/gnovm/stdlibs/sort/sort.gno (about) 1 // Copyright 2009 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 //go:generate go run genzfunc.go 6 7 // Package sort provides primitives for sorting slices and user-defined collections. 8 package sort 9 10 // An implementation of Interface can be sorted by the routines in this package. 11 // The methods refer to elements of the underlying collection by integer index. 12 type Interface interface { 13 // Len is the number of elements in the collection. 14 Len() int 15 16 // Less reports whether the element with index i 17 // must sort before the element with index j. 18 // 19 // If both Less(i, j) and Less(j, i) are false, 20 // then the elements at index i and j are considered equal. 21 // Sort may place equal elements in any order in the final result, 22 // while Stable preserves the original input order of equal elements. 23 // 24 // Less must describe a transitive ordering: 25 // - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well. 26 // - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well. 27 // 28 // Note that floating-point comparison (the < operator on float32 or float64 values) 29 // is not a transitive ordering when not-a-number (NaN) values are involved. 30 // See Float64Slice.Less for a correct implementation for floating-point values. 31 Less(i, j int) bool 32 33 // Swap swaps the elements with indexes i and j. 34 Swap(i, j int) 35 } 36 37 // insertionSort sorts data[a:b] using insertion sort. 38 func insertionSort(data Interface, a, b int) { 39 for i := a + 1; i < b; i++ { 40 for j := i; j > a && data.Less(j, j-1); j-- { 41 data.Swap(j, j-1) 42 } 43 } 44 } 45 46 // siftDown implements the heap property on data[lo:hi]. 47 // first is an offset into the array where the root of the heap lies. 48 func siftDown(data Interface, lo, hi, first int) { 49 root := lo 50 for { 51 child := 2*root + 1 52 if child >= hi { 53 break 54 } 55 if child+1 < hi && data.Less(first+child, first+child+1) { 56 child++ 57 } 58 if !data.Less(first+root, first+child) { 59 return 60 } 61 data.Swap(first+root, first+child) 62 root = child 63 } 64 } 65 66 func heapSort(data Interface, a, b int) { 67 first := a 68 lo := 0 69 hi := b - a 70 71 // Build heap with greatest element at top. 72 for i := (hi - 1) / 2; i >= 0; i-- { 73 siftDown(data, i, hi, first) 74 } 75 76 // Pop elements, largest first, into end of data. 77 for i := hi - 1; i >= 0; i-- { 78 data.Swap(first, first+i) 79 siftDown(data, lo, i, first) 80 } 81 } 82 83 // Quicksort, loosely following Bentley and McIlroy, 84 // ``Engineering a Sort Function,'' SP&E November 1993. 85 86 // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. 87 func medianOfThree(data Interface, m1, m0, m2 int) { 88 // sort 3 elements 89 if data.Less(m1, m0) { 90 data.Swap(m1, m0) 91 } 92 // data[m0] <= data[m1] 93 if data.Less(m2, m1) { 94 data.Swap(m2, m1) 95 // data[m0] <= data[m2] && data[m1] < data[m2] 96 if data.Less(m1, m0) { 97 data.Swap(m1, m0) 98 } 99 } 100 // now data[m0] <= data[m1] <= data[m2] 101 } 102 103 func swapRange(data Interface, a, b, n int) { 104 for i := 0; i < n; i++ { 105 data.Swap(a+i, b+i) 106 } 107 } 108 109 func doPivot(data Interface, lo, hi int) (midlo, midhi int) { 110 m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. 111 if hi-lo > 40 { 112 // Tukey's ``Ninther,'' median of three medians of three. 113 s := (hi - lo) / 8 114 medianOfThree(data, lo, lo+s, lo+2*s) 115 medianOfThree(data, m, m-s, m+s) 116 medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) 117 } 118 medianOfThree(data, lo, m, hi-1) 119 120 // Invariants are: 121 // data[lo] = pivot (set up by ChoosePivot) 122 // data[lo < i < a] < pivot 123 // data[a <= i < b] <= pivot 124 // data[b <= i < c] unexamined 125 // data[c <= i < hi-1] > pivot 126 // data[hi-1] >= pivot 127 pivot := lo 128 a, c := lo+1, hi-1 129 130 for ; a < c && data.Less(a, pivot); a++ { 131 } 132 b := a 133 for { 134 for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot 135 } 136 for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot 137 } 138 if b >= c { 139 break 140 } 141 // data[b] > pivot; data[c-1] <= pivot 142 data.Swap(b, c-1) 143 b++ 144 c-- 145 } 146 // If hi-c<3 then there are duplicates (by property of median of nine). 147 // Let's be a bit more conservative, and set border to 5. 148 protect := hi-c < 5 149 if !protect && hi-c < (hi-lo)/4 { 150 // Lets test some points for equality to pivot 151 dups := 0 152 if !data.Less(pivot, hi-1) { // data[hi-1] = pivot 153 data.Swap(c, hi-1) 154 c++ 155 dups++ 156 } 157 if !data.Less(b-1, pivot) { // data[b-1] = pivot 158 b-- 159 dups++ 160 } 161 // m-lo = (hi-lo)/2 > 6 162 // b-lo > (hi-lo)*3/4-1 > 8 163 // ==> m < b ==> data[m] <= pivot 164 if !data.Less(m, pivot) { // data[m] = pivot 165 data.Swap(m, b-1) 166 b-- 167 dups++ 168 } 169 // if at least 2 points are equal to pivot, assume skewed distribution 170 protect = dups > 1 171 } 172 if protect { 173 // Protect against a lot of duplicates 174 // Add invariant: 175 // data[a <= i < b] unexamined 176 // data[b <= i < c] = pivot 177 for { 178 for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot 179 } 180 for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot 181 } 182 if a >= b { 183 break 184 } 185 // data[a] == pivot; data[b-1] < pivot 186 data.Swap(a, b-1) 187 a++ 188 b-- 189 } 190 } 191 // Swap pivot into middle 192 data.Swap(pivot, b-1) 193 return b - 1, c 194 } 195 196 func quickSort(data Interface, a, b, maxDepth int) { 197 for b-a > 12 { // Use ShellSort for slices <= 12 elements 198 if maxDepth == 0 { 199 heapSort(data, a, b) 200 return 201 } 202 maxDepth-- 203 mlo, mhi := doPivot(data, a, b) 204 // Avoiding recursion on the larger subproblem guarantees 205 // a stack depth of at most lg(b-a). 206 if mlo-a < b-mhi { 207 quickSort(data, a, mlo, maxDepth) 208 a = mhi // i.e., quickSort(data, mhi, b) 209 } else { 210 quickSort(data, mhi, b, maxDepth) 211 b = mlo // i.e., quickSort(data, a, mlo) 212 } 213 } 214 if b-a > 1 { 215 // Do ShellSort pass with gap 6 216 // It could be written in this simplified form cause b-a <= 12 217 for i := a + 6; i < b; i++ { 218 if data.Less(i, i-6) { 219 data.Swap(i, i-6) 220 } 221 } 222 insertionSort(data, a, b) 223 } 224 } 225 226 // Sort sorts data. 227 // It makes one call to data.Len to determine n and O(n*log(n)) calls to 228 // data.Less and data.Swap. The sort is not guaranteed to be stable. 229 func Sort(data Interface) { 230 n := data.Len() 231 quickSort(data, 0, n, maxDepth(n)) 232 } 233 234 // maxDepth returns a threshold at which quicksort should switch 235 // to heapsort. It returns 2*ceil(lg(n+1)). 236 func maxDepth(n int) int { 237 var depth int 238 for i := n; i > 0; i >>= 1 { 239 depth++ 240 } 241 return depth * 2 242 } 243 244 // lessSwap is a pair of Less and Swap function for use with the 245 // auto-generated func-optimized variant of sort.go in 246 // zfuncversion.go. 247 type lessSwap struct { 248 Less func(i, j int) bool 249 Swap func(i, j int) 250 } 251 252 type reverse struct { 253 // This embedded Interface permits Reverse to use the methods of 254 // another Interface implementation. 255 Interface 256 } 257 258 // Less returns the opposite of the embedded implementation's Less method. 259 func (r reverse) Less(i, j int) bool { 260 return r.Interface.Less(j, i) 261 } 262 263 // Reverse returns the reverse order for data. 264 func Reverse(data Interface) Interface { 265 return &reverse{data} 266 } 267 268 // IsSorted reports whether data is sorted. 269 func IsSorted(data Interface) bool { 270 n := data.Len() 271 for i := n - 1; i > 0; i-- { 272 if data.Less(i, i-1) { 273 return false 274 } 275 } 276 return true 277 } 278 279 // Convenience types for common cases 280 281 // IntSlice attaches the methods of Interface to []int, sorting in increasing order. 282 type IntSlice []int 283 284 func (x IntSlice) Len() int { return len(x) } 285 func (x IntSlice) Less(i, j int) bool { return x[i] < x[j] } 286 func (x IntSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 287 288 // Sort is a convenience method: x.Sort() calls Sort(x). 289 func (x IntSlice) Sort() { Sort(x) } 290 291 // Float64Slice implements Interface for a []float64, sorting in increasing order, 292 // with not-a-number (NaN) values ordered before other values. 293 type Float64Slice []float64 294 295 func (x Float64Slice) Len() int { return len(x) } 296 297 // Less reports whether x[i] should be ordered before x[j], as required by the sort Interface. 298 // Note that floating-point comparison by itself is not a transitive relation: it does not 299 // report a consistent ordering for not-a-number (NaN) values. 300 // This implementation of Less places NaN values before any others, by using: 301 // 302 // x[i] < x[j] || (math.IsNaN(x[i]) && !math.IsNaN(x[j])) 303 func (x Float64Slice) Less(i, j int) bool { return x[i] < x[j] || (isNaN(x[i]) && !isNaN(x[j])) } 304 func (x Float64Slice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 305 306 // isNaN is a copy of math.IsNaN to avoid a dependency on the math package. 307 func isNaN(f float64) bool { 308 return f != f 309 } 310 311 // Sort is a convenience method: x.Sort() calls Sort(x). 312 func (x Float64Slice) Sort() { Sort(x) } 313 314 // StringSlice attaches the methods of Interface to []string, sorting in increasing order. 315 type StringSlice []string 316 317 func (x StringSlice) Len() int { return len(x) } 318 func (x StringSlice) Less(i, j int) bool { return x[i] < x[j] } 319 func (x StringSlice) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 320 321 // Sort is a convenience method: x.Sort() calls Sort(x). 322 func (x StringSlice) Sort() { Sort(x) } 323 324 // Convenience wrappers for common cases 325 326 // Ints sorts a slice of ints in increasing order. 327 func Ints(x []int) { Sort(IntSlice(x)) } 328 329 // Float64s sorts a slice of float64s in increasing order. 330 // Not-a-number (NaN) values are ordered before other values. 331 func Float64s(x []float64) { Sort(Float64Slice(x)) } 332 333 // Strings sorts a slice of strings in increasing order. 334 func Strings(x []string) { Sort(StringSlice(x)) } 335 336 // IntsAreSorted reports whether the slice x is sorted in increasing order. 337 func IntsAreSorted(x []int) bool { return IsSorted(IntSlice(x)) } 338 339 // Float64sAreSorted reports whether the slice x is sorted in increasing order, 340 // with not-a-number (NaN) values before any other values. 341 func Float64sAreSorted(x []float64) bool { return IsSorted(Float64Slice(x)) } 342 343 // StringsAreSorted reports whether the slice x is sorted in increasing order. 344 func StringsAreSorted(x []string) bool { return IsSorted(StringSlice(x)) } 345 346 // Notes on stable sorting: 347 // The used algorithms are simple and provable correct on all input and use 348 // only logarithmic additional stack space. They perform well if compared 349 // experimentally to other stable in-place sorting algorithms. 350 // 351 // Remarks on other algorithms evaluated: 352 // - GCC's 4.6.3 stable_sort with merge_without_buffer from libstdc++: 353 // Not faster. 354 // - GCC's __rotate for block rotations: Not faster. 355 // - "Practical in-place mergesort" from Jyrki Katajainen, Tomi A. Pasanen 356 // and Jukka Teuhola; Nordic Journal of Computing 3,1 (1996), 27-40: 357 // The given algorithms are in-place, number of Swap and Assignments 358 // grow as n log n but the algorithm is not stable. 359 // - "Fast Stable In-Place Sorting with O(n) Data Moves" J.I. Munro and 360 // V. Raman in Algorithmica (1996) 16, 115-160: 361 // This algorithm either needs additional 2n bits or works only if there 362 // are enough different elements available to encode some permutations 363 // which have to be undone later (so not stable on any input). 364 // - All the optimal in-place sorting/merging algorithms I found are either 365 // unstable or rely on enough different elements in each step to encode the 366 // performed block rearrangements. See also "In-Place Merging Algorithms", 367 // Denham Coates-Evely, Department of Computer Science, Kings College, 368 // January 2004 and the references in there. 369 // - Often "optimal" algorithms are optimal in the number of assignments 370 // but Interface has only Swap as operation. 371 372 // Stable sorts data while keeping the original order of equal elements. 373 // 374 // It makes one call to data.Len to determine n, O(n*log(n)) calls to 375 // data.Less and O(n*log(n)*log(n)) calls to data.Swap. 376 func Stable(data Interface) { 377 stable(data, data.Len()) 378 } 379 380 func stable(data Interface, n int) { 381 blockSize := 20 // must be > 0 382 a, b := 0, blockSize 383 for b <= n { 384 insertionSort(data, a, b) 385 a = b 386 b += blockSize 387 } 388 insertionSort(data, a, n) 389 390 for blockSize < n { 391 a, b = 0, 2*blockSize 392 for b <= n { 393 symMerge(data, a, a+blockSize, b) 394 a = b 395 b += 2 * blockSize 396 } 397 if m := a + blockSize; m < n { 398 symMerge(data, a, m, n) 399 } 400 blockSize *= 2 401 } 402 } 403 404 // symMerge merges the two sorted subsequences data[a:m] and data[m:b] using 405 // the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum 406 // Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz 407 // Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in 408 // Computer Science, pages 714-723. Springer, 2004. 409 // 410 // Let M = m-a and N = b-n. Wolog M < N. 411 // The recursion depth is bound by ceil(log(N+M)). 412 // The algorithm needs O(M*log(N/M + 1)) calls to data.Less. 413 // The algorithm needs O((M+N)*log(M)) calls to data.Swap. 414 // 415 // The paper gives O((M+N)*log(M)) as the number of assignments assuming a 416 // rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation 417 // in the paper carries through for Swap operations, especially as the block 418 // swapping rotate uses only O(M+N) Swaps. 419 // 420 // symMerge assumes non-degenerate arguments: a < m && m < b. 421 // Having the caller check this condition eliminates many leaf recursion calls, 422 // which improves performance. 423 func symMerge(data Interface, a, m, b int) { 424 // Avoid unnecessary recursions of symMerge 425 // by direct insertion of data[a] into data[m:b] 426 // if data[a:m] only contains one element. 427 if m-a == 1 { 428 // Use binary search to find the lowest index i 429 // such that data[i] >= data[a] for m <= i < b. 430 // Exit the search loop with i == b in case no such index exists. 431 i := m 432 j := b 433 for i < j { 434 h := int(uint(i+j) >> 1) 435 if data.Less(h, a) { 436 i = h + 1 437 } else { 438 j = h 439 } 440 } 441 // Swap values until data[a] reaches the position before i. 442 for k := a; k < i-1; k++ { 443 data.Swap(k, k+1) 444 } 445 return 446 } 447 448 // Avoid unnecessary recursions of symMerge 449 // by direct insertion of data[m] into data[a:m] 450 // if data[m:b] only contains one element. 451 if b-m == 1 { 452 // Use binary search to find the lowest index i 453 // such that data[i] > data[m] for a <= i < m. 454 // Exit the search loop with i == m in case no such index exists. 455 i := a 456 j := m 457 for i < j { 458 h := int(uint(i+j) >> 1) 459 if !data.Less(m, h) { 460 i = h + 1 461 } else { 462 j = h 463 } 464 } 465 // Swap values until data[m] reaches the position i. 466 for k := m; k > i; k-- { 467 data.Swap(k, k-1) 468 } 469 return 470 } 471 472 mid := int(uint(a+b) >> 1) 473 n := mid + m 474 var start, r int 475 if m > mid { 476 start = n - b 477 r = mid 478 } else { 479 start = a 480 r = m 481 } 482 p := n - 1 483 484 for start < r { 485 c := int(uint(start+r) >> 1) 486 if !data.Less(p-c, c) { 487 start = c + 1 488 } else { 489 r = c 490 } 491 } 492 493 end := n - start 494 if start < m && m < end { 495 rotate(data, start, m, end) 496 } 497 if a < start && start < mid { 498 symMerge(data, a, start, mid) 499 } 500 if mid < end && end < b { 501 symMerge(data, mid, end, b) 502 } 503 } 504 505 // rotate rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: 506 // Data of the form 'x u v y' is changed to 'x v u y'. 507 // rotate performs at most b-a many calls to data.Swap, 508 // and it assumes non-degenerate arguments: a < m && m < b. 509 func rotate(data Interface, a, m, b int) { 510 i := m - a 511 j := b - m 512 513 for i != j { 514 if i > j { 515 swapRange(data, m-i, m, j) 516 i -= j 517 } else { 518 swapRange(data, m-i, m+j-i, i) 519 j -= i 520 } 521 } 522 // i == j 523 swapRange(data, m-i, m, i) 524 } 525 526 /* 527 Complexity of Stable Sorting 528 529 530 Complexity of block swapping rotation 531 532 Each Swap puts one new element into its correct, final position. 533 Elements which reach their final position are no longer moved. 534 Thus block swapping rotation needs |u|+|v| calls to Swaps. 535 This is best possible as each element might need a move. 536 537 Pay attention when comparing to other optimal algorithms which 538 typically count the number of assignments instead of swaps: 539 E.g. the optimal algorithm of Dudzinski and Dydek for in-place 540 rotations uses O(u + v + gcd(u,v)) assignments which is 541 better than our O(3 * (u+v)) as gcd(u,v) <= u. 542 543 544 Stable sorting by SymMerge and BlockSwap rotations 545 546 SymMerg complexity for same size input M = N: 547 Calls to Less: O(M*log(N/M+1)) = O(N*log(2)) = O(N) 548 Calls to Swap: O((M+N)*log(M)) = O(2*N*log(N)) = O(N*log(N)) 549 550 (The following argument does not fuzz over a missing -1 or 551 other stuff which does not impact the final result). 552 553 Let n = data.Len(). Assume n = 2^k. 554 555 Plain merge sort performs log(n) = k iterations. 556 On iteration i the algorithm merges 2^(k-i) blocks, each of size 2^i. 557 558 Thus iteration i of merge sort performs: 559 Calls to Less O(2^(k-i) * 2^i) = O(2^k) = O(2^log(n)) = O(n) 560 Calls to Swap O(2^(k-i) * 2^i * log(2^i)) = O(2^k * i) = O(n*i) 561 562 In total k = log(n) iterations are performed; so in total: 563 Calls to Less O(log(n) * n) 564 Calls to Swap O(n + 2*n + 3*n + ... + (k-1)*n + k*n) 565 = O((k/2) * k * n) = O(n * k^2) = O(n * log^2(n)) 566 567 568 Above results should generalize to arbitrary n = 2^k + p 569 and should not be influenced by the initial insertion sort phase: 570 Insertion sort is O(n^2) on Swap and Less, thus O(bs^2) per block of 571 size bs at n/bs blocks: O(bs*n) Swaps and Less during insertion sort. 572 Merge sort iterations start at i = log(bs). With t = log(bs) constant: 573 Calls to Less O((log(n)-t) * n + bs*n) = O(log(n)*n + (bs-t)*n) 574 = O(n * log(n)) 575 Calls to Swap O(n * log^2(n) - (t^2+t)/2*n) = O(n * log^2(n)) 576 577 */