| ... | ... | @@ -47,10 +47,10 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void { |
| 47 | 47 | const max_limit = std.math.floorPowerOfTwo(usize, b - a) + 1; |
| 48 | 48 | |
| 49 | 49 | // set upper bound on stack memory usage. |
| 50 | | const Range = struct { a: usize, b: usize, limit: usize }; |
| 50 | const Range = struct { a: usize, b: usize, limit: usize, leftmost: bool }; |
| 51 | 51 | const stack_size = math.log2(math.maxInt(usize) + 1); |
| 52 | 52 | var stack: [stack_size]Range = undefined; |
| 53 | | var range = Range{ .a = a, .b = b, .limit = max_limit }; |
| 53 | var range = Range{ .a = a, .b = b, .limit = max_limit, .leftmost = true }; |
| 54 | 54 | var top: usize = 0; |
| 55 | 55 | |
| 56 | 56 | while (true) { |
| ... | ... | @@ -62,7 +62,11 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void { |
| 62 | 62 | |
| 63 | 63 | // very short slices get sorted using insertion sort. |
| 64 | 64 | if (len <= max_insertion) { |
| 65 | | break sort.insertionContext(range.a, range.b, context); |
| 65 | if (range.leftmost) { |
| 66 | break sort.insertionContext(range.a, range.b, context); |
| 67 | } else { |
| 68 | break unguardedInsertionContext(range.a, range.b, context); |
| 69 | } |
| 66 | 70 | } |
| 67 | 71 | |
| 68 | 72 | // if too many bad pivot choices were made, simply fall back to heapsort in order to |
| ... | ... | @@ -115,12 +119,13 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void { |
| 115 | 119 | const balanced_threshold = len / 8; |
| 116 | 120 | if (left_len < right_len) { |
| 117 | 121 | was_balanced = left_len >= balanced_threshold; |
| 118 | | stack[top] = .{ .a = range.a, .b = mid, .limit = range.limit }; |
| 122 | stack[top] = .{ .a = range.a, .b = mid, .limit = range.limit, .leftmost = range.leftmost }; |
| 119 | 123 | top += 1; |
| 120 | 124 | range.a = mid + 1; |
| 125 | range.leftmost = false; |
| 121 | 126 | } else { |
| 122 | 127 | was_balanced = right_len >= balanced_threshold; |
| 123 | | stack[top] = .{ .a = mid + 1, .b = range.b, .limit = range.limit }; |
| 128 | stack[top] = .{ .a = mid + 1, .b = range.b, .limit = range.limit, .leftmost = false }; |
| 124 | 129 | top += 1; |
| 125 | 130 | range.b = mid; |
| 126 | 131 | } |
| ... | ... | @@ -131,6 +136,18 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void { |
| 131 | 136 | } |
| 132 | 137 | } |
| 133 | 138 | |
| 139 | /// Insertion sort that assumes `items[a-1]` exists and is <= all elements in `[a, b)`, |
| 140 | /// allowing the inner loop to skip the bounds check. |
| 141 | fn unguardedInsertionContext(a: usize, b: usize, context: anytype) void { |
| 142 | var i = a + 1; |
| 143 | while (i < b) : (i += 1) { |
| 144 | var j = i; |
| 145 | while (context.lessThan(j, j - 1)) : (j -= 1) { |
| 146 | context.swap(j, j - 1); |
| 147 | } |
| 148 | } |
| 149 | } |
| 150 | |
| 134 | 151 | /// partitions `items[a..b]` into elements smaller than `items[pivot]`, |
| 135 | 152 | /// followed by elements greater than or equal to `items[pivot]`. |
| 136 | 153 | /// |