| ... | @@ -4,6 +4,19 @@ const math = @import("math/index.zig"); | ... | @@ -4,6 +4,19 @@ const math = @import("math/index.zig"); |
| 4 | | 4 | |
| 5 | pub const Cmp = math.Cmp; | 5 | pub const Cmp = math.Cmp; |
| 6 | | 6 | |
| | 7 | /// Stable sort using O(1) space. Currently implemented as insertion sort. |
| | 8 | pub fn sort_stable(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { |
| | 9 | {var i: usize = 1; while (i < array.len) : (i += 1) { |
| | 10 | const x = array[i]; |
| | 11 | var j: usize = i; |
| | 12 | while (j > 0 and cmp(array[j - 1], x) == Cmp.Greater) : (j -= 1) { |
| | 13 | array[j] = array[j - 1]; |
| | 14 | } |
| | 15 | array[j] = x; |
| | 16 | }} |
| | 17 | } |
| | 18 | |
| | 19 | /// Unstable sort using O(n) stack space. Currentl implemented as quicksort. |
| 7 | pub fn sort(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { | 20 | pub fn sort(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) { |
| 8 | if (array.len > 0) { | 21 | if (array.len > 0) { |
| 9 | quicksort(T, array, 0, array.len - 1, cmp); | 22 | quicksort(T, array, 0, array.len - 1, cmp); |
| ... | @@ -58,6 +71,62 @@ fn reverse(was: Cmp) -> Cmp { | ... | @@ -58,6 +71,62 @@ fn reverse(was: Cmp) -> Cmp { |
| 58 | // --------------------------------------- | 71 | // --------------------------------------- |
| 59 | // tests | 72 | // tests |
| 60 | | 73 | |
| | 74 | test "stable sort" { |
| | 75 | testStableSort(); |
| | 76 | comptime testStableSort(); |
| | 77 | } |
| | 78 | fn testStableSort() { |
| | 79 | var expected = []IdAndValue { |
| | 80 | IdAndValue{.id = 0, .value = 0}, |
| | 81 | IdAndValue{.id = 1, .value = 0}, |
| | 82 | IdAndValue{.id = 2, .value = 0}, |
| | 83 | IdAndValue{.id = 0, .value = 1}, |
| | 84 | IdAndValue{.id = 1, .value = 1}, |
| | 85 | IdAndValue{.id = 2, .value = 1}, |
| | 86 | IdAndValue{.id = 0, .value = 2}, |
| | 87 | IdAndValue{.id = 1, .value = 2}, |
| | 88 | IdAndValue{.id = 2, .value = 2}, |
| | 89 | }; |
| | 90 | var cases = [][9]IdAndValue { |
| | 91 | []IdAndValue { |
| | 92 | IdAndValue{.id = 0, .value = 0}, |
| | 93 | IdAndValue{.id = 0, .value = 1}, |
| | 94 | IdAndValue{.id = 0, .value = 2}, |
| | 95 | IdAndValue{.id = 1, .value = 0}, |
| | 96 | IdAndValue{.id = 1, .value = 1}, |
| | 97 | IdAndValue{.id = 1, .value = 2}, |
| | 98 | IdAndValue{.id = 2, .value = 0}, |
| | 99 | IdAndValue{.id = 2, .value = 1}, |
| | 100 | IdAndValue{.id = 2, .value = 2}, |
| | 101 | }, |
| | 102 | []IdAndValue { |
| | 103 | IdAndValue{.id = 0, .value = 2}, |
| | 104 | IdAndValue{.id = 0, .value = 1}, |
| | 105 | IdAndValue{.id = 0, .value = 0}, |
| | 106 | IdAndValue{.id = 1, .value = 2}, |
| | 107 | IdAndValue{.id = 1, .value = 1}, |
| | 108 | IdAndValue{.id = 1, .value = 0}, |
| | 109 | IdAndValue{.id = 2, .value = 2}, |
| | 110 | IdAndValue{.id = 2, .value = 1}, |
| | 111 | IdAndValue{.id = 2, .value = 0}, |
| | 112 | }, |
| | 113 | }; |
| | 114 | for (cases) |*case| { |
| | 115 | sort_stable(IdAndValue, (*case)[0..], cmpByValue); |
| | 116 | for (*case) |item, i| { |
| | 117 | assert(item.id == expected[i].id); |
| | 118 | assert(item.value == expected[i].value); |
| | 119 | } |
| | 120 | } |
| | 121 | } |
| | 122 | const IdAndValue = struct { |
| | 123 | id: i32, |
| | 124 | value: i32, |
| | 125 | }; |
| | 126 | fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> Cmp { |
| | 127 | return i32asc(a.value, b.value); |
| | 128 | } |
| | 129 | |
| 61 | test "testSort" { | 130 | test "testSort" { |
| 62 | const u8cases = [][]const []const u8 { | 131 | const u8cases = [][]const []const u8 { |
| 63 | [][]const u8{"", ""}, | 132 | [][]const u8{"", ""}, |