Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0e79794
chore: update quicksort from iterative noir_sort version, wip debuggi…
michaeljklein Feb 11, 2025
f174f16
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Feb 13, 2025
eb04358
add regression test
michaeljklein Feb 13, 2025
e39bdce
Merge branch 'master' into michaeljklein/iterative-quicksort
TomAFrench Feb 25, 2025
ff114ec
.
TomAFrench Feb 25, 2025
71c04a4
.
TomAFrench Feb 25, 2025
1f21300
.
TomAFrench Feb 25, 2025
ab441f1
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Feb 26, 2025
6a81368
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Feb 27, 2025
b36039c
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Mar 3, 2025
0107384
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Mar 25, 2025
5d61d00
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Mar 25, 2025
fcd620c
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 9, 2025
67a150b
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 9, 2025
657d360
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 9, 2025
80b5d46
fix parser error from merging master
michaeljklein Apr 9, 2025
3c34884
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 19, 2025
313579e
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 21, 2025
ff2c2b4
cargo insta
michaeljklein Apr 21, 2025
36de989
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 21, 2025
b5f702e
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 28, 2025
d161d97
restore missing 'fn[Env]' from debugging, try to fix snap files
michaeljklein Apr 29, 2025
aea2eb4
Merge branch 'master' into michaeljklein/iterative-quicksort
michaeljklein Apr 29, 2025
b099b16
skip tests timing out
michaeljklein Apr 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 98 additions & 12 deletions noir_stdlib/src/array/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -269,20 +269,18 @@ where
// Safety: `sorted` array is checked to be:
// a. a permutation of `input`'s elements
// b. satisfying the predicate `ordering`
unsafe {
let sorted = quicksort::quicksort(self, ordering);

if !is_unconstrained() {
for i in 0..N - 1 {
assert(
ordering(sorted[i], sorted[i + 1]),
"Array has not been sorted correctly according to `ordering`.",
);
}
check_shuffle::check_shuffle(self, sorted);
let sorted = unsafe { quicksort::quicksort(self, ordering) };

if !is_unconstrained() {
for i in 0..N - 1 {
assert(
ordering(sorted[i], sorted[i + 1]),
"Array has not been sorted correctly according to `ordering`.",
);
}
sorted
check_shuffle::check_shuffle(self, sorted);
}
sorted
}
}

Expand Down Expand Up @@ -315,6 +313,94 @@ mod test {
assert_eq([].map(|x| x + 1), []);
}

global arr_with_100_values: [u32; 100] = [
42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2, 54,
89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41, 19, 98,
53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21, 43, 86, 35,
21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15, 127, 81, 30, 8,
125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,
];
global expected_with_100_values: [u32; 100] = [
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,
32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58, 61, 62,
62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82, 84, 84, 86,
86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114, 114, 116, 118,
119, 120, 121, 123, 123, 123, 125, 126, 127,
];
fn sort_u32(a: u32, b: u32) -> bool {
a <= b
}

#[test]
fn test_sort() {
let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];

let sorted = arr.sort();

let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
assert(sorted == expected);
}

#[test]
fn test_sort_100_values() {
let mut arr: [u32; 100] = [
42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2,
54, 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41,
19, 98, 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21,
43, 86, 35, 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15,
127, 81, 30, 8, 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,
];

let sorted = arr.sort();

let expected: [u32; 100] = [
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, 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58,
61, 62, 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82,
84, 84, 86, 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114,
114, 116, 118, 119, 120, 121, 123, 123, 123, 125, 126, 127,
];
assert(sorted == expected);
}

#[test]
fn test_sort_100_values_comptime() {
let sorted = arr_with_100_values.sort();
assert(sorted == expected_with_100_values);
}

#[test]
fn test_sort_via() {
let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];

let sorted = arr.sort_via(sort_u32);

let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
assert(sorted == expected);
}

#[test]
fn test_sort_via_100_values() {
let mut arr: [u32; 100] = [
42, 123, 87, 93, 48, 80, 50, 5, 104, 84, 70, 47, 119, 66, 71, 121, 3, 29, 42, 118, 2,
54, 89, 44, 81, 0, 26, 106, 68, 96, 84, 48, 95, 54, 45, 32, 89, 100, 109, 19, 37, 41,
19, 98, 53, 114, 107, 66, 6, 74, 13, 19, 105, 64, 123, 28, 44, 50, 89, 58, 123, 126, 21,
43, 86, 35, 21, 62, 82, 0, 108, 120, 72, 72, 62, 80, 12, 71, 70, 86, 116, 73, 38, 15,
127, 81, 30, 8, 125, 28, 26, 69, 114, 63, 27, 28, 61, 42, 13, 32,
];

let sorted = arr.sort_via(sort_u32);

let expected: [u32; 100] = [
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, 32, 35, 37, 38, 41, 42, 42, 42, 43, 44, 44, 45, 47, 48, 48, 50, 50, 53, 54, 54, 58,
61, 62, 62, 63, 64, 66, 66, 68, 69, 70, 70, 71, 71, 72, 72, 73, 74, 80, 80, 81, 81, 82,
84, 84, 86, 86, 87, 89, 89, 89, 93, 95, 96, 98, 100, 104, 105, 106, 107, 108, 109, 114,
114, 116, 118, 119, 120, 121, 123, 123, 123, 125, 126, 127,
];
assert(sorted == expected);
}

#[test]
fn mapi_empty() {
assert_eq([].mapi(|i, x| i * x + 1), []);
Expand Down
44 changes: 29 additions & 15 deletions noir_stdlib/src/array/quicksort.nr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
unconstrained fn partition<T, Env, let N: u32>(
unconstrained fn partition<T, let N: u32, Env>(
arr: &mut [T; N],
low: u32,
high: u32,
sortfn: fn[Env](T, T) -> bool,
sortfn: unconstrained fn[Env](T, T) -> bool,
) -> u32 {
let pivot = high;
let mut i = low;
Expand All @@ -14,34 +14,48 @@ unconstrained fn partition<T, Env, let N: u32>(
i += 1;
}
}

let temp = arr[i];
arr[i] = arr[pivot];
arr[pivot] = temp;
i
}

unconstrained fn quicksort_recursive<T, Env, let N: u32>(
unconstrained fn quicksort_loop<T, let N: u32, Env>(
arr: &mut [T; N],
low: u32,
high: u32,
sortfn: fn[Env](T, T) -> bool,
sortfn: unconstrained fn[Env](T, T) -> bool,
) {
if low < high {
let pivot_index = partition(arr, low, high, sortfn);
if pivot_index > 0 {
quicksort_recursive(arr, low, pivot_index - 1, sortfn);
let mut stack: [(u32, u32)] = &[(low, high)];
// TODO(https://github.com/noir-lang/noir_sort/issues/22): use 'loop' once it's stabilized
for _ in 0..2 * N {
if stack.len() == 0 {
break;
}

let (new_stack, (new_low, new_high)) = stack.pop_back();
stack = new_stack;

if new_high < new_low + 1 {
continue;
}

let pivot_index = partition(arr, new_low, new_high, sortfn);
stack = stack.push_back((pivot_index + 1, new_high));
if 0 < pivot_index {
stack = stack.push_back((new_low, pivot_index - 1));
}
quicksort_recursive(arr, pivot_index + 1, high, sortfn);
}
}

pub(crate) unconstrained fn quicksort<T, Env, let N: u32>(
_arr: [T; N],
sortfn: fn[Env](T, T) -> bool,
pub unconstrained fn quicksort<T, let N: u32, Env>(
arr: [T; N],
sortfn: unconstrained fn[Env](T, T) -> bool,
) -> [T; N] {
let mut arr: [T; N] = _arr;
if arr.len() <= 1 {} else {
quicksort_recursive(&mut arr, 0, arr.len() - 1, sortfn);
let mut arr: [T; N] = arr;
if arr.len() > 1 {
quicksort_loop(&mut arr, 0, arr.len() - 1, sortfn);
}
arr
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "regression_noir_sort"
type = "bin"
authors = [""]

[dependencies]
46 changes: 46 additions & 0 deletions test_programs/noir_test_success/regression_noir_sort/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
mod quicksort;

pub fn sort<T, let N: u32>(input: [T; N]) -> [T; N]
where
T: Ord,
{
sort_via(input, |a, b| a <= b)
}

pub fn sort_via<T, let N: u32>(input: [T; N], sortfn: fn(T, T) -> bool) -> [T; N] {
// Safety: test
let sorted = unsafe { quicksort::quicksort(input, sortfn) };

for i in 0..N - 1 {
assert(
sortfn(sorted[i], sorted[i + 1]),
"Array has not been sorted correctly according to `ordering`.",
);
}

sorted
}

mod test {
use crate::sort;

global arr_comptime: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];
global expected_comptime: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];

#[test]
fn test_sort() {
let mut arr: [u32; 7] = [3, 6, 8, 10, 1, 2, 1];

let sorted = sort(arr);

let expected: [u32; 7] = [1, 1, 2, 3, 6, 8, 10];
assert(sorted == expected);
}

#[test]
fn test_sort_comptime() {
let sorted = sort(arr_comptime);

assert(sorted == expected_comptime);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
unconstrained fn partition<T, let N: u32>(
arr: &mut [T; N],
low: u32,
high: u32,
sortfn: unconstrained fn(T, T) -> bool,
) -> u32 {
let pivot = high;
let mut i = low;
for j in low..high {
if (sortfn(arr[j], arr[pivot])) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i += 1;
}
}

let temp = arr[i];
arr[i] = arr[pivot];
arr[pivot] = temp;
i
}

unconstrained fn quicksort_loop<T, let N: u32>(
arr: &mut [T; N],
low: u32,
high: u32,
sortfn: unconstrained fn(T, T) -> bool,
) {
let mut stack: [(u32, u32)] = &[(low, high)];
// TODO(https://github.com/noir-lang/noir_sort/issues/22): use 'loop' once it's stabilized
for _ in 0..2 * N {
if stack.len() == 0 {
break;
}

let (new_stack, (new_low, new_high)) = stack.pop_back();
stack = new_stack;

if new_high < new_low + 1 {
continue;
}

let pivot_index = partition(arr, new_low, new_high, sortfn);
stack = stack.push_back((pivot_index + 1, new_high));
if 0 < pivot_index {
stack = stack.push_back((new_low, pivot_index - 1));
}
}
}

pub unconstrained fn quicksort<T, let N: u32>(
arr: [T; N],
sortfn: unconstrained fn(T, T) -> bool,
) -> [T; N] {
let mut arr: [T; N] = arr;
if arr.len() <= 1 {} else {
quicksort_loop(&mut arr, 0, arr.len() - 1, sortfn);
}
arr
}
2 changes: 2 additions & 0 deletions tooling/debugger/ignored-noir-tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ test_pow
test_pow_and_show
test_add
test_add_and_show
test_sort
test_sort_comptime
Loading
Loading