From 9f5af5b7b0041ead792ced6b18173b265688085f Mon Sep 17 00:00:00 2001 From: jialinli Date: Wed, 16 Jul 2025 21:54:09 -0700 Subject: [PATCH 1/7] change Field to u32 --- Nargo.toml | 2 +- src/lib.nr | 53 +++++++++++++----------------- src/mut_sparse_array.nr | 72 ++++++++++++++++++++--------------------- 3 files changed, 60 insertions(+), 67 deletions(-) diff --git a/Nargo.toml b/Nargo.toml index 55c004c..0a5fa61 100644 --- a/Nargo.toml +++ b/Nargo.toml @@ -5,4 +5,4 @@ authors = [""] compiler_version = ">=0.36.0" [dependencies] -sort = { tag = "v0.2.3", git = "https://github.com/noir-lang/noir_sort" } +sort = { tag = "v0.3.0", git = "https://github.com/noir-lang/noir_sort" } diff --git a/src/lib.nr b/src/lib.nr index bcc6df7..5cf7502 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -1,14 +1,13 @@ mod mut_sparse_array; use dep::sort::sort_advanced; -unconstrained fn __sort_field_as_u32(lhs: Field, rhs: Field) -> bool { +unconstrained fn __sort_field_as_u32(lhs: u32, rhs: u32) -> bool { // lhs.lt(rhs) - lhs as u32 < rhs as u32 + lhs < rhs } -fn assert_sorted(lhs: Field, rhs: Field) { - let result = (rhs - lhs - 1); - result.assert_max_bit_size::<32>(); +fn assert_sorted(lhs: u32, rhs: u32) { + assert(lhs < rhs); } /** @@ -24,10 +23,10 @@ fn assert_sorted(lhs: Field, rhs: Field) { **/ struct MutSparseArrayBase { values: [T; N + 3], - keys: [Field; N + 2], - linked_keys: [Field; N + 2], - tail_ptr: Field, - maximum: Field, + keys: [u32; N + 2], + linked_keys: [u32; N + 2], + tail_ptr: u32, + maximum: u32, } struct U32RangeTraits {} @@ -47,9 +46,9 @@ pub struct MutSparseArray { * 2. values[0] is an empty object. when calling `get(idx)`, if `idx` is not in `keys` we will return `values[0]` **/ pub struct SparseArray { - keys: [Field; N + 2], + keys: [u32; N + 2], values: [T; N + 3], - maximum: Field, // can be up to 2^32 + maximum: u32, // can be up to 2^32 } impl SparseArray where @@ -59,7 +58,7 @@ where /** * @brief construct a SparseArray **/ - pub(crate) fn create(_keys: [Field; N], _values: [T; N], size: Field) -> Self { + pub(crate) fn create(_keys: [u32; N], _values: [T; N], size: u32) -> Self { let _maximum = size - 1; let mut r: Self = SparseArray { keys: [0; N + 2], values: [T::default(); N + 3], maximum: _maximum }; @@ -103,9 +102,6 @@ where // because `self.keys` is sorted, we can simply validate that // sorted_keys.sorted[0] < 2^32 // sorted_keys.sorted[N-1] < maximum - sorted_keys.sorted[0].assert_max_bit_size::<32>(); - _maximum.assert_max_bit_size::<32>(); - (_maximum - sorted_keys.sorted[N - 1]).assert_max_bit_size::<32>(); r } @@ -113,32 +109,32 @@ where * @brief determine whether `target` is present in `self.keys` * @details if `found == false`, `self.keys[found_index] < target < self.keys[found_index + 1]` **/ - unconstrained fn search_for_key(self, target: Field) -> (Field, Field) { + unconstrained fn search_for_key(self, target: u32) -> (u32, u32) { let mut found = false; - let mut found_index = 0; + let mut found_index: u32 = 0; let mut previous_less_than_or_equal_to_target = false; for i in 0..N + 2 { // if target = 0xffffffff we need to be able to add 1 here, so use u64 let current_less_than_or_equal_to_target = self.keys[i] as u64 <= target as u64; if (self.keys[i] == target) { found = true; - found_index = i as Field; + found_index = i; break; } if (previous_less_than_or_equal_to_target & !current_less_than_or_equal_to_target) { - found_index = i as Field - 1; + found_index = i - 1; break; } previous_less_than_or_equal_to_target = current_less_than_or_equal_to_target; } - (found as Field, found_index) + (found as u32, found_index) } /** * @brief return element `idx` from the sparse array * @details cost is 14.5 gates per lookup **/ - fn get(self, idx: Field) -> T { + fn get(self, idx: u32) -> T { let (found, found_index) = unsafe { self.search_for_key(idx) }; // bool check. 0.25 gates cheaper than a raw `bool` type. need to fix at some point assert(found * found == found); @@ -153,10 +149,6 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[found_index]; let rhs = self.keys[found_index + 1 - found]; - let lhs_condition = idx - lhs - 1 + found; - let rhs_condition = rhs - 1 + found - idx; - lhs_condition.assert_max_bit_size::<32>(); - rhs_condition.assert_max_bit_size::<32>(); // self.keys[i] maps to self.values[i+1] // however...if we did not find a non-sparse entry, we want to return self.values[0] (the default value) @@ -179,7 +171,7 @@ mod test { for i in 0..100 { if ((i != 1) & (i != 5) & (i != 7) & (i != 99)) { - assert(example.get(i as Field) == 0); + assert(example.get(i) == 0); } } } @@ -206,7 +198,8 @@ mod test { assert(example.get(100000) == 0); } - + + /** #[test(should_fail_with = "call to assert_max_bit_size")] fn test_sparse_lookup_boundary_case_overflow() { let example = @@ -214,7 +207,7 @@ mod test { assert(example.get(0x100000000) == 0); } - + **/ #[test(should_fail_with = "call to assert_max_bit_size")] fn test_sparse_lookup_key_exceeds_maximum() { let example = @@ -236,7 +229,7 @@ mod test { for i in 0..100 { if ((i != 1) & (i != 5) & (i != 7) & (i != 99)) { - assert(example.get(i as Field) == 0); + assert(example.get(i) == 0); } } } @@ -272,7 +265,7 @@ mod test { assert(example.get(99) == values[1]); for i in 0..100 { if ((i != 1) & (i != 5) & (i != 7) & (i != 99)) { - assert(example.get(i as Field) == F::default()); + assert(example.get(i) == F::default()); } } } diff --git a/src/mut_sparse_array.nr b/src/mut_sparse_array.nr index f254260..05b4658 100644 --- a/src/mut_sparse_array.nr +++ b/src/mut_sparse_array.nr @@ -1,12 +1,11 @@ use crate::{MutSparseArray, MutSparseArrayBase, U32RangeTraits}; use dep::sort::sort_advanced; -unconstrained fn __sort_field_as_u32(lhs: Field, rhs: Field) -> bool { - lhs as u32 < rhs as u32 +unconstrained fn __sort_field_as_u32(lhs: u32, rhs: u32) -> bool { + lhs < rhs } -fn assert_sorted(lhs: Field, rhs: Field) { - let result = (rhs - lhs - 1); - result.assert_max_bit_size::<32>(); +fn assert_sorted(lhs: u32, rhs: u32) { + assert(lhs < rhs); } trait RangeTraits { @@ -62,9 +61,9 @@ where ComparisonFuncs: RangeTraits, { - pub(crate) fn create(_keys: [Field; M], _values: [T; M], size: Field) -> Self { + pub(crate) fn create(_keys: [u32; M], _values: [T; M], size: u32) -> Self { assert(M <= N); - let _maximum = size - 1; + let _maximum: u32 = size - 1; let mut r: Self = MutSparseArrayBase { keys: [0; N + 2], values: [T::default(); N + 3], @@ -86,9 +85,10 @@ where r.keys[M + 1] = _maximum; for i in 0..M + 2 { - r.linked_keys[i] = i as Field + 1; + r.linked_keys[i] = i + 1; } - r.linked_keys[M + 1] = -1; + // set the last linked key to 2^32 - 1 + r.linked_keys[M + 1] = 0xFFFFFFFF; // populate values based on the sorted keys // note: self.keys[i] maps to self.values[i+1] @@ -117,17 +117,17 @@ where // because `self.keys` is sorted, we can simply validate that // sorted_keys.sorted[0] < 2^32 // sorted_keys.sorted[N-1] < maximum - ComparisonFuncs::assert_greater_than_or_equal(sorted_keys.sorted[0], 0); - ComparisonFuncs::assert_greater_than_or_equal(_maximum, 0); - ComparisonFuncs::assert_greater_than_or_equal(_maximum, sorted_keys.sorted[M - 1]); + //ComparisonFuncs::assert_greater_than_or_equal(sorted_keys.sorted[0], 0); + //ComparisonFuncs::assert_greater_than_or_equal(_maximum, 0); + //ComparisonFuncs::assert_greater_than_or_equal(_maximum, sorted_keys.sorted[M - 1]); // _maximum.assert_max_bit_size::<32>(); // (_maximum - sorted_keys.sorted[M - 1]).assert_max_bit_size::<32>(); - r.tail_ptr = M as Field + 2; + r.tail_ptr = M + 2; r } - unconstrained fn search_for_key(self, target: Field) -> (Field, Field) { + unconstrained fn search_for_key(self, target: u32) -> (u32, u32) { let mut found = false; let mut found_index = 0; let mut previous_less_than_or_equal_to_target = false; @@ -137,11 +137,11 @@ where let current_less_than_or_equal_to_target = self.keys[iterator] as u64 <= target as u64; if (self.keys[iterator] == target) { found = true; - found_index = iterator as Field; + found_index = iterator; break; } if (previous_less_than_or_equal_to_target & !current_less_than_or_equal_to_target) { - found_index = prev as Field; + found_index = prev; break; } previous_less_than_or_equal_to_target = current_less_than_or_equal_to_target; @@ -150,17 +150,17 @@ where // } } - (found as Field, found_index) + (found as u32, found_index) } - unconstrained fn __check_if_can_insert(self, found: Field) { + unconstrained fn __check_if_can_insert(self, found: u32) { assert( - (found == 1) | (self.tail_ptr as u32 < N + 2), + (found == 1) | (self.tail_ptr < N + 2), "MutSparseArray::set exceeded maximum size of array", ); } - fn set(&mut self, idx: Field, value: T) { + fn set(&mut self, idx: u32, value: T) { let (found, found_index) = unsafe { self.search_for_key(idx) }; assert(found * found == found); @@ -183,10 +183,10 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - let lhs_condition = idx - lhs - 1 + found; - let rhs_condition = rhs - 1 + found - idx; - ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); - ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); + let lhs_condition = idx + found - lhs - 1; + let rhs_condition = rhs + found - 1 - idx; + //ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); + //ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); // lhs_condition.assert_max_bit_size::<32>(); // rhs_condition.assert_max_bit_size::<32>(); @@ -205,7 +205,7 @@ where } } - fn get(self, idx: Field) -> T { + fn get(self, idx: u32) -> T { let (found, found_index) = unsafe { self.search_for_key(idx) }; println(f"f {found} fi {found_index}"); assert(found * found == found); @@ -225,10 +225,10 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - let lhs_condition = idx - lhs - 1 + found; - let rhs_condition = rhs - 1 + found - idx; - ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); - ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); + let lhs_condition = idx + found- lhs - 1; + let rhs_condition = rhs + found - 1 - idx; + //ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); + //ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); // lhs_condition.assert_max_bit_size::<32>(); // rhs_condition.assert_max_bit_size::<32>(); @@ -241,19 +241,19 @@ impl MutSparseArray where T: std::default::Default, { - pub(crate) fn create(_keys: [Field; M], _values: [T; M], size: Field) -> Self { + pub(crate) fn create(_keys: [u32; M], _values: [T; M], size: u32) -> Self { Self { inner: MutSparseArrayBase::create(_keys, _values, size) } } - fn get(self, idx: Field) -> T { + fn get(self, idx: u32) -> T { self.inner.get(idx) } - fn set(&mut self, idx: Field, value: T) { + fn set(&mut self, idx: u32, value: T) { self.inner.set(idx, value); } - fn length(self) -> Field { + fn length(self) -> u32 { self.inner.maximum + 1 } } @@ -276,7 +276,7 @@ mod test { assert(example.get(55) == 333); for i in 0..100 { if ((i != 1) & (i != 5) & (i != 7) & (i != 55) & (i != 99)) { - assert(example.get(i as Field) == 0); + assert(example.get(i) == 0); } } } @@ -350,7 +350,7 @@ mod test { for i in 0..100 { if ((i != 1) & (i != 5) & (i != 7) & (i != 99)) { - assert(example.get(i as Field) == 0); + assert(example.get(i) == 0); } } } @@ -386,7 +386,7 @@ mod test { assert(example.get(99) == values[1]); for i in 0..100 { if ((i != 1) & (i != 5) & (i != 7) & (i != 99)) { - assert(example.get(i as Field) == F::default()); + assert(example.get(i) == F::default()); } } } From 9fe96ebc44af663fe922f355ca74ad7fd6ca0519 Mon Sep 17 00:00:00 2001 From: jialinli Date: Wed, 16 Jul 2025 22:16:08 -0700 Subject: [PATCH 2/7] fix test --- src/lib.nr | 4 +++- src/mut_sparse_array.nr | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/lib.nr b/src/lib.nr index 5cf7502..2aff2dd 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -182,7 +182,7 @@ mod test { let example = SparseArray::create( [0, 99999, 7, 0xffffffff], [123, 101112, 789, 456], - 0x100000000, + 0xFFFFFFFF, ); assert(example.get(0) == 123); @@ -192,12 +192,14 @@ mod test { assert(example.get(0xfffffffe) == 0); } + /** #[test(should_fail_with = "call to assert_max_bit_size")] fn test_sparse_lookup_overflow() { let example = SparseArray::create([1, 5, 7, 99999], [123, 456, 789, 101112], 100000); assert(example.get(100000) == 0); } + **/ /** #[test(should_fail_with = "call to assert_max_bit_size")] diff --git a/src/mut_sparse_array.nr b/src/mut_sparse_array.nr index 05b4658..271f344 100644 --- a/src/mut_sparse_array.nr +++ b/src/mut_sparse_array.nr @@ -286,7 +286,7 @@ mod test { let example: MutSparseArray<6, _> = MutSparseArray::create( [0, 99999, 7, 0xffffffff], [123, 101112, 789, 456], - 0x100000000, + 0xFFFFFFFF, ); assert(example.get(0) == 123); @@ -321,13 +321,15 @@ mod test { assert(example.get(100000) == 0); } + /** #[test(should_fail_with = "call to assert_max_bit_size")] fn test_sparse_lookup_boundary_case_overflow() { let example: MutSparseArray<4, _> = - MutSparseArray::create([0, 5, 7, 0xffffffff], [123, 456, 789, 101112], 0x100000000); + MutSparseArray::create([0, 5, 7, 0xffffffff], [123, 456, 789, 101112], 0xFFFFFFFF); - assert(example.get(0x100000000) == 0); + assert(example.get(0xFFFFFFFF) == 0); } + **/ #[test(should_fail_with = "call to assert_max_bit_size")] fn test_sparse_lookup_key_exceeds_maximum() { From 318ff8e5531daa1870b858e639c52183fa313a68 Mon Sep 17 00:00:00 2001 From: jialinli Date: Wed, 16 Jul 2025 22:48:19 -0700 Subject: [PATCH 3/7] debug --- src/lib.nr | 23 ++++++++++++++--------- src/mut_sparse_array.nr | 35 +++++++++++++---------------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/lib.nr b/src/lib.nr index 2aff2dd..f732bd5 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -102,6 +102,7 @@ where // because `self.keys` is sorted, we can simply validate that // sorted_keys.sorted[0] < 2^32 // sorted_keys.sorted[N-1] < maximum + assert(_maximum >= sorted_keys.sorted[N - 1]); r } @@ -149,6 +150,12 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[found_index]; let rhs = self.keys[found_index + 1 - found]; + //let lhs_condition = idx - lhs - 1 + found; + assert(lhs + 1 - found <= idx); + assert(idx <= rhs + found - 1); + //let rhs_condition = rhs - 1 + found - idx; + //lhs_condition.assert_max_bit_size::<32>(); + //rhs_condition.assert_max_bit_size::<32>(); // self.keys[i] maps to self.values[i+1] // however...if we did not find a non-sparse entry, we want to return self.values[0] (the default value) @@ -180,27 +187,25 @@ mod test { fn test_sparse_lookup_boundary_cases() { // what about when keys[0] = 0 and keys[N-1] = 2^32 - 1? let example = SparseArray::create( - [0, 99999, 7, 0xffffffff], + [0, 99999, 7, 0xfffffffe], [123, 101112, 789, 456], - 0xFFFFFFFF, + 0xffffffff, ); assert(example.get(0) == 123); assert(example.get(99999) == 101112); assert(example.get(7) == 789); - assert(example.get(0xffffffff) == 456); - assert(example.get(0xfffffffe) == 0); + assert(example.get(0xfffffffe) == 456); + assert(example.get(0xfffffffd) == 0); } - /** - #[test(should_fail_with = "call to assert_max_bit_size")] + #[test(should_fail)] fn test_sparse_lookup_overflow() { let example = SparseArray::create([1, 5, 7, 99999], [123, 456, 789, 101112], 100000); assert(example.get(100000) == 0); } - **/ - + /** #[test(should_fail_with = "call to assert_max_bit_size")] fn test_sparse_lookup_boundary_case_overflow() { @@ -210,7 +215,7 @@ mod test { assert(example.get(0x100000000) == 0); } **/ - #[test(should_fail_with = "call to assert_max_bit_size")] + #[test(should_fail)] fn test_sparse_lookup_key_exceeds_maximum() { let example = SparseArray::create([0, 5, 7, 0xffffffff], [123, 456, 789, 101112], 0xffffffff); diff --git a/src/mut_sparse_array.nr b/src/mut_sparse_array.nr index 271f344..07e4066 100644 --- a/src/mut_sparse_array.nr +++ b/src/mut_sparse_array.nr @@ -88,7 +88,7 @@ where r.linked_keys[i] = i + 1; } // set the last linked key to 2^32 - 1 - r.linked_keys[M + 1] = 0xFFFFFFFF; + r.linked_keys[M + 1] = 0xFFFFFFFF; // populate values based on the sorted keys // note: self.keys[i] maps to self.values[i+1] @@ -117,12 +117,9 @@ where // because `self.keys` is sorted, we can simply validate that // sorted_keys.sorted[0] < 2^32 // sorted_keys.sorted[N-1] < maximum - //ComparisonFuncs::assert_greater_than_or_equal(sorted_keys.sorted[0], 0); - //ComparisonFuncs::assert_greater_than_or_equal(_maximum, 0); - //ComparisonFuncs::assert_greater_than_or_equal(_maximum, sorted_keys.sorted[M - 1]); - // _maximum.assert_max_bit_size::<32>(); - // (_maximum - sorted_keys.sorted[M - 1]).assert_max_bit_size::<32>(); + let val = sorted_keys.sorted[M - 1]; + assert(_maximum >= sorted_keys.sorted[M - 1]); r.tail_ptr = M + 2; r } @@ -183,13 +180,10 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - let lhs_condition = idx + found - lhs - 1; - let rhs_condition = rhs + found - 1 - idx; - //ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); - //ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); - // lhs_condition.assert_max_bit_size::<32>(); - // rhs_condition.assert_max_bit_size::<32>(); + assert(lhs + 1 - found <= idx); + assert(idx <= rhs + found - 1); + // lhs points to tail_ptr // tail_ptr points to rhs if (found == 0) { @@ -207,7 +201,6 @@ where fn get(self, idx: u32) -> T { let (found, found_index) = unsafe { self.search_for_key(idx) }; - println(f"f {found} fi {found_index}"); assert(found * found == found); let lhs_index = found_index; @@ -225,7 +218,7 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - let lhs_condition = idx + found- lhs - 1; + let lhs_condition = idx + found - lhs - 1; let rhs_condition = rhs + found - 1 - idx; //ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); //ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); @@ -284,7 +277,7 @@ mod test { fn test_sparse_lookup_boundary_cases() { // what about when keys[0] = 0 and keys[N-1] = 2^32 - 1? let example: MutSparseArray<6, _> = MutSparseArray::create( - [0, 99999, 7, 0xffffffff], + [0, 99999, 7, 0xfffffffe], [123, 101112, 789, 456], 0xFFFFFFFF, ); @@ -292,8 +285,8 @@ mod test { assert(example.get(0) == 123); assert(example.get(99999) == 101112); assert(example.get(7) == 789); - assert(example.get(0xffffffff) == 456); - assert(example.get(0xfffffffe) == 0); + assert(example.get(0xfffffffe) == 456); + assert(example.get(0xfffffffd) == 0); } #[test] @@ -313,7 +306,7 @@ mod test { assert(example.get(100000) == 0); } - #[test(should_fail_with = "call to assert_max_bit_size")] + #[test(should_fail)] fn test_sparse_lookup_overflow() { let example: MutSparseArray<8, _> = MutSparseArray::create([1, 5, 7, 99999], [123, 456, 789, 101112], 100000); @@ -321,17 +314,15 @@ mod test { assert(example.get(100000) == 0); } - /** - #[test(should_fail_with = "call to assert_max_bit_size")] + #[test(should_fail)] fn test_sparse_lookup_boundary_case_overflow() { let example: MutSparseArray<4, _> = MutSparseArray::create([0, 5, 7, 0xffffffff], [123, 456, 789, 101112], 0xFFFFFFFF); assert(example.get(0xFFFFFFFF) == 0); } - **/ - #[test(should_fail_with = "call to assert_max_bit_size")] + #[test(should_fail)] fn test_sparse_lookup_key_exceeds_maximum() { let example: MutSparseArray<6, _> = MutSparseArray::create([0, 5, 7, 0xffffffff], [123, 456, 789, 101112], 0xffffffff); From c1a94c7351b9c4439420bc0cf3bcb559da07a0fc Mon Sep 17 00:00:00 2001 From: jialinli Date: Thu, 17 Jul 2025 00:41:01 -0700 Subject: [PATCH 4/7] fix --- src/lib.nr | 7 ++----- src/mut_sparse_array.nr | 12 +++--------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/lib.nr b/src/lib.nr index f732bd5..f6cd026 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -48,7 +48,7 @@ pub struct MutSparseArray { pub struct SparseArray { keys: [u32; N + 2], values: [T; N + 3], - maximum: u32, // can be up to 2^32 + maximum: u32, // can be up to 2^32 - 1 } impl SparseArray where @@ -59,6 +59,7 @@ where * @brief construct a SparseArray **/ pub(crate) fn create(_keys: [u32; N], _values: [T; N], size: u32) -> Self { + assert(size >= 1); let _maximum = size - 1; let mut r: Self = SparseArray { keys: [0; N + 2], values: [T::default(); N + 3], maximum: _maximum }; @@ -150,12 +151,8 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[found_index]; let rhs = self.keys[found_index + 1 - found]; - //let lhs_condition = idx - lhs - 1 + found; assert(lhs + 1 - found <= idx); assert(idx <= rhs + found - 1); - //let rhs_condition = rhs - 1 + found - idx; - //lhs_condition.assert_max_bit_size::<32>(); - //rhs_condition.assert_max_bit_size::<32>(); // self.keys[i] maps to self.values[i+1] // however...if we did not find a non-sparse entry, we want to return self.values[0] (the default value) diff --git a/src/mut_sparse_array.nr b/src/mut_sparse_array.nr index 07e4066..9fbad18 100644 --- a/src/mut_sparse_array.nr +++ b/src/mut_sparse_array.nr @@ -63,6 +63,7 @@ where pub(crate) fn create(_keys: [u32; M], _values: [T; M], size: u32) -> Self { assert(M <= N); + assert(size >= 1); let _maximum: u32 = size - 1; let mut r: Self = MutSparseArrayBase { keys: [0; N + 2], @@ -117,8 +118,6 @@ where // because `self.keys` is sorted, we can simply validate that // sorted_keys.sorted[0] < 2^32 // sorted_keys.sorted[N-1] < maximum - - let val = sorted_keys.sorted[M - 1]; assert(_maximum >= sorted_keys.sorted[M - 1]); r.tail_ptr = M + 2; r @@ -218,13 +217,8 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - let lhs_condition = idx + found - lhs - 1; - let rhs_condition = rhs + found - 1 - idx; - //ComparisonFuncs::assert_greater_than_or_equal(lhs_condition, 0); - //ComparisonFuncs::assert_greater_than_or_equal(rhs_condition, 0); - - // lhs_condition.assert_max_bit_size::<32>(); - // rhs_condition.assert_max_bit_size::<32>(); + assert(lhs + 1 - found <= idx); + assert(idx <= rhs + found - 1); let value_index = (lhs_index + 1) * found; self.values[value_index] } From 2e637f3715a83f9a261238aece73fd8650d9d879 Mon Sep 17 00:00:00 2001 From: jialinli Date: Thu, 17 Jul 2025 12:47:38 -0700 Subject: [PATCH 5/7] use bool for found --- src/lib.nr | 14 ++++++-------- src/mut_sparse_array.nr | 30 ++++++++++++------------------ 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/src/lib.nr b/src/lib.nr index f6cd026..de539b4 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -111,7 +111,7 @@ where * @brief determine whether `target` is present in `self.keys` * @details if `found == false`, `self.keys[found_index] < target < self.keys[found_index + 1]` **/ - unconstrained fn search_for_key(self, target: u32) -> (u32, u32) { + unconstrained fn search_for_key(self, target: u32) -> (bool, u32) { let mut found = false; let mut found_index: u32 = 0; let mut previous_less_than_or_equal_to_target = false; @@ -129,7 +129,7 @@ where } previous_less_than_or_equal_to_target = current_less_than_or_equal_to_target; } - (found as u32, found_index) + (found, found_index) } /** @@ -138,8 +138,6 @@ where **/ fn get(self, idx: u32) -> T { let (found, found_index) = unsafe { self.search_for_key(idx) }; - // bool check. 0.25 gates cheaper than a raw `bool` type. need to fix at some point - assert(found * found == found); // OK! So we have the following cases to check // 1. if `found` then `self.keys[found_index] == idx` @@ -150,13 +148,13 @@ where // combine the two into the following single statement: // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[found_index]; - let rhs = self.keys[found_index + 1 - found]; - assert(lhs + 1 - found <= idx); - assert(idx <= rhs + found - 1); + let rhs = self.keys[found_index + 1 - found as u32]; + assert(lhs + 1 - found as u32 <= idx); + assert(idx <= rhs + found as u32 - 1); // self.keys[i] maps to self.values[i+1] // however...if we did not find a non-sparse entry, we want to return self.values[0] (the default value) - let value_index = (found_index + 1) * found; + let value_index = (found_index + 1) * found as u32; self.values[value_index] } } diff --git a/src/mut_sparse_array.nr b/src/mut_sparse_array.nr index 9fbad18..ffc186e 100644 --- a/src/mut_sparse_array.nr +++ b/src/mut_sparse_array.nr @@ -123,7 +123,7 @@ where r } - unconstrained fn search_for_key(self, target: u32) -> (u32, u32) { + unconstrained fn search_for_key(self, target: u32) -> (bool, u32) { let mut found = false; let mut found_index = 0; let mut previous_less_than_or_equal_to_target = false; @@ -146,19 +146,18 @@ where // } } - (found as u32, found_index) + (found, found_index) } - unconstrained fn __check_if_can_insert(self, found: u32) { + unconstrained fn __check_if_can_insert(self, found: bool) { assert( - (found == 1) | (self.tail_ptr < N + 2), + (found == true) | (self.tail_ptr < N + 2), "MutSparseArray::set exceeded maximum size of array", ); } fn set(&mut self, idx: u32, value: T) { let (found, found_index) = unsafe { self.search_for_key(idx) }; - assert(found * found == found); // check can be unsafe because, if check fails, unsatisfiable constraints are created // due to an array overflow when accesing `self.linked_keys[self.tail_ptr]` @@ -167,8 +166,6 @@ where let lhs_index = found_index; let rhs_index = self.linked_keys[found_index]; - assert(found * found == found); - // OK! So we have the following cases to check // 1. if `found` then `self.keys[found_index] == idx` // 2. if `!found` then `self.keys[found_index] < idx < self.keys[found_index + 1] @@ -180,17 +177,17 @@ where let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - assert(lhs + 1 - found <= idx); - assert(idx <= rhs + found - 1); + assert(lhs + 1 - found as u32 <= idx); + assert(idx <= rhs + found as u32 - 1); // lhs points to tail_ptr // tail_ptr points to rhs - if (found == 0) { + if (found == false) { self.keys[self.tail_ptr] = idx; - self.linked_keys[found_index] = self.tail_ptr * (1 - found) + found * rhs_index; + self.linked_keys[found_index] = self.tail_ptr; - self.linked_keys[self.tail_ptr] = rhs_index * (1 - found); + self.linked_keys[self.tail_ptr] = rhs_index; self.values[self.tail_ptr + 1] = value; self.tail_ptr += 1; } else { @@ -200,13 +197,10 @@ where fn get(self, idx: u32) -> T { let (found, found_index) = unsafe { self.search_for_key(idx) }; - assert(found * found == found); let lhs_index = found_index; let rhs_index = self.linked_keys[found_index]; - assert(found * found == found); - // OK! So we have the following cases to check // 1. if `found` then `self.keys[found_index] == idx` // 2. if `!found` then `self.keys[found_index] < idx < self.keys[found_index + 1] @@ -217,9 +211,9 @@ where // `self.keys[found_index] + 1 - found <= idx <= self.keys[found_index + 1 - found] - 1 + found let lhs = self.keys[lhs_index]; let rhs = self.keys[rhs_index]; - assert(lhs + 1 - found <= idx); - assert(idx <= rhs + found - 1); - let value_index = (lhs_index + 1) * found; + assert(lhs + 1 - found as u32 <= idx); + assert(idx <= rhs + found as u32 - 1); + let value_index = (lhs_index + 1) * found as u32; self.values[value_index] } } From 733f78975d2d55fb22a58cc79b936d5bee926434 Mon Sep 17 00:00:00 2001 From: jialinli Date: Mon, 21 Jul 2025 09:12:19 -0700 Subject: [PATCH 6/7] rename --- src/lib.nr | 4 ++-- src/mut_sparse_array.nr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.nr b/src/lib.nr index de539b4..31a2bc1 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -1,7 +1,7 @@ mod mut_sparse_array; use dep::sort::sort_advanced; -unconstrained fn __sort_field_as_u32(lhs: u32, rhs: u32) -> bool { +unconstrained fn __sort(lhs: u32, rhs: u32) -> bool { // lhs.lt(rhs) lhs < rhs } @@ -67,7 +67,7 @@ where // for any valid index, we want to ensure the following is satified: // self.keys[X] <= index <= self.keys[X+1] // this requires us to sort hte keys, and insert a startpoint and endpoint - let sorted_keys = sort_advanced(_keys, __sort_field_as_u32, assert_sorted); + let sorted_keys = sort_advanced(_keys, __sort, assert_sorted); // insert start and endpoints r.keys[0] = 0; diff --git a/src/mut_sparse_array.nr b/src/mut_sparse_array.nr index ffc186e..241f7a7 100644 --- a/src/mut_sparse_array.nr +++ b/src/mut_sparse_array.nr @@ -1,6 +1,6 @@ use crate::{MutSparseArray, MutSparseArrayBase, U32RangeTraits}; use dep::sort::sort_advanced; -unconstrained fn __sort_field_as_u32(lhs: u32, rhs: u32) -> bool { +unconstrained fn __sort(lhs: u32, rhs: u32) -> bool { lhs < rhs } @@ -76,7 +76,7 @@ where // for any valid index, we want to ensure the following is satified: // self.keys[X] <= index <= self.keys[X+1] // this requires us to sort hte keys, and insert a startpoint and endpoint - let sorted_keys = sort_advanced(_keys, __sort_field_as_u32, assert_sorted); + let sorted_keys = sort_advanced(_keys, __sort, assert_sorted); // insert start and endpoints r.keys[0] = 0; @@ -222,8 +222,8 @@ impl MutSparseArray where T: std::default::Default, { - pub(crate) fn create(_keys: [u32; M], _values: [T; M], size: u32) -> Self { - Self { inner: MutSparseArrayBase::create(_keys, _values, size) } + pub(crate) fn create(keys: [u32; M], values: [T; M], size: u32) -> Self { + Self { inner: MutSparseArrayBase::create(keys, values, size) } } fn get(self, idx: u32) -> T { From 499a7dc44d74ea9723e11118334f8cf8e60f2188 Mon Sep 17 00:00:00 2001 From: jialinli Date: Tue, 22 Jul 2025 19:42:30 -0700 Subject: [PATCH 7/7] change minimum version of noir --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5cf0dc..6209288 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ on: env: CARGO_TERM_COLOR: always - MINIMUM_NOIR_VERSION: v0.36.0 + MINIMUM_NOIR_VERSION: v1.0.0-beta.4 jobs: noir-version-list: