// insertionSort sorts data[a:b] using insertion sort. func insertionSort(data Interface, a, b int) { … } // siftDown implements the heap property on data[lo:hi]. // first is an offset into the array where the root of the heap lies. func siftDown(data Interface, lo, hi, first int) { … } func heapSort(data Interface, a, b int) { … } // pdqsort 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 pdqsort(data Interface, a, b, limit int) { … } // partition 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 partition(data Interface, a, b, pivot int) (newpivot int, alreadyPartitioned bool) { … } // partitionEqual 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 partitionEqual(data Interface, a, b, pivot int) (newpivot int) { … } // partialInsertionSort partially sorts a slice, returns true if the slice is sorted at the end. func partialInsertionSort(data Interface, a, b int) bool { … } // breakPatterns scatters some elements around in an attempt to break some patterns // that might cause imbalanced partitions in quicksort. func breakPatterns(data Interface, a, b int) { … } // choosePivot 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 choosePivot(data Interface, a, b int) (pivot int, hint sortedHint) { … } // order2 returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. func order2(data Interface, a, b int, swaps *int) (int, int) { … } // median returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. func median(data Interface, a, b, c int, swaps *int) int { … } // medianAdjacent finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. func medianAdjacent(data Interface, a int, swaps *int) int { … } func reverseRange(data Interface, a, b int) { … } func swapRange(data Interface, a, b, n int) { … } func stable(data Interface, n int) { … } // symMerge 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 // Computer Science, pages 714-723. Springer, 2004. // // Let M = m-a and N = b-n. Wolog M < N. // The recursion depth is bound by ceil(log(N+M)). // The algorithm needs O(M*log(N/M + 1)) calls to data.Less. // The algorithm needs O((M+N)*log(M)) calls to data.Swap. // // The paper gives O((M+N)*log(M)) as the number of assignments assuming a // rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation // in the paper carries through for Swap operations, especially as the block // swapping rotate uses only O(M+N) Swaps. // // symMerge assumes non-degenerate arguments: a < m && m < b. // Having the caller check this condition eliminates many leaf recursion calls, // which improves performance. func symMerge(data Interface, a, m, b int) { … } // rotate 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 rotate(data Interface, a, m, b int) { … }