authorgravatar for Validark@pm.meNiles Salter <Validark@pm.me> 2023-06-13 14:55:58-06:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-06-13 16:55:58-04:00
log700ea694b293565ececb7571e4d9613d2c143ca6
tree309bcb58d4892e8b38fdee6a972ffc1788a2e4ac
parent129afba460bd654230c8d678a53d7a1b5fec9132
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Fix pdqSort+heapSort for ranges besides 0..len (#15982)


2 files changed, 71 insertions(+), 23 deletions(-)

lib/std/sort.zig+63-13
...@@ -74,29 +74,29 @@ pub fn heap(...@@ -74,29 +74,29 @@ pub fn heap(
74/// Sorts in ascending order with respect to the given `lessThan` function.74/// Sorts in ascending order with respect to the given `lessThan` function.
75pub fn heapContext(a: usize, b: usize, context: anytype) void {75pub fn heapContext(a: usize, b: usize, context: anytype) void {
76 // build the heap in linear time.76 // build the heap in linear time.
77 var i = b / 2;77 var i = a + (b - a) / 2;
78 while (i > a) : (i -= 1) {78 while (i > a) {
79 siftDown(i - 1, b, context);79 i -= 1;
80 siftDown(a, i, b, context);
80 }81 }
8182
82 // pop maximal elements from the heap.83 // pop maximal elements from the heap.
83 i = b;84 i = b;
84 while (i > a) : (i -= 1) {85 while (i > a) {
85 context.swap(a, i - 1);86 i -= 1;
86 siftDown(a, i - 1, context);87 context.swap(a, i);
88 siftDown(a, a, i, context);
87 }89 }
88}90}
8991
90fn siftDown(root: usize, n: usize, context: anytype) void {92fn siftDown(a: usize, root: usize, n: usize, context: anytype) void {
91 var node = root;93 var node = root;
92 while (true) {94 while (true) {
93 var child = 2 * node + 1;95 var child = a + 2 * (node - a) + 1;
94 if (child >= n) break;96 if (child >= n) break;
9597
96 // choose the greater child.98 // choose the greater child.
97 if (child + 1 < n and context.lessThan(child, child + 1)) {99 child += @boolToInt(child + 1 < n and context.lessThan(child, child + 1));
98 child += 1;
99 }
100100
101 // stop if the invariant holds at `node`.101 // stop if the invariant holds at `node`.
102 if (!context.lessThan(node, child)) break;102 if (!context.lessThan(node, child)) break;
...@@ -138,6 +138,13 @@ const sort_funcs = &[_]fn (comptime type, anytype, anytype, comptime anytype) vo...@@ -138,6 +138,13 @@ const sort_funcs = &[_]fn (comptime type, anytype, anytype, comptime anytype) vo
138 heap,138 heap,
139};139};
140140
141const context_sort_funcs = &[_]fn (usize, usize, anytype) void{
142 // blockContext,
143 pdqContext,
144 insertionContext,
145 heapContext,
146};
147
141const IdAndValue = struct {148const IdAndValue = struct {
142 id: usize,149 id: usize,
143 value: i32,150 value: i32,
...@@ -248,11 +255,15 @@ test "sort" {...@@ -248,11 +255,15 @@ test "sort" {
248 &[_]i32{ 2, 1, 3 },255 &[_]i32{ 2, 1, 3 },
249 &[_]i32{ 1, 2, 3 },256 &[_]i32{ 1, 2, 3 },
250 },257 },
258 &[_][]const i32{
259 &[_]i32{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 55, 32, 39, 58, 21, 88, 43, 22, 59 },
260 &[_]i32{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 21, 22, 32, 39, 43, 55, 58, 59, 88 },
261 },
251 };262 };
252263
253 inline for (sort_funcs) |sortFn| {264 inline for (sort_funcs) |sortFn| {
254 for (u8cases) |case| {265 for (u8cases) |case| {
255 var buf: [8]u8 = undefined;266 var buf: [20]u8 = undefined;
256 const slice = buf[0..case[0].len];267 const slice = buf[0..case[0].len];
257 @memcpy(slice, case[0]);268 @memcpy(slice, case[0]);
258 sortFn(u8, slice, {}, asc_u8);269 sortFn(u8, slice, {}, asc_u8);
...@@ -260,7 +271,7 @@ test "sort" {...@@ -260,7 +271,7 @@ test "sort" {
260 }271 }
261272
262 for (i32cases) |case| {273 for (i32cases) |case| {
263 var buf: [8]i32 = undefined;274 var buf: [20]i32 = undefined;
264 const slice = buf[0..case[0].len];275 const slice = buf[0..case[0].len];
265 @memcpy(slice, case[0]);276 @memcpy(slice, case[0]);
266 sortFn(i32, slice, {}, asc_i32);277 sortFn(i32, slice, {}, asc_i32);
...@@ -308,6 +319,45 @@ test "sort descending" {...@@ -308,6 +319,45 @@ test "sort descending" {
308 }319 }
309}320}
310321
322test "sort with context in the middle of a slice" {
323 const Context = struct {
324 items: []i32,
325
326 pub fn lessThan(ctx: @This(), a: usize, b: usize) bool {
327 return ctx.items[a] < ctx.items[b];
328 }
329
330 pub fn swap(ctx: @This(), a: usize, b: usize) void {
331 return mem.swap(i32, &ctx.items[a], &ctx.items[b]);
332 }
333 };
334
335 const i32cases = [_][]const []const i32{
336 &[_][]const i32{
337 &[_]i32{ 0, 1, 8, 3, 6, 5, 4, 2, 9, 7, 10, 55, 32, 39, 58, 21, 88, 43, 22, 59 },
338 &[_]i32{ 50, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21, 22, 32, 39, 43, 55, 58, 59, 88 },
339 },
340 };
341
342 const ranges = [_]struct { start: usize, end: usize }{
343 .{ .start = 10, .end = 20 },
344 .{ .start = 1, .end = 11 },
345 .{ .start = 3, .end = 7 },
346 };
347
348 inline for (context_sort_funcs) |sortFn| {
349 for (i32cases) |case| {
350 for (ranges) |range| {
351 var buf: [20]i32 = undefined;
352 const slice = buf[0..case[0].len];
353 @memcpy(slice, case[0]);
354 sortFn(range.start, range.end, Context{ .items = slice });
355 try testing.expectEqualSlices(i32, slice[range.start..range.end], case[1][range.start..range.end]);
356 }
357 }
358 }
359}
360
311test "sort fuzz testing" {361test "sort fuzz testing" {
312 var prng = std.rand.DefaultPrng.init(0x12345678);362 var prng = std.rand.DefaultPrng.init(0x12345678);
313 const random = prng.random();363 const random = prng.random();
lib/std/sort/pdq.zig+8-10
...@@ -43,7 +43,7 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {...@@ -43,7 +43,7 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {
43 // slices of up to this length get sorted using insertion sort.43 // slices of up to this length get sorted using insertion sort.
44 const max_insertion = 24;44 const max_insertion = 24;
45 // number of allowed imbalanced partitions before switching to heap sort.45 // number of allowed imbalanced partitions before switching to heap sort.
46 const max_limit = std.math.floorPowerOfTwo(usize, b) + 1;46 const max_limit = std.math.floorPowerOfTwo(usize, b - a) + 1;
4747
48 // set upper bound on stack memory usage.48 // set upper bound on stack memory usage.
49 const Range = struct { a: usize, b: usize, limit: usize };49 const Range = struct { a: usize, b: usize, limit: usize };
...@@ -100,7 +100,7 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {...@@ -100,7 +100,7 @@ pub fn pdqContext(a: usize, b: usize, context: anytype) void {
100 // if the chosen pivot is equal to the predecessor, then it's the smallest element in the100 // if the chosen pivot is equal to the predecessor, then it's the smallest element in the
101 // slice. Partition the slice into elements equal to and elements greater than the pivot.101 // slice. Partition the slice into elements equal to and elements greater than the pivot.
102 // This case is usually hit when the slice contains many duplicate elements.102 // This case is usually hit when the slice contains many duplicate elements.
103 if (range.a > 0 and !context.lessThan(range.a - 1, pivot)) {103 if (range.a > a and !context.lessThan(range.a - 1, pivot)) {
104 range.a = partitionEqual(range.a, range.b, pivot, context);104 range.a = partitionEqual(range.a, range.b, pivot, context);
105 continue;105 continue;
106 }106 }
...@@ -284,13 +284,13 @@ fn chosePivot(a: usize, b: usize, pivot: *usize, context: anytype) Hint {...@@ -284,13 +284,13 @@ fn chosePivot(a: usize, b: usize, pivot: *usize, context: anytype) Hint {
284 if (len >= 8) {284 if (len >= 8) {
285 if (len >= shortest_ninther) {285 if (len >= shortest_ninther) {
286 // find medians in the neighborhoods of `i`, `j` and `k`286 // find medians in the neighborhoods of `i`, `j` and `k`
287 i = sort3(i - 1, i, i + 1, &swaps, context);287 sort3(i - 1, i, i + 1, &swaps, context);
288 j = sort3(j - 1, j, j + 1, &swaps, context);288 sort3(j - 1, j, j + 1, &swaps, context);
289 k = sort3(k - 1, k, k + 1, &swaps, context);289 sort3(k - 1, k, k + 1, &swaps, context);
290 }290 }
291291
292 // find the median among `i`, `j` and `k`292 // find the median among `i`, `j` and `k` and stores it in `j`
293 j = sort3(i, j, k, &swaps, context);293 sort3(i, j, k, &swaps, context);
294 }294 }
295295
296 pivot.* = j;296 pivot.* = j;
...@@ -301,7 +301,7 @@ fn chosePivot(a: usize, b: usize, pivot: *usize, context: anytype) Hint {...@@ -301,7 +301,7 @@ fn chosePivot(a: usize, b: usize, pivot: *usize, context: anytype) Hint {
301 };301 };
302}302}
303303
304fn sort3(a: usize, b: usize, c: usize, swaps: *usize, context: anytype) usize {304fn sort3(a: usize, b: usize, c: usize, swaps: *usize, context: anytype) void {
305 if (context.lessThan(b, a)) {305 if (context.lessThan(b, a)) {
306 swaps.* += 1;306 swaps.* += 1;
307 context.swap(b, a);307 context.swap(b, a);
...@@ -316,8 +316,6 @@ fn sort3(a: usize, b: usize, c: usize, swaps: *usize, context: anytype) usize {...@@ -316,8 +316,6 @@ fn sort3(a: usize, b: usize, c: usize, swaps: *usize, context: anytype) usize {
316 swaps.* += 1;316 swaps.* += 1;
317 context.swap(b, a);317 context.swap(b, a);
318 }318 }
319
320 return b;
321}319}
322320
323fn reverseRange(a: usize, b: usize, context: anytype) void {321fn reverseRange(a: usize, b: usize, context: anytype) void {