github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sqlbase/sort.go (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 licenses/BSD-golang.txt. 4 5 // Portions of this file are additionally subject to the following 6 // license and copyright. 7 // 8 // Copyright 2017 The Cockroach Authors. 9 // 10 // Use of this software is governed by the Business Source License 11 // included in the file licenses/BSL.txt. 12 // 13 // As of the Change Date specified in that file, in accordance with 14 // the Business Source License, use of this software will be governed 15 // by the Apache License, Version 2.0, included in the file 16 // licenses/APL.txt. 17 18 package sqlbase 19 20 import "sort" 21 22 // The next few sorting functions are largely unmodified 23 // from the GO standard library implementation, save for occasional 24 // cancellation checks. 25 // 26 // TODO(itsbilal): Consider referencing the RowContainer pointer type 27 // directly instead of redirecting all calls through sort.Interface. 28 29 // Insertion sort 30 func insertionSort(data sort.Interface, a, b int) { 31 for i := a + 1; i < b; i++ { 32 for j := i; j > a && data.Less(j, j-1); j-- { 33 data.Swap(j, j-1) 34 } 35 } 36 } 37 38 // siftDown implements the heap property on data[lo, hi). 39 // first is an offset into the array where the root of the heap lies. 40 func siftDown(data sort.Interface, lo, hi, first int) { 41 root := lo 42 for { 43 child := 2*root + 1 44 if child >= hi { 45 break 46 } 47 if child+1 < hi && data.Less(first+child, first+child+1) { 48 child++ 49 } 50 if !data.Less(first+root, first+child) { 51 return 52 } 53 data.Swap(first+root, first+child) 54 root = child 55 } 56 } 57 58 func heapSort(data sort.Interface, a, b int, cancelChecker *CancelChecker) { 59 first := a 60 lo := 0 61 hi := b - a 62 63 // Build heap with greatest element at top. 64 for i := (hi - 1) / 2; i >= 0; i-- { 65 if cancelChecker.Check() != nil { 66 return 67 } 68 siftDown(data, i, hi, first) 69 } 70 71 // Pop elements, largest first, into end of data. 72 for i := hi - 1; i >= 0; i-- { 73 data.Swap(first, first+i) 74 siftDown(data, lo, i, first) 75 } 76 } 77 78 // Quicksort, loosely following Bentley and McIlroy, 79 // ``Engineering a Sort Function,'' SP&E November 1993. 80 81 // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. 82 func medianOfThree(data sort.Interface, m1, m0, m2 int) { 83 // sort 3 elements 84 if data.Less(m1, m0) { 85 data.Swap(m1, m0) 86 } 87 // data[m0] <= data[m1] 88 if data.Less(m2, m1) { 89 data.Swap(m2, m1) 90 // data[m0] <= data[m2] && data[m1] < data[m2] 91 if data.Less(m1, m0) { 92 data.Swap(m1, m0) 93 } 94 } 95 // now data[m0] <= data[m1] <= data[m2] 96 } 97 98 func doPivot(data sort.Interface, lo, hi int) (midlo, midhi int) { 99 m := lo + (hi-lo)/2 // Written like this to avoid integer overflow. 100 if hi-lo > 40 { 101 // Tukey's ``Ninther,'' median of three medians of three. 102 s := (hi - lo) / 8 103 medianOfThree(data, lo, lo+s, lo+2*s) 104 medianOfThree(data, m, m-s, m+s) 105 medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) 106 } 107 medianOfThree(data, lo, m, hi-1) 108 109 // Invariants are: 110 // data[lo] = pivot (set up by ChoosePivot) 111 // data[lo < i < a] < pivot 112 // data[a <= i < b] <= pivot 113 // data[b <= i < c] unexamined 114 // data[c <= i < hi-1] > pivot 115 // data[hi-1] >= pivot 116 pivot := lo 117 a, c := lo+1, hi-1 118 119 for ; a < c && data.Less(a, pivot); a++ { 120 } 121 b := a 122 for { 123 for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot 124 } 125 for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot 126 } 127 if b >= c { 128 break 129 } 130 // data[b] > pivot; data[c-1] <= pivot 131 data.Swap(b, c-1) 132 b++ 133 c-- 134 } 135 // If hi-c<3 then there are duplicates (by property of median of nine). 136 // Let be a bit more conservative, and set border to 5. 137 protect := hi-c < 5 138 if !protect && hi-c < (hi-lo)/4 { 139 // Lets test some points for equality to pivot 140 dups := 0 141 if !data.Less(pivot, hi-1) { // data[hi-1] = pivot 142 data.Swap(c, hi-1) 143 c++ 144 dups++ 145 } 146 if !data.Less(b-1, pivot) { // data[b-1] = pivot 147 b-- 148 dups++ 149 } 150 // m-lo = (hi-lo)/2 > 6 151 // b-lo > (hi-lo)*3/4-1 > 8 152 // ==> m < b ==> data[m] <= pivot 153 if !data.Less(m, pivot) { // data[m] = pivot 154 data.Swap(m, b-1) 155 b-- 156 dups++ 157 } 158 // if at least 2 points are equal to pivot, assume skewed distribution 159 protect = dups > 1 160 } 161 if protect { 162 // Protect against a lot of duplicates 163 // Add invariant: 164 // data[a <= i < b] unexamined 165 // data[b <= i < c] = pivot 166 for { 167 for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot 168 } 169 for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot 170 } 171 if a >= b { 172 break 173 } 174 // data[a] == pivot; data[b-1] < pivot 175 data.Swap(a, b-1) 176 a++ 177 b-- 178 } 179 } 180 // Swap pivot into middle 181 data.Swap(pivot, b-1) 182 return b - 1, c 183 } 184 185 // maxDepth returns a threshold at which quicksort should switch 186 // to heapsort. It returns 2*ceil(lg(n+1)). 187 func maxDepth(n int) int { 188 var depth int 189 for i := n; i > 0; i >>= 1 { 190 depth++ 191 } 192 return depth * 2 193 } 194 195 func quickSort(data sort.Interface, a, b, maxDepth int, cancelChecker *CancelChecker) { 196 for b-a > 12 { // Use ShellSort for slices <= 12 elements 197 if maxDepth == 0 { 198 heapSort(data, a, b, cancelChecker) 199 return 200 } 201 maxDepth-- 202 // Short-circuit sort if necessary. 203 if cancelChecker.Check() != nil { 204 return 205 } 206 mlo, mhi := doPivot(data, a, b) 207 // Avoiding recursion on the larger subproblem guarantees 208 // a stack depth of at most lg(b-a). 209 if mlo-a < b-mhi { 210 quickSort(data, a, mlo, maxDepth, cancelChecker) 211 a = mhi // i.e., quickSort(data, mhi, b) 212 } else { 213 quickSort(data, mhi, b, maxDepth, cancelChecker) 214 b = mlo // i.e., quickSort(data, a, mlo) 215 } 216 } 217 if b-a > 1 { 218 // Do ShellSort pass with gap 6 219 // It could be written in this simplified form cause b-a <= 12 220 for i := a + 6; i < b; i++ { 221 if data.Less(i, i-6) { 222 data.Swap(i, i-6) 223 } 224 } 225 insertionSort(data, a, b) 226 } 227 } 228 229 // Sort sorts data. 230 // It makes one call to data.Len to determine n, and O(n*log(n)) calls to 231 // data.Less and data.Swap. The sort is not guaranteed to be stable. 232 func Sort(data sort.Interface, cancelChecker *CancelChecker) { 233 n := data.Len() 234 quickSort(data, 0, n, maxDepth(n), cancelChecker) 235 }