Skip to content

Commit 9c4cb32

Browse files
Merge 6a81368 into 826b18a
2 parents 826b18a + 6a81368 commit 9c4cb32

File tree

5 files changed

+241
-28
lines changed

5 files changed

+241
-28
lines changed

noir_stdlib/src/array/mod.nr

Lines changed: 99 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -183,24 +183,22 @@ where
183183
/// assert(sorted_descending == [32, 42]); // does not verify
184184
/// }
185185
/// ```
186-
pub fn sort_via<Env>(self, ordering: fn[Env](T, T) -> bool) -> Self {
186+
pub fn sort_via(self, ordering: fn(T, T) -> bool) -> Self {
187187
// Safety: `sorted` array is checked to be:
188188
// a. a permutation of `input`'s elements
189189
// b. satisfying the predicate `ordering`
190-
unsafe {
191-
let sorted = quicksort::quicksort(self, ordering);
192-
193-
if !is_unconstrained() {
194-
for i in 0..N - 1 {
195-
assert(
196-
ordering(sorted[i], sorted[i + 1]),
197-
"Array has not been sorted correctly according to `ordering`.",
198-
);
199-
}
200-
check_shuffle::check_shuffle(self, sorted);
190+
let sorted = unsafe { quicksort::quicksort(self, ordering) };
191+
192+
if !is_unconstrained() {
193+
for i in 0..N - 1 {
194+
assert(
195+
ordering(sorted[i], sorted[i + 1]),
196+
"Array has not been sorted correctly according to `ordering`.",
197+
);
201198
}
202-
sorted
199+
check_shuffle::check_shuffle(self, sorted);
203200
}
201+
sorted
204202
}
205203
}
206204

@@ -232,4 +230,92 @@ mod test {
232230
fn map_empty() {
233231
assert_eq([].map(|x| x + 1), []);
234232
}
233+
234+
global arr_with_100_values: [u32; 100] = [
235+
42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2, 54,
236+
89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41, 19, 98,
237+
53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21, 43, 86, 35,
238+
21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15, 127, 81, 30, 8,
239+
125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,
240+
];
241+
global expected_with_100_values: [u32; 100] = [
242+
0, 0, 2, 3, 5, 6, 8, 12, 13, 13, 15, 19, 19, 19, 21, 21, 26, 26, 27, 28, 28, 28, 29, 30, 32,
243+
32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58, 61, 62,
244+
62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82, 84, 84, 86,
245+
86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114, 114, 116, 118,
246+
119, 120, 121, 123, 123, 123, 125, 126, 127,
247+
];
248+
fn sort_u32(a: u32, b: u32) -> bool {
249+
a <= b
250+
}
251+
252+
#[test]
253+
fn test_sort() {
254+
let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];
255+
256+
let sorted = arr.sort();
257+
258+
let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
259+
assert(sorted == expected);
260+
}
261+
262+
#[test]
263+
fn test_sort_100_values() {
264+
let mut arr: [u32; 100] = [
265+
42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2,
266+
54, 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41,
267+
19, 98, 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21,
268+
43, 86, 35, 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15,
269+
127, 81, 30, 8, 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,
270+
];
271+
272+
let sorted = arr.sort();
273+
274+
let expected: [u32; 100] = [
275+
0, 0, 2, 3, 5, 6, 8, 12, 13, 13, 15, 19, 19, 19, 21, 21, 26, 26, 27, 28, 28, 28, 29, 30,
276+
32, 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58,
277+
61, 62, 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82,
278+
84, 84, 86, 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114,
279+
114, 116, 118, 119, 120, 121, 123, 123, 123, 125, 126, 127,
280+
];
281+
assert(sorted == expected);
282+
}
283+
284+
#[test]
285+
fn test_sort_100_values_comptime() {
286+
let sorted = arr_with_100_values.sort();
287+
assert(sorted == expected_with_100_values);
288+
}
289+
290+
#[test]
291+
fn test_sort_via() {
292+
let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];
293+
294+
let sorted = arr.sort_via(sort_u32);
295+
296+
let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
297+
assert(sorted == expected);
298+
}
299+
300+
#[test]
301+
fn test_sort_via_100_values() {
302+
let mut arr: [u32; 100] = [
303+
42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2,
304+
54, 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41,
305+
19, 98, 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21,
306+
43, 86, 35, 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15,
307+
127, 81, 30, 8, 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,
308+
];
309+
310+
let sorted = arr.sort_via(sort_u32);
311+
312+
let expected: [u32; 100] = [
313+
0, 0, 2, 3, 5, 6, 8, 12, 13, 13, 15, 19, 19, 19, 21, 21, 26, 26, 27, 28, 28, 28, 29, 30,
314+
32, 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58,
315+
61, 62, 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82,
316+
84, 84, 86, 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114,
317+
114, 116, 118, 119, 120, 121, 123, 123, 123, 125, 126, 127,
318+
];
319+
assert(sorted == expected);
320+
}
235321
}

noir_stdlib/src/array/quicksort.nr

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
unconstrained fn partition<T, Env, let N: u32>(
1+
unconstrained fn partition<T, let N: u32>(
22
arr: &mut [T; N],
33
low: u32,
44
high: u32,
5-
sortfn: fn[Env](T, T) -> bool,
5+
sortfn: unconstrained fn(T, T) -> bool,
66
) -> u32 {
77
let pivot = high;
88
let mut i = low;
@@ -14,34 +14,48 @@ unconstrained fn partition<T, Env, let N: u32>(
1414
i += 1;
1515
}
1616
}
17+
1718
let temp = arr[i];
1819
arr[i] = arr[pivot];
1920
arr[pivot] = temp;
2021
i
2122
}
2223

23-
unconstrained fn quicksort_recursive<T, Env, let N: u32>(
24+
unconstrained fn quicksort_loop<T, let N: u32>(
2425
arr: &mut [T; N],
2526
low: u32,
2627
high: u32,
27-
sortfn: fn[Env](T, T) -> bool,
28+
sortfn: unconstrained fn(T, T) -> bool,
2829
) {
29-
if low < high {
30-
let pivot_index = partition(arr, low, high, sortfn);
31-
if pivot_index > 0 {
32-
quicksort_recursive(arr, low, pivot_index - 1, sortfn);
30+
let mut stack: [(u32, u32)] = &[(low, high)];
31+
// TODO(https://github.com/noir-lang/noir_sort/issues/22): use 'loop' once it's stabilized
32+
for _ in 0..2 * N {
33+
if stack.len() == 0 {
34+
break;
35+
}
36+
37+
let (new_stack, (new_low, new_high)) = stack.pop_back();
38+
stack = new_stack;
39+
40+
if new_high < new_low + 1 {
41+
continue;
42+
}
43+
44+
let pivot_index = partition(arr, new_low, new_high, sortfn);
45+
stack = stack.push_back((pivot_index + 1, new_high));
46+
if 0 < pivot_index {
47+
stack = stack.push_back((new_low, pivot_index - 1));
3348
}
34-
quicksort_recursive(arr, pivot_index + 1, high, sortfn);
3549
}
3650
}
3751

38-
pub(crate) unconstrained fn quicksort<T, Env, let N: u32>(
39-
_arr: [T; N],
40-
sortfn: fn[Env](T, T) -> bool,
52+
pub unconstrained fn quicksort<T, let N: u32>(
53+
arr: [T; N],
54+
sortfn: unconstrained fn(T, T) -> bool,
4155
) -> [T; N] {
42-
let mut arr: [T; N] = _arr;
43-
if arr.len() <= 1 {} else {
44-
quicksort_recursive(&mut arr, 0, arr.len() - 1, sortfn);
56+
let mut arr: [T; N] = arr;
57+
if arr.len() > 1 {
58+
quicksort_loop(&mut arr, 0, arr.len() - 1, sortfn);
4559
}
4660
arr
4761
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "regression_noir_sort"
3+
type = "bin"
4+
authors = [""]
5+
6+
[dependencies]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
mod quicksort;
2+
3+
pub fn sort<T, let N: u32>(input: [T; N]) -> [T; N]
4+
where
5+
T: Ord,
6+
{
7+
sort_via(input, |a, b| a <= b)
8+
}
9+
10+
pub fn sort_via<T, let N: u32>(input: [T; N], sortfn: fn(T, T) -> bool) -> [T; N] {
11+
// Safety: test
12+
let sorted = unsafe { quicksort::quicksort(input, sortfn) };
13+
14+
for i in 0..N - 1 {
15+
assert(
16+
sortfn(sorted[i], sorted[i + 1]),
17+
"Array has not been sorted correctly according to `ordering`.",
18+
);
19+
}
20+
21+
sorted
22+
}
23+
24+
mod test {
25+
use crate::sort;
26+
27+
global arr_comptime: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];
28+
global expected_comptime: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
29+
30+
#[test]
31+
fn test_sort() {
32+
let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];
33+
34+
let sorted = sort(arr);
35+
36+
let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
37+
assert(sorted == expected);
38+
}
39+
40+
#[test]
41+
fn test_sort_comptime() {
42+
let sorted = sort(arr_comptime);
43+
44+
assert(sorted == expected_comptime);
45+
}
46+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
unconstrained fn partition<T, let N: u32>(
2+
arr: &mut [T; N],
3+
low: u32,
4+
high: u32,
5+
sortfn: unconstrained fn(T, T) -> bool,
6+
) -> u32 {
7+
let pivot = high;
8+
let mut i = low;
9+
for j in low..high {
10+
if (sortfn(arr[j], arr[pivot])) {
11+
let temp = arr[i];
12+
arr[i] = arr[j];
13+
arr[j] = temp;
14+
i += 1;
15+
}
16+
}
17+
18+
let temp = arr[i];
19+
arr[i] = arr[pivot];
20+
arr[pivot] = temp;
21+
i
22+
}
23+
24+
unconstrained fn quicksort_loop<T, let N: u32>(
25+
arr: &mut [T; N],
26+
low: u32,
27+
high: u32,
28+
sortfn: unconstrained fn(T, T) -> bool,
29+
) {
30+
let mut stack: [(u32, u32)] = &[(low, high)];
31+
// TODO(https://github.com/noir-lang/noir_sort/issues/22): use 'loop' once it's stabilized
32+
for _ in 0..2 * N {
33+
if stack.len() == 0 {
34+
break;
35+
}
36+
37+
let (new_stack, (new_low, new_high)) = stack.pop_back();
38+
stack = new_stack;
39+
40+
if new_high < new_low + 1 {
41+
continue;
42+
}
43+
44+
let pivot_index = partition(arr, new_low, new_high, sortfn);
45+
stack = stack.push_back((pivot_index + 1, new_high));
46+
if 0 < pivot_index {
47+
stack = stack.push_back((new_low, pivot_index - 1));
48+
}
49+
}
50+
}
51+
52+
pub unconstrained fn quicksort<T, let N: u32>(
53+
arr: [T; N],
54+
sortfn: unconstrained fn(T, T) -> bool,
55+
) -> [T; N] {
56+
let mut arr: [T; N] = arr;
57+
if arr.len() <= 1 {} else {
58+
quicksort_loop(&mut arr, 0, arr.len() - 1, sortfn);
59+
}
60+
arr
61+
}

0 commit comments

Comments
 (0)