github.com/tobgu/qframe@v0.4.0/internal/sort/sorter.go (about) 1 // This is a straight copy of the sort functions found in the Go stdlib with 2 // the interface type Interface replaced with a concrete type for performance reasons 3 // the original licence text is available in the GO-LICENCE file. 4 5 package sort 6 7 import ( 8 "github.com/tobgu/qframe/internal/column" 9 "github.com/tobgu/qframe/internal/index" 10 ) 11 12 type Sorter struct { 13 index index.Int 14 columns []column.Comparable 15 } 16 17 func New(ix index.Int, columns []column.Comparable) Sorter { 18 return Sorter{index: ix, columns: columns} 19 } 20 21 func (s Sorter) Sort() { 22 n := s.Len() 23 quickSort(s, 0, n, maxDepth(n)) 24 } 25 26 func (s Sorter) Len() int { 27 return len(s.index) 28 } 29 30 func (s Sorter) Swap(i, j int) { 31 s.index[i], s.index[j] = s.index[j], s.index[i] 32 } 33 34 func (s Sorter) Less(i, j int) bool { 35 di, dj := s.index[i], s.index[j] 36 for _, s := range s.columns { 37 r := s.Compare(di, dj) 38 if r == column.LessThan { 39 return true 40 } 41 42 if r == column.GreaterThan { 43 return false 44 } 45 } 46 47 return false 48 } 49 50 // Copyright 2009 The Go Authors. All rights reserved. 51 // Use of this source code is governed by a BSD-style 52 // license that can be found in the LICENSE file. 53 54 // Insertion sort 55 func insertionSort(data Sorter, a, b int) { 56 for i := a + 1; i < b; i++ { 57 for j := i; j > a && data.Less(j, j-1); j-- { 58 data.Swap(j, j-1) 59 } 60 } 61 } 62 63 // siftDown implements the heap property on data[lo, hi). 64 // first is an offset into the array where the root of the heap lies. 65 func siftDown(data Sorter, lo, hi, first int) { 66 root := lo 67 for { 68 child := 2*root + 1 69 if child >= hi { 70 break 71 } 72 if child+1 < hi && data.Less(first+child, first+child+1) { 73 child++ 74 } 75 if !data.Less(first+root, first+child) { 76 return 77 } 78 data.Swap(first+root, first+child) 79 root = child 80 } 81 } 82 83 func heapSort(data Sorter, a, b int) { 84 first := a 85 lo := 0 86 hi := b - a 87 88 // Build heap with greatest element at top. 89 for i := (hi - 1) / 2; i >= 0; i-- { 90 siftDown(data, i, hi, first) 91 } 92 93 // Pop elements, largest first, into end of data. 94 for i := hi - 1; i >= 0; i-- { 95 data.Swap(first, first+i) 96 siftDown(data, lo, i, first) 97 } 98 } 99 100 // Quicksort, loosely following Bentley and McIlroy, 101 // ``Engineering a Sort Function,'' SP&E November 1993. 102 103 // medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1]. 104 func medianOfThree(data Sorter, m1, m0, m2 int) { 105 // sort 3 elements 106 if data.Less(m1, m0) { 107 data.Swap(m1, m0) 108 } 109 // data[m0] <= data[m1] 110 if data.Less(m2, m1) { 111 data.Swap(m2, m1) 112 // data[m0] <= data[m2] && data[m1] < data[m2] 113 if data.Less(m1, m0) { 114 data.Swap(m1, m0) 115 } 116 } 117 // now data[m0] <= data[m1] <= data[m2] 118 } 119 120 func doPivot(data Sorter, lo, hi int) (midlo, midhi int) { 121 m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow. 122 if hi-lo > 40 { 123 // Tukey's ``Ninther,'' median of three medians of three. 124 s := (hi - lo) / 8 125 medianOfThree(data, lo, lo+s, lo+2*s) 126 medianOfThree(data, m, m-s, m+s) 127 medianOfThree(data, hi-1, hi-1-s, hi-1-2*s) 128 } 129 medianOfThree(data, lo, m, hi-1) 130 131 // Invariants are: 132 // data[lo] = pivot (set up by ChoosePivot) 133 // data[lo < i < a] < pivot 134 // data[a <= i < b] <= pivot 135 // data[b <= i < c] unexamined 136 // data[c <= i < hi-1] > pivot 137 // data[hi-1] >= pivot 138 pivot := lo 139 a, c := lo+1, hi-1 140 141 for ; a < c && data.Less(a, pivot); a++ { 142 } 143 b := a 144 for { 145 for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot 146 } 147 for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot 148 } 149 if b >= c { 150 break 151 } 152 // data[b] > pivot; data[c-1] <= pivot 153 data.Swap(b, c-1) 154 b++ 155 c-- 156 } 157 // If hi-c<3 then there are duplicates (by property of median of nine). 158 // Let be a bit more conservative, and set border to 5. 159 protect := hi-c < 5 160 if !protect && hi-c < (hi-lo)/4 { 161 // Lets test some points for equality to pivot 162 dups := 0 163 if !data.Less(pivot, hi-1) { // data[hi-1] = pivot 164 data.Swap(c, hi-1) 165 c++ 166 dups++ 167 } 168 if !data.Less(b-1, pivot) { // data[b-1] = pivot 169 b-- 170 dups++ 171 } 172 // m-lo = (hi-lo)/2 > 6 173 // b-lo > (hi-lo)*3/4-1 > 8 174 // ==> m < b ==> data[m] <= pivot 175 if !data.Less(m, pivot) { // data[m] = pivot 176 data.Swap(m, b-1) 177 b-- 178 dups++ 179 } 180 // if at least 2 points are equal to pivot, assume skewed distribution 181 protect = dups > 1 182 } 183 if protect { 184 // Protect against a lot of duplicates 185 // Add invariant: 186 // data[a <= i < b] unexamined 187 // data[b <= i < c] = pivot 188 for { 189 for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot 190 } 191 for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot 192 } 193 if a >= b { 194 break 195 } 196 // data[a] == pivot; data[b-1] < pivot 197 data.Swap(a, b-1) 198 a++ 199 b-- 200 } 201 } 202 // Swap pivot into middle 203 data.Swap(pivot, b-1) 204 return b - 1, c 205 } 206 207 func quickSort(data Sorter, a, b, maxDepth int) { 208 for b-a > 12 { // Use ShellSort for slices <= 12 elements 209 if maxDepth == 0 { 210 heapSort(data, a, b) 211 return 212 } 213 maxDepth-- 214 mlo, mhi := doPivot(data, a, b) 215 // Avoiding recursion on the larger subproblem guarantees 216 // a stack depth of at most lg(b-a). 217 if mlo-a < b-mhi { 218 quickSort(data, a, mlo, maxDepth) 219 a = mhi // i.e., quickSort(data, mhi, b) 220 } else { 221 quickSort(data, mhi, b, maxDepth) 222 b = mlo // i.e., quickSort(data, a, mlo) 223 } 224 } 225 if b-a > 1 { 226 // Do ShellSort pass with gap 6 227 // It could be written in this simplified form cause b-a <= 12 228 for i := a + 6; i < b; i++ { 229 if data.Less(i, i-6) { 230 data.Swap(i, i-6) 231 } 232 } 233 insertionSort(data, a, b) 234 } 235 } 236 237 // maxDepth returns a threshold at which quicksort should switch 238 // to heapsort. It returns 2*ceil(lg(n+1)). 239 func maxDepth(n int) int { 240 var depth int 241 for i := n; i > 0; i >>= 1 { 242 depth++ 243 } 244 return depth * 2 245 }