Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
158 changes: 154 additions & 4 deletions noir_stdlib/src/array/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,74 @@ use crate::runtime::is_unconstrained;
mod check_shuffle;
mod quicksort;

/// Creates a new array by applying a function to each index.
/// This is a utility function to create arrays with custom initialization.
///
/// Example:
/// ```noir
/// fn main() {
/// let arr = std::array::from_fn(5, |i| i * 2);
/// assert(arr == [0, 2, 4, 6, 8]);
/// }
/// ```
pub fn from_fn<T>(size: u32, f: fn(u32) -> T) -> [T] {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function does not return an array, they return a slice. I suggest we remove it, and rename the from_fn_fixed to from_fn instead.

let mut result = [];
for i in 0..size {
result.push(f(i));
}
result
}

/// Creates a new array containing numbers from 0 to N-1.
/// This is a utility function to create arrays from ranges.
///
/// Example:
/// ```noir
/// fn main() {
/// let arr = std::array::range(5);
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn range(size: u32) -> [u32] {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: it returns a slice with a dynamic length. Should be removed in favour of renaming range_fixed to range.

from_fn(size, |i| i)
}

/// Creates a new fixed-size array by applying a function to each index.
/// This is a utility function to create arrays with custom initialization.
/// The size parameter can be less than N to partially fill the array.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The size parameter can be less than N to partially fill the array.

///
/// Example:
/// ```noir
/// fn main() {
/// let arr: [Field; 5] = std::array::from_fn_fixed(5, |i| i as Field);
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn from_fn_fixed<T, let N: u32>(size: u32, f: fn(u32) -> T) -> [T; N] {
assert(size <= N, "Size cannot exceed array capacity N");

let mut result = [crate::mem::zeroed(); N];
for i in 0..size {
result[i] = f(i);
}
Comment on lines +46 to +57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// fn main() {
/// let arr: [Field; 5] = std::array::from_fn_fixed(5, |i| i as Field);
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn from_fn_fixed<T, let N: u32>(size: u32, f: fn(u32) -> T) -> [T; N] {
assert(size <= N, "Size cannot exceed array capacity N");
let mut result = [crate::mem::zeroed(); N];
for i in 0..size {
result[i] = f(i);
}
/// fn main() {
/// let arr: [Field; 5] = std::array::from_fn(|i| i as Field);
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn from_fn<T, let N: u32>(f: fn(u32) -> T) -> [T; N] {
let mut result = [crate::mem::zeroed(); N];
for i in 0..N {
result[i] = f(i);
}

It's simpler this way, I don't think the partial fill was a good suggestion on my part, sorry.

result
}

/// Creates a new fixed-size array containing numbers from 0 to size-1.
/// This is a utility function to create arrays from ranges.
/// The size parameter can be less than N to partially fill the array.
///
/// Example:
/// ```noir
/// fn main() {
/// let arr: [u32; 5] = std::array::range_fixed(5);
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn range_fixed<let N: u32>(size: u32) -> [u32; N] {
from_fn_fixed(size, |i| i)
}
Comment on lines +63 to +74
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The size parameter can be less than N to partially fill the array.
///
/// Example:
/// ```noir
/// fn main() {
/// let arr: [u32; 5] = std::array::range_fixed(5);
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn range_fixed<let N: u32>(size: u32) -> [u32; N] {
from_fn_fixed(size, |i| i)
}
///
/// Example:
/// ```noir
/// fn main() {
/// let arr: [u32; 5] = std::array::range();
/// assert(arr == [0, 1, 2, 3, 4]);
/// }
/// ```
pub fn range<let N: u32>() -> [u32; N] {
from_fn(|i| i)
}


impl<T, let N: u32> [T; N] {
/// Returns the length of this array.
///
Expand Down Expand Up @@ -136,6 +204,28 @@ impl<T, let N: u32> [T; N] {
}
ret
}

/// Returns a new array with elements in reverse order.
/// The original array remains untouched.
///
/// Example:
/// ```noir
/// fn main() {
/// let arr = [1, 2, 3];
/// let reversed = arr.reverse();
/// assert(reversed == [3, 2, 1]);
/// }
/// ```
pub fn reverse(self) -> Self {
let mut result = self;
let len = self.len();
for i in 0..(len / 2) {
let temp = result[i];
result[i] = result[len - 1 - i];
result[len - 1 - i] = temp;
}
result
}
}

impl<T, let N: u32> [T; N]
Expand All @@ -157,7 +247,7 @@ where
/// }
/// ```
pub fn sort(self) -> Self {
self.sort_via(|a, b| a <= b)
self.sort_via(|a: T, b: T| a <= b)
}
}

Expand All @@ -184,10 +274,11 @@ where
/// }
/// ```
pub fn sort_via<Env>(self, ordering: fn[Env](T, T) -> bool) -> Self {
/// Safety: `sorted` array is checked to be:
/// a. a permutation of `input`'s elements
/// b. satisfying the predicate `ordering`
unsafe {
/*@safety: `sorted` array is checked to be:
a. a permutation of `input`'s elements
b. satisfying the predicate `ordering`
*/
let sorted = quicksort::quicksort(self, ordering);

if !is_unconstrained() {
Expand Down Expand Up @@ -232,4 +323,63 @@ mod test {
fn map_empty() {
assert_eq([].map(|x| x + 1), []);
}

#[test]
fn test_range() {
let arr = range(5);
assert_eq(arr, [0_u32, 1_u32, 2_u32, 3_u32, 4_u32]);

let empty = range(0);
assert_eq(empty, []);
}

#[test]
fn test_from_fn() {
let arr = from_fn(5, |i| i * 2);
assert_eq(arr, [0_u32, 2_u32, 4_u32, 6_u32, 8_u32]);

let field_arr = from_fn(3, |i| i as Field);
assert_eq(field_arr, [0_Field, 1_Field, 2_Field]);
}

#[test]
fn test_range_fixed() {
let arr: [u32; 5] = range_fixed(5);
assert_eq(arr, [0_u32, 1_u32, 2_u32, 3_u32, 4_u32]);

// Partial filling test
let arr2: [u32; 5] = range_fixed(3);
assert_eq(arr2[0], 0_u32);
assert_eq(arr2[1], 1_u32);
assert_eq(arr2[2], 2_u32);
}

#[test]
fn test_from_fn_fixed() {
let arr: [u32; 5] = from_fn_fixed(5, |i| i * 2);
assert_eq(arr, [0_u32, 2_u32, 4_u32, 6_u32, 8_u32]);

// Test with Field type
let field_arr: [Field; 3] = from_fn_fixed(3, |i| i as Field);
assert_eq(field_arr, [0_Field, 1_Field, 2_Field]);

// Partial filling test
let arr2: [u32; 5] = from_fn_fixed(3, |i| i * 3);
assert_eq(arr2[0], 0_u32);
assert_eq(arr2[1], 3_u32);
assert_eq(arr2[2], 6_u32);
}

#[test]
fn test_reverse() {
let arr = [1, 2, 3, 4, 5];
let reversed = arr.reverse();
assert_eq(reversed, [5, 4, 3, 2, 1]);

let empty: [Field; 0] = [];
assert_eq(empty.reverse(), []);

let single = [1];
assert_eq(single.reverse(), [1]);
}
}
173 changes: 173 additions & 0 deletions noir_stdlib/src/slice.nr
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,34 @@ impl<T> [T] {
ret
}

// Apply a function to each element of the slice with its index, returning a
// new slice containing the mapped elements.
pub fn mapi<U, Env>(self, f: fn[Env](u32, T) -> U) -> [U] {
let mut ret = &[];
let mut index = 0;
for elem in self {
ret = ret.push_back(f(index, elem));
index += 1;
}
ret
}

// Apply a function to each element of the slice
pub fn for_each<Env>(self, f: fn[Env](T) -> ()) {
for elem in self {
f(elem);
}
}

// Apply a function to each element of the slice with its index
pub fn for_eachi<Env>(self, f: fn[Env](u32, T) -> ()) {
let mut index = 0;
for elem in self {
f(index, elem);
index += 1;
}
}

// Apply a function to each element of the slice and an accumulator value,
// returning the final accumulated value. This function is also sometimes
// called `foldl`, `fold_left`, `reduce`, or `inject`.
Expand Down Expand Up @@ -135,4 +163,149 @@ impl<T> [T] {
}
ret
}

/// Returns a new slice with elements in reverse order.
/// The original slice remains untouched.
///
/// Example:
/// ```noir
/// fn main() {
/// let slice = &[1, 2, 3];
/// let reversed = slice.reverse();
/// assert(reversed == &[3, 2, 1]);
/// }
/// ```
pub fn reverse(self) -> Self {
let mut result = &[];
let len = self.len();

for i in 0..len {
result = result.push_front(self[len - 1 - i]);
}

result
}
}

/// Creates a new slice containing numbers from 0 to size-1.
/// This is a utility function to create slices from ranges.
///
/// Example:
/// ```noir
/// fn main() {
/// let slice = std::slice::range(5);
/// assert(slice == &[0, 1, 2, 3, 4]);
/// }
/// ```
pub fn range(size: u32) -> [u32] {
let mut result = &[];
for i in 0..size {
result = result.push_back(i);
}
result
}

/// Creates a new slice by applying a function to each index.
/// This is a utility function to create slices with custom initialization.
///
/// Example:
/// ```noir
/// fn main() {
/// let slice = std::slice::from_fn(5, |i| i * 2);
/// assert(slice == &[0, 2, 4, 6, 8]);
/// }
/// ```
pub fn from_fn<T>(size: u32, f: fn(u32) -> T) -> [T] {
let mut result = &[];
for i in 0..size {
result = result.push_back(f(i));
}
result
}

mod test {
#[test]
fn map_empty() {
assert_eq(&[].map(|x| x + 1), &[]);
}

#[test]
fn mapi_empty() {
assert_eq(&[].mapi(|i, x| i * x + 1), &[]);
}

#[test]
fn for_each_empty() {
let empty_slice: [Field] = &[];
empty_slice.for_each(|_x| assert(false));
}

#[test]
fn for_eachi_empty() {
let empty_slice: [Field] = &[];
empty_slice.for_eachi(|_i, _x| assert(false));
}

#[test]
fn map_example() {
let a = &[1, 2, 3];
let b = a.map(|a| a * 2);
assert_eq(b, &[2, 4, 6]);
}

#[test]
fn mapi_example() {
let a = &[1, 2, 3];
let b = a.mapi(|i, a| i * a + 1);
assert_eq(b, &[1, 3, 7]);
}

#[test]
fn for_each_example() {
let a = &[1, 2, 3];
let mut b = &[];
let b_ref = &mut b;
a.for_each(|a| { *b_ref = b_ref.push_back(a * 2); });
assert_eq(b, &[2, 4, 6]);
}

#[test]
fn for_eachi_example() {
let a = &[1, 2, 3];
let mut b = &[];
let b_ref = &mut b;
a.for_eachi(|i, a| { *b_ref = b_ref.push_back(i + a * 2); });
assert_eq(b, &[2, 5, 8]);
}

#[test]
fn test_slice_range() {
let slice = range(5);
assert_eq(slice, &[0_u32, 1_u32, 2_u32, 3_u32, 4_u32]);

let empty = range(0);
assert_eq(empty, &[]);
}

#[test]
fn test_slice_from_fn() {
let slice = from_fn(5, |i| i * 2);
assert_eq(slice, &[0_u32, 2_u32, 4_u32, 6_u32, 8_u32]);

let field_slice = from_fn(3, |i| i as Field);
assert_eq(field_slice, &[0_Field, 1_Field, 2_Field]);
}

#[test]
fn test_slice_reverse() {
let slice = &[1, 2, 3, 4, 5];
let reversed = slice.reverse();
assert_eq(reversed, &[5, 4, 3, 2, 1]);

let empty_slice: [Field] = &[];
assert_eq(empty_slice.reverse(), &[]);

let single = &[1];
assert_eq(single.reverse(), &[1]);
}
}