authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-03 18:41:56-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-08 15:16:40-04:00
logd4d954abd20d57ff62940993f5b95700ebfbbda7
treef9ddfc68a65ba322b221e560ea66c7e2ab603b1c
parentc405844b0a039e6f2ba766d80644e06c1438654c

std.sort: give comparator functions a context parameter


4 files changed, 379 insertions(+), 247 deletions(-)

lib/std/comptime_string_map.zig+24-24
......@@ -17,18 +17,18 @@ pub fn ComptimeStringMap(comptime V: type, comptime kvs: var) type {
1717 };
1818 var sorted_kvs: [kvs.len]KV = undefined;
1919 const lenAsc = (struct {
20 fn lenAsc(a: KV, b: KV) bool {
20 fn lenAsc(context: void, a: KV, b: KV) bool {
2121 return a.key.len < b.key.len;
2222 }
2323 }).lenAsc;
2424 for (kvs) |kv, i| {
2525 if (V != void) {
26 sorted_kvs[i] = .{.key = kv.@"0", .value = kv.@"1"};
26 sorted_kvs[i] = .{ .key = kv.@"0", .value = kv.@"1" };
2727 } else {
28 sorted_kvs[i] = .{.key = kv.@"0", .value = {}};
28 sorted_kvs[i] = .{ .key = kv.@"0", .value = {} };
2929 }
3030 }
31 std.sort.sort(KV, &sorted_kvs, lenAsc);
31 std.sort.sort(KV, &sorted_kvs, {}, lenAsc);
3232 const min_len = sorted_kvs[0].key.len;
3333 const max_len = sorted_kvs[sorted_kvs.len - 1].key.len;
3434 var len_indexes: [max_len + 1]usize = undefined;
......@@ -83,11 +83,11 @@ const TestEnum = enum {
8383
8484test "ComptimeStringMap list literal of list literals" {
8585 const map = ComptimeStringMap(TestEnum, .{
86 .{"these", .D},
87 .{"have", .A},
88 .{"nothing", .B},
89 .{"incommon", .C},
90 .{"samelen", .E},
86 .{ "these", .D },
87 .{ "have", .A },
88 .{ "nothing", .B },
89 .{ "incommon", .C },
90 .{ "samelen", .E },
9191 });
9292
9393 testMap(map);
......@@ -99,11 +99,11 @@ test "ComptimeStringMap array of structs" {
9999 @"1": TestEnum,
100100 };
101101 const map = ComptimeStringMap(TestEnum, [_]KV{
102 .{.@"0" = "these", .@"1" = .D},
103 .{.@"0" = "have", .@"1" = .A},
104 .{.@"0" = "nothing", .@"1" = .B},
105 .{.@"0" = "incommon", .@"1" = .C},
106 .{.@"0" = "samelen", .@"1" = .E},
102 .{ .@"0" = "these", .@"1" = .D },
103 .{ .@"0" = "have", .@"1" = .A },
104 .{ .@"0" = "nothing", .@"1" = .B },
105 .{ .@"0" = "incommon", .@"1" = .C },
106 .{ .@"0" = "samelen", .@"1" = .E },
107107 });
108108
109109 testMap(map);
......@@ -115,11 +115,11 @@ test "ComptimeStringMap slice of structs" {
115115 @"1": TestEnum,
116116 };
117117 const slice: []const KV = &[_]KV{
118 .{.@"0" = "these", .@"1" = .D},
119 .{.@"0" = "have", .@"1" = .A},
120 .{.@"0" = "nothing", .@"1" = .B},
121 .{.@"0" = "incommon", .@"1" = .C},
122 .{.@"0" = "samelen", .@"1" = .E},
118 .{ .@"0" = "these", .@"1" = .D },
119 .{ .@"0" = "have", .@"1" = .A },
120 .{ .@"0" = "nothing", .@"1" = .B },
121 .{ .@"0" = "incommon", .@"1" = .C },
122 .{ .@"0" = "samelen", .@"1" = .E },
123123 };
124124 const map = ComptimeStringMap(TestEnum, slice);
125125
......@@ -142,11 +142,11 @@ test "ComptimeStringMap void value type, slice of structs" {
142142 @"0": []const u8,
143143 };
144144 const slice: []const KV = &[_]KV{
145 .{.@"0" = "these"},
146 .{.@"0" = "have"},
147 .{.@"0" = "nothing"},
148 .{.@"0" = "incommon"},
149 .{.@"0" = "samelen"},
145 .{ .@"0" = "these" },
146 .{ .@"0" = "have" },
147 .{ .@"0" = "nothing" },
148 .{ .@"0" = "incommon" },
149 .{ .@"0" = "samelen" },
150150 };
151151 const map = ComptimeStringMap(void, slice);
152152
lib/std/http/headers.zig+2-2
......@@ -58,7 +58,7 @@ const HeaderEntry = struct {
5858 self.never_index = never_index orelse never_index_default(self.name);
5959 }
6060
61 fn compare(a: HeaderEntry, b: HeaderEntry) bool {
61 fn compare(context: void, a: HeaderEntry, b: HeaderEntry) bool {
6262 if (a.name.ptr != b.name.ptr and a.name.len != b.name.len) {
6363 // Things beginning with a colon *must* be before others
6464 const a_is_colon = a.name[0] == ':';
......@@ -342,7 +342,7 @@ pub const Headers = struct {
342342 }
343343
344344 pub fn sort(self: *Self) void {
345 std.sort.sort(HeaderEntry, self.data.items, HeaderEntry.compare);
345 std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare);
346346 self.rebuild_index();
347347 }
348348
lib/std/net.zig+2-2
......@@ -836,7 +836,7 @@ fn linuxLookupName(
836836 key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT;
837837 addr.sortkey = key;
838838 }
839 std.sort.sort(LookupAddr, addrs.span(), addrCmpLessThan);
839 std.sort.sort(LookupAddr, addrs.span(), {}, addrCmpLessThan);
840840}
841841
842842const Policy = struct {
......@@ -953,7 +953,7 @@ fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool {
953953}
954954
955955// Parameters `b` and `a` swapped to make this descending.
956fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool {
956fn addrCmpLessThan(context: void, b: LookupAddr, a: LookupAddr) bool {
957957 return a.sortkey < b.sortkey;
958958}
959959
lib/std/sort.zig+351-219
......@@ -5,7 +5,13 @@ const mem = std.mem;
55const math = std.math;
66const builtin = @import("builtin");
77
8pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compareFn: fn (lhs: T, rhs: T) math.Order) ?usize {
8pub fn binarySearch(
9 comptime T: type,
10 key: T,
11 items: []const T,
12 context: var,
13 comptime compareFn: fn (context: @TypeOf(context), lhs: T, rhs: T) math.Order,
14) ?usize {
915 var left: usize = 0;
1016 var right: usize = items.len;
1117
......@@ -13,7 +19,7 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare
1319 // Avoid overflowing in the midpoint calculation
1420 const mid = left + (right - left) / 2;
1521 // Compare the key with the midpoint element
16 switch (compareFn(key, items[mid])) {
22 switch (compareFn(context, key, items[mid])) {
1723 .eq => return mid,
1824 .gt => left = mid + 1,
1925 .lt => right = mid,
......@@ -23,56 +29,61 @@ pub fn binarySearch(comptime T: type, key: T, items: []const T, comptime compare
2329 return null;
2430}
2531
26test "std.sort.binarySearch" {
32test "binarySearch" {
2733 const S = struct {
28 fn order_u32(lhs: u32, rhs: u32) math.Order {
34 fn order_u32(context: void, lhs: u32, rhs: u32) math.Order {
2935 return math.order(lhs, rhs);
3036 }
31 fn order_i32(lhs: i32, rhs: i32) math.Order {
37 fn order_i32(context: void, lhs: i32, rhs: i32) math.Order {
3238 return math.order(lhs, rhs);
3339 }
3440 };
3541 testing.expectEqual(
3642 @as(?usize, null),
37 binarySearch(u32, 1, &[_]u32{}, S.order_u32),
43 binarySearch(u32, 1, &[_]u32{}, {}, S.order_u32),
3844 );
3945 testing.expectEqual(
4046 @as(?usize, 0),
41 binarySearch(u32, 1, &[_]u32{1}, S.order_u32),
47 binarySearch(u32, 1, &[_]u32{1}, {}, S.order_u32),
4248 );
4349 testing.expectEqual(
4450 @as(?usize, null),
45 binarySearch(u32, 1, &[_]u32{0}, S.order_u32),
51 binarySearch(u32, 1, &[_]u32{0}, {}, S.order_u32),
4652 );
4753 testing.expectEqual(
4854 @as(?usize, null),
49 binarySearch(u32, 0, &[_]u32{1}, S.order_u32),
55 binarySearch(u32, 0, &[_]u32{1}, {}, S.order_u32),
5056 );
5157 testing.expectEqual(
5258 @as(?usize, 4),
53 binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, S.order_u32),
59 binarySearch(u32, 5, &[_]u32{ 1, 2, 3, 4, 5 }, {}, S.order_u32),
5460 );
5561 testing.expectEqual(
5662 @as(?usize, 0),
57 binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, S.order_u32),
63 binarySearch(u32, 2, &[_]u32{ 2, 4, 8, 16, 32, 64 }, {}, S.order_u32),
5864 );
5965 testing.expectEqual(
6066 @as(?usize, 1),
61 binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, S.order_i32),
67 binarySearch(i32, -4, &[_]i32{ -7, -4, 0, 9, 10 }, {}, S.order_i32),
6268 );
6369 testing.expectEqual(
6470 @as(?usize, 3),
65 binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, S.order_i32),
71 binarySearch(i32, 98, &[_]i32{ -100, -25, 2, 98, 99, 100 }, {}, S.order_i32),
6672 );
6773}
6874
6975/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).
70pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void {
76pub fn insertionSort(
77 comptime T: type,
78 items: []T,
79 context: var,
80 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
81) void {
7182 var i: usize = 1;
7283 while (i < items.len) : (i += 1) {
7384 const x = items[i];
7485 var j: usize = i;
75 while (j > 0 and lessThan(x, items[j - 1])) : (j -= 1) {
86 while (j > 0 and lessThan(context, x, items[j - 1])) : (j -= 1) {
7687 items[j] = items[j - 1];
7788 }
7889 items[j] = x;
......@@ -168,20 +179,25 @@ const Pull = struct {
168179
169180/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).
170181/// Currently implemented as block sort.
171pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void {
182pub fn sort(
183 comptime T: type,
184 items: []T,
185 context: var,
186 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
187) void {
172188 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
173189 var cache: [512]T = undefined;
174190
175191 if (items.len < 4) {
176192 if (items.len == 3) {
177193 // hard coded insertion sort
178 if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);
179 if (lessThan(items[2], items[1])) {
194 if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]);
195 if (lessThan(context, items[2], items[1])) {
180196 mem.swap(T, &items[1], &items[2]);
181 if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);
197 if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]);
182198 }
183199 } else if (items.len == 2) {
184 if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);
200 if (lessThan(context, items[1], items[0])) mem.swap(T, &items[0], &items[1]);
185201 }
186202 return;
187203 }
......@@ -197,75 +213,75 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
197213 const sliced_items = items[range.start..];
198214 switch (range.length()) {
199215 8 => {
200 swap(T, sliced_items, lessThan, &order, 0, 1);
201 swap(T, sliced_items, lessThan, &order, 2, 3);
202 swap(T, sliced_items, lessThan, &order, 4, 5);
203 swap(T, sliced_items, lessThan, &order, 6, 7);
204 swap(T, sliced_items, lessThan, &order, 0, 2);
205 swap(T, sliced_items, lessThan, &order, 1, 3);
206 swap(T, sliced_items, lessThan, &order, 4, 6);
207 swap(T, sliced_items, lessThan, &order, 5, 7);
208 swap(T, sliced_items, lessThan, &order, 1, 2);
209 swap(T, sliced_items, lessThan, &order, 5, 6);
210 swap(T, sliced_items, lessThan, &order, 0, 4);
211 swap(T, sliced_items, lessThan, &order, 3, 7);
212 swap(T, sliced_items, lessThan, &order, 1, 5);
213 swap(T, sliced_items, lessThan, &order, 2, 6);
214 swap(T, sliced_items, lessThan, &order, 1, 4);
215 swap(T, sliced_items, lessThan, &order, 3, 6);
216 swap(T, sliced_items, lessThan, &order, 2, 4);
217 swap(T, sliced_items, lessThan, &order, 3, 5);
218 swap(T, sliced_items, lessThan, &order, 3, 4);
216 swap(T, sliced_items, context, lessThan, &order, 0, 1);
217 swap(T, sliced_items, context, lessThan, &order, 2, 3);
218 swap(T, sliced_items, context, lessThan, &order, 4, 5);
219 swap(T, sliced_items, context, lessThan, &order, 6, 7);
220 swap(T, sliced_items, context, lessThan, &order, 0, 2);
221 swap(T, sliced_items, context, lessThan, &order, 1, 3);
222 swap(T, sliced_items, context, lessThan, &order, 4, 6);
223 swap(T, sliced_items, context, lessThan, &order, 5, 7);
224 swap(T, sliced_items, context, lessThan, &order, 1, 2);
225 swap(T, sliced_items, context, lessThan, &order, 5, 6);
226 swap(T, sliced_items, context, lessThan, &order, 0, 4);
227 swap(T, sliced_items, context, lessThan, &order, 3, 7);
228 swap(T, sliced_items, context, lessThan, &order, 1, 5);
229 swap(T, sliced_items, context, lessThan, &order, 2, 6);
230 swap(T, sliced_items, context, lessThan, &order, 1, 4);
231 swap(T, sliced_items, context, lessThan, &order, 3, 6);
232 swap(T, sliced_items, context, lessThan, &order, 2, 4);
233 swap(T, sliced_items, context, lessThan, &order, 3, 5);
234 swap(T, sliced_items, context, lessThan, &order, 3, 4);
219235 },
220236 7 => {
221 swap(T, sliced_items, lessThan, &order, 1, 2);
222 swap(T, sliced_items, lessThan, &order, 3, 4);
223 swap(T, sliced_items, lessThan, &order, 5, 6);
224 swap(T, sliced_items, lessThan, &order, 0, 2);
225 swap(T, sliced_items, lessThan, &order, 3, 5);
226 swap(T, sliced_items, lessThan, &order, 4, 6);
227 swap(T, sliced_items, lessThan, &order, 0, 1);
228 swap(T, sliced_items, lessThan, &order, 4, 5);
229 swap(T, sliced_items, lessThan, &order, 2, 6);
230 swap(T, sliced_items, lessThan, &order, 0, 4);
231 swap(T, sliced_items, lessThan, &order, 1, 5);
232 swap(T, sliced_items, lessThan, &order, 0, 3);
233 swap(T, sliced_items, lessThan, &order, 2, 5);
234 swap(T, sliced_items, lessThan, &order, 1, 3);
235 swap(T, sliced_items, lessThan, &order, 2, 4);
236 swap(T, sliced_items, lessThan, &order, 2, 3);
237 swap(T, sliced_items, context, lessThan, &order, 1, 2);
238 swap(T, sliced_items, context, lessThan, &order, 3, 4);
239 swap(T, sliced_items, context, lessThan, &order, 5, 6);
240 swap(T, sliced_items, context, lessThan, &order, 0, 2);
241 swap(T, sliced_items, context, lessThan, &order, 3, 5);
242 swap(T, sliced_items, context, lessThan, &order, 4, 6);
243 swap(T, sliced_items, context, lessThan, &order, 0, 1);
244 swap(T, sliced_items, context, lessThan, &order, 4, 5);
245 swap(T, sliced_items, context, lessThan, &order, 2, 6);
246 swap(T, sliced_items, context, lessThan, &order, 0, 4);
247 swap(T, sliced_items, context, lessThan, &order, 1, 5);
248 swap(T, sliced_items, context, lessThan, &order, 0, 3);
249 swap(T, sliced_items, context, lessThan, &order, 2, 5);
250 swap(T, sliced_items, context, lessThan, &order, 1, 3);
251 swap(T, sliced_items, context, lessThan, &order, 2, 4);
252 swap(T, sliced_items, context, lessThan, &order, 2, 3);
237253 },
238254 6 => {
239 swap(T, sliced_items, lessThan, &order, 1, 2);
240 swap(T, sliced_items, lessThan, &order, 4, 5);
241 swap(T, sliced_items, lessThan, &order, 0, 2);
242 swap(T, sliced_items, lessThan, &order, 3, 5);
243 swap(T, sliced_items, lessThan, &order, 0, 1);
244 swap(T, sliced_items, lessThan, &order, 3, 4);
245 swap(T, sliced_items, lessThan, &order, 2, 5);
246 swap(T, sliced_items, lessThan, &order, 0, 3);
247 swap(T, sliced_items, lessThan, &order, 1, 4);
248 swap(T, sliced_items, lessThan, &order, 2, 4);
249 swap(T, sliced_items, lessThan, &order, 1, 3);
250 swap(T, sliced_items, lessThan, &order, 2, 3);
255 swap(T, sliced_items, context, lessThan, &order, 1, 2);
256 swap(T, sliced_items, context, lessThan, &order, 4, 5);
257 swap(T, sliced_items, context, lessThan, &order, 0, 2);
258 swap(T, sliced_items, context, lessThan, &order, 3, 5);
259 swap(T, sliced_items, context, lessThan, &order, 0, 1);
260 swap(T, sliced_items, context, lessThan, &order, 3, 4);
261 swap(T, sliced_items, context, lessThan, &order, 2, 5);
262 swap(T, sliced_items, context, lessThan, &order, 0, 3);
263 swap(T, sliced_items, context, lessThan, &order, 1, 4);
264 swap(T, sliced_items, context, lessThan, &order, 2, 4);
265 swap(T, sliced_items, context, lessThan, &order, 1, 3);
266 swap(T, sliced_items, context, lessThan, &order, 2, 3);
251267 },
252268 5 => {
253 swap(T, sliced_items, lessThan, &order, 0, 1);
254 swap(T, sliced_items, lessThan, &order, 3, 4);
255 swap(T, sliced_items, lessThan, &order, 2, 4);
256 swap(T, sliced_items, lessThan, &order, 2, 3);
257 swap(T, sliced_items, lessThan, &order, 1, 4);
258 swap(T, sliced_items, lessThan, &order, 0, 3);
259 swap(T, sliced_items, lessThan, &order, 0, 2);
260 swap(T, sliced_items, lessThan, &order, 1, 3);
261 swap(T, sliced_items, lessThan, &order, 1, 2);
269 swap(T, sliced_items, context, lessThan, &order, 0, 1);
270 swap(T, sliced_items, context, lessThan, &order, 3, 4);
271 swap(T, sliced_items, context, lessThan, &order, 2, 4);
272 swap(T, sliced_items, context, lessThan, &order, 2, 3);
273 swap(T, sliced_items, context, lessThan, &order, 1, 4);
274 swap(T, sliced_items, context, lessThan, &order, 0, 3);
275 swap(T, sliced_items, context, lessThan, &order, 0, 2);
276 swap(T, sliced_items, context, lessThan, &order, 1, 3);
277 swap(T, sliced_items, context, lessThan, &order, 1, 2);
262278 },
263279 4 => {
264 swap(T, sliced_items, lessThan, &order, 0, 1);
265 swap(T, sliced_items, lessThan, &order, 2, 3);
266 swap(T, sliced_items, lessThan, &order, 0, 2);
267 swap(T, sliced_items, lessThan, &order, 1, 3);
268 swap(T, sliced_items, lessThan, &order, 1, 2);
280 swap(T, sliced_items, context, lessThan, &order, 0, 1);
281 swap(T, sliced_items, context, lessThan, &order, 2, 3);
282 swap(T, sliced_items, context, lessThan, &order, 0, 2);
283 swap(T, sliced_items, context, lessThan, &order, 1, 3);
284 swap(T, sliced_items, context, lessThan, &order, 1, 2);
269285 },
270286 else => {},
271287 }
......@@ -288,16 +304,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
288304 var A2 = iterator.nextRange();
289305 var B2 = iterator.nextRange();
290306
291 if (lessThan(items[B1.end - 1], items[A1.start])) {
307 if (lessThan(context, items[B1.end - 1], items[A1.start])) {
292308 // the two ranges are in reverse order, so copy them in reverse order into the cache
293309 mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]);
294310 mem.copy(T, cache[0..], items[B1.start..B1.end]);
295 } else if (lessThan(items[B1.start], items[A1.end - 1])) {
311 } else if (lessThan(context, items[B1.start], items[A1.end - 1])) {
296312 // these two ranges weren't already in order, so merge them into the cache
297 mergeInto(T, items, A1, B1, lessThan, cache[0..]);
313 mergeInto(T, items, A1, B1, context, lessThan, cache[0..]);
298314 } else {
299315 // if A1, B1, A2, and B2 are all in order, skip doing anything else
300 if (!lessThan(items[B2.start], items[A2.end - 1]) and !lessThan(items[A2.start], items[B1.end - 1])) continue;
316 if (!lessThan(context, items[B2.start], items[A2.end - 1]) and !lessThan(context, items[A2.start], items[B1.end - 1])) continue;
301317
302318 // copy A1 and B1 into the cache in the same order
303319 mem.copy(T, cache[0..], items[A1.start..A1.end]);
......@@ -306,13 +322,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
306322 A1 = Range.init(A1.start, B1.end);
307323
308324 // merge A2 and B2 into the cache
309 if (lessThan(items[B2.end - 1], items[A2.start])) {
325 if (lessThan(context, items[B2.end - 1], items[A2.start])) {
310326 // the two ranges are in reverse order, so copy them in reverse order into the cache
311327 mem.copy(T, cache[A1.length() + B2.length() ..], items[A2.start..A2.end]);
312328 mem.copy(T, cache[A1.length()..], items[B2.start..B2.end]);
313 } else if (lessThan(items[B2.start], items[A2.end - 1])) {
329 } else if (lessThan(context, items[B2.start], items[A2.end - 1])) {
314330 // these two ranges weren't already in order, so merge them into the cache
315 mergeInto(T, items, A2, B2, lessThan, cache[A1.length()..]);
331 mergeInto(T, items, A2, B2, context, lessThan, cache[A1.length()..]);
316332 } else {
317333 // copy A2 and B2 into the cache in the same order
318334 mem.copy(T, cache[A1.length()..], items[A2.start..A2.end]);
......@@ -324,13 +340,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
324340 const A3 = Range.init(0, A1.length());
325341 const B3 = Range.init(A1.length(), A1.length() + A2.length());
326342
327 if (lessThan(cache[B3.end - 1], cache[A3.start])) {
343 if (lessThan(context, cache[B3.end - 1], cache[A3.start])) {
328344 // the two ranges are in reverse order, so copy them in reverse order into the items
329345 mem.copy(T, items[A1.start + A2.length() ..], cache[A3.start..A3.end]);
330346 mem.copy(T, items[A1.start..], cache[B3.start..B3.end]);
331 } else if (lessThan(cache[B3.start], cache[A3.end - 1])) {
347 } else if (lessThan(context, cache[B3.start], cache[A3.end - 1])) {
332348 // these two ranges weren't already in order, so merge them back into the items
333 mergeInto(T, cache[0..], A3, B3, lessThan, items[A1.start..]);
349 mergeInto(T, cache[0..], A3, B3, context, lessThan, items[A1.start..]);
334350 } else {
335351 // copy A3 and B3 into the items in the same order
336352 mem.copy(T, items[A1.start..], cache[A3.start..A3.end]);
......@@ -347,13 +363,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
347363 var A = iterator.nextRange();
348364 var B = iterator.nextRange();
349365
350 if (lessThan(items[B.end - 1], items[A.start])) {
366 if (lessThan(context, items[B.end - 1], items[A.start])) {
351367 // the two ranges are in reverse order, so a simple rotation should fix it
352368 mem.rotate(T, items[A.start..B.end], A.length());
353 } else if (lessThan(items[B.start], items[A.end - 1])) {
369 } else if (lessThan(context, items[B.start], items[A.end - 1])) {
354370 // these two ranges weren't already in order, so we'll need to merge them!
355371 mem.copy(T, cache[0..], items[A.start..A.end]);
356 mergeExternal(T, items, A, B, lessThan, cache[0..]);
372 mergeExternal(T, items, A, B, context, lessThan, cache[0..]);
357373 }
358374 }
359375 }
......@@ -435,7 +451,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
435451 last = index;
436452 count += 1;
437453 }) {
438 index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), lessThan, find - count);
454 index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), context, lessThan, find - count);
439455 if (index == A.end) break;
440456 }
441457 index = last;
......@@ -493,7 +509,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
493509 last = index - 1;
494510 count += 1;
495511 }) {
496 index = findFirstBackward(T, items, items[last], Range.init(B.start, last), lessThan, find - count);
512 index = findFirstBackward(T, items, items[last], Range.init(B.start, last), context, lessThan, find - count);
497513 if (index == B.start) break;
498514 }
499515 index = last;
......@@ -558,7 +574,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
558574 index = pull[pull_index].from;
559575 count = 1;
560576 while (count < length) : (count += 1) {
561 index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), lessThan, length - count);
577 index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), context, lessThan, length - count);
562578 const range = Range.init(index + 1, pull[pull_index].from + 1);
563579 mem.rotate(T, items[range.start..range.end], range.length() - count);
564580 pull[pull_index].from = index + count;
......@@ -568,7 +584,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
568584 index = pull[pull_index].from + 1;
569585 count = 1;
570586 while (count < length) : (count += 1) {
571 index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), lessThan, length - count);
587 index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), context, lessThan, length - count);
572588 const range = Range.init(pull[pull_index].from, index - 1);
573589 mem.rotate(T, items[range.start..range.end], count);
574590 pull[pull_index].from = index - 1 - count;
......@@ -615,10 +631,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
615631 }
616632 }
617633
618 if (lessThan(items[B.end - 1], items[A.start])) {
634 if (lessThan(context, items[B.end - 1], items[A.start])) {
619635 // the two ranges are in reverse order, so a simple rotation should fix it
620636 mem.rotate(T, items[A.start..B.end], A.length());
621 } else if (lessThan(items[A.end], items[A.end - 1])) {
637 } else if (lessThan(context, items[A.end], items[A.end - 1])) {
622638 // these two ranges weren't already in order, so we'll need to merge them!
623639 var findA: usize = undefined;
624640
......@@ -656,16 +672,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
656672 while (true) {
657673 // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,
658674 // then drop that minimum A block behind. or if there are no B blocks left then keep dropping the remaining A blocks.
659 if ((lastB.length() > 0 and !lessThan(items[lastB.end - 1], items[indexA])) or blockB.length() == 0) {
675 if ((lastB.length() > 0 and !lessThan(context, items[lastB.end - 1], items[indexA])) or blockB.length() == 0) {
660676 // figure out where to split the previous B block, and rotate it at the split
661 const B_split = binaryFirst(T, items, items[indexA], lastB, lessThan);
677 const B_split = binaryFirst(T, items, items[indexA], lastB, context, lessThan);
662678 const B_remaining = lastB.end - B_split;
663679
664680 // swap the minimum A block to the beginning of the rolling A blocks
665681 var minA = blockA.start;
666682 findA = minA + block_size;
667683 while (findA < blockA.end) : (findA += block_size) {
668 if (lessThan(items[findA], items[minA])) {
684 if (lessThan(context, items[findA], items[minA])) {
669685 minA = findA;
670686 }
671687 }
......@@ -681,11 +697,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
681697 // or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)
682698
683699 if (lastA.length() <= cache.len) {
684 mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, cache[0..]);
700 mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan, cache[0..]);
685701 } else if (buffer2.length() > 0) {
686 mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, buffer2);
702 mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan, buffer2);
687703 } else {
688 mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), lessThan);
704 mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), context, lessThan);
689705 }
690706
691707 if (buffer2.length() > 0 or block_size <= cache.len) {
......@@ -741,11 +757,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
741757
742758 // merge the last A block with the remaining B values
743759 if (lastA.length() <= cache.len) {
744 mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, cache[0..]);
760 mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan, cache[0..]);
745761 } else if (buffer2.length() > 0) {
746 mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, buffer2);
762 mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan, buffer2);
747763 } else {
748 mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), lessThan);
764 mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), context, lessThan);
749765 }
750766 }
751767 }
......@@ -755,7 +771,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
755771
756772 // while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,
757773 // even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here
758 insertionSort(T, items[buffer2.start..buffer2.end], lessThan);
774 insertionSort(T, items[buffer2.start..buffer2.end], context, lessThan);
759775
760776 pull_index = 0;
761777 while (pull_index < 2) : (pull_index += 1) {
......@@ -764,7 +780,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
764780 // the values were pulled out to the left, so redistribute them back to the right
765781 var buffer = Range.init(pull[pull_index].range.start, pull[pull_index].range.start + pull[pull_index].count);
766782 while (buffer.length() > 0) {
767 index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), lessThan, unique);
783 index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), context, lessThan, unique);
768784 const amount = index - buffer.end;
769785 mem.rotate(T, items[buffer.start..index], buffer.length());
770786 buffer.start += (amount + 1);
......@@ -775,7 +791,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
775791 // the values were pulled out to the right, so redistribute them back to the left
776792 var buffer = Range.init(pull[pull_index].range.end - pull[pull_index].count, pull[pull_index].range.end);
777793 while (buffer.length() > 0) {
778 index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), lessThan, unique);
794 index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), context, lessThan, unique);
779795 const amount = buffer.start - index;
780796 mem.rotate(T, items[index..buffer.end], amount);
781797 buffer.start -= amount;
......@@ -792,7 +808,14 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) vo
792808}
793809
794810// merge operation without a buffer
795fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessThan: fn (T, T) bool) void {
811fn mergeInPlace(
812 comptime T: type,
813 items: []T,
814 A_arg: Range,
815 B_arg: Range,
816 context: var,
817 comptime lessThan: fn (@TypeOf(context), T, T) bool,
818) void {
796819 if (A_arg.length() == 0 or B_arg.length() == 0) return;
797820
798821 // this just repeatedly binary searches into B and rotates A into position.
......@@ -818,7 +841,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh
818841
819842 while (true) {
820843 // find the first place in B where the first item in A needs to be inserted
821 const mid = binaryFirst(T, items, items[A.start], B, lessThan);
844 const mid = binaryFirst(T, items, items[A.start], B, context, lessThan);
822845
823846 // rotate A into place
824847 const amount = mid - A.end;
......@@ -828,13 +851,21 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessTh
828851 // calculate the new A and B ranges
829852 B.start = mid;
830853 A = Range.init(A.start + amount, B.start);
831 A.start = binaryLast(T, items, items[A.start], A, lessThan);
854 A.start = binaryLast(T, items, items[A.start], A, context, lessThan);
832855 if (A.length() == 0) break;
833856 }
834857}
835858
836859// merge operation using an internal buffer
837fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, buffer: Range) void {
860fn mergeInternal(
861 comptime T: type,
862 items: []T,
863 A: Range,
864 B: Range,
865 context: var,
866 comptime lessThan: fn (@TypeOf(context), T, T) bool,
867 buffer: Range,
868) void {
838869 // whenever we find a value to add to the final array, swap it with the value that's already in that spot
839870 // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order
840871 var A_count: usize = 0;
......@@ -843,7 +874,7 @@ fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn
843874
844875 if (B.length() > 0 and A.length() > 0) {
845876 while (true) {
846 if (!lessThan(items[B.start + B_count], items[buffer.start + A_count])) {
877 if (!lessThan(context, items[B.start + B_count], items[buffer.start + A_count])) {
847878 mem.swap(T, &items[A.start + insert], &items[buffer.start + A_count]);
848879 A_count += 1;
849880 insert += 1;
......@@ -870,63 +901,102 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s
870901
871902// combine a linear search with a binary search to reduce the number of comparisons in situations
872903// where have some idea as to how many unique values there are and where the next value might be
873fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
904fn findFirstForward(
905 comptime T: type,
906 items: []T,
907 value: T,
908 range: Range,
909 context: var,
910 comptime lessThan: fn (@TypeOf(context), T, T) bool,
911 unique: usize,
912) usize {
874913 if (range.length() == 0) return range.start;
875914 const skip = math.max(range.length() / unique, @as(usize, 1));
876915
877916 var index = range.start + skip;
878 while (lessThan(items[index - 1], value)) : (index += skip) {
917 while (lessThan(context, items[index - 1], value)) : (index += skip) {
879918 if (index >= range.end - skip) {
880 return binaryFirst(T, items, value, Range.init(index, range.end), lessThan);
919 return binaryFirst(T, items, value, Range.init(index, range.end), context, lessThan);
881920 }
882921 }
883922
884 return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);
923 return binaryFirst(T, items, value, Range.init(index - skip, index), context, lessThan);
885924}
886925
887fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
926fn findFirstBackward(
927 comptime T: type,
928 items: []T,
929 value: T,
930 range: Range,
931 context: var,
932 comptime lessThan: fn (@TypeOf(context), T, T) bool,
933 unique: usize,
934) usize {
888935 if (range.length() == 0) return range.start;
889936 const skip = math.max(range.length() / unique, @as(usize, 1));
890937
891938 var index = range.end - skip;
892 while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) {
939 while (index > range.start and !lessThan(context, items[index - 1], value)) : (index -= skip) {
893940 if (index < range.start + skip) {
894 return binaryFirst(T, items, value, Range.init(range.start, index), lessThan);
941 return binaryFirst(T, items, value, Range.init(range.start, index), context, lessThan);
895942 }
896943 }
897944
898 return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);
945 return binaryFirst(T, items, value, Range.init(index, index + skip), context, lessThan);
899946}
900947
901fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
948fn findLastForward(
949 comptime T: type,
950 items: []T,
951 value: T,
952 range: Range,
953 context: var,
954 comptime lessThan: fn (@TypeOf(context), T, T) bool,
955 unique: usize,
956) usize {
902957 if (range.length() == 0) return range.start;
903958 const skip = math.max(range.length() / unique, @as(usize, 1));
904959
905960 var index = range.start + skip;
906 while (!lessThan(value, items[index - 1])) : (index += skip) {
961 while (!lessThan(context, value, items[index - 1])) : (index += skip) {
907962 if (index >= range.end - skip) {
908 return binaryLast(T, items, value, Range.init(index, range.end), lessThan);
963 return binaryLast(T, items, value, Range.init(index, range.end), context, lessThan);
909964 }
910965 }
911966
912 return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);
967 return binaryLast(T, items, value, Range.init(index - skip, index), context, lessThan);
913968}
914969
915fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
970fn findLastBackward(
971 comptime T: type,
972 items: []T,
973 value: T,
974 range: Range,
975 context: var,
976 comptime lessThan: fn (@TypeOf(context), T, T) bool,
977 unique: usize,
978) usize {
916979 if (range.length() == 0) return range.start;
917980 const skip = math.max(range.length() / unique, @as(usize, 1));
918981
919982 var index = range.end - skip;
920 while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) {
983 while (index > range.start and lessThan(context, value, items[index - 1])) : (index -= skip) {
921984 if (index < range.start + skip) {
922 return binaryLast(T, items, value, Range.init(range.start, index), lessThan);
985 return binaryLast(T, items, value, Range.init(range.start, index), context, lessThan);
923986 }
924987 }
925988
926 return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);
989 return binaryLast(T, items, value, Range.init(index, index + skip), context, lessThan);
927990}
928991
929fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize {
992fn binaryFirst(
993 comptime T: type,
994 items: []T,
995 value: T,
996 range: Range,
997 context: var,
998 comptime lessThan: fn (@TypeOf(context), T, T) bool,
999) usize {
9301000 var curr = range.start;
9311001 var size = range.length();
9321002 if (range.start >= range.end) return range.end;
......@@ -935,14 +1005,21 @@ fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: f
9351005
9361006 size /= 2;
9371007 const mid = items[curr + size];
938 if (lessThan(mid, value)) {
1008 if (lessThan(context, mid, value)) {
9391009 curr += size + offset;
9401010 }
9411011 }
9421012 return curr;
9431013}
9441014
945fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize {
1015fn binaryLast(
1016 comptime T: type,
1017 items: []T,
1018 value: T,
1019 range: Range,
1020 context: var,
1021 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1022) usize {
9461023 var curr = range.start;
9471024 var size = range.length();
9481025 if (range.start >= range.end) return range.end;
......@@ -951,14 +1028,22 @@ fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn
9511028
9521029 size /= 2;
9531030 const mid = items[curr + size];
954 if (!lessThan(value, mid)) {
1031 if (!lessThan(context, value, mid)) {
9551032 curr += size + offset;
9561033 }
9571034 }
9581035 return curr;
9591036}
9601037
961fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T) bool, into: []T) void {
1038fn mergeInto(
1039 comptime T: type,
1040 from: []T,
1041 A: Range,
1042 B: Range,
1043 context: var,
1044 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1045 into: []T,
1046) void {
9621047 var A_index: usize = A.start;
9631048 var B_index: usize = B.start;
9641049 const A_last = A.end;
......@@ -966,7 +1051,7 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T
9661051 var insert_index: usize = 0;
9671052
9681053 while (true) {
969 if (!lessThan(from[B_index], from[A_index])) {
1054 if (!lessThan(context, from[B_index], from[A_index])) {
9701055 into[insert_index] = from[A_index];
9711056 A_index += 1;
9721057 insert_index += 1;
......@@ -988,7 +1073,15 @@ fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T
9881073 }
9891074}
9901075
991fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, cache: []T) void {
1076fn mergeExternal(
1077 comptime T: type,
1078 items: []T,
1079 A: Range,
1080 B: Range,
1081 context: var,
1082 comptime lessThan: fn (@TypeOf(context), T, T) bool,
1083 cache: []T,
1084) void {
9921085 // A fits into the cache, so use that instead of the internal buffer
9931086 var A_index: usize = 0;
9941087 var B_index: usize = B.start;
......@@ -998,7 +1091,7 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn
9981091
9991092 if (B.length() > 0 and A.length() > 0) {
10001093 while (true) {
1001 if (!lessThan(items[B_index], cache[A_index])) {
1094 if (!lessThan(context, items[B_index], cache[A_index])) {
10021095 items[insert_index] = cache[A_index];
10031096 A_index += 1;
10041097 insert_index += 1;
......@@ -1016,17 +1109,25 @@ fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn
10161109 mem.copy(T, items[insert_index..], cache[A_index..A_last]);
10171110}
10181111
1019fn swap(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool, order: *[8]u8, x: usize, y: usize) void {
1020 if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) {
1112fn swap(
1113 comptime T: type,
1114 items: []T,
1115 context: var,
1116 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
1117 order: *[8]u8,
1118 x: usize,
1119 y: usize,
1120) void {
1121 if (lessThan(context, items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(context, items[x], items[y]))) {
10211122 mem.swap(T, &items[x], &items[y]);
10221123 mem.swap(u8, &(order.*)[x], &(order.*)[y]);
10231124 }
10241125}
10251126
1026// Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
1027pub fn asc(comptime T: type) fn (T, T) bool {
1127/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
1128pub fn asc(comptime T: type) fn (void, T, T) bool {
10281129 const impl = struct {
1029 fn inner(a: T, b: T) bool {
1130 fn inner(context: void, a: T, b: T) bool {
10301131 return a < b;
10311132 }
10321133 };
......@@ -1034,9 +1135,10 @@ pub fn asc(comptime T: type) fn (T, T) bool {
10341135 return impl.inner;
10351136}
10361137
1037pub fn desc(comptime T: type) fn (T, T) bool {
1138/// Use to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
1139pub fn desc(comptime T: type) fn (void, T, T) bool {
10381140 const impl = struct {
1039 fn inner(a: T, b: T) bool {
1141 fn inner(context: void, a: T, b: T) bool {
10401142 return a > b;
10411143 }
10421144 };
......@@ -1085,7 +1187,7 @@ fn testStableSort() void {
10851187 },
10861188 };
10871189 for (cases) |*case| {
1088 insertionSort(IdAndValue, (case.*)[0..], cmpByValue);
1190 insertionSort(IdAndValue, (case.*)[0..], {}, cmpByValue);
10891191 for (case.*) |item, i| {
10901192 testing.expect(item.id == expected[i].id);
10911193 testing.expect(item.value == expected[i].value);
......@@ -1096,11 +1198,16 @@ const IdAndValue = struct {
10961198 id: usize,
10971199 value: i32,
10981200};
1099fn cmpByValue(a: IdAndValue, b: IdAndValue) bool {
1100 return asc(i32)(a.value, b.value);
1201fn cmpByValue(context: void, a: IdAndValue, b: IdAndValue) bool {
1202 return asc_i32(context, a.value, b.value);
11011203}
11021204
1103test "std.sort" {
1205const asc_u8 = asc(u8);
1206const asc_i32 = asc(i32);
1207const desc_u8 = desc(u8);
1208const desc_i32 = desc(i32);
1209
1210test "sort" {
11041211 const u8cases = [_][]const []const u8{
11051212 &[_][]const u8{
11061213 "",
......@@ -1132,7 +1239,7 @@ test "std.sort" {
11321239 var buf: [8]u8 = undefined;
11331240 const slice = buf[0..case[0].len];
11341241 mem.copy(u8, slice, case[0]);
1135 sort(u8, slice, asc(u8));
1242 sort(u8, slice, {}, asc_u8);
11361243 testing.expect(mem.eql(u8, slice, case[1]));
11371244 }
11381245
......@@ -1167,12 +1274,12 @@ test "std.sort" {
11671274 var buf: [8]i32 = undefined;
11681275 const slice = buf[0..case[0].len];
11691276 mem.copy(i32, slice, case[0]);
1170 sort(i32, slice, asc(i32));
1277 sort(i32, slice, {}, asc_i32);
11711278 testing.expect(mem.eql(i32, slice, case[1]));
11721279 }
11731280}
11741281
1175test "std.sort descending" {
1282test "sort descending" {
11761283 const rev_cases = [_][]const []const i32{
11771284 &[_][]const i32{
11781285 &[_]i32{},
......@@ -1204,14 +1311,14 @@ test "std.sort descending" {
12041311 var buf: [8]i32 = undefined;
12051312 const slice = buf[0..case[0].len];
12061313 mem.copy(i32, slice, case[0]);
1207 sort(i32, slice, desc(i32));
1314 sort(i32, slice, {}, desc_i32);
12081315 testing.expect(mem.eql(i32, slice, case[1]));
12091316 }
12101317}
12111318
12121319test "another sort case" {
12131320 var arr = [_]i32{ 5, 3, 1, 2, 4 };
1214 sort(i32, arr[0..], asc(i32));
1321 sort(i32, arr[0..], {}, asc_i32);
12151322
12161323 testing.expect(mem.eql(i32, &arr, &[_]i32{ 1, 2, 3, 4, 5 }));
12171324}
......@@ -1236,7 +1343,7 @@ fn fuzzTest(rng: *std.rand.Random) !void {
12361343 item.id = index;
12371344 item.value = rng.intRangeLessThan(i32, 0, 100);
12381345 }
1239 sort(IdAndValue, array, cmpByValue);
1346 sort(IdAndValue, array, {}, cmpByValue);
12401347
12411348 var index: usize = 1;
12421349 while (index < array.len) : (index += 1) {
......@@ -1248,7 +1355,12 @@ fn fuzzTest(rng: *std.rand.Random) !void {
12481355 }
12491356}
12501357
1251pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize {
1358pub fn argMin(
1359 comptime T: type,
1360 items: []const T,
1361 context: var,
1362 comptime lessThan: fn (@TypeOf(context), lhs: T, rhs: T) bool,
1363) ?usize {
12521364 if (items.len == 0) {
12531365 return null;
12541366 }
......@@ -1256,7 +1368,7 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
12561368 var smallest = items[0];
12571369 var smallest_index: usize = 0;
12581370 for (items[1..]) |item, i| {
1259 if (lessThan(item, smallest)) {
1371 if (lessThan(context, item, smallest)) {
12601372 smallest = item;
12611373 smallest_index = i + 1;
12621374 }
......@@ -1265,32 +1377,42 @@ pub fn argMin(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
12651377 return smallest_index;
12661378}
12671379
1268test "std.sort.argMin" {
1269 testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, asc(i32)));
1270 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, asc(i32)));
1271 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));
1272 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));
1273 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));
1274 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));
1275 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));
1380test "argMin" {
1381 testing.expectEqual(@as(?usize, null), argMin(i32, &[_]i32{}, {}, asc_i32));
1382 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{1}, {}, asc_i32));
1383 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1384 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1385 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1386 testing.expectEqual(@as(?usize, 0), argMin(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1387 testing.expectEqual(@as(?usize, 3), argMin(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
12761388}
12771389
1278pub fn min(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T {
1279 const i = argMin(T, items, lessThan) orelse return null;
1390pub fn min(
1391 comptime T: type,
1392 items: []const T,
1393 context: var,
1394 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1395) ?T {
1396 const i = argMin(T, items, context, lessThan) orelse return null;
12801397 return items[i];
12811398}
12821399
1283test "std.sort.min" {
1284 testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, asc(i32)));
1285 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, asc(i32)));
1286 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));
1287 testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));
1288 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));
1289 testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));
1290 testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));
1400test "min" {
1401 testing.expectEqual(@as(?i32, null), min(i32, &[_]i32{}, {}, asc_i32));
1402 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{1}, {}, asc_i32));
1403 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1404 testing.expectEqual(@as(?i32, 2), min(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1405 testing.expectEqual(@as(?i32, 1), min(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1406 testing.expectEqual(@as(?i32, -10), min(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1407 testing.expectEqual(@as(?i32, 7), min(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
12911408}
12921409
1293pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?usize {
1410pub fn argMax(
1411 comptime T: type,
1412 items: []const T,
1413 context: var,
1414 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1415) ?usize {
12941416 if (items.len == 0) {
12951417 return null;
12961418 }
......@@ -1298,7 +1420,7 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
12981420 var biggest = items[0];
12991421 var biggest_index: usize = 0;
13001422 for (items[1..]) |item, i| {
1301 if (lessThan(biggest, item)) {
1423 if (lessThan(context, biggest, item)) {
13021424 biggest = item;
13031425 biggest_index = i + 1;
13041426 }
......@@ -1307,35 +1429,45 @@ pub fn argMax(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T)
13071429 return biggest_index;
13081430}
13091431
1310test "std.sort.argMax" {
1311 testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, asc(i32)));
1312 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, asc(i32)));
1313 testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));
1314 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));
1315 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));
1316 testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));
1317 testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));
1432test "argMax" {
1433 testing.expectEqual(@as(?usize, null), argMax(i32, &[_]i32{}, {}, asc_i32));
1434 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{1}, {}, asc_i32));
1435 testing.expectEqual(@as(?usize, 4), argMax(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1436 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1437 testing.expectEqual(@as(?usize, 0), argMax(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1438 testing.expectEqual(@as(?usize, 2), argMax(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1439 testing.expectEqual(@as(?usize, 1), argMax(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
13181440}
13191441
1320pub fn max(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) ?T {
1321 const i = argMax(T, items, lessThan) orelse return null;
1442pub fn max(
1443 comptime T: type,
1444 items: []const T,
1445 context: var,
1446 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1447) ?T {
1448 const i = argMax(T, items, context, lessThan) orelse return null;
13221449 return items[i];
13231450}
13241451
1325test "std.sort.max" {
1326 testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, asc(i32)));
1327 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, asc(i32)));
1328 testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));
1329 testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, asc(i32)));
1330 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));
1331 testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, asc(i32)));
1332 testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, desc(i32)));
1452test "max" {
1453 testing.expectEqual(@as(?i32, null), max(i32, &[_]i32{}, {}, asc_i32));
1454 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{1}, {}, asc_i32));
1455 testing.expectEqual(@as(?i32, 5), max(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1456 testing.expectEqual(@as(?i32, 9), max(i32, &[_]i32{ 9, 3, 8, 2, 5 }, {}, asc_i32));
1457 testing.expectEqual(@as(?i32, 1), max(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1458 testing.expectEqual(@as(?i32, 10), max(i32, &[_]i32{ -10, 1, 10 }, {}, asc_i32));
1459 testing.expectEqual(@as(?i32, 3), max(i32, &[_]i32{ 6, 3, 5, 7, 6 }, {}, desc_i32));
13331460}
13341461
1335pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T) bool) bool {
1462pub fn isSorted(
1463 comptime T: type,
1464 items: []const T,
1465 context: var,
1466 comptime lessThan: fn (context: @TypeOf(context), lhs: T, rhs: T) bool,
1467) bool {
13361468 var i: usize = 1;
13371469 while (i < items.len) : (i += 1) {
1338 if (lessThan(items[i], items[i - 1])) {
1470 if (lessThan(context, items[i], items[i - 1])) {
13391471 return false;
13401472 }
13411473 }
......@@ -1343,29 +1475,29 @@ pub fn isSorted(comptime T: type, items: []const T, lessThan: fn (lhs: T, rhs: T
13431475 return true;
13441476}
13451477
1346test "std.sort.isSorted" {
1347 testing.expect(isSorted(i32, &[_]i32{}, asc(i32)));
1348 testing.expect(isSorted(i32, &[_]i32{10}, asc(i32)));
1349 testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, asc(i32)));
1350 testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, asc(i32)));
1478test "isSorted" {
1479 testing.expect(isSorted(i32, &[_]i32{}, {}, asc_i32));
1480 testing.expect(isSorted(i32, &[_]i32{10}, {}, asc_i32));
1481 testing.expect(isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, asc_i32));
1482 testing.expect(isSorted(i32, &[_]i32{ -10, 1, 1, 1, 10 }, {}, asc_i32));
13511483
1352 testing.expect(isSorted(i32, &[_]i32{}, desc(i32)));
1353 testing.expect(isSorted(i32, &[_]i32{-20}, desc(i32)));
1354 testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, desc(i32)));
1355 testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, desc(i32)));
1484 testing.expect(isSorted(i32, &[_]i32{}, {}, desc_i32));
1485 testing.expect(isSorted(i32, &[_]i32{-20}, {}, desc_i32));
1486 testing.expect(isSorted(i32, &[_]i32{ 3, 2, 1, 0, -1 }, {}, desc_i32));
1487 testing.expect(isSorted(i32, &[_]i32{ 10, -10 }, {}, desc_i32));
13561488
1357 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, asc(i32)));
1358 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, desc(i32)));
1489 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, asc_i32));
1490 testing.expect(isSorted(i32, &[_]i32{ 1, 1, 1, 1, 1 }, {}, desc_i32));
13591491
1360 testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, asc(i32)));
1361 testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, desc(i32)));
1492 testing.expectEqual(false, isSorted(i32, &[_]i32{ 5, 4, 3, 2, 1 }, {}, asc_i32));
1493 testing.expectEqual(false, isSorted(i32, &[_]i32{ 1, 2, 3, 4, 5 }, {}, desc_i32));
13621494
1363 testing.expect(isSorted(u8, "abcd", asc(u8)));
1364 testing.expect(isSorted(u8, "zyxw", desc(u8)));
1495 testing.expect(isSorted(u8, "abcd", {}, asc_u8));
1496 testing.expect(isSorted(u8, "zyxw", {}, desc_u8));
13651497
1366 testing.expectEqual(false, isSorted(u8, "abcd", desc(u8)));
1367 testing.expectEqual(false, isSorted(u8, "zyxw", asc(u8)));
1498 testing.expectEqual(false, isSorted(u8, "abcd", {}, desc_u8));
1499 testing.expectEqual(false, isSorted(u8, "zyxw", {}, asc_u8));
13681500
1369 testing.expect(isSorted(u8, "ffff", asc(u8)));
1370 testing.expect(isSorted(u8, "ffff", desc(u8)));
1501 testing.expect(isSorted(u8, "ffff", {}, asc_u8));
1502 testing.expect(isSorted(u8, "ffff", {}, desc_u8));
13711503}