From 650ffc5053cdca4b6ad2e027fa1f4fd90ef64871 Mon Sep 17 00:00:00 2001 From: Nikita Masych <92444221+NikitaMasych@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:55:17 +0200 Subject: [PATCH 001/416] feat: Add HashMap to the stdlib (#4242) # Description This PR shall bring HashMap into the `stdlib` of Noir. ## Problem\* Resolves #4241 ## Summary\* Implementation of `HashMap` with open addressing and quadratic probing scheme. Since Noir requires knowing loop bounds (and recursive calls) at compile time, `HashMap` is of fixed capacity and **no** dynamic resize is accomplished with regard to load factor. Furthermore, contribution includes implementation of `PedersenHasher` to be used for now. One can examine potentially better and less heavy prehash functions. I tried to conform with best practices of engineering, however since Noir is in rapid development, there are certain things which may be optimized in future, both from the code style and performance point of view. ## Additional Context I put the `PedersenHasher` among the `poseidon.nr` and `mimc.nr`, so one can consider moving declaration of other pedersen-related functionality there, however that would be a breaking change. ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/collections.nr | 1 + noir_stdlib/src/collections/map.nr | 456 ++++++++++++++++++ noir_stdlib/src/hash.nr | 52 ++ noir_stdlib/src/hash/pedersen.nr | 24 + .../hashmap_load_factor/Nargo.toml | 6 + .../hashmap_load_factor/Prover.toml | 26 + .../hashmap_load_factor/src/main.nr | 35 ++ .../execution_success/hashmap/Nargo.toml | 6 + .../execution_success/hashmap/Prover.toml | 26 + .../execution_success/hashmap/src/main.nr | 192 ++++++++ .../execution_success/hashmap/src/utils.nr | 10 + 11 files changed, 834 insertions(+) create mode 100644 noir_stdlib/src/collections/map.nr create mode 100644 noir_stdlib/src/hash/pedersen.nr create mode 100644 test_programs/compile_failure/hashmap_load_factor/Nargo.toml create mode 100644 test_programs/compile_failure/hashmap_load_factor/Prover.toml create mode 100644 test_programs/compile_failure/hashmap_load_factor/src/main.nr create mode 100644 test_programs/execution_success/hashmap/Nargo.toml create mode 100644 test_programs/execution_success/hashmap/Prover.toml create mode 100644 test_programs/execution_success/hashmap/src/main.nr create mode 100644 test_programs/execution_success/hashmap/src/utils.nr diff --git a/noir_stdlib/src/collections.nr b/noir_stdlib/src/collections.nr index 177ca96816f..2d952f4d6cd 100644 --- a/noir_stdlib/src/collections.nr +++ b/noir_stdlib/src/collections.nr @@ -1,2 +1,3 @@ mod vec; mod bounded_vec; +mod map; diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr new file mode 100644 index 00000000000..d9eb83ff5dc --- /dev/null +++ b/noir_stdlib/src/collections/map.nr @@ -0,0 +1,456 @@ +use crate::cmp::Eq; +use crate::collections::vec::Vec; +use crate::option::Option; +use crate::default::Default; +use crate::hash::{Hash, Hasher, BuildHasher}; + +// We use load factor α_max = 0.75. +// Upon exceeding it, assert will fail in order to inform the user +// about performance degradation, so that he can adjust the capacity. +global MAX_LOAD_FACTOR_NUMERATOR = 3; +global MAX_LOAD_FACTOR_DEN0MINATOR = 4; + +// Hash table with open addressing and quadratic probing. +// Size of the underlying table must be known at compile time. +// It is advised to select capacity N as a power of two, or a prime number +// because utilized probing scheme is best tailored for it. +struct HashMap { + _table: [Slot; N], + + // Amount of valid elements in the map. + _len: u64, + + _build_hasher: B +} + +// Data unit in the HashMap table. +// In case Noir adds support for enums in the future, this +// should be refactored to have three states: +// 1. (key, value) +// 2. (empty) +// 3. (deleted) +struct Slot { + _key_value: Option<(K, V)>, + _is_deleted: bool, +} + +impl Default for Slot{ + fn default() -> Self{ + Slot{ + _key_value: Option::none(), + _is_deleted: false + } + } +} + +impl Slot { + fn is_valid(self) -> bool { + !self._is_deleted & self._key_value.is_some() + } + + fn is_available(self) -> bool { + self._is_deleted | self._key_value.is_none() + } + + fn key_value(self) -> Option<(K, V)> { + self._key_value + } + + fn key_value_unchecked(self) -> (K, V) { + self._key_value.unwrap_unchecked() + } + + fn set(&mut self, key: K, value: V) { + self._key_value = Option::some((key, value)); + self._is_deleted = false; + } + + // Shall not override `_key_value` with Option::none(), + // because we must be able to differentiate empty + // and deleted slots for lookup. + fn mark_deleted(&mut self) { + self._is_deleted = true; + } +} + +// While conducting lookup, we iterate attempt from 0 to N - 1 due to heuristic, +// that if we have went that far without finding desired, +// it is very unlikely to be after - performance will be heavily degraded. +impl HashMap { + // Creates a new instance of HashMap with specified BuildHasher. + pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher { + let _table = [Slot::default(); N]; + let _len = 0; + Self { _table, _len, _build_hasher } + } + + // Clears the map, removing all key-value entries. + pub fn clear(&mut self) { + self._table = [Slot::default(); N]; + self._len = 0; + } + + // Returns true if the map contains a value for the specified key. + pub fn contains_key( + self, + key: K + ) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher { + self.get(key).is_some() + } + + // Returns true if the map contains no elements. + pub fn is_empty(self) -> bool { + self._len == 0 + } + + // Get the Option<(K, V) array of valid entries + // with a length of map capacity. First len() elements + // are safe to unwrap_unchecked(), whilst remaining + // are guaranteed to be Option::none(). + // + // This design is reasoned by compile-time limitations and + // temporary nested slices ban. + pub fn entries(self) -> [Option<(K, V)>; N] { + let mut entries = [Option::none(); N]; + let mut valid_amount = 0; + + for slot in self._table { + if slot.is_valid() { + entries[valid_amount] = slot.key_value(); + valid_amount += 1; + } + } + + let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}."; + assert(valid_amount == self._len, msg); + + entries + } + + // Get the Option array of valid keys + // with a length of map capacity. First len() elements + // are safe to unwrap_unchecked(), whilst remaining + // are guaranteed to be Option::none(). + // + // This design is reasoned by compile-time limitations and + // temporary nested slices ban. + pub fn keys(self) -> [Option; N] { + let mut keys = [Option::none(); N]; + let mut valid_amount = 0; + + for slot in self._table { + if slot.is_valid() { + let (key, _) = slot.key_value_unchecked(); + keys[valid_amount] = Option::some(key); + valid_amount += 1; + } + } + + let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}."; + assert(valid_amount == self._len, msg); + + keys + } + + // Get the Option array of valid values + // with a length of map capacity. First len() elements + // are safe to unwrap_unchecked(), whilst remaining + // are guaranteed to be Option::none(). + // + // This design is reasoned by compile-time limitations and + // temporary nested slices ban. + pub fn values(self) -> [Option; N] { + let mut values = [Option::none(); N]; + let mut valid_amount = 0; + + for slot in self._table { + if slot.is_valid() { + let (_, value) = slot.key_value_unchecked(); + values[valid_amount] = Option::some(value); + valid_amount += 1; + } + } + + let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}."; + assert(valid_amount == self._len, msg); + + values + } + + // For each key-value entry applies mutator function. + pub fn iter_mut( + &mut self, + f: fn(K, V) -> (K, V) + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { + let mut entries = self.entries(); + let mut new_map = HashMap::with_hasher(self._build_hasher); + + for i in 0..N { + if i < self._len { + let entry = entries[i].unwrap_unchecked(); + let (key, value) = f(entry.0, entry.1); + new_map.insert(key, value); + } + } + + self._table = new_map._table; + } + + // For each key applies mutator function. + pub fn iter_keys_mut( + &mut self, + f: fn(K) -> K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { + let mut entries = self.entries(); + let mut new_map = HashMap::with_hasher(self._build_hasher); + + for i in 0..N { + if i < self._len { + let entry = entries[i].unwrap_unchecked(); + let (key, value) = (f(entry.0), entry.1); + new_map.insert(key, value); + } + } + + self._table = new_map._table; + } + + // For each value applies mutator function. + pub fn iter_values_mut(&mut self, f: fn(V) -> V) { + for i in 0..N { + let mut slot = self._table[i]; + if slot.is_valid() { + let (key, value) = slot.key_value_unchecked(); + slot.set(key, f(value)); + self._table[i] = slot; + } + } + } + + // Retains only the elements specified by the predicate. + pub fn retain(&mut self, f: fn(K, V) -> bool) { + for index in 0..N { + let mut slot = self._table[index]; + if slot.is_valid() { + let (key, value) = slot.key_value_unchecked(); + if !f(key, value) { + slot.mark_deleted(); + self._len -= 1; + self._table[index] = slot; + } + } + } + } + + // Amount of active key-value entries. + pub fn len(self) -> u64 { + self._len + } + + // Get the compile-time map capacity. + pub fn capacity(_self: Self) -> u64 { + N + } + + // Get the value by key. If it does not exist, returns none(). + pub fn get( + self, + key: K + ) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { + let mut result = Option::none(); + + let hash = self.hash(key); + let mut break = false; + + for attempt in 0..N { + if !break { + let index = self.quadratic_probe(hash, attempt as u64); + let slot = self._table[index]; + + // Not marked as deleted and has key-value. + if slot.is_valid() { + let (current_key, value) = slot.key_value_unchecked(); + if current_key == key { + result = Option::some(value); + break = true; + } + } + } + } + + result + } + + // Insert key-value entry. In case key was already present, value is overridden. + pub fn insert( + &mut self, + key: K, + value: V + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { + self.assert_load_factor(); + + let hash = self.hash(key); + let mut break = false; + + for attempt in 0..N { + if !break { + let index = self.quadratic_probe(hash, attempt as u64); + let mut slot = self._table[index]; + let mut insert = false; + + // Either marked as deleted or has unset key-value. + if slot.is_available() { + insert = true; + self._len += 1; + } else { + let (current_key, _) = slot.key_value_unchecked(); + if current_key == key { + insert = true; + } + } + + if insert { + slot.set(key, value); + self._table[index] = slot; + break = true; + } + } + } + } + + // Remove key-value entry. If key is not present, HashMap remains unchanged. + pub fn remove( + &mut self, + key: K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { + let hash = self.hash(key); + let mut break = false; + + for attempt in 0..N { + if !break { + let index = self.quadratic_probe(hash, attempt as u64); + let mut slot = self._table[index]; + + // Not marked as deleted and has key-value. + if slot.is_valid() { + let (current_key, _) = slot.key_value_unchecked(); + if current_key == key { + slot.mark_deleted(); + self._table[index] = slot; + self._len -= 1; + break = true; + } + } + } + } + } + + // Apply HashMap's hasher onto key to obtain pre-hash for probing. + fn hash( + self, + key: K + ) -> u64 + where + K: Hash, + B: BuildHasher, + H: Hasher { + let mut hasher = self._build_hasher.build_hasher(); + key.hash(&mut hasher); + hasher.finish() as u64 + } + + // Probing scheme: quadratic function. + // We use 0.5 constant near variadic attempt and attempt^2 monomials. + // This ensures good uniformity of distribution for table sizes + // equal to prime numbers or powers of two. + fn quadratic_probe(_self: Self, hash: u64, attempt: u64) -> u64 { + (hash + (attempt + attempt * attempt) / 2) % N + } + + // Amount of elements in the table in relation to available slots exceeds α_max. + // To avoid a comparatively more expensive division operation + // we conduct cross-multiplication instead. + // n / m >= MAX_LOAD_FACTOR_NUMERATOR / MAX_LOAD_FACTOR_DEN0MINATOR + // n * MAX_LOAD_FACTOR_DEN0MINATOR >= m * MAX_LOAD_FACTOR_NUMERATOR + fn assert_load_factor(self) { + let lhs = self._len * MAX_LOAD_FACTOR_DEN0MINATOR; + let rhs = self._table.len() as u64 * MAX_LOAD_FACTOR_NUMERATOR; + let exceeded = lhs >= rhs; + assert(!exceeded, "Load factor is exceeded, consider increasing the capacity."); + } +} + +// Equality class on HashMap has to test that they have +// equal sets of key-value entries, +// thus one is a subset of the other and vice versa. +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher +{ + fn eq(self, other: HashMap) -> bool{ + let mut equal = false; + + if self.len() == other.len(){ + equal = true; + for slot in self._table{ + // Not marked as deleted and has key-value. + if equal & slot.is_valid(){ + let (key, value) = slot.key_value_unchecked(); + let other_value = other.get(key); + + if other_value.is_none(){ + equal = false; + }else{ + let other_value = other_value.unwrap_unchecked(); + if value != other_value{ + equal = false; + } + } + } + } + } + + equal + } +} + +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self{ + let _build_hasher = B::default(); + let map: HashMap = HashMap::with_hasher(_build_hasher); + map + } +} diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index cc864039a90..7a931f7c047 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -1,5 +1,8 @@ mod poseidon; mod mimc; +mod pedersen; + +use crate::default::Default; #[foreign(sha256)] // docs:start:sha256 @@ -74,3 +77,52 @@ pub fn poseidon2_permutation(_input: [u8; N], _state_length: u32) -> [u8; N] #[foreign(sha256_compression)] pub fn sha256_compression(_input: [u32; 16], _state: [u32; 8]) -> [u32; 8] {} + +// Generic hashing support. +// Partially ported and impacted by rust. + +// Hash trait shall be implemented per type. +trait Hash{ + fn hash(self, state: &mut H) where H: Hasher; +} + +// Hasher trait shall be implemented by algorithms to provide hash-agnostic means. +// TODO: consider making the types generic here ([u8], [Field], etc.) +trait Hasher{ + fn finish(self) -> Field; + + fn write(&mut self, input: [Field]); +} + +// BuildHasher is a factory trait, responsible for production of specific Hasher. +trait BuildHasher where H: Hasher{ + fn build_hasher(self) -> H; +} + +struct BuildHasherDefault; + +impl BuildHasher for BuildHasherDefault +where + H: Hasher + Default +{ + fn build_hasher(_self: Self) -> H{ + H::default() + } +} + +impl Default for BuildHasherDefault +where + H: Hasher + Default +{ + fn default() -> Self{ + BuildHasherDefault{} + } +} + +// TODO: add implementations for the remainder of primitive types. +impl Hash for Field{ + fn hash(self, state: &mut H) where H: Hasher{ + let input: [Field] = [self]; + H::write(state, input); + } +} diff --git a/noir_stdlib/src/hash/pedersen.nr b/noir_stdlib/src/hash/pedersen.nr new file mode 100644 index 00000000000..ace6851099d --- /dev/null +++ b/noir_stdlib/src/hash/pedersen.nr @@ -0,0 +1,24 @@ +use crate::hash::{Hasher, pedersen_hash}; +use crate::default::Default; + +struct PedersenHasher{ + _state: [Field] +} + +impl Hasher for PedersenHasher { + fn finish(self) -> Field { + pedersen_hash(self._state) + } + + fn write(&mut self, input: [Field]){ + self._state = self._state.append(input); + } +} + +impl Default for PedersenHasher{ + fn default() -> Self{ + PedersenHasher{ + _state: [] + } + } +} diff --git a/test_programs/compile_failure/hashmap_load_factor/Nargo.toml b/test_programs/compile_failure/hashmap_load_factor/Nargo.toml new file mode 100644 index 00000000000..92da5a357f4 --- /dev/null +++ b/test_programs/compile_failure/hashmap_load_factor/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "hashmap_load_factor" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/hashmap_load_factor/Prover.toml b/test_programs/compile_failure/hashmap_load_factor/Prover.toml new file mode 100644 index 00000000000..e54319c61e9 --- /dev/null +++ b/test_programs/compile_failure/hashmap_load_factor/Prover.toml @@ -0,0 +1,26 @@ +# Expected 6 key-value entries for hashmap capacity of 8. +# These must be distinct (both key-to-key, and value-to-value) for correct testing. + +[[input]] +key = 2 +value = 17 + +[[input]] +key = 3 +value = 19 + +[[input]] +key = 5 +value = 23 + +[[input]] +key = 7 +value = 29 + +[[input]] +key = 11 +value = 31 + +[[input]] +key = 41 +value = 43 \ No newline at end of file diff --git a/test_programs/compile_failure/hashmap_load_factor/src/main.nr b/test_programs/compile_failure/hashmap_load_factor/src/main.nr new file mode 100644 index 00000000000..ade43f898e1 --- /dev/null +++ b/test_programs/compile_failure/hashmap_load_factor/src/main.nr @@ -0,0 +1,35 @@ +use dep::std::collections::map::HashMap; +use dep::std::hash::BuildHasherDefault; +use dep::std::hash::pedersen::PedersenHasher; + +struct Entry{ + key: Field, + value: Field +} + +global HASHMAP_CAP = 8; +global HASHMAP_LEN = 6; + +fn allocate_hashmap() -> HashMap> { + HashMap::default() +} + +fn main(input: [Entry; HASHMAP_LEN]) { + test_load_factor(input); +} + +// In this test we exceed load factor: +// α_max = 0.75, thus for capacity of 8 and lenght of 6 +// insertion of new unique key (7-th) should throw assertion error. +fn test_load_factor(input: [Entry; HASHMAP_LEN]) { + let mut hashmap = allocate_hashmap(); + + for entry in input { + hashmap.insert(entry.key, entry.value); + } + + // We use prime numbers for testing, + // therefore it is guaranteed that doubling key we get unique value. + let key = input[0].key * 2; + hashmap.insert(key, input[0].value); +} diff --git a/test_programs/execution_success/hashmap/Nargo.toml b/test_programs/execution_success/hashmap/Nargo.toml new file mode 100644 index 00000000000..c09debc9833 --- /dev/null +++ b/test_programs/execution_success/hashmap/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "hashmap" +type = "bin" +authors = [""] + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/hashmap/Prover.toml b/test_programs/execution_success/hashmap/Prover.toml new file mode 100644 index 00000000000..84d4c0733e4 --- /dev/null +++ b/test_programs/execution_success/hashmap/Prover.toml @@ -0,0 +1,26 @@ +# Input: 6 key-value entries for hashmap capacity of 8. +# These must be distinct (both key-to-key, and value-to-value) for correct testing. + +[[input]] +key = 2 +value = 17 + +[[input]] +key = 3 +value = 19 + +[[input]] +key = 5 +value = 23 + +[[input]] +key = 7 +value = 29 + +[[input]] +key = 11 +value = 31 + +[[input]] +key = 41 +value = 43 \ No newline at end of file diff --git a/test_programs/execution_success/hashmap/src/main.nr b/test_programs/execution_success/hashmap/src/main.nr new file mode 100644 index 00000000000..597a5c0b7de --- /dev/null +++ b/test_programs/execution_success/hashmap/src/main.nr @@ -0,0 +1,192 @@ +mod utils; + +use dep::std::collections::map::HashMap; +use dep::std::hash::BuildHasherDefault; +use dep::std::hash::pedersen::PedersenHasher; +use dep::std::cmp::Eq; + +use utils::cut; + +type K = Field; +type V = Field; + +// It is more convenient and readable to use structs as input. +struct Entry{ + key: Field, + value: Field +} + +global HASHMAP_CAP = 8; +global HASHMAP_LEN = 6; + +global FIELD_CMP = |a: Field, b: Field| a.lt(b); + +global K_CMP = FIELD_CMP; +global V_CMP = FIELD_CMP; +global KV_CMP = |a: (K, V), b: (K, V)| a.0.lt(b.0); + +global ALLOCATE_HASHMAP = || -> HashMap> + HashMap::default(); + +fn main(input: [Entry; HASHMAP_LEN]) { + test_sequential(input[0].key, input[0].value); + test_multiple_equal_insert(input[1].key, input[1].value); + test_value_override(input[2].key, input[2].value, input[3].value); + test_insert_and_methods(input); + test_hashmaps_equality(input); + test_retain(); + test_iterators(); + test_mut_iterators(); +} + +// Insert, get, remove. +fn test_sequential(key: K, value: V) { + let mut hashmap = ALLOCATE_HASHMAP(); + assert(hashmap.is_empty(), "New HashMap should be empty."); + + hashmap.insert(key, value); + assert(hashmap.len() == 1, "HashMap after one insert should have a length of 1 element."); + + let got = hashmap.get(key); + assert(got.is_some(), "Got none value."); + let got = got.unwrap_unchecked(); + assert(value == got, f"Inserted {value} but got {got} for the same key."); + + hashmap.remove(key); + assert(hashmap.is_empty(), "HashMap after one insert and corresponding removal should be empty."); + let got = hashmap.get(key); + assert(got.is_none(), "Value has been removed, but is still available (not none)."); +} + +// Insert same pair several times. +fn test_multiple_equal_insert(key: K, value: V) { + let mut hashmap = ALLOCATE_HASHMAP(); + assert(hashmap.is_empty(), "New HashMap should be empty."); + + for _ in 0..HASHMAP_LEN { + hashmap.insert(key, value); + } + + let len = hashmap.len(); + assert(len == 1, f"HashMap length must be 1, got {len}."); + + let got = hashmap.get(key); + assert(got.is_some(), "Got none value."); + let got = got.unwrap_unchecked(); + assert(value == got, f"Inserted {value} but got {got} for the same key."); +} + +// Override value for existing pair. +fn test_value_override(key: K, value: V, new_value: V) { + let mut hashmap = ALLOCATE_HASHMAP(); + assert(hashmap.is_empty(), "New hashmap should be empty."); + + hashmap.insert(key, value); + hashmap.insert(key, new_value); + assert(hashmap.len() == 1, "HashMap length is invalid."); + + let got = hashmap.get(key); + assert(got.is_some(), "Got none value."); + let got = got.unwrap_unchecked(); + assert(got == new_value, f"Expected {new_value}, but got {got}."); +} + +// Insert several distinct pairs and test auxiliary methods. +fn test_insert_and_methods(input: [Entry; HASHMAP_LEN]) { + let mut hashmap = ALLOCATE_HASHMAP(); + assert(hashmap.is_empty(), "New HashMap should be empty."); + + for entry in input { + hashmap.insert(entry.key, entry.value); + } + + assert(hashmap.len() == HASHMAP_LEN, "hashmap.len() does not match input lenght."); + + for entry in input { + assert(hashmap.contains_key(entry.key), f"Not found inserted key {entry.key}."); + } + + hashmap.clear(); + assert(hashmap.is_empty(), "HashMap after clear() should be empty."); +} + +// Insert several pairs and test retaining. +fn test_retain() { + let mut hashmap = ALLOCATE_HASHMAP(); + assert(hashmap.is_empty(), "New HashMap should be empty."); + + let (key, value) = (5, 11); + hashmap.insert(key, value); + let (key, value) = (2, 13); + hashmap.insert(key, value); + let (key, value) = (11, 5); + hashmap.insert(key, value); + + let predicate = |key: K, value: V| -> bool {key * value == 55}; + hashmap.retain(predicate); + + assert(hashmap.len() == 2, "HashMap should have retained 2 elements."); + assert(hashmap.get(2).is_none(), "Pair should have been removed, since it does not match predicate."); +} + +// Equality trait check. +fn test_hashmaps_equality(input: [Entry; HASHMAP_LEN]) { + let mut hashmap_1 = ALLOCATE_HASHMAP(); + let mut hashmap_2 = ALLOCATE_HASHMAP(); + + for entry in input { + hashmap_1.insert(entry.key, entry.value); + hashmap_2.insert(entry.key, entry.value); + } + + assert(hashmap_1 == hashmap_2, "HashMaps should be equal."); + + hashmap_2.remove(input[0].key); + + assert(hashmap_1 != hashmap_2, "HashMaps should not be equal."); +} + +// Test entries, keys, values. +fn test_iterators() { + let mut hashmap = ALLOCATE_HASHMAP(); + + hashmap.insert(2, 3); + hashmap.insert(5, 7); + hashmap.insert(11, 13); + + let keys: [K; 3] = cut(hashmap.keys()).map(|k: Option| k.unwrap_unchecked()).sort_via(K_CMP); + let values: [V; 3] = cut(hashmap.values()).map(|v: Option| v.unwrap_unchecked()).sort_via(V_CMP); + let entries: [(K, V); 3] = cut(hashmap.entries()).map(|e: Option<(K, V)>| e.unwrap_unchecked()).sort_via(KV_CMP); + + assert(keys == [2, 5, 11], "Got incorrect iteration of keys."); + assert(values == [3, 7, 13], "Got incorrect iteration of values."); + assert(entries == [(2, 3), (5, 7), (11, 13)], "Got incorrect iteration of entries."); +} + +// Test mutable iteration over keys, values and entries. +fn test_mut_iterators() { + let mut hashmap = ALLOCATE_HASHMAP(); + + hashmap.insert(2, 3); + hashmap.insert(5, 7); + hashmap.insert(11, 13); + + let f = |k: K| -> K{ k * 3}; + hashmap.iter_keys_mut(f); + + let f = |v: V| -> V{ v * 5}; + hashmap.iter_values_mut(f); + + let keys: [K; 3] = cut(hashmap.keys()).map(|k: Option| k.unwrap_unchecked()).sort_via(K_CMP); + let values: [V; 3] = cut(hashmap.values()).map(|v: Option| v.unwrap_unchecked()).sort_via(V_CMP); + + assert(keys == [6, 15, 33], f"Got incorrect iteration of keys: {keys}"); + assert(values == [15, 35, 65], "Got incorrect iteration of values."); + + let f = |k: K, v: V| -> (K, V){(k * 2, v * 2)}; + hashmap.iter_mut(f); + + let entries: [(K, V); 3] = cut(hashmap.entries()).map(|e: Option<(K, V)>| e.unwrap_unchecked()).sort_via(KV_CMP); + + assert(entries == [(12, 30), (30, 70), (66, 130)], "Got incorrect iteration of entries."); +} diff --git a/test_programs/execution_success/hashmap/src/utils.nr b/test_programs/execution_success/hashmap/src/utils.nr new file mode 100644 index 00000000000..45c9ca9bbf7 --- /dev/null +++ b/test_programs/execution_success/hashmap/src/utils.nr @@ -0,0 +1,10 @@ +// Compile-time: cuts the M first elements from the [T; N] array. +pub(crate) fn cut(input: [T; N]) -> [T; M] { + assert(M as u64 < N as u64, "M should be less than N."); + + let mut new = [dep::std::unsafe::zeroed(); M]; + for i in 0..M { + new[i] = input[i]; + } + new +} From 16d5f18c68cc3da1d11c98e101e3942d2437c3a8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 23 Feb 2024 15:06:15 +0000 Subject: [PATCH 002/416] chore(ssa): Remove mem2reg run before flattening (#4415) # Description ## Problem\* Before https://github.com/noir-lang/noir/pull/4240 we needed mem2reg to be run as to not panic when fetching slice lengths. ## Summary\* After the linked PR we have an improved startegy for tracking slice capacities by generating a slice capacities map before merging of values. This should enable us to remove a mem2reg pass that is run before flattening. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index d19c4467235..0bb81efe977 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -54,11 +54,6 @@ pub(crate) fn optimize_into_acir( .try_run_pass(Ssa::evaluate_assert_constant, "After Assert Constant:")? .try_run_pass(Ssa::unroll_loops, "After Unrolling:")? .run_pass(Ssa::simplify_cfg, "After Simplifying:") - // Run mem2reg before flattening to handle any promotion - // of values that can be accessed after loop unrolling. - // If there are slice mergers uncovered by loop unrolling - // and this pass is missed, slice merging will fail inside of flattening. - .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::flatten_cfg, "After Flattening:") .run_pass(Ssa::remove_bit_shifts, "After Removing Bit Shifts:") // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores From 27c66b3d0741e68ed591ae8a16b47b30bc87175f Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Sat, 24 Feb 2024 14:00:22 +0000 Subject: [PATCH 003/416] fix: remove print from monomorphization pass (#4417) # Description ## Problem\* Resolves ## Summary\* We're currently printing out every expression we're monomorphising. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 6f59fa13274..d8857f9e599 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -128,7 +128,6 @@ impl<'a> FunctionContext<'a> { } fn codegen_expression(&mut self, expr: &Expression) -> Result { - eprintln!("Codegen {expr}"); match expr { Expression::Ident(ident) => Ok(self.codegen_ident(ident)), Expression::Literal(literal) => self.codegen_literal(literal), From 33860678a642a76d8251ef42ffbe6d8a5a013528 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Sat, 24 Feb 2024 14:15:25 +0000 Subject: [PATCH 004/416] chore: remove unwanted prints (#4419) # Description ## Problem\* Resolves ## Summary\* This removes some unwanted prints which were left in from #4376 ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index d8857f9e599..d95295ae3c9 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -347,10 +347,8 @@ impl<'a> FunctionContext<'a> { } fn codegen_binary(&mut self, binary: &ast::Binary) -> Result { - eprintln!("Start binary"); let lhs = self.codegen_non_tuple_expression(&binary.lhs)?; let rhs = self.codegen_non_tuple_expression(&binary.rhs)?; - eprintln!("Insert binary"); Ok(self.insert_binary(lhs, binary.operator, rhs, binary.location)) } From 29e9b5e5d0f7a00c806639e900f2f8209675ee0e Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:54:28 +0100 Subject: [PATCH 005/416] chore: do not panic when dividing by zero (#4424) # Description ## Problem\* Resolves #2480 ## Summary\* In BrilligVM, we have now the same behaviour regarding division by 0. Whether it is a field or integer division, we return 0 when dividing by zero. Since we have a constraint which checks that the inverse times itself is 1, this constraint will always fail if we divide by zero (instead of panic or returning an error). ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/brillig_vm/src/arithmetic.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 263a733e3c4..9d7b6fe8f02 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -36,18 +36,20 @@ pub(crate) fn evaluate_binary_bigint_op( BinaryIntOp::UnsignedDiv => { let b_mod = b % bit_modulo; if b_mod.is_zero() { - return Err("Division by zero".to_owned()); + BigUint::zero() + } else { + (a % bit_modulo) / b_mod } - (a % bit_modulo) / b_mod } // Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation. BinaryIntOp::SignedDiv => { let b_signed = to_big_signed(b, bit_size); if b_signed.is_zero() { - return Err("Division by zero".to_owned()); + BigUint::zero() + } else { + let signed_div = to_big_signed(a, bit_size) / b_signed; + to_big_unsigned(signed_div, bit_size) } - let signed_div = to_big_signed(a, bit_size) / b_signed; - to_big_unsigned(signed_div, bit_size) } // Perform a == operation, returning 0 or 1 BinaryIntOp::Equals => { From 7cd5fdb3d2a53475b7c8681231d517cab30f9f9b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 26 Feb 2024 13:56:14 +0000 Subject: [PATCH 006/416] feat: expose separate functions to compile programs vs contracts in `noir_wasm` (#4413) # Description ## Problem\* Resolves ## Summary\* This PR exposes separate functions to compile contracts vs programs in the wasm compiler. This allows us to simplify various code paths as we don't need to deal with the potential for the two artifact types as this just leads to us asserting types and breaking type safety. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/wasm/src/compile.rs | 212 ++++++++++-------- compiler/wasm/src/compile_new.rs | 75 +++++-- compiler/wasm/src/index.cts | 77 ++++++- compiler/wasm/src/index.mts | 77 ++++++- compiler/wasm/src/lib.rs | 4 +- compiler/wasm/src/noir/noir-wasm-compiler.ts | 61 ++++- compiler/wasm/src/types/noir_artifact.ts | 19 -- .../test/compiler/browser/compile.test.ts | 8 +- .../wasm/test/compiler/node/compile.test.ts | 8 +- .../wasm/test/compiler/shared/compile.test.ts | 10 +- compiler/wasm/test/wasm/browser/index.test.ts | 10 +- compiler/wasm/test/wasm/node/index.test.ts | 10 +- 12 files changed, 377 insertions(+), 194 deletions(-) diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index c8b1680bc00..ca6c8efedb1 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -6,8 +6,7 @@ use nargo::artifacts::{ program::ProgramArtifact, }; use noirc_driver::{ - add_dep, compile_contract, compile_main, file_manager_with_stdlib, prepare_crate, - prepare_dependency, CompileOptions, CompiledContract, CompiledProgram, + add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, }; use noirc_evaluator::errors::SsaReport; @@ -60,51 +59,64 @@ extern "C" { #[derive(Clone, Debug, PartialEq, Eq)] pub type JsDependencyGraph; - #[wasm_bindgen(extends = Object, js_name = "CompileResult", typescript_type = "CompileResult")] + #[wasm_bindgen(extends = Object, js_name = "ProgramCompileResult", typescript_type = "ProgramCompileResult")] #[derive(Clone, Debug, PartialEq, Eq)] - pub type JsCompileResult; + pub type JsCompileProgramResult; #[wasm_bindgen(constructor, js_class = "Object")] - fn constructor() -> JsCompileResult; + fn constructor() -> JsCompileProgramResult; + + #[wasm_bindgen(extends = Object, js_name = "ContractCompileResult", typescript_type = "ContractCompileResult")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsCompileContractResult; + + #[wasm_bindgen(constructor, js_class = "Object")] + fn constructor() -> JsCompileContractResult; } -impl JsCompileResult { - const CONTRACT_PROP: &'static str = "contract"; +impl JsCompileProgramResult { const PROGRAM_PROP: &'static str = "program"; const WARNINGS_PROP: &'static str = "warnings"; - pub fn new(resp: CompileResult) -> JsCompileResult { - let obj = JsCompileResult::constructor(); - match resp { - CompileResult::Contract { contract, warnings } => { - js_sys::Reflect::set( - &obj, - &JsString::from(JsCompileResult::CONTRACT_PROP), - &::from_serde(&contract).unwrap(), - ) - .unwrap(); - js_sys::Reflect::set( - &obj, - &JsString::from(JsCompileResult::WARNINGS_PROP), - &::from_serde(&warnings).unwrap(), - ) - .unwrap(); - } - CompileResult::Program { program, warnings } => { - js_sys::Reflect::set( - &obj, - &JsString::from(JsCompileResult::PROGRAM_PROP), - &::from_serde(&program).unwrap(), - ) - .unwrap(); - js_sys::Reflect::set( - &obj, - &JsString::from(JsCompileResult::WARNINGS_PROP), - &::from_serde(&warnings).unwrap(), - ) - .unwrap(); - } - }; + pub fn new(program: ProgramArtifact, warnings: Vec) -> JsCompileProgramResult { + let obj = JsCompileProgramResult::constructor(); + + js_sys::Reflect::set( + &obj, + &JsString::from(JsCompileProgramResult::PROGRAM_PROP), + &::from_serde(&program).unwrap(), + ) + .unwrap(); + js_sys::Reflect::set( + &obj, + &JsString::from(JsCompileProgramResult::WARNINGS_PROP), + &::from_serde(&warnings).unwrap(), + ) + .unwrap(); + + obj + } +} + +impl JsCompileContractResult { + const CONTRACT_PROP: &'static str = "contract"; + const WARNINGS_PROP: &'static str = "warnings"; + + pub fn new(contract: ContractArtifact, warnings: Vec) -> JsCompileContractResult { + let obj = JsCompileContractResult::constructor(); + + js_sys::Reflect::set( + &obj, + &JsString::from(JsCompileContractResult::CONTRACT_PROP), + &::from_serde(&contract).unwrap(), + ) + .unwrap(); + js_sys::Reflect::set( + &obj, + &JsString::from(JsCompileContractResult::WARNINGS_PROP), + &::from_serde(&warnings).unwrap(), + ) + .unwrap(); obj } @@ -144,73 +156,98 @@ pub(crate) fn parse_all(fm: &FileManager) -> ParsedFiles { fm.as_file_map().all_file_ids().map(|&file_id| (file_id, parse_file(fm, file_id))).collect() } -pub enum CompileResult { - Contract { contract: ContractArtifact, warnings: Vec }, - Program { program: ProgramArtifact, warnings: Vec }, -} - #[wasm_bindgen] -pub fn compile( +pub fn compile_program( entry_point: String, - contracts: Option, dependency_graph: Option, file_source_map: PathToFileSourceMap, -) -> Result { +) -> Result { console_error_panic_hook::set_once(); - - let dependency_graph: DependencyGraph = if let Some(dependency_graph) = dependency_graph { - ::into_serde(&JsValue::from(dependency_graph)) - .map_err(|err| err.to_string())? - } else { - DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } - }; - - let fm = file_manager_with_source_map(file_source_map); - let parsed_files = parse_all(&fm); - let mut context = Context::new(fm, parsed_files); - - let path = Path::new(&entry_point); - let crate_id = prepare_crate(&mut context, path); - - process_dependency_graph(&mut context, dependency_graph); + let (crate_id, mut context) = prepare_context(entry_point, dependency_graph, file_source_map)?; let compile_options = CompileOptions::default(); - // For now we default to a bounded width of 3, though we can add it as a parameter let expression_width = acvm::acir::circuit::ExpressionWidth::Bounded { width: 3 }; - if contracts.unwrap_or_default() { - let compiled_contract = compile_contract(&mut context, crate_id, &compile_options) + let compiled_program = + noirc_driver::compile_main(&mut context, crate_id, &compile_options, None) .map_err(|errs| { CompileError::with_file_diagnostics( - "Failed to compile contract", + "Failed to compile program", errs, &context.file_manager, ) })? .0; - let optimized_contract = - nargo::ops::transform_contract(compiled_contract, expression_width); + let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); + let warnings = optimized_program.warnings.clone(); - let compile_output = generate_contract_artifact(optimized_contract); - Ok(JsCompileResult::new(compile_output)) - } else { - let compiled_program = compile_main(&mut context, crate_id, &compile_options, None) + Ok(JsCompileProgramResult::new(optimized_program.into(), warnings)) +} + +#[wasm_bindgen] +pub fn compile_contract( + entry_point: String, + dependency_graph: Option, + file_source_map: PathToFileSourceMap, +) -> Result { + console_error_panic_hook::set_once(); + let (crate_id, mut context) = prepare_context(entry_point, dependency_graph, file_source_map)?; + + let compile_options = CompileOptions::default(); + // For now we default to a bounded width of 3, though we can add it as a parameter + let expression_width = acvm::acir::circuit::ExpressionWidth::Bounded { width: 3 }; + + let compiled_contract = + noirc_driver::compile_contract(&mut context, crate_id, &compile_options) .map_err(|errs| { CompileError::with_file_diagnostics( - "Failed to compile program", + "Failed to compile contract", errs, &context.file_manager, ) })? .0; - let optimized_program = nargo::ops::transform_program(compiled_program, expression_width); + let optimized_contract = nargo::ops::transform_contract(compiled_contract, expression_width); - let compile_output = generate_program_artifact(optimized_program); - Ok(JsCompileResult::new(compile_output)) - } + let functions = + optimized_contract.functions.into_iter().map(ContractFunctionArtifact::from).collect(); + + let contract_artifact = ContractArtifact { + noir_version: String::from(NOIR_ARTIFACT_VERSION_STRING), + name: optimized_contract.name, + functions, + events: optimized_contract.events, + file_map: optimized_contract.file_map, + }; + + Ok(JsCompileContractResult::new(contract_artifact, optimized_contract.warnings)) +} + +fn prepare_context( + entry_point: String, + dependency_graph: Option, + file_source_map: PathToFileSourceMap, +) -> Result<(CrateId, Context<'static, 'static>), JsCompileError> { + let dependency_graph: DependencyGraph = if let Some(dependency_graph) = dependency_graph { + ::into_serde(&JsValue::from(dependency_graph)) + .map_err(|err| err.to_string())? + } else { + DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } + }; + + let fm = file_manager_with_source_map(file_source_map); + let parsed_files = parse_all(&fm); + let mut context = Context::new(fm, parsed_files); + + let path = Path::new(&entry_point); + let crate_id = prepare_crate(&mut context, path); + + process_dependency_graph(&mut context, dependency_graph); + + Ok((crate_id, context)) } // Create a new FileManager with the given source map @@ -270,25 +307,6 @@ fn add_noir_lib(context: &mut Context, library_name: &CrateName) -> CrateId { prepare_dependency(context, &path_to_lib) } -pub(crate) fn generate_program_artifact(program: CompiledProgram) -> CompileResult { - let warnings = program.warnings.clone(); - CompileResult::Program { program: program.into(), warnings } -} - -pub(crate) fn generate_contract_artifact(contract: CompiledContract) -> CompileResult { - let functions = contract.functions.into_iter().map(ContractFunctionArtifact::from).collect(); - - let contract_artifact = ContractArtifact { - noir_version: String::from(NOIR_ARTIFACT_VERSION_STRING), - name: contract.name, - functions, - events: contract.events, - file_map: contract.file_map, - }; - - CompileResult::Contract { contract: contract_artifact, warnings: contract.warnings } -} - #[cfg(test)] mod test { use noirc_driver::prepare_crate; diff --git a/compiler/wasm/src/compile_new.rs b/compiler/wasm/src/compile_new.rs index f8fbed4f470..2a5f7ab6545 100644 --- a/compiler/wasm/src/compile_new.rs +++ b/compiler/wasm/src/compile_new.rs @@ -1,10 +1,12 @@ use crate::compile::{ - file_manager_with_source_map, generate_contract_artifact, generate_program_artifact, parse_all, - JsCompileResult, PathToFileSourceMap, + file_manager_with_source_map, parse_all, JsCompileContractResult, JsCompileProgramResult, + PathToFileSourceMap, }; use crate::errors::{CompileError, JsCompileError}; +use nargo::artifacts::contract::{ContractArtifact, ContractFunctionArtifact}; use noirc_driver::{ add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, + NOIR_ARTIFACT_VERSION_STRING, }; use noirc_frontend::{ graph::{CrateId, CrateName}, @@ -92,7 +94,7 @@ impl CompilerContext { pub fn compile_program( mut self, program_width: usize, - ) -> Result { + ) -> Result { let compile_options = CompileOptions::default(); let np_language = acvm::acir::circuit::ExpressionWidth::Bounded { width: program_width }; @@ -110,15 +112,15 @@ impl CompilerContext { .0; let optimized_program = nargo::ops::transform_program(compiled_program, np_language); + let warnings = optimized_program.warnings.clone(); - let compile_output = generate_program_artifact(optimized_program); - Ok(JsCompileResult::new(compile_output)) + Ok(JsCompileProgramResult::new(optimized_program.into(), warnings)) } pub fn compile_contract( mut self, program_width: usize, - ) -> Result { + ) -> Result { let compile_options = CompileOptions::default(); let np_language = acvm::acir::circuit::ExpressionWidth::Bounded { width: program_width }; let root_crate_id = *self.context.root_crate_id(); @@ -136,24 +138,64 @@ impl CompilerContext { let optimized_contract = nargo::ops::transform_contract(compiled_contract, np_language); - let compile_output = generate_contract_artifact(optimized_contract); - Ok(JsCompileResult::new(compile_output)) + let functions = + optimized_contract.functions.into_iter().map(ContractFunctionArtifact::from).collect(); + + let contract_artifact = ContractArtifact { + noir_version: String::from(NOIR_ARTIFACT_VERSION_STRING), + name: optimized_contract.name, + functions, + events: optimized_contract.events, + file_map: optimized_contract.file_map, + }; + + Ok(JsCompileContractResult::new(contract_artifact, optimized_contract.warnings)) } } /// This is a method that exposes the same API as `compile` /// But uses the Context based APi internally #[wasm_bindgen] -pub fn compile_( +pub fn compile_program_( entry_point: String, - contracts: Option, dependency_graph: Option, file_source_map: PathToFileSourceMap, -) -> Result { - use std::collections::HashMap; +) -> Result { + console_error_panic_hook::set_once(); + + let compiler_context = + prepare_compiler_context(entry_point, dependency_graph, file_source_map)?; + let program_width = 3; + compiler_context.compile_program(program_width) +} + +/// This is a method that exposes the same API as `compile` +/// But uses the Context based APi internally +#[wasm_bindgen] +pub fn compile_contract_( + entry_point: String, + dependency_graph: Option, + file_source_map: PathToFileSourceMap, +) -> Result { console_error_panic_hook::set_once(); + let compiler_context = + prepare_compiler_context(entry_point, dependency_graph, file_source_map)?; + let program_width = 3; + + compiler_context.compile_contract(program_width) +} + +/// This is a method that exposes the same API as `prepare_context` +/// But uses the Context based API internally +fn prepare_compiler_context( + entry_point: String, + dependency_graph: Option, + file_source_map: PathToFileSourceMap, +) -> Result { + use std::collections::HashMap; + let dependency_graph: crate::compile::DependencyGraph = if let Some(dependency_graph) = dependency_graph { ::into_serde( @@ -218,14 +260,7 @@ pub fn compile_( } } - let is_contract = contracts.unwrap_or(false); - let program_width = 3; - - if is_contract { - compiler_context.compile_contract(program_width) - } else { - compiler_context.compile_program(program_width) - } + Ok(compiler_context) } #[cfg(test)] diff --git a/compiler/wasm/src/index.cts b/compiler/wasm/src/index.cts index 7c707e662d8..234bfa7280c 100644 --- a/compiler/wasm/src/index.cts +++ b/compiler/wasm/src/index.cts @@ -2,7 +2,7 @@ import { FileManager } from './noir/file-manager/file-manager'; import { createNodejsFileManager } from './noir/file-manager/nodejs-file-manager'; import { NoirWasmCompiler } from './noir/noir-wasm-compiler'; import { LogData, LogFn } from './utils'; -import { CompilationResult } from './types/noir_artifact'; +import { ContractCompilationArtifacts, ProgramCompilationArtifacts } from './types/noir_artifact'; import { inflateDebugSymbols } from './noir/debug'; /** @@ -17,36 +17,86 @@ import { inflateDebugSymbols } from './noir/debug'; * ```typescript * // Node.js * - * import { compile, createFileManager } from '@noir-lang/noir_wasm'; + * import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; * * const fm = createFileManager(myProjectPath); - * const myCompiledCode = await compile(fm); + * const myCompiledCode = await compile_program(fm); * ``` * * ```typescript * // Browser * - * import { compile, createFileManager } from '@noir-lang/noir_wasm'; + * import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; * * const fm = createFileManager('/'); * for (const path of files) { * await fm.writeFile(path, await getFileAsStream(path)); * } - * const myCompiledCode = await compile(fm); + * const myCompiledCode = await compile_program(fm); * ``` */ -async function compile( +async function compile_program( fileManager: FileManager, projectPath?: string, logFn?: LogFn, debugLogFn?: LogFn, -): Promise { +): Promise { + const compiler = await setup_compiler(fileManager, projectPath, logFn, debugLogFn); + return await compiler.compile_program(); +} + +/** + * Compiles a Noir project + * + * @param fileManager - The file manager to use + * @param projectPath - The path to the project inside the file manager. Defaults to the root of the file manager + * @param logFn - A logging function. If not provided, console.log will be used + * @param debugLogFn - A debug logging function. If not provided, logFn will be used + * + * @example + * ```typescript + * // Node.js + * + * import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + * + * const fm = createFileManager(myProjectPath); + * const myCompiledCode = await compile_contract(fm); + * ``` + * + * ```typescript + * // Browser + * + * import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + * + * const fm = createFileManager('/'); + * for (const path of files) { + * await fm.writeFile(path, await getFileAsStream(path)); + * } + * const myCompiledCode = await compile_contract(fm); + * ``` + */ +async function compile_contract( + fileManager: FileManager, + projectPath?: string, + logFn?: LogFn, + debugLogFn?: LogFn, +): Promise { + const compiler = await setup_compiler(fileManager, projectPath, logFn, debugLogFn); + return await compiler.compile_contract(); +} + +async function setup_compiler( + fileManager: FileManager, + projectPath?: string, + logFn?: LogFn, + debugLogFn?: LogFn, +): Promise { if (logFn && !debugLogFn) { debugLogFn = logFn; } const cjs = await require('../build/cjs'); - const compiler = await NoirWasmCompiler.new( + return await NoirWasmCompiler.new( fileManager, projectPath ?? fileManager.getDataDir(), cjs, @@ -72,9 +122,16 @@ async function compile( }, }, ); - return await compiler.compile(); } const createFileManager = createNodejsFileManager; -export { compile, createFileManager, inflateDebugSymbols, CompilationResult }; +export { + compile_program as compile, + compile_program, + compile_contract, + createFileManager, + inflateDebugSymbols, + ProgramCompilationArtifacts, + ContractCompilationArtifacts, +}; diff --git a/compiler/wasm/src/index.mts b/compiler/wasm/src/index.mts index d4ed0beccfc..326a7337117 100644 --- a/compiler/wasm/src/index.mts +++ b/compiler/wasm/src/index.mts @@ -2,7 +2,7 @@ import { FileManager } from './noir/file-manager/file-manager'; import { createNodejsFileManager } from './noir/file-manager/nodejs-file-manager'; import { NoirWasmCompiler } from './noir/noir-wasm-compiler'; import { LogData, LogFn } from './utils'; -import { CompilationResult } from './types/noir_artifact'; +import { ContractCompilationArtifacts, ProgramCompilationArtifacts } from './types/noir_artifact'; import { inflateDebugSymbols } from './noir/debug'; /** @@ -17,30 +17,80 @@ import { inflateDebugSymbols } from './noir/debug'; * ```typescript * // Node.js * - * import { compile, createFileManager } from '@noir-lang/noir_wasm'; + * import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; * * const fm = createFileManager(myProjectPath); - * const myCompiledCode = await compile(fm); + * const myCompiledCode = await compile_program(fm); * ``` * * ```typescript * // Browser * - * import { compile, createFileManager } from '@noir-lang/noir_wasm'; + * import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; * * const fm = createFileManager('/'); * for (const path of files) { * await fm.writeFile(path, await getFileAsStream(path)); * } - * const myCompiledCode = await compile(fm); + * const myCompiledCode = await compile_program(fm); * ``` */ -async function compile( +async function compile_program( fileManager: FileManager, projectPath?: string, logFn?: LogFn, debugLogFn?: LogFn, -): Promise { +): Promise { + const compiler = await setup_compiler(fileManager, projectPath, logFn, debugLogFn); + return await compiler.compile_program(); +} + +/** + * Compiles a Noir project + * + * @param fileManager - The file manager to use + * @param projectPath - The path to the project inside the file manager. Defaults to the root of the file manager + * @param logFn - A logging function. If not provided, console.log will be used + * @param debugLogFn - A debug logging function. If not provided, logFn will be used + * + * @example + * ```typescript + * // Node.js + * + * import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + * + * const fm = createFileManager(myProjectPath); + * const myCompiledCode = await compile_contract(fm); + * ``` + * + * ```typescript + * // Browser + * + * import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + * + * const fm = createFileManager('/'); + * for (const path of files) { + * await fm.writeFile(path, await getFileAsStream(path)); + * } + * const myCompiledCode = await compile_contract(fm); + * ``` + */ +async function compile_contract( + fileManager: FileManager, + projectPath?: string, + logFn?: LogFn, + debugLogFn?: LogFn, +): Promise { + const compiler = await setup_compiler(fileManager, projectPath, logFn, debugLogFn); + return await compiler.compile_contract(); +} + +async function setup_compiler( + fileManager: FileManager, + projectPath?: string, + logFn?: LogFn, + debugLogFn?: LogFn, +): Promise { if (logFn && !debugLogFn) { debugLogFn = logFn; } @@ -48,7 +98,7 @@ async function compile( const esm = await import(/* webpackMode: "eager" */ '../build/esm'); await esm.default(); - const compiler = await NoirWasmCompiler.new( + return await NoirWasmCompiler.new( fileManager, projectPath ?? fileManager.getDataDir(), esm, @@ -74,9 +124,16 @@ async function compile( }, }, ); - return await compiler.compile(); } const createFileManager = createNodejsFileManager; -export { compile, createFileManager, inflateDebugSymbols, CompilationResult }; +export { + compile_program as compile, + compile_program, + compile_contract, + createFileManager, + inflateDebugSymbols, + ProgramCompilationArtifacts, + ContractCompilationArtifacts, +}; diff --git a/compiler/wasm/src/lib.rs b/compiler/wasm/src/lib.rs index 174d9b9ce9c..6753faf2009 100644 --- a/compiler/wasm/src/lib.rs +++ b/compiler/wasm/src/lib.rs @@ -18,10 +18,10 @@ mod compile; mod compile_new; mod errors; -pub use compile::compile; +pub use compile::{compile_contract, compile_program}; // Expose the new Context-Centric API -pub use compile_new::{compile_, CompilerContext, CrateIDWrapper}; +pub use compile_new::{compile_contract_, compile_program_, CompilerContext, CrateIDWrapper}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; #[derive(Serialize, Deserialize)] diff --git a/compiler/wasm/src/noir/noir-wasm-compiler.ts b/compiler/wasm/src/noir/noir-wasm-compiler.ts index 2a0af5d8fee..1ec3af1fd65 100644 --- a/compiler/wasm/src/noir/noir-wasm-compiler.ts +++ b/compiler/wasm/src/noir/noir-wasm-compiler.ts @@ -6,7 +6,7 @@ import { LocalDependencyResolver } from './dependencies/local-dependency-resolve import { FileManager } from './file-manager/file-manager'; import { Package } from './package'; import { LogFn } from '../utils'; -import { CompilationResult } from '../types/noir_artifact'; +import { ContractCompilationArtifacts, ProgramCompilationArtifacts } from '../types/noir_artifact'; /** Compilation options */ export type NoirWasmCompileOptions = { @@ -84,21 +84,64 @@ export class NoirWasmCompiler { /** * Compile EntryPoint */ + public async compile_program(): Promise { + console.log(`Compiling at ${this.#package.getEntryPointPath()}`); + + if (this.#package.getType() !== 'bin') { + throw new Error(`Expected to find package type "bin" but found ${this.#package.getType()}`); + } + await this.#dependencyManager.resolveDependencies(); + this.#debugLog(`Dependencies: ${this.#dependencyManager.getPackageNames().join(', ')}`); + + try { + const entrypoint = this.#package.getEntryPointPath(); + const deps = { + /* eslint-disable camelcase */ + root_dependencies: this.#dependencyManager.getEntrypointDependencies(), + library_dependencies: this.#dependencyManager.getLibraryDependencies(), + /* eslint-enable camelcase */ + }; + const packageSources = await this.#package.getSources(this.#fm); + const librarySources = ( + await Promise.all( + this.#dependencyManager + .getLibraries() + .map(async ([alias, library]) => await library.package.getSources(this.#fm, alias)), + ) + ).flat(); + [...packageSources, ...librarySources].forEach((sourceFile) => { + this.#debugLog(`Adding source ${sourceFile.path}`); + this.#sourceMap.add_source_code(sourceFile.path, sourceFile.source); + }); + const result = this.#wasmCompiler.compile_program(entrypoint, deps, this.#sourceMap); + + return result; + } catch (err) { + if (err instanceof Error && err.name === 'CompileError') { + const logs = await this.#processCompileError(err); + for (const log of logs) { + this.#log(log); + } + throw new Error(logs.join('\n')); + } + + throw err; + } + } + /** * Compile EntryPoint */ - public async compile(): Promise { + public async compile_contract(): Promise { console.log(`Compiling at ${this.#package.getEntryPointPath()}`); - if (!(this.#package.getType() === 'contract' || this.#package.getType() === 'bin')) { - throw new Error(`Only supports compiling "contract" and "bin" package types (${this.#package.getType()})`); + if (this.#package.getType() !== 'contract') { + throw new Error(`Expected to find package type "contract" but found ${this.#package.getType()}`); } await this.#dependencyManager.resolveDependencies(); this.#debugLog(`Dependencies: ${this.#dependencyManager.getPackageNames().join(', ')}`); try { - const isContract: boolean = this.#package.getType() === 'contract'; - const entrypoint = this.#package.getEntryPointPath(); const deps = { /* eslint-disable camelcase */ @@ -118,11 +161,7 @@ export class NoirWasmCompiler { this.#debugLog(`Adding source ${sourceFile.path}`); this.#sourceMap.add_source_code(sourceFile.path, sourceFile.source); }); - const result = this.#wasmCompiler.compile(entrypoint, isContract, deps, this.#sourceMap); - - if ((isContract && !('contract' in result)) || (!isContract && !('program' in result))) { - throw new Error('Invalid compilation result'); - } + const result = this.#wasmCompiler.compile_contract(entrypoint, deps, this.#sourceMap); return result; } catch (err) { diff --git a/compiler/wasm/src/types/noir_artifact.ts b/compiler/wasm/src/types/noir_artifact.ts index e636212a487..832a6ed9bf9 100644 --- a/compiler/wasm/src/types/noir_artifact.ts +++ b/compiler/wasm/src/types/noir_artifact.ts @@ -180,22 +180,3 @@ export interface ProgramCompilationArtifacts { /** Compilation warnings. */ warnings: Warning[]; } - -/** - * output of Noir Wasm compilation, can be for a contract or lib/binary - */ -export type CompilationResult = ContractCompilationArtifacts | ProgramCompilationArtifacts; - -/** - * Check if it has Contract unique property - */ -export function isContractCompilationArtifacts(artifact: CompilationResult): artifact is ContractCompilationArtifacts { - return (artifact as ContractCompilationArtifacts).contract !== undefined; -} - -/** - * Check if it has Contract unique property - */ -export function isProgramCompilationArtifacts(artifact: CompilationResult): artifact is ProgramCompilationArtifacts { - return (artifact as ProgramCompilationArtifacts).program !== undefined; -} diff --git a/compiler/wasm/test/compiler/browser/compile.test.ts b/compiler/wasm/test/compiler/browser/compile.test.ts index b7e6c27427f..7d4b3da55aa 100644 --- a/compiler/wasm/test/compiler/browser/compile.test.ts +++ b/compiler/wasm/test/compiler/browser/compile.test.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ import { getPaths } from '../../shared'; import { expect } from '@esm-bundle/chai'; -import { compile, createFileManager } from '@noir-lang/noir_wasm'; +import { compile_program, compile_contract, createFileManager } from '@noir-lang/noir_wasm'; import { ContractArtifact, ProgramArtifact } from '../../../src/types/noir_artifact'; import { shouldCompileContractIdentically, shouldCompileProgramIdentically } from '../shared/compile.test'; @@ -33,7 +33,7 @@ describe('noir-compiler/browser', () => { await fm.writeFile(path, (await getFile(path)).body as ReadableStream); } const nargoArtifact = (await getPrecompiledSource(simpleScriptExpectedArtifact)) as ProgramArtifact; - const noirWasmArtifact = await compile(fm, '/fixtures/simple'); + const noirWasmArtifact = await compile_program(fm, '/fixtures/simple'); return { nargoArtifact, noirWasmArtifact }; }, @@ -51,7 +51,7 @@ describe('noir-compiler/browser', () => { await fm.writeFile(path, (await getFile(path)).body as ReadableStream); } const nargoArtifact = (await getPrecompiledSource(depsScriptExpectedArtifact)) as ProgramArtifact; - const noirWasmArtifact = await compile(fm, '/fixtures/with-deps'); + const noirWasmArtifact = await compile_program(fm, '/fixtures/with-deps'); return { nargoArtifact, noirWasmArtifact }; }, @@ -69,7 +69,7 @@ describe('noir-compiler/browser', () => { await fm.writeFile(path, (await getFile(path)).body as ReadableStream); } const nargoArtifact = (await getPrecompiledSource(contractExpectedArtifact)) as ContractArtifact; - const noirWasmArtifact = await compile(fm, '/fixtures/noir-contract'); + const noirWasmArtifact = await compile_contract(fm, '/fixtures/noir-contract'); return { nargoArtifact, noirWasmArtifact }; }, diff --git a/compiler/wasm/test/compiler/node/compile.test.ts b/compiler/wasm/test/compiler/node/compile.test.ts index 9af98195825..811dc95ce16 100644 --- a/compiler/wasm/test/compiler/node/compile.test.ts +++ b/compiler/wasm/test/compiler/node/compile.test.ts @@ -2,7 +2,7 @@ import { join, resolve } from 'path'; import { getPaths } from '../../shared'; import { expect } from 'chai'; -import { compile, createFileManager } from '@noir-lang/noir_wasm'; +import { compile_program, compile_contract, createFileManager } from '@noir-lang/noir_wasm'; import { readFile } from 'fs/promises'; import { ContractArtifact, ProgramArtifact } from '../../../src/types/noir_artifact'; import { shouldCompileContractIdentically, shouldCompileProgramIdentically } from '../shared/compile.test'; @@ -15,7 +15,7 @@ describe('noir-compiler/node', () => { const fm = createFileManager(simpleScriptProjectPath); const nargoArtifact = JSON.parse((await readFile(simpleScriptExpectedArtifact)).toString()) as ProgramArtifact; - const noirWasmArtifact = await compile(fm); + const noirWasmArtifact = await compile_program(fm); return { nargoArtifact, noirWasmArtifact }; }, expect); @@ -24,7 +24,7 @@ describe('noir-compiler/node', () => { const fm = createFileManager(depsScriptProjectPath); const nargoArtifact = JSON.parse((await readFile(depsScriptExpectedArtifact)).toString()) as ProgramArtifact; - const noirWasmArtifact = await compile(fm); + const noirWasmArtifact = await compile_program(fm); return { nargoArtifact, noirWasmArtifact }; }, expect); @@ -33,7 +33,7 @@ describe('noir-compiler/node', () => { const fm = createFileManager(contractProjectPath); const nargoArtifact = JSON.parse((await readFile(contractExpectedArtifact)).toString()) as ContractArtifact; - const noirWasmArtifact = await compile(fm); + const noirWasmArtifact = await compile_contract(fm); return { nargoArtifact, noirWasmArtifact }; }, expect); }); diff --git a/compiler/wasm/test/compiler/shared/compile.test.ts b/compiler/wasm/test/compiler/shared/compile.test.ts index 88e8e8c8e5a..52cef14968b 100644 --- a/compiler/wasm/test/compiler/shared/compile.test.ts +++ b/compiler/wasm/test/compiler/shared/compile.test.ts @@ -1,4 +1,4 @@ -import { CompilationResult, inflateDebugSymbols } from '@noir-lang/noir_wasm'; +import { inflateDebugSymbols } from '@noir-lang/noir_wasm'; import { type expect as Expect } from 'chai'; import { ContractArtifact, @@ -11,7 +11,7 @@ import { } from '../../../src/types/noir_artifact'; export function shouldCompileProgramIdentically( - compileFn: () => Promise<{ nargoArtifact: ProgramArtifact; noirWasmArtifact: CompilationResult }>, + compileFn: () => Promise<{ nargoArtifact: ProgramArtifact; noirWasmArtifact: ProgramCompilationArtifacts }>, expect: typeof Expect, timeout = 5000, ) { @@ -24,7 +24,7 @@ export function shouldCompileProgramIdentically( normalizeVersion(nargoArtifact); // Prepare noir-wasm artifact - const noirWasmProgram = (noirWasmArtifact as unknown as ProgramCompilationArtifacts).program; + const noirWasmProgram = noirWasmArtifact.program; expect(noirWasmProgram).not.to.be.undefined; const [_noirWasmDebugInfos, norWasmFileMap] = deleteProgramDebugMetadata(noirWasmProgram); normalizeVersion(noirWasmProgram); @@ -47,7 +47,7 @@ export function shouldCompileProgramIdentically( } export function shouldCompileContractIdentically( - compileFn: () => Promise<{ nargoArtifact: ContractArtifact; noirWasmArtifact: CompilationResult }>, + compileFn: () => Promise<{ nargoArtifact: ContractArtifact; noirWasmArtifact: ContractCompilationArtifacts }>, expect: typeof Expect, timeout = 5000, ) { @@ -60,7 +60,7 @@ export function shouldCompileContractIdentically( normalizeVersion(nargoArtifact); // Prepare noir-wasm artifact - const noirWasmContract = (noirWasmArtifact as unknown as ContractCompilationArtifacts).contract; + const noirWasmContract = noirWasmArtifact.contract; expect(noirWasmContract).not.to.be.undefined; const [noirWasmDebugInfos, norWasmFileMap] = deleteContractDebugMetadata(noirWasmContract); normalizeVersion(noirWasmContract); diff --git a/compiler/wasm/test/wasm/browser/index.test.ts b/compiler/wasm/test/wasm/browser/index.test.ts index 3122fa57945..b59b4ae417a 100644 --- a/compiler/wasm/test/wasm/browser/index.test.ts +++ b/compiler/wasm/test/wasm/browser/index.test.ts @@ -2,7 +2,7 @@ import { getPaths } from '../../shared'; import { expect } from '@esm-bundle/chai'; -import init, { compile, PathToFileSourceMap, compile_, CompilerContext } from '../../../build/esm'; +import init, { compile_program, PathToFileSourceMap, compile_program_, CompilerContext } from '../../../build/esm'; // @ts-ignore await init(); @@ -35,7 +35,7 @@ describe('noir wasm compilation', () => { it('matching nargos compilation', async () => { const sourceMap = new PathToFileSourceMap(); sourceMap.add_source_code('script/main.nr', await getFileAsString(simpleScriptSourcePath)); - const wasmCircuit = compile('script/main.nr', undefined, undefined, sourceMap); + const wasmCircuit = compile_program('script/main.nr', undefined, sourceMap); const cliCircuit = await getPrecompiledSource(simpleScriptExpectedArtifact); if (!('program' in wasmCircuit)) { @@ -58,9 +58,8 @@ describe('noir wasm compilation', () => { }); it('matching nargos compilation', async () => { - const wasmCircuit = compile( + const wasmCircuit = compile_program( 'script/main.nr', - false, { root_dependencies: ['lib_a'], library_dependencies: { @@ -132,9 +131,8 @@ describe('noir wasm compilation', () => { }).timeout(60 * 20e3); it('matching nargos compilation - context-implementation-compile-api', async () => { - const wasmCircuit = await compile_( + const wasmCircuit = await compile_program_( 'script/main.nr', - false, { root_dependencies: ['lib_a'], library_dependencies: { diff --git a/compiler/wasm/test/wasm/node/index.test.ts b/compiler/wasm/test/wasm/node/index.test.ts index c73ce7477e5..23c87cc059a 100644 --- a/compiler/wasm/test/wasm/node/index.test.ts +++ b/compiler/wasm/test/wasm/node/index.test.ts @@ -3,7 +3,7 @@ import { readFileSync } from 'fs'; import { join, resolve } from 'path'; import { expect } from 'chai'; -import { compile, PathToFileSourceMap, compile_, CompilerContext } from '../../../build/cjs'; +import { compile_program, PathToFileSourceMap, compile_program_, CompilerContext } from '../../../build/cjs'; const basePath = resolve(join(__dirname, '../../')); const { @@ -26,7 +26,7 @@ describe('noir wasm compilation', () => { it('matching nargos compilation', async () => { const sourceMap = new PathToFileSourceMap(); sourceMap.add_source_code(simpleScriptSourcePath, readFileSync(simpleScriptSourcePath, 'utf-8')); - const wasmCircuit = compile(simpleScriptSourcePath, undefined, undefined, sourceMap); + const wasmCircuit = compile_program(simpleScriptSourcePath, undefined, sourceMap); const cliCircuit = await getPrecompiledSource(simpleScriptExpectedArtifact); if (!('program' in wasmCircuit)) { @@ -49,9 +49,8 @@ describe('noir wasm compilation', () => { }); it('matching nargos compilation', async () => { - const wasmCircuit = compile( + const wasmCircuit = compile_program( 'script/main.nr', - false, { root_dependencies: ['lib_a'], library_dependencies: { @@ -123,9 +122,8 @@ describe('noir wasm compilation', () => { }).timeout(60 * 20e3); it('matching nargos compilation - context-implementation-compile-api', async () => { - const wasmCircuit = await compile_( + const wasmCircuit = await compile_program_( 'script/main.nr', - false, { root_dependencies: ['lib_a'], library_dependencies: { From 176fab42970ff0a9797b7f8c7ce53817e7d85b90 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:05:41 +0000 Subject: [PATCH 007/416] chore(ci): prevent msrv checks from blocking PRs (#4414) # Description ## Problem\* Resolves ## Summary\* Current the MSRV check CI runs on every PR so if one of our dependencies breaks us, all merges halt until we fix this. This is unnecessary as we only need to stop publishing releases and normal development work can continue. This PR switches this workflow to instead run only on master and on a nightly schedule. If the workflow fails then an issue will be raised. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. Co-authored-by: kevaundray --- .github/ACVM_NOT_PUBLISHABLE.md | 13 +++++++ .../workflows/test-rust-workspace-msrv.yml | 37 +++++++++++++------ .github/workflows/test-rust-workspace.yml | 20 +++++----- cspell.json | 1 + 4 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 .github/ACVM_NOT_PUBLISHABLE.md diff --git a/.github/ACVM_NOT_PUBLISHABLE.md b/.github/ACVM_NOT_PUBLISHABLE.md new file mode 100644 index 00000000000..e7eacb3b523 --- /dev/null +++ b/.github/ACVM_NOT_PUBLISHABLE.md @@ -0,0 +1,13 @@ +--- +title: "ACVM crates are not publishable" +assignees: TomAFrench kevaundray savio-sou +--- + + +The ACVM crates are currently unpublishable, making a release will NOT push our crates to crates.io. + +This is likely due to a crate we depend on bumping its MSRV above our own. Our lockfile is not taken into account when publishing to crates.io (as people downloading our crate don't use it) so we need to be able to use the most up to date versions of our dependencies (including transient dependencies) specified. + +Check the [MSRV check]({{env.WORKFLOW_URL}}) workflow for details. + +This issue was raised by the workflow `{{env.WORKFLOW_NAME}}` diff --git a/.github/workflows/test-rust-workspace-msrv.yml b/.github/workflows/test-rust-workspace-msrv.yml index 061fc65ca8b..0b2855fa834 100644 --- a/.github/workflows/test-rust-workspace-msrv.yml +++ b/.github/workflows/test-rust-workspace-msrv.yml @@ -6,8 +6,9 @@ name: Test (MSRV check) # We must then always be able to build the workspace using the latest versions of all of our dependencies, so we explicitly update them and build in this workflow. on: - pull_request: - merge_group: + schedule: + # Run a nightly check at 2 AM UTC + - cron: "0 2 * * *" push: branches: - master @@ -100,13 +101,25 @@ jobs: - run-tests steps: - - name: Report overall success - run: | - if [[ $FAIL == true ]]; then - exit 1 - else - exit 0 - fi - env: - # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. - FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} + - name: Report overall success + run: | + if [[ $FAIL == true ]]; then + exit 1 + else + exit 0 + fi + env: + # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} + + # Raise an issue if the tests failed + - name: Alert on failed publish + uses: JasonEtco/create-an-issue@v2 + if: ${{ failure() }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + WORKFLOW_NAME: ${{ github.workflow }} + WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + update_existing: true + filename: .github/JS_PUBLISH_FAILED.md \ No newline at end of file diff --git a/.github/workflows/test-rust-workspace.yml b/.github/workflows/test-rust-workspace.yml index c12dcaba0ba..22684de3044 100644 --- a/.github/workflows/test-rust-workspace.yml +++ b/.github/workflows/test-rust-workspace.yml @@ -88,13 +88,13 @@ jobs: - run-tests steps: - - name: Report overall success - run: | - if [[ $FAIL == true ]]; then - exit 1 - else - exit 0 - fi - env: - # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. - FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} + - name: Report overall success + run: | + if [[ $FAIL == true ]]; then + exit 1 + else + exit 0 + fi + env: + # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} diff --git a/cspell.json b/cspell.json index be6b7c5c7e8..23659b39c68 100644 --- a/cspell.json +++ b/cspell.json @@ -118,6 +118,7 @@ "monomorphizes", "monomorphizing", "montcurve", + "MSRV", "nand", "nargo", "neovim", From ab25b5ed3cd17e3f53c5cc873571fe4c08bad35d Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:40:31 +0000 Subject: [PATCH 008/416] chore: remove duplicate `parse_all` function in wasm compiler (#4411) # Description ## Problem\* Resolves ## Summary\* This removes a function which exists in `nargo` from being defined again in `noir_wasm` ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/wasm/src/compile.rs | 20 +++++++++----------- compiler/wasm/src/compile_new.rs | 6 ++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index ca6c8efedb1..9e6fca1126e 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -1,9 +1,12 @@ use fm::FileManager; use gloo_utils::format::JsValueSerdeExt; use js_sys::{JsString, Object}; -use nargo::artifacts::{ - contract::{ContractArtifact, ContractFunctionArtifact}, - program::ProgramArtifact, +use nargo::{ + artifacts::{ + contract::{ContractArtifact, ContractFunctionArtifact}, + program::ProgramArtifact, + }, + parse_all, }; use noirc_driver::{ add_dep, file_manager_with_stdlib, prepare_crate, prepare_dependency, CompileOptions, @@ -12,7 +15,7 @@ use noirc_driver::{ use noirc_evaluator::errors::SsaReport; use noirc_frontend::{ graph::{CrateId, CrateName}, - hir::{def_map::parse_file, Context, ParsedFiles}, + hir::Context, }; use serde::Deserialize; use std::{collections::HashMap, path::Path}; @@ -152,10 +155,6 @@ impl PathToFileSourceMap { } } -pub(crate) fn parse_all(fm: &FileManager) -> ParsedFiles { - fm.as_file_map().all_file_ids().map(|&file_id| (file_id, parse_file(fm, file_id))).collect() -} - #[wasm_bindgen] pub fn compile_program( entry_point: String, @@ -309,14 +308,13 @@ fn add_noir_lib(context: &mut Context, library_name: &CrateName) -> CrateId { #[cfg(test)] mod test { + use nargo::parse_all; use noirc_driver::prepare_crate; use noirc_frontend::{graph::CrateName, hir::Context}; use crate::compile::PathToFileSourceMap; - use super::{ - file_manager_with_source_map, parse_all, process_dependency_graph, DependencyGraph, - }; + use super::{file_manager_with_source_map, process_dependency_graph, DependencyGraph}; use std::{collections::HashMap, path::Path}; fn setup_test_context(source_map: PathToFileSourceMap) -> Context<'static, 'static> { diff --git a/compiler/wasm/src/compile_new.rs b/compiler/wasm/src/compile_new.rs index 2a5f7ab6545..d6b382f669f 100644 --- a/compiler/wasm/src/compile_new.rs +++ b/compiler/wasm/src/compile_new.rs @@ -1,9 +1,10 @@ use crate::compile::{ - file_manager_with_source_map, parse_all, JsCompileContractResult, JsCompileProgramResult, + file_manager_with_source_map, JsCompileContractResult, JsCompileProgramResult, PathToFileSourceMap, }; use crate::errors::{CompileError, JsCompileError}; use nargo::artifacts::contract::{ContractArtifact, ContractFunctionArtifact}; +use nargo::parse_all; use noirc_driver::{ add_dep, compile_contract, compile_main, prepare_crate, prepare_dependency, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, @@ -265,10 +266,11 @@ fn prepare_compiler_context( #[cfg(test)] mod test { + use nargo::parse_all; use noirc_driver::prepare_crate; use noirc_frontend::hir::Context; - use crate::compile::{file_manager_with_source_map, parse_all, PathToFileSourceMap}; + use crate::compile::{file_manager_with_source_map, PathToFileSourceMap}; use std::path::Path; From cd796dea4937dd1a261f154e5f2e599bbc649165 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:22:01 +0000 Subject: [PATCH 009/416] fix: correct formatting for databus visibility types (#4423) # Description ## Problem\* Resolves ## Summary\* This PR fixes an issue uncovered by #4422 where we're not properly formatting databus visibility modifiers. I've fixed this and added a new test case for regressions. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_fmt/src/utils.rs | 14 +++++++++++++- tooling/nargo_fmt/src/visitor/item.rs | 14 ++++++++++---- tooling/nargo_fmt/tests/expected/databus.nr | 2 ++ tooling/nargo_fmt/tests/input/databus.nr | 2 ++ 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 tooling/nargo_fmt/tests/expected/databus.nr create mode 100644 tooling/nargo_fmt/tests/input/databus.nr diff --git a/tooling/nargo_fmt/src/utils.rs b/tooling/nargo_fmt/src/utils.rs index 5874ebdebbc..94969d45e81 100644 --- a/tooling/nargo_fmt/src/utils.rs +++ b/tooling/nargo_fmt/src/utils.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use crate::items::HasItem; use crate::rewrite; use crate::visitor::{FmtVisitor, Shape}; @@ -143,7 +145,7 @@ impl HasItem for Param { fn format(self, visitor: &FmtVisitor, shape: Shape) -> String { let pattern = visitor.slice(self.pattern.span()); let visibility = match self.visibility { - Visibility::Public => "pub ", + Visibility::Public => "pub", Visibility::Private => "", Visibility::DataBus => "call_data", }; @@ -152,6 +154,7 @@ impl HasItem for Param { pattern.to_string() } else { let ty = rewrite::typ(visitor, shape, self.typ); + let visibility = append_space_if_nonempty(visibility.into()); format!("{pattern}: {visibility}{ty}") } } @@ -183,6 +186,15 @@ pub(crate) fn last_line_contains_single_line_comment(s: &str) -> bool { s.lines().last().map_or(false, |line| line.contains("//")) } +pub(crate) fn append_space_if_nonempty(mut string: Cow) -> Cow { + if !string.is_empty() { + let inner = string.to_mut(); + inner.push(' '); + } + + string +} + pub(crate) fn last_line_used_width(s: &str, offset: usize) -> usize { if s.contains('\n') { last_line_width(s) diff --git a/tooling/nargo_fmt/src/visitor/item.rs b/tooling/nargo_fmt/src/visitor/item.rs index 1825a6e05b0..28aad3c551f 100644 --- a/tooling/nargo_fmt/src/visitor/item.rs +++ b/tooling/nargo_fmt/src/visitor/item.rs @@ -7,7 +7,10 @@ use noirc_frontend::{ use crate::{ rewrite::{self, UseTree}, - utils::{last_line_contains_single_line_comment, last_line_used_width, FindToken}, + utils::{ + append_space_if_nonempty, last_line_contains_single_line_comment, last_line_used_width, + FindToken, + }, visitor::expr::{format_seq, NewlineMode}, }; @@ -119,9 +122,12 @@ impl super::FmtVisitor<'_> { result.push_str("distinct "); } - if let Visibility::Public = func.def.return_visibility { - result.push_str("pub "); - } + let visibility = match func.def.return_visibility { + Visibility::Public => "pub", + Visibility::DataBus => "return_data", + Visibility::Private => "", + }; + result.push_str(&append_space_if_nonempty(visibility.into())); let typ = rewrite::typ(self, self.shape(), func.return_type()); result.push_str(&typ); diff --git a/tooling/nargo_fmt/tests/expected/databus.nr b/tooling/nargo_fmt/tests/expected/databus.nr new file mode 100644 index 00000000000..60934b60b2f --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/databus.nr @@ -0,0 +1,2 @@ +fn main(x: pub u8, y: call_data u8) -> return_data u32 {} + diff --git a/tooling/nargo_fmt/tests/input/databus.nr b/tooling/nargo_fmt/tests/input/databus.nr new file mode 100644 index 00000000000..60934b60b2f --- /dev/null +++ b/tooling/nargo_fmt/tests/input/databus.nr @@ -0,0 +1,2 @@ +fn main(x: pub u8, y: call_data u8) -> return_data u32 {} + From 15c5618c6d15af527287d21ac74eb07cd2b98c14 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:06:54 +0000 Subject: [PATCH 010/416] chore(ci): enforce formatting of noir code in CI (#4422) # Description ## Problem\* Resolves ## Summary\* We currently format everything in the repository except our noir source code. If we enforce this internally then we'll uncover issues in the formatter earlier and provide a good example of what Noir source should look like. We then now run `nargo fmt --check` on the stdlib and `test_programs/execution_success` ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/formatting.yml | 65 +++++++++ noir_stdlib/src/array.nr | 23 ++-- noir_stdlib/src/bigint.nr | 16 +-- noir_stdlib/src/collections/bounded_vec.nr | 2 +- noir_stdlib/src/collections/vec.nr | 8 +- noir_stdlib/src/ec/montcurve.nr | 89 ++++++------ noir_stdlib/src/ec/swcurve.nr | 127 +++++++++--------- noir_stdlib/src/ec/tecurve.nr | 122 ++++++++--------- noir_stdlib/src/ecdsa_secp256k1.nr | 2 +- noir_stdlib/src/ecdsa_secp256r1.nr | 2 +- noir_stdlib/src/field.nr | 11 +- noir_stdlib/src/option.nr | 24 +--- noir_stdlib/src/scalar_mul.nr | 2 +- noir_stdlib/src/schnorr.nr | 2 +- noir_stdlib/src/slice.nr | 12 +- noir_stdlib/src/string.nr | 2 +- noir_stdlib/src/test.nr | 4 +- noir_stdlib/src/uint128.nr | 65 ++++----- test_programs/.gitignore | 3 +- .../closure_explicit_types/src/main.nr | 4 +- .../conditional_regression_579/src/main.nr | 4 +- .../reexports/src/main.nr | 4 +- .../specialization/src/main.nr | 8 +- .../1327_concrete_in_generic/src/main.nr | 28 ++-- .../array_dynamic/src/main.nr | 4 +- .../array_dynamic_blackbox_input/src/main.nr | 2 +- .../array_dynamic_main_output/src/main.nr | 2 +- .../assert_statement_recursive/src/main.nr | 2 +- .../execution_success/bigint/src/main.nr | 10 +- .../execution_success/brillig_cow/src/main.nr | 27 ++-- .../brillig_cow_regression/src/main.nr | 74 +++++----- .../brillig_fns_as_values/src/main.nr | 2 +- .../conditional_regression_661/src/main.nr | 4 +- .../execution_success/databus/src/main.nr | 8 +- .../execution_success/debug_logs/src/main.nr | 6 +- .../distinct_keyword/src/main.nr | 2 +- .../ecdsa_secp256k1/src/main.nr | 10 +- .../ecdsa_secp256r1/src/main.nr | 2 +- .../main_bool_arg/src/main.nr | 2 +- .../operator_overloading/src/main.nr | 6 +- .../regression_3394/src/main.nr | 2 +- .../regression_3607/src/main.nr | 2 +- .../regression_3889/src/main.nr | 1 - .../side_effects_constrain_array/src/main.nr | 4 +- .../execution_success/struct/src/main.nr | 4 +- test_programs/format.sh | 47 +++++++ 46 files changed, 455 insertions(+), 397 deletions(-) create mode 100755 test_programs/format.sh diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml index 43fd6daa91d..279e90f5f6f 100644 --- a/.github/workflows/formatting.yml +++ b/.github/workflows/formatting.yml @@ -63,3 +63,68 @@ jobs: - name: Run `yarn lint` run: yarn lint + + build-nargo: + runs-on: ubuntu-22.04 + timeout-minutes: 30 + + steps: + - name: Checkout Noir repo + uses: actions/checkout@v4 + + - name: Setup toolchain + uses: dtolnay/rust-toolchain@1.73.0 + + - uses: Swatinem/rust-cache@v2 + with: + key: x86_64-unknown-linux-gnu + cache-on-failure: true + save-if: ${{ github.event_name != 'merge_group' }} + + - name: Build Nargo + run: cargo build --package nargo_cli --release + + - name: Package artifacts + run: | + mkdir dist + cp ./target/release/nargo ./dist/nargo + 7z a -ttar -so -an ./dist/* | 7z a -si ./nargo-x86_64-unknown-linux-gnu.tar.gz + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: nargo + path: ./dist/* + retention-days: 3 + + nargo_fmt: + needs: [build-nargo] + name: Nargo fmt + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download nargo binary + uses: actions/download-artifact@v4 + with: + name: nargo + path: ./nargo + + - name: Set nargo on PATH + run: | + nargo_binary="${{ github.workspace }}/nargo/nargo" + chmod +x $nargo_binary + echo "$(dirname $nargo_binary)" >> $GITHUB_PATH + export PATH="$PATH:$(dirname $nargo_binary)" + nargo -V + + - name: Format stdlib + working-directory: ./noir_stdlib + run: nargo fmt --check + + - name: Format test suite + working-directory: ./test_programs + run: ./format.sh check diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index 7871b1a6f9a..3da4b649174 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -17,14 +17,14 @@ impl [T; N] { for i in 0..N { let pos = find_index(sorted_index, i); assert(sorted_index[pos] == i); - } + } // Sort the array using the indexes - for i in 0..N { + for i in 0..N { result[i] = self[sorted_index[i]]; - } + } // Ensure the array is sorted - for i in 0..N-1 { - assert(ordering(result[i], result[i+1])); + for i in 0..N - 1 { + assert(ordering(result[i], result[i + 1])); } result @@ -32,12 +32,12 @@ impl [T; N] { /// Returns the index of the elements in the array that would sort it, using the provided custom sorting function. unconstrained fn get_sorting_index(self, ordering: fn[Env](T, T) -> bool) -> [u64; N] { - let mut result = [0;N]; + let mut result = [0; N]; let mut a = self; for i in 0..N { result[i] = i; } - for i in 1 .. N { + for i in 1..N { for j in 0..i { if ordering(a[i], a[j]) { let old_a_j = a[j]; @@ -45,14 +45,13 @@ impl [T; N] { a[i] = old_a_j; let old_j = result[j]; result[j] = result[i]; - result[i] = old_j; + result[i] = old_j; } } } result } - // Converts an array into a slice. pub fn as_slice(self) -> [T] { let mut slice = []; @@ -68,7 +67,7 @@ impl [T; N] { let first_elem = f(self[0]); let mut ret = [first_elem; N]; - for i in 1 .. self.len() { + for i in 1..self.len() { ret[i] = f(self[i]); } @@ -90,7 +89,7 @@ impl [T; N] { // element of the given array as its starting accumulator value. pub fn reduce(self, f: fn[Env](T, T) -> T) -> T { let mut accumulator = self[0]; - for i in 1 .. self.len() { + for i in 1..self.len() { accumulator = f(accumulator, self[i]); } accumulator @@ -122,7 +121,7 @@ unconstrained fn find_index(a: [u64; N], find: u64) -> u64 { for i in 0..a.len() { if a[i] == find { result = i; - } + } } result } diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index 11026651207..66e81f05812 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -1,5 +1,4 @@ -use crate::ops::{Add, Sub, Mul, Div, Rem,}; - +use crate::ops::{Add, Sub, Mul, Div, Rem}; global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; @@ -13,7 +12,6 @@ global secpr1_fq = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; global secpr1_fr = [0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; - struct BigInt { pointer: u32, @@ -22,17 +20,13 @@ struct BigInt { impl BigInt { #[builtin(bigint_add)] - fn bigint_add(self, other: BigInt) -> BigInt { - } + fn bigint_add(self, other: BigInt) -> BigInt {} #[builtin(bigint_sub)] - fn bigint_sub(self, other: BigInt) -> BigInt { - } + fn bigint_sub(self, other: BigInt) -> BigInt {} #[builtin(bigint_mul)] - fn bigint_mul(self, other: BigInt) -> BigInt { - } + fn bigint_mul(self, other: BigInt) -> BigInt {} #[builtin(bigint_div)] - fn bigint_div(self, other: BigInt) -> BigInt { - } + fn bigint_div(self, other: BigInt) -> BigInt {} #[builtin(bigint_from_le_bytes)] fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} #[builtin(bigint_to_le_bytes)] diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index a4aa4823f38..f78d86de77d 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -29,7 +29,7 @@ impl BoundedVec { self.len } - pub fn max_len(_self: BoundedVec) -> u64{ + pub fn max_len(_self: BoundedVec) -> u64 { MaxLen } diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index 2e7945be827..deec98185ff 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -19,12 +19,12 @@ impl Vec { /// points beyond the end of the vector. pub fn get(self, index: u64) -> T { self.slice[index] - } + } /// Push a new element to the end of the vector, returning a /// new vector with a length one greater than the /// original unmodified vector. - pub fn push(&mut self, elem: T) { + pub fn push(&mut self, elem: T) { self.slice = self.slice.push_back(elem); } @@ -32,7 +32,7 @@ impl Vec { /// a new vector with a length of one less than the given vector, /// as well as the popped element. /// Panics if the given vector's length is zero. - pub fn pop(&mut self) -> T { + pub fn pop(&mut self) -> T { let (popped_slice, last_elem) = self.slice.pop_back(); self.slice = popped_slice; last_elem @@ -42,7 +42,7 @@ impl Vec { /// after it to the right pub fn insert(&mut self, index: u64, elem: T) { self.slice = self.slice.insert(index, elem); - } + } /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the removed element diff --git a/noir_stdlib/src/ec/montcurve.nr b/noir_stdlib/src/ec/montcurve.nr index 83a17bae322..7dc756781c0 100644 --- a/noir_stdlib/src/ec/montcurve.nr +++ b/noir_stdlib/src/ec/montcurve.nr @@ -31,7 +31,7 @@ mod affine { impl Point { // Point constructor pub fn new(x: Field, y: Field) -> Self { - Self {x, y, infty: false} + Self { x, y, infty: false } } // Check if zero @@ -45,30 +45,30 @@ mod affine { curvegroup::Point::zero() } else { let (x,y) = (self.x, self.y); - curvegroup::Point::new(x,y,1) + curvegroup::Point::new(x, y, 1) } } // Additive identity pub fn zero() -> Self { - Self {x: 0, y: 0, infty: true} + Self { x: 0, y: 0, infty: true } } // Negation fn negate(self) -> Self { let Self {x, y, infty} = self; - Self {x, y: 0-y, infty} + Self { x, y: 0 - y, infty } } // Map into equivalent Twisted Edwards curve fn into_tecurve(self) -> TEPoint { let Self {x, y, infty} = self; - - if infty | (y*(x+1) == 0) { + + if infty | (y * (x + 1) == 0) { TEPoint::zero() } else { - TEPoint::new(x/y, (x-1)/(x+1)) + TEPoint::new(x / y, (x - 1) / (x + 1)) } } } @@ -84,9 +84,9 @@ mod affine { pub fn new(j: Field, k: Field, gen: Point) -> Self { // Check curve coefficients assert(k != 0); - assert(j*j != 4); + assert(j * j != 4); - let curve = Self {j, k, gen}; + let curve = Self { j, k, gen }; // gen should be on the curve assert(curve.contains(curve.gen)); @@ -103,8 +103,8 @@ mod affine { pub fn contains(self, p: Point) -> bool { let Self {j, k, gen: _gen} = self; let Point {x, y, infty: infty} = p; - - infty | (k*y*y == x*(x*x + j*x + 1)) + + infty | (k * y * y == x * (x * x + j * x + 1)) } // Point addition @@ -122,7 +122,7 @@ mod affine { fn mul(self, n: Field, p: Point) -> Point { self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() } - + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); @@ -142,15 +142,15 @@ mod affine { // Conversion to equivalent Twisted Edwards curve fn into_tecurve(self) -> TECurve { let Self {j, k, gen} = self; - TECurve::new((j+2)/k, (j-2)/k, gen.into_tecurve()) + TECurve::new((j + 2) / k, (j - 2) / k, gen.into_tecurve()) } // Conversion to equivalent Short Weierstraß curve pub fn into_swcurve(self) -> SWCurve { let j = self.j; let k = self.k; - let a0 = (3-j*j)/(3*k*k); - let b0 = (2*j*j*j - 9*j)/(27*k*k*k); + let a0 = (3 - j * j) / (3 * k * k); + let b0 = (2 * j * j * j - 9 * j) / (27 * k * k * k); SWCurve::new(a0, b0, self.map_into_swcurve(self.gen)) } @@ -160,8 +160,7 @@ mod affine { if p.is_zero() { SWPoint::zero() } else { - SWPoint::new((3*p.x + self.j)/(3*self.k), - p.y/self.k) + SWPoint::new((3 * p.x + self.j) / (3 * self.k), p.y / self.k) } } @@ -170,8 +169,8 @@ mod affine { let SWPoint {x, y, infty} = p; let j = self.j; let k = self.k; - - Point {x: (3*k*x - j)/3, y: y*k, infty} + + Point { x: (3 * k * x - j) / 3, y: y * k, infty } } // Elligator 2 map-to-curve method; see . @@ -179,18 +178,18 @@ mod affine { let j = self.j; let k = self.k; let z = ZETA; // Non-square Field element required for map - + // Check whether curve is admissible assert(j != 0); - let l = (j*j - 4)/(k*k); + let l = (j * j - 4) / (k * k); assert(l != 0); assert(is_square(l) == false); - let x1 = safe_inverse(1+z*u*u)*(0 - (j/k)); - - let gx1 = x1*x1*x1 + (j/k)*x1*x1 + x1/(k*k); - let x2 = 0 - x1 - (j/k); - let gx2 = x2*x2*x2 + (j/k)*x2*x2 + x2/(k*k); + let x1 = safe_inverse(1 + z * u * u) * (0 - (j / k)); + + let gx1 = x1 * x1 * x1 + (j / k) * x1 * x1 + x1 / (k * k); + let x2 = 0 - x1 - (j / k); + let gx2 = x2 * x2 * x2 + (j / k) * x2 * x2 + x2 / (k * k); let x = if is_square(gx1) { x1 } else { x2 }; @@ -202,13 +201,12 @@ mod affine { if y0.sgn0() == 0 { y0 } else { 0 - y0 } }; - Point::new(x*k, y*k) - + Point::new(x * k, y * k) } // SWU map-to-curve method (via rational map) fn swu_map(self, z: Field, u: Field) -> Point { - self.map_from_swcurve(self.into_swcurve().swu_map(z,u)) + self.map_from_swcurve(self.into_swcurve().swu_map(z, u)) } } } @@ -240,7 +238,7 @@ mod curvegroup { impl Point { // Point constructor pub fn new(x: Field, y: Field, z: Field) -> Self { - Self {x, y, z} + Self { x, y, z } } // Check if zero @@ -254,20 +252,20 @@ mod curvegroup { affine::Point::zero() } else { let (x,y,z) = (self.x, self.y, self.z); - affine::Point::new(x/z, y/z) + affine::Point::new(x / z, y / z) } } // Additive identity pub fn zero() -> Self { - Self {x: 0, y: 1,z: 0} + Self { x: 0, y: 1, z: 0 } } // Negation fn negate(self) -> Self { let Self {x, y, z} = self; - Point::new(x, 0-y, z) + Point::new(x, 0 - y, z) } // Map into equivalent Twisted Edwards curve @@ -287,9 +285,9 @@ mod curvegroup { pub fn new(j: Field, k: Field, gen: Point) -> Self { // Check curve coefficients assert(k != 0); - assert(j*j != 4); + assert(j * j != 4); - let curve = Self {j, k, gen}; + let curve = Self { j, k, gen }; // gen should be on the curve assert(curve.contains(curve.gen)); @@ -306,8 +304,8 @@ mod curvegroup { pub fn contains(self, p: Point) -> bool { let Self {j, k, gen: _gen} = self; let Point {x, y, z} = p; - - k*y*y*z == x*(x*x + j*x*z + z*z) + + k * y * y * z == x * (x * x + j * x * z + z * z) } // Point addition @@ -320,12 +318,12 @@ mod curvegroup { fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_tecurve().bit_mul(bits, p.into_tecurve()).into_montcurve() } - + // Scalar multiplication (p + ... + p n times) pub fn mul(self, n: Field, p: Point) -> Point { self.into_tecurve().mul(n, p.into_tecurve()).into_montcurve() } - + // Multi-scalar multiplication (n[0]*p[0] + ... + n[N]*p[N], where * denotes scalar multiplication) fn msm(self, n: [Field; N], p: [Point; N]) -> Point { let mut out = Point::zero(); @@ -345,18 +343,17 @@ mod curvegroup { // Conversion to equivalent Twisted Edwards curve fn into_tecurve(self) -> TECurve { let Self {j, k, gen} = self; - TECurve::new((j+2)/k, (j-2)/k, gen.into_tecurve()) + TECurve::new((j + 2) / k, (j - 2) / k, gen.into_tecurve()) } // Conversion to equivalent Short Weierstraß curve fn into_swcurve(self) -> SWCurve { let j = self.j; let k = self.k; - let a0 = (3-j*j)/(3*k*k); - let b0 = (2*j*j*j - 9*j)/(27*k*k*k); + let a0 = (3 - j * j) / (3 * k * k); + let b0 = (2 * j * j * j - 9 * j) / (27 * k * k * k); - SWCurve::new(a0, b0, - self.map_into_swcurve(self.gen)) + SWCurve::new(a0, b0, self.map_into_swcurve(self.gen)) } // Point mapping into equivalent Short Weierstraß curve @@ -373,10 +370,10 @@ mod curvegroup { fn elligator2_map(self, u: Field) -> Point { self.into_affine().elligator2_map(u).into_group() } - + // SWU map-to-curve method (via rational map) fn swu_map(self, z: Field, u: Field) -> Point { - self.into_affine().swu_map(z,u).into_group() + self.into_affine().swu_map(z, u).into_group() } } } diff --git a/noir_stdlib/src/ec/swcurve.nr b/noir_stdlib/src/ec/swcurve.nr index e64f5a7be02..9dd324f3085 100644 --- a/noir_stdlib/src/ec/swcurve.nr +++ b/noir_stdlib/src/ec/swcurve.nr @@ -27,14 +27,14 @@ mod affine { impl Point { // Point constructor pub fn new(x: Field, y: Field) -> Self { - Self {x, y, infty: false} + Self { x, y, infty: false } } // Check if zero pub fn is_zero(self) -> bool { self.eq(Point::zero()) } - + // Conversion to CurveGroup coordinates fn into_group(self) -> curvegroup::Point { let Self {x, y, infty} = self; @@ -45,16 +45,16 @@ mod affine { curvegroup::Point::new(x, y, 1) } } - + // Additive identity pub fn zero() -> Self { - Self {x: 0, y: 0, infty: true} + Self { x: 0, y: 0, infty: true } } - + // Negation fn negate(self) -> Self { let Self {x, y, infty} = self; - Self {x, y: 0-y, infty} + Self { x, y: 0 - y, infty } } } @@ -72,8 +72,8 @@ mod affine { // Curve constructor pub fn new(a: Field, b: Field, gen: Point) -> Curve { // Check curve coefficients - assert(4*a*a*a + 27*b*b != 0); - + assert(4 * a * a * a + 27 * b * b != 0); + let curve = Curve { a, b, gen }; // gen should be on the curve @@ -85,16 +85,16 @@ mod affine { // Conversion to CurveGroup coordinates fn into_group(self) -> curvegroup::Curve { let Curve{a, b, gen} = self; - - curvegroup::Curve {a, b, gen: gen.into_group()} + + curvegroup::Curve { a, b, gen: gen.into_group() } } // Membership check pub fn contains(self, p: Point) -> bool { let Point {x, y, infty} = p; - infty | (y*y == x*x*x + self.a*x + self.b) + infty | (y * y == x * x * x + self.a * x + self.b) } - + // Point addition, implemented in terms of mixed addition for reasons of efficiency pub fn add(self, p1: Point, p2: Point) -> Point { self.mixed_add(p1, p2.into_group()).into_affine() @@ -109,9 +109,9 @@ mod affine { } else { let Point {x: x1, y: y1, infty: _inf} = p1; let curvegroup::Point {x: x2, y: y2, z: z2} = p2; - let you1 = x1*z2*z2; + let you1 = x1 * z2 * z2; let you2 = x2; - let s1 = y1*z2*z2*z2; + let s1 = y1 * z2 * z2 * z2; let s2 = y2; if you1 == you2 { @@ -120,15 +120,14 @@ mod affine { } else { self.into_group().double(p2) } - } else - { + } else { let h = you2 - you1; let r = s2 - s1; - let x3 = r*r - h*h*h - 2*you1*h*h; - let y3 = r*(you1*h*h - x3) - s1*h*h*h; - let z3 = h*z2; + let x3 = r * r - h * h * h - 2 * you1 * h * h; + let y3 = r * (you1 * h * h - x3) - s1 * h * h * h; + let z3 = h * z2; - curvegroup::Point::new(x3,y3,z3) + curvegroup::Point::new(x3, y3, z3) } } } @@ -138,7 +137,7 @@ mod affine { fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_group().bit_mul(bits, p.into_group()).into_affine() } - + // Scalar multiplication (p + ... + p n times) pub fn mul(self, n: Field, p: Point) -> Point { self.into_group().mul(n, p.into_group()).into_affine() @@ -165,17 +164,25 @@ mod affine { // where g(x) = x^3 + a*x + b. swu_map(c,z,.) then maps a Field element to a point on curve c. fn swu_map(self, z: Field, u: Field) -> Point { // Check whether curve is admissible - assert(self.a*self.b != 0); - + assert(self.a * self.b != 0); + let Curve {a, b, gen: _gen} = self; - - let tv1 = safe_inverse(z*z*u*u*u*u + u*u*z); - let x1 = if tv1 == 0 {b/(z*a)} else {(0-b/a)*(1 + tv1)}; - let gx1 = x1*x1*x1 + a*x1 + b; - let x2 = z*u*u*x1; - let gx2 = x2*x2*x2 + a*x2 + b; - let (x,y) = if is_square(gx1) {(x1, sqrt(gx1))} else {(x2, sqrt(gx2))}; - Point::new(x, if u.sgn0() != y.sgn0() {0-y} else {y}) + + let tv1 = safe_inverse(z * z * u * u * u * u + u * u * z); + let x1 = if tv1 == 0 { + b / (z * a) + } else { + (0 - b / a) * (1 + tv1) + }; + let gx1 = x1 * x1 * x1 + a * x1 + b; + let x2 = z * u * u * x1; + let gx2 = x2 * x2 * x2 + a * x2 + b; + let (x,y) = if is_square(gx1) { + (x1, sqrt(gx1)) + } else { + (x2, sqrt(gx2)) + }; + Point::new(x, if u.sgn0() != y.sgn0() { 0 - y } else { y }) } } } @@ -205,14 +212,14 @@ mod curvegroup { impl Point { // Point constructor pub fn new(x: Field, y: Field, z: Field) -> Self { - Self {x, y, z} + Self { x, y, z } } // Check if zero pub fn is_zero(self) -> bool { self.eq(Point::zero()) } - + // Conversion to affine coordinates pub fn into_affine(self) -> affine::Point { let Self {x, y, z} = self; @@ -220,20 +227,19 @@ mod curvegroup { if z == 0 { affine::Point::zero() } else { - affine::Point::new(x/(z*z), y/(z*z*z)) + affine::Point::new(x / (z * z), y / (z * z * z)) } } // Additive identity pub fn zero() -> Self { - Self {x: 0, y: 0, z: 0} + Self { x: 0, y: 0, z: 0 } } - - + // Negation fn negate(self) -> Self { let Self {x, y, z} = self; - Self {x, y: 0-y, z} + Self { x, y: 0 - y, z } } } @@ -250,8 +256,8 @@ mod curvegroup { // Curve constructor pub fn new(a: Field, b: Field, gen: Point) -> Curve { // Check curve coefficients - assert(4*a*a*a + 27*b*b != 0); - + assert(4 * a * a * a + 27 * b * b != 0); + let curve = Curve { a, b, gen }; // gen should be on the curve @@ -264,7 +270,7 @@ mod curvegroup { pub fn into_affine(self) -> affine::Curve { let Curve{a, b, gen} = self; - affine::Curve {a, b, gen: gen.into_affine()} + affine::Curve { a, b, gen: gen.into_affine() } } // Membership check @@ -273,13 +279,12 @@ mod curvegroup { if z == 0 { true } else { - y*y == x*x*x + self.a*x*z*z*z*z + self.b*z*z*z*z*z*z + y * y == x * x * x + self.a * x * z * z * z * z + self.b * z * z * z * z * z * z } } - + // Addition pub fn add(self, p1: Point, p2: Point) -> Point { - if p1.is_zero() { p2 } else if p2.is_zero() { @@ -287,10 +292,10 @@ mod curvegroup { } else { let Point {x: x1, y: y1, z: z1} = p1; let Point {x: x2, y: y2, z: z2} = p2; - let you1 = x1*z2*z2; - let you2 = x2*z1*z1; - let s1 = y1*z2*z2*z2; - let s2 = y2*z1*z1*z1; + let you1 = x1 * z2 * z2; + let you2 = x2 * z1 * z1; + let s1 = y1 * z2 * z2 * z2; + let s2 = y2 * z1 * z1 * z1; if you1 == you2 { if s1 != s2 { @@ -301,11 +306,11 @@ mod curvegroup { } else { let h = you2 - you1; let r = s2 - s1; - let x3 = r*r - h*h*h - 2*you1*h*h; - let y3 = r*(you1*h*h - x3) - s1*h*h*h; - let z3 = h*z1*z2; + let x3 = r * r - h * h * h - 2 * you1 * h * h; + let y3 = r * (you1 * h * h - x3) - s1 * h * h * h; + let z3 = h * z1 * z2; - Point::new(x3,y3,z3) + Point::new(x3, y3, z3) } } } @@ -313,19 +318,19 @@ mod curvegroup { // Point doubling pub fn double(self, p: Point) -> Point { let Point {x, y, z} = p; - + if p.is_zero() { p } else if y == 0 { Point::zero() } else { - let s = 4*x*y*y; - let m = 3*x*x + self.a*z*z*z*z; - let x0 = m*m - 2*s; - let y0 = m*(s-x0) - 8*y*y*y*y; - let z0 = 2*y*z; + let s = 4 * x * y * y; + let m = 3 * x * x + self.a * z * z * z * z; + let x0 = m * m - 2 * s; + let y0 = m * (s - x0) - 8 * y * y * y * y; + let z0 = 2 * y * z; - Point::new(x0,y0,z0) + Point::new(x0, y0, z0) } } @@ -351,7 +356,7 @@ mod curvegroup { let mut n_as_bits: [u1; 254] = [0; 254]; let tmp = n.to_le_bits(N_BITS as u32); for i in 0..254 { - n_as_bits[i] = tmp[i]; + n_as_bits[i] = tmp[i]; } self.bit_mul(n_as_bits, p) @@ -375,7 +380,7 @@ mod curvegroup { // Simplified SWU map-to-curve method fn swu_map(self, z: Field, u: Field) -> Point { - self.into_affine().swu_map(z,u).into_group() + self.into_affine().swu_map(z, u).into_group() } } } diff --git a/noir_stdlib/src/ec/tecurve.nr b/noir_stdlib/src/ec/tecurve.nr index 5333ece4c4a..506fe89313a 100644 --- a/noir_stdlib/src/ec/tecurve.nr +++ b/noir_stdlib/src/ec/tecurve.nr @@ -40,18 +40,18 @@ mod affine { fn into_group(self) -> curvegroup::Point { let Self {x, y} = self; - curvegroup::Point::new(x, y, x*y, 1) + curvegroup::Point::new(x, y, x * y, 1) } // Additive identity pub fn zero() -> Self { - Point::new(0,1) + Point::new(0, 1) } // Negation fn negate(self) -> Self { let Self {x, y} = self; - Point::new(0-x, y) + Point::new(0 - x, y) } // Map into prime-order subgroup of equivalent Montgomery curve @@ -60,10 +60,10 @@ mod affine { MPoint::zero() } else { let Self {x, y} = self; - let x0 = (1+y)/(1-y); - let y0 = (1+y)/(x*(1-y)); + let x0 = (1 + y) / (1 - y); + let y0 = (1 + y) / (x * (1 - y)); - MPoint::new(x0,y0) + MPoint::new(x0, y0) } } } @@ -81,9 +81,9 @@ mod affine { // Curve constructor pub fn new(a: Field, d: Field, gen: Point) -> Curve { // Check curve coefficients - assert(a*d*(a-d) != 0); - - let curve = Curve {a, d, gen}; + assert(a * d * (a - d) != 0); + + let curve = Curve { a, d, gen }; // gen should be on the curve assert(curve.contains(curve.gen)); @@ -95,15 +95,15 @@ mod affine { fn into_group(self) -> curvegroup::Curve { let Curve{a, d, gen} = self; - curvegroup::Curve {a, d, gen: gen.into_group()} + curvegroup::Curve { a, d, gen: gen.into_group() } } - + // Membership check pub fn contains(self, p: Point) -> bool { let Point {x, y} = p; - self.a*x*x + y*y == 1 + self.d*x*x*y*y + self.a * x * x + y * y == 1 + self.d * x * x * y * y } - + // Point addition, implemented in terms of mixed addition for reasons of efficiency pub fn add(self, p1: Point, p2: Point) -> Point { self.mixed_add(p1, p2.into_group()).into_affine() @@ -114,20 +114,20 @@ mod affine { let Point{x: x1, y: y1} = p1; let curvegroup::Point{x: x2, y: y2, t: t2, z: z2} = p2; - let a = x1*x2; - let b = y1*y2; - let c = self.d*x1*y1*t2; - let e = (x1 + y1)*(x2 + y2) - a - b; + let a = x1 * x2; + let b = y1 * y2; + let c = self.d * x1 * y1 * t2; + let e = (x1 + y1) * (x2 + y2) - a - b; let f = z2 - c; let g = z2 + c; - let h = b - self.a*a; + let h = b - self.a * a; - let x = e*f; - let y = g*h; - let t = e*h; - let z = f*g; + let x = e * f; + let y = g * h; + let t = e * h; + let z = f * g; - curvegroup::Point::new(x,y,t,z) + curvegroup::Point::new(x, y, t, z) } // Scalar multiplication with scalar represented by a bit array (little-endian convention). @@ -135,7 +135,7 @@ mod affine { fn bit_mul(self, bits: [u1; N], p: Point) -> Point { self.into_group().bit_mul(bits, p.into_group()).into_affine() } - + // Scalar multiplication (p + ... + p n times) fn mul(self, n: Field, p: Point) -> Point { self.into_group().mul(n, p.into_group()).into_affine() @@ -159,10 +159,10 @@ mod affine { // Conversion to equivalent Montgomery curve pub fn into_montcurve(self) -> MCurve { - let j = 2*(self.a + self.d)/(self.a - self.d); - let k = 4/(self.a - self.d); + let j = 2 * (self.a + self.d) / (self.a - self.d); + let k = 4 / (self.a - self.d); let gen_montcurve = self.gen.into_montcurve(); - + MCurve::new(j, k, gen_montcurve) } @@ -188,7 +188,7 @@ mod affine { // Simplified SWU map-to-curve method (via rational map) fn swu_map(self, z: Field, u: Field) -> Point { - self.into_montcurve().swu_map(z,u).into_tecurve() + self.into_montcurve().swu_map(z, u).into_tecurve() } } } @@ -222,7 +222,7 @@ mod curvegroup { impl Point { // Point constructor pub fn new(x: Field, y: Field, t: Field, z: Field) -> Self { - Self {x, y, t, z} + Self { x, y, t, z } } // Check if zero @@ -235,19 +235,19 @@ mod curvegroup { pub fn into_affine(self) -> affine::Point { let Self {x, y, t: _t, z} = self; - affine::Point::new(x/z, y/z) + affine::Point::new(x / z, y / z) } // Additive identity pub fn zero() -> Self { - Point::new(0,1,0,1) + Point::new(0, 1, 0, 1) } // Negation fn negate(self) -> Self { let Self {x, y, t, z} = self; - Point::new(0-x, y, 0-t, z) + Point::new(0 - x, y, 0 - t, z) } // Map into prime-order subgroup of equivalent Montgomery curve @@ -269,8 +269,8 @@ mod curvegroup { // Curve constructor pub fn new(a: Field, d: Field, gen: Point) -> Curve { // Check curve coefficients - assert(a*d*(a-d) != 0); - + assert(a * d * (a - d) != 0); + let curve = Curve { a, d, gen }; // gen should be on the curve @@ -283,14 +283,16 @@ mod curvegroup { pub fn into_affine(self) -> affine::Curve { let Curve{a, d, gen} = self; - affine::Curve {a, d, gen: gen.into_affine()} + affine::Curve { a, d, gen: gen.into_affine() } } // Membership check pub fn contains(self, p: Point) -> bool { let Point {x, y, t, z} = p; - (z != 0) & (z*t == x*y) & (z*z*(self.a*x*x + y*y) == z*z*z*z + self.d*x*x*y*y) + (z != 0) + & (z * t == x * y) + & (z * z * (self.a * x * x + y * y) == z * z * z * z + self.d * x * x * y * y) } // Point addition @@ -298,40 +300,40 @@ mod curvegroup { let Point{x: x1, y: y1, t: t1, z: z1} = p1; let Point{x: x2, y: y2, t: t2, z: z2} = p2; - let a = x1*x2; - let b = y1*y2; - let c = self.d*t1*t2; - let d = z1*z2; - let e = (x1 + y1)*(x2 + y2) - a - b; + let a = x1 * x2; + let b = y1 * y2; + let c = self.d * t1 * t2; + let d = z1 * z2; + let e = (x1 + y1) * (x2 + y2) - a - b; let f = d - c; let g = d + c; - let h = b - self.a*a; + let h = b - self.a * a; - let x = e*f; - let y = g*h; - let t = e*h; - let z = f*g; + let x = e * f; + let y = g * h; + let t = e * h; + let z = f * g; - Point::new(x,y,t,z) + Point::new(x, y, t, z) } // Point doubling, cf. §3.3 pub fn double(self, p: Point) -> Point { let Point{x, y, t: _t, z} = p; - let a = x*x; - let b = y*y; - let c = 2*z*z; - let d = self.a*a; - let e = (x + y)*(x + y) - a - b; + let a = x * x; + let b = y * y; + let c = 2 * z * z; + let d = self.a * a; + let e = (x + y) * (x + y) - a - b; let g = d + b; let f = g - c; let h = d - b; - let x0 = e*f; - let y0 = g*h; - let t0 = e*h; - let z0 = f*g; + let x0 = e * f; + let y0 = g * h; + let t0 = e * h; + let z0 = f * g; Point::new(x0, y0, t0, z0) } @@ -340,7 +342,7 @@ mod curvegroup { // If k is the natural number represented by `bits`, then this computes p + ... + p k times. fn bit_mul(self, bits: [u1; N], p: Point) -> Point { let mut out = Point::zero(); - + for i in 0..N { out = self.add( self.add(out, out), @@ -349,7 +351,7 @@ mod curvegroup { out } - + // Scalar multiplication (p + ... + p n times) pub fn mul(self, n: Field, p: Point) -> Point { let N_BITS = crate::field::modulus_num_bits(); @@ -358,7 +360,7 @@ mod curvegroup { let mut n_as_bits: [u1; 254] = [0; 254]; let tmp = n.to_le_bits(N_BITS as u32); for i in 0..254 { - n_as_bits[i] = tmp[i]; + n_as_bits[i] = tmp[i]; } self.bit_mul(n_as_bits, p) @@ -407,7 +409,7 @@ mod curvegroup { // Simplified SWU map-to-curve method (via rational map) fn swu_map(self, z: Field, u: Field) -> Point { - self.into_montcurve().swu_map(z,u).into_tecurve() + self.into_montcurve().swu_map(z, u).into_tecurve() } } } diff --git a/noir_stdlib/src/ecdsa_secp256k1.nr b/noir_stdlib/src/ecdsa_secp256k1.nr index e8d9af2230f..b72a1acd041 100644 --- a/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir_stdlib/src/ecdsa_secp256k1.nr @@ -7,4 +7,4 @@ pub fn verify_signature( message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256k1 -{} \ No newline at end of file +{} diff --git a/noir_stdlib/src/ecdsa_secp256r1.nr b/noir_stdlib/src/ecdsa_secp256r1.nr index 9fe932a2f3d..ef92bf24ae4 100644 --- a/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir_stdlib/src/ecdsa_secp256r1.nr @@ -7,4 +7,4 @@ pub fn verify_signature( message_hash: [u8; N] ) -> bool // docs:end:ecdsa_secp256r1 -{} \ No newline at end of file +{} diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index a7278d85999..0f4c2caffdf 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -6,7 +6,7 @@ impl Field { crate::assert_constant(bit_size); self.__to_le_bits(bit_size) } - + pub fn to_be_bits(self: Self, bit_size: u32) -> [u1] { crate::assert_constant(bit_size); self.__to_be_bits(bit_size) @@ -14,7 +14,7 @@ impl Field { #[builtin(to_le_bits)] fn __to_le_bits(self, _bit_size: u32) -> [u1] {} - + #[builtin(to_be_bits)] fn __to_be_bits(self, bit_size: u32) -> [u1] {} @@ -35,7 +35,6 @@ impl Field { self.to_be_radix(256, byte_size) } - pub fn to_le_radix(self: Self, radix: u32, result_len: u32) -> [u8] { crate::assert_constant(radix); crate::assert_constant(result_len); @@ -48,17 +47,14 @@ impl Field { self.__to_be_radix(radix, result_len) } - - // decompose `_self` into a `_result_len` vector over the `_radix` basis // `_radix` must be less than 256 #[builtin(to_le_radix)] fn __to_le_radix(self, radix: u32, result_len: u32) -> [u8] {} - + #[builtin(to_be_radix)] fn __to_be_radix(self, radix: u32, result_len: u32) -> [u8] {} - // Returns self to the power of the given exponent value. // Caution: we assume the exponent fits into 32 bits // using a bigger bit size impacts negatively the performance and should be done only if the exponent does not fit in 32 bits @@ -85,7 +81,6 @@ impl Field { lt_fallback(self, another) } } - } #[builtin(modulus_num_bits)] diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index cab95731d05..1c32f758af7 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -39,11 +39,7 @@ impl Option { /// Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. pub fn unwrap_or(self, default: T) -> T { - if self._is_some { - self._value - } else { - default - } + if self._is_some { self._value } else { default } } /// Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return @@ -112,31 +108,19 @@ impl Option { /// If self is Some, return self. Otherwise, return `other`. pub fn or(self, other: Self) -> Self { - if self._is_some { - self - } else { - other - } + if self._is_some { self } else { other } } /// If self is Some, return self. Otherwise, return `default()`. pub fn or_else(self, default: fn[Env]() -> Self) -> Self { - if self._is_some { - self - } else { - default() - } + if self._is_some { self } else { default() } } // If only one of the two Options is Some, return that option. // Otherwise, if both options are Some or both are None, None is returned. pub fn xor(self, other: Self) -> Self { if self._is_some { - if other._is_some { - Option::none() - } else { - self - } + if other._is_some { Option::none() } else { self } } else if other._is_some { other } else { diff --git a/noir_stdlib/src/scalar_mul.nr b/noir_stdlib/src/scalar_mul.nr index 1a7f1ad707c..eee7aac39f2 100644 --- a/noir_stdlib/src/scalar_mul.nr +++ b/noir_stdlib/src/scalar_mul.nr @@ -6,7 +6,7 @@ struct EmbeddedCurvePoint { } impl EmbeddedCurvePoint { - fn double(self) -> EmbeddedCurvePoint { + fn double(self) -> EmbeddedCurvePoint { embedded_curve_add(self, self) } } diff --git a/noir_stdlib/src/schnorr.nr b/noir_stdlib/src/schnorr.nr index 33656254550..757963d40d7 100644 --- a/noir_stdlib/src/schnorr.nr +++ b/noir_stdlib/src/schnorr.nr @@ -7,4 +7,4 @@ pub fn verify_signature( message: [u8; N] ) -> bool // docs:end:schnorr_verify -{} \ No newline at end of file +{} diff --git a/noir_stdlib/src/slice.nr b/noir_stdlib/src/slice.nr index bb5c43e497b..ea8d09d14ce 100644 --- a/noir_stdlib/src/slice.nr +++ b/noir_stdlib/src/slice.nr @@ -3,34 +3,34 @@ impl [T] { /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_back)] - pub fn push_back(self, elem: T) -> Self { } + pub fn push_back(self, elem: T) -> Self {} /// Push a new element to the front of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. #[builtin(slice_push_front)] - pub fn push_front(self, elem: T) -> Self { } + pub fn push_front(self, elem: T) -> Self {} /// Remove the last element of the slice, returning the /// popped slice and the element in a tuple #[builtin(slice_pop_back)] - pub fn pop_back(self) -> (Self, T) { } + pub fn pop_back(self) -> (Self, T) {} /// Remove the first element of the slice, returning the /// element and the popped slice in a tuple #[builtin(slice_pop_front)] - pub fn pop_front(self) -> (T, Self) { } + pub fn pop_front(self) -> (T, Self) {} /// Insert an element at a specified index, shifting all elements /// after it to the right #[builtin(slice_insert)] - pub fn insert(self, index: u64, elem: T) -> Self { } + pub fn insert(self, index: u64, elem: T) -> Self {} /// Remove an element at a specified index, shifting all elements /// after it to the left, returning the altered slice and /// the removed element #[builtin(slice_remove)] - pub fn remove(self, index: u64) -> (Self, T) { } + pub fn remove(self, index: u64) -> (Self, T) {} // Append each element of the `other` slice to the end of `self`. // This returns a new slice and leaves both input slices unchanged. diff --git a/noir_stdlib/src/string.nr b/noir_stdlib/src/string.nr index ad6fd19e2de..12b5a1e75ec 100644 --- a/noir_stdlib/src/string.nr +++ b/noir_stdlib/src/string.nr @@ -2,7 +2,7 @@ use crate::collections::vec::Vec; impl str { /// Converts the given string into a byte array #[builtin(str_as_bytes)] - pub fn as_bytes(self) -> [u8; N] { } + pub fn as_bytes(self) -> [u8; N] {} /// return a byte vector of the str content pub fn as_bytes_vec(self: Self) -> Vec { diff --git a/noir_stdlib/src/test.nr b/noir_stdlib/src/test.nr index 560cfde741c..e1c320215de 100644 --- a/noir_stdlib/src/test.nr +++ b/noir_stdlib/src/test.nr @@ -19,9 +19,7 @@ struct OracleMock { impl OracleMock { unconstrained pub fn mock(name: str) -> Self { - Self { - id: create_mock_oracle(name), - } + Self { id: create_mock_oracle(name) } } unconstrained pub fn with_params

(self, params: P) -> Self { diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index c8c6217de90..d6f0b1e2232 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -13,14 +13,11 @@ impl U128 { pub fn from_u64s_le(lo: u64, hi: u64) -> U128 { // in order to handle multiplication, we need to represent the product of two u64 without overflow assert(crate::field::modulus_num_bits() as u32 > 128); - U128 { - lo: lo as Field, - hi: hi as Field, - } + U128 { lo: lo as Field, hi: hi as Field } } pub fn from_u64s_be(hi: u64, lo: u64) -> U128 { - U128::from_u64s_le(lo,hi) + U128::from_u64s_le(lo, hi) } pub fn from_le_bytes(bytes: [u8; 16]) -> U128 { @@ -36,16 +33,13 @@ impl U128 { hi += (bytes[i] as Field)*base; base *= 256; } - U128 { - lo, - hi, - } + U128 { lo, hi } } pub fn to_be_bytes(self: Self) -> [u8; 16] { let lo = self.lo.to_be_bytes(8); let hi = self.hi.to_be_bytes(8); - let mut bytes = [0;16]; + let mut bytes = [0; 16]; for i in 0..8 { bytes[i] = hi[i]; bytes[i+8] = lo[i]; @@ -56,7 +50,7 @@ impl U128 { pub fn to_le_bytes(self: Self) -> [u8; 16] { let lo = self.lo.to_le_bytes(8); let hi = self.hi.to_le_bytes(8); - let mut bytes = [0;16]; + let mut bytes = [0; 16]; for i in 0..8 { bytes[i] = lo[i]; bytes[i+8] = hi[i]; @@ -73,9 +67,9 @@ impl U128 { let mut lo = 0; let mut hi = 0; - let mut base = 1; + let mut base = 1; if N <= 18 { - for i in 0..N-2 { + for i in 0..N - 2 { lo += U128::decode_ascii(bytes[N-i-1])*base; base = base*16; } @@ -85,27 +79,21 @@ impl U128 { base = base*16; } base = 1; - for i in 17..N-1 { + for i in 17..N - 1 { hi += U128::decode_ascii(bytes[N-i])*base; base = base*16; } } - U128 { - lo: lo as Field, - hi: hi as Field, - } + U128 { lo: lo as Field, hi: hi as Field } } fn decode_ascii(ascii: u8) -> Field { if ascii < 58 { ascii - 48 + } else if ascii < 71 { + ascii - 55 } else { - if ascii < 71 { - ascii - 55 - } else { - ascii - 87 - } - + ascii - 87 } as Field } @@ -114,15 +102,14 @@ impl U128 { (U128::from_u64s_le(0, 0), self) } else { //TODO check if this can overflow? - let (q,r) = self.unconstrained_div(b * U128::from_u64s_le(2,0)); - let q_mul_2 = q * U128::from_u64s_le(2,0); + let (q,r) = self.unconstrained_div(b * U128::from_u64s_le(2, 0)); + let q_mul_2 = q * U128::from_u64s_le(2, 0); if r < b { (q_mul_2, r) } else { - (q_mul_2 + U128::from_u64s_le(1,0), r - b) + (q_mul_2 + U128::from_u64s_le(1, 0), r - b) } - - } + } } pub fn from_integer(i: T) -> U128 { @@ -130,31 +117,25 @@ impl U128 { // Reject values which would overflow a u128 f.assert_max_bit_size(128); let lo = f as u64 as Field; - let hi = (f-lo) / pow64; - U128 { - lo, - hi, - } + let hi = (f - lo) / pow64; + U128 { lo, hi } } pub fn to_integer(self) -> T { - crate::from_field(self.lo+self.hi*pow64) + crate::from_field(self.lo + self.hi * pow64) } fn wrapping_mul(self: Self, b: U128) -> U128 { - let low = self.lo*b.lo; + let low = self.lo * b.lo; let lo = low as u64 as Field; let carry = (low - lo) / pow64; let high = if crate::field::modulus_num_bits() as u32 > 196 { - (self.lo+self.hi)*(b.lo+b.hi) - low + carry + (self.lo + self.hi) * (b.lo + b.hi) - low + carry } else { - self.lo*b.hi + self.hi*b.lo + carry + self.lo * b.hi + self.hi * b.lo + carry }; let hi = high as u64 as Field; - U128 { - lo, - hi, - } + U128 { lo, hi } } } diff --git a/test_programs/.gitignore b/test_programs/.gitignore index a229df6197f..e98a2fb38b6 100644 --- a/test_programs/.gitignore +++ b/test_programs/.gitignore @@ -1,2 +1,3 @@ acir_artifacts -execution_success/**/crs \ No newline at end of file +execution_success/**/crs +Nargo.toml diff --git a/test_programs/compile_success_empty/closure_explicit_types/src/main.nr b/test_programs/compile_success_empty/closure_explicit_types/src/main.nr index eec2b90b5b2..b6c8a6b7b3c 100644 --- a/test_programs/compile_success_empty/closure_explicit_types/src/main.nr +++ b/test_programs/compile_success_empty/closure_explicit_types/src/main.nr @@ -7,13 +7,13 @@ fn ret_closure1() -> fn[(Field,)]() -> Field { || x + 10 } // return lamda that captures two things -fn ret_closure2() -> fn[(Field,Field)]() -> Field { +fn ret_closure2() -> fn[(Field, Field)]() -> Field { let x = 20; let y = 10; || x + y + 10 } // return lamda that captures two things with different types -fn ret_closure3() -> fn[(u32,u64)]() -> u64 { +fn ret_closure3() -> fn[(u32, u64)]() -> u64 { let x: u32 = 20; let y: u64 = 10; || x as u64 + y + 10 diff --git a/test_programs/compile_success_empty/conditional_regression_579/src/main.nr b/test_programs/compile_success_empty/conditional_regression_579/src/main.nr index a479a7a6fbf..a517f4fdb70 100644 --- a/test_programs/compile_success_empty/conditional_regression_579/src/main.nr +++ b/test_programs/compile_success_empty/conditional_regression_579/src/main.nr @@ -12,9 +12,7 @@ struct MyStruct579 { impl MyStruct579 { fn new(array_param: [u32; 2]) -> MyStruct579 { - MyStruct579 { - array_param: array_param - } + MyStruct579 { array_param } } } diff --git a/test_programs/compile_success_empty/reexports/src/main.nr b/test_programs/compile_success_empty/reexports/src/main.nr index bb94b21b221..ed469ff77d0 100644 --- a/test_programs/compile_success_empty/reexports/src/main.nr +++ b/test_programs/compile_success_empty/reexports/src/main.nr @@ -1,8 +1,6 @@ use dep::reexporting_lib::{FooStruct, MyStruct, lib}; fn main() { - let x: FooStruct = MyStruct { - inner: 0 - }; + let x: FooStruct = MyStruct { inner: 0 }; assert(lib::is_struct_zero(x)); } diff --git a/test_programs/compile_success_empty/specialization/src/main.nr b/test_programs/compile_success_empty/specialization/src/main.nr index 9cd32e0f1eb..30116330a86 100644 --- a/test_programs/compile_success_empty/specialization/src/main.nr +++ b/test_programs/compile_success_empty/specialization/src/main.nr @@ -1,11 +1,15 @@ struct Foo {} impl Foo { - fn foo(_self: Self) -> Field { 1 } + fn foo(_self: Self) -> Field { + 1 + } } impl Foo { - fn foo(_self: Self) -> Field { 2 } + fn foo(_self: Self) -> Field { + 2 + } } fn main() { diff --git a/test_programs/execution_success/1327_concrete_in_generic/src/main.nr b/test_programs/execution_success/1327_concrete_in_generic/src/main.nr index e1d601b13c9..8250b31789b 100644 --- a/test_programs/execution_success/1327_concrete_in_generic/src/main.nr +++ b/test_programs/execution_success/1327_concrete_in_generic/src/main.nr @@ -10,15 +10,15 @@ struct B { } impl B { - fn new(new_concrete_t_c_constructor: fn () -> T_C) -> B { - B { new_concrete_t_c_constructor } - } + fn new(new_concrete_t_c_constructor: fn() -> T_C) -> B { + B { new_concrete_t_c_constructor } + } - fn get_t_c(self) -> T_C { - let new_concrete_t_c_constructor = self.new_concrete_t_c_constructor; - new_concrete_t_c_constructor() - } + fn get_t_c(self) -> T_C { + let new_concrete_t_c_constructor = self.new_concrete_t_c_constructor; + new_concrete_t_c_constructor() } +} // --- // Set struct C { @@ -26,15 +26,15 @@ struct C { } impl C { - fn new (t_d_interface: MethodInterface) -> Self { - C { t_d_interface } - } + fn new(t_d_interface: MethodInterface) -> Self { + C { t_d_interface } + } - fn call_method_of_t_d(self, t_d: T_D) -> Field { - let some_method_on_t_d = self.t_d_interface.some_method_on_t_d; - some_method_on_t_d(t_d) - } + fn call_method_of_t_d(self, t_d: T_D) -> Field { + let some_method_on_t_d = self.t_d_interface.some_method_on_t_d; + some_method_on_t_d(t_d) } +} // --- struct MethodInterface { some_method_on_t_d: fn(T_D)->Field, diff --git a/test_programs/execution_success/array_dynamic/src/main.nr b/test_programs/execution_success/array_dynamic/src/main.nr index dde7bacc455..6b51095bd8c 100644 --- a/test_programs/execution_success/array_dynamic/src/main.nr +++ b/test_programs/execution_success/array_dynamic/src/main.nr @@ -2,8 +2,8 @@ fn main( x: [u32; 5], mut z: u32, t: u32, - index: [Field;5], - index2: [Field;5], + index: [Field; 5], + index2: [Field; 5], offset: Field, sublen: Field ) { diff --git a/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr b/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr index aabf7fc9d5c..4cbf1bd8e6d 100644 --- a/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr +++ b/test_programs/execution_success/array_dynamic_blackbox_input/src/main.nr @@ -24,4 +24,4 @@ fn compute_root(leaf: [u8; 32], path: [u8; 64], _index: u32, root: [u8; 32]) { // Regression for issue #4258 assert(root == current); -} \ No newline at end of file +} diff --git a/test_programs/execution_success/array_dynamic_main_output/src/main.nr b/test_programs/execution_success/array_dynamic_main_output/src/main.nr index ccb7016a190..50feb71f983 100644 --- a/test_programs/execution_success/array_dynamic_main_output/src/main.nr +++ b/test_programs/execution_success/array_dynamic_main_output/src/main.nr @@ -1,4 +1,4 @@ fn main(mut x: [Field; 10], index: u8) -> pub [Field; 10] { x[index] = 0; x -} \ No newline at end of file +} diff --git a/test_programs/execution_success/assert_statement_recursive/src/main.nr b/test_programs/execution_success/assert_statement_recursive/src/main.nr index 687a0d324ba..d89ea3d35bb 100644 --- a/test_programs/execution_success/assert_statement_recursive/src/main.nr +++ b/test_programs/execution_success/assert_statement_recursive/src/main.nr @@ -8,4 +8,4 @@ fn main(x: Field, y: pub Field) { assert(x == y, "x and y are not equal"); assert_eq(x, y, "x and y are not equal"); -} \ No newline at end of file +} diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr index 74949a5f785..046d7d07d5e 100644 --- a/test_programs/execution_success/bigint/src/main.nr +++ b/test_programs/execution_success/bigint/src/main.nr @@ -1,8 +1,8 @@ use dep::std::bigint; -fn main(mut x: [u8;5], y: [u8;5]) { - let a = bigint::BigInt::secpk1_fq_from_le_bytes([x[0],x[1],x[2],x[3],x[4]]); - let b = bigint::BigInt::secpk1_fq_from_le_bytes([y[0],y[1],y[2],y[3],y[4]]); +fn main(mut x: [u8; 5], y: [u8; 5]) { + let a = bigint::BigInt::secpk1_fq_from_le_bytes([x[0], x[1], x[2], x[3], x[4]]); + let b = bigint::BigInt::secpk1_fq_from_le_bytes([y[0], y[1], y[2], y[3], y[4]]); let a_bytes = a.to_le_bytes(); let b_bytes = b.to_le_bytes(); @@ -11,11 +11,11 @@ fn main(mut x: [u8;5], y: [u8;5]) { assert(b_bytes[i] == y[i]); } - let d = a*b - b; + let d = a * b - b; let d_bytes = d.to_le_bytes(); let d1 = bigint::BigInt::secpk1_fq_from_le_bytes(597243850900842442924.to_le_bytes(10)); let d1_bytes = d1.to_le_bytes(); for i in 0..32 { - assert(d_bytes[i] == d1_bytes[i]); + assert(d_bytes[i] == d1_bytes[i]); } } diff --git a/test_programs/execution_success/brillig_cow/src/main.nr b/test_programs/execution_success/brillig_cow/src/main.nr index 7d847e085fe..52ce8b8be3c 100644 --- a/test_programs/execution_success/brillig_cow/src/main.nr +++ b/test_programs/execution_success/brillig_cow/src/main.nr @@ -10,42 +10,37 @@ struct ExecutionResult { impl ExecutionResult { fn is_equal(self, other: ExecutionResult) -> bool { - (self.original == other.original) & - (self.modified_once == other.modified_once) & - (self.modified_twice == other.modified_twice) + (self.original == other.original) + & (self.modified_once == other.modified_once) + & (self.modified_twice == other.modified_twice) } } fn modify_in_inlined_constrained(original: [Field; ARRAY_SIZE], index: u64) -> ExecutionResult { let mut modified = original; - + modified[index] = 27; let modified_once = modified; modified[index+1] = 27; - ExecutionResult { - original, - modified_once, - modified_twice: modified - } + ExecutionResult { original, modified_once, modified_twice: modified } } -unconstrained fn modify_in_unconstrained(original: [Field; ARRAY_SIZE], index: u64) -> ExecutionResult { +unconstrained fn modify_in_unconstrained( + original: [Field; ARRAY_SIZE], + index: u64 +) -> ExecutionResult { let mut modified = original; - + modified[index] = 27; let modified_once = modified; modified[index+1] = 27; - ExecutionResult { - original, - modified_once, - modified_twice: modified - } + ExecutionResult { original, modified_once, modified_twice: modified } } unconstrained fn main(original: [Field; ARRAY_SIZE], index: u64, expected_result: ExecutionResult) { diff --git a/test_programs/execution_success/brillig_cow_regression/src/main.nr b/test_programs/execution_success/brillig_cow_regression/src/main.nr index 74aeda18261..7f3dd766480 100644 --- a/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -47,54 +47,54 @@ struct U256 { } impl U256 { - pub fn from_bytes32(bytes : [u8;32]) -> U256 { + pub fn from_bytes32(bytes: [u8; 32]) -> U256 { // We use addition rather than a bitwise OR as the bitshifts ensure that none of the bytes overlap each other. let high_0 = ((bytes[0] as u64) << 56) - + ((bytes[1] as u64) << 48) - + ((bytes[2] as u64) << 40) - + ((bytes[3] as u64) << 32) - + ((bytes[4] as u64) << 24) - + ((bytes[5] as u64) << 16) - + ((bytes[6] as u64) << 8) - + (bytes[7] as u64); - + + ((bytes[1] as u64) << 48) + + ((bytes[2] as u64) << 40) + + ((bytes[3] as u64) << 32) + + ((bytes[4] as u64) << 24) + + ((bytes[5] as u64) << 16) + + ((bytes[6] as u64) << 8) + + (bytes[7] as u64); + let high_1 = ((bytes[8] as u64) << 56) - + ((bytes[9] as u64) << 48) - + ((bytes[10] as u64) << 40) - + ((bytes[11] as u64) << 32) - + ((bytes[12] as u64) << 24) - + ((bytes[13] as u64) << 16) - + ((bytes[14] as u64) << 8) - + (bytes[15] as u64); - + + ((bytes[9] as u64) << 48) + + ((bytes[10] as u64) << 40) + + ((bytes[11] as u64) << 32) + + ((bytes[12] as u64) << 24) + + ((bytes[13] as u64) << 16) + + ((bytes[14] as u64) << 8) + + (bytes[15] as u64); + let low_0 = ((bytes[16] as u64) << 56) - + ((bytes[17] as u64) << 48) - + ((bytes[18] as u64) << 40) - + ((bytes[19] as u64) << 32) - + ((bytes[20] as u64) << 24) - + ((bytes[21] as u64) << 16) - + ((bytes[22] as u64) << 8) - + (bytes[23] as u64); - + + ((bytes[17] as u64) << 48) + + ((bytes[18] as u64) << 40) + + ((bytes[19] as u64) << 32) + + ((bytes[20] as u64) << 24) + + ((bytes[21] as u64) << 16) + + ((bytes[22] as u64) << 8) + + (bytes[23] as u64); + let low_1 = ((bytes[24] as u64) << 56) - + ((bytes[25] as u64) << 48) - + ((bytes[26] as u64) << 40) - + ((bytes[27] as u64) << 32) - + ((bytes[28] as u64) << 24) - + ((bytes[29] as u64) << 16) - + ((bytes[30] as u64) << 8) - + (bytes[31] as u64); - - U256{inner : [high_0, high_1, low_0, low_1]} + + ((bytes[25] as u64) << 48) + + ((bytes[26] as u64) << 40) + + ((bytes[27] as u64) << 32) + + ((bytes[28] as u64) << 24) + + ((bytes[29] as u64) << 16) + + ((bytes[30] as u64) << 8) + + (bytes[31] as u64); + + U256 { inner: [high_0, high_1, low_0, low_1] } } - pub fn to_u128_limbs(self) -> [Field;2] { + pub fn to_u128_limbs(self) -> [Field; 2] { let two_pow_64 = 2.pow_32(64); let high = (self.inner[0] as Field) * two_pow_64 + self.inner[1] as Field; let low = (self.inner[2] as Field) * two_pow_64 + self.inner[3] as Field; - - [high,low] + + [high, low] } } diff --git a/test_programs/execution_success/brillig_fns_as_values/src/main.nr b/test_programs/execution_success/brillig_fns_as_values/src/main.nr index 2f5d14583d5..ea3148915b8 100644 --- a/test_programs/execution_success/brillig_fns_as_values/src/main.nr +++ b/test_programs/execution_success/brillig_fns_as_values/src/main.nr @@ -14,7 +14,7 @@ fn main(x: u32) { assert(increment(x) == x + 1); } -unconstrained fn wrapper(func: fn (u32) -> u32, param: u32) -> u32 { +unconstrained fn wrapper(func: fn(u32) -> u32, param: u32) -> u32 { func(param) } diff --git a/test_programs/execution_success/conditional_regression_661/src/main.nr b/test_programs/execution_success/conditional_regression_661/src/main.nr index 03102eb775e..26521a88358 100644 --- a/test_programs/execution_success/conditional_regression_661/src/main.nr +++ b/test_programs/execution_success/conditional_regression_661/src/main.nr @@ -16,11 +16,11 @@ fn test5(a: u32) { } } -fn issue_661_foo(array: [u32;4], b: u32) -> [u32;1] { +fn issue_661_foo(array: [u32; 4], b: u32) -> [u32; 1] { [array[0] + b] } -fn issue_661_bar(a: [u32;4]) -> [u32;4] { +fn issue_661_bar(a: [u32; 4]) -> [u32; 4] { let mut b: [u32; 4] = [0; 4]; b[0]=a[0]+1; b diff --git a/test_programs/execution_success/databus/src/main.nr b/test_programs/execution_success/databus/src/main.nr index 61a9637f5fe..1cf95be8a22 100644 --- a/test_programs/execution_success/databus/src/main.nr +++ b/test_programs/execution_success/databus/src/main.nr @@ -1,12 +1,12 @@ use dep::std; -fn main(mut x: u32, y: call_data u32, z: call_data [u32;4]) -> return_data u32 { - let a = z[x]; - a+foo(y) +fn main(mut x: u32, y: call_data u32, z: call_data [u32; 4]) -> return_data u32 { + let a = z[x]; + a + foo(y) } // Use an unconstrained function to force the compiler to avoid inlining unconstrained fn foo(x: u32) -> u32 { - x+1 + x + 1 } diff --git a/test_programs/execution_success/debug_logs/src/main.nr b/test_programs/execution_success/debug_logs/src/main.nr index c628a9ae6a4..ec24b0cc8e8 100644 --- a/test_programs/execution_success/debug_logs/src/main.nr +++ b/test_programs/execution_success/debug_logs/src/main.nr @@ -1,4 +1,4 @@ -fn main(x: Field, y: pub Field) { +fn main(x: Field, y: pub Field) { let string = "i: {i}, j: {j}"; println(string); @@ -102,8 +102,8 @@ fn regression_2903() { let a = v[0]; println(a); // will print `1` - let bytes = [ "aaa", "bbb", "ccc" ]; - println(bytes); + let bytes = ["aaa", "bbb", "ccc"]; + println(bytes); } fn regression_2906() { diff --git a/test_programs/execution_success/distinct_keyword/src/main.nr b/test_programs/execution_success/distinct_keyword/src/main.nr index 0e55a011a48..8e9b5c008ed 100644 --- a/test_programs/execution_success/distinct_keyword/src/main.nr +++ b/test_programs/execution_success/distinct_keyword/src/main.nr @@ -1,4 +1,4 @@ // Example that uses the distinct keyword -fn main(x: pub Field) -> distinct pub [Field;2] { +fn main(x: pub Field) -> distinct pub [Field; 2] { [x + 1, x] } diff --git a/test_programs/execution_success/ecdsa_secp256k1/src/main.nr b/test_programs/execution_success/ecdsa_secp256k1/src/main.nr index 2f410755f74..ac0359e4bb8 100644 --- a/test_programs/execution_success/ecdsa_secp256k1/src/main.nr +++ b/test_programs/execution_success/ecdsa_secp256k1/src/main.nr @@ -1,11 +1,11 @@ use dep::std; fn main( - message: [u8;38], - hashed_message: [u8;32], - pub_key_x: [u8;32], - pub_key_y: [u8;32], - signature: [u8;64] + message: [u8; 38], + hashed_message: [u8; 32], + pub_key_x: [u8; 32], + pub_key_y: [u8; 32], + signature: [u8; 64] ) { // Hash the message, since secp256k1 expects a hashed_message let expected = std::hash::sha256(message); diff --git a/test_programs/execution_success/ecdsa_secp256r1/src/main.nr b/test_programs/execution_success/ecdsa_secp256r1/src/main.nr index d23573d13a6..c64e390d652 100644 --- a/test_programs/execution_success/ecdsa_secp256r1/src/main.nr +++ b/test_programs/execution_success/ecdsa_secp256r1/src/main.nr @@ -1,6 +1,6 @@ use dep::std; -fn main(hashed_message: [u8;32], pub_key_x: [u8;32], pub_key_y: [u8;32], signature: [u8;64]) { +fn main(hashed_message: [u8; 32], pub_key_x: [u8; 32], pub_key_y: [u8; 32], signature: [u8; 64]) { let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); assert(valid_signature); } diff --git a/test_programs/execution_success/main_bool_arg/src/main.nr b/test_programs/execution_success/main_bool_arg/src/main.nr index 111a23ec0c2..2c50d7dee16 100644 --- a/test_programs/execution_success/main_bool_arg/src/main.nr +++ b/test_programs/execution_success/main_bool_arg/src/main.nr @@ -1,4 +1,4 @@ -fn main(x: bool, y: [bool;2]) { +fn main(x: bool, y: [bool; 2]) { if x { assert(1 != 2); } diff --git a/test_programs/execution_success/operator_overloading/src/main.nr b/test_programs/execution_success/operator_overloading/src/main.nr index 3867531abca..d61e1da170e 100644 --- a/test_programs/execution_success/operator_overloading/src/main.nr +++ b/test_programs/execution_success/operator_overloading/src/main.nr @@ -1,4 +1,4 @@ -use dep::std::ops::{ Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shl, Shr }; +use dep::std::ops::{Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shl, Shr}; use dep::std::cmp::Ordering; // x = 3, y = 9 @@ -126,10 +126,6 @@ impl Ord for Wrapper { } } - - - - struct Pair { x: Wrapper, y: Wrapper, diff --git a/test_programs/execution_success/regression_3394/src/main.nr b/test_programs/execution_success/regression_3394/src/main.nr index cc45487b98b..94b6c818ff2 100644 --- a/test_programs/execution_success/regression_3394/src/main.nr +++ b/test_programs/execution_success/regression_3394/src/main.nr @@ -3,4 +3,4 @@ use dep::std; fn main() { let x : i8 = -128; std::println(x); -} \ No newline at end of file +} diff --git a/test_programs/execution_success/regression_3607/src/main.nr b/test_programs/execution_success/regression_3607/src/main.nr index c09211c2810..9c7ef243f60 100644 --- a/test_programs/execution_success/regression_3607/src/main.nr +++ b/test_programs/execution_success/regression_3607/src/main.nr @@ -5,4 +5,4 @@ fn main(mut x: u32) { x = (x+1) / x; } assert(x != 0); -} \ No newline at end of file +} diff --git a/test_programs/execution_success/regression_3889/src/main.nr b/test_programs/execution_success/regression_3889/src/main.nr index 10b8ecabee3..402a69a10da 100644 --- a/test_programs/execution_success/regression_3889/src/main.nr +++ b/test_programs/execution_success/regression_3889/src/main.nr @@ -17,7 +17,6 @@ mod Baz { use crate::Bar::NewType; } - fn main(works: Baz::Works, fails: Baz::BarStruct, also_fails: Bar::NewType) -> pub Field { works.a + fails.a + also_fails.a } diff --git a/test_programs/execution_success/side_effects_constrain_array/src/main.nr b/test_programs/execution_success/side_effects_constrain_array/src/main.nr index fb3c346a460..c4a62603bc3 100644 --- a/test_programs/execution_success/side_effects_constrain_array/src/main.nr +++ b/test_programs/execution_success/side_effects_constrain_array/src/main.nr @@ -7,11 +7,11 @@ fn main(y: pub u32) { // The assert inside the if should be hit if y < 10 { - assert(bar.inner == [100, 101, 102]); + assert(bar.inner == [100, 101, 102]); } // The assert inside the if should not be hit if y > 10 { assert(bar.inner == [0, 1, 2]); } -} \ No newline at end of file +} diff --git a/test_programs/execution_success/struct/src/main.nr b/test_programs/execution_success/struct/src/main.nr index 45c5e347e5a..de08f42f79d 100644 --- a/test_programs/execution_success/struct/src/main.nr +++ b/test_programs/execution_success/struct/src/main.nr @@ -9,8 +9,8 @@ struct Pair { } impl Foo { - fn default(x: Field,y: Field) -> Self { - Self { bar: 0, array: [x,y] } + fn default(x: Field, y: Field) -> Self { + Self { bar: 0, array: [x, y] } } } diff --git a/test_programs/format.sh b/test_programs/format.sh new file mode 100755 index 00000000000..3c679b8689e --- /dev/null +++ b/test_programs/format.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -e + +# These tests are incompatible with gas reporting +excluded_dirs=("workspace" "workspace_default_member" "workspace_reexport_bug") + +# These tests cause failures in CI with a stack overflow for some reason. +ci_excluded_dirs=("eddsa") + +current_dir=$(pwd) + +# We generate a Noir workspace which contains all of the test cases +# This allows us to generate a gates report using `nargo info` for all of them at once. + + +function collect_dirs { + test_dirs=$(ls $current_dir/$1) + + for dir in $test_dirs; do + if [[ " ${excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + if [[ ${CI-false} = "true" ]] && [[ " ${ci_excluded_dirs[@]} " =~ " ${dir} " ]]; then + continue + fi + + echo " \"$1/$dir\"," >> Nargo.toml +done +} + +echo "[workspace]" > Nargo.toml +echo "members = [" >> Nargo.toml + +collect_dirs compile_success_empty +collect_dirs execution_success + +echo "]" >> Nargo.toml + +if [ "$1" == "check" ]; then + nargo fmt --check +else + nargo fmt +fi + + +rm Nargo.toml From 00ab3db86b06111d144516e862902b8604284611 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:28:28 +0000 Subject: [PATCH 011/416] fix: remove panic when generic array length is not resolvable (#4408) # Description ## Problem\* Resolves #4407 ## Summary\* We currently have no way to gracefully error during monomorphization and so must panic if we run into any errors. This PR then adds the `MonomorphizationError` enum with an example error type. We've also added a `CompileError` which unifies `RuntimeError` and `MonomorphizationError` so they can be converted into `FileDiagnostic`s ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 1 + compiler/noirc_driver/Cargo.toml | 1 + compiler/noirc_driver/src/lib.rs | 27 +- compiler/noirc_evaluator/src/errors.rs | 2 +- .../src/monomorphization/debug.rs | 38 ++- .../src/monomorphization/mod.rs | 286 ++++++++++++------ compiler/noirc_frontend/src/tests.rs | 2 +- tooling/nargo/src/ops/test.rs | 10 +- 8 files changed, 246 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d8b12d5379..0f575d9c46e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2947,6 +2947,7 @@ dependencies = [ "noirc_macros", "rust-embed", "serde", + "thiserror", "tracing", ] diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index d9b240101d8..681976735f3 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -23,6 +23,7 @@ serde.workspace = true fxhash.workspace = true rust-embed.workspace = true tracing.workspace = true +thiserror.workspace = true aztec_macros = { path = "../../aztec_macros" } noirc_macros = { path = "../../noirc_macros" } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 8b0fc5dc97a..11f53cdb749 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -16,9 +16,10 @@ use noirc_frontend::graph::{CrateId, CrateName}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; use noirc_frontend::macros_api::MacroProcessor; -use noirc_frontend::monomorphization::{monomorphize, monomorphize_debug}; +use noirc_frontend::monomorphization::{monomorphize, monomorphize_debug, MonomorphizationError}; use noirc_frontend::node_interner::FuncId; use std::path::Path; +use thiserror::Error; use tracing::info; mod abi_gen; @@ -107,6 +108,24 @@ fn parse_expression_width(input: &str) -> Result for FileDiagnostic { + fn from(error: CompileError) -> FileDiagnostic { + match error { + CompileError::RuntimeError(err) => err.into(), + CompileError::MonomorphizationError(err) => err.into(), + } + } +} + /// Helper type used to signify where only warnings are expected in file diagnostics pub type Warnings = Vec; @@ -436,11 +455,11 @@ pub fn compile_no_check( main_function: FuncId, cached_program: Option, force_compile: bool, -) -> Result { +) -> Result { let program = if options.instrument_debug { - monomorphize_debug(main_function, &mut context.def_interner, &context.debug_instrumenter) + monomorphize_debug(main_function, &mut context.def_interner, &context.debug_instrumenter)? } else { - monomorphize(main_function, &mut context.def_interner) + monomorphize(main_function, &mut context.def_interner)? }; let hash = fxhash::hash64(&program); diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index ed94adac28e..40f4336e0b5 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -158,7 +158,7 @@ impl RuntimeError { RuntimeError::InternalError(cause) => { Diagnostic::simple_error( "Internal Consistency Evaluators Errors: \n - This is likely a bug. Consider Opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), + This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), cause.to_string(), noirc_errors::Span::inclusive(0, 0) ) diff --git a/compiler/noirc_frontend/src/monomorphization/debug.rs b/compiler/noirc_frontend/src/monomorphization/debug.rs index 5837d67660a..a8ff4399f99 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -8,7 +8,7 @@ use crate::hir_def::expr::*; use crate::node_interner::ExprId; use super::ast::{Expression, Ident}; -use super::Monomorphizer; +use super::{MonomorphizationError, Monomorphizer}; const DEBUG_MEMBER_ASSIGN_PREFIX: &str = "__debug_member_assign_"; const DEBUG_VAR_ID_ARG_SLOT: usize = 0; @@ -39,18 +39,19 @@ impl<'interner> Monomorphizer<'interner> { &mut self, call: &HirCallExpression, arguments: &mut [Expression], - ) { - let original_func = Box::new(self.expr(call.func)); + ) -> Result<(), MonomorphizationError> { + let original_func = Box::new(self.expr(call.func)?); if let Expression::Ident(Ident { name, .. }) = original_func.as_ref() { if name == "__debug_var_assign" { - self.patch_debug_var_assign(call, arguments); + self.patch_debug_var_assign(call, arguments)?; } else if name == "__debug_var_drop" { - self.patch_debug_var_drop(call, arguments); + self.patch_debug_var_drop(call, arguments)?; } else if let Some(arity) = name.strip_prefix(DEBUG_MEMBER_ASSIGN_PREFIX) { let arity = arity.parse::().expect("failed to parse member assign arity"); - self.patch_debug_member_assign(call, arguments, arity); + self.patch_debug_member_assign(call, arguments, arity)?; } } + Ok(()) } /// Update instrumentation code inserted on variable assignment. We need to @@ -59,7 +60,11 @@ impl<'interner> Monomorphizer<'interner> { /// variable are possible if using generic functions, hence the temporary ID /// created when injecting the instrumentation code can map to multiple IDs /// at runtime. - fn patch_debug_var_assign(&mut self, call: &HirCallExpression, arguments: &mut [Expression]) { + fn patch_debug_var_assign( + &mut self, + call: &HirCallExpression, + arguments: &mut [Expression], + ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { @@ -73,13 +78,18 @@ impl<'interner> Monomorphizer<'interner> { // then update the ID used for tracking at runtime let var_id = self.debug_type_tracker.insert_var(source_var_id, var_type); let interned_var_id = self.intern_var_id(var_id, &call.location); - arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id); + arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id)?; + Ok(()) } /// Update instrumentation code for a variable being dropped out of scope. /// Given the source_var_id we search for the last assigned debug var_id and /// replace it instead. - fn patch_debug_var_drop(&mut self, call: &HirCallExpression, arguments: &mut [Expression]) { + fn patch_debug_var_drop( + &mut self, + call: &HirCallExpression, + arguments: &mut [Expression], + ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { @@ -92,7 +102,8 @@ impl<'interner> Monomorphizer<'interner> { .get_var_id(source_var_id) .unwrap_or_else(|| unreachable!("failed to find debug variable")); let interned_var_id = self.intern_var_id(var_id, &call.location); - arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id); + arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id)?; + Ok(()) } /// Update instrumentation code inserted when assigning to a member of an @@ -106,7 +117,7 @@ impl<'interner> Monomorphizer<'interner> { call: &HirCallExpression, arguments: &mut [Expression], arity: usize, - ) { + ) -> Result<(), MonomorphizationError> { let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); let var_id_arg = hir_arguments.get(DEBUG_VAR_ID_ARG_SLOT); let Some(HirExpression::Literal(HirLiteral::Integer(source_var_id, _))) = var_id_arg else { @@ -149,7 +160,7 @@ impl<'interner> Monomorphizer<'interner> { call.location.span, call.location.file, ); - arguments[DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i] = self.expr(index_id); + arguments[DEBUG_MEMBER_FIELD_INDEX_ARG_SLOT + i] = self.expr(index_id)?; } else { // array/string element using constant index cursor_type = element_type_at_index(cursor_type, index as usize); @@ -165,7 +176,8 @@ impl<'interner> Monomorphizer<'interner> { .get_var_id(source_var_id) .unwrap_or_else(|| unreachable!("failed to find debug variable")); let interned_var_id = self.intern_var_id(var_id, &call.location); - arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id); + arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id)?; + Ok(()) } fn intern_var_id(&mut self, var_id: DebugVarId, location: &Location) -> ExprId { diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2e714da21c6..ce880401d77 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -9,13 +9,14 @@ //! The entry point to this pass is the `monomorphize` function which, starting from a given //! function, will monomorphize the entire reachable program. use acvm::FieldElement; -use iter_extended::{btree_map, vecmap}; -use noirc_errors::Location; +use iter_extended::{btree_map, try_vecmap, vecmap}; +use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; use noirc_printable_type::PrintableType; use std::{ collections::{BTreeMap, HashMap, VecDeque}, unreachable, }; +use thiserror::Error; use crate::{ debug::DebugInstrumenter, @@ -87,6 +88,40 @@ struct Monomorphizer<'interner> { type HirType = crate::Type; +#[derive(Debug, Error)] +pub enum MonomorphizationError { + #[error("Length of generic array could not be determined.")] + UnknownArrayLength { location: Location }, +} + +impl MonomorphizationError { + fn call_stack(&self) -> Vec { + match self { + MonomorphizationError::UnknownArrayLength { location } => vec![*location], + } + } +} + +impl From for FileDiagnostic { + fn from(error: MonomorphizationError) -> FileDiagnostic { + let call_stack = error.call_stack(); + let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); + let diagnostic = error.into_diagnostic(); + diagnostic.in_file(file_id).with_call_stack(call_stack) + } +} + +impl MonomorphizationError { + fn into_diagnostic(self) -> CustomDiagnostic { + CustomDiagnostic::simple_error( + "Internal Consistency Evaluators Errors: \n + This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), + self.to_string(), + noirc_errors::Span::inclusive(0, 0) + ) + } +} + /// Starting from the given `main` function, monomorphize the entire program, /// replacing all references to type variables and NamedGenerics with concrete /// types, duplicating definitions as necessary to do so. @@ -99,7 +134,10 @@ type HirType = crate::Type; /// this function. Typically, this is the function named "main" in the source project, /// but it can also be, for example, an arbitrary test function for running `nargo test`. #[tracing::instrument(level = "trace", skip(main, interner))] -pub fn monomorphize(main: node_interner::FuncId, interner: &mut NodeInterner) -> Program { +pub fn monomorphize( + main: node_interner::FuncId, + interner: &mut NodeInterner, +) -> Result { monomorphize_debug(main, interner, &DebugInstrumenter::default()) } @@ -107,10 +145,10 @@ pub fn monomorphize_debug( main: node_interner::FuncId, interner: &mut NodeInterner, debug_instrumenter: &DebugInstrumenter, -) -> Program { +) -> Result { let debug_type_tracker = DebugTypeTracker::build_from_debug_instrumenter(debug_instrumenter); let mut monomorphizer = Monomorphizer::new(interner, debug_type_tracker); - let function_sig = monomorphizer.compile_main(main); + let function_sig = monomorphizer.compile_main(main)?; while !monomorphizer.queue.is_empty() { let (next_fn_id, new_id, bindings, trait_method) = monomorphizer.queue.pop_front().unwrap(); @@ -118,7 +156,7 @@ pub fn monomorphize_debug( perform_instantiation_bindings(&bindings); let impl_bindings = monomorphizer.perform_impl_bindings(trait_method, next_fn_id); - monomorphizer.function(next_fn_id, new_id); + monomorphizer.function(next_fn_id, new_id)?; undo_instantiation_bindings(impl_bindings); undo_instantiation_bindings(bindings); } @@ -128,7 +166,7 @@ pub fn monomorphize_debug( monomorphizer.interner.function_meta(&main); let (debug_variables, debug_types) = monomorphizer.debug_type_tracker.extract_vars_and_types(); - Program::new( + let program = Program::new( functions, function_sig, *return_distinctness, @@ -137,7 +175,8 @@ pub fn monomorphize_debug( *kind == FunctionKind::Recursive, debug_variables, debug_types, - ) + ); + Ok(program) } impl<'interner> Monomorphizer<'interner> { @@ -233,10 +272,13 @@ impl<'interner> Monomorphizer<'interner> { self.globals.entry(id).or_default().insert(typ, new_id); } - fn compile_main(&mut self, main_id: node_interner::FuncId) -> FunctionSignature { + fn compile_main( + &mut self, + main_id: node_interner::FuncId, + ) -> Result { let new_main_id = self.next_function_id(); assert_eq!(new_main_id, Program::main_id()); - self.function(main_id, new_main_id); + self.function(main_id, new_main_id)?; self.return_location = self.interner.function(&main_id).block(self.interner).statements().last().and_then( |x| match self.interner.statement(x) { @@ -245,10 +287,14 @@ impl<'interner> Monomorphizer<'interner> { }, ); let main_meta = self.interner.function_meta(&main_id); - main_meta.function_signature() + Ok(main_meta.function_signature()) } - fn function(&mut self, f: node_interner::FuncId, id: FuncId) { + fn function( + &mut self, + f: node_interner::FuncId, + id: FuncId, + ) -> Result<(), MonomorphizationError> { if let Some((self_type, trait_id)) = self.interner.get_function_trait(&f) { let the_trait = self.interner.get_trait(trait_id); the_trait.self_type_typevar.force_bind(self_type); @@ -268,10 +314,11 @@ impl<'interner> Monomorphizer<'interner> { || matches!(modifiers.contract_function_type, Some(ContractFunctionType::Open)); let parameters = self.parameters(&meta.parameters); - let body = self.expr(body_expr_id); + let body = self.expr(body_expr_id)?; let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; self.push_function(id, function); + Ok(()) } fn push_function(&mut self, id: FuncId, function: ast::Function) { @@ -331,15 +378,18 @@ impl<'interner> Monomorphizer<'interner> { } } - fn expr(&mut self, expr: node_interner::ExprId) -> ast::Expression { + fn expr( + &mut self, + expr: node_interner::ExprId, + ) -> Result { use ast::Expression::Literal; use ast::Literal::*; - match self.interner.expression(&expr) { - HirExpression::Ident(ident) => self.ident(ident, expr), + let expr = match self.interner.expression(&expr) { + HirExpression::Ident(ident) => self.ident(ident, expr)?, HirExpression::Literal(HirLiteral::Str(contents)) => Literal(Str(contents)), HirExpression::Literal(HirLiteral::FmtStr(contents, idents)) => { - let fields = vecmap(idents, |ident| self.expr(ident)); + let fields = try_vecmap(idents, |ident| self.expr(ident))?; Literal(FmtStr( contents, fields.len() as u64, @@ -367,27 +417,27 @@ impl<'interner> Monomorphizer<'interner> { } } HirExpression::Literal(HirLiteral::Array(array)) => match array { - HirArrayLiteral::Standard(array) => self.standard_array(expr, array), + HirArrayLiteral::Standard(array) => self.standard_array(expr, array)?, HirArrayLiteral::Repeated { repeated_element, length } => { - self.repeated_array(expr, repeated_element, length) + self.repeated_array(expr, repeated_element, length)? } }, HirExpression::Literal(HirLiteral::Unit) => ast::Expression::Block(vec![]), - HirExpression::Block(block) => self.block(block.0), + HirExpression::Block(block) => self.block(block.0)?, HirExpression::Prefix(prefix) => { let location = self.interner.expr_location(&expr); ast::Expression::Unary(ast::Unary { operator: prefix.operator, - rhs: Box::new(self.expr(prefix.rhs)), + rhs: Box::new(self.expr(prefix.rhs)?), result_type: self.convert_type(&self.interner.id_type(expr)), location, }) } HirExpression::Infix(infix) => { - let lhs = self.expr(infix.lhs); - let rhs = self.expr(infix.rhs); + let lhs = self.expr(infix.lhs)?; + let rhs = self.expr(infix.rhs)?; let operator = infix.operator.kind; let location = self.interner.expr_location(&expr); if self.interner.get_selected_impl_for_expression(expr).is_some() { @@ -418,26 +468,27 @@ impl<'interner> Monomorphizer<'interner> { } } - HirExpression::Index(index) => self.index(expr, index), + HirExpression::Index(index) => self.index(expr, index)?, HirExpression::MemberAccess(access) => { let field_index = self.interner.get_field_index(expr); - let expr = Box::new(self.expr(access.lhs)); + let expr = Box::new(self.expr(access.lhs)?); ast::Expression::ExtractTupleField(expr, field_index) } - HirExpression::Call(call) => self.function_call(call, expr), + HirExpression::Call(call) => self.function_call(call, expr)?, HirExpression::Cast(cast) => ast::Expression::Cast(ast::Cast { - lhs: Box::new(self.expr(cast.lhs)), + lhs: Box::new(self.expr(cast.lhs)?), r#type: self.convert_type(&cast.r#type), location: self.interner.expr_location(&expr), }), HirExpression::If(if_expr) => { - let cond = self.expr(if_expr.condition); - let then = self.expr(if_expr.consequence); - let else_ = if_expr.alternative.map(|alt| Box::new(self.expr(alt))); + let cond = self.expr(if_expr.condition)?; + let then = self.expr(if_expr.consequence)?; + let else_ = + if_expr.alternative.map(|alt| self.expr(alt)).transpose()?.map(Box::new); ast::Expression::If(ast::If { condition: Box::new(cond), consequence: Box::new(then), @@ -447,28 +498,30 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Tuple(fields) => { - let fields = vecmap(fields, |id| self.expr(id)); + let fields = try_vecmap(fields, |id| self.expr(id))?; ast::Expression::Tuple(fields) } - HirExpression::Constructor(constructor) => self.constructor(constructor, expr), + HirExpression::Constructor(constructor) => self.constructor(constructor, expr)?, - HirExpression::Lambda(lambda) => self.lambda(lambda, expr), + HirExpression::Lambda(lambda) => self.lambda(lambda, expr)?, HirExpression::MethodCall(hir_method_call) => { unreachable!("Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}") } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), - } + }; + + Ok(expr) } fn standard_array( &mut self, array: node_interner::ExprId, array_elements: Vec, - ) -> ast::Expression { + ) -> Result { let typ = self.convert_type(&self.interner.id_type(array)); - let contents = vecmap(array_elements, |id| self.expr(id)); - ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ })) + let contents = try_vecmap(array_elements, |id| self.expr(id))?; + Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) } fn repeated_array( @@ -476,48 +529,56 @@ impl<'interner> Monomorphizer<'interner> { array: node_interner::ExprId, repeated_element: node_interner::ExprId, length: HirType, - ) -> ast::Expression { + ) -> Result { let typ = self.convert_type(&self.interner.id_type(array)); - let length = length - .evaluate_to_u64() - .expect("Length of array is unknown when evaluating numeric generic"); + let length = length.evaluate_to_u64().ok_or_else(|| { + let location = self.interner.expr_location(&array); + MonomorphizationError::UnknownArrayLength { location } + })?; - let contents = vecmap(0..length, |_| self.expr(repeated_element)); - ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ })) + let contents = try_vecmap(0..length, |_| self.expr(repeated_element))?; + Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) } - fn index(&mut self, id: node_interner::ExprId, index: HirIndexExpression) -> ast::Expression { + fn index( + &mut self, + id: node_interner::ExprId, + index: HirIndexExpression, + ) -> Result { let element_type = self.convert_type(&self.interner.id_type(id)); - let collection = Box::new(self.expr(index.collection)); - let index = Box::new(self.expr(index.index)); + let collection = Box::new(self.expr(index.collection)?); + let index = Box::new(self.expr(index.index)?); let location = self.interner.expr_location(&id); - ast::Expression::Index(ast::Index { collection, index, element_type, location }) + Ok(ast::Expression::Index(ast::Index { collection, index, element_type, location })) } - fn statement(&mut self, id: StmtId) -> ast::Expression { + fn statement(&mut self, id: StmtId) -> Result { match self.interner.statement(&id) { HirStatement::Let(let_statement) => self.let_statement(let_statement), HirStatement::Constrain(constrain) => { - let expr = self.expr(constrain.0); + let expr = self.expr(constrain.0)?; let location = self.interner.expr_location(&constrain.0); - let assert_message = - constrain.2.map(|assert_msg_expr| Box::new(self.expr(assert_msg_expr))); - ast::Expression::Constrain(Box::new(expr), location, assert_message) + let assert_message = constrain + .2 + .map(|assert_msg_expr| self.expr(assert_msg_expr)) + .transpose()? + .map(Box::new); + Ok(ast::Expression::Constrain(Box::new(expr), location, assert_message)) } HirStatement::Assign(assign) => self.assign(assign), HirStatement::For(for_loop) => { self.is_range_loop = true; - let start = self.expr(for_loop.start_range); - let end = self.expr(for_loop.end_range); + let start = self.expr(for_loop.start_range)?; + let end = self.expr(for_loop.end_range)?; self.is_range_loop = false; let index_variable = self.next_local_id(); self.define_local(for_loop.identifier.id, index_variable); - let block = Box::new(self.expr(for_loop.block)); + let block = Box::new(self.expr(for_loop.block)?); - ast::Expression::For(ast::For { + Ok(ast::Expression::For(ast::For { index_variable, index_name: self.interner.definition_name(for_loop.identifier.id).to_owned(), index_type: self.convert_type(&self.interner.id_type(for_loop.start_range)), @@ -526,25 +587,30 @@ impl<'interner> Monomorphizer<'interner> { start_range_location: self.interner.expr_location(&for_loop.start_range), end_range_location: self.interner.expr_location(&for_loop.end_range), block, - }) + })) } HirStatement::Expression(expr) => self.expr(expr), - HirStatement::Semi(expr) => ast::Expression::Semi(Box::new(self.expr(expr))), + HirStatement::Semi(expr) => { + self.expr(expr).map(|expr| ast::Expression::Semi(Box::new(expr))) + } HirStatement::Error => unreachable!(), } } - fn let_statement(&mut self, let_statement: HirLetStatement) -> ast::Expression { - let expr = self.expr(let_statement.expression); + fn let_statement( + &mut self, + let_statement: HirLetStatement, + ) -> Result { + let expr = self.expr(let_statement.expression)?; let expected_type = self.interner.id_type(let_statement.expression); - self.unpack_pattern(let_statement.pattern, expr, &expected_type) + Ok(self.unpack_pattern(let_statement.pattern, expr, &expected_type)) } fn constructor( &mut self, constructor: HirConstructorExpression, id: node_interner::ExprId, - ) -> ast::Expression { + ) -> Result { let typ = self.interner.id_type(id); let field_types = unwrap_struct_type(&typ); @@ -561,7 +627,7 @@ impl<'interner> Monomorphizer<'interner> { let typ = self.convert_type(field_type); field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); - let expression = Box::new(self.expr(expr_id)); + let expression = Box::new(self.expr(expr_id)?); new_exprs.push(ast::Expression::Let(ast::Let { id: new_id, @@ -586,11 +652,15 @@ impl<'interner> Monomorphizer<'interner> { // Finally we can return the created Tuple from the new block new_exprs.push(ast::Expression::Tuple(field_idents)); - ast::Expression::Block(new_exprs) + Ok(ast::Expression::Block(new_exprs)) } - fn block(&mut self, statement_ids: Vec) -> ast::Expression { - ast::Expression::Block(vecmap(statement_ids, |id| self.statement(id))) + fn block( + &mut self, + statement_ids: Vec, + ) -> Result { + let stmts = try_vecmap(statement_ids, |id| self.statement(id)); + stmts.map(ast::Expression::Block) } fn unpack_pattern( @@ -701,15 +771,19 @@ impl<'interner> Monomorphizer<'interner> { Some(ast::Ident { location: Some(ident.location), mutable, definition, name, typ }) } - fn ident(&mut self, ident: HirIdent, expr_id: node_interner::ExprId) -> ast::Expression { + fn ident( + &mut self, + ident: HirIdent, + expr_id: node_interner::ExprId, + ) -> Result { let typ = self.interner.id_type(expr_id); if let ImplKind::TraitMethod(method, _, _) = ident.impl_kind { - return self.resolve_trait_method_reference(expr_id, typ, method); + return Ok(self.resolve_trait_method_reference(expr_id, typ, method)); } let definition = self.interner.definition(ident.id); - match &definition.kind { + let ident = match &definition.kind { DefinitionKind::Function(func_id) => { let mutable = definition.mutable; let location = Some(ident.location); @@ -736,7 +810,7 @@ impl<'interner> Monomorphizer<'interner> { "Globals should have a corresponding let statement by monomorphization" ) }; - self.expr(let_.expression) + self.expr(let_.expression)? } DefinitionKind::Local(_) => self.lookup_captured_expr(ident.id).unwrap_or_else(|| { let ident = self.local_ident(&ident).unwrap(); @@ -757,7 +831,9 @@ impl<'interner> Monomorphizer<'interner> { let typ = self.convert_type(&typ); ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) } - } + }; + + Ok(ident) } /// Convert a non-tuple/struct type to a monomorphized type @@ -949,12 +1025,12 @@ impl<'interner> Monomorphizer<'interner> { &mut self, call: HirCallExpression, id: node_interner::ExprId, - ) -> ast::Expression { - let original_func = Box::new(self.expr(call.func)); - let mut arguments = vecmap(&call.arguments, |id| self.expr(*id)); + ) -> Result { + let original_func = Box::new(self.expr(call.func)?); + let mut arguments = try_vecmap(&call.arguments, |id| self.expr(*id))?; let hir_arguments = vecmap(&call.arguments, |id| self.interner.expression(id)); - self.patch_debug_instrumentation_call(&call, &mut arguments); + self.patch_debug_instrumentation_call(&call, &mut arguments)?; let return_type = self.interner.id_type(id); let return_type = self.convert_type(&return_type); @@ -969,7 +1045,7 @@ impl<'interner> Monomorphizer<'interner> { // The second argument is expected to always be an ident self.append_printable_type_info(&hir_arguments[1], &mut arguments); } else if name.as_str() == "assert_message" { - // The first argument to the `assert_message` oracle is the expression passed as a mesage to an `assert` or `assert_eq` statement + // The first argument to the `assert_message` oracle is the expression passed as a message to an `assert` or `assert_eq` statement self.append_printable_type_info(&hir_arguments[0], &mut arguments); } } @@ -1017,9 +1093,9 @@ impl<'interner> Monomorphizer<'interner> { if !block_expressions.is_empty() { block_expressions.push(call); - ast::Expression::Block(block_expressions) + Ok(ast::Expression::Block(block_expressions)) } else { - call + Ok(call) } } @@ -1190,47 +1266,59 @@ impl<'interner> Monomorphizer<'interner> { .collect() } - fn assign(&mut self, assign: HirAssignStatement) -> ast::Expression { - let expression = Box::new(self.expr(assign.expression)); - let lvalue = self.lvalue(assign.lvalue); - ast::Expression::Assign(ast::Assign { expression, lvalue }) + fn assign( + &mut self, + assign: HirAssignStatement, + ) -> Result { + let expression = Box::new(self.expr(assign.expression)?); + let lvalue = self.lvalue(assign.lvalue)?; + Ok(ast::Expression::Assign(ast::Assign { expression, lvalue })) } - fn lvalue(&mut self, lvalue: HirLValue) -> ast::LValue { - match lvalue { + fn lvalue(&mut self, lvalue: HirLValue) -> Result { + let value = match lvalue { HirLValue::Ident(ident, _) => self .lookup_captured_lvalue(ident.id) .unwrap_or_else(|| ast::LValue::Ident(self.local_ident(&ident).unwrap())), HirLValue::MemberAccess { object, field_index, .. } => { let field_index = field_index.unwrap(); - let object = Box::new(self.lvalue(*object)); + let object = Box::new(self.lvalue(*object)?); ast::LValue::MemberAccess { object, field_index } } HirLValue::Index { array, index, typ } => { let location = self.interner.expr_location(&index); - let array = Box::new(self.lvalue(*array)); - let index = Box::new(self.expr(index)); + let array = Box::new(self.lvalue(*array)?); + let index = Box::new(self.expr(index)?); let element_type = self.convert_type(&typ); ast::LValue::Index { array, index, element_type, location } } HirLValue::Dereference { lvalue, element_type } => { - let reference = Box::new(self.lvalue(*lvalue)); + let reference = Box::new(self.lvalue(*lvalue)?); let element_type = self.convert_type(&element_type); ast::LValue::Dereference { reference, element_type } } - } + }; + + Ok(value) } - fn lambda(&mut self, lambda: HirLambda, expr: node_interner::ExprId) -> ast::Expression { + fn lambda( + &mut self, + lambda: HirLambda, + expr: node_interner::ExprId, + ) -> Result { if lambda.captures.is_empty() { self.lambda_no_capture(lambda) } else { - let (setup, closure_variable) = self.lambda_with_setup(lambda, expr); - ast::Expression::Block(vec![setup, closure_variable]) + let (setup, closure_variable) = self.lambda_with_setup(lambda, expr)?; + Ok(ast::Expression::Block(vec![setup, closure_variable])) } } - fn lambda_no_capture(&mut self, lambda: HirLambda) -> ast::Expression { + fn lambda_no_capture( + &mut self, + lambda: HirLambda, + ) -> Result { let ret_type = self.convert_type(&lambda.return_type); let lambda_name = "lambda"; let parameter_types = vecmap(&lambda.parameters, |(_, typ)| self.convert_type(typ)); @@ -1240,7 +1328,7 @@ impl<'interner> Monomorphizer<'interner> { vecmap(lambda.parameters, |(pattern, typ)| (pattern, typ, Visibility::Private)).into(); let parameters = self.parameters(¶meters); - let body = self.expr(lambda.body); + let body = self.expr(lambda.body)?; let id = self.next_function_id(); let return_type = ret_type.clone(); @@ -1254,20 +1342,20 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::Function(parameter_types, Box::new(ret_type), Box::new(ast::Type::Unit)); let name = lambda_name.to_owned(); - ast::Expression::Ident(ast::Ident { + Ok(ast::Expression::Ident(ast::Ident { definition: Definition::Function(id), mutable: false, location: None, name, typ, - }) + })) } fn lambda_with_setup( &mut self, lambda: HirLambda, expr: node_interner::ExprId, - ) -> (ast::Expression, ast::Expression) { + ) -> Result<(ast::Expression, ast::Expression), MonomorphizationError> { // returns (, ) // which can be used directly in callsites or transformed // directly to a single `Expression` @@ -1343,7 +1431,7 @@ impl<'interner> Monomorphizer<'interner> { self.lambda_envs_stack .push(LambdaContext { env_ident: env_ident.clone(), captures: lambda.captures }); - let body = self.expr(lambda.body); + let body = self.expr(lambda.body)?; self.lambda_envs_stack.pop(); let lambda_fn_typ: ast::Type = @@ -1385,7 +1473,7 @@ impl<'interner> Monomorphizer<'interner> { typ: ast::Type::Tuple(vec![env_typ, lambda_fn_typ]), }); - (block_let_stmt, closure_ident) + Ok((block_let_stmt, closure_ident)) } /// Implements std::unsafe::zeroed by returning an appropriate zeroed diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c18379f1c26..c661cc92eef 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1130,7 +1130,7 @@ mod test { fn check_rewrite(src: &str, expected: &str) { let (_program, mut context, _errors) = get_program(src); let main_func_id = context.def_interner.find_function("main").unwrap(); - let program = monomorphize(main_func_id, &mut context.def_interner); + let program = monomorphize(main_func_id, &mut context.def_interner).unwrap(); assert!(format!("{}", program) == expected); } diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 0929739a6ab..92c09ec889e 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -1,5 +1,5 @@ use acvm::{acir::native_types::WitnessMap, BlackBoxFunctionSolver}; -use noirc_driver::{compile_no_check, CompileOptions}; +use noirc_driver::{compile_no_check, CompileError, CompileOptions}; use noirc_errors::{debug_info::DebugInfo, FileDiagnostic}; use noirc_evaluator::errors::RuntimeError; use noirc_frontend::hir::{def_map::TestFunction, Context}; @@ -45,14 +45,18 @@ pub fn run_test( /// that a constraint was never satisfiable. /// An example of this is the program `assert(false)` /// In that case, we check if the test function should fail, and if so, we return `TestStatus::Pass`. -fn test_status_program_compile_fail(err: RuntimeError, test_function: TestFunction) -> TestStatus { +fn test_status_program_compile_fail(err: CompileError, test_function: TestFunction) -> TestStatus { // The test has failed compilation, but it should never fail. Report error. if !test_function.should_fail() { return TestStatus::CompileError(err.into()); } // The test has failed compilation, extract the assertion message if present and check if it's expected. - let assert_message = if let RuntimeError::FailedConstraint { assert_message, .. } = &err { + let assert_message = if let CompileError::RuntimeError(RuntimeError::FailedConstraint { + assert_message, + .. + }) = &err + { assert_message.clone() } else { None From 10e82920798380f50046e52db4a20ca205191ab7 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:36:25 +0100 Subject: [PATCH 012/416] feat: add poseidon2 opcode implementation for acvm/brillig, and Noir (#4398) and poseidon2 noir implementation # Description ## Problem\* Resolves #4170 ## Summary\* The PR implements Poseidon2 permutation for ACMV and Brillig, enabling the use of the opcode. Then it also includes a Noir implementation of Poseidon2 using the opcode in the stdlib ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [X] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: kevaundray --- Cargo.lock | 1 + acvm-repo/acvm/src/pwg/blackbox/hash.rs | 36 +- acvm-repo/acvm/src/pwg/blackbox/mod.rs | 8 +- .../src/curve_specific_solver.rs | 12 + acvm-repo/bn254_blackbox_solver/Cargo.toml | 1 + acvm-repo/bn254_blackbox_solver/src/lib.rs | 11 + .../bn254_blackbox_solver/src/poseidon2.rs | 1043 +++++++++++++++++ acvm-repo/brillig_vm/src/black_box.rs | 13 +- acvm-repo/brillig_vm/src/lib.rs | 7 + .../noirc_evaluator/src/brillig/brillig_ir.rs | 8 + .../cryptographic_primitives/hashes.mdx | 13 + noir_stdlib/src/hash.nr | 3 +- noir_stdlib/src/hash/poseidon2.nr | 119 ++ .../poseidon_bn254_hash/Prover.toml | 5 + .../poseidon_bn254_hash/src/main.nr | 6 +- tooling/lsp/src/solver.rs | 8 + 16 files changed, 1288 insertions(+), 6 deletions(-) create mode 100644 acvm-repo/bn254_blackbox_solver/src/poseidon2.rs create mode 100644 noir_stdlib/src/hash/poseidon2.nr diff --git a/Cargo.lock b/Cargo.lock index 0f575d9c46e..714b700119a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -589,6 +589,7 @@ dependencies = [ "js-sys", "noir_grumpkin", "num-bigint", + "num-traits", "pkg-config", "reqwest", "rust-embed", diff --git a/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 06489822c92..1bc26f06188 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -3,7 +3,7 @@ use acir::{ native_types::{Witness, WitnessMap}, BlackBoxFunc, FieldElement, }; -use acvm_blackbox_solver::{sha256compression, BlackBoxResolutionError}; +use acvm_blackbox_solver::{sha256compression, BlackBoxFunctionSolver, BlackBoxResolutionError}; use crate::pwg::{insert_value, witness_to_value}; use crate::OpcodeResolutionError; @@ -131,3 +131,37 @@ pub(crate) fn solve_sha_256_permutation_opcode( Ok(()) } + +pub(crate) fn solve_poseidon2_permutation_opcode( + backend: &impl BlackBoxFunctionSolver, + initial_witness: &mut WitnessMap, + inputs: &[FunctionInput], + outputs: &[Witness], + len: u32, +) -> Result<(), OpcodeResolutionError> { + if len as usize != inputs.len() { + return Err(OpcodeResolutionError::BlackBoxFunctionFailed( + acir::BlackBoxFunc::Poseidon2Permutation, + format!( + "the number of inputs does not match specified length. {} > {}", + inputs.len(), + len + ), + )); + } + + // Read witness assignments + let mut state = Vec::new(); + for input in inputs.iter() { + let witness_assignment = witness_to_value(initial_witness, input.witness)?; + state.push(*witness_assignment); + } + + let state = backend.poseidon2_permutation(&state, len)?; + + // Write witness assignments + for (output_witness, value) in outputs.iter().zip(state.into_iter()) { + insert_value(output_witness, value, initial_witness)?; + } + Ok(()) +} diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 7ae92fd84fc..4309cad1b2e 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -5,7 +5,9 @@ use acir::{ }; use acvm_blackbox_solver::{blake2s, blake3, keccak256, keccakf1600, sha256}; -use self::{bigint::BigIntSolver, pedersen::pedersen_hash}; +use self::{ + bigint::BigIntSolver, hash::solve_poseidon2_permutation_opcode, pedersen::pedersen_hash, +}; use super::{insert_value, OpcodeNotSolvable, OpcodeResolutionError}; use crate::{pwg::witness_to_value, BlackBoxFunctionSolver}; @@ -204,7 +206,6 @@ pub(crate) fn solve( BlackBoxFuncCall::BigIntToLeBytes { input, outputs } => { bigint_solver.bigint_to_bytes(*input, outputs, initial_witness) } - BlackBoxFuncCall::Poseidon2Permutation { .. } => todo!(), BlackBoxFuncCall::Sha256Compression { inputs, hash_values, outputs } => { solve_sha_256_permutation_opcode( initial_witness, @@ -214,5 +215,8 @@ pub(crate) fn solve( bb_func.get_black_box_func(), ) } + BlackBoxFuncCall::Poseidon2Permutation { inputs, outputs, len } => { + solve_poseidon2_permutation_opcode(backend, initial_witness, inputs, outputs, *len) + } } } diff --git a/acvm-repo/blackbox_solver/src/curve_specific_solver.rs b/acvm-repo/blackbox_solver/src/curve_specific_solver.rs index 2234710dec0..f0ab4561229 100644 --- a/acvm-repo/blackbox_solver/src/curve_specific_solver.rs +++ b/acvm-repo/blackbox_solver/src/curve_specific_solver.rs @@ -36,6 +36,11 @@ pub trait BlackBoxFunctionSolver { input2_x: &FieldElement, input2_y: &FieldElement, ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError>; + fn poseidon2_permutation( + &self, + _inputs: &[FieldElement], + _len: u32, + ) -> Result, BlackBoxResolutionError>; } pub struct StubbedBlackBoxSolver; @@ -89,4 +94,11 @@ impl BlackBoxFunctionSolver for StubbedBlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Err(Self::fail(BlackBoxFunc::EmbeddedCurveAdd)) } + fn poseidon2_permutation( + &self, + _inputs: &[FieldElement], + _len: u32, + ) -> Result, BlackBoxResolutionError> { + Err(Self::fail(BlackBoxFunc::Poseidon2Permutation)) + } } diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index ef80e2c1c0f..ea601a6b80f 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -16,6 +16,7 @@ repository.workspace = true acir.workspace = true acvm_blackbox_solver.workspace = true thiserror.workspace = true +num-traits.workspace = true rust-embed = { version = "6.6.0", features = [ "debug-embed", diff --git a/acvm-repo/bn254_blackbox_solver/src/lib.rs b/acvm-repo/bn254_blackbox_solver/src/lib.rs index 13aa956f9e1..be0e60ada96 100644 --- a/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -6,9 +6,11 @@ use acir::{BlackBoxFunc, FieldElement}; use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; mod fixed_base_scalar_mul; +mod poseidon2; mod wasm; pub use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; +use poseidon2::Poseidon2; use wasm::Barretenberg; use self::wasm::{Pedersen, SchnorrSig}; @@ -97,4 +99,13 @@ impl BlackBoxFunctionSolver for Bn254BlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { embedded_curve_add(*input1_x, *input1_y, *input2_x, *input2_y) } + + fn poseidon2_permutation( + &self, + inputs: &[FieldElement], + len: u32, + ) -> Result, BlackBoxResolutionError> { + let poseidon = Poseidon2::new(); + poseidon.permutation(inputs, len) + } } diff --git a/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs b/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs new file mode 100644 index 00000000000..e0ed5bcd053 --- /dev/null +++ b/acvm-repo/bn254_blackbox_solver/src/poseidon2.rs @@ -0,0 +1,1043 @@ +use acir::FieldElement; +use acvm_blackbox_solver::BlackBoxResolutionError; +use num_bigint::BigUint; +use num_traits::Num; + +pub(crate) struct Poseidon2 { + t: u32, + rounds_f: u32, + rounds_p: u32, + internal_matrix_diagonal: [FieldElement; 4], + round_constant: [[FieldElement; 4]; 64], +} + +impl Poseidon2 { + pub(crate) fn new() -> Self { + Poseidon2 { + t: 4, + rounds_f: 8, + rounds_p: 56, + internal_matrix_diagonal: [ + Poseidon2::field_from_hex( + "0x10dc6e9c006ea38b04b1e03b4bd9490c0d03f98929ca1d7fb56821fd19d3b6e7", + ), + Poseidon2::field_from_hex( + "0x0c28145b6a44df3e0149b3d0a30b3bb599df9756d4dd9b84a86b38cfb45a740b", + ), + Poseidon2::field_from_hex( + "0x00544b8338791518b2c7645a50392798b21f75bb60e3596170067d00141cac15", + ), + Poseidon2::field_from_hex( + "0x222c01175718386f2e2e82eb122789e352e105a3b8fa852613bc534433ee428b", + ), + ], + round_constant: [ + [ + Poseidon2::field_from_hex( + "0x19b849f69450b06848da1d39bd5e4a4302bb86744edc26238b0878e269ed23e5", + ), + Poseidon2::field_from_hex( + "0x265ddfe127dd51bd7239347b758f0a1320eb2cc7450acc1dad47f80c8dcf34d6", + ), + Poseidon2::field_from_hex( + "0x199750ec472f1809e0f66a545e1e51624108ac845015c2aa3dfc36bab497d8aa", + ), + Poseidon2::field_from_hex( + "0x157ff3fe65ac7208110f06a5f74302b14d743ea25067f0ffd032f787c7f1cdf8", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2e49c43c4569dd9c5fd35ac45fca33f10b15c590692f8beefe18f4896ac94902", + ), + Poseidon2::field_from_hex( + "0x0e35fb89981890520d4aef2b6d6506c3cb2f0b6973c24fa82731345ffa2d1f1e", + ), + Poseidon2::field_from_hex( + "0x251ad47cb15c4f1105f109ae5e944f1ba9d9e7806d667ffec6fe723002e0b996", + ), + Poseidon2::field_from_hex( + "0x13da07dc64d428369873e97160234641f8beb56fdd05e5f3563fa39d9c22df4e", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0c009b84e650e6d23dc00c7dccef7483a553939689d350cd46e7b89055fd4738", + ), + Poseidon2::field_from_hex( + "0x011f16b1c63a854f01992e3956f42d8b04eb650c6d535eb0203dec74befdca06", + ), + Poseidon2::field_from_hex( + "0x0ed69e5e383a688f209d9a561daa79612f3f78d0467ad45485df07093f367549", + ), + Poseidon2::field_from_hex( + "0x04dba94a7b0ce9e221acad41472b6bbe3aec507f5eb3d33f463672264c9f789b", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0a3f2637d840f3a16eb094271c9d237b6036757d4bb50bf7ce732ff1d4fa28e8", + ), + Poseidon2::field_from_hex( + "0x259a666f129eea198f8a1c502fdb38fa39b1f075569564b6e54a485d1182323f", + ), + Poseidon2::field_from_hex( + "0x28bf7459c9b2f4c6d8e7d06a4ee3a47f7745d4271038e5157a32fdf7ede0d6a1", + ), + Poseidon2::field_from_hex( + "0x0a1ca941f057037526ea200f489be8d4c37c85bbcce6a2aeec91bd6941432447", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0c6f8f958be0e93053d7fd4fc54512855535ed1539f051dcb43a26fd926361cf", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x123106a93cd17578d426e8128ac9d90aa9e8a00708e296e084dd57e69caaf811", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x26e1ba52ad9285d97dd3ab52f8e840085e8fa83ff1e8f1877b074867cd2dee75", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1cb55cad7bd133de18a64c5c47b9c97cbe4d8b7bf9e095864471537e6a4ae2c5", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1dcd73e46acd8f8e0e2c7ce04bde7f6d2a53043d5060a41c7143f08e6e9055d0", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x011003e32f6d9c66f5852f05474a4def0cda294a0eb4e9b9b12b9bb4512e5574", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2b1e809ac1d10ab29ad5f20d03a57dfebadfe5903f58bafed7c508dd2287ae8c", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2539de1785b735999fb4dac35ee17ed0ef995d05ab2fc5faeaa69ae87bcec0a5", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0c246c5a2ef8ee0126497f222b3e0a0ef4e1c3d41c86d46e43982cb11d77951d", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x192089c4974f68e95408148f7c0632edbb09e6a6ad1a1c2f3f0305f5d03b527b", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1eae0ad8ab68b2f06a0ee36eeb0d0c058529097d91096b756d8fdc2fb5a60d85", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x179190e5d0e22179e46f8282872abc88db6e2fdc0dee99e69768bd98c5d06bfb", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x29bb9e2c9076732576e9a81c7ac4b83214528f7db00f31bf6cafe794a9b3cd1c", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x225d394e42207599403efd0c2464a90d52652645882aac35b10e590e6e691e08", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x064760623c25c8cf753d238055b444532be13557451c087de09efd454b23fd59", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x10ba3a0e01df92e87f301c4b716d8a394d67f4bf42a75c10922910a78f6b5b87", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0e070bf53f8451b24f9c6e96b0c2a801cb511bc0c242eb9d361b77693f21471c", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1b94cd61b051b04dd39755ff93821a73ccd6cb11d2491d8aa7f921014de252fb", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1d7cb39bafb8c744e148787a2e70230f9d4e917d5713bb050487b5aa7d74070b", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2ec93189bd1ab4f69117d0fe980c80ff8785c2961829f701bb74ac1f303b17db", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2db366bfdd36d277a692bb825b86275beac404a19ae07a9082ea46bd83517926", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x062100eb485db06269655cf186a68532985275428450359adc99cec6960711b8", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0761d33c66614aaa570e7f1e8244ca1120243f92fa59e4f900c567bf41f5a59b", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x20fc411a114d13992c2705aa034e3f315d78608a0f7de4ccf7a72e494855ad0d", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x25b5c004a4bdfcb5add9ec4e9ab219ba102c67e8b3effb5fc3a30f317250bc5a", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x23b1822d278ed632a494e58f6df6f5ed038b186d8474155ad87e7dff62b37f4b", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x22734b4c5c3f9493606c4ba9012499bf0f14d13bfcfcccaa16102a29cc2f69e0", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x26c0c8fe09eb30b7e27a74dc33492347e5bdff409aa3610254413d3fad795ce5", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x070dd0ccb6bd7bbae88eac03fa1fbb26196be3083a809829bbd626df348ccad9", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x12b6595bdb329b6fb043ba78bb28c3bec2c0a6de46d8c5ad6067c4ebfd4250da", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x248d97d7f76283d63bec30e7a5876c11c06fca9b275c671c5e33d95bb7e8d729", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1a306d439d463b0816fc6fd64cc939318b45eb759ddde4aa106d15d9bd9baaaa", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x28a8f8372e3c38daced7c00421cb4621f4f1b54ddc27821b0d62d3d6ec7c56cf", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0094975717f9a8a8bb35152f24d43294071ce320c829f388bc852183e1e2ce7e", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x04d5ee4c3aa78f7d80fde60d716480d3593f74d4f653ae83f4103246db2e8d65", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2a6cf5e9aa03d4336349ad6fb8ed2269c7bef54b8822cc76d08495c12efde187", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2304d31eaab960ba9274da43e19ddeb7f792180808fd6e43baae48d7efcba3f3", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x03fd9ac865a4b2a6d5e7009785817249bff08a7e0726fcb4e1c11d39d199f0b0", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x00b7258ded52bbda2248404d55ee5044798afc3a209193073f7954d4d63b0b64", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x159f81ada0771799ec38fca2d4bf65ebb13d3a74f3298db36272c5ca65e92d9a", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1ef90e67437fbc8550237a75bc28e3bb9000130ea25f0c5471e144cf4264431f", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1e65f838515e5ff0196b49aa41a2d2568df739bc176b08ec95a79ed82932e30d", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2b1b045def3a166cec6ce768d079ba74b18c844e570e1f826575c1068c94c33f", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0832e5753ceb0ff6402543b1109229c165dc2d73bef715e3f1c6e07c168bb173", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x02f614e9cedfb3dc6b762ae0a37d41bab1b841c2e8b6451bc5a8e3c390b6ad16", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0e2427d38bd46a60dd640b8e362cad967370ebb777bedff40f6a0be27e7ed705", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0493630b7c670b6deb7c84d414e7ce79049f0ec098c3c7c50768bbe29214a53a", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x22ead100e8e482674decdab17066c5a26bb1515355d5461a3dc06cc85327cea9", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x25b3e56e655b42cdaae2626ed2554d48583f1ae35626d04de5084e0b6d2a6f16", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1e32752ada8836ef5837a6cde8ff13dbb599c336349e4c584b4fdc0a0cf6f9d0", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2fa2a871c15a387cc50f68f6f3c3455b23c00995f05078f672a9864074d412e5", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x2f569b8a9a4424c9278e1db7311e889f54ccbf10661bab7fcd18e7c7a7d83505", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x044cb455110a8fdd531ade530234c518a7df93f7332ffd2144165374b246b43d", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x227808de93906d5d420246157f2e42b191fe8c90adfe118178ddc723a5319025", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x02fcca2934e046bc623adead873579865d03781ae090ad4a8579d2e7a6800355", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0ef915f0ac120b876abccceb344a1d36bad3f3c5ab91a8ddcbec2e060d8befac", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + Poseidon2::field_from_hex( + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ], + [ + Poseidon2::field_from_hex( + "0x1797130f4b7a3e1777eb757bc6f287f6ab0fb85f6be63b09f3b16ef2b1405d38", + ), + Poseidon2::field_from_hex( + "0x0a76225dc04170ae3306c85abab59e608c7f497c20156d4d36c668555decc6e5", + ), + Poseidon2::field_from_hex( + "0x1fffb9ec1992d66ba1e77a7b93209af6f8fa76d48acb664796174b5326a31a5c", + ), + Poseidon2::field_from_hex( + "0x25721c4fc15a3f2853b57c338fa538d85f8fbba6c6b9c6090611889b797b9c5f", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0c817fd42d5f7a41215e3d07ba197216adb4c3790705da95eb63b982bfcaf75a", + ), + Poseidon2::field_from_hex( + "0x13abe3f5239915d39f7e13c2c24970b6df8cf86ce00a22002bc15866e52b5a96", + ), + Poseidon2::field_from_hex( + "0x2106feea546224ea12ef7f39987a46c85c1bc3dc29bdbd7a92cd60acb4d391ce", + ), + Poseidon2::field_from_hex( + "0x21ca859468a746b6aaa79474a37dab49f1ca5a28c748bc7157e1b3345bb0f959", + ), + ], + [ + Poseidon2::field_from_hex( + "0x05ccd6255c1e6f0c5cf1f0df934194c62911d14d0321662a8f1a48999e34185b", + ), + Poseidon2::field_from_hex( + "0x0f0e34a64b70a626e464d846674c4c8816c4fb267fe44fe6ea28678cb09490a4", + ), + Poseidon2::field_from_hex( + "0x0558531a4e25470c6157794ca36d0e9647dbfcfe350d64838f5b1a8a2de0d4bf", + ), + Poseidon2::field_from_hex( + "0x09d3dca9173ed2faceea125157683d18924cadad3f655a60b72f5864961f1455", + ), + ], + [ + Poseidon2::field_from_hex( + "0x0328cbd54e8c0913493f866ed03d218bf23f92d68aaec48617d4c722e5bd4335", + ), + Poseidon2::field_from_hex( + "0x2bf07216e2aff0a223a487b1a7094e07e79e7bcc9798c648ee3347dd5329d34b", + ), + Poseidon2::field_from_hex( + "0x1daf345a58006b736499c583cb76c316d6f78ed6a6dffc82111e11a63fe412df", + ), + Poseidon2::field_from_hex( + "0x176563472456aaa746b694c60e1823611ef39039b2edc7ff391e6f2293d2c404", + ), + ], + ], + } + } + fn field_from_hex(hex: &str) -> FieldElement { + let bigint = BigUint::from_str_radix(hex.strip_prefix("0x").unwrap(), 16).unwrap(); + FieldElement::from_be_bytes_reduce(&bigint.to_bytes_be()) + } + + fn single_box(x: FieldElement) -> FieldElement { + let s = x * x; + s * s * x + } + + fn s_box(input: &mut [FieldElement]) { + for i in input { + *i = Self::single_box(*i); + } + } + + fn add_round_constants(&self, state: &mut [FieldElement], round: usize) { + for (state_element, constant_element) in state.iter_mut().zip(self.round_constant[round]) { + *state_element += constant_element; + } + } + + /// Algorithm is taken directly from the Poseidon2 implementation in Barretenberg crypto module. + fn matrix_multiplication_4x4(input: &mut [FieldElement]) { + assert!(input.len() == 4); + let t0 = input[0] + input[1]; // A + B + let t1 = input[2] + input[3]; // C + D + let mut t2 = input[1] + input[1]; // 2B + t2 += t1; // 2B + C + D + let mut t3 = input[3] + input[3]; // 2D + t3 += t0; // 2D + A + B + let mut t4 = t1 + t1; + t4 += t4; + t4 += t3; // A + B + 4C + 6D + let mut t5 = t0 + t0; + t5 += t5; + t5 += t2; // 4A + 6B + C + D + let t6 = t3 + t5; // 5A + 7B + C + 3D + let t7 = t2 + t4; // A + 3B + 5C + 7D + input[0] = t6; + input[1] = t5; + input[2] = t7; + input[3] = t4; + } + + fn internal_m_multiplication(&self, input: &mut [FieldElement]) { + let mut sum = FieldElement::zero(); + for i in input.iter() { + sum += *i; + } + for (index, i) in input.iter_mut().enumerate() { + *i = *i * self.internal_matrix_diagonal[index]; + *i += sum; + } + } + + pub(crate) fn permutation( + &self, + inputs: &[FieldElement], + len: u32, + ) -> Result, BlackBoxResolutionError> { + if len as usize != inputs.len() { + return Err(BlackBoxResolutionError::Failed( + acir::BlackBoxFunc::Poseidon2Permutation, + format!( + "the number of inputs does not match specified length. {} > {}", + inputs.len(), + len + ), + )); + } + if len != self.t { + return Err(BlackBoxResolutionError::Failed( + acir::BlackBoxFunc::Poseidon2Permutation, + format!("Expected {} values but encountered {}", self.t, len), + )); + } + // Read witness assignments + let mut state = [FieldElement::zero(); 4]; + for (index, input) in inputs.iter().enumerate() { + state[index] = *input; + } + // Apply 1st linear layer + Self::matrix_multiplication_4x4(&mut state); + + // First set of external rounds + let rf_first = self.rounds_f / 2; + for r in 0..rf_first { + self.add_round_constants(&mut state, r as usize); + Self::s_box(&mut state); + Self::matrix_multiplication_4x4(&mut state); + } + // Internal rounds + let p_end = rf_first + self.rounds_p; + for r in rf_first..p_end { + state[0] += self.round_constant[r as usize][0]; + state[0] = Self::single_box(state[0]); + self.internal_m_multiplication(&mut state); + } + + // Remaining external rounds + let num_rounds = self.rounds_f + self.rounds_p; + for i in p_end..num_rounds { + self.add_round_constants(&mut state, i as usize); + Self::s_box(&mut state); + Self::matrix_multiplication_4x4(&mut state); + } + Ok(state.into()) + } +} diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index 5b2680465ab..73b57b907f3 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -184,7 +184,18 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::BigIntDiv { .. } => todo!(), BlackBoxOp::BigIntFromLeBytes { .. } => todo!(), BlackBoxOp::BigIntToLeBytes { .. } => todo!(), - BlackBoxOp::Poseidon2Permutation { .. } => todo!(), + BlackBoxOp::Poseidon2Permutation { message, output, len } => { + let input = read_heap_vector(memory, message); + let input: Vec = input.iter().map(|x| x.to_field()).collect(); + let len = memory.read(*len).to_u128() as u32; + let result = solver.poseidon2_permutation(&input, len)?; + let mut values = Vec::new(); + for i in result { + values.push(Value::from(i)); + } + memory.write_slice(memory.read_ref(output.pointer), &values); + Ok(()) + } BlackBoxOp::Sha256Compression { input, hash_values, output } => { let mut message = [0; 16]; let inputs = read_heap_vector(memory, input); diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 13accbeacb3..c7bf014f068 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -568,6 +568,13 @@ impl BlackBoxFunctionSolver for DummyBlackBoxSolver { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { Ok((5_u128.into(), 6_u128.into())) } + fn poseidon2_permutation( + &self, + _input: &[FieldElement], + len: u32, + ) -> Result, BlackBoxResolutionError> { + Ok(vec![0_u128.into(); len as usize]) + } } #[cfg(test)] diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 073b0e6f59f..90608974f98 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -1151,6 +1151,14 @@ pub(crate) mod tests { ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { panic!("Path not trodden by this test") } + + fn poseidon2_permutation( + &self, + _inputs: &[FieldElement], + _len: u32, + ) -> Result, BlackBoxResolutionError> { + Ok(vec![0_u128.into(), 1_u128.into(), 2_u128.into(), 3_u128.into()]) + } } pub(crate) fn create_context() -> BrilligContext { diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index 85706384eee..b9239f822e8 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -114,6 +114,19 @@ example: #include_code poseidon test_programs/execution_success/poseidon_bn254_hash/src/main.nr rust +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +The above example for Poseidon also includes Poseidon2. + ## mimc_bn254 and mimc `mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index 7a931f7c047..fcf21436197 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -1,5 +1,6 @@ mod poseidon; mod mimc; +mod poseidon2; mod pedersen; use crate::default::Default; @@ -73,7 +74,7 @@ pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] {} #[foreign(poseidon2_permutation)] -pub fn poseidon2_permutation(_input: [u8; N], _state_length: u32) -> [u8; N] {} +pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} #[foreign(sha256_compression)] pub fn sha256_compression(_input: [u32; 16], _state: [u32; 8]) -> [u32; 8] {} diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr new file mode 100644 index 00000000000..8e0fcc6858e --- /dev/null +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -0,0 +1,119 @@ +global rate = 3; + +struct Poseidon2 { + cache: [Field;3], + state: [Field;4], + cache_size: u32, + squeeze_mode: bool, // 0 => absorb, 1 => squeeze +} + +impl Poseidon2 { + + pub fn hash(input: [Field; N], message_size: u32) -> Field { + if message_size == N { + Poseidon2::hash_internal(input, N, false) + } else { + Poseidon2::hash_internal(input, message_size, true) + } + } + + fn new(iv: Field) -> Poseidon2 { + let mut result = Poseidon2 { + cache: [0;3], + state: [0;4], + cache_size: 0, + squeeze_mode: false, + }; + result.state[rate] = iv; + result + } + + fn perform_duplex(&mut self) -> [Field; rate] { + // zero-pad the cache + for i in 0..rate { + if i >= self.cache_size { + self.cache[i] = 0; + } + } + // add the cache into sponge state + for i in 0..rate { + self.state[i] += self.cache[i]; + } + self.state = crate::hash::poseidon2_permutation(self.state, 4); + // return `rate` number of field elements from the sponge state. + let mut result = [0; rate]; + for i in 0..rate { + result[i] = self.state[i]; + } + result + } + + fn absorb(&mut self, input: Field) { + if (!self.squeeze_mode) & (self.cache_size == rate) { + // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache + let _ = self.perform_duplex(); + self.cache[0] = input; + self.cache_size = 1; + } else if (!self.squeeze_mode) & (self.cache_size != rate) { + // If we're absorbing, and the cache is not full, add the input into the cache + self.cache[self.cache_size] = input; + self.cache_size += 1; + } else if self.squeeze_mode { + // If we're in squeeze mode, switch to absorb mode and add the input into the cache. + // N.B. I don't think this code path can be reached?! + self.cache[0] = input; + self.cache_size = 1; + self.squeeze_mode = false; + } + } + + fn squeeze(&mut self) -> Field + { + if self.squeeze_mode & (self.cache_size == 0) { + // If we're in squeze mode and the cache is empty, there is nothing left to squeeze out of the sponge! + // Switch to absorb mode. + self.squeeze_mode = false; + self.cache_size = 0; + } + if !self.squeeze_mode { + // If we're in absorb mode, apply sponge permutation to compress the cache, populate cache with compressed + // state and switch to squeeze mode. Note: this code block will execute if the previous `if` condition was + // matched + let new_output_elements = self.perform_duplex(); + self.squeeze_mode = true; + for i in 0..rate { + self.cache[i] = new_output_elements[i]; + } + self.cache_size = rate; + } + // By this point, we should have a non-empty cache. Pop one item off the top of the cache and return it. + let result = self.cache[0]; + for i in 1..rate { + if i < self.cache_size { + self.cache[i - 1] = self.cache[i]; + } + } + self.cache_size -= 1; + self.cache[self.cache_size] = 0; + result + } + + fn hash_internal(input:[Field;N], in_len:u32, is_variable_length: bool) -> Field + { + let iv : Field = (in_len as Field)*18446744073709551616; + let mut sponge = Poseidon2::new(iv); + for i in 0..input.len() { + if i as u32 < in_len { + sponge.absorb(input[i]); + } + } + + // In the case where the hash preimage is variable-length, we append `1` to the end of the input, to distinguish + // from fixed-length hashes. (the combination of this additional field element + the hash IV ensures + // fixed-length and variable-length hashes do not collide) + if is_variable_length { + sponge.absorb(1); + } + sponge.squeeze() + } +} diff --git a/test_programs/execution_success/poseidon_bn254_hash/Prover.toml b/test_programs/execution_success/poseidon_bn254_hash/Prover.toml index 8eecf9a3db2..fa6fd05b0a3 100644 --- a/test_programs/execution_success/poseidon_bn254_hash/Prover.toml +++ b/test_programs/execution_success/poseidon_bn254_hash/Prover.toml @@ -2,3 +2,8 @@ x1 = [1,2] y1 = "0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a" x2 = [1,2,3,4] y2 = "0x299c867db6c1fdd79dcefa40e4510b9837e60ebb1ce0663dbaa525df65250465" +x3 = ["4218458030232820015255714794613421442512497197372123294583664908262453897094", + "4218458030232820015255714794613421442512497197372123294583664908262453897094", + "4218458030232820015255714794613421442512497197372123294583664908262453897094", + "4218458030232820015255714794613421442512497197372123294583664908262453897094"] + y3 = "0x2f43a0f83b51a6f5fc839dea0ecec74947637802a579fa9841930a25a0bcec11" diff --git a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr index e742a440d1c..939b99595c7 100644 --- a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr +++ b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr @@ -1,11 +1,15 @@ // docs:start:poseidon use dep::std::hash::poseidon; +use dep::std::hash::poseidon2; -fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field) { +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field; 4], y3: Field) { let hash1 = poseidon::bn254::hash_2(x1); assert(hash1 == y1); let hash2 = poseidon::bn254::hash_4(x2); assert(hash2 == y2); + + let hash3 = poseidon2::Poseidon2::hash(x3, x3.len() as u32); + assert(hash3 == y3); } // docs:end:poseidon diff --git a/tooling/lsp/src/solver.rs b/tooling/lsp/src/solver.rs index f001cebaa4d..d0acbf1aec5 100644 --- a/tooling/lsp/src/solver.rs +++ b/tooling/lsp/src/solver.rs @@ -49,4 +49,12 @@ impl BlackBoxFunctionSolver for WrapperSolver { ) -> Result<(acvm::FieldElement, acvm::FieldElement), acvm::BlackBoxResolutionError> { self.0.ec_add(input1_x, input1_y, input2_x, input2_y) } + + fn poseidon2_permutation( + &self, + inputs: &[acvm::FieldElement], + len: u32, + ) -> Result, acvm::BlackBoxResolutionError> { + self.0.poseidon2_permutation(inputs, len) + } } From 568a7812b6a11cd427ffca38103179c7ec0830db Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:19:15 +0000 Subject: [PATCH 013/416] chore: nargo fmt (#4434) # Description ## Problem\* Resolves ## Summary\* This PR fixes the formatting CI ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/collections/bounded_vec.nr | 2 +- noir_stdlib/src/hash/poseidon2.nr | 41 +++++++++------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index f78d86de77d..4a14bd16376 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -77,7 +77,7 @@ impl BoundedVec { let mut exceeded_len = false; for i in 0..MaxLen { exceeded_len |= i == self.len; - if (!exceeded_len) { + if !exceeded_len { ret |= predicate(self.storage[i]); } } diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 8e0fcc6858e..64c1876b4e2 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -1,4 +1,4 @@ -global rate = 3; +global RATE = 3; struct Poseidon2 { cache: [Field;3], @@ -18,43 +18,38 @@ impl Poseidon2 { } fn new(iv: Field) -> Poseidon2 { - let mut result = Poseidon2 { - cache: [0;3], - state: [0;4], - cache_size: 0, - squeeze_mode: false, - }; - result.state[rate] = iv; + let mut result = Poseidon2 { cache: [0; 3], state: [0; 4], cache_size: 0, squeeze_mode: false }; + result.state[RATE] = iv; result } - fn perform_duplex(&mut self) -> [Field; rate] { + fn perform_duplex(&mut self) -> [Field; RATE] { // zero-pad the cache - for i in 0..rate { + for i in 0..RATE { if i >= self.cache_size { self.cache[i] = 0; } } // add the cache into sponge state - for i in 0..rate { + for i in 0..RATE { self.state[i] += self.cache[i]; } self.state = crate::hash::poseidon2_permutation(self.state, 4); - // return `rate` number of field elements from the sponge state. - let mut result = [0; rate]; - for i in 0..rate { + // return `RATE` number of field elements from the sponge state. + let mut result = [0; RATE]; + for i in 0..RATE { result[i] = self.state[i]; } result } fn absorb(&mut self, input: Field) { - if (!self.squeeze_mode) & (self.cache_size == rate) { + if (!self.squeeze_mode) & (self.cache_size == RATE) { // If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache let _ = self.perform_duplex(); self.cache[0] = input; self.cache_size = 1; - } else if (!self.squeeze_mode) & (self.cache_size != rate) { + } else if (!self.squeeze_mode) & (self.cache_size != RATE) { // If we're absorbing, and the cache is not full, add the input into the cache self.cache[self.cache_size] = input; self.cache_size += 1; @@ -67,8 +62,7 @@ impl Poseidon2 { } } - fn squeeze(&mut self) -> Field - { + fn squeeze(&mut self) -> Field { if self.squeeze_mode & (self.cache_size == 0) { // If we're in squeze mode and the cache is empty, there is nothing left to squeeze out of the sponge! // Switch to absorb mode. @@ -81,14 +75,14 @@ impl Poseidon2 { // matched let new_output_elements = self.perform_duplex(); self.squeeze_mode = true; - for i in 0..rate { + for i in 0..RATE { self.cache[i] = new_output_elements[i]; } - self.cache_size = rate; + self.cache_size = RATE; } // By this point, we should have a non-empty cache. Pop one item off the top of the cache and return it. let result = self.cache[0]; - for i in 1..rate { + for i in 1..RATE { if i < self.cache_size { self.cache[i - 1] = self.cache[i]; } @@ -98,9 +92,8 @@ impl Poseidon2 { result } - fn hash_internal(input:[Field;N], in_len:u32, is_variable_length: bool) -> Field - { - let iv : Field = (in_len as Field)*18446744073709551616; + fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field { + let iv : Field = (in_len as Field) * 18446744073709551616; let mut sponge = Poseidon2::new(iv); for i in 0..input.len() { if i as u32 < in_len { From b9384fb23abf4ab15e880fb7e03c21509a9fa8a6 Mon Sep 17 00:00:00 2001 From: jfecher Date: Tue, 27 Feb 2024 10:44:23 +0000 Subject: [PATCH 014/416] chore!: Remove empty value from bounded vec (#4431) # Description ## Problem\* ## Summary\* Removes the `empty_value` field from the bounded vec. This muddies the API and shouldn't be needed since we have `crate::unsafe::zeroed()` instead. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[Exceptional Case]** Documentation to be submitted in a separate PR. - Included in https://github.com/noir-lang/noir/pull/4430 # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Tom French --- noir_stdlib/src/collections/bounded_vec.nr | 8 ++--- .../noir_test_success/bounded_vec/src/main.nr | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 4a14bd16376..752b96d6591 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -1,12 +1,12 @@ struct BoundedVec { storage: [T; MaxLen], len: u64, - empty_value: T, } impl BoundedVec { - pub fn new(initial_value: T) -> Self { - BoundedVec { storage: [initial_value; MaxLen], len: 0, empty_value: initial_value } + pub fn new() -> Self { + let zeroed = crate::unsafe::zeroed(); + BoundedVec { storage: [zeroed; MaxLen], len: 0 } } pub fn get(mut self: Self, index: u64) -> T { @@ -68,7 +68,7 @@ impl BoundedVec { self.len -= 1; let elem = self.storage[self.len]; - self.storage[self.len] = self.empty_value; + self.storage[self.len] = crate::unsafe::zeroed(); elem } diff --git a/test_programs/noir_test_success/bounded_vec/src/main.nr b/test_programs/noir_test_success/bounded_vec/src/main.nr index d51d2cc3685..0e2c89c9064 100644 --- a/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -1,6 +1,6 @@ #[test] fn test_vec_push_pop() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); assert(vec.len == 0); vec.push(2); assert(vec.len == 1); @@ -17,7 +17,7 @@ fn test_vec_push_pop() { #[test] fn test_vec_extend_from_array() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); assert(vec.len == 2); assert(vec.get(0) == 2); @@ -26,13 +26,13 @@ fn test_vec_extend_from_array() { #[test(should_fail_with="extend_from_array out of bounds")] fn test_vec_extend_from_array_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4, 6]); } #[test(should_fail_with="extend_from_array out of bounds")] fn test_vec_extend_from_array_twice_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2]); assert(vec.len == 1); vec.extend_from_array([4, 6]); @@ -40,36 +40,36 @@ fn test_vec_extend_from_array_twice_out_of_bound() { #[test(should_fail)] fn test_vec_get_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); let _x = vec.get(2); } #[test(should_fail)] fn test_vec_get_not_declared() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2]); let _x = vec.get(1); } #[test(should_fail)] fn test_vec_get_uninitialized() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); let _x = vec.get(0); } #[test(should_fail_with="push out of bounds")] fn test_vec_push_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.push(1); vec.push(2); } #[test(should_fail_with="extend_from_bounded_vec out of bounds")] fn test_vec_extend_from_bounded_vec_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); - let mut another_vec: BoundedVec = BoundedVec::new(0); + let mut another_vec: BoundedVec = BoundedVec::new(); another_vec.extend_from_array([1, 2, 3]); vec.extend_from_bounded_vec(another_vec); @@ -77,10 +77,10 @@ fn test_vec_extend_from_bounded_vec_out_of_bound() { #[test(should_fail_with="extend_from_bounded_vec out of bounds")] fn test_vec_extend_from_bounded_vec_twice_out_of_bound() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([1, 2]); - let mut another_vec: BoundedVec = BoundedVec::new(0); + let mut another_vec: BoundedVec = BoundedVec::new(); another_vec.push(3); vec.extend_from_bounded_vec(another_vec); @@ -88,7 +88,7 @@ fn test_vec_extend_from_bounded_vec_twice_out_of_bound() { #[test] fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(0); + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4, 6]); assert(vec.any(|v| v == 2) == true); assert(vec.any(|v| v == 4) == true); @@ -98,8 +98,8 @@ fn test_vec_any() { #[test] fn test_vec_any_not_default() { - let default_value = 1; - let mut vec: BoundedVec = BoundedVec::new(default_value); + let default_value = 0; + let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); assert(vec.any(|v| v == default_value) == false); -} \ No newline at end of file +} From 8f935af0813c4f012005c8b3eb70441b44db5714 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:43:55 +0000 Subject: [PATCH 015/416] chore(docs): fix external contributor force push workflow (#4437) # Description ## Problem\* Resolves # ## Summary\* This workflow was failing due to the fact that we're storing the message in a file but not checking out the repository. While checking out would be safe in this case, I've just moved the comment into the workflow for paranoia reasons. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/EXTERNAL_CONTRIBUTOR_PR_COMMENT.md | 5 ----- .github/workflows/pull-request-title.yml | 6 +++++- 2 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 .github/EXTERNAL_CONTRIBUTOR_PR_COMMENT.md diff --git a/.github/EXTERNAL_CONTRIBUTOR_PR_COMMENT.md b/.github/EXTERNAL_CONTRIBUTOR_PR_COMMENT.md deleted file mode 100644 index 4031bcdb61c..00000000000 --- a/.github/EXTERNAL_CONTRIBUTOR_PR_COMMENT.md +++ /dev/null @@ -1,5 +0,0 @@ -Thank you for your contribution to the Noir language. - -Please **do not force push to this branch** after the Noir team have reviewed this PR. Doing so will only delay us merging your PR as we will need to start the review process from scratch. - -Thanks for your understanding. \ No newline at end of file diff --git a/.github/workflows/pull-request-title.yml b/.github/workflows/pull-request-title.yml index 8f863160cf1..7e9b729da28 100644 --- a/.github/workflows/pull-request-title.yml +++ b/.github/workflows/pull-request-title.yml @@ -39,6 +39,10 @@ jobs: - name: Post comment on force pushes uses: marocchino/sticky-pull-request-comment@v2 with: - path: ./.github/EXTERNAL_CONTRIBUTOR_PR_COMMENT.md + message: | + Thank you for your contribution to the Noir language. + Please **do not force push to this branch** after the Noir team have started review of this PR. Doing so will only delay us merging your PR as we will need to start the review process from scratch. + + Thanks for your understanding. \ No newline at end of file From 2498115cf197450f33af0b9c158fa2ee4ce3e222 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 27 Feb 2024 06:35:28 -0500 Subject: [PATCH 016/416] feat: Sync from aztec-packages (#4390) BEGIN_COMMIT_OVERRIDE chore: bootstrap improvements. (https://github.com/AztecProtocol/aztec-packages/pull/4711) chore: Add full recursive verification test (https://github.com/AztecProtocol/aztec-packages/pull/4658) chore: add struct for each bigint modulus (https://github.com/AztecProtocol/aztec-packages/pull/4422) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: sirasistant --- Cargo.lock | 1 + Dockerfile | 6 +- aztec_macros/Cargo.toml | 1 + aztec_macros/src/lib.rs | 269 ++++++++++++-- bootstrap.sh | 3 + bootstrap_cache.sh | 4 + .../src/hir/def_collector/dc_crate.rs | 14 + .../noirc_frontend/src/hir/def_map/mod.rs | 5 + compiler/noirc_frontend/src/hir/mod.rs | 4 + compiler/noirc_frontend/src/lib.rs | 11 + compiler/noirc_frontend/src/node_interner.rs | 2 +- noir_stdlib/src/bigint.nr | 327 ++++++++++++++++-- noir_stdlib/src/uint128.nr | 2 +- noirc_macros/src/lib.rs | 12 + scripts/test_native.sh | 4 +- .../1327_concrete_in_generic/src/main.nr | 4 +- .../execution_success/bigint/src/main.nr | 13 +- .../brillig_cow_regression/Prover.toml | 2 +- .../brillig_cow_regression/src/main.nr | 24 +- .../double_verify_nested_proof/Nargo.toml | 7 + .../double_verify_nested_proof/Prover.toml | 5 + .../double_verify_nested_proof/src/main.nr | 28 ++ .../double_verify_proof/src/main.nr | 3 +- .../regression_4124/src/main.nr | 8 +- test_programs/gates_report.sh | 2 +- tooling/debugger/ignored-tests.txt | 1 + tooling/nargo_fmt/tests/expected/contract.nr | 21 +- tooling/nargo_fmt/tests/input/contract.nr | 37 +- 28 files changed, 688 insertions(+), 132 deletions(-) create mode 100644 test_programs/execution_success/double_verify_nested_proof/Nargo.toml create mode 100644 test_programs/execution_success/double_verify_nested_proof/Prover.toml create mode 100644 test_programs/execution_success/double_verify_nested_proof/src/main.nr diff --git a/Cargo.lock b/Cargo.lock index 714b700119a..c0438eaf81f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,6 +417,7 @@ version = "0.24.0" dependencies = [ "convert_case 0.6.0", "iter-extended", + "noirc_errors", "noirc_frontend", ] diff --git a/Dockerfile b/Dockerfile index 000292e0a47..3a478c3f95a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ -FROM rust:bookworm +FROM rust:bullseye WORKDIR /usr/src/noir COPY . . RUN ./scripts/bootstrap_native.sh # When running the container, mount the users home directory to same location. -FROM ubuntu:lunar +FROM ubuntu:focal # Install Tini as nargo doesn't handle signals properly. # Install git as nargo needs it to clone. RUN apt-get update && apt-get install -y git tini && rm -rf /var/lib/apt/lists/* && apt-get clean COPY --from=0 /usr/src/noir/target/release/nargo /usr/src/noir/target/release/nargo -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/src/noir/target/release/nargo"] \ No newline at end of file +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/src/noir/target/release/nargo"] diff --git a/aztec_macros/Cargo.toml b/aztec_macros/Cargo.toml index 5e908b2e672..ed9821fabcf 100644 --- a/aztec_macros/Cargo.toml +++ b/aztec_macros/Cargo.toml @@ -11,5 +11,6 @@ repository.workspace = true [dependencies] noirc_frontend.workspace = true +noirc_errors.workspace = true iter-extended.workspace = true convert_case = "0.6.0" diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 0b93dbaa634..0ccc421d3bc 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -3,6 +3,10 @@ use std::vec; use convert_case::{Case, Casing}; use iter_extended::vecmap; +use noirc_errors::Location; +use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; +use noirc_frontend::hir::def_map::{LocalModuleId, ModuleId}; +use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::FieldElement; use noirc_frontend::macros_api::{ BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, @@ -16,9 +20,8 @@ use noirc_frontend::macros_api::{ use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; -use noirc_frontend::node_interner::{TraitId, TraitImplKind}; +use noirc_frontend::node_interner::{FuncId, TraitId, TraitImplId, TraitImplKind}; use noirc_frontend::Lambda; - pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -31,6 +34,25 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } + fn process_unresolved_traits_impls( + &self, + crate_id: &CrateId, + context: &mut HirContext, + unresolved_traits_impls: &[UnresolvedTraitImpl], + collected_functions: &mut Vec, + ) -> Result<(), (MacroError, FileId)> { + if has_aztec_dependency(crate_id, context) { + inject_compute_note_hash_and_nullifier( + crate_id, + context, + unresolved_traits_impls, + collected_functions, + ) + } else { + Ok(()) + } + } + fn process_typed_ast( &self, crate_id: &CrateId, @@ -46,7 +68,6 @@ const MAX_CONTRACT_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); #[derive(Debug, Clone)] pub enum AztecMacroError { AztecDepNotFound, - ComputeNoteHashAndNullifierNotFound { span: Span }, ContractHasTooManyFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, @@ -63,11 +84,6 @@ impl From for MacroError { secondary_message: None, span: None, }, - AztecMacroError::ComputeNoteHashAndNullifierNotFound { span } => MacroError { - primary_message: "compute_note_hash_and_nullifier function not found. Define it in your contract. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#compute_note_hash_and_nullifier-function-not-found-define-it-in-your-contract".to_owned(), - secondary_message: None, - span: Some(span), - }, AztecMacroError::ContractHasTooManyFunctions { span } => MacroError { primary_message: format!("Contract can only have a maximum of {} functions", MAX_CONTRACT_FUNCTIONS), secondary_message: None, @@ -313,15 +329,17 @@ fn check_for_aztec_dependency( crate_id: &CrateId, context: &HirContext, ) -> Result<(), (MacroError, FileId)> { - let crate_graph = &context.crate_graph[crate_id]; - let has_aztec_dependency = crate_graph.dependencies.iter().any(|dep| dep.as_name() == "aztec"); - if has_aztec_dependency { + if has_aztec_dependency(crate_id, context) { Ok(()) } else { - Err((AztecMacroError::AztecDepNotFound.into(), crate_graph.root_file_id)) + Err((AztecMacroError::AztecDepNotFound.into(), context.crate_graph[crate_id].root_file_id)) } } +fn has_aztec_dependency(crate_id: &CrateId, context: &HirContext) -> bool { + context.crate_graph[crate_id].dependencies.iter().any(|dep| dep.as_name() == "aztec") +} + // Check to see if the user has defined a storage struct fn check_for_storage_definition(module: &SortedModule) -> bool { module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") @@ -338,27 +356,30 @@ fn check_for_storage_implementation(module: &SortedModule) -> bool { } // Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined -fn check_for_compute_note_hash_and_nullifier_definition(module: &SortedModule) -> bool { - module.functions.iter().any(|func| { - func.def.name.0.contents == "compute_note_hash_and_nullifier" - && func.def.parameters.len() == 5 - && match &func.def.parameters[0].typ.typ { +fn check_for_compute_note_hash_and_nullifier_definition( + functions_data: &[(LocalModuleId, FuncId, NoirFunction)], + module_id: LocalModuleId, +) -> bool { + functions_data.iter().filter(|func_data| func_data.0 == module_id).any(|func_data| { + func_data.2.def.name.0.contents == "compute_note_hash_and_nullifier" + && func_data.2.def.parameters.len() == 5 + && match &func_data.2.def.parameters[0].typ.typ { UnresolvedTypeData::Named(path, _, _) => path.segments.last().unwrap().0.contents == "AztecAddress", _ => false, } - && func.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement - && func.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement - && func.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement // checks if the 5th parameter is an array and the Box in // Array(Option, Box) contains only fields - && match &func.def.parameters[4].typ.typ { + && match &func_data.2.def.parameters[4].typ.typ { UnresolvedTypeData::Array(_, inner_type) => { matches!(inner_type.typ, UnresolvedTypeData::FieldElement) }, _ => false, } // We check the return type the same way as we did the 5th parameter - && match &func.def.return_type { + && match &func_data.2.def.return_type { FunctionReturnType::Default(_) => false, FunctionReturnType::Ty(unresolved_type) => { match &unresolved_type.typ { @@ -401,13 +422,6 @@ fn transform_module( generate_storage_implementation(module).map_err(|err| (err, crate_graph.root_file_id))?; } - if storage_defined && !check_for_compute_note_hash_and_nullifier_definition(module) { - return Err(( - AztecMacroError::ComputeNoteHashAndNullifierNotFound { span: Span::default() }, - crate_graph.root_file_id, - )); - } - for structure in module.types.iter() { if structure.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { module.impls.push(generate_selector_impl(structure)); @@ -596,7 +610,7 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte /// If it does, it will insert the following things: /// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs /// - Hashes all of the function input variables -/// - This instantiates a helper function +/// - This instantiates a helper function fn transform_function( ty: &str, func: &mut NoirFunction, @@ -826,8 +840,8 @@ fn get_serialized_length( && !interner.lookup_all_trait_implementations(stored_in_state, trait_id).is_empty() }); - // Maps and (private) Notes always occupy a single slot. Someone could store a Note in PublicState for whatever reason though. - if struct_name == "Map" || (is_note && struct_name != "PublicState") { + // Maps and (private) Notes always occupy a single slot. Someone could store a Note in PublicMutable for whatever reason though. + if struct_name == "Map" || (is_note && struct_name != "PublicMutable") { return Ok(1); } @@ -1601,3 +1615,194 @@ fn event_signature(event: &StructType) -> String { let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); format!("{}({})", event.name.0.contents, fields.join(",")) } + +fn inject_compute_note_hash_and_nullifier( + crate_id: &CrateId, + context: &mut HirContext, + unresolved_traits_impls: &[UnresolvedTraitImpl], + collected_functions: &mut [UnresolvedFunctions], +) -> Result<(), (MacroError, FileId)> { + // We first fetch modules in this crate which correspond to contracts, along with their file id. + let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context + .def_map(crate_id) + .expect("ICE: Missing crate in def_map") + .modules() + .iter() + .filter(|(_, module)| module.is_contract) + .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) + .collect(); + + // If the current crate does not contain a contract module we simply skip it. + if contract_module_file_ids.is_empty() { + return Ok(()); + } else if contract_module_file_ids.len() != 1 { + panic!("Found multiple contracts in the same crate"); + } + + let (module_id, file_id) = contract_module_file_ids[0]; + + // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an + // escape hatch for this mechanism. + // TODO(#4647): improve this diagnosis and error messaging. + if collected_functions.iter().any(|coll_funcs_data| { + check_for_compute_note_hash_and_nullifier_definition(&coll_funcs_data.functions, module_id) + }) { + return Ok(()); + } + + // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the + // contract might use. These are the types that implement the NoteInterface trait, which provides the + // get_note_type_id function. + let note_types = fetch_struct_trait_impls(context, unresolved_traits_impls, "NoteInterface"); + + // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. + let func = generate_compute_note_hash_and_nullifier(¬e_types); + + // And inject the newly created function into the contract. + + // TODO(#4373): We don't have a reasonable location for the source code of this autogenerated function, so we simply + // pass an empty span. This function should not produce errors anyway so this should not matter. + let location = Location::new(Span::empty(0), file_id); + + // These are the same things the ModCollector does when collecting functions: we push the function to the + // NodeInterner, declare it in the module (which checks for duplicate definitions), and finally add it to the list + // on collected but unresolved functions. + + let func_id = context.def_interner.push_empty_fn(); + context.def_interner.push_function( + func_id, + &func.def, + ModuleId { krate: *crate_id, local_id: module_id }, + location, + ); + + context.def_map_mut(crate_id).unwrap() + .modules_mut()[module_id.0] + .declare_function( + func.name_ident().clone(), func_id + ).expect( + "Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647." + ); + + collected_functions + .iter_mut() + .find(|fns| fns.file_id == file_id) + .expect("ICE: no functions found in contract file") + .push_fn(module_id, func_id, func.clone()); + + Ok(()) +} + +// Fetches the name of all structs that implement trait_name, both in the current crate and all of its dependencies. +fn fetch_struct_trait_impls( + context: &mut HirContext, + unresolved_traits_impls: &[UnresolvedTraitImpl], + trait_name: &str, +) -> Vec { + let mut struct_typenames: Vec = Vec::new(); + + // These structs can be declared in either external crates or the current one. External crates that contain + // dependencies have already been processed and resolved, but are available here via the NodeInterner. Note that + // crates on which the current crate does not depend on may not have been processed, and will be ignored. + for trait_impl_id in 0..context.def_interner.next_trait_impl_id().0 { + let trait_impl = &context.def_interner.get_trait_implementation(TraitImplId(trait_impl_id)); + + if trait_impl.borrow().ident.0.contents == *trait_name { + if let Type::Struct(s, _) = &trait_impl.borrow().typ { + struct_typenames.push(s.borrow().name.0.contents.clone()); + } else { + panic!("Found impl for {} on non-Struct", trait_name); + } + } + } + + // This crate's traits and impls have not yet been resolved, so we look for impls in unresolved_trait_impls. + struct_typenames.extend( + unresolved_traits_impls + .iter() + .filter(|trait_impl| { + trait_impl + .trait_path + .segments + .last() + .expect("ICE: empty trait_impl path") + .0 + .contents + == *trait_name + }) + .filter_map(|trait_impl| match &trait_impl.object_type.typ { + UnresolvedTypeData::Named(path, _, _) => { + Some(path.segments.last().unwrap().0.contents.clone()) + } + _ => None, + }), + ); + + struct_typenames +} + +fn generate_compute_note_hash_and_nullifier(note_types: &Vec) -> NoirFunction { + let function_source = generate_compute_note_hash_and_nullifier_source(note_types); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors.clone()); + } + assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); + + let mut function_ast = function_ast.into_sorted(); + function_ast.functions.remove(0) +} + +fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> String { + // TODO(#4649): The serialized_note parameter is a fixed-size array, but we don't know what length it should have. + // For now we hardcode it to 20, which is the same as MAX_NOTE_FIELDS_LENGTH. + + if note_types.is_empty() { + // TODO(#4520): Even if the contract does not include any notes, other parts of the stack expect for this + // function to exist, so we include a dummy version. We likely should error out here instead. + " + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; 20] + ) -> pub [Field; 4] { + [0, 0, 0, 0] + }" + .to_string() + } else { + // For contracts that include notes we do a simple if-else chain comparing note_type_id with the different + // get_note_type_id of each of the note types. + + let if_statements: Vec = note_types.iter().map(|note_type| format!( + "if (note_type_id == {0}::get_note_type_id()) {{ + note_utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) + }}" + , note_type)).collect(); + + // TODO(#4520): error out on the else instead of returning a zero array + let full_if_statement = if_statements.join(" else ") + + " + else { + [0, 0, 0, 0] + }"; + + format!( + " + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; 20] + ) -> pub [Field; 4] {{ + let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + + {} + }}", + full_if_statement + ) + } +} diff --git a/bootstrap.sh b/bootstrap.sh index 5ebe7ade090..1f9506904a4 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -15,5 +15,8 @@ if [ -n "$CMD" ]; then fi fi +# Attempt to just pull artefacts from CI and exit on success. +./bootstrap_cache.sh && exit + ./scripts/bootstrap_native.sh ./scripts/bootstrap_packages.sh \ No newline at end of file diff --git a/bootstrap_cache.sh b/bootstrap_cache.sh index 672702416bd..d06aa493662 100755 --- a/bootstrap_cache.sh +++ b/bootstrap_cache.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash set -eu +[ -z "${NO_CACHE:-}" ] && type docker &> /dev/null && [ -f ~/.aws/credentials ] || exit 1 + cd "$(dirname "$0")" source ../build-system/scripts/setup_env '' '' mainframe_$USER > /dev/null @@ -9,3 +11,5 @@ extract_repo noir-packages /usr/src/noir/packages ./ echo -e "\033[1mRetrieving nargo from remote cache...\033[0m" extract_repo noir /usr/src/noir/target/release ./target/ +remove_old_images noir-packages +remove_old_images noir diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 0d1dd1b4337..7f36af5b30e 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -256,6 +256,20 @@ impl DefCollector { // Add the current crate to the collection of DefMaps context.def_maps.insert(crate_id, def_collector.def_map); + // TODO(#4653): generalize this function + for macro_processor in ¯o_processors { + macro_processor + .process_unresolved_traits_impls( + &crate_id, + context, + &def_collector.collected_traits_impls, + &mut def_collector.collected_functions, + ) + .unwrap_or_else(|(macro_err, file_id)| { + errors.push((macro_err.into(), file_id)); + }); + } + inject_prelude(crate_id, context, crate_root, &mut def_collector.collected_imports); for submodule in submodules { inject_prelude( diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 8e0dacc294b..8721bdb6c3c 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -135,6 +135,11 @@ impl CrateDefMap { pub fn modules(&self) -> &Arena { &self.modules } + + pub fn modules_mut(&mut self) -> &mut Arena { + &mut self.modules + } + pub fn krate(&self) -> CrateId { self.krate } diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 4d3800f1a50..00bcb0cdebf 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -91,6 +91,10 @@ impl Context<'_, '_> { self.def_maps.get(crate_id) } + pub fn def_map_mut(&mut self, crate_id: &CrateId) -> Option<&mut CrateDefMap> { + self.def_maps.get_mut(crate_id) + } + /// Return the CrateId for each crate that has been compiled /// successfully pub fn crates(&self) -> impl Iterator + '_ { diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index eb00a61adf6..be007929fc4 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -45,6 +45,7 @@ pub mod macros_api { pub use noirc_errors::Span; pub use crate::graph::CrateId; + use crate::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; pub use crate::hir::def_collector::errors::MacroError; pub use crate::hir_def::expr::{HirExpression, HirLiteral}; pub use crate::hir_def::stmt::HirStatement; @@ -74,6 +75,16 @@ pub mod macros_api { crate_id: &CrateId, context: &HirContext, ) -> Result; + + // TODO(#4653): generalize this function + fn process_unresolved_traits_impls( + &self, + _crate_id: &CrateId, + _context: &mut HirContext, + _unresolved_traits_impls: &[UnresolvedTraitImpl], + _collected_functions: &mut Vec, + ) -> Result<(), (MacroError, FileId)>; + /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. fn process_typed_ast( diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 7d533947f65..5de43e59254 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -367,7 +367,7 @@ impl TraitId { } #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] -pub struct TraitImplId(usize); +pub struct TraitImplId(pub usize); #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct TraitMethodId { diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index 66e81f05812..98237a54779 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -1,4 +1,5 @@ -use crate::ops::{Add, Sub, Mul, Div, Rem}; +use crate::ops::{Add, Sub, Mul, Div}; +use crate::cmp::Eq; global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; @@ -30,46 +31,320 @@ impl BigInt { #[builtin(bigint_from_le_bytes)] fn from_le_bytes(bytes: [u8], modulus: [u8]) -> BigInt {} #[builtin(bigint_to_le_bytes)] - pub fn to_le_bytes(self) -> [u8] {} + fn to_le_bytes(self) -> [u8] {} - pub fn bn254_fr_from_le_bytes(bytes: [u8]) -> BigInt { - BigInt::from_le_bytes(bytes, bn254_fr) + fn check_32_bytes(self: Self, other: BigInt) -> bool { + let bytes = self.to_le_bytes(); + let o_bytes = other.to_le_bytes(); + let mut result = true; + for i in 0..32 { + result = result & (bytes[i] == o_bytes[i]); + } + result } - pub fn bn254_fq_from_le_bytes(bytes: [u8]) -> BigInt { - BigInt::from_le_bytes(bytes, bn254_fq) +} + +trait BigField { + fn from_le_bytes(bytes: [u8]) -> Self; + fn to_le_bytes(self) -> [u8]; +} + +struct Secpk1Fq { + inner: BigInt, +} + +impl BigField for Secpk1Fq { + fn from_le_bytes(bytes: [u8]) -> Secpk1Fq { + Secpk1Fq { + inner: BigInt::from_le_bytes(bytes, secpk1_fq) + } + } + fn to_le_bytes(self) -> [u8] { + self.inner.to_le_bytes() + } +} + +impl Add for Secpk1Fq { + fn add(self: Self, other: Secpk1Fq) -> Secpk1Fq { + Secpk1Fq { + inner: self.inner.bigint_add(other.inner) + } + } +} +impl Sub for Secpk1Fq { + fn sub(self: Self, other: Secpk1Fq) -> Secpk1Fq { + Secpk1Fq { + inner: self.inner.bigint_sub(other.inner) + } + } +} +impl Mul for Secpk1Fq { + fn mul(self: Self, other: Secpk1Fq) -> Secpk1Fq { + Secpk1Fq { + inner: self.inner.bigint_mul(other.inner) + } + + } +} +impl Div for Secpk1Fq { + fn div(self: Self, other: Secpk1Fq) -> Secpk1Fq { + Secpk1Fq { + inner: self.inner.bigint_div(other.inner) + } + } +} +impl Eq for Secpk1Fq { + fn eq(self: Self, other: Secpk1Fq) -> bool { + self.inner.check_32_bytes(other.inner) + } +} + +struct Secpk1Fr { + inner: BigInt, +} + +impl BigField for Secpk1Fr { + fn from_le_bytes(bytes: [u8]) -> Secpk1Fr { + Secpk1Fr { + inner: BigInt::from_le_bytes(bytes, secpk1_fr) + } } - pub fn secpk1_fq_from_le_bytes(bytes: [u8]) -> BigInt { - BigInt::from_le_bytes(bytes, secpk1_fq) + fn to_le_bytes(self) -> [u8] { + self.inner.to_le_bytes() } - pub fn secpk1_fr_from_le_bytes(bytes: [u8]) -> BigInt { - BigInt::from_le_bytes(bytes, secpk1_fr) +} + +impl Add for Secpk1Fr { + fn add(self: Self, other: Secpk1Fr) -> Secpk1Fr { + Secpk1Fr { + inner: self.inner.bigint_add(other.inner) + } + } +} +impl Sub for Secpk1Fr { + fn sub(self: Self, other: Secpk1Fr) -> Secpk1Fr { + Secpk1Fr { + inner: self.inner.bigint_sub(other.inner) + } + } +} +impl Mul for Secpk1Fr { + fn mul(self: Self, other: Secpk1Fr) -> Secpk1Fr { + Secpk1Fr { + inner: self.inner.bigint_mul(other.inner) + } + + } +} +impl Div for Secpk1Fr { + fn div(self: Self, other: Secpk1Fr) -> Secpk1Fr { + Secpk1Fr { + inner: self.inner.bigint_div(other.inner) + } + } +} +impl Eq for Secpk1Fr { + fn eq(self: Self, other: Secpk1Fr) -> bool { + self.inner.check_32_bytes(other.inner) + } +} + +struct Bn254Fr { + inner: BigInt, +} + +impl BigField for Bn254Fr { + fn from_le_bytes(bytes: [u8]) -> Bn254Fr { + Bn254Fr { + inner: BigInt::from_le_bytes(bytes, bn254_fr) + } + } + fn to_le_bytes(self) -> [u8] { + self.inner.to_le_bytes() } } -impl Add for BigInt { - fn add(self: Self, other: BigInt) -> BigInt { - self.bigint_add(other) +impl Add for Bn254Fr { + fn add(self: Self, other: Bn254Fr) -> Bn254Fr { + Bn254Fr { + inner: self.inner.bigint_add(other.inner) + } + } +} +impl Sub for Bn254Fr { + fn sub(self: Self, other: Bn254Fr) -> Bn254Fr { + Bn254Fr { + inner: self.inner.bigint_sub(other.inner) + } } } -impl Sub for BigInt { - fn sub(self: Self, other: BigInt) -> BigInt { - self.bigint_sub(other) +impl Mul for Bn254Fr { + fn mul(self: Self, other: Bn254Fr) -> Bn254Fr { + Bn254Fr { + inner: self.inner.bigint_mul(other.inner) + } + } } -impl Mul for BigInt { - fn mul(self: Self, other: BigInt) -> BigInt { - self.bigint_mul(other) +impl Div for Bn254Fr { + fn div(self: Self, other: Bn254Fr) -> Bn254Fr { + Bn254Fr { + inner: self.inner.bigint_div(other.inner) + } } } -impl Div for BigInt { - fn div(self: Self, other: BigInt) -> BigInt { - self.bigint_div(other) +impl Eq for Bn254Fr { + fn eq(self: Self, other: Bn254Fr) -> bool { + self.inner.check_32_bytes(other.inner) } } -impl Rem for BigInt { - fn rem(self: Self, other: BigInt) -> BigInt { - let quotient = self.bigint_div(other); - self.bigint_sub(quotient.bigint_mul(other)) + +struct Bn254Fq { + inner: BigInt, +} + +impl BigField for Bn254Fq { + fn from_le_bytes(bytes: [u8]) -> Bn254Fq { + Bn254Fq { + inner: BigInt::from_le_bytes(bytes, bn254_fq) + } + } + fn to_le_bytes(self) -> [u8] { + self.inner.to_le_bytes() } } +impl Add for Bn254Fq { + fn add(self: Self, other: Bn254Fq) -> Bn254Fq { + Bn254Fq { + inner: self.inner.bigint_add(other.inner) + } + } +} +impl Sub for Bn254Fq { + fn sub(self: Self, other: Bn254Fq) -> Bn254Fq { + Bn254Fq { + inner: self.inner.bigint_sub(other.inner) + } + } +} +impl Mul for Bn254Fq { + fn mul(self: Self, other: Bn254Fq) -> Bn254Fq { + Bn254Fq { + inner: self.inner.bigint_mul(other.inner) + } + + } +} +impl Div for Bn254Fq { + fn div(self: Self, other: Bn254Fq) -> Bn254Fq { + Bn254Fq { + inner: self.inner.bigint_div(other.inner) + } + } +} +impl Eq for Bn254Fq { + fn eq(self: Self, other: Bn254Fq) -> bool { + self.inner.check_32_bytes(other.inner) + } +} + +struct Secpr1Fq { + inner: BigInt, +} + +impl BigField for Secpr1Fq { + fn from_le_bytes(bytes: [u8]) -> Secpr1Fq { + Secpr1Fq { + inner: BigInt::from_le_bytes(bytes, secpr1_fq) + } + } + fn to_le_bytes(self) -> [u8] { + self.inner.to_le_bytes() + } +} + +impl Add for Secpr1Fq { + fn add(self: Self, other: Secpr1Fq) -> Secpr1Fq { + Secpr1Fq { + inner: self.inner.bigint_add(other.inner) + } + } +} +impl Sub for Secpr1Fq { + fn sub(self: Self, other: Secpr1Fq) -> Secpr1Fq { + Secpr1Fq { + inner: self.inner.bigint_sub(other.inner) + } + } +} +impl Mul for Secpr1Fq { + fn mul(self: Self, other: Secpr1Fq) -> Secpr1Fq { + Secpr1Fq { + inner: self.inner.bigint_mul(other.inner) + } + + } +} +impl Div for Secpr1Fq { + fn div(self: Self, other: Secpr1Fq) -> Secpr1Fq { + Secpr1Fq { + inner: self.inner.bigint_div(other.inner) + } + } +} +impl Eq for Secpr1Fq { + fn eq(self: Self, other: Secpr1Fq) -> bool { + self.inner.check_32_bytes(other.inner) + } +} + +struct Secpr1Fr { + inner: BigInt, +} + +impl BigField for Secpr1Fr { + fn from_le_bytes(bytes: [u8]) -> Secpr1Fr { + Secpr1Fr { + inner: BigInt::from_le_bytes(bytes, secpr1_fr) + } + } + fn to_le_bytes(self) -> [u8] { + self.inner.to_le_bytes() + } +} + +impl Add for Secpr1Fr { + fn add(self: Self, other: Secpr1Fr) -> Secpr1Fr { + Secpr1Fr { + inner: self.inner.bigint_add(other.inner) + } + } +} +impl Sub for Secpr1Fr { + fn sub(self: Self, other: Secpr1Fr) -> Secpr1Fr { + Secpr1Fr { + inner: self.inner.bigint_sub(other.inner) + } + } +} +impl Mul for Secpr1Fr { + fn mul(self: Self, other: Secpr1Fr) -> Secpr1Fr { + Secpr1Fr { + inner: self.inner.bigint_mul(other.inner) + } + + } +} +impl Div for Secpr1Fr { + fn div(self: Self, other: Secpr1Fr) -> Secpr1Fr { + Secpr1Fr { + inner: self.inner.bigint_div(other.inner) + } + } +} +impl Eq for Secpr1Fr { + fn eq(self: Self, other: Secpr1Fr) -> bool { + self.inner.check_32_bytes(other.inner) + } +} diff --git a/noir_stdlib/src/uint128.nr b/noir_stdlib/src/uint128.nr index d6f0b1e2232..b91ed5c4cb2 100644 --- a/noir_stdlib/src/uint128.nr +++ b/noir_stdlib/src/uint128.nr @@ -161,7 +161,7 @@ impl Sub for U128 { let borrow = (low == lo) as Field; let high = self.hi - b.hi - borrow; let hi = high as u64 as Field; - assert(hi == high, "attempt to subtract with overflow"); + assert(hi == high, "attempt to subtract with underflow"); U128 { lo, hi, diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs index 4337214d69f..9a916843200 100644 --- a/noirc_macros/src/lib.rs +++ b/noirc_macros/src/lib.rs @@ -1,3 +1,5 @@ +use noirc_frontend::hir::def_collector::dc_crate::UnresolvedFunctions; +use noirc_frontend::hir::def_collector::dc_crate::UnresolvedTraitImpl; use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::HirContext; use noirc_frontend::macros_api::SortedModule; @@ -16,6 +18,16 @@ impl MacroProcessor for AssertMessageMacro { transform(ast, crate_id) } + fn process_unresolved_traits_impls( + &self, + _crate_id: &CrateId, + _context: &mut HirContext, + _unresolved_traits_impls: &[UnresolvedTraitImpl], + _collected_functions: &mut Vec, + ) -> Result<(), (MacroError, FileId)> { + Ok(()) + } + // This macro does not need to process any information after name resolution fn process_typed_ast( &self, diff --git a/scripts/test_native.sh b/scripts/test_native.sh index bc1c47ecf12..9b9aa0ce4d7 100755 --- a/scripts/test_native.sh +++ b/scripts/test_native.sh @@ -12,4 +12,6 @@ else export GIT_COMMIT=$(git rev-parse --verify HEAD) fi -cargo test --workspace --locked --release \ No newline at end of file +cargo fmt --all --check +cargo clippy --workspace --locked --release +cargo test --workspace --locked --release diff --git a/test_programs/execution_success/1327_concrete_in_generic/src/main.nr b/test_programs/execution_success/1327_concrete_in_generic/src/main.nr index 8250b31789b..3e476107c29 100644 --- a/test_programs/execution_success/1327_concrete_in_generic/src/main.nr +++ b/test_programs/execution_success/1327_concrete_in_generic/src/main.nr @@ -20,7 +20,7 @@ impl B { } } // --- -// Set +// PrivateSet struct C { t_d_interface: MethodInterface, } @@ -55,7 +55,7 @@ fn get_d_method_interface() -> MethodInterface { // --- fn main(input: Field) -> pub Field { let b: B> = B::new(new_concrete_c_over_d); - let c: C = b.get_t_c(); // Singleton + let c: C = b.get_t_c(); // PrivateMutable let d: D = D { d: input }; // Note let output = c.call_method_of_t_d(d); diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr index 046d7d07d5e..b93fec370e5 100644 --- a/test_programs/execution_success/bigint/src/main.nr +++ b/test_programs/execution_success/bigint/src/main.nr @@ -1,9 +1,8 @@ use dep::std::bigint; fn main(mut x: [u8; 5], y: [u8; 5]) { - let a = bigint::BigInt::secpk1_fq_from_le_bytes([x[0], x[1], x[2], x[3], x[4]]); - let b = bigint::BigInt::secpk1_fq_from_le_bytes([y[0], y[1], y[2], y[3], y[4]]); - + let a = bigint::Secpk1Fq::from_le_bytes([x[0], x[1], x[2], x[3], x[4]]); + let b = bigint::Secpk1Fq::from_le_bytes([y[0], y[1], y[2], y[3], y[4]]); let a_bytes = a.to_le_bytes(); let b_bytes = b.to_le_bytes(); for i in 0..5 { @@ -12,10 +11,6 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { } let d = a * b - b; - let d_bytes = d.to_le_bytes(); - let d1 = bigint::BigInt::secpk1_fq_from_le_bytes(597243850900842442924.to_le_bytes(10)); - let d1_bytes = d1.to_le_bytes(); - for i in 0..32 { - assert(d_bytes[i] == d1_bytes[i]); - } + let d1 = bigint::Secpk1Fq::from_le_bytes(597243850900842442924.to_le_bytes(10)); + assert(d1 == d); } diff --git a/test_programs/execution_success/brillig_cow_regression/Prover.toml b/test_programs/execution_success/brillig_cow_regression/Prover.toml index f0a4dc2485d..44813823448 100644 --- a/test_programs/execution_success/brillig_cow_regression/Prover.toml +++ b/test_programs/execution_success/brillig_cow_regression/Prover.toml @@ -3,7 +3,7 @@ encrypted_logs_hash = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", ] -new_commitments = [ +new_note_hashes = [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", diff --git a/test_programs/execution_success/brillig_cow_regression/src/main.nr b/test_programs/execution_success/brillig_cow_regression/src/main.nr index 7f3dd766480..ba51548d9dd 100644 --- a/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -1,12 +1,12 @@ // Tests a performance regression found in aztec-packages with brillig cow optimization -global MAX_NEW_COMMITMENTS_PER_TX = 64; -global MAX_NEW_NULLIFIERS_PER_TX = 64; -global MAX_NEW_L2_TO_L1_MSGS_PER_TX = 2; -global MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX = 16; -global MAX_NEW_CONTRACTS_PER_TX = 1; -global NUM_ENCRYPTED_LOGS_HASHES_PER_TX = 1; -global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX = 1; +global MAX_NEW_NOTE_HASHES_PER_TX: u64 = 64; +global MAX_NEW_NULLIFIERS_PER_TX: u64 = 64; +global MAX_NEW_L2_TO_L1_MSGS_PER_TX: u64 = 2; +global MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX: u64 = 16; +global MAX_NEW_CONTRACTS_PER_TX: u64 = 1; +global NUM_ENCRYPTED_LOGS_HASHES_PER_TX: u64 = 1; +global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: u64 = 1; global NUM_FIELDS_PER_SHA256 = 2; global CALLDATA_HASH_INPUT_SIZE = 169; global CALL_DATA_HASH_LOG_FIELDS = 4; @@ -30,7 +30,7 @@ impl NewContractData { } struct DataToHash { - new_commitments: [Field; MAX_NEW_COMMITMENTS_PER_TX], + new_note_hashes: [Field; MAX_NEW_NOTE_HASHES_PER_TX], new_nullifiers: [Field; MAX_NEW_NULLIFIERS_PER_TX], public_data_update_requests: [PublicDataUpdateRequest; MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX], new_l2_to_l1_msgs: [Field; MAX_NEW_L2_TO_L1_MSGS_PER_TX], @@ -101,7 +101,7 @@ impl U256 { unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA256] { let mut calldata_hash_inputs = [0; CALLDATA_HASH_INPUT_SIZE]; - let new_commitments = kernel_data.new_commitments; + let new_note_hashes = kernel_data.new_note_hashes; let new_nullifiers = kernel_data.new_nullifiers; let public_data_update_requests = kernel_data.public_data_update_requests; let newL2ToL1msgs = kernel_data.new_l2_to_l1_msgs; @@ -110,10 +110,10 @@ unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA let mut offset = 0; - for j in 0..MAX_NEW_COMMITMENTS_PER_TX { - calldata_hash_inputs[offset + j] = new_commitments[j]; + for j in 0..MAX_NEW_NOTE_HASHES_PER_TX { + calldata_hash_inputs[offset + j] = new_note_hashes[j]; } - offset += MAX_NEW_COMMITMENTS_PER_TX ; + offset += MAX_NEW_NOTE_HASHES_PER_TX ; for j in 0..MAX_NEW_NULLIFIERS_PER_TX { calldata_hash_inputs[offset + j] = new_nullifiers[j]; diff --git a/test_programs/execution_success/double_verify_nested_proof/Nargo.toml b/test_programs/execution_success/double_verify_nested_proof/Nargo.toml new file mode 100644 index 00000000000..3ead649c879 --- /dev/null +++ b/test_programs/execution_success/double_verify_nested_proof/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "double_verify_nested_proof" +type = "bin" +authors = [""] +compiler_version = ">=0.24.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/double_verify_nested_proof/Prover.toml b/test_programs/execution_success/double_verify_nested_proof/Prover.toml new file mode 100644 index 00000000000..2a2b4b33586 --- /dev/null +++ b/test_programs/execution_success/double_verify_nested_proof/Prover.toml @@ -0,0 +1,5 @@ +key_hash = "0x13fd5b632ce9e9d12c9ac56c150ed09413df3edf40d1b7ab8ced9f262ec61b29" +proof_b = ["0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x0000000000000000000000000000004bdfb9b586a637ceebd99ff26dcd3af427","0x0000000000000000000000000000000000265c2a5caf8e033e32d192807f5353","0x000000000000000000000000000000c0ab1db6ea40ac087cdc82c4a61ab00c86","0x0000000000000000000000000000000000010800ea8010f4bd3dd432d1cc11ed","0x000000000000000000000000000000eb3db3c41e3e636d686fd2903b1b913a01","0x000000000000000000000000000000000009bbab6b90377114c9e33d2a302226","0x000000000000000000000000000000758726e60ef4b211cf1c965fe08293365b","0x0000000000000000000000000000000000290ec193bc7f4f9006b9cea136bff5","0x0000000000000000000000000000005a2a389cd1702b3aa37f30ed974147d343","0x00000000000000000000000000000000001d83087d6efe0db3f482730b8d5e32","0x000000000000000000000000000000ad015051ed84c11d061e63eddbc3c0417a","0x0000000000000000000000000000000000155011c8b0167ff694740c48d67683","0x00000000000000000000000000000010c638a3b13dba3e77be3f10a3d096927c","0x00000000000000000000000000000000002372b9853214a1f76e5636dc26f146","0x00000000000000000000000000000005d9ca201c07bd4216689677feb9227715","0x000000000000000000000000000000000001dcf09921797dffb8eb21abef187b","0x00000000000000000000000000000070af16c9644b777dcf84d69e820e1ed895","0x00000000000000000000000000000000002d5e8f7eb7a4e20964dd94dc141534","0x0000000000000000000000000000003636871dbe453b366c3351be6e84144683","0x0000000000000000000000000000000000206464e290e4f4764365038ac77edf","0x000000000000000000000000000000175c20da35cc833dd542af57de9b62a2da","0x00000000000000000000000000000000001d2e31de3715e05ff6278f88e5a0db","0x000000000000000000000000000000328610e4eabb48be78d3c75f7c159205c5","0x000000000000000000000000000000000026720634b8076fee0a17b358b04653","0x0000000000000000000000000000000e5f48906892ffbff91e8b58ceabba0949","0x000000000000000000000000000000000013c349df687926ccb712622fc72a36","0x000000000000000000000000000000a4b8c9046c7e7e4cc19bbf9a367668eac7","0x00000000000000000000000000000000002a81128e53672c33bb0dae0ff18f41","0x000000000000000000000000000000edb79df57c4a2303ed1e5c2d7ed1e1bdaf","0x000000000000000000000000000000000018d3cea4ce204eafd70c0ded024650","0x000000000000000000000000000000e5f82856854fe0a2d587f6a9ae8555f321","0x0000000000000000000000000000000000235480ec2adc05f04261054345e568","0x00000000000000000000000000000083607465f60b70b092f606853f4d9e96eb","0x000000000000000000000000000000000006569e3a3174bcb71efe46f7fb7e0f","0x000000000000000000000000000000cb4d5fc546f20f63e3b7cf60341956f36f","0x00000000000000000000000000000000000e14b1932630bf606a637eabb7c80f","0x000000000000000000000000000000786f31c2e082aa7e398e6323bb48a27472","0x00000000000000000000000000000000002dd72746f5e5a4a438def122ae6bba","0x000000000000000000000000000000d007be60a28b744e49279fab277c8bd623","0x00000000000000000000000000000000000e52e2b940b9cd8d001209cc40f7c8","0x000000000000000000000000000000dd4357e24a1bda0b5a6c5eee657cfe9091","0x0000000000000000000000000000000000047bb24b20feb0b66089a96671c901","0x0000000000000000000000000000003fe7f42f34e3360ef0fa8bd9c17e6190a3","0x0000000000000000000000000000000000161d17a3848118e91b435b553d34e9","0x216fa2905e105e0c767687f9b5e81c2e4ce03abe2993ac8dcd9e8d89e088966f","0x1288ba942d41c7f4b048e125454253bc7d7ffc0875365c0b8f75a2bb3ea90b42","0x1ad706f84cffcc62fa030f1bc57cb478a687aa74c1019beeda9bab4e40d35373","0x03050c8016b8041a557a46840ab4166a9c2531eb7c3985a447996a334e0caf5f","0x2b3c485da75bdaef8cec120bd08bc21e3ff717740114d13d3811006215a1fb24","0x008fc8c76c4d8cbba8653bf0919c047d379941be60c7afc7250bc6bfc5f29ad5","0x1993ae2a0da54e5e643533fdefbf54a0df21115b2ee79a63a7f477c2c9c4a5d5","0x22520fa7fde2d72b9776c07c9b897ef7ce48f8a7937ec0cacb01d3e23f72b78a","0x259b7b9c1dbfe88d613102f0e8548f0c770a1c83876b26a5cb4b6790740cb487","0x043006102e519b8011d089f51811337fbdedc856a73842f7c8197be176b08d38","0x2222bd509df909ce38b67b3172b24c8ce1e0e1dd0d811f4fae6957e3418415ac","0x1b1204474652fa85979f0274145680718bed80466f4c91ad58f37df1b4fe2395","0x08d57251b42c0697535617ae239d7f3ef9d1558c1bb71fa01c68e7b5fd266139","0x04ca7f21f1d0ba50ecf00c615d18bf8f7291bb04f513cbef78fb6d03ed9b0cb2","0x070ae1119c80846863a4cd971e535ff87fe34473eb5730b14e5b30212b7b78a1","0x1128027ded5032cc265c96ff81d76e2ce06420702fd4e5bc4e24fda695961651","0x1ef7a9e5885b934eee2b44335157309de2f60519e50a8471e5e24495dff2a9fe","0x2d0dad89e5633da796c0c897804575879bc5dc7ad3805b44260943101ac9609e","0x287edcbd60e9d636ba1cd1c9ff3ec2b71b694112c65876525f5e2f8209cd747f","0x24b1157a1cb5bdbd2829de066b8c5573584f9b8638bf9bad476a1fe1172da4b9","0x1f9825731638cd1c43f7cf035b808a1704f122453318cb88fe3b1164f034e170","0x07003a6552f3a6ab1ad3e0717be0af049767b554ff88986c4e48224632523405","0x288002c2ff29874077b2c216a35cb61ecc97d12750a3a86574d50acd42607095","0x0a12fc37918ce7dcbd0d354a05bdbb409a8e4530d86f3d8ce07231240590f65c","0x2ec631b05fc693b07286eecf6b6ac1aef0d073cdced8e050244ec7cf4e8f6e42","0x107bc98da225efe7749d51b9966c3edd6c245f2e5cf183a924ba982817e4525a","0x2ca603f77ea0ca42b6f38cd43bc3cc09442906373a2f197fdc976533066ac343","0x138ace5653809375aa9d95240fa9b6508860a471aed70bcc8b7dd52ae34809f3","0x21e1eb924951881c3d0ce5657d2e26a3e5150ce8f49c9e4d0476c5fdf1e43a54","0x2e2daec93f5e94f6784ce569883cf285da12244b38fb001b94bfb99bb4de060c","0x186a8d30c973bef6286115865a690a2528adbeea8376e5221fffeb6a135d9904","0x1e0d9d90628be31ebc16ef1d85d5f9e6fb8cb57e6a74e576f958cf21db45042e","0x124ceb5e1d9da6d0fe163e961643bb0423c926ef4e0c583eaba9e32d99ec6c7c","0x2db34cc38a50bfea50750830710c13b4d80f4ec0e8df8f186047ee36f338eeeb","0x0b174aa403b42235d5bdde8e9f5bb6c52ae62fec2884334cbe3e53418bd2463d","0x1571ebd9c3854c2f63418f206c6937495450ab9238a238b9c63fbf5867378c5b","0x24f92d1ab27e5810e5b7f4b31254526822f866602922258135c5eb5a2b21ca04","0x20cc7f5ba8df67d9c95642e2662654eb2305c6a280ce1747aec88a581ee50647","0x24112b99f63bbda7487709396dff22aae89ae809263021b65503ff7f809c7e38","0x06805c80f64efd1fa7f08382c9981aad9cecad78808da670477566674141bc48","0x146d4801d6f5898051ee0d7c95375a65ea0e6deeac6ffee1d9b9cf64da72dc3e","0x000000000000000000000000000000425b99a5c96b22ba0286d9ebeecf8e4559","0x0000000000000000000000000000000000110be4b8fe46a96303c205d3a1d61d","0x000000000000000000000000000000d9ff7ae757f2f0c91d1f1e71fac1b27b74","0x000000000000000000000000000000000009b0c285f6c221f6eba93b1e330ac4","0x0000000000000000000000000000004055cd5738a25ab1860a1e35555962dc19","0x00000000000000000000000000000000001a8726ccf54e17cf1b005e3e04879a","0x0000000000000000000000000000007be4dc343e9c2e0d4a9156f1ef9769f65a","0x00000000000000000000000000000000002b0e96f68f6509615ca0544dfa3107"] +public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] +verification_key = ["0x2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd","0x0000000000000000000000000000000000000000000000000000000000080000","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000080000","0x0000000000000000000000000000000000000000000000000000000000000011","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000003","0x0000000000000000000000000000000000000000000000000000000000000004","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000006","0x0000000000000000000000000000000000000000000000000000000000000007","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000009","0x000000000000000000000000000000000000000000000000000000000000000a","0x000000000000000000000000000000000000000000000000000000000000000b","0x000000000000000000000000000000000000000000000000000000000000000c","0x000000000000000000000000000000000000000000000000000000000000000d","0x000000000000000000000000000000000000000000000000000000000000000e","0x000000000000000000000000000000000000000000000000000000000000000f","0x0000000000000000000000000000000000000000000000000000000000000010","0x000000000000000000000000000000ba765ed919550454064328e0fd7c51ff4a","0x00000000000000000000000000000000000418b2f4104c289eb20cb95344c850","0x0000000000000000000000000000006defa500aab13c8cf3c00117db573bef2c","0x000000000000000000000000000000000026dea3ea8fb7e77b5bfa8443397dc7","0x0000000000000000000000000000009a5c33c4054817f3402e68aeca4728a405","0x00000000000000000000000000000000002abf5ea67ec384cb2e5998c7a48b3a","0x000000000000000000000000000000ee78817f7d959ea45abb27404e3371c708","0x0000000000000000000000000000000000260a979e8190a83b0bce1351b92d3c","0x000000000000000000000000000000ec447bd83a83883ce4c11573ce24845c74","0x000000000000000000000000000000000005b23c2076f50d10baa061a67b9200","0x00000000000000000000000000000058ffc16cfb64ec06a56a2b1a9047fb8f0c","0x000000000000000000000000000000000011d2f5833d720e1d0a02749471e7ad","0x000000000000000000000000000000416dd6c8c0d1cbb185b3c3197eac767d0b","0x000000000000000000000000000000000023b9c5a4e525926d64247ec92e0baf","0x000000000000000000000000000000a55f5f52ebc8936a58e413a1068d94d376","0x00000000000000000000000000000000000be3f377ccc88a6cb5df6f230da95e","0x00000000000000000000000000000070a162a08d4d4800f450af94888f8f3480","0x0000000000000000000000000000000000085883b02590372a7b36a1c57db4c3","0x00000000000000000000000000000045b0b661ea73930ee3327ccff8a0ca9ce1","0x00000000000000000000000000000000002854cab8629792eb07e9ef81bc46ee","0x00000000000000000000000000000067f365021e0e42117c43a39419d1d9cc73","0x000000000000000000000000000000000022c370b38f0a97eb3d718146f2284b","0x00000000000000000000000000000016de6670aba605233072b8eecfa9069b06","0x000000000000000000000000000000000002c29c49d66457bcbd4fa5bf6096fd","0x000000000000000000000000000000e32e8ce4f18ba30ce53245044d0c60508a","0x00000000000000000000000000000000001170220489121b8eedd58a4b5599df","0x000000000000000000000000000000139ed828b410380d053ec0f056656f5703","0x0000000000000000000000000000000000072aebdce25ba333c86769adec1362","0x000000000000000000000000000000aa352ee565f91fc2b73323fc824bc14636","0x00000000000000000000000000000000001f3e272a192808ec9283ee3bb4df4b","0x00000000000000000000000000000005c72c8c88be0259ae226ccb0488452b4b","0x00000000000000000000000000000000001c68407d694502b929b77cbbab8374","0x0000000000000000000000000000003716bda8267f29931ed0aa811e4607f1c6","0x000000000000000000000000000000000007d888936af2141bb2f6823a587e81","0x0000000000000000000000000000004cf1a4f39c5363f70ecc9e433d751ea529","0x00000000000000000000000000000000002e8a81232ec84e48032178f1ee6edb","0x000000000000000000000000000000388e8265061fa0c92c96fc85d99bac7891","0x00000000000000000000000000000000002e3c516222565332e6e7362400bc5f","0x0000000000000000000000000000003a68d13661a0906e5828fe8271a336bf64","0x00000000000000000000000000000000001412d3e67497c98e5ec2aaee8779f5","0x000000000000000000000000000000b5d123498733b5279d8bcbade0d8345ef7","0x00000000000000000000000000000000000fa572890537089a5fb36953e7a1ca","0x0000000000000000000000000000004d8ff057fc9936a693035266c80c6ea57d","0x00000000000000000000000000000000001907a614968d777fcc506f639799f6","0x00000000000000000000000000000010769533212d3cafbf6ac378c8055c33a2","0x00000000000000000000000000000000000eac32851272327acdc0890792dfb7","0x000000000000000000000000000000e3e32f343643d319a977beb0c2b0ab9b31","0x00000000000000000000000000000000000c10c4c9dce6ff648ef70f54d45ba6","0x00000000000000000000000000000025721304165b9b313b94cf2c77b61dc1ef","0x000000000000000000000000000000000024b8083b0f323c2703a7255caa7078","0x0000000000000000000000000000002b860372c65049c88f6532cbd360917b11","0x000000000000000000000000000000000011ee2ac2bc36cdfdc107eca47369f3","0x0000000000000000000000000000001c1b0233882acb5a78a977642e4dce91d5","0x000000000000000000000000000000000020922a70853993b3516eeb01d7c8a4","0x0000000000000000000000000000001f90b5fade69a55a2da8d2db3c62b62d7c","0x0000000000000000000000000000000000173312bb89c6722b548ff87a7487a2","0x0000000000000000000000000000009d618ffd933cf58a8a0953dc76f97cf108","0x00000000000000000000000000000000000ddc3b6d8e59cf0996ca71ad4132ca","0x000000000000000000000000000000ec4c6a253f431d3f3fc06aa0e5b0448b8c","0x0000000000000000000000000000000000153193287060386695f4f2d0d3525d","0x0000000000000000000000000000004bd25585edb9319128045c005d48491b1e","0x00000000000000000000000000000000001170f0ece62f8c572bca96b141d27f","0x0000000000000000000000000000003dd2e37b8edb1f56b785809d7710bf1c88","0x0000000000000000000000000000000000246cd041690f653f88ed0c56ad282a","0x00000000000000000000000000000034bc8a00ce9d452888e5fc2b5a7e14fed7","0x000000000000000000000000000000000026153c937447356a0c6d6be09d85eb","0x000000000000000000000000000000555388ad9364679246b07992f84b4e91b2","0x0000000000000000000000000000000000189da022421fbd8dfd7973084d978e","0x000000000000000000000000000000e8c0f9753e2a5a35acec051fafe2cecce5","0x0000000000000000000000000000000000285311c5e9a4cbb56a3f04f29d5443","0x00000000000000000000000000000092d2d0ac76a1be7f1fad96cbd997175312","0x00000000000000000000000000000000002436400260c9d3180beedd0bf49fec","0x000000000000000000000000000000887d86d95387bbb29616cc5c41ee4a2669","0x0000000000000000000000000000000000063bf32f8addf7a3e1cf6cd223cb71","0x000000000000000000000000000000d841dc7d9da6cc699e8377b2a04723fea0","0x00000000000000000000000000000000002ce091428268c212a2bcfea0edb338","0x00000000000000000000000000000012fe4771092fa47e4d6050701527133f09","0x00000000000000000000000000000000002f36672865c5ae4976486fdaf2d81d","0x0000000000000000000000000000008e6bced56a3d94dfe9d476da3a424b8eff","0x00000000000000000000000000000000002d6303cf28aa721f4e5348a0d83642","0x0000000000000000000000000000008c5807dace05b2079d200f7f71caffdaf7","0x000000000000000000000000000000000008f7beb50cb16f3b6210aff1bdb05d","0x0000000000000000000000000000004f9ee08a49536eb54a238b982c4dfd5446","0x000000000000000000000000000000000014f55e7065eabacf1a7d6cbf1f6765","0x00000000000000000000000000000021150153ec654b02a66d9bea056185877e","0x00000000000000000000000000000000000e7bf50a142b21057bcfd340a5e77c","0x00000000000000000000000000000038110629263a662f10464b375f988cccda","0x00000000000000000000000000000000001964a0ab814f71282cd159df492710","0x000000000000000000000000000000b9310dd49ea52ba735b9654ebced7bc67b","0x000000000000000000000000000000000019ad72f92554ce44921ca3f420f995","0x000000000000000000000000000000d67d7e81fa6e1cdfae6d84510a8cb7e257","0x00000000000000000000000000000000000a6ec9d85c10a85e8f31eaedb4e459"] +proof = ["0x0000000000000000000000000000000000000000000000042ab5d6d1986846cf","0x00000000000000000000000000000000000000000000000b75c020998797da78","0x0000000000000000000000000000000000000000000000005a107acb64952eca","0x000000000000000000000000000000000000000000000000000031e97a575e9d","0x00000000000000000000000000000000000000000000000b5666547acf8bd5a4","0x00000000000000000000000000000000000000000000000c410db10a01750aeb","0x00000000000000000000000000000000000000000000000d722669117f9758a4","0x000000000000000000000000000000000000000000000000000178cbf4206471","0x000000000000000000000000000000000000000000000000e91b8a11e7842c38","0x000000000000000000000000000000000000000000000007fd51009034b3357f","0x000000000000000000000000000000000000000000000009889939f81e9c7402","0x0000000000000000000000000000000000000000000000000000f94656a2ca48","0x000000000000000000000000000000000000000000000006fb128b46c1ddb67f","0x0000000000000000000000000000000000000000000000093fe27776f50224bd","0x000000000000000000000000000000000000000000000004a0c80c0da527a081","0x0000000000000000000000000000000000000000000000000001b52c2020d746","0x00000000000000000000000000000063cb03b1d83ae3942e11ca8ec63055898b","0x00000000000000000000000000000000001edaf70d547a857fbed6a9ff8a38c9","0x000000000000000000000000000000097fb881332193ff4489e213f600e6a007","0x00000000000000000000000000000000001f2903742639c3595d22b96d4d9c21","0x000000000000000000000000000000bca7215bb1bcdde52ed9cf845b7e54072d","0x0000000000000000000000000000000000188bd12b19073eb01e8be5bda41b3e","0x0000000000000000000000000000007d1a114656606c391bfb286ea4e14062a5","0x000000000000000000000000000000000026d8a3b8821da41b6b1d6b85872260","0x000000000000000000000000000000c49078b857741b82cba39d8a394c1876c1","0x00000000000000000000000000000000002f9b9f76f80a4ff456e60c024f8d03","0x0000000000000000000000000000004bab3e60680935219213ea32be70ec5100","0x00000000000000000000000000000000002c45bda56f0115cfde2678889694ab","0x0000000000000000000000000000006434e56313172088d5a6b10fdd1b94b4ca","0x000000000000000000000000000000000007ad41e7980534fc2f89e8ad7366ad","0x00000000000000000000000000000023d769c68ef65f0b4f06a01e655fb265e7","0x0000000000000000000000000000000000008d3b5d5b201ed6773c369fe20d10","0x0000000000000000000000000000005eacdd2121ba4b1cf0df09632df6991fcf","0x0000000000000000000000000000000000005e98e857c8c1eb16cef913e44f90","0x0000000000000000000000000000003449da35dc7c0b67b0c3e99ced603ea381","0x000000000000000000000000000000000022347c8daec6739b183413a787fd13","0x000000000000000000000000000000df23d8f1ac4ddfced428737db15e63f603","0x000000000000000000000000000000000015e03670ba72d84269d764d8f8e725","0x000000000000000000000000000000457a7f854dbab545c8c94ccdb8e4b9ad45","0x00000000000000000000000000000000000a268fc41b7031912cec59dc0a7078","0x00000000000000000000000000000022fcb55824b67af33225f8f2e614fbbdb4","0x0000000000000000000000000000000000235f698e6aee7bf8ca94f4a44db006","0x000000000000000000000000000000a327da390bd3e01e4a7b639605fdfd9c42","0x0000000000000000000000000000000000210196c4fb53d660a3824867b2b1c5","0x000000000000000000000000000000728fb44750fa2b956221bd441fa61e32d6","0x0000000000000000000000000000000000073db9e2cafdf0fe22b5090855533e","0x0000000000000000000000000000004fe310e93730876891eebab46db9496dbc","0x000000000000000000000000000000000007d3574fe79c87011abdbd51a46670","0x000000000000000000000000000000adc522f42e085c51403fc50c83f35904b9","0x00000000000000000000000000000000000d2d9ef8fc0031b4568842a99b34eb","0x00000000000000000000000000000098586d928c8abc7cc56d571c8eded52168","0x000000000000000000000000000000000024279c001a40e94d3d149ec01a468a","0x00000000000000000000000000000066122aaf47d9d5060a2ce1d17cc5201be0","0x00000000000000000000000000000000001c21031d83d52e27867a611229d2ca","0x000000000000000000000000000000838dfc066499f7715682f755b42f3a4869","0x00000000000000000000000000000000001f816d2c5b2e903496f1443cb91de3","0x0000000000000000000000000000007ef917b6df805f430f8a0833942a7c3094","0x00000000000000000000000000000000000a9cefe716f31dbe37485179d60f0e","0x00000000000000000000000000000028adb1040bd0c07448de51d5cac9fd0495","0x00000000000000000000000000000000000c66b25a22c8b3ba82ec09ab4bdef3","0x2cc791d253f03f47cc88f7f0aeae481762f4aa6426712772544aaeca72466cb7","0x14197950f448f679eeff75c4e83dac9f0ebd5aa194709ea3875fb4e4b15bc2f2","0x1a92022c2ed8f8a41e3f392e22f1875f6916543bbb22c3aaf50d703de649c381","0x2ee77a26e78d5e1093dabd3612beee4b515a4f159992138e13ecd3f0afcfba18","0x2c280cba627b147142a2d333ee856000298708f9b5df0cc8d23c26d0936d6869","0x1b2569bb6f6b60b6f743ff892a39a490770d4ad40a961a06149d4968b0487a40","0x2f80351e43621d69b7e620338b2822e15dec9e6a2de16e8d04bb559153cd53a3","0x15a78b8ae9b3be431b609250b69c7cb746c6a689b2122150f258c6f7d67409fc","0x1334c47f273be542576813933e89a9130a342846272b39a2eab3ab7fc022d5fe","0x1031bdcafc5c0dad81c8b6c4931c9b442cd0c8a0bb9a729cc2f6bf0a18dc1b82","0x177f92f0cef76c5c45f55d16fa2be426354cdd4af6ac9aaad479c9b47f88656d","0x0064c0e0ec8984d612189e5287d59eedc1a6de52fc78bf72028f744350c27a0e","0x2c06222cf0d415c976e6904f1706b77cf438636ada3222e1c31c4957d6877dac","0x173da534b7001f44f19bb3e3f8601ac94fbf90b2e39b7d4079d8fac2d65102ea","0x012909bcdbd1167010cf0084028e851f3448f58946f4951b1b8d544d86b138c8","0x2975c3987f110c06bd8ced1d8bb0d398ac72c6f196ea639bdde58fa4b899d4a0","0x05c196fb2f6ccfd92a38ae526af85bccc3695ea0e2561e7a211c60360187602d","0x18a288590dd0cbfe5b7652458c9caddc9eac2f08e5822b64141ed1b4e805bda3","0x0cd08c41605b22a7ae31c3961486f645a32bff0ccaef63b6d661ef356db78560","0x05d5e48184693259f722f84ea48f9b84667d1e9db19e1381b2279fe24b01484b","0x2187a6f6a2398e5f0137880a983ff6b682b5a7c2b62e4bdfff6ff6becd0d53ab","0x1d4764ca9346e8ac48675320521e0daba651f480efe932302e8a9673580fc0d8","0x00cfcb920adeb0293acf26e63aeac4489622e4c806b93f1c72f8491cba3d0196","0x1bcd6a556800b8385ba1250afd69999fe2bb5518a6ba2cc461a4afba21ffbedb","0x11a15b3c8ef0e4ac0ff151fba72b922f6c005519151a4f88557352265944aeea","0x063d550a154f2ce80b08fb169d137fa96dcea6a6c489e98e1390aa9a5db18928","0x25da993132041b9f667de044194f5c6b0cdae961cdea5f6dbbda8595f213ac08","0x22fcecc2e3794814bbb84700031cd75ec9817201c8c88df2e86407a14412f902","0x01583d25d2f91d646da02a520d3dbf758b0a0590a533bd1417a717fd0cd18915","0x18ebacffdc81e15547232dfc1a0e31ec2848a1e5b9c8509a92432c2549d93091","0x20a3d15aa70d04a841802fe1d990f56c6b9e6eadc17da2c0dfd2a817e6cf0430","0x0b497cc2e54412ce07c52effdce6c01de2c1a0e1d095a2a37f5351232400c0a1","0x14419bb69d02675b8d58e60ce88a2f4b6a43674461e4015e2e302285a42c5784","0x0c84db03ff77d0729bb68eab2d6d697b7caebd4ea3db781499492a6f0ef67765","0x1a676b1c6b0ab1c85b31af681e05751296c3d0a1a883668f5fe971827ce86fc9","0x08da949bf7603bfe20f3c152abe727051c6306cff322197e8fa56b390f565b5b","0x1fd77e041239f94e907dc3ae3069a70cbff726b9d8b3a368a4910c8a070a9c9a","0x03755d83a4f0fdfbb4fd1b2b465842e1bb707a419c2952a2ca9faba50d4be379","0x0ee90c8166adcb238d85c72a85db2248353610c55390a2ed54e59dd1c35c12d2","0x170bcd78efaa1b19bcfd065c2ec60b48aa1e62465df73e62f3bd291115315144","0x015d60e5cc5c7d67853993261bd9e3c6e56f95dee8724ce79c7601ee10c1a731","0x000000000000000000000000000000f0a8b99d65fc1555bafb688233a6489aea","0x0000000000000000000000000000000000043849f038ec96c8c1c6e242351361","0x0000000000000000000000000000001ad41d3dfebb280623d5b325f0a7aa38f7","0x00000000000000000000000000000000002e5f2119536daa9e6d1f9b82b797dd","0x000000000000000000000000000000e5570c2b6e74d0994e2fc8be1a9dab4160","0x00000000000000000000000000000000002ed426a78ed52d4c13f2c651a6d4ec","0x000000000000000000000000000000aba14637487e4d3ca30dc397416696c85c","0x000000000000000000000000000000000005ae1eb3eee0cdf5e5c7bb0ac9be07"] diff --git a/test_programs/execution_success/double_verify_nested_proof/src/main.nr b/test_programs/execution_success/double_verify_nested_proof/src/main.nr new file mode 100644 index 00000000000..0466f2a226d --- /dev/null +++ b/test_programs/execution_success/double_verify_nested_proof/src/main.nr @@ -0,0 +1,28 @@ +use dep::std; + +fn main( + verification_key: [Field; 114], + // This is the proof without public inputs attached. + // + // This means: the size of this does not change with the number of public inputs. + proof: [Field; 109], + public_inputs: pub [Field; 1], + // This is currently not public. It is fine given that the vk is a part of the circuit definition. + // I believe we want to eventually make it public too though. + key_hash: Field, + proof_b: [Field; 109] +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} diff --git a/test_programs/execution_success/double_verify_proof/src/main.nr b/test_programs/execution_success/double_verify_proof/src/main.nr index ce087dc4e61..e4c6926efbc 100644 --- a/test_programs/execution_success/double_verify_proof/src/main.nr +++ b/test_programs/execution_success/double_verify_proof/src/main.nr @@ -1,12 +1,13 @@ use dep::std; +#[recursive] fn main( verification_key: [Field; 114], // This is the proof without public inputs attached. // // This means: the size of this does not change with the number of public inputs. proof: [Field; 93], - public_inputs: [Field; 1], + public_inputs: pub [Field; 1], // This is currently not public. It is fine given that the vk is a part of the circuit definition. // I believe we want to eventually make it public too though. key_hash: Field, diff --git a/test_programs/execution_success/regression_4124/src/main.nr b/test_programs/execution_success/regression_4124/src/main.nr index b47bf28d461..49ff68ee6ad 100644 --- a/test_programs/execution_success/regression_4124/src/main.nr +++ b/test_programs/execution_success/regression_4124/src/main.nr @@ -14,14 +14,14 @@ pub fn storage_read() -> [Field; N] { dep::std::unsafe::zeroed() } -struct PublicState { +struct PublicMutable { storage_slot: Field, } -impl PublicState { +impl PublicMutable { pub fn new(storage_slot: Field) -> Self { assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); - PublicState { storage_slot } + PublicMutable { storage_slot } } pub fn read(_self: Self) -> T where T: MyDeserialize { @@ -32,7 +32,7 @@ impl PublicState { } fn main(value: Field) { - let ps: PublicState = PublicState::new(27); + let ps: PublicMutable = PublicMutable::new(27); // error here assert(ps.read() == value); diff --git a/test_programs/gates_report.sh b/test_programs/gates_report.sh index 4192c581376..3b0b4d9e148 100755 --- a/test_programs/gates_report.sh +++ b/test_programs/gates_report.sh @@ -2,7 +2,7 @@ set -e # These tests are incompatible with gas reporting -excluded_dirs=("workspace" "workspace_default_member") +excluded_dirs=("workspace" "workspace_default_member" "double_verify_nested_proof") # These tests cause failures in CI with a stack overflow for some reason. ci_excluded_dirs=("eddsa") diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index 7ac440c335b..c472e828739 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -7,6 +7,7 @@ brillig_nested_arrays brillig_references brillig_to_bytes_integration debug_logs +double_verify_nested_proof double_verify_proof modulus nested_array_dynamic diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr index b80efeeb692..a03b8774700 100644 --- a/tooling/nargo_fmt/tests/expected/contract.nr +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -10,14 +10,14 @@ contract Benchmarking { use dep::aztec::{ context::{Context}, note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, - log::emit_unencrypted_log, state_vars::{map::Map, public_state::PublicState, set::Set}, + log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, types::address::{AztecAddress} }; struct Storage { - notes: Map>, - balances: Map>, + notes: Map>, + balances: Map>, } impl Storage { @@ -26,12 +26,12 @@ contract Benchmarking { notes: Map::new( context, 1, - |context, slot| { Set::new(context, slot, ValueNoteMethods) } + |context, slot| { PrivateSet::new(context, slot, ValueNoteMethods) } ), balances: Map::new( context, 2, - |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) } + |context, slot| { PublicMutable::new(context, slot, FieldSerializationMethods) } ) } } @@ -74,17 +74,6 @@ contract Benchmarking { fn broadcast(owner: Field) { emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - preimage: [Field; VALUE_NOTE_LEN] - ) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } } // Uses the token bridge contract, which tells which input token we need to talk to and handles the exit funds to L1 diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr index d10bfb745b6..a03b8774700 100644 --- a/tooling/nargo_fmt/tests/input/contract.nr +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -5,30 +5,34 @@ contract Benchmarking { use dep::aztec::protocol_types::abis::function_selector::FunctionSelector; - use dep::value_note::{ - utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, - }; + use dep::value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; use dep::aztec::{ context::{Context}, note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, - log::emit_unencrypted_log, - state_vars::{map::Map, public_state::PublicState, set::Set}, + log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - types::address::{AztecAddress}, + types::address::{AztecAddress} }; struct Storage { - notes: Map>, - balances: Map>, + notes: Map>, + balances: Map>, } impl Storage { fn init(context: Context) -> pub Self { Storage { - notes: Map::new(context, 1, |context, slot| { Set::new(context, slot, ValueNoteMethods) }), - balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) }), + notes: Map::new( + context, + 1, + |context, slot| { PrivateSet::new(context, slot, ValueNoteMethods) } + ), + balances: Map::new( + context, + 2, + |context, slot| { PublicMutable::new(context, slot, FieldSerializationMethods) } + ) } } } @@ -70,17 +74,6 @@ contract Benchmarking { fn broadcast(owner: Field) { emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); } - - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - preimage: [Field; VALUE_NOTE_LEN] - ) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } } // Uses the token bridge contract, which tells which input token we need to talk to and handles the exit funds to L1 From e80c5f73a4cdcba3f5cf44576c605ba1e611a2ab Mon Sep 17 00:00:00 2001 From: NaijaCoderGirl <150683513+NaijaCoderGirl@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:59:22 +0000 Subject: [PATCH 017/416] chore(docs): correct 'Edit this page' URL for dev docs (#4433) # Description ## Problem* The "Edit this page" button in the generated documentation for the `noir-lang` project leads to a 404 - Page Not Found error for documents in the development version. This issue is due to the button's link containing `/processed-docs` in the path instead of `/docs`. The problem affects the development version of the documentation, whereas versioned documentation (e.g., v0.23.0) correctly links to editable markdown files on GitHub. | Before | After | | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | ![Before](https://github.com/noir-lang/noir/assets/150683513/260f8eb5-4ef9-46db-ba39-ae836d2bb7de) | ![After](https://github.com/noir-lang/noir/assets/150683513/087e2c8c-b9dc-4c35-b824-2b95742872f6) | ## Summary* This pull request fixes the incorrect URL generated for the "Edit this page" button in the docusaurus configuration. By modifying the `editUrl` function within `docusaurus.config.ts`, the path now correctly replaces `processed-docs` with `docs` for the development version of the documentation. This change guarantees that contributors are directed to the correct GitHub page to edit the documentation, thus eliminating the 404 error previously encountered. ## Additional Context The issue was identified when attempting to edit pages from the development version of the docs. The button's link incorrectly pointed to a non-existent path due to the inclusion of `processed-docs` in the URL. ## Documentation* - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- docs/docusaurus.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 1b6c65d5139..49566c5c380 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -38,7 +38,7 @@ export default { }, }, editUrl: ({ versionDocsDirPath, docPath }) => - `https://github.com/noir-lang/noir/edit/master/docs/${versionDocsDirPath}/${docPath}`, + `https://github.com/noir-lang/noir/edit/master/docs/${versionDocsDirPath.replace('processed-docs', 'docs')}/${docPath}`, }, blog: false, theme: { From a25d5da32c1eb868fa400c61245edbd3db72ba3b Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 27 Feb 2024 08:33:39 -0500 Subject: [PATCH 018/416] feat: Sync from aztec-packages (#4438) BEGIN_COMMIT_OVERRIDE chore: bootstrap improvements. (https://github.com/AztecProtocol/aztec-packages/pull/4711) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French --- bootstrap.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 1f9506904a4..54129c3d61a 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -16,7 +16,7 @@ if [ -n "$CMD" ]; then fi # Attempt to just pull artefacts from CI and exit on success. -./bootstrap_cache.sh && exit +[ -n "${USE_CACHE:-}" ] && ./bootstrap_cache.sh && exit ./scripts/bootstrap_native.sh -./scripts/bootstrap_packages.sh \ No newline at end of file +./scripts/bootstrap_packages.sh From a112b303650e71db7406011edde92942c571654e Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 27 Feb 2024 09:55:34 -0500 Subject: [PATCH 019/416] feat: Sync from aztec-packages (#4439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BEGIN_COMMIT_OVERRIDE chore(aztec): Change function limit to private function limit (https://github.com/AztecProtocol/aztec-packages/pull/4785) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French Co-authored-by: Álvaro Rodríguez --- .aztec-sync-commit | 1 + aztec_macros/src/lib.rs | 24 ++++++++++++++----- bootstrap_cache.sh | 2 -- noir_stdlib/src/collections/map.nr | 2 +- noir_stdlib/src/sha256.nr | 2 +- noir_stdlib/src/sha512.nr | 2 +- .../execution_success/regression/src/main.nr | 6 ++--- 7 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 .aztec-sync-commit diff --git a/.aztec-sync-commit b/.aztec-sync-commit new file mode 100644 index 00000000000..9acc65d8b9c --- /dev/null +++ b/.aztec-sync-commit @@ -0,0 +1 @@ +e69b58660ff843350e1e098d8f1a84f4ce3d3c34 diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 0ccc421d3bc..156ba1d5b08 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -63,12 +63,12 @@ impl MacroProcessor for AztecMacro { } const FUNCTION_TREE_HEIGHT: u32 = 5; -const MAX_CONTRACT_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); +const MAX_CONTRACT_PRIVATE_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); #[derive(Debug, Clone)] pub enum AztecMacroError { AztecDepNotFound, - ContractHasTooManyFunctions { span: Span }, + ContractHasTooManyPrivateFunctions { span: Span }, ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, @@ -84,8 +84,8 @@ impl From for MacroError { secondary_message: None, span: None, }, - AztecMacroError::ContractHasTooManyFunctions { span } => MacroError { - primary_message: format!("Contract can only have a maximum of {} functions", MAX_CONTRACT_FUNCTIONS), + AztecMacroError::ContractHasTooManyPrivateFunctions { span } => MacroError { + primary_message: format!("Contract can only have a maximum of {} private functions", MAX_CONTRACT_PRIVATE_FUNCTIONS), secondary_message: None, span: Some(span), }, @@ -456,10 +456,22 @@ fn transform_module( if has_transformed_module { // We only want to run these checks if the macro processor has found the module to be an Aztec contract. - if module.functions.len() > MAX_CONTRACT_FUNCTIONS { + let private_functions_count = module + .functions + .iter() + .filter(|func| { + func.def + .attributes + .secondary + .iter() + .any(|attr| is_custom_attribute(attr, "aztec(private)")) + }) + .count(); + + if private_functions_count > MAX_CONTRACT_PRIVATE_FUNCTIONS { let crate_graph = &context.crate_graph[crate_id]; return Err(( - AztecMacroError::ContractHasTooManyFunctions { span: Span::default() }, + AztecMacroError::ContractHasTooManyPrivateFunctions { span: Span::default() }, crate_graph.root_file_id, )); } diff --git a/bootstrap_cache.sh b/bootstrap_cache.sh index d06aa493662..1cec6c81d8e 100755 --- a/bootstrap_cache.sh +++ b/bootstrap_cache.sh @@ -1,8 +1,6 @@ #!/usr/bin/env bash set -eu -[ -z "${NO_CACHE:-}" ] && type docker &> /dev/null && [ -f ~/.aws/credentials ] || exit 1 - cd "$(dirname "$0")" source ../build-system/scripts/setup_env '' '' mainframe_$USER > /dev/null diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr index d9eb83ff5dc..056299b4238 100644 --- a/noir_stdlib/src/collections/map.nr +++ b/noir_stdlib/src/collections/map.nr @@ -400,7 +400,7 @@ impl HashMap { // n * MAX_LOAD_FACTOR_DEN0MINATOR >= m * MAX_LOAD_FACTOR_NUMERATOR fn assert_load_factor(self) { let lhs = self._len * MAX_LOAD_FACTOR_DEN0MINATOR; - let rhs = self._table.len() as u64 * MAX_LOAD_FACTOR_NUMERATOR; + let rhs = self._table.len() * MAX_LOAD_FACTOR_NUMERATOR; let exceeded = lhs >= rhs; assert(!exceeded, "Load factor is exceeded, consider increasing the capacity."); } diff --git a/noir_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index 6bcc5ea74c6..2f686a64165 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -57,7 +57,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { msg_block[i as Field] = 0; i = i + 1; } else if i < 64 { - let mut len = 8 * msg.len() as u64; + let mut len = 8 * msg.len(); for j in 0..8 { msg_block[63 - j] = len as u8; len >>= 8; diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index 155ba593bba..4dfe78308e2 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -136,7 +136,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { msg_block[i as Field] = 0; i += 1; } else if i < 128 { - let mut len = 8 * msg.len() as u64; // u128 unsupported + let mut len = 8 * msg.len(); for j in 0..16 { msg_block[127 - j] = len as u8; len >>= 8; diff --git a/test_programs/execution_success/regression/src/main.nr b/test_programs/execution_success/regression/src/main.nr index c70e2e75fa8..c56f3ef4190 100644 --- a/test_programs/execution_success/regression/src/main.nr +++ b/test_programs/execution_success/regression/src/main.nr @@ -1,4 +1,4 @@ -global NIBBLE_LENGTH: Field = 16; +global NIBBLE_LENGTH: u64 = 16; struct U4 { inner: u8, @@ -21,8 +21,8 @@ impl Eq for U4 { } fn compact_decode(input: [u8; N], length: Field) -> ([U4; NIBBLE_LENGTH], Field) { - assert(2 * input.len() as u64 <= NIBBLE_LENGTH as u64); - assert(length as u64 <= input.len() as u64); + assert(2 * input.len() <= NIBBLE_LENGTH); + assert(length as u64 <= input.len()); let mut nibble = [U4::zero(); NIBBLE_LENGTH]; From 97bcae2725fc59c7d4ce5212990a40fdcd993e53 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:29:30 +0100 Subject: [PATCH 020/416] chore: address code review comments of PR4398 (#4435) # Description ## Problem\* Related to #4170, this PR address comments in PR #4398 ## Summary\* Add a comment and a sanity check. ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/acvm/src/pwg/blackbox/hash.rs | 12 +++++++++++- noir_stdlib/src/hash/poseidon2.nr | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/acvm-repo/acvm/src/pwg/blackbox/hash.rs b/acvm-repo/acvm/src/pwg/blackbox/hash.rs index 1bc26f06188..24c835a636a 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/hash.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/hash.rs @@ -143,12 +143,22 @@ pub(crate) fn solve_poseidon2_permutation_opcode( return Err(OpcodeResolutionError::BlackBoxFunctionFailed( acir::BlackBoxFunc::Poseidon2Permutation, format!( - "the number of inputs does not match specified length. {} > {}", + "the number of inputs does not match specified length. {} != {}", inputs.len(), len ), )); } + if len as usize != outputs.len() { + return Err(OpcodeResolutionError::BlackBoxFunctionFailed( + acir::BlackBoxFunc::Poseidon2Permutation, + format!( + "the number of outputs does not match specified length. {} != {}", + outputs.len(), + len + ), + )); + } // Read witness assignments let mut state = Vec::new(); diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 64c1876b4e2..40eea029e82 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -93,7 +93,8 @@ impl Poseidon2 { } fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field { - let iv : Field = (in_len as Field) * 18446744073709551616; + let two_pow_64 = 18446744073709551616; + let iv : Field = (in_len as Field) * two_pow_64; let mut sponge = Poseidon2::new(iv); for i in 0..input.len() { if i as u32 < in_len { From 9544813fabbd18a87dd88456e6a5b781bd0cf008 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:12:59 +0000 Subject: [PATCH 021/416] chore!: reserve `unchecked` keyword (#4432) # Description ## Problem\* Resolves ## Summary\* There's a decent chance that we're going to add some form of unchecked maths in future so I'd like to reserve this keyword in preparation for that. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/lexer/token.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index fe12132e202..5674ae5a39a 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -673,6 +673,7 @@ pub enum Keyword { Struct, Trait, Type, + Unchecked, Unconstrained, Use, Where, @@ -715,6 +716,7 @@ impl fmt::Display for Keyword { Keyword::Struct => write!(f, "struct"), Keyword::Trait => write!(f, "trait"), Keyword::Type => write!(f, "type"), + Keyword::Unchecked => write!(f, "unchecked"), Keyword::Unconstrained => write!(f, "unconstrained"), Keyword::Use => write!(f, "use"), Keyword::Where => write!(f, "where"), @@ -760,6 +762,7 @@ impl Keyword { "struct" => Keyword::Struct, "trait" => Keyword::Trait, "type" => Keyword::Type, + "unchecked" => Keyword::Unchecked, "unconstrained" => Keyword::Unconstrained, "use" => Keyword::Use, "where" => Keyword::Where, From 9c2053879389af16167143eab2203c91375a249d Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Wed, 28 Feb 2024 07:19:24 -0500 Subject: [PATCH 022/416] feat: Sync from aztec-packages (#4444) BEGIN_COMMIT_OVERRIDE chore: remove original return from aztec fns (https://github.com/AztecProtocol/aztec-packages/pull/4804) END_COMMIT_OVERRIDE --------- Co-authored-by: TomAFrench --- .aztec-sync-commit | 2 +- aztec_macros/src/lib.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 9acc65d8b9c..c97738f7226 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -e69b58660ff843350e1e098d8f1a84f4ce3d3c34 +9e246c1289fa40c35c4b28d2f0081dfdc2aa9d19 diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 156ba1d5b08..09deb2c9712 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -648,6 +648,10 @@ fn transform_function( // Abstract return types such that they get added to the kernel's return_values if let Some(return_values) = abstract_return_values(func) { + // In case we are pushing return values to the context, we remove the statement that originated it + // This avoids running duplicate code, since blocks like if/else can be value returning statements + func.def.body.0.pop(); + // Add the new return statement func.def.body.0.push(return_values); } @@ -1255,13 +1259,11 @@ fn create_avm_context() -> Result { /// Any primitive type that can be cast will be casted to a field and pushed to the context. fn abstract_return_values(func: &NoirFunction) -> Option { let current_return_type = func.return_type().typ; - let len = func.def.body.len(); - let last_statement = &func.def.body.0[len - 1]; + let last_statement = func.def.body.0.last()?; // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size // Doesn't need done until we have settled on a kernel size // TODO: support tuples here and in inputs -> convert into an issue - // Check if the return type is an expression, if it is, we can handle it match last_statement { Statement { kind: StatementKind::Expression(expression), .. } => { From cc298cd6f8de77e3aa4aeaba516e249c4cbd388f Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:10:52 +0000 Subject: [PATCH 023/416] chore: split up parser into separate files for code organisation (#4420) # Description ## Problem\* Resolves ## Summary\* I find the parser quite difficult to navigate due to the fact that it's all in a single file and quite disorganised. For example, `lambda` is 1000 lines below `lambda_parameters` despite it being the only place where `lambda_parameters` is called. This also makes it much harder to see where we have test coverage within the parser due to all the tests being in a single location whereas ideally we'd have these attached to the parser functions they're testing. This PR then splits the parser into submodules based on sections of the grammar which it is responsible for parsing. It's not perfect but is an improvement and we can modify this further if necessary in future. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: kevaundray --- compiler/noirc_frontend/src/parser/parser.rs | 1145 +---------------- .../src/parser/parser/assertion.rs | 219 ++++ .../src/parser/parser/attributes.rs | 46 + .../src/parser/parser/function.rs | 224 ++++ .../src/parser/parser/lambdas.rs | 42 + .../src/parser/parser/literals.rs | 157 +++ .../noirc_frontend/src/parser/parser/path.rs | 78 ++ .../src/parser/parser/primitives.rs | 101 ++ .../src/parser/parser/structs.rs | 97 ++ .../src/parser/parser/test_helpers.rs | 122 ++ .../src/parser/parser/traits.rs | 217 ++++ .../noirc_frontend/src/parser/parser/types.rs | 172 +++ 12 files changed, 1524 insertions(+), 1096 deletions(-) create mode 100644 compiler/noirc_frontend/src/parser/parser/assertion.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/attributes.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/function.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/lambdas.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/literals.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/path.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/primitives.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/structs.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/test_helpers.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/traits.rs create mode 100644 compiler/noirc_frontend/src/parser/parser/types.rs diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 1cb81e26a0a..75f4a6359bf 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -23,6 +23,8 @@ //! prevent other parsers from being tried afterward since there is no longer an error. Thus, they should //! be limited to cases like the above `fn` example where it is clear we shouldn't back out of the //! current parser to try alternative parsers in a `choice` expression. +use self::primitives::{keyword, mutable_reference, variable}; + use super::{ foldl_with_span, labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, parenthesized, then_commit, then_commit_ignore, top_level_statement_recovery, ExprParser, @@ -35,13 +37,11 @@ use crate::ast::{ }; use crate::lexer::Lexer; use crate::parser::{force, ignore_then_commit, statement_recovery}; -use crate::token::{Attribute, Attributes, Keyword, SecondaryAttribute, Token, TokenKind}; +use crate::token::{Keyword, Token, TokenKind}; use crate::{ - BinaryOp, BinaryOpKind, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, - ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, - IfExpression, InfixExpression, LValue, Lambda, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTraitImpl, NoirTypeAlias, Param, Path, PathKind, Pattern, Recoverable, Statement, - TraitBound, TraitImplItem, TraitItem, TypeImpl, UnaryOp, UnresolvedTraitConstraint, + BinaryOp, BinaryOpKind, BlockExpression, Distinctness, ForLoopStatement, ForRange, + FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Literal, NoirTypeAlias, + Param, Path, Pattern, Recoverable, Statement, TraitBound, TypeImpl, UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; @@ -49,6 +49,23 @@ use chumsky::prelude::*; use iter_extended::vecmap; use noirc_errors::{Span, Spanned}; +mod assertion; +mod attributes; +mod function; +mod lambdas; +mod literals; +mod path; +mod primitives; +mod structs; +mod traits; + +#[cfg(test)] +mod test_helpers; + +use literals::literal; +use path::{maybe_empty_path, path}; +use primitives::{dereference, ident, negation, not, nothing, right_shift_operator, token_kind}; + /// Entry function for the parser - also handles lexing internally. /// /// Given a source_program string, return the ParsedModule Ast representation @@ -109,10 +126,10 @@ fn top_level_statement( module_parser: impl NoirParser, ) -> impl NoirParser { choice(( - function_definition(false).map(TopLevelStatement::Function), - struct_definition(), - trait_definition(), - trait_implementation(), + function::function_definition(false).map(TopLevelStatement::Function), + structs::struct_definition(), + traits::trait_definition(), + traits::trait_implementation(), implementation(), type_alias_definition().then_ignore(force(just(Token::Semicolon))), submodule(module_parser.clone()), @@ -124,6 +141,21 @@ fn top_level_statement( .recover_via(top_level_statement_recovery()) } +/// Parses a non-trait implementation, adding a set of methods to a type. +/// +/// implementation: 'impl' generics type '{' function_definition ... '}' +fn implementation() -> impl NoirParser { + keyword(Keyword::Impl) + .ignore_then(function::generics()) + .then(parse_type().map_with_span(|typ, span| (typ, span))) + .then_ignore(just(Token::LeftBrace)) + .then(spanned(function::function_definition(true)).repeated()) + .then_ignore(just(Token::RightBrace)) + .map(|((generics, (object_type, type_span)), methods)| { + TopLevelStatement::Impl(TypeImpl { generics, object_type, type_span, methods }) + }) +} + /// global_declaration: 'global' ident global_type_annotation '=' literal fn global_declaration() -> impl NoirParser { let p = ignore_then_commit( @@ -160,121 +192,11 @@ fn contract(module_parser: impl NoirParser) -> impl NoirParser impl NoirParser { - attributes() - .then(function_modifiers()) - .then_ignore(keyword(Keyword::Fn)) - .then(ident()) - .then(generics()) - .then(parenthesized(function_parameters(allow_self))) - .then(function_return_type()) - .then(where_clause()) - .then(spanned(block(fresh_statement()))) - .validate(|(((args, ret), where_clause), (body, body_span)), span, emit| { - let ((((attributes, modifiers), name), generics), parameters) = args; - - // Validate collected attributes, filtering them into function and secondary variants - let attributes = validate_attributes(attributes, span, emit); - FunctionDefinition { - span: body_span, - name, - attributes, - is_unconstrained: modifiers.0, - is_open: modifiers.2, - is_internal: modifiers.3, - visibility: if modifiers.1 { - FunctionVisibility::PublicCrate - } else if modifiers.4 { - FunctionVisibility::Public - } else { - FunctionVisibility::Private - }, - generics, - parameters, - body, - where_clause, - return_type: ret.1, - return_visibility: ret.0 .1, - return_distinctness: ret.0 .0, - } - .into() - }) -} - -/// function_modifiers: 'unconstrained'? 'pub(crate)'? 'pub'? 'open'? 'internal'? -/// -/// returns (is_unconstrained, is_pub_crate, is_open, is_internal, is_pub) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool, bool)> { - keyword(Keyword::Unconstrained) - .or_not() - .then(is_pub_crate()) - .then(keyword(Keyword::Pub).or_not()) - .then(keyword(Keyword::Open).or_not()) - .then(keyword(Keyword::Internal).or_not()) - .map(|((((unconstrained, pub_crate), public), open), internal)| { - ( - unconstrained.is_some(), - pub_crate, - open.is_some(), - internal.is_some(), - public.is_some(), - ) - }) -} - -fn is_pub_crate() -> impl NoirParser { - (keyword(Keyword::Pub) - .then_ignore(just(Token::LeftParen)) - .then_ignore(keyword(Keyword::Crate)) - .then_ignore(just(Token::RightParen))) - .or_not() - .map(|a| a.is_some()) -} - -/// non_empty_ident_list: ident ',' non_empty_ident_list -/// | ident -/// -/// generics: '<' non_empty_ident_list '>' -/// | %empty -fn generics() -> impl NoirParser> { - ident() - .separated_by(just(Token::Comma)) - .allow_trailing() - .at_least(1) - .delimited_by(just(Token::Less), just(Token::Greater)) - .or_not() - .map(|opt| opt.unwrap_or_default()) -} - -fn struct_definition() -> impl NoirParser { - use self::Keyword::Struct; - use Token::*; - - let fields = struct_fields() - .delimited_by(just(LeftBrace), just(RightBrace)) - .recover_with(nested_delimiters( - LeftBrace, - RightBrace, - [(LeftParen, RightParen), (LeftBracket, RightBracket)], - |_| vec![], - )) - .or(just(Semicolon).to(Vec::new())); - - attributes().then_ignore(keyword(Struct)).then(ident()).then(generics()).then(fields).validate( - |(((raw_attributes, name), generics), fields), span, emit| { - let attributes = validate_struct_attributes(raw_attributes, span, emit); - TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) - }, - ) -} - fn type_alias_definition() -> impl NoirParser { use self::Keyword::Type; let p = ignore_then_commit(keyword(Type), ident()); - let p = then_commit(p, generics()); + let p = then_commit(p, function::generics()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, parse_type()); @@ -283,13 +205,6 @@ fn type_alias_definition() -> impl NoirParser { }) } -fn lambda_return_type() -> impl NoirParser { - just(Token::Arrow) - .ignore_then(parse_type()) - .or_not() - .map(|ret| ret.unwrap_or_else(UnresolvedType::unspecified)) -} - fn function_return_type() -> impl NoirParser<((Distinctness, Visibility), FunctionReturnType)> { just(Token::Arrow) .ignore_then(optional_distinctness()) @@ -305,69 +220,6 @@ fn function_return_type() -> impl NoirParser<((Distinctness, Visibility), Functi }) } -fn attribute() -> impl NoirParser { - token_kind(TokenKind::Attribute).map(|token| match token { - Token::Attribute(attribute) => attribute, - _ => unreachable!("Parser should have already errored due to token not being an attribute"), - }) -} - -fn attributes() -> impl NoirParser> { - attribute().repeated() -} - -fn struct_fields() -> impl NoirParser> { - ident() - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .separated_by(just(Token::Comma)) - .allow_trailing() -} - -fn lambda_parameters() -> impl NoirParser> { - let typ = parse_type().recover_via(parameter_recovery()); - let typ = just(Token::Colon).ignore_then(typ); - - let parameter = pattern() - .recover_via(parameter_name_recovery()) - .then(typ.or_not().map(|typ| typ.unwrap_or_else(UnresolvedType::unspecified))); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { - let typ = parse_type().recover_via(parameter_recovery()); - - let full_parameter = pattern() - .recover_via(parameter_name_recovery()) - .then_ignore(just(Token::Colon)) - .then(optional_visibility()) - .then(typ) - .map_with_span(|((pattern, visibility), typ), span| Param { - visibility, - pattern, - typ, - span, - }); - - let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; - - let parameter = full_parameter.or(self_parameter); - - parameter - .separated_by(just(Token::Comma)) - .allow_trailing() - .labelled(ParsingRuleLabel::Parameter) -} - -/// This parser always parses no input and fails -fn nothing() -> impl NoirParser { - one_of([]).map(|_| unreachable!("parser should always error")) -} - fn self_parameter() -> impl NoirParser { let mut_ref_pattern = just(Token::Ampersand).then_ignore(keyword(Keyword::Mut)); let mut_pattern = keyword(Keyword::Mut); @@ -401,111 +253,6 @@ fn self_parameter() -> impl NoirParser { }) } -fn trait_definition() -> impl NoirParser { - keyword(Keyword::Trait) - .ignore_then(ident()) - .then(generics()) - .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_body()) - .then_ignore(just(Token::RightBrace)) - .map_with_span(|(((name, generics), where_clause), items), span| { - TopLevelStatement::Trait(NoirTrait { name, generics, where_clause, span, items }) - }) -} - -fn trait_body() -> impl NoirParser> { - trait_function_declaration() - .or(trait_type_declaration()) - .or(trait_constant_declaration()) - .repeated() -} - -fn optional_default_value() -> impl NoirParser> { - ignore_then_commit(just(Token::Assign), expression()).or_not() -} - -fn trait_constant_declaration() -> impl NoirParser { - keyword(Keyword::Let) - .ignore_then(ident()) - .then_ignore(just(Token::Colon)) - .then(parse_type()) - .then(optional_default_value()) - .then_ignore(just(Token::Semicolon)) - .validate(|((name, typ), default_value), span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Associated constants"), - span, - )); - TraitItem::Constant { name, typ, default_value } - }) -} - -/// trait_function_declaration: 'fn' ident generics '(' declaration_parameters ')' function_return_type -fn trait_function_declaration() -> impl NoirParser { - let trait_function_body_or_semicolon = - block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); - - keyword(Keyword::Fn) - .ignore_then(ident()) - .then(generics()) - .then(parenthesized(function_declaration_parameters())) - .then(function_return_type().map(|(_, typ)| typ)) - .then(where_clause()) - .then(trait_function_body_or_semicolon) - .map(|(((((name, generics), parameters), return_type), where_clause), body)| { - TraitItem::Function { name, generics, parameters, return_type, where_clause, body } - }) -} - -fn validate_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Attributes { - let mut primary = None; - let mut secondary = Vec::new(); - - for attribute in attributes { - match attribute { - Attribute::Function(attr) => { - if primary.is_some() { - emit(ParserError::with_reason( - ParserErrorReason::MultipleFunctionAttributesFound, - span, - )); - } - primary = Some(attr); - } - Attribute::Secondary(attr) => secondary.push(attr), - } - } - - Attributes { function: primary, secondary } -} - -fn validate_struct_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Vec { - let mut struct_attributes = vec![]; - - for attribute in attributes { - match attribute { - Attribute::Function(..) => { - emit(ParserError::with_reason( - ParserErrorReason::NoFunctionAttributesAllowedOnStruct, - span, - )); - } - Attribute::Secondary(attr) => struct_attributes.push(attr), - } - } - - struct_attributes -} - /// Function declaration parameters differ from other parameters in that parameter /// patterns are not allowed in declarations. All parameters must be identifiers. fn function_declaration_parameters() -> impl NoirParser> { @@ -536,89 +283,6 @@ fn function_declaration_parameters() -> impl NoirParser impl NoirParser { - keyword(Keyword::Type).ignore_then(ident()).then_ignore(just(Token::Semicolon)).validate( - |name, span, emit| { - emit(ParserError::with_reason( - ParserErrorReason::ExperimentalFeature("Associated types"), - span, - )); - TraitItem::Type { name } - }, - ) -} - -/// Parses a non-trait implementation, adding a set of methods to a type. -/// -/// implementation: 'impl' generics type '{' function_definition ... '}' -fn implementation() -> impl NoirParser { - keyword(Keyword::Impl) - .ignore_then(generics()) - .then(parse_type().map_with_span(|typ, span| (typ, span))) - .then_ignore(just(Token::LeftBrace)) - .then(spanned(function_definition(true)).repeated()) - .then_ignore(just(Token::RightBrace)) - .map(|((generics, (object_type, type_span)), methods)| { - TopLevelStatement::Impl(TypeImpl { generics, object_type, type_span, methods }) - }) -} - -/// Parses a trait implementation, implementing a particular trait for a type. -/// This has a similar syntax to `implementation`, but the `for type` clause is required, -/// and an optional `where` clause is also useable. -/// -/// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' -fn trait_implementation() -> impl NoirParser { - keyword(Keyword::Impl) - .ignore_then(generics()) - .then(path()) - .then(generic_type_args(parse_type())) - .then_ignore(keyword(Keyword::For)) - .then(parse_type()) - .then(where_clause()) - .then_ignore(just(Token::LeftBrace)) - .then(trait_implementation_body()) - .then_ignore(just(Token::RightBrace)) - .map(|args| { - let ((other_args, where_clause), items) = args; - let (((impl_generics, trait_name), trait_generics), object_type) = other_args; - - TopLevelStatement::TraitImpl(NoirTraitImpl { - impl_generics, - trait_name, - trait_generics, - object_type, - items, - where_clause, - }) - }) -} - -fn trait_implementation_body() -> impl NoirParser> { - let function = function_definition(true).validate(|mut f, span, emit| { - if f.def().is_internal - || f.def().is_unconstrained - || f.def().is_open - || f.def().visibility != FunctionVisibility::Private - { - emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); - } - // Trait impl functions are always public - f.def_mut().visibility = FunctionVisibility::Public; - TraitImplItem::Function(f) - }); - - let alias = keyword(Keyword::Type) - .ignore_then(ident()) - .then_ignore(just(Token::Assign)) - .then(parse_type()) - .then_ignore(just(Token::Semicolon)) - .map(|(name, alias)| TraitImplItem::Type { name, alias }); - - function.or(alias).repeated() -} - fn where_clause() -> impl NoirParser> { struct MultiTraitConstraint { typ: UnresolvedType, @@ -713,45 +377,6 @@ fn use_statement() -> impl NoirParser { keyword(Keyword::Use).ignore_then(use_tree()).map(TopLevelStatement::Import) } -fn keyword(keyword: Keyword) -> impl NoirParser { - just(Token::Keyword(keyword)) -} - -fn token_kind(token_kind: TokenKind) -> impl NoirParser { - filter_map(move |span, found: Token| { - if found.kind() == token_kind { - Ok(found) - } else { - Err(ParserError::expected_label( - ParsingRuleLabel::TokenKind(token_kind.clone()), - found, - span, - )) - } - }) -} - -fn path() -> impl NoirParser { - let idents = || ident().separated_by(just(Token::DoubleColon)).at_least(1); - let make_path = |kind| move |segments, span| Path { segments, kind, span }; - - let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); - let path_kind = |key, kind| prefix(key).ignore_then(idents()).map_with_span(make_path(kind)); - - choice(( - path_kind(Keyword::Crate, PathKind::Crate), - path_kind(Keyword::Dep, PathKind::Dep), - idents().map_with_span(make_path(PathKind::Plain)), - )) -} - -fn empty_path() -> impl NoirParser { - let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; - let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); - - choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep))) -} - fn rename() -> impl NoirParser> { ignore_then_commit(keyword(Keyword::As), ident()).or_not() } @@ -764,7 +389,7 @@ fn use_tree() -> impl NoirParser { }); let list = { - let prefix = path().or(empty_path()).then_ignore(just(Token::DoubleColon)); + let prefix = maybe_empty_path().then_ignore(just(Token::DoubleColon)); let tree = use_tree .separated_by(just(Token::Comma)) .allow_trailing() @@ -778,10 +403,6 @@ fn use_tree() -> impl NoirParser { }) } -fn ident() -> impl NoirParser { - token_kind(TokenKind::Ident).map_with_span(Ident::from_token) -} - fn statement<'a, P, P2>( expr_parser: P, expr_no_constructors: P2, @@ -792,9 +413,9 @@ where { recursive(|statement| { choice(( - constrain(expr_parser.clone()), - assertion(expr_parser.clone()), - assertion_eq(expr_parser.clone()), + assertion::constrain(expr_parser.clone()), + assertion::assertion(expr_parser.clone()), + assertion::assertion_eq(expr_parser.clone()), declaration(expr_parser.clone()), assignment(expr_parser.clone()), for_loop(expr_no_constructors, statement), @@ -808,64 +429,6 @@ fn fresh_statement() -> impl NoirParser { statement(expression(), expression_no_constructors(expression())) } -fn constrain<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - ignore_then_commit( - keyword(Keyword::Constrain).labelled(ParsingRuleLabel::Statement), - expr_parser, - ) - .map(|expr| StatementKind::Constrain(ConstrainStatement(expr, None, ConstrainKind::Constrain))) - .validate(|expr, span, emit| { - emit(ParserError::with_reason(ParserErrorReason::ConstrainDeprecated, span)); - expr - }) -} - -fn assertion<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let argument_parser = - expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(1).at_most(2); - - ignore_then_commit(keyword(Keyword::Assert), parenthesized(argument_parser)) - .labelled(ParsingRuleLabel::Statement) - .validate(|expressions, span, _| { - let condition = expressions.first().unwrap_or(&Expression::error(span)).clone(); - let message = expressions.get(1).cloned(); - StatementKind::Constrain(ConstrainStatement(condition, message, ConstrainKind::Assert)) - }) -} - -fn assertion_eq<'a, P>(expr_parser: P) -> impl NoirParser + 'a -where - P: ExprParser + 'a, -{ - let argument_parser = - expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(2).at_most(3); - - ignore_then_commit(keyword(Keyword::AssertEq), parenthesized(argument_parser)) - .labelled(ParsingRuleLabel::Statement) - .validate(|exprs: Vec, span, _| { - let predicate = Expression::new( - ExpressionKind::Infix(Box::new(InfixExpression { - lhs: exprs.first().unwrap_or(&Expression::error(span)).clone(), - rhs: exprs.get(1).unwrap_or(&Expression::error(span)).clone(), - operator: Spanned::from(span, BinaryOpKind::Equal), - })), - span, - ); - let message = exprs.get(2).cloned(); - StatementKind::Constrain(ConstrainStatement( - predicate, - message, - ConstrainKind::AssertEq, - )) - }) -} - fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -1308,13 +871,6 @@ fn create_infix_expression(lhs: Expression, (operator, rhs): (BinaryOp, Expressi Expression { span, kind: ExpressionKind::Infix(infix) } } -// Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier -// to parse nested generic types. For normal expressions however, it means we have to manually -// parse two greater-than tokens as a single right-shift here. -fn right_shift_operator() -> impl NoirParser { - just(Token::Greater).then(just(Token::Greater)).to(Token::ShiftRight) -} - fn operator_with_precedence(precedence: Precedence) -> impl NoirParser> { right_shift_operator() .or(any()) // Parse any single token, we're validating it as an operator next @@ -1454,18 +1010,6 @@ where }) } -fn lambda<'a>( - expr_parser: impl NoirParser + 'a, -) -> impl NoirParser + 'a { - lambda_parameters() - .delimited_by(just(Token::Pipe), just(Token::Pipe)) - .then(lambda_return_type()) - .then(expr_parser) - .map(|((parameters, return_type), body)| { - ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) - }) -} - fn for_loop<'a, P, S>(expr_no_constructors: P, statement: S) -> impl NoirParser + 'a where P: ExprParser + 'a, @@ -1530,41 +1074,6 @@ where expr_parser.separated_by(just(Token::Comma)).allow_trailing() } -fn not

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Bang).ignore_then(term_parser).map(|rhs| ExpressionKind::prefix(UnaryOp::Not, rhs)) -} - -fn negation

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Minus) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::Minus, rhs)) -} - -fn mutable_reference

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Ampersand) - .ignore_then(keyword(Keyword::Mut)) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::MutableReference, rhs)) -} - -fn dereference

(term_parser: P) -> impl NoirParser -where - P: ExprParser, -{ - just(Token::Star) - .ignore_then(term_parser) - .map(|rhs| ExpressionKind::prefix(UnaryOp::Dereference { implicitly_added: false }, rhs)) -} - /// Atoms are parameterized on whether constructor expressions are allowed or not. /// Certain constructs like `if` and `for` disallow constructor expressions when a /// block may be expected. @@ -1587,7 +1096,7 @@ where } else { nothing().boxed() }, - lambda(expr_parser.clone()), + lambdas::lambda(expr_parser.clone()), block(statement).map(ExpressionKind::Block), variable(), literal(), @@ -1655,167 +1164,12 @@ where long_form.or(short_form) } -fn variable() -> impl NoirParser { - path().map(ExpressionKind::Variable) -} - -fn literal() -> impl NoirParser { - token_kind(TokenKind::Literal).map(|token| match token { - Token::Int(x) => ExpressionKind::integer(x), - Token::Bool(b) => ExpressionKind::boolean(b), - Token::Str(s) => ExpressionKind::string(s), - Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes), - Token::FmtStr(s) => ExpressionKind::format_string(s), - unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected), - }) -} - #[cfg(test)] mod test { - use noirc_errors::CustomDiagnostic; - + use super::test_helpers::*; use super::*; use crate::{ArrayLiteral, Literal}; - fn parse_with(parser: P, program: &str) -> Result> - where - P: NoirParser, - { - let (tokens, lexer_errors) = Lexer::lex(program); - if !lexer_errors.is_empty() { - return Err(vecmap(lexer_errors, Into::into)); - } - parser - .then_ignore(just(Token::EOF)) - .parse(tokens) - .map_err(|errors| vecmap(errors, Into::into)) - } - - fn parse_recover(parser: P, program: &str) -> (Option, Vec) - where - P: NoirParser, - { - let (tokens, lexer_errors) = Lexer::lex(program); - let (opt, errs) = parser.then_ignore(force(just(Token::EOF))).parse_recovery(tokens); - - let mut errors = vecmap(lexer_errors, Into::into); - errors.extend(errs.into_iter().map(Into::into)); - - (opt, errors) - } - - fn parse_all(parser: P, programs: Vec<&str>) -> Vec - where - P: NoirParser, - { - vecmap(programs, move |program| { - let message = format!("Failed to parse:\n{program}"); - let (op_t, diagnostics) = parse_recover(&parser, program); - diagnostics.iter().for_each(|diagnostic| { - if diagnostic.is_error() { - panic!("{} with error {}", &message, diagnostic); - } - }); - op_t.expect(&message) - }) - } - - fn parse_all_failing(parser: P, programs: Vec<&str>) -> Vec - where - P: NoirParser, - T: std::fmt::Display, - { - programs - .into_iter() - .flat_map(|program| match parse_with(&parser, program) { - Ok(expr) => { - unreachable!( - "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", - program, expr - ) - } - Err(diagnostics) => { - if diagnostics.iter().all(|diagnostic: &CustomDiagnostic| diagnostic.is_warning()) { - unreachable!( - "Expected at least one error when parsing:\n{}\nYet it successfully parsed without errors:\n", - program - ) - }; - diagnostics - } - }) - .collect() - } - - #[derive(Copy, Clone)] - struct Case { - source: &'static str, - errors: usize, - expect: &'static str, - } - - fn check_cases_with_errors(cases: &[Case], parser: P) - where - P: NoirParser + Clone, - T: std::fmt::Display, - { - let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); - - let results = vecmap(cases, |&case| { - let (opt, errors) = parse_recover(parser.clone(), case.source); - let actual = opt.map(|ast| ast.to_string()); - let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; - - let result = ((errors.len(), actual.clone()), (case.errors, case.expect.to_string())); - if result.0 != result.1 { - let num_errors = errors.len(); - let shown_errors = show_errors(errors); - eprintln!( - concat!( - "\nExpected {expected_errors} error(s) and got {num_errors}:", - "\n\n{shown_errors}", - "\n\nFrom input: {src}", - "\nExpected AST: {expected_result}", - "\nActual AST: {actual}\n", - ), - expected_errors = case.errors, - num_errors = num_errors, - shown_errors = shown_errors, - src = case.source, - expected_result = case.expect, - actual = actual, - ); - } - result - }); - - assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); - } - - #[test] - fn regression_skip_comment() { - parse_all( - function_definition(false), - vec![ - "fn main( - // This comment should be skipped - x : Field, - // And this one - y : Field, - ) { - }", - "fn main(x : Field, y : Field,) { - foo::bar( - // Comment for x argument - x, - // Comment for y argument - y - ) - }", - ], - ); - } - #[test] fn parse_infix() { let valid = vec!["x + 6", "x - k", "x + (x + a)", " x * (x + a) + (x - 4)"]; @@ -1966,142 +1320,6 @@ mod test { } } - /// Deprecated constrain usage test - #[test] - fn parse_constrain() { - let errors = parse_with(constrain(expression()), "constrain x == y").unwrap_err(); - assert_eq!(errors.len(), 1); - assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); - - // Currently we disallow constrain statements where the outer infix operator - // produces a value. This would require an implicit `==` which - // may not be intuitive to the user. - // - // If this is deemed useful, one would either apply a transformation - // or interpret it with an `==` in the evaluator - let disallowed_operators = vec![ - BinaryOpKind::And, - BinaryOpKind::Subtract, - BinaryOpKind::Divide, - BinaryOpKind::Multiply, - BinaryOpKind::Or, - ]; - - for operator in disallowed_operators { - let src = format!("constrain x {} y;", operator.as_string()); - let errors = parse_with(constrain(expression()), &src).unwrap_err(); - assert_eq!(errors.len(), 2); - assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); - } - - // These are general cases which should always work. - // - // The first case is the most noteworthy. It contains two `==` - // The first (inner) `==` is a predicate which returns 0/1 - // The outer layer is an infix `==` which is - // associated with the Constrain statement - let errors = parse_all_failing( - constrain(expression()), - vec![ - "constrain ((x + y) == k) + z == y", - "constrain (x + !y) == y", - "constrain (x ^ y) == y", - "constrain (x ^ y) == (y + m)", - "constrain x + x ^ x == y | m", - ], - ); - assert_eq!(errors.len(), 5); - assert!(errors - .iter() - .all(|err| { err.is_error() && err.to_string().contains("deprecated") })); - } - - /// This is the standard way to declare an assert statement - #[test] - fn parse_assert() { - parse_with(assertion(expression()), "assert(x == y)").unwrap(); - - // Currently we disallow constrain statements where the outer infix operator - // produces a value. This would require an implicit `==` which - // may not be intuitive to the user. - // - // If this is deemed useful, one would either apply a transformation - // or interpret it with an `==` in the evaluator - let disallowed_operators = vec![ - BinaryOpKind::And, - BinaryOpKind::Subtract, - BinaryOpKind::Divide, - BinaryOpKind::Multiply, - BinaryOpKind::Or, - ]; - - for operator in disallowed_operators { - let src = format!("assert(x {} y);", operator.as_string()); - parse_with(assertion(expression()), &src).unwrap_err(); - } - - // These are general cases which should always work. - // - // The first case is the most noteworthy. It contains two `==` - // The first (inner) `==` is a predicate which returns 0/1 - // The outer layer is an infix `==` which is - // associated with the Constrain statement - parse_all( - assertion(expression()), - vec![ - "assert(((x + y) == k) + z == y)", - "assert((x + !y) == y)", - "assert((x ^ y) == y)", - "assert((x ^ y) == (y + m))", - "assert(x + x ^ x == y | m)", - ], - ); - - match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() - { - StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - let message = message.unwrap(); - match message.kind { - ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message".to_owned()); - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } - - /// This is the standard way to assert that two expressions are equivalent - #[test] - fn parse_assert_eq() { - parse_all( - assertion_eq(expression()), - vec![ - "assert_eq(x, y)", - "assert_eq(((x + y) == k) + z, y)", - "assert_eq(x + !y, y)", - "assert_eq(x ^ y, y)", - "assert_eq(x ^ y, y + m)", - "assert_eq(x + x ^ x, y | m)", - ], - ); - match parse_with(assertion_eq(expression()), "assert_eq(x, y, \"assertion message\")") - .unwrap() - { - StatementKind::Constrain(ConstrainStatement(_, message, _)) => { - let message = message.unwrap(); - match message.kind { - ExpressionKind::Literal(Literal::Str(message_string)) => { - assert_eq!(message_string, "assertion message".to_owned()); - } - _ => unreachable!(), - } - } - _ => unreachable!(), - } - } - #[test] fn parse_let() { // Why is it valid to specify a let declaration as having type u8? @@ -2135,84 +1353,6 @@ mod test { ); } - #[test] - fn parse_function() { - parse_all( - function_definition(false), - vec![ - "fn func_name() {}", - "fn f(foo: pub u8, y : pub Field) -> u8 { x + a }", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) {}", - "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", - "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", - "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", - "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", - "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", - "fn func_name(f: Field, y : T) where T: SomeTrait {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", - "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 + TraitY {}", - "fn func_name(f: Field, y : T, z : U) where SomeStruct: SomeTrait {}", - // 'where u32: SomeTrait' is allowed in Rust. - // It will result in compiler error in case SomeTrait isn't implemented for u32. - "fn func_name(f: Field, y : T) where u32: SomeTrait {}", - // A trailing plus is allowed by Rust, so we support it as well. - "fn func_name(f: Field, y : T) where T: SomeTrait + {}", - // The following should produce compile error on later stage. From the parser's perspective it's fine - "fn func_name(f: Field, y : Field, z : Field) where T: SomeTrait {}", - ], - ); - - parse_all_failing( - function_definition(false), - vec![ - "fn x2( f: []Field,,) {}", - "fn ( f: []Field) {}", - "fn ( f: []Field) {}", - // TODO: Check for more specific error messages - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", - "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", - // A leading plus is not allowed. - "fn func_name(f: Field, y : T) where T: + SomeTrait {}", - "fn func_name(f: Field, y : T) where T: TraitX + {}", - ], - ); - } - - #[test] - fn parse_trait() { - parse_all( - trait_definition(), - vec![ - // Empty traits are legal in Rust and sometimes used as a way to whitelist certain types - // for a particular operation. Also known as `tag` or `marker` traits: - // https://stackoverflow.com/questions/71895489/what-is-the-purpose-of-defining-empty-impl-in-rust - "trait Empty {}", - "trait TraitWithDefaultBody { fn foo(self) {} }", - "trait TraitAcceptingMutableRef { fn foo(&mut self); }", - "trait TraitWithTypeBoundOperation { fn identity() -> Self; }", - "trait TraitWithAssociatedType { type Element; fn item(self, index: Field) -> Self::Element; }", - "trait TraitWithAssociatedConstant { let Size: Field; }", - "trait TraitWithAssociatedConstantWithDefaultValue { let Size: Field = 10; }", - "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", - "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", - "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", - ], - ); - - parse_all_failing( - trait_definition(), - vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], - ); - } - #[test] fn parse_parenthesized_expression() { parse_all( @@ -2243,104 +1383,12 @@ mod test { ); } - fn expr_to_lit(expr: ExpressionKind) -> Literal { - match expr { - ExpressionKind::Literal(literal) => literal, - _ => unreachable!("expected a literal"), - } - } - - #[test] - fn parse_int() { - let int = parse_with(literal(), "5").unwrap(); - let hex = parse_with(literal(), "0x05").unwrap(); - - match (expr_to_lit(int), expr_to_lit(hex)) { - (Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex), - _ => unreachable!(), - } - } - - #[test] - fn parse_string() { - let expr = parse_with(literal(), r#""hello""#).unwrap(); - match expr_to_lit(expr) { - Literal::Str(s) => assert_eq!(s, "hello"), - _ => unreachable!(), - }; - } - - #[test] - fn parse_bool() { - let expr_true = parse_with(literal(), "true").unwrap(); - let expr_false = parse_with(literal(), "false").unwrap(); - - match (expr_to_lit(expr_true), expr_to_lit(expr_false)) { - (Literal::Bool(t), Literal::Bool(f)) => { - assert!(t); - assert!(!f); - } - _ => unreachable!(), - }; - } - #[test] fn parse_module_declaration() { parse_with(module_declaration(), "mod foo").unwrap(); parse_with(module_declaration(), "mod 1").unwrap_err(); } - #[test] - fn parse_path() { - let cases = vec![ - ("std", vec!["std"]), - ("std::hash", vec!["std", "hash"]), - ("std::hash::collections", vec!["std", "hash", "collections"]), - ("dep::foo::bar", vec!["foo", "bar"]), - ("crate::std::hash", vec!["std", "hash"]), - ]; - - for (src, expected_segments) in cases { - let path: Path = parse_with(path(), src).unwrap(); - for (segment, expected) in path.segments.into_iter().zip(expected_segments) { - assert_eq!(segment.0.contents, expected); - } - } - - parse_all_failing(path(), vec!["std::", "::std", "std::hash::", "foo::1"]); - } - - #[test] - fn parse_path_kinds() { - let cases = vec![ - ("std", PathKind::Plain), - ("dep::hash::collections", PathKind::Dep), - ("crate::std::hash", PathKind::Crate), - ]; - - for (src, expected_path_kind) in cases { - let path = parse_with(path(), src).unwrap(); - assert_eq!(path.kind, expected_path_kind); - } - - parse_all_failing( - path(), - vec!["dep", "crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], - ); - } - - #[test] - fn parse_unary() { - parse_all( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], - ); - parse_all_failing( - term(expression(), expression_no_constructors(expression()), fresh_statement(), true), - vec!["+hello", "/hello"], - ); - } - #[test] fn parse_use() { parse_all( @@ -2372,26 +1420,6 @@ mod test { ); } - #[test] - fn parse_structs() { - let cases = vec![ - "struct Foo;", - "struct Foo { }", - "struct Bar { ident: Field, }", - "struct Baz { ident: Field, other: Field }", - "#[attribute] struct Baz { ident: Field, other: Field }", - ]; - parse_all(struct_definition(), cases); - - let failing = vec![ - "struct { }", - "struct Foo { bar: pub Field }", - "struct Foo { bar: pub Field }", - "#[oracle(some)] struct Foo { bar: Field }", - ]; - parse_all_failing(struct_definition(), failing); - } - #[test] fn parse_type_aliases() { let cases = vec!["type foo = u8", "type bar = String", "type baz = Vec"]; @@ -2563,79 +1591,4 @@ mod test { check_cases_with_errors(&cases[..], block(fresh_statement())); } - - #[test] - fn parse_raw_string_expr() { - let cases = vec![ - Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, - Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, - // backslash - Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, - Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, - Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, - Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, - // escape sequence - Case { - source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, - expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, - errors: 0, - }, - Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, - // mismatch - errors: - Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - // mismatch: short: - Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 }, - Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 }, - // empty string - Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 }, - Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, - // miscellaneous - Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 }, - Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - // missing 'r' letter - Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 }, - Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 }, - // whitespace - Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 }, - Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 }, - Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 }, - // after identifier - Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 }, - // nested - Case { - source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, - expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, - errors: 0, - }, - ]; - - check_cases_with_errors(&cases[..], expression()); - } - - #[test] - fn parse_raw_string_lit() { - let lit_cases = vec![ - Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, - Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, - // backslash - Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, - Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, - Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, - Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, - // escape sequence - Case { - source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, - expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, - errors: 0, - }, - Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, - // mismatch - errors: - Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, - Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, - ]; - - check_cases_with_errors(&lit_cases[..], literal()); - } } diff --git a/compiler/noirc_frontend/src/parser/parser/assertion.rs b/compiler/noirc_frontend/src/parser/parser/assertion.rs new file mode 100644 index 00000000000..f9c8d7aa46b --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/assertion.rs @@ -0,0 +1,219 @@ +use crate::ast::{Expression, ExpressionKind, StatementKind}; +use crate::parser::{ + ignore_then_commit, labels::ParsingRuleLabel, parenthesized, ExprParser, NoirParser, + ParserError, ParserErrorReason, +}; + +use crate::token::{Keyword, Token}; +use crate::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Recoverable}; + +use chumsky::prelude::*; +use noirc_errors::Spanned; + +use super::keyword; + +pub(super) fn constrain<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + ignore_then_commit( + keyword(Keyword::Constrain).labelled(ParsingRuleLabel::Statement), + expr_parser, + ) + .map(|expr| StatementKind::Constrain(ConstrainStatement(expr, None, ConstrainKind::Constrain))) + .validate(|expr, span, emit| { + emit(ParserError::with_reason(ParserErrorReason::ConstrainDeprecated, span)); + expr + }) +} + +pub(super) fn assertion<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + let argument_parser = + expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(1).at_most(2); + + ignore_then_commit(keyword(Keyword::Assert), parenthesized(argument_parser)) + .labelled(ParsingRuleLabel::Statement) + .validate(|expressions, span, _| { + let condition = expressions.first().unwrap_or(&Expression::error(span)).clone(); + let message = expressions.get(1).cloned(); + StatementKind::Constrain(ConstrainStatement(condition, message, ConstrainKind::Assert)) + }) +} + +pub(super) fn assertion_eq<'a, P>(expr_parser: P) -> impl NoirParser + 'a +where + P: ExprParser + 'a, +{ + let argument_parser = + expr_parser.separated_by(just(Token::Comma)).allow_trailing().at_least(2).at_most(3); + + ignore_then_commit(keyword(Keyword::AssertEq), parenthesized(argument_parser)) + .labelled(ParsingRuleLabel::Statement) + .validate(|exprs: Vec, span, _| { + let predicate = Expression::new( + ExpressionKind::Infix(Box::new(InfixExpression { + lhs: exprs.first().unwrap_or(&Expression::error(span)).clone(), + rhs: exprs.get(1).unwrap_or(&Expression::error(span)).clone(), + operator: Spanned::from(span, BinaryOpKind::Equal), + })), + span, + ); + let message = exprs.get(2).cloned(); + StatementKind::Constrain(ConstrainStatement( + predicate, + message, + ConstrainKind::AssertEq, + )) + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + parser::parser::{ + expression, + test_helpers::{parse_all, parse_all_failing, parse_with}, + }, + Literal, + }; + + /// Deprecated constrain usage test + #[test] + fn parse_constrain() { + let errors = parse_with(constrain(expression()), "constrain x == y").unwrap_err(); + assert_eq!(errors.len(), 1); + assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); + + // Currently we disallow constrain statements where the outer infix operator + // produces a value. This would require an implicit `==` which + // may not be intuitive to the user. + // + // If this is deemed useful, one would either apply a transformation + // or interpret it with an `==` in the evaluator + let disallowed_operators = vec![ + BinaryOpKind::And, + BinaryOpKind::Subtract, + BinaryOpKind::Divide, + BinaryOpKind::Multiply, + BinaryOpKind::Or, + ]; + + for operator in disallowed_operators { + let src = format!("constrain x {} y;", operator.as_string()); + let errors = parse_with(constrain(expression()), &src).unwrap_err(); + assert_eq!(errors.len(), 2); + assert!(format!("{}", errors.first().unwrap()).contains("deprecated")); + } + + // These are general cases which should always work. + // + // The first case is the most noteworthy. It contains two `==` + // The first (inner) `==` is a predicate which returns 0/1 + // The outer layer is an infix `==` which is + // associated with the Constrain statement + let errors = parse_all_failing( + constrain(expression()), + vec![ + "constrain ((x + y) == k) + z == y", + "constrain (x + !y) == y", + "constrain (x ^ y) == y", + "constrain (x ^ y) == (y + m)", + "constrain x + x ^ x == y | m", + ], + ); + assert_eq!(errors.len(), 5); + assert!(errors + .iter() + .all(|err| { err.is_error() && err.to_string().contains("deprecated") })); + } + + /// This is the standard way to declare an assert statement + #[test] + fn parse_assert() { + parse_with(assertion(expression()), "assert(x == y)").unwrap(); + + // Currently we disallow constrain statements where the outer infix operator + // produces a value. This would require an implicit `==` which + // may not be intuitive to the user. + // + // If this is deemed useful, one would either apply a transformation + // or interpret it with an `==` in the evaluator + let disallowed_operators = vec![ + BinaryOpKind::And, + BinaryOpKind::Subtract, + BinaryOpKind::Divide, + BinaryOpKind::Multiply, + BinaryOpKind::Or, + ]; + + for operator in disallowed_operators { + let src = format!("assert(x {} y);", operator.as_string()); + parse_with(assertion(expression()), &src).unwrap_err(); + } + + // These are general cases which should always work. + // + // The first case is the most noteworthy. It contains two `==` + // The first (inner) `==` is a predicate which returns 0/1 + // The outer layer is an infix `==` which is + // associated with the Constrain statement + parse_all( + assertion(expression()), + vec![ + "assert(((x + y) == k) + z == y)", + "assert((x + !y) == y)", + "assert((x ^ y) == y)", + "assert((x ^ y) == (y + m))", + "assert(x + x ^ x == y | m)", + ], + ); + + match parse_with(assertion(expression()), "assert(x == y, \"assertion message\")").unwrap() + { + StatementKind::Constrain(ConstrainStatement(_, message, _)) => { + let message = message.unwrap(); + match message.kind { + ExpressionKind::Literal(Literal::Str(message_string)) => { + assert_eq!(message_string, "assertion message".to_owned()); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } + + /// This is the standard way to assert that two expressions are equivalent + #[test] + fn parse_assert_eq() { + parse_all( + assertion_eq(expression()), + vec![ + "assert_eq(x, y)", + "assert_eq(((x + y) == k) + z, y)", + "assert_eq(x + !y, y)", + "assert_eq(x ^ y, y)", + "assert_eq(x ^ y, y + m)", + "assert_eq(x + x ^ x, y | m)", + ], + ); + match parse_with(assertion_eq(expression()), "assert_eq(x, y, \"assertion message\")") + .unwrap() + { + StatementKind::Constrain(ConstrainStatement(_, message, _)) => { + let message = message.unwrap(); + match message.kind { + ExpressionKind::Literal(Literal::Str(message_string)) => { + assert_eq!(message_string, "assertion message".to_owned()); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs new file mode 100644 index 00000000000..4b256a95c8b --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -0,0 +1,46 @@ +use chumsky::Parser; +use noirc_errors::Span; + +use crate::{ + parser::{NoirParser, ParserError, ParserErrorReason}, + token::{Attribute, Attributes, Token, TokenKind}, +}; + +use super::primitives::token_kind; + +fn attribute() -> impl NoirParser { + token_kind(TokenKind::Attribute).map(|token| match token { + Token::Attribute(attribute) => attribute, + _ => unreachable!("Parser should have already errored due to token not being an attribute"), + }) +} + +pub(super) fn attributes() -> impl NoirParser> { + attribute().repeated() +} + +pub(super) fn validate_attributes( + attributes: Vec, + span: Span, + emit: &mut dyn FnMut(ParserError), +) -> Attributes { + let mut primary = None; + let mut secondary = Vec::new(); + + for attribute in attributes { + match attribute { + Attribute::Function(attr) => { + if primary.is_some() { + emit(ParserError::with_reason( + ParserErrorReason::MultipleFunctionAttributesFound, + span, + )); + } + primary = Some(attr); + } + Attribute::Secondary(attr) => secondary.push(attr), + } + } + + Attributes { function: primary, secondary } +} diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs new file mode 100644 index 00000000000..0d34c719061 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -0,0 +1,224 @@ +use super::{ + attributes::{attributes, validate_attributes}, + block, fresh_statement, ident, keyword, nothing, optional_distinctness, optional_visibility, + parameter_name_recovery, parameter_recovery, parenthesized, parse_type, pattern, + self_parameter, where_clause, NoirParser, +}; +use crate::parser::labels::ParsingRuleLabel; +use crate::parser::spanned; +use crate::token::{Keyword, Token}; +use crate::{ + Distinctness, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, NoirFunction, + Param, Visibility, +}; + +use chumsky::prelude::*; + +/// function_definition: attribute function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block +/// function_modifiers 'fn' ident generics '(' function_parameters ')' function_return_type block +pub(super) fn function_definition(allow_self: bool) -> impl NoirParser { + attributes() + .then(function_modifiers()) + .then_ignore(keyword(Keyword::Fn)) + .then(ident()) + .then(generics()) + .then(parenthesized(function_parameters(allow_self))) + .then(function_return_type()) + .then(where_clause()) + .then(spanned(block(fresh_statement()))) + .validate(|(((args, ret), where_clause), (body, body_span)), span, emit| { + let ((((attributes, modifiers), name), generics), parameters) = args; + + // Validate collected attributes, filtering them into function and secondary variants + let attributes = validate_attributes(attributes, span, emit); + FunctionDefinition { + span: body_span, + name, + attributes, + is_unconstrained: modifiers.0, + is_open: modifiers.2, + is_internal: modifiers.3, + visibility: if modifiers.1 { + FunctionVisibility::PublicCrate + } else if modifiers.4 { + FunctionVisibility::Public + } else { + FunctionVisibility::Private + }, + generics, + parameters, + body, + where_clause, + return_type: ret.1, + return_visibility: ret.0 .1, + return_distinctness: ret.0 .0, + } + .into() + }) +} + +/// function_modifiers: 'unconstrained'? 'pub(crate)'? 'pub'? 'open'? 'internal'? +/// +/// returns (is_unconstrained, is_pub_crate, is_open, is_internal, is_pub) for whether each keyword was present +fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool, bool)> { + keyword(Keyword::Unconstrained) + .or_not() + .then(is_pub_crate()) + .then(keyword(Keyword::Pub).or_not()) + .then(keyword(Keyword::Open).or_not()) + .then(keyword(Keyword::Internal).or_not()) + .map(|((((unconstrained, pub_crate), public), open), internal)| { + ( + unconstrained.is_some(), + pub_crate, + open.is_some(), + internal.is_some(), + public.is_some(), + ) + }) +} + +fn is_pub_crate() -> impl NoirParser { + (keyword(Keyword::Pub) + .then_ignore(just(Token::LeftParen)) + .then_ignore(keyword(Keyword::Crate)) + .then_ignore(just(Token::RightParen))) + .or_not() + .map(|a| a.is_some()) +} + +/// non_empty_ident_list: ident ',' non_empty_ident_list +/// | ident +/// +/// generics: '<' non_empty_ident_list '>' +/// | %empty +pub(super) fn generics() -> impl NoirParser> { + ident() + .separated_by(just(Token::Comma)) + .allow_trailing() + .at_least(1) + .delimited_by(just(Token::Less), just(Token::Greater)) + .or_not() + .map(|opt| opt.unwrap_or_default()) +} + +fn function_return_type() -> impl NoirParser<((Distinctness, Visibility), FunctionReturnType)> { + just(Token::Arrow) + .ignore_then(optional_distinctness()) + .then(optional_visibility()) + .then(spanned(parse_type())) + .or_not() + .map_with_span(|ret, span| match ret { + Some((head, (ty, _))) => (head, FunctionReturnType::Ty(ty)), + None => ( + (Distinctness::DuplicationAllowed, Visibility::Private), + FunctionReturnType::Default(span), + ), + }) +} + +fn function_parameters<'a>(allow_self: bool) -> impl NoirParser> + 'a { + let typ = parse_type().recover_via(parameter_recovery()); + + let full_parameter = pattern() + .recover_via(parameter_name_recovery()) + .then_ignore(just(Token::Colon)) + .then(optional_visibility()) + .then(typ) + .map_with_span(|((pattern, visibility), typ), span| Param { + visibility, + pattern, + typ, + span, + }); + + let self_parameter = if allow_self { self_parameter().boxed() } else { nothing().boxed() }; + + let parameter = full_parameter.or(self_parameter); + + parameter + .separated_by(just(Token::Comma)) + .allow_trailing() + .labelled(ParsingRuleLabel::Parameter) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn regression_skip_comment() { + parse_all( + function_definition(false), + vec![ + "fn main( + // This comment should be skipped + x : Field, + // And this one + y : Field, + ) { + }", + "fn main(x : Field, y : Field,) { + foo::bar( + // Comment for x argument + x, + // Comment for y argument + y + ) + }", + ], + ); + } + + #[test] + fn parse_function() { + parse_all( + function_definition(false), + vec![ + "fn func_name() {}", + "fn f(foo: pub u8, y : pub Field) -> u8 { x + a }", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) {}", + "fn f(f: pub Field, y : Field, z : Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : Field) -> u8 { x + a }", + "fn func_name(x: [Field], y : [Field;2],y : pub [Field;2], z : pub [u8;5]) {}", + "fn main(x: pub u8, y: pub u8) -> distinct pub [u8; 2] { [x, y] }", + "fn f(f: pub Field, y : Field, z : comptime Field) -> u8 { x + a }", + "fn f(f: pub Field, y : T, z : comptime Field) -> u8 { x + a }", + "fn func_name(f: Field, y : T) where T: SomeTrait {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait, T: SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 {}", + "fn func_name(f: Field, y : T) where T: SomeTrait + SomeTrait2 + TraitY {}", + "fn func_name(f: Field, y : T, z : U) where SomeStruct: SomeTrait {}", + // 'where u32: SomeTrait' is allowed in Rust. + // It will result in compiler error in case SomeTrait isn't implemented for u32. + "fn func_name(f: Field, y : T) where u32: SomeTrait {}", + // A trailing plus is allowed by Rust, so we support it as well. + "fn func_name(f: Field, y : T) where T: SomeTrait + {}", + // The following should produce compile error on later stage. From the parser's perspective it's fine + "fn func_name(f: Field, y : Field, z : Field) where T: SomeTrait {}", + ], + ); + + parse_all_failing( + function_definition(false), + vec![ + "fn x2( f: []Field,,) {}", + "fn ( f: []Field) {}", + "fn ( f: []Field) {}", + // TODO: Check for more specific error messages + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where T: {}", + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) where SomeTrait {}", + "fn func_name(f: Field, y : pub Field, z : pub [u8;5],) SomeTrait {}", + // A leading plus is not allowed. + "fn func_name(f: Field, y : T) where T: + SomeTrait {}", + "fn func_name(f: Field, y : T) where T: TraitX + {}", + ], + ); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/lambdas.rs b/compiler/noirc_frontend/src/parser/parser/lambdas.rs new file mode 100644 index 00000000000..48ddd41ab44 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/lambdas.rs @@ -0,0 +1,42 @@ +use chumsky::{primitive::just, Parser}; + +use crate::{ + parser::{labels::ParsingRuleLabel, parameter_name_recovery, parameter_recovery, NoirParser}, + token::Token, + Expression, ExpressionKind, Lambda, Pattern, UnresolvedType, +}; + +use super::{parse_type, pattern}; + +pub(super) fn lambda<'a>( + expr_parser: impl NoirParser + 'a, +) -> impl NoirParser + 'a { + lambda_parameters() + .delimited_by(just(Token::Pipe), just(Token::Pipe)) + .then(lambda_return_type()) + .then(expr_parser) + .map(|((parameters, return_type), body)| { + ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) + }) +} + +fn lambda_parameters() -> impl NoirParser> { + let typ = parse_type().recover_via(parameter_recovery()); + let typ = just(Token::Colon).ignore_then(typ); + + let parameter = pattern() + .recover_via(parameter_name_recovery()) + .then(typ.or_not().map(|typ| typ.unwrap_or_else(UnresolvedType::unspecified))); + + parameter + .separated_by(just(Token::Comma)) + .allow_trailing() + .labelled(ParsingRuleLabel::Parameter) +} + +fn lambda_return_type() -> impl NoirParser { + just(Token::Arrow) + .ignore_then(parse_type()) + .or_not() + .map(|ret| ret.unwrap_or_else(UnresolvedType::unspecified)) +} diff --git a/compiler/noirc_frontend/src/parser/parser/literals.rs b/compiler/noirc_frontend/src/parser/parser/literals.rs new file mode 100644 index 00000000000..32f4f03de2e --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/literals.rs @@ -0,0 +1,157 @@ +use chumsky::Parser; + +use crate::{ + parser::NoirParser, + token::{Token, TokenKind}, + ExpressionKind, +}; + +use super::primitives::token_kind; + +pub(super) fn literal() -> impl NoirParser { + token_kind(TokenKind::Literal).map(|token| match token { + Token::Int(x) => ExpressionKind::integer(x), + Token::Bool(b) => ExpressionKind::boolean(b), + Token::Str(s) => ExpressionKind::string(s), + Token::RawStr(s, hashes) => ExpressionKind::raw_string(s, hashes), + Token::FmtStr(s) => ExpressionKind::format_string(s), + unexpected => unreachable!("Non-literal {} parsed as a literal", unexpected), + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::{ + expression, expression_no_constructors, fresh_statement, term, test_helpers::*, + }; + use crate::Literal; + + fn expr_to_lit(expr: ExpressionKind) -> Literal { + match expr { + ExpressionKind::Literal(literal) => literal, + _ => unreachable!("expected a literal"), + } + } + + #[test] + fn parse_int() { + let int = parse_with(literal(), "5").unwrap(); + let hex = parse_with(literal(), "0x05").unwrap(); + + match (expr_to_lit(int), expr_to_lit(hex)) { + (Literal::Integer(int, false), Literal::Integer(hex, false)) => assert_eq!(int, hex), + _ => unreachable!(), + } + } + + #[test] + fn parse_string() { + let expr = parse_with(literal(), r#""hello""#).unwrap(); + match expr_to_lit(expr) { + Literal::Str(s) => assert_eq!(s, "hello"), + _ => unreachable!(), + }; + } + + #[test] + fn parse_bool() { + let expr_true = parse_with(literal(), "true").unwrap(); + let expr_false = parse_with(literal(), "false").unwrap(); + + match (expr_to_lit(expr_true), expr_to_lit(expr_false)) { + (Literal::Bool(t), Literal::Bool(f)) => { + assert!(t); + assert!(!f); + } + _ => unreachable!(), + }; + } + + #[test] + fn parse_unary() { + parse_all( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], + ); + parse_all_failing( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["+hello", "/hello"], + ); + } + + #[test] + fn parse_raw_string_expr() { + let cases = vec![ + Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, + Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, + // backslash + Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, + Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, + Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, + Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, + // escape sequence + Case { + source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, + expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, + errors: 0, + }, + Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, + // mismatch - errors: + Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, + Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, + // mismatch: short: + Case { source: r##" r"foo"# "##, expect: r#"r"foo""#, errors: 1 }, + Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 }, + // empty string + Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 }, + Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, + // miscellaneous + Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 }, + Case { source: r#" r\"foo\" "#, expect: "plain::r", errors: 1 }, + Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, + // missing 'r' letter + Case { source: r##" ##"foo"# "##, expect: r#""foo""#, errors: 2 }, + Case { source: r#" #"foo" "#, expect: "plain::foo", errors: 2 }, + // whitespace + Case { source: r##" r #"foo"# "##, expect: "plain::r", errors: 2 }, + Case { source: r##" r# "foo"# "##, expect: "plain::r", errors: 3 }, + Case { source: r#" r#"foo" # "#, expect: "(none)", errors: 2 }, + // after identifier + Case { source: r##" bar#"foo"# "##, expect: "plain::bar", errors: 2 }, + // nested + Case { + source: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, + expect: r###"r##"foo r#"bar"# r"baz" ### bye"##"###, + errors: 0, + }, + ]; + + check_cases_with_errors(&cases[..], expression()); + } + + #[test] + fn parse_raw_string_lit() { + let lit_cases = vec![ + Case { source: r#" r"foo" "#, expect: r#"r"foo""#, errors: 0 }, + Case { source: r##" r#"foo"# "##, expect: r##"r#"foo"#"##, errors: 0 }, + // backslash + Case { source: r#" r"\\" "#, expect: r#"r"\\""#, errors: 0 }, + Case { source: r##" r#"\"# "##, expect: r##"r#"\"#"##, errors: 0 }, + Case { source: r##" r#"\\"# "##, expect: r##"r#"\\"#"##, errors: 0 }, + Case { source: r##" r#"\\\"# "##, expect: r##"r#"\\\"#"##, errors: 0 }, + // escape sequence + Case { + source: r##" r#"\t\n\\t\\n\\\t\\\n\\\\"# "##, + expect: r##"r#"\t\n\\t\\n\\\t\\\n\\\\"#"##, + errors: 0, + }, + Case { source: r##" r#"\\\\\\\\"# "##, expect: r##"r#"\\\\\\\\"#"##, errors: 0 }, + // mismatch - errors: + Case { source: r###" r#"foo"## "###, expect: r##"r#"foo"#"##, errors: 1 }, + Case { source: r##" r##"foo"# "##, expect: "(none)", errors: 2 }, + ]; + + check_cases_with_errors(&lit_cases[..], literal()); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs new file mode 100644 index 00000000000..ab812c07dce --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -0,0 +1,78 @@ +use crate::parser::NoirParser; +use crate::{Path, PathKind}; + +use crate::token::{Keyword, Token}; + +use chumsky::prelude::*; + +use super::{ident, keyword}; + +pub(super) fn path() -> impl NoirParser { + let idents = || ident().separated_by(just(Token::DoubleColon)).at_least(1); + let make_path = |kind| move |segments, span| Path { segments, kind, span }; + + let prefix = |key| keyword(key).ignore_then(just(Token::DoubleColon)); + let path_kind = |key, kind| prefix(key).ignore_then(idents()).map_with_span(make_path(kind)); + + choice(( + path_kind(Keyword::Crate, PathKind::Crate), + path_kind(Keyword::Dep, PathKind::Dep), + idents().map_with_span(make_path(PathKind::Plain)), + )) +} + +fn empty_path() -> impl NoirParser { + let make_path = |kind| move |_, span| Path { segments: Vec::new(), kind, span }; + let path_kind = |key, kind| keyword(key).map_with_span(make_path(kind)); + + choice((path_kind(Keyword::Crate, PathKind::Crate), path_kind(Keyword::Dep, PathKind::Dep))) +} + +pub(super) fn maybe_empty_path() -> impl NoirParser { + path().or(empty_path()) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::{parse_all_failing, parse_with}; + + #[test] + fn parse_path() { + let cases = vec![ + ("std", vec!["std"]), + ("std::hash", vec!["std", "hash"]), + ("std::hash::collections", vec!["std", "hash", "collections"]), + ("dep::foo::bar", vec!["foo", "bar"]), + ("crate::std::hash", vec!["std", "hash"]), + ]; + + for (src, expected_segments) in cases { + let path: Path = parse_with(path(), src).unwrap(); + for (segment, expected) in path.segments.into_iter().zip(expected_segments) { + assert_eq!(segment.0.contents, expected); + } + } + + parse_all_failing(path(), vec!["std::", "::std", "std::hash::", "foo::1"]); + } + + #[test] + fn parse_path_kinds() { + let cases = vec![ + ("std", PathKind::Plain), + ("dep::hash::collections", PathKind::Dep), + ("crate::std::hash", PathKind::Crate), + ]; + + for (src, expected_path_kind) in cases { + let path = parse_with(path(), src).unwrap(); + assert_eq!(path.kind, expected_path_kind); + } + + parse_all_failing( + path(), + vec!["dep", "crate", "crate::std::crate", "foo::bar::crate", "foo::dep"], + ); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/primitives.rs b/compiler/noirc_frontend/src/parser/parser/primitives.rs new file mode 100644 index 00000000000..34927278038 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/primitives.rs @@ -0,0 +1,101 @@ +use chumsky::prelude::*; + +use crate::{ + parser::{labels::ParsingRuleLabel, ExprParser, NoirParser, ParserError}, + token::{Keyword, Token, TokenKind}, + ExpressionKind, Ident, UnaryOp, +}; + +use super::path; + +/// This parser always parses no input and fails +pub(super) fn nothing() -> impl NoirParser { + one_of([]).map(|_| unreachable!("parser should always error")) +} + +pub(super) fn keyword(keyword: Keyword) -> impl NoirParser { + just(Token::Keyword(keyword)) +} + +pub(super) fn token_kind(token_kind: TokenKind) -> impl NoirParser { + filter_map(move |span, found: Token| { + if found.kind() == token_kind { + Ok(found) + } else { + Err(ParserError::expected_label( + ParsingRuleLabel::TokenKind(token_kind.clone()), + found, + span, + )) + } + }) +} + +pub(super) fn ident() -> impl NoirParser { + token_kind(TokenKind::Ident).map_with_span(Ident::from_token) +} + +// Right-shift (>>) is issued as two separate > tokens by the lexer as this makes it easier +// to parse nested generic types. For normal expressions however, it means we have to manually +// parse two greater-than tokens as a single right-shift here. +pub(super) fn right_shift_operator() -> impl NoirParser { + just(Token::Greater).then(just(Token::Greater)).to(Token::ShiftRight) +} + +pub(super) fn not

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Bang).ignore_then(term_parser).map(|rhs| ExpressionKind::prefix(UnaryOp::Not, rhs)) +} + +pub(super) fn negation

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Minus) + .ignore_then(term_parser) + .map(|rhs| ExpressionKind::prefix(UnaryOp::Minus, rhs)) +} + +pub(super) fn mutable_reference

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Ampersand) + .ignore_then(keyword(Keyword::Mut)) + .ignore_then(term_parser) + .map(|rhs| ExpressionKind::prefix(UnaryOp::MutableReference, rhs)) +} + +pub(super) fn dereference

(term_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Star) + .ignore_then(term_parser) + .map(|rhs| ExpressionKind::prefix(UnaryOp::Dereference { implicitly_added: false }, rhs)) +} + +pub(super) fn variable() -> impl NoirParser { + path().map(ExpressionKind::Variable) +} + +#[cfg(test)] +mod test { + use crate::parser::parser::{ + expression, expression_no_constructors, fresh_statement, term, test_helpers::*, + }; + + #[test] + fn parse_unary() { + parse_all( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["!hello", "-hello", "--hello", "-!hello", "!-hello"], + ); + parse_all_failing( + term(expression(), expression_no_constructors(expression()), fresh_statement(), true), + vec!["+hello", "/hello"], + ); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs new file mode 100644 index 00000000000..0212f56783f --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -0,0 +1,97 @@ +use chumsky::prelude::*; +use noirc_errors::Span; + +use crate::{ + macros_api::SecondaryAttribute, + parser::{ + parser::{ + attributes::attributes, + function, parse_type, + primitives::{ident, keyword}, + }, + NoirParser, ParserError, ParserErrorReason, TopLevelStatement, + }, + token::{Attribute, Keyword, Token}, + Ident, NoirStruct, UnresolvedType, +}; + +pub(super) fn struct_definition() -> impl NoirParser { + use self::Keyword::Struct; + use Token::*; + + let fields = struct_fields() + .delimited_by(just(LeftBrace), just(RightBrace)) + .recover_with(nested_delimiters( + LeftBrace, + RightBrace, + [(LeftParen, RightParen), (LeftBracket, RightBracket)], + |_| vec![], + )) + .or(just(Semicolon).to(Vec::new())); + + attributes() + .then_ignore(keyword(Struct)) + .then(ident()) + .then(function::generics()) + .then(fields) + .validate(|(((raw_attributes, name), generics), fields), span, emit| { + let attributes = validate_struct_attributes(raw_attributes, span, emit); + TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) + }) +} + +fn struct_fields() -> impl NoirParser> { + ident() + .then_ignore(just(Token::Colon)) + .then(parse_type()) + .separated_by(just(Token::Comma)) + .allow_trailing() +} + +fn validate_struct_attributes( + attributes: Vec, + span: Span, + emit: &mut dyn FnMut(ParserError), +) -> Vec { + let mut struct_attributes = vec![]; + + for attribute in attributes { + match attribute { + Attribute::Function(..) => { + emit(ParserError::with_reason( + ParserErrorReason::NoFunctionAttributesAllowedOnStruct, + span, + )); + } + Attribute::Secondary(attr) => struct_attributes.push(attr), + } + } + + struct_attributes +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn parse_structs() { + let cases = vec![ + "struct Foo;", + "struct Foo { }", + "struct Bar { ident: Field, }", + "struct Baz { ident: Field, other: Field }", + "#[attribute] struct Baz { ident: Field, other: Field }", + ]; + parse_all(struct_definition(), cases); + + let failing = vec![ + "struct { }", + "struct Foo { bar: pub Field }", + "struct Foo { bar: pub Field }", + "#[oracle(some)] struct Foo { bar: Field }", + ]; + parse_all_failing(struct_definition(), failing); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/test_helpers.rs b/compiler/noirc_frontend/src/parser/parser/test_helpers.rs new file mode 100644 index 00000000000..6b8cb80a0a0 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/test_helpers.rs @@ -0,0 +1,122 @@ +use chumsky::primitive::just; +use chumsky::Parser; +use iter_extended::vecmap; +use noirc_errors::CustomDiagnostic; + +use crate::{ + lexer::Lexer, + parser::{force, NoirParser}, + token::Token, +}; + +pub(crate) fn parse_with(parser: P, program: &str) -> Result> +where + P: NoirParser, +{ + let (tokens, lexer_errors) = Lexer::lex(program); + if !lexer_errors.is_empty() { + return Err(vecmap(lexer_errors, Into::into)); + } + parser.then_ignore(just(Token::EOF)).parse(tokens).map_err(|errors| vecmap(errors, Into::into)) +} + +pub(crate) fn parse_recover(parser: P, program: &str) -> (Option, Vec) +where + P: NoirParser, +{ + let (tokens, lexer_errors) = Lexer::lex(program); + let (opt, errs) = parser.then_ignore(force(just(Token::EOF))).parse_recovery(tokens); + + let mut errors = vecmap(lexer_errors, Into::into); + errors.extend(errs.into_iter().map(Into::into)); + + (opt, errors) +} + +pub(crate) fn parse_all(parser: P, programs: Vec<&str>) -> Vec +where + P: NoirParser, +{ + vecmap(programs, move |program| { + let message = format!("Failed to parse:\n{program}"); + let (op_t, diagnostics) = parse_recover(&parser, program); + diagnostics.iter().for_each(|diagnostic| { + if diagnostic.is_error() { + panic!("{} with error {}", &message, diagnostic); + } + }); + op_t.expect(&message) + }) +} + +pub(crate) fn parse_all_failing(parser: P, programs: Vec<&str>) -> Vec +where + P: NoirParser, + T: std::fmt::Display, +{ + programs + .into_iter() + .flat_map(|program| match parse_with(&parser, program) { + Ok(expr) => { + unreachable!( + "Expected this input to fail:\n{}\nYet it successfully parsed as:\n{}", + program, expr + ) + } + Err(diagnostics) => { + if diagnostics.iter().all(|diagnostic: &CustomDiagnostic| diagnostic.is_warning()) { + unreachable!( + "Expected at least one error when parsing:\n{}\nYet it successfully parsed without errors:\n", + program + ) + }; + diagnostics + } + }) + .collect() +} + +#[derive(Copy, Clone)] +pub(crate) struct Case { + pub(crate) source: &'static str, + pub(crate) errors: usize, + pub(crate) expect: &'static str, +} + +pub(crate) fn check_cases_with_errors(cases: &[Case], parser: P) +where + P: NoirParser + Clone, + T: std::fmt::Display, +{ + let show_errors = |v| vecmap(&v, ToString::to_string).join("\n"); + + let results = vecmap(cases, |&case| { + let (opt, errors) = parse_recover(parser.clone(), case.source); + let actual = opt.map(|ast| ast.to_string()); + let actual = if let Some(s) = &actual { s.to_string() } else { "(none)".to_string() }; + + let result = ((errors.len(), actual.clone()), (case.errors, case.expect.to_string())); + if result.0 != result.1 { + let num_errors = errors.len(); + let shown_errors = show_errors(errors); + eprintln!( + concat!( + "\nExpected {expected_errors} error(s) and got {num_errors}:", + "\n\n{shown_errors}", + "\n\nFrom input: {src}", + "\nExpected AST: {expected_result}", + "\nActual AST: {actual}\n", + ), + expected_errors = case.errors, + num_errors = num_errors, + shown_errors = shown_errors, + src = case.source, + expected_result = case.expect, + actual = actual, + ); + } + result + }); + + assert_eq!(vecmap(&results, |t| t.0.clone()), vecmap(&results, |t| t.1.clone()),); +} diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs new file mode 100644 index 00000000000..0d72fbd5303 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -0,0 +1,217 @@ +use chumsky::prelude::*; + +use super::{ + block, expression, fresh_statement, function, function_declaration_parameters, + function_return_type, +}; + +use crate::{ + parser::{ + ignore_then_commit, parenthesized, parser::primitives::keyword, NoirParser, ParserError, + ParserErrorReason, TopLevelStatement, + }, + token::{Keyword, Token}, + Expression, FunctionVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, + UnresolvedTraitConstraint, UnresolvedType, +}; + +use super::{generic_type_args, parse_type, path, primitives::ident}; + +pub(super) fn trait_definition() -> impl NoirParser { + keyword(Keyword::Trait) + .ignore_then(ident()) + .then(function::generics()) + .then(where_clause()) + .then_ignore(just(Token::LeftBrace)) + .then(trait_body()) + .then_ignore(just(Token::RightBrace)) + .map_with_span(|(((name, generics), where_clause), items), span| { + TopLevelStatement::Trait(NoirTrait { name, generics, where_clause, span, items }) + }) +} + +fn trait_body() -> impl NoirParser> { + trait_function_declaration() + .or(trait_type_declaration()) + .or(trait_constant_declaration()) + .repeated() +} + +fn optional_default_value() -> impl NoirParser> { + ignore_then_commit(just(Token::Assign), expression()).or_not() +} + +fn trait_constant_declaration() -> impl NoirParser { + keyword(Keyword::Let) + .ignore_then(ident()) + .then_ignore(just(Token::Colon)) + .then(parse_type()) + .then(optional_default_value()) + .then_ignore(just(Token::Semicolon)) + .validate(|((name, typ), default_value), span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("Associated constants"), + span, + )); + TraitItem::Constant { name, typ, default_value } + }) +} + +/// trait_function_declaration: 'fn' ident generics '(' declaration_parameters ')' function_return_type +fn trait_function_declaration() -> impl NoirParser { + let trait_function_body_or_semicolon = + block(fresh_statement()).map(Option::from).or(just(Token::Semicolon).to(Option::None)); + + keyword(Keyword::Fn) + .ignore_then(ident()) + .then(function::generics()) + .then(parenthesized(function_declaration_parameters())) + .then(function_return_type().map(|(_, typ)| typ)) + .then(where_clause()) + .then(trait_function_body_or_semicolon) + .map(|(((((name, generics), parameters), return_type), where_clause), body)| { + TraitItem::Function { name, generics, parameters, return_type, where_clause, body } + }) +} + +/// trait_type_declaration: 'type' ident generics +fn trait_type_declaration() -> impl NoirParser { + keyword(Keyword::Type).ignore_then(ident()).then_ignore(just(Token::Semicolon)).validate( + |name, span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("Associated types"), + span, + )); + TraitItem::Type { name } + }, + ) +} + +/// Parses a trait implementation, implementing a particular trait for a type. +/// This has a similar syntax to `implementation`, but the `for type` clause is required, +/// and an optional `where` clause is also useable. +/// +/// trait_implementation: 'impl' generics ident generic_args for type '{' trait_implementation_body '}' +pub(super) fn trait_implementation() -> impl NoirParser { + keyword(Keyword::Impl) + .ignore_then(function::generics()) + .then(path()) + .then(generic_type_args(parse_type())) + .then_ignore(keyword(Keyword::For)) + .then(parse_type()) + .then(where_clause()) + .then_ignore(just(Token::LeftBrace)) + .then(trait_implementation_body()) + .then_ignore(just(Token::RightBrace)) + .map(|args| { + let ((other_args, where_clause), items) = args; + let (((impl_generics, trait_name), trait_generics), object_type) = other_args; + + TopLevelStatement::TraitImpl(NoirTraitImpl { + impl_generics, + trait_name, + trait_generics, + object_type, + items, + where_clause, + }) + }) +} + +fn trait_implementation_body() -> impl NoirParser> { + let function = function::function_definition(true).validate(|mut f, span, emit| { + if f.def().is_internal + || f.def().is_unconstrained + || f.def().is_open + || f.def().visibility != FunctionVisibility::Private + { + emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); + } + // Trait impl functions are always public + f.def_mut().visibility = FunctionVisibility::Public; + TraitImplItem::Function(f) + }); + + let alias = keyword(Keyword::Type) + .ignore_then(ident()) + .then_ignore(just(Token::Assign)) + .then(parse_type()) + .then_ignore(just(Token::Semicolon)) + .map(|(name, alias)| TraitImplItem::Type { name, alias }); + + function.or(alias).repeated() +} + +fn where_clause() -> impl NoirParser> { + struct MultiTraitConstraint { + typ: UnresolvedType, + trait_bounds: Vec, + } + + let constraints = parse_type() + .then_ignore(just(Token::Colon)) + .then(trait_bounds()) + .map(|(typ, trait_bounds)| MultiTraitConstraint { typ, trait_bounds }); + + keyword(Keyword::Where) + .ignore_then(constraints.separated_by(just(Token::Comma))) + .or_not() + .map(|option| option.unwrap_or_default()) + .map(|x: Vec| { + let mut result: Vec = Vec::new(); + for constraint in x { + for bound in constraint.trait_bounds { + result.push(UnresolvedTraitConstraint { + typ: constraint.typ.clone(), + trait_bound: bound, + }); + } + } + result + }) +} + +fn trait_bounds() -> impl NoirParser> { + trait_bound().separated_by(just(Token::Plus)).at_least(1).allow_trailing() +} + +fn trait_bound() -> impl NoirParser { + path().then(generic_type_args(parse_type())).map(|(trait_path, trait_generics)| TraitBound { + trait_path, + trait_generics, + trait_id: None, + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn parse_trait() { + parse_all( + trait_definition(), + vec![ + // Empty traits are legal in Rust and sometimes used as a way to whitelist certain types + // for a particular operation. Also known as `tag` or `marker` traits: + // https://stackoverflow.com/questions/71895489/what-is-the-purpose-of-defining-empty-impl-in-rust + "trait Empty {}", + "trait TraitWithDefaultBody { fn foo(self) {} }", + "trait TraitAcceptingMutableRef { fn foo(&mut self); }", + "trait TraitWithTypeBoundOperation { fn identity() -> Self; }", + "trait TraitWithAssociatedType { type Element; fn item(self, index: Field) -> Self::Element; }", + "trait TraitWithAssociatedConstant { let Size: Field; }", + "trait TraitWithAssociatedConstantWithDefaultValue { let Size: Field = 10; }", + "trait GenericTrait { fn elem(&mut self, index: Field) -> T; }", + "trait GenericTraitWithConstraints where T: SomeTrait { fn elem(self, index: Field) -> T; }", + "trait TraitWithMultipleGenericParams where A: SomeTrait, B: AnotherTrait { let Size: Field; fn zero() -> Self; }", + ], + ); + + parse_all_failing( + trait_definition(), + vec!["trait MissingBody", "trait WrongDelimiter { fn foo() -> u8, fn bar() -> u8 }"], + ); + } +} diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs new file mode 100644 index 00000000000..572397d6527 --- /dev/null +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -0,0 +1,172 @@ +use super::{ + expression_with_precedence, keyword, nothing, parenthesized, NoirParser, ParserError, + ParserErrorReason, Precedence, +}; +use crate::ast::{UnresolvedType, UnresolvedTypeData}; + +use crate::parser::labels::ParsingRuleLabel; +use crate::token::{Keyword, Token}; +use crate::{Recoverable, UnresolvedTypeExpression}; + +use chumsky::prelude::*; +use noirc_errors::Span; + +fn maybe_comp_time() -> impl NoirParser<()> { + keyword(Keyword::CompTime).or_not().validate(|opt, span, emit| { + if opt.is_some() { + emit(ParserError::with_reason(ParserErrorReason::ComptimeDeprecated, span)); + } + }) +} + +pub(super) fn parenthesized_type( + recursive_type_parser: impl NoirParser, +) -> impl NoirParser { + recursive_type_parser + .delimited_by(just(Token::LeftParen), just(Token::RightParen)) + .map_with_span(|typ, span| UnresolvedType { + typ: UnresolvedTypeData::Parenthesized(Box::new(typ)), + span: span.into(), + }) +} + +pub(super) fn field_type() -> impl NoirParser { + maybe_comp_time() + .then_ignore(keyword(Keyword::Field)) + .map_with_span(|_, span| UnresolvedTypeData::FieldElement.with_span(span)) +} + +pub(super) fn bool_type() -> impl NoirParser { + maybe_comp_time() + .then_ignore(keyword(Keyword::Bool)) + .map_with_span(|_, span| UnresolvedTypeData::Bool.with_span(span)) +} + +pub(super) fn string_type() -> impl NoirParser { + keyword(Keyword::String) + .ignore_then( + type_expression().delimited_by(just(Token::Less), just(Token::Greater)).or_not(), + ) + .map_with_span(|expr, span| UnresolvedTypeData::String(expr).with_span(span)) +} + +pub(super) fn format_string_type( + type_parser: impl NoirParser, +) -> impl NoirParser { + keyword(Keyword::FormatString) + .ignore_then( + type_expression() + .then_ignore(just(Token::Comma)) + .then(type_parser) + .delimited_by(just(Token::Less), just(Token::Greater)), + ) + .map_with_span(|(size, fields), span| { + UnresolvedTypeData::FormatString(size, Box::new(fields)).with_span(span) + }) +} + +pub(super) fn int_type() -> impl NoirParser { + maybe_comp_time() + .then(filter_map(|span, token: Token| match token { + Token::IntType(int_type) => Ok(int_type), + unexpected => { + Err(ParserError::expected_label(ParsingRuleLabel::IntegerType, unexpected, span)) + } + })) + .validate(|(_, token), span, emit| { + UnresolvedTypeData::from_int_token(token) + .map(|data| data.with_span(span)) + .unwrap_or_else(|err| { + emit(ParserError::with_reason(ParserErrorReason::InvalidBitSize(err.0), span)); + UnresolvedType::error(span) + }) + }) +} + +pub(super) fn array_type( + type_parser: impl NoirParser, +) -> impl NoirParser { + just(Token::LeftBracket) + .ignore_then(type_parser) + .then(just(Token::Semicolon).ignore_then(type_expression()).or_not()) + .then_ignore(just(Token::RightBracket)) + .map_with_span(|(element_type, size), span| { + UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) + }) +} + +pub(super) fn type_expression() -> impl NoirParser { + recursive(|expr| { + expression_with_precedence( + Precedence::lowest_type_precedence(), + expr, + nothing(), + nothing(), + true, + false, + ) + }) + .labelled(ParsingRuleLabel::TypeExpression) + .try_map(UnresolvedTypeExpression::from_expr) +} + +pub(super) fn tuple_type(type_parser: T) -> impl NoirParser +where + T: NoirParser, +{ + let fields = type_parser.separated_by(just(Token::Comma)).allow_trailing(); + parenthesized(fields).map_with_span(|fields, span| { + if fields.is_empty() { + UnresolvedTypeData::Unit.with_span(span) + } else { + UnresolvedTypeData::Tuple(fields).with_span(span) + } + }) +} + +pub(super) fn function_type(type_parser: T) -> impl NoirParser +where + T: NoirParser, +{ + let args = parenthesized(type_parser.clone().separated_by(just(Token::Comma)).allow_trailing()); + + let env = just(Token::LeftBracket) + .ignore_then(type_parser.clone()) + .then_ignore(just(Token::RightBracket)) + .or_not() + .map_with_span(|t, span| { + t.unwrap_or_else(|| UnresolvedTypeData::Unit.with_span(Span::empty(span.end()))) + }); + + keyword(Keyword::Fn) + .ignore_then(env) + .then(args) + .then_ignore(just(Token::Arrow)) + .then(type_parser) + .map_with_span(|((env, args), ret), span| { + UnresolvedTypeData::Function(args, Box::new(ret), Box::new(env)).with_span(span) + }) +} + +pub(super) fn mutable_reference_type(type_parser: T) -> impl NoirParser +where + T: NoirParser, +{ + just(Token::Ampersand) + .ignore_then(keyword(Keyword::Mut)) + .ignore_then(type_parser) + .map_with_span(|element, span| { + UnresolvedTypeData::MutableReference(Box::new(element)).with_span(span) + }) +} + +#[cfg(test)] +mod test { + use super::*; + use crate::parser::parser::test_helpers::*; + + #[test] + fn parse_type_expression() { + parse_all(type_expression(), vec!["(123)", "123", "(1 + 1)", "(1 + (1))"]); + } +} From 261531d096f43844eb12a245a3c1d4ece0a88692 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 28 Feb 2024 10:23:31 -0600 Subject: [PATCH 024/416] chore: Document BoundedVec (#4430) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4348 ## Summary\* Documents `BoundedVec` ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Tom French --- .../standard_library/containers/boundedvec.md | 210 ++++++++++++++++++ .../noir_test_success/bounded_vec/src/main.nr | 154 ++++++++++++- 2 files changed, 357 insertions(+), 7 deletions(-) create mode 100644 docs/docs/noir/standard_library/containers/boundedvec.md diff --git a/docs/docs/noir/standard_library/containers/boundedvec.md b/docs/docs/noir/standard_library/containers/boundedvec.md new file mode 100644 index 00000000000..cd0f725f870 --- /dev/null +++ b/docs/docs/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,210 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +#include_code new_example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +#include_code get_unchecked_example test_programs/noir_test_success/bounded_vec/src/main.nr rust + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +#include_code bounded-vec-push-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +#include_code bounded-vec-pop-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +#include_code bounded-vec-len-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +#include_code bounded-vec-max-len-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +#include_code bounded-vec-storage-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +#include_code bounded-vec-extend-from-array-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +#include_code bounded-vec-extend-from-bounded-vec-example test_programs/noir_test_success/bounded_vec/src/main.nr rust + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +#include_code bounded-vec-any-example test_programs/noir_test_success/bounded_vec/src/main.nr rust diff --git a/test_programs/noir_test_success/bounded_vec/src/main.nr b/test_programs/noir_test_success/bounded_vec/src/main.nr index 0e2c89c9064..22ec291f9d6 100644 --- a/test_programs/noir_test_success/bounded_vec/src/main.nr +++ b/test_programs/noir_test_success/bounded_vec/src/main.nr @@ -1,3 +1,31 @@ +#[test] +fn test_vec_new_foo() { + foo(); +} + +#[test(should_fail)] +fn test_vec_new_bad() { + bad(); +} + +// docs:start:new_example +fn foo() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of foo's return value + v2 +} + +fn bad() { + let mut v3 = BoundedVec::new(); + + // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. + v3.push(5); +} +// docs:end:new_example + #[test] fn test_vec_push_pop() { let mut vec: BoundedVec = BoundedVec::new(); @@ -15,13 +43,123 @@ fn test_vec_push_pop() { assert(vec.get(1) == 4); } +#[test] +fn test_vec_get_unchecked() { + let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([1, 2, 3, 4]); + let sum = sum_of_first_three(vec); + assert_eq(sum, 6); +} + +// docs:start:get_unchecked_example +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +// docs:end:get_unchecked_example + +#[test(should_fail_with = "push out of bounds")] +fn push_docs_example() { + // docs:start:bounded-vec-push-example + let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); + // docs:end:bounded-vec-push-example +} + +#[test] +fn pop_docs_example() { + // docs:start:bounded-vec-pop-example + let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); + // docs:end:bounded-vec-pop-example +} + +#[test] +fn len_docs_example() { + // docs:start:bounded-vec-len-example + let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); + // docs:end:bounded-vec-len-example +} + +#[test] +fn max_len_docs_example() { + // docs:start:bounded-vec-max-len-example + let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); + // docs:end:bounded-vec-max-len-example +} + +#[test] +fn storage_docs_example() { + // docs:start:bounded-vec-storage-example + let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); + // docs:end:bounded-vec-storage-example +} + #[test] fn test_vec_extend_from_array() { + // docs:start:bounded-vec-extend-from-array-example let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); + assert(vec.len == 2); assert(vec.get(0) == 2); assert(vec.get(1) == 4); + // docs:end:bounded-vec-extend-from-array-example +} + +#[test] +fn test_vec_extend_from_bounded_vec() { + // docs:start:bounded-vec-extend-from-bounded-vec-example + let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); + // docs:end:bounded-vec-extend-from-bounded-vec-example } #[test(should_fail_with="extend_from_array out of bounds")] @@ -88,12 +226,13 @@ fn test_vec_extend_from_bounded_vec_twice_out_of_bound() { #[test] fn test_vec_any() { - let mut vec: BoundedVec = BoundedVec::new(); - vec.extend_from_array([2, 4, 6]); - assert(vec.any(|v| v == 2) == true); - assert(vec.any(|v| v == 4) == true); - assert(vec.any(|v| v == 6) == true); - assert(vec.any(|v| v == 3) == false); + // docs:start:bounded-vec-any-example + let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); + // docs:end:bounded-vec-any-example } #[test] @@ -101,5 +240,6 @@ fn test_vec_any_not_default() { let default_value = 0; let mut vec: BoundedVec = BoundedVec::new(); vec.extend_from_array([2, 4]); - assert(vec.any(|v| v == default_value) == false); + assert(!vec.any(|v| v == default_value)); } + From f2105fed292fc8d3ee6c3722e9dedb8b3b16453d Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:08:24 +0000 Subject: [PATCH 025/416] chore: create parser specifically for function visibility (#4425) # Description ## Problem\* Resolves ## Summary\* This PR creates a parser for `'pub(crate)'|'pub'|''` which returns the `FunctionVisibility`. This allows parsing visibility more reusable for when we add visibility for imports, structs, etc. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/parser/parser/function.rs | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 0d34c719061..42ee484bfc9 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -38,13 +38,7 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser impl NoirParser { + let is_pub_crate = (keyword(Keyword::Pub) + .then_ignore(just(Token::LeftParen)) + .then_ignore(keyword(Keyword::Crate)) + .then_ignore(just(Token::RightParen))) + .map(|_| FunctionVisibility::PublicCrate); + + let is_pub = keyword(Keyword::Pub).map(|_| FunctionVisibility::Public); + + let is_private = empty().map(|_| FunctionVisibility::Private); + + choice((is_pub_crate, is_pub, is_private)) +} + +/// function_modifiers: 'unconstrained'? (visibility)? 'open'? 'internal'? /// -/// returns (is_unconstrained, is_pub_crate, is_open, is_internal, is_pub) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, bool, bool, bool, bool)> { +/// returns (is_unconstrained, visibility, is_open, is_internal) for whether each keyword was present +fn function_modifiers() -> impl NoirParser<(bool, FunctionVisibility, bool, bool)> { keyword(Keyword::Unconstrained) .or_not() - .then(is_pub_crate()) - .then(keyword(Keyword::Pub).or_not()) + .then(visibility_modifier()) .then(keyword(Keyword::Open).or_not()) .then(keyword(Keyword::Internal).or_not()) - .map(|((((unconstrained, pub_crate), public), open), internal)| { - ( - unconstrained.is_some(), - pub_crate, - open.is_some(), - internal.is_some(), - public.is_some(), - ) + .map(|(((unconstrained, visibility), open), internal)| { + (unconstrained.is_some(), visibility, open.is_some(), internal.is_some()) }) } -fn is_pub_crate() -> impl NoirParser { - (keyword(Keyword::Pub) - .then_ignore(just(Token::LeftParen)) - .then_ignore(keyword(Keyword::Crate)) - .then_ignore(just(Token::RightParen))) - .or_not() - .map(|a| a.is_some()) -} - /// non_empty_ident_list: ident ',' non_empty_ident_list /// | ident /// From db9ea8481197286e38b89269aa6e8f14acf1ce93 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 28 Feb 2024 20:24:19 +0000 Subject: [PATCH 026/416] =?UTF-8?q?chore:=20only=20ignore=20Nargo.toml=20i?= =?UTF-8?q?n=20`test=5Fprograms`=20directory=20and=20not=20su=E2=80=A6=20(?= =?UTF-8?q?#4451)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …bdirectories # Description ## Problem\* Resolves ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- test_programs/.gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_programs/.gitignore b/test_programs/.gitignore index e98a2fb38b6..6da0100814a 100644 --- a/test_programs/.gitignore +++ b/test_programs/.gitignore @@ -1,3 +1,3 @@ acir_artifacts execution_success/**/crs -Nargo.toml +./Nargo.toml From cb1ceee58b11b0ce6f8845361af3418d13c506bd Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 29 Feb 2024 11:35:29 +0000 Subject: [PATCH 027/416] fix: build noir_codegen when publishing (#4448) # Description ## Problem\* Resolves #4446 ## Summary\* We're currently not building `noir_codegen` before we publish so I've updated the build script. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f460b3db711..4987602c709 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "build:types": "yarn workspace @noir-lang/types run build", "build:backend_barretenberg": "yarn workspace @noir-lang/backend_barretenberg run build", "build:noir_js": "yarn workspace @noir-lang/noir_js run build", - "build:js:only": "yarn build:types && yarn build:backend_barretenberg && yarn build:noir_js", + "build:js:only": "yarn workspaces foreach -vtp --from \"{@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js,@noir-lang/noir_codegen}\" run build", "prepare:publish": "yarn clean && yarn install:from:nix && yarn build:js:only", "nightly:version": "yarn workspaces foreach run nightly:version", "publish:all": "yarn install && yarn workspaces foreach run publish" From ebaf05ab10834dd10e04c7ea5130f96c6cdf98ed Mon Sep 17 00:00:00 2001 From: Jordan Ellis Coppard Date: Thu, 29 Feb 2024 21:50:58 +0800 Subject: [PATCH 028/416] fix(flake): stop flake.nix removing ignored-tests.txt (#4455) # Description Fixes a problem with `flake.nix` that prevented building packages via said flake. ## Problem\* Currently `flake.nix` does not keep files ending with `.txt` when building packages, this means https://github.com/noir-lang/noir/blob/master/tooling/debugger/build.rs#L44 fails and so does the build.

Expand for full error trace. ```txt @nix { "action": "setPhase", "phase": "unpackPhase" } unpacking sources unpacking source archive /nix/store/s9q28qnr32r9cd62pm3vapvq87ipdpyq-source source root is source @nix { "action": "setPhase", "phase": "patchPhase" } patching sources Executing configureCargoCommonVars copying cargo artifacts from /nix/store/21iwpq5w9854ga1mmb8g4r4r2p20202b-nargo-deps-0.24.0/target to target @nix { "action": "setPhase", "phase": "updateAutotoolsGnuConfigScriptsPhase" } updateAutotoolsGnuConfigScriptsPhase @nix { "action": "setPhase", "phase": "configurePhase" } configuring will append /private/tmp/nix-build-nargo-0.24.0.drv-0/source/.cargo-home/config.toml with contents of /nix/store/rl1r6p506a4hk57049rsql0cnd9f6236-vendor-cargo-deps/config.toml default configurePhase, nothing to do @nix { "action": "setPhase", "phase": "buildPhase" } building ++ command cargo --version cargo 1.73.0 (9c4383fb5 2023-08-26) ++ command cargo build --release --message-format json-render-diagnostics Compiling acir_field v0.40.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/acvm-repo/acir_field) Compiling iter-extended v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/compiler/utils/iter-extended) Compiling fm v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/compiler/fm) Compiling arena v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/compiler/utils/arena) Compiling noirc_driver v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/compiler/noirc_driver) Compiling nargo v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/tooling/nargo) Compiling bb_abstraction_leaks v0.11.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/tooling/bb_abstraction_leaks) Compiling nargo_fmt v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/tooling/nargo_fmt) Compiling bn254_blackbox_solver v0.39.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/acvm-repo/bn254_blackbox_solver) Compiling noir_debugger v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/tooling/debugger) Compiling nargo_cli v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/tooling/nargo_cli) Compiling brillig v0.40.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/acvm-repo/brillig) error: failed to run custom build command for `noir_debugger v0.24.0 (/private/tmp/nix-build-nargo-0.24.0.drv-0/source/tooling/debugger)` note: To improve backtraces for build dependencies, set the CARGO_PROFILE_RELEASE_BUILD_OVERRIDE_DEBUG=true environment variable to enable debug information generation. Caused by: process didn't exit successfully: `/private/tmp/nix-build-nargo-0.24.0.drv-0/source/target/release/build/noir_debugger-64219c61a62b2d11/build-script-build` (exit status: 101) --- stdout cargo:rerun-if-changed=tests cargo:rerun-if-changed=ignored-tests.txt cargo:rerun-if-changed=/private/tmp/nix-build-nargo-0.24.0.drv-0/source/test_programs --- stderr thread 'main' panicked at tooling/debugger/build.rs:44:74: called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" } stack backtrace: 0: rust_begin_unwind at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/std/src/panicking.rs:595:5 1: core::panicking::panic_fmt at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/panicking.rs:67:14 2: core::result::unwrap_failed at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/result.rs:1652:5 3: core::result::Result::unwrap 4: build_script_build::generate_debugger_tests 5: build_script_build::main 6: core::ops::function::FnOnce::call_once note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace. warning: build failed, waiting for other jobs to finish.. ```
Expand for the (probably poorly written) _consuming_ `flake.nix` If you use the patched version `noir.url` (this PR) the build now works. ```nix { description = "Tikan -- Fog of War Chess"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; devshell.url = "github:numtide/devshell"; noir.url = "github:noir-lang/noir/db9ea8481197286e38b89269aa6e8f14acf1ce93"; # noir.url = "github:tsujp/noir/0cf043d81e2da7c4b23c4b18e0c3a944dd7ea017"; }; outputs = inputs@{ self, nixpkgs, flake-parts, ... }: flake-parts.lib.mkFlake { inherit inputs; } { imports = [ inputs.devshell.flakeModule ]; systems = [ "aarch64-linux" "aarch64-darwin" "x86_64-linux" "x86_64-darwin" ]; perSystem = { pkgs, ... }: rec { packages.noir = inputs.noir.packages; devshells.default = { packages = with pkgs; [ bun ] ++ [ packages.noir.${system}.nargo ]; }; }; }; } ```
## Documentation\* Check one: - [x] No documentation needed. - ~[ ] Documentation included in this PR.~ - ~[ ] **[Exceptional Case]** Documentation to be submitted in a separate PR.~ # PR Checklist\* - [x] I have tested the changes locally. - ~[ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.~ --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 4c5db8bfaae..5125dad06be 100644 --- a/flake.nix +++ b/flake.nix @@ -81,7 +81,7 @@ # Custom filter with various file extensions that we rely upon to build packages # Currently: `.nr`, `.sol`, `.sh`, `.json`, `.md` and `.wasm` filter = path: type: - (builtins.match ".*\.(nr|sol|sh|json|md|wasm)$" path != null) || (craneLib.filterCargoSources path type); + (builtins.match ".*\.(nr|sol|sh|json|md|wasm|txt)$" path != null) || (craneLib.filterCargoSources path type); }; # TODO(#1198): It'd be nice to include these flags when running `cargo clippy` in a devShell. From 21fc4b85763dccae6dce0a46a318718c3c913471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 29 Feb 2024 17:42:28 +0100 Subject: [PATCH 029/416] feat: Add overflow and underflow checks for unsigned integers in brillig (#4445) # Description ## Problem\* When overflow checks were added they were only added for ACIR. This adds add, sub and multiply overflow checks for unsigned operations in brillig. ## Summary\* ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_block.rs | 111 +++++++++++++++--- .../noirc_evaluator/src/brillig/brillig_ir.rs | 30 +++-- .../brillig_fns_as_values/Prover.toml | 2 +- .../brillig_fns_as_values/src/main.nr | 4 +- .../brillig_wrapping/Nargo.toml | 6 + .../brillig_wrapping/Prover.toml | 2 + .../brillig_wrapping/src/main.nr | 8 ++ .../brillig_overflow_checks/Nargo.toml | 5 + .../brillig_overflow_checks/Prover.toml | 0 .../brillig_overflow_checks/src/main.nr | 23 ++++ 10 files changed, 158 insertions(+), 33 deletions(-) create mode 100644 test_programs/execution_success/brillig_wrapping/Nargo.toml create mode 100644 test_programs/execution_success/brillig_wrapping/Prover.toml create mode 100644 test_programs/execution_success/brillig_wrapping/src/main.nr create mode 100644 test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml create mode 100644 test_programs/noir_test_success/brillig_overflow_checks/Prover.toml create mode 100644 test_programs/noir_test_success/brillig_overflow_checks/src/main.nr diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index f01f60252f6..a3ee3e7ca7e 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,9 +1,7 @@ use crate::brillig::brillig_ir::brillig_variable::{ type_to_heap_value_type, BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }; -use crate::brillig::brillig_ir::{ - BrilligBinaryOp, BrilligContext, BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, -}; +use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::instruction::ConstrainError; use crate::ssa::ir::{ @@ -1197,7 +1195,7 @@ impl<'block> BrilligBlock<'block> { let left = self.convert_ssa_single_addr_value(binary.lhs, dfg); let right = self.convert_ssa_single_addr_value(binary.rhs, dfg); - let brillig_binary_op = + let (brillig_binary_op, is_signed) = convert_ssa_binary_op_to_brillig_binary_op(binary.operator, &binary_type); self.brillig_context.binary_instruction( @@ -1206,6 +1204,93 @@ impl<'block> BrilligBlock<'block> { result_variable.address, brillig_binary_op, ); + + self.add_overflow_check(brillig_binary_op, left, right, result_variable, is_signed); + } + + fn add_overflow_check( + &mut self, + binary_operation: BrilligBinaryOp, + left: SingleAddrVariable, + right: SingleAddrVariable, + result: SingleAddrVariable, + is_signed: bool, + ) { + let (op, bit_size) = if let BrilligBinaryOp::Integer { op, bit_size } = binary_operation { + (op, bit_size) + } else { + return; + }; + + match (op, is_signed) { + (BinaryIntOp::Add, false) => { + let condition = self.brillig_context.allocate_register(); + // Check that lhs <= result + self.brillig_context.binary_instruction( + left.address, + result.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::LessThanEquals, bit_size }, + ); + self.brillig_context.constrain_instruction( + condition, + Some("attempt to add with overflow".to_string()), + ); + self.brillig_context.deallocate_register(condition); + } + (BinaryIntOp::Sub, false) => { + let condition = self.brillig_context.allocate_register(); + // Check that rhs <= lhs + self.brillig_context.binary_instruction( + right.address, + left.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::LessThanEquals, bit_size }, + ); + self.brillig_context.constrain_instruction( + condition, + Some("attempt to subtract with overflow".to_string()), + ); + self.brillig_context.deallocate_register(condition); + } + (BinaryIntOp::Mul, false) => { + // Multiplication overflow is only possible for bit sizes > 1 + if bit_size > 1 { + let is_right_zero = self.brillig_context.allocate_register(); + let zero = self.brillig_context.make_constant(0_usize.into(), bit_size); + self.brillig_context.binary_instruction( + zero, + right.address, + is_right_zero, + BrilligBinaryOp::Integer { op: BinaryIntOp::Equals, bit_size }, + ); + self.brillig_context.if_not_instruction(is_right_zero, |ctx| { + let condition = ctx.allocate_register(); + // Check that result / rhs == lhs + ctx.binary_instruction( + result.address, + right.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::UnsignedDiv, bit_size }, + ); + ctx.binary_instruction( + condition, + left.address, + condition, + BrilligBinaryOp::Integer { op: BinaryIntOp::Equals, bit_size }, + ); + ctx.constrain_instruction( + condition, + Some("attempt to multiply with overflow".to_string()), + ); + ctx.deallocate_register(condition); + }); + self.brillig_context.deallocate_register(is_right_zero); + self.brillig_context.deallocate_register(zero); + } + } + _ => {} + } } /// Converts an SSA `ValueId` into a `RegisterOrMemory`. Initializes if necessary. @@ -1403,8 +1488,6 @@ impl<'block> BrilligBlock<'block> { } /// Returns the type of the operation considering the types of the operands -/// TODO: SSA issues binary operations between fields and integers. -/// This probably should be explicitly casted in SSA to avoid having to coerce at this level. pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type { match (lhs_type, rhs_type) { (_, Type::Function) | (Type::Function, _) => { @@ -1419,10 +1502,6 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type (_, Type::Slice(..)) | (Type::Slice(..), _) => { unreachable!("Arrays are invalid in binary operations") } - // If either side is a Field constant then, we coerce into the type - // of the other operand - (Type::Numeric(NumericType::NativeField), typ) - | (typ, Type::Numeric(NumericType::NativeField)) => typ.clone(), // If both sides are numeric type, then we expect their types to be // the same. (Type::Numeric(lhs_type), Type::Numeric(rhs_type)) => { @@ -1441,7 +1520,7 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( ssa_op: BinaryOp, typ: &Type, -) -> BrilligBinaryOp { +) -> (BrilligBinaryOp, bool) { // First get the bit size and whether its a signed integer, if it is a numeric type // if it is not,then we return None, indicating that // it is a Field. @@ -1461,10 +1540,6 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( BinaryOp::Mul => BrilligBinaryOp::Field { op: BinaryFieldOp::Mul }, BinaryOp::Div => BrilligBinaryOp::Field { op: BinaryFieldOp::Div }, BinaryOp::Eq => BrilligBinaryOp::Field { op: BinaryFieldOp::Equals }, - BinaryOp::Lt => BrilligBinaryOp::Integer { - op: BinaryIntOp::LessThan, - bit_size: BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE, - }, _ => unreachable!( "Field type cannot be used with {op}. This should have been caught by the frontend" ), @@ -1500,7 +1575,9 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( // If bit size is available then it is a binary integer operation match bit_size_signedness { - Some((bit_size, is_signed)) => binary_op_to_int_op(ssa_op, *bit_size, is_signed), - None => binary_op_to_field_op(ssa_op), + Some((bit_size, is_signed)) => { + (binary_op_to_int_op(ssa_op, *bit_size, is_signed), is_signed) + } + None => (binary_op_to_field_op(ssa_op), false), } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 90608974f98..f1a8f24ed03 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -29,17 +29,6 @@ use acvm::{ use debug_show::DebugShow; use num_bigint::BigUint; -/// Integer arithmetic in Brillig is limited to 127 bit -/// integers. -/// -/// We could lift this in the future and have Brillig -/// do big integer arithmetic when it exceeds the field size -/// or we could have users re-implement big integer arithmetic -/// in Brillig. -/// Since constrained functions do not have this property, it -/// would mean that unconstrained functions will differ from -/// constrained functions in terms of syntax compatibility. -pub(crate) const BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE: u32 = 127; /// The Brillig VM does not apply a limit to the memory address space, /// As a convention, we take use 64 bits. This means that we assume that /// memory has 2^64 memory slots. @@ -356,6 +345,21 @@ impl BrilligContext { self.enter_section(end_section); } + /// This instruction issues a branch that jumps over the code generated by the given function if the condition is truthy + pub(crate) fn if_not_instruction( + &mut self, + condition: MemoryAddress, + f: impl FnOnce(&mut BrilligContext), + ) { + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, end_label.clone()); + + f(self); + + self.enter_section(end_section); + } + /// Adds a label to the next opcode pub(crate) fn enter_context(&mut self, label: T) { self.debug_show.enter_context(label.to_string()); @@ -533,7 +537,7 @@ impl BrilligContext { result: MemoryAddress, operation: BrilligBinaryOp, ) { - self.debug_show.binary_instruction(lhs, rhs, result, operation.clone()); + self.debug_show.binary_instruction(lhs, rhs, result, operation); match operation { BrilligBinaryOp::Field { op } => { let opcode = BrilligOpcode::BinaryFieldOp { op, destination: result, lhs, rhs }; @@ -1082,7 +1086,7 @@ impl BrilligContext { } /// Type to encapsulate the binary operation types in Brillig -#[derive(Clone)] +#[derive(Clone, Copy)] pub(crate) enum BrilligBinaryOp { Field { op: BinaryFieldOp }, Integer { op: BinaryIntOp, bit_size: u32 }, diff --git a/test_programs/execution_success/brillig_fns_as_values/Prover.toml b/test_programs/execution_success/brillig_fns_as_values/Prover.toml index 11497a473bc..4dd6b405159 100644 --- a/test_programs/execution_success/brillig_fns_as_values/Prover.toml +++ b/test_programs/execution_success/brillig_fns_as_values/Prover.toml @@ -1 +1 @@ -x = "0" +x = "1" diff --git a/test_programs/execution_success/brillig_fns_as_values/src/main.nr b/test_programs/execution_success/brillig_fns_as_values/src/main.nr index ea3148915b8..9248bff2f4c 100644 --- a/test_programs/execution_success/brillig_fns_as_values/src/main.nr +++ b/test_programs/execution_success/brillig_fns_as_values/src/main.nr @@ -7,9 +7,9 @@ struct MyStruct { fn main(x: u32) { assert(wrapper(increment, x) == x + 1); assert(wrapper(increment_acir, x) == x + 1); - assert(wrapper(decrement, x) == std::wrapping_sub(x, 1)); + assert(wrapper(decrement, x) == x - 1); assert(wrapper_with_struct(MyStruct { operation: increment }, x) == x + 1); - assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == std::wrapping_sub(x, 1)); + assert(wrapper_with_struct(MyStruct { operation: decrement }, x) == x - 1); // https://github.com/noir-lang/noir/issues/1975 assert(increment(x) == x + 1); } diff --git a/test_programs/execution_success/brillig_wrapping/Nargo.toml b/test_programs/execution_success/brillig_wrapping/Nargo.toml new file mode 100644 index 00000000000..a52246ba908 --- /dev/null +++ b/test_programs/execution_success/brillig_wrapping/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "brillig_wrapping" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/brillig_wrapping/Prover.toml b/test_programs/execution_success/brillig_wrapping/Prover.toml new file mode 100644 index 00000000000..346fd2764a7 --- /dev/null +++ b/test_programs/execution_success/brillig_wrapping/Prover.toml @@ -0,0 +1,2 @@ +x = 0 +y = 255 diff --git a/test_programs/execution_success/brillig_wrapping/src/main.nr b/test_programs/execution_success/brillig_wrapping/src/main.nr new file mode 100644 index 00000000000..4153a466057 --- /dev/null +++ b/test_programs/execution_success/brillig_wrapping/src/main.nr @@ -0,0 +1,8 @@ +use dep::std; + +unconstrained fn main(x: u8, y: u8) { + assert(std::wrapping_sub(x, 1) == y); + assert(std::wrapping_add(y, 1) == x); + assert(std::wrapping_mul(y, y) == 1); +} + diff --git a/test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml b/test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml new file mode 100644 index 00000000000..b2d47d258ed --- /dev/null +++ b/test_programs/noir_test_success/brillig_overflow_checks/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "brillig_overflow_checks" +type = "bin" +authors = [""] +[dependencies] diff --git a/test_programs/noir_test_success/brillig_overflow_checks/Prover.toml b/test_programs/noir_test_success/brillig_overflow_checks/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr b/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr new file mode 100644 index 00000000000..5d73ef96d49 --- /dev/null +++ b/test_programs/noir_test_success/brillig_overflow_checks/src/main.nr @@ -0,0 +1,23 @@ +use dep::std::field::bn254::{TWO_POW_128, assert_gt}; + +#[test(should_fail_with = "attempt to add with overflow")] +unconstrained fn test_overflow_add() { + let a: u8 = 255; + let b: u8 = 1; + assert_eq(a + b, 0); +} + +#[test(should_fail_with = "attempt to subtract with overflow")] +unconstrained fn test_overflow_sub() { + let a: u8 = 0; + let b: u8 = 1; + assert_eq(a - b, 255); +} + +#[test(should_fail_with = "attempt to multiply with overflow")] +unconstrained fn test_overflow_mul() { + let a: u8 = 128; + let b: u8 = 2; + assert_eq(a * b, 0); +} + From ac60ef5e12fcfb907fbdcff709d7cbad05f2b939 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 1 Mar 2024 04:18:23 -0600 Subject: [PATCH 030/416] fix: Variables from trait constraints being permanently bound over when used within a trait impl (#4450) # Description ## Problem\* Resolves #4436 ## Summary\* This issue stemmed from calling another trait method within an impl for the trait. The trait method required a trait constraint of the same trait to be valid and was using the trait with its original type variables. These would later be bound over when an impl was searched for for this trait. I've added a somewhat hacky solution of avoiding inserting `t = t` bindings from the trait constraint when checking identifiers. Avoiding inserting this binding means `t` in this case will later be instantiated to a fresh type variable later on in the `instantiate_with_bindings` call. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_frontend/src/hir/type_check/expr.rs | 5 ++- compiler/noirc_frontend/src/hir_def/types.rs | 2 +- .../regression_4436/Nargo.toml | 5 +++ .../regression_4436/src/main.nr | 31 +++++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 test_programs/execution_success/regression_4436/Nargo.toml create mode 100644 test_programs/execution_success/regression_4436/src/main.nr diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index b78f07c88f2..7b854e58fca 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -326,7 +326,10 @@ impl<'interner> TypeChecker<'interner> { assert_eq!(the_trait.generics.len(), constraint.trait_generics.len()); for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics) { - bindings.insert(param.id(), (param.clone(), arg.clone())); + // Avoid binding t = t + if !arg.occurs(param.id()) { + bindings.insert(param.id(), (param.clone(), arg.clone())); + } } } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index e105da1ccf0..b70aa43701c 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1558,7 +1558,7 @@ impl Type { } /// True if the given TypeVariableId is free anywhere within self - fn occurs(&self, target_id: TypeVariableId) -> bool { + pub fn occurs(&self, target_id: TypeVariableId) -> bool { match self { Type::Array(len, elem) => len.occurs(target_id) || elem.occurs(target_id), Type::String(len) => len.occurs(target_id), diff --git a/test_programs/execution_success/regression_4436/Nargo.toml b/test_programs/execution_success/regression_4436/Nargo.toml new file mode 100644 index 00000000000..0904d858596 --- /dev/null +++ b/test_programs/execution_success/regression_4436/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "regression_4436" +type = "bin" +authors = [""] +compiler_version = ">=0.22.0" diff --git a/test_programs/execution_success/regression_4436/src/main.nr b/test_programs/execution_success/regression_4436/src/main.nr new file mode 100644 index 00000000000..834ea3250cc --- /dev/null +++ b/test_programs/execution_success/regression_4436/src/main.nr @@ -0,0 +1,31 @@ +trait LibTrait { + fn broadcast(); + fn get_constant() -> Field; +} + +global STRUCT_A_LEN: Field = 3; +global STRUCT_B_LEN: Field = 5; + +struct StructA; +struct StructB; + +impl LibTrait for StructA { + fn broadcast() { + Self::get_constant(); + } + + fn get_constant() -> Field { + 1 + } +} +impl LibTrait for StructB { + fn broadcast() { + Self::get_constant(); + } + + fn get_constant() -> Field { + 1 + } +} + +fn main() {} From cb4c1c5264b95d01f69d99f916ced71ad9cdc9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Fri, 1 Mar 2024 11:51:00 +0100 Subject: [PATCH 031/416] feat: skip redundant range checks in brillig (#4460) # Description ## Problem\* Avoid codegening overhead for no-op range checks generated by https://github.com/noir-lang/noir/issues/4456 ## Summary\* ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_block.rs | 59 ++++++++++--------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index a3ee3e7ca7e..c04d8475f08 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -624,37 +624,40 @@ impl<'block> BrilligBlock<'block> { } Instruction::RangeCheck { value, max_bit_size, assert_message } => { let value = self.convert_ssa_single_addr_value(*value, dfg); - // Cast original value to field - let left = SingleAddrVariable { - address: self.brillig_context.allocate_register(), - bit_size: FieldElement::max_num_bits(), - }; - self.convert_cast(left, value); + // SSA generates redundant range checks. A range check with a max bit size >= value.bit_size will always pass. + if value.bit_size > *max_bit_size { + // Cast original value to field + let left = SingleAddrVariable { + address: self.brillig_context.allocate_register(), + bit_size: FieldElement::max_num_bits(), + }; + self.convert_cast(left, value); - // Create a field constant with the max - let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); - let right = self.brillig_context.make_constant( - FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), - FieldElement::max_num_bits(), - ); + // Create a field constant with the max + let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); + let right = self.brillig_context.make_constant( + FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), + FieldElement::max_num_bits(), + ); - // Check if lte max - let brillig_binary_op = BrilligBinaryOp::Integer { - op: BinaryIntOp::LessThanEquals, - bit_size: FieldElement::max_num_bits(), - }; - let condition = self.brillig_context.allocate_register(); - self.brillig_context.binary_instruction( - left.address, - right, - condition, - brillig_binary_op, - ); + // Check if lte max + let brillig_binary_op = BrilligBinaryOp::Integer { + op: BinaryIntOp::LessThanEquals, + bit_size: FieldElement::max_num_bits(), + }; + let condition = self.brillig_context.allocate_register(); + self.brillig_context.binary_instruction( + left.address, + right, + condition, + brillig_binary_op, + ); - self.brillig_context.constrain_instruction(condition, assert_message.clone()); - self.brillig_context.deallocate_register(condition); - self.brillig_context.deallocate_register(left.address); - self.brillig_context.deallocate_register(right); + self.brillig_context.constrain_instruction(condition, assert_message.clone()); + self.brillig_context.deallocate_register(condition); + self.brillig_context.deallocate_register(left.address); + self.brillig_context.deallocate_register(right); + } } Instruction::IncrementRc { value } => { let rc_register = match self.convert_ssa_value(*value, dfg) { From aad0da024c69663f42e6913e674682d5864b26ae Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 4 Mar 2024 10:08:39 +0000 Subject: [PATCH 032/416] feat: backpropagate constants in ACIR during optimization (#3926) # Description ## Problem\* Resolves ## Summary\* This is a mildy bruteforce-y optimisation method where we just literally attempt to execute the circuit backwards. Any witnesses which we can determine from this can just be written into the circuit directly. A lot of the complexity here comes from the fact that memory opcodes, etc. require witnesses to be unassigned at the point at which the opcode is encountered so we need to "forget" certain witnesses so that we don't optimise them away. Draft as I'm just pushing this up to track effects. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: kevaundray Co-authored-by: Maxim Vezenov --- .../opcodes/black_box_function_call.rs | 6 +- .../optimizers/constant_backpropagation.rs | 331 ++++++++++++++++++ .../acvm/src/compiler/optimizers/general.rs | 20 +- acvm-repo/acvm/src/compiler/optimizers/mod.rs | 21 +- acvm-repo/acvm/src/pwg/arithmetic.rs | 38 +- acvm-repo/acvm/src/pwg/blackbox/mod.rs | 2 +- acvm-repo/acvm/src/pwg/blackbox/range.rs | 2 +- acvm-repo/acvm/src/pwg/brillig.rs | 6 +- acvm-repo/acvm/src/pwg/directives/mod.rs | 2 +- acvm-repo/acvm/src/pwg/memory_op.rs | 2 +- acvm-repo/acvm/src/pwg/mod.rs | 6 +- cspell.json | 2 + 12 files changed, 403 insertions(+), 35 deletions(-) create mode 100644 acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index f73417a4b5b..8a0c4692282 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -217,8 +217,10 @@ impl BlackBoxFuncCall { | BlackBoxFuncCall::PedersenCommitment { inputs, .. } | BlackBoxFuncCall::PedersenHash { inputs, .. } | BlackBoxFuncCall::BigIntFromLeBytes { inputs, .. } - | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } - | BlackBoxFuncCall::Sha256Compression { inputs, .. } => inputs.to_vec(), + | BlackBoxFuncCall::Poseidon2Permutation { inputs, .. } => inputs.to_vec(), + BlackBoxFuncCall::Sha256Compression { inputs, hash_values, .. } => { + inputs.iter().chain(hash_values).copied().collect() + } BlackBoxFuncCall::AND { lhs, rhs, .. } | BlackBoxFuncCall::XOR { lhs, rhs, .. } => { vec![*lhs, *rhs] } diff --git a/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs b/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs new file mode 100644 index 00000000000..0e7d28104da --- /dev/null +++ b/acvm-repo/acvm/src/compiler/optimizers/constant_backpropagation.rs @@ -0,0 +1,331 @@ +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +use crate::{ + compiler::optimizers::GeneralOptimizer, + pwg::{ + arithmetic::ExpressionSolver, blackbox::solve_range_opcode, directives::solve_directives, + BrilligSolver, BrilligSolverStatus, + }, +}; +use acir::{ + circuit::{ + brillig::{Brillig, BrilligInputs, BrilligOutputs}, + directives::Directive, + opcodes::BlackBoxFuncCall, + Circuit, Opcode, + }, + native_types::{Expression, Witness, WitnessMap}, +}; +use acvm_blackbox_solver::StubbedBlackBoxSolver; + +/// `ConstantBackpropagationOptimizer` will attempt to determine any constant witnesses within the program. +/// It does this by attempting to solve the program without any inputs (i.e. using an empty witness map), +/// any values which it can determine are then enforced to be constant values. +/// +/// The optimizer will then replace any witnesses wherever they appear within the circuit with these constant values. +/// This is repeated until the circuit stabilizes. +pub(crate) struct ConstantBackpropagationOptimizer { + circuit: Circuit, +} + +impl ConstantBackpropagationOptimizer { + /// Creates a new `ConstantBackpropagationOptimizer` + pub(crate) fn new(circuit: Circuit) -> Self { + Self { circuit } + } + + fn gather_known_witnesses(&self) -> (WitnessMap, BTreeSet) { + // We do not want to affect the circuit's interface so avoid optimizing away these witnesses. + let mut required_witnesses: BTreeSet = self + .circuit + .private_parameters + .union(&self.circuit.public_parameters.0) + .chain(&self.circuit.return_values.0) + .copied() + .collect(); + + for opcode in &self.circuit.opcodes { + match &opcode { + Opcode::BlackBoxFuncCall(func_call) => { + required_witnesses.extend( + func_call.get_inputs_vec().into_iter().map(|func_input| func_input.witness), + ); + required_witnesses.extend(func_call.get_outputs_vec()); + } + + Opcode::MemoryInit { init, .. } => { + required_witnesses.extend(init); + } + + Opcode::MemoryOp { op, .. } => { + required_witnesses.insert(op.index.to_witness().unwrap()); + required_witnesses.insert(op.value.to_witness().unwrap()); + } + + _ => (), + }; + } + + let mut known_witnesses = WitnessMap::new(); + for opcode in self.circuit.opcodes.iter().rev() { + if let Opcode::AssertZero(expr) = opcode { + let solve_result = ExpressionSolver::solve(&mut known_witnesses, expr); + // It doesn't matter what the result is. We expect most opcodes to not be solved successfully so we discard errors. + // At the same time, if the expression can be solved then we track this by the updates to `known_witnesses` + drop(solve_result); + } + } + + // We want to retain any references to required witnesses so we "forget" these assignments. + let known_witnesses: BTreeMap<_, _> = known_witnesses + .into_iter() + .filter(|(witness, _)| !required_witnesses.contains(witness)) + .collect(); + + (known_witnesses.into(), required_witnesses) + } + + /// Returns a `Circuit` where with any constant witnesses replaced with the constant they resolve to. + #[tracing::instrument(level = "trace", skip_all)] + pub(crate) fn backpropagate_constants( + circuit: Circuit, + order_list: Vec, + ) -> (Circuit, Vec) { + let old_circuit_size = circuit.opcodes.len(); + + let optimizer = Self::new(circuit); + let (circuit, order_list) = optimizer.backpropagate_constants_iteration(order_list); + + let new_circuit_size = circuit.opcodes.len(); + if new_circuit_size < old_circuit_size { + Self::backpropagate_constants(circuit, order_list) + } else { + (circuit, order_list) + } + } + + /// Applies a single round of constant backpropagation to a `Circuit`. + pub(crate) fn backpropagate_constants_iteration( + mut self, + order_list: Vec, + ) -> (Circuit, Vec) { + let (mut known_witnesses, required_witnesses) = self.gather_known_witnesses(); + + let opcodes = std::mem::take(&mut self.circuit.opcodes); + + fn remap_expression(known_witnesses: &WitnessMap, expression: Expression) -> Expression { + GeneralOptimizer::optimize(ExpressionSolver::evaluate(&expression, known_witnesses)) + } + + let mut new_order_list = Vec::with_capacity(order_list.len()); + let mut new_opcodes = Vec::with_capacity(opcodes.len()); + for (idx, opcode) in opcodes.into_iter().enumerate() { + let new_opcode = match opcode { + Opcode::AssertZero(expression) => { + let new_expr = remap_expression(&known_witnesses, expression); + if new_expr.is_zero() { + continue; + } + + // Attempt to solve the opcode to see if we can determine the value of any witnesses in the expression. + // We only do this _after_ we apply any simplifications to create the new opcode as we want to + // keep the constraint on the witness which we are solving for here. + let solve_result = ExpressionSolver::solve(&mut known_witnesses, &new_expr); + // It doesn't matter what the result is. We expect most opcodes to not be solved successfully so we discard errors. + // At the same time, if the expression can be solved then we track this by the updates to `known_witnesses` + drop(solve_result); + + Opcode::AssertZero(new_expr) + } + Opcode::Brillig(brillig) => { + let remapped_inputs = brillig + .inputs + .into_iter() + .map(|input| match input { + BrilligInputs::Single(expr) => { + BrilligInputs::Single(remap_expression(&known_witnesses, expr)) + } + BrilligInputs::Array(expr_array) => { + let new_input: Vec<_> = expr_array + .into_iter() + .map(|expr| remap_expression(&known_witnesses, expr)) + .collect(); + + BrilligInputs::Array(new_input) + } + input @ BrilligInputs::MemoryArray(_) => input, + }) + .collect(); + + let remapped_predicate = brillig + .predicate + .map(|predicate| remap_expression(&known_witnesses, predicate)); + + let new_brillig = Brillig { + inputs: remapped_inputs, + predicate: remapped_predicate, + ..brillig + }; + + let brillig_output_is_required_witness = + new_brillig.outputs.iter().any(|output| match output { + BrilligOutputs::Simple(witness) => required_witnesses.contains(witness), + BrilligOutputs::Array(witness_array) => witness_array + .iter() + .any(|witness| required_witnesses.contains(witness)), + }); + + if brillig_output_is_required_witness { + // If one of the brillig opcode's outputs is a required witness then we can't remove the opcode. In this case we can't replace + // all of the uses of this witness with the calculated constant so we'll be attempting to use an uninitialized witness. + // + // We then do not attempt execution of this opcode and just simplify the inputs. + Opcode::Brillig(new_brillig) + } else if let Ok(mut solver) = BrilligSolver::new( + &known_witnesses, + &HashMap::new(), + &new_brillig, + &StubbedBlackBoxSolver, + idx, + ) { + match solver.solve() { + Ok(BrilligSolverStatus::Finished) => { + // Write execution outputs + match solver.finalize(&mut known_witnesses, &new_brillig) { + Ok(()) => { + // If we've managed to execute the brillig opcode at compile time, we can now just write in the + // results as constants for the rest of the circuit. + continue; + } + _ => Opcode::Brillig(new_brillig), + } + } + Ok(BrilligSolverStatus::InProgress) => unreachable!( + "Solver should either finish, block on foreign call, or error." + ), + Ok(BrilligSolverStatus::ForeignCallWait(_)) | Err(_) => { + Opcode::Brillig(new_brillig) + } + } + } else { + Opcode::Brillig(new_brillig) + } + } + + Opcode::Directive(Directive::ToLeRadix { a, b, radix }) => { + if b.iter().all(|output| known_witnesses.contains_key(output)) { + continue; + } else if b.iter().any(|witness| required_witnesses.contains(witness)) { + // If one of the brillig opcode's outputs is a required witness then we can't remove the opcode. In this case we can't replace + // all of the uses of this witness with the calculated constant so we'll be attempting to use an uninitialized witness. + // + // We then do not attempt execution of this opcode and just simplify the inputs. + Opcode::Directive(Directive::ToLeRadix { + a: remap_expression(&known_witnesses, a), + b, + radix, + }) + } else { + let directive = Directive::ToLeRadix { + a: remap_expression(&known_witnesses, a), + b, + radix, + }; + let result = solve_directives(&mut known_witnesses, &directive); + + match result { + Ok(()) => continue, + Err(_) => Opcode::Directive(directive), + } + } + } + + Opcode::BlackBoxFuncCall(BlackBoxFuncCall::RANGE { input }) => { + if solve_range_opcode(&known_witnesses, &input).is_ok() { + continue; + } else { + opcode + } + } + + Opcode::BlackBoxFuncCall(_) + | Opcode::MemoryOp { .. } + | Opcode::MemoryInit { .. } => opcode, + }; + + new_opcodes.push(new_opcode); + new_order_list.push(order_list[idx]); + } + + self.circuit.opcodes = new_opcodes; + + (self.circuit, new_order_list) + } +} + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + + use crate::compiler::optimizers::constant_backpropagation::ConstantBackpropagationOptimizer; + use acir::{ + brillig::MemoryAddress, + circuit::{ + brillig::{Brillig, BrilligOutputs}, + opcodes::{BlackBoxFuncCall, FunctionInput}, + Circuit, ExpressionWidth, Opcode, PublicInputs, + }, + native_types::Witness, + }; + use brillig_vm::brillig::Opcode as BrilligOpcode; + + fn test_circuit(opcodes: Vec) -> Circuit { + Circuit { + current_witness_index: 1, + expression_width: ExpressionWidth::Bounded { width: 3 }, + opcodes, + private_parameters: BTreeSet::new(), + public_parameters: PublicInputs::default(), + return_values: PublicInputs::default(), + assert_messages: Default::default(), + recursive: false, + } + } + + #[test] + fn retain_brillig_with_required_witness_outputs() { + let brillig_opcode = Opcode::Brillig(Brillig { + inputs: Vec::new(), + outputs: vec![BrilligOutputs::Simple(Witness(1))], + bytecode: vec![ + BrilligOpcode::Const { + destination: MemoryAddress(0), + bit_size: 32, + value: 1u128.into(), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 1 }, + ], + predicate: None, + }); + let blackbox_opcode = Opcode::BlackBoxFuncCall(BlackBoxFuncCall::AND { + lhs: FunctionInput { witness: Witness(1), num_bits: 64 }, + rhs: FunctionInput { witness: Witness(2), num_bits: 64 }, + output: Witness(3), + }); + + let opcodes = vec![brillig_opcode, blackbox_opcode]; + // The optimizer should keep the lowest bit size range constraint + let circuit = test_circuit(opcodes); + let acir_opcode_positions = circuit.opcodes.iter().enumerate().map(|(i, _)| i).collect(); + let optimizer = ConstantBackpropagationOptimizer::new(circuit); + + let (optimized_circuit, _) = + optimizer.backpropagate_constants_iteration(acir_opcode_positions); + + assert_eq!( + optimized_circuit.opcodes.len(), + 2, + "The brillig opcode should not be removed as the output is needed as a witness" + ); + } +} diff --git a/acvm-repo/acvm/src/compiler/optimizers/general.rs b/acvm-repo/acvm/src/compiler/optimizers/general.rs index 2bd781f7bb5..a48a590a05e 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/general.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/general.rs @@ -13,7 +13,8 @@ impl GeneralOptimizer { pub(crate) fn optimize(opcode: Expression) -> Expression { // XXX: Perhaps this optimization can be done on the fly let opcode = remove_zero_coefficients(opcode); - simplify_mul_terms(opcode) + let opcode = simplify_mul_terms(opcode); + simplify_linear_terms(opcode) } } @@ -42,3 +43,20 @@ fn simplify_mul_terms(mut gate: Expression) -> Expression { gate.mul_terms = hash_map.into_iter().map(|((w_l, w_r), scale)| (scale, w_l, w_r)).collect(); gate } + +// Simplifies all linear terms with the same variables +fn simplify_linear_terms(mut gate: Expression) -> Expression { + let mut hash_map: IndexMap = IndexMap::new(); + + // Canonicalize the ordering of the terms, lets just order by variable name + for (scale, witness) in gate.linear_combinations.into_iter() { + *hash_map.entry(witness).or_insert_with(FieldElement::zero) += scale; + } + + gate.linear_combinations = hash_map + .into_iter() + .filter(|(_, scale)| scale != &FieldElement::zero()) + .map(|(witness, scale)| (scale, witness)) + .collect(); + gate +} diff --git a/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 923756580b3..599bdabd420 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,5 +1,6 @@ use acir::circuit::{Circuit, Opcode}; +mod constant_backpropagation; mod general; mod redundant_range; mod unused_memory; @@ -8,6 +9,7 @@ pub(crate) use general::GeneralOptimizer; pub(crate) use redundant_range::RangeOptimizer; use tracing::info; +use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; use super::{transform_assert_messages, AcirTransformationMap}; @@ -26,6 +28,15 @@ pub fn optimize(acir: Circuit) -> (Circuit, AcirTransformationMap) { /// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] independent optimizations to a [`Circuit`]. #[tracing::instrument(level = "trace", name = "optimize_acir" skip(acir))] pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { + // Track original acir opcode positions throughout the transformation passes of the compilation + // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) + let acir_opcode_positions = (0..acir.opcodes.len()).collect(); + + if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::Brillig(_)) { + info!("Program is fully unconstrained, skipping optimization pass"); + return (acir, acir_opcode_positions); + } + info!("Number of opcodes before: {}", acir.opcodes.len()); // General optimizer pass @@ -42,20 +53,22 @@ pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { .collect(); let acir = Circuit { opcodes, ..acir }; - // Track original acir opcode positions throughout the transformation passes of the compilation - // by applying the modifications done to the circuit opcodes and also to the opcode_positions (delete and insert) - let acir_opcode_positions = (0..acir.opcodes.len()).collect(); - // Unused memory optimization pass let memory_optimizer = UnusedMemoryOptimizer::new(acir); let (acir, acir_opcode_positions) = memory_optimizer.remove_unused_memory_initializations(acir_opcode_positions); + let (acir, acir_opcode_positions) = + ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + // Range optimization pass let range_optimizer = RangeOptimizer::new(acir); let (acir, acir_opcode_positions) = range_optimizer.replace_redundant_ranges(acir_opcode_positions); + let (acir, acir_opcode_positions) = + ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + info!("Number of opcodes after: {}", acir.opcodes.len()); (acir, acir_opcode_positions) diff --git a/acvm-repo/acvm/src/pwg/arithmetic.rs b/acvm-repo/acvm/src/pwg/arithmetic.rs index 81462ea495e..dc9e13d44b6 100644 --- a/acvm-repo/acvm/src/pwg/arithmetic.rs +++ b/acvm-repo/acvm/src/pwg/arithmetic.rs @@ -7,7 +7,7 @@ use super::{insert_value, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionErro /// An Expression solver will take a Circuit's assert-zero opcodes with witness assignments /// and create the other witness variables -pub(super) struct ExpressionSolver; +pub(crate) struct ExpressionSolver; #[allow(clippy::enum_variant_names)] pub(super) enum OpcodeStatus { @@ -24,13 +24,18 @@ pub(crate) enum MulTerm { impl ExpressionSolver { /// Derives the rest of the witness based on the initial low level variables - pub(super) fn solve( + pub(crate) fn solve( initial_witness: &mut WitnessMap, opcode: &Expression, ) -> Result<(), OpcodeResolutionError> { let opcode = &ExpressionSolver::evaluate(opcode, initial_witness); // Evaluate multiplication term - let mul_result = ExpressionSolver::solve_mul_term(opcode, initial_witness); + let mul_result = + ExpressionSolver::solve_mul_term(opcode, initial_witness).map_err(|_| { + OpcodeResolutionError::OpcodeNotSolvable( + OpcodeNotSolvable::ExpressionHasTooManyUnknowns(opcode.clone()), + ) + })?; // Evaluate the fan-in terms let opcode_status = ExpressionSolver::solve_fan_in_term(opcode, initial_witness); @@ -54,9 +59,7 @@ impl ExpressionSolver { } } else { let assignment = -total_sum / (q + b); - // Add this into the witness assignments - insert_value(&w1, assignment, initial_witness)?; - Ok(()) + insert_value(&w1, assignment, initial_witness) } } else { // TODO: can we be more specific with this error? @@ -84,9 +87,7 @@ impl ExpressionSolver { } } else { let assignment = -(total_sum / partial_prod); - // Add this into the witness assignments - insert_value(&unknown_var, assignment, initial_witness)?; - Ok(()) + insert_value(&unknown_var, assignment, initial_witness) } } (MulTerm::Solved(a), OpcodeStatus::OpcodeSatisfied(b)) => { @@ -118,9 +119,7 @@ impl ExpressionSolver { } } else { let assignment = -(total_sum / coeff); - // Add this into the witness assignments - insert_value(&unknown_var, assignment, initial_witness)?; - Ok(()) + insert_value(&unknown_var, assignment, initial_witness) } } } @@ -130,16 +129,19 @@ impl ExpressionSolver { /// If the witness values are not known, then the function returns a None /// XXX: Do we need to account for the case where 5xy + 6x = 0 ? We do not know y, but it can be solved given x . But I believe x can be solved with another opcode /// XXX: What about making a mul opcode = a constant 5xy + 7 = 0 ? This is the same as the above. - fn solve_mul_term(arith_opcode: &Expression, witness_assignments: &WitnessMap) -> MulTerm { + fn solve_mul_term( + arith_opcode: &Expression, + witness_assignments: &WitnessMap, + ) -> Result { // First note that the mul term can only contain one/zero term // We are assuming it has been optimized. match arith_opcode.mul_terms.len() { - 0 => MulTerm::Solved(FieldElement::zero()), - 1 => ExpressionSolver::solve_mul_term_helper( + 0 => Ok(MulTerm::Solved(FieldElement::zero())), + 1 => Ok(ExpressionSolver::solve_mul_term_helper( &arith_opcode.mul_terms[0], witness_assignments, - ), - _ => panic!("Mul term in the assert-zero opcode must contain either zero or one term"), + )), + _ => Err(OpcodeStatus::OpcodeUnsolvable), } } @@ -209,7 +211,7 @@ impl ExpressionSolver { } // Partially evaluate the opcode using the known witnesses - pub(super) fn evaluate(expr: &Expression, initial_witness: &WitnessMap) -> Expression { + pub(crate) fn evaluate(expr: &Expression, initial_witness: &WitnessMap) -> Expression { let mut result = Expression::default(); for &(c, w1, w2) in &expr.mul_terms { let mul_result = ExpressionSolver::solve_mul_term_helper(&(c, w1, w2), initial_witness); diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 4309cad1b2e..6ee926043cd 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -25,7 +25,7 @@ use fixed_base_scalar_mul::{embedded_curve_add, fixed_base_scalar_mul}; use hash::{solve_generic_256_hash_opcode, solve_sha_256_permutation_opcode}; use logic::{and, xor}; use pedersen::pedersen; -use range::solve_range_opcode; +pub(crate) use range::solve_range_opcode; use signature::{ ecdsa::{secp256k1_prehashed, secp256r1_prehashed}, schnorr::schnorr_verify, diff --git a/acvm-repo/acvm/src/pwg/blackbox/range.rs b/acvm-repo/acvm/src/pwg/blackbox/range.rs index 1b976e30ed5..2afe820b636 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -4,7 +4,7 @@ use crate::{ }; use acir::{circuit::opcodes::FunctionInput, native_types::WitnessMap}; -pub(super) fn solve_range_opcode( +pub(crate) fn solve_range_opcode( initial_witness: &WitnessMap, input: &FunctionInput, ) -> Result<(), OpcodeResolutionError> { diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index b0fb7469fd9..51c7f4c6203 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -65,7 +65,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { /// Constructs a solver for a Brillig block given the bytecode and initial /// witness. - pub(super) fn new( + pub(crate) fn new( initial_witness: &WitnessMap, memory: &HashMap, brillig: &'b Brillig, @@ -134,7 +134,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { self.vm.get_call_stack() } - pub(super) fn solve(&mut self) -> Result { + pub(crate) fn solve(&mut self) -> Result { let status = self.vm.process_opcodes(); self.handle_vm_status(status) } @@ -177,7 +177,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { } } - pub(super) fn finalize( + pub(crate) fn finalize( self, witness: &mut WitnessMap, brillig: &Brillig, diff --git a/acvm-repo/acvm/src/pwg/directives/mod.rs b/acvm-repo/acvm/src/pwg/directives/mod.rs index 07226c85b27..ee544521fc7 100644 --- a/acvm-repo/acvm/src/pwg/directives/mod.rs +++ b/acvm-repo/acvm/src/pwg/directives/mod.rs @@ -11,7 +11,7 @@ use super::{get_value, insert_value, ErrorLocation}; /// Returns `Ok(OpcodeResolution)` to signal whether the directive was successful solved. /// /// Returns `Err(OpcodeResolutionError)` if a circuit constraint is unsatisfied. -pub(super) fn solve_directives( +pub(crate) fn solve_directives( initial_witness: &mut WitnessMap, directive: &Directive, ) -> Result<(), OpcodeResolutionError> { diff --git a/acvm-repo/acvm/src/pwg/memory_op.rs b/acvm-repo/acvm/src/pwg/memory_op.rs index 49ec652289e..e51797707a7 100644 --- a/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/acvm-repo/acvm/src/pwg/memory_op.rs @@ -13,7 +13,7 @@ type MemoryIndex = u32; /// Maintains the state for solving [`MemoryInit`][`acir::circuit::Opcode::MemoryInit`] and [`MemoryOp`][`acir::circuit::Opcode::MemoryOp`] opcodes. #[derive(Default)] -pub(super) struct MemoryOpSolver { +pub(crate) struct MemoryOpSolver { pub(super) block_value: HashMap, pub(super) block_len: u32, } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 2ee39a289e7..d8323e5ef5f 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -21,11 +21,11 @@ use thiserror::Error; // arithmetic pub(crate) mod arithmetic; // Brillig bytecode -mod brillig; +pub(crate) mod brillig; // Directives -mod directives; +pub(crate) mod directives; // black box functions -mod blackbox; +pub(crate) mod blackbox; mod memory_op; pub use self::brillig::{BrilligSolver, BrilligSolverStatus}; diff --git a/cspell.json b/cspell.json index 23659b39c68..a96e3de901a 100644 --- a/cspell.json +++ b/cspell.json @@ -13,6 +13,8 @@ "arithmetization", "arity", "arkworks", + "backpropagate", + "Backpropagation", "barebones", "barretenberg", "bincode", From 8a6b131402892a570bc2de6f5869de73b0bd979e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Mon, 4 Mar 2024 10:49:31 +0000 Subject: [PATCH 033/416] feat: add option to set max memory for bb.js (#4227) # Description Makes `BackendBarretenberg` pass all the options to the `bb.js` instance. ## Summary\* Makes `BackendBarretenberg` pass all the options to the `bb.js` instance, namely min and max memory. Manually setting max memory makes iOS not immediately kill workers on the browser. This is already implemented in BB as per [this PR](https://github.com/AztecProtocol/aztec-packages/pull/3265) Replicates the change I made in a pinch for `progrcrypto` Istanbul. [Example usage here](https://github.com/signorecello/progcrypto23-act/blob/f95f267f2460a423f99865ca30e29f89e8104bc5/packages/user/pages/index.tsx#L31) --- compiler/integration-tests/package.json | 1 + compiler/integration-tests/test/mocks/os.js | 1 + .../web-test-runner.config.mjs | 15 +++++++++++-- .../noir_js_backend_barretenberg/src/index.ts | 12 +++++++++- .../noir_js_backend_barretenberg/src/types.ts | 1 + yarn.lock | 22 +++++++++++++++++++ 6 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 compiler/integration-tests/test/mocks/os.js diff --git a/compiler/integration-tests/package.json b/compiler/integration-tests/package.json index 2e75ebc201e..798b7c55312 100644 --- a/compiler/integration-tests/package.json +++ b/compiler/integration-tests/package.json @@ -19,6 +19,7 @@ "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", "@nomicfoundation/hardhat-ethers": "^3.0.0", "@web/dev-server-esbuild": "^0.3.6", + "@web/dev-server-import-maps": "^0.2.0", "@web/test-runner": "^0.15.3", "@web/test-runner-playwright": "^0.10.0", "eslint": "^8.56.0", diff --git a/compiler/integration-tests/test/mocks/os.js b/compiler/integration-tests/test/mocks/os.js new file mode 100644 index 00000000000..32333568316 --- /dev/null +++ b/compiler/integration-tests/test/mocks/os.js @@ -0,0 +1 @@ +export const os = { cpus: () => new Array(4) }; diff --git a/compiler/integration-tests/web-test-runner.config.mjs b/compiler/integration-tests/web-test-runner.config.mjs index 665ea262f99..4dfc96dd0a6 100644 --- a/compiler/integration-tests/web-test-runner.config.mjs +++ b/compiler/integration-tests/web-test-runner.config.mjs @@ -2,7 +2,8 @@ import { defaultReporter } from '@web/test-runner'; import { summaryReporter } from '@web/test-runner'; import { fileURLToPath } from 'url'; import { esbuildPlugin } from '@web/dev-server-esbuild'; -import { playwrightLauncher } from "@web/test-runner-playwright"; +import { playwrightLauncher } from '@web/test-runner-playwright'; +import { importMapsPlugin } from '@web/dev-server-import-maps'; let reporter = summaryReporter(); const debugPlugins = []; @@ -21,7 +22,7 @@ if (process.env.CI !== 'true' || process.env.RUNNER_DEBUG === '1') { export default { browsers: [ - playwrightLauncher({ product: "chromium" }), + playwrightLauncher({ product: 'chromium' }), // playwrightLauncher({ product: "webkit" }), // playwrightLauncher({ product: "firefox" }), ], @@ -29,6 +30,16 @@ export default { esbuildPlugin({ ts: true, }), + importMapsPlugin({ + inject: { + importMap: { + imports: { + // mock os module + os: '/test/mocks/os.js', + }, + }, + }, + }), ...debugPlugins, ], files: ['test/browser/**/*.test.ts'], diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index d79b487c3cf..af03743eb2f 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -33,8 +33,18 @@ export class BarretenbergBackend implements Backend { /** @ignore */ async instantiate(): Promise { if (!this.api) { + if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { + this.options.threads = navigator.hardwareConcurrency; + } else { + try { + const os = await import('os'); + this.options.threads = os.cpus().length; + } catch (e) { + console.log('Could not detect environment. Falling back to one thread.', e); + } + } const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); - const api = await Barretenberg.new({ threads: this.options.threads }); + const api = await Barretenberg.new(this.options); const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); const crs = await Crs.new(subgroupSize + 1); diff --git a/tooling/noir_js_backend_barretenberg/src/types.ts b/tooling/noir_js_backend_barretenberg/src/types.ts index 041e36fdf91..fac23030aad 100644 --- a/tooling/noir_js_backend_barretenberg/src/types.ts +++ b/tooling/noir_js_backend_barretenberg/src/types.ts @@ -5,4 +5,5 @@ export type BackendOptions = { /** @description Number of threads */ threads: number; + memory?: { maximum: number }; }; diff --git a/yarn.lock b/yarn.lock index ace7959279f..f5f3a29f08a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3951,6 +3951,13 @@ __metadata: languageName: node linkType: hard +"@import-maps/resolve@npm:^1.0.1": + version: 1.0.1 + resolution: "@import-maps/resolve@npm:1.0.1" + checksum: 17ee033e26a0fd82294de87eae76d32b553a130fdbf0fb8c70d39f2087a3e8a4a5908970a99aa32bd175153efe9b7dfee6b7f99df36f41abed08c1911dbdb19c + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -6677,6 +6684,20 @@ __metadata: languageName: node linkType: hard +"@web/dev-server-import-maps@npm:^0.2.0": + version: 0.2.0 + resolution: "@web/dev-server-import-maps@npm:0.2.0" + dependencies: + "@import-maps/resolve": ^1.0.1 + "@types/parse5": ^6.0.1 + "@web/dev-server-core": ^0.7.0 + "@web/parse5-utils": ^2.1.0 + parse5: ^6.0.1 + picomatch: ^2.2.2 + checksum: 15dabfa385f023bab70758b80cc09443455830799793c1a404a7230d90ebf60e40984a10d8a6ceea2afb8f057e90a9f7356a76f867d5e5a2eeacbc397e41535a + languageName: node + linkType: hard + "@web/dev-server-rollup@npm:^0.4.1": version: 0.4.1 resolution: "@web/dev-server-rollup@npm:0.4.1" @@ -13416,6 +13437,7 @@ __metadata: "@nomicfoundation/hardhat-chai-matchers": ^2.0.0 "@nomicfoundation/hardhat-ethers": ^3.0.0 "@web/dev-server-esbuild": ^0.3.6 + "@web/dev-server-import-maps": ^0.2.0 "@web/test-runner": ^0.15.3 "@web/test-runner-playwright": ^0.10.0 eslint: ^8.56.0 From 17f343b3c3bf5792a8358aaeef969ee95d45ed1a Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:56:20 +0000 Subject: [PATCH 034/416] chore: export `report_errors` from `nargo` (#4461) # Description ## Problem\* Related to #3503 ## Summary\* This PR removes the extra copy of the `report_errors` function. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/lsp/src/requests/profile_run.rs | 14 +++- tooling/nargo/src/ops/compile.rs | 66 +++++++++------ tooling/nargo/src/ops/mod.rs | 3 +- tooling/nargo_cli/src/cli/check_cmd.rs | 11 +-- .../nargo_cli/src/cli/codegen_verifier_cmd.rs | 3 +- tooling/nargo_cli/src/cli/compile_cmd.rs | 82 ++++++------------- tooling/nargo_cli/src/cli/debug_cmd.rs | 3 +- tooling/nargo_cli/src/cli/execute_cmd.rs | 3 +- tooling/nargo_cli/src/cli/export_cmd.rs | 2 +- tooling/nargo_cli/src/cli/fmt_cmd.rs | 4 +- tooling/nargo_cli/src/cli/info_cmd.rs | 11 ++- tooling/nargo_cli/src/cli/prove_cmd.rs | 3 +- tooling/nargo_cli/src/cli/verify_cmd.rs | 3 +- 13 files changed, 98 insertions(+), 110 deletions(-) diff --git a/tooling/lsp/src/requests/profile_run.rs b/tooling/lsp/src/requests/profile_run.rs index 917c247410d..89719947689 100644 --- a/tooling/lsp/src/requests/profile_run.rs +++ b/tooling/lsp/src/requests/profile_run.rs @@ -5,7 +5,10 @@ use std::{ use acvm::acir::circuit::ExpressionWidth; use async_lsp::{ErrorCode, ResponseError}; -use nargo::{artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager}; +use nargo::{ + artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager, + ops::report_errors, +}; use nargo_toml::{find_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ file_manager_with_stdlib, CompileOptions, DebugFile, NOIR_ARTIFACT_VERSION_STRING, @@ -60,11 +63,18 @@ fn on_profile_run_request_inner( Some(_package) => { let expression_width = ExpressionWidth::Bounded { width: 3 }; - let (compiled_programs, compiled_contracts) = nargo::ops::compile_workspace( + let compiled_workspace = nargo::ops::compile_workspace( &workspace_file_manager, &parsed_files, &workspace, &CompileOptions::default(), + ); + + let (compiled_programs, compiled_contracts) = report_errors( + compiled_workspace, + &workspace_file_manager, + CompileOptions::default().deny_warnings, + CompileOptions::default().silence_warnings, ) .map_err(|err| ResponseError::new(ErrorCode::REQUEST_FAILED, err))?; diff --git a/tooling/nargo/src/ops/compile.rs b/tooling/nargo/src/ops/compile.rs index bd1850649c4..d7c7cc2c123 100644 --- a/tooling/nargo/src/ops/compile.rs +++ b/tooling/nargo/src/ops/compile.rs @@ -21,7 +21,7 @@ pub fn compile_workspace( parsed_files: &ParsedFiles, workspace: &Workspace, compile_options: &CompileOptions, -) -> Result<(Vec, Vec), CompileError> { +) -> CompilationResult<(Vec, Vec)> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace .into_iter() .filter(|package| !package.is_library()) @@ -38,31 +38,20 @@ pub fn compile_workspace( .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) .collect(); - // Report any warnings/errors which were encountered during compilation. - let compiled_programs: Vec = program_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - let compiled_contracts: Vec = contract_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - - Ok((compiled_programs, compiled_contracts)) + // Collate any warnings/errors which were encountered during compilation. + let compiled_programs = collect_errors(program_results); + let compiled_contracts = collect_errors(contract_results); + + match (compiled_programs, compiled_contracts) { + (Ok((programs, program_warnings)), Ok((contracts, contract_warnings))) => { + let warnings = [program_warnings, contract_warnings].concat(); + Ok(((programs, contracts), warnings)) + } + (Err(program_errors), Err(contract_errors)) => { + Err([program_errors, contract_errors].concat()) + } + (Err(errors), _) | (_, Err(errors)) => Err(errors), + } } pub fn compile_program( @@ -107,7 +96,30 @@ pub fn compile_contract( noirc_driver::compile_contract(&mut context, crate_id, compile_options) } -pub(crate) fn report_errors( +/// Constructs a single `CompilationResult` for a collection of `CompilationResult`s, merging the set of warnings/errors. +pub fn collect_errors(results: Vec>) -> CompilationResult> { + let mut artifacts = Vec::new(); + let mut warnings = Vec::new(); + let mut errors = Vec::new(); + + for result in results { + match result { + Ok((new_artifact, new_warnings)) => { + artifacts.push(new_artifact); + warnings.extend(new_warnings); + } + Err(new_errors) => errors.extend(new_errors), + } + } + + if errors.is_empty() { + Ok((artifacts, warnings)) + } else { + Err(errors) + } +} + +pub fn report_errors( result: CompilationResult, file_manager: &FileManager, deny_warnings: bool, diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 23dd0db15b9..55e9e927800 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -1,5 +1,6 @@ pub use self::compile::{ - compile_contract, compile_program, compile_program_with_debug_instrumenter, compile_workspace, + collect_errors, compile_contract, compile_program, compile_program_with_debug_instrumenter, + compile_workspace, report_errors, }; pub use self::execute::execute_circuit; pub use self::foreign_calls::{ diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index 4da06d2536a..242a640e484 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -5,8 +5,8 @@ use clap::Args; use fm::FileManager; use iter_extended::btree_map; use nargo::{ - errors::CompileError, insert_all_files_for_workspace_into_file_manager, package::Package, - parse_all, prepare_package, + errors::CompileError, insert_all_files_for_workspace_into_file_manager, ops::report_errors, + package::Package, parse_all, prepare_package, }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_abi::{AbiParameter, AbiType, MAIN_RETURN_NAME}; @@ -152,12 +152,7 @@ pub(crate) fn check_crate_and_report_errors( silence_warnings: bool, ) -> Result<(), CompileError> { let result = check_crate(context, crate_id, deny_warnings, disable_macros); - super::compile_cmd::report_errors( - result, - &context.file_manager, - deny_warnings, - silence_warnings, - ) + report_errors(result, &context.file_manager, deny_warnings, silence_warnings) } #[cfg(test)] diff --git a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index 63d27e30836..f0fe2e0ea78 100644 --- a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -1,11 +1,10 @@ use super::fs::{create_named_dir, write_to_file}; use super::NargoConfig; use crate::backends::Backend; -use crate::cli::compile_cmd::report_errors; use crate::errors::CliError; use clap::Args; -use nargo::ops::compile_program; +use nargo::ops::{compile_program, report_errors}; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 34fb05249b5..4309f0db3ea 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -2,8 +2,7 @@ use std::path::Path; use fm::FileManager; use nargo::artifacts::program::ProgramArtifact; -use nargo::errors::CompileError; -use nargo::ops::{compile_contract, compile_program}; +use nargo::ops::{collect_errors, compile_contract, compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -65,11 +64,18 @@ pub(crate) fn run( .compile_options .expression_width .unwrap_or_else(|| backend.get_backend_info_or_default()); - let (compiled_program, compiled_contracts) = compile_workspace( + let compiled_workspace = compile_workspace( &workspace_file_manager, &parsed_files, &workspace, &args.compile_options, + ); + + let (compiled_programs, compiled_contracts) = report_errors( + compiled_workspace, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace @@ -80,7 +86,7 @@ pub(crate) fn run( // Save build artifacts to disk. let only_acir = args.compile_options.only_acir; - for (package, program) in binary_packages.into_iter().zip(compiled_program) { + for (package, program) in binary_packages.into_iter().zip(compiled_programs) { let program = nargo::ops::transform_program(program, expression_width); save_program(program.clone(), &package, &workspace.target_directory_path(), only_acir); } @@ -97,7 +103,7 @@ pub(super) fn compile_workspace( parsed_files: &ParsedFiles, workspace: &Workspace, compile_options: &CompileOptions, -) -> Result<(Vec, Vec), CliError> { +) -> CompilationResult<(Vec, Vec)> { let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace .into_iter() .filter(|package| !package.is_library()) @@ -123,31 +129,20 @@ pub(super) fn compile_workspace( .map(|package| compile_contract(file_manager, parsed_files, package, compile_options)) .collect(); - // Report any warnings/errors which were encountered during compilation. - let compiled_programs: Vec = program_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - let compiled_contracts: Vec = contract_results - .into_iter() - .map(|compilation_result| { - report_errors( - compilation_result, - file_manager, - compile_options.deny_warnings, - compile_options.silence_warnings, - ) - }) - .collect::>()?; - - Ok((compiled_programs, compiled_contracts)) + // Collate any warnings/errors which were encountered during compilation. + let compiled_programs = collect_errors(program_results); + let compiled_contracts = collect_errors(contract_results); + + match (compiled_programs, compiled_contracts) { + (Ok((programs, program_warnings)), Ok((contracts, contract_warnings))) => { + let warnings = [program_warnings, contract_warnings].concat(); + Ok(((programs, contracts), warnings)) + } + (Err(program_errors), Err(contract_errors)) => { + Err([program_errors, contract_errors].concat()) + } + (Err(errors), _) | (_, Err(errors)) => Err(errors), + } } pub(super) fn save_program( @@ -172,30 +167,3 @@ fn save_contract(contract: CompiledContract, package: &Package, circuit_dir: &Pa circuit_dir, ); } - -/// Helper function for reporting any errors in a `CompilationResult` -/// structure that is commonly used as a return result in this file. -pub(crate) fn report_errors( - result: CompilationResult, - file_manager: &FileManager, - deny_warnings: bool, - silence_warnings: bool, -) -> Result { - let (t, warnings) = result.map_err(|errors| { - noirc_errors::reporter::report_all( - file_manager.as_file_map(), - &errors, - deny_warnings, - silence_warnings, - ) - })?; - - noirc_errors::reporter::report_all( - file_manager.as_file_map(), - &warnings, - deny_warnings, - silence_warnings, - ); - - Ok(t) -} diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 130a07b5c90..2c4937b6f16 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -8,7 +8,7 @@ use fm::FileManager; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::CompileError; -use nargo::ops::{compile_program, compile_program_with_debug_instrumenter}; +use nargo::ops::{compile_program, compile_program_with_debug_instrumenter, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -22,7 +22,6 @@ use noirc_frontend::debug::DebugInstrumenter; use noirc_frontend::graph::CrateName; use noirc_frontend::hir::ParsedFiles; -use super::compile_cmd::report_errors; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; use crate::backends::Backend; diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index a3fcebab94f..85c0a4160a7 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -5,7 +5,7 @@ use clap::Args; use nargo::artifacts::debug::DebugArtifact; use nargo::constants::PROVER_INPUT_FILE; use nargo::errors::try_to_diagnose_runtime_error; -use nargo::ops::{compile_program, DefaultForeignCallExecutor}; +use nargo::ops::{compile_program, report_errors, DefaultForeignCallExecutor}; use nargo::package::Package; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; @@ -19,7 +19,6 @@ use noirc_frontend::graph::CrateName; use super::fs::{inputs::read_inputs_from_file, witness::save_witness_to_dir}; use super::NargoConfig; use crate::backends::Backend; -use crate::cli::compile_cmd::report_errors; use crate::errors::CliError; /// Executes a circuit to calculate its return value diff --git a/tooling/nargo_cli/src/cli/export_cmd.rs b/tooling/nargo_cli/src/cli/export_cmd.rs index 96b24796a2b..044c2cb4ebb 100644 --- a/tooling/nargo_cli/src/cli/export_cmd.rs +++ b/tooling/nargo_cli/src/cli/export_cmd.rs @@ -1,4 +1,5 @@ use nargo::errors::CompileError; +use nargo::ops::report_errors; use noirc_errors::FileDiagnostic; use noirc_frontend::hir::ParsedFiles; use rayon::prelude::*; @@ -24,7 +25,6 @@ use crate::errors::CliError; use super::check_cmd::check_crate_and_report_errors; -use super::compile_cmd::report_errors; use super::fs::program::save_program_to_file; use super::NargoConfig; diff --git a/tooling/nargo_cli/src/cli/fmt_cmd.rs b/tooling/nargo_cli/src/cli/fmt_cmd.rs index 0bd25a3a0ce..2e0ca5632f1 100644 --- a/tooling/nargo_cli/src/cli/fmt_cmd.rs +++ b/tooling/nargo_cli/src/cli/fmt_cmd.rs @@ -1,7 +1,7 @@ use std::{fs::DirEntry, path::Path}; use clap::Args; -use nargo::insert_all_files_for_workspace_into_file_manager; +use nargo::{insert_all_files_for_workspace_into_file_manager, ops::report_errors}; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{file_manager_with_stdlib, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::CustomDiagnostic; @@ -53,7 +53,7 @@ pub(crate) fn run(args: FormatCommand, config: NargoConfig) -> Result<(), CliErr }) .collect(); - let _ = super::compile_cmd::report_errors::<()>( + let _ = report_errors::<()>( Err(errors), &workspace_file_manager, false, diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index ef0df0bf25b..300e1a35be2 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -6,7 +6,7 @@ use clap::Args; use iter_extended::vecmap; use nargo::{ artifacts::debug::DebugArtifact, insert_all_files_for_workspace_into_file_manager, - package::Package, parse_all, + ops::report_errors, package::Package, parse_all, }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ @@ -73,11 +73,18 @@ pub(crate) fn run( .compile_options .expression_width .unwrap_or_else(|| backend.get_backend_info_or_default()); - let (compiled_programs, compiled_contracts) = compile_workspace( + let compiled_workspace = compile_workspace( &workspace_file_manager, &parsed_files, &workspace, &args.compile_options, + ); + + let (compiled_programs, compiled_contracts) = report_errors( + compiled_workspace, + &workspace_file_manager, + args.compile_options.deny_warnings, + args.compile_options.silence_warnings, )?; let compiled_programs = vecmap(compiled_programs, |program| { diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index cc39b0535bc..f0a9b3185b9 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,6 +1,6 @@ use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; -use nargo::ops::compile_program; +use nargo::ops::{compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; @@ -11,7 +11,6 @@ use noirc_driver::{ }; use noirc_frontend::graph::CrateName; -use super::compile_cmd::report_errors; use super::fs::{ inputs::{read_inputs_from_file, write_inputs_to_file}, proof::save_proof_to_dir, diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index 66b88a22f2a..1063b50ab6c 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -1,11 +1,10 @@ -use super::compile_cmd::report_errors; use super::fs::{inputs::read_inputs_from_file, load_hex_data}; use super::NargoConfig; use crate::{backends::Backend, errors::CliError}; use clap::Args; use nargo::constants::{PROOF_EXT, VERIFIER_INPUT_FILE}; -use nargo::ops::compile_program; +use nargo::ops::{compile_program, report_errors}; use nargo::package::Package; use nargo::workspace::Workspace; use nargo::{insert_all_files_for_workspace_into_file_manager, parse_all}; From 2ce89587980cf064baecab50ccf4db14b43bf5cd Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 4 Mar 2024 13:08:51 -0600 Subject: [PATCH 035/416] chore: Capitalize containers section in docs (#4471) # Description ## Problem\* ## Summary\* The "containers" section of noir's docs was the only section not capitalized previously ## Additional Context I checked "Documentation included in this PR." so that docs can be regenerated for this PR and we can see the section is capitalized. ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- docs/docs/noir/standard_library/containers/index.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/docs/noir/standard_library/containers/index.md diff --git a/docs/docs/noir/standard_library/containers/index.md b/docs/docs/noir/standard_library/containers/index.md new file mode 100644 index 00000000000..ea84c6d5c21 --- /dev/null +++ b/docs/docs/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- From 86a0029b727b74c9ba1157f01c89daa7bfd60788 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 00:44:09 +0000 Subject: [PATCH 036/416] chore(deps): bump mio from 0.8.10 to 0.8.11 (#4478) Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.10 to 0.8.11.
Changelog

Sourced from mio's changelog.

0.8.11

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=mio&package-manager=cargo&previous-version=0.8.10&new-version=0.8.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/noir-lang/noir/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0438eaf81f..b2b6f8037bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2634,9 +2634,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", From 00d2c32e58176cc5de3574c8435a54d415c4a5fa Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 5 Mar 2024 08:06:07 -0500 Subject: [PATCH 037/416] feat: Add eddsa_poseidon_to_pub function to stdlib with test + docs (#4473) # Description ## Problem\* Gives a source of truth for deriving public keys for EdDSA signatures, e.g. for testing. ## Summary\* Adds this function: ```rust fn eddsa_poseidon_to_pub(secret: Field) -> (Field, Field) ``` ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: kevaundray --- .../cryptographic_primitives/eddsa.mdx | 11 +++++++++++ noir_stdlib/src/eddsa.nr | 7 +++++++ test_programs/execution_success/eddsa/src/main.nr | 10 ++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx index a9c10da6c06..99b7f830a20 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -16,3 +16,14 @@ fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s ``` + +## eddsa::eddsa_to_pub + +Private to public key conversion. + +Returns `(pub_key_x, pub_key_y)` + +```rust +fn eddsa_to_pub(secret : Field) -> (Field, Field) +``` + diff --git a/noir_stdlib/src/eddsa.nr b/noir_stdlib/src/eddsa.nr index 657e791e9c7..966bc1da2a1 100644 --- a/noir_stdlib/src/eddsa.nr +++ b/noir_stdlib/src/eddsa.nr @@ -38,3 +38,10 @@ pub fn eddsa_poseidon_verify( left.eq(right) } + +// Returns the public key of the given secret key as (pub_key_x, pub_key_y) +pub fn eddsa_to_pub(secret: Field) -> (Field, Field) { + let bjj = baby_jubjub(); + let pub_key = bjj.curve.mul(secret, bjj.curve.gen); + (pub_key.x, pub_key.y) +} diff --git a/test_programs/execution_success/eddsa/src/main.nr b/test_programs/execution_success/eddsa/src/main.nr index 12e8ea92785..4404ffe75f7 100644 --- a/test_programs/execution_success/eddsa/src/main.nr +++ b/test_programs/execution_success/eddsa/src/main.nr @@ -1,14 +1,20 @@ use dep::std::compat; use dep::std::ec::consts::te::baby_jubjub; +use dep::std::ec::tecurve::affine::Point as TEPoint; use dep::std::hash; -use dep::std::eddsa::eddsa_poseidon_verify; +use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify}; + fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // Skip this test for non-bn254 backends if compat::is_bn254() { let bjj = baby_jubjub(); let pub_key_a = bjj.curve.mul(_priv_key_a, bjj.curve.gen); - // let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); + let pub_key_b = bjj.curve.mul(_priv_key_b, bjj.curve.gen); + let (pub_key_a_x, pub_key_a_y) = eddsa_to_pub(_priv_key_a); + let (pub_key_b_x, pub_key_b_y) = eddsa_to_pub(_priv_key_b); + assert(TEPoint::new(pub_key_a_x, pub_key_a_y) == pub_key_a); + assert(TEPoint::new(pub_key_b_x, pub_key_b_y) == pub_key_b); // Manually computed as fields can't use modulo. Importantantly the commitment is within // the subgroup order. Note that choice of hash is flexible for this step. // let r_a = hash::pedersen_commitment([_priv_key_a, msg])[0] % bjj.suborder; // modulus computed manually From 5f57ebb7ff4b810802f90699a10f4325ef904f2e Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:48:57 -0500 Subject: [PATCH 038/416] feat: Sync from aztec-packages (#4465) BEGIN_COMMIT_OVERRIDE feat!: Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) chore: remove import of `dep::aztec` from aztec_macros (https://github.com/AztecProtocol/aztec-packages/pull/4941) fix: noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .aztec-sync-commit | 2 +- .dockerignore | 27 --- .gitignore | 3 - Dockerfile | 12 - Dockerfile.packages | 22 -- .../acvm_js/test/node/build_info.test.ts | 12 +- aztec_macros/src/lib.rs | 226 +++++++++++++----- bootstrap.sh | 22 -- bootstrap_cache.sh | 13 - compiler/noirc_frontend/src/lexer/token.rs | 3 - .../src/parser/parser/function.rs | 14 +- compiler/wasm/scripts/build-fixtures.sh | 2 +- noir_stdlib/src/sha512.nr | 2 +- package.json | 2 +- scripts/bootstrap_native.sh | 26 -- scripts/bootstrap_packages.sh | 31 --- scripts/test_js_packages.sh | 25 -- scripts/test_native.sh | 17 -- .../simple_contract/src/main.nr | 4 +- .../Prover.toml | 0 .../src/main.nr | 11 + .../src/cli/noir_template_files/contract.nr | 2 +- tooling/nargo_cli/src/cli/test_cmd.rs | 6 +- tooling/nargo_fmt/tests/expected/contract.nr | 3 +- tooling/nargo_fmt/tests/input/contract.nr | 3 +- 25 files changed, 210 insertions(+), 280 deletions(-) delete mode 100644 .dockerignore delete mode 100644 Dockerfile delete mode 100644 Dockerfile.packages delete mode 100755 bootstrap.sh delete mode 100755 bootstrap_cache.sh delete mode 100755 scripts/bootstrap_native.sh delete mode 100755 scripts/bootstrap_packages.sh delete mode 100755 scripts/test_js_packages.sh delete mode 100755 scripts/test_native.sh create mode 100644 test_programs/noir_test_failure/should_fail_suite_with_one_failure/Prover.toml create mode 100644 test_programs/noir_test_failure/should_fail_suite_with_one_failure/src/main.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index c97738f7226..78e53d61647 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -9e246c1289fa40c35c4b28d2f0081dfdc2aa9d19 +73d640a4a033f0c865d45da470ef40c1fb03a844 diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 559b271bf38..00000000000 --- a/.dockerignore +++ /dev/null @@ -1,27 +0,0 @@ -Dockerfile* -.dockerignore - -# Yarn -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions - -packages -**/package.tgz -**/target -**/node_modules -**/outputs - -# Noir.js -tooling/noir_js/lib - -# Wasm build artifacts -compiler/wasm/nodejs -compiler/wasm/web -tooling/noirc_abi_wasm/nodejs -tooling/noirc_abi_wasm/web -tooling/noir_js/lib diff --git a/.gitignore b/.gitignore index 8d02d34d463..5f41566c94b 100644 --- a/.gitignore +++ b/.gitignore @@ -50,8 +50,5 @@ tooling/noirc_abi_wasm/nodejs tooling/noirc_abi_wasm/web tooling/noir_js/lib -**/package.tgz -packages - # docs autogen build /docs/docs/noir_js/reference/ diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 3a478c3f95a..00000000000 --- a/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM rust:bullseye -WORKDIR /usr/src/noir -COPY . . -RUN ./scripts/bootstrap_native.sh - -# When running the container, mount the users home directory to same location. -FROM ubuntu:focal -# Install Tini as nargo doesn't handle signals properly. -# Install git as nargo needs it to clone. -RUN apt-get update && apt-get install -y git tini && rm -rf /var/lib/apt/lists/* && apt-get clean -COPY --from=0 /usr/src/noir/target/release/nargo /usr/src/noir/target/release/nargo -ENTRYPOINT ["/usr/bin/tini", "--", "/usr/src/noir/target/release/nargo"] diff --git a/Dockerfile.packages b/Dockerfile.packages deleted file mode 100644 index f40670c19e4..00000000000 --- a/Dockerfile.packages +++ /dev/null @@ -1,22 +0,0 @@ -FROM rust:alpine3.17 -RUN apk update \ - && apk upgrade \ - && apk add --no-cache \ - build-base \ - pkgconfig \ - openssl-dev \ - npm \ - yarn \ - bash \ - jq \ - git \ - curl - -WORKDIR /usr/src/noir -COPY . . -RUN ./scripts/bootstrap_packages.sh - -FROM scratch -COPY --from=0 /usr/src/noir/packages /usr/src/noir/packages -# For some unknown reason, on alpine only, we need this to exist. -COPY --from=0 /usr/src/noir/node_modules/@noir-lang /usr/src/noir/node_modules/@noir-lang diff --git a/acvm-repo/acvm_js/test/node/build_info.test.ts b/acvm-repo/acvm_js/test/node/build_info.test.ts index 23100505011..014bb6f422d 100644 --- a/acvm-repo/acvm_js/test/node/build_info.test.ts +++ b/acvm-repo/acvm_js/test/node/build_info.test.ts @@ -3,12 +3,20 @@ import { BuildInfo, buildInfo } from '@noir-lang/acvm_js'; import child_process from 'child_process'; import pkg from '../../package.json'; -it('returns the correct build into', () => { +it('returns the correct build info', () => { + let revision: string; + + try { + revision = child_process.execSync('git rev-parse HEAD').toString().trim(); + } catch (error) { + console.log('Failed to get revision, skipping test.'); + return; + } + const info: BuildInfo = buildInfo(); // TODO: enforce that `package.json` and `Cargo.toml` are consistent. expect(info.version).to.be.eq(pkg.version); - const revision = child_process.execSync('git rev-parse HEAD').toString().trim(); expect(info.gitHash).to.be.eq(revision); }); diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 09deb2c9712..f860f2c465a 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -3,7 +3,7 @@ use std::vec; use convert_case::{Case, Casing}; use iter_extended::vecmap; -use noirc_errors::Location; +use noirc_errors::{Location, Spanned}; use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; use noirc_frontend::hir::def_map::{LocalModuleId, ModuleId}; use noirc_frontend::macros_api::parse_program; @@ -11,17 +11,17 @@ use noirc_frontend::macros_api::FieldElement; use noirc_frontend::macros_api::{ BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, - HirContext, HirExpression, HirLiteral, HirStatement, Ident, ImportStatement, IndexExpression, - LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, - Param, Path, PathKind, Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, - Statement, StatementKind, StructType, Type, TypeImpl, UnaryOp, UnresolvedType, - UnresolvedTypeData, Visibility, + HirContext, HirExpression, HirLiteral, HirStatement, Ident, IndexExpression, LetStatement, + Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, Param, Path, + PathKind, Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, Statement, + StatementKind, StructType, Type, TypeImpl, UnaryOp, UnresolvedType, UnresolvedTypeData, + Visibility, }; use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; use noirc_frontend::node_interner::{FuncId, TraitId, TraitImplId, TraitImplKind}; -use noirc_frontend::Lambda; +use noirc_frontend::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Lambda}; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -74,6 +74,7 @@ pub enum AztecMacroError { UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, EventError { span: Span, message: String }, + UnsupportedAttributes { span: Span, secondary_message: Option }, } impl From for MacroError { @@ -114,6 +115,11 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { + primary_message: "Unsupported attributes in contract function".to_string(), + secondary_message, + span: Some(span), + }, } } } @@ -215,6 +221,14 @@ fn lambda(parameters: Vec<(Pattern, UnresolvedType)>, body: Expression) -> Expre }))) } +fn make_eq(lhs: Expression, rhs: Expression) -> Expression { + expression(ExpressionKind::Infix(Box::new(InfixExpression { + lhs, + rhs, + operator: Spanned::from(Span::default(), BinaryOpKind::Equal), + }))) +} + macro_rules! chained_path { ( $base:expr ) => { { @@ -267,10 +281,6 @@ fn index_array_variable(array: Expression, index: &str) -> Expression { }))) } -fn import(path: Path) -> ImportStatement { - ImportStatement { path, alias: None } -} - // // Create AST Nodes for Aztec // @@ -290,7 +300,6 @@ fn transform( .map_err(|(err, file_id)| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; - include_relevant_imports(&mut submodule.contents); } } Ok(ast) @@ -309,21 +318,6 @@ fn transform_hir( assign_storage_slots(crate_id, context) } -/// Includes an import to the aztec library if it has not been included yet -fn include_relevant_imports(ast: &mut SortedModule) { - // Create the aztec import path using the assumed chained_dep! macro - let aztec_import_path = import(chained_dep!("aztec")); - - // Check if the aztec import already exists - let is_aztec_imported = - ast.imports.iter().any(|existing_import| existing_import.path == aztec_import_path.path); - - // If aztec is not imported, add the import at the beginning - if !is_aztec_imported { - ast.imports.insert(0, aztec_import_path); - } -} - /// Creates an error alerting the user that they have not downloaded the Aztec-noir library fn check_for_aztec_dependency( crate_id: &CrateId, @@ -429,23 +423,58 @@ fn transform_module( } } + let has_initializer = module.functions.iter().any(|func| { + func.def + .attributes + .secondary + .iter() + .any(|attr| is_custom_attribute(attr, "aztec(initializer)")) + }); + for func in module.functions.iter_mut() { + let mut is_private = false; + let mut is_public = false; + let mut is_public_vm = false; + let mut is_initializer = false; + let mut is_internal = false; + let mut insert_init_check = has_initializer; + for secondary_attribute in func.def.attributes.secondary.clone() { - let crate_graph = &context.crate_graph[crate_id]; if is_custom_attribute(&secondary_attribute, "aztec(private)") { - transform_function("Private", func, storage_defined) - .map_err(|err| (err, crate_graph.root_file_id))?; - has_transformed_module = true; + is_private = true; + } else if is_custom_attribute(&secondary_attribute, "aztec(initializer)") { + is_initializer = true; + insert_init_check = false; + } else if is_custom_attribute(&secondary_attribute, "aztec(noinitcheck)") { + insert_init_check = false; + } else if is_custom_attribute(&secondary_attribute, "aztec(internal)") { + is_internal = true; } else if is_custom_attribute(&secondary_attribute, "aztec(public)") { - transform_function("Public", func, storage_defined) - .map_err(|err| (err, crate_graph.root_file_id))?; - has_transformed_module = true; + is_public = true; + insert_init_check = false; } else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") { - transform_vm_function(func, storage_defined) - .map_err(|err| (err, crate_graph.root_file_id))?; - has_transformed_module = true; + is_public_vm = true; } } + + // Apply transformations to the function based on collected attributes + if is_private || is_public { + transform_function( + if is_private { "Private" } else { "Public" }, + func, + storage_defined, + is_initializer, + insert_init_check, + is_internal, + ) + .map_err(|err| (err, crate_graph.root_file_id))?; + has_transformed_module = true; + } else if is_public_vm { + transform_vm_function(func, storage_defined) + .map_err(|err| (err, crate_graph.root_file_id))?; + has_transformed_module = true; + } + // Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract if storage_defined && func.def.is_unconstrained { transform_unconstrained(func); @@ -511,7 +540,7 @@ fn generate_storage_field_constructor( ( pattern("context"), make_type(UnresolvedTypeData::Named( - chained_path!("aztec", "context", "Context"), + chained_dep!("aztec", "context", "Context"), vec![], true, )), @@ -595,7 +624,7 @@ fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), Azte &[( ident("context"), make_type(UnresolvedTypeData::Named( - chained_path!("aztec", "context", "Context"), + chained_dep!("aztec", "context", "Context"), vec![], true, )), @@ -627,11 +656,36 @@ fn transform_function( ty: &str, func: &mut NoirFunction, storage_defined: bool, + is_initializer: bool, + insert_init_check: bool, + is_internal: bool, ) -> Result<(), AztecMacroError> { let context_name = format!("{}Context", ty); let inputs_name = format!("{}ContextInputs", ty); let return_type_name = format!("{}CircuitPublicInputs", ty); + // Add check that msg sender equals this address and flag function as internal + if is_internal { + let is_internal_check = create_internal_check(func.name()); + func.def.body.0.insert(0, is_internal_check); + func.def.is_internal = true; + } + + // Add initialization check + if insert_init_check { + if ty == "Public" { + let error = AztecMacroError::UnsupportedAttributes { + span: func.def.name.span(), + secondary_message: Some( + "public functions do not yet support initialization check".to_owned(), + ), + }; + return Err(error); + } + let init_check = create_init_check(); + func.def.body.0.insert(0, init_check); + } + // Add access to the storage struct if storage_defined { let storage_def = abstract_storage(&ty.to_lowercase(), false); @@ -655,6 +709,21 @@ fn transform_function( func.def.body.0.push(return_values); } + // Before returning mark the contract as initialized + if is_initializer { + if ty == "Public" { + let error = AztecMacroError::UnsupportedAttributes { + span: func.def.name.span(), + secondary_message: Some( + "public functions cannot yet be used as initializers".to_owned(), + ), + }; + return Err(error); + } + let mark_initialized = create_mark_as_initialized(); + func.def.body.0.push(mark_initialized); + } + // Push the finish method call to the end of the function let finish_def = create_context_finish(); func.def.body.0.push(finish_def); @@ -1028,7 +1097,7 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); let selector_path = - chained_path!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); + chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); let mut from_signature_path = selector_path.clone(); from_signature_path.segments.push(ident("from_signature")); @@ -1083,7 +1152,7 @@ fn create_inputs(ty: &str) -> Param { let context_pattern = Pattern::Identifier(context_ident); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context_inputs - let type_path = chained_path!("aztec", "context", "inputs", &path_snippet, ty); + let type_path = chained_dep!("aztec", "context", "inputs", &path_snippet, ty); let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![], true)); let visibility = Visibility::Private; @@ -1091,6 +1160,51 @@ fn create_inputs(ty: &str) -> Param { Param { pattern: context_pattern, typ: context_type, visibility, span: Span::default() } } +/// Creates an initialization check to ensure that the contract has been initialized, meant to +/// be injected as the first statement of any function after the context has been created. +/// +/// ```noir +/// assert_is_initialized(&mut context); +/// ``` +fn create_init_check() -> Statement { + make_statement(StatementKind::Expression(call( + variable_path(chained_dep!("aztec", "initializer", "assert_is_initialized")), + vec![mutable_reference("context")], + ))) +} + +/// Creates a call to mark_as_initialized which emits the initialization nullifier, meant to +/// be injected as the last statement before returning in a constructor. +/// +/// ```noir +/// mark_as_initialized(&mut context); +/// ``` +fn create_mark_as_initialized() -> Statement { + make_statement(StatementKind::Expression(call( + variable_path(chained_dep!("aztec", "initializer", "mark_as_initialized")), + vec![mutable_reference("context")], + ))) +} + +/// Creates a check for internal functions ensuring that the caller is self. +/// +/// ```noir +/// assert(context.msg_sender() == context.this_address(), "Function can only be called internally"); +/// ``` +fn create_internal_check(fname: &str) -> Statement { + make_statement(StatementKind::Constrain(ConstrainStatement( + make_eq( + method_call(variable("context"), "msg_sender", vec![]), + method_call(variable("context"), "this_address", vec![]), + ), + Some(expression(ExpressionKind::Literal(Literal::Str(format!( + "Function {} can only be called internally", + fname + ))))), + ConstrainKind::Assert, + ))) +} + /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be /// appended into the args hash object. /// @@ -1123,8 +1237,8 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac let let_hasher = mutable_assignment( "hasher", // Assigned to call( - variable_path(chained_path!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path + vec![], // args ), ); @@ -1192,8 +1306,8 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac let let_context = mutable_assignment( "context", // Assigned to call( - variable_path(chained_path!("aztec", "context", &path_snippet, ty, "new")), // Path - vec![inputs_expression, hash_call], // args + variable_path(chained_dep!("aztec", "context", &path_snippet, ty, "new")), // Path + vec![inputs_expression, hash_call], // args ), ); injected_expressions.push(let_context); @@ -1223,8 +1337,8 @@ fn create_avm_context() -> Result { let let_context = mutable_assignment( "context", // Assigned to call( - variable_path(chained_path!("aztec", "context", "AVMContext", "new")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path + vec![], // args ), ); @@ -1259,13 +1373,13 @@ fn create_avm_context() -> Result { /// Any primitive type that can be cast will be casted to a field and pushed to the context. fn abstract_return_values(func: &NoirFunction) -> Option { let current_return_type = func.return_type().typ; - let last_statement = func.def.body.0.last()?; + let last_statement = func.def.body.0.last(); // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size // Doesn't need done until we have settled on a kernel size // TODO: support tuples here and in inputs -> convert into an issue // Check if the return type is an expression, if it is, we can handle it - match last_statement { + match last_statement? { Statement { kind: StatementKind::Expression(expression), .. } => { match current_return_type { // Call serialize on structs, push the whole array, calling push_array @@ -1309,13 +1423,13 @@ fn abstract_return_values(func: &NoirFunction) -> Option { fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { let init_context_call = if unconstrained { call( - variable_path(chained_path!("aztec", "context", "Context", "none")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "context", "Context", "none")), // Path + vec![], // args ) } else { call( - variable_path(chained_path!("aztec", "context", "Context", typ)), // Path - vec![mutable_reference("context")], // args + variable_path(chained_dep!("aztec", "context", "Context", typ)), // Path + vec![mutable_reference("context")], // args ) }; @@ -1436,7 +1550,7 @@ fn make_castable_return_type(expression: Expression) -> Statement { /// } fn create_return_type(ty: &str) -> FunctionReturnType { let path_snippet = ty.to_case(Case::Snake); // e.g. private_circuit_public_inputs or public_circuit_public_inputs - let return_path = chained_path!("aztec", "protocol_types", "abis", &path_snippet, ty); + let return_path = chained_dep!("aztec", "protocol_types", "abis", &path_snippet, ty); return_type(return_path) } @@ -1792,7 +1906,7 @@ fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> let if_statements: Vec = note_types.iter().map(|note_type| format!( "if (note_type_id == {0}::get_note_type_id()) {{ - note_utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) + dep::aztec::note::utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) }}" , note_type)).collect(); @@ -1812,7 +1926,7 @@ fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> note_type_id: Field, serialized_note: [Field; 20] ) -> pub [Field; 4] {{ - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); + let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); {} }}", diff --git a/bootstrap.sh b/bootstrap.sh deleted file mode 100755 index 54129c3d61a..00000000000 --- a/bootstrap.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -eu - -cd $(dirname "$0") - -CMD=${1:-} - -if [ -n "$CMD" ]; then - if [ "$CMD" = "clean" ]; then - git clean -fdx - exit 0 - else - echo "Unknown command: $CMD" - exit 1 - fi -fi - -# Attempt to just pull artefacts from CI and exit on success. -[ -n "${USE_CACHE:-}" ] && ./bootstrap_cache.sh && exit - -./scripts/bootstrap_native.sh -./scripts/bootstrap_packages.sh diff --git a/bootstrap_cache.sh b/bootstrap_cache.sh deleted file mode 100755 index 1cec6c81d8e..00000000000 --- a/bootstrap_cache.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -eu - -cd "$(dirname "$0")" -source ../build-system/scripts/setup_env '' '' mainframe_$USER > /dev/null - -echo -e "\033[1mRetrieving noir packages from remote cache...\033[0m" -extract_repo noir-packages /usr/src/noir/packages ./ -echo -e "\033[1mRetrieving nargo from remote cache...\033[0m" -extract_repo noir /usr/src/noir/target/release ./target/ - -remove_old_images noir-packages -remove_old_images noir diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 5674ae5a39a..f096c220200 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -661,7 +661,6 @@ pub enum Keyword { If, Impl, In, - Internal, Let, Mod, Mut, @@ -704,7 +703,6 @@ impl fmt::Display for Keyword { Keyword::If => write!(f, "if"), Keyword::Impl => write!(f, "impl"), Keyword::In => write!(f, "in"), - Keyword::Internal => write!(f, "internal"), Keyword::Let => write!(f, "let"), Keyword::Mod => write!(f, "mod"), Keyword::Mut => write!(f, "mut"), @@ -750,7 +748,6 @@ impl Keyword { "if" => Keyword::If, "impl" => Keyword::Impl, "in" => Keyword::In, - "internal" => Keyword::Internal, "let" => Keyword::Let, "mod" => Keyword::Mod, "mut" => Keyword::Mut, diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 42ee484bfc9..7448d84cfe1 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -37,7 +37,8 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser { choice((is_pub_crate, is_pub, is_private)) } -/// function_modifiers: 'unconstrained'? (visibility)? 'open'? 'internal'? +/// function_modifiers: 'unconstrained'? (visibility)? 'open'? /// -/// returns (is_unconstrained, visibility, is_open, is_internal) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, FunctionVisibility, bool, bool)> { +/// returns (is_unconstrained, visibility, is_open) for whether each keyword was present +fn function_modifiers() -> impl NoirParser<(bool, FunctionVisibility, bool)> { keyword(Keyword::Unconstrained) .or_not() .then(visibility_modifier()) .then(keyword(Keyword::Open).or_not()) - .then(keyword(Keyword::Internal).or_not()) - .map(|(((unconstrained, visibility), open), internal)| { - (unconstrained.is_some(), visibility, open.is_some(), internal.is_some()) + .map(|((unconstrained, visibility), open)| { + (unconstrained.is_some(), visibility, open.is_some()) }) } diff --git a/compiler/wasm/scripts/build-fixtures.sh b/compiler/wasm/scripts/build-fixtures.sh index 3a2330d4726..4c0505ef519 100755 --- a/compiler/wasm/scripts/build-fixtures.sh +++ b/compiler/wasm/scripts/build-fixtures.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash nargo compile --program-dir ./test/fixtures/simple -nargo compile --program-dir ./test/fixtures/with-deps +nargo compile --program-dir ./test/fixtures/with-deps nargo compile --program-dir ./test/fixtures/noir-contract \ No newline at end of file diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index 4dfe78308e2..f3155dd7528 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -136,7 +136,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { msg_block[i as Field] = 0; i += 1; } else if i < 128 { - let mut len = 8 * msg.len(); + let mut len = 8 * msg.len(); // u128 unsupported for j in 0..16 { msg_block[127 - j] = len as u8; len >>= 8; diff --git a/package.json b/package.json index 4987602c709..fcf36c9b969 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ ], "scripts": { "build": "yarn workspaces foreach --parallel --topological-dev --verbose run build", - "test": "yarn workspaces foreach run test", + "test": "yarn workspaces foreach --parallel --verbose run test", "test:integration": "yarn workspace integration-tests test", "clean:workspaces": "yarn workspaces foreach --exclude @noir-lang/root run clean", "clean:root": "rm -rf ./result ./target ./packages", diff --git a/scripts/bootstrap_native.sh b/scripts/bootstrap_native.sh deleted file mode 100755 index 974f0edcfec..00000000000 --- a/scripts/bootstrap_native.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -eu - -cd $(dirname "$0")/.. - -# If this project has been subrepod into another project, set build data manually. -export SOURCE_DATE_EPOCH=$(date +%s) -export GIT_DIRTY=false -if [ -f ".gitrepo" ]; then - export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) -else - export GIT_COMMIT=$(git rev-parse --verify HEAD) -fi - -# Check if the 'cargo' command is available in the system -if ! command -v cargo > /dev/null; then - echo "Cargo is not installed. Please install Cargo and the Rust toolchain." - exit 1 -fi - -# Build native. -if [ -n "${DEBUG:-}" ]; then - cargo build -else - cargo build --release -fi diff --git a/scripts/bootstrap_packages.sh b/scripts/bootstrap_packages.sh deleted file mode 100755 index 47ffe12beec..00000000000 --- a/scripts/bootstrap_packages.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -set -eu - -cd $(dirname "$0")/.. - -./.github/scripts/wasm-bindgen-install.sh - -# If this project has been subrepod into another project, set build data manually. -export SOURCE_DATE_EPOCH=$(date +%s) -export GIT_DIRTY=false -if [ -f ".gitrepo" ]; then - export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) -else - export GIT_COMMIT=$(git rev-parse --verify HEAD) -fi - -yarn --immutable -yarn build - -# We create a folder called packages, that contains each package as it would be published to npm, named correctly. -# These can be useful for testing, or portaling into other projects. -yarn workspaces foreach pack - -rm -rf packages && mkdir -p packages -tar zxfv acvm-repo/acvm_js/package.tgz -C packages && mv packages/package packages/acvm_js -tar zxfv compiler/wasm/package.tgz -C packages && mv packages/package packages/noir_wasm -tar zxfv tooling/noir_codegen/package.tgz -C packages && mv packages/package packages/noir_codegen -tar zxfv tooling/noir_js/package.tgz -C packages && mv packages/package packages/noir_js -tar zxfv tooling/noir_js_backend_barretenberg/package.tgz -C packages && mv packages/package packages/backend_barretenberg -tar zxfv tooling/noir_js_types/package.tgz -C packages && mv packages/package packages/types -tar zxfv tooling/noirc_abi_wasm/package.tgz -C packages && mv packages/package packages/noirc_abi diff --git a/scripts/test_js_packages.sh b/scripts/test_js_packages.sh deleted file mode 100755 index e1e10c543e0..00000000000 --- a/scripts/test_js_packages.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -eu - -cd $(dirname "$0")/.. - -./.github/scripts/wasm-bindgen-install.sh - -# If this project has been subrepod into another project, set build data manually. -export SOURCE_DATE_EPOCH=$(date +%s) -export GIT_DIRTY=false -if [ -f ".gitrepo" ]; then - export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) -else - export GIT_COMMIT=$(git rev-parse --verify HEAD) -fi - -cargo build --release -export PATH="${PATH}:/usr/src/noir/target/release/" - -yarn --immutable -yarn build -./.github/scripts/playwright-install.sh - -./scripts/test.sh -yarn test diff --git a/scripts/test_native.sh b/scripts/test_native.sh deleted file mode 100755 index 9b9aa0ce4d7..00000000000 --- a/scripts/test_native.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -eu - -cd $(dirname "$0")/.. - -# If this project has been subrepod into another project, set build data manually. -export SOURCE_DATE_EPOCH=$(date +%s) -export GIT_DIRTY=false -if [ -f ".gitrepo" ]; then - export GIT_COMMIT=$(awk '/commit =/ {print $3}' .gitrepo) -else - export GIT_COMMIT=$(git rev-parse --verify HEAD) -fi - -cargo fmt --all --check -cargo clippy --workspace --locked --release -cargo test --workspace --locked --release diff --git a/test_programs/compile_success_contract/simple_contract/src/main.nr b/test_programs/compile_success_contract/simple_contract/src/main.nr index fea0ae08c22..ed90ac8bd1d 100644 --- a/test_programs/compile_success_contract/simple_contract/src/main.nr +++ b/test_programs/compile_success_contract/simple_contract/src/main.nr @@ -5,10 +5,10 @@ contract Foo { fn triple(x: Field) -> pub Field { x * 3 } - internal fn quadruple(x: Field) -> pub Field { + fn quadruple(x: Field) -> pub Field { x * 4 } - open internal fn skibbidy(x: Field) -> pub Field { + open fn skibbidy(x: Field) -> pub Field { x * 5 } // Regression for issue #3344 diff --git a/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Prover.toml b/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/noir_test_failure/should_fail_suite_with_one_failure/src/main.nr b/test_programs/noir_test_failure/should_fail_suite_with_one_failure/src/main.nr new file mode 100644 index 00000000000..8ed9003164a --- /dev/null +++ b/test_programs/noir_test_failure/should_fail_suite_with_one_failure/src/main.nr @@ -0,0 +1,11 @@ +/// Test to make sure the entire test suite fails, even if some of the tests pass! + +#[test] +fn this_will_pass() { + assert(true); +} + +#[test] +fn this_will_fail() { + assert(false); +} diff --git a/tooling/nargo_cli/src/cli/noir_template_files/contract.nr b/tooling/nargo_cli/src/cli/noir_template_files/contract.nr index e126726393d..3cd3b5b3766 100644 --- a/tooling/nargo_cli/src/cli/noir_template_files/contract.nr +++ b/tooling/nargo_cli/src/cli/noir_template_files/contract.nr @@ -1,5 +1,5 @@ contract Main { - internal fn double(x: Field) -> pub Field { x * 2 } + fn double(x: Field) -> pub Field { x * 2 } fn triple(x: Field) -> pub Field { x * 3 } fn quadruple(x: Field) -> pub Field { double(double(x)) } } diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 503fd5afdd4..68660d62d23 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -116,10 +116,10 @@ pub(crate) fn run( }; } - if test_report.iter().any(|(_, status)| !matches!(status, TestStatus::Fail { .. })) { - Ok(()) - } else { + if test_report.iter().any(|(_, status)| matches!(status, TestStatus::Fail { .. })) { Err(CliError::Generic(String::new())) + } else { + Ok(()) } } diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr index a03b8774700..c5b19a686d2 100644 --- a/tooling/nargo_fmt/tests/expected/contract.nr +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -8,8 +8,7 @@ contract Benchmarking { use dep::value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; use dep::aztec::{ - context::{Context}, - note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, + context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, types::address::{AztecAddress} diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr index a03b8774700..c5b19a686d2 100644 --- a/tooling/nargo_fmt/tests/input/contract.nr +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -8,8 +8,7 @@ contract Benchmarking { use dep::value_note::{utils::{increment, decrement}, value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}}; use dep::aztec::{ - context::{Context}, - note::{utils as note_utils, note_getter_options::NoteGetterOptions, note_header::NoteHeader}, + context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, types::address::{AztecAddress} From fe8f2776ccfde29209a2c3fc162311c99e4f59be Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 5 Mar 2024 13:49:54 -0500 Subject: [PATCH 039/416] feat: Sync from aztec-packages (#4483) BEGIN_COMMIT_OVERRIDE feat: Add init check by default to public fns (https://github.com/AztecProtocol/aztec-packages/pull/4897) feat: Enable public constructor functions (https://github.com/AztecProtocol/aztec-packages/pull/4896) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .aztec-sync-commit | 2 +- acvm-repo/acvm/src/compiler/optimizers/mod.rs | 12 ++++---- aztec_macros/src/lib.rs | 30 ++++--------------- noir_stdlib/src/sha512.nr | 2 +- 4 files changed, 14 insertions(+), 32 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 78e53d61647..6841c89b691 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -73d640a4a033f0c865d45da470ef40c1fb03a844 +7ff9b71d8d87fc93ae7dbd8ba63f5176b0cd17be diff --git a/acvm-repo/acvm/src/compiler/optimizers/mod.rs b/acvm-repo/acvm/src/compiler/optimizers/mod.rs index 599bdabd420..04d3f99a408 100644 --- a/acvm-repo/acvm/src/compiler/optimizers/mod.rs +++ b/acvm-repo/acvm/src/compiler/optimizers/mod.rs @@ -1,6 +1,6 @@ use acir::circuit::{Circuit, Opcode}; -mod constant_backpropagation; +// mod constant_backpropagation; mod general; mod redundant_range; mod unused_memory; @@ -9,7 +9,7 @@ pub(crate) use general::GeneralOptimizer; pub(crate) use redundant_range::RangeOptimizer; use tracing::info; -use self::constant_backpropagation::ConstantBackpropagationOptimizer; +// use self::constant_backpropagation::ConstantBackpropagationOptimizer; use self::unused_memory::UnusedMemoryOptimizer; use super::{transform_assert_messages, AcirTransformationMap}; @@ -58,16 +58,16 @@ pub(super) fn optimize_internal(acir: Circuit) -> (Circuit, Vec) { let (acir, acir_opcode_positions) = memory_optimizer.remove_unused_memory_initializations(acir_opcode_positions); - let (acir, acir_opcode_positions) = - ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + // let (acir, acir_opcode_positions) = + // ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); // Range optimization pass let range_optimizer = RangeOptimizer::new(acir); let (acir, acir_opcode_positions) = range_optimizer.replace_redundant_ranges(acir_opcode_positions); - let (acir, acir_opcode_positions) = - ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); + // let (acir, acir_opcode_positions) = + // ConstantBackpropagationOptimizer::backpropagate_constants(acir, acir_opcode_positions); info!("Number of opcodes after: {}", acir.opcodes.len()); diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index f860f2c465a..f9df3f10129 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -451,7 +451,6 @@ fn transform_module( is_internal = true; } else if is_custom_attribute(&secondary_attribute, "aztec(public)") { is_public = true; - insert_init_check = false; } else if is_custom_attribute(&secondary_attribute, "aztec(public-vm)") { is_public_vm = true; } @@ -673,15 +672,6 @@ fn transform_function( // Add initialization check if insert_init_check { - if ty == "Public" { - let error = AztecMacroError::UnsupportedAttributes { - span: func.def.name.span(), - secondary_message: Some( - "public functions do not yet support initialization check".to_owned(), - ), - }; - return Err(error); - } let init_check = create_init_check(); func.def.body.0.insert(0, init_check); } @@ -711,16 +701,7 @@ fn transform_function( // Before returning mark the contract as initialized if is_initializer { - if ty == "Public" { - let error = AztecMacroError::UnsupportedAttributes { - span: func.def.name.span(), - secondary_message: Some( - "public functions cannot yet be used as initializers".to_owned(), - ), - }; - return Err(error); - } - let mark_initialized = create_mark_as_initialized(); + let mark_initialized = create_mark_as_initialized(ty); func.def.body.0.push(mark_initialized); } @@ -1179,9 +1160,10 @@ fn create_init_check() -> Statement { /// ```noir /// mark_as_initialized(&mut context); /// ``` -fn create_mark_as_initialized() -> Statement { +fn create_mark_as_initialized(ty: &str) -> Statement { + let name = if ty == "Public" { "mark_as_initialized_public" } else { "mark_as_initialized" }; make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", "mark_as_initialized")), + variable_path(chained_dep!("aztec", "initializer", name)), vec![mutable_reference("context")], ))) } @@ -1373,13 +1355,13 @@ fn create_avm_context() -> Result { /// Any primitive type that can be cast will be casted to a field and pushed to the context. fn abstract_return_values(func: &NoirFunction) -> Option { let current_return_type = func.return_type().typ; - let last_statement = func.def.body.0.last(); + let last_statement = func.def.body.0.last()?; // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size // Doesn't need done until we have settled on a kernel size // TODO: support tuples here and in inputs -> convert into an issue // Check if the return type is an expression, if it is, we can handle it - match last_statement? { + match last_statement { Statement { kind: StatementKind::Expression(expression), .. } => { match current_return_type { // Call serialize on structs, push the whole array, calling push_array diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index f3155dd7528..4dfe78308e2 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -136,7 +136,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { msg_block[i as Field] = 0; i += 1; } else if i < 128 { - let mut len = 8 * msg.len(); // u128 unsupported + let mut len = 8 * msg.len(); for j in 0..16 { msg_block[127 - j] = len as u8; len >>= 8; From 761734e6cb3ff5911aa85d0cee96ad26092b4905 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 5 Mar 2024 21:12:58 +0000 Subject: [PATCH 040/416] feat: run tests in parallel in `nargo test` (#4484) # Description ## Problem\* Resolves ## Summary\* This is a dirty hack to get tests running in parallel. To do this, we've given up printing the test results in a stream as they're run but we instead wait for all tests in a package to be run before we print them out in one go. This takes the noir-protocol-circuits test suite from taking 1:46s to 27s. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/lsp/src/requests/test_run.rs | 2 +- tooling/nargo/src/ops/test.rs | 14 +++- tooling/nargo_cli/src/cli/test_cmd.rs | 114 ++++++++++++++++++-------- 3 files changed, 92 insertions(+), 38 deletions(-) diff --git a/tooling/lsp/src/requests/test_run.rs b/tooling/lsp/src/requests/test_run.rs index 0b88d814265..1844a3d9bf0 100644 --- a/tooling/lsp/src/requests/test_run.rs +++ b/tooling/lsp/src/requests/test_run.rs @@ -84,7 +84,7 @@ fn on_test_run_request_inner( let test_result = run_test( &state.solver, &mut context, - test_function, + &test_function, false, None, &CompileOptions::default(), diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 92c09ec889e..8ddcb5cf8d2 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -14,10 +14,16 @@ pub enum TestStatus { CompileError(FileDiagnostic), } +impl TestStatus { + pub fn failed(&self) -> bool { + !matches!(self, TestStatus::Pass) + } +} + pub fn run_test( blackbox_solver: &B, context: &mut Context, - test_function: TestFunction, + test_function: &TestFunction, show_output: bool, foreign_call_resolver_url: Option<&str>, config: &CompileOptions, @@ -45,7 +51,7 @@ pub fn run_test( /// that a constraint was never satisfiable. /// An example of this is the program `assert(false)` /// In that case, we check if the test function should fail, and if so, we return `TestStatus::Pass`. -fn test_status_program_compile_fail(err: CompileError, test_function: TestFunction) -> TestStatus { +fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunction) -> TestStatus { // The test has failed compilation, but it should never fail. Report error. if !test_function.should_fail() { return TestStatus::CompileError(err.into()); @@ -70,7 +76,7 @@ fn test_status_program_compile_fail(err: CompileError, test_function: TestFuncti /// We now check whether execution passed/failed and whether it should have /// passed/failed to determine the test status. fn test_status_program_compile_pass( - test_function: TestFunction, + test_function: &TestFunction, debug: DebugInfo, circuit_execution: Result, ) -> TestStatus { @@ -109,7 +115,7 @@ fn test_status_program_compile_pass( } fn check_expected_failure_message( - test_function: TestFunction, + test_function: &TestFunction, failed_assertion: Option, error_diagnostic: Option, ) -> TestStatus { diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 68660d62d23..47face9943a 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -11,11 +11,14 @@ use nargo::{ parse_all, prepare_package, }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; -use noirc_driver::{file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING}; +use noirc_driver::{ + check_crate, file_manager_with_stdlib, CompileOptions, NOIR_ARTIFACT_VERSION_STRING, +}; use noirc_frontend::{ graph::CrateName, hir::{FunctionNameMatch, ParsedFiles}, }; +use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use crate::{backends::Backend, cli::check_cmd::check_crate_and_report_errors, errors::CliError}; @@ -82,15 +85,12 @@ pub(crate) fn run( None => FunctionNameMatch::Anything, }; - let blackbox_solver = Bn254BlackBoxSolver::new(); - let test_reports: Vec> = workspace .into_iter() .map(|package| { - run_tests( + run_tests::( &workspace_file_manager, &parsed_files, - &blackbox_solver, package, pattern, args.show_output, @@ -116,24 +116,77 @@ pub(crate) fn run( }; } - if test_report.iter().any(|(_, status)| matches!(status, TestStatus::Fail { .. })) { + if test_report.iter().any(|(_, status)| status.failed()) { Err(CliError::Generic(String::new())) } else { Ok(()) } } -#[allow(clippy::too_many_arguments)] -fn run_tests( +fn run_tests( file_manager: &FileManager, parsed_files: &ParsedFiles, - blackbox_solver: &S, package: &Package, fn_name: FunctionNameMatch, show_output: bool, foreign_call_resolver_url: Option<&str>, compile_options: &CompileOptions, ) -> Result, CliError> { + let test_functions = + get_tests_in_package(file_manager, parsed_files, package, fn_name, compile_options)?; + + let count_all = test_functions.len(); + + let plural = if count_all == 1 { "" } else { "s" }; + println!("[{}] Running {count_all} test function{plural}", package.name); + + let test_report: Vec<(String, TestStatus)> = test_functions + .into_par_iter() + .map(|test_name| { + // This is really hacky but we can't share `Context` or `S` across threads. + // We then need to construct a separate copy for each test. + + let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); + check_crate( + &mut context, + crate_id, + compile_options.deny_warnings, + compile_options.disable_macros, + ) + .expect("Any errors should have occurred when collecting test functions"); + + let test_functions = context.get_all_test_functions_in_crate_matching( + &crate_id, + FunctionNameMatch::Exact(&test_name), + ); + let (_, test_function) = test_functions.first().expect("Test function should exist"); + + let blackbox_solver = S::default(); + + let test_output = run_test( + &blackbox_solver, + &mut context, + test_function, + show_output, + foreign_call_resolver_url, + compile_options, + ); + + (test_name, test_output) + }) + .collect(); + + display_test_report(file_manager, package, compile_options, &test_report)?; + Ok(test_report) +} + +fn get_tests_in_package( + file_manager: &FileManager, + parsed_files: &ParsedFiles, + package: &Package, + fn_name: FunctionNameMatch, + compile_options: &CompileOptions, +) -> Result, CliError> { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); check_crate_and_report_errors( &mut context, @@ -143,30 +196,27 @@ fn run_tests( compile_options.silence_warnings, )?; - let test_functions = context.get_all_test_functions_in_crate_matching(&crate_id, fn_name); - let count_all = test_functions.len(); - - let plural = if count_all == 1 { "" } else { "s" }; - println!("[{}] Running {count_all} test function{plural}", package.name); + Ok(context + .get_all_test_functions_in_crate_matching(&crate_id, fn_name) + .into_iter() + .map(|(test_name, _)| test_name) + .collect()) +} +fn display_test_report( + file_manager: &FileManager, + package: &Package, + compile_options: &CompileOptions, + test_report: &[(String, TestStatus)], +) -> Result<(), CliError> { let writer = StandardStream::stderr(ColorChoice::Always); let mut writer = writer.lock(); - let mut test_report: Vec<(String, TestStatus)> = Vec::new(); - for (test_name, test_function) in test_functions { + for (test_name, test_status) in test_report { write!(writer, "[{}] Testing {test_name}... ", package.name) .expect("Failed to write to stderr"); writer.flush().expect("Failed to flush writer"); - let test_status = run_test( - blackbox_solver, - &mut context, - test_function, - show_output, - foreign_call_resolver_url, - compile_options, - ); - match &test_status { TestStatus::Pass { .. } => { writer @@ -181,7 +231,7 @@ fn run_tests( writeln!(writer, "FAIL\n{message}\n").expect("Failed to write to stderr"); if let Some(diag) = error_diagnostic { noirc_errors::reporter::report_all( - context.file_manager.as_file_map(), + file_manager.as_file_map(), &[diag.clone()], compile_options.deny_warnings, compile_options.silence_warnings, @@ -190,23 +240,21 @@ fn run_tests( } TestStatus::CompileError(err) => { noirc_errors::reporter::report_all( - context.file_manager.as_file_map(), + file_manager.as_file_map(), &[err.clone()], compile_options.deny_warnings, compile_options.silence_warnings, ); } } - - test_report.push((test_name, test_status)); - writer.reset().expect("Failed to reset writer"); } write!(writer, "[{}] ", package.name).expect("Failed to write to stderr"); - let count_failed = - test_report.iter().filter(|(_, status)| !matches!(status, TestStatus::Pass)).count(); + let count_all = test_report.len(); + let count_failed = test_report.iter().filter(|(_, status)| status.failed()).count(); + let plural = if count_all == 1 { "" } else { "s" }; if count_failed == 0 { writer.set_color(ColorSpec::new().set_fg(Some(Color::Green))).expect("Failed to set color"); write!(writer, "{count_all} test{plural} passed").expect("Failed to write to stderr"); @@ -231,5 +279,5 @@ fn run_tests( writer.reset().expect("Failed to reset writer"); } - Ok(test_report) + Ok(()) } From 1ed9061d1f2e32603ece34653c0e6d224621e456 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:50:22 +0000 Subject: [PATCH 041/416] chore: update cargo deny config (#4486) # Description ## Problem\* Resolves ## Summary\* cargo deny is deprecating a bunch of its config file so this PR updates to the new format. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- deny.toml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/deny.toml b/deny.toml index 72150f08a3c..578f8427263 100644 --- a/deny.toml +++ b/deny.toml @@ -2,11 +2,13 @@ # More documentation for the advisories section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] -vulnerability = "deny" -unmaintained = "warn" -unsound = "warn" +version = 2 yanked = "warn" -notice = "warn" + +ignore = [ + "RUSTSEC-2020-0168", # mach unmaintained + "RUSTSEC-2020-0016" # net2 unmaintained +] # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: @@ -32,9 +34,8 @@ skip = [] skip-tree = [] [licenses] -unlicensed = "deny" +version = 2 confidence-threshold = 0.9 -# copyleft = "deny" # List of explicitly allowed licenses # See https://spdx.org/licenses/ for list of possible licenses From 18e1fe228f9add11c6f33f889d62d69cf7fec450 Mon Sep 17 00:00:00 2001 From: Tom French Date: Wed, 6 Mar 2024 14:00:31 +0000 Subject: [PATCH 042/416] chore: pull out separate function for compiling and running a test --- tooling/nargo_cli/src/cli/test_cmd.rs | 77 ++++++++++++++++----------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 47face9943a..2828aaf01eb 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -5,10 +5,8 @@ use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use fm::FileManager; use nargo::{ - insert_all_files_for_workspace_into_file_manager, - ops::{run_test, TestStatus}, - package::Package, - parse_all, prepare_package, + insert_all_files_for_workspace_into_file_manager, ops::TestStatus, package::Package, parse_all, + prepare_package, }; use nargo_toml::{get_package_manifest, resolve_workspace_from_toml, PackageSelection}; use noirc_driver::{ @@ -18,7 +16,7 @@ use noirc_frontend::{ graph::CrateName, hir::{FunctionNameMatch, ParsedFiles}, }; -use rayon::prelude::{IntoParallelIterator, ParallelIterator}; +use rayon::prelude::{IntoParallelIterator, ParallelBridge, ParallelIterator}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use crate::{backends::Backend, cli::check_cmd::check_crate_and_report_errors, errors::CliError}; @@ -87,6 +85,7 @@ pub(crate) fn run( let test_reports: Vec> = workspace .into_iter() + .par_bridge() .map(|package| { run_tests::( &workspace_file_manager, @@ -143,36 +142,17 @@ fn run_tests( let test_report: Vec<(String, TestStatus)> = test_functions .into_par_iter() .map(|test_name| { - // This is really hacky but we can't share `Context` or `S` across threads. - // We then need to construct a separate copy for each test. - - let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); - check_crate( - &mut context, - crate_id, - compile_options.deny_warnings, - compile_options.disable_macros, - ) - .expect("Any errors should have occurred when collecting test functions"); - - let test_functions = context.get_all_test_functions_in_crate_matching( - &crate_id, - FunctionNameMatch::Exact(&test_name), - ); - let (_, test_function) = test_functions.first().expect("Test function should exist"); - - let blackbox_solver = S::default(); - - let test_output = run_test( - &blackbox_solver, - &mut context, - test_function, + let status = run_test::( + file_manager, + parsed_files, + package, + &test_name, show_output, foreign_call_resolver_url, compile_options, ); - (test_name, test_output) + (test_name, status) }) .collect(); @@ -180,6 +160,43 @@ fn run_tests( Ok(test_report) } +fn run_test( + file_manager: &FileManager, + parsed_files: &ParsedFiles, + package: &Package, + fn_name: &str, + show_output: bool, + foreign_call_resolver_url: Option<&str>, + compile_options: &CompileOptions, +) -> TestStatus { + // This is really hacky but we can't share `Context` or `S` across threads. + // We then need to construct a separate copy for each test. + + let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); + check_crate( + &mut context, + crate_id, + compile_options.deny_warnings, + compile_options.disable_macros, + ) + .expect("Any errors should have occurred when collecting test functions"); + + let test_functions = context + .get_all_test_functions_in_crate_matching(&crate_id, FunctionNameMatch::Exact(fn_name)); + let (_, test_function) = test_functions.first().expect("Test function should exist"); + + let blackbox_solver = S::default(); + + nargo::ops::run_test( + &blackbox_solver, + &mut context, + test_function, + show_output, + foreign_call_resolver_url, + compile_options, + ) +} + fn get_tests_in_package( file_manager: &FileManager, parsed_files: &ParsedFiles, From ea9a834ec9ddf6a23d8e0f28b1f7784f484eec05 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Wed, 6 Mar 2024 14:22:15 +0000 Subject: [PATCH 043/416] chore: Move templated code for assert_message into the stdlib (#4475) # Description ## Problem\* We currently have a noirc_macros crate which checks for the stdlib and inserts two functions into the stdlib. The motivation being that we did not want users to be able to see these functions. Introducing a new crate for this reason, does not seem to be worth it since its ~7 lines of noir code. ## Solution\* This PR removes the noirc_macros crate and copies the noir code that was being pasted into the stdlib, directly into the stdlib ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Tom French --- Cargo.lock | 9 --- Cargo.toml | 3 +- compiler/noirc_driver/Cargo.toml | 1 - compiler/noirc_driver/src/lib.rs | 7 +- .../src/hir/resolution/resolver.rs | 8 +- noir_stdlib/src/internal.nr | 12 +++ noir_stdlib/src/lib.nr | 1 + noirc_macros/Cargo.toml | 14 ---- noirc_macros/src/lib.rs | 73 ------------------- 9 files changed, 22 insertions(+), 106 deletions(-) create mode 100644 noir_stdlib/src/internal.nr delete mode 100644 noirc_macros/Cargo.toml delete mode 100644 noirc_macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b2b6f8037bb..18c1f7ad40e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2946,7 +2946,6 @@ dependencies = [ "noirc_errors", "noirc_evaluator", "noirc_frontend", - "noirc_macros", "rust-embed", "serde", "thiserror", @@ -3012,14 +3011,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "noirc_macros" -version = "0.24.0" -dependencies = [ - "iter-extended", - "noirc_frontend", -] - [[package]] name = "noirc_printable_type" version = "0.24.0" diff --git a/Cargo.toml b/Cargo.toml index 7d5da7b00d0..38f39137360 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,8 @@ [workspace] members = [ - # Macros crates for metaprogramming + # Aztec Macro crate for metaprogramming "aztec_macros", - "noirc_macros", # Compiler crates "compiler/noirc_evaluator", "compiler/noirc_frontend", diff --git a/compiler/noirc_driver/Cargo.toml b/compiler/noirc_driver/Cargo.toml index 681976735f3..a7fe0b4b610 100644 --- a/compiler/noirc_driver/Cargo.toml +++ b/compiler/noirc_driver/Cargo.toml @@ -26,4 +26,3 @@ tracing.workspace = true thiserror.workspace = true aztec_macros = { path = "../../aztec_macros" } -noirc_macros = { path = "../../noirc_macros" } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 11f53cdb749..97fc66688e1 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -238,12 +238,9 @@ pub fn check_crate( disable_macros: bool, ) -> CompilationResult<()> { let macros: Vec<&dyn MacroProcessor> = if disable_macros { - vec![&noirc_macros::AssertMessageMacro as &dyn MacroProcessor] + vec![] } else { - vec![ - &aztec_macros::AztecMacro as &dyn MacroProcessor, - &noirc_macros::AssertMessageMacro as &dyn MacroProcessor, - ] + vec![&aztec_macros::AztecMacro as &dyn MacroProcessor] }; let mut errors = vec![]; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7f9e48353a7..7789c06ca69 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1256,13 +1256,17 @@ impl<'a> Resolver<'a> { let is_in_stdlib = self.path_resolver.module_id().krate.is_stdlib(); let assert_msg_call_path = if is_in_stdlib { ExpressionKind::Variable(Path { - segments: vec![Ident::from("resolve_assert_message")], + segments: vec![Ident::from("internal"), Ident::from("resolve_assert_message")], kind: PathKind::Crate, span, }) } else { ExpressionKind::Variable(Path { - segments: vec![Ident::from("std"), Ident::from("resolve_assert_message")], + segments: vec![ + Ident::from("std"), + Ident::from("internal"), + Ident::from("resolve_assert_message"), + ], kind: PathKind::Dep, span, }) diff --git a/noir_stdlib/src/internal.nr b/noir_stdlib/src/internal.nr new file mode 100644 index 00000000000..8d5c01dda7f --- /dev/null +++ b/noir_stdlib/src/internal.nr @@ -0,0 +1,12 @@ +// This file contains functions which should only be used in calls injected by the Noir compiler. +// These functions should not be called manually in user code. +// +// Changes to this file will not be considered breaking. + +#[oracle(assert_message)] +unconstrained fn assert_message_oracle(_input: T) {} +unconstrained pub fn resolve_assert_message(input: T, condition: bool) { + if !condition { + assert_message_oracle(input); + } +} diff --git a/noir_stdlib/src/lib.nr b/noir_stdlib/src/lib.nr index ebde4b88858..90c04472066 100644 --- a/noir_stdlib/src/lib.nr +++ b/noir_stdlib/src/lib.nr @@ -26,6 +26,7 @@ mod default; mod prelude; mod uint128; mod bigint; +mod internal; // Oracle calls are required to be wrapped in an unconstrained function // Thus, the only argument to the `println` oracle is expected to always be an ident diff --git a/noirc_macros/Cargo.toml b/noirc_macros/Cargo.toml deleted file mode 100644 index 699e6b01cae..00000000000 --- a/noirc_macros/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "noirc_macros" -version.workspace = true -authors.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -repository.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -noirc_frontend.workspace = true -iter-extended.workspace = true \ No newline at end of file diff --git a/noirc_macros/src/lib.rs b/noirc_macros/src/lib.rs deleted file mode 100644 index 9a916843200..00000000000 --- a/noirc_macros/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -use noirc_frontend::hir::def_collector::dc_crate::UnresolvedFunctions; -use noirc_frontend::hir::def_collector::dc_crate::UnresolvedTraitImpl; -use noirc_frontend::macros_api::parse_program; -use noirc_frontend::macros_api::HirContext; -use noirc_frontend::macros_api::SortedModule; -use noirc_frontend::macros_api::{CrateId, FileId}; -use noirc_frontend::macros_api::{MacroError, MacroProcessor}; - -pub struct AssertMessageMacro; - -impl MacroProcessor for AssertMessageMacro { - fn process_untyped_ast( - &self, - ast: SortedModule, - crate_id: &CrateId, - _context: &HirContext, - ) -> Result { - transform(ast, crate_id) - } - - fn process_unresolved_traits_impls( - &self, - _crate_id: &CrateId, - _context: &mut HirContext, - _unresolved_traits_impls: &[UnresolvedTraitImpl], - _collected_functions: &mut Vec, - ) -> Result<(), (MacroError, FileId)> { - Ok(()) - } - - // This macro does not need to process any information after name resolution - fn process_typed_ast( - &self, - _crate_id: &CrateId, - _context: &mut HirContext, - ) -> Result<(), (MacroError, FileId)> { - Ok(()) - } -} - -fn transform(ast: SortedModule, crate_id: &CrateId) -> Result { - let ast = add_resolve_assert_message_funcs(ast, crate_id)?; - - Ok(ast) -} - -fn add_resolve_assert_message_funcs( - mut ast: SortedModule, - crate_id: &CrateId, -) -> Result { - if !crate_id.is_stdlib() { - return Ok(ast); - } - let assert_message_oracles = " - #[oracle(assert_message)] - unconstrained fn assert_message_oracle(_input: T) {} - unconstrained pub fn resolve_assert_message(input: T, condition: bool) { - if !condition { - assert_message_oracle(input); - } - }"; - - let (assert_msg_funcs_ast, errors) = parse_program(assert_message_oracles); - assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); - - let assert_msg_funcs_ast = assert_msg_funcs_ast.into_sorted(); - - for func in assert_msg_funcs_ast.functions { - ast.functions.push(func) - } - - Ok(ast) -} From 33c1ef70e7859fdee7babfb5d38191f53e73a0df Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:19:50 +0100 Subject: [PATCH 044/416] fix: iterative flattening pass (#4492) # Description ## Problem\* Resolves #4449 ## Summary\* The issue is fixed by implementing an iterative version of the flattening pass. ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- .../src/ssa/opt/flatten_cfg.rs | 543 ++++++++++-------- 1 file changed, 312 insertions(+), 231 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 943a57c1bc0..46f1e7a2765 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -199,22 +199,47 @@ struct Context<'f> { /// found, the top of this conditions stack is popped since we are no longer under that /// condition. If we are under multiple conditions (a nested if), the topmost condition is /// the most recent condition combined with all previous conditions via `And` instructions. - conditions: Vec<(BasicBlockId, ValueId)>, + condition_stack: Vec, /// Maps SSA array values with a slice type to their size. /// This is maintained by appropriate calls to the `SliceCapacityTracker` and is used by the `ValueMerger`. slice_sizes: HashMap, + + /// Stack of block arguments + /// When processing a block, we pop this stack to get its arguments + /// and at the end we push the arguments for his successor + arguments_stack: Vec>, } +#[derive(Clone)] pub(crate) struct Store { old_value: ValueId, new_value: ValueId, } -struct Branch { - condition: ValueId, +#[derive(Clone)] +struct ConditionalBranch { + // Contains the last processed block during the processing of the branch. last_block: BasicBlockId, + // The unresolved condition of the branch + old_condition: ValueId, + // The condition of the branch + condition: ValueId, + // The store values accumulated when processing the branch store_values: HashMap, + // The allocations accumulated when processing the branch + local_allocations: HashSet, +} + +struct ConditionalContext { + // Condition from the conditional statement + condition: ValueId, + // Block containing the conditional statement + entry_block: BasicBlockId, + // First block of the then branch + then_branch: ConditionalBranch, + // First block of the else branch + else_branch: Option, } fn flatten_function_cfg(function: &mut Function) { @@ -233,90 +258,117 @@ fn flatten_function_cfg(function: &mut Function) { store_values: HashMap::default(), local_allocations: HashSet::new(), branch_ends, - conditions: Vec::new(), slice_sizes: HashMap::default(), + condition_stack: Vec::new(), + arguments_stack: Vec::new(), }; context.flatten(); } impl<'f> Context<'f> { fn flatten(&mut self) { - // Start with following the terminator of the entry block since we don't - // need to flatten the entry block into itself. - self.handle_terminator(self.inserter.function.entry_block()); + // Flatten the CFG by inlining all instructions from the queued blocks + // until all blocks have been flattened. + // We follow the terminator of each block to determine which blocks to + // process next + let mut queue = vec![self.inserter.function.entry_block()]; + while let Some(block) = queue.pop() { + self.inline_block(block); + let to_process = self.handle_terminator(block, &queue); + for incoming_block in to_process { + if !queue.contains(&incoming_block) { + queue.push(incoming_block); + } + } + } } - /// Check the terminator of the given block and recursively inline any blocks reachable from - /// it. Since each block from a jmpif terminator is inlined successively, we must handle - /// instructions with side effects like constrain and store specially to preserve correctness. - /// For these instructions we must keep track of what the current condition is and modify - /// the instructions according to the module-level comment at the top of this file. Note that - /// the current condition is all the jmpif conditions required to reach the current block, - /// combined via `And` instructions. - /// - /// Returns the last block to be inlined. This is either the return block of the function or, - /// if self.conditions is not empty, the end block of the most recent condition. - fn handle_terminator(&mut self, block: BasicBlockId) -> BasicBlockId { - // As we recursively flatten inner blocks, we need to track the slice information - // for the outer block before we start recursively inlining - let outer_block_instructions = self.inserter.function.dfg[block].instructions(); - let mut capacity_tracker = SliceCapacityTracker::new(&self.inserter.function.dfg); - for instruction in outer_block_instructions { - let results = self.inserter.function.dfg.instruction_results(*instruction); - let instruction = &self.inserter.function.dfg[*instruction]; + /// Returns the updated condition so that + /// it is 'AND-ed' with the previous condition (if any) + fn link_condition(&mut self, condition: ValueId) -> ValueId { + // Retrieve the previous condition + if let Some(context) = self.condition_stack.last() { + let previous_branch = context.else_branch.as_ref().unwrap_or(&context.then_branch); + let and = Instruction::binary(BinaryOp::And, previous_branch.condition, condition); + self.insert_instruction(and, CallStack::new()) + } else { + condition + } + } + + /// Returns the current condition + fn get_last_condition(&self) -> Option { + self.condition_stack.last().map(|context| match &context.else_branch { + Some(else_branch) => else_branch.condition, + None => context.then_branch.condition, + }) + } + + // Inline all instructions from the given block into the entry block, and track slice capacities + fn inline_block(&mut self, block: BasicBlockId) { + if self.inserter.function.entry_block() == block { + // we do not inline the entry block into itself + // for the outer block before we start inlining + let outer_block_instructions = self.inserter.function.dfg[block].instructions(); + let mut capacity_tracker = SliceCapacityTracker::new(&self.inserter.function.dfg); + for instruction in outer_block_instructions { + let results = self.inserter.function.dfg.instruction_results(*instruction); + let instruction = &self.inserter.function.dfg[*instruction]; + capacity_tracker.collect_slice_information( + instruction, + &mut self.slice_sizes, + results.to_vec(), + ); + } + + return; + } + + let arguments = self.arguments_stack.pop().unwrap(); + self.inserter.remember_block_params(block, &arguments); + + // If this is not a separate variable, clippy gets confused and says the to_vec is + // unnecessary, when removing it actually causes an aliasing/mutability error. + let instructions = self.inserter.function.dfg[block].instructions().to_vec(); + for instruction in instructions.iter() { + let results = self.push_instruction(*instruction); + let (instruction, _) = self.inserter.map_instruction(*instruction); + let mut capacity_tracker = SliceCapacityTracker::new(&self.inserter.function.dfg); capacity_tracker.collect_slice_information( - instruction, + &instruction, &mut self.slice_sizes, - results.to_vec(), + results, ); } + } - match self.inserter.function.dfg[block].unwrap_terminator() { + /// Returns the list of blocks that need to be processed after the given block + /// For a normal block, it would be its successor + /// For blocks related to a conditional statement, we ensure to process + /// the 'then-branch', then the 'else-branch' (if it exists), and finally the end block + fn handle_terminator( + &mut self, + block: BasicBlockId, + work_list: &[BasicBlockId], + ) -> Vec { + let terminator = self.inserter.function.dfg[block].unwrap_terminator().clone(); + match &terminator { TerminatorInstruction::JmpIf { condition, then_destination, else_destination } => { - let old_condition = *condition; - let then_block = *then_destination; - let else_block = *else_destination; - let then_condition = self.inserter.resolve(old_condition); - - let one = FieldElement::one(); - let then_branch = - self.inline_branch(block, then_block, old_condition, then_condition, one); - - let else_condition = - self.insert_instruction(Instruction::Not(then_condition), CallStack::new()); - let zero = FieldElement::zero(); - - // Make sure the else branch sees the previous values of each store - // rather than any values created in the 'then' branch. - self.undo_stores_in_then_branch(&then_branch); - - let else_branch = - self.inline_branch(block, else_block, old_condition, else_condition, zero); - - // We must remember to reset whether side effects are enabled when both branches - // end, in addition to resetting the value of old_condition since it is set to - // known to be true/false within the then/else branch respectively. - self.insert_current_side_effects_enabled(); - - // We must map back to `then_condition` here. Mapping `old_condition` to itself would - // lose any previous mappings. - self.inserter.map_value(old_condition, then_condition); - - // While there is a condition on the stack we don't compile outside the condition - // until it is popped. This ensures we inline the full then and else branches - // before continuing from the end of the conditional here where they can be merged properly. - let end = self.branch_ends[&block]; - self.inline_branch_end(end, then_branch, else_branch) + self.arguments_stack.push(vec![]); + self.if_start(condition, then_destination, else_destination, &block) } TerminatorInstruction::Jmp { destination, arguments, call_stack: _ } => { - if let Some((end_block, _)) = self.conditions.last() { - if destination == end_block { - return block; + let arguments = vecmap(arguments.clone(), |value| self.inserter.resolve(value)); + self.arguments_stack.push(arguments); + if work_list.contains(destination) { + if work_list.last() == Some(destination) { + self.else_stop(&block) + } else { + self.then_stop(&block) } + } else { + vec![*destination] } - let destination = *destination; - let arguments = vecmap(arguments.clone(), |value| self.inserter.resolve(value)); - self.inline_block(destination, &arguments) } TerminatorInstruction::Return { return_values, call_stack } => { let call_stack = call_stack.clone(); @@ -326,133 +378,133 @@ impl<'f> Context<'f> { let entry = self.inserter.function.entry_block(); self.inserter.function.dfg.set_block_terminator(entry, new_return); - block + vec![] } } } - /// Push a condition to the stack of conditions. - /// - /// This condition should be present while we're inlining each block reachable from the 'then' - /// branch of a jmpif instruction, until the branches eventually join back together. Likewise, - /// !condition should be present while we're inlining each block reachable from the 'else' - /// branch of a jmpif instruction until the join block. - fn push_condition(&mut self, start_block: BasicBlockId, condition: ValueId) { - let end_block = self.branch_ends[&start_block]; - - if let Some((_, previous_condition)) = self.conditions.last() { - let and = Instruction::binary(BinaryOp::And, *previous_condition, condition); - let new_condition = self.insert_instruction(and, CallStack::new()); - self.conditions.push((end_block, new_condition)); - } else { - self.conditions.push((end_block, condition)); + /// Process a conditional statement + fn if_start( + &mut self, + condition: &ValueId, + then_destination: &BasicBlockId, + else_destination: &BasicBlockId, + if_entry: &BasicBlockId, + ) -> Vec { + // manage conditions + let old_condition = *condition; + let then_condition = self.inserter.resolve(old_condition); + + let one = FieldElement::one(); + let old_stores = std::mem::take(&mut self.store_values); + let old_allocations = std::mem::take(&mut self.local_allocations); + let branch = ConditionalBranch { + old_condition, + condition: self.link_condition(then_condition), + store_values: old_stores, + local_allocations: old_allocations, + last_block: *then_destination, + }; + let cond_context = ConditionalContext { + condition: then_condition, + entry_block: *if_entry, + then_branch: branch, + else_branch: None, + }; + self.condition_stack.push(cond_context); + self.insert_current_side_effects_enabled(); + // Optimization: within the then branch we know the condition to be true, so replace + // any references of it within this branch with true. Likewise, do the same with false + // with the else branch. We must be careful not to replace the condition if it is a + // known constant, otherwise we can end up setting 1 = 0 or vice-versa. + if self.inserter.function.dfg.get_numeric_constant(old_condition).is_none() { + let known_value = self.inserter.function.dfg.make_constant(one, Type::bool()); + + self.inserter.map_value(old_condition, known_value); } + vec![self.branch_ends[if_entry], *else_destination, *then_destination] } - /// Insert a new instruction into the function's entry block. - /// Unlike push_instruction, this function will not map any ValueIds. - /// within the given instruction, nor will it modify self.values in any way. - fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStack) -> ValueId { - let block = self.inserter.function.entry_block(); - self.inserter - .function - .dfg - .insert_instruction_and_results(instruction, block, None, call_stack) - .first() + /// Switch context to the 'else-branch' + fn then_stop(&mut self, block: &BasicBlockId) -> Vec { + let mut cond_context = self.condition_stack.pop().unwrap(); + cond_context.then_branch.last_block = *block; + + let else_condition = + self.insert_instruction(Instruction::Not(cond_context.condition), CallStack::new()); + let else_condition = self.link_condition(else_condition); + + let zero = FieldElement::zero(); + // Make sure the else branch sees the previous values of each store + // rather than any values created in the 'then' branch. + let old_stores = std::mem::take(&mut cond_context.then_branch.store_values); + cond_context.then_branch.store_values = std::mem::take(&mut self.store_values); + self.undo_stores_in_then_branch(&cond_context.then_branch.store_values); + + let old_allocations = std::mem::take(&mut self.local_allocations); + let else_branch = ConditionalBranch { + old_condition: cond_context.then_branch.old_condition, + condition: else_condition, + store_values: old_stores, + local_allocations: old_allocations, + last_block: *block, + }; + let old_condition = else_branch.old_condition; + cond_context.then_branch.local_allocations.clear(); + cond_context.else_branch = Some(else_branch); + self.condition_stack.push(cond_context); + + self.insert_current_side_effects_enabled(); + // Optimization: within the then branch we know the condition to be true, so replace + // any references of it within this branch with true. Likewise, do the same with false + // with the else branch. We must be careful not to replace the condition if it is a + // known constant, otherwise we can end up setting 1 = 0 or vice-versa. + if self.inserter.function.dfg.get_numeric_constant(old_condition).is_none() { + let known_value = self.inserter.function.dfg.make_constant(zero, Type::bool()); + + self.inserter.map_value(old_condition, known_value); + } + assert_eq!(self.cfg.successors(*block).len(), 1); + vec![self.cfg.successors(*block).next().unwrap()] } - /// Inserts a new instruction into the function's entry block, using the given - /// control type variables to specify result types if needed. - /// Unlike push_instruction, this function will not map any ValueIds. - /// within the given instruction, nor will it modify self.values in any way. - fn insert_instruction_with_typevars( - &mut self, - instruction: Instruction, - ctrl_typevars: Option>, - ) -> InsertInstructionResult { - let block = self.inserter.function.entry_block(); - self.inserter.function.dfg.insert_instruction_and_results( - instruction, - block, - ctrl_typevars, - CallStack::new(), - ) - } + /// Process the 'exit' block of a conditional statement + fn else_stop(&mut self, block: &BasicBlockId) -> Vec { + let mut cond_context = self.condition_stack.pop().unwrap(); + if cond_context.else_branch.is_none() { + // then_stop() has not been called, this means that the conditional statement has no else branch + // so we simply do the then_stop() now + self.condition_stack.push(cond_context); + self.then_stop(block); + cond_context = self.condition_stack.pop().unwrap(); + } - /// Checks the branch condition on the top of the stack and uses it to build and insert an - /// `EnableSideEffects` instruction into the entry block. - /// - /// If the stack is empty, a "true" u1 constant is taken to be the active condition. This is - /// necessary for re-enabling side-effects when re-emerging to a branch depth of 0. - fn insert_current_side_effects_enabled(&mut self) { - let condition = match self.conditions.last() { - Some((_, cond)) => *cond, - None => { - self.inserter.function.dfg.make_constant(FieldElement::one(), Type::unsigned(1)) - } - }; - let enable_side_effects = Instruction::EnableSideEffects { condition }; - self.insert_instruction_with_typevars(enable_side_effects, None); - } + let mut else_branch = cond_context.else_branch.unwrap(); + let stores_in_branch = std::mem::replace(&mut self.store_values, else_branch.store_values); + self.local_allocations = std::mem::take(&mut else_branch.local_allocations); + else_branch.last_block = *block; + else_branch.store_values = stores_in_branch; + cond_context.else_branch = Some(else_branch); - /// Inline one branch of a jmpif instruction. - /// - /// This will continue inlining recursively until the next end block is reached where each branch - /// of the jmpif instruction is joined back into a single block. - /// - /// Within a branch of a jmpif instruction, we can assume the condition of the jmpif to be - /// always true or false, depending on which branch we're in. - /// - /// Returns the ending block / join block of this branch. - fn inline_branch( - &mut self, - jmpif_block: BasicBlockId, - destination: BasicBlockId, - old_condition: ValueId, - new_condition: ValueId, - condition_value: FieldElement, - ) -> Branch { - if destination == self.branch_ends[&jmpif_block] { - // If the branch destination is the same as the end of the branch, this must be the - // 'else' case of an if with no else - so there is no else branch. - Branch { - condition: new_condition, - // The last block here is somewhat arbitrary. It only matters that it has no Jmp - // args that will be merged by inline_branch_end. Since jmpifs don't have - // block arguments, it is safe to use the jmpif block here. - last_block: jmpif_block, - store_values: HashMap::default(), - } - } else { - self.push_condition(jmpif_block, new_condition); - self.insert_current_side_effects_enabled(); - let old_stores = std::mem::take(&mut self.store_values); - let old_allocations = std::mem::take(&mut self.local_allocations); - - // Optimization: within the then branch we know the condition to be true, so replace - // any references of it within this branch with true. Likewise, do the same with false - // with the else branch. We must be careful not to replace the condition if it is a - // known constant, otherwise we can end up setting 1 = 0 or vice-versa. - if self.inserter.function.dfg.get_numeric_constant(old_condition).is_none() { - let known_value = - self.inserter.function.dfg.make_constant(condition_value, Type::bool()); - - self.inserter.map_value(old_condition, known_value); - } + // We must remember to reset whether side effects are enabled when both branches + // end, in addition to resetting the value of old_condition since it is set to + // known to be true/false within the then/else branch respectively. + self.insert_current_side_effects_enabled(); - let final_block = self.inline_block(destination, &[]); + // We must map back to `then_condition` here. Mapping `old_condition` to itself would + // lose any previous mappings. + self.inserter + .map_value(cond_context.then_branch.old_condition, cond_context.then_branch.condition); - self.conditions.pop(); + // While there is a condition on the stack we don't compile outside the condition + // until it is popped. This ensures we inline the full then and else branches + // before continuing from the end of the conditional here where they can be merged properly. + let end = self.branch_ends[&cond_context.entry_block]; - let stores_in_branch = std::mem::replace(&mut self.store_values, old_stores); - self.local_allocations = old_allocations; + // Merge arguments and stores from the else/end branches + self.inline_branch_end(end, cond_context); - Branch { - condition: new_condition, - last_block: final_block, - store_values: stores_in_branch, - } - } + vec![self.cfg.successors(*block).next().unwrap()] } /// Inline the ending block of a branch, the point where all blocks from a jmpif instruction @@ -467,15 +519,17 @@ impl<'f> Context<'f> { fn inline_branch_end( &mut self, destination: BasicBlockId, - then_branch: Branch, - else_branch: Branch, + cond_context: ConditionalContext, ) -> BasicBlockId { assert_eq!(self.cfg.predecessors(destination).len(), 2); + let last_then = cond_context.then_branch.last_block; + let mut else_args = Vec::new(); + if cond_context.else_branch.is_some() { + let last_else = cond_context.else_branch.clone().unwrap().last_block; + else_args = self.inserter.function.dfg[last_else].terminator_arguments().to_vec(); + } - let then_args = - self.inserter.function.dfg[then_branch.last_block].terminator_arguments().to_vec(); - let else_args = - self.inserter.function.dfg[else_branch.last_block].terminator_arguments().to_vec(); + let then_args = self.inserter.function.dfg[last_then].terminator_arguments().to_vec(); let params = self.inserter.function.dfg.block_parameters(destination); assert_eq!(params.len(), then_args.len()); @@ -500,17 +554,64 @@ impl<'f> Context<'f> { // Cannot include this in the previous vecmap since it requires exclusive access to self let args = vecmap(args, |(then_arg, else_arg)| { value_merger.merge_values( - then_branch.condition, - else_branch.condition, + cond_context.then_branch.condition, + cond_context.else_branch.clone().unwrap().condition, then_arg, else_arg, ) }); - self.merge_stores(then_branch, else_branch); + self.merge_stores(cond_context.then_branch, cond_context.else_branch); + self.arguments_stack.pop(); + self.arguments_stack.pop(); + self.arguments_stack.push(args); + destination + } - // insert merge instruction - self.inline_block(destination, &args) + /// Insert a new instruction into the function's entry block. + /// Unlike push_instruction, this function will not map any ValueIds. + /// within the given instruction, nor will it modify self.values in any way. + fn insert_instruction(&mut self, instruction: Instruction, call_stack: CallStack) -> ValueId { + let block = self.inserter.function.entry_block(); + self.inserter + .function + .dfg + .insert_instruction_and_results(instruction, block, None, call_stack) + .first() + } + + /// Inserts a new instruction into the function's entry block, using the given + /// control type variables to specify result types if needed. + /// Unlike push_instruction, this function will not map any ValueIds. + /// within the given instruction, nor will it modify self.values in any way. + fn insert_instruction_with_typevars( + &mut self, + instruction: Instruction, + ctrl_typevars: Option>, + ) -> InsertInstructionResult { + let block = self.inserter.function.entry_block(); + self.inserter.function.dfg.insert_instruction_and_results( + instruction, + block, + ctrl_typevars, + CallStack::new(), + ) + } + + /// Checks the branch condition on the top of the stack and uses it to build and insert an + /// `EnableSideEffects` instruction into the entry block. + /// + /// If the stack is empty, a "true" u1 constant is taken to be the active condition. This is + /// necessary for re-enabling side-effects when re-emerging to a branch depth of 0. + fn insert_current_side_effects_enabled(&mut self) { + let condition = match self.get_last_condition() { + Some(cond) => cond, + None => { + self.inserter.function.dfg.make_constant(FieldElement::one(), Type::unsigned(1)) + } + }; + let enable_side_effects = Instruction::EnableSideEffects { condition }; + self.insert_instruction_with_typevars(enable_side_effects, None); } /// Merge any store instructions found in each branch. @@ -518,7 +619,11 @@ impl<'f> Context<'f> { /// This function relies on the 'then' branch being merged before the 'else' branch of a jmpif /// instruction. If this ordering is changed, the ordering that store values are merged within /// this function also needs to be changed to reflect that. - fn merge_stores(&mut self, then_branch: Branch, else_branch: Branch) { + fn merge_stores( + &mut self, + then_branch: ConditionalBranch, + else_branch: Option, + ) { // Address -> (then_value, else_value, value_before_the_if) let mut new_map = BTreeMap::new(); @@ -526,11 +631,13 @@ impl<'f> Context<'f> { new_map.insert(address, (store.new_value, store.old_value, store.old_value)); } - for (address, store) in else_branch.store_values { - if let Some(entry) = new_map.get_mut(&address) { - entry.1 = store.new_value; - } else { - new_map.insert(address, (store.old_value, store.new_value, store.old_value)); + if else_branch.is_some() { + for (address, store) in else_branch.clone().unwrap().store_values { + if let Some(entry) = new_map.get_mut(&address) { + entry.1 = store.new_value; + } else { + new_map.insert(address, (store.old_value, store.new_value, store.old_value)); + } } } @@ -544,8 +651,11 @@ impl<'f> Context<'f> { } let then_condition = then_branch.condition; - let else_condition = else_branch.condition; - + let else_condition = if let Some(branch) = else_branch { + branch.condition + } else { + self.inserter.function.dfg.make_constant(FieldElement::zero(), Type::bool()) + }; let block = self.inserter.function.entry_block(); let mut value_merger = @@ -607,35 +717,6 @@ impl<'f> Context<'f> { } } - /// Inline all instructions from the given destination block into the entry block. - /// Afterwards, check the block's terminator and continue inlining recursively. - /// - /// Returns the final block that was inlined. - /// - /// Expects that the `arguments` given are already translated via self.inserter.resolve. - /// If they are not, it is possible some values which no longer exist, such as block - /// parameters, will be kept in the program. - fn inline_block(&mut self, destination: BasicBlockId, arguments: &[ValueId]) -> BasicBlockId { - self.inserter.remember_block_params(destination, arguments); - - // If this is not a separate variable, clippy gets confused and says the to_vec is - // unnecessary, when removing it actually causes an aliasing/mutability error. - let instructions = self.inserter.function.dfg[destination].instructions().to_vec(); - - for instruction in instructions.iter() { - let results = self.push_instruction(*instruction); - let (instruction, _) = self.inserter.map_instruction(*instruction); - let mut capacity_tracker = SliceCapacityTracker::new(&self.inserter.function.dfg); - capacity_tracker.collect_slice_information( - &instruction, - &mut self.slice_sizes, - results, - ); - } - - self.handle_terminator(destination) - } - /// Push the given instruction to the end of the entry block of the current function. /// /// Note that each ValueId of the instruction will be mapped via self.inserter.resolve. @@ -666,7 +747,7 @@ impl<'f> Context<'f> { instruction: Instruction, call_stack: CallStack, ) -> Instruction { - if let Some((_, condition)) = self.conditions.last().copied() { + if let Some(condition) = self.get_last_condition() { match instruction { Instruction::Constrain(lhs, rhs, message) => { // Replace constraint `lhs == rhs` with `condition * lhs == condition * rhs`. @@ -741,8 +822,8 @@ impl<'f> Context<'f> { } } - fn undo_stores_in_then_branch(&mut self, then_branch: &Branch) { - for (address, store) in &then_branch.store_values { + fn undo_stores_in_then_branch(&mut self, store_values: &HashMap) { + for (address, store) in store_values { let address = *address; let value = store.old_value; self.insert_instruction_with_typevars(Instruction::Store { address, value }, None); From 1d653704715bf9999eb6a40ed7500e752e2c73b7 Mon Sep 17 00:00:00 2001 From: Gregorio Juliana Date: Thu, 7 Mar 2024 12:34:12 +0100 Subject: [PATCH 045/416] fix: handling of gh deps in noir_wasm (#4499) # Description Github deps are directly added to the file manager, which causes them to be missing the absolute path to the source file and only include the extraction directory relative to the fm root directory. This caused the path to the file to be formatted in an unexpected way and the compiler to panic. Resolves: https://github.com/noir-lang/noir/issues/4355 Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/wasm/src/noir/package.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/wasm/src/noir/package.ts b/compiler/wasm/src/noir/package.ts index 81178e6ae96..2856798273a 100644 --- a/compiler/wasm/src/noir/package.ts +++ b/compiler/wasm/src/noir/package.ts @@ -105,7 +105,12 @@ export class Package { handles .filter((handle) => SOURCE_EXTENSIONS.find((ext) => handle.endsWith(ext))) .map(async (file) => { - const suffix = file.replace(this.#srcPath, ''); + // Github deps are directly added to the file manager, which causes them to be missing the absolute path to the source file + // and only include the extraction directory relative to the fm root directory + // This regexp ensures we remove the "real" source path for all dependencies, providing the compiler with what it expects for each source file: + // -> for bin/contract packages + // -> for libs + const suffix = file.replace(new RegExp(`.*${this.#srcPath}`), ''); return { path: this.getType() === 'lib' ? `${alias ? alias : this.#config.package.name}${suffix}` : file, source: (await fm.readFile(file, 'utf-8')).toString(), From 2de8fbf3f21aa2e374ac75075fbed711ae502a97 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:49:08 +0000 Subject: [PATCH 046/416] chore: bump bb to 0.26.3 (#4488) # Description ## Problem\* Resolves #4468 ## Summary\* Throwing this up here so we're ready for when the artifacts are finally published on GH. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/bb_abstraction_leaks/build.rs | 2 +- tooling/noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index f9effd5d991..0bd2b1c076a 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.24.0"; +const VERSION: &str = "0.26.3"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 28c3609fd14..06c034725e3 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.24.0", + "@aztec/bb.js": "0.26.3", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index f5f3a29f08a..49485193532 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.24.0": - version: 0.24.0 - resolution: "@aztec/bb.js@npm:0.24.0" +"@aztec/bb.js@npm:0.26.3": + version: 0.26.3 + resolution: "@aztec/bb.js@npm:0.26.3" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: a086dabf30084cfa526e512148b9c02f0a0770dcc19b7dca4af9a3e98612b716acc7eaac6b52c0f12d985932e866d1cb9e534ded6ac9d747f3dd021afe25de27 + checksum: 74c2b7ef5405f56472cf7c41d1c13261df07b1d5019e3ede9b63d218378e0fb73ccf5c52f1cc524505efad5799b347b497349d7c9b6fe82286014b1574f12309 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.24.0 + "@aztec/bb.js": 0.26.3 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 99c4d27242c27eea9237ab042d85f42de484ac72 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 7 Mar 2024 16:48:23 +0000 Subject: [PATCH 047/416] chore: pass macro processors by reference (#4501) # Description ## Problem\* Resolves ## Summary\* We're cloning unnecessarily here as we can just pass the processors by reference. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Jake Fecher --- compiler/noirc_driver/src/lib.rs | 7 ++----- .../noirc_frontend/src/hir/def_collector/dc_crate.rs | 10 +++------- compiler/noirc_frontend/src/hir/def_map/mod.rs | 12 +++--------- compiler/noirc_frontend/src/tests.rs | 2 +- 4 files changed, 9 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 97fc66688e1..fa134f8a0dd 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -237,11 +237,8 @@ pub fn check_crate( deny_warnings: bool, disable_macros: bool, ) -> CompilationResult<()> { - let macros: Vec<&dyn MacroProcessor> = if disable_macros { - vec![] - } else { - vec![&aztec_macros::AztecMacro as &dyn MacroProcessor] - }; + let macros: &[&dyn MacroProcessor] = + if disable_macros { &[] } else { &[&aztec_macros::AztecMacro as &dyn MacroProcessor] }; let mut errors = vec![]; let diagnostics = CrateDefMap::collect_defs(crate_id, context, macros); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 7f36af5b30e..4a6ca5c6993 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -207,7 +207,7 @@ impl DefCollector { context: &mut Context, ast: SortedModule, root_file_id: FileId, - macro_processors: Vec<&dyn MacroProcessor>, + macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; let crate_id = def_map.krate; @@ -220,11 +220,7 @@ impl DefCollector { let crate_graph = &context.crate_graph[crate_id]; for dep in crate_graph.dependencies.clone() { - errors.extend(CrateDefMap::collect_defs( - dep.crate_id, - context, - macro_processors.clone(), - )); + errors.extend(CrateDefMap::collect_defs(dep.crate_id, context, macro_processors)); let dep_def_root = context.def_map(&dep.crate_id).expect("ice: def map was just created").root; @@ -257,7 +253,7 @@ impl DefCollector { context.def_maps.insert(crate_id, def_collector.def_map); // TODO(#4653): generalize this function - for macro_processor in ¯o_processors { + for macro_processor in macro_processors { macro_processor .process_unresolved_traits_impls( &crate_id, diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 8721bdb6c3c..1326ffca9f7 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -73,7 +73,7 @@ impl CrateDefMap { pub fn collect_defs( crate_id: CrateId, context: &mut Context, - macro_processors: Vec<&dyn MacroProcessor>, + macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { // Check if this Crate has already been compiled // XXX: There is probably a better alternative for this. @@ -90,7 +90,7 @@ impl CrateDefMap { let (ast, parsing_errors) = context.parsed_file_results(root_file_id); let mut ast = ast.into_sorted(); - for macro_processor in ¯o_processors { + for macro_processor in macro_processors { match macro_processor.process_untyped_ast(ast.clone(), &crate_id, context) { Ok(processed_ast) => { ast = processed_ast; @@ -115,13 +115,7 @@ impl CrateDefMap { }; // Now we want to populate the CrateDefMap using the DefCollector - errors.extend(DefCollector::collect( - def_map, - context, - ast, - root_file_id, - macro_processors.clone(), - )); + errors.extend(DefCollector::collect(def_map, context, ast, root_file_id, macro_processors)); errors.extend( parsing_errors.iter().map(|e| (e.clone().into(), root_file_id)).collect::>(), diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c661cc92eef..0c8c677d9af 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -81,7 +81,7 @@ mod test { &mut context, program.clone().into_sorted(), root_file_id, - Vec::new(), // No macro processors + &[], // No macro processors )); } (program, context, errors) From 2a555a041dbd3e40cbf768ca131f508570f4ba50 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Thu, 7 Mar 2024 19:37:17 +0100 Subject: [PATCH 048/416] chore: add regression test for issue 4449 (#4503) # Description ## Problem\* Related to issue #4449 ## Summary\* This PR adds a regression test which ensure PR #4492 is able to handle large CFG. ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X ] I have tested the changes locally. - [ X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../execution_success/regression_4449/Nargo.toml | 6 ++++++ .../execution_success/regression_4449/Prover.toml | 3 +++ .../execution_success/regression_4449/src/main.nr | 14 ++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 test_programs/execution_success/regression_4449/Nargo.toml create mode 100644 test_programs/execution_success/regression_4449/Prover.toml create mode 100644 test_programs/execution_success/regression_4449/src/main.nr diff --git a/test_programs/execution_success/regression_4449/Nargo.toml b/test_programs/execution_success/regression_4449/Nargo.toml new file mode 100644 index 00000000000..925420a03a8 --- /dev/null +++ b/test_programs/execution_success/regression_4449/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "regression_4449" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/regression_4449/Prover.toml b/test_programs/execution_success/regression_4449/Prover.toml new file mode 100644 index 00000000000..81af476bcc9 --- /dev/null +++ b/test_programs/execution_success/regression_4449/Prover.toml @@ -0,0 +1,3 @@ + +x = 0xbd +result = [204, 59, 83, 197, 18, 1, 128, 43, 247, 28, 104, 225, 106, 13, 20, 187, 42, 26, 67, 150, 48, 75, 238, 168, 121, 247, 142, 160, 71, 222, 97, 188] \ No newline at end of file diff --git a/test_programs/execution_success/regression_4449/src/main.nr b/test_programs/execution_success/regression_4449/src/main.nr new file mode 100644 index 00000000000..454a93f5d1a --- /dev/null +++ b/test_programs/execution_success/regression_4449/src/main.nr @@ -0,0 +1,14 @@ +// Regression test for issue #4449 +use dep::std; + +fn main(x: u8, result: [u8; 32]) { + let x = x % 31; + let mut digest = [0; 32]; + for i in 0..70 { + let y = x + i; + let a = [y, x, 32, 0, y + 1, y - 1, y - 2, 5]; + digest = std::sha256::digest(a); + } + + assert(digest == result); +} From ae1a9d923998177516919bbba6ff4b0584fa1e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gustavo=20Gir=C3=A1ldez?= Date: Thu, 7 Mar 2024 14:44:34 -0500 Subject: [PATCH 049/416] feat: Track stack frames and their variables in the debugger (#4188) # Description ## Problem\* Part of #3015 We want to track call frames and observe variables local to the currently executing frame. ## Summary\* This PR adds a bit more debugging instrumentation to track when function call frames are entered and exited, which allows to determine which variables are "live" at the current point in execution. This new instrumentation also allows labeling the frames in the call stack from the names of the functions being executed. Also, it includes improvements on how source breakpoints are mapped into opcode breakpoints. Both features combined bring substantial improvements to the debugger usability. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [X] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: synthia Co-authored-by: Martin Verzilli --- compiler/noirc_errors/src/debug_info.rs | 14 ++- compiler/noirc_evaluator/src/ssa.rs | 3 +- compiler/noirc_frontend/src/debug/mod.rs | 104 ++++++++++++++-- compiler/noirc_frontend/src/hir_def/types.rs | 8 +- .../src/monomorphization/ast.rs | 5 +- .../src/monomorphization/debug.rs | 2 +- .../src/monomorphization/debug_types.rs | 54 +++++---- .../src/monomorphization/mod.rs | 4 +- compiler/noirc_printable_type/src/lib.rs | 8 +- tooling/debugger/ignored-tests.txt | 10 +- tooling/debugger/src/context.rs | 107 ++++++++++++++++- tooling/debugger/src/dap.rs | 111 ++++-------------- tooling/debugger/src/foreign_calls.rs | 46 ++++++-- tooling/debugger/src/repl.rs | 14 ++- tooling/debugger/src/source_code_printer.rs | 10 +- tooling/debugger/tests/debug.rs | 2 +- tooling/nargo/src/artifacts/debug.rs | 10 +- tooling/nargo/src/artifacts/debug_vars.rs | 96 ++++++++++----- 18 files changed, 415 insertions(+), 193 deletions(-) diff --git a/compiler/noirc_errors/src/debug_info.rs b/compiler/noirc_errors/src/debug_info.rs index 67ec851d46d..09117bdc3b7 100644 --- a/compiler/noirc_errors/src/debug_info.rs +++ b/compiler/noirc_errors/src/debug_info.rs @@ -24,6 +24,9 @@ use serde::{ #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] pub struct DebugVarId(pub u32); +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] +pub struct DebugFnId(pub u32); + #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] pub struct DebugTypeId(pub u32); @@ -33,7 +36,14 @@ pub struct DebugVariable { pub debug_type_id: DebugTypeId, } +#[derive(Debug, Clone, Hash, Deserialize, Serialize)] +pub struct DebugFunction { + pub name: String, + pub arg_names: Vec, +} + pub type DebugVariables = BTreeMap; +pub type DebugFunctions = BTreeMap; pub type DebugTypes = BTreeMap; #[serde_as] @@ -45,6 +55,7 @@ pub struct DebugInfo { #[serde_as(as = "BTreeMap")] pub locations: BTreeMap>, pub variables: DebugVariables, + pub functions: DebugFunctions, pub types: DebugTypes, } @@ -60,9 +71,10 @@ impl DebugInfo { pub fn new( locations: BTreeMap>, variables: DebugVariables, + functions: DebugFunctions, types: DebugTypes, ) -> Self { - Self { locations, variables, types } + Self { locations, variables, functions, types } } /// Updates the locations map when the [`Circuit`][acvm::acir::circuit::Circuit] is modified. diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 0bb81efe977..56cb76adbe4 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -88,6 +88,7 @@ pub fn create_circuit( ) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { let debug_variables = program.debug_variables.clone(); let debug_types = program.debug_types.clone(); + let debug_functions = program.debug_functions.clone(); let func_sig = program.main_function_signature.clone(); let recursive = program.recursive; let mut generated_acir = optimize_into_acir( @@ -130,7 +131,7 @@ pub fn create_circuit( .map(|(index, locations)| (index, locations.into_iter().collect())) .collect(); - let mut debug_info = DebugInfo::new(locations, debug_variables, debug_types); + let mut debug_info = DebugInfo::new(locations, debug_variables, debug_functions, debug_types); // Perform any ACIR-level optimizations let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit); diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index a88567fcaf9..05916502d73 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -4,9 +4,11 @@ use crate::{ ast::{Path, PathKind}, parser::{Item, ItemKind}, }; +use noirc_errors::debug_info::{DebugFnId, DebugFunction}; use noirc_errors::{Span, Spanned}; use std::collections::HashMap; use std::collections::VecDeque; +use std::mem::take; const MAX_MEMBER_ASSIGN_DEPTH: usize = 8; @@ -26,8 +28,12 @@ pub struct DebugInstrumenter { // all field names referenced when assigning to a member of a variable pub field_names: HashMap, + // all collected function metadata (name + argument names) + pub functions: HashMap, + next_var_id: u32, next_field_name_id: u32, + next_fn_id: u32, // last seen variable names and their IDs grouped by scope scope: Vec>, @@ -38,9 +44,11 @@ impl Default for DebugInstrumenter { Self { variables: HashMap::default(), field_names: HashMap::default(), + functions: HashMap::default(), scope: vec![], next_var_id: 0, next_field_name_id: 1, + next_fn_id: 0, } } } @@ -76,10 +84,22 @@ impl DebugInstrumenter { field_name_id } + fn insert_function(&mut self, fn_name: String, arguments: Vec) -> DebugFnId { + let fn_id = DebugFnId(self.next_fn_id); + self.next_fn_id += 1; + self.functions.insert(fn_id, DebugFunction { name: fn_name, arg_names: arguments }); + fn_id + } + fn walk_fn(&mut self, func: &mut ast::FunctionDefinition) { + let func_name = func.name.0.contents.clone(); + let func_args = + func.parameters.iter().map(|param| pattern_to_string(¶m.pattern)).collect(); + let fn_id = self.insert_function(func_name, func_args); + let enter_stmt = build_debug_call_stmt("enter", fn_id, func.span); self.scope.push(HashMap::default()); - let set_fn_params = func + let set_fn_params: Vec<_> = func .parameters .iter() .flat_map(|param| { @@ -93,10 +113,21 @@ impl DebugInstrumenter { }) .collect(); - self.walk_scope(&mut func.body.0, func.span); + let func_body = &mut func.body.0; + let mut statements = take(func_body); + + self.walk_scope(&mut statements, func.span); - // prepend fn params: - func.body.0 = [set_fn_params, func.body.0.clone()].concat(); + // walk_scope ensures that the last statement is the return value of the function + let last_stmt = statements.pop().expect("at least one statement after walk_scope"); + let exit_stmt = build_debug_call_stmt("exit", fn_id, last_stmt.span); + + // rebuild function body + func_body.push(enter_stmt); + func_body.extend(set_fn_params); + func_body.extend(statements); + func_body.push(exit_stmt); + func_body.push(last_stmt); } // Modify a vector of statements in-place, adding instrumentation for sets and drops. @@ -427,6 +458,8 @@ impl DebugInstrumenter { use dep::__debug::{{ __debug_var_assign, __debug_var_drop, + __debug_fn_enter, + __debug_fn_exit, __debug_dereference_assign, {member_assigns}, }};"# @@ -451,14 +484,32 @@ pub fn build_debug_crate_file() -> String { } #[oracle(__debug_var_drop)] - unconstrained fn __debug_var_drop_oracle(_var_id: u32) {} - unconstrained fn __debug_var_drop_inner(var_id: u32) { + unconstrained fn __debug_var_drop_oracle(_var_id: u32) {} + unconstrained fn __debug_var_drop_inner(var_id: u32) { __debug_var_drop_oracle(var_id); } - pub fn __debug_var_drop(var_id: u32) { + pub fn __debug_var_drop(var_id: u32) { __debug_var_drop_inner(var_id); } + #[oracle(__debug_fn_enter)] + unconstrained fn __debug_fn_enter_oracle(_fn_id: u32) {} + unconstrained fn __debug_fn_enter_inner(fn_id: u32) { + __debug_fn_enter_oracle(fn_id); + } + pub fn __debug_fn_enter(fn_id: u32) { + __debug_fn_enter_inner(fn_id); + } + + #[oracle(__debug_fn_exit)] + unconstrained fn __debug_fn_exit_oracle(_fn_id: u32) {} + unconstrained fn __debug_fn_exit_inner(fn_id: u32) { + __debug_fn_exit_oracle(fn_id); + } + pub fn __debug_fn_exit(fn_id: u32) { + __debug_fn_exit_inner(fn_id); + } + #[oracle(__debug_dereference_assign)] unconstrained fn __debug_dereference_assign_oracle(_var_id: u32, _value: T) {} unconstrained fn __debug_dereference_assign_inner(var_id: u32, value: T) { @@ -561,6 +612,21 @@ fn build_assign_member_stmt( ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } } +fn build_debug_call_stmt(fname: &str, fn_id: DebugFnId, span: Span) -> ast::Statement { + let kind = ast::ExpressionKind::Call(Box::new(ast::CallExpression { + func: Box::new(ast::Expression { + kind: ast::ExpressionKind::Variable(ast::Path { + segments: vec![ident(&format!["__debug_fn_{fname}"], span)], + kind: PathKind::Plain, + span, + }), + span, + }), + arguments: vec![uint_expr(fn_id.0 as u128, span)], + })); + ast::Statement { kind: ast::StatementKind::Semi(ast::Expression { kind, span }), span } +} + fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { let mut vars = vec![]; let mut stack = VecDeque::from([(pattern, false)]); @@ -585,6 +651,30 @@ fn pattern_vars(pattern: &ast::Pattern) -> Vec<(ast::Ident, bool)> { vars } +fn pattern_to_string(pattern: &ast::Pattern) -> String { + match pattern { + ast::Pattern::Identifier(id) => id.0.contents.clone(), + ast::Pattern::Mutable(mpat, _, _) => format!("mut {}", pattern_to_string(mpat.as_ref())), + ast::Pattern::Tuple(elements, _) => format!( + "({})", + elements.iter().map(pattern_to_string).collect::>().join(", ") + ), + ast::Pattern::Struct(name, fields, _) => { + format!( + "{} {{ {} }}", + name, + fields + .iter() + .map(|(field_ident, field_pattern)| { + format!("{}: {}", &field_ident.0.contents, pattern_to_string(field_pattern)) + }) + .collect::>() + .join(", "), + ) + } + } +} + fn ident(s: &str, span: Span) -> ast::Ident { ast::Ident(Spanned::from(span, s.to_string())) } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index b70aa43701c..13fa41733cf 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1768,9 +1768,11 @@ impl From<&Type> for PrintableType { Type::TypeVariable(_, _) => unreachable!(), Type::NamedGeneric(..) => unreachable!(), Type::Forall(..) => unreachable!(), - Type::Function(_, _, env) => { - PrintableType::Function { env: Box::new(env.as_ref().into()) } - } + Type::Function(arguments, return_type, env) => PrintableType::Function { + arguments: arguments.iter().map(|arg| arg.into()).collect(), + return_type: Box::new(return_type.as_ref().into()), + env: Box::new(env.as_ref().into()), + }, Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index e4e619d5d92..7fcf8e87792 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -1,7 +1,7 @@ use acvm::FieldElement; use iter_extended::vecmap; use noirc_errors::{ - debug_info::{DebugTypes, DebugVariables}, + debug_info::{DebugFunctions, DebugTypes, DebugVariables}, Location, }; @@ -253,6 +253,7 @@ pub struct Program { /// Indicates to a backend whether a SNARK-friendly prover should be used. pub recursive: bool, pub debug_variables: DebugVariables, + pub debug_functions: DebugFunctions, pub debug_types: DebugTypes, } @@ -266,6 +267,7 @@ impl Program { return_visibility: Visibility, recursive: bool, debug_variables: DebugVariables, + debug_functions: DebugFunctions, debug_types: DebugTypes, ) -> Program { Program { @@ -276,6 +278,7 @@ impl Program { return_visibility, recursive, debug_variables, + debug_functions, debug_types, } } diff --git a/compiler/noirc_frontend/src/monomorphization/debug.rs b/compiler/noirc_frontend/src/monomorphization/debug.rs index a8ff4399f99..cf4e0ab792e 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -76,7 +76,7 @@ impl<'interner> Monomorphizer<'interner> { let var_type = self.interner.id_type(call.arguments[DEBUG_VALUE_ARG_SLOT]); let source_var_id = source_var_id.to_u128().into(); // then update the ID used for tracking at runtime - let var_id = self.debug_type_tracker.insert_var(source_var_id, var_type); + let var_id = self.debug_type_tracker.insert_var(source_var_id, &var_type); let interned_var_id = self.intern_var_id(var_id, &call.location); arguments[DEBUG_VAR_ID_ARG_SLOT] = self.expr(interned_var_id)?; Ok(()) diff --git a/compiler/noirc_frontend/src/monomorphization/debug_types.rs b/compiler/noirc_frontend/src/monomorphization/debug_types.rs index fea073d394f..16b82d1e7b9 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug_types.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug_types.rs @@ -3,7 +3,8 @@ use crate::{ hir_def::types::Type, }; use noirc_errors::debug_info::{ - DebugTypeId, DebugTypes, DebugVarId, DebugVariable, DebugVariables, + DebugFnId, DebugFunction, DebugFunctions, DebugTypeId, DebugTypes, DebugVarId, DebugVariable, + DebugVariables, }; use noirc_printable_type::PrintableType; use std::collections::HashMap; @@ -30,7 +31,10 @@ pub struct DebugTypeTracker { // All instances of tracked variables variables: HashMap, - // Types of tracked variables + // Function metadata collected during instrumentation injection + functions: HashMap, + + // Types of tracked variables and functions types: HashMap, types_reverse: HashMap, @@ -43,34 +47,29 @@ impl DebugTypeTracker { DebugTypeTracker { source_variables: instrumenter.variables.clone(), source_field_names: instrumenter.field_names.clone(), + functions: instrumenter.functions.clone(), ..DebugTypeTracker::default() } } - pub fn extract_vars_and_types(&self) -> (DebugVariables, DebugTypes) { + pub fn extract_vars_and_types(&self) -> (DebugVariables, DebugFunctions, DebugTypes) { let debug_variables = self .variables .clone() .into_iter() .map(|(var_id, (source_var_id, type_id))| { - ( - var_id, - DebugVariable { - name: self.source_variables.get(&source_var_id).cloned().unwrap_or_else( - || { - unreachable!( - "failed to retrieve variable name for {source_var_id:?}" - ); - }, - ), - debug_type_id: type_id, - }, - ) + let var_name = + self.source_variables.get(&source_var_id).cloned().unwrap_or_else(|| { + unreachable!("failed to retrieve variable name for {source_var_id:?}"); + }); + (var_id, DebugVariable { name: var_name, debug_type_id: type_id }) }) .collect(); + + let debug_functions = self.functions.clone().into_iter().collect(); let debug_types = self.types.clone().into_iter().collect(); - (debug_variables, debug_types) + (debug_variables, debug_functions, debug_types) } pub fn resolve_field_index( @@ -83,19 +82,24 @@ impl DebugTypeTracker { .and_then(|field_name| get_field(cursor_type, field_name)) } - pub fn insert_var(&mut self, source_var_id: SourceVarId, var_type: Type) -> DebugVarId { - if !self.source_variables.contains_key(&source_var_id) { - unreachable!("cannot find source debug variable {source_var_id:?}"); - } - - let ptype: PrintableType = var_type.follow_bindings().into(); - let type_id = self.types_reverse.get(&ptype).copied().unwrap_or_else(|| { + fn insert_type(&mut self, the_type: &Type) -> DebugTypeId { + let ptype: PrintableType = the_type.follow_bindings().into(); + self.types_reverse.get(&ptype).copied().unwrap_or_else(|| { let type_id = DebugTypeId(self.next_type_id); self.next_type_id += 1; self.types_reverse.insert(ptype.clone(), type_id); self.types.insert(type_id, ptype); type_id - }); + }) + } + + pub fn insert_var(&mut self, source_var_id: SourceVarId, var_type: &Type) -> DebugVarId { + if !self.source_variables.contains_key(&source_var_id) { + unreachable!("cannot find source debug variable {source_var_id:?}"); + } + + let type_id = self.insert_type(var_type); + // check if we need to instantiate the var with a new type let existing_var_id = self.source_to_debug_vars.get(&source_var_id).and_then(|var_id| { let (_, existing_type_id) = self.variables.get(var_id).unwrap(); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index ce880401d77..cfd9a61d13f 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -165,7 +165,8 @@ pub fn monomorphize_debug( let FuncMeta { return_distinctness, return_visibility, kind, .. } = monomorphizer.interner.function_meta(&main); - let (debug_variables, debug_types) = monomorphizer.debug_type_tracker.extract_vars_and_types(); + let (debug_variables, debug_functions, debug_types) = + monomorphizer.debug_type_tracker.extract_vars_and_types(); let program = Program::new( functions, function_sig, @@ -174,6 +175,7 @@ pub fn monomorphize_debug( *return_visibility, *kind == FunctionKind::Recursive, debug_variables, + debug_functions, debug_types, ); Ok(program) diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index 24f4f275a14..60f233cd86d 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -33,6 +33,8 @@ pub enum PrintableType { length: u64, }, Function { + arguments: Vec, + return_type: Box, env: Box, }, MutableReference { @@ -176,8 +178,8 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { output.push_str("false"); } } - (PrintableValue::Field(_), PrintableType::Function { .. }) => { - output.push_str("<>"); + (PrintableValue::Field(_), PrintableType::Function { arguments, return_type, .. }) => { + output.push_str(&format!("< {:?}>>", arguments, return_type,)); } (_, PrintableType::MutableReference { .. }) => { output.push_str("<>"); @@ -350,7 +352,7 @@ pub fn decode_value( PrintableValue::Struct(struct_map) } - PrintableType::Function { env } => { + PrintableType::Function { env, .. } => { let field_element = field_iterator.next().unwrap(); let func_ref = PrintableValue::Field(field_element); // we want to consume the fields from the environment, but for now they are not actually printed diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index c472e828739..231d4d897a9 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -1,21 +1,13 @@ array_dynamic_blackbox_input -array_sort -assign_ex +bigint bit_shifts_comptime -brillig_cow -brillig_nested_arrays brillig_references brillig_to_bytes_integration debug_logs double_verify_nested_proof double_verify_proof modulus -nested_array_dynamic -nested_array_in_slice -nested_arrays_from_brillig references scalar_mul signed_comparison -simple_2d_array to_bytes_integration -bigint diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 515edf0bb06..a3ee89263a4 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -8,11 +8,14 @@ use acvm::pwg::{ }; use acvm::{BlackBoxFunctionSolver, FieldElement}; -use nargo::artifacts::debug::DebugArtifact; +use codespan_reporting::files::{Files, SimpleFile}; +use fm::FileId; +use nargo::artifacts::debug::{DebugArtifact, StackFrame}; use nargo::errors::{ExecutionError, Location}; use nargo::NargoError; -use noirc_printable_type::{PrintableType, PrintableValue}; +use noirc_driver::DebugFile; +use std::collections::BTreeMap; use std::collections::{hash_set::Iter, HashSet}; #[derive(Debug)] @@ -29,6 +32,7 @@ pub(super) struct DebugContext<'a, B: BlackBoxFunctionSolver> { foreign_call_executor: Box, debug_artifact: &'a DebugArtifact, breakpoints: HashSet, + source_to_opcodes: BTreeMap>, } impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { @@ -39,12 +43,14 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { initial_witness: WitnessMap, foreign_call_executor: Box, ) -> Self { + let source_to_opcodes = build_source_to_opcode_debug_mappings(debug_artifact); Self { acvm: ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness), brillig_solver: None, foreign_call_executor, debug_artifact, breakpoints: HashSet::new(), + source_to_opcodes, } } @@ -100,10 +106,50 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { self.debug_artifact .file_map .get(&location.file) - .map(|file| file.path.starts_with("__debug/")) + .map(is_debug_file_in_debug_crate) .unwrap_or(false) } + /// Find an opcode location matching a source code location + // We apply some heuristics here, and there are four possibilities for the + // return value of this function: + // 1. the source location is not found -> None + // 2. an exact unique location is found (very rare) -> Some(opcode_location) + // 3. an exact but not unique location is found, ie. a source location may + // be mapped to multiple opcodes, and those may be disjoint, for example for + // functions called multiple times throughout the program + // -> return the first opcode in program order that matches the source location + // 4. exact location is not found, so an opcode for a nearby source location + // is returned (this again could actually be more than one opcodes) + // -> return the opcode for the next source line that is mapped + pub(super) fn find_opcode_for_source_location( + &self, + file_id: &FileId, + line: i64, + ) -> Option { + let line = line as usize; + let Some(line_to_opcodes) = self.source_to_opcodes.get(file_id) else { + return None; + }; + let found_index = match line_to_opcodes.binary_search_by(|x| x.0.cmp(&line)) { + Ok(index) => { + // move backwards to find the first opcode which matches the line + let mut index = index; + while index > 0 && line_to_opcodes[index - 1].0 == line { + index -= 1; + } + line_to_opcodes[index].1 + } + Err(index) => { + if index >= line_to_opcodes.len() { + return None; + } + line_to_opcodes[index].1 + } + }; + Some(found_index) + } + /// Returns the callstack in source code locations for the currently /// executing opcode. This can be `None` if the execution finished (and /// `get_current_opcode_location()` returns `None`) or if the opcode is not @@ -128,6 +174,9 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { &self, opcode_location: &OpcodeLocation, ) -> Vec { + // TODO: this assumes we're debugging a program (ie. the DebugArtifact + // will contain a single DebugInfo), but this assumption doesn't hold + // for contracts self.debug_artifact.debug_symbols[0] .opcode_location(opcode_location) .map(|source_locations| { @@ -467,10 +516,14 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } - pub(super) fn get_variables(&self) -> Vec<(&str, &PrintableValue, &PrintableType)> { + pub(super) fn get_variables(&self) -> Vec { return self.foreign_call_executor.get_variables(); } + pub(super) fn current_stack_frame(&self) -> Option { + return self.foreign_call_executor.current_stack_frame(); + } + fn breakpoint_reached(&self) -> bool { if let Some(location) = self.get_current_opcode_location() { self.breakpoints.contains(&location) @@ -526,6 +579,52 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { } } +fn is_debug_file_in_debug_crate(debug_file: &DebugFile) -> bool { + debug_file.path.starts_with("__debug/") +} + +/// Builds a map from FileId to an ordered vector of tuples with line +/// numbers and opcode locations corresponding to those line numbers +fn build_source_to_opcode_debug_mappings( + debug_artifact: &DebugArtifact, +) -> BTreeMap> { + if debug_artifact.debug_symbols.is_empty() { + return BTreeMap::new(); + } + let locations = &debug_artifact.debug_symbols[0].locations; + let simple_files: BTreeMap<_, _> = debug_artifact + .file_map + .iter() + .filter(|(_, debug_file)| !is_debug_file_in_debug_crate(debug_file)) + .map(|(file_id, debug_file)| { + ( + file_id, + SimpleFile::new(debug_file.path.to_str().unwrap(), debug_file.source.as_str()), + ) + }) + .collect(); + + let mut result: BTreeMap> = BTreeMap::new(); + locations.iter().for_each(|(opcode_location, source_locations)| { + source_locations.iter().for_each(|source_location| { + let span = source_location.span; + let file_id = source_location.file; + let Some(file) = simple_files.get(&file_id) else { + return; + }; + let Ok(line_index) = file.line_index((), span.start() as usize) else { + return; + }; + let line_number = line_index + 1; + + result.entry(file_id).or_default().push((line_number, *opcode_location)); + }); + }); + result.iter_mut().for_each(|(_, file_locations)| file_locations.sort_by_key(|x| (x.0, x.1))); + + result +} + #[cfg(test)] mod tests { use super::*; diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 7e67a26b257..7c722ed0a61 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -5,7 +5,6 @@ use std::str::FromStr; use acvm::acir::circuit::{Circuit, OpcodeLocation}; use acvm::acir::native_types::WitnessMap; use acvm::BlackBoxFunctionSolver; -use codespan_reporting::files::{Files, SimpleFile}; use crate::context::DebugCommandResult; use crate::context::DebugContext; @@ -30,15 +29,16 @@ use nargo::artifacts::debug::DebugArtifact; use fm::FileId; use noirc_driver::CompiledProgram; +type BreakpointId = i64; + pub struct DapSession<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> { server: Server, context: DebugContext<'a, B>, debug_artifact: &'a DebugArtifact, running: bool, - source_to_opcodes: BTreeMap>, - next_breakpoint_id: i64, - instruction_breakpoints: Vec<(OpcodeLocation, i64)>, - source_breakpoints: BTreeMap>, + next_breakpoint_id: BreakpointId, + instruction_breakpoints: Vec<(OpcodeLocation, BreakpointId)>, + source_breakpoints: BTreeMap>, } enum ScopeReferences { @@ -57,8 +57,6 @@ impl From for ScopeReferences { } } -// BTreeMap - impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { pub fn new( server: Server, @@ -67,7 +65,6 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { debug_artifact: &'a DebugArtifact, initial_witness: WitnessMap, ) -> Self { - let source_to_opcodes = Self::build_source_to_opcode_debug_mappings(debug_artifact); let context = DebugContext::new( solver, circuit, @@ -79,7 +76,6 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { server, context, debug_artifact, - source_to_opcodes, running: false, next_breakpoint_id: 1, instruction_breakpoints: vec![], @@ -87,46 +83,6 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { } } - /// Builds a map from FileId to an ordered vector of tuples with line - /// numbers and opcode locations corresponding to those line numbers - fn build_source_to_opcode_debug_mappings( - debug_artifact: &'a DebugArtifact, - ) -> BTreeMap> { - if debug_artifact.debug_symbols.is_empty() { - return BTreeMap::new(); - } - let locations = &debug_artifact.debug_symbols[0].locations; - let simple_files: BTreeMap<_, _> = debug_artifact - .file_map - .iter() - .map(|(file_id, debug_file)| { - ( - file_id, - SimpleFile::new(debug_file.path.to_str().unwrap(), debug_file.source.as_str()), - ) - }) - .collect(); - - let mut result: BTreeMap> = BTreeMap::new(); - locations.iter().for_each(|(opcode_location, source_locations)| { - if source_locations.is_empty() { - return; - } - let source_location = source_locations[0]; - let span = source_location.span; - let file_id = source_location.file; - let Ok(line_index) = &simple_files[&file_id].line_index((), span.start() as usize) - else { - return; - }; - let line_number = line_index + 1; - - result.entry(file_id).or_default().push((line_number, *opcode_location)); - }); - result.iter_mut().for_each(|(_, file_locations)| file_locations.sort_by_key(|x| x.0)); - result - } - fn send_stopped_event(&mut self, reason: StoppedEventReason) -> Result<(), ServerError> { let description = format!("{:?}", &reason); self.server.send_event(Event::Stopped(StoppedEventBody { @@ -230,6 +186,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { } fn build_stack_trace(&self) -> Vec { + let stack_frames = self.context.get_variables(); + self.context .get_source_call_stack() .iter() @@ -239,9 +197,15 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { self.debug_artifact.location_line_number(*source_location).unwrap(); let column_number = self.debug_artifact.location_column_number(*source_location).unwrap(); + + let name = match stack_frames.get(index) { + Some(frame) => format!("{} {}", frame.function_name, index), + None => format!("frame #{index}"), + }; + StackFrame { id: index as i64, - name: format!("frame #{index}"), + name, source: Some(Source { path: self.debug_artifact.file_map[&source_location.file] .path @@ -422,7 +386,7 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { Ok(()) } - fn get_next_breakpoint_id(&mut self) -> i64 { + fn get_next_breakpoint_id(&mut self) -> BreakpointId { let id = self.next_breakpoint_id; self.next_breakpoint_id += 1; id @@ -493,36 +457,6 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { found.map(|iter| *iter.0) } - // TODO: there are four possibilities for the return value of this function: - // 1. the source location is not found -> None - // 2. an exact unique location is found -> Some(opcode_location) - // 3. an exact but not unique location is found (ie. a source location may - // be mapped to multiple opcodes, and those may be disjoint, for example for - // functions called multiple times throughout the program) - // 4. exact location is not found, so an opcode for a nearby source location - // is returned (this again could actually be more than one opcodes) - // Case 3 is not supported yet, and 4 is not correctly handled. - fn find_opcode_for_source_location( - &self, - file_id: &FileId, - line: i64, - ) -> Option { - let line = line as usize; - let Some(line_to_opcodes) = self.source_to_opcodes.get(file_id) else { - return None; - }; - let found_index = match line_to_opcodes.binary_search_by(|x| x.0.cmp(&line)) { - Ok(index) => line_to_opcodes[index].1, - Err(index) => { - if index >= line_to_opcodes.len() { - return None; - } - line_to_opcodes[index].1 - } - }; - Some(found_index) - } - fn map_source_breakpoints(&mut self, args: &SetBreakpointsArguments) -> Vec { let Some(ref source) = &args.source.path else { return vec![]; @@ -539,7 +473,8 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { .iter() .map(|breakpoint| { let line = breakpoint.line; - let Some(location) = self.find_opcode_for_source_location(&file_id, line) else { + let Some(location) = self.context.find_opcode_for_source_location(&file_id, line) + else { return Breakpoint { verified: false, message: Some(String::from( @@ -608,16 +543,20 @@ impl<'a, R: Read, W: Write, B: BlackBoxFunctionSolver> DapSession<'a, R, W, B> { } fn build_local_variables(&self) -> Vec { - let mut variables: Vec<_> = self - .context - .get_variables() + let Some(current_stack_frame) = self.context.current_stack_frame() else { + return vec![]; + }; + + let mut variables = current_stack_frame + .variables .iter() .map(|(name, value, _var_type)| Variable { name: String::from(*name), value: format!("{:?}", *value), ..Variable::default() }) - .collect(); + .collect::>(); + variables.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap()); variables } diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 68c4d3947b0..25f126ff490 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -3,17 +3,19 @@ use acvm::{ pwg::ForeignCallWaitInfo, }; use nargo::{ - artifacts::debug::{DebugArtifact, DebugVars}, + artifacts::debug::{DebugArtifact, DebugVars, StackFrame}, ops::{DefaultForeignCallExecutor, ForeignCallExecutor, NargoForeignCallResult}, }; -use noirc_errors::debug_info::DebugVarId; -use noirc_printable_type::{ForeignCallError, PrintableType, PrintableValue}; +use noirc_errors::debug_info::{DebugFnId, DebugVarId}; +use noirc_printable_type::ForeignCallError; pub(crate) enum DebugForeignCall { VarAssign, VarDrop, MemberAssign(u32), DerefAssign, + FnEnter, + FnExit, } impl DebugForeignCall { @@ -28,13 +30,16 @@ impl DebugForeignCall { "__debug_var_assign" => Some(DebugForeignCall::VarAssign), "__debug_var_drop" => Some(DebugForeignCall::VarDrop), "__debug_deref_assign" => Some(DebugForeignCall::DerefAssign), + "__debug_fn_enter" => Some(DebugForeignCall::FnEnter), + "__debug_fn_exit" => Some(DebugForeignCall::FnExit), _ => None, } } } pub trait DebugForeignCallExecutor: ForeignCallExecutor { - fn get_variables(&self) -> Vec<(&str, &PrintableValue, &PrintableType)>; + fn get_variables(&self) -> Vec; + fn current_stack_frame(&self) -> Option; } pub struct DefaultDebugForeignCallExecutor { @@ -57,23 +62,33 @@ impl DefaultDebugForeignCallExecutor { } pub fn load_artifact(&mut self, artifact: &DebugArtifact) { - artifact.debug_symbols.iter().for_each(|info| { - self.debug_vars.insert_variables(&info.variables); - self.debug_vars.insert_types(&info.types); - }); + // TODO: handle loading from the correct DebugInfo when we support + // debugging contracts + let Some(info) = artifact.debug_symbols.get(0) else { + return; + }; + self.debug_vars.insert_debug_info(info); } } impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { - fn get_variables(&self) -> Vec<(&str, &PrintableValue, &PrintableType)> { + fn get_variables(&self) -> Vec { self.debug_vars.get_variables() } + + fn current_stack_frame(&self) -> Option { + self.debug_vars.current_stack_frame() + } } fn debug_var_id(value: &Value) -> DebugVarId { DebugVarId(value.to_u128() as u32) } +fn debug_fn_id(value: &Value) -> DebugFnId { + DebugFnId(value.to_u128() as u32) +} + impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { fn execute( &mut self, @@ -136,6 +151,19 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { } Ok(ForeignCallResult::default().into()) } + Some(DebugForeignCall::FnEnter) => { + let fcp_fn_id = &foreign_call.inputs[0]; + let ForeignCallParam::Single(fn_id_value) = fcp_fn_id else { + panic!("unexpected foreign call parameter in fn enter: {fcp_fn_id:?}") + }; + let fn_id = debug_fn_id(fn_id_value); + self.debug_vars.push_fn(fn_id); + Ok(ForeignCallResult::default().into()) + } + Some(DebugForeignCall::FnExit) => { + self.debug_vars.pop_fn(); + Ok(ForeignCallResult::default().into()) + } None => self.executor.execute(foreign_call), } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 8441dbde9be..41dbf604f99 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -337,11 +337,13 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { } pub fn show_vars(&self) { - let vars = self.context.get_variables(); - for (var_name, value, var_type) in vars.iter() { - let printable_value = - PrintableValueDisplay::Plain((*value).clone(), (*var_type).clone()); - println!("{var_name}:{var_type:?} = {}", printable_value); + for frame in self.context.get_variables() { + println!("{}({})", frame.function_name, frame.function_params.join(", ")); + for (var_name, value, var_type) in frame.variables.iter() { + let printable_value = + PrintableValueDisplay::Plain((*value).clone(), (*var_type).clone()); + println!(" {var_name}:{var_type:?} = {}", printable_value); + } } } @@ -530,7 +532,7 @@ pub fn run( .add( "vars", command! { - "show variable values available at this point in execution", + "show variables for each function scope available at this point in execution", () => || { ref_context.borrow_mut().show_vars(); Ok(CommandStatus::Done) diff --git a/tooling/debugger/src/source_code_printer.rs b/tooling/debugger/src/source_code_printer.rs index b5ffdb12d01..e298eb8aadd 100644 --- a/tooling/debugger/src/source_code_printer.rs +++ b/tooling/debugger/src/source_code_printer.rs @@ -30,7 +30,7 @@ struct LocationPrintContext { // Given a DebugArtifact and an OpcodeLocation, prints all the source code // locations the OpcodeLocation maps to, with some surrounding context and // visual aids to highlight the location itself. -pub(crate) fn print_source_code_location(debug_artifact: &DebugArtifact, locations: &[Location]) { +pub(super) fn print_source_code_location(debug_artifact: &DebugArtifact, locations: &[Location]) { let locations = locations.iter(); for loc in locations { @@ -269,8 +269,12 @@ mod tests { let mut opcode_locations = BTreeMap::>::new(); opcode_locations.insert(OpcodeLocation::Acir(42), vec![loc]); - let debug_symbols = - vec![DebugInfo::new(opcode_locations, BTreeMap::default(), BTreeMap::default())]; + let debug_symbols = vec![DebugInfo::new( + opcode_locations, + BTreeMap::default(), + BTreeMap::default(), + BTreeMap::default(), + )]; let debug_artifact = DebugArtifact::new(debug_symbols, &fm); let location_rendered: Vec<_> = render_location(&debug_artifact, &loc).collect(); diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 4cb678192b8..143ee7987f8 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -12,7 +12,7 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); - let mut dbg_session = spawn_bash(Some(10000)).expect("Could not start bash session"); + let mut dbg_session = spawn_bash(Some(15000)).expect("Could not start bash session"); // Set backend to `/dev/null` to force an error if nargo tries to speak to a backend. dbg_session diff --git a/tooling/nargo/src/artifacts/debug.rs b/tooling/nargo/src/artifacts/debug.rs index a249ecb03ad..fbdf59805c9 100644 --- a/tooling/nargo/src/artifacts/debug.rs +++ b/tooling/nargo/src/artifacts/debug.rs @@ -8,7 +8,7 @@ use std::{ ops::Range, }; -pub use super::debug_vars::DebugVars; +pub use super::debug_vars::{DebugVars, StackFrame}; use fm::{FileId, FileManager, PathString}; /// A Debug Artifact stores, for a given program, the debug info for every function @@ -231,8 +231,12 @@ mod tests { let mut opcode_locations = BTreeMap::>::new(); opcode_locations.insert(OpcodeLocation::Acir(42), vec![loc]); - let debug_symbols = - vec![DebugInfo::new(opcode_locations, BTreeMap::default(), BTreeMap::default())]; + let debug_symbols = vec![DebugInfo::new( + opcode_locations, + BTreeMap::default(), + BTreeMap::default(), + BTreeMap::default(), + )]; let debug_artifact = DebugArtifact::new(debug_symbols, &fm); let location_in_line = debug_artifact.location_in_line(loc).expect("Expected a range"); diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index 20f2637f7d6..66568bec833 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -1,54 +1,85 @@ use acvm::brillig_vm::brillig::Value; use noirc_errors::debug_info::{ - DebugTypeId, DebugTypes, DebugVarId, DebugVariable, DebugVariables, + DebugFnId, DebugFunction, DebugInfo, DebugTypeId, DebugVarId, DebugVariable, }; use noirc_printable_type::{decode_value, PrintableType, PrintableValue}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; #[derive(Debug, Default, Clone)] pub struct DebugVars { variables: HashMap, + functions: HashMap, types: HashMap, - active: HashSet, - values: HashMap, + frames: Vec<(DebugFnId, HashMap)>, +} + +pub struct StackFrame<'a> { + pub function_name: &'a str, + pub function_params: Vec<&'a str>, + pub variables: Vec<(&'a str, &'a PrintableValue, &'a PrintableType)>, } impl DebugVars { - pub fn get_variables(&self) -> Vec<(&str, &PrintableValue, &PrintableType)> { - self.active - .iter() - .filter_map(|var_id| { - self.variables.get(var_id).and_then(|debug_var| { - let Some(value) = self.values.get(var_id) else { - return None; - }; - let Some(ptype) = self.types.get(&debug_var.debug_type_id) else { - return None; - }; - Some((debug_var.name.as_str(), value, ptype)) - }) - }) - .collect() + pub fn insert_debug_info(&mut self, info: &DebugInfo) { + self.variables.extend(info.variables.clone()); + self.types.extend(info.types.clone()); + self.functions.extend(info.functions.clone()); + } + + pub fn get_variables(&self) -> Vec { + self.frames.iter().map(|(fn_id, frame)| self.build_stack_frame(fn_id, frame)).collect() } - pub fn insert_variables(&mut self, vars: &DebugVariables) { - self.variables.extend(vars.clone()); + pub fn current_stack_frame(&self) -> Option { + self.frames.last().map(|(fn_id, frame)| self.build_stack_frame(fn_id, frame)) } - pub fn insert_types(&mut self, types: &DebugTypes) { - self.types.extend(types.clone()); + fn lookup_var(&self, var_id: DebugVarId) -> Option<(&str, &PrintableType)> { + self.variables.get(&var_id).and_then(|debug_var| { + let Some(ptype) = self.types.get(&debug_var.debug_type_id) else { + return None; + }; + Some((debug_var.name.as_str(), ptype)) + }) + } + + fn build_stack_frame<'a>( + &'a self, + fn_id: &DebugFnId, + frame: &'a HashMap, + ) -> StackFrame { + let debug_fn = &self.functions.get(fn_id).expect("failed to find function metadata"); + + let params: Vec<&str> = + debug_fn.arg_names.iter().map(|arg_name| arg_name.as_str()).collect(); + let vars: Vec<(&str, &PrintableValue, &PrintableType)> = frame + .iter() + .filter_map(|(var_id, var_value)| { + self.lookup_var(*var_id).map(|(name, typ)| (name, var_value, typ)) + }) + .collect(); + + StackFrame { + function_name: debug_fn.name.as_str(), + function_params: params, + variables: vars, + } } pub fn assign_var(&mut self, var_id: DebugVarId, values: &[Value]) { - self.active.insert(var_id); let type_id = &self.variables.get(&var_id).unwrap().debug_type_id; let ptype = self.types.get(type_id).unwrap(); - self.values.insert(var_id, decode_value(&mut values.iter().map(|v| v.to_field()), ptype)); + + self.frames + .last_mut() + .expect("unexpected empty stack frames") + .1 + .insert(var_id, decode_value(&mut values.iter().map(|v| v.to_field()), ptype)); } pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[Value]) { - let mut cursor: &mut PrintableValue = self - .values + let current_frame = &mut self.frames.last_mut().expect("unexpected empty stack frames").1; + let mut cursor: &mut PrintableValue = current_frame .get_mut(&var_id) .unwrap_or_else(|| panic!("value unavailable for var_id {var_id:?}")); let cursor_type_id = &self @@ -102,7 +133,6 @@ impl DebugVars { }; } *cursor = decode_value(&mut values.iter().map(|v| v.to_field()), cursor_type); - self.active.insert(var_id); } pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[Value]) { @@ -114,6 +144,14 @@ impl DebugVars { } pub fn drop_var(&mut self, var_id: DebugVarId) { - self.active.remove(&var_id); + self.frames.last_mut().expect("unexpected empty stack frames").1.remove(&var_id); + } + + pub fn push_fn(&mut self, fn_id: DebugFnId) { + self.frames.push((fn_id, HashMap::default())); + } + + pub fn pop_fn(&mut self) { + self.frames.pop(); } } From 1510702305f8c14d49f0096395c264eb641b79d5 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 7 Mar 2024 21:05:36 +0000 Subject: [PATCH 050/416] chore: pass `import_directive` by reference (#4511) # Description ## Problem\* Resolves ## Summary\* We don't need to pass by value here so we can just pass by reference instead. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs | 2 +- compiler/noirc_frontend/src/hir/resolution/import.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 4a6ca5c6993..27b1d376f11 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -278,7 +278,7 @@ impl DefCollector { // Resolve unresolved imports collected from the crate, one by one. for collected_import in def_collector.collected_imports { - match resolve_import(crate_id, collected_import, &context.def_maps) { + match resolve_import(crate_id, &collected_import, &context.def_maps) { Ok(resolved_import) => { // Populate module namespaces according to the imports used let current_def_map = context.def_maps.get_mut(&crate_id).unwrap(); diff --git a/compiler/noirc_frontend/src/hir/resolution/import.rs b/compiler/noirc_frontend/src/hir/resolution/import.rs index e6ac33053a0..9c8418daf80 100644 --- a/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -52,7 +52,7 @@ impl From for CustomDiagnostic { pub fn resolve_import( crate_id: CrateId, - import_directive: ImportDirective, + import_directive: &ImportDirective, def_maps: &BTreeMap, ) -> Result { let def_map = &def_maps[&crate_id]; @@ -62,10 +62,10 @@ pub fn resolve_import( let module_scope = import_directive.module_id; let resolved_namespace = - resolve_path_to_ns(&import_directive, def_map, def_maps, allow_contracts) + resolve_path_to_ns(import_directive, def_map, def_maps, allow_contracts) .map_err(|error| (error, module_scope))?; - let name = resolve_path_name(&import_directive); + let name = resolve_path_name(import_directive); Ok(ResolvedImport { name, resolved_namespace, From 169127444e8b16a8aad4acfe29ba812894fd897c Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 7 Mar 2024 15:09:50 -0600 Subject: [PATCH 051/416] fix: Force src impl for == on slices (#4507) # Description ## Problem\* Resolves #4506 ## Summary\* `==` on slices previously tried to use the built-in impl we have but this leads to a panic when evaluating binary operators in SSA. We expect both sides of the binary to be non-tuples but this isn't true for slice values. Instead of making this work for tuples I changed the type checker to force slices to use the stdlib impl we have for `==` rather than the built in one. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/hir/type_check/expr.rs | 8 +++++++- test_programs/execution_success/slices/src/main.nr | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 7b854e58fca..c5287d35caf 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -863,7 +863,13 @@ impl<'interner> TypeChecker<'interner> { span: op.location.span, }); - self.comparator_operand_type_rules(x_type, y_type, op, span) + let (_, use_impl) = self.comparator_operand_type_rules(x_type, y_type, op, span)?; + + // If the size is not constant, we must fall back to a user-provided impl for + // equality on slices. + let size = x_size.follow_bindings(); + let use_impl = use_impl || size.evaluate_to_u64().is_none(); + Ok((Bool, use_impl)) } (String(x_size), String(y_size)) => { diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr index eca42a660c4..6823bf05d96 100644 --- a/test_programs/execution_success/slices/src/main.nr +++ b/test_programs/execution_success/slices/src/main.nr @@ -50,6 +50,8 @@ fn main(x: Field, y: pub Field) { // The parameters to this function must come from witness values (inputs to main) regression_merge_slices(x, y); regression_2370(); + + regression_4506(); } // Ensure that slices of struct/tuple values work. fn regression_2083() { @@ -297,3 +299,8 @@ fn regression_2370() { let mut slice = []; slice = [1, 2, 3]; } + +fn regression_4506() { + let slice: [Field] = [1, 2, 3]; + assert(slice == slice); +} From 3163d81a357161979ea63ecb9ea09df20502ff0b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 7 Mar 2024 21:42:48 +0000 Subject: [PATCH 052/416] chore: add `ModuleDeclaration` struct (#4512) # Description ## Problem\* Resolves ## Summary\* This is some groundwork pulled out from https://github.com/noir-lang/noir/pull/4491 We're going to want to apply visibility modifiers to module declarations so this PR creates a proper struct for these so it's easier for us to add an extra field to hold the visibility. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/ast/statement.rs | 11 +++++++++++ .../src/hir/def_collector/dc_mod.rs | 18 +++++++++--------- compiler/noirc_frontend/src/parser/mod.rs | 18 +++++++++--------- compiler/noirc_frontend/src/parser/parser.rs | 10 ++++++---- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index f39b71405d3..387840b57c4 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -240,6 +240,17 @@ pub trait Recoverable { fn error(span: Span) -> Self; } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct ModuleDeclaration { + pub ident: Ident, +} + +impl std::fmt::Display for ModuleDeclaration { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "mod {}", self.ident) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] pub struct ImportStatement { pub path: Path, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 77224cc311c..5b2f815d636 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -9,8 +9,8 @@ use crate::{ hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, - FunctionDefinition, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, NoirTraitImpl, - NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, + FunctionDefinition, Ident, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, + NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, }; use super::{ @@ -522,15 +522,15 @@ impl<'a> ModCollector<'a> { fn parse_module_declaration( &mut self, context: &mut Context, - mod_name: &Ident, + mod_decl: &ModuleDeclaration, crate_id: CrateId, ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; let child_file_id = - match find_module(&context.file_manager, self.file_id, &mod_name.0.contents) { + match find_module(&context.file_manager, self.file_id, &mod_decl.ident.0.contents) { Ok(child_file_id) => child_file_id, Err(expected_path) => { - let mod_name = mod_name.clone(); + let mod_name = mod_decl.ident.clone(); let err = DefCollectorErrorKind::UnresolvedModuleDecl { mod_name, expected_path }; errors.push((err.into(), self.file_id)); @@ -538,17 +538,17 @@ impl<'a> ModCollector<'a> { } }; - let location = Location { file: self.file_id, span: mod_name.span() }; + let location = Location { file: self.file_id, span: mod_decl.ident.span() }; if let Some(old_location) = context.visited_files.get(&child_file_id) { let error = DefCollectorErrorKind::ModuleAlreadyPartOfCrate { - mod_name: mod_name.clone(), + mod_name: mod_decl.ident.clone(), span: location.span, }; errors.push((error.into(), location.file)); let error = DefCollectorErrorKind::ModuleOriginallyDefined { - mod_name: mod_name.clone(), + mod_name: mod_decl.ident.clone(), span: old_location.span, }; errors.push((error.into(), old_location.file)); @@ -566,7 +566,7 @@ impl<'a> ModCollector<'a> { ); // Add module into def collector and get a ModuleId - match self.push_child_module(mod_name, child_file_id, true, false) { + match self.push_child_module(&mod_decl.ident, child_file_id, true, false) { Ok(child_mod_id) => { errors.extend(collect_defs( self.def_collector, diff --git a/compiler/noirc_frontend/src/parser/mod.rs b/compiler/noirc_frontend/src/parser/mod.rs index 0ff7819c00f..ea96dee8a47 100644 --- a/compiler/noirc_frontend/src/parser/mod.rs +++ b/compiler/noirc_frontend/src/parser/mod.rs @@ -14,8 +14,8 @@ mod parser; use crate::token::{Keyword, Token}; use crate::{ast::ImportStatement, Expression, NoirStruct}; use crate::{ - Ident, LetStatement, NoirFunction, NoirTrait, NoirTraitImpl, NoirTypeAlias, Recoverable, - StatementKind, TypeImpl, UseTree, + Ident, LetStatement, ModuleDeclaration, NoirFunction, NoirTrait, NoirTraitImpl, NoirTypeAlias, + Recoverable, StatementKind, TypeImpl, UseTree, }; use chumsky::prelude::*; @@ -28,7 +28,7 @@ pub use parser::parse_program; #[derive(Debug, Clone)] pub(crate) enum TopLevelStatement { Function(NoirFunction), - Module(Ident), + Module(ModuleDeclaration), Import(UseTree), Struct(NoirStruct), Trait(NoirTrait), @@ -220,7 +220,7 @@ pub struct SortedModule { pub globals: Vec, /// Module declarations like `mod foo;` - pub module_decls: Vec, + pub module_decls: Vec, /// Full submodules as in `mod foo { ... definitions ... }` pub submodules: Vec, @@ -229,7 +229,7 @@ pub struct SortedModule { impl std::fmt::Display for SortedModule { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for decl in &self.module_decls { - writeln!(f, "mod {decl};")?; + writeln!(f, "{decl};")?; } for import in &self.imports { @@ -309,7 +309,7 @@ pub enum ItemKind { Impl(TypeImpl), TypeAlias(NoirTypeAlias), Global(LetStatement), - ModuleDecl(Ident), + ModuleDecl(ModuleDeclaration), Submodules(ParsedSubModule), } @@ -380,8 +380,8 @@ impl SortedModule { self.imports.extend(import_stmt.desugar(None)); } - fn push_module_decl(&mut self, mod_name: Ident) { - self.module_decls.push(mod_name); + fn push_module_decl(&mut self, mod_decl: ModuleDeclaration) { + self.module_decls.push(mod_decl); } fn push_submodule(&mut self, submodule: SortedSubModule) { @@ -474,7 +474,7 @@ impl std::fmt::Display for TopLevelStatement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { TopLevelStatement::Function(fun) => fun.fmt(f), - TopLevelStatement::Module(m) => write!(f, "mod {m}"), + TopLevelStatement::Module(m) => m.fmt(f), TopLevelStatement::Import(tree) => write!(f, "use {tree}"), TopLevelStatement::Trait(t) => t.fmt(f), TopLevelStatement::TraitImpl(i) => i.fmt(f), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 75f4a6359bf..383a1ffafc9 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -40,9 +40,9 @@ use crate::parser::{force, ignore_then_commit, statement_recovery}; use crate::token::{Keyword, Token, TokenKind}; use crate::{ BinaryOp, BinaryOpKind, BlockExpression, Distinctness, ForLoopStatement, ForRange, - FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Literal, NoirTypeAlias, - Param, Path, Pattern, Recoverable, Statement, TraitBound, TypeImpl, UnresolvedTraitConstraint, - UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, + FunctionReturnType, Ident, IfExpression, InfixExpression, LValue, Literal, ModuleDeclaration, + NoirTypeAlias, Param, Path, Pattern, Recoverable, Statement, TraitBound, TypeImpl, + UnresolvedTraitConstraint, UnresolvedTypeExpression, UseTree, UseTreeKind, Visibility, }; use chumsky::prelude::*; @@ -370,7 +370,9 @@ fn optional_type_annotation<'a>() -> impl NoirParser + 'a { } fn module_declaration() -> impl NoirParser { - keyword(Keyword::Mod).ignore_then(ident()).map(TopLevelStatement::Module) + keyword(Keyword::Mod) + .ignore_then(ident()) + .map(|ident| TopLevelStatement::Module(ModuleDeclaration { ident })) } fn use_statement() -> impl NoirParser { From 8a5359c012579e54c2766de1074482a36ecada32 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 7 Mar 2024 16:01:08 -0600 Subject: [PATCH 053/416] fix: Allow type aliases in main (#4505) # Description ## Problem\* Resolves #4500 ## Summary\* This was due to this check https://github.com/noir-lang/noir/blob/master/compiler/noirc_frontend/src/hir_def/types.rs#L706-L709 which was needed since we checked if types were valid for main during name resolution - which is before we know type aliases are not cyclic. I've moved this check to type checking instead. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/resolution/errors.rs | 5 -- .../src/hir/resolution/resolver.rs | 88 +------------------ .../src/hir/type_check/errors.rs | 5 ++ .../noirc_frontend/src/hir/type_check/mod.rs | 20 ++++- .../noirc_frontend/src/hir_def/function.rs | 4 + compiler/noirc_frontend/src/hir_def/types.rs | 8 +- compiler/noirc_frontend/src/tests.rs | 9 ++ 7 files changed, 42 insertions(+), 97 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index d2fe67da38c..1049599f079 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -80,8 +80,6 @@ pub enum ResolverError { PrivateFunctionCalled { name: String, span: Span }, #[error("{name} is not visible from the current crate")] NonCrateFunctionCalled { name: String, span: Span }, - #[error("Only sized types may be used in the entry point to a program")] - InvalidTypeForEntryPoint { span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, #[error("#[recursive] attribute is only allowed on entry points to a program")] @@ -309,9 +307,6 @@ impl From for Diagnostic { ResolverError::NonCrateFunctionCalled { span, name } => Diagnostic::simple_warning( format!("{name} is not visible from the current crate"), format!("{name} is only visible within its crate"), span), - ResolverError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( - "Only sized types may be used in the entry point to a program".to_string(), - "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), ResolverError::NestedSlices { span } => Diagnostic::simple_error( "Nested slices are not supported".into(), "Try to use a constant sized array instead".into(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 7789c06ca69..322891f0ae9 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -911,10 +911,6 @@ impl<'a> Resolver<'a> { }); } - if self.is_entry_point_function(func) { - self.verify_type_valid_for_program_input(&typ); - } - let pattern = self.resolve_pattern(pattern, DefinitionKind::Local(None)); let typ = self.resolve_type_inner(typ, &mut generics); @@ -991,6 +987,7 @@ impl<'a> Resolver<'a> { return_distinctness: func.def.return_distinctness, has_body: !func.def.body.is_empty(), trait_constraints: self.resolve_trait_constraints(&func.def.where_clause), + is_entry_point: self.is_entry_point_function(func), } } @@ -2003,89 +2000,6 @@ impl<'a> Resolver<'a> { } HirLiteral::FmtStr(str, fmt_str_idents) } - - /// Only sized types are valid to be used as main's parameters or the parameters to a contract - /// function. If the given type is not sized (e.g. contains a slice or NamedGeneric type), an - /// error is issued. - fn verify_type_valid_for_program_input(&mut self, typ: &UnresolvedType) { - match &typ.typ { - UnresolvedTypeData::FieldElement - | UnresolvedTypeData::Integer(_, _) - | UnresolvedTypeData::Bool - | UnresolvedTypeData::Unit - | UnresolvedTypeData::Error => (), - - UnresolvedTypeData::MutableReference(_) - | UnresolvedTypeData::Function(_, _, _) - | UnresolvedTypeData::FormatString(_, _) - | UnresolvedTypeData::TraitAsType(..) - | UnresolvedTypeData::Unspecified => { - let span = typ.span.expect("Function parameters should always have spans"); - self.push_err(ResolverError::InvalidTypeForEntryPoint { span }); - } - - UnresolvedTypeData::Array(length, element) => { - if let Some(length) = length { - self.verify_type_expression_valid_for_program_input(length); - } else { - let span = typ.span.expect("Function parameters should always have spans"); - self.push_err(ResolverError::InvalidTypeForEntryPoint { span }); - } - self.verify_type_valid_for_program_input(element); - } - UnresolvedTypeData::Expression(expression) => { - self.verify_type_expression_valid_for_program_input(expression); - } - UnresolvedTypeData::String(length) => { - if let Some(length) = length { - self.verify_type_expression_valid_for_program_input(length); - } else { - let span = typ.span.expect("Function parameters should always have spans"); - self.push_err(ResolverError::InvalidTypeForEntryPoint { span }); - } - } - UnresolvedTypeData::Named(path, generics, _) => { - // Since the type is named, we need to resolve it to see what it actually refers to - // in order to check whether it is valid. Since resolving it may lead to a - // resolution error, we have to truncate our error count to the previous count just - // in case. This is to ensure resolution errors are not issued twice when this type - // is later resolved properly. - let error_count = self.errors.len(); - let resolved = self.resolve_named_type(path.clone(), generics.clone(), &mut vec![]); - self.errors.truncate(error_count); - - if !resolved.is_valid_for_program_input() { - let span = typ.span.expect("Function parameters should always have spans"); - self.push_err(ResolverError::InvalidTypeForEntryPoint { span }); - } - } - UnresolvedTypeData::Tuple(elements) => { - for element in elements { - self.verify_type_valid_for_program_input(element); - } - } - UnresolvedTypeData::Parenthesized(typ) => self.verify_type_valid_for_program_input(typ), - } - } - - fn verify_type_expression_valid_for_program_input(&mut self, expr: &UnresolvedTypeExpression) { - match expr { - UnresolvedTypeExpression::Constant(_, _) => (), - UnresolvedTypeExpression::Variable(path) => { - let error_count = self.errors.len(); - let resolved = self.resolve_named_type(path.clone(), vec![], &mut vec![]); - self.errors.truncate(error_count); - - if !resolved.is_valid_for_program_input() { - self.push_err(ResolverError::InvalidTypeForEntryPoint { span: path.span() }); - } - } - UnresolvedTypeExpression::BinaryOperation(lhs, _, rhs, _) => { - self.verify_type_expression_valid_for_program_input(lhs); - self.verify_type_expression_valid_for_program_input(rhs); - } - } - } } /// Gives an error if a user tries to create a mutable reference diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 96d30100d8b..cba2400441f 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -122,6 +122,8 @@ pub enum TypeCheckError { ConstrainedReferenceToUnconstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, + #[error("Only sized types may be used in the entry point to a program")] + InvalidTypeForEntryPoint { span: Span }, } impl TypeCheckError { @@ -284,6 +286,9 @@ impl From for Diagnostic { let msg = format!("Constraint for `{typ}: {trait_name}` is not needed, another matching impl is already in scope"); Diagnostic::simple_warning(msg, "Unnecessary trait constraint in where clause".into(), span) } + TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( + "Only sized types may be used in the entry point to a program".to_string(), + "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 21d1c75a0f2..aab793ec867 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -14,7 +14,7 @@ mod stmt; pub use errors::TypeCheckError; use crate::{ - hir_def::{expr::HirExpression, stmt::HirStatement, traits::TraitConstraint}, + hir_def::{expr::HirExpression, function::Param, stmt::HirStatement, traits::TraitConstraint}, node_interner::{ExprId, FuncId, GlobalId, NodeInterner}, Type, }; @@ -74,6 +74,7 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec Vec, + func_id: FuncId, + param: &Param, + errors: &mut Vec, +) { + let meta = type_checker.interner.function_meta(&func_id); + if meta.is_entry_point && !param.1.is_valid_for_program_input() { + let span = param.0.span(); + errors.push(TypeCheckError::InvalidTypeForEntryPoint { span }); + } +} + fn function_info(interner: &NodeInterner, function_body_id: &ExprId) -> (noirc_errors::Span, bool) { let (expr_span, empty_function) = if let HirExpression::Block(block) = interner.expression(function_body_id) { @@ -329,6 +346,7 @@ mod test { trait_impl: None, return_type: FunctionReturnType::Default(Span::default()), trait_constraints: Vec::new(), + is_entry_point: true, }; interner.push_fn_meta(func_meta, func_id); diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index d3ab2a9393b..82bbe1aa5b6 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -112,6 +112,10 @@ pub struct FuncMeta { /// The trait impl this function belongs to, if any pub trait_impl: Option, + + /// True if this function is an entry point to the program. + /// For non-contracts, this means the function is `main`. + pub is_entry_point: bool, } impl FuncMeta { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 13fa41733cf..5ab036eef5b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -703,10 +703,10 @@ impl Type { | Type::TraitAsType(..) | Type::NotConstant => false, - // This function is called during name resolution before we've verified aliases - // are not cyclic. As a result, it wouldn't be safe to check this alias' definition - // to see if the aliased type is valid. - Type::Alias(..) => false, + Type::Alias(alias, generics) => { + let alias = alias.borrow(); + alias.get_type(generics).is_valid_for_program_input() + } Type::Array(length, element) => { length.is_valid_for_program_input() && element.is_valid_for_program_input() diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 0c8c677d9af..9be6252b10a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1206,4 +1206,13 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { "#; assert_eq!(get_program_errors(src).len(), 1); } + + #[test] + fn type_aliases_in_entry_point() { + let src = r#" + type Foo = u8; + fn main(_x: Foo) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); + } } From b60279bd4107cab04d95e0a59caecd4910228eeb Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 7 Mar 2024 22:10:41 +0000 Subject: [PATCH 054/416] chore: update various dependencies (#4513) # Description ## Problem\* Resolves ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 243 ++++++++++----------- acvm-repo/bn254_blackbox_solver/Cargo.toml | 4 +- 2 files changed, 116 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18c1f7ad40e..317418276b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,7 +391,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138985dd8aefbefeaa66b01b7f5b2b6b4c333fcef1cc5f32c63a2aabe37d6de3" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "lsp-types 0.94.1", "pin-project-lite", "rustix", @@ -521,9 +521,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bitmaps" @@ -947,14 +947,14 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode 0.3.6", "lazy_static", "libc", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -1585,12 +1585,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1743,9 +1743,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1758,9 +1758,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1768,15 +1768,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1786,15 +1786,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -1803,21 +1803,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures 0.1.31", "futures-channel", @@ -2076,9 +2076,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -2091,7 +2091,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -2100,9 +2100,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -2343,7 +2343,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" dependencies = [ "derive_more", - "futures 0.3.28", + "futures 0.3.30", "jsonrpc-core", "jsonrpc-pubsub", "log", @@ -2358,7 +2358,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "futures-executor", "futures-util", "log", @@ -2373,7 +2373,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "jsonrpc-client-transports", ] @@ -2395,7 +2395,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "hyper", "jsonrpc-core", "jsonrpc-server-utils", @@ -2411,7 +2411,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "jsonrpc-core", "lazy_static", "log", @@ -2427,7 +2427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes", - "futures 0.3.28", + "futures 0.3.30", "globset", "jsonrpc-core", "lazy_static", @@ -2485,9 +2485,9 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -3451,7 +3451,7 @@ checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.3.3", + "bitflags 2.4.2", "lazy_static", "num-traits", "rand 0.8.5", @@ -3913,15 +3913,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.4" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4292,9 +4292,9 @@ dependencies = [ [[package]] name = "shared-buffer" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cf61602ee61e2f83dd016b3e6387245291cf728ea071c378b35088125b4d995" +checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" dependencies = [ "bytes", "memmap2 0.6.2", @@ -4412,16 +4412,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -4733,7 +4723,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -5249,9 +5239,9 @@ dependencies = [ [[package]] name = "wasmer" -version = "4.2.4" +version = "4.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce45cc009177ca345a6d041f9062305ad467d15e7d41494f5b81ab46d62d7a58" +checksum = "5c15724dc25d1ee57962334aea8e41ade2675e5ea2ac6b8d42da6051b0face66" dependencies = [ "bytes", "cfg-if 1.0.0", @@ -5265,23 +5255,23 @@ dependencies = [ "shared-buffer", "target-lexicon", "thiserror", + "tracing", "wasm-bindgen", "wasmer-compiler", "wasmer-compiler-cranelift", "wasmer-derive", "wasmer-types", "wasmer-vm", - "wasmparser 0.83.0", - "wasmparser 0.95.0", + "wasmparser", "wat", "winapi", ] [[package]] name = "wasmer-compiler" -version = "4.2.4" +version = "4.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e044f6140c844602b920deb4526aea3cc9c0d7cf23f00730bb9b2034669f522a" +checksum = "55a7f3b3a96f8d844c25e2c032af9572306dd63fa93dc17bcca4c5458ac569bd" dependencies = [ "backtrace", "bytes", @@ -5300,15 +5290,15 @@ dependencies = [ "thiserror", "wasmer-types", "wasmer-vm", - "wasmparser 0.95.0", + "wasmparser", "winapi", ] [[package]] name = "wasmer-compiler-cranelift" -version = "4.2.4" +version = "4.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ce02358eb44a149d791c1d6648fb7f8b2f99cd55e3c4eef0474653ec8cc889" +checksum = "102e2c5bacac69495c4025767e2fa26797ffb27f242dccb7cf57d9cefd944386" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -5325,9 +5315,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "4.2.4" +version = "4.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c782d80401edb08e1eba206733f7859db6c997fc5a7f5fb44edc3ecd801468f6" +checksum = "0ea737fa08f95d6abc4459f42a70a9833e8974b814e74971d77ef473814f4d4c" dependencies = [ "proc-macro-error", "proc-macro2", @@ -5337,9 +5327,9 @@ dependencies = [ [[package]] name = "wasmer-types" -version = "4.2.4" +version = "4.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd09e80d4d74bb9fd0ce6c3c106b1ceba1a050f9948db9d9b78ae53c172d6157" +checksum = "b0689110e291b0f07fc665f2824e5ff81df120848e8a9acfbf1a9bf7990773f9" dependencies = [ "bytecheck", "enum-iterator", @@ -5353,9 +5343,9 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "4.2.4" +version = "4.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdcd8a4fd36414a7b6a003dbfbd32393bce3e155d715dd877c05c1b7a41d224d" +checksum = "4cd41f822a1ac4242d478754e8ceba2806a00ea5072803622e1fe91e8e28b2a1" dependencies = [ "backtrace", "cc", @@ -5381,18 +5371,13 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.83.0" +version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" - -[[package]] -name = "wasmparser" -version = "0.95.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ - "indexmap 1.9.3", - "url 2.4.0", + "bitflags 2.4.2", + "indexmap 2.0.0", + "semver", ] [[package]] @@ -5485,15 +5470,6 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5504,18 +5480,12 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.52.4", ] [[package]] @@ -5534,10 +5504,19 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" +name = "windows-targets" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] [[package]] name = "windows_aarch64_gnullvm" @@ -5545,6 +5524,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.33.0" @@ -5553,15 +5538,15 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -5571,15 +5556,15 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -5589,15 +5574,15 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -5607,27 +5592,27 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -5637,15 +5622,15 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index ea601a6b80f..a0a15409604 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -32,7 +32,7 @@ ark-ff = { version = "^0.4.0", default-features = false } num-bigint.workspace = true [target.'cfg(target_arch = "wasm32")'.dependencies] -wasmer = { version = "4.2.3", default-features = false, features = [ +wasmer = { version = "4.2.6", default-features = false, features = [ "js-default", ] } @@ -42,7 +42,7 @@ js-sys.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] getrandom.workspace = true -wasmer = "4.2.3" +wasmer = "4.2.6" [build-dependencies] pkg-config = "0.3" From 9cee413b73298df98a5650bf3d05121213e982e2 Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:02:25 +0100 Subject: [PATCH 055/416] chore: custom hash for eddsa (#4440) # Description ## Problem\* Resolves #3642 ## Summary\* Eddsa verification takes now a hasher so that it can be used with anything having the Hasher trait. I added this trait to the stdlib implementations of mimc, poseidon and poseidon2. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [X] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .../cryptographic_primitives/eddsa.mdx | 8 ++ noir_stdlib/src/eddsa.nr | 31 +++++++- noir_stdlib/src/hash/mimc.nr | 50 ++++++++++-- noir_stdlib/src/hash/poseidon.nr | 76 +++++++++++++++++++ noir_stdlib/src/hash/poseidon2.nr | 39 +++++++++- .../execution_success/eddsa/src/main.nr | 10 ++- .../poseidon_bn254_hash/src/main.nr | 2 +- 7 files changed, 202 insertions(+), 14 deletions(-) diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx index 99b7f830a20..c2c0624dfad 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -15,6 +15,14 @@ Verifier for EdDSA signatures fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool ``` +It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify_with_hasher` function with a parameter implementing the Hasher trait. For instance, if you want to use Poseidon2 instead, you can do the following: +```rust +use dep::std::hash::poseidon2::Poseidon2Hasher; + +let mut hasher = Poseidon2Hasher::default(); +eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher); +``` + ## eddsa::eddsa_to_pub diff --git a/noir_stdlib/src/eddsa.nr b/noir_stdlib/src/eddsa.nr index 966bc1da2a1..3aff6043ffd 100644 --- a/noir_stdlib/src/eddsa.nr +++ b/noir_stdlib/src/eddsa.nr @@ -1,6 +1,8 @@ use crate::hash::poseidon; use crate::ec::consts::te::baby_jubjub; use crate::ec::tecurve::affine::Point as TEPoint; +use crate::hash::{Hash, Hasher, BuildHasher, BuildHasherDefault}; +use crate::hash::poseidon::PoseidonHasher; // Returns true if signature is valid pub fn eddsa_poseidon_verify( @@ -11,6 +13,28 @@ pub fn eddsa_poseidon_verify( signature_r8_y: Field, message: Field ) -> bool { + let mut hasher = PoseidonHasher::default(); + eddsa_verify_with_hasher( + pub_key_x, + pub_key_y, + signature_s, + signature_r8_x, + signature_r8_y, + message, + &mut hasher + ) +} + +pub fn eddsa_verify_with_hasher( + pub_key_x: Field, + pub_key_y: Field, + signature_s: Field, + signature_r8_x: Field, + signature_r8_y: Field, + message: Field, + hasher: &mut H +) -> bool +where H: Hasher { // Verifies by testing: // S * B8 = R8 + H(R8, A, m) * A8 let bjj = baby_jubjub(); @@ -23,7 +47,12 @@ pub fn eddsa_poseidon_verify( // Ensure S < Subgroup Order assert(signature_s.lt(bjj.suborder)); // Calculate the h = H(R, A, msg) - let hash: Field = poseidon::bn254::hash_5([signature_r8_x, signature_r8_y, pub_key_x, pub_key_y, message]); + signature_r8_x.hash(hasher); + signature_r8_y.hash(hasher); + pub_key_x.hash(hasher); + pub_key_y.hash(hasher); + message.hash(hasher); + let hash: Field = (*hasher).finish(); // Calculate second part of the right side: right2 = h*8*A // Multiply by 8 by doubling 3 times. This also ensures that the result is in the subgroup. let pub_key_mul_2 = bjj.curve.add(pub_key, pub_key); diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr index 10c0a48917c..db8a32d7909 100644 --- a/noir_stdlib/src/hash/mimc.nr +++ b/noir_stdlib/src/hash/mimc.nr @@ -1,3 +1,6 @@ +use crate::hash::Hasher; +use crate::default::Default; + // mimc-p/p implementation // constants are (publicly generated) random numbers, for instance using keccak as a ROM. // You must use constants generated for the native field @@ -16,13 +19,8 @@ fn mimc(x: Field, k: Field, constants: [Field; N], exp: Field) -> Field { } global MIMC_BN254_ROUNDS = 91; -//mimc implementation with hardcoded parameters for BN254 curve. -#[field(bn254)] -pub fn mimc_bn254(array: [Field; N]) -> Field { - //mimc parameters - let exponent = 7; - //generated from seed "mimc" using keccak256 - let constants: [Field; MIMC_BN254_ROUNDS] = [ +//generated from seed "mimc" using keccak256 +global MIMC_BN254_CONSTANTS: [Field; MIMC_BN254_ROUNDS] = [ 0, 20888961410941983456478427210666206549300505294776164667214940546594746570981, 15265126113435022738560151911929040668591755459209400716467504685752745317193, @@ -116,10 +114,46 @@ pub fn mimc_bn254(array: [Field; N]) -> Field { 13602139229813231349386885113156901793661719180900395818909719758150455500533 ]; +//mimc implementation with hardcoded parameters for BN254 curve. +#[field(bn254)] +pub fn mimc_bn254(array: [Field; N]) -> Field { + let exponent = 7; let mut r = 0; for elem in array { - let h = mimc(elem, r, constants, exponent); + let h = mimc(elem, r, MIMC_BN254_CONSTANTS, exponent); r = r + elem + h; } r } + +struct MimcHasher{ + _state: [Field], + _len: u64, +} + +impl Hasher for MimcHasher { + #[field(bn254)] + fn finish(self) -> Field { + let exponent = 7; + let mut r = 0; + for i in 0..self._len { + let h = mimc(self._state[i], r, MIMC_BN254_CONSTANTS, exponent); + r = r + self._state[i] + h; + } + r + } + + fn write(&mut self, input: [Field]){ + self._state = self._state.append(input); + self._len += input.len(); + } +} + +impl Default for MimcHasher{ + fn default() -> Self{ + MimcHasher{ + _state: [], + _len: 0, + } + } +} diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr index b1a7c4a2367..7f99ad36316 100644 --- a/noir_stdlib/src/hash/poseidon.nr +++ b/noir_stdlib/src/hash/poseidon.nr @@ -1,5 +1,7 @@ mod bn254; // Instantiations of Poseidon for prime field of the same order as BN254 use crate::field::modulus_num_bits; +use crate::hash::Hasher; +use crate::default::Default; struct PoseidonConfig { t: Field, // Width, i.e. state size @@ -100,3 +102,77 @@ fn apply_matrix(a: [Field; M], x: [Field; N]) -> [Field; N] { y } + +struct PoseidonHasher{ + _state: [Field], + _len: u64, +} + +impl Hasher for PoseidonHasher { + #[field(bn254)] + fn finish(self) -> Field { + let mut result = 0; + assert(self._len < 16); + if self._len == 1 { + result = bn254::hash_1([self._state[0]]); + } + if self._len == 2 { + result = bn254::hash_2([self._state[0],self._state[1]]); + } + if self._len == 3 { + result = bn254::hash_3([self._state[0],self._state[1],self._state[2]]); + } + if self._len == 4 { + result = bn254::hash_4([self._state[0],self._state[1],self._state[2],self._state[3]]); + } + if self._len == 5 { + result = bn254::hash_5([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4]]); + } + if self._len == 6 { + result = bn254::hash_6([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5]]); + } + if self._len == 7 { + result = bn254::hash_7([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6]]); + } + if self._len == 8 { + result = bn254::hash_8([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7]]); + } + if self._len == 9 { + result = bn254::hash_9([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8]]); + } + if self._len == 10 { + result = bn254::hash_10([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9]]); + } + if self._len == 11 { + result = bn254::hash_11([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10]]); + } + if self._len == 12 { + result = bn254::hash_12([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11]]); + } + if self._len == 13 { + result = bn254::hash_13([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12]]); + } + if self._len == 14 { + result = bn254::hash_14([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13]]); + } + if self._len == 15 { + result = bn254::hash_15([self._state[0],self._state[1],self._state[2],self._state[3],self._state[4], self._state[5], self._state[6], self._state[7], self._state[8], self._state[9], self._state[10], self._state[11], self._state[12], self._state[13], self._state[14]]); + } + + result + } + + fn write(&mut self, input: [Field]){ + self._state = self._state.append(input); + self._len += input.len(); + } +} + +impl Default for PoseidonHasher{ + fn default() -> Self{ + PoseidonHasher{ + _state: [], + _len: 0, + } + } +} diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 40eea029e82..52229f18dbd 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -1,3 +1,6 @@ +use crate::hash::Hasher; +use crate::default::Default; + global RATE = 3; struct Poseidon2 { @@ -9,7 +12,7 @@ struct Poseidon2 { impl Poseidon2 { - pub fn hash(input: [Field; N], message_size: u32) -> Field { + pub fn hash(input: [Field; N], message_size: u64) -> Field { if message_size == N { Poseidon2::hash_internal(input, N, false) } else { @@ -92,12 +95,12 @@ impl Poseidon2 { result } - fn hash_internal(input: [Field; N], in_len: u32, is_variable_length: bool) -> Field { + fn hash_internal(input: [Field; N], in_len: u64, is_variable_length: bool) -> Field { let two_pow_64 = 18446744073709551616; let iv : Field = (in_len as Field) * two_pow_64; let mut sponge = Poseidon2::new(iv); for i in 0..input.len() { - if i as u32 < in_len { + if i < in_len { sponge.absorb(input[i]); } } @@ -111,3 +114,33 @@ impl Poseidon2 { sponge.squeeze() } } + +struct Poseidon2Hasher{ + _state: [Field], + _len: u64, +} + +impl Hasher for Poseidon2Hasher { + fn finish(self) -> Field { + let iv : Field = (self._state.len() as Field)*18446744073709551616; // iv = (self._state.len() << 64) + let mut sponge = Poseidon2::new(iv); + for i in 0..self._len { + sponge.absorb(self._state[i]); + } + sponge.squeeze() + } + + fn write(&mut self, input: [Field]){ + self._state = self._state.append(input); + self._len += input.len(); + } +} + +impl Default for Poseidon2Hasher{ + fn default() -> Self{ + Poseidon2Hasher{ + _state: [], + _len: 0, + } + } +} diff --git a/test_programs/execution_success/eddsa/src/main.nr b/test_programs/execution_success/eddsa/src/main.nr index 4404ffe75f7..fd1a95ee5fb 100644 --- a/test_programs/execution_success/eddsa/src/main.nr +++ b/test_programs/execution_success/eddsa/src/main.nr @@ -2,7 +2,9 @@ use dep::std::compat; use dep::std::ec::consts::te::baby_jubjub; use dep::std::ec::tecurve::affine::Point as TEPoint; use dep::std::hash; -use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify}; +use dep::std::eddsa::{eddsa_to_pub, eddsa_poseidon_verify, eddsa_verify_with_hasher}; +use dep::std::hash::poseidon2::Poseidon2Hasher; +use dep::std::hash::pedersen::PedersenHasher; fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { // Skip this test for non-bn254 backends @@ -48,5 +50,11 @@ fn main(msg: pub Field, _priv_key_a: Field, _priv_key_b: Field) { assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_b, r8_b.x, r8_b.y, msg)); // User A's signature over the message can't be used with another message assert(!eddsa_poseidon_verify(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg + 1)); + // Using a different hash should fail + let mut hasher = Poseidon2Hasher::default(); + assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher)); + // Using a different hash should fail + let mut hasher = PedersenHasher::default(); + assert(!eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher)); } } diff --git a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr index 939b99595c7..a1607956190 100644 --- a/test_programs/execution_success/poseidon_bn254_hash/src/main.nr +++ b/test_programs/execution_success/poseidon_bn254_hash/src/main.nr @@ -9,7 +9,7 @@ fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field let hash2 = poseidon::bn254::hash_4(x2); assert(hash2 == y2); - let hash3 = poseidon2::Poseidon2::hash(x3, x3.len() as u32); + let hash3 = poseidon2::Poseidon2::hash(x3, x3.len()); assert(hash3 == y3); } // docs:end:poseidon From 66f22aab1f0a0e9dc970e32e54c28f12fb39ea8f Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 8 Mar 2024 14:03:13 -0600 Subject: [PATCH 056/416] chore: Add HashMap docs (#4457) # Description ## Problem\* ## Summary\* Adds docs for https://github.com/noir-lang/noir/pull/4242 I've also edited the interface for `entries`, `keys`, and `values` to return `BoundedVec`s instead of arrays with optional elements. This is much more natural I think. I originally intended this for a separate PR, but I didn't want to write documentation for the old version of these functions only to immediately have to edit them afterward. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- .../standard_library/containers/hashmap.md | 270 ++++++++++++++++++ noir_stdlib/src/collections/map.nr | 112 +++++--- .../execution_success/hashmap/src/main.nr | 182 +++++++++++- .../execution_success/hashmap/src/utils.nr | 12 +- 4 files changed, 517 insertions(+), 59 deletions(-) create mode 100644 docs/docs/noir/standard_library/containers/hashmap.md diff --git a/docs/docs/noir/standard_library/containers/hashmap.md b/docs/docs/noir/standard_library/containers/hashmap.md new file mode 100644 index 00000000000..093b6d38d11 --- /dev/null +++ b/docs/docs/noir/standard_library/containers/hashmap.md @@ -0,0 +1,270 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already +known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which +will likely change the result of the program. This behavior is set to become an error in future +versions instead. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a pedersen hash +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +#include_code default noir_stdlib/src/collections/map.nr rust + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +#include_code default_example test_programs/execution_success/hashmap/src/main.nr rust + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +#include_code type_alias test_programs/execution_success/hashmap/src/main.nr rust + +### with_hasher + +#include_code with_hasher noir_stdlib/src/collections/map.nr rust + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +#include_code with_hasher_example test_programs/execution_success/hashmap/src/main.nr rust + +### get + +#include_code get noir_stdlib/src/collections/map.nr rust + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +#include_code get_example test_programs/execution_success/hashmap/src/main.nr rust + +### insert + +#include_code insert noir_stdlib/src/collections/map.nr rust + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +#include_code insert_example test_programs/execution_success/hashmap/src/main.nr rust + +### remove + +#include_code remove noir_stdlib/src/collections/map.nr rust + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +#include_code remove_example test_programs/execution_success/hashmap/src/main.nr rust + +### is_empty + +#include_code is_empty noir_stdlib/src/collections/map.nr rust + +True if the length of the hash map is empty. + +Example: + +#include_code is_empty_example test_programs/execution_success/hashmap/src/main.nr rust + +### len + +#include_code len noir_stdlib/src/collections/map.nr rust + +Returns the current length of this hash map. + +Example: + +#include_code len_example test_programs/execution_success/hashmap/src/main.nr rust + +### capacity + +#include_code capacity noir_stdlib/src/collections/map.nr rust + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +#include_code capacity_example test_programs/execution_success/hashmap/src/main.nr rust + +### clear + +#include_code clear noir_stdlib/src/collections/map.nr rust + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +#include_code clear_example test_programs/execution_success/hashmap/src/main.nr rust + +### contains_key + +#include_code contains_key noir_stdlib/src/collections/map.nr rust + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +#include_code contains_key_example test_programs/execution_success/hashmap/src/main.nr rust + +### entries + +#include_code entries noir_stdlib/src/collections/map.nr rust + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +#include_code entries_example test_programs/execution_success/hashmap/src/main.nr rust + +### keys + +#include_code keys noir_stdlib/src/collections/map.nr rust + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +#include_code keys_example test_programs/execution_success/hashmap/src/main.nr rust + +### values + +#include_code values noir_stdlib/src/collections/map.nr rust + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +#include_code values_example test_programs/execution_success/hashmap/src/main.nr rust + +### iter_mut + +#include_code iter_mut noir_stdlib/src/collections/map.nr rust + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +#include_code iter_mut_example test_programs/execution_success/hashmap/src/main.nr rust + +### iter_keys_mut + +#include_code iter_keys_mut noir_stdlib/src/collections/map.nr rust + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +#include_code iter_keys_mut_example test_programs/execution_success/hashmap/src/main.nr rust + +### iter_values_mut + +#include_code iter_values_mut noir_stdlib/src/collections/map.nr rust + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +#include_code iter_values_mut_example test_programs/execution_success/hashmap/src/main.nr rust + +### retain + +#include_code retain noir_stdlib/src/collections/map.nr rust + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +#include_code retain_example test_programs/execution_success/hashmap/src/main.nr rust + +## Trait Implementations + +### default + +#include_code default noir_stdlib/src/collections/map.nr rust + +Constructs an empty HashMap. + +Example: + +#include_code default_example test_programs/execution_success/hashmap/src/main.nr rust + +### eq + +#include_code eq noir_stdlib/src/collections/map.nr rust + +Checks if two HashMaps are equal. + +Example: + +#include_code eq_example test_programs/execution_success/hashmap/src/main.nr rust diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr index 056299b4238..2d76acf1f3a 100644 --- a/noir_stdlib/src/collections/map.nr +++ b/noir_stdlib/src/collections/map.nr @@ -3,6 +3,7 @@ use crate::collections::vec::Vec; use crate::option::Option; use crate::default::Default; use crate::hash::{Hash, Hasher, BuildHasher}; +use crate::collections::bounded_vec::BoundedVec; // We use load factor α_max = 0.75. // Upon exceeding it, assert will fail in order to inform the user @@ -78,21 +79,26 @@ impl Slot { // it is very unlikely to be after - performance will be heavily degraded. impl HashMap { // Creates a new instance of HashMap with specified BuildHasher. + // docs:start:with_hasher pub fn with_hasher(_build_hasher: B) -> Self where B: BuildHasher { + // docs:end:with_hasher let _table = [Slot::default(); N]; let _len = 0; Self { _table, _len, _build_hasher } } // Clears the map, removing all key-value entries. + // docs:start:clear pub fn clear(&mut self) { + // docs:end:clear self._table = [Slot::default(); N]; self._len = 0; } // Returns true if the map contains a value for the specified key. + // docs:start:contains_key pub fn contains_key( self, key: K @@ -101,89 +107,80 @@ impl HashMap { K: Hash + Eq, B: BuildHasher, H: Hasher { + // docs:end:contains_key self.get(key).is_some() } // Returns true if the map contains no elements. + // docs:start:is_empty pub fn is_empty(self) -> bool { + // docs:end:is_empty self._len == 0 } - // Get the Option<(K, V) array of valid entries - // with a length of map capacity. First len() elements - // are safe to unwrap_unchecked(), whilst remaining - // are guaranteed to be Option::none(). - // - // This design is reasoned by compile-time limitations and - // temporary nested slices ban. - pub fn entries(self) -> [Option<(K, V)>; N] { - let mut entries = [Option::none(); N]; - let mut valid_amount = 0; + // Returns a BoundedVec of all valid entries in this HashMap. + // The length of the returned vector will always match the length of this HashMap. + // docs:start:entries + pub fn entries(self) -> BoundedVec<(K, V), N> { + // docs:end:entries + let mut entries = BoundedVec::new(); for slot in self._table { if slot.is_valid() { - entries[valid_amount] = slot.key_value(); - valid_amount += 1; + // SAFETY: slot.is_valid() should ensure there is a valid key-value pairing here + let key_value = slot.key_value().unwrap_unchecked(); + entries.push(key_value); } } - let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}."; - assert(valid_amount == self._len, msg); + let msg = f"Amount of valid elements should have been {self._len} times, but got {entries.len()}."; + assert(entries.len() == self._len, msg); entries } - // Get the Option array of valid keys - // with a length of map capacity. First len() elements - // are safe to unwrap_unchecked(), whilst remaining - // are guaranteed to be Option::none(). - // - // This design is reasoned by compile-time limitations and - // temporary nested slices ban. - pub fn keys(self) -> [Option; N] { - let mut keys = [Option::none(); N]; - let mut valid_amount = 0; + // Returns a BoundedVec containing all the keys within this HashMap. + // The length of the returned vector will always match the length of this HashMap. + // docs:start:keys + pub fn keys(self) -> BoundedVec { + // docs:end:keys + let mut keys = BoundedVec::new(); for slot in self._table { if slot.is_valid() { let (key, _) = slot.key_value_unchecked(); - keys[valid_amount] = Option::some(key); - valid_amount += 1; + keys.push(key); } } - let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}."; - assert(valid_amount == self._len, msg); + let msg = f"Amount of valid elements should have been {self._len} times, but got {keys.len()}."; + assert(keys.len() == self._len, msg); keys } - // Get the Option array of valid values - // with a length of map capacity. First len() elements - // are safe to unwrap_unchecked(), whilst remaining - // are guaranteed to be Option::none(). - // - // This design is reasoned by compile-time limitations and - // temporary nested slices ban. - pub fn values(self) -> [Option; N] { - let mut values = [Option::none(); N]; - let mut valid_amount = 0; + // Returns a BoundedVec containing all the values within this HashMap. + // The length of the returned vector will always match the length of this HashMap. + // docs:start:values + pub fn values(self) -> BoundedVec { + // docs:end:values + let mut values = BoundedVec::new(); for slot in self._table { if slot.is_valid() { let (_, value) = slot.key_value_unchecked(); - values[valid_amount] = Option::some(value); - valid_amount += 1; + values.push(value); } } - let msg = f"Amount of valid elements should have been {self._len} times, but got {valid_amount}."; - assert(valid_amount == self._len, msg); + let msg = f"Amount of valid elements should have been {self._len} times, but got {values.len()}."; + assert(values.len() == self._len, msg); values } // For each key-value entry applies mutator function. + // docs:start:iter_mut pub fn iter_mut( &mut self, f: fn(K, V) -> (K, V) @@ -192,12 +189,13 @@ impl HashMap { K: Eq + Hash, B: BuildHasher, H: Hasher { + // docs:end:iter_mut let mut entries = self.entries(); let mut new_map = HashMap::with_hasher(self._build_hasher); for i in 0..N { if i < self._len { - let entry = entries[i].unwrap_unchecked(); + let entry = entries.get_unchecked(i); let (key, value) = f(entry.0, entry.1); new_map.insert(key, value); } @@ -207,6 +205,7 @@ impl HashMap { } // For each key applies mutator function. + // docs:start:iter_keys_mut pub fn iter_keys_mut( &mut self, f: fn(K) -> K @@ -215,12 +214,13 @@ impl HashMap { K: Eq + Hash, B: BuildHasher, H: Hasher { + // docs:end:iter_keys_mut let mut entries = self.entries(); let mut new_map = HashMap::with_hasher(self._build_hasher); for i in 0..N { if i < self._len { - let entry = entries[i].unwrap_unchecked(); + let entry = entries.get_unchecked(i); let (key, value) = (f(entry.0), entry.1); new_map.insert(key, value); } @@ -230,7 +230,9 @@ impl HashMap { } // For each value applies mutator function. + // docs:start:iter_values_mut pub fn iter_values_mut(&mut self, f: fn(V) -> V) { + // docs:end:iter_values_mut for i in 0..N { let mut slot = self._table[i]; if slot.is_valid() { @@ -242,7 +244,9 @@ impl HashMap { } // Retains only the elements specified by the predicate. + // docs:start:retain pub fn retain(&mut self, f: fn(K, V) -> bool) { + // docs:end:retain for index in 0..N { let mut slot = self._table[index]; if slot.is_valid() { @@ -257,16 +261,21 @@ impl HashMap { } // Amount of active key-value entries. + // docs:start:len pub fn len(self) -> u64 { + // docs:end:len self._len } // Get the compile-time map capacity. + // docs:start:capacity pub fn capacity(_self: Self) -> u64 { + // docs:end:capacity N } // Get the value by key. If it does not exist, returns none(). + // docs:start:get pub fn get( self, key: K @@ -275,6 +284,7 @@ impl HashMap { K: Eq + Hash, B: BuildHasher, H: Hasher { + // docs:end:get let mut result = Option::none(); let hash = self.hash(key); @@ -300,6 +310,7 @@ impl HashMap { } // Insert key-value entry. In case key was already present, value is overridden. + // docs:start:insert pub fn insert( &mut self, key: K, @@ -309,6 +320,7 @@ impl HashMap { K: Eq + Hash, B: BuildHasher, H: Hasher { + // docs:end:insert self.assert_load_factor(); let hash = self.hash(key); @@ -340,7 +352,8 @@ impl HashMap { } } - // Remove key-value entry. If key is not present, HashMap remains unchanged. + // Removes a key-value entry. If key is not present, HashMap remains unchanged. + // docs:start:remove pub fn remove( &mut self, key: K @@ -349,6 +362,7 @@ impl HashMap { K: Eq + Hash, B: BuildHasher, H: Hasher { + // docs:end:remove let hash = self.hash(key); let mut break = false; @@ -409,6 +423,7 @@ impl HashMap { // Equality class on HashMap has to test that they have // equal sets of key-value entries, // thus one is a subset of the other and vice versa. +// docs:start:eq impl Eq for HashMap where K: Eq + Hash, @@ -416,7 +431,8 @@ where B: BuildHasher, H: Hasher { - fn eq(self, other: HashMap) -> bool{ + fn eq(self, other: HashMap) -> bool { +// docs:end:eq let mut equal = false; if self.len() == other.len(){ @@ -443,12 +459,14 @@ where } } +// docs:start:default impl Default for HashMap where B: BuildHasher + Default, H: Hasher + Default { - fn default() -> Self{ + fn default() -> Self { +// docs:end:default let _build_hasher = B::default(); let map: HashMap = HashMap::with_hasher(_build_hasher); map diff --git a/test_programs/execution_success/hashmap/src/main.nr b/test_programs/execution_success/hashmap/src/main.nr index 597a5c0b7de..4d2cbd45993 100644 --- a/test_programs/execution_success/hashmap/src/main.nr +++ b/test_programs/execution_success/hashmap/src/main.nr @@ -37,6 +37,8 @@ fn main(input: [Entry; HASHMAP_LEN]) { test_retain(); test_iterators(); test_mut_iterators(); + + doc_tests(); } // Insert, get, remove. @@ -154,9 +156,9 @@ fn test_iterators() { hashmap.insert(5, 7); hashmap.insert(11, 13); - let keys: [K; 3] = cut(hashmap.keys()).map(|k: Option| k.unwrap_unchecked()).sort_via(K_CMP); - let values: [V; 3] = cut(hashmap.values()).map(|v: Option| v.unwrap_unchecked()).sort_via(V_CMP); - let entries: [(K, V); 3] = cut(hashmap.entries()).map(|e: Option<(K, V)>| e.unwrap_unchecked()).sort_via(KV_CMP); + let keys: [K; 3] = cut(hashmap.keys()).sort_via(K_CMP); + let values: [V; 3] = cut(hashmap.values()).sort_via(V_CMP); + let entries: [(K, V); 3] = cut(hashmap.entries()).sort_via(KV_CMP); assert(keys == [2, 5, 11], "Got incorrect iteration of keys."); assert(values == [3, 7, 13], "Got incorrect iteration of values."); @@ -177,8 +179,8 @@ fn test_mut_iterators() { let f = |v: V| -> V{ v * 5}; hashmap.iter_values_mut(f); - let keys: [K; 3] = cut(hashmap.keys()).map(|k: Option| k.unwrap_unchecked()).sort_via(K_CMP); - let values: [V; 3] = cut(hashmap.values()).map(|v: Option| v.unwrap_unchecked()).sort_via(V_CMP); + let keys: [K; 3] = cut(hashmap.keys()).sort_via(K_CMP); + let values: [V; 3] = cut(hashmap.values()).sort_via(V_CMP); assert(keys == [6, 15, 33], f"Got incorrect iteration of keys: {keys}"); assert(values == [15, 35, 65], "Got incorrect iteration of values."); @@ -186,7 +188,175 @@ fn test_mut_iterators() { let f = |k: K, v: V| -> (K, V){(k * 2, v * 2)}; hashmap.iter_mut(f); - let entries: [(K, V); 3] = cut(hashmap.entries()).map(|e: Option<(K, V)>| e.unwrap_unchecked()).sort_via(KV_CMP); + let entries: [(K, V); 3] = cut(hashmap.entries()).sort_via(KV_CMP); assert(entries == [(12, 30), (30, 70), (66, 130)], "Got incorrect iteration of entries."); } + +// docs:start:type_alias +type MyMap = HashMap>; +// docs:end:type_alias + +/// Tests examples from the stdlib hashmap documentation +fn doc_tests() { + // docs:start:default_example + let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); + // docs:end:default_example + + // docs:start:with_hasher_example + let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); + // docs:end:with_hasher_example + + // docs:start:insert_example + let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); + // docs:end:insert_example + + get_example(map); + + // docs:start:remove_example + map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); + // docs:end:remove_example + + // docs:start:is_empty_example + assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); + // docs:end:is_empty_example + + // docs:start:len_example + // This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); + // docs:end:len_example + + // docs:start:capacity_example + let empty_map: HashMap> = HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); + // docs:end:capacity_example + + // docs:start:clear_example + assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); + // docs:end:clear_example + + // docs:start:contains_key_example + if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } + // docs:end:contains_key_example + + entries_examples(map); + iter_examples(map); + + // docs:start:retain_example + map.retain(|k, v| (k != 0) & (v != 0)); + // docs:end:retain_example + + // docs:start:eq_example + let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); + // docs:end:eq_example +} + +// docs:start:get_example +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +// docs:end:get_example + +fn entries_examples(map: HashMap>) { + // docs:start:entries_example + let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } + // docs:end:entries_example + + // docs:start:keys_example + let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } + // docs:end:keys_example + + // docs:start:values_example + let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } + // docs:end:values_example +} + +fn iter_examples(mut map: HashMap>) { + // docs:start:iter_mut_example + // Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); + // docs:end:iter_mut_example + + // docs:start:iter_keys_mut_example + // Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); + // docs:end:iter_keys_mut_example + + // docs:start:iter_values_mut_example + // Halve each value + map.iter_values_mut(|v| v / 2); + // docs:end:iter_values_mut_example +} diff --git a/test_programs/execution_success/hashmap/src/utils.nr b/test_programs/execution_success/hashmap/src/utils.nr index 45c9ca9bbf7..ee73245a902 100644 --- a/test_programs/execution_success/hashmap/src/utils.nr +++ b/test_programs/execution_success/hashmap/src/utils.nr @@ -1,10 +1,10 @@ -// Compile-time: cuts the M first elements from the [T; N] array. -pub(crate) fn cut(input: [T; N]) -> [T; M] { - assert(M as u64 < N as u64, "M should be less than N."); +// Compile-time: cuts the M first elements from the BoundedVec. +pub(crate) fn cut(input: BoundedVec) -> [T; M] { + assert(M < N, "M should be less than N."); - let mut new = [dep::std::unsafe::zeroed(); M]; + let mut new = BoundedVec::new(); for i in 0..M { - new[i] = input[i]; + new.push(input.get(i)); } - new + new.storage() } From f988d020e43cdf36a38613f2052d4518de39193a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Fri, 8 Mar 2024 20:03:50 +0000 Subject: [PATCH 057/416] fix(ssa): Handle mergers of slices returned from calls (#4496) # Description ## Problem\* Resolves #4418 ## Summary\* We need to add handling for slices returned from `ToRadix` and `ToBits`. ~~This PR does two fixes.~~ EDIT: After moving to the iterative flattening appraoch w are now accurately tracking slice capacities when they are the parameters of blocks. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../ssa/opt/flatten_cfg/capacity_tracker.rs | 32 ++++++++++++++++--- .../execution_success/slices/src/main.nr | 26 +++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs index 7cd0fe3084e..bdfc04f0bbe 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs @@ -5,6 +5,7 @@ use crate::ssa::ir::{ value::{Value, ValueId}, }; +use acvm::FieldElement; use fxhash::FxHashMap as HashMap; pub(crate) struct SliceCapacityTracker<'a> { @@ -62,21 +63,27 @@ impl<'a> SliceCapacityTracker<'a> { | Intrinsic::SlicePushFront | Intrinsic::SlicePopBack | Intrinsic::SliceInsert - | Intrinsic::SliceRemove => (1, 1), + | Intrinsic::SliceRemove => (Some(1), 1), // `pop_front` returns the popped element, and then the respective slice. // This means in the case of a slice with structs, the result index of the popped slice // will change depending on the number of elements in the struct. // For example, a slice with four elements will look as such in SSA: // v3, v4, v5, v6, v7, v8 = call slice_pop_front(v1, v2) // where v7 is the slice length and v8 is the popped slice itself. - Intrinsic::SlicePopFront => (1, results.len() - 1), + Intrinsic::SlicePopFront => (Some(1), results.len() - 1), + // The slice capacity of these intrinsics is not determined by the arguments of the function. + Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => (None, 1), _ => return, }; - let slice_contents = arguments[argument_index]; + let result_slice = results[result_index]; match intrinsic { Intrinsic::SlicePushBack | Intrinsic::SlicePushFront | Intrinsic::SliceInsert => { + let argument_index = argument_index + .expect("ICE: Should have an argument index for slice intrinsics"); + let slice_contents = arguments[argument_index]; + for arg in &arguments[(argument_index + 1)..] { let element_typ = self.dfg.type_of_value(*arg); if element_typ.contains_slice_element() { @@ -85,20 +92,35 @@ impl<'a> SliceCapacityTracker<'a> { } if let Some(contents_capacity) = slice_sizes.get(&slice_contents) { let new_capacity = *contents_capacity + 1; - slice_sizes.insert(results[result_index], new_capacity); + slice_sizes.insert(result_slice, new_capacity); } } Intrinsic::SlicePopBack | Intrinsic::SliceRemove | Intrinsic::SlicePopFront => { + let argument_index = argument_index + .expect("ICE: Should have an argument index for slice intrinsics"); + let slice_contents = arguments[argument_index]; + // We do not decrement the size on intrinsics that could remove values from a slice. // This is because we could potentially go back to the smaller slice and not fill in dummies. // This pass should be tracking the potential max that a slice ***could be*** if let Some(contents_capacity) = slice_sizes.get(&slice_contents) { let new_capacity = *contents_capacity - 1; - slice_sizes.insert(results[result_index], new_capacity); + slice_sizes.insert(result_slice, new_capacity); } } + Intrinsic::ToBits(_) => { + // Compiler sanity check + assert!(matches!(self.dfg.type_of_value(result_slice), Type::Slice(_))); + slice_sizes.insert(result_slice, FieldElement::max_num_bits() as usize); + } + Intrinsic::ToRadix(_) => { + // Compiler sanity check + assert!(matches!(self.dfg.type_of_value(result_slice), Type::Slice(_))); + slice_sizes + .insert(result_slice, FieldElement::max_num_bytes() as usize); + } _ => {} } } diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr index 6823bf05d96..e44edf872a4 100644 --- a/test_programs/execution_success/slices/src/main.nr +++ b/test_programs/execution_success/slices/src/main.nr @@ -51,6 +51,8 @@ fn main(x: Field, y: pub Field) { regression_merge_slices(x, y); regression_2370(); + regression_4418(x); + regression_slice_call_result(x, y); regression_4506(); } // Ensure that slices of struct/tuple values work. @@ -300,6 +302,30 @@ fn regression_2370() { slice = [1, 2, 3]; } +fn regression_4418(x: Field) { + let mut crash = x.to_be_bytes(32); + + if x != 0 { + crash[0] = 10; + } +} + +fn regression_slice_call_result(x: Field, y: Field) { + let mut slice = merge_slices_return(x, y); + if x != 0 { + slice = slice.push_back(5); + slice = slice.push_back(10); + } else { + slice = slice.push_back(5); + } + assert(slice.len() == 5); + assert(slice[0] == 0); + assert(slice[1] == 0); + assert(slice[2] == 10); + assert(slice[3] == 5); + assert(slice[4] == 10); +} + fn regression_4506() { let slice: [Field] = [1, 2, 3]; assert(slice == slice); From 810d1958dcff55deac1867d3484bbc8bbe43d0fe Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 11 Mar 2024 09:45:58 +0000 Subject: [PATCH 058/416] chore(ci): fix JS publishing workflow checking out inconsistent commits (#4493) --- .github/JS_PUBLISH_FAILED.md | 2 +- .github/workflows/publish-es-packages.yml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/JS_PUBLISH_FAILED.md b/.github/JS_PUBLISH_FAILED.md index 5b9f79aac1f..9adba2776c8 100644 --- a/.github/JS_PUBLISH_FAILED.md +++ b/.github/JS_PUBLISH_FAILED.md @@ -1,6 +1,6 @@ --- title: "JS packages failed to publish" -assignees: TomAFrench kevaundray savio-sou +assignees: TomAFrench kevaundray Savio-Sou labels: js --- diff --git a/.github/workflows/publish-es-packages.yml b/.github/workflows/publish-es-packages.yml index f72a97b2684..470db3b78f7 100644 --- a/.github/workflows/publish-es-packages.yml +++ b/.github/workflows/publish-es-packages.yml @@ -20,7 +20,9 @@ jobs: steps: - name: Checkout Noir repo uses: actions/checkout@v4 - + with: + ref: ${{ inputs.noir-ref }} + - name: Setup toolchain uses: dtolnay/rust-toolchain@1.73.0 @@ -87,6 +89,8 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + with: + ref: ${{ inputs.noir-ref }} - name: Setup toolchain uses: dtolnay/rust-toolchain@1.73.0 @@ -164,4 +168,4 @@ jobs: WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} with: update_existing: true - filename: .github/JS_PUBLISH_FAILED.md \ No newline at end of file + filename: .github/JS_PUBLISH_FAILED.md From 8d6667773db13392a97a610cb5b996bffdf3e405 Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 11 Mar 2024 05:30:26 -0500 Subject: [PATCH 059/416] chore: Move `check_method_signatures` to type checking phase (#4516) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/3583 ## Summary\* Resolves an old TODO by moving this function to the type checking phase. I've rewritten the function since it was binding certain type variables then unbound them afterward. I've changed that to substitute new variables and only use `try_unify` to avoid accidentally binding anything. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/def_collector/dc_crate.rs | 176 +++--------------- .../src/hir/def_collector/errors.rs | 20 -- .../src/hir/resolution/resolver.rs | 10 +- .../src/hir/resolution/traits.rs | 17 +- .../src/hir/type_check/errors.rs | 32 +++- .../noirc_frontend/src/hir/type_check/expr.rs | 4 +- .../noirc_frontend/src/hir/type_check/mod.rs | 159 +++++++++++++++- .../noirc_frontend/src/hir_def/function.rs | 10 +- compiler/noirc_frontend/src/tests.rs | 16 +- 9 files changed, 239 insertions(+), 205 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 27b1d376f11..62febc89899 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -5,13 +5,14 @@ use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; use crate::hir::resolution::import::{resolve_import, ImportDirective}; -use crate::hir::resolution::resolver::Resolver; use crate::hir::resolution::{ collect_impls, collect_trait_impls, path_resolver, resolve_free_functions, resolve_globals, resolve_impls, resolve_structs, resolve_trait_by_path, resolve_trait_impls, resolve_traits, resolve_type_aliases, }; -use crate::hir::type_check::{type_check_func, TypeCheckError, TypeChecker}; +use crate::hir::type_check::{ + check_trait_impl_method_matches_declaration, type_check_func, TypeCheckError, TypeChecker, +}; use crate::hir::Context; use crate::macros_api::{MacroError, MacroProcessor}; @@ -20,8 +21,7 @@ use crate::node_interner::{FuncId, GlobalId, NodeInterner, StructId, TraitId, Ty use crate::parser::{ParserError, SortedModule}; use crate::{ ExpressionKind, Ident, LetStatement, Literal, NoirFunction, NoirStruct, NoirTrait, - NoirTypeAlias, Path, PathKind, Type, TypeBindings, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, + NoirTypeAlias, Path, PathKind, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, }; use fm::FileId; use iter_extended::vecmap; @@ -368,12 +368,12 @@ impl DefCollector { &mut errors, )); - functions.extend(resolve_trait_impls( + let impl_functions = resolve_trait_impls( context, def_collector.collected_traits_impls, crate_id, &mut errors, - )); + ); for macro_processor in macro_processors { macro_processor.process_typed_ast(&crate_id, context).unwrap_or_else( @@ -387,6 +387,8 @@ impl DefCollector { errors.extend(type_check_globals(&mut context.def_interner, resolved_globals.globals)); errors.extend(type_check_functions(&mut context.def_interner, functions)); + errors.extend(type_check_trait_impl_signatures(&mut context.def_interner, &impl_functions)); + errors.extend(type_check_functions(&mut context.def_interner, impl_functions)); errors } } @@ -468,158 +470,24 @@ fn type_check_functions( .into_iter() .flat_map(|(file, func)| { type_check_func(interner, func) - .iter() - .cloned() + .into_iter() .map(|e| (e.into(), file)) .collect::>() }) .collect() } -// TODO(vitkov): Move this out of here and into type_check -#[allow(clippy::too_many_arguments)] -pub(crate) fn check_methods_signatures( - resolver: &mut Resolver, - impl_methods: &[(FileId, FuncId)], - trait_id: TraitId, - trait_name_span: Span, - // These are the generics on the trait itself from the impl. - // E.g. in `impl Foo for Bar`, this is `vec![A, B]`. - trait_generics: Vec, - trait_impl_generic_count: usize, - file_id: FileId, - errors: &mut Vec<(CompilationError, FileId)>, -) { - let self_type = resolver.get_self_type().expect("trait impl must have a Self type").clone(); - let trait_generics = vecmap(trait_generics, |typ| resolver.resolve_type(typ)); - - // Temporarily bind the trait's Self type to self_type so we can type check - let the_trait = resolver.interner.get_trait_mut(trait_id); - the_trait.self_type_typevar.bind(self_type); - - if trait_generics.len() != the_trait.generics.len() { - let error = DefCollectorErrorKind::MismatchGenericCount { - actual_generic_count: trait_generics.len(), - expected_generic_count: the_trait.generics.len(), - // Preferring to use 'here' over a more precise term like 'this reference' - // to try to make the error easier to understand for newer users. - location: "here it", - origin: the_trait.name.to_string(), - span: trait_name_span, - }; - errors.push((error.into(), file_id)); - } - - // We also need to bind the traits generics to the trait's generics on the impl - for (generic, binding) in the_trait.generics.iter().zip(trait_generics) { - generic.bind(binding); - } - - // Temporarily take the trait's methods so we can use both them and a mutable reference - // to the interner within the loop. - let trait_methods = std::mem::take(&mut the_trait.methods); - - for (file_id, func_id) in impl_methods { - let func_name = resolver.interner.function_name(func_id).to_owned(); - - // This is None in the case where the impl block has a method that's not part of the trait. - // If that's the case, a `MethodNotInTrait` error has already been thrown, and we can ignore - // the impl method, since there's nothing in the trait to match its signature against. - if let Some(trait_method) = - trait_methods.iter().find(|method| method.name.0.contents == func_name) - { - let impl_method = resolver.interner.function_meta(func_id); - - let impl_method_generic_count = - impl_method.typ.generic_count() - trait_impl_generic_count; - - // We subtract 1 here to account for the implicit generic `Self` type that is on all - // traits (and thus trait methods) but is not required (or allowed) for users to specify. - let the_trait = resolver.interner.get_trait(trait_id); - let trait_method_generic_count = - trait_method.generics().len() - 1 - the_trait.generics.len(); - - if impl_method_generic_count != trait_method_generic_count { - let trait_name = resolver.interner.get_trait(trait_id).name.clone(); - - let error = DefCollectorErrorKind::MismatchGenericCount { - actual_generic_count: impl_method_generic_count, - expected_generic_count: trait_method_generic_count, - origin: format!("{}::{}", trait_name, func_name), - location: "this method", - span: impl_method.location.span, - }; - errors.push((error.into(), *file_id)); - } - - // This instantiation is technically not needed. We could bind each generic in the - // trait function to the impl's corresponding generic but to do so we'd have to rely - // on the trait function's generics being first in the generic list, since the same - // list also contains the generic `Self` variable, and any generics on the trait itself. - // - // Instantiating the impl method's generics here instead is a bit less precise but - // doesn't rely on any orderings that may be changed. - let impl_function_type = impl_method.typ.instantiate(resolver.interner).0; - - let mut bindings = TypeBindings::new(); - let mut typecheck_errors = Vec::new(); - - if let Type::Function(impl_params, impl_return, _) = impl_function_type.as_monotype() { - if trait_method.arguments().len() != impl_params.len() { - let error = DefCollectorErrorKind::MismatchTraitImplementationNumParameters { - actual_num_parameters: impl_method.parameters.0.len(), - expected_num_parameters: trait_method.arguments().len(), - trait_name: resolver.interner.get_trait(trait_id).name.to_string(), - method_name: func_name.to_string(), - span: impl_method.location.span, - }; - errors.push((error.into(), *file_id)); - } - - // Check the parameters of the impl method against the parameters of the trait method - let args = trait_method.arguments().iter(); - let args_and_params = args.zip(impl_params).zip(&impl_method.parameters.0); - - for (parameter_index, ((expected, actual), (hir_pattern, _, _))) in - args_and_params.enumerate() - { - if expected.try_unify(actual, &mut bindings).is_err() { - typecheck_errors.push(TypeCheckError::TraitMethodParameterTypeMismatch { - method_name: func_name.to_string(), - expected_typ: expected.to_string(), - actual_typ: actual.to_string(), - parameter_span: hir_pattern.span(), - parameter_index: parameter_index + 1, - }); - } - } - - if trait_method.return_type().try_unify(impl_return, &mut bindings).is_err() { - let impl_method = resolver.interner.function_meta(func_id); - let ret_type_span = impl_method.return_type.get_type().span; - let expr_span = ret_type_span.expect("return type must always have a span"); - - let expected_typ = trait_method.return_type().to_string(); - let expr_typ = impl_method.return_type().to_string(); - let error = TypeCheckError::TypeMismatch { expr_typ, expected_typ, expr_span }; - typecheck_errors.push(error); - } - } else { - unreachable!( - "impl_function_type is not a function type, it is: {impl_function_type}" - ); - } - - errors.extend(typecheck_errors.iter().cloned().map(|e| (e.into(), *file_id))); - } - } - - // Now unbind `Self` and the trait's generics - let the_trait = resolver.interner.get_trait_mut(trait_id); - the_trait.set_methods(trait_methods); - the_trait.self_type_typevar.unbind(the_trait.self_type_typevar_id); - - for generic in &the_trait.generics { - generic.unbind(generic.id()); - } +fn type_check_trait_impl_signatures( + interner: &mut NodeInterner, + file_func_ids: &[(FileId, FuncId)], +) -> Vec<(CompilationError, fm::FileId)> { + file_func_ids + .iter() + .flat_map(|(file, func)| { + check_trait_impl_method_matches_declaration(interner, *func) + .into_iter() + .map(|e| (e.into(), *file)) + .collect::>() + }) + .collect() } diff --git a/compiler/noirc_frontend/src/hir/def_collector/errors.rs b/compiler/noirc_frontend/src/hir/def_collector/errors.rs index de45be48c4e..29daf5d6369 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/errors.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/errors.rs @@ -41,14 +41,6 @@ pub enum DefCollectorErrorKind { OverlappingImplNote { span: Span }, #[error("Cannot `impl` a type defined outside the current crate")] ForeignImpl { span: Span, type_name: String }, - #[error("Mismatched number of parameters in trait implementation")] - MismatchTraitImplementationNumParameters { - actual_num_parameters: usize, - expected_num_parameters: usize, - trait_name: String, - method_name: String, - span: Span, - }, #[error("Mismatched number of generics in {location}")] MismatchGenericCount { actual_generic_count: usize, @@ -176,18 +168,6 @@ impl From for Diagnostic { "".to_string(), trait_path.span(), ), - DefCollectorErrorKind::MismatchTraitImplementationNumParameters { - expected_num_parameters, - actual_num_parameters, - trait_name, - method_name, - span, - } => { - let plural = if expected_num_parameters == 1 { "" } else { "s" }; - let primary_message = format!( - "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}"); - Diagnostic::simple_error(primary_message, "".to_string(), span) - } DefCollectorErrorKind::MismatchGenericCount { actual_generic_count, expected_generic_count, diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 322891f0ae9..875d0ceb85e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -217,6 +217,7 @@ impl<'a> Resolver<'a> { pub fn resolve_trait_function( &mut self, name: &Ident, + generics: &UnresolvedGenerics, parameters: &[(Ident, UnresolvedType)], return_type: &FunctionReturnType, where_clause: &[UnresolvedTraitConstraint], @@ -237,7 +238,7 @@ impl<'a> Resolver<'a> { is_internal: false, is_unconstrained: false, visibility: FunctionVisibility::Public, // Trait functions are always public - generics: Vec::new(), // self.generics should already be set + generics: generics.clone(), parameters: vecmap(parameters, |(name, typ)| Param { visibility: Visibility::Private, pattern: Pattern::Identifier(name.clone()), @@ -975,11 +976,18 @@ impl<'a> Resolver<'a> { self.handle_function_type(&func_id); self.handle_is_function_internal(&func_id); + let direct_generics = func.def.generics.iter(); + let direct_generics = direct_generics + .filter_map(|generic| self.find_generic(&generic.0.contents)) + .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) + .collect(); + FuncMeta { name: name_ident, kind: func.kind, location, typ, + direct_generics, trait_impl: self.current_trait_impl, parameters: parameters.into(), return_type: func.def.return_type.clone(), diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 8f966be312b..5d546954f0d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -8,9 +8,7 @@ use crate::{ graph::CrateId, hir::{ def_collector::{ - dc_crate::{ - check_methods_signatures, CompilationError, UnresolvedTrait, UnresolvedTraitImpl, - }, + dc_crate::{CompilationError, UnresolvedTrait, UnresolvedTraitImpl}, errors::{DefCollectorErrorKind, DuplicateType}, }, def_map::{CrateDefMap, ModuleDefId, ModuleId}, @@ -131,6 +129,7 @@ fn resolve_trait_methods( let func_id = unresolved_trait.method_ids[&name.0.contents]; let (_, func_meta) = resolver.resolve_trait_function( name, + generics, parameters, return_type, where_clause, @@ -365,6 +364,7 @@ pub(crate) fn resolve_trait_by_path( Err(_) => Err(DefCollectorErrorKind::TraitNotFound { trait_path: path }), } } + pub(crate) fn resolve_trait_impls( context: &mut Context, traits: Vec, @@ -424,17 +424,6 @@ pub(crate) fn resolve_trait_impls( new_resolver.set_self_type(Some(self_type.clone())); if let Some(trait_id) = maybe_trait_id { - check_methods_signatures( - &mut new_resolver, - &impl_methods, - trait_id, - trait_impl.trait_path.span(), - trait_impl.trait_generics, - trait_impl.generics.len(), - trait_impl.file_id, - errors, - ); - let where_clause = trait_impl .where_clause .into_iter() diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index cba2400441f..7eacc8eb2d1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -44,7 +44,7 @@ pub enum TypeCheckError { #[error("Expected type {expected} is not the same as {actual}")] TypeMismatchWithSource { expected: Type, actual: Type, span: Span, source: Source }, #[error("Expected {expected:?} found {found:?}")] - ArityMisMatch { expected: u16, found: u16, span: Span }, + ArityMisMatch { expected: usize, found: usize, span: Span }, #[error("Return type in a function cannot be public")] PublicReturnType { typ: Type, span: Span }, #[error("Cannot cast type {from}, 'as' is only for primitive field or integer types")] @@ -53,8 +53,10 @@ pub enum TypeCheckError { ExpectedFunction { found: Type, span: Span }, #[error("Type {lhs_type} has no member named {field_name}")] AccessUnknownMember { lhs_type: Type, field_name: String, span: Span }, - #[error("Function expects {expected} parameters but {found} given")] + #[error("Function expects {expected} parameters but {found} were given")] ParameterCountMismatch { expected: usize, found: usize, span: Span }, + #[error("{item} expects {expected} generics but {found} were given")] + GenericCountMismatch { item: String, expected: usize, found: usize, span: Span }, #[error("Only integer and Field types may be casted to")] UnsupportedCast { span: Span }, #[error("Index {index} is out of bounds for this tuple {lhs_type} of length {length}")] @@ -124,6 +126,14 @@ pub enum TypeCheckError { UnconstrainedSliceReturnToConstrained { span: Span }, #[error("Only sized types may be used in the entry point to a program")] InvalidTypeForEntryPoint { span: Span }, + #[error("Mismatched number of parameters in trait implementation")] + MismatchTraitImplNumParameters { + actual_num_parameters: usize, + expected_num_parameters: usize, + trait_name: String, + method_name: String, + span: Span, + }, } impl TypeCheckError { @@ -193,6 +203,12 @@ impl From for Diagnostic { let msg = format!("Function expects {expected} parameter{empty_or_s} but {found} {was_or_were} given"); Diagnostic::simple_error(msg, String::new(), span) } + TypeCheckError::GenericCountMismatch { item, expected, found, span } => { + let empty_or_s = if expected == 1 { "" } else { "s" }; + let was_or_were = if found == 1 { "was" } else { "were" }; + let msg = format!("{item} expects {expected} generic{empty_or_s} but {found} {was_or_were} given"); + Diagnostic::simple_error(msg, String::new(), span) + } TypeCheckError::InvalidCast { span, .. } | TypeCheckError::ExpectedFunction { span, .. } | TypeCheckError::AccessUnknownMember { span, .. } @@ -289,6 +305,18 @@ impl From for Diagnostic { TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), + TypeCheckError::MismatchTraitImplNumParameters { + expected_num_parameters, + actual_num_parameters, + trait_name, + method_name, + span, + } => { + let plural = if expected_num_parameters == 1 { "" } else { "s" }; + let primary_message = format!( + "`{trait_name}::{method_name}` expects {expected_num_parameters} parameter{plural}, but this method has {actual_num_parameters}"); + Diagnostic::simple_error(primary_message, "".to_string(), span) + } } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index c5287d35caf..7219f4d09c6 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -1040,9 +1040,9 @@ impl<'interner> TypeChecker<'interner> { } ret } + // ignoring env for subtype on purpose Type::Function(parameters, ret, _env) => { - // ignoring env for subtype on purpose - self.bind_function_type_impl(parameters.as_ref(), ret.as_ref(), args.as_ref(), span) + self.bind_function_type_impl(¶meters, &ret, &args, span) } Type::Error => Type::Error, found => { diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index aab793ec867..ab759f454e5 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -12,11 +12,17 @@ mod expr; mod stmt; pub use errors::TypeCheckError; +use noirc_errors::Span; use crate::{ - hir_def::{expr::HirExpression, function::Param, stmt::HirStatement, traits::TraitConstraint}, + hir_def::{ + expr::HirExpression, + function::{Param, Parameters}, + stmt::HirStatement, + traits::TraitConstraint, + }, node_interner::{ExprId, FuncId, GlobalId, NodeInterner}, - Type, + Type, TypeBindings, }; use self::errors::Source; @@ -179,6 +185,154 @@ fn function_info(interner: &NodeInterner, function_body_id: &ExprId) -> (noirc_e (expr_span, empty_function) } +/// Checks that the type of a function in a trait impl matches the type +/// of the corresponding function declaration in the trait itself. +/// +/// To do this, given a trait such as: +/// `trait Foo { fn foo(...); }` +/// +/// And an impl such as: +/// `impl Foo for Bar { fn foo(...); } ` +/// +/// We have to substitute: +/// - Self for Bar +/// - A for D +/// - B for F +/// +/// Before we can type check. Finally, we must also check that the unification +/// result does not introduce any new bindings. This can happen if the impl +/// function's type is more general than that of the trait function. E.g. +/// `fn baz(a: A, b: B)` when the impl required `fn baz(a: A, b: A)`. +/// +/// This does not type check the body of the impl function. +pub(crate) fn check_trait_impl_method_matches_declaration( + interner: &mut NodeInterner, + function: FuncId, +) -> Vec { + let meta = interner.function_meta(&function); + let method_name = interner.function_name(&function); + let mut errors = Vec::new(); + + let definition_type = meta.typ.as_monotype(); + + let impl_ = + meta.trait_impl.expect("Trait impl function should have a corresponding trait impl"); + let impl_ = interner.get_trait_implementation(impl_); + let impl_ = impl_.borrow(); + let trait_info = interner.get_trait(impl_.trait_id); + + let mut bindings = TypeBindings::new(); + bindings.insert( + trait_info.self_type_typevar_id, + (trait_info.self_type_typevar.clone(), impl_.typ.clone()), + ); + + if trait_info.generics.len() != impl_.trait_generics.len() { + let expected = trait_info.generics.len(); + let found = impl_.trait_generics.len(); + let span = impl_.ident.span(); + let item = trait_info.name.to_string(); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + } + + // Substitute each generic on the trait with the corresponding generic on the impl + for (generic, arg) in trait_info.generics.iter().zip(&impl_.trait_generics) { + bindings.insert(generic.id(), (generic.clone(), arg.clone())); + } + + // If this is None, the trait does not have the corresponding function. + // This error should have been caught in name resolution already so we don't + // issue an error for it here. + if let Some(trait_fn_id) = trait_info.method_ids.get(method_name) { + let trait_fn_meta = interner.function_meta(trait_fn_id); + + if trait_fn_meta.direct_generics.len() != meta.direct_generics.len() { + let expected = trait_fn_meta.direct_generics.len(); + let found = meta.direct_generics.len(); + let span = meta.name.location.span; + let item = method_name.to_string(); + errors.push(TypeCheckError::GenericCountMismatch { item, expected, found, span }); + } + + // Substitute each generic on the trait function with the corresponding generic on the impl function + for ((_, trait_fn_generic), (name, impl_fn_generic)) in + trait_fn_meta.direct_generics.iter().zip(&meta.direct_generics) + { + let arg = Type::NamedGeneric(impl_fn_generic.clone(), name.clone()); + bindings.insert(trait_fn_generic.id(), (trait_fn_generic.clone(), arg)); + } + + let (declaration_type, _) = trait_fn_meta.typ.instantiate_with_bindings(bindings, interner); + + check_function_type_matches_expected_type( + &declaration_type, + definition_type, + method_name, + &meta.parameters, + meta.name.location.span, + &trait_info.name.0.contents, + &mut errors, + ); + } + + errors +} + +fn check_function_type_matches_expected_type( + expected: &Type, + actual: &Type, + method_name: &str, + actual_parameters: &Parameters, + span: Span, + trait_name: &str, + errors: &mut Vec, +) { + let mut bindings = TypeBindings::new(); + // Shouldn't need to unify envs, they should always be equal since they're both free functions + if let (Type::Function(params_a, ret_a, _env_a), Type::Function(params_b, ret_b, _env_b)) = + (expected, actual) + { + if params_a.len() == params_b.len() { + for (i, (a, b)) in params_a.iter().zip(params_b.iter()).enumerate() { + if a.try_unify(b, &mut bindings).is_err() { + errors.push(TypeCheckError::TraitMethodParameterTypeMismatch { + method_name: method_name.to_string(), + expected_typ: a.to_string(), + actual_typ: b.to_string(), + parameter_span: actual_parameters.0[i].0.span(), + parameter_index: i + 1, + }); + } + } + + if ret_b.try_unify(ret_a, &mut bindings).is_err() { + errors.push(TypeCheckError::TypeMismatch { + expected_typ: ret_a.to_string(), + expr_typ: ret_b.to_string(), + expr_span: span, + }); + } + } else { + errors.push(TypeCheckError::MismatchTraitImplNumParameters { + actual_num_parameters: params_b.len(), + expected_num_parameters: params_a.len(), + trait_name: trait_name.to_string(), + method_name: method_name.to_string(), + span, + }); + } + } + + // If result bindings is not empty, a type variable was bound which means the two + // signatures were not a perfect match. Note that this relies on us already binding + // all the expected generics to each other prior to this check. + if !bindings.is_empty() { + let expected_typ = expected.to_string(); + let expr_typ = actual.to_string(); + errors.push(TypeCheckError::TypeMismatch { expected_typ, expr_typ, expr_span: span }); + } +} + impl<'interner> TypeChecker<'interner> { fn new(interner: &'interner mut NodeInterner) -> Self { Self { interner, errors: Vec::new(), trait_constraints: Vec::new(), current_function: None } @@ -346,6 +500,7 @@ mod test { trait_impl: None, return_type: FunctionReturnType::Default(Span::default()), trait_constraints: Vec::new(), + direct_generics: Vec::new(), is_entry_point: true, }; interner.push_fn_meta(func_meta, func_id); diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 82bbe1aa5b6..56543e8185c 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -1,12 +1,14 @@ use iter_extended::vecmap; use noirc_errors::{Location, Span}; +use std::rc::Rc; + use super::expr::{HirBlockExpression, HirExpression, HirIdent}; use super::stmt::HirPattern; use super::traits::TraitConstraint; use crate::node_interner::{ExprId, NodeInterner, TraitImplId}; use crate::FunctionKind; -use crate::{Distinctness, FunctionReturnType, Type, Visibility}; +use crate::{Distinctness, FunctionReturnType, Type, TypeVariable, Visibility}; /// A Hir function is a block expression /// with a list of statements @@ -103,6 +105,12 @@ pub struct FuncMeta { /// or a Type::Forall for generic functions. pub typ: Type, + /// The set of generics that are declared directly on this function in the source code. + /// This does not include generics from an outer scope, like those introduced by + /// an `impl` block. This also does not include implicit generics added by the compiler + /// such as a trait's `Self` type variable. + pub direct_generics: Vec<(Rc, TypeVariable)>, + pub location: Location, // This flag is needed for the attribute check pass diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 9be6252b10a..3f78bd43ba9 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -535,15 +535,13 @@ mod test { assert!(errors.len() == 1, "Expected 1 error, got: {:?}", errors); for (err, _file_id) in errors { match &err { - CompilationError::DefinitionError( - DefCollectorErrorKind::MismatchTraitImplementationNumParameters { - actual_num_parameters, - expected_num_parameters, - trait_name, - method_name, - .. - }, - ) => { + CompilationError::TypeError(TypeCheckError::MismatchTraitImplNumParameters { + actual_num_parameters, + expected_num_parameters, + trait_name, + method_name, + .. + }) => { assert_eq!(actual_num_parameters, &1_usize); assert_eq!(expected_num_parameters, &2_usize); assert_eq!(method_name, "default"); From 2a53545f4238c9b8535e6bc5b0720fa15f44f946 Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 11 Mar 2024 09:11:09 -0500 Subject: [PATCH 060/416] fix: Fix brillig slowdown when assigning arrays in loops (#4472) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/3795 ## Summary\* This is an older version of https://github.com/noir-lang/noir/pull/4210 which undoes the change in https://github.com/noir-lang/noir/pull/4210/commits/d331ee27e63df73e7e294fba7031a4c0ab073294 due to a regression https://github.com/noir-lang/noir/issues/4332. This PR is not yet confirmed to work since I do not have a test case for it! @sirasistant, do you mind seeing if this fixes the regression issue? ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/function_builder/mod.rs | 19 +++++++++++++-- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 5 ++++ .../brillig_cow_assign/Nargo.toml | 7 ++++++ .../brillig_cow_assign/Prover.toml | 2 ++ .../brillig_cow_assign/src/main.nr | 23 +++++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 test_programs/execution_success/brillig_cow_assign/Nargo.toml create mode 100644 test_programs/execution_success/brillig_cow_assign/Prover.toml create mode 100644 test_programs/execution_success/brillig_cow_assign/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 9d27554dcaa..bf34a47485b 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -393,10 +393,25 @@ impl FunctionBuilder { self.increment_array_reference_count(value); } } - Type::Array(..) | Type::Slice(..) => { - self.insert_instruction(Instruction::IncrementRc { value }, None); + typ @ Type::Array(..) | typ @ Type::Slice(..) => { // If there are nested arrays or slices, we wait until ArrayGet // is issued to increment the count of that array. + self.insert_instruction(Instruction::IncrementRc { value }, None); + + // This is a bit odd, but in brillig the inc_rc instruction operates on + // a copy of the array's metadata, so we need to re-store a loaded array + // even if there have been no other changes to it. + if let Value::Instruction { instruction, .. } = &self.current_function.dfg[value] { + let instruction = &self.current_function.dfg[*instruction]; + if let Instruction::Load { address } = instruction { + // We can't re-use `value` in case the original address was stored + // to again in the meantime. So introduce another load. + let address = *address; + let value = self.insert_load(address, typ); + self.insert_instruction(Instruction::IncrementRc { value }, None); + self.insert_store(address, value); + } + } } } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index d95295ae3c9..f3fa5d1d2f8 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -722,6 +722,11 @@ impl<'a> FunctionContext<'a> { let lhs = self.extract_current_value(&assign.lvalue)?; let rhs = self.codegen_expression(&assign.expression)?; + rhs.clone().for_each(|value| { + let value = value.eval(self); + self.builder.increment_array_reference_count(value); + }); + self.assign_new_value(lhs, rhs); Ok(Self::unit_value()) } diff --git a/test_programs/execution_success/brillig_cow_assign/Nargo.toml b/test_programs/execution_success/brillig_cow_assign/Nargo.toml new file mode 100644 index 00000000000..a878566a372 --- /dev/null +++ b/test_programs/execution_success/brillig_cow_assign/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_cow_assign" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] diff --git a/test_programs/execution_success/brillig_cow_assign/Prover.toml b/test_programs/execution_success/brillig_cow_assign/Prover.toml new file mode 100644 index 00000000000..882c73b83f8 --- /dev/null +++ b/test_programs/execution_success/brillig_cow_assign/Prover.toml @@ -0,0 +1,2 @@ +items_to_update = 10 +index = 6 diff --git a/test_programs/execution_success/brillig_cow_assign/src/main.nr b/test_programs/execution_success/brillig_cow_assign/src/main.nr new file mode 100644 index 00000000000..e5c3e2bd2f5 --- /dev/null +++ b/test_programs/execution_success/brillig_cow_assign/src/main.nr @@ -0,0 +1,23 @@ +global N = 10; + +unconstrained fn main() { + let mut arr = [0; N]; + let mut mid_change = arr; + + for i in 0..N { + if i == N / 2 { + mid_change = arr; + } + arr[i] = 27; + } + + // Expect: + // arr = [27, 27, 27, 27, 27, 27, 27, 27, 27, 27] + // mid_change = [27, 27, 27, 27, 27, 0, 0, 0, 0, 0] + + let modified_i = N / 2 + 1; + assert_eq(arr[modified_i], 27); + + // Fail here! + assert(mid_change[modified_i] != 27); +} From b94adb92657e2b4a51dc7216a88e080aed1cf8b0 Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 11 Mar 2024 11:55:32 -0500 Subject: [PATCH 061/416] fix: Add `follow_bindings` to follow `Type::Alias` links (#4521) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4518 ## Summary\* ## Additional Context A match case for `Type::Alias` was forgotten when that was initially added. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_frontend/src/monomorphization/mod.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index cfd9a61d13f..9d11ecd54bc 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1691,23 +1691,15 @@ impl<'interner> Monomorphizer<'interner> { } fn unwrap_tuple_type(typ: &HirType) -> Vec { - match typ { + match typ.follow_bindings() { HirType::Tuple(fields) => fields.clone(), - HirType::TypeVariable(binding, TypeVariableKind::Normal) => match &*binding.borrow() { - TypeBinding::Bound(binding) => unwrap_tuple_type(binding), - TypeBinding::Unbound(_) => unreachable!(), - }, other => unreachable!("unwrap_tuple_type: expected tuple, found {:?}", other), } } fn unwrap_struct_type(typ: &HirType) -> Vec<(String, HirType)> { - match typ { - HirType::Struct(def, args) => def.borrow().get_fields(args), - HirType::TypeVariable(binding, TypeVariableKind::Normal) => match &*binding.borrow() { - TypeBinding::Bound(binding) => unwrap_struct_type(binding), - TypeBinding::Unbound(_) => unreachable!(), - }, + match typ.follow_bindings() { + HirType::Struct(def, args) => def.borrow().get_fields(&args), other => unreachable!("unwrap_struct_type: expected struct, found {:?}", other), } } From c6e6efd5fa314c61055e6cb15565ccbec4c78bbf Mon Sep 17 00:00:00 2001 From: guipublic <47281315+guipublic@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:02:54 +0100 Subject: [PATCH 062/416] chore: document big integers (#4487) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Resolves #4205 ## Summary\* Add a section on standard library documentation ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [X] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: José Pedro Sousa Co-authored-by: Savio <72797635+Savio-Sou@users.noreply.github.com> --- cspell.json | 2 + docs/.markdownlint.json | 3 + docs/docs/noir/standard_library/bigint.md | 102 ++++++++++++++++++ noir_stdlib/src/bigint.nr | 2 + .../execution_success/bigint/src/main.nr | 12 +++ 5 files changed, 121 insertions(+) create mode 100644 docs/.markdownlint.json create mode 100644 docs/docs/noir/standard_library/bigint.md diff --git a/cspell.json b/cspell.json index a96e3de901a..d961b600f40 100644 --- a/cspell.json +++ b/cspell.json @@ -154,6 +154,8 @@ "sdiv", "secp256k1", "secp256r1", + "Secpk", + "Secpr", "signedness", "signorecello", "smol", diff --git a/docs/.markdownlint.json b/docs/.markdownlint.json new file mode 100644 index 00000000000..40896b4542f --- /dev/null +++ b/docs/.markdownlint.json @@ -0,0 +1,3 @@ +{ + "no-missing-space-atx": false +} diff --git a/docs/docs/noir/standard_library/bigint.md b/docs/docs/noir/standard_library/bigint.md new file mode 100644 index 00000000000..7d7931840cf --- /dev/null +++ b/docs/docs/noir/standard_library/bigint.md @@ -0,0 +1,102 @@ +--- +title: Big Integers +description: How to use big integers from Noir standard library +keywords: + [ + Big Integer, + Noir programming language, + Noir libraries, + ] +--- + +The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. + +:::note + +The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. + +::: + +Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: + +- BN254 Fq: Bn254Fq +- BN254 Fr: Bn254Fr +- Secp256k1 Fq: Secpk1Fq +- Secp256k1 Fr: Secpk1Fr +- Secp256r1 Fr: Secpr1Fr +- Secp256r1 Fq: Secpr1Fq + +Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. +For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. + +Feel free to explore the source code for the other primes: + +#include_code curve_order_base noir_stdlib/src/bigint.nr rust + +## Example usage + +A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: + +#include_code big_int_example test_programs/execution_success/bigint/src/main.nr rust + +## Methods + +The available operations for each big integer are: + +### from_le_bytes + +Construct a big integer from its little-endian bytes representation. Example: + +```rust + let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]); + ``` + +Sure, here's the formatted version of the remaining methods: + +### to_le_bytes + +Return the little-endian bytes representation of a big integer. Example: + +```rust +let bytes = a.to_le_bytes(); +``` + +### add + +Add two big integers. Example: + +```rust +let sum = a + b; +``` + +### sub + +Subtract two big integers. Example: + +```rust +let difference = a - b; +``` + +### mul + +Multiply two big integers. Example: + +```rust +let product = a * b; +``` + +### div + +Divide two big integers. Note that division is field division and not euclidean division. Example: + +```rust +let quotient = a / b; +``` + +### eq + +Compare two big integers. Example: + +```rust +let are_equal = a == b; +``` diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index 98237a54779..e7fc28ba39b 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -1,6 +1,7 @@ use crate::ops::{Add, Sub, Mul, Div}; use crate::cmp::Eq; +// docs:start:curve_order_base global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; global bn254_fr = [0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28, @@ -13,6 +14,7 @@ global secpr1_fq = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; global secpr1_fr = [0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; +// docs:end:curve_order_base struct BigInt { pointer: u32, diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr index b93fec370e5..b6c23276c15 100644 --- a/test_programs/execution_success/bigint/src/main.nr +++ b/test_programs/execution_success/bigint/src/main.nr @@ -1,4 +1,5 @@ use dep::std::bigint; +use dep::std::{bigint::Secpk1Fq, println}; fn main(mut x: [u8; 5], y: [u8; 5]) { let a = bigint::Secpk1Fq::from_le_bytes([x[0], x[1], x[2], x[3], x[4]]); @@ -13,4 +14,15 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { let d = a * b - b; let d1 = bigint::Secpk1Fq::from_le_bytes(597243850900842442924.to_le_bytes(10)); assert(d1 == d); + // big_int_example(x[0], x[1]); } + +// docs:start:big_int_example +fn big_int_example(x: u8, y: u8) { + let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]); + let b = Secpk1Fq::from_le_bytes([y, x, 9]); + let c = (a + b) * b / a; + let d = c.to_le_bytes(); + println(d[0]); +} +// docs:end:big_int_example From 3f676051a6073d6eabdc7fee68e4b522334344f6 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 11 Mar 2024 20:27:40 +0000 Subject: [PATCH 063/416] chore: Release Noir(0.25.0) (#4352) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* ---
0.25.0 ## [0.25.0](https://github.com/noir-lang/noir/compare/v0.24.0...v0.25.0) (2024-03-11) ### ⚠ BREAKING CHANGES * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) * reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432)) * Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431)) * Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376)) * bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) * **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385)) * Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235)) * move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) * note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ### Features * Add eddsa_poseidon_to_pub function to stdlib with test + docs ([#4473](https://github.com/noir-lang/noir/issues/4473)) ([00d2c32](https://github.com/noir-lang/noir/commit/00d2c32e58176cc5de3574c8435a54d415c4a5fa)) * Add HashMap to the stdlib ([#4242](https://github.com/noir-lang/noir/issues/4242)) ([650ffc5](https://github.com/noir-lang/noir/commit/650ffc5053cdca4b6ad2e027fa1f4fd90ef64871)) * Add option to set max memory for bb.js ([#4227](https://github.com/noir-lang/noir/issues/4227)) ([8a6b131](https://github.com/noir-lang/noir/commit/8a6b131402892a570bc2de6f5869de73b0bd979e)) * Add overflow and underflow checks for unsigned integers in brillig ([#4445](https://github.com/noir-lang/noir/issues/4445)) ([21fc4b8](https://github.com/noir-lang/noir/commit/21fc4b85763dccae6dce0a46a318718c3c913471)) * Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) * Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Allow type aliases to reference other aliases ([#4353](https://github.com/noir-lang/noir/issues/4353)) ([c44ef14](https://github.com/noir-lang/noir/commit/c44ef14847a436733206b6dd9590a7ab214ecd97)) * Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) * **ci:** Use wasm-opt when compiling wasm packages ([#4334](https://github.com/noir-lang/noir/issues/4334)) ([e382921](https://github.com/noir-lang/noir/commit/e3829213d8411f84e117a14b43816967925095e0)) * DAP Preflight and debugger compilation options ([#4185](https://github.com/noir-lang/noir/issues/4185)) ([e0ad0b2](https://github.com/noir-lang/noir/commit/e0ad0b2b31f6d46be75d23aec6a82850a9c4bd75)) * Expose separate functions to compile programs vs contracts in `noir_wasm` ([#4413](https://github.com/noir-lang/noir/issues/4413)) ([7cd5fdb](https://github.com/noir-lang/noir/commit/7cd5fdb3d2a53475b7c8681231d517cab30f9f9b)) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235)) ([1048f81](https://github.com/noir-lang/noir/commit/1048f815abb1f27e9c84ab5b9568a3673c12a50a)) * Run tests in parallel in `nargo test` ([#4484](https://github.com/noir-lang/noir/issues/4484)) ([761734e](https://github.com/noir-lang/noir/commit/761734e6cb3ff5911aa85d0cee96ad26092b4905)) * Skip redundant range checks in brillig ([#4460](https://github.com/noir-lang/noir/issues/4460)) ([cb4c1c5](https://github.com/noir-lang/noir/commit/cb4c1c5264b95d01f69d99f916ced71ad9cdc9d1)) * Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) * Track stack frames and their variables in the debugger ([#4188](https://github.com/noir-lang/noir/issues/4188)) ([ae1a9d9](https://github.com/noir-lang/noir/commit/ae1a9d923998177516919bbba6ff4b0584fa1e9f)) * TypeVariableKind for just Integers ([#4118](https://github.com/noir-lang/noir/issues/4118)) ([c956be8](https://github.com/noir-lang/noir/commit/c956be870fb47403a6da6585fce6bea2d40ee268)) * Update error message when trying to load workspace as dependency ([#4393](https://github.com/noir-lang/noir/issues/4393)) ([d2585e7](https://github.com/noir-lang/noir/commit/d2585e738a63208fca3c9e26242e896d7f1df1e4)) ### Bug Fixes * **acir:** Array dynamic flatten ([#4351](https://github.com/noir-lang/noir/issues/4351)) ([b2aaeab](https://github.com/noir-lang/noir/commit/b2aaeab319a0c66c431a7db6852f743eccde8e98)) * **acir:** Use types on dynamic arrays ([#4364](https://github.com/noir-lang/noir/issues/4364)) ([ba2c541](https://github.com/noir-lang/noir/commit/ba2c541ec45de92bba98de34771b73cbb7865c93)) * Add `follow_bindings` to follow `Type::Alias` links ([#4521](https://github.com/noir-lang/noir/issues/4521)) ([b94adb9](https://github.com/noir-lang/noir/commit/b94adb92657e2b4a51dc7216a88e080aed1cf8b0)) * Add handling to `noir_wasm` for projects without dependencies ([#4344](https://github.com/noir-lang/noir/issues/4344)) ([4982251](https://github.com/noir-lang/noir/commit/49822511710a7f1c42b8ed343e80456f8e6db2d9)) * Allow type aliases in main ([#4505](https://github.com/noir-lang/noir/issues/4505)) ([8a5359c](https://github.com/noir-lang/noir/commit/8a5359c012579e54c2766de1074482a36ecada32)) * Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376)) ([601fd9a](https://github.com/noir-lang/noir/commit/601fd9afc502236af1db0c4492698ba2298c7501)) * Brillig range check with consistent bit size ([#4357](https://github.com/noir-lang/noir/issues/4357)) ([ea47d4a](https://github.com/noir-lang/noir/commit/ea47d4a67c6a18e4a7d3a49079d9eb24a1026a25)) * Build noir_codegen when publishing ([#4448](https://github.com/noir-lang/noir/issues/4448)) ([cb1ceee](https://github.com/noir-lang/noir/commit/cb1ceee58b11b0ce6f8845361af3418d13c506bd)) * Consistent bit size for truncate ([#4370](https://github.com/noir-lang/noir/issues/4370)) ([dcd7a1e](https://github.com/noir-lang/noir/commit/dcd7a1e561a68504b9038ffbb3c80f5c981f9f0c)) * Correct formatting for databus visibility types ([#4423](https://github.com/noir-lang/noir/issues/4423)) ([cd796de](https://github.com/noir-lang/noir/commit/cd796dea4937dd1a261f154e5f2e599bbc649165)) * Correct invalid brillig codegen for `EmbeddedCurvePoint.add` ([#4382](https://github.com/noir-lang/noir/issues/4382)) ([5051ec4](https://github.com/noir-lang/noir/commit/5051ec4d434a9e5cf405c68357faaf213e68de9e)) * **docs:** Update install versions ([#4396](https://github.com/noir-lang/noir/issues/4396)) ([b283637](https://github.com/noir-lang/noir/commit/b283637e092038eb296c468168aec2d41e1c2734)) * **docs:** Update noirjs_app for 0.23 ([#4378](https://github.com/noir-lang/noir/issues/4378)) ([f77f702](https://github.com/noir-lang/noir/commit/f77f702e0cfb81dcce4dd97e274b831e887ba5d2)) * Enforce matching types of binary ops in SSA ([#4391](https://github.com/noir-lang/noir/issues/4391)) ([70866ae](https://github.com/noir-lang/noir/commit/70866aea976d59dbcbd4af34067fdd8f46555673)) * Fix brillig slowdown when assigning arrays in loops ([#4472](https://github.com/noir-lang/noir/issues/4472)) ([2a53545](https://github.com/noir-lang/noir/commit/2a53545f4238c9b8535e6bc5b0720fa15f44f946)) * **flake:** Stop flake.nix removing ignored-tests.txt ([#4455](https://github.com/noir-lang/noir/issues/4455)) ([ebaf05a](https://github.com/noir-lang/noir/commit/ebaf05ab10834dd10e04c7ea5130f96c6cdf98ed)) * Force src impl for == on slices ([#4507](https://github.com/noir-lang/noir/issues/4507)) ([1691274](https://github.com/noir-lang/noir/commit/169127444e8b16a8aad4acfe29ba812894fd897c)) * Handling of gh deps in noir_wasm ([#4499](https://github.com/noir-lang/noir/issues/4499)) ([1d65370](https://github.com/noir-lang/noir/commit/1d653704715bf9999eb6a40ed7500e752e2c73b7)) * Iterative flattening pass ([#4492](https://github.com/noir-lang/noir/issues/4492)) ([33c1ef7](https://github.com/noir-lang/noir/commit/33c1ef70e7859fdee7babfb5d38191f53e73a0df)) * Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Only add `.nr` files to file manager ([#4380](https://github.com/noir-lang/noir/issues/4380)) ([8536c7c](https://github.com/noir-lang/noir/commit/8536c7c8ea8fc6b740b2ae6d1aef3bc7e1907b8c)) * Remove panic when generic array length is not resolvable ([#4408](https://github.com/noir-lang/noir/issues/4408)) ([00ab3db](https://github.com/noir-lang/noir/commit/00ab3db86b06111d144516e862902b8604284611)) * Remove print from monomorphization pass ([#4417](https://github.com/noir-lang/noir/issues/4417)) ([27c66b3](https://github.com/noir-lang/noir/commit/27c66b3d0741e68ed591ae8a16b47b30bc87175f)) * **ssa:** Handle mergers of slices returned from calls ([#4496](https://github.com/noir-lang/noir/issues/4496)) ([f988d02](https://github.com/noir-lang/noir/commit/f988d020e43cdf36a38613f2052d4518de39193a)) * Use correct type for numeric generics ([#4386](https://github.com/noir-lang/noir/issues/4386)) ([0a1d109](https://github.com/noir-lang/noir/commit/0a1d109f478c997da5c43876fd12464af638bb15)) * Variables from trait constraints being permanently bound over when used within a trait impl ([#4450](https://github.com/noir-lang/noir/issues/4450)) ([ac60ef5](https://github.com/noir-lang/noir/commit/ac60ef5e12fcfb907fbdcff709d7cbad05f2b939)) ### Miscellaneous Chores * Bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) ([b5e5c30](https://github.com/noir-lang/noir/commit/b5e5c30f4db52c79ef556e80660f39db369b1911)) * **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385)) ([2fc95d2](https://github.com/noir-lang/noir/commit/2fc95d2d82b3220267ce7d5815e7073e00ef1360)) * Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431)) ([b9384fb](https://github.com/noir-lang/noir/commit/b9384fb23abf4ab15e880fb7e03c21509a9fa8a6)) * Reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432)) ([9544813](https://github.com/noir-lang/noir/commit/9544813fabbd18a87dd88456e6a5b781bd0cf008))
0.41.0 ## [0.41.0](https://github.com/noir-lang/noir/compare/v0.40.0...v0.41.0) (2024-03-11) ### ⚠ BREAKING CHANGES * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) * move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) * note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) * rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) * init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) * Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) * Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) * Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) * Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) * Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ### Features * Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) * Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) * Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de)) * Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) * **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Aztec-packages ([#3754](https://github.com/noir-lang/noir/issues/3754)) ([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2)) * Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) * Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c)) * Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) * Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671)) * Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3)) * Speed up transformation of debug messages ([#3815](https://github.com/noir-lang/noir/issues/3815)) ([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48)) * Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c)) * Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc)) * Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138)) * Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) ### Bug Fixes * Deserialize odd length hex literals ([#3747](https://github.com/noir-lang/noir/issues/3747)) ([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2)) * Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b)) * Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925)) ### Miscellaneous Chores * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805)) * Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579)) * Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e)) * Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: Savio <72797635+Savio-Sou@users.noreply.github.com> --- .release-please-manifest.json | 4 +- CHANGELOG.md | 77 +++ Cargo.lock | 50 +- Cargo.toml | 14 +- acvm-repo/CHANGELOG.md | 64 ++ acvm-repo/acir/Cargo.toml | 2 +- acvm-repo/acir_field/Cargo.toml | 2 +- acvm-repo/acvm/Cargo.toml | 2 +- acvm-repo/acvm_js/Cargo.toml | 2 +- acvm-repo/acvm_js/package.json | 2 +- acvm-repo/blackbox_solver/Cargo.toml | 2 +- acvm-repo/brillig/Cargo.toml | 2 +- acvm-repo/brillig_vm/Cargo.toml | 2 +- compiler/wasm/package.json | 2 +- .../explainers/explainer-oracle.md | 57 ++ .../explainers/explainer-recursion.md | 176 ++++++ .../getting_started/_category_.json | 5 + .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 142 +++++ .../hello_noir/project_breakdown.md | 199 ++++++ .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 ++ .../installation/other_install_methods.md | 254 ++++++++ .../getting_started/tooling/_category_.json | 6 + .../getting_started/tooling/index.mdx | 38 ++ .../tooling/language_server.md | 43 ++ .../getting_started/tooling/testing.md | 62 ++ .../version-v0.25.0/how_to/_category_.json | 5 + .../version-v0.25.0/how_to/how-to-oracles.md | 280 +++++++++ .../how_to/how-to-recursion.md | 179 ++++++ .../how_to/how-to-solidity-verifier.md | 231 +++++++ .../version-v0.25.0/how_to/merkle-proof.mdx | 48 ++ .../how_to/using-devcontainers.mdx | 110 ++++ docs/versioned_docs/version-v0.25.0/index.mdx | 67 +++ .../version-v0.25.0/migration_notes.md | 105 ++++ .../noir/concepts/_category_.json | 6 + .../version-v0.25.0/noir/concepts/assert.md | 45 ++ .../version-v0.25.0/noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 45 ++ .../version-v0.25.0/noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 251 ++++++++ .../noir/concepts/data_types/booleans.md | 31 + .../noir/concepts/data_types/fields.md | 192 ++++++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 110 ++++ .../noir/concepts/data_types/integers.md | 155 +++++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 147 +++++ .../noir/concepts/data_types/strings.md | 80 +++ .../noir/concepts/data_types/structs.md | 70 +++ .../noir/concepts/data_types/tuples.md | 48 ++ .../version-v0.25.0/noir/concepts/distinct.md | 64 ++ .../noir/concepts/functions.md | 226 +++++++ .../version-v0.25.0/noir/concepts/generics.md | 106 ++++ .../version-v0.25.0/noir/concepts/globals.md | 72 +++ .../version-v0.25.0/noir/concepts/lambdas.md | 81 +++ .../noir/concepts/mutability.md | 121 ++++ .../version-v0.25.0/noir/concepts/ops.md | 98 +++ .../version-v0.25.0/noir/concepts/oracles.md | 23 + .../noir/concepts/shadowing.md | 44 ++ .../version-v0.25.0/noir/concepts/traits.md | 389 ++++++++++++ .../noir/concepts/unconstrained.md | 95 +++ .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 ++ .../modules_packages_crates/dependencies.md | 124 ++++ .../noir/modules_packages_crates/modules.md | 105 ++++ .../modules_packages_crates/workspaces.md | 40 ++ .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/black_box_fns.md | 31 + .../noir/standard_library/bn254.md | 46 ++ .../standard_library/containers/boundedvec.md | 326 ++++++++++ .../standard_library/containers/hashmap.md | 569 ++++++++++++++++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 151 +++++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ec_primitives.md | 102 ++++ .../ecdsa_sig_verification.mdx | 60 ++ .../cryptographic_primitives/eddsa.mdx | 37 ++ .../cryptographic_primitives/hashes.mdx | 251 ++++++++ .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/scalar.mdx | 33 + .../cryptographic_primitives/schnorr.mdx | 45 ++ .../noir/standard_library/logging.md | 78 +++ .../noir/standard_library/merkle_trees.md | 58 ++ .../noir/standard_library/options.md | 101 ++++ .../noir/standard_library/recursion.md | 88 +++ .../noir/standard_library/traits.md | 399 ++++++++++++ .../noir/standard_library/zeroed.md | 25 + .../version-v0.25.0/reference/_category_.json | 5 + .../reference/nargo_commands.md | 380 ++++++++++++ .../version-v0.25.0/tutorials/noirjs_app.md | 279 +++++++++ .../version-v0.25.0-sidebars.json | 83 +++ flake.nix | 2 +- tooling/noir_codegen/package.json | 2 +- tooling/noir_js/package.json | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- tooling/noir_js_types/package.json | 2 +- tooling/noirc_abi_wasm/package.json | 2 +- 99 files changed, 8358 insertions(+), 49 deletions(-) create mode 100644 docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md create mode 100644 docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md create mode 100644 docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/index.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/migration_notes.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/scalar.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/logging.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/merkle_trees.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/options.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/traits.md create mode 100644 docs/versioned_docs/version-v0.25.0/noir/standard_library/zeroed.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/_category_.json create mode 100644 docs/versioned_docs/version-v0.25.0/reference/nargo_commands.md create mode 100644 docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md create mode 100644 docs/versioned_sidebars/version-v0.25.0-sidebars.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8916585d7f1..b38234ca0b9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.24.0", - "acvm-repo": "0.40.0" + ".": "0.25.0", + "acvm-repo": "0.41.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e9b2dfb48a5..cdbccf768ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,82 @@ # Changelog +## [0.25.0](https://github.com/noir-lang/noir/compare/v0.24.0...v0.25.0) (2024-03-11) + + +### ⚠ BREAKING CHANGES + +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) +* reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432)) +* Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431)) +* Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376)) +* bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) +* **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385)) +* Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235)) +* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) +* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) + +### Features + +* Add eddsa_poseidon_to_pub function to stdlib with test + docs ([#4473](https://github.com/noir-lang/noir/issues/4473)) ([00d2c32](https://github.com/noir-lang/noir/commit/00d2c32e58176cc5de3574c8435a54d415c4a5fa)) +* Add HashMap to the stdlib ([#4242](https://github.com/noir-lang/noir/issues/4242)) ([650ffc5](https://github.com/noir-lang/noir/commit/650ffc5053cdca4b6ad2e027fa1f4fd90ef64871)) +* Add option to set max memory for bb.js ([#4227](https://github.com/noir-lang/noir/issues/4227)) ([8a6b131](https://github.com/noir-lang/noir/commit/8a6b131402892a570bc2de6f5869de73b0bd979e)) +* Add overflow and underflow checks for unsigned integers in brillig ([#4445](https://github.com/noir-lang/noir/issues/4445)) ([21fc4b8](https://github.com/noir-lang/noir/commit/21fc4b85763dccae6dce0a46a318718c3c913471)) +* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) +* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Allow type aliases to reference other aliases ([#4353](https://github.com/noir-lang/noir/issues/4353)) ([c44ef14](https://github.com/noir-lang/noir/commit/c44ef14847a436733206b6dd9590a7ab214ecd97)) +* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) +* **ci:** Use wasm-opt when compiling wasm packages ([#4334](https://github.com/noir-lang/noir/issues/4334)) ([e382921](https://github.com/noir-lang/noir/commit/e3829213d8411f84e117a14b43816967925095e0)) +* DAP Preflight and debugger compilation options ([#4185](https://github.com/noir-lang/noir/issues/4185)) ([e0ad0b2](https://github.com/noir-lang/noir/commit/e0ad0b2b31f6d46be75d23aec6a82850a9c4bd75)) +* Expose separate functions to compile programs vs contracts in `noir_wasm` ([#4413](https://github.com/noir-lang/noir/issues/4413)) ([7cd5fdb](https://github.com/noir-lang/noir/commit/7cd5fdb3d2a53475b7c8681231d517cab30f9f9b)) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Restrict bit sizes ([#4235](https://github.com/noir-lang/noir/issues/4235)) ([1048f81](https://github.com/noir-lang/noir/commit/1048f815abb1f27e9c84ab5b9568a3673c12a50a)) +* Run tests in parallel in `nargo test` ([#4484](https://github.com/noir-lang/noir/issues/4484)) ([761734e](https://github.com/noir-lang/noir/commit/761734e6cb3ff5911aa85d0cee96ad26092b4905)) +* Skip redundant range checks in brillig ([#4460](https://github.com/noir-lang/noir/issues/4460)) ([cb4c1c5](https://github.com/noir-lang/noir/commit/cb4c1c5264b95d01f69d99f916ced71ad9cdc9d1)) +* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) +* Track stack frames and their variables in the debugger ([#4188](https://github.com/noir-lang/noir/issues/4188)) ([ae1a9d9](https://github.com/noir-lang/noir/commit/ae1a9d923998177516919bbba6ff4b0584fa1e9f)) +* TypeVariableKind for just Integers ([#4118](https://github.com/noir-lang/noir/issues/4118)) ([c956be8](https://github.com/noir-lang/noir/commit/c956be870fb47403a6da6585fce6bea2d40ee268)) +* Update error message when trying to load workspace as dependency ([#4393](https://github.com/noir-lang/noir/issues/4393)) ([d2585e7](https://github.com/noir-lang/noir/commit/d2585e738a63208fca3c9e26242e896d7f1df1e4)) + + +### Bug Fixes + +* **acir:** Array dynamic flatten ([#4351](https://github.com/noir-lang/noir/issues/4351)) ([b2aaeab](https://github.com/noir-lang/noir/commit/b2aaeab319a0c66c431a7db6852f743eccde8e98)) +* **acir:** Use types on dynamic arrays ([#4364](https://github.com/noir-lang/noir/issues/4364)) ([ba2c541](https://github.com/noir-lang/noir/commit/ba2c541ec45de92bba98de34771b73cbb7865c93)) +* Add `follow_bindings` to follow `Type::Alias` links ([#4521](https://github.com/noir-lang/noir/issues/4521)) ([b94adb9](https://github.com/noir-lang/noir/commit/b94adb92657e2b4a51dc7216a88e080aed1cf8b0)) +* Add handling to `noir_wasm` for projects without dependencies ([#4344](https://github.com/noir-lang/noir/issues/4344)) ([4982251](https://github.com/noir-lang/noir/commit/49822511710a7f1c42b8ed343e80456f8e6db2d9)) +* Allow type aliases in main ([#4505](https://github.com/noir-lang/noir/issues/4505)) ([8a5359c](https://github.com/noir-lang/noir/commit/8a5359c012579e54c2766de1074482a36ecada32)) +* Ban Fields in for loop indices and bitwise ops ([#4376](https://github.com/noir-lang/noir/issues/4376)) ([601fd9a](https://github.com/noir-lang/noir/commit/601fd9afc502236af1db0c4492698ba2298c7501)) +* Brillig range check with consistent bit size ([#4357](https://github.com/noir-lang/noir/issues/4357)) ([ea47d4a](https://github.com/noir-lang/noir/commit/ea47d4a67c6a18e4a7d3a49079d9eb24a1026a25)) +* Build noir_codegen when publishing ([#4448](https://github.com/noir-lang/noir/issues/4448)) ([cb1ceee](https://github.com/noir-lang/noir/commit/cb1ceee58b11b0ce6f8845361af3418d13c506bd)) +* Consistent bit size for truncate ([#4370](https://github.com/noir-lang/noir/issues/4370)) ([dcd7a1e](https://github.com/noir-lang/noir/commit/dcd7a1e561a68504b9038ffbb3c80f5c981f9f0c)) +* Correct formatting for databus visibility types ([#4423](https://github.com/noir-lang/noir/issues/4423)) ([cd796de](https://github.com/noir-lang/noir/commit/cd796dea4937dd1a261f154e5f2e599bbc649165)) +* Correct invalid brillig codegen for `EmbeddedCurvePoint.add` ([#4382](https://github.com/noir-lang/noir/issues/4382)) ([5051ec4](https://github.com/noir-lang/noir/commit/5051ec4d434a9e5cf405c68357faaf213e68de9e)) +* **docs:** Update install versions ([#4396](https://github.com/noir-lang/noir/issues/4396)) ([b283637](https://github.com/noir-lang/noir/commit/b283637e092038eb296c468168aec2d41e1c2734)) +* **docs:** Update noirjs_app for 0.23 ([#4378](https://github.com/noir-lang/noir/issues/4378)) ([f77f702](https://github.com/noir-lang/noir/commit/f77f702e0cfb81dcce4dd97e274b831e887ba5d2)) +* Enforce matching types of binary ops in SSA ([#4391](https://github.com/noir-lang/noir/issues/4391)) ([70866ae](https://github.com/noir-lang/noir/commit/70866aea976d59dbcbd4af34067fdd8f46555673)) +* Fix brillig slowdown when assigning arrays in loops ([#4472](https://github.com/noir-lang/noir/issues/4472)) ([2a53545](https://github.com/noir-lang/noir/commit/2a53545f4238c9b8535e6bc5b0720fa15f44f946)) +* **flake:** Stop flake.nix removing ignored-tests.txt ([#4455](https://github.com/noir-lang/noir/issues/4455)) ([ebaf05a](https://github.com/noir-lang/noir/commit/ebaf05ab10834dd10e04c7ea5130f96c6cdf98ed)) +* Force src impl for == on slices ([#4507](https://github.com/noir-lang/noir/issues/4507)) ([1691274](https://github.com/noir-lang/noir/commit/169127444e8b16a8aad4acfe29ba812894fd897c)) +* Handling of gh deps in noir_wasm ([#4499](https://github.com/noir-lang/noir/issues/4499)) ([1d65370](https://github.com/noir-lang/noir/commit/1d653704715bf9999eb6a40ed7500e752e2c73b7)) +* Iterative flattening pass ([#4492](https://github.com/noir-lang/noir/issues/4492)) ([33c1ef7](https://github.com/noir-lang/noir/commit/33c1ef70e7859fdee7babfb5d38191f53e73a0df)) +* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Only add `.nr` files to file manager ([#4380](https://github.com/noir-lang/noir/issues/4380)) ([8536c7c](https://github.com/noir-lang/noir/commit/8536c7c8ea8fc6b740b2ae6d1aef3bc7e1907b8c)) +* Remove panic when generic array length is not resolvable ([#4408](https://github.com/noir-lang/noir/issues/4408)) ([00ab3db](https://github.com/noir-lang/noir/commit/00ab3db86b06111d144516e862902b8604284611)) +* Remove print from monomorphization pass ([#4417](https://github.com/noir-lang/noir/issues/4417)) ([27c66b3](https://github.com/noir-lang/noir/commit/27c66b3d0741e68ed591ae8a16b47b30bc87175f)) +* **ssa:** Handle mergers of slices returned from calls ([#4496](https://github.com/noir-lang/noir/issues/4496)) ([f988d02](https://github.com/noir-lang/noir/commit/f988d020e43cdf36a38613f2052d4518de39193a)) +* Use correct type for numeric generics ([#4386](https://github.com/noir-lang/noir/issues/4386)) ([0a1d109](https://github.com/noir-lang/noir/commit/0a1d109f478c997da5c43876fd12464af638bb15)) +* Variables from trait constraints being permanently bound over when used within a trait impl ([#4450](https://github.com/noir-lang/noir/issues/4450)) ([ac60ef5](https://github.com/noir-lang/noir/commit/ac60ef5e12fcfb907fbdcff709d7cbad05f2b939)) + + +### Miscellaneous Chores + +* Bump msrv to 1.73.0 ([#4406](https://github.com/noir-lang/noir/issues/4406)) ([b5e5c30](https://github.com/noir-lang/noir/commit/b5e5c30f4db52c79ef556e80660f39db369b1911)) +* **ci:** Bump MSRV to 1.72.1 and enforce that ACVM can be published using updated lockfile ([#4385](https://github.com/noir-lang/noir/issues/4385)) ([2fc95d2](https://github.com/noir-lang/noir/commit/2fc95d2d82b3220267ce7d5815e7073e00ef1360)) +* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove empty value from bounded vec ([#4431](https://github.com/noir-lang/noir/issues/4431)) ([b9384fb](https://github.com/noir-lang/noir/commit/b9384fb23abf4ab15e880fb7e03c21509a9fa8a6)) +* Reserve `unchecked` keyword ([#4432](https://github.com/noir-lang/noir/issues/4432)) ([9544813](https://github.com/noir-lang/noir/commit/9544813fabbd18a87dd88456e6a5b781bd0cf008)) + ## [0.24.0](https://github.com/noir-lang/noir/compare/v0.23.0...v0.24.0) (2024-02-12) diff --git a/Cargo.lock b/Cargo.lock index 317418276b1..ad3dce229fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.40.0" +version = "0.41.0" dependencies = [ "acir_field", "base64 0.21.2", @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.40.0" +version = "0.41.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -37,7 +37,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.40.0" +version = "0.41.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -53,7 +53,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.40.0" +version = "0.41.0" dependencies = [ "acir", "blake2", @@ -68,7 +68,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.40.0" +version = "0.41.0" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -212,7 +212,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arena" -version = "0.24.0" +version = "0.25.0" [[package]] name = "ark-bls12-381" @@ -413,7 +413,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aztec_macros" -version = "0.24.0" +version = "0.25.0" dependencies = [ "convert_case 0.6.0", "iter-extended", @@ -602,7 +602,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.40.0" +version = "0.41.0" dependencies = [ "acir_field", "serde", @@ -610,7 +610,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.40.0" +version = "0.41.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1706,7 +1706,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.24.0" +version = "0.25.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -2298,7 +2298,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.24.0" +version = "0.25.0" [[package]] name = "itertools" @@ -2651,7 +2651,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nargo" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "codespan-reporting", @@ -2678,7 +2678,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "assert_cmd", @@ -2730,7 +2730,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.24.0" +version = "0.25.0" dependencies = [ "bytecount", "noirc_frontend", @@ -2742,7 +2742,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.24.0" +version = "0.25.0" dependencies = [ "dirs", "fm", @@ -2815,7 +2815,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "assert_cmd", @@ -2850,7 +2850,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "async-lsp", @@ -2876,7 +2876,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "build-data", @@ -2899,7 +2899,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "iter-extended", @@ -2916,7 +2916,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "build-data", @@ -2933,7 +2933,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "aztec_macros", @@ -2954,7 +2954,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "base64 0.21.2", @@ -2972,7 +2972,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "fxhash", @@ -2988,7 +2988,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "arena", @@ -3013,7 +3013,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.24.0" +version = "0.25.0" dependencies = [ "acvm", "iter-extended", diff --git a/Cargo.toml b/Cargo.toml index 38f39137360..2ddb9c9e28f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.24.0" +version = "0.25.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -51,12 +51,12 @@ repository = "https://github.com/noir-lang/noir/" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "0.40.0", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "0.40.0", path = "acvm-repo/acir", default-features = false } -acvm = { version = "0.40.0", path = "acvm-repo/acvm" } -brillig = { version = "0.40.0", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "0.40.0", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "0.40.0", path = "acvm-repo/blackbox_solver", default-features = false } +acir_field = { version = "0.41.0", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "0.41.0", path = "acvm-repo/acir", default-features = false } +acvm = { version = "0.41.0", path = "acvm-repo/acvm" } +brillig = { version = "0.41.0", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "0.41.0", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "0.41.0", path = "acvm-repo/blackbox_solver", default-features = false } bn254_blackbox_solver = { version = "0.39.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md index acb465e5cc9..4f220d6eeba 100644 --- a/acvm-repo/CHANGELOG.md +++ b/acvm-repo/CHANGELOG.md @@ -5,6 +5,70 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.41.0](https://github.com/noir-lang/noir/compare/v0.40.0...v0.41.0) (2024-03-11) + + +### ⚠ BREAKING CHANGES + +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) +* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) +* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) +* rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) +* init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) +* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) +* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) +* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) +* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) +* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) + +### Features + +* Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) +* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) +* Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de)) +* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) +* **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Aztec-packages ([#3754](https://github.com/noir-lang/noir/issues/3754)) ([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2)) +* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) +* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c)) +* Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) +* Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671)) +* Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3)) +* Speed up transformation of debug messages ([#3815](https://github.com/noir-lang/noir/issues/3815)) ([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48)) +* Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c)) +* Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc)) +* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138)) +* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) + + +### Bug Fixes + +* Deserialize odd length hex literals ([#3747](https://github.com/noir-lang/noir/issues/3747)) ([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2)) +* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b)) +* Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925)) + + +### Miscellaneous Chores + +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805)) +* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579)) +* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e)) +* Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) + ## [0.40.0](https://github.com/noir-lang/noir/compare/v0.39.0...v0.40.0) (2024-02-12) diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml index 7021333486f..be859d7d054 100644 --- a/acvm-repo/acir/Cargo.toml +++ b/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index 6f4971770bd..c2056b73277 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index fce9a8e8e8b..d585850170a 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 7ec814a72e5..63fca2bd32a 100644 --- a/acvm-repo/acvm_js/Cargo.toml +++ b/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index 876db9ccb62..0a9cd7235f5 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "0.40.0", + "version": "0.41.0", "publishConfig": { "access": "public" }, diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index 0794b2dbe7e..a783193edba 100644 --- a/acvm-repo/blackbox_solver/Cargo.toml +++ b/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml index 8d91d19e117..57f89e091b4 100644 --- a/acvm-repo/brillig/Cargo.toml +++ b/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 272e8389413..1c7add5cb40 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "0.40.0" +version = "0.41.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index 67584a2def1..6dfa3215483 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.24.0", + "version": "0.25.0", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md new file mode 100644 index 00000000000..18846176ca7 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..743c4d8d634 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/index.md @@ -0,0 +1,142 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution of our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..6160a102c6c --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,199 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. + +Take a private asset transfer as an example: + +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..a35e34aaf9c --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/installation/other_install_methods.md @@ -0,0 +1,254 @@ +--- +title: Alternative Install Methods +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Shell & editor experience + Building and testing + Uninstalling Nargo + Noir vs code extension, + ] +sidebar_position: 1 +--- + +## Encouraged Installation Method: Noirup + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + ```sh + noirup --path ./path/to/local/source + ``` + +## Alternate Installation Methods (No Longer Recommended) + +While the following methods are available, they are no longer recommended. We advise using noirup for a more efficient and flexible installation experience. + +However, there are other methods for installing Nargo: + +- [Binaries](#option-1-installing-from-binaries) +- [Compiling from Source](#option-2-compile-from-source) +- [WSL for Windows](#option-3-wsl-for-windows) + +### Option 1: Installing from Binaries + +See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous +platform specific binaries. + +#### Step 1 + +Paste and run the following in the terminal to extract and install the binary: + +> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend +> `sudo` and re-run it. + +##### macOS (Apple Silicon) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-aarch64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### macOS (Intel) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-x86_64-apple-darwin.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ +echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ +source ~/.zshrc +``` + +##### Linux (Bash) + +```bash +mkdir -p $HOME/.nargo/bin && \ +curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ +tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ +echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ +source ~/.bashrc +``` + +#### Step 2 + +Check if the installation was successful by running `nargo --version`. You should get a version number. + +> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from +> Finder. Close the new terminal popped up and `nargo` should now be accessible. + +### Option 2: Compile from Source + +Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). + +Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. + +#### Setting up your environment + +For the best experience, please follow these instructions to setup your environment: + +1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. +2. Create the file `~/.config/nix/nix.conf` with the contents: + +```ini +experimental-features = nix-command +extra-experimental-features = flakes +``` + +3. Install direnv into your Nix profile by running: + +```sh +nix profile install nixpkgs#direnv +``` + +4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). + 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. +5. Restart your shell. + +#### Shell & editor experience + +Now that your environment is set up, you can get to work on the project. + +1. Clone the repository, such as: + +```sh +git clone git@github.com:noir-lang/noir +``` + +> Replacing `noir` with whichever repository you want to work on. + +2. Navigate to the directory: + +```sh +cd noir +``` + +> Replacing `noir` with whichever repository you cloned. + +3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: + +```sh +direnv allow +``` + +4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. + +5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): + +```sh +code . +``` + +6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. + +#### Building and testing + +Assuming you are using `direnv` to populate your environment, building and testing the project can be done +with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.73.0 at the time of this writing. + +If you want to build the entire project in an isolated sandbox, you can use Nix commands: + +1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. +2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. + +#### Without `direnv` + +If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. + +Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! + +### Option 3: WSL (for Windows) + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#encouraged-installation-method-noirup). + +## Uninstalling Nargo + +### Noirup + +If you installed Nargo with `noirup` or through directly downloading binaries, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` + +### Nix + +If you installed Nargo with Nix or compiled it from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. + +```bash +rm ~/.nix-profile/bin/nargo +``` diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json new file mode 100644 index 00000000000..55804c03a71 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 2, + "label": "Tooling", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx new file mode 100644 index 00000000000..ac480f3c9f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/index.mdx @@ -0,0 +1,38 @@ +--- +title: Tooling +Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] +--- + +Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. + +## Playground + +The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). + +## IDE tools + +When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. + +The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +## Codespaces + +Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. + + + +## GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. + +## Community projects + +As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/getting_started/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/docs/versioned_docs/version-v0.25.0/how_to/_category_.json b/docs/versioned_docs/version-v0.25.0/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md new file mode 100644 index 00000000000..0d84d992320 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md @@ -0,0 +1,280 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("getSqrt", async (params) => { + const values = params[0].Array.map(({ inner }) => { + return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; + }); + return { values: [{ Array: values }] }; +}); +``` + +:::tip + +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: + +```json +{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} +{ "values": [{ "Single": { "inner": "1" }}]} +{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface Value { + inner: string, +} + +interface SingleForeignCallParam { + Single: Value, +} + +interface ArrayForeignCallParam { + Array: Value[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.generateFinalProof(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, + ]); + return [oracleReturn.values[0].Array.map((x) => x.inner)]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md new file mode 100644 index 00000000000..4c45bb87ae2 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-recursion.md @@ -0,0 +1,179 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit, backend) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const recursiveProof = await noir_programs.recursive.generateProof(recursiveInputs) +``` + +::: diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +
Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..34074659ac1 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.25.0/index.mdx b/docs/versioned_docs/version-v0.25.0/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.25.0/migration_notes.md b/docs/versioned_docs/version-v0.25.0/migration_notes.md new file mode 100644 index 00000000000..6bd740024e5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md new file mode 100644 index 00000000000..bcff613a695 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/assert.md @@ -0,0 +1,45 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md new file mode 100644 index 00000000000..4ce65236db3 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/control_flow.md @@ -0,0 +1,45 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +}; +``` + +The index for loops is of type `u64`. + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..a8bd338e736 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/arrays.md @@ -0,0 +1,251 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = []; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..69826fcd724 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/booleans.md @@ -0,0 +1,31 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..99b4aa63549 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/fields.md @@ -0,0 +1,192 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..97b3b2cb094 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md @@ -0,0 +1,110 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..4d58d96fed5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/integers.md @@ -0,0 +1,155 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. The Noir frontend supports both unsigned and signed integer types. The allowed sizes are 1, 8, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let x = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..4a6ee816aa2 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/slices.mdx @@ -0,0 +1,147 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = [0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = []; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = [1, 2].append([3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..311dfd64416 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/strings.md @@ -0,0 +1,80 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md new file mode 100644 index 00000000000..6c993b8b5e0 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/distinct.md @@ -0,0 +1,64 @@ +--- +title: Distinct Witnesses +sidebar_position: 11 +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md new file mode 100644 index 00000000000..48aba9cd058 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main([1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md new file mode 100644 index 00000000000..ddd42bf1f9b --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md new file mode 100644 index 00000000000..063a3d89248 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/globals.md @@ -0,0 +1,72 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type can be inferred by the compiler entirely: + +```rust +global N = 5; // Same as `global N: Field = 5` + +global TUPLE = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: Field = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md new file mode 100644 index 00000000000..fdeef6a87c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md new file mode 100644 index 00000000000..60425cb8994 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md new file mode 100644 index 00000000000..2e6a6818d48 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md new file mode 100644 index 00000000000..ef1445a5907 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: dep::some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: dep::some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..89d12c1c971 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/unconstrained.md @@ -0,0 +1,95 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..95ee9f52ab2 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..04c1703d929 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use dep::ecrecover; +use dep::lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..67a1dafa372 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,40 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..eae8744abf0 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [SHA256](./cryptographic_primitives/hashes#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr) +- [Blake2s](./cryptographic_primitives/hashes#blake2s) +- [Blake3](./cryptographic_primitives/hashes#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md new file mode 100644 index 00000000000..3294f005dbb --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md new file mode 100644 index 00000000000..ce4529f6e57 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,326 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn foo() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of foo's return value + v2 +} + +fn bad() { + let mut v3 = BoundedVec::new(); + + // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L68-L76 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L81-L93 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L98-L113 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L118-L124 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L129-L136 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L141-L148 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L153-L162 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L229-L235 + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md new file mode 100644 index 00000000000..91604af765d --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/hashmap.md @@ -0,0 +1,569 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already +known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which +will likely change the result of the program. This behavior is set to become an error in future +versions instead. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a pedersen hash +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L196-L198 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L82-L86 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L211 + + +### get + +```rust title="get" showLineNumbers +pub fn get( + self, + key: K + ) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L278-L287 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L299-L307 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert( + &mut self, + key: K, + value: V + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L313-L323 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L213-L217 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove( + &mut self, + key: K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L356-L365 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L221-L228 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L115-L117 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L230-L238 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u64 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L264-L266 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L240-L255 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u64 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L271-L273 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L261 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L93-L95 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key( + self, + key: K + ) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L101-L110 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L310-L321 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L144-L146 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L323-L333 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L164-L166 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L335-L344 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut( + &mut self, + f: fn(K, V) -> (K, V) + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L183-L192 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut( + &mut self, + f: fn(K) -> K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L208-L217 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L353-L356 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L233-L235 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L358-L361 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L247-L249 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L281-L283 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher +{ + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L426-L435 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L285-L296 + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md new file mode 100644 index 00000000000..ea84c6d5c21 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx new file mode 100644 index 00000000000..1954f05bc76 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/containers/vec.mdx @@ -0,0 +1,151 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let arr: [Field] = [1, 2, 3]; +let vector_from_slice = Vec::from_slice(arr); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice([10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice([10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..d2b42d67b7c --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..4bf09cef178 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,60 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..c2c0624dfad --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,37 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + +It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify_with_hasher` function with a parameter implementing the Hasher trait. For instance, if you want to use Poseidon2 instead, you can do the following: +```rust +use dep::std::hash::poseidon2::Poseidon2Hasher; + +let mut hasher = Poseidon2Hasher::default(); +eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher); +``` + + + +## eddsa::eddsa_to_pub + +Private to public key conversion. + +Returns `(pub_key_x, pub_key_y)` + +```rust +fn eddsa_to_pub(secret : Field) -> (Field, Field) +``` + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..119d8ccc70e --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,251 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. + +```rust title="sha256" showLineNumbers +pub fn sha256(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L9-L11 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L15-L17 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L21-L23 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L46-L48 + + +example: + +```rust title="pedersen-hash" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 + + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment" showLineNumbers +struct PedersenPoint { + x : Field, + y : Field, +} + +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint +``` +> Source code: noir_stdlib/src/hash.nr#L26-L33 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes +(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes +of the input. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L71-L73 + + +example: + +```rust title="keccak256" showLineNumbers +use dep::std; + +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use dep::std::hash::poseidon; +use dep::std::hash::poseidon2; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field; 4], y3: Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); + + let hash3 = poseidon2::Poseidon2::hash(x3, x3.len()); + assert(hash3 == y3); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L15 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +The above example for Poseidon also includes Poseidon2. + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field; N]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/scalar.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/scalar.mdx new file mode 100644 index 00000000000..df411ca5443 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -0,0 +1,33 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust title="fixed_base_embedded_curve" showLineNumbers +pub fn fixed_base_embedded_curve( + low: Field, + high: Field +) -> [Field; 2] +``` +> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 + + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + println(scal); +} +``` + + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..ae12e6c12dc --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,45 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). + +```rust title="schnorr_verify" showLineNumbers +pub fn verify_signature( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L2-L9 + + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..fa488677884 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/options.md new file mode 100644 index 00000000000..a1bd4e1de5f --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md new file mode 100644 index 00000000000..9337499dac8 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/recursion.md @@ -0,0 +1,88 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## The `#[recursive]` Attribute + +In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. + +### Example usage with `#[recursive]` + +```rust +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); +} + +// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit +// are intended for recursive verification. +``` + +By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. + +## Verifying Recursive Proofs + +```rust +#[foreign(verify_proof)] +fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Example usage + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/traits.md new file mode 100644 index 00000000000..ba9fa2ee841 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/traits.md @@ -0,0 +1,399 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L1-L5 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type. + + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +``` +> Source code: noir_stdlib/src/convert.nr#L25-L52 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +trait Into { + fn into(input: Self) -> T; +} + +impl Into for U where T: From { + fn into(input: U) -> T { + T::from(input) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L23 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L1-L5 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L92-L96 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L17-L21 + +```rust title="mul-trait" showLineNumbers +trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L33-L37 + +```rust title="div-trait" showLineNumbers +trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L49-L53 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +trait Rem{ + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L65-L69 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L79-L83 + +```rust title="bitand-trait" showLineNumbers +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L95-L99 + +```rust title="bitxor-trait" showLineNumbers +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L111-L115 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +trait Shl { + fn shl(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L127-L131 + +```rust title="shr-trait" showLineNumbers +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L142-L146 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..97dab02dac2 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/zeroed.md @@ -0,0 +1,25 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.25.0/reference/_category_.json b/docs/versioned_docs/version-v0.25.0/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.25.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.25.0/reference/nargo_commands.md new file mode 100644 index 00000000000..8a309ef4e7e --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/nargo_commands.md @@ -0,0 +1,380 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo backend`↴](#nargo-backend) +* [`nargo backend current`↴](#nargo-backend-current) +* [`nargo backend ls`↴](#nargo-backend-ls) +* [`nargo backend use`↴](#nargo-backend-use) +* [`nargo backend install`↴](#nargo-backend-install) +* [`nargo backend uninstall`↴](#nargo-backend-uninstall) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo codegen-verifier`↴](#nargo-codegen-verifier) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo prove`↴](#nargo-prove) +* [`nargo verify`↴](#nargo-verify) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `backend` — Install and select custom backends used to generate and verify proofs +* `check` — Checks the constraint system for errors +* `fmt` — Format the Noir files in a workspace +* `codegen-verifier` — Generates a Solidity verifier smart contract for the program +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `prove` — Create proof for this program. The proof is returned as a hex encoded string +* `verify` — Given a proof and a program, verify whether the proof is valid +* `test` — Run the tests for this program +* `info` — Provides detailed information on a circuit +* `lsp` — Starts the Noir LSP server + +###### **Options:** + + + + +## `nargo backend` + +Install and select custom backends used to generate and verify proofs + +**Usage:** `nargo backend ` + +###### **Subcommands:** + +* `current` — Prints the name of the currently active backend +* `ls` — Prints the list of currently installed backends +* `use` — Select the backend to use +* `install` — Install a new backend from a URL +* `uninstall` — Uninstalls a backend + + + +## `nargo backend current` + +Prints the name of the currently active backend + +**Usage:** `nargo backend current` + + + +## `nargo backend ls` + +Prints the list of currently installed backends + +**Usage:** `nargo backend ls` + + + +## `nargo backend use` + +Select the backend to use + +**Usage:** `nargo backend use ` + +###### **Arguments:** + +* `` + + + +## `nargo backend install` + +Install a new backend from a URL + +**Usage:** `nargo backend install ` + +###### **Arguments:** + +* `` — The name of the backend to install +* `` — The URL from which to download the backend + + + +## `nargo backend uninstall` + +Uninstalls a backend + +**Usage:** `nargo backend uninstall ` + +###### **Arguments:** + +* `` — The name of the backend to uninstall + + + +## `nargo check` + +Checks the constraint system for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to check +* `--workspace` — Check all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + + +## `nargo codegen-verifier` + +Generates a Solidity verifier smart contract for the program + +**Usage:** `nargo codegen-verifier [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to codegen +* `--workspace` — Codegen all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to compile +* `--workspace` — Compile all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--workspace` — Execute all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo prove` + +Create proof for this program. The proof is returned as a hex encoded string + +**Usage:** `nargo prove [OPTIONS]` + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `-v`, `--verifier-name ` — The name of the toml file which contains the inputs for the verifier + + Default value: `Verifier` +* `--verify` — Verify proof after proving +* `--package ` — The name of the package to prove +* `--workspace` — Prove all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid + +**Usage:** `nargo verify [OPTIONS]` + +###### **Options:** + +* `-v`, `--verifier-name ` — The name of the toml file which contains the inputs for the verifier + + Default value: `Verifier` +* `--package ` — The name of the package verify +* `--workspace` — Verify all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAME]` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements +* `--exact` — Only run tests that match exactly +* `--package ` — The name of the package to test +* `--workspace` — Test all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo info` + +Provides detailed information on a circuit + +Current information provided: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to detail +* `--workspace` — Detail all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md new file mode 100644 index 00000000000..ad76dd255cc --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md @@ -0,0 +1,279 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. + +In this guide, we will be pinned to 0.19.4. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +```nargo new circuit``` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +```nargo compile``` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: + +```bash +npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It *could* be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import("@noir-lang/noirc_abi").then(module => + module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + ), + import("@noir-lang/acvm_js").then(module => + module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) + ) + ]); +} + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch(err) { + display("logs", "Oh 💔 Wrong guess") + } +}); + +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateFinalProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyFinalProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/docs/versioned_sidebars/version-v0.25.0-sidebars.json b/docs/versioned_sidebars/version-v0.25.0-sidebars.json new file mode 100644 index 00000000000..b16f79cc176 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.25.0-sidebars.json @@ -0,0 +1,83 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/flake.nix b/flake.nix index 5125dad06be..785d5aead56 100644 --- a/flake.nix +++ b/flake.nix @@ -73,7 +73,7 @@ # Configuration shared between builds config = { # x-release-please-start-version - version = "0.24.0"; + version = "0.25.0"; # x-release-please-end src = pkgs.lib.cleanSourceWith { diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json index 6bb9d06f718..68ee7184928 100644 --- a/tooling/noir_codegen/package.json +++ b/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.24.0", + "version": "0.25.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index 6f1899fae52..0a40d300ea4 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.24.0", + "version": "0.25.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 06c034725e3..d94999f324b 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.24.0", + "version": "0.25.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index a3b5c85897a..d853813303d 100644 --- a/tooling/noir_js_types/package.json +++ b/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "0.24.0", + "version": "0.25.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index 05fcc270402..8fb9eb314a5 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.24.0", + "version": "0.25.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { From f93d16e3e89c5df358c982deae4f3c2d4c82b77f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 11 Mar 2024 22:49:24 +0000 Subject: [PATCH 064/416] fix(acir_gen): More granular element sizes array check (#4528) # Description ## Problem\* Resolves ## Summary\* The `array_typ.contains_slice_element()` check in `can_omit_element_sizes_array` is incorrect. This should have been `is_nested_slice()` but we already banned nested slices so I just removed it. This leads to improvements for slices in general so I have made it a separate PR. This was also a blocker for https://github.com/noir-lang/noir/pull/4523. When working with `AsSlice` we would omit the element sizes array. When it came time to access a slice created with `as_slice()` we would attempt to use the element sizes array which was never initialized correctly and we would go out of bounds. ## Additional Context ## Documentation\* Check one: - [X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 8d4d0668534..140ed0b53ff 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -2268,11 +2268,9 @@ impl Context { // We can omit the element size array for arrays which don't contain arrays or slices. fn can_omit_element_sizes_array(array_typ: &Type) -> bool { - if array_typ.contains_slice_element() { - return false; - } - let Type::Array(types, _) = array_typ else { - panic!("ICE: expected array type"); + let types = match array_typ { + Type::Array(types, _) | Type::Slice(types) => types, + _ => panic!("ICE: expected array or slice type"), }; !types.iter().any(|typ| typ.contains_an_array()) From b3fd24d77e141c3a6562b6bcfc62d2ecb572e191 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:33:51 +0000 Subject: [PATCH 065/416] chore: organize the `blackbox_solver` crate (#4519) # Description ## Problem\* Resolves ## Summary\* No material changes, I'm just moving function into different modules to make testing less messy. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/blackbox_solver/src/ecdsa/mod.rs | 22 + .../blackbox_solver/src/ecdsa/secp256k1.rs | 137 ++++++ .../blackbox_solver/src/ecdsa/secp256r1.rs | 133 ++++++ acvm-repo/blackbox_solver/src/hash.rs | 126 +++++ acvm-repo/blackbox_solver/src/lib.rs | 446 +----------------- 5 files changed, 422 insertions(+), 442 deletions(-) create mode 100644 acvm-repo/blackbox_solver/src/ecdsa/mod.rs create mode 100644 acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs create mode 100644 acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs create mode 100644 acvm-repo/blackbox_solver/src/hash.rs diff --git a/acvm-repo/blackbox_solver/src/ecdsa/mod.rs b/acvm-repo/blackbox_solver/src/ecdsa/mod.rs new file mode 100644 index 00000000000..cb3134bf0ab --- /dev/null +++ b/acvm-repo/blackbox_solver/src/ecdsa/mod.rs @@ -0,0 +1,22 @@ +use crate::BlackBoxResolutionError; + +mod secp256k1; +mod secp256r1; + +pub fn ecdsa_secp256k1_verify( + hashed_msg: &[u8], + public_key_x: &[u8; 32], + public_key_y: &[u8; 32], + signature: &[u8; 64], +) -> Result { + Ok(secp256k1::verify_signature(hashed_msg, public_key_x, public_key_y, signature)) +} + +pub fn ecdsa_secp256r1_verify( + hashed_msg: &[u8], + public_key_x: &[u8; 32], + public_key_y: &[u8; 32], + signature: &[u8; 64], +) -> Result { + Ok(secp256r1::verify_signature(hashed_msg, public_key_x, public_key_y, signature)) +} diff --git a/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs b/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs new file mode 100644 index 00000000000..17c51353f7f --- /dev/null +++ b/acvm-repo/blackbox_solver/src/ecdsa/secp256k1.rs @@ -0,0 +1,137 @@ +use k256::elliptic_curve::sec1::FromEncodedPoint; +use k256::elliptic_curve::PrimeField; + +use blake2::digest::generic_array::GenericArray; +use k256::{ecdsa::Signature, Scalar}; +use k256::{ + elliptic_curve::{ + sec1::{Coordinates, ToEncodedPoint}, + IsHigh, + }, + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, +}; + +pub(super) fn verify_signature( + hashed_msg: &[u8], + public_key_x_bytes: &[u8; 32], + public_key_y_bytes: &[u8; 32], + signature: &[u8; 64], +) -> bool { + // Convert the inputs into k256 data structures + let Ok(signature) = Signature::try_from(signature.as_slice()) else { + // Signature `r` and `s` are forbidden from being zero. + return false; + }; + + let point = EncodedPoint::from_affine_coordinates( + public_key_x_bytes.into(), + public_key_y_bytes.into(), + true, + ); + + let pubkey = PublicKey::from_encoded_point(&point); + let pubkey = if pubkey.is_some().into() { + pubkey.unwrap() + } else { + // Public key must sit on the Secp256k1 curve. + return false; + }; + + // Note: This is incorrect as it will panic if `hashed_msg >= k256::Secp256k1::ORDER`. + // In this scenario we should just take the leftmost bits from `hashed_msg` up to the group order length. + let z = Scalar::from_repr(*GenericArray::from_slice(hashed_msg)).unwrap(); + + // Finished converting bytes into data structures + + let r = signature.r(); + let s = signature.s(); + + // Ensure signature is "low S" normalized ala BIP 0062 + if s.is_high().into() { + return false; + } + + let s_inv = s.invert().unwrap(); + let u1 = z * s_inv; + let u2 = *r * s_inv; + + #[allow(non_snake_case)] + let R: AffinePoint = ((ProjectivePoint::GENERATOR * u1) + + (ProjectivePoint::from(*pubkey.as_affine()) * u2)) + .to_affine(); + + match R.to_encoded_point(false).coordinates() { + Coordinates::Uncompressed { x, y: _ } => Scalar::from_repr(*x).unwrap().eq(&r), + _ => unreachable!("Point is uncompressed"), + } +} + +#[cfg(test)] +mod secp256k1_tests { + use super::verify_signature; + + // 0x3a73f4123a5cd2121f21cd7e8d358835476949d035d9c2da6806b4633ac8c1e2, + const HASHED_MESSAGE: [u8; 32] = [ + 0x3a, 0x73, 0xf4, 0x12, 0x3a, 0x5c, 0xd2, 0x12, 0x1f, 0x21, 0xcd, 0x7e, 0x8d, 0x35, 0x88, + 0x35, 0x47, 0x69, 0x49, 0xd0, 0x35, 0xd9, 0xc2, 0xda, 0x68, 0x06, 0xb4, 0x63, 0x3a, 0xc8, + 0xc1, 0xe2, + ]; + // 0xa0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7 + const PUB_KEY_X: [u8; 32] = [ + 0xa0, 0x43, 0x4d, 0x9e, 0x47, 0xf3, 0xc8, 0x62, 0x35, 0x47, 0x7c, 0x7b, 0x1a, 0xe6, 0xae, + 0x5d, 0x34, 0x42, 0xd4, 0x9b, 0x19, 0x43, 0xc2, 0xb7, 0x52, 0xa6, 0x8e, 0x2a, 0x47, 0xe2, + 0x47, 0xc7, + ]; + // 0x893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7 + const PUB_KEY_Y: [u8; 32] = [ + 0x89, 0x3a, 0xba, 0x42, 0x54, 0x19, 0xbc, 0x27, 0xa3, 0xb6, 0xc7, 0xe6, 0x93, 0xa2, 0x4c, + 0x69, 0x6f, 0x79, 0x4c, 0x2e, 0xd8, 0x77, 0xa1, 0x59, 0x3c, 0xbe, 0xe5, 0x3b, 0x03, 0x73, + 0x68, 0xd7, + ]; + // 0xe5081c80ab427dc370346f4a0e31aa2bad8d9798c38061db9ae55a4e8df454fd28119894344e71b78770cc931d61f480ecbb0b89d6eb69690161e49a715fcd55 + const SIGNATURE: [u8; 64] = [ + 0xe5, 0x08, 0x1c, 0x80, 0xab, 0x42, 0x7d, 0xc3, 0x70, 0x34, 0x6f, 0x4a, 0x0e, 0x31, 0xaa, + 0x2b, 0xad, 0x8d, 0x97, 0x98, 0xc3, 0x80, 0x61, 0xdb, 0x9a, 0xe5, 0x5a, 0x4e, 0x8d, 0xf4, + 0x54, 0xfd, 0x28, 0x11, 0x98, 0x94, 0x34, 0x4e, 0x71, 0xb7, 0x87, 0x70, 0xcc, 0x93, 0x1d, + 0x61, 0xf4, 0x80, 0xec, 0xbb, 0x0b, 0x89, 0xd6, 0xeb, 0x69, 0x69, 0x01, 0x61, 0xe4, 0x9a, + 0x71, 0x5f, 0xcd, 0x55, + ]; + + #[test] + fn verifies_valid_signature_with_low_s_value() { + let valid = verify_signature(&HASHED_MESSAGE, &PUB_KEY_X, &PUB_KEY_Y, &SIGNATURE); + + assert!(valid); + } + + #[test] + fn rejects_invalid_signature() { + // This signature is invalid as ECDSA specifies that `r` and `s` must be non-zero. + let invalid_signature: [u8; 64] = [0x00; 64]; + + let valid = verify_signature(&HASHED_MESSAGE, &PUB_KEY_X, &PUB_KEY_Y, &invalid_signature); + assert!(!valid); + } + + #[test] + fn rejects_invalid_public_key() { + let invalid_pub_key_x: [u8; 32] = [0xff; 32]; + let invalid_pub_key_y: [u8; 32] = [0xff; 32]; + + let valid = + verify_signature(&HASHED_MESSAGE, &invalid_pub_key_x, &invalid_pub_key_y, &SIGNATURE); + + assert!(!valid); + } + + #[test] + #[ignore = "ECDSA verification does not currently handle long hashes correctly"] + fn trims_overly_long_hashes_to_correct_length() { + let mut long_hashed_message = HASHED_MESSAGE.to_vec(); + long_hashed_message.push(0xff); + + let valid = verify_signature(&long_hashed_message, &PUB_KEY_X, &PUB_KEY_Y, &SIGNATURE); + + assert!(valid); + } +} diff --git a/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs b/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs new file mode 100644 index 00000000000..54559d7c774 --- /dev/null +++ b/acvm-repo/blackbox_solver/src/ecdsa/secp256r1.rs @@ -0,0 +1,133 @@ +use p256::elliptic_curve::sec1::FromEncodedPoint; +use p256::elliptic_curve::PrimeField; + +use blake2::digest::generic_array::GenericArray; +use p256::{ecdsa::Signature, Scalar}; +use p256::{ + elliptic_curve::{ + sec1::{Coordinates, ToEncodedPoint}, + IsHigh, + }, + AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, +}; + +pub(super) fn verify_signature( + hashed_msg: &[u8], + public_key_x_bytes: &[u8; 32], + public_key_y_bytes: &[u8; 32], + signature: &[u8; 64], +) -> bool { + // Convert the inputs into k256 data structures + let Ok(signature) = Signature::try_from(signature.as_slice()) else { + // Signature `r` and `s` are forbidden from being zero. + return false; + }; + + let point = EncodedPoint::from_affine_coordinates( + public_key_x_bytes.into(), + public_key_y_bytes.into(), + true, + ); + + let pubkey = PublicKey::from_encoded_point(&point); + let pubkey = if pubkey.is_some().into() { + pubkey.unwrap() + } else { + // Public key must sit on the Secp256r1 curve. + return false; + }; + + // Note: This is incorrect as it will panic if `hashed_msg >= p256::NistP256::ORDER`. + // In this scenario we should just take the leftmost bits from `hashed_msg` up to the group order length. + let z = Scalar::from_repr(*GenericArray::from_slice(hashed_msg)).unwrap(); + + // Finished converting bytes into data structures + + let r = signature.r(); + let s = signature.s(); + + // Ensure signature is "low S" normalized ala BIP 0062 + if s.is_high().into() { + return false; + } + + let s_inv = s.invert().unwrap(); + let u1 = z * s_inv; + let u2 = *r * s_inv; + + #[allow(non_snake_case)] + let R: AffinePoint = ((ProjectivePoint::GENERATOR * u1) + + (ProjectivePoint::from(*pubkey.as_affine()) * u2)) + .to_affine(); + + match R.to_encoded_point(false).coordinates() { + Coordinates::Uncompressed { x, y: _ } => Scalar::from_repr(*x).unwrap().eq(&r), + _ => unreachable!("Point is uncompressed"), + } +} + +#[cfg(test)] +mod secp256r1_tests { + use super::verify_signature; + + // 0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad + const HASHED_MESSAGE: [u8; 32] = [ + 84, 112, 91, 163, 186, 175, 219, 223, 186, 140, 95, 154, 112, 247, 168, 155, 238, 152, 217, + 6, 181, 62, 49, 7, 77, 167, 186, 236, 220, 13, 169, 173, + ]; + // 0x550f471003f3df97c3df506ac797f6721fb1a1fb7b8f6f83d224498a65c88e24 + const PUB_KEY_X: [u8; 32] = [ + 85, 15, 71, 16, 3, 243, 223, 151, 195, 223, 80, 106, 199, 151, 246, 114, 31, 177, 161, 251, + 123, 143, 111, 131, 210, 36, 73, 138, 101, 200, 142, 36, + ]; + // 0x136093d7012e509a73715cbd0b00a3cc0ff4b5c01b3ffa196ab1fb327036b8e6 + const PUB_KEY_Y: [u8; 32] = [ + 19, 96, 147, 215, 1, 46, 80, 154, 115, 113, 92, 189, 11, 0, 163, 204, 15, 244, 181, 192, + 27, 63, 250, 25, 106, 177, 251, 50, 112, 54, 184, 230, + ]; + // 0x2c70a8d084b62bfc5ce03641caf9f72ad4da8c81bfe6ec9487bb5e1bef62a13218ad9ee29eaf351fdc50f1520c425e9b908a07278b43b0ec7b872778c14e0784 + const SIGNATURE: [u8; 64] = [ + 44, 112, 168, 208, 132, 182, 43, 252, 92, 224, 54, 65, 202, 249, 247, 42, 212, 218, 140, + 129, 191, 230, 236, 148, 135, 187, 94, 27, 239, 98, 161, 50, 24, 173, 158, 226, 158, 175, + 53, 31, 220, 80, 241, 82, 12, 66, 94, 155, 144, 138, 7, 39, 139, 67, 176, 236, 123, 135, + 39, 120, 193, 78, 7, 132, + ]; + + #[test] + fn verifies_valid_signature_with_low_s_value() { + let valid = verify_signature(&HASHED_MESSAGE, &PUB_KEY_X, &PUB_KEY_Y, &SIGNATURE); + + assert!(valid); + } + + #[test] + fn rejects_invalid_signature() { + // This signature is invalid as ECDSA specifies that `r` and `s` must be non-zero. + let invalid_signature: [u8; 64] = [0x00; 64]; + + let valid = verify_signature(&HASHED_MESSAGE, &PUB_KEY_X, &PUB_KEY_Y, &invalid_signature); + assert!(!valid); + } + + #[test] + fn rejects_invalid_public_key() { + let invalid_pub_key_x: [u8; 32] = [0xff; 32]; + let invalid_pub_key_y: [u8; 32] = [0xff; 32]; + + let valid = + verify_signature(&HASHED_MESSAGE, &invalid_pub_key_x, &invalid_pub_key_y, &SIGNATURE); + + assert!(!valid); + } + + #[test] + #[ignore = "ECDSA verification does not currently handle long hashes correctly"] + fn trims_overly_long_hashes_to_correct_length() { + let mut long_hashed_message = HASHED_MESSAGE.to_vec(); + long_hashed_message.push(0xff); + + let valid = verify_signature(&long_hashed_message, &PUB_KEY_X, &PUB_KEY_Y, &SIGNATURE); + + assert!(valid); + } +} diff --git a/acvm-repo/blackbox_solver/src/hash.rs b/acvm-repo/blackbox_solver/src/hash.rs new file mode 100644 index 00000000000..ac56029b436 --- /dev/null +++ b/acvm-repo/blackbox_solver/src/hash.rs @@ -0,0 +1,126 @@ +use acir::BlackBoxFunc; +use blake2::digest::generic_array::GenericArray; +use blake2::{Blake2s256, Digest}; +use sha2::Sha256; +use sha3::Keccak256; + +use crate::BlackBoxResolutionError; + +/// Does a generic hash of the inputs returning the resulting 32 bytes separately. +fn generic_hash_256(message: &[u8]) -> Result<[u8; 32], String> { + let output_bytes: [u8; 32] = + D::digest(message).as_slice().try_into().map_err(|_| "digest should be 256 bits")?; + + Ok(output_bytes) +} + +pub fn sha256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { + generic_hash_256::(inputs) + .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::SHA256, err)) +} + +pub fn blake2s(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { + generic_hash_256::(inputs) + .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Blake2s, err)) +} + +pub fn blake3(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { + Ok(blake3::hash(inputs).into()) +} + +pub fn keccak256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { + generic_hash_256::(inputs) + .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Keccak256, err)) +} + +pub fn sha256compression(state: &mut [u32; 8], msg_blocks: &[u32; 16]) { + let mut blocks = [0_u8; 64]; + for (i, block) in msg_blocks.iter().enumerate() { + let bytes = block.to_be_bytes(); + blocks[i * 4..i * 4 + 4].copy_from_slice(&bytes); + } + let blocks: GenericArray = blocks.into(); + sha2::compress256(state, &[blocks]); +} + +const KECCAK_LANES: usize = 25; + +pub fn keccakf1600( + mut state: [u64; KECCAK_LANES], +) -> Result<[u64; KECCAK_LANES], BlackBoxResolutionError> { + keccak::f1600(&mut state); + Ok(state) +} + +#[cfg(test)] +mod keccakf1600_tests { + use super::keccakf1600; + + #[test] + fn sanity_check() { + // Test vectors are copied from XKCP (eXtended Keccak Code Package) + // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt + let zero_state = [0u64; 25]; + + let expected_state_first = [ + 0xF1258F7940E1DDE7, + 0x84D5CCF933C0478A, + 0xD598261EA65AA9EE, + 0xBD1547306F80494D, + 0x8B284E056253D057, + 0xFF97A42D7F8E6FD4, + 0x90FEE5A0A44647C4, + 0x8C5BDA0CD6192E76, + 0xAD30A6F71B19059C, + 0x30935AB7D08FFC64, + 0xEB5AA93F2317D635, + 0xA9A6E6260D712103, + 0x81A57C16DBCF555F, + 0x43B831CD0347C826, + 0x01F22F1A11A5569F, + 0x05E5635A21D9AE61, + 0x64BEFEF28CC970F2, + 0x613670957BC46611, + 0xB87C5A554FD00ECB, + 0x8C3EE88A1CCF32C8, + 0x940C7922AE3A2614, + 0x1841F924A2C509E4, + 0x16F53526E70465C2, + 0x75F644E97F30A13B, + 0xEAF1FF7B5CECA249, + ]; + let expected_state_second = [ + 0x2D5C954DF96ECB3C, + 0x6A332CD07057B56D, + 0x093D8D1270D76B6C, + 0x8A20D9B25569D094, + 0x4F9C4F99E5E7F156, + 0xF957B9A2DA65FB38, + 0x85773DAE1275AF0D, + 0xFAF4F247C3D810F7, + 0x1F1B9EE6F79A8759, + 0xE4FECC0FEE98B425, + 0x68CE61B6B9CE68A1, + 0xDEEA66C4BA8F974F, + 0x33C43D836EAFB1F5, + 0xE00654042719DBD9, + 0x7CF8A9F009831265, + 0xFD5449A6BF174743, + 0x97DDAD33D8994B40, + 0x48EAD5FC5D0BE774, + 0xE3B8C8EE55B7B03C, + 0x91A0226E649E42E9, + 0x900E3129E7BADD7B, + 0x202A9EC5FAA3CCE8, + 0x5B3402464E1C3DB6, + 0x609F4E62A44C1059, + 0x20D06CD26A8FBF5C, + ]; + + let state_first = keccakf1600(zero_state).unwrap(); + let state_second = keccakf1600(state_first).unwrap(); + + assert_eq!(state_first, expected_state_first); + assert_eq!(state_second, expected_state_second); + } +} diff --git a/acvm-repo/blackbox_solver/src/lib.rs b/acvm-repo/blackbox_solver/src/lib.rs index e033344fefa..dc798bdab32 100644 --- a/acvm-repo/blackbox_solver/src/lib.rs +++ b/acvm-repo/blackbox_solver/src/lib.rs @@ -8,456 +8,18 @@ //! For functions that have a reference implementation, such as [keccak256], this crate exports the reference implementation directly. use acir::BlackBoxFunc; -use blake2::digest::generic_array::GenericArray; -use blake2::{Blake2s256, Digest}; -use sha2::Sha256; -use sha3::Keccak256; use thiserror::Error; mod curve_specific_solver; +mod ecdsa; +mod hash; pub use curve_specific_solver::{BlackBoxFunctionSolver, StubbedBlackBoxSolver}; +pub use ecdsa::{ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; +pub use hash::{blake2s, blake3, keccak256, keccakf1600, sha256, sha256compression}; #[derive(Clone, PartialEq, Eq, Debug, Error)] pub enum BlackBoxResolutionError { #[error("failed to solve blackbox function: {0}, reason: {1}")] Failed(BlackBoxFunc, String), } - -pub fn sha256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { - generic_hash_256::(inputs) - .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::SHA256, err)) -} - -pub fn blake2s(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { - generic_hash_256::(inputs) - .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Blake2s, err)) -} - -pub fn blake3(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { - Ok(blake3::hash(inputs).into()) -} - -pub fn keccak256(inputs: &[u8]) -> Result<[u8; 32], BlackBoxResolutionError> { - generic_hash_256::(inputs) - .map_err(|err| BlackBoxResolutionError::Failed(BlackBoxFunc::Keccak256, err)) -} - -pub fn sha256compression(state: &mut [u32; 8], msg_blocks: &[u32; 16]) { - let mut blocks = [0_u8; 64]; - for (i, block) in msg_blocks.iter().enumerate() { - let bytes = block.to_be_bytes(); - blocks[i * 4..i * 4 + 4].copy_from_slice(&bytes); - } - let blocks: GenericArray = blocks.into(); - sha2::compress256(state, &[blocks]); -} - -const KECCAK_LANES: usize = 25; - -pub fn keccakf1600( - mut state: [u64; KECCAK_LANES], -) -> Result<[u64; KECCAK_LANES], BlackBoxResolutionError> { - keccak::f1600(&mut state); - Ok(state) -} - -pub fn ecdsa_secp256k1_verify( - hashed_msg: &[u8], - public_key_x: &[u8; 32], - public_key_y: &[u8; 32], - signature: &[u8; 64], -) -> Result { - Ok(verify_secp256k1_ecdsa_signature(hashed_msg, public_key_x, public_key_y, signature)) -} - -pub fn ecdsa_secp256r1_verify( - hashed_msg: &[u8], - public_key_x: &[u8; 32], - public_key_y: &[u8; 32], - signature: &[u8; 64], -) -> Result { - Ok(verify_secp256r1_ecdsa_signature(hashed_msg, public_key_x, public_key_y, signature)) -} - -/// Does a generic hash of the inputs returning the resulting 32 bytes separately. -fn generic_hash_256(message: &[u8]) -> Result<[u8; 32], String> { - let output_bytes: [u8; 32] = - D::digest(message).as_slice().try_into().map_err(|_| "digest should be 256 bits")?; - - Ok(output_bytes) -} - -fn verify_secp256k1_ecdsa_signature( - hashed_msg: &[u8], - public_key_x_bytes: &[u8; 32], - public_key_y_bytes: &[u8; 32], - signature: &[u8; 64], -) -> bool { - use k256::elliptic_curve::sec1::FromEncodedPoint; - use k256::elliptic_curve::PrimeField; - - use k256::{ecdsa::Signature, Scalar}; - use k256::{ - elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, - IsHigh, - }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, - }; - // Convert the inputs into k256 data structures - - let Ok(signature) = Signature::try_from(signature.as_slice()) else { - // Signature `r` and `s` are forbidden from being zero. - return false; - }; - - let point = EncodedPoint::from_affine_coordinates( - public_key_x_bytes.into(), - public_key_y_bytes.into(), - true, - ); - - let pubkey = PublicKey::from_encoded_point(&point); - let pubkey = if pubkey.is_some().into() { - pubkey.unwrap() - } else { - // Public key must sit on the Secp256k1 curve. - return false; - }; - - // Note: This is incorrect as it will panic if `hashed_msg >= k256::Secp256k1::ORDER`. - // In this scenario we should just take the leftmost bits from `hashed_msg` up to the group order length. - let z = Scalar::from_repr(*GenericArray::from_slice(hashed_msg)).unwrap(); - - // Finished converting bytes into data structures - - let r = signature.r(); - let s = signature.s(); - - // Ensure signature is "low S" normalized ala BIP 0062 - if s.is_high().into() { - return false; - } - - let s_inv = s.invert().unwrap(); - let u1 = z * s_inv; - let u2 = *r * s_inv; - - #[allow(non_snake_case)] - let R: AffinePoint = ((ProjectivePoint::GENERATOR * u1) - + (ProjectivePoint::from(*pubkey.as_affine()) * u2)) - .to_affine(); - - match R.to_encoded_point(false).coordinates() { - Coordinates::Uncompressed { x, y: _ } => Scalar::from_repr(*x).unwrap().eq(&r), - _ => unreachable!("Point is uncompressed"), - } -} - -fn verify_secp256r1_ecdsa_signature( - hashed_msg: &[u8], - public_key_x_bytes: &[u8; 32], - public_key_y_bytes: &[u8; 32], - signature: &[u8; 64], -) -> bool { - use p256::elliptic_curve::sec1::FromEncodedPoint; - use p256::elliptic_curve::PrimeField; - - use p256::{ecdsa::Signature, Scalar}; - use p256::{ - elliptic_curve::{ - sec1::{Coordinates, ToEncodedPoint}, - IsHigh, - }, - AffinePoint, EncodedPoint, ProjectivePoint, PublicKey, - }; - - // Convert the inputs into k256 data structures - - let Ok(signature) = Signature::try_from(signature.as_slice()) else { - // Signature `r` and `s` are forbidden from being zero. - return false; - }; - - let point = EncodedPoint::from_affine_coordinates( - public_key_x_bytes.into(), - public_key_y_bytes.into(), - true, - ); - - let pubkey = PublicKey::from_encoded_point(&point); - let pubkey = if pubkey.is_some().into() { - pubkey.unwrap() - } else { - // Public key must sit on the Secp256r1 curve. - return false; - }; - - // Note: This is incorrect as it will panic if `hashed_msg >= p256::NistP256::ORDER`. - // In this scenario we should just take the leftmost bits from `hashed_msg` up to the group order length. - let z = Scalar::from_repr(*GenericArray::from_slice(hashed_msg)).unwrap(); - - // Finished converting bytes into data structures - - let r = signature.r(); - let s = signature.s(); - - // Ensure signature is "low S" normalized ala BIP 0062 - if s.is_high().into() { - return false; - } - - let s_inv = s.invert().unwrap(); - let u1 = z * s_inv; - let u2 = *r * s_inv; - - #[allow(non_snake_case)] - let R: AffinePoint = ((ProjectivePoint::GENERATOR * u1) - + (ProjectivePoint::from(*pubkey.as_affine()) * u2)) - .to_affine(); - - match R.to_encoded_point(false).coordinates() { - Coordinates::Uncompressed { x, y: _ } => Scalar::from_repr(*x).unwrap().eq(&r), - _ => unreachable!("Point is uncompressed"), - } -} - -#[cfg(test)] -mod keccakf1600_tests { - use crate::keccakf1600; - - #[test] - fn sanity_check() { - // Test vectors are copied from XKCP (eXtended Keccak Code Package) - // https://github.com/XKCP/XKCP/blob/master/tests/TestVectors/KeccakF-1600-IntermediateValues.txt - let zero_state = [0u64; 25]; - - let expected_state_first = [ - 0xF1258F7940E1DDE7, - 0x84D5CCF933C0478A, - 0xD598261EA65AA9EE, - 0xBD1547306F80494D, - 0x8B284E056253D057, - 0xFF97A42D7F8E6FD4, - 0x90FEE5A0A44647C4, - 0x8C5BDA0CD6192E76, - 0xAD30A6F71B19059C, - 0x30935AB7D08FFC64, - 0xEB5AA93F2317D635, - 0xA9A6E6260D712103, - 0x81A57C16DBCF555F, - 0x43B831CD0347C826, - 0x01F22F1A11A5569F, - 0x05E5635A21D9AE61, - 0x64BEFEF28CC970F2, - 0x613670957BC46611, - 0xB87C5A554FD00ECB, - 0x8C3EE88A1CCF32C8, - 0x940C7922AE3A2614, - 0x1841F924A2C509E4, - 0x16F53526E70465C2, - 0x75F644E97F30A13B, - 0xEAF1FF7B5CECA249, - ]; - let expected_state_second = [ - 0x2D5C954DF96ECB3C, - 0x6A332CD07057B56D, - 0x093D8D1270D76B6C, - 0x8A20D9B25569D094, - 0x4F9C4F99E5E7F156, - 0xF957B9A2DA65FB38, - 0x85773DAE1275AF0D, - 0xFAF4F247C3D810F7, - 0x1F1B9EE6F79A8759, - 0xE4FECC0FEE98B425, - 0x68CE61B6B9CE68A1, - 0xDEEA66C4BA8F974F, - 0x33C43D836EAFB1F5, - 0xE00654042719DBD9, - 0x7CF8A9F009831265, - 0xFD5449A6BF174743, - 0x97DDAD33D8994B40, - 0x48EAD5FC5D0BE774, - 0xE3B8C8EE55B7B03C, - 0x91A0226E649E42E9, - 0x900E3129E7BADD7B, - 0x202A9EC5FAA3CCE8, - 0x5B3402464E1C3DB6, - 0x609F4E62A44C1059, - 0x20D06CD26A8FBF5C, - ]; - - let state_first = keccakf1600(zero_state).unwrap(); - let state_second = keccakf1600(state_first).unwrap(); - - assert_eq!(state_first, expected_state_first); - assert_eq!(state_second, expected_state_second); - } -} - -#[cfg(test)] -mod secp256k1_tests { - use super::verify_secp256k1_ecdsa_signature; - - // 0x3a73f4123a5cd2121f21cd7e8d358835476949d035d9c2da6806b4633ac8c1e2, - const HASHED_MESSAGE: [u8; 32] = [ - 0x3a, 0x73, 0xf4, 0x12, 0x3a, 0x5c, 0xd2, 0x12, 0x1f, 0x21, 0xcd, 0x7e, 0x8d, 0x35, 0x88, - 0x35, 0x47, 0x69, 0x49, 0xd0, 0x35, 0xd9, 0xc2, 0xda, 0x68, 0x06, 0xb4, 0x63, 0x3a, 0xc8, - 0xc1, 0xe2, - ]; - // 0xa0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7 - const PUB_KEY_X: [u8; 32] = [ - 0xa0, 0x43, 0x4d, 0x9e, 0x47, 0xf3, 0xc8, 0x62, 0x35, 0x47, 0x7c, 0x7b, 0x1a, 0xe6, 0xae, - 0x5d, 0x34, 0x42, 0xd4, 0x9b, 0x19, 0x43, 0xc2, 0xb7, 0x52, 0xa6, 0x8e, 0x2a, 0x47, 0xe2, - 0x47, 0xc7, - ]; - // 0x893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7 - const PUB_KEY_Y: [u8; 32] = [ - 0x89, 0x3a, 0xba, 0x42, 0x54, 0x19, 0xbc, 0x27, 0xa3, 0xb6, 0xc7, 0xe6, 0x93, 0xa2, 0x4c, - 0x69, 0x6f, 0x79, 0x4c, 0x2e, 0xd8, 0x77, 0xa1, 0x59, 0x3c, 0xbe, 0xe5, 0x3b, 0x03, 0x73, - 0x68, 0xd7, - ]; - // 0xe5081c80ab427dc370346f4a0e31aa2bad8d9798c38061db9ae55a4e8df454fd28119894344e71b78770cc931d61f480ecbb0b89d6eb69690161e49a715fcd55 - const SIGNATURE: [u8; 64] = [ - 0xe5, 0x08, 0x1c, 0x80, 0xab, 0x42, 0x7d, 0xc3, 0x70, 0x34, 0x6f, 0x4a, 0x0e, 0x31, 0xaa, - 0x2b, 0xad, 0x8d, 0x97, 0x98, 0xc3, 0x80, 0x61, 0xdb, 0x9a, 0xe5, 0x5a, 0x4e, 0x8d, 0xf4, - 0x54, 0xfd, 0x28, 0x11, 0x98, 0x94, 0x34, 0x4e, 0x71, 0xb7, 0x87, 0x70, 0xcc, 0x93, 0x1d, - 0x61, 0xf4, 0x80, 0xec, 0xbb, 0x0b, 0x89, 0xd6, 0xeb, 0x69, 0x69, 0x01, 0x61, 0xe4, 0x9a, - 0x71, 0x5f, 0xcd, 0x55, - ]; - - #[test] - fn verifies_valid_signature_with_low_s_value() { - let valid = - verify_secp256k1_ecdsa_signature(&HASHED_MESSAGE, &PUB_KEY_X, &PUB_KEY_Y, &SIGNATURE); - - assert!(valid); - } - - #[test] - fn rejects_invalid_signature() { - // This signature is invalid as ECDSA specifies that `r` and `s` must be non-zero. - let invalid_signature: [u8; 64] = [0x00; 64]; - - let valid = verify_secp256k1_ecdsa_signature( - &HASHED_MESSAGE, - &PUB_KEY_X, - &PUB_KEY_Y, - &invalid_signature, - ); - assert!(!valid); - } - - #[test] - fn rejects_invalid_public_key() { - let invalid_pub_key_x: [u8; 32] = [0xff; 32]; - let invalid_pub_key_y: [u8; 32] = [0xff; 32]; - - let valid = verify_secp256k1_ecdsa_signature( - &HASHED_MESSAGE, - &invalid_pub_key_x, - &invalid_pub_key_y, - &SIGNATURE, - ); - - assert!(!valid); - } - - #[test] - #[ignore = "ECDSA verification does not currently handle long hashes correctly"] - fn trims_overly_long_hashes_to_correct_length() { - let mut long_hashed_message = HASHED_MESSAGE.to_vec(); - long_hashed_message.push(0xff); - - let valid = verify_secp256k1_ecdsa_signature( - &long_hashed_message, - &PUB_KEY_X, - &PUB_KEY_Y, - &SIGNATURE, - ); - - assert!(valid); - } -} - -#[cfg(test)] -mod secp256r1_tests { - use super::verify_secp256r1_ecdsa_signature; - - // 0x54705ba3baafdbdfba8c5f9a70f7a89bee98d906b53e31074da7baecdc0da9ad - const HASHED_MESSAGE: [u8; 32] = [ - 84, 112, 91, 163, 186, 175, 219, 223, 186, 140, 95, 154, 112, 247, 168, 155, 238, 152, 217, - 6, 181, 62, 49, 7, 77, 167, 186, 236, 220, 13, 169, 173, - ]; - // 0x550f471003f3df97c3df506ac797f6721fb1a1fb7b8f6f83d224498a65c88e24 - const PUB_KEY_X: [u8; 32] = [ - 85, 15, 71, 16, 3, 243, 223, 151, 195, 223, 80, 106, 199, 151, 246, 114, 31, 177, 161, 251, - 123, 143, 111, 131, 210, 36, 73, 138, 101, 200, 142, 36, - ]; - // 0x136093d7012e509a73715cbd0b00a3cc0ff4b5c01b3ffa196ab1fb327036b8e6 - const PUB_KEY_Y: [u8; 32] = [ - 19, 96, 147, 215, 1, 46, 80, 154, 115, 113, 92, 189, 11, 0, 163, 204, 15, 244, 181, 192, - 27, 63, 250, 25, 106, 177, 251, 50, 112, 54, 184, 230, - ]; - // 0x2c70a8d084b62bfc5ce03641caf9f72ad4da8c81bfe6ec9487bb5e1bef62a13218ad9ee29eaf351fdc50f1520c425e9b908a07278b43b0ec7b872778c14e0784 - const SIGNATURE: [u8; 64] = [ - 44, 112, 168, 208, 132, 182, 43, 252, 92, 224, 54, 65, 202, 249, 247, 42, 212, 218, 140, - 129, 191, 230, 236, 148, 135, 187, 94, 27, 239, 98, 161, 50, 24, 173, 158, 226, 158, 175, - 53, 31, 220, 80, 241, 82, 12, 66, 94, 155, 144, 138, 7, 39, 139, 67, 176, 236, 123, 135, - 39, 120, 193, 78, 7, 132, - ]; - - #[test] - fn verifies_valid_signature_with_low_s_value() { - let valid = - verify_secp256r1_ecdsa_signature(&HASHED_MESSAGE, &PUB_KEY_X, &PUB_KEY_Y, &SIGNATURE); - - assert!(valid); - } - - #[test] - fn rejects_invalid_signature() { - // This signature is invalid as ECDSA specifies that `r` and `s` must be non-zero. - let invalid_signature: [u8; 64] = [0x00; 64]; - - let valid = verify_secp256r1_ecdsa_signature( - &HASHED_MESSAGE, - &PUB_KEY_X, - &PUB_KEY_Y, - &invalid_signature, - ); - assert!(!valid); - } - - #[test] - fn rejects_invalid_public_key() { - let invalid_pub_key_x: [u8; 32] = [0xff; 32]; - let invalid_pub_key_y: [u8; 32] = [0xff; 32]; - - let valid = verify_secp256r1_ecdsa_signature( - &HASHED_MESSAGE, - &invalid_pub_key_x, - &invalid_pub_key_y, - &SIGNATURE, - ); - - assert!(!valid); - } - - #[test] - #[ignore = "ECDSA verification does not currently handle long hashes correctly"] - fn trims_overly_long_hashes_to_correct_length() { - let mut long_hashed_message = HASHED_MESSAGE.to_vec(); - long_hashed_message.push(0xff); - - let valid = verify_secp256r1_ecdsa_signature( - &long_hashed_message, - &PUB_KEY_X, - &PUB_KEY_Y, - &SIGNATURE, - ); - - assert!(valid); - } -} From e24d3fc5a084610d9511e3c5421275cb9c84a548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Tue, 12 Mar 2024 12:42:14 +0100 Subject: [PATCH 066/416] fix: Dynamic assert messages in brillig (#4531) # Description ## Problem\* Temporary regisers are not conserved when calling and returning from another function, only alive variables are conserved. The condition in an assert was computed before the call to the dynamic message handler so it could be overwritten by the callee. I just compute the condition after the potential call. ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_block.rs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index c04d8475f08..7330269fb4b 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -248,17 +248,6 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_binary(binary, dfg, result_var); } Instruction::Constrain(lhs, rhs, assert_message) => { - let condition = SingleAddrVariable { - address: self.brillig_context.allocate_register(), - bit_size: 1, - }; - - self.convert_ssa_binary( - &Binary { lhs: *lhs, rhs: *rhs, operator: BinaryOp::Eq }, - dfg, - condition, - ); - let assert_message = if let Some(error) = assert_message { match error.as_ref() { ConstrainError::Static(string) => Some(string.clone()), @@ -282,6 +271,17 @@ impl<'block> BrilligBlock<'block> { None }; + let condition = SingleAddrVariable { + address: self.brillig_context.allocate_register(), + bit_size: 1, + }; + + self.convert_ssa_binary( + &Binary { lhs: *lhs, rhs: *rhs, operator: BinaryOp::Eq }, + dfg, + condition, + ); + self.brillig_context.constrain_instruction(condition.address, assert_message); self.brillig_context.deallocate_register(condition.address); } From 604ee8f2d559d8ac8420c5a449904f2bd6ffe32e Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 12 Mar 2024 13:30:40 +0000 Subject: [PATCH 067/416] chore: generalise `FunctionVisibility` to `ItemVisibility` (#4495) # Description ## Problem\* Resolves ## Summary\* This pulls out a renaming from #4491. We have the concept of `FunctionVisibility` which determines whether a function can be called from other modules in the same crate/other crates. If we're expanding the same concept of visibility to items other than functions we need a generic name for this which doesn't mention functions. I've then renamed it to `ModuleVisibility` to reflect that it shows how this item is visible in other modules but I'm open to suggestions. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- aztec_macros/src/lib.rs | 17 +++++++++-------- compiler/noirc_frontend/src/ast/expression.rs | 6 +++--- compiler/noirc_frontend/src/ast/mod.rs | 4 ++-- .../src/hir/def_collector/dc_mod.rs | 2 +- .../src/hir/def_map/item_scope.rs | 13 ++++--------- .../src/hir/def_map/namespace.rs | 11 ++++++----- .../src/hir/resolution/resolver.rs | 18 ++++++++---------- compiler/noirc_frontend/src/lib.rs | 11 +++++------ compiler/noirc_frontend/src/node_interner.rs | 8 ++++---- .../src/parser/parser/function.rs | 12 ++++++------ .../noirc_frontend/src/parser/parser/traits.rs | 6 +++--- 11 files changed, 51 insertions(+), 57 deletions(-) diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index f9df3f10129..c21e26bdcad 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -10,18 +10,19 @@ use noirc_frontend::macros_api::parse_program; use noirc_frontend::macros_api::FieldElement; use noirc_frontend::macros_api::{ BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, - ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, FunctionVisibility, - HirContext, HirExpression, HirLiteral, HirStatement, Ident, IndexExpression, LetStatement, - Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, Param, Path, - PathKind, Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, Statement, - StatementKind, StructType, Type, TypeImpl, UnaryOp, UnresolvedType, UnresolvedTypeData, - Visibility, + ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, HirContext, HirExpression, + HirLiteral, HirStatement, Ident, IndexExpression, LetStatement, Literal, + MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, Param, Path, PathKind, + Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, Statement, StatementKind, + StructType, Type, TypeImpl, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, }; use noirc_frontend::macros_api::{CrateId, FileId}; use noirc_frontend::macros_api::{MacroError, MacroProcessor}; use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; use noirc_frontend::node_interner::{FuncId, TraitId, TraitImplId, TraitImplKind}; -use noirc_frontend::{BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, Lambda}; +use noirc_frontend::{ + BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, ItemVisibility, Lambda, +}; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -1100,7 +1101,7 @@ fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { &return_type, ); - selector_fn_def.visibility = FunctionVisibility::Public; + selector_fn_def.visibility = ItemVisibility::Public; // Seems to be necessary on contract modules selector_fn_def.return_visibility = Visibility::Public; diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 2a252633a29..def1b082890 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use crate::token::{Attributes, Token}; use crate::{ - Distinctness, FunctionVisibility, Ident, Path, Pattern, Recoverable, Statement, StatementKind, + Distinctness, Ident, ItemVisibility, Path, Pattern, Recoverable, Statement, StatementKind, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, Visibility, }; use acvm::FieldElement; @@ -378,7 +378,7 @@ pub struct FunctionDefinition { pub is_unconstrained: bool, /// Indicate if this function was defined with the 'pub' keyword - pub visibility: FunctionVisibility, + pub visibility: ItemVisibility, pub generics: UnresolvedGenerics, pub parameters: Vec, @@ -677,7 +677,7 @@ impl FunctionDefinition { is_open: false, is_internal: false, is_unconstrained: false, - visibility: FunctionVisibility::Private, + visibility: ItemVisibility::Private, generics: generics.clone(), parameters: p, body: body.clone(), diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 29edbaca594..8a420c32fb8 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -354,8 +354,8 @@ impl UnresolvedTypeExpression { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -/// Represents whether the function can be called outside its module/crate -pub enum FunctionVisibility { +/// Represents whether the definition can be referenced outside its module/crate +pub enum ItemVisibility { Public, Private, PublicCrate, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 5b2f815d636..6ac263d80be 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -394,7 +394,7 @@ impl<'a> ModCollector<'a> { let modifiers = FunctionModifiers { name: name.to_string(), - visibility: crate::FunctionVisibility::Public, + visibility: crate::ItemVisibility::Public, // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 attributes: crate::token::Attributes::empty(), is_unconstrained: false, diff --git a/compiler/noirc_frontend/src/hir/def_map/item_scope.rs b/compiler/noirc_frontend/src/hir/def_map/item_scope.rs index 523def89518..178b91e1e84 100644 --- a/compiler/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/compiler/noirc_frontend/src/hir/def_map/item_scope.rs @@ -1,16 +1,11 @@ use super::{namespace::PerNs, ModuleDefId, ModuleId}; use crate::{ node_interner::{FuncId, TraitId}, - Ident, + Ident, ItemVisibility, }; use std::collections::{hash_map::Entry, HashMap}; -type Scope = HashMap, (ModuleDefId, Visibility, bool /*is_prelude*/)>; - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum Visibility { - Public, -} +type Scope = HashMap, (ModuleDefId, ItemVisibility, bool /*is_prelude*/)>; #[derive(Default, Debug, PartialEq, Eq)] pub struct ItemScope { @@ -55,12 +50,12 @@ impl ItemScope { Err((old_ident.clone(), name)) } } else { - trait_hashmap.insert(trait_id, (mod_def, Visibility::Public, is_prelude)); + trait_hashmap.insert(trait_id, (mod_def, ItemVisibility::Public, is_prelude)); Ok(()) } } else { let mut trait_hashmap = HashMap::new(); - trait_hashmap.insert(trait_id, (mod_def, Visibility::Public, is_prelude)); + trait_hashmap.insert(trait_id, (mod_def, ItemVisibility::Public, is_prelude)); map.insert(name, trait_hashmap); Ok(()) } diff --git a/compiler/noirc_frontend/src/hir/def_map/namespace.rs b/compiler/noirc_frontend/src/hir/def_map/namespace.rs index ca14d9f8617..5e349f46e14 100644 --- a/compiler/noirc_frontend/src/hir/def_map/namespace.rs +++ b/compiler/noirc_frontend/src/hir/def_map/namespace.rs @@ -1,15 +1,16 @@ -use super::{item_scope::Visibility, ModuleDefId}; +use super::ModuleDefId; +use crate::ItemVisibility; // This works exactly the same as in r-a, just simplified #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct PerNs { - pub types: Option<(ModuleDefId, Visibility, bool)>, - pub values: Option<(ModuleDefId, Visibility, bool)>, + pub types: Option<(ModuleDefId, ItemVisibility, bool)>, + pub values: Option<(ModuleDefId, ItemVisibility, bool)>, } impl PerNs { pub fn types(t: ModuleDefId) -> PerNs { - PerNs { types: Some((t, Visibility::Public, false)), values: None } + PerNs { types: Some((t, ItemVisibility::Public, false)), values: None } } pub fn take_types(self) -> Option { @@ -24,7 +25,7 @@ impl PerNs { self.types.map(|it| it.0).into_iter().chain(self.values.map(|it| it.0)) } - pub fn iter_items(self) -> impl Iterator { + pub fn iter_items(self) -> impl Iterator { self.types.into_iter().chain(self.values) } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 875d0ceb85e..567cc20f789 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -38,8 +38,8 @@ use crate::{ }; use crate::{ ArrayLiteral, ContractFunctionType, Distinctness, ForRange, FunctionDefinition, - FunctionReturnType, FunctionVisibility, Generics, LValue, NoirStruct, NoirTypeAlias, Param, - Path, PathKind, Pattern, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, + FunctionReturnType, Generics, ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, + PathKind, Pattern, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, }; @@ -237,7 +237,7 @@ impl<'a> Resolver<'a> { is_open: false, is_internal: false, is_unconstrained: false, - visibility: FunctionVisibility::Public, // Trait functions are always public + visibility: ItemVisibility::Public, // Trait functions are always public generics: generics.clone(), parameters: vecmap(parameters, |(name, typ)| Param { visibility: Visibility::Private, @@ -1320,7 +1320,7 @@ impl<'a> Resolver<'a> { &mut self, func: FuncId, span: Span, - visibility: FunctionVisibility, + visibility: ItemVisibility, ) { let function_module = self.interner.function_module(func); let current_module = self.path_resolver.module_id(); @@ -1330,8 +1330,8 @@ impl<'a> Resolver<'a> { let current_module = current_module.local_id; let name = self.interner.function_name(&func).to_string(); match visibility { - FunctionVisibility::Public => (), - FunctionVisibility::Private => { + ItemVisibility::Public => (), + ItemVisibility::Private => { if !same_crate || !self.module_descendent_of_target( krate, @@ -1342,7 +1342,7 @@ impl<'a> Resolver<'a> { self.errors.push(ResolverError::PrivateFunctionCalled { span, name }); } } - FunctionVisibility::PublicCrate => { + ItemVisibility::PublicCrate => { if !same_crate { self.errors.push(ResolverError::NonCrateFunctionCalled { span, name }); } @@ -1451,9 +1451,7 @@ impl<'a> Resolver<'a> { self.interner.add_function_dependency(current_item, id); } - if self.interner.function_visibility(id) - != FunctionVisibility::Public - { + if self.interner.function_visibility(id) != ItemVisibility::Public { let span = hir_ident.location.span; self.check_can_reference_function( id, diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index be007929fc4..800d66b0dfb 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -56,14 +56,13 @@ pub mod macros_api { pub use crate::hir::def_map::ModuleDefId; pub use crate::{ hir::Context as HirContext, BlockExpression, CallExpression, CastExpression, Distinctness, - Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, LetStatement, - Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, Path, PathKind, - Pattern, Statement, UnresolvedType, UnresolvedTypeData, Visibility, + Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, ItemVisibility, + LetStatement, Literal, MemberAccessExpression, MethodCallExpression, NoirFunction, Path, + PathKind, Pattern, Statement, UnresolvedType, UnresolvedTypeData, Visibility, }; pub use crate::{ - ForLoopStatement, ForRange, FunctionDefinition, FunctionVisibility, ImportStatement, - NoirStruct, Param, PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, - UnaryOp, + ForLoopStatement, ForRange, FunctionDefinition, ImportStatement, NoirStruct, Param, + PrefixExpression, Signedness, StatementKind, StructType, Type, TypeImpl, UnaryOp, }; /// Methods to process the AST before and after type checking diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 5de43e59254..dc632527898 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -28,7 +28,7 @@ use crate::hir_def::{ }; use crate::token::{Attributes, SecondaryAttribute}; use crate::{ - BinaryOpKind, ContractFunctionType, FunctionDefinition, FunctionVisibility, Generics, Shared, + BinaryOpKind, ContractFunctionType, FunctionDefinition, Generics, ItemVisibility, Shared, TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, }; @@ -236,7 +236,7 @@ pub struct FunctionModifiers { pub name: String, /// Whether the function is `pub` or not. - pub visibility: FunctionVisibility, + pub visibility: ItemVisibility, pub attributes: Attributes, @@ -259,7 +259,7 @@ impl FunctionModifiers { pub fn new() -> Self { Self { name: String::new(), - visibility: FunctionVisibility::Public, + visibility: ItemVisibility::Public, attributes: Attributes::empty(), is_unconstrained: false, is_internal: None, @@ -799,7 +799,7 @@ impl NodeInterner { /// /// The underlying function_visibilities map is populated during def collection, /// so this function can be called anytime afterward. - pub fn function_visibility(&self, func: FuncId) -> FunctionVisibility { + pub fn function_visibility(&self, func: FuncId) -> ItemVisibility { self.function_modifiers[&func].visibility } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 7448d84cfe1..a2a4577a993 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -8,7 +8,7 @@ use crate::parser::labels::ParsingRuleLabel; use crate::parser::spanned; use crate::token::{Keyword, Token}; use crate::{ - Distinctness, FunctionDefinition, FunctionReturnType, FunctionVisibility, Ident, NoirFunction, + Distinctness, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, NoirFunction, Param, Visibility, }; @@ -53,16 +53,16 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser { +fn visibility_modifier() -> impl NoirParser { let is_pub_crate = (keyword(Keyword::Pub) .then_ignore(just(Token::LeftParen)) .then_ignore(keyword(Keyword::Crate)) .then_ignore(just(Token::RightParen))) - .map(|_| FunctionVisibility::PublicCrate); + .map(|_| ItemVisibility::PublicCrate); - let is_pub = keyword(Keyword::Pub).map(|_| FunctionVisibility::Public); + let is_pub = keyword(Keyword::Pub).map(|_| ItemVisibility::Public); - let is_private = empty().map(|_| FunctionVisibility::Private); + let is_private = empty().map(|_| ItemVisibility::Private); choice((is_pub_crate, is_pub, is_private)) } @@ -70,7 +70,7 @@ fn visibility_modifier() -> impl NoirParser { /// function_modifiers: 'unconstrained'? (visibility)? 'open'? /// /// returns (is_unconstrained, visibility, is_open) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, FunctionVisibility, bool)> { +fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { keyword(Keyword::Unconstrained) .or_not() .then(visibility_modifier()) diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index 0d72fbd5303..df8d50178d5 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -11,7 +11,7 @@ use crate::{ ParserErrorReason, TopLevelStatement, }, token::{Keyword, Token}, - Expression, FunctionVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, + Expression, ItemVisibility, NoirTrait, NoirTraitImpl, TraitBound, TraitImplItem, TraitItem, UnresolvedTraitConstraint, UnresolvedType, }; @@ -123,12 +123,12 @@ fn trait_implementation_body() -> impl NoirParser> { if f.def().is_internal || f.def().is_unconstrained || f.def().is_open - || f.def().visibility != FunctionVisibility::Private + || f.def().visibility != ItemVisibility::Private { emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); } // Trait impl functions are always public - f.def_mut().visibility = FunctionVisibility::Public; + f.def_mut().visibility = ItemVisibility::Public; TraitImplItem::Function(f) }); From 00d6494ae70b10e1872d96fb4e57ecb0b5f01787 Mon Sep 17 00:00:00 2001 From: jfecher Date: Tue, 12 Mar 2024 17:41:33 -0500 Subject: [PATCH 068/416] fix: Allow non-integer globals to reference struct methods (#4490) # Description ## Problem\* Resolves #1440 ## Summary\* This was a fairly simple change - non-integer globals used to be resolved before struct methods were even collected. I've moved this to after methods are collected. Now global resolution is the first step of resolving in general. Integer globals are still resolved early since structs may refer to them in numeric generics. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/def_collector/dc_crate.rs | 10 +++++----- .../execution_success/global_consts/src/main.nr | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 62febc89899..a5dfc738824 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -327,11 +327,6 @@ impl DefCollector { // Must resolve structs before we resolve globals. errors.extend(resolve_structs(context, def_collector.collected_types, crate_id)); - // We must wait to resolve non-integer globals until after we resolve structs since struct - // globals will need to reference the struct type they're initialized to to ensure they are valid. - resolved_globals.extend(resolve_globals(context, other_globals, crate_id)); - errors.extend(resolved_globals.errors); - // Bind trait impls to their trait. Collect trait functions, that have a // default implementation, which hasn't been overridden. errors.extend(collect_trait_impls( @@ -349,6 +344,11 @@ impl DefCollector { // over trait methods if there are name conflicts. errors.extend(collect_impls(context, crate_id, &def_collector.collected_impls)); + // We must wait to resolve non-integer globals until after we resolve structs since struct + // globals will need to reference the struct type they're initialized to to ensure they are valid. + resolved_globals.extend(resolve_globals(context, other_globals, crate_id)); + errors.extend(resolved_globals.errors); + // Resolve each function in the crate. This is now possible since imports have been resolved let mut functions = Vec::new(); functions.extend(resolve_free_functions( diff --git a/test_programs/execution_success/global_consts/src/main.nr b/test_programs/execution_success/global_consts/src/main.nr index 3c8ecc67a0c..52ffe3e823b 100644 --- a/test_programs/execution_success/global_consts/src/main.nr +++ b/test_programs/execution_success/global_consts/src/main.nr @@ -107,3 +107,20 @@ mod my_submodule { x } } + +struct Foo { + a: Field, +} + +struct Bar {} + +impl Bar { + fn get_a() -> Field { + 1 + } +} + +// Regression for #1440 +global foo = Foo { + a: Bar::get_a(), +}; From f3243b763c0b15ae90beb8e35630df27f3d314c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Wed, 13 Mar 2024 12:53:35 +0100 Subject: [PATCH 069/416] feat: Add checks for bit size consistency on brillig gen (#4542) # Description ## Problem\* Adds checks for brillig codegen for #4369 (Previously we only had consistency checks in SSA). ## Summary\* This checks have caught some inconsistencies in the process, including those that were caught manually in https://github.com/AztecProtocol/aztec-packages/pull/5091 ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_block.rs | 209 ++++++------- .../brillig_gen/brillig_block_variables.rs | 19 +- .../src/brillig/brillig_gen/brillig_fn.rs | 14 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 88 ++++-- .../noirc_evaluator/src/brillig/brillig_ir.rs | 285 +++++++++++------- .../brillig/brillig_ir/brillig_variable.rs | 12 + .../src/brillig/brillig_ir/debug_show.rs | 22 +- .../src/brillig/brillig_ir/entry_point.rs | 22 +- .../src/ssa/function_builder/data_bus.rs | 2 +- 9 files changed, 368 insertions(+), 305 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 7330269fb4b..530b6dc69fc 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1,7 +1,9 @@ use crate::brillig::brillig_ir::brillig_variable::{ type_to_heap_value_type, BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable, }; -use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; +use crate::brillig::brillig_ir::{ + BrilligBinaryOp, BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, +}; use crate::ssa::ir::dfg::CallStack; use crate::ssa::ir::instruction::ConstrainError; use crate::ssa::ir::{ @@ -23,7 +25,7 @@ use num_bigint::BigUint; use super::brillig_black_box::convert_black_box_call; use super::brillig_block_variables::BlockVariables; -use super::brillig_fn::{get_bit_size_from_ssa_type, FunctionContext}; +use super::brillig_fn::FunctionContext; /// Generate the compilation artifacts for compiling a function into brillig bytecode. pub(crate) struct BrilligBlock<'block> { @@ -282,8 +284,8 @@ impl<'block> BrilligBlock<'block> { condition, ); - self.brillig_context.constrain_instruction(condition.address, assert_message); - self.brillig_context.deallocate_register(condition.address); + self.brillig_context.constrain_instruction(condition, assert_message); + self.brillig_context.deallocate_single_addr(condition); } Instruction::Allocate => { let result_value = dfg.instruction_results(instruction_id)[0]; @@ -496,10 +498,10 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.mov_instruction(target_len.address, limb_count.address); self.brillig_context.radix_instruction( - source.address, + source, target_vector, - radix.address, - limb_count.address, + radix, + limb_count, matches!(endianness, Endian::Big), ); } @@ -530,22 +532,20 @@ impl<'block> BrilligBlock<'block> { BrilligVariable::SingleAddr(..) => unreachable!("ICE: ToBits on non-array"), }; - let radix = self - .brillig_context - .make_constant(2_usize.into(), FieldElement::max_num_bits()); + let radix = self.brillig_context.make_constant(2_usize.into(), 32); // Update the user-facing slice length self.brillig_context.mov_instruction(target_len.address, limb_count.address); self.brillig_context.radix_instruction( - source.address, + source, target_vector, radix, - limb_count.address, + limb_count, matches!(endianness, Endian::Big), ); - self.brillig_context.deallocate_register(radix); + self.brillig_context.deallocate_single_addr(radix); } _ => { unreachable!("unsupported function call type {:?}", dfg[*func]) @@ -597,7 +597,7 @@ impl<'block> BrilligBlock<'block> { self.validate_array_index(array_variable, index_variable); self.retrieve_variable_from_array( array_pointer, - index_variable.address, + index_variable, destination_variable, ); } @@ -641,22 +641,20 @@ impl<'block> BrilligBlock<'block> { ); // Check if lte max - let brillig_binary_op = BrilligBinaryOp::Integer { - op: BinaryIntOp::LessThanEquals, - bit_size: FieldElement::max_num_bits(), - }; - let condition = self.brillig_context.allocate_register(); + let brillig_binary_op = BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals); + let condition = + SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); self.brillig_context.binary_instruction( - left.address, + left, right, condition, brillig_binary_op, ); self.brillig_context.constrain_instruction(condition, assert_message.clone()); - self.brillig_context.deallocate_register(condition); - self.brillig_context.deallocate_register(left.address); - self.brillig_context.deallocate_register(right); + self.brillig_context.deallocate_single_addr(condition); + self.brillig_context.deallocate_single_addr(left); + self.brillig_context.deallocate_single_addr(right); } } Instruction::IncrementRc { value } => { @@ -751,16 +749,18 @@ impl<'block> BrilligBlock<'block> { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { (self.brillig_context.make_usize_constant(size.into()), true) } - BrilligVariable::BrilligVector(BrilligVector { size, .. }) => (size, false), + BrilligVariable::BrilligVector(BrilligVector { size, .. }) => { + (SingleAddrVariable::new_usize(size), false) + } _ => unreachable!("ICE: validate array index on non-array"), }; - let condition = self.brillig_context.allocate_register(); + let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); self.brillig_context.memory_op( index_register.address, - size_as_register, - condition, + size_as_register.address, + condition.address, BinaryIntOp::LessThan, ); @@ -768,28 +768,28 @@ impl<'block> BrilligBlock<'block> { .constrain_instruction(condition, Some("Array index out of bounds".to_owned())); if should_deallocate_size { - self.brillig_context.deallocate_register(size_as_register); + self.brillig_context.deallocate_single_addr(size_as_register); } - self.brillig_context.deallocate_register(condition); + self.brillig_context.deallocate_single_addr(condition); } pub(crate) fn retrieve_variable_from_array( &mut self, array_pointer: MemoryAddress, - index_register: MemoryAddress, + index_var: SingleAddrVariable, destination_variable: BrilligVariable, ) { match destination_variable { BrilligVariable::SingleAddr(destination_register) => { self.brillig_context.array_get( array_pointer, - index_register, + index_var, destination_register.address, ); } BrilligVariable::BrilligArray(..) | BrilligVariable::BrilligVector(..) => { let reference = self.brillig_context.allocate_register(); - self.brillig_context.array_get(array_pointer, index_register, reference); + self.brillig_context.array_get(array_pointer, index_var, reference); self.brillig_context.load_variable_instruction(destination_variable, reference); self.brillig_context.deallocate_register(reference); } @@ -831,15 +831,9 @@ impl<'block> BrilligBlock<'block> { _ => unreachable!("ICE: array set on non-array"), }; - let one = self.brillig_context.make_usize_constant(1_usize.into()); let condition = self.brillig_context.allocate_register(); - self.brillig_context.binary_instruction( - reference_count, - one, - condition, - BrilligBinaryOp::Field { op: BinaryFieldOp::Equals }, - ); + self.brillig_context.usize_op(reference_count, condition, BinaryIntOp::Equals, 1_usize); self.brillig_context.branch_instruction(condition, |ctx, cond| { if cond { @@ -852,7 +846,10 @@ impl<'block> BrilligBlock<'block> { ctx.copy_array_instruction( source_pointer, destination_pointer, - source_size_as_register, + SingleAddrVariable::new( + source_size_as_register, + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ), ); } }); @@ -873,17 +870,20 @@ impl<'block> BrilligBlock<'block> { } // Then set the value in the newly created array - self.store_variable_in_array(destination_pointer, index_register, value_variable); + self.store_variable_in_array( + destination_pointer, + SingleAddrVariable::new_usize(index_register), + value_variable, + ); self.brillig_context.deallocate_register(source_size_as_register); - self.brillig_context.deallocate_register(one); self.brillig_context.deallocate_register(condition); } pub(crate) fn store_variable_in_array_with_ctx( ctx: &mut BrilligContext, destination_pointer: MemoryAddress, - index_register: MemoryAddress, + index_register: SingleAddrVariable, value_variable: BrilligVariable, ) { match value_variable { @@ -910,13 +910,13 @@ impl<'block> BrilligBlock<'block> { pub(crate) fn store_variable_in_array( &mut self, destination_pointer: MemoryAddress, - index_register: MemoryAddress, + index_variable: SingleAddrVariable, value_variable: BrilligVariable, ) { Self::store_variable_in_array_with_ctx( self.brillig_context, destination_pointer, - index_register, + index_variable, value_variable, ); } @@ -1082,9 +1082,9 @@ impl<'block> BrilligBlock<'block> { let converted_index = self.brillig_context.make_usize_constant(element_size.into()); self.brillig_context.memory_op( - converted_index, + converted_index.address, user_index.address, - converted_index, + converted_index.address, BinaryIntOp::Mul, ); @@ -1095,7 +1095,7 @@ impl<'block> BrilligBlock<'block> { self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); self.slice_insert_operation(target_vector, source_vector, converted_index, &items); - self.brillig_context.deallocate_register(converted_index); + self.brillig_context.deallocate_single_addr(converted_index); } Value::Intrinsic(Intrinsic::SliceRemove) => { let target_len = match self.variables.define_variable( @@ -1124,9 +1124,9 @@ impl<'block> BrilligBlock<'block> { let converted_index = self.brillig_context.make_usize_constant(element_size.into()); self.brillig_context.memory_op( - converted_index, + converted_index.address, user_index.address, - converted_index, + converted_index.address, BinaryIntOp::Mul, ); @@ -1148,7 +1148,7 @@ impl<'block> BrilligBlock<'block> { &removed_items, ); - self.brillig_context.deallocate_register(converted_index); + self.brillig_context.deallocate_single_addr(converted_index); } _ => unreachable!("ICE: Slice operation not supported"), } @@ -1201,12 +1201,7 @@ impl<'block> BrilligBlock<'block> { let (brillig_binary_op, is_signed) = convert_ssa_binary_op_to_brillig_binary_op(binary.operator, &binary_type); - self.brillig_context.binary_instruction( - left.address, - right.address, - result_variable.address, - brillig_binary_op, - ); + self.brillig_context.binary_instruction(left, right, result_variable, brillig_binary_op); self.add_overflow_check(brillig_binary_op, left, right, result_variable, is_signed); } @@ -1219,77 +1214,83 @@ impl<'block> BrilligBlock<'block> { result: SingleAddrVariable, is_signed: bool, ) { - let (op, bit_size) = if let BrilligBinaryOp::Integer { op, bit_size } = binary_operation { - (op, bit_size) + let (op, bit_size) = if let BrilligBinaryOp::Integer(op) = binary_operation { + // Bit size is checked at compile time to be equal for left and right + (op, left.bit_size) } else { return; }; match (op, is_signed) { (BinaryIntOp::Add, false) => { - let condition = self.brillig_context.allocate_register(); + let condition = + SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); // Check that lhs <= result self.brillig_context.binary_instruction( - left.address, - result.address, + left, + result, condition, - BrilligBinaryOp::Integer { op: BinaryIntOp::LessThanEquals, bit_size }, + BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals), ); self.brillig_context.constrain_instruction( condition, Some("attempt to add with overflow".to_string()), ); - self.brillig_context.deallocate_register(condition); + self.brillig_context.deallocate_single_addr(condition); } (BinaryIntOp::Sub, false) => { - let condition = self.brillig_context.allocate_register(); + let condition = + SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); // Check that rhs <= lhs self.brillig_context.binary_instruction( - right.address, - left.address, + right, + left, condition, - BrilligBinaryOp::Integer { op: BinaryIntOp::LessThanEquals, bit_size }, + BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals), ); self.brillig_context.constrain_instruction( condition, Some("attempt to subtract with overflow".to_string()), ); - self.brillig_context.deallocate_register(condition); + self.brillig_context.deallocate_single_addr(condition); } (BinaryIntOp::Mul, false) => { // Multiplication overflow is only possible for bit sizes > 1 if bit_size > 1 { - let is_right_zero = self.brillig_context.allocate_register(); + let is_right_zero = + SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); let zero = self.brillig_context.make_constant(0_usize.into(), bit_size); self.brillig_context.binary_instruction( zero, - right.address, + right, is_right_zero, - BrilligBinaryOp::Integer { op: BinaryIntOp::Equals, bit_size }, + BrilligBinaryOp::Integer(BinaryIntOp::Equals), ); - self.brillig_context.if_not_instruction(is_right_zero, |ctx| { - let condition = ctx.allocate_register(); + self.brillig_context.if_not_instruction(is_right_zero.address, |ctx| { + let condition = SingleAddrVariable::new(ctx.allocate_register(), 1); + let division = SingleAddrVariable::new(ctx.allocate_register(), bit_size); // Check that result / rhs == lhs ctx.binary_instruction( - result.address, - right.address, - condition, - BrilligBinaryOp::Integer { op: BinaryIntOp::UnsignedDiv, bit_size }, + result, + right, + division, + BrilligBinaryOp::Integer(BinaryIntOp::UnsignedDiv), ); ctx.binary_instruction( + division, + left, condition, - left.address, - condition, - BrilligBinaryOp::Integer { op: BinaryIntOp::Equals, bit_size }, + BrilligBinaryOp::Integer(BinaryIntOp::Equals), ); ctx.constrain_instruction( condition, Some("attempt to multiply with overflow".to_string()), ); - ctx.deallocate_register(condition); + ctx.deallocate_single_addr(condition); + ctx.deallocate_single_addr(division); }); - self.brillig_context.deallocate_register(is_right_zero); - self.brillig_context.deallocate_register(zero); + self.brillig_context.deallocate_single_addr(is_right_zero); + self.brillig_context.deallocate_single_addr(zero); } } _ => {} @@ -1307,7 +1308,7 @@ impl<'block> BrilligBlock<'block> { // converted to registers so we fetch from the cache. self.variables.get_allocation(self.function_context, value_id, dfg) } - Value::NumericConstant { constant, typ } => { + Value::NumericConstant { constant, .. } => { // Constants might have been converted previously or not, so we get or create and // (re)initialize the value inside. if let Some(variable) = self.variables.get_constant(value_id, dfg) { @@ -1315,13 +1316,9 @@ impl<'block> BrilligBlock<'block> { } else { let new_variable = self.variables.allocate_constant(self.brillig_context, value_id, dfg); - let register_index = new_variable.extract_single_addr(); - self.brillig_context.const_instruction( - register_index.address, - (*constant).into(), - get_bit_size_from_ssa_type(typ), - ); + self.brillig_context + .const_instruction(new_variable.extract_single_addr(), (*constant).into()); new_variable } } @@ -1366,13 +1363,13 @@ impl<'block> BrilligBlock<'block> { self.store_variable_in_array(pointer, iterator_register, element_variable); // Increment the iterator self.brillig_context.usize_op_in_place( - iterator_register, + iterator_register.address, BinaryIntOp::Add, 1, ); } - self.brillig_context.deallocate_register(iterator_register); + self.brillig_context.deallocate_single_addr(iterator_register); new_variable } @@ -1384,12 +1381,10 @@ impl<'block> BrilligBlock<'block> { // value. let new_variable = self.variables.allocate_constant(self.brillig_context, value_id, dfg); - let register_index = new_variable.extract_single_addr(); self.brillig_context.const_instruction( - register_index.address, + new_variable.extract_single_addr(), value_id.to_usize().into(), - 32, ); new_variable } @@ -1538,18 +1533,18 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( fn binary_op_to_field_op(op: BinaryOp) -> BrilligBinaryOp { match op { - BinaryOp::Add => BrilligBinaryOp::Field { op: BinaryFieldOp::Add }, - BinaryOp::Sub => BrilligBinaryOp::Field { op: BinaryFieldOp::Sub }, - BinaryOp::Mul => BrilligBinaryOp::Field { op: BinaryFieldOp::Mul }, - BinaryOp::Div => BrilligBinaryOp::Field { op: BinaryFieldOp::Div }, - BinaryOp::Eq => BrilligBinaryOp::Field { op: BinaryFieldOp::Equals }, + BinaryOp::Add => BrilligBinaryOp::Field(BinaryFieldOp::Add), + BinaryOp::Sub => BrilligBinaryOp::Field(BinaryFieldOp::Sub), + BinaryOp::Mul => BrilligBinaryOp::Field(BinaryFieldOp::Mul), + BinaryOp::Div => BrilligBinaryOp::Field(BinaryFieldOp::Div), + BinaryOp::Eq => BrilligBinaryOp::Field(BinaryFieldOp::Equals), _ => unreachable!( "Field type cannot be used with {op}. This should have been caught by the frontend" ), } } - fn binary_op_to_int_op(op: BinaryOp, bit_size: u32, is_signed: bool) -> BrilligBinaryOp { + fn binary_op_to_int_op(op: BinaryOp, is_signed: bool) -> BrilligBinaryOp { let operation = match op { BinaryOp::Add => BinaryIntOp::Add, BinaryOp::Sub => BinaryIntOp::Sub, @@ -1561,9 +1556,7 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( BinaryIntOp::UnsignedDiv } } - BinaryOp::Mod => { - return BrilligBinaryOp::Modulo { is_signed_integer: is_signed, bit_size } - } + BinaryOp::Mod => return BrilligBinaryOp::Modulo { is_signed_integer: is_signed }, BinaryOp::Eq => BinaryIntOp::Equals, BinaryOp::Lt => BinaryIntOp::LessThan, BinaryOp::And => BinaryIntOp::And, @@ -1573,14 +1566,12 @@ pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( BinaryOp::Shr => BinaryIntOp::Shr, }; - BrilligBinaryOp::Integer { op: operation, bit_size } + BrilligBinaryOp::Integer(operation) } // If bit size is available then it is a binary integer operation match bit_size_signedness { - Some((bit_size, is_signed)) => { - (binary_op_to_int_op(ssa_op, *bit_size, is_signed), is_signed) - } + Some((_, is_signed)) => (binary_op_to_int_op(ssa_op, is_signed), is_signed), None => (binary_op_to_field_op(ssa_op), false), } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index f463bd4de4d..dc9900daee3 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -3,7 +3,7 @@ use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ brillig::brillig_ir::{ brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, - BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + BrilligContext, }, ssa::ir::{ basic_block::BasicBlockId, @@ -13,7 +13,7 @@ use crate::{ }, }; -use super::brillig_fn::FunctionContext; +use super::brillig_fn::{get_bit_size_from_ssa_type, FunctionContext}; #[derive(Debug, Default)] pub(crate) struct BlockVariables { @@ -189,21 +189,10 @@ pub(crate) fn allocate_value( let typ = dfg.type_of_value(value_id); match typ { - Type::Numeric(numeric_type) => BrilligVariable::SingleAddr(SingleAddrVariable { - address: brillig_context.allocate_register(), - bit_size: numeric_type.bit_size(), - }), - Type::Reference(_) => BrilligVariable::SingleAddr(SingleAddrVariable { - address: brillig_context.allocate_register(), - bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - }), - Type::Function => { - // NB. function references are converted to a constant when - // translating from SSA to Brillig (to allow for debugger - // instrumentation to work properly) + Type::Numeric(_) | Type::Reference(_) | Type::Function => { BrilligVariable::SingleAddr(SingleAddrVariable { address: brillig_context.allocate_register(), - bit_size: 32, + bit_size: get_bit_size_from_ssa_type(&typ), }) } Type::Array(item_typ, elem_count) => { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index b5da8296ba5..42765d10ce2 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -1,4 +1,3 @@ -use acvm::FieldElement; use iter_extended::vecmap; use crate::{ @@ -11,7 +10,7 @@ use crate::{ basic_block::BasicBlockId, function::{Function, FunctionId}, post_order::PostOrder, - types::{NumericType, Type}, + types::Type, value::ValueId, }, }; @@ -116,11 +115,12 @@ impl FunctionContext { pub(crate) fn get_bit_size_from_ssa_type(typ: &Type) -> u32 { match typ { - Type::Numeric(num_type) => match num_type { - NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => *bit_size, - NumericType::NativeField => FieldElement::max_num_bits(), - }, + Type::Numeric(num_type) => num_type.bit_size(), Type::Reference(_) => BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - _ => unreachable!("ICE bitwise not on a non numeric type"), + // NB. function references are converted to a constant when + // translating from SSA to Brillig (to allow for debugger + // instrumentation to work properly) + Type::Function => 32, + _ => unreachable!("ICE bit size not on a non numeric type"), } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 3fc0e981165..969f95cff20 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,6 +1,8 @@ -use acvm::brillig_vm::brillig::{BinaryIntOp, MemoryAddress}; +use acvm::brillig_vm::brillig::BinaryIntOp; -use crate::brillig::brillig_ir::brillig_variable::{BrilligVariable, BrilligVector}; +use crate::brillig::brillig_ir::brillig_variable::{ + BrilligVariable, BrilligVector, SingleAddrVariable, +}; use super::brillig_block::BrilligBlock; @@ -26,19 +28,19 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.copy_array_instruction( source_vector.pointer, target_vector.pointer, - source_vector.size, + SingleAddrVariable::new_usize(source_vector.size), ); for (index, variable) in variables_to_insert.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant(index.into()); self.brillig_context.memory_op( - target_index, + target_index.address, source_vector.size, - target_index, + target_index.address, BinaryIntOp::Add, ); self.store_variable_in_array(target_vector.pointer, target_index, *variable); - self.brillig_context.deallocate_register(target_index); + self.brillig_context.deallocate_single_addr(target_index); } } @@ -72,14 +74,14 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.copy_array_instruction( source_vector.pointer, destination_copy_pointer, - source_vector.size, + SingleAddrVariable::new_usize(source_vector.size), ); // Then we write the items to insert at the start for (index, variable) in variables_to_insert.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant(index.into()); self.store_variable_in_array(target_vector.pointer, target_index, *variable); - self.brillig_context.deallocate_register(target_index); + self.brillig_context.deallocate_single_addr(target_index); } self.brillig_context.deallocate_register(destination_copy_pointer); @@ -115,13 +117,13 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.copy_array_instruction( source_copy_pointer, target_vector.pointer, - target_vector.size, + SingleAddrVariable::new_usize(target_vector.size), ); for (index, variable) in removed_items.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant(index.into()); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); - self.brillig_context.deallocate_register(target_index); + self.brillig_context.deallocate_single_addr(target_index); } self.brillig_context.deallocate_register(source_copy_pointer); @@ -148,19 +150,19 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.copy_array_instruction( source_vector.pointer, target_vector.pointer, - target_vector.size, + SingleAddrVariable::new_usize(target_vector.size), ); for (index, variable) in removed_items.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant(index.into()); self.brillig_context.memory_op( - target_index, + target_index.address, target_vector.size, - target_index, + target_index.address, BinaryIntOp::Add, ); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); - self.brillig_context.deallocate_register(target_index); + self.brillig_context.deallocate_single_addr(target_index); } } @@ -168,7 +170,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: MemoryAddress, + index: SingleAddrVariable, items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() @@ -193,7 +195,7 @@ impl<'block> BrilligBlock<'block> { let source_pointer_at_index = self.brillig_context.allocate_register(); self.brillig_context.memory_op( source_vector.pointer, - index, + index.address, source_pointer_at_index, BinaryIntOp::Add, ); @@ -202,7 +204,7 @@ impl<'block> BrilligBlock<'block> { let target_pointer_after_index = self.brillig_context.allocate_register(); self.brillig_context.memory_op( target_vector.pointer, - index, + index.address, target_pointer_after_index, BinaryIntOp::Add, ); @@ -214,21 +216,31 @@ impl<'block> BrilligBlock<'block> { // Compute the number of elements to the right of the index let item_count = self.brillig_context.allocate_register(); - self.brillig_context.memory_op(source_vector.size, index, item_count, BinaryIntOp::Sub); + self.brillig_context.memory_op( + source_vector.size, + index.address, + item_count, + BinaryIntOp::Sub, + ); // Copy the elements to the right of the index self.brillig_context.copy_array_instruction( source_pointer_at_index, target_pointer_after_index, - item_count, + SingleAddrVariable::new_usize(item_count), ); // Write the items to insert starting at the index for (subitem_index, variable) in items.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant(subitem_index.into()); - self.brillig_context.memory_op(target_index, index, target_index, BinaryIntOp::Add); + self.brillig_context.memory_op( + target_index.address, + index.address, + target_index.address, + BinaryIntOp::Add, + ); self.store_variable_in_array(target_vector.pointer, target_index, *variable); - self.brillig_context.deallocate_register(target_index); + self.brillig_context.deallocate_single_addr(target_index); } self.brillig_context.deallocate_register(source_pointer_at_index); @@ -240,7 +252,7 @@ impl<'block> BrilligBlock<'block> { &mut self, target_vector: BrilligVector, source_vector: BrilligVector, - index: MemoryAddress, + index: SingleAddrVariable, removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() @@ -265,7 +277,7 @@ impl<'block> BrilligBlock<'block> { let source_pointer_after_index = self.brillig_context.allocate_register(); self.brillig_context.memory_op( source_vector.pointer, - index, + index.address, source_pointer_after_index, BinaryIntOp::Add, ); @@ -279,29 +291,39 @@ impl<'block> BrilligBlock<'block> { let target_pointer_at_index = self.brillig_context.allocate_register(); self.brillig_context.memory_op( target_vector.pointer, - index, + index.address, target_pointer_at_index, BinaryIntOp::Add, ); // Compute the number of elements to the right of the index let item_count = self.brillig_context.allocate_register(); - self.brillig_context.memory_op(source_vector.size, index, item_count, BinaryIntOp::Sub); + self.brillig_context.memory_op( + source_vector.size, + index.address, + item_count, + BinaryIntOp::Sub, + ); self.brillig_context.usize_op_in_place(item_count, BinaryIntOp::Sub, removed_items.len()); // Copy the elements to the right of the index self.brillig_context.copy_array_instruction( source_pointer_after_index, target_pointer_at_index, - item_count, + SingleAddrVariable::new_usize(item_count), ); // Get the removed items for (subitem_index, variable) in removed_items.iter().enumerate() { let target_index = self.brillig_context.make_usize_constant(subitem_index.into()); - self.brillig_context.memory_op(target_index, index, target_index, BinaryIntOp::Add); + self.brillig_context.memory_op( + target_index.address, + index.address, + target_index.address, + BinaryIntOp::Add, + ); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); - self.brillig_context.deallocate_register(target_index); + self.brillig_context.deallocate_single_addr(target_index); } self.brillig_context.deallocate_register(source_pointer_after_index); @@ -592,7 +614,10 @@ mod tests { address: context.allocate_register(), bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, }; - let index_to_insert = context.allocate_register(); + let index_to_insert = SingleAddrVariable::new( + context.allocate_register(), + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ); // Cast the source array to a vector let source_vector = context.array_to_vector(&array_variable); @@ -710,7 +735,10 @@ mod tests { size: array.len(), rc: context.allocate_register(), }; - let index_to_insert = context.allocate_register(); + let index_to_insert = SingleAddrVariable::new( + context.allocate_register(), + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ); // Cast the source array to a vector let source_vector = context.array_to_vector(&array_variable); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index f1a8f24ed03..1e53713dfe0 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -32,7 +32,7 @@ use num_bigint::BigUint; /// The Brillig VM does not apply a limit to the memory address space, /// As a convention, we take use 64 bits. This means that we assume that /// memory has 2^64 memory slots. -pub(crate) const BRILLIG_MEMORY_ADDRESSING_BIT_SIZE: u32 = 32; +pub(crate) const BRILLIG_MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; // Registers reserved in runtime for special purposes. pub(crate) enum ReservedRegisters { @@ -123,8 +123,8 @@ impl BrilligContext { ) { // debug_show handled by allocate_array_instruction let size_register = self.make_usize_constant(size.into()); - self.allocate_array_instruction(pointer_register, size_register); - self.deallocate_register(size_register); + self.allocate_array_instruction(pointer_register, size_register.address); + self.deallocate_single_addr(size_register); } /// Allocates an array of size contained in size_register and stores the @@ -172,11 +172,11 @@ impl BrilligContext { }); self.memory_op( ReservedRegisters::stack_pointer(), - size_register, + size_register.address, ReservedRegisters::stack_pointer(), BinaryIntOp::Add, ); - self.deallocate_register(size_register); + self.deallocate_single_addr(size_register); } pub(crate) fn allocate_single_addr_reference_instruction( @@ -207,18 +207,14 @@ impl BrilligContext { pub(crate) fn array_get( &mut self, array_ptr: MemoryAddress, - index: MemoryAddress, + index: SingleAddrVariable, result: MemoryAddress, ) { - self.debug_show.array_get(array_ptr, index, result); + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + self.debug_show.array_get(array_ptr, index.address, result); // Computes array_ptr + index, ie array[index] let index_of_element_in_memory = self.allocate_register(); - self.binary_instruction( - array_ptr, - index, - index_of_element_in_memory, - BrilligBinaryOp::Field { op: BinaryFieldOp::Add }, - ); + self.memory_op(array_ptr, index.address, index_of_element_in_memory, BinaryIntOp::Add); self.load_instruction(result, index_of_element_in_memory); // Free up temporary register @@ -229,18 +225,14 @@ impl BrilligContext { pub(crate) fn array_set( &mut self, array_ptr: MemoryAddress, - index: MemoryAddress, + index: SingleAddrVariable, value: MemoryAddress, ) { - self.debug_show.array_set(array_ptr, index, value); + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + self.debug_show.array_set(array_ptr, index.address, value); // Computes array_ptr + index, ie array[index] let index_of_element_in_memory = self.allocate_register(); - self.binary_instruction( - array_ptr, - index, - index_of_element_in_memory, - BrilligBinaryOp::Field { op: BinaryFieldOp::Add }, - ); + self.memory_op(array_ptr, index.address, index_of_element_in_memory, BinaryIntOp::Add); self.store_instruction(index_of_element_in_memory, value); // Free up temporary register @@ -253,17 +245,18 @@ impl BrilligContext { &mut self, source_pointer: MemoryAddress, destination_pointer: MemoryAddress, - num_elements_register: MemoryAddress, + num_elements_variable: SingleAddrVariable, ) { + assert!(num_elements_variable.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); self.debug_show.copy_array_instruction( source_pointer, destination_pointer, - num_elements_register, + num_elements_variable.address, ); let value_register = self.allocate_register(); - self.loop_instruction(num_elements_register, |ctx, iterator| { + self.loop_instruction(num_elements_variable.address, |ctx, iterator| { ctx.array_get(source_pointer, iterator, value_register); ctx.array_set(destination_pointer, iterator, value_register); }); @@ -275,7 +268,7 @@ impl BrilligContext { /// The body of the loop should be issued by the caller in the on_iteration closure. pub(crate) fn loop_instruction(&mut self, iteration_count: MemoryAddress, on_iteration: F) where - F: FnOnce(&mut BrilligContext, MemoryAddress), + F: FnOnce(&mut BrilligContext, SingleAddrVariable), { let iterator_register = self.make_usize_constant(0_u128.into()); @@ -289,7 +282,7 @@ impl BrilligContext { SingleAddrVariable { address: self.allocate_register(), bit_size: 1 }; self.memory_op( - iterator_register, + iterator_register.address, iteration_count, iterator_less_than_iterations.address, BinaryIntOp::LessThan, @@ -305,7 +298,7 @@ impl BrilligContext { on_iteration(self, iterator_register); // Increment the iterator register - self.usize_op_in_place(iterator_register, BinaryIntOp::Add, 1); + self.usize_op_in_place(iterator_register.address, BinaryIntOp::Add, 1); self.jump_instruction(loop_label); @@ -313,8 +306,8 @@ impl BrilligContext { self.enter_section(exit_loop_section); // Deallocate our temporary registers - self.deallocate_register(iterator_less_than_iterations.address); - self.deallocate_register(iterator_register); + self.deallocate_single_addr(iterator_less_than_iterations); + self.deallocate_single_addr(iterator_register); } /// This instruction will issue an if-then branch that will check if the condition is true @@ -430,11 +423,14 @@ impl BrilligContext { } /// Push a register to the deallocation list, ready for reuse. - /// TODO(AD): currently, register deallocation is only done with immediate values. - /// TODO(AD): See https://github.com/noir-lang/noir/issues/1720 pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { self.registers.deallocate_register(register_index); } + + /// Deallocates the address where the single address variable is stored + pub(crate) fn deallocate_single_addr(&mut self, var: SingleAddrVariable) { + self.deallocate_register(var.address); + } } impl BrilligContext { @@ -442,12 +438,16 @@ impl BrilligContext { /// is false. pub(crate) fn constrain_instruction( &mut self, - condition: MemoryAddress, + condition: SingleAddrVariable, assert_message: Option, ) { - self.debug_show.constrain_instruction(condition); + assert!(condition.bit_size == 1); + self.debug_show.constrain_instruction(condition.address); let (next_section, next_label) = self.reserve_next_section_label(); - self.add_unresolved_jump(BrilligOpcode::JumpIf { condition, location: 0 }, next_label); + self.add_unresolved_jump( + BrilligOpcode::JumpIf { condition: condition.address, location: 0 }, + next_label, + ); self.push_opcode(BrilligOpcode::Trap); if let Some(assert_message) = assert_message { self.obj.add_assert_message_to_last_opcode(assert_message); @@ -526,47 +526,81 @@ impl BrilligContext { }); } + fn binary_result_bit_size(operation: BrilligBinaryOp, arguments_bit_size: u32) -> u32 { + match operation { + BrilligBinaryOp::Field(BinaryFieldOp::Equals) + | BrilligBinaryOp::Integer(BinaryIntOp::Equals) + | BrilligBinaryOp::Integer(BinaryIntOp::LessThan) + | BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals) => 1, + _ => arguments_bit_size, + } + } + /// Processes a binary instruction according `operation`. /// /// This method will compute lhs rhs /// and store the result in the `result` register. pub(crate) fn binary_instruction( &mut self, - lhs: MemoryAddress, - rhs: MemoryAddress, - result: MemoryAddress, + lhs: SingleAddrVariable, + rhs: SingleAddrVariable, + result: SingleAddrVariable, operation: BrilligBinaryOp, ) { - self.debug_show.binary_instruction(lhs, rhs, result, operation); + assert!( + lhs.bit_size == rhs.bit_size, + "Not equal bit size for lhs and rhs: lhs {}, rhs {}", + lhs.bit_size, + rhs.bit_size + ); + let expected_result_bit_size = + BrilligContext::binary_result_bit_size(operation, lhs.bit_size); + assert!( + result.bit_size == expected_result_bit_size, + "Expected result bit size to be {}, got {} for operation {:?}", + expected_result_bit_size, + result.bit_size, + operation + ); + self.debug_show.binary_instruction(lhs.address, rhs.address, result.address, operation); match operation { - BrilligBinaryOp::Field { op } => { - let opcode = BrilligOpcode::BinaryFieldOp { op, destination: result, lhs, rhs }; + BrilligBinaryOp::Field(op) => { + let opcode = BrilligOpcode::BinaryFieldOp { + op, + destination: result.address, + lhs: lhs.address, + rhs: rhs.address, + }; self.push_opcode(opcode); } - BrilligBinaryOp::Integer { op, bit_size } => { - let opcode = - BrilligOpcode::BinaryIntOp { op, destination: result, bit_size, lhs, rhs }; + BrilligBinaryOp::Integer(op) => { + let opcode = BrilligOpcode::BinaryIntOp { + op, + destination: result.address, + bit_size: lhs.bit_size, + lhs: lhs.address, + rhs: rhs.address, + }; self.push_opcode(opcode); } - BrilligBinaryOp::Modulo { is_signed_integer, bit_size } => { - self.modulo_instruction(result, lhs, rhs, bit_size, is_signed_integer); + BrilligBinaryOp::Modulo { is_signed_integer } => { + self.modulo_instruction(result, lhs, rhs, is_signed_integer); } } } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction( - &mut self, - result: MemoryAddress, - constant: Value, - bit_size: u32, - ) { - self.debug_show.const_instruction(result, constant); - self.push_opcode(BrilligOpcode::Const { destination: result, value: constant, bit_size }); + pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { + self.debug_show.const_instruction(result.address, constant); + self.push_opcode(BrilligOpcode::Const { + destination: result.address, + value: constant, + bit_size: result.bit_size, + }); } pub(crate) fn usize_const(&mut self, result: MemoryAddress, constant: Value) { - self.const_instruction(result, constant, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + self.const_instruction(SingleAddrVariable::new_usize(result), constant); } /// Processes a not instruction. @@ -583,15 +617,16 @@ impl BrilligContext { let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) - FieldElement::one(); let max = self.make_constant(Value::from(u_max), input.bit_size); + let opcode = BrilligOpcode::BinaryIntOp { destination: result.address, op: BinaryIntOp::Sub, bit_size: input.bit_size, - lhs: max, + lhs: max.address, rhs: input.address, }; self.push_opcode(opcode); - self.deallocate_register(max); + self.deallocate_single_addr(max); } /// Processes a foreign call instruction. @@ -750,13 +785,13 @@ impl BrilligContext { ); self.binary_instruction( - value_to_truncate.address, + value_to_truncate, mask_constant, - destination_of_truncated_value.address, - BrilligBinaryOp::Integer { op: BinaryIntOp::And, bit_size: value_to_truncate.bit_size }, + destination_of_truncated_value, + BrilligBinaryOp::Integer(BinaryIntOp::And), ); - self.deallocate_register(mask_constant); + self.deallocate_single_addr(mask_constant); } /// Emits a stop instruction @@ -766,17 +801,17 @@ impl BrilligContext { } /// Returns a register which holds the value of a constant - pub(crate) fn make_constant(&mut self, constant: Value, bit_size: u32) -> MemoryAddress { - let register = self.allocate_register(); - self.const_instruction(register, constant, bit_size); - register + pub(crate) fn make_constant(&mut self, constant: Value, bit_size: u32) -> SingleAddrVariable { + let var = SingleAddrVariable::new(self.allocate_register(), bit_size); + self.const_instruction(var, constant); + var } /// Returns a register which holds the value of an usize constant - pub(crate) fn make_usize_constant(&mut self, constant: Value) -> MemoryAddress { + pub(crate) fn make_usize_constant(&mut self, constant: Value) -> SingleAddrVariable { let register = self.allocate_register(); self.usize_const(register, constant); - register + SingleAddrVariable::new_usize(register) } /// Computes left % right by emitting the necessary Brillig opcodes. @@ -790,16 +825,22 @@ impl BrilligContext { /// to other binary instructions. pub(crate) fn modulo_instruction( &mut self, - result_register: MemoryAddress, - left: MemoryAddress, - right: MemoryAddress, - bit_size: u32, + result: SingleAddrVariable, + left: SingleAddrVariable, + right: SingleAddrVariable, signed: bool, ) { // no debug_show, shown in binary instruction let scratch_register_i = self.allocate_register(); let scratch_register_j = self.allocate_register(); + assert!( + left.bit_size == right.bit_size, + "Not equal bitsize: lhs {}, rhs {}", + left.bit_size, + right.bit_size + ); + let bit_size = left.bit_size; // i = left / right self.push_opcode(BrilligOpcode::BinaryIntOp { op: match signed { @@ -808,8 +849,8 @@ impl BrilligContext { }, destination: scratch_register_i, bit_size, - lhs: left, - rhs: right, + lhs: left.address, + rhs: right.address, }); // j = i * right @@ -818,15 +859,15 @@ impl BrilligContext { destination: scratch_register_j, bit_size, lhs: scratch_register_i, - rhs: right, + rhs: right.address, }); // result_register = left - j self.push_opcode(BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Sub, - destination: result_register, + destination: result.address, bit_size, - lhs: left, + lhs: left.address, rhs: scratch_register_j, }); // Free scratch registers @@ -909,9 +950,9 @@ impl BrilligContext { constant: usize, ) { let const_register = self.make_usize_constant(Value::from(constant)); - self.memory_op(operand, const_register, destination, op); + self.memory_op(operand, const_register.address, destination, op); // Mark as no longer used for this purpose, frees for reuse - self.deallocate_register(const_register); + self.deallocate_single_addr(const_register); } /// Utility method to perform a binary instruction with a memory address @@ -923,10 +964,16 @@ impl BrilligContext { op: BinaryIntOp, ) { self.binary_instruction( - lhs, - rhs, - destination, - BrilligBinaryOp::Integer { op, bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE }, + SingleAddrVariable::new_usize(lhs), + SingleAddrVariable::new_usize(rhs), + SingleAddrVariable::new( + destination, + BrilligContext::binary_result_bit_size( + BrilligBinaryOp::Integer(op), + BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + ), + ), + BrilligBinaryOp::Integer(op), ); } @@ -983,7 +1030,7 @@ impl BrilligContext { /// Utility method to transform a HeapArray to a HeapVector by making a runtime constant with the size. pub(crate) fn array_to_vector(&mut self, array: &BrilligArray) -> BrilligVector { let size_register = self.make_usize_constant(array.size.into()); - BrilligVector { size: size_register, pointer: array.pointer, rc: array.rc } + BrilligVector { size: size_register.address, pointer: array.pointer, rc: array.rc } } /// Issues a blackbox operation. @@ -996,47 +1043,48 @@ impl BrilligContext { /// And the radix register limb_count times to the target vector. pub(crate) fn radix_instruction( &mut self, - source: MemoryAddress, + source_field: SingleAddrVariable, target_vector: BrilligVector, - radix: MemoryAddress, - limb_count: MemoryAddress, + radix: SingleAddrVariable, + limb_count: SingleAddrVariable, big_endian: bool, ) { - self.mov_instruction(target_vector.size, limb_count); + assert!(source_field.bit_size == FieldElement::max_num_bits()); + assert!(radix.bit_size == 32); + assert!(limb_count.bit_size == 32); + let radix_as_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + self.cast_instruction(radix_as_field, radix); + + self.cast_instruction(SingleAddrVariable::new_usize(target_vector.size), limb_count); self.usize_const(target_vector.rc, 1_usize.into()); self.allocate_array_instruction(target_vector.pointer, target_vector.size); - let shifted_register = self.allocate_register(); - self.mov_instruction(shifted_register, source); + let shifted_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + self.mov_instruction(shifted_field.address, source_field.address); - let modulus_register: MemoryAddress = self.allocate_register(); + let modulus_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); self.loop_instruction(target_vector.size, |ctx, iterator_register| { // Compute the modulus - ctx.modulo_instruction( - modulus_register, - shifted_register, - radix, - FieldElement::max_num_bits(), - false, - ); + ctx.modulo_instruction(modulus_field, shifted_field, radix_as_field, false); // Write it - ctx.array_set(target_vector.pointer, iterator_register, modulus_register); + ctx.array_set(target_vector.pointer, iterator_register, modulus_field.address); // Integer div the field ctx.binary_instruction( - shifted_register, - radix, - shifted_register, - BrilligBinaryOp::Integer { - op: BinaryIntOp::UnsignedDiv, - bit_size: FieldElement::max_num_bits(), - }, + shifted_field, + radix_as_field, + shifted_field, + BrilligBinaryOp::Integer(BinaryIntOp::UnsignedDiv), ); }); // Deallocate our temporary registers - self.deallocate_register(shifted_register); - self.deallocate_register(modulus_register); + self.deallocate_single_addr(shifted_field); + self.deallocate_single_addr(modulus_field); + self.deallocate_single_addr(radix_as_field); if big_endian { self.reverse_vector_in_place_instruction(target_vector); @@ -1061,16 +1109,24 @@ impl BrilligContext { ctx.usize_op_in_place(index_at_end_of_array, BinaryIntOp::Sub, 1); ctx.memory_op( index_at_end_of_array, - iterator_register, + iterator_register.address, index_at_end_of_array, BinaryIntOp::Sub, ); - ctx.array_get(vector.pointer, index_at_end_of_array, end_value_register); + ctx.array_get( + vector.pointer, + SingleAddrVariable::new_usize(index_at_end_of_array), + end_value_register, + ); // Write both values ctx.array_set(vector.pointer, iterator_register, end_value_register); - ctx.array_set(vector.pointer, index_at_end_of_array, start_value_register); + ctx.array_set( + vector.pointer, + SingleAddrVariable::new_usize(index_at_end_of_array), + start_value_register, + ); }); self.deallocate_register(iteration_count); @@ -1086,13 +1142,12 @@ impl BrilligContext { } /// Type to encapsulate the binary operation types in Brillig -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub(crate) enum BrilligBinaryOp { - Field { op: BinaryFieldOp }, - Integer { op: BinaryIntOp, bit_size: u32 }, - // Modulo operation requires more than one opcode - // Brillig. - Modulo { is_signed_integer: bool, bit_size: u32 }, + Field(BinaryFieldOp), + Integer(BinaryIntOp), + // Modulo operation requires more than one brillig opcode + Modulo { is_signed_integer: bool }, } #[cfg(test)] diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index 48ad3c5bae4..b94f8140ddd 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -5,12 +5,24 @@ use serde::{Deserialize, Serialize}; use crate::ssa::ir::types::Type; +use super::BRILLIG_MEMORY_ADDRESSING_BIT_SIZE; + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct SingleAddrVariable { pub(crate) address: MemoryAddress, pub(crate) bit_size: u32, } +impl SingleAddrVariable { + pub(crate) fn new(address: MemoryAddress, bit_size: u32) -> Self { + SingleAddrVariable { address, bit_size } + } + + pub(crate) fn new_usize(address: MemoryAddress) -> Self { + SingleAddrVariable { address, bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE } + } +} + /// The representation of a noir array in the Brillig IR #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Copy)] pub(crate) struct BrilligArray { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index dd57f0c4426..e32ce6f6b92 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -1,7 +1,7 @@ //! This module contains functions for producing a higher level view disassembler of Brillig. use super::BrilligBinaryOp; -use crate::brillig::brillig_ir::{ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE}; +use crate::brillig::brillig_ir::ReservedRegisters; use acvm::acir::brillig::{ BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, ValueOrArray, @@ -83,23 +83,11 @@ impl DebugToString for BinaryIntOp { impl DebugToString for BrilligBinaryOp { fn debug_to_string(&self) -> String { match self { - BrilligBinaryOp::Field { op } => op.debug_to_string(), - BrilligBinaryOp::Integer { op, bit_size } => { - // rationale: if there's >= 64 bits, we should not bother with this detail - if *bit_size >= BRILLIG_MEMORY_ADDRESSING_BIT_SIZE { - op.debug_to_string() - } else { - format!("i{}::{}", bit_size, op.debug_to_string()) - } - } - BrilligBinaryOp::Modulo { is_signed_integer, bit_size } => { + BrilligBinaryOp::Field(op) => op.debug_to_string(), + BrilligBinaryOp::Integer(op) => op.debug_to_string(), + BrilligBinaryOp::Modulo { is_signed_integer } => { let op = if *is_signed_integer { "%" } else { "%%" }; - // rationale: if there's >= 64 bits, we should not bother with this detail - if *bit_size >= BRILLIG_MEMORY_ADDRESSING_BIT_SIZE { - op.into() - } else { - format!("{op}:{bit_size}") - } + op.into() } } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 9d186f9bc60..83440e4a51d 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -79,9 +79,9 @@ impl BrilligContext { let rc_register = self.make_usize_constant(1_usize.into()); let flattened_size = BrilligContext::flattened_size(argument); let var = BrilligVariable::BrilligArray(BrilligArray { - pointer: pointer_to_the_array_in_calldata, + pointer: pointer_to_the_array_in_calldata.address, size: flattened_size, - rc: rc_register, + rc: rc_register.address, }); current_calldata_pointer += flattened_size; @@ -218,7 +218,7 @@ impl BrilligContext { self.mov_instruction(nested_array_pointer, flattened_array_pointer); self.memory_op( nested_array_pointer, - source_index, + source_index.address, nested_array_pointer, acvm::brillig_vm::brillig::BinaryIntOp::Add, ); @@ -253,8 +253,8 @@ impl BrilligContext { BrilligParameter::Slice(..) => unreachable!("ICE: Cannot deflatten slices"), } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_single_addr(source_index); + self.deallocate_single_addr(target_index); } } @@ -323,11 +323,11 @@ impl BrilligContext { self.flatten_array( item_type, *item_count, - pointer_to_return_data, + pointer_to_return_data.address, returned_pointer, ); - self.deallocate_register(pointer_to_return_data); + self.deallocate_single_addr(pointer_to_return_data); return_data_index += BrilligContext::flattened_size(return_param); } BrilligParameter::Slice(..) => { @@ -412,7 +412,7 @@ impl BrilligContext { self.memory_op( flattened_nested_array_pointer, - target_index, + target_index.address, flattened_nested_array_pointer, acvm::brillig_vm::brillig::BinaryIntOp::Add, ); @@ -436,8 +436,8 @@ impl BrilligContext { BrilligParameter::Slice(..) => unreachable!("ICE: Cannot flatten slices"), } - self.deallocate_register(source_index); - self.deallocate_register(target_index); + self.deallocate_single_addr(source_index); + self.deallocate_single_addr(target_index); } } @@ -449,7 +449,7 @@ impl BrilligContext { flattened_array_pointer, item_count, ); - self.deallocate_register(item_count); + self.deallocate_single_addr(item_count); } } } diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index cbaeb2477d6..e785212f8d2 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -92,7 +92,7 @@ impl FunctionBuilder { let index = self .current_function .dfg - .make_constant(FieldElement::from(i as i128), Type::field()); + .make_constant(FieldElement::from(i as i128), Type::length_type()); let element = self.insert_array_get(value, index, typ[0].clone()); self.add_to_data_bus(element, databus); } From a6016b46abf6da6de4566cf6d35a675d805dd9b5 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Wed, 13 Mar 2024 08:30:25 -0400 Subject: [PATCH 070/416] feat: Sync from aztec-packages (#4494) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE chore!: Remove open keyword from Noir (https://github.com/AztecProtocol/aztec-packages/pull/4967) chore: aztec-macros refactor (https://github.com/AztecProtocol/aztec-packages/pull/5127) feat: make brillig-gen more AVM-friendly (https://github.com/AztecProtocol/aztec-packages/pull/5091) feat: Integrated native ACVM (https://github.com/AztecProtocol/aztec-packages/pull/4903) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant Co-authored-by: Maddiaa <47148561+Maddiaa0@users.noreply.github.com> Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: TomAFrench Co-authored-by: Alex Gherghisan --- .aztec-sync-commit | 2 +- Cargo.lock | 20 + Cargo.toml | 4 +- aztec_macros/src/lib.rs | 1782 +---------------- .../compute_note_hash_and_nullifier.rs | 195 ++ aztec_macros/src/transforms/events.rs | 178 ++ aztec_macros/src/transforms/functions.rs | 707 +++++++ aztec_macros/src/transforms/mod.rs | 4 + aztec_macros/src/transforms/storage.rs | 346 ++++ aztec_macros/src/utils/ast_utils.rs | 183 ++ aztec_macros/src/utils/checks.rs | 22 + aztec_macros/src/utils/constants.rs | 3 + aztec_macros/src/utils/errors.rs | 69 + aztec_macros/src/utils/hir_utils.rs | 118 ++ aztec_macros/src/utils/mod.rs | 5 + compiler/noirc_driver/src/contract.rs | 31 +- compiler/noirc_driver/src/lib.rs | 20 +- .../src/brillig/brillig_gen/brillig_block.rs | 11 +- .../noirc_evaluator/src/brillig/brillig_ir.rs | 32 +- compiler/noirc_frontend/src/ast/expression.rs | 19 - .../src/hir/def_collector/dc_crate.rs | 3 +- .../src/hir/def_collector/dc_mod.rs | 37 +- .../src/hir/resolution/errors.rs | 14 - .../src/hir/resolution/resolver.rs | 39 +- compiler/noirc_frontend/src/lexer/token.rs | 3 - compiler/noirc_frontend/src/lib.rs | 6 +- .../src/monomorphization/mod.rs | 7 +- compiler/noirc_frontend/src/node_interner.rs | 21 +- .../src/parser/parser/function.rs | 14 +- .../src/parser/parser/traits.rs | 6 +- compiler/wasm/src/types/noir_artifact.ts | 11 +- .../wasm/test/fixtures/deps/lib-c/src/lib.nr | 2 +- .../test/fixtures/deps/lib-c/src/module.nr | 2 +- .../fixtures/deps/lib-c/src/module/foo.nr | 2 +- .../test/fixtures/noir-contract/src/main.nr | 3 +- docs/scripts/codegen_nargo_reference.sh | 2 +- .../assert_msg_runtime/src/main.nr | 2 +- .../brillig_assert_msg_runtime/src/main.nr | 2 +- .../brillig_mut_ref_from_acir/src/main.nr | 2 +- .../contract_with_impl/src/main.nr | 2 +- .../simple_contract/src/main.nr | 5 +- .../brillig_cow_regression/src/main.nr | 42 +- .../double_verify_proof/Nargo.toml | 4 +- .../double_verify_proof/src/main.nr | 1 - .../double_verify_proof_recursive/Nargo.toml | 5 + .../double_verify_proof_recursive/Prover.toml | 5 + .../double_verify_proof_recursive/src/main.nr | 29 + .../Nargo.toml | 5 + .../test_libraries/exporting_lib/src/lib.nr | 1 - tooling/acvm_cli/Cargo.toml | 38 + tooling/acvm_cli/src/cli/execute_cmd.rs | 78 + tooling/acvm_cli/src/cli/fs/inputs.rs | 54 + tooling/acvm_cli/src/cli/fs/mod.rs | 2 + tooling/acvm_cli/src/cli/fs/witness.rs | 36 + tooling/acvm_cli/src/cli/mod.rs | 41 + tooling/acvm_cli/src/errors.rs | 52 + tooling/acvm_cli/src/main.rs | 36 + tooling/debugger/ignored-tests.txt | 1 + tooling/nargo/src/artifacts/contract.rs | 10 +- 59 files changed, 2415 insertions(+), 1961 deletions(-) create mode 100644 aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs create mode 100644 aztec_macros/src/transforms/events.rs create mode 100644 aztec_macros/src/transforms/functions.rs create mode 100644 aztec_macros/src/transforms/mod.rs create mode 100644 aztec_macros/src/transforms/storage.rs create mode 100644 aztec_macros/src/utils/ast_utils.rs create mode 100644 aztec_macros/src/utils/checks.rs create mode 100644 aztec_macros/src/utils/constants.rs create mode 100644 aztec_macros/src/utils/errors.rs create mode 100644 aztec_macros/src/utils/hir_utils.rs create mode 100644 aztec_macros/src/utils/mod.rs create mode 100644 test_programs/execution_success/double_verify_proof_recursive/Nargo.toml create mode 100644 test_programs/execution_success/double_verify_proof_recursive/Prover.toml create mode 100644 test_programs/execution_success/double_verify_proof_recursive/src/main.nr create mode 100644 test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml create mode 100644 tooling/acvm_cli/Cargo.toml create mode 100644 tooling/acvm_cli/src/cli/execute_cmd.rs create mode 100644 tooling/acvm_cli/src/cli/fs/inputs.rs create mode 100644 tooling/acvm_cli/src/cli/fs/mod.rs create mode 100644 tooling/acvm_cli/src/cli/fs/witness.rs create mode 100644 tooling/acvm_cli/src/cli/mod.rs create mode 100644 tooling/acvm_cli/src/errors.rs create mode 100644 tooling/acvm_cli/src/main.rs diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 6841c89b691..5a1cd9c70bd 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -7ff9b71d8d87fc93ae7dbd8ba63f5176b0cd17be +58e15edf7fd3d32267b0aed883fc84f6cee327c9 diff --git a/Cargo.lock b/Cargo.lock index ad3dce229fc..d5ce8a10509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "acvm_cli" +version = "0.40.0" +dependencies = [ + "acir", + "acvm", + "bn254_blackbox_solver", + "clap", + "color-eyre", + "const_format", + "nargo", + "paste", + "proptest", + "rand 0.8.5", + "thiserror", + "toml 0.7.6", + "tracing-appender", + "tracing-subscriber", +] + [[package]] name = "acvm_js" version = "0.41.0" diff --git a/Cargo.toml b/Cargo.toml index 2ddb9c9e28f..b8f9b9ceacc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ members = [ "tooling/nargo_toml", "tooling/noirc_abi", "tooling/noirc_abi_wasm", + "tooling/acvm_cli", # ACVM "acvm-repo/acir_field", "acvm-repo/acir", @@ -35,7 +36,7 @@ members = [ "acvm-repo/blackbox_solver", "acvm-repo/bn254_blackbox_solver", ] -default-members = ["tooling/nargo_cli"] +default-members = ["tooling/nargo_cli", "tooling/acvm_cli"] resolver = "2" [workspace.package] @@ -77,6 +78,7 @@ noir_lsp = { path = "tooling/lsp" } noir_debugger = { path = "tooling/debugger" } noirc_abi = { path = "tooling/noirc_abi" } bb_abstraction_leaks = { path = "tooling/bb_abstraction_leaks" } +acvm_cli = { path = "tooling/acvm_cli" } # LSP async-lsp = { version = "0.1.0", default-features = false } diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index c21e26bdcad..1f3546cbb6a 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -1,28 +1,26 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::vec; +mod transforms; +mod utils; + +use transforms::{ + compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, + events::{generate_selector_impl, transform_events}, + functions::{transform_function, transform_unconstrained, transform_vm_function}, + storage::{ + assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, + generate_storage_implementation, + }, +}; -use convert_case::{Case, Casing}; -use iter_extended::vecmap; -use noirc_errors::{Location, Spanned}; use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; -use noirc_frontend::hir::def_map::{LocalModuleId, ModuleId}; -use noirc_frontend::macros_api::parse_program; -use noirc_frontend::macros_api::FieldElement; -use noirc_frontend::macros_api::{ - BlockExpression, CallExpression, CastExpression, Distinctness, Expression, ExpressionKind, - ForLoopStatement, ForRange, FunctionDefinition, FunctionReturnType, HirContext, HirExpression, - HirLiteral, HirStatement, Ident, IndexExpression, LetStatement, Literal, - MemberAccessExpression, MethodCallExpression, NoirFunction, NoirStruct, Param, Path, PathKind, - Pattern, PrefixExpression, SecondaryAttribute, Signedness, Span, Statement, StatementKind, - StructType, Type, TypeImpl, UnaryOp, UnresolvedType, UnresolvedTypeData, Visibility, -}; -use noirc_frontend::macros_api::{CrateId, FileId}; -use noirc_frontend::macros_api::{MacroError, MacroProcessor}; -use noirc_frontend::macros_api::{ModuleDefId, NodeInterner, SortedModule, StructId}; -use noirc_frontend::node_interner::{FuncId, TraitId, TraitImplId, TraitImplKind}; -use noirc_frontend::{ - BinaryOpKind, ConstrainKind, ConstrainStatement, InfixExpression, ItemVisibility, Lambda, -}; + +use noirc_frontend::macros_api::SortedModule; +use noirc_frontend::macros_api::{CrateId, MacroError}; +use noirc_frontend::macros_api::{FileId, MacroProcessor}; +use noirc_frontend::macros_api::{HirContext, SecondaryAttribute, Span}; + +use utils::ast_utils::is_custom_attribute; +use utils::checks::{check_for_aztec_dependency, has_aztec_dependency}; +use utils::{constants::MAX_CONTRACT_PRIVATE_FUNCTIONS, errors::AztecMacroError}; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -35,23 +33,14 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, context) } - fn process_unresolved_traits_impls( + fn process_collected_defs( &self, crate_id: &CrateId, context: &mut HirContext, - unresolved_traits_impls: &[UnresolvedTraitImpl], - collected_functions: &mut Vec, + collected_trait_impls: &[UnresolvedTraitImpl], + collected_functions: &mut [UnresolvedFunctions], ) -> Result<(), (MacroError, FileId)> { - if has_aztec_dependency(crate_id, context) { - inject_compute_note_hash_and_nullifier( - crate_id, - context, - unresolved_traits_impls, - collected_functions, - ) - } else { - Ok(()) - } + transform_collected_defs(crate_id, context, collected_trait_impls, collected_functions) } fn process_typed_ast( @@ -63,225 +52,6 @@ impl MacroProcessor for AztecMacro { } } -const FUNCTION_TREE_HEIGHT: u32 = 5; -const MAX_CONTRACT_PRIVATE_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); - -#[derive(Debug, Clone)] -pub enum AztecMacroError { - AztecDepNotFound, - ContractHasTooManyPrivateFunctions { span: Span }, - ContractConstructorMissing { span: Span }, - UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, - UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, - CouldNotAssignStorageSlots { secondary_message: Option }, - EventError { span: Span, message: String }, - UnsupportedAttributes { span: Span, secondary_message: Option }, -} - -impl From for MacroError { - fn from(err: AztecMacroError) -> Self { - match err { - AztecMacroError::AztecDepNotFound {} => MacroError { - primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), - secondary_message: None, - span: None, - }, - AztecMacroError::ContractHasTooManyPrivateFunctions { span } => MacroError { - primary_message: format!("Contract can only have a maximum of {} private functions", MAX_CONTRACT_PRIVATE_FUNCTIONS), - secondary_message: None, - span: Some(span), - }, - AztecMacroError::ContractConstructorMissing { span } => MacroError { - primary_message: "Contract must have a constructor function".to_owned(), - secondary_message: None, - span: Some(span), - }, - AztecMacroError::UnsupportedFunctionArgumentType { span, typ } => MacroError { - primary_message: format!("Provided parameter type `{typ:?}` is not supported in Aztec contract interface"), - secondary_message: None, - span: Some(span), - }, - AztecMacroError::UnsupportedStorageType { span, typ } => MacroError { - primary_message: format!("Provided storage type `{typ:?}` is not directly supported in Aztec. Please provide a custom storage implementation"), - secondary_message: None, - span, - }, - AztecMacroError::CouldNotAssignStorageSlots { secondary_message } => MacroError { - primary_message: "Could not assign storage slots, please provide a custom storage implementation".to_string(), - secondary_message, - span: None, - }, - AztecMacroError::EventError { span, message } => MacroError { - primary_message: message, - secondary_message: None, - span: Some(span), - }, - AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { - primary_message: "Unsupported attributes in contract function".to_string(), - secondary_message, - span: Some(span), - }, - } - } -} - -// -// Helper macros for creating noir ast nodes -// -fn ident(name: &str) -> Ident { - Ident::new(name.to_string(), Span::default()) -} - -fn ident_path(name: &str) -> Path { - Path::from_ident(ident(name)) -} - -fn path(ident: Ident) -> Path { - Path::from_ident(ident) -} - -fn expression(kind: ExpressionKind) -> Expression { - Expression::new(kind, Span::default()) -} - -fn variable(name: &str) -> Expression { - expression(ExpressionKind::Variable(ident_path(name))) -} - -fn variable_ident(identifier: Ident) -> Expression { - expression(ExpressionKind::Variable(path(identifier))) -} - -fn variable_path(path: Path) -> Expression { - expression(ExpressionKind::Variable(path)) -} - -fn method_call(object: Expression, method_name: &str, arguments: Vec) -> Expression { - expression(ExpressionKind::MethodCall(Box::new(MethodCallExpression { - object, - method_name: ident(method_name), - arguments, - }))) -} - -fn call(func: Expression, arguments: Vec) -> Expression { - expression(ExpressionKind::Call(Box::new(CallExpression { func: Box::new(func), arguments }))) -} - -fn pattern(name: &str) -> Pattern { - Pattern::Identifier(ident(name)) -} - -fn mutable(name: &str) -> Pattern { - Pattern::Mutable(Box::new(pattern(name)), Span::default(), true) -} - -fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { - make_statement(StatementKind::Let(LetStatement { - pattern: mutable(name), - r#type: make_type(UnresolvedTypeData::Unspecified), - expression: assigned_to, - })) -} - -fn mutable_reference(variable_name: &str) -> Expression { - expression(ExpressionKind::Prefix(Box::new(PrefixExpression { - operator: UnaryOp::MutableReference, - rhs: variable(variable_name), - }))) -} - -fn assignment(name: &str, assigned_to: Expression) -> Statement { - make_statement(StatementKind::Let(LetStatement { - pattern: pattern(name), - r#type: make_type(UnresolvedTypeData::Unspecified), - expression: assigned_to, - })) -} - -fn member_access(lhs: &str, rhs: &str) -> Expression { - expression(ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: variable(lhs), - rhs: ident(rhs), - }))) -} - -fn return_type(path: Path) -> FunctionReturnType { - let ty = make_type(UnresolvedTypeData::Named(path, vec![], true)); - FunctionReturnType::Ty(ty) -} - -fn lambda(parameters: Vec<(Pattern, UnresolvedType)>, body: Expression) -> Expression { - expression(ExpressionKind::Lambda(Box::new(Lambda { - parameters, - return_type: UnresolvedType { - typ: UnresolvedTypeData::Unspecified, - span: Some(Span::default()), - }, - body, - }))) -} - -fn make_eq(lhs: Expression, rhs: Expression) -> Expression { - expression(ExpressionKind::Infix(Box::new(InfixExpression { - lhs, - rhs, - operator: Spanned::from(Span::default(), BinaryOpKind::Equal), - }))) -} - -macro_rules! chained_path { - ( $base:expr ) => { - { - ident_path($base) - } - }; - ( $base:expr $(, $tail:expr)* ) => { - { - let mut base_path = ident_path($base); - $( - base_path.segments.push(ident($tail)); - )* - base_path - } - } -} - -macro_rules! chained_dep { - ( $base:expr $(, $tail:expr)* ) => { - { - let mut base_path = ident_path($base); - base_path.kind = PathKind::Dep; - $( - base_path.segments.push(ident($tail)); - )* - base_path - } - } -} - -fn cast(lhs: Expression, ty: UnresolvedTypeData) -> Expression { - expression(ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type: make_type(ty) }))) -} - -fn make_type(typ: UnresolvedTypeData) -> UnresolvedType { - UnresolvedType { typ, span: Some(Span::default()) } -} - -fn index_array(array: Ident, index: &str) -> Expression { - expression(ExpressionKind::Index(Box::new(IndexExpression { - collection: variable_path(path(array)), - index: variable(index), - }))) -} - -fn index_array_variable(array: Expression, index: &str) -> Expression { - expression(ExpressionKind::Index(Box::new(IndexExpression { - collection: array, - index: variable(index), - }))) -} - // // Create AST Nodes for Aztec // @@ -294,7 +64,6 @@ fn transform( context: &HirContext, ) -> Result { // Usage -> mut ast -> aztec_library::transform(&mut ast) - // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { if transform_module(&mut submodule.contents, crate_id, context) @@ -306,97 +75,6 @@ fn transform( Ok(ast) } -// -// Transform Hir Nodes for Aztec -// - -/// Completes the Hir with data gathered from type resolution -fn transform_hir( - crate_id: &CrateId, - context: &mut HirContext, -) -> Result<(), (AztecMacroError, FileId)> { - transform_events(crate_id, context)?; - assign_storage_slots(crate_id, context) -} - -/// Creates an error alerting the user that they have not downloaded the Aztec-noir library -fn check_for_aztec_dependency( - crate_id: &CrateId, - context: &HirContext, -) -> Result<(), (MacroError, FileId)> { - if has_aztec_dependency(crate_id, context) { - Ok(()) - } else { - Err((AztecMacroError::AztecDepNotFound.into(), context.crate_graph[crate_id].root_file_id)) - } -} - -fn has_aztec_dependency(crate_id: &CrateId, context: &HirContext) -> bool { - context.crate_graph[crate_id].dependencies.iter().any(|dep| dep.as_name() == "aztec") -} - -// Check to see if the user has defined a storage struct -fn check_for_storage_definition(module: &SortedModule) -> bool { - module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") -} - -// Check to see if the user has defined a storage struct -fn check_for_storage_implementation(module: &SortedModule) -> bool { - module.impls.iter().any(|r#impl| match &r#impl.object_type.typ { - UnresolvedTypeData::Named(path, _, _) => { - path.segments.last().is_some_and(|segment| segment.0.contents == "Storage") - } - _ => false, - }) -} - -// Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined -fn check_for_compute_note_hash_and_nullifier_definition( - functions_data: &[(LocalModuleId, FuncId, NoirFunction)], - module_id: LocalModuleId, -) -> bool { - functions_data.iter().filter(|func_data| func_data.0 == module_id).any(|func_data| { - func_data.2.def.name.0.contents == "compute_note_hash_and_nullifier" - && func_data.2.def.parameters.len() == 5 - && match &func_data.2.def.parameters[0].typ.typ { - UnresolvedTypeData::Named(path, _, _) => path.segments.last().unwrap().0.contents == "AztecAddress", - _ => false, - } - && func_data.2.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement - && func_data.2.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement - && func_data.2.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement - // checks if the 5th parameter is an array and the Box in - // Array(Option, Box) contains only fields - && match &func_data.2.def.parameters[4].typ.typ { - UnresolvedTypeData::Array(_, inner_type) => { - matches!(inner_type.typ, UnresolvedTypeData::FieldElement) - }, - _ => false, - } - // We check the return type the same way as we did the 5th parameter - && match &func_data.2.def.return_type { - FunctionReturnType::Default(_) => false, - FunctionReturnType::Ty(unresolved_type) => { - match &unresolved_type.typ { - UnresolvedTypeData::Array(_, inner_type) => { - matches!(inner_type.typ, UnresolvedTypeData::FieldElement) - }, - _ => false, - } - } - } - }) -} - -/// Checks if an attribute is a custom attribute with a specific name -fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool { - if let SecondaryAttribute::Custom(custom_attr) = attr { - custom_attr.as_str() == attribute_name - } else { - false - } -} - /// Determines if ast nodes are annotated with aztec attributes. /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise @@ -473,10 +151,7 @@ fn transform_module( transform_vm_function(func, storage_defined) .map_err(|err| (err, crate_graph.root_file_id))?; has_transformed_module = true; - } - - // Add the storage struct to the beginning of the function if it is unconstrained in an aztec contract - if storage_defined && func.def.is_unconstrained { + } else if storage_defined && func.def.is_unconstrained { transform_unconstrained(func); has_transformed_module = true; } @@ -518,1402 +193,33 @@ fn transform_module( Ok(has_transformed_module) } -/// Auxiliary function to generate the storage constructor for a given field, using -/// the Storage definition as a reference. Supports nesting. -fn generate_storage_field_constructor( - (type_ident, unresolved_type): &(Ident, UnresolvedType), - slot: Expression, -) -> Result { - let typ = &unresolved_type.typ; - match typ { - UnresolvedTypeData::Named(path, generics, _) => { - let mut new_path = path.clone().to_owned(); - new_path.segments.push(ident("new")); - match path.segments.last().unwrap().0.contents.as_str() { - "Map" => Ok(call( - variable_path(new_path), - vec![ - variable("context"), - slot, - lambda( - vec![ - ( - pattern("context"), - make_type(UnresolvedTypeData::Named( - chained_dep!("aztec", "context", "Context"), - vec![], - true, - )), - ), - ( - Pattern::Identifier(ident("slot")), - make_type(UnresolvedTypeData::FieldElement), - ), - ], - generate_storage_field_constructor( - &(type_ident.clone(), generics.iter().last().unwrap().clone()), - variable("slot"), - )?, - ), - ], - )), - _ => Ok(call(variable_path(new_path), vec![variable("context"), slot])), - } - } - _ => Err(AztecMacroError::UnsupportedStorageType { - typ: typ.clone(), - span: Some(type_ident.span()), - }), - } -} - -// Generates the Storage implementation block from the Storage struct definition if it does not exist -/// From: -/// -/// struct Storage { -/// a_map: Map>, -/// a_nested_map: Map>>, -/// a_field: SomeStoragePrimitive, -/// } -/// -/// To: -/// -/// impl Storage { -/// fn init(context: Context) -> Self { -/// Storage { -/// a_map: Map::new(context, 0, |context, slot| { -/// SomeStoragePrimitive::new(context, slot) -/// }), -/// a_nested_map: Map::new(context, 0, |context, slot| { -/// Map::new(context, slot, |context, slot| { -/// SomeStoragePrimitive::new(context, slot) -/// }) -/// }), -/// a_field: SomeStoragePrimitive::new(context, 0), -/// } -/// } -/// } -/// -/// Storage slots are generated as 0 and will be populated using the information from the HIR -/// at a later stage. -fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), AztecMacroError> { - let definition = - module.types.iter().find(|r#struct| r#struct.name.0.contents == "Storage").unwrap(); - - let slot_zero = expression(ExpressionKind::Literal(Literal::Integer( - FieldElement::from(i128::from(0)), - false, - ))); - - let field_constructors = definition - .fields - .iter() - .flat_map(|field| { - generate_storage_field_constructor(field, slot_zero.clone()) - .map(|expression| (field.0.clone(), expression)) - }) - .collect(); - - let storage_constructor_statement = make_statement(StatementKind::Expression(expression( - ExpressionKind::constructor((chained_path!("Storage"), field_constructors)), - ))); - - let init = NoirFunction::normal(FunctionDefinition::normal( - &ident("init"), - &vec![], - &[( - ident("context"), - make_type(UnresolvedTypeData::Named( - chained_dep!("aztec", "context", "Context"), - vec![], - true, - )), - )], - &BlockExpression(vec![storage_constructor_statement]), - &[], - &return_type(chained_path!("Self")), - )); - - let storage_impl = TypeImpl { - object_type: UnresolvedType { - typ: UnresolvedTypeData::Named(chained_path!("Storage"), vec![], true), - span: Some(Span::default()), - }, - type_span: Span::default(), - generics: vec![], - methods: vec![(init, Span::default())], - }; - module.impls.push(storage_impl); - - Ok(()) -} - -/// If it does, it will insert the following things: -/// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs -/// - Hashes all of the function input variables -/// - This instantiates a helper function -fn transform_function( - ty: &str, - func: &mut NoirFunction, - storage_defined: bool, - is_initializer: bool, - insert_init_check: bool, - is_internal: bool, -) -> Result<(), AztecMacroError> { - let context_name = format!("{}Context", ty); - let inputs_name = format!("{}ContextInputs", ty); - let return_type_name = format!("{}CircuitPublicInputs", ty); - - // Add check that msg sender equals this address and flag function as internal - if is_internal { - let is_internal_check = create_internal_check(func.name()); - func.def.body.0.insert(0, is_internal_check); - func.def.is_internal = true; - } - - // Add initialization check - if insert_init_check { - let init_check = create_init_check(); - func.def.body.0.insert(0, init_check); - } - - // Add access to the storage struct - if storage_defined { - let storage_def = abstract_storage(&ty.to_lowercase(), false); - func.def.body.0.insert(0, storage_def); - } - - // Insert the context creation as the first action - let create_context = create_context(&context_name, &func.def.parameters)?; - func.def.body.0.splice(0..0, (create_context).iter().cloned()); - - // Add the inputs to the params - let input = create_inputs(&inputs_name); - func.def.parameters.insert(0, input); - - // Abstract return types such that they get added to the kernel's return_values - if let Some(return_values) = abstract_return_values(func) { - // In case we are pushing return values to the context, we remove the statement that originated it - // This avoids running duplicate code, since blocks like if/else can be value returning statements - func.def.body.0.pop(); - // Add the new return statement - func.def.body.0.push(return_values); - } - - // Before returning mark the contract as initialized - if is_initializer { - let mark_initialized = create_mark_as_initialized(ty); - func.def.body.0.push(mark_initialized); - } - - // Push the finish method call to the end of the function - let finish_def = create_context_finish(); - func.def.body.0.push(finish_def); - - let return_type = create_return_type(&return_type_name); - func.def.return_type = return_type; - func.def.return_visibility = Visibility::Public; - - // Distinct return types are only required for private functions - // Public functions should have open auto-inferred - match ty { - "Private" => func.def.return_distinctness = Distinctness::Distinct, - "Public" => func.def.is_open = true, - _ => (), - } - - Ok(()) -} - -/// Transform a function to work with AVM bytecode -fn transform_vm_function( - func: &mut NoirFunction, - _storage_defined: bool, -) -> Result<(), AztecMacroError> { - // Push Avm context creation to the beginning of the function - let create_context = create_avm_context()?; - func.def.body.0.insert(0, create_context); - - // We want the function to be seen as a public function - func.def.is_open = true; - - // NOTE: the line below is a temporary hack to trigger external transpilation tools - // It will be removed once the transpiler is integrated into the Noir compiler - func.def.name.0.contents = format!("avm_{}", func.def.name.0.contents); - Ok(()) -} - -/// Transform Unconstrained -/// -/// Inserts the following code at the beginning of an unconstrained function -/// ```noir -/// let storage = Storage::init(Context::none()); -/// ``` -/// -/// This will allow developers to access their contract' storage struct in unconstrained functions -fn transform_unconstrained(func: &mut NoirFunction) { - func.def.body.0.insert(0, abstract_storage("Unconstrained", true)); -} - -fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec { - context - .def_map(crate_id) - .expect("ICE: Missing crate in def_map") - .modules() - .iter() - .flat_map(|(_, module)| { - module.type_definitions().filter_map(|typ| { - if let ModuleDefId::TypeId(struct_id) = typ { - Some(struct_id) - } else { - None - } - }) - }) - .collect() -} - -fn collect_traits(context: &HirContext) -> Vec { - let crates = context.crates(); - crates - .flat_map(|crate_id| context.def_map(&crate_id).map(|def_map| def_map.modules())) - .flatten() - .flat_map(|module| { - module.type_definitions().filter_map(|typ| { - if let ModuleDefId::TraitId(struct_id) = typ { - Some(struct_id) - } else { - None - } - }) - }) - .collect() -} - -/// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. -fn transform_event( - struct_id: StructId, - interner: &mut NodeInterner, -) -> Result<(), (AztecMacroError, FileId)> { - let struct_type = interner.get_struct(struct_id); - let selector_id = interner - .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) - .ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Selector method not found".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - let selector_function = interner.function(&selector_id); - - let compute_selector_statement = interner.statement( - selector_function.block(interner).statements().first().ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement not found".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?, - ); - - let compute_selector_expression = match compute_selector_statement { - HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { - HirExpression::Call(hir_call_expression) => Some(hir_call_expression), - _ => None, - }, - _ => None, - } - .ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement is not a call expression".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - - let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { - let error = AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Compute selector statement is not a call expression".to_owned(), - }; - (error, struct_type.borrow().location.file) - })?; - - match interner.expression(first_arg_id) { - HirExpression::Literal(HirLiteral::Str(signature)) - if signature == SIGNATURE_PLACEHOLDER => - { - let selector_literal_id = *first_arg_id; - - let structure = interner.get_struct(struct_id); - let signature = event_signature(&structure.borrow()); - interner.update_expression(selector_literal_id, |expr| { - *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); - }); - - // Also update the type! It might have a different length now than the placeholder. - interner.push_expr_type( - selector_literal_id, - Type::String(Box::new(Type::Constant(signature.len() as u64))), - ); - Ok(()) - } - _ => Err(( - AztecMacroError::EventError { - span: struct_type.borrow().location.span, - message: "Signature placeholder literal does not match".to_owned(), - }, - struct_type.borrow().location.file, - )), - } -} - -fn transform_events( +fn transform_collected_defs( crate_id: &CrateId, context: &mut HirContext, -) -> Result<(), (AztecMacroError, FileId)> { - for struct_id in collect_crate_structs(crate_id, context) { - let attributes = context.def_interner.struct_attributes(&struct_id); - if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { - transform_event(struct_id, &mut context.def_interner)?; - } - } - Ok(()) -} - -/// Obtains the serialized length of a type that implements the Serialize trait. -fn get_serialized_length( - traits: &[TraitId], - typ: &Type, - interner: &NodeInterner, -) -> Result { - let (struct_name, maybe_stored_in_state) = match typ { - Type::Struct(struct_type, generics) => { - Ok((struct_type.borrow().name.0.contents.clone(), generics.first())) - } - _ => Err(AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some("State storage variable must be a struct".to_string()), - }), - }?; - let stored_in_state = - maybe_stored_in_state.ok_or(AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some("State storage variable must be generic".to_string()), - })?; - - let is_note = traits.iter().any(|&trait_id| { - let r#trait = interner.get_trait(trait_id); - r#trait.name.0.contents == "NoteInterface" - && !interner.lookup_all_trait_implementations(stored_in_state, trait_id).is_empty() - }); - - // Maps and (private) Notes always occupy a single slot. Someone could store a Note in PublicMutable for whatever reason though. - if struct_name == "Map" || (is_note && struct_name != "PublicMutable") { - return Ok(1); - } - - let serialized_trait_impl_kind = traits - .iter() - .find_map(|&trait_id| { - let r#trait = interner.get_trait(trait_id); - if r#trait.borrow().name.0.contents == "Serialize" - && r#trait.borrow().generics.len() == 1 - { - interner - .lookup_all_trait_implementations(stored_in_state, trait_id) - .into_iter() - .next() - } else { - None - } - }) - .ok_or(AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some("Stored data must implement Serialize trait".to_string()), - })?; - - let serialized_trait_impl_id = match serialized_trait_impl_kind { - TraitImplKind::Normal(trait_impl_id) => Ok(trait_impl_id), - _ => Err(AztecMacroError::CouldNotAssignStorageSlots { secondary_message: None }), - }?; - - let serialized_trait_impl_shared = interner.get_trait_implementation(*serialized_trait_impl_id); - let serialized_trait_impl = serialized_trait_impl_shared.borrow(); - - match serialized_trait_impl.trait_generics.first().unwrap() { - Type::Constant(value) => Ok(*value), - _ => Err(AztecMacroError::CouldNotAssignStorageSlots { secondary_message: None }), - } -} - -/// Assigns storage slots to the storage struct fields based on the serialized length of the types. This automatic assignment -/// will only trigger if the assigned storage slot is invalid (0 as generated by generate_storage_implementation) -fn assign_storage_slots( - crate_id: &CrateId, - context: &mut HirContext, -) -> Result<(), (AztecMacroError, FileId)> { - let traits: Vec<_> = collect_traits(context); - for struct_id in collect_crate_structs(crate_id, context) { - let interner: &mut NodeInterner = context.def_interner.borrow_mut(); - let r#struct = interner.get_struct(struct_id); - let file_id = r#struct.borrow().location.file; - if r#struct.borrow().name.0.contents == "Storage" && r#struct.borrow().id.krate().is_root() - { - let init_id = interner - .lookup_method( - &Type::Struct(interner.get_struct(struct_id), vec![]), - struct_id, - "init", - false, - ) - .ok_or(( - AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some( - "Storage struct must have an init function".to_string(), - ), - }, - file_id, - ))?; - let init_function = interner.function(&init_id).block(interner); - let init_function_statement_id = init_function.statements().first().ok_or(( - AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some("Init storage statement not found".to_string()), - }, - file_id, - ))?; - let storage_constructor_statement = interner.statement(init_function_statement_id); - - let storage_constructor_expression = match storage_constructor_statement { - HirStatement::Expression(expression_id) => { - match interner.expression(&expression_id) { - HirExpression::Constructor(hir_constructor_expression) => { - Ok(hir_constructor_expression) - } - _ => Err((AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some( - "Storage constructor statement must be a constructor expression" - .to_string(), - ), - }, file_id)) - } - } - _ => Err(( - AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some( - "Storage constructor statement must be an expression".to_string(), - ), - }, - file_id, - )), - }?; - - let mut storage_slot: u64 = 1; - for (index, (_, expr_id)) in storage_constructor_expression.fields.iter().enumerate() { - let fields = r#struct.borrow().get_fields(&[]); - let (_, field_type) = fields.get(index).unwrap(); - let new_call_expression = match interner.expression(expr_id) { - HirExpression::Call(hir_call_expression) => Ok(hir_call_expression), - _ => Err(( - AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some( - "Storage field initialization expression is not a call expression" - .to_string(), - ), - }, - file_id, - )), - }?; - - let slot_arg_expression = interner.expression(&new_call_expression.arguments[1]); - - let current_storage_slot = match slot_arg_expression { - HirExpression::Literal(HirLiteral::Integer(slot, _)) => Ok(slot.to_u128()), - _ => Err(( - AztecMacroError::CouldNotAssignStorageSlots { - secondary_message: Some( - "Storage slot argument expression must be a literal integer" - .to_string(), - ), - }, - file_id, - )), - }?; - - if current_storage_slot != 0 { - continue; - } - - let type_serialized_len = get_serialized_length(&traits, field_type, interner) - .map_err(|err| (err, file_id))?; - interner.update_expression(new_call_expression.arguments[1], |expr| { - *expr = HirExpression::Literal(HirLiteral::Integer( - FieldElement::from(u128::from(storage_slot)), - false, - )); - }); - - storage_slot += type_serialized_len; - } - } - } - Ok(()) -} - -const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; - -/// Generates the impl for an event selector -/// -/// Inserts the following code: -/// ```noir -/// impl SomeStruct { -/// fn selector() -> FunctionSelector { -/// aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature("SIGNATURE_PLACEHOLDER") -/// } -/// } -/// ``` -/// -/// This allows developers to emit events without having to write the signature of the event every time they emit it. -/// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. -/// It'll get resolved after by transforming the HIR. -fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { - let struct_type = - make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); - - let selector_path = - chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); - let mut from_signature_path = selector_path.clone(); - from_signature_path.segments.push(ident("from_signature")); - - let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call( - variable_path(from_signature_path), - vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))], - )))]); - - // Define `FunctionSelector` return type - let return_type = - FunctionReturnType::Ty(make_type(UnresolvedTypeData::Named(selector_path, vec![], true))); - - let mut selector_fn_def = FunctionDefinition::normal( - &ident("selector"), - &vec![], - &[], - &selector_fun_body, - &[], - &return_type, - ); - - selector_fn_def.visibility = ItemVisibility::Public; - - // Seems to be necessary on contract modules - selector_fn_def.return_visibility = Visibility::Public; - - TypeImpl { - object_type: struct_type, - type_span: structure.span, - generics: vec![], - methods: vec![(NoirFunction::normal(selector_fn_def), Span::default())], - } -} - -/// Helper function that returns what the private context would look like in the ast -/// This should make it available to be consumed within aztec private annotated functions. -/// -/// The replaced code: -/// ```noir -/// /// Before -/// fn foo(inputs: PrivateContextInputs) { -/// // ... -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() { -/// // ... -/// } -fn create_inputs(ty: &str) -> Param { - let context_ident = ident("inputs"); - let context_pattern = Pattern::Identifier(context_ident); - - let path_snippet = ty.to_case(Case::Snake); // e.g. private_context_inputs - let type_path = chained_dep!("aztec", "context", "inputs", &path_snippet, ty); - - let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![], true)); - let visibility = Visibility::Private; - - Param { pattern: context_pattern, typ: context_type, visibility, span: Span::default() } -} - -/// Creates an initialization check to ensure that the contract has been initialized, meant to -/// be injected as the first statement of any function after the context has been created. -/// -/// ```noir -/// assert_is_initialized(&mut context); -/// ``` -fn create_init_check() -> Statement { - make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", "assert_is_initialized")), - vec![mutable_reference("context")], - ))) -} - -/// Creates a call to mark_as_initialized which emits the initialization nullifier, meant to -/// be injected as the last statement before returning in a constructor. -/// -/// ```noir -/// mark_as_initialized(&mut context); -/// ``` -fn create_mark_as_initialized(ty: &str) -> Statement { - let name = if ty == "Public" { "mark_as_initialized_public" } else { "mark_as_initialized" }; - make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", name)), - vec![mutable_reference("context")], - ))) -} - -/// Creates a check for internal functions ensuring that the caller is self. -/// -/// ```noir -/// assert(context.msg_sender() == context.this_address(), "Function can only be called internally"); -/// ``` -fn create_internal_check(fname: &str) -> Statement { - make_statement(StatementKind::Constrain(ConstrainStatement( - make_eq( - method_call(variable("context"), "msg_sender", vec![]), - method_call(variable("context"), "this_address", vec![]), - ), - Some(expression(ExpressionKind::Literal(Literal::Str(format!( - "Function {} can only be called internally", - fname - ))))), - ConstrainKind::Assert, - ))) -} - -/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be -/// appended into the args hash object. -/// -/// The replaced code: -/// ```noir -/// #[aztec(private)] -/// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the hasher object -/// let mut hasher = Hasher::new(); -/// -/// // struct inputs call serialize on them to add an array of fields -/// hasher.add_multiple(structInput.serialize()); -/// -/// // Array inputs are iterated over and each element is added to the hasher (as a field) -/// for i in 0..arrayInput.len() { -/// hasher.add(arrayInput[i] as Field); -/// } -/// // Field inputs are added to the hasher -/// hasher.add({ident}); -/// -/// // Create the context -/// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hasher.hash()); -/// } -/// ``` -fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { - let mut injected_expressions: Vec = vec![]; - - // `let mut hasher = Hasher::new();` - let let_hasher = mutable_assignment( - "hasher", // Assigned to - call( - variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args - ), - ); - - // Completes: `let mut hasher = Hasher::new();` - injected_expressions.push(let_hasher); - - // Iterate over each of the function parameters, adding to them to the hasher - for Param { pattern, typ, span, .. } in params { - match pattern { - Pattern::Identifier(identifier) => { - // Match the type to determine the padding to do - let unresolved_type = &typ.typ; - let expression = match unresolved_type { - // `hasher.add_multiple({ident}.serialize())` - UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier), - UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type) - } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` - UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier) - } - UnresolvedTypeData::String(..) => { - let (var_bytes, id) = str_to_bytes(identifier); - injected_expressions.push(var_bytes); - add_array_to_hasher( - &id, - &UnresolvedType { - typ: UnresolvedTypeData::Integer( - Signedness::Unsigned, - noirc_frontend::IntegerBitSize::ThirtyTwo, - ), - span: None, - }, - ) - } - _ => { - return Err(AztecMacroError::UnsupportedFunctionArgumentType { - typ: unresolved_type.clone(), - span: *span, - }) - } - }; - injected_expressions.push(expression); - } - _ => todo!(), // Maybe unreachable? - } - } - - // Create the inputs to the context - let inputs_expression = variable("inputs"); - // `hasher.hash()` - let hash_call = method_call( - variable("hasher"), // variable - "hash", // method name - vec![], // args - ); - - let path_snippet = ty.to_case(Case::Snake); // e.g. private_context - - // let mut context = {ty}::new(inputs, hash); - let let_context = mutable_assignment( - "context", // Assigned to - call( - variable_path(chained_dep!("aztec", "context", &path_snippet, ty, "new")), // Path - vec![inputs_expression, hash_call], // args - ), - ); - injected_expressions.push(let_context); - - // Return all expressions that will be injected by the hasher - Ok(injected_expressions) -} - -/// Creates an mutable avm context -/// -/// ```noir -/// /// Before -/// #[aztec(public-vm)] -/// fn foo() -> Field { -/// let mut context = aztec::context::AVMContext::new(); -/// let timestamp = context.timestamp(); -/// // ... -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() -> Field { -/// let mut timestamp = context.timestamp(); -/// // ... -/// } -fn create_avm_context() -> Result { - let let_context = mutable_assignment( - "context", // Assigned to - call( - variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path - vec![], // args - ), - ); - - Ok(let_context) -} - -/// Abstract Return Type -/// -/// This function intercepts the function's current return type and replaces it with pushes -/// To the kernel -/// -/// The replaced code: -/// ```noir -/// /// Before -/// #[aztec(private)] -/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { -/// // ... -/// let my_return_value: Field = 10; -/// context.return_values.push(my_return_value); -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() -> Field { -/// // ... -/// let my_return_value: Field = 10; -/// my_return_value -/// } -/// ``` -/// Similarly; Structs will be pushed to the context, after serialize() is called on them. -/// Arrays will be iterated over and each element will be pushed to the context. -/// Any primitive type that can be cast will be casted to a field and pushed to the context. -fn abstract_return_values(func: &NoirFunction) -> Option { - let current_return_type = func.return_type().typ; - let last_statement = func.def.body.0.last()?; - - // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size - // Doesn't need done until we have settled on a kernel size - // TODO: support tuples here and in inputs -> convert into an issue - // Check if the return type is an expression, if it is, we can handle it - match last_statement { - Statement { kind: StatementKind::Expression(expression), .. } => { - match current_return_type { - // Call serialize on structs, push the whole array, calling push_array - UnresolvedTypeData::Named(..) => Some(make_struct_return_type(expression.clone())), - UnresolvedTypeData::Array(..) => Some(make_array_return_type(expression.clone())), - // Cast these types to a field before pushing - UnresolvedTypeData::Bool | UnresolvedTypeData::Integer(..) => { - Some(make_castable_return_type(expression.clone())) - } - UnresolvedTypeData::FieldElement => Some(make_return_push(expression.clone())), - _ => None, - } - } - _ => None, - } -} - -/// Abstract storage -/// -/// For private functions: -/// ```noir -/// #[aztec(private)] -/// fn lol() { -/// let storage = Storage::init(Context::private(context)); -/// } -/// ``` -/// -/// For public functions: -/// ```noir -/// #[aztec(public)] -/// fn lol() { -/// let storage = Storage::init(Context::public(context)); -/// } -/// ``` -/// -/// For unconstrained functions: -/// ```noir -/// unconstrained fn lol() { -/// let storage = Storage::init(Context::none()); -/// } -fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { - let init_context_call = if unconstrained { - call( - variable_path(chained_dep!("aztec", "context", "Context", "none")), // Path - vec![], // args + collected_trait_impls: &[UnresolvedTraitImpl], + collected_functions: &mut [UnresolvedFunctions], +) -> Result<(), (MacroError, FileId)> { + if has_aztec_dependency(crate_id, context) { + inject_compute_note_hash_and_nullifier( + crate_id, + context, + collected_trait_impls, + collected_functions, ) } else { - call( - variable_path(chained_dep!("aztec", "context", "Context", typ)), // Path - vec![mutable_reference("context")], // args - ) - }; - - assignment( - "storage", // Assigned to - call( - variable_path(chained_path!("Storage", "init")), // Path - vec![init_context_call], // args - ), - ) -} - -/// Context Return Values -/// -/// Creates an instance to the context return values -/// ```noir -/// `context.return_values` -/// ``` -fn context_return_values() -> Expression { - member_access("context", "return_values") -} - -fn make_statement(kind: StatementKind) -> Statement { - Statement { span: Span::default(), kind } -} - -/// Make return Push -/// -/// Translates to: -/// `context.return_values.push({push_value})` -fn make_return_push(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "push", - vec![push_value], - ))) -} - -/// Make Return push array -/// -/// Translates to: -/// `context.return_values.extend_from_array({push_value})` -fn make_return_extend_from_array(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "extend_from_array", - vec![push_value], - ))) -} - -/// Make struct return type -/// -/// Translates to: -/// ```noir -/// `context.return_values.extend_from_array({push_value}.serialize())` -fn make_struct_return_type(expression: Expression) -> Statement { - let serialized_call = method_call( - expression, // variable - "serialize", // method name - vec![], // args - ); - make_return_extend_from_array(serialized_call) -} - -/// Make array return type -/// -/// Translates to: -/// ```noir -/// for i in 0..{ident}.len() { -/// context.return_values.push({ident}[i] as Field) -/// } -/// ``` -fn make_array_return_type(expression: Expression) -> Statement { - let inner_cast_expression = - cast(index_array_variable(expression.clone(), "i"), UnresolvedTypeData::FieldElement); - let assignment = make_statement(StatementKind::Semi(method_call( - context_return_values(), // variable - "push", // method name - vec![inner_cast_expression], - ))); - - create_loop_over(expression, vec![assignment]) -} - -/// Castable return type -/// -/// Translates to: -/// ```noir -/// context.return_values.push({ident} as Field) -/// ``` -fn make_castable_return_type(expression: Expression) -> Statement { - // Cast these types to a field before pushing - let cast_expression = cast(expression, UnresolvedTypeData::FieldElement); - make_return_push(cast_expression) -} - -/// Create Return Type -/// -/// Public functions return protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs while -/// private functions return protocol_types::abis::private_circuit_public_inputs::::PrivateCircuitPublicInputs -/// -/// This call constructs an ast token referencing the above types -/// The name is set in the function above `transform`, hence the -/// whole token name is passed in -/// -/// The replaced code: -/// ```noir -/// -/// /// Before -/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { -/// // ... -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() { -/// // ... -/// } -fn create_return_type(ty: &str) -> FunctionReturnType { - let path_snippet = ty.to_case(Case::Snake); // e.g. private_circuit_public_inputs or public_circuit_public_inputs - let return_path = chained_dep!("aztec", "protocol_types", "abis", &path_snippet, ty); - return_type(return_path) -} - -/// Create Context Finish -/// -/// Each aztec function calls `context.finish()` at the end of a function -/// to return values required by the kernel. -/// -/// The replaced code: -/// ```noir -/// /// Before -/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { -/// // ... -/// context.finish() -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() { -/// // ... -/// } -fn create_context_finish() -> Statement { - let method_call = method_call( - variable("context"), // variable - "finish", // method name - vec![], // args - ); - make_statement(StatementKind::Expression(method_call)) + Ok(()) + } } // -// Methods to create hasher inputs +// Transform Hir Nodes for Aztec // -fn add_struct_to_hasher(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the hasher - let serialized_call = method_call( - variable_path(path(identifier.clone())), // variable - "serialize", // method name - vec![], // args - ); - - make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add_multiple", // method name - vec![serialized_call], // args - ))) -} - -fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { - // let identifier_as_bytes = identifier.as_bytes(); - let var = variable_ident(identifier.clone()); - let contents = if let ExpressionKind::Variable(p) = &var.kind { - p.segments.first().cloned().unwrap_or_else(|| panic!("No segments")).0.contents - } else { - panic!("Unexpected identifier type") - }; - let bytes_name = format!("{}_bytes", contents); - let var_bytes = assignment(&bytes_name, method_call(var, "as_bytes", vec![])); - let id = Ident::new(bytes_name, Span::default()); - - (var_bytes, id) -} - -fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher - // casted to a field - let span = var.span; - - // `array.len()` - let end_range_expression = method_call( - var, // variable - "len", // method name - vec![], // args - ); - - // What will be looped over - // - `hasher.add({ident}[i] as Field)` - let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); - - // `for i in 0..{ident}.len()` - make_statement(StatementKind::For(ForLoopStatement { - range: ForRange::Range( - expression(ExpressionKind::Literal(Literal::Integer( - FieldElement::from(i128::from(0)), - false, - ))), - end_range_expression, - ), - identifier: ident("i"), - block: for_loop_block, - span, - })) -} - -fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher - // casted to a field - - // Wrap in the semi thing - does that mean ended with semi colon? - // `hasher.add({ident}[i] as Field)` - - let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, hasher_method_name) = match arr_type.typ { - UnresolvedTypeData::Named(..) => { - let hasher_method_name = "add_multiple".to_owned(); - let call = method_call( - // All serialize on each element - arr_index, // variable - "serialize", // method name - vec![], // args - ); - (call, hasher_method_name) - } - _ => { - let hasher_method_name = "add".to_owned(); - let call = cast( - arr_index, // lhs - `ident[i]` - UnresolvedTypeData::FieldElement, // cast to - `as Field` - ); - (call, hasher_method_name) - } - }; - - let block_statement = make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - &hasher_method_name, // method name - vec![add_expression], - ))); - - create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) -} - -fn add_field_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident})` - let ident = variable_path(path(identifier.clone())); - make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![ident], // args - ))) -} - -fn add_cast_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident} as Field)` - // `{ident} as Field` - let cast_operation = cast( - variable_path(path(identifier.clone())), // lhs - UnresolvedTypeData::FieldElement, // rhs - ); - - // `hasher.add({ident} as Field)` - make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![cast_operation], // args - ))) -} - -/// Computes the aztec signature for a resolved type. -fn signature_of_type(typ: &Type) -> String { - match typ { - Type::Integer(Signedness::Signed, bit_size) => format!("i{}", bit_size), - Type::Integer(Signedness::Unsigned, bit_size) => format!("u{}", bit_size), - Type::FieldElement => "Field".to_owned(), - Type::Bool => "bool".to_owned(), - Type::Array(len, typ) => { - if let Type::Constant(len) = **len { - format!("[{};{len}]", signature_of_type(typ)) - } else { - unimplemented!("Cannot generate signature for array with length type {:?}", typ) - } - } - Type::Struct(def, args) => { - let fields = def.borrow().get_fields(args); - let fields = vecmap(fields, |(_, typ)| signature_of_type(&typ)); - format!("({})", fields.join(",")) - } - Type::Tuple(types) => { - let fields = vecmap(types, signature_of_type); - format!("({})", fields.join(",")) - } - _ => unimplemented!("Cannot generate signature for type {:?}", typ), - } -} - -/// Computes the signature for a resolved event type. -/// It has the form 'EventName(Field,(Field),[u8;2])' -fn event_signature(event: &StructType) -> String { - let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); - format!("{}({})", event.name.0.contents, fields.join(",")) -} - -fn inject_compute_note_hash_and_nullifier( +/// Completes the Hir with data gathered from type resolution +fn transform_hir( crate_id: &CrateId, context: &mut HirContext, - unresolved_traits_impls: &[UnresolvedTraitImpl], - collected_functions: &mut [UnresolvedFunctions], -) -> Result<(), (MacroError, FileId)> { - // We first fetch modules in this crate which correspond to contracts, along with their file id. - let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context - .def_map(crate_id) - .expect("ICE: Missing crate in def_map") - .modules() - .iter() - .filter(|(_, module)| module.is_contract) - .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) - .collect(); - - // If the current crate does not contain a contract module we simply skip it. - if contract_module_file_ids.is_empty() { - return Ok(()); - } else if contract_module_file_ids.len() != 1 { - panic!("Found multiple contracts in the same crate"); - } - - let (module_id, file_id) = contract_module_file_ids[0]; - - // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an - // escape hatch for this mechanism. - // TODO(#4647): improve this diagnosis and error messaging. - if collected_functions.iter().any(|coll_funcs_data| { - check_for_compute_note_hash_and_nullifier_definition(&coll_funcs_data.functions, module_id) - }) { - return Ok(()); - } - - // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the - // contract might use. These are the types that implement the NoteInterface trait, which provides the - // get_note_type_id function. - let note_types = fetch_struct_trait_impls(context, unresolved_traits_impls, "NoteInterface"); - - // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. - let func = generate_compute_note_hash_and_nullifier(¬e_types); - - // And inject the newly created function into the contract. - - // TODO(#4373): We don't have a reasonable location for the source code of this autogenerated function, so we simply - // pass an empty span. This function should not produce errors anyway so this should not matter. - let location = Location::new(Span::empty(0), file_id); - - // These are the same things the ModCollector does when collecting functions: we push the function to the - // NodeInterner, declare it in the module (which checks for duplicate definitions), and finally add it to the list - // on collected but unresolved functions. - - let func_id = context.def_interner.push_empty_fn(); - context.def_interner.push_function( - func_id, - &func.def, - ModuleId { krate: *crate_id, local_id: module_id }, - location, - ); - - context.def_map_mut(crate_id).unwrap() - .modules_mut()[module_id.0] - .declare_function( - func.name_ident().clone(), func_id - ).expect( - "Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647." - ); - - collected_functions - .iter_mut() - .find(|fns| fns.file_id == file_id) - .expect("ICE: no functions found in contract file") - .push_fn(module_id, func_id, func.clone()); - - Ok(()) -} - -// Fetches the name of all structs that implement trait_name, both in the current crate and all of its dependencies. -fn fetch_struct_trait_impls( - context: &mut HirContext, - unresolved_traits_impls: &[UnresolvedTraitImpl], - trait_name: &str, -) -> Vec { - let mut struct_typenames: Vec = Vec::new(); - - // These structs can be declared in either external crates or the current one. External crates that contain - // dependencies have already been processed and resolved, but are available here via the NodeInterner. Note that - // crates on which the current crate does not depend on may not have been processed, and will be ignored. - for trait_impl_id in 0..context.def_interner.next_trait_impl_id().0 { - let trait_impl = &context.def_interner.get_trait_implementation(TraitImplId(trait_impl_id)); - - if trait_impl.borrow().ident.0.contents == *trait_name { - if let Type::Struct(s, _) = &trait_impl.borrow().typ { - struct_typenames.push(s.borrow().name.0.contents.clone()); - } else { - panic!("Found impl for {} on non-Struct", trait_name); - } - } - } - - // This crate's traits and impls have not yet been resolved, so we look for impls in unresolved_trait_impls. - struct_typenames.extend( - unresolved_traits_impls - .iter() - .filter(|trait_impl| { - trait_impl - .trait_path - .segments - .last() - .expect("ICE: empty trait_impl path") - .0 - .contents - == *trait_name - }) - .filter_map(|trait_impl| match &trait_impl.object_type.typ { - UnresolvedTypeData::Named(path, _, _) => { - Some(path.segments.last().unwrap().0.contents.clone()) - } - _ => None, - }), - ); - - struct_typenames -} - -fn generate_compute_note_hash_and_nullifier(note_types: &Vec) -> NoirFunction { - let function_source = generate_compute_note_hash_and_nullifier_source(note_types); - - let (function_ast, errors) = parse_program(&function_source); - if !errors.is_empty() { - dbg!(errors.clone()); - } - assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); - - let mut function_ast = function_ast.into_sorted(); - function_ast.functions.remove(0) -} - -fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> String { - // TODO(#4649): The serialized_note parameter is a fixed-size array, but we don't know what length it should have. - // For now we hardcode it to 20, which is the same as MAX_NOTE_FIELDS_LENGTH. - - if note_types.is_empty() { - // TODO(#4520): Even if the contract does not include any notes, other parts of the stack expect for this - // function to exist, so we include a dummy version. We likely should error out here instead. - " - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 20] - ) -> pub [Field; 4] { - [0, 0, 0, 0] - }" - .to_string() - } else { - // For contracts that include notes we do a simple if-else chain comparing note_type_id with the different - // get_note_type_id of each of the note types. - - let if_statements: Vec = note_types.iter().map(|note_type| format!( - "if (note_type_id == {0}::get_note_type_id()) {{ - dep::aztec::note::utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) - }}" - , note_type)).collect(); - - // TODO(#4520): error out on the else instead of returning a zero array - let full_if_statement = if_statements.join(" else ") - + " - else { - [0, 0, 0, 0] - }"; - - format!( - " - unconstrained fn compute_note_hash_and_nullifier( - contract_address: AztecAddress, - nonce: Field, - storage_slot: Field, - note_type_id: Field, - serialized_note: [Field; 20] - ) -> pub [Field; 4] {{ - let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); - - {} - }}", - full_if_statement - ) - } +) -> Result<(), (AztecMacroError, FileId)> { + transform_events(crate_id, context)?; + assign_storage_slots(crate_id, context) } diff --git a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs new file mode 100644 index 00000000000..4f8f3f19ab8 --- /dev/null +++ b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs @@ -0,0 +1,195 @@ +use noirc_errors::{Location, Span}; +use noirc_frontend::{ + graph::CrateId, + hir::{ + def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, + def_map::{LocalModuleId, ModuleId}, + }, + macros_api::{FileId, HirContext, MacroError}, + node_interner::FuncId, + parse_program, FunctionReturnType, NoirFunction, UnresolvedTypeData, +}; + +use crate::utils::hir_utils::fetch_struct_trait_impls; + +// Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined +fn check_for_compute_note_hash_and_nullifier_definition( + functions_data: &[(LocalModuleId, FuncId, NoirFunction)], + module_id: LocalModuleId, +) -> bool { + functions_data.iter().filter(|func_data| func_data.0 == module_id).any(|func_data| { + func_data.2.def.name.0.contents == "compute_note_hash_and_nullifier" + && func_data.2.def.parameters.len() == 5 + && match &func_data.2.def.parameters[0].typ.typ { + UnresolvedTypeData::Named(path, _, _) => path.segments.last().unwrap().0.contents == "AztecAddress", + _ => false, + } + && func_data.2.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement + && func_data.2.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement + // checks if the 5th parameter is an array and the Box in + // Array(Option, Box) contains only fields + && match &func_data.2.def.parameters[4].typ.typ { + UnresolvedTypeData::Array(_, inner_type) => { + matches!(inner_type.typ, UnresolvedTypeData::FieldElement) + }, + _ => false, + } + // We check the return type the same way as we did the 5th parameter + && match &func_data.2.def.return_type { + FunctionReturnType::Default(_) => false, + FunctionReturnType::Ty(unresolved_type) => { + match &unresolved_type.typ { + UnresolvedTypeData::Array(_, inner_type) => { + matches!(inner_type.typ, UnresolvedTypeData::FieldElement) + }, + _ => false, + } + } + } + }) +} + +pub fn inject_compute_note_hash_and_nullifier( + crate_id: &CrateId, + context: &mut HirContext, + unresolved_traits_impls: &[UnresolvedTraitImpl], + collected_functions: &mut [UnresolvedFunctions], +) -> Result<(), (MacroError, FileId)> { + // We first fetch modules in this crate which correspond to contracts, along with their file id. + let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context + .def_map(crate_id) + .expect("ICE: Missing crate in def_map") + .modules() + .iter() + .filter(|(_, module)| module.is_contract) + .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) + .collect(); + + // If the current crate does not contain a contract module we simply skip it. + if contract_module_file_ids.is_empty() { + return Ok(()); + } else if contract_module_file_ids.len() != 1 { + panic!("Found multiple contracts in the same crate"); + } + + let (module_id, file_id) = contract_module_file_ids[0]; + + // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an + // escape hatch for this mechanism. + // TODO(#4647): improve this diagnosis and error messaging. + if collected_functions.iter().any(|coll_funcs_data| { + check_for_compute_note_hash_and_nullifier_definition(&coll_funcs_data.functions, module_id) + }) { + return Ok(()); + } + + // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the + // contract might use. These are the types that implement the NoteInterface trait, which provides the + // get_note_type_id function. + let note_types = fetch_struct_trait_impls(context, unresolved_traits_impls, "NoteInterface"); + + // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. + let func = generate_compute_note_hash_and_nullifier(¬e_types); + + // And inject the newly created function into the contract. + + // TODO(#4373): We don't have a reasonable location for the source code of this autogenerated function, so we simply + // pass an empty span. This function should not produce errors anyway so this should not matter. + let location = Location::new(Span::empty(0), file_id); + + // These are the same things the ModCollector does when collecting functions: we push the function to the + // NodeInterner, declare it in the module (which checks for duplicate definitions), and finally add it to the list + // on collected but unresolved functions. + + let func_id = context.def_interner.push_empty_fn(); + context.def_interner.push_function( + func_id, + &func.def, + ModuleId { krate: *crate_id, local_id: module_id }, + location, + ); + + context.def_map_mut(crate_id).unwrap() + .modules_mut()[module_id.0] + .declare_function( + func.name_ident().clone(), func_id + ).expect( + "Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647." + ); + + collected_functions + .iter_mut() + .find(|fns| fns.file_id == file_id) + .expect("ICE: no functions found in contract file") + .push_fn(module_id, func_id, func.clone()); + + Ok(()) +} + +fn generate_compute_note_hash_and_nullifier(note_types: &Vec) -> NoirFunction { + let function_source = generate_compute_note_hash_and_nullifier_source(note_types); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors.clone()); + } + assert_eq!(errors.len(), 0, "Failed to parse Noir macro code. This is either a bug in the compiler or the Noir macro code"); + + let mut function_ast = function_ast.into_sorted(); + function_ast.functions.remove(0) +} + +fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> String { + // TODO(#4649): The serialized_note parameter is a fixed-size array, but we don't know what length it should have. + // For now we hardcode it to 20, which is the same as MAX_NOTE_FIELDS_LENGTH. + + if note_types.is_empty() { + // Even if the contract does not include any notes, other parts of the stack expect for this function to exist, + // so we include a dummy version. + " + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; 20] + ) -> pub [Field; 4] { + assert(false, \"This contract does not use private notes\"); + [0, 0, 0, 0] + }" + .to_string() + } else { + // For contracts that include notes we do a simple if-else chain comparing note_type_id with the different + // get_note_type_id of each of the note types. + + let if_statements: Vec = note_types.iter().map(|note_type| format!( + "if (note_type_id == {0}::get_note_type_id()) {{ + dep::aztec::note::utils::compute_note_hash_and_nullifier({0}::deserialize_content, note_header, serialized_note) + }}" + , note_type)).collect(); + + let full_if_statement = if_statements.join(" else ") + + " + else { + assert(false, \"Unknown note type ID\"); + [0, 0, 0, 0] + }"; + + format!( + " + unconstrained fn compute_note_hash_and_nullifier( + contract_address: AztecAddress, + nonce: Field, + storage_slot: Field, + note_type_id: Field, + serialized_note: [Field; 20] + ) -> pub [Field; 4] {{ + let note_header = dep::aztec::prelude::NoteHeader::new(contract_address, nonce, storage_slot); + + {} + }}", + full_if_statement + ) + } +} diff --git a/aztec_macros/src/transforms/events.rs b/aztec_macros/src/transforms/events.rs new file mode 100644 index 00000000000..b02709efacb --- /dev/null +++ b/aztec_macros/src/transforms/events.rs @@ -0,0 +1,178 @@ +use iter_extended::vecmap; +use noirc_errors::Span; +use noirc_frontend::{ + graph::CrateId, + macros_api::{ + BlockExpression, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, + NoirStruct, PathKind, StatementKind, StructId, StructType, Type, TypeImpl, + UnresolvedTypeData, + }, + token::SecondaryAttribute, + ExpressionKind, FunctionDefinition, FunctionReturnType, ItemVisibility, Literal, NoirFunction, + Visibility, +}; + +use crate::{ + chained_dep, + utils::{ + ast_utils::{ + call, expression, ident, ident_path, make_statement, make_type, path, variable_path, + }, + constants::SIGNATURE_PLACEHOLDER, + errors::AztecMacroError, + hir_utils::{collect_crate_structs, signature_of_type}, + }, +}; + +/// Generates the impl for an event selector +/// +/// Inserts the following code: +/// ```noir +/// impl SomeStruct { +/// fn selector() -> FunctionSelector { +/// aztec::protocol_types::abis::function_selector::FunctionSelector::from_signature("SIGNATURE_PLACEHOLDER") +/// } +/// } +/// ``` +/// +/// This allows developers to emit events without having to write the signature of the event every time they emit it. +/// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. +/// It'll get resolved after by transforming the HIR. +pub fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { + let struct_type = + make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); + + let selector_path = + chained_dep!("aztec", "protocol_types", "abis", "function_selector", "FunctionSelector"); + let mut from_signature_path = selector_path.clone(); + from_signature_path.segments.push(ident("from_signature")); + + let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call( + variable_path(from_signature_path), + vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))], + )))]); + + // Define `FunctionSelector` return type + let return_type = + FunctionReturnType::Ty(make_type(UnresolvedTypeData::Named(selector_path, vec![], true))); + + let mut selector_fn_def = FunctionDefinition::normal( + &ident("selector"), + &vec![], + &[], + &selector_fun_body, + &[], + &return_type, + ); + + selector_fn_def.visibility = ItemVisibility::Public; + + // Seems to be necessary on contract modules + selector_fn_def.return_visibility = Visibility::Public; + + TypeImpl { + object_type: struct_type, + type_span: structure.span, + generics: vec![], + methods: vec![(NoirFunction::normal(selector_fn_def), Span::default())], + } +} + +/// Computes the signature for a resolved event type. +/// It has the form 'EventName(Field,(Field),[u8;2])' +fn event_signature(event: &StructType) -> String { + let fields = vecmap(event.get_fields(&[]), |(_, typ)| signature_of_type(&typ)); + format!("{}({})", event.name.0.contents, fields.join(",")) +} + +/// Substitutes the signature literal that was introduced in the selector method previously with the actual signature. +fn transform_event( + struct_id: StructId, + interner: &mut NodeInterner, +) -> Result<(), (AztecMacroError, FileId)> { + let struct_type = interner.get_struct(struct_id); + let selector_id = interner + .lookup_method(&Type::Struct(struct_type.clone(), vec![]), struct_id, "selector", false) + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Selector method not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + let selector_function = interner.function(&selector_id); + + let compute_selector_statement = interner.statement( + selector_function.block(interner).statements().first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement not found".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?, + ); + + let compute_selector_expression = match compute_selector_statement { + HirStatement::Expression(expression_id) => match interner.expression(&expression_id) { + HirExpression::Call(hir_call_expression) => Some(hir_call_expression), + _ => None, + }, + _ => None, + } + .ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + let first_arg_id = compute_selector_expression.arguments.first().ok_or_else(|| { + let error = AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Compute selector statement is not a call expression".to_owned(), + }; + (error, struct_type.borrow().location.file) + })?; + + match interner.expression(first_arg_id) { + HirExpression::Literal(HirLiteral::Str(signature)) + if signature == SIGNATURE_PLACEHOLDER => + { + let selector_literal_id = *first_arg_id; + + let structure = interner.get_struct(struct_id); + let signature = event_signature(&structure.borrow()); + interner.update_expression(selector_literal_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Str(signature.clone())); + }); + + // Also update the type! It might have a different length now than the placeholder. + interner.push_expr_type( + selector_literal_id, + Type::String(Box::new(Type::Constant(signature.len() as u64))), + ); + Ok(()) + } + _ => Err(( + AztecMacroError::EventError { + span: struct_type.borrow().location.span, + message: "Signature placeholder literal does not match".to_owned(), + }, + struct_type.borrow().location.file, + )), + } +} + +pub fn transform_events( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + for struct_id in collect_crate_structs(crate_id, context) { + let attributes = context.def_interner.struct_attributes(&struct_id); + if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { + transform_event(struct_id, &mut context.def_interner)?; + } + } + Ok(()) +} diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs new file mode 100644 index 00000000000..8b3c7d2f53b --- /dev/null +++ b/aztec_macros/src/transforms/functions.rs @@ -0,0 +1,707 @@ +use convert_case::{Case, Casing}; +use noirc_errors::Span; +use noirc_frontend::{ + macros_api::FieldElement, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, + Expression, ExpressionKind, ForLoopStatement, ForRange, FunctionReturnType, Ident, Literal, + NoirFunction, Param, PathKind, Pattern, Signedness, Statement, StatementKind, UnresolvedType, + UnresolvedTypeData, Visibility, +}; + +use crate::{ + chained_dep, chained_path, + utils::{ + ast_utils::{ + assignment, call, cast, expression, ident, ident_path, index_array, + index_array_variable, make_eq, make_statement, make_type, member_access, method_call, + mutable_assignment, mutable_reference, path, return_type, variable, variable_ident, + variable_path, + }, + errors::AztecMacroError, + }, +}; + +// If it does, it will insert the following things: +/// - A new Input that is provided for a kernel app circuit, named: {Public/Private}ContextInputs +/// - Hashes all of the function input variables +/// - This instantiates a helper function +pub fn transform_function( + ty: &str, + func: &mut NoirFunction, + storage_defined: bool, + is_initializer: bool, + insert_init_check: bool, + is_internal: bool, +) -> Result<(), AztecMacroError> { + let context_name = format!("{}Context", ty); + let inputs_name = format!("{}ContextInputs", ty); + let return_type_name = format!("{}CircuitPublicInputs", ty); + + // Add check that msg sender equals this address and flag function as internal + if is_internal { + let is_internal_check = create_internal_check(func.name()); + func.def.body.0.insert(0, is_internal_check); + } + + // Add initialization check + if insert_init_check { + let init_check = create_init_check(); + func.def.body.0.insert(0, init_check); + } + + // Add access to the storage struct + if storage_defined { + let storage_def = abstract_storage(&ty.to_lowercase(), false); + func.def.body.0.insert(0, storage_def); + } + + // Insert the context creation as the first action + let create_context = create_context(&context_name, &func.def.parameters)?; + func.def.body.0.splice(0..0, (create_context).iter().cloned()); + + // Add the inputs to the params + let input = create_inputs(&inputs_name); + func.def.parameters.insert(0, input); + + // Abstract return types such that they get added to the kernel's return_values + if let Some(return_values) = abstract_return_values(func) { + // In case we are pushing return values to the context, we remove the statement that originated it + // This avoids running duplicate code, since blocks like if/else can be value returning statements + func.def.body.0.pop(); + // Add the new return statement + func.def.body.0.push(return_values); + } + + // Before returning mark the contract as initialized + if is_initializer { + let mark_initialized = create_mark_as_initialized(); + func.def.body.0.push(mark_initialized); + } + + // Push the finish method call to the end of the function + let finish_def = create_context_finish(); + func.def.body.0.push(finish_def); + + let return_type = create_return_type(&return_type_name); + func.def.return_type = return_type; + func.def.return_visibility = Visibility::Public; + + // Distinct return types are only required for private functions + // Public functions should have unconstrained auto-inferred + match ty { + "Private" => func.def.return_distinctness = Distinctness::Distinct, + "Public" => func.def.is_unconstrained = true, + _ => (), + } + + Ok(()) +} + +/// Transform a function to work with AVM bytecode +pub fn transform_vm_function( + func: &mut NoirFunction, + storage_defined: bool, +) -> Result<(), AztecMacroError> { + // Create access to storage + if storage_defined { + let storage = abstract_storage("public_vm", true); + func.def.body.0.insert(0, storage); + } + + // Push Avm context creation to the beginning of the function + let create_context = create_avm_context()?; + func.def.body.0.insert(0, create_context); + + // We want the function to be seen as a public function + func.def.is_unconstrained = true; + + // NOTE: the line below is a temporary hack to trigger external transpilation tools + // It will be removed once the transpiler is integrated into the Noir compiler + func.def.name.0.contents = format!("avm_{}", func.def.name.0.contents); + Ok(()) +} + +/// Transform Unconstrained +/// +/// Inserts the following code at the beginning of an unconstrained function +/// ```noir +/// let storage = Storage::init(Context::none()); +/// ``` +/// +/// This will allow developers to access their contract' storage struct in unconstrained functions +pub fn transform_unconstrained(func: &mut NoirFunction) { + func.def.body.0.insert(0, abstract_storage("Unconstrained", true)); +} + +/// Helper function that returns what the private context would look like in the ast +/// This should make it available to be consumed within aztec private annotated functions. +/// +/// The replaced code: +/// ```noir +/// /// Before +/// fn foo(inputs: PrivateContextInputs) { +/// // ... +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() { +/// // ... +/// } +fn create_inputs(ty: &str) -> Param { + let context_ident = ident("inputs"); + let context_pattern = Pattern::Identifier(context_ident); + + let path_snippet = ty.to_case(Case::Snake); // e.g. private_context_inputs + let type_path = chained_dep!("aztec", "context", "inputs", &path_snippet, ty); + + let context_type = make_type(UnresolvedTypeData::Named(type_path, vec![], true)); + let visibility = Visibility::Private; + + Param { pattern: context_pattern, typ: context_type, visibility, span: Span::default() } +} + +/// Creates an initialization check to ensure that the contract has been initialized, meant to +/// be injected as the first statement of any function after the context has been created. +/// +/// ```noir +/// assert_is_initialized(&mut context); +/// ``` +fn create_init_check() -> Statement { + make_statement(StatementKind::Expression(call( + variable_path(chained_dep!("aztec", "initializer", "assert_is_initialized")), + vec![mutable_reference("context")], + ))) +} + +/// Creates a call to mark_as_initialized which emits the initialization nullifier, meant to +/// be injected as the last statement before returning in a constructor. +/// +/// ```noir +/// mark_as_initialized(&mut context); +/// ``` +fn create_mark_as_initialized() -> Statement { + make_statement(StatementKind::Expression(call( + variable_path(chained_dep!("aztec", "initializer", "mark_as_initialized")), + vec![mutable_reference("context")], + ))) +} + +/// Creates a check for internal functions ensuring that the caller is self. +/// +/// ```noir +/// assert(context.msg_sender() == context.this_address(), "Function can only be called internally"); +/// ``` +fn create_internal_check(fname: &str) -> Statement { + make_statement(StatementKind::Constrain(ConstrainStatement( + make_eq( + method_call(variable("context"), "msg_sender", vec![]), + method_call(variable("context"), "this_address", vec![]), + ), + Some(expression(ExpressionKind::Literal(Literal::Str(format!( + "Function {} can only be called internally", + fname + ))))), + ConstrainKind::Assert, + ))) +} + +/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be +/// appended into the args hash object. +/// +/// The replaced code: +/// ```noir +/// #[aztec(private)] +/// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { +/// // Create the hasher object +/// let mut hasher = Hasher::new(); +/// +/// // struct inputs call serialize on them to add an array of fields +/// hasher.add_multiple(structInput.serialize()); +/// +/// // Array inputs are iterated over and each element is added to the hasher (as a field) +/// for i in 0..arrayInput.len() { +/// hasher.add(arrayInput[i] as Field); +/// } +/// // Field inputs are added to the hasher +/// hasher.add({ident}); +/// +/// // Create the context +/// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context +/// let mut context = PrivateContext::new(inputs, hasher.hash()); +/// } +/// ``` +fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { + let mut injected_expressions: Vec = vec![]; + + // `let mut hasher = Hasher::new();` + let let_hasher = mutable_assignment( + "hasher", // Assigned to + call( + variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path + vec![], // args + ), + ); + + // Completes: `let mut hasher = Hasher::new();` + injected_expressions.push(let_hasher); + + // Iterate over each of the function parameters, adding to them to the hasher + for Param { pattern, typ, span, .. } in params { + match pattern { + Pattern::Identifier(identifier) => { + // Match the type to determine the padding to do + let unresolved_type = &typ.typ; + let expression = match unresolved_type { + // `hasher.add_multiple({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier), + UnresolvedTypeData::Array(_, arr_type) => { + add_array_to_hasher(identifier, arr_type) + } + // `hasher.add({ident})` + UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), + // Add the integer to the hasher, casted to a field + // `hasher.add({ident} as Field)` + UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { + add_cast_to_hasher(identifier) + } + UnresolvedTypeData::String(..) => { + let (var_bytes, id) = str_to_bytes(identifier); + injected_expressions.push(var_bytes); + add_array_to_hasher( + &id, + &UnresolvedType { + typ: UnresolvedTypeData::Integer( + Signedness::Unsigned, + noirc_frontend::IntegerBitSize::ThirtyTwo, + ), + span: None, + }, + ) + } + _ => { + return Err(AztecMacroError::UnsupportedFunctionArgumentType { + typ: unresolved_type.clone(), + span: *span, + }) + } + }; + injected_expressions.push(expression); + } + _ => todo!(), // Maybe unreachable? + } + } + + // Create the inputs to the context + let inputs_expression = variable("inputs"); + // `hasher.hash()` + let hash_call = method_call( + variable("hasher"), // variable + "hash", // method name + vec![], // args + ); + + let path_snippet = ty.to_case(Case::Snake); // e.g. private_context + + // let mut context = {ty}::new(inputs, hash); + let let_context = mutable_assignment( + "context", // Assigned to + call( + variable_path(chained_dep!("aztec", "context", &path_snippet, ty, "new")), // Path + vec![inputs_expression, hash_call], // args + ), + ); + injected_expressions.push(let_context); + + // Return all expressions that will be injected by the hasher + Ok(injected_expressions) +} + +/// Creates an mutable avm context +/// +/// ```noir +/// /// Before +/// #[aztec(public-vm)] +/// fn foo() -> Field { +/// let mut context = aztec::context::AVMContext::new(); +/// let timestamp = context.timestamp(); +/// // ... +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() -> Field { +/// let mut timestamp = context.timestamp(); +/// // ... +/// } +fn create_avm_context() -> Result { + let let_context = mutable_assignment( + "context", // Assigned to + call( + variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path + vec![], // args + ), + ); + + Ok(let_context) +} + +/// Abstract Return Type +/// +/// This function intercepts the function's current return type and replaces it with pushes +/// To the kernel +/// +/// The replaced code: +/// ```noir +/// /// Before +/// #[aztec(private)] +/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { +/// // ... +/// let my_return_value: Field = 10; +/// context.return_values.push(my_return_value); +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() -> Field { +/// // ... +/// let my_return_value: Field = 10; +/// my_return_value +/// } +/// ``` +/// Similarly; Structs will be pushed to the context, after serialize() is called on them. +/// Arrays will be iterated over and each element will be pushed to the context. +/// Any primitive type that can be cast will be casted to a field and pushed to the context. +fn abstract_return_values(func: &NoirFunction) -> Option { + let current_return_type = func.return_type().typ; + let last_statement = func.def.body.0.last()?; + + // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size + // Doesn't need done until we have settled on a kernel size + // TODO: support tuples here and in inputs -> convert into an issue + // Check if the return type is an expression, if it is, we can handle it + match last_statement { + Statement { kind: StatementKind::Expression(expression), .. } => { + match current_return_type { + // Call serialize on structs, push the whole array, calling push_array + UnresolvedTypeData::Named(..) => Some(make_struct_return_type(expression.clone())), + UnresolvedTypeData::Array(..) => Some(make_array_return_type(expression.clone())), + // Cast these types to a field before pushing + UnresolvedTypeData::Bool | UnresolvedTypeData::Integer(..) => { + Some(make_castable_return_type(expression.clone())) + } + UnresolvedTypeData::FieldElement => Some(make_return_push(expression.clone())), + _ => None, + } + } + _ => None, + } +} + +/// Abstract storage +/// +/// For private functions: +/// ```noir +/// #[aztec(private)] +/// fn lol() { +/// let storage = Storage::init(Context::private(context)); +/// } +/// ``` +/// +/// For public functions: +/// ```noir +/// #[aztec(public)] +/// fn lol() { +/// let storage = Storage::init(Context::public(context)); +/// } +/// ``` +/// +/// For unconstrained functions: +/// ```noir +/// unconstrained fn lol() { +/// let storage = Storage::init(Context::none()); +/// } +fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { + let init_context_call = if unconstrained { + call( + variable_path(chained_dep!("aztec", "context", "Context", "none")), // Path + vec![], // args + ) + } else { + call( + variable_path(chained_dep!("aztec", "context", "Context", typ)), // Path + vec![mutable_reference("context")], // args + ) + }; + + assignment( + "storage", // Assigned to + call( + variable_path(chained_path!("Storage", "init")), // Path + vec![init_context_call], // args + ), + ) +} + +/// Context Return Values +/// +/// Creates an instance to the context return values +/// ```noir +/// `context.return_values` +/// ``` +fn context_return_values() -> Expression { + member_access("context", "return_values") +} + +/// Make return Push +/// +/// Translates to: +/// `context.return_values.push({push_value})` +fn make_return_push(push_value: Expression) -> Statement { + make_statement(StatementKind::Semi(method_call( + context_return_values(), + "push", + vec![push_value], + ))) +} + +/// Make Return push array +/// +/// Translates to: +/// `context.return_values.extend_from_array({push_value})` +fn make_return_extend_from_array(push_value: Expression) -> Statement { + make_statement(StatementKind::Semi(method_call( + context_return_values(), + "extend_from_array", + vec![push_value], + ))) +} + +/// Make struct return type +/// +/// Translates to: +/// ```noir +/// `context.return_values.extend_from_array({push_value}.serialize())` +fn make_struct_return_type(expression: Expression) -> Statement { + let serialized_call = method_call( + expression, // variable + "serialize", // method name + vec![], // args + ); + make_return_extend_from_array(serialized_call) +} + +/// Make array return type +/// +/// Translates to: +/// ```noir +/// for i in 0..{ident}.len() { +/// context.return_values.push({ident}[i] as Field) +/// } +/// ``` +fn make_array_return_type(expression: Expression) -> Statement { + let inner_cast_expression = + cast(index_array_variable(expression.clone(), "i"), UnresolvedTypeData::FieldElement); + let assignment = make_statement(StatementKind::Semi(method_call( + context_return_values(), // variable + "push", // method name + vec![inner_cast_expression], + ))); + + create_loop_over(expression, vec![assignment]) +} + +/// Castable return type +/// +/// Translates to: +/// ```noir +/// context.return_values.push({ident} as Field) +/// ``` +fn make_castable_return_type(expression: Expression) -> Statement { + // Cast these types to a field before pushing + let cast_expression = cast(expression, UnresolvedTypeData::FieldElement); + make_return_push(cast_expression) +} + +/// Create Return Type +/// +/// Public functions return protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs while +/// private functions return protocol_types::abis::private_circuit_public_inputs::::PrivateCircuitPublicInputs +/// +/// This call constructs an ast token referencing the above types +/// The name is set in the function above `transform`, hence the +/// whole token name is passed in +/// +/// The replaced code: +/// ```noir +/// +/// /// Before +/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { +/// // ... +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() { +/// // ... +/// } +fn create_return_type(ty: &str) -> FunctionReturnType { + let path_snippet = ty.to_case(Case::Snake); // e.g. private_circuit_public_inputs or public_circuit_public_inputs + let return_path = chained_dep!("aztec", "protocol_types", "abis", &path_snippet, ty); + return_type(return_path) +} + +/// Create Context Finish +/// +/// Each aztec function calls `context.finish()` at the end of a function +/// to return values required by the kernel. +/// +/// The replaced code: +/// ```noir +/// /// Before +/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { +/// // ... +/// context.finish() +/// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() { +/// // ... +/// } +fn create_context_finish() -> Statement { + let method_call = method_call( + variable("context"), // variable + "finish", // method name + vec![], // args + ); + make_statement(StatementKind::Expression(method_call)) +} + +// +// Methods to create hasher inputs +// + +fn add_struct_to_hasher(identifier: &Ident) -> Statement { + // If this is a struct, we call serialize and add the array to the hasher + let serialized_call = method_call( + variable_path(path(identifier.clone())), // variable + "serialize", // method name + vec![], // args + ); + + make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + "add_multiple", // method name + vec![serialized_call], // args + ))) +} + +fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { + // let identifier_as_bytes = identifier.as_bytes(); + let var = variable_ident(identifier.clone()); + let contents = if let ExpressionKind::Variable(p) = &var.kind { + p.segments.first().cloned().unwrap_or_else(|| panic!("No segments")).0.contents + } else { + panic!("Unexpected identifier type") + }; + let bytes_name = format!("{}_bytes", contents); + let var_bytes = assignment(&bytes_name, method_call(var, "as_bytes", vec![])); + let id = Ident::new(bytes_name, Span::default()); + + (var_bytes, id) +} + +fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // casted to a field + let span = var.span; + + // `array.len()` + let end_range_expression = method_call( + var, // variable + "len", // method name + vec![], // args + ); + + // What will be looped over + // - `hasher.add({ident}[i] as Field)` + let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); + + // `for i in 0..{ident}.len()` + make_statement(StatementKind::For(ForLoopStatement { + range: ForRange::Range( + expression(ExpressionKind::Literal(Literal::Integer( + FieldElement::from(i128::from(0)), + false, + ))), + end_range_expression, + ), + identifier: ident("i"), + block: for_loop_block, + span, + })) +} + +fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // casted to a field + + // Wrap in the semi thing - does that mean ended with semi colon? + // `hasher.add({ident}[i] as Field)` + + let arr_index = index_array(identifier.clone(), "i"); + let (add_expression, hasher_method_name) = match arr_type.typ { + UnresolvedTypeData::Named(..) => { + let hasher_method_name = "add_multiple".to_owned(); + let call = method_call( + // All serialize on each element + arr_index, // variable + "serialize", // method name + vec![], // args + ); + (call, hasher_method_name) + } + _ => { + let hasher_method_name = "add".to_owned(); + let call = cast( + arr_index, // lhs - `ident[i]` + UnresolvedTypeData::FieldElement, // cast to - `as Field` + ); + (call, hasher_method_name) + } + }; + + let block_statement = make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + &hasher_method_name, // method name + vec![add_expression], + ))); + + create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) +} + +fn add_field_to_hasher(identifier: &Ident) -> Statement { + // `hasher.add({ident})` + let ident = variable_path(path(identifier.clone())); + make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + "add", // method name + vec![ident], // args + ))) +} + +fn add_cast_to_hasher(identifier: &Ident) -> Statement { + // `hasher.add({ident} as Field)` + // `{ident} as Field` + let cast_operation = cast( + variable_path(path(identifier.clone())), // lhs + UnresolvedTypeData::FieldElement, // rhs + ); + + // `hasher.add({ident} as Field)` + make_statement(StatementKind::Semi(method_call( + variable("hasher"), // variable + "add", // method name + vec![cast_operation], // args + ))) +} diff --git a/aztec_macros/src/transforms/mod.rs b/aztec_macros/src/transforms/mod.rs new file mode 100644 index 00000000000..144ffc3efc3 --- /dev/null +++ b/aztec_macros/src/transforms/mod.rs @@ -0,0 +1,4 @@ +pub mod compute_note_hash_and_nullifier; +pub mod events; +pub mod functions; +pub mod storage; diff --git a/aztec_macros/src/transforms/storage.rs b/aztec_macros/src/transforms/storage.rs new file mode 100644 index 00000000000..40a094f78e3 --- /dev/null +++ b/aztec_macros/src/transforms/storage.rs @@ -0,0 +1,346 @@ +use std::borrow::{Borrow, BorrowMut}; + +use noirc_errors::Span; +use noirc_frontend::{ + graph::CrateId, + macros_api::{ + FieldElement, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, + }, + node_interner::{TraitId, TraitImplKind}, + parser::SortedModule, + BlockExpression, Expression, ExpressionKind, FunctionDefinition, Ident, Literal, NoirFunction, + PathKind, Pattern, StatementKind, Type, TypeImpl, UnresolvedType, UnresolvedTypeData, +}; + +use crate::{ + chained_dep, chained_path, + utils::{ + ast_utils::{ + call, expression, ident, ident_path, lambda, make_statement, make_type, pattern, + return_type, variable, variable_path, + }, + errors::AztecMacroError, + hir_utils::{collect_crate_structs, collect_traits}, + }, +}; + +// Check to see if the user has defined a storage struct +pub fn check_for_storage_definition(module: &SortedModule) -> bool { + module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") +} + +// Check to see if the user has defined a storage struct +pub fn check_for_storage_implementation(module: &SortedModule) -> bool { + module.impls.iter().any(|r#impl| match &r#impl.object_type.typ { + UnresolvedTypeData::Named(path, _, _) => { + path.segments.last().is_some_and(|segment| segment.0.contents == "Storage") + } + _ => false, + }) +} + +/// Auxiliary function to generate the storage constructor for a given field, using +/// the Storage definition as a reference. Supports nesting. +pub fn generate_storage_field_constructor( + (type_ident, unresolved_type): &(Ident, UnresolvedType), + slot: Expression, +) -> Result { + let typ = &unresolved_type.typ; + match typ { + UnresolvedTypeData::Named(path, generics, _) => { + let mut new_path = path.clone().to_owned(); + new_path.segments.push(ident("new")); + match path.segments.last().unwrap().0.contents.as_str() { + "Map" => Ok(call( + variable_path(new_path), + vec![ + variable("context"), + slot, + lambda( + vec![ + ( + pattern("context"), + make_type(UnresolvedTypeData::Named( + chained_dep!("aztec", "context", "Context"), + vec![], + true, + )), + ), + ( + Pattern::Identifier(ident("slot")), + make_type(UnresolvedTypeData::FieldElement), + ), + ], + generate_storage_field_constructor( + &(type_ident.clone(), generics.iter().last().unwrap().clone()), + variable("slot"), + )?, + ), + ], + )), + _ => Ok(call(variable_path(new_path), vec![variable("context"), slot])), + } + } + _ => Err(AztecMacroError::UnsupportedStorageType { + typ: typ.clone(), + span: Some(type_ident.span()), + }), + } +} + +// Generates the Storage implementation block from the Storage struct definition if it does not exist +/// From: +/// +/// struct Storage { +/// a_map: Map>, +/// a_nested_map: Map>>, +/// a_field: SomeStoragePrimitive, +/// } +/// +/// To: +/// +/// impl Storage { +/// fn init(context: Context) -> Self { +/// Storage { +/// a_map: Map::new(context, 0, |context, slot| { +/// SomeStoragePrimitive::new(context, slot) +/// }), +/// a_nested_map: Map::new(context, 0, |context, slot| { +/// Map::new(context, slot, |context, slot| { +/// SomeStoragePrimitive::new(context, slot) +/// }) +/// }), +/// a_field: SomeStoragePrimitive::new(context, 0), +/// } +/// } +/// } +/// +/// Storage slots are generated as 0 and will be populated using the information from the HIR +/// at a later stage. +pub fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), AztecMacroError> { + let definition = + module.types.iter().find(|r#struct| r#struct.name.0.contents == "Storage").unwrap(); + + let slot_zero = expression(ExpressionKind::Literal(Literal::Integer( + FieldElement::from(i128::from(0)), + false, + ))); + + let field_constructors = definition + .fields + .iter() + .flat_map(|field| { + generate_storage_field_constructor(field, slot_zero.clone()) + .map(|expression| (field.0.clone(), expression)) + }) + .collect(); + + let storage_constructor_statement = make_statement(StatementKind::Expression(expression( + ExpressionKind::constructor((chained_path!("Storage"), field_constructors)), + ))); + + let init = NoirFunction::normal(FunctionDefinition::normal( + &ident("init"), + &vec![], + &[( + ident("context"), + make_type(UnresolvedTypeData::Named( + chained_dep!("aztec", "context", "Context"), + vec![], + true, + )), + )], + &BlockExpression(vec![storage_constructor_statement]), + &[], + &return_type(chained_path!("Self")), + )); + + let storage_impl = TypeImpl { + object_type: UnresolvedType { + typ: UnresolvedTypeData::Named(chained_path!("Storage"), vec![], true), + span: Some(Span::default()), + }, + type_span: Span::default(), + generics: vec![], + methods: vec![(init, Span::default())], + }; + module.impls.push(storage_impl); + + Ok(()) +} + +/// Obtains the serialized length of a type that implements the Serialize trait. +fn get_serialized_length( + traits: &[TraitId], + typ: &Type, + interner: &NodeInterner, +) -> Result { + let (struct_name, maybe_stored_in_state) = match typ { + Type::Struct(struct_type, generics) => { + Ok((struct_type.borrow().name.0.contents.clone(), generics.first())) + } + _ => Err(AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("State storage variable must be a struct".to_string()), + }), + }?; + let stored_in_state = + maybe_stored_in_state.ok_or(AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("State storage variable must be generic".to_string()), + })?; + + let is_note = traits.iter().any(|&trait_id| { + let r#trait = interner.get_trait(trait_id); + r#trait.name.0.contents == "NoteInterface" + && !interner.lookup_all_trait_implementations(stored_in_state, trait_id).is_empty() + }); + + // Maps and (private) Notes always occupy a single slot. Someone could store a Note in PublicMutable for whatever reason though. + if struct_name == "Map" || (is_note && struct_name != "PublicMutable") { + return Ok(1); + } + + let serialized_trait_impl_kind = traits + .iter() + .find_map(|&trait_id| { + let r#trait = interner.get_trait(trait_id); + if r#trait.borrow().name.0.contents == "Serialize" + && r#trait.borrow().generics.len() == 1 + { + interner + .lookup_all_trait_implementations(stored_in_state, trait_id) + .into_iter() + .next() + } else { + None + } + }) + .ok_or(AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("Stored data must implement Serialize trait".to_string()), + })?; + + let serialized_trait_impl_id = match serialized_trait_impl_kind { + TraitImplKind::Normal(trait_impl_id) => Ok(trait_impl_id), + _ => Err(AztecMacroError::CouldNotAssignStorageSlots { secondary_message: None }), + }?; + + let serialized_trait_impl_shared = interner.get_trait_implementation(*serialized_trait_impl_id); + let serialized_trait_impl = serialized_trait_impl_shared.borrow(); + + match serialized_trait_impl.trait_generics.first().unwrap() { + Type::Constant(value) => Ok(*value), + _ => Err(AztecMacroError::CouldNotAssignStorageSlots { secondary_message: None }), + } +} + +/// Assigns storage slots to the storage struct fields based on the serialized length of the types. This automatic assignment +/// will only trigger if the assigned storage slot is invalid (0 as generated by generate_storage_implementation) +pub fn assign_storage_slots( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + let traits: Vec<_> = collect_traits(context); + for struct_id in collect_crate_structs(crate_id, context) { + let interner: &mut NodeInterner = context.def_interner.borrow_mut(); + let r#struct = interner.get_struct(struct_id); + let file_id = r#struct.borrow().location.file; + if r#struct.borrow().name.0.contents == "Storage" && r#struct.borrow().id.krate().is_root() + { + let init_id = interner + .lookup_method( + &Type::Struct(interner.get_struct(struct_id), vec![]), + struct_id, + "init", + false, + ) + .ok_or(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage struct must have an init function".to_string(), + ), + }, + file_id, + ))?; + let init_function = interner.function(&init_id).block(interner); + let init_function_statement_id = init_function.statements().first().ok_or(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some("Init storage statement not found".to_string()), + }, + file_id, + ))?; + let storage_constructor_statement = interner.statement(init_function_statement_id); + + let storage_constructor_expression = match storage_constructor_statement { + HirStatement::Expression(expression_id) => { + match interner.expression(&expression_id) { + HirExpression::Constructor(hir_constructor_expression) => { + Ok(hir_constructor_expression) + } + _ => Err((AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage constructor statement must be a constructor expression" + .to_string(), + ), + }, file_id)) + } + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage constructor statement must be an expression".to_string(), + ), + }, + file_id, + )), + }?; + + let mut storage_slot: u64 = 1; + for (index, (_, expr_id)) in storage_constructor_expression.fields.iter().enumerate() { + let fields = r#struct.borrow().get_fields(&[]); + let (_, field_type) = fields.get(index).unwrap(); + let new_call_expression = match interner.expression(expr_id) { + HirExpression::Call(hir_call_expression) => Ok(hir_call_expression), + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage field initialization expression is not a call expression" + .to_string(), + ), + }, + file_id, + )), + }?; + + let slot_arg_expression = interner.expression(&new_call_expression.arguments[1]); + + let current_storage_slot = match slot_arg_expression { + HirExpression::Literal(HirLiteral::Integer(slot, _)) => Ok(slot.to_u128()), + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "Storage slot argument expression must be a literal integer" + .to_string(), + ), + }, + file_id, + )), + }?; + + if current_storage_slot != 0 { + continue; + } + + let type_serialized_len = get_serialized_length(&traits, field_type, interner) + .map_err(|err| (err, file_id))?; + interner.update_expression(new_call_expression.arguments[1], |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer( + FieldElement::from(u128::from(storage_slot)), + false, + )); + }); + + storage_slot += type_serialized_len; + } + } + } + Ok(()) +} diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs new file mode 100644 index 00000000000..71c6a93f388 --- /dev/null +++ b/aztec_macros/src/utils/ast_utils.rs @@ -0,0 +1,183 @@ +use noirc_errors::{Span, Spanned}; +use noirc_frontend::{ + token::SecondaryAttribute, BinaryOpKind, CallExpression, CastExpression, Expression, + ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, + LetStatement, MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, + Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, +}; + +// +// Helper macros for creating noir ast nodes +// +pub fn ident(name: &str) -> Ident { + Ident::new(name.to_string(), Span::default()) +} + +pub fn ident_path(name: &str) -> Path { + Path::from_ident(ident(name)) +} + +pub fn path(ident: Ident) -> Path { + Path::from_ident(ident) +} + +pub fn expression(kind: ExpressionKind) -> Expression { + Expression::new(kind, Span::default()) +} + +pub fn variable(name: &str) -> Expression { + expression(ExpressionKind::Variable(ident_path(name))) +} + +pub fn variable_ident(identifier: Ident) -> Expression { + expression(ExpressionKind::Variable(path(identifier))) +} + +pub fn variable_path(path: Path) -> Expression { + expression(ExpressionKind::Variable(path)) +} + +pub fn method_call( + object: Expression, + method_name: &str, + arguments: Vec, +) -> Expression { + expression(ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object, + method_name: ident(method_name), + arguments, + }))) +} + +pub fn call(func: Expression, arguments: Vec) -> Expression { + expression(ExpressionKind::Call(Box::new(CallExpression { func: Box::new(func), arguments }))) +} + +pub fn pattern(name: &str) -> Pattern { + Pattern::Identifier(ident(name)) +} + +pub fn mutable(name: &str) -> Pattern { + Pattern::Mutable(Box::new(pattern(name)), Span::default(), true) +} + +pub fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { + make_statement(StatementKind::Let(LetStatement { + pattern: mutable(name), + r#type: make_type(UnresolvedTypeData::Unspecified), + expression: assigned_to, + })) +} + +pub fn mutable_reference(variable_name: &str) -> Expression { + expression(ExpressionKind::Prefix(Box::new(PrefixExpression { + operator: UnaryOp::MutableReference, + rhs: variable(variable_name), + }))) +} + +pub fn assignment(name: &str, assigned_to: Expression) -> Statement { + make_statement(StatementKind::Let(LetStatement { + pattern: pattern(name), + r#type: make_type(UnresolvedTypeData::Unspecified), + expression: assigned_to, + })) +} + +pub fn member_access(lhs: &str, rhs: &str) -> Expression { + expression(ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: variable(lhs), + rhs: ident(rhs), + }))) +} + +pub fn return_type(path: Path) -> FunctionReturnType { + let ty = make_type(UnresolvedTypeData::Named(path, vec![], true)); + FunctionReturnType::Ty(ty) +} + +pub fn lambda(parameters: Vec<(Pattern, UnresolvedType)>, body: Expression) -> Expression { + expression(ExpressionKind::Lambda(Box::new(Lambda { + parameters, + return_type: UnresolvedType { + typ: UnresolvedTypeData::Unspecified, + span: Some(Span::default()), + }, + body, + }))) +} + +pub fn make_eq(lhs: Expression, rhs: Expression) -> Expression { + expression(ExpressionKind::Infix(Box::new(InfixExpression { + lhs, + rhs, + operator: Spanned::from(Span::default(), BinaryOpKind::Equal), + }))) +} + +pub fn make_statement(kind: StatementKind) -> Statement { + Statement { span: Span::default(), kind } +} + +#[macro_export] +macro_rules! chained_path { + ( $base:expr ) => { + { + ident_path($base) + } + }; + ( $base:expr $(, $tail:expr)* ) => { + { + let mut base_path = ident_path($base); + $( + base_path.segments.push(ident($tail)); + )* + base_path + } + } +} + +#[macro_export] +macro_rules! chained_dep { + ( $base:expr $(, $tail:expr)* ) => { + { + let mut base_path = ident_path($base); + base_path.kind = PathKind::Dep; + $( + base_path.segments.push(ident($tail)); + )* + base_path + } + } +} + +pub fn cast(lhs: Expression, ty: UnresolvedTypeData) -> Expression { + expression(ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type: make_type(ty) }))) +} + +pub fn make_type(typ: UnresolvedTypeData) -> UnresolvedType { + UnresolvedType { typ, span: Some(Span::default()) } +} + +pub fn index_array(array: Ident, index: &str) -> Expression { + expression(ExpressionKind::Index(Box::new(IndexExpression { + collection: variable_path(path(array)), + index: variable(index), + }))) +} + +pub fn index_array_variable(array: Expression, index: &str) -> Expression { + expression(ExpressionKind::Index(Box::new(IndexExpression { + collection: array, + index: variable(index), + }))) +} + +/// Checks if an attribute is a custom attribute with a specific name +pub fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool { + if let SecondaryAttribute::Custom(custom_attr) = attr { + custom_attr.as_str() == attribute_name + } else { + false + } +} diff --git a/aztec_macros/src/utils/checks.rs b/aztec_macros/src/utils/checks.rs new file mode 100644 index 00000000000..5232f67ae87 --- /dev/null +++ b/aztec_macros/src/utils/checks.rs @@ -0,0 +1,22 @@ +use noirc_frontend::{ + graph::CrateId, + macros_api::{FileId, HirContext, MacroError}, +}; + +use super::errors::AztecMacroError; + +/// Creates an error alerting the user that they have not downloaded the Aztec-noir library +pub fn check_for_aztec_dependency( + crate_id: &CrateId, + context: &HirContext, +) -> Result<(), (MacroError, FileId)> { + if has_aztec_dependency(crate_id, context) { + Ok(()) + } else { + Err((AztecMacroError::AztecDepNotFound.into(), context.crate_graph[crate_id].root_file_id)) + } +} + +pub fn has_aztec_dependency(crate_id: &CrateId, context: &HirContext) -> bool { + context.crate_graph[crate_id].dependencies.iter().any(|dep| dep.as_name() == "aztec") +} diff --git a/aztec_macros/src/utils/constants.rs b/aztec_macros/src/utils/constants.rs new file mode 100644 index 00000000000..464cd10e2c7 --- /dev/null +++ b/aztec_macros/src/utils/constants.rs @@ -0,0 +1,3 @@ +pub const FUNCTION_TREE_HEIGHT: u32 = 5; +pub const MAX_CONTRACT_PRIVATE_FUNCTIONS: usize = 2_usize.pow(FUNCTION_TREE_HEIGHT); +pub const SIGNATURE_PLACEHOLDER: &str = "SIGNATURE_PLACEHOLDER"; diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs new file mode 100644 index 00000000000..63892b58af9 --- /dev/null +++ b/aztec_macros/src/utils/errors.rs @@ -0,0 +1,69 @@ +use noirc_errors::Span; +use noirc_frontend::{macros_api::MacroError, UnresolvedTypeData}; + +use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS; + +#[derive(Debug, Clone)] +pub enum AztecMacroError { + AztecDepNotFound, + ContractHasTooManyPrivateFunctions { span: Span }, + ContractConstructorMissing { span: Span }, + UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, + CouldNotAssignStorageSlots { secondary_message: Option }, + CouldNotImplementNoteSerialization { span: Option, typ: UnresolvedTypeData }, + EventError { span: Span, message: String }, + UnsupportedAttributes { span: Span, secondary_message: Option }, +} + +impl From for MacroError { + fn from(err: AztecMacroError) -> Self { + match err { + AztecMacroError::AztecDepNotFound {} => MacroError { + primary_message: "Aztec dependency not found. Please add aztec as a dependency in your Cargo.toml. For more information go to https://docs.aztec.network/developers/debugging/aztecnr-errors#aztec-dependency-not-found-please-add-aztec-as-a-dependency-in-your-nargotoml".to_owned(), + secondary_message: None, + span: None, + }, + AztecMacroError::ContractHasTooManyPrivateFunctions { span } => MacroError { + primary_message: format!("Contract can only have a maximum of {} private functions", MAX_CONTRACT_PRIVATE_FUNCTIONS), + secondary_message: None, + span: Some(span), + }, + AztecMacroError::ContractConstructorMissing { span } => MacroError { + primary_message: "Contract must have a constructor function".to_owned(), + secondary_message: None, + span: Some(span), + }, + AztecMacroError::UnsupportedFunctionArgumentType { span, typ } => MacroError { + primary_message: format!("Provided parameter type `{typ:?}` is not supported in Aztec contract interface"), + secondary_message: None, + span: Some(span), + }, + AztecMacroError::UnsupportedStorageType { span, typ } => MacroError { + primary_message: format!("Provided storage type `{typ:?}` is not directly supported in Aztec. Please provide a custom storage implementation"), + secondary_message: None, + span, + }, + AztecMacroError::CouldNotAssignStorageSlots { secondary_message } => MacroError { + primary_message: "Could not assign storage slots, please provide a custom storage implementation".to_string(), + secondary_message, + span: None, + }, + AztecMacroError::CouldNotImplementNoteSerialization { span, typ } => MacroError { + primary_message: format!("Could not implement serialization methods for note `{typ:?}`, please provide a serialize_content and deserialize_content methods"), + secondary_message: None, + span, + }, + AztecMacroError::EventError { span, message } => MacroError { + primary_message: message, + secondary_message: None, + span: Some(span), + }, +AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { + primary_message: "Unsupported attributes in contract function".to_string(), + secondary_message, + span: Some(span), + }, + } + } +} diff --git a/aztec_macros/src/utils/hir_utils.rs b/aztec_macros/src/utils/hir_utils.rs new file mode 100644 index 00000000000..f31a0584261 --- /dev/null +++ b/aztec_macros/src/utils/hir_utils.rs @@ -0,0 +1,118 @@ +use iter_extended::vecmap; +use noirc_frontend::{ + graph::CrateId, + hir::def_collector::dc_crate::UnresolvedTraitImpl, + macros_api::{HirContext, ModuleDefId, StructId}, + node_interner::{TraitId, TraitImplId}, + Signedness, Type, UnresolvedTypeData, +}; + +pub fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec { + context + .def_map(crate_id) + .expect("ICE: Missing crate in def_map") + .modules() + .iter() + .flat_map(|(_, module)| { + module.type_definitions().filter_map(|typ| { + if let ModuleDefId::TypeId(struct_id) = typ { + Some(struct_id) + } else { + None + } + }) + }) + .collect() +} + +pub fn collect_traits(context: &HirContext) -> Vec { + let crates = context.crates(); + crates + .flat_map(|crate_id| context.def_map(&crate_id).map(|def_map| def_map.modules())) + .flatten() + .flat_map(|module| { + module.type_definitions().filter_map(|typ| { + if let ModuleDefId::TraitId(struct_id) = typ { + Some(struct_id) + } else { + None + } + }) + }) + .collect() +} + +/// Computes the aztec signature for a resolved type. +pub fn signature_of_type(typ: &Type) -> String { + match typ { + Type::Integer(Signedness::Signed, bit_size) => format!("i{}", bit_size), + Type::Integer(Signedness::Unsigned, bit_size) => format!("u{}", bit_size), + Type::FieldElement => "Field".to_owned(), + Type::Bool => "bool".to_owned(), + Type::Array(len, typ) => { + if let Type::Constant(len) = **len { + format!("[{};{len}]", signature_of_type(typ)) + } else { + unimplemented!("Cannot generate signature for array with length type {:?}", typ) + } + } + Type::Struct(def, args) => { + let fields = def.borrow().get_fields(args); + let fields = vecmap(fields, |(_, typ)| signature_of_type(&typ)); + format!("({})", fields.join(",")) + } + Type::Tuple(types) => { + let fields = vecmap(types, signature_of_type); + format!("({})", fields.join(",")) + } + _ => unimplemented!("Cannot generate signature for type {:?}", typ), + } +} + +// Fetches the name of all structs that implement trait_name, both in the current crate and all of its dependencies. +pub fn fetch_struct_trait_impls( + context: &mut HirContext, + unresolved_traits_impls: &[UnresolvedTraitImpl], + trait_name: &str, +) -> Vec { + let mut struct_typenames: Vec = Vec::new(); + + // These structs can be declared in either external crates or the current one. External crates that contain + // dependencies have already been processed and resolved, but are available here via the NodeInterner. Note that + // crates on which the current crate does not depend on may not have been processed, and will be ignored. + for trait_impl_id in 0..context.def_interner.next_trait_impl_id().0 { + let trait_impl = &context.def_interner.get_trait_implementation(TraitImplId(trait_impl_id)); + + if trait_impl.borrow().ident.0.contents == *trait_name { + if let Type::Struct(s, _) = &trait_impl.borrow().typ { + struct_typenames.push(s.borrow().name.0.contents.clone()); + } else { + panic!("Found impl for {} on non-Struct", trait_name); + } + } + } + + // This crate's traits and impls have not yet been resolved, so we look for impls in unresolved_trait_impls. + struct_typenames.extend( + unresolved_traits_impls + .iter() + .filter(|trait_impl| { + trait_impl + .trait_path + .segments + .last() + .expect("ICE: empty trait_impl path") + .0 + .contents + == *trait_name + }) + .filter_map(|trait_impl| match &trait_impl.object_type.typ { + UnresolvedTypeData::Named(path, _, _) => { + Some(path.segments.last().unwrap().0.contents.clone()) + } + _ => None, + }), + ); + + struct_typenames +} diff --git a/aztec_macros/src/utils/mod.rs b/aztec_macros/src/utils/mod.rs new file mode 100644 index 00000000000..c8914f83025 --- /dev/null +++ b/aztec_macros/src/utils/mod.rs @@ -0,0 +1,5 @@ +pub mod ast_utils; +pub mod checks; +pub mod constants; +pub mod errors; +pub mod hir_utils; diff --git a/compiler/noirc_driver/src/contract.rs b/compiler/noirc_driver/src/contract.rs index 5f4b66e7dd2..66e8dc0e730 100644 --- a/compiler/noirc_driver/src/contract.rs +++ b/compiler/noirc_driver/src/contract.rs @@ -9,23 +9,6 @@ use noirc_evaluator::errors::SsaReport; use super::debug::DebugFile; -/// Describes the types of smart contract functions that are allowed. -/// Unlike the similar enum in noirc_frontend, 'open' and 'unconstrained' -/// are mutually exclusive here. In the case a function is both, 'unconstrained' -/// takes precedence. -#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] -pub enum ContractFunctionType { - /// This function will be executed in a private - /// context. - Secret, - /// This function will be executed in a public - /// context. - Open, - /// This function cannot constrain any values and can use nondeterministic features - /// like arrays of a dynamic size. - Unconstrained, -} - #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CompiledContract { pub noir_version: String, @@ -55,9 +38,9 @@ pub struct CompiledContract { pub struct ContractFunction { pub name: String, - pub function_type: ContractFunctionType, + pub is_unconstrained: bool, - pub is_internal: bool, + pub custom_attributes: Vec, pub abi: Abi, @@ -69,13 +52,3 @@ pub struct ContractFunction { pub debug: DebugInfo, } - -impl ContractFunctionType { - pub(super) fn new(kind: noirc_frontend::ContractFunctionType, is_unconstrained: bool) -> Self { - match (kind, is_unconstrained) { - (_, true) => Self::Unconstrained, - (noirc_frontend::ContractFunctionType::Secret, false) => Self::Secret, - (noirc_frontend::ContractFunctionType::Open, false) => Self::Open, - } - } -} diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index fa134f8a0dd..c9494a64b41 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -18,6 +18,7 @@ use noirc_frontend::hir::Context; use noirc_frontend::macros_api::MacroProcessor; use noirc_frontend::monomorphization::{monomorphize, monomorphize_debug, MonomorphizationError}; use noirc_frontend::node_interner::FuncId; +use noirc_frontend::token::SecondaryAttribute; use std::path::Path; use thiserror::Error; use tracing::info; @@ -30,7 +31,7 @@ mod stdlib; use debug::filter_relevant_files; -pub use contract::{CompiledContract, ContractFunction, ContractFunctionType}; +pub use contract::{CompiledContract, ContractFunction}; pub use debug::DebugFile; pub use program::CompiledProgram; @@ -398,19 +399,24 @@ fn compile_contract_inner( }; warnings.extend(function.warnings); let modifiers = context.def_interner.function_modifiers(&function_id); - let func_type = modifiers - .contract_function_type - .expect("Expected contract function to have a contract visibility"); - let function_type = ContractFunctionType::new(func_type, modifiers.is_unconstrained); + let custom_attributes = modifiers + .attributes + .secondary + .iter() + .filter_map( + |attr| if let SecondaryAttribute::Custom(tag) = attr { Some(tag) } else { None }, + ) + .cloned() + .collect(); functions.push(ContractFunction { name, - function_type, - is_internal: modifiers.is_internal.unwrap_or(false), + custom_attributes, abi: function.abi, bytecode: function.circuit, debug: function.debug, + is_unconstrained: modifiers.is_unconstrained, }); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 530b6dc69fc..911f4c1924e 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -831,10 +831,15 @@ impl<'block> BrilligBlock<'block> { _ => unreachable!("ICE: array set on non-array"), }; + // Here we want to compare the reference count against 1. + let one = self.brillig_context.make_usize_constant(1_usize.into()); let condition = self.brillig_context.allocate_register(); - - self.brillig_context.usize_op(reference_count, condition, BinaryIntOp::Equals, 1_usize); - + self.brillig_context.memory_op( + reference_count, + one.address, + condition, + BinaryIntOp::Equals, + ); self.brillig_context.branch_instruction(condition, |ctx, cond| { if cond { // Reference count is 1, we can mutate the array directly diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 1e53713dfe0..2a96965171b 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -27,7 +27,6 @@ use acvm::{ FieldElement, }; use debug_show::DebugShow; -use num_bigint::BigUint; /// The Brillig VM does not apply a limit to the memory address space, /// As a convention, we take use 64 bits. This means that we assume that @@ -215,7 +214,6 @@ impl BrilligContext { // Computes array_ptr + index, ie array[index] let index_of_element_in_memory = self.allocate_register(); self.memory_op(array_ptr, index.address, index_of_element_in_memory, BinaryIntOp::Add); - self.load_instruction(result, index_of_element_in_memory); // Free up temporary register self.deallocate_register(index_of_element_in_memory); @@ -232,7 +230,12 @@ impl BrilligContext { self.debug_show.array_set(array_ptr, index.address, value); // Computes array_ptr + index, ie array[index] let index_of_element_in_memory = self.allocate_register(); - self.memory_op(array_ptr, index.address, index_of_element_in_memory, BinaryIntOp::Add); + self.binary_instruction( + SingleAddrVariable::new_usize(array_ptr), + index, + SingleAddrVariable::new_usize(index_of_element_in_memory), + BrilligBinaryOp::Integer(BinaryIntOp::Add), + ); self.store_instruction(index_of_element_in_memory, value); // Free up temporary register @@ -592,6 +595,7 @@ impl BrilligContext { /// Stores the value of `constant` in the `result` register pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { self.debug_show.const_instruction(result.address, constant); + self.push_opcode(BrilligOpcode::Const { destination: result.address, value: constant, @@ -760,6 +764,8 @@ impl BrilligContext { /// Instead truncation instructions are emitted as to when a /// truncation should be done. /// For Brillig, all integer operations will overflow as its cheap. + /// We currently use cast to truncate: we cast to the required bit size + /// and back to the original bit size. pub(crate) fn truncate_instruction( &mut self, destination_of_truncated_value: SingleAddrVariable, @@ -778,20 +784,12 @@ impl BrilligContext { value_to_truncate.bit_size ); - let mask = BigUint::from(2_u32).pow(bit_size) - BigUint::from(1_u32); - let mask_constant = self.make_constant( - FieldElement::from_be_bytes_reduce(&mask.to_bytes_be()).into(), - value_to_truncate.bit_size, - ); - - self.binary_instruction( - value_to_truncate, - mask_constant, - destination_of_truncated_value, - BrilligBinaryOp::Integer(BinaryIntOp::And), - ); - - self.deallocate_single_addr(mask_constant); + // We cast back and forth to ensure that the value is truncated. + let intermediate_register = + SingleAddrVariable { address: self.allocate_register(), bit_size }; + self.cast_instruction(intermediate_register, value_to_truncate); + self.cast_instruction(destination_of_truncated_value, intermediate_register); + self.deallocate_register(intermediate_register.address); } /// Emits a stop instruction diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index def1b082890..a9ecc1a53e5 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -369,11 +369,6 @@ pub struct FunctionDefinition { // and `secondary` attributes (ones that do not change the function kind) pub attributes: Attributes, - /// True if this function was defined with the 'open' keyword - pub is_open: bool, - - pub is_internal: bool, - /// True if this function was defined with the 'unconstrained' keyword pub is_unconstrained: bool, @@ -406,18 +401,6 @@ pub enum FunctionReturnType { Ty(UnresolvedType), } -/// Describes the types of smart contract functions that are allowed. -/// - All Noir programs in the non-contract context can be seen as `Secret`. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum ContractFunctionType { - /// This function will be executed in a private - /// context. - Secret, - /// This function will be executed in a public - /// context. - Open, -} - #[derive(Debug, PartialEq, Eq, Clone)] pub enum ArrayLiteral { Standard(Vec), @@ -674,8 +657,6 @@ impl FunctionDefinition { FunctionDefinition { name: name.clone(), attributes: Attributes::empty(), - is_open: false, - is_internal: false, is_unconstrained: false, visibility: ItemVisibility::Private, generics: generics.clone(), diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index a5dfc738824..0c53bff4a54 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -246,6 +246,7 @@ impl DefCollector { crate_root, crate_id, context, + macro_processors, )); let submodules = vecmap(def_collector.def_map.modules().iter(), |(index, _)| index); @@ -255,7 +256,7 @@ impl DefCollector { // TODO(#4653): generalize this function for macro_processor in macro_processors { macro_processor - .process_unresolved_traits_impls( + .process_collected_defs( &crate_id, context, &def_collector.collected_traits_impls, diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 6ac263d80be..ae99e61e534 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -7,6 +7,7 @@ use noirc_errors::Location; use crate::{ graph::CrateId, hir::def_collector::dc_crate::{UnresolvedStruct, UnresolvedTrait}, + macros_api::MacroProcessor, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, FunctionDefinition, Ident, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, @@ -41,16 +42,28 @@ pub fn collect_defs( module_id: LocalModuleId, crate_id: CrateId, context: &mut Context, + macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut collector = ModCollector { def_collector, file_id, module_id }; let mut errors: Vec<(CompilationError, FileId)> = vec![]; // First resolve the module declarations for decl in ast.module_decls { - errors.extend(collector.parse_module_declaration(context, &decl, crate_id)); + errors.extend(collector.parse_module_declaration( + context, + &decl, + crate_id, + macro_processors, + )); } - errors.extend(collector.collect_submodules(context, crate_id, ast.submodules, file_id)); + errors.extend(collector.collect_submodules( + context, + crate_id, + ast.submodules, + file_id, + macro_processors, + )); // Then add the imports to defCollector to resolve once all modules in the hierarchy have been resolved for import in ast.imports { @@ -398,8 +411,6 @@ impl<'a> ModCollector<'a> { // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 attributes: crate::token::Attributes::empty(), is_unconstrained: false, - contract_function_type: None, - is_internal: None, }; let location = Location::new(name.span(), self.file_id); @@ -494,6 +505,7 @@ impl<'a> ModCollector<'a> { crate_id: CrateId, submodules: Vec, file_id: FileId, + macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; for submodule in submodules { @@ -506,6 +518,7 @@ impl<'a> ModCollector<'a> { child, crate_id, context, + macro_processors, )); } Err(error) => { @@ -524,6 +537,7 @@ impl<'a> ModCollector<'a> { context: &mut Context, mod_decl: &ModuleDeclaration, crate_id: CrateId, + macro_processors: &[&dyn MacroProcessor], ) -> Vec<(CompilationError, FileId)> { let mut errors: Vec<(CompilationError, FileId)> = vec![]; let child_file_id = @@ -559,7 +573,19 @@ impl<'a> ModCollector<'a> { // Parse the AST for the module we just found and then recursively look for it's defs let (ast, parsing_errors) = context.parsed_file_results(child_file_id); - let ast = ast.into_sorted(); + let mut ast = ast.into_sorted(); + + for macro_processor in macro_processors { + match macro_processor.process_untyped_ast(ast.clone(), &crate_id, context) { + Ok(processed_ast) => { + ast = processed_ast; + } + Err((error, file_id)) => { + let def_error = DefCollectorErrorKind::MacroError(error); + errors.push((def_error.into(), file_id)); + } + } + } errors.extend( parsing_errors.iter().map(|e| (e.clone().into(), child_file_id)).collect::>(), @@ -575,6 +601,7 @@ impl<'a> ModCollector<'a> { child_mod_id, crate_id, context, + macro_processors, )); } Err(error) => { diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 1049599f079..30a1ba2ee34 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -64,14 +64,10 @@ pub enum ResolverError { IncorrectGenericCount { span: Span, item_name: String, actual: usize, expected: usize }, #[error("{0}")] ParserError(Box), - #[error("Function is not defined in a contract yet sets its contract visibility")] - ContractFunctionTypeInNormalFunction { span: Span }, #[error("Cannot create a mutable reference to {variable}, it was declared to be immutable")] MutableReferenceToImmutableVariable { variable: String, span: Span }, #[error("Mutable references to array indices are unsupported")] MutableReferenceToArrayElement { span: Span }, - #[error("Function is not defined in a contract yet sets is_internal")] - ContractFunctionInternalInNormalFunction { span: Span }, #[error("Numeric constants should be printed without formatting braces")] NumericConstantInFormatString { name: String, span: Span }, #[error("Closure environment must be a tuple or unit type")] @@ -276,22 +272,12 @@ impl From for Diagnostic { ) } ResolverError::ParserError(error) => (*error).into(), - ResolverError::ContractFunctionTypeInNormalFunction { span } => Diagnostic::simple_error( - "Only functions defined within contracts can set their contract function type".into(), - "Non-contract functions cannot be 'open'".into(), - span, - ), ResolverError::MutableReferenceToImmutableVariable { variable, span } => { Diagnostic::simple_error(format!("Cannot mutably reference the immutable variable {variable}"), format!("{variable} is immutable"), span) }, ResolverError::MutableReferenceToArrayElement { span } => { Diagnostic::simple_error("Mutable references to array elements are currently unsupported".into(), "Try storing the element in a fresh variable first".into(), span) }, - ResolverError::ContractFunctionInternalInNormalFunction { span } => Diagnostic::simple_error( - "Only functions defined within contracts can set their functions to be internal".into(), - "Non-contract functions cannot be 'internal'".into(), - span, - ), ResolverError::NumericConstantInFormatString { name, span } => Diagnostic::simple_error( format!("cannot find `{name}` in this scope "), "Numeric constants should be printed without formatting braces".to_string(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 567cc20f789..00b1b443430 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -37,11 +37,11 @@ use crate::{ StatementKind, }; use crate::{ - ArrayLiteral, ContractFunctionType, Distinctness, ForRange, FunctionDefinition, - FunctionReturnType, Generics, ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, - PathKind, Pattern, Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, - UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, - UnresolvedTypeExpression, Visibility, ERROR_IDENT, + ArrayLiteral, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, Generics, + ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Shared, + StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, UnresolvedGenerics, + UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, + Visibility, ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -234,8 +234,6 @@ impl<'a> Resolver<'a> { let def = FunctionDefinition { name: name.clone(), attributes: Attributes::empty(), - is_open: false, - is_internal: false, is_unconstrained: false, visibility: ItemVisibility::Public, // Trait functions are always public generics: generics.clone(), @@ -973,9 +971,6 @@ impl<'a> Resolver<'a> { self.interner.push_definition_type(name_ident.id, typ.clone()); - self.handle_function_type(&func_id); - self.handle_is_function_internal(&func_id); - let direct_generics = func.def.generics.iter(); let direct_generics = direct_generics .filter_map(|generic| self.find_generic(&generic.0.contents)) @@ -1023,34 +1018,14 @@ impl<'a> Resolver<'a> { /// True if the `distinct` keyword is allowed on a function's return type fn distinct_allowed(&self, func: &NoirFunction) -> bool { if self.in_contract { - // "open" and "unconstrained" functions are compiled to brillig and thus duplication of + // "unconstrained" functions are compiled to brillig and thus duplication of // witness indices in their abis is not a concern. - !func.def.is_unconstrained && !func.def.is_open + !func.def.is_unconstrained } else { func.name() == MAIN_FUNCTION } } - fn handle_function_type(&mut self, function: &FuncId) { - let function_type = self.interner.function_modifiers(function).contract_function_type; - - if !self.in_contract && function_type == Some(ContractFunctionType::Open) { - let span = self.interner.function_ident(function).span(); - self.errors.push(ResolverError::ContractFunctionTypeInNormalFunction { span }); - self.interner.function_modifiers_mut(function).contract_function_type = None; - } - } - - fn handle_is_function_internal(&mut self, function: &FuncId) { - if !self.in_contract { - if self.interner.function_modifiers(function).is_internal == Some(true) { - let span = self.interner.function_ident(function).span(); - self.push_err(ResolverError::ContractFunctionInternalInNormalFunction { span }); - } - self.interner.function_modifiers_mut(function).is_internal = None; - } - } - fn declare_numeric_generics(&mut self, params: &[Type], return_type: &Type) { if self.generics.is_empty() { return; diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index f096c220200..3dc9d05b15e 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -664,7 +664,6 @@ pub enum Keyword { Let, Mod, Mut, - Open, Pub, Return, ReturnData, @@ -706,7 +705,6 @@ impl fmt::Display for Keyword { Keyword::Let => write!(f, "let"), Keyword::Mod => write!(f, "mod"), Keyword::Mut => write!(f, "mut"), - Keyword::Open => write!(f, "open"), Keyword::Pub => write!(f, "pub"), Keyword::Return => write!(f, "return"), Keyword::ReturnData => write!(f, "return_data"), @@ -751,7 +749,6 @@ impl Keyword { "let" => Keyword::Let, "mod" => Keyword::Mod, "mut" => Keyword::Mut, - "open" => Keyword::Open, "pub" => Keyword::Pub, "return" => Keyword::Return, "return_data" => Keyword::ReturnData, diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 800d66b0dfb..1871b594ae7 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -76,12 +76,12 @@ pub mod macros_api { ) -> Result; // TODO(#4653): generalize this function - fn process_unresolved_traits_impls( + fn process_collected_defs( &self, _crate_id: &CrateId, _context: &mut HirContext, - _unresolved_traits_impls: &[UnresolvedTraitImpl], - _collected_functions: &mut Vec, + _collected_trait_impls: &[UnresolvedTraitImpl], + _collected_functions: &mut [UnresolvedFunctions], ) -> Result<(), (MacroError, FileId)>; /// Function to manipulate the AST after type checking has been completed. diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 9d11ecd54bc..4938d33aff9 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -28,8 +28,8 @@ use crate::{ }, node_interner::{self, DefinitionKind, NodeInterner, StmtId, TraitImplKind, TraitMethodId}, token::FunctionAttribute, - ContractFunctionType, FunctionKind, IntegerBitSize, Signedness, Type, TypeBinding, - TypeBindings, TypeVariable, TypeVariableKind, UnaryOp, Visibility, + FunctionKind, IntegerBitSize, Signedness, Type, TypeBinding, TypeBindings, TypeVariable, + TypeVariableKind, UnaryOp, Visibility, }; use self::ast::{Definition, FuncId, Function, LocalId, Program}; @@ -312,8 +312,7 @@ impl<'interner> Monomorphizer<'interner> { Type::TraitAsType(..) => &body_return_type, _ => meta.return_type(), }); - let unconstrained = modifiers.is_unconstrained - || matches!(modifiers.contract_function_type, Some(ContractFunctionType::Open)); + let unconstrained = modifiers.is_unconstrained; let parameters = self.parameters(&meta.parameters); let body = self.expr(body_expr_id)?; diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index dc632527898..b83d2008530 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -28,8 +28,8 @@ use crate::hir_def::{ }; use crate::token::{Attributes, SecondaryAttribute}; use crate::{ - BinaryOpKind, ContractFunctionType, FunctionDefinition, Generics, ItemVisibility, Shared, - TypeAlias, TypeBindings, TypeVariable, TypeVariableId, TypeVariableKind, + BinaryOpKind, FunctionDefinition, Generics, ItemVisibility, Shared, TypeAlias, TypeBindings, + TypeVariable, TypeVariableId, TypeVariableKind, }; /// An arbitrary number to limit the recursion depth when searching for trait impls. @@ -241,15 +241,6 @@ pub struct FunctionModifiers { pub attributes: Attributes, pub is_unconstrained: bool, - - /// This function's type in its contract. - /// If this function is not in a contract, this is always 'Secret'. - pub contract_function_type: Option, - - /// This function's contract visibility. - /// If this function is internal can only be called by itself. - /// Will be None if not in contract. - pub is_internal: Option, } impl FunctionModifiers { @@ -262,8 +253,6 @@ impl FunctionModifiers { visibility: ItemVisibility::Public, attributes: Attributes::empty(), is_unconstrained: false, - is_internal: None, - contract_function_type: None, } } } @@ -759,17 +748,11 @@ impl NodeInterner { module: ModuleId, location: Location, ) -> DefinitionId { - use ContractFunctionType::*; - - // We're filling in contract_function_type and is_internal now, but these will be verified - // later during name resolution. let modifiers = FunctionModifiers { name: function.name.0.contents.clone(), visibility: function.visibility, attributes: function.attributes.clone(), is_unconstrained: function.is_unconstrained, - contract_function_type: Some(if function.is_open { Open } else { Secret }), - is_internal: Some(function.is_internal), }; self.push_function_definition(id, modifiers, module, location) } diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index a2a4577a993..06e1a958eb1 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -36,9 +36,6 @@ pub(super) fn function_definition(allow_self: bool) -> impl NoirParser impl NoirParser { choice((is_pub_crate, is_pub, is_private)) } -/// function_modifiers: 'unconstrained'? (visibility)? 'open'? +/// function_modifiers: 'unconstrained'? (visibility)? /// -/// returns (is_unconstrained, visibility, is_open) for whether each keyword was present -fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility, bool)> { +/// returns (is_unconstrained, visibility) for whether each keyword was present +fn function_modifiers() -> impl NoirParser<(bool, ItemVisibility)> { keyword(Keyword::Unconstrained) .or_not() .then(visibility_modifier()) - .then(keyword(Keyword::Open).or_not()) - .map(|((unconstrained, visibility), open)| { - (unconstrained.is_some(), visibility, open.is_some()) - }) + .map(|(unconstrained, visibility)| (unconstrained.is_some(), visibility)) } /// non_empty_ident_list: ident ',' non_empty_ident_list diff --git a/compiler/noirc_frontend/src/parser/parser/traits.rs b/compiler/noirc_frontend/src/parser/parser/traits.rs index df8d50178d5..1e2a6b4d65d 100644 --- a/compiler/noirc_frontend/src/parser/parser/traits.rs +++ b/compiler/noirc_frontend/src/parser/parser/traits.rs @@ -120,11 +120,7 @@ pub(super) fn trait_implementation() -> impl NoirParser { fn trait_implementation_body() -> impl NoirParser> { let function = function::function_definition(true).validate(|mut f, span, emit| { - if f.def().is_internal - || f.def().is_unconstrained - || f.def().is_open - || f.def().visibility != ItemVisibility::Private - { + if f.def().is_unconstrained || f.def().visibility != ItemVisibility::Private { emit(ParserError::with_reason(ParserErrorReason::TraitImplFunctionModifiers, span)); } // Trait impl functions are always public diff --git a/compiler/wasm/src/types/noir_artifact.ts b/compiler/wasm/src/types/noir_artifact.ts index 832a6ed9bf9..935c99043da 100644 --- a/compiler/wasm/src/types/noir_artifact.ts +++ b/compiler/wasm/src/types/noir_artifact.ts @@ -32,19 +32,16 @@ export interface EventAbi { fields: ABIVariable[]; } -/** The Noir function types. */ -export type NoirFunctionType = 'Open' | 'Secret' | 'Unconstrained'; - /** * The compilation result of an Noir function. */ export interface NoirFunctionEntry { /** The name of the function. */ name: string; - /** The type of the function. */ - function_type: NoirFunctionType; - /** Whether the function is internal. */ - is_internal: boolean; + /** Whether the function is unconstrained. */ + is_unconstrained: boolean; + /** The custom attributes applied to the function. */ + custom_attributes: string[]; /** The ABI of the function. */ abi: Abi; /** The bytecode of the function in base64. */ diff --git a/compiler/wasm/test/fixtures/deps/lib-c/src/lib.nr b/compiler/wasm/test/fixtures/deps/lib-c/src/lib.nr index 5c0b5a621e0..144bcec0532 100644 --- a/compiler/wasm/test/fixtures/deps/lib-c/src/lib.nr +++ b/compiler/wasm/test/fixtures/deps/lib-c/src/lib.nr @@ -1 +1 @@ -mod module; \ No newline at end of file +mod module; diff --git a/compiler/wasm/test/fixtures/deps/lib-c/src/module.nr b/compiler/wasm/test/fixtures/deps/lib-c/src/module.nr index 2746c97edf0..f4ad3bff5c9 100644 --- a/compiler/wasm/test/fixtures/deps/lib-c/src/module.nr +++ b/compiler/wasm/test/fixtures/deps/lib-c/src/module.nr @@ -1 +1 @@ -mod foo; \ No newline at end of file +mod foo; diff --git a/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr b/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr index e0c82fb1960..0376cd4cb87 100644 --- a/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr +++ b/compiler/wasm/test/fixtures/deps/lib-c/src/module/foo.nr @@ -1,3 +1,3 @@ pub fn bar(param: Field) -> Field { - dep::std::hash::pedersen_hash([param]) + dep::std::hash::pedersen_hash([param]) } diff --git a/compiler/wasm/test/fixtures/noir-contract/src/main.nr b/compiler/wasm/test/fixtures/noir-contract/src/main.nr index b980af369cf..fc1dc8a5a17 100644 --- a/compiler/wasm/test/fixtures/noir-contract/src/main.nr +++ b/compiler/wasm/test/fixtures/noir-contract/src/main.nr @@ -5,8 +5,7 @@ contract TestContract { [foo::bar(param), param + pub_param] } - open fn openFunction() -> pub Field { + fn someFunction() -> pub Field { 42 } - } diff --git a/docs/scripts/codegen_nargo_reference.sh b/docs/scripts/codegen_nargo_reference.sh index 4ff7d43d142..6a9fda9420b 100755 --- a/docs/scripts/codegen_nargo_reference.sh +++ b/docs/scripts/codegen_nargo_reference.sh @@ -30,4 +30,4 @@ sidebar_position: 0 --- " > $NARGO_REFERENCE -cargo run -F codegen-docs -- info >> $NARGO_REFERENCE +cargo run --bin nargo -F codegen-docs -- info >> $NARGO_REFERENCE diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/compile_failure/assert_msg_runtime/src/main.nr index bec3082550a..fa21442e816 100644 --- a/test_programs/compile_failure/assert_msg_runtime/src/main.nr +++ b/test_programs/compile_failure/assert_msg_runtime/src/main.nr @@ -4,4 +4,4 @@ fn main(x: Field, y: pub Field) { let z = x + y; assert(z != y, f"Expected z != y, but got both equal {z}"); assert_eq(x, y, f"Expected x == y, but x is {x} and y is {y}"); -} \ No newline at end of file +} diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr b/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr index 428b2006363..bd77551e304 100644 --- a/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr +++ b/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr @@ -7,4 +7,4 @@ unconstrained fn conditional(x: Field) -> Field { assert_eq(z, 25, f"Expected 25 but got {z}"); assert(x == 10, f"Expected x to equal 10, but got {x}"); 1 -} \ No newline at end of file +} diff --git a/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr b/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr index cf3279cac0d..473ad8e8d6a 100644 --- a/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr +++ b/test_programs/compile_failure/brillig_mut_ref_from_acir/src/main.nr @@ -5,4 +5,4 @@ unconstrained fn mut_ref_identity(value: &mut Field) -> Field { fn main(mut x: Field, y: pub Field) { let returned_x = mut_ref_identity(&mut x); assert(returned_x == x); -} \ No newline at end of file +} diff --git a/test_programs/compile_success_contract/contract_with_impl/src/main.nr b/test_programs/compile_success_contract/contract_with_impl/src/main.nr index ddcb5d54d78..1c6b6c217c4 100644 --- a/test_programs/compile_success_contract/contract_with_impl/src/main.nr +++ b/test_programs/compile_success_contract/contract_with_impl/src/main.nr @@ -2,6 +2,6 @@ contract Foo { struct T { x: [Field] } impl T { - fn t(self){} + fn t(self) {} } } diff --git a/test_programs/compile_success_contract/simple_contract/src/main.nr b/test_programs/compile_success_contract/simple_contract/src/main.nr index ed90ac8bd1d..7412e1386bf 100644 --- a/test_programs/compile_success_contract/simple_contract/src/main.nr +++ b/test_programs/compile_success_contract/simple_contract/src/main.nr @@ -8,12 +8,9 @@ contract Foo { fn quadruple(x: Field) -> pub Field { x * 4 } - open fn skibbidy(x: Field) -> pub Field { - x * 5 - } // Regression for issue #3344 #[contract_library_method] - fn foo(x : u8) -> u8 { + fn foo(x: u8) -> u8 { x } } diff --git a/test_programs/execution_success/brillig_cow_regression/src/main.nr b/test_programs/execution_success/brillig_cow_regression/src/main.nr index ba51548d9dd..1cae9b1ba41 100644 --- a/test_programs/execution_success/brillig_cow_regression/src/main.nr +++ b/test_programs/execution_success/brillig_cow_regression/src/main.nr @@ -8,9 +8,9 @@ global MAX_NEW_CONTRACTS_PER_TX: u64 = 1; global NUM_ENCRYPTED_LOGS_HASHES_PER_TX: u64 = 1; global NUM_UNENCRYPTED_LOGS_HASHES_PER_TX: u64 = 1; global NUM_FIELDS_PER_SHA256 = 2; -global CALLDATA_HASH_INPUT_SIZE = 169; -global CALL_DATA_HASH_LOG_FIELDS = 4; -global CALL_DATA_HASH_FULL_FIELDS = 165; +global TX_EFFECT_HASH_INPUT_SIZE = 169; +global TX_EFFECT_HASH_LOG_FIELDS = 4; +global TX_EFFECT_HASH_FULL_FIELDS = 165; struct PublicDataUpdateRequest { leaf_slot : Field, @@ -99,7 +99,7 @@ impl U256 { } unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA256] { - let mut calldata_hash_inputs = [0; CALLDATA_HASH_INPUT_SIZE]; + let mut tx_effects_hash_inputs = [0; TX_EFFECT_HASH_INPUT_SIZE]; let new_note_hashes = kernel_data.new_note_hashes; let new_nullifiers = kernel_data.new_nullifiers; @@ -111,65 +111,65 @@ unconstrained fn main(kernel_data: DataToHash) -> pub [Field; NUM_FIELDS_PER_SHA let mut offset = 0; for j in 0..MAX_NEW_NOTE_HASHES_PER_TX { - calldata_hash_inputs[offset + j] = new_note_hashes[j]; + tx_effects_hash_inputs[offset + j] = new_note_hashes[j]; } offset += MAX_NEW_NOTE_HASHES_PER_TX ; for j in 0..MAX_NEW_NULLIFIERS_PER_TX { - calldata_hash_inputs[offset + j] = new_nullifiers[j]; + tx_effects_hash_inputs[offset + j] = new_nullifiers[j]; } offset += MAX_NEW_NULLIFIERS_PER_TX ; for j in 0..MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX { - calldata_hash_inputs[offset + j * 2] = + tx_effects_hash_inputs[offset + j * 2] = public_data_update_requests[j].leaf_slot; - calldata_hash_inputs[offset + j * 2 + 1] = + tx_effects_hash_inputs[offset + j * 2 + 1] = public_data_update_requests[j].new_value; } offset += MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * 2; for j in 0..MAX_NEW_L2_TO_L1_MSGS_PER_TX { - calldata_hash_inputs[offset + j] = newL2ToL1msgs[j]; + tx_effects_hash_inputs[offset + j] = newL2ToL1msgs[j]; } offset += MAX_NEW_L2_TO_L1_MSGS_PER_TX; let contract_leaf = kernel_data.new_contracts[0]; - calldata_hash_inputs[offset] = contract_leaf.hash(); + tx_effects_hash_inputs[offset] = contract_leaf.hash(); offset += MAX_NEW_CONTRACTS_PER_TX; let new_contracts = kernel_data.new_contracts; - calldata_hash_inputs[offset] = new_contracts[0].contract_address; + tx_effects_hash_inputs[offset] = new_contracts[0].contract_address; - calldata_hash_inputs[offset + 1] = new_contracts[0].portal_contract_address; + tx_effects_hash_inputs[offset + 1] = new_contracts[0].portal_contract_address; offset += MAX_NEW_CONTRACTS_PER_TX * 2; for j in 0..NUM_FIELDS_PER_SHA256 { - calldata_hash_inputs[offset + j] = encryptedLogsHash[j]; + tx_effects_hash_inputs[offset + j] = encryptedLogsHash[j]; } offset += NUM_ENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256; for j in 0..NUM_FIELDS_PER_SHA256 { - calldata_hash_inputs[offset + j] = unencryptedLogsHash[j]; + tx_effects_hash_inputs[offset + j] = unencryptedLogsHash[j]; } offset += NUM_UNENCRYPTED_LOGS_HASHES_PER_TX * NUM_FIELDS_PER_SHA256; - assert_eq(offset, CALLDATA_HASH_INPUT_SIZE); // Sanity check + assert_eq(offset, TX_EFFECT_HASH_INPUT_SIZE); // Sanity check - let mut hash_input_flattened = [0; CALL_DATA_HASH_FULL_FIELDS * 32 + CALL_DATA_HASH_LOG_FIELDS * 16]; - for offset in 0..CALL_DATA_HASH_FULL_FIELDS { - let input_as_bytes = calldata_hash_inputs[offset].to_be_bytes(32); + let mut hash_input_flattened = [0; TX_EFFECT_HASH_FULL_FIELDS * 32 + TX_EFFECT_HASH_LOG_FIELDS * 16]; + for offset in 0..TX_EFFECT_HASH_FULL_FIELDS { + let input_as_bytes = tx_effects_hash_inputs[offset].to_be_bytes(32); for byte_index in 0..32 { hash_input_flattened[offset * 32 + byte_index] = input_as_bytes[byte_index]; } } - for log_field_index in 0..CALL_DATA_HASH_LOG_FIELDS { - let input_as_bytes = calldata_hash_inputs[CALL_DATA_HASH_FULL_FIELDS + log_field_index].to_be_bytes(16); + for log_field_index in 0..TX_EFFECT_HASH_LOG_FIELDS { + let input_as_bytes = tx_effects_hash_inputs[TX_EFFECT_HASH_FULL_FIELDS + log_field_index].to_be_bytes(16); for byte_index in 0..16 { - hash_input_flattened[CALL_DATA_HASH_FULL_FIELDS * 32 + log_field_index * 16 + byte_index] = input_as_bytes[byte_index]; + hash_input_flattened[TX_EFFECT_HASH_FULL_FIELDS * 32 + log_field_index * 16 + byte_index] = input_as_bytes[byte_index]; } } diff --git a/test_programs/execution_success/double_verify_proof/Nargo.toml b/test_programs/execution_success/double_verify_proof/Nargo.toml index a4edd2e4288..c5954f54bdb 100644 --- a/test_programs/execution_success/double_verify_proof/Nargo.toml +++ b/test_programs/execution_success/double_verify_proof/Nargo.toml @@ -2,4 +2,6 @@ name = "double_verify_proof" type = "bin" authors = [""] -[dependencies] +compiler_version = ">=0.24.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/double_verify_proof/src/main.nr b/test_programs/execution_success/double_verify_proof/src/main.nr index e4c6926efbc..4edf5c5af9f 100644 --- a/test_programs/execution_success/double_verify_proof/src/main.nr +++ b/test_programs/execution_success/double_verify_proof/src/main.nr @@ -1,6 +1,5 @@ use dep::std; -#[recursive] fn main( verification_key: [Field; 114], // This is the proof without public inputs attached. diff --git a/test_programs/execution_success/double_verify_proof_recursive/Nargo.toml b/test_programs/execution_success/double_verify_proof_recursive/Nargo.toml new file mode 100644 index 00000000000..b7688b33bfd --- /dev/null +++ b/test_programs/execution_success/double_verify_proof_recursive/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "double_verify_proof_recursive" +type = "bin" +authors = [""] +[dependencies] diff --git a/test_programs/execution_success/double_verify_proof_recursive/Prover.toml b/test_programs/execution_success/double_verify_proof_recursive/Prover.toml new file mode 100644 index 00000000000..dff48212e50 --- /dev/null +++ b/test_programs/execution_success/double_verify_proof_recursive/Prover.toml @@ -0,0 +1,5 @@ +key_hash = "0x096129b1c6e108252fc5c829c4cc9b7e8f0d1fd9f29c2532b563d6396645e08f" +proof = ["0x000000000000000000000000000000d62b795bec274279129a71195796825fcc","0x00000000000000000000000000000000000793ab763140f20a68a6bd2721fd74","0x00000000000000000000000000000053141d06d3307b36153f321511199e579c","0x00000000000000000000000000000000000a4b55d6c21f98a9c434911dcb5c67","0x0000000000000000000000000000005f9d324c0abd22cec92d99dbec438e9491","0x0000000000000000000000000000000000240dfafe1b53dc27147cbab14ea893","0x000000000000000000000000000000044a61d3aac32c6931247cf334a19d9611","0x000000000000000000000000000000000003f0f8cf4207bfa85c23ec9f8d0c88","0x00000000000000000000000000000002168a470e39ba2ac266f6b474de12045f","0x000000000000000000000000000000000025791e7d3feab542345c00ec5a30df","0x000000000000000000000000000000dcafd76d4c3640969c80e017b951ef6397","0x00000000000000000000000000000000001d27f75a1256771e88e0c86fc42dbc","0x0000000000000000000000000000007347ae7d2d9d7fc2b8f0baa014ee1fed9f","0x000000000000000000000000000000000018bd927f42bf7caf9555f56f09000d","0x000000000000000000000000000000041f765f83cbe5904c8f453f70a4531d10","0x00000000000000000000000000000000001858aabeeb5331a221419f4fed1c19","0x000000000000000000000000000000d254a54caaedf8287b9af951b2f2611121","0x000000000000000000000000000000000005ab493623c9563cf2e55ba5f18200","0x00000000000000000000000000000014f24cddc1a02440dc63637df8032c8074","0x000000000000000000000000000000000011950c16cef98471b1d78b935195a4","0x000000000000000000000000000000b0340b459e6bd5cc8f031c8654a502897f","0x00000000000000000000000000000000000e1cf3968dac4545a76a2ae58e512c","0x0000000000000000000000000000002adf7218aa06ca0d2c2e600dcc39193a2d","0x00000000000000000000000000000000001302e7e4b0f14749bd885ca25588b6","0x00000000000000000000000000000092009ce4056e79ab815d8cdfd4491138ae","0x000000000000000000000000000000000018af11e853c6cf2f0f6274b0da8133","0x000000000000000000000000000000dd3dc6f49232141718527b3a0e4b26e21d","0x00000000000000000000000000000000001a877853348a8b695c4f9a9aa4ce68","0x000000000000000000000000000000aecfc56ba07155450b368140d6324023b5","0x000000000000000000000000000000000029c11052798c57ece614617d33fcc2","0x000000000000000000000000000000eb106ffc816d16fb84e84b0b61157b2603","0x000000000000000000000000000000000026c3cac16206899a21cb5126841446","0x000000000000000000000000000000a782ed54805fe845068b362b58e2fa34ec","0x00000000000000000000000000000000000cf046a1bfcc666b7f28b572676073","0x000000000000000000000000000000b931c8dda60bb4aca4cc817f5540f1209f","0x000000000000000000000000000000000024ad50c3936fafc3d190e6a4874223","0x000000000000000000000000000000cce90cfbaf5671c8c8652db28a3a9566f7","0x000000000000000000000000000000000003574db9d0f84380c9635660f86354","0x0000000000000000000000000000003eb3e1dc31846a90f721e7a08c6d6dc4f7","0x000000000000000000000000000000000028999a700cd1abae1a288eebb9a91c","0x000000000000000000000000000000c1be4d385b11387e14eb9817050d772f78","0x000000000000000000000000000000000003c56b5bad8b4484c66ac921f1f102","0x000000000000000000000000000000ace245cabf0f00dc7fd253dd8af0377a14","0x0000000000000000000000000000000000107f1731fcf34b364c813599fa1df7","0x035b937d404932b542b706eb810ef4a7dca4566d4dde1ad6a8717f46167ead7e","0x17608cef3dc7960f41cb1295706df663727d45ee598a61e05e989d111449fb65","0x054712a950ad67da3aa860e49e6891f99b586b7f37caff94eb013fdb374b61ee","0x04b755083086c769b7f593e0e48d68dc54be808203351380ca5566a48149d8bb","0x17d7670b0915235f626fdc1d7e1134d2be906ef138d7843384b3ebc23b1d630f","0x064cf544ab5f4e3dab47960502cccc83321fb275068dfbdd3a2fcbc6dddcaa65","0x083338262712e2b66769ea40d9f412b18caa1bc81a51ff5a50b6c41f8c4b3d23","0x0cdd38958cab97defde00f4a5961b6fd676e29d9f2c352f6bb2c68b91f83f8af","0x02c8bdd005c2f43a0a8cbb2744916ce5c322dfa5b23367a829c12699f4036d32","0x25bac73c7e7b659fbea3135b7a0decf9db8dc3045bd2837dae337c64cc722546","0x19eb361aa419d37bce3d2e8b2b7692a02a9559e83d7f3d8fe9169970fbbc2cba","0x2494bd5106d00e05c7ea60e632e9fe03773b7f2c5b662aa37ec512a01f4a0775","0x18c52c2f2c6e7be1d7847c15e452a3a9c64316103d12e4b5b9a82fac4e940ee9","0x0e0342810456ef78f498c1bfa085a5f3cbc06db1f32fabd0ea9ad27dccac1680","0x024c13d6ef56af33ed7164ea8e47ddecc8a487b000d8b1b45edcd3895a503ba2","0x26e0d127f626bd39b55bc5d0c131dbf03fe006dc5c3edc57dda1e629799a4317","0x1b1140061bc52b15c4f5e100729a81968ee79dc03deb966a18850335a8e44a8b","0x1bb76f945199e71d531a89288912087a02dd0e83020e65d671485bf2e5e86e1a","0x29269900859c6d86e404185b415bf3b279cd100f38cfdb0077e8d6a299c4fd35","0x22b5e94bae2f6f0cdb424a3b12c4bf82cec3fb228e012c1974ed457827bbe012","0x18d3543a93249778e7a57936170dae85ffc47c2567f2d0076a32c0bb86fcf10a","0x03721dc2670206cde42a175fd56bcce32cf6cb8801450a8e8e4b3d4e07785973","0x2806db136dd214d3ac1478460855cae6a4324ab45cab35320d104fee26c260e8","0x1c3749f1937082afbbae9375b9be708cf339e1983e57ef4447f36cfa560c685c","0x1067b8cfb90ef08bcb48aea56b2716334241787c2004a95682d68a0685566fd0","0x0f41aee4416398f1d48ffc302403273cddef34a41f98507c53682041d82e51ff","0x10d854c9f0bfbdff7ca91a68f4978e9a79e7b14243d92f465f17bdf88d9f64f8","0x00000000000000000000000000000000018938b11099e0cdc05ddab84a153a97","0x0000000000000000000000000000000001d7dda1471f0dc3b3a3d3438c197982","0x00000000000000000000000000000000022682917da43ab9a6e9cbcece1db86d","0x2453913e6b0f36eab883ac4b0e0604d56aaeb9c55e641135173e63c342f1a660","0x05216c1b58dc43a49d01aaba3113b0e86be450fc17d28016e648e7162a1b67fb","0x152b34845a0222a2b41354c0d395a250d8363dc18748647d85acd89d6934ec56","0x1dfc6e971ce82b7dcda1f7f282713c6e22a8c79258a61209bda69719806da544","0x2968dd8b3af8e3953f1fbbd72f4c49b8270597bb27d4037adc157ac6083bee60","0x1b9425b88a4c7d39b3d75afe66917a9aa1d2055724392bc01fb918d84ff1410e","0x04ab571f236d8e750904dc307dd274003d9130f1a7110e4c1521cfb408877c73","0x2ad84f26fdc5831545272d02b806bb0e6dae44e71f73552c4eb9ff06030748c7","0x020e632b99d325db774b8630fb50b9a4e74d35b7f27d9fc02c65087ee747e42c","0x09a8c5a3171268cb61c02515c01c109889200ed13f415ae54df2078bbb887f92","0x1143281a9451abbb4c34c3fa84e7678c2af2e7ea8c05160a6f7f06988fc91af8","0x000000000000000000000000000000cbda736ca5cf6bc75413c2cc9e28ab0a68","0x00000000000000000000000000000000001ee78c9cc56aa5991062ae2e338587","0x000000000000000000000000000000bc9bfcdebb486f4cb314e681d2cc5f8df6","0x00000000000000000000000000000000000ad538431d04771bca7f633cb659ff","0x000000000000000000000000000000d45b317afcefa466a59bba9e171f1af70c","0x0000000000000000000000000000000000133c50180ea17932e4881124e7a7c6","0x000000000000000000000000000000fc9ed37f543775849f3e84eaa06f77f992","0x00000000000000000000000000000000001372873c9c051d1baff99248b8f70e"] +public_inputs = ["0x0000000000000000000000000000000000000000000000000000000000000003"] +verification_key = ["0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000005","0x0000000000000000000000000000000000000000000000000000000000000008","0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000","0x00000000000000000000000000000092139c61bae1a44f0fc7689507414be688","0x00000000000000000000000000000000000160ce4e279582f91bde4f03f5e9a2","0x0000000000000000000000000000005dc2d37f658c3b2d60f24740eb13b65d79","0x000000000000000000000000000000000007e3e8a5d98a1177ec85bf88f163a5","0x000000000000000000000000000000dc3035fbd7ff16412a8fd7da587a935298","0x000000000000000000000000000000000023d08e2817ac16990004ed11d8fc66","0x000000000000000000000000000000356a5ad59c646c746a8d09f5d154e47c4f","0x00000000000000000000000000000000000708529196af3c8e16ffa580c26182","0x0000000000000000000000000000002ddfe70eb7a1280596e8e4a804f118a6dd","0x000000000000000000000000000000000013757e15a0905f298303784a161b21","0x000000000000000000000000000000a23a729df796935c7824e3a26be794829b","0x000000000000000000000000000000000005775b6c146c4a59856e869fe5a70e","0x000000000000000000000000000000eef0c9e088fd2d45aa40311082d1f2809b","0x00000000000000000000000000000000001d539ccbfc556d0ad59307a218de65","0x000000000000000000000000000000a2c848beceb6ab7806fd3b88037b8410fc","0x0000000000000000000000000000000000177004deeb1f9d401fd7b1af1a5ac8","0x0000000000000000000000000000002508eb63672a733f20de1a97644be4f540","0x00000000000000000000000000000000000d82d51f2f75d806285fd248c819b8","0x000000000000000000000000000000d002f9100cbba8a29f13b11513c53c59d0","0x000000000000000000000000000000000006cd3b0e3460533b9e5ea2cdc0fcbb","0x000000000000000000000000000000f45ea38a93b2f810c5633ddb54927c1c96","0x000000000000000000000000000000000021791de65f9a28ec7024b1a87ab4f3","0x000000000000000000000000000000926511a0439502c86885a8c6f0327aa7ad","0x000000000000000000000000000000000029fa14a969c5d81ed3abbbfb11220a","0x000000000000000000000000000000b84c3258e8206f560e5b5b18cbeafef87e","0x00000000000000000000000000000000002a910445cd8fc895e5d235cd8ea185","0x000000000000000000000000000000887e67f15e84bcb8507a5064a363f6043b","0x000000000000000000000000000000000014dc6643d801c3ef27c2066b6e2bb4","0x000000000000000000000000000000e38e900b42c314ba803088e8fbf125203f","0x000000000000000000000000000000000020690fd4869db418306046b38161dc","0x0000000000000000000000000000001e2fa856bf7951b8292b1e88185993629c","0x0000000000000000000000000000000000048a85e0bbac7c60ad3d78f601f63c","0x0000000000000000000000000000006f457719495073d3666d77a625aeab0c51","0x00000000000000000000000000000000002623ad892dc62b1fa7d0a650f0d470","0x000000000000000000000000000000dbfcc8a467e021c03b13f74a9f79c3a10c","0x0000000000000000000000000000000000295f6f10976c37bd9c6f96bb7187d5","0x000000000000000000000000000000c13ef9a937cc12420fb38d9ab8e848e85e","0x000000000000000000000000000000000003560a3b334e887532f605c9cb7628","0x0000000000000000000000000000009bcebf08a4599cdda0fb96312d4dc0c7a9","0x000000000000000000000000000000000015adc8bb1e01c835f48959d1237bd6","0x00000000000000000000000000000047762ab839e4ff63c77605a9f383da37c2","0x000000000000000000000000000000000016a8c3c53d89660cf271522cd301fb","0x000000000000000000000000000000f0c8539a0b5f94420a513f9c305b932bfe","0x00000000000000000000000000000000002957ba01d9de5638f808f88a692533","0x000000000000000000000000000000ab17c6189d67d3bf5dd2f3885de0151b6f","0x0000000000000000000000000000000000060d8aa43fdc434d1942263f364d95","0x0000000000000000000000000000005d292333b3adb497f00b4bc32d45229060","0x00000000000000000000000000000000001a1018a66221883639f2898a66f345","0x00000000000000000000000000000006555a806b1993291deba0dc44e2abf431","0x00000000000000000000000000000000000cacff7099a9d5e35a21f4a00b2dc3","0x000000000000000000000000000000f50c11ba95d349c36d143eefd12e494950","0x00000000000000000000000000000000001022e8c5f02d639bc9dd8bc4407f99","0x000000000000000000000000000000c76828795098eda73d50b4b585c60afc60","0x00000000000000000000000000000000002bf09c0ec7011e93888962f2406630","0x00000000000000000000000000000049e5c83a8978d832fb8e144548e3ca1adb","0x00000000000000000000000000000000000e0ec242c2e160a984f61ca5adf5f5","0x0000000000000000000000000000009c5d6e08a6605ab4513748ac0fa017dd1c","0x00000000000000000000000000000000001f54baa07558e5fb055bd9ba49c067","0x0000000000000000000000000000001e1ee7ee29bbb5e4b080c6091c1433ce62","0x000000000000000000000000000000000024aec62a9d9763499267dc98c33428","0x0000000000000000000000000000001a96755946ff16f0d6632365f0eb0ab4d4","0x000000000000000000000000000000000028cf3e22bcd53782ebc3e0490e27e5","0x00000000000000000000000000000043148d7d8c9ba43f2133fab4201435a364","0x0000000000000000000000000000000000234ce541f1f5117dd404cfaf01a229","0x000000000000000000000000000000a7fb95ffb461d9514a1070e2d2403982ef","0x00000000000000000000000000000000003016955028b6390f446c3fd0c5b424","0x00000000000000000000000000000008863c3b7cd7cddc20ba79ce915051c56e","0x000000000000000000000000000000000013ef666111b0be56a235983d397d2a","0x000000000000000000000000000000e3993f465fc9f56e93ac769e597b752c1c","0x0000000000000000000000000000000000217f7c4235161e9a3c16c45b6ca499","0x0000000000000000000000000000008ffa4cd96bc67b0b7df5678271e1114075","0x0000000000000000000000000000000000256467bfcb63d9fdcb5dde397757ad","0x00000000000000000000000000000054e5eb270bb64bde6e6ececadfd8c3236c","0x00000000000000000000000000000000000e52d1bd75812c33c6f3d79ee4b94c","0x000000000000000000000000000000484a2c641dce55bc2dd64ef0cd790a7fea","0x00000000000000000000000000000000000ff417d256be43e73c8b1aa85bdda3","0x0000000000000000000000000000000b72e7b7713ab5da44e0f864182e748a23","0x00000000000000000000000000000000001a221055f1625ad833a44705f5f74e","0x00000000000000000000000000000067a99a34e9b81a17ad001db02e29bcb82a","0x000000000000000000000000000000000018a6c02e398389827568fa960e86e2","0x000000000000000000000000000000bb29f26f9890d6cc6401f4921d5884edca","0x00000000000000000000000000000000000868357b28039385c5a5058b6d358e","0x00000000000000000000000000000036fb6e229dde8edf7ec858b12d7e8be485","0x00000000000000000000000000000000001060afe929554ca473103f5e68193c","0x00000000000000000000000000000015226e07e207744c0857074dcab883af4a","0x00000000000000000000000000000000000b1c02619282755533457230b19b4a","0x0000000000000000000000000000001f2a0277e4807e6e1cbabca21dde5eb5e1","0x00000000000000000000000000000000000d928deafed363659688ed4ccdef52","0x000000000000000000000000000000363f0c994e91cecad25835338edee2294f","0x00000000000000000000000000000000002eea648c8732596b1314fe2a4d2f05","0x000000000000000000000000000000b2671d2ae51d31c1210433c3972bb64578","0x00000000000000000000000000000000000ab49886c2b94bd0bd3f6ed1dbbe2c"] +proof_b = ["0x000000000000000000000000000000f05c69448ca29bdf52076f9b073bb30fed","0x000000000000000000000000000000000028c86bb3e27b4aaaaef126f7df5349","0x00000000000000000000000000000026ae031fc93594375dfc7f3bbe027f97d5","0x000000000000000000000000000000000000dd12c7290fe7f775796a233b8590","0x000000000000000000000000000000c1ee6631704de424d010c5c4ac8293ac49","0x00000000000000000000000000000000002f41818c9aa83f5c8d9bdd128015b9","0x000000000000000000000000000000b50a5801482f7e3a5de8ab3cce0f10b0d3","0x000000000000000000000000000000000022a0bc69c293dbf293b25bc9eef7f8","0x0000000000000000000000000000003b02abf1967ef394154dc15d763135e903","0x00000000000000000000000000000000000d8a2ee46acc6d1ed8d517b56d47c8","0x00000000000000000000000000000039bf0d1b3d8cf9de898f101c626e978d78","0x0000000000000000000000000000000000008faa7df2451a24d291a9b584f1a5","0x000000000000000000000000000000c1dae329ed7adf63a2d89a5f16fb98b6d8","0x00000000000000000000000000000000001ff0bc16fc0bd4aa2d6255690453c2","0x000000000000000000000000000000d12d7589f853a9b472613efa56689beaf1","0x00000000000000000000000000000000002d6fbc798f4403751df6aeee8bedd3","0x0000000000000000000000000000007c1fa069cb17194fecf88db9dd54a4ee36","0x0000000000000000000000000000000000268e026f9814822a42b2d59eec5d24","0x000000000000000000000000000000c3fb56beab774218cd63498fc050a5fd9b","0x00000000000000000000000000000000000071c014d7b5063f005a0bc2ee1af4","0x000000000000000000000000000000ae12b25371c6af42bbe0a85cddd2eaebc7","0x000000000000000000000000000000000026d270e1ffc9c7c344c694dfadda83","0x00000000000000000000000000000080280858c6be461716921caa3c26f3f6f3","0x000000000000000000000000000000000001dcdd3f39e27d0ce6aa5d14dff4c1","0x000000000000000000000000000000080e1d2c913c834ebcf7e0600c076c08fd","0x00000000000000000000000000000000002df3d142217694e65fb7c355d62764","0x000000000000000000000000000000e5e336f3f59d77e500f49771bfbeb12e83","0x000000000000000000000000000000000028fffe08bdc4c0690643d2e1a1275f","0x000000000000000000000000000000db5618b32afc13e18f21b39f3fbede9d11","0x00000000000000000000000000000000001d244818370d43fb7e8bc67e03787b","0x0000000000000000000000000000006bcc1fd3f9f78449ad1df1bc11bc379edd","0x000000000000000000000000000000000009ac9cbb285edbf5b3a973f3f5f1cb","0x000000000000000000000000000000fd885905b6c0fc95bb4dd0b11f6797d4b3","0x000000000000000000000000000000000021f07995cdd835145e19c38127c562","0x000000000000000000000000000000bbbf2b975c2c97ae4b45c4a52059e53ee3","0x000000000000000000000000000000000024158163788841cf4590bbc1e89a90","0x0000000000000000000000000000009aca93d2b1386ea412d4b36ea5bb9894a8","0x00000000000000000000000000000000002532d1d210e8ed4c2f5c00cbaaa475","0x000000000000000000000000000000634a88caa1d77cb6b5fe77cac31458fc31","0x00000000000000000000000000000000000bdf18bae92fce7cfddab5520cac6e","0x000000000000000000000000000000622e9626255170ccec77602c755aa193e1","0x000000000000000000000000000000000001d4edba370e04436a988bad05dada","0x000000000000000000000000000000b52934323a0aec8f803cdaafee2ab7bfb2","0x0000000000000000000000000000000000155312af5e0e25ca9fd61aef9e58ed","0x06270b517855f6f6a608e432883d1d1030a12a1e33022dc142b7728691421da2","0x2af7c794d7b720b25eb1df0afd8c8e3c15b6e518194c3caea7966a5f8210ff04","0x073fe573aeb27d81a5713be93e1365390dcbc3c8e7439ff1d36a84cc014f5642","0x11351b961147431e54535248b58b35cf5cddb9b13827899167617d7a96794d64","0x297c9421c9c3db286770787c35b86bc41583386491b4ae55e5fa81aefa21efc4","0x0f4eeca3ff4a3495f859898937688652d33f9b4dd3e003e12adf15278e0997c3","0x133e3d8b82721d40d919f2326810ba6f07eff3f7d20d86b2bde692a811522019","0x2c502f53c9698b73bb8c8f9b9cf2d705d16a64a7040348b4b39c637a2064316c","0x0cbc1971e1c566cde9d9125c91cdc88e817db182692f836c1a5170a6246eaf73","0x12c47793e7db706c637cd4b4d96d227f569850176b852b1fe8ad522ddb38ef0e","0x0cd7b300e9309a135285be1aeb02b152f97931a7357ab6d609a2cb1970aab877","0x2a7789dfe286c9d0a7592f1c9316e730cb14c9d843aefc4764d76e7f8571c96a","0x248ac54ce3dbf37796621882a4ac76046df5ab680da487fd85cce76b1ae392d3","0x149d1d07cebe320f77b03533e34912545cedeae62bd9778d37724728762b5710","0x00fe29daebdaed61309790e70e2dcefa3f3af4c6c965ce424b8dbcf09b8e4b49","0x2b75b3bace61b731d7f0c003a144b62b0a4fbe9f0d14ca89b0652b70210014b3","0x2588ef27cfb6e0d8c6f9a969b2da44fead30a02ed70a563fd15aa45bb671de1c","0x2b74d7674b55642697b4a1e226eddb0e4918b2d57aa5b99093dc46cadcdea000","0x244c626845d3a5040f08f01e9611f968ad675ca857789149b13a0cfa83a2e064","0x2cb8d02f90cae33fd7bcfb80af4aff067c4f5fc4b3f9228d5b8f768bc8f6c971","0x1372f3d1f04e0c39a50e823d5da03d70bebe19a1b8e28f8c2ff601cc0bfc0095","0x19af6601d2613426a50b7c35d60562a5f2f2634e6af56dac13459632e15570ee","0x13c2a16ed3b65dcd9414659be79af17995d344de34eaf962343b0f1e76c73a57","0x0dd5dcdbd50b8774831d4f01f930804d38b4266dfee085185530880a0c3903c0","0x07e91848d660b11b722638680ac60f20db9507fdc8d610ce762600f5a1aacd29","0x1f9c2a94d10c0a7fb60292cfc46fd3d2501181bea0ffe1f5f2501d474be3a785","0x14edb9c5bd389eae08a5ea2a7a1662894e1e878c142084d966a625bef68cf7c3","0x00000000000000000000000000000000cecd01810814d175f0a533f0067618c4","0x00000000000000000000000000000000f82935013ce5c82720c63e533af41db8","0x000000000000000000000000000000012185688171b6bed850e748b66f7222ac","0x2dd7f5ff2150155c2ac86ebe28d9ecbca2eea812b0021ab2bceae111cfea8325","0x04ea6c2daf2b9e827d2213c3d03953410dcf1ed67ba34a3c00e772be92606a8b","0x163f2bd18dcde52f99b9867c944780fd718d1612927053b139b280fc55013d1b","0x05e388fd160ccac30a8f7b18a4bd042f705e92b5937e8c0e9478e2ff623907c6","0x00ba3f6f527d6ed3ff17a63b1d5be3c42bdfae88fdf63311fc7b871157939309","0x16187d9daa8c2e5a1a9ab15be7ca6a8feebfb31bea76f9a3ca69381881c70561","0x0f64522e4904edb7377b14a7b9dad848829167324ef5c016346b3ad8251191ee","0x273bbe6000a4001dce369e5a36cc0b0ca3fd351665b688238aa8c556a6ca6b8e","0x022d2232efb2faa8307846c9a4c697aabad1b7f1336b35ad72fa8922975b49d9","0x0d82d478bff3955c4b0a34ef94427ca5f9da23147ad953c89f2e428277ec2825","0x18d886be90343010659c231583be61a138e28e37c24771e3cb61fbe2587d0671","0x000000000000000000000000000000196ba6a58dbeb7c34cb1d6287e23d434de","0x00000000000000000000000000000000001df8ae8a1589590f8863c1fefd8dfd","0x000000000000000000000000000000f30e11b2c5fbefa166cbb9f58c5f8e1a4c","0x000000000000000000000000000000000026420ade7666bc0ab1cf1fd9d0c534","0x0000000000000000000000000000000feb5b7d8260d25a1ee1ce76ff461673fc","0x00000000000000000000000000000000002bd2ac6223a80671b777bf5dca70a4","0x000000000000000000000000000000690f757006d2fa1ddb0114c9f268783537","0x000000000000000000000000000000000023ad36feadd91e50118f32e97a0204"] \ No newline at end of file diff --git a/test_programs/execution_success/double_verify_proof_recursive/src/main.nr b/test_programs/execution_success/double_verify_proof_recursive/src/main.nr new file mode 100644 index 00000000000..e4c6926efbc --- /dev/null +++ b/test_programs/execution_success/double_verify_proof_recursive/src/main.nr @@ -0,0 +1,29 @@ +use dep::std; + +#[recursive] +fn main( + verification_key: [Field; 114], + // This is the proof without public inputs attached. + // + // This means: the size of this does not change with the number of public inputs. + proof: [Field; 93], + public_inputs: pub [Field; 1], + // This is currently not public. It is fine given that the vk is a part of the circuit definition. + // I believe we want to eventually make it public too though. + key_hash: Field, + proof_b: [Field; 93] +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} diff --git a/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml b/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml new file mode 100644 index 00000000000..3d2cf2c6096 --- /dev/null +++ b/test_programs/noir_test_failure/should_fail_suite_with_one_failure/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "should_fail_with_mismatch" +type = "bin" +authors = [""] +[dependencies] diff --git a/test_programs/test_libraries/exporting_lib/src/lib.nr b/test_programs/test_libraries/exporting_lib/src/lib.nr index af1fd7a32de..bfb1819132a 100644 --- a/test_programs/test_libraries/exporting_lib/src/lib.nr +++ b/test_programs/test_libraries/exporting_lib/src/lib.nr @@ -1,4 +1,3 @@ - struct MyStruct { inner: Field } diff --git a/tooling/acvm_cli/Cargo.toml b/tooling/acvm_cli/Cargo.toml new file mode 100644 index 00000000000..72424405d36 --- /dev/null +++ b/tooling/acvm_cli/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "acvm_cli" +description = "The entrypoint for executing the ACVM" +# x-release-please-start-version +version = "0.40.0" +# x-release-please-end +authors.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true +repository.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +# Rename binary from `acvm_cli` to `acvm` +[[bin]] +name = "acvm" +path = "src/main.rs" + +[dependencies] +thiserror.workspace = true +toml.workspace = true +color-eyre = "0.6.2" +clap.workspace = true +acvm.workspace = true +nargo.workspace = true +const_format.workspace = true +bn254_blackbox_solver.workspace = true +acir.workspace = true + +# Logs +tracing-subscriber.workspace = true +tracing-appender = "0.2.3" + +[dev-dependencies] +rand = "0.8.5" +proptest = "1.2.0" +paste = "1.0.14" diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs new file mode 100644 index 00000000000..255b6131fd6 --- /dev/null +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -0,0 +1,78 @@ +use std::io::{self, Write}; + +use acir::circuit::Circuit; +use acir::native_types::WitnessMap; +use bn254_blackbox_solver::Bn254BlackBoxSolver; +use clap::Args; + +use crate::cli::fs::inputs::{read_bytecode_from_file, read_inputs_from_file}; +use crate::cli::fs::witness::save_witness_to_dir; +use crate::errors::CliError; +use nargo::ops::{execute_circuit, DefaultForeignCallExecutor}; + +use super::fs::witness::create_output_witness_string; + +/// Executes a circuit to calculate its return value +#[derive(Debug, Clone, Args)] +pub(crate) struct ExecuteCommand { + /// Write the execution witness to named file + #[clap(long, short)] + output_witness: Option, + + /// The name of the toml file which contains the input witness map + #[clap(long, short)] + input_witness: String, + + /// The name of the binary file containing circuit bytecode + #[clap(long, short)] + bytecode: String, + + /// The working directory + #[clap(long, short)] + working_directory: String, + + /// Set to print output witness to stdout + #[clap(long, short, action)] + print: bool, +} + +fn run_command(args: ExecuteCommand) -> Result { + let bytecode = read_bytecode_from_file(&args.working_directory, &args.bytecode)?; + let circuit_inputs = read_inputs_from_file(&args.working_directory, &args.input_witness)?; + let output_witness = execute_program_from_witness(&circuit_inputs, &bytecode, None)?; + let output_witness_string = create_output_witness_string(&output_witness)?; + if args.output_witness.is_some() { + save_witness_to_dir( + &output_witness_string, + &args.working_directory, + &args.output_witness.unwrap(), + )?; + } + Ok(output_witness_string) +} + +pub(crate) fn run(args: ExecuteCommand) -> Result { + let print = args.print; + let output_witness_string = run_command(args)?; + if print { + io::stdout().write_all(output_witness_string.as_bytes()).unwrap(); + } + Ok(output_witness_string) +} + +pub(crate) fn execute_program_from_witness( + inputs_map: &WitnessMap, + bytecode: &[u8], + foreign_call_resolver_url: Option<&str>, +) -> Result { + let blackbox_solver = Bn254BlackBoxSolver::new(); + let circuit: Circuit = Circuit::deserialize_circuit(bytecode) + .map_err(|_| CliError::CircuitDeserializationError())?; + execute_circuit( + &circuit, + inputs_map.clone(), + &blackbox_solver, + &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), + ) + .map_err(CliError::CircuitExecutionError) +} diff --git a/tooling/acvm_cli/src/cli/fs/inputs.rs b/tooling/acvm_cli/src/cli/fs/inputs.rs new file mode 100644 index 00000000000..2a46cfba884 --- /dev/null +++ b/tooling/acvm_cli/src/cli/fs/inputs.rs @@ -0,0 +1,54 @@ +use acir::{ + native_types::{Witness, WitnessMap}, + FieldElement, +}; +use toml::Table; + +use crate::errors::{CliError, FilesystemError}; +use std::{fs::read, path::Path}; + +/// Returns the circuit's parameters parsed from a toml file at the given location +pub(crate) fn read_inputs_from_file>( + working_directory: P, + file_name: &String, +) -> Result { + let file_path = working_directory.as_ref().join(file_name); + if !file_path.exists() { + return Err(CliError::FilesystemError(FilesystemError::MissingTomlFile( + file_name.to_owned(), + file_path, + ))); + } + + let input_string = std::fs::read_to_string(file_path) + .map_err(|_| FilesystemError::InvalidTomlFile(file_name.clone()))?; + let input_map = input_string + .parse::() + .map_err(|_| FilesystemError::InvalidTomlFile(file_name.clone()))?; + let mut witnesses: WitnessMap = WitnessMap::new(); + for (key, value) in input_map.into_iter() { + let index = + Witness(key.trim().parse().map_err(|_| CliError::WitnessIndexError(key.clone()))?); + if !value.is_str() { + return Err(CliError::WitnessValueError(key.clone())); + } + let field = FieldElement::from_hex(value.as_str().unwrap()).unwrap(); + witnesses.insert(index, field); + } + + Ok(witnesses) +} + +/// Returns the circuit's bytecode read from the file at the given location +pub(crate) fn read_bytecode_from_file>( + working_directory: P, + file_name: &String, +) -> Result, FilesystemError> { + let file_path = working_directory.as_ref().join(file_name); + if !file_path.exists() { + return Err(FilesystemError::MissingBytecodeFile(file_name.to_owned(), file_path)); + } + let bytecode: Vec = + read(file_path).map_err(|_| FilesystemError::InvalidBytecodeFile(file_name.clone()))?; + Ok(bytecode) +} diff --git a/tooling/acvm_cli/src/cli/fs/mod.rs b/tooling/acvm_cli/src/cli/fs/mod.rs new file mode 100644 index 00000000000..f23ba06fd8b --- /dev/null +++ b/tooling/acvm_cli/src/cli/fs/mod.rs @@ -0,0 +1,2 @@ +pub(super) mod inputs; +pub(super) mod witness; diff --git a/tooling/acvm_cli/src/cli/fs/witness.rs b/tooling/acvm_cli/src/cli/fs/witness.rs new file mode 100644 index 00000000000..2daaa5a3a58 --- /dev/null +++ b/tooling/acvm_cli/src/cli/fs/witness.rs @@ -0,0 +1,36 @@ +use std::{ + collections::BTreeMap, + fs::File, + io::Write, + path::{Path, PathBuf}, +}; + +use acvm::acir::native_types::WitnessMap; + +use crate::errors::{CliError, FilesystemError}; + +/// Saves the provided output witnesses to a toml file created at the given location +pub(crate) fn save_witness_to_dir>( + output_witness: &String, + witness_dir: P, + file_name: &String, +) -> Result { + let witness_path = witness_dir.as_ref().join(file_name); + + let mut file = File::create(&witness_path) + .map_err(|_| FilesystemError::OutputWitnessCreationFailed(file_name.clone()))?; + write!(file, "{}", output_witness) + .map_err(|_| FilesystemError::OutputWitnessWriteFailed(file_name.clone()))?; + + Ok(witness_path) +} + +/// Creates a toml representation of the provided witness map +pub(crate) fn create_output_witness_string(witnesses: &WitnessMap) -> Result { + let mut witness_map: BTreeMap = BTreeMap::new(); + for (key, value) in witnesses.clone().into_iter() { + witness_map.insert(key.0.to_string(), format!("0x{}", value.to_hex())); + } + + toml::to_string(&witness_map).map_err(|_| CliError::OutputWitnessSerializationFailed()) +} diff --git a/tooling/acvm_cli/src/cli/mod.rs b/tooling/acvm_cli/src/cli/mod.rs new file mode 100644 index 00000000000..a610b08ab77 --- /dev/null +++ b/tooling/acvm_cli/src/cli/mod.rs @@ -0,0 +1,41 @@ +use clap::{Parser, Subcommand}; +use color_eyre::eyre; +use const_format::formatcp; + +mod execute_cmd; +mod fs; + +const ACVM_VERSION: &str = env!("CARGO_PKG_VERSION"); + +static VERSION_STRING: &str = formatcp!("version = {}\n", ACVM_VERSION,); + +#[derive(Parser, Debug)] +#[command(name="acvm", author, version=VERSION_STRING, about, long_about = None)] +struct ACVMCli { + #[command(subcommand)] + command: ACVMCommand, +} + +#[non_exhaustive] +#[derive(Subcommand, Clone, Debug)] +enum ACVMCommand { + Execute(execute_cmd::ExecuteCommand), +} + +#[cfg(not(feature = "codegen-docs"))] +pub(crate) fn start_cli() -> eyre::Result<()> { + let ACVMCli { command } = ACVMCli::parse(); + + match command { + ACVMCommand::Execute(args) => execute_cmd::run(args), + }?; + + Ok(()) +} + +#[cfg(feature = "codegen-docs")] +pub(crate) fn start_cli() -> eyre::Result<()> { + let markdown: String = clap_markdown::help_markdown::(); + println!("{markdown}"); + Ok(()) +} diff --git a/tooling/acvm_cli/src/errors.rs b/tooling/acvm_cli/src/errors.rs new file mode 100644 index 00000000000..035388d05f7 --- /dev/null +++ b/tooling/acvm_cli/src/errors.rs @@ -0,0 +1,52 @@ +use nargo::NargoError; +use std::path::PathBuf; +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) enum FilesystemError { + #[error( + " Error: cannot find {0} in expected location {1:?}.\n Please generate this file at the expected location." + )] + MissingTomlFile(String, PathBuf), + #[error(" Error: failed to parse toml file {0}.")] + InvalidTomlFile(String), + #[error( + " Error: cannot find {0} in expected location {1:?}.\n Please generate this file at the expected location." + )] + MissingBytecodeFile(String, PathBuf), + + #[error(" Error: failed to read bytecode file {0}.")] + InvalidBytecodeFile(String), + + #[error(" Error: failed to create output witness file {0}.")] + OutputWitnessCreationFailed(String), + + #[error(" Error: failed to write output witness file {0}.")] + OutputWitnessWriteFailed(String), +} + +#[derive(Debug, Error)] +pub(crate) enum CliError { + /// Filesystem errors + #[error(transparent)] + FilesystemError(#[from] FilesystemError), + + /// Error related to circuit deserialization + #[error("Error: failed to deserialize circuit")] + CircuitDeserializationError(), + + /// Error related to circuit execution + #[error(transparent)] + CircuitExecutionError(#[from] NargoError), + + /// Input Witness Value Error + #[error("Error: failed to parse witness value {0}")] + WitnessValueError(String), + + /// Input Witness Index Error + #[error("Error: failed to parse witness index {0}")] + WitnessIndexError(String), + + #[error(" Error: failed to serialize output witness.")] + OutputWitnessSerializationFailed(), +} diff --git a/tooling/acvm_cli/src/main.rs b/tooling/acvm_cli/src/main.rs new file mode 100644 index 00000000000..33cadc73a7c --- /dev/null +++ b/tooling/acvm_cli/src/main.rs @@ -0,0 +1,36 @@ +#![forbid(unsafe_code)] +#![warn(unreachable_pub)] +#![warn(clippy::semicolon_if_nothing_returned)] +#![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] + +mod cli; +mod errors; + +use std::env; + +use tracing_appender::rolling; +use tracing_subscriber::{fmt::format::FmtSpan, EnvFilter}; + +fn main() { + // Setup tracing + if let Ok(log_dir) = env::var("ACVM_LOG_DIR") { + let debug_file = rolling::daily(log_dir, "acvm-log"); + tracing_subscriber::fmt() + .with_span_events(FmtSpan::ACTIVE) + .with_writer(debug_file) + .with_ansi(false) + .with_env_filter(EnvFilter::from_default_env()) + .init(); + } else { + tracing_subscriber::fmt() + .with_span_events(FmtSpan::ACTIVE) + .with_ansi(true) + .with_env_filter(EnvFilter::from_env("NOIR_LOG")) + .init(); + } + + if let Err(report) = cli::start_cli() { + eprintln!("{report}"); + std::process::exit(1); + } +} diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index 231d4d897a9..854e284dd43 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -6,6 +6,7 @@ brillig_to_bytes_integration debug_logs double_verify_nested_proof double_verify_proof +double_verify_proof_recursive modulus references scalar_mul diff --git a/tooling/nargo/src/artifacts/contract.rs b/tooling/nargo/src/artifacts/contract.rs index d928b09fcb9..020ce49662f 100644 --- a/tooling/nargo/src/artifacts/contract.rs +++ b/tooling/nargo/src/artifacts/contract.rs @@ -1,6 +1,6 @@ use acvm::acir::circuit::Circuit; use noirc_abi::{Abi, ContractEvent}; -use noirc_driver::{CompiledContract, ContractFunction, ContractFunctionType}; +use noirc_driver::{CompiledContract, ContractFunction}; use serde::{Deserialize, Serialize}; use noirc_driver::DebugFile; @@ -43,9 +43,9 @@ impl From for ContractArtifact { pub struct ContractFunctionArtifact { pub name: String, - pub function_type: ContractFunctionType, + pub is_unconstrained: bool, - pub is_internal: bool, + pub custom_attributes: Vec, pub abi: Abi, @@ -66,8 +66,8 @@ impl From for ContractFunctionArtifact { fn from(func: ContractFunction) -> Self { ContractFunctionArtifact { name: func.name, - function_type: func.function_type, - is_internal: func.is_internal, + is_unconstrained: func.is_unconstrained, + custom_attributes: func.custom_attributes, abi: func.abi, bytecode: func.bytecode, debug_symbols: func.debug, From 773cf190ee21381d826ba80391a5d7d5efae9174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Garc=C3=ADa?= Date: Wed, 13 Mar 2024 10:00:17 -0500 Subject: [PATCH 071/416] feat: Visible aliases for nargo commands (#4453) # Description Closes #4452 ## Documentation\ Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/src/cli/check_cmd.rs | 1 + tooling/nargo_cli/src/cli/execute_cmd.rs | 1 + tooling/nargo_cli/src/cli/info_cmd.rs | 1 + tooling/nargo_cli/src/cli/prove_cmd.rs | 1 + tooling/nargo_cli/src/cli/test_cmd.rs | 1 + tooling/nargo_cli/src/cli/verify_cmd.rs | 1 + 6 files changed, 6 insertions(+) diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index 242a640e484..897073f4e20 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -24,6 +24,7 @@ use super::NargoConfig; /// Checks the constraint system for errors #[derive(Debug, Clone, Args)] +#[clap(visible_alias = "c")] pub(crate) struct CheckCommand { /// The name of the package to check #[clap(long, conflicts_with = "workspace")] diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 85c0a4160a7..1be2fbf61d9 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -23,6 +23,7 @@ use crate::errors::CliError; /// Executes a circuit to calculate its return value #[derive(Debug, Clone, Args)] +#[clap(visible_alias = "e")] pub(crate) struct ExecuteCommand { /// Write the execution witness to named file witness_name: Option, diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index 300e1a35be2..391e8061a07 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -30,6 +30,7 @@ use super::{compile_cmd::compile_workspace, NargoConfig}; /// 1. The number of ACIR opcodes /// 2. Counts the final number gates in the circuit used by a backend #[derive(Debug, Clone, Args)] +#[clap(visible_alias = "i")] pub(crate) struct InfoCommand { /// The name of the package to detail #[clap(long, conflicts_with = "workspace")] diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index f0a9b3185b9..e413db0e5f3 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -20,6 +20,7 @@ use crate::{backends::Backend, cli::execute_cmd::execute_program, errors::CliErr /// Create proof for this program. The proof is returned as a hex encoded string. #[derive(Debug, Clone, Args)] +#[clap(visible_alias = "p")] pub(crate) struct ProveCommand { /// The name of the toml file which contains the inputs for the prover #[clap(long, short, default_value = PROVER_INPUT_FILE)] diff --git a/tooling/nargo_cli/src/cli/test_cmd.rs b/tooling/nargo_cli/src/cli/test_cmd.rs index 2828aaf01eb..88a804d5cf4 100644 --- a/tooling/nargo_cli/src/cli/test_cmd.rs +++ b/tooling/nargo_cli/src/cli/test_cmd.rs @@ -25,6 +25,7 @@ use super::NargoConfig; /// Run the tests for this program #[derive(Debug, Clone, Args)] +#[clap(visible_alias = "t")] pub(crate) struct TestCommand { /// If given, only tests with names containing this string will be run test_name: Option, diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index 1063b50ab6c..3e23c9a3e9f 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -17,6 +17,7 @@ use noirc_frontend::graph::CrateName; /// Given a proof and a program, verify whether the proof is valid #[derive(Debug, Clone, Args)] +#[clap(visible_alias = "v")] pub(crate) struct VerifyCommand { /// The name of the toml file which contains the inputs for the verifier #[clap(long, short, default_value = VERIFIER_INPUT_FILE)] From 9ca1a60f9310f9822414c62dfa410c85bfe1737b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:30:50 +0000 Subject: [PATCH 072/416] chore: allow setting namespace visibility on functions (#4510) # Description ## Problem\* Resolves ## Summary\* This PR allows setting non-public visibilities when adding functions to namespaces. This doesn't currently cause any changes as we never actually read this visibility but we start to do so in a later PR. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../compute_note_hash_and_nullifier.rs | 4 +-- .../src/hir/def_collector/dc_mod.rs | 11 +++---- .../src/hir/def_map/item_scope.rs | 13 ++++++-- .../src/hir/def_map/module_data.rs | 30 +++++++++++-------- .../src/hir/resolution/impls.rs | 11 +++++-- .../src/hir/resolution/traits.rs | 11 +++++-- 6 files changed, 54 insertions(+), 26 deletions(-) diff --git a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs index 4f8f3f19ab8..fd538dc578b 100644 --- a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs +++ b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs @@ -7,7 +7,7 @@ use noirc_frontend::{ }, macros_api::{FileId, HirContext, MacroError}, node_interner::FuncId, - parse_program, FunctionReturnType, NoirFunction, UnresolvedTypeData, + parse_program, FunctionReturnType, ItemVisibility, NoirFunction, UnresolvedTypeData, }; use crate::utils::hir_utils::fetch_struct_trait_impls; @@ -113,7 +113,7 @@ pub fn inject_compute_note_hash_and_nullifier( context.def_map_mut(crate_id).unwrap() .modules_mut()[module_id.0] .declare_function( - func.name_ident().clone(), func_id + func.name_ident().clone(), ItemVisibility::Public, func_id ).expect( "Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647." ); diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index ae99e61e534..5adb9eb5b7e 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -10,8 +10,8 @@ use crate::{ macros_api::MacroProcessor, node_interner::{FunctionModifiers, TraitId, TypeAliasId}, parser::{SortedModule, SortedSubModule}, - FunctionDefinition, Ident, LetStatement, ModuleDeclaration, NoirFunction, NoirStruct, - NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, + FunctionDefinition, Ident, ItemVisibility, LetStatement, ModuleDeclaration, NoirFunction, + NoirStruct, NoirTrait, NoirTraitImpl, NoirTypeAlias, TraitImplItem, TraitItem, TypeImpl, }; use super::{ @@ -232,6 +232,7 @@ impl<'a> ModCollector<'a> { let name = function.name_ident().clone(); let func_id = context.def_interner.push_empty_fn(); + let visibility = function.def.visibility; // First create dummy function in the DefInterner // So that we can get a FuncId @@ -248,7 +249,7 @@ impl<'a> ModCollector<'a> { // Add function to scope/ns of the module let result = self.def_collector.def_map.modules[self.module_id.0] - .declare_function(name, func_id); + .declare_function(name, visibility, func_id); if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -407,7 +408,7 @@ impl<'a> ModCollector<'a> { let modifiers = FunctionModifiers { name: name.to_string(), - visibility: crate::ItemVisibility::Public, + visibility: ItemVisibility::Public, // TODO(Maddiaa): Investigate trait implementations with attributes see: https://github.com/noir-lang/noir/issues/2629 attributes: crate::token::Attributes::empty(), is_unconstrained: false, @@ -419,7 +420,7 @@ impl<'a> ModCollector<'a> { .push_function_definition(func_id, modifiers, trait_id.0, location); match self.def_collector.def_map.modules[trait_id.0.local_id.0] - .declare_function(name.clone(), func_id) + .declare_function(name.clone(), ItemVisibility::Public, func_id) { Ok(()) => { if let Some(body) = body { diff --git a/compiler/noirc_frontend/src/hir/def_map/item_scope.rs b/compiler/noirc_frontend/src/hir/def_map/item_scope.rs index 178b91e1e84..cd4eafdf669 100644 --- a/compiler/noirc_frontend/src/hir/def_map/item_scope.rs +++ b/compiler/noirc_frontend/src/hir/def_map/item_scope.rs @@ -19,10 +19,11 @@ impl ItemScope { pub fn add_definition( &mut self, name: Ident, + visibility: ItemVisibility, mod_def: ModuleDefId, trait_id: Option, ) -> Result<(), (Ident, Ident)> { - self.add_item_to_namespace(name, mod_def, trait_id, false)?; + self.add_item_to_namespace(name, visibility, mod_def, trait_id, false)?; self.defs.push(mod_def); Ok(()) } @@ -33,6 +34,7 @@ impl ItemScope { pub fn add_item_to_namespace( &mut self, name: Ident, + visibility: ItemVisibility, mod_def: ModuleDefId, trait_id: Option, is_prelude: bool, @@ -41,6 +43,11 @@ impl ItemScope { if let Entry::Occupied(mut o) = map.entry(name.clone()) { let trait_hashmap = o.get_mut(); if let Entry::Occupied(mut n) = trait_hashmap.entry(trait_id) { + // Generally we want to reject having two of the same ident in the same namespace. + // The exception to this is when we're explicitly importing something + // which exists in the Noir stdlib prelude. + // + // In this case we ignore the prelude and favour the explicit import. let is_prelude = std::mem::replace(&mut n.get_mut().2, is_prelude); let old_ident = o.key(); @@ -50,12 +57,12 @@ impl ItemScope { Err((old_ident.clone(), name)) } } else { - trait_hashmap.insert(trait_id, (mod_def, ItemVisibility::Public, is_prelude)); + trait_hashmap.insert(trait_id, (mod_def, visibility, is_prelude)); Ok(()) } } else { let mut trait_hashmap = HashMap::new(); - trait_hashmap.insert(trait_id, (mod_def, ItemVisibility::Public, is_prelude)); + trait_hashmap.insert(trait_id, (mod_def, visibility, is_prelude)); map.insert(name, trait_hashmap); Ok(()) } diff --git a/compiler/noirc_frontend/src/hir/def_map/module_data.rs b/compiler/noirc_frontend/src/hir/def_map/module_data.rs index 309618dd011..4dd38f0e3e5 100644 --- a/compiler/noirc_frontend/src/hir/def_map/module_data.rs +++ b/compiler/noirc_frontend/src/hir/def_map/module_data.rs @@ -4,7 +4,7 @@ use noirc_errors::Location; use crate::{ node_interner::{FuncId, GlobalId, StructId, TraitId, TypeAliasId}, - Ident, + Ident, ItemVisibility, }; use super::{ItemScope, LocalModuleId, ModuleDefId, ModuleId, PerNs}; @@ -48,18 +48,24 @@ impl ModuleData { fn declare( &mut self, name: Ident, + visibility: ItemVisibility, item_id: ModuleDefId, trait_id: Option, ) -> Result<(), (Ident, Ident)> { - self.scope.add_definition(name.clone(), item_id, trait_id)?; + self.scope.add_definition(name.clone(), visibility, item_id, trait_id)?; // definitions is a subset of self.scope so it is expected if self.scope.define_func_def // returns without error, so will self.definitions.define_func_def. - self.definitions.add_definition(name, item_id, trait_id) + self.definitions.add_definition(name, visibility, item_id, trait_id) } - pub fn declare_function(&mut self, name: Ident, id: FuncId) -> Result<(), (Ident, Ident)> { - self.declare(name, id.into(), None) + pub fn declare_function( + &mut self, + name: Ident, + visibility: ItemVisibility, + id: FuncId, + ) -> Result<(), (Ident, Ident)> { + self.declare(name, visibility, id.into(), None) } pub fn declare_trait_function( @@ -68,7 +74,7 @@ impl ModuleData { id: FuncId, trait_id: TraitId, ) -> Result<(), (Ident, Ident)> { - self.declare(name, id.into(), Some(trait_id)) + self.declare(name, ItemVisibility::Public, id.into(), Some(trait_id)) } pub fn remove_function(&mut self, name: &Ident) { @@ -77,11 +83,11 @@ impl ModuleData { } pub fn declare_global(&mut self, name: Ident, id: GlobalId) -> Result<(), (Ident, Ident)> { - self.declare(name, id.into(), None) + self.declare(name, ItemVisibility::Public, id.into(), None) } pub fn declare_struct(&mut self, name: Ident, id: StructId) -> Result<(), (Ident, Ident)> { - self.declare(name, ModuleDefId::TypeId(id), None) + self.declare(name, ItemVisibility::Public, ModuleDefId::TypeId(id), None) } pub fn declare_type_alias( @@ -89,11 +95,11 @@ impl ModuleData { name: Ident, id: TypeAliasId, ) -> Result<(), (Ident, Ident)> { - self.declare(name, id.into(), None) + self.declare(name, ItemVisibility::Public, id.into(), None) } pub fn declare_trait(&mut self, name: Ident, id: TraitId) -> Result<(), (Ident, Ident)> { - self.declare(name, ModuleDefId::TraitId(id), None) + self.declare(name, ItemVisibility::Public, ModuleDefId::TraitId(id), None) } pub fn declare_child_module( @@ -101,7 +107,7 @@ impl ModuleData { name: Ident, child_id: ModuleId, ) -> Result<(), (Ident, Ident)> { - self.declare(name, child_id.into(), None) + self.declare(name, ItemVisibility::Public, child_id.into(), None) } pub fn find_func_with_name(&self, name: &Ident) -> Option { @@ -114,7 +120,7 @@ impl ModuleData { id: ModuleDefId, is_prelude: bool, ) -> Result<(), (Ident, Ident)> { - self.scope.add_item_to_namespace(name, id, None, is_prelude) + self.scope.add_item_to_namespace(name, ItemVisibility::Public, id, None, is_prelude) } pub fn find_name(&self, name: &Ident) -> PerNs { diff --git a/compiler/noirc_frontend/src/hir/resolution/impls.rs b/compiler/noirc_frontend/src/hir/resolution/impls.rs index 4aa70f00cfc..72f6adc3770 100644 --- a/compiler/noirc_frontend/src/hir/resolution/impls.rs +++ b/compiler/noirc_frontend/src/hir/resolution/impls.rs @@ -13,7 +13,7 @@ use crate::{ Context, }, node_interner::{FuncId, NodeInterner}, - Type, + ItemVisibility, Type, }; use super::{ @@ -67,7 +67,14 @@ pub(crate) fn collect_impls( // be accessed with the `TypeName::method` syntax. We'll check later whether the // object types in each method overlap or not. If they do, we issue an error. // If not, that is specialization which is allowed. - if module.declare_function(method.name_ident().clone(), *method_id).is_err() { + if module + .declare_function( + method.name_ident().clone(), + ItemVisibility::Public, + *method_id, + ) + .is_err() + { module.remove_function(method.name_ident()); } } diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 5d546954f0d..04da558a642 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -16,7 +16,7 @@ use crate::{ }, hir_def::traits::{TraitConstant, TraitFunction, TraitImpl, TraitType}, node_interner::{FuncId, NodeInterner, TraitId}, - Generics, Path, Shared, TraitItem, Type, TypeVariable, TypeVariableKind, + Generics, ItemVisibility, Path, Shared, TraitItem, Type, TypeVariable, TypeVariableKind, }; use super::{ @@ -301,7 +301,14 @@ fn collect_trait_impl( // be accessed with the `TypeName::method` syntax. We'll check later whether the // object types in each method overlap or not. If they do, we issue an error. // If not, that is specialization which is allowed. - if module.declare_function(method.name_ident().clone(), *method_id).is_err() { + if module + .declare_function( + method.name_ident().clone(), + ItemVisibility::Public, + *method_id, + ) + .is_err() + { module.remove_function(method.name_ident()); } } From 80373d612c023e3e165b49b6d1729486b0ba3b4b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 13 Mar 2024 17:04:16 +0000 Subject: [PATCH 073/416] feat: optimize sha2 implementation (#4441) # Description ## Problem\* Resolves ## Summary\* We're currently performing byte decompositions in the sha2 functions through repeated division. This would be more efficient if we just did the full byte decomposition at once and then iterate through the results. I've also removed some noop casts. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/sha256.nr | 22 ++++++++++++---------- noir_stdlib/src/sha512.nr | 22 ++++++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/noir_stdlib/src/sha256.nr b/noir_stdlib/src/sha256.nr index 2f686a64165..8ca6808568d 100644 --- a/noir_stdlib/src/sha256.nr +++ b/noir_stdlib/src/sha256.nr @@ -6,9 +6,11 @@ fn msg_u8_to_u32(msg: [u8; 64]) -> [u32; 16] { let mut msg32: [u32; 16] = [0; 16]; for i in 0..16 { + let mut msg_field: Field = 0; for j in 0..4 { - msg32[15 - i] = (msg32[15 - i] << 8) + msg[64 - 4*(i + 1) + j] as u32; + msg_field = msg_field * 256 + msg[64 - 4*(i + 1) + j] as Field; } + msg32[15 - i] = msg_field as u32; } msg32 @@ -21,7 +23,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { let mut i: u64 = 0; // Message byte pointer for k in 0..N { // Populate msg_block - msg_block[i as Field] = msg[k]; + msg_block[i] = msg[k]; i = i + 1; if i == 64 { // Enough to hash block @@ -32,7 +34,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { } // Pad the rest such that we have a [u32; 2] block at the end representing the length // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - msg_block[i as Field] = 1 << 7; + msg_block[i] = 1 << 7; i = i + 1; // If i >= 57, there aren't enough bits in the current message block to accomplish this, so // the 1 and 0s fill up the current block, which we then compress accordingly. @@ -41,7 +43,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { if i < 64 { for _i in 57..64 { if i <= 63 { - msg_block[i as Field] = 0; + msg_block[i] = 0; i += 1; } } @@ -51,16 +53,16 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { i = 0; } + let len = 8 * msg.len(); + let len_bytes = (len as Field).to_le_bytes(8); for _i in 0..64 { // In any case, fill blocks up with zeros until the last 64 (i.e. until i = 56). if i < 56 { - msg_block[i as Field] = 0; + msg_block[i] = 0; i = i + 1; } else if i < 64 { - let mut len = 8 * msg.len(); for j in 0..8 { - msg_block[63 - j] = len as u8; - len >>= 8; + msg_block[63 - j] = len_bytes[j]; } i += 8; } @@ -70,9 +72,9 @@ pub fn digest(msg: [u8; N]) -> [u8; 32] { // Return final hash as byte array for j in 0..8 { + let h_bytes = (h[7 - j] as Field).to_le_bytes(4); for k in 0..4 { - out_h[31 - 4*j - k] = h[7 - j] as u8; - h[7-j] >>= 8; + out_h[31 - 4*j - k] = h_bytes[k]; } } diff --git a/noir_stdlib/src/sha512.nr b/noir_stdlib/src/sha512.nr index 4dfe78308e2..a766ae50d55 100644 --- a/noir_stdlib/src/sha512.nr +++ b/noir_stdlib/src/sha512.nr @@ -77,9 +77,11 @@ fn msg_u8_to_u64(msg: [u8; 128]) -> [u64; 16] { let mut msg64: [u64; 16] = [0; 16]; for i in 0..16 { + let mut msg_field: Field = 0; for j in 0..8 { - msg64[15 - i] = (msg64[15 - i] << 8) + msg[128 - 8*(i + 1) + j] as u64; + msg_field = msg_field * 256 + msg[128 - 8*(i + 1) + j] as Field; } + msg64[15 - i] = msg_field as u64; } msg64 @@ -94,7 +96,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { let mut i: u64 = 0; // Message byte pointer for k in 0..msg.len() { // Populate msg_block - msg_block[i as Field] = msg[k]; + msg_block[i] = msg[k]; i = i + 1; if i == 128 { // Enough to hash block @@ -108,7 +110,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { } // Pad the rest such that we have a [u64; 2] block at the end representing the length // of the message, and a block of 1 0 ... 0 following the message (i.e. [1 << 7, 0, ..., 0]). - msg_block[i as Field] = 1 << 7; + msg_block[i] = 1 << 7; i += 1; // If i >= 113, there aren't enough bits in the current message block to accomplish this, so // the 1 and 0s fill up the current block, which we then compress accordingly. @@ -117,7 +119,7 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { if i < 128 { for _i in 113..128 { if i <= 127 { - msg_block[i as Field] = 0; + msg_block[i] = 0; i += 1; } } @@ -130,16 +132,16 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { i = 0; } + let len = 8 * msg.len(); + let len_bytes = (len as Field).to_le_bytes(16); for _i in 0..128 { // In any case, fill blocks up with zeros until the last 128 (i.e. until i = 112). if i < 112 { - msg_block[i as Field] = 0; + msg_block[i] = 0; i += 1; } else if i < 128 { - let mut len = 8 * msg.len(); for j in 0..16 { - msg_block[127 - j] = len as u8; - len >>= 8; + msg_block[127 - j] = len_bytes[j]; } i += 16; // Done. } @@ -151,9 +153,9 @@ pub fn digest(msg: [u8; N]) -> [u8; 64] { } // Return final hash as byte array for j in 0..8 { + let h_bytes = (h[7 - j] as Field).to_le_bytes(8); for k in 0..8 { - out_h[63 - 8*j - k] = h[7 - j] as u8; - h[7-j] >>= 8; + out_h[63 - 8*j - k] = h_bytes[k]; } } From eeeebacd10698e847f773e26dac8a4a5eb8e84ed Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 13 Mar 2024 12:26:44 -0500 Subject: [PATCH 074/416] fix: Substitute generics when checking the field count of a type (#4547) # Description ## Problem\* Resolves #4545 ## Summary\* A prior assumption we had where we didn't need to bind generics just to check how many fields a type can have turned out not to be true. Binding generics is important for cases such as the new test case where generic lengths are used. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/hir_def/types.rs | 6 +----- compiler/noirc_frontend/src/tests.rs | 10 ++++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 5ab036eef5b..60bc5b2470f 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -127,11 +127,7 @@ impl Type { let fields = struct_type.get_fields(args); fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count()) } - Type::Alias(def, _) => { - // It is safe to access `typ` without instantiating generics here since generics - // cannot change the number of fields in `typ`. - def.borrow().typ.field_count() - } + Type::Alias(def, generics) => def.borrow().get_type(generics).field_count(), Type::Tuple(fields) => { fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count()) } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 3f78bd43ba9..1158f4c229a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1213,4 +1213,14 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { "#; assert_eq!(get_program_errors(src).len(), 0); } + + // Regression for #4545 + #[test] + fn type_aliases_in_main() { + let src = r#" + type Outer = [u8; N]; + fn main(_arg: Outer<1>) {} + "#; + assert_eq!(get_program_errors(src).len(), 0); + } } From 44e60b67469de88f20842c4eead64d736f7bd4a0 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 13 Mar 2024 18:15:25 +0000 Subject: [PATCH 075/416] feat: add `nargo compile --watch` command (#4464) # Description ## Problem\* Resolves ## Summary\* This PR creates the outline of a `watch` command to be built on in subsequent PRs. Currently this just watches the full workspace directory and on any change to a `.nr` file, will recompile the workspace/package and display any errors/warnings. ## Additional Context ## Documentation\* Check one: - [] No documentation needed. - [ ] Documentation included in this PR. - [x] **[Exceptional Case]** Documentation to be submitted in a separate PR. Will be documented when feature is public. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 283 ++++++++++++++++++++--- cspell.json | 1 + deny.toml | 1 + tooling/nargo_cli/Cargo.toml | 6 +- tooling/nargo_cli/src/cli/compile_cmd.rs | 97 ++++++-- 5 files changed, 336 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5ce8a10509..3b7c1b6e56e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,7 +411,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138985dd8aefbefeaa66b01b7f5b2b6b4c333fcef1cc5f32c63a2aabe37d6de3" dependencies = [ - "futures 0.3.30", + "futures 0.3.28", "lsp-types 0.94.1", "pin-project-lite", "rustix", @@ -967,14 +967,14 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "console" -version = "0.15.8" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" dependencies = [ "encode_unicode 0.3.6", "lazy_static", "libc", - "windows-sys 0.52.0", + "windows-sys 0.45.0", ] [[package]] @@ -1675,6 +1675,15 @@ dependencies = [ "rayon", ] +[[package]] +name = "file-id" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6584280525fb2059cba3db2c04abf947a1a29a45ddae89f3870f8281704fafc9" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "filetime" version = "0.2.22" @@ -1749,6 +1758,15 @@ dependencies = [ "percent-encoding 2.3.0", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "funty" version = "2.0.0" @@ -1763,9 +1781,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1778,9 +1796,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1788,15 +1806,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1806,15 +1824,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", @@ -1823,21 +1841,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures 0.1.31", "futures-channel", @@ -2096,9 +2114,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -2111,7 +2129,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -2120,9 +2138,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ "futures-util", "http", @@ -2290,6 +2308,26 @@ dependencies = [ "str_stack", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -2363,7 +2401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" dependencies = [ "derive_more", - "futures 0.3.30", + "futures 0.3.28", "jsonrpc-core", "jsonrpc-pubsub", "log", @@ -2378,7 +2416,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures 0.3.30", + "futures 0.3.28", "futures-executor", "futures-util", "log", @@ -2393,7 +2431,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" dependencies = [ - "futures 0.3.30", + "futures 0.3.28", "jsonrpc-client-transports", ] @@ -2415,7 +2453,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ - "futures 0.3.30", + "futures 0.3.28", "hyper", "jsonrpc-core", "jsonrpc-server-utils", @@ -2431,7 +2469,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" dependencies = [ - "futures 0.3.30", + "futures 0.3.28", "jsonrpc-core", "lazy_static", "log", @@ -2447,7 +2485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes", - "futures 0.3.30", + "futures 0.3.28", "globset", "jsonrpc-core", "lazy_static", @@ -2479,6 +2517,26 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2503,6 +2561,17 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -2659,6 +2728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", + "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -2727,6 +2797,8 @@ dependencies = [ "noirc_driver", "noirc_errors", "noirc_frontend", + "notify", + "notify-debouncer-full", "paste", "pprof", "predicates 2.1.5", @@ -2738,6 +2810,7 @@ dependencies = [ "similar-asserts", "tempfile", "termcolor", + "termion", "test-binary", "thiserror", "tokio", @@ -3050,6 +3123,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.4.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "notify-debouncer-full" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f5dab59c348b9b50cf7f261960a20e389feb2713636399cd9082cd4b536154" +dependencies = [ + "crossbeam-channel", + "file-id", + "log", + "notify", + "parking_lot 0.12.1", + "walkdir", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3111,6 +3217,12 @@ dependencies = [ "libc", ] +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + [[package]] name = "object" version = "0.31.1" @@ -3670,6 +3782,21 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_termios" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" + [[package]] name = "redox_users" version = "0.4.3" @@ -4432,6 +4559,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.5" @@ -4608,6 +4745,18 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termion" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417813675a504dfbbf21bfde32c03e5bf9f2413999962b479023c02848c1c7a5" +dependencies = [ + "libc", + "libredox", + "numtoa", + "redox_termios", +] + [[package]] name = "termtree" version = "0.4.1" @@ -4743,7 +4892,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] @@ -5490,6 +5639,15 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -5508,6 +5666,21 @@ dependencies = [ "windows-targets 0.52.4", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.1" @@ -5538,6 +5711,12 @@ dependencies = [ "windows_x86_64_msvc 0.52.4", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" @@ -5556,6 +5735,12 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" @@ -5574,6 +5759,12 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.0" @@ -5592,6 +5783,12 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.0" @@ -5610,6 +5807,12 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" @@ -5622,6 +5825,12 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" @@ -5640,6 +5849,12 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/cspell.json b/cspell.json index d961b600f40..34dafa2b614 100644 --- a/cspell.json +++ b/cspell.json @@ -52,6 +52,7 @@ "csat", "curvegroup", "databus", + "debouncer", "deflater", "deflatten", "deflattened", diff --git a/deny.toml b/deny.toml index 578f8427263..eff233687e8 100644 --- a/deny.toml +++ b/deny.toml @@ -69,6 +69,7 @@ exceptions = [ # https://tldrlegal.com/license/creative-commons-cc0-1.0-universal { allow = ["CC0-1.0"], name = "more-asserts" }, { allow = ["CC0-1.0"], name = "jsonrpc" }, + { allow = ["CC0-1.0"], name = "notify" }, { allow = ["MPL-2.0"], name = "sized-chunks" }, { allow = ["MPL-2.0"], name = "webpki-roots" }, diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index 57edbf5ae04..f92a2421b0f 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -50,6 +50,10 @@ tokio = { version = "1.0", features = ["io-std"] } dap.workspace = true clap-markdown = { git = "https://github.com/noir-lang/clap-markdown", rev = "450d759532c88f0dba70891ceecdbc9ff8f25d2b", optional = true } +notify = "6.1.1" +notify-debouncer-full = "0.3.1" +termion = "3.0.0" + # Backends backend-interface = { path = "../backend_interface" } @@ -86,4 +90,4 @@ name = "iai" harness = false [features] -codegen-docs = ["dep:clap-markdown"] \ No newline at end of file +codegen-docs = ["dep:clap-markdown"] diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 4309f0db3ea..9b7bf9cdb0c 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -1,4 +1,6 @@ +use std::io::Write; use std::path::Path; +use std::time::Duration; use fm::FileManager; use nargo::artifacts::program::ProgramArtifact; @@ -15,6 +17,8 @@ use noirc_frontend::graph::CrateName; use clap::Args; use noirc_frontend::hir::ParsedFiles; +use notify::{EventKind, RecursiveMode, Watcher}; +use notify_debouncer_full::new_debouncer; use crate::backends::Backend; use crate::errors::CliError; @@ -31,17 +35,21 @@ pub(crate) struct CompileCommand { #[clap(long, conflicts_with = "workspace")] package: Option, - /// Compile all packages in the workspace + /// Compile all packages in the workspace. #[clap(long, conflicts_with = "package")] workspace: bool, #[clap(flatten)] compile_options: CompileOptions, + + /// Watch workspace and recompile on changes. + #[clap(long, hide = true)] + watch: bool, } pub(crate) fn run( backend: &Backend, - args: CompileCommand, + mut args: CompileCommand, config: NargoConfig, ) -> Result<(), CliError> { let toml_path = get_package_manifest(&config.program_dir)?; @@ -54,28 +62,82 @@ pub(crate) fn run( selection, Some(NOIR_ARTIFACT_VERSION_STRING.to_owned()), )?; - let circuit_dir = workspace.target_directory_path(); + if args.compile_options.expression_width.is_none() { + args.compile_options.expression_width = Some(backend.get_backend_info_or_default()); + }; + + if args.watch { + watch_workspace(&workspace, &args.compile_options) + .map_err(|err| CliError::Generic(err.to_string()))?; + } else { + compile_workspace_full(&workspace, &args.compile_options)?; + } + + Ok(()) +} + +fn watch_workspace(workspace: &Workspace, compile_options: &CompileOptions) -> notify::Result<()> { + let (tx, rx) = std::sync::mpsc::channel(); + + // No specific tickrate, max debounce time 1 seconds + let mut debouncer = new_debouncer(Duration::from_secs(1), None, tx)?; + + // Add a path to be watched. All files and directories at that path and + // below will be monitored for changes. + debouncer.watcher().watch(&workspace.root_dir, RecursiveMode::Recursive)?; + + let mut screen = std::io::stdout(); + write!(screen, "{}", termion::cursor::Save).unwrap(); + screen.flush().unwrap(); + let _ = compile_workspace_full(workspace, compile_options); + for res in rx { + let debounced_events = res.map_err(|mut err| err.remove(0))?; + + // We only want to trigger a rebuild if a noir source file has been modified. + let noir_files_modified = debounced_events.iter().any(|event| { + let mut event_paths = event.event.paths.iter(); + let event_affects_noir_file = + event_paths.any(|path| path.extension().map_or(false, |ext| ext == "nr")); + + let is_relevant_event_kind = matches!( + event.kind, + EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) + ); + + is_relevant_event_kind && event_affects_noir_file + }); + + if noir_files_modified { + write!(screen, "{}{}", termion::cursor::Restore, termion::clear::AfterCursor).unwrap(); + screen.flush().unwrap(); + let _ = compile_workspace_full(workspace, compile_options); + } + } + + screen.flush().unwrap(); + + Ok(()) +} + +fn compile_workspace_full( + workspace: &Workspace, + compile_options: &CompileOptions, +) -> Result<(), CliError> { let mut workspace_file_manager = file_manager_with_stdlib(&workspace.root_dir); - insert_all_files_for_workspace_into_file_manager(&workspace, &mut workspace_file_manager); + insert_all_files_for_workspace_into_file_manager(workspace, &mut workspace_file_manager); let parsed_files = parse_all(&workspace_file_manager); - let expression_width = args - .compile_options - .expression_width - .unwrap_or_else(|| backend.get_backend_info_or_default()); - let compiled_workspace = compile_workspace( - &workspace_file_manager, - &parsed_files, - &workspace, - &args.compile_options, - ); + let expression_width = + compile_options.expression_width.expect("expression width should have been set"); + let compiled_workspace = + compile_workspace(&workspace_file_manager, &parsed_files, workspace, compile_options); let (compiled_programs, compiled_contracts) = report_errors( compiled_workspace, &workspace_file_manager, - args.compile_options.deny_warnings, - args.compile_options.silence_warnings, + compile_options.deny_warnings, + compile_options.silence_warnings, )?; let (binary_packages, contract_packages): (Vec<_>, Vec<_>) = workspace @@ -85,11 +147,12 @@ pub(crate) fn run( .partition(|package| package.is_binary()); // Save build artifacts to disk. - let only_acir = args.compile_options.only_acir; + let only_acir = compile_options.only_acir; for (package, program) in binary_packages.into_iter().zip(compiled_programs) { let program = nargo::ops::transform_program(program, expression_width); save_program(program.clone(), &package, &workspace.target_directory_path(), only_acir); } + let circuit_dir = workspace.target_directory_path(); for (package, contract) in contract_packages.into_iter().zip(compiled_contracts) { let contract = nargo::ops::transform_contract(contract, expression_width); save_contract(contract, &package, &circuit_dir); From d8710c4442be2fcffc348f1f5776bc278d028ad0 Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 13 Mar 2024 15:17:41 -0500 Subject: [PATCH 076/416] chore: Add `Instruction::DecrementRc` (#4525) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4522 ## Summary\* Experimenting with this to see how much it improves performance of unconstrained code using arrays. ## Additional Context Currently the new dec_rc instruction is only issued for function parameters when a function is finished - assuming the parameters are not also returned. CC @sirasistant for visibility ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Álvaro Rodríguez --- .../src/brillig/brillig_gen/brillig_block.rs | 18 ++++++++--- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 2 +- .../src/ssa/function_builder/mod.rs | 21 ++++++++++++- .../noirc_evaluator/src/ssa/ir/instruction.rs | 16 +++++++++- .../noirc_evaluator/src/ssa/ir/printer.rs | 3 ++ compiler/noirc_evaluator/src/ssa/opt/die.rs | 26 +++++++++------- .../src/ssa/ssa_gen/context.rs | 31 +++++++++++++++++++ .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 9 +++--- 8 files changed, 103 insertions(+), 23 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 911f4c1924e..938a80e87d1 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -227,9 +227,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); } - _ => { - todo!("ICE: Param type not supported") - } + Type::Function => todo!("ICE: Type::Function Param not supported"), } } } @@ -661,11 +659,21 @@ impl<'block> BrilligBlock<'block> { let rc_register = match self.convert_ssa_value(*value, dfg) { BrilligVariable::BrilligArray(BrilligArray { rc, .. }) | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, - _ => unreachable!("ICE: increment rc on non-array"), + other => unreachable!("ICE: increment rc on non-array: {other:?}"), }; self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Add, 1); } - _ => todo!("ICE: Instruction not supported {instruction:?}"), + Instruction::DecrementRc { value } => { + let rc_register = match self.convert_ssa_value(*value, dfg) { + BrilligVariable::BrilligArray(BrilligArray { rc, .. }) + | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, + other => unreachable!("ICE: decrement rc on non-array: {other:?}"), + }; + self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Sub, 1); + } + Instruction::EnableSideEffects { .. } => { + todo!("enable_side_effects not supported by brillig") + } }; let dead_variables = self diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 140ed0b53ff..4442efe286a 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -503,7 +503,7 @@ impl Context { Instruction::Load { .. } => { unreachable!("Expected all load instructions to be removed before acir_gen") } - Instruction::IncrementRc { .. } => { + Instruction::IncrementRc { .. } | Instruction::DecrementRc { .. } => { // Do nothing. Only Brillig needs to worry about reference counted arrays } Instruction::RangeCheck { value, max_bit_size, assert_message } => { diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index bf34a47485b..2c39c83b342 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -384,6 +384,20 @@ impl FunctionBuilder { /// within the given value. If the given value is not an array and does not contain /// any arrays, this does nothing. pub(crate) fn increment_array_reference_count(&mut self, value: ValueId) { + self.update_array_reference_count(value, true); + } + + /// Insert instructions to decrement the reference count of any array(s) stored + /// within the given value. If the given value is not an array and does not contain + /// any arrays, this does nothing. + pub(crate) fn decrement_array_reference_count(&mut self, value: ValueId) { + self.update_array_reference_count(value, false); + } + + /// Increment or decrement the given value's reference count if it is an array. + /// If it is not an array, this does nothing. Note that inc_rc and dec_rc instructions + /// are ignored outside of unconstrained code. + pub(crate) fn update_array_reference_count(&mut self, value: ValueId, increment: bool) { match self.type_of_value(value) { Type::Numeric(_) => (), Type::Function => (), @@ -396,7 +410,12 @@ impl FunctionBuilder { typ @ Type::Array(..) | typ @ Type::Slice(..) => { // If there are nested arrays or slices, we wait until ArrayGet // is issued to increment the count of that array. - self.insert_instruction(Instruction::IncrementRc { value }, None); + let instruction = if increment { + Instruction::IncrementRc { value } + } else { + Instruction::DecrementRc { value } + }; + self.insert_instruction(instruction, None); // This is a bit odd, but in brillig the inc_rc instruction operates on // a copy of the array's metadata, so we need to re-store a loaded array diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 0b6c7074e45..afade4b0616 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -194,6 +194,13 @@ pub(crate) enum Instruction { /// implemented via reference counting. In ACIR code this is done with im::Vector and these /// IncrementRc instructions are ignored. IncrementRc { value: ValueId }, + + /// An instruction to decrement the reference count of a value. + /// + /// This currently only has an effect in Brillig code where array sharing and copy on write is + /// implemented via reference counting. In ACIR code this is done with im::Vector and these + /// DecrementRc instructions are ignored. + DecrementRc { value: ValueId }, } impl Instruction { @@ -214,6 +221,7 @@ impl Instruction { Instruction::Constrain(..) | Instruction::Store { .. } | Instruction::IncrementRc { .. } + | Instruction::DecrementRc { .. } | Instruction::RangeCheck { .. } | Instruction::EnableSideEffects { .. } => InstructionResultType::None, Instruction::Allocate { .. } @@ -250,6 +258,7 @@ impl Instruction { | Load { .. } | Store { .. } | IncrementRc { .. } + | DecrementRc { .. } | RangeCheck { .. } => false, Call { func, .. } => match dfg[*func] { @@ -285,6 +294,7 @@ impl Instruction { | Store { .. } | EnableSideEffects { .. } | IncrementRc { .. } + | DecrementRc { .. } | RangeCheck { .. } => true, // Some `Intrinsic`s have side effects so we must check what kind of `Call` this is. @@ -353,6 +363,7 @@ impl Instruction { Instruction::ArraySet { array: f(*array), index: f(*index), value: f(*value) } } Instruction::IncrementRc { value } => Instruction::IncrementRc { value: f(*value) }, + Instruction::DecrementRc { value } => Instruction::DecrementRc { value: f(*value) }, Instruction::RangeCheck { value, max_bit_size, assert_message } => { Instruction::RangeCheck { value: f(*value), @@ -409,7 +420,9 @@ impl Instruction { Instruction::EnableSideEffects { condition } => { f(*condition); } - Instruction::IncrementRc { value } | Instruction::RangeCheck { value, .. } => { + Instruction::IncrementRc { value } + | Instruction::DecrementRc { value } + | Instruction::RangeCheck { value, .. } => { f(*value); } } @@ -554,6 +567,7 @@ impl Instruction { Instruction::Load { .. } => None, Instruction::Store { .. } => None, Instruction::IncrementRc { .. } => None, + Instruction::DecrementRc { .. } => None, Instruction::RangeCheck { value, max_bit_size, .. } => { if let Some(numeric_constant) = dfg.get_numeric_constant(*value) { if numeric_constant.num_bits() < *max_bit_size { diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 9bd43fab1ff..6ef618fba6f 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -188,6 +188,9 @@ fn display_instruction_inner( Instruction::IncrementRc { value } => { writeln!(f, "inc_rc {}", show(*value)) } + Instruction::DecrementRc { value } => { + writeln!(f, "dec_rc {}", show(*value)) + } Instruction::RangeCheck { value, max_bit_size, .. } => { writeln!(f, "range_check {} to {} bits", show(*value), *max_bit_size,) } diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index f7d8adb5275..4c7beff0fbe 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -44,7 +44,7 @@ fn dead_instruction_elimination(function: &mut Function) { context.remove_unused_instructions_in_block(function, *block); } - context.remove_increment_rc_instructions(&mut function.dfg); + context.remove_rc_instructions(&mut function.dfg); } /// Per function context for tracking unused values and which instructions to remove. @@ -53,10 +53,10 @@ struct Context { used_values: HashSet, instructions_to_remove: HashSet, - /// IncrementRc instructions must be revisited after the main DIE pass since + /// IncrementRc & DecrementRc instructions must be revisited after the main DIE pass since /// they technically contain side-effects but we still want to remove them if their /// `value` parameter is not used elsewhere. - increment_rc_instructions: Vec<(InstructionId, BasicBlockId)>, + rc_instructions: Vec<(InstructionId, BasicBlockId)>, } impl Context { @@ -85,8 +85,9 @@ impl Context { } else { let instruction = &function.dfg[*instruction_id]; - if let Instruction::IncrementRc { .. } = instruction { - self.increment_rc_instructions.push((*instruction_id, block_id)); + use Instruction::*; + if matches!(instruction, IncrementRc { .. } | DecrementRc { .. }) { + self.rc_instructions.push((*instruction_id, block_id)); } else { instruction.for_each_value(|value| { self.mark_used_instruction_results(&function.dfg, value); @@ -145,16 +146,19 @@ impl Context { } } - fn remove_increment_rc_instructions(self, dfg: &mut DataFlowGraph) { - for (increment_rc, block) in self.increment_rc_instructions { - let value = match &dfg[increment_rc] { + fn remove_rc_instructions(self, dfg: &mut DataFlowGraph) { + for (rc, block) in self.rc_instructions { + let value = match &dfg[rc] { Instruction::IncrementRc { value } => *value, - other => unreachable!("Expected IncrementRc instruction, found {other:?}"), + Instruction::DecrementRc { value } => *value, + other => { + unreachable!("Expected IncrementRc or DecrementRc instruction, found {other:?}") + } }; - // This could be more efficient if we have to remove multiple IncrementRcs in a single block + // This could be more efficient if we have to remove multiple instructions in a single block if !self.used_values.contains(&value) { - dfg[block].instructions_mut().retain(|instruction| *instruction != increment_rc); + dfg[block].instructions_mut().retain(|instruction| *instruction != rc); } } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 9c760c013a9..409b99958a9 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -10,6 +10,7 @@ use noirc_frontend::{BinaryOpKind, Signedness}; use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; +use crate::ssa::ir::basic_block::BasicBlockId; use crate::ssa::ir::dfg::DataFlowGraph; use crate::ssa::ir::function::FunctionId as IrFunctionId; use crate::ssa::ir::function::{Function, RuntimeType}; @@ -1022,6 +1023,36 @@ impl<'a> FunctionContext<'a> { } } } + + /// Increments the reference count of all parameters. Returns the entry block of the function. + /// + /// This is done on parameters rather than call arguments so that we can optimize out + /// paired inc/dec instructions within brillig functions more easily. + pub(crate) fn increment_parameter_rcs(&mut self) -> BasicBlockId { + let entry = self.builder.current_function.entry_block(); + let parameters = self.builder.current_function.dfg.block_parameters(entry).to_vec(); + + for parameter in parameters { + self.builder.increment_array_reference_count(parameter); + } + + entry + } + + /// Ends a local scope of a function. + /// This will issue DecrementRc instructions for any arrays in the given starting scope + /// block's parameters. Arrays that are also used in terminator instructions for the scope are + /// ignored. + pub(crate) fn end_scope(&mut self, scope: BasicBlockId, terminator_args: &[ValueId]) { + let mut dropped_parameters = + self.builder.current_function.dfg.block_parameters(scope).to_vec(); + + dropped_parameters.retain(|parameter| !terminator_args.contains(parameter)); + + for parameter in dropped_parameters { + self.builder.decrement_array_reference_count(parameter); + } + } } /// True if the given operator cannot be encoded directly and needs diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index f3fa5d1d2f8..3d8ae0bb3eb 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -121,8 +121,11 @@ impl<'a> FunctionContext<'a> { /// Codegen a function's body and set its return value to that of its last parameter. /// For functions returning nothing, this will be an empty list. fn codegen_function_body(&mut self, body: &Expression) -> Result<(), RuntimeError> { + let entry_block = self.increment_parameter_rcs(); let return_value = self.codegen_expression(body)?; let results = return_value.into_value_list(self); + self.end_scope(entry_block, &results); + self.builder.terminate_with_return(results); Ok(()) } @@ -595,10 +598,8 @@ impl<'a> FunctionContext<'a> { arguments.append(&mut values); } - // If an array is passed as an argument we increase its reference count - for argument in &arguments { - self.builder.increment_array_reference_count(*argument); - } + // Don't need to increment array reference counts when passed in as arguments + // since it is done within the function to each parameter already. self.codegen_intrinsic_call_checks(function, &arguments, call.location); Ok(self.insert_call(function, arguments, &call.return_type, call.location)) From d4213a03c9f77ee8e7663fc965a825258d90a368 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Thu, 14 Mar 2024 05:55:50 -0400 Subject: [PATCH 077/416] feat: Sync from aztec-packages (#4546) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE chore: Pull noir (https://github.com/AztecProtocol/aztec-packages/pull/5193) feat: Check initialization arguments in constructors (https://github.com/AztecProtocol/aztec-packages/pull/5144) END_COMMIT_OVERRIDE --------- Co-authored-by: Tom French --- .aztec-sync-commit | 2 +- aztec_macros/src/transforms/functions.rs | 23 +++++++++++++++++++++++ cspell.json | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 5a1cd9c70bd..4d9320c79ba 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -58e15edf7fd3d32267b0aed883fc84f6cee327c9 +aa90f6ed7bfae06bdf6990816d154bbd24993689 diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index 8b3c7d2f53b..09c11e173fe 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -48,6 +48,12 @@ pub fn transform_function( func.def.body.0.insert(0, init_check); } + // Add assertion for initialization arguments + if is_initializer { + let assert_init_args = create_assert_init_args(); + func.def.body.0.insert(0, assert_init_args); + } + // Add access to the storage struct if storage_defined { let storage_def = abstract_storage(&ty.to_lowercase(), false); @@ -205,6 +211,23 @@ fn create_internal_check(fname: &str) -> Statement { ))) } +/// Creates a call to assert_initialization_args_match_address_preimage to ensure +/// the initialization arguments used in the init call match the address preimage. +/// +/// ```noir +/// assert_initialization_args_match_address_preimage(context); +/// ``` +fn create_assert_init_args() -> Statement { + make_statement(StatementKind::Expression(call( + variable_path(chained_dep!( + "aztec", + "initializer", + "assert_initialization_args_match_address_preimage" + )), + vec![variable("context")], + ))) +} + /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be /// appended into the args hash object. /// diff --git a/cspell.json b/cspell.json index 34dafa2b614..8cfc5e695a2 100644 --- a/cspell.json +++ b/cspell.json @@ -140,6 +140,7 @@ "plonkc", "PLONKish", "pprof", + "preimage", "preprocess", "prettytable", "printstd", From 57dd91b18faaceba457ee8b76b6819cea6bb7359 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 14 Mar 2024 10:02:35 -0500 Subject: [PATCH 078/416] chore: Add more `Hash` impls to stdlib (#4470) # Description ## Problem\* Resolves a TODO in #4241 ## Summary\* Adds the remaining Hash impls for primitive types in the stdlib ## Additional Context I've marked this as "no documentation needed" but we should probably document somewhere that these types are hashable. Where should this go? The existing "hash methods" page doesn't seem to fit. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/hash.nr | 101 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index fcf21436197..896dae15371 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -4,6 +4,7 @@ mod poseidon2; mod pedersen; use crate::default::Default; +use crate::uint128::U128; #[foreign(sha256)] // docs:start:sha256 @@ -120,10 +121,102 @@ where } } -// TODO: add implementations for the remainder of primitive types. -impl Hash for Field{ +impl Hash for Field { fn hash(self, state: &mut H) where H: Hasher{ - let input: [Field] = [self]; - H::write(state, input); + H::write(state, [self]); + } +} + +impl Hash for u8 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for u32 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for u64 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for i8 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for i32 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for i64 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for bool { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self as Field]); + } +} + +impl Hash for () { + fn hash(_self: Self, _state: &mut H) where H: Hasher {} +} + +impl Hash for U128 { + fn hash(self, state: &mut H) where H: Hasher{ + H::write(state, [self.lo as Field, self.hi as Field]); + } +} + +impl Hash for [T; N] where T: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + for elem in self { + elem.hash(state); + } + } +} + +impl Hash for (A, B) where A: Hash, B: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + } +} + +impl Hash for (A, B, C) where A: Hash, B: Hash, C: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + } +} + +impl Hash for (A, B, C, D) where A: Hash, B: Hash, C: Hash, D: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + self.3.hash(state); + } +} + +impl Hash for (A, B, C, D, E) where A: Hash, B: Hash, C: Hash, D: Hash, E: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.0.hash(state); + self.1.hash(state); + self.2.hash(state); + self.3.hash(state); + self.4.hash(state); } } From c8aa16bc7e78456cce1736fac82496996a8761f4 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 14 Mar 2024 10:40:26 -0500 Subject: [PATCH 079/416] fix: Evaluate operators in globals in types (#4537) # Description ## Problem\* Resolves ## Summary\* Evaluate operators in the definitions of globals when these globals are used in a type position. This often happens when using a global as the size of an array where the global is the result of some arithmetic expression. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/resolution/resolver.rs | 65 +++++++++++++++++-- compiler/noirc_frontend/src/tests.rs | 12 ++++ 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 00b1b443430..c33b83257b0 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -37,11 +37,11 @@ use crate::{ StatementKind, }; use crate::{ - ArrayLiteral, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, Generics, - ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, Shared, - StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, UnresolvedGenerics, - UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, - Visibility, ERROR_IDENT, + ArrayLiteral, BinaryOpKind, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, + Generics, ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, + Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, + UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, + UnresolvedTypeExpression, Visibility, ERROR_IDENT, }; use fm::FileId; use iter_extended::vecmap; @@ -1943,10 +1943,65 @@ impl<'a> Resolver<'a> { rhs: ExprId, span: Span, ) -> Result> { + // Arbitrary amount of recursive calls to try before giving up + let fuel = 100; + self.try_eval_array_length_id_with_fuel(rhs, span, fuel) + } + + fn try_eval_array_length_id_with_fuel( + &self, + rhs: ExprId, + span: Span, + fuel: u32, + ) -> Result> { + if fuel == 0 { + // If we reach here, it is likely from evaluating cyclic globals. We expect an error to + // be issued for them after name resolution so issue no error now. + return Err(None); + } + match self.interner.expression(&rhs) { HirExpression::Literal(HirLiteral::Integer(int, false)) => { int.try_into_u128().ok_or(Some(ResolverError::IntegerTooLarge { span })) } + HirExpression::Ident(ident) => { + let definition = self.interner.definition(ident.id); + match definition.kind { + DefinitionKind::Global(global_id) => { + let let_statement = self.interner.get_global_let_statement(global_id); + if let Some(let_statement) = let_statement { + let expression = let_statement.expression; + self.try_eval_array_length_id_with_fuel(expression, span, fuel - 1) + } else { + Err(Some(ResolverError::InvalidArrayLengthExpr { span })) + } + } + _ => Err(Some(ResolverError::InvalidArrayLengthExpr { span })), + } + } + HirExpression::Infix(infix) => { + let lhs = self.try_eval_array_length_id_with_fuel(infix.lhs, span, fuel - 1)?; + let rhs = self.try_eval_array_length_id_with_fuel(infix.rhs, span, fuel - 1)?; + + match infix.operator.kind { + BinaryOpKind::Add => Ok(lhs + rhs), + BinaryOpKind::Subtract => Ok(lhs - rhs), + BinaryOpKind::Multiply => Ok(lhs * rhs), + BinaryOpKind::Divide => Ok(lhs / rhs), + BinaryOpKind::Equal => Ok((lhs == rhs) as u128), + BinaryOpKind::NotEqual => Ok((lhs != rhs) as u128), + BinaryOpKind::Less => Ok((lhs < rhs) as u128), + BinaryOpKind::LessEqual => Ok((lhs <= rhs) as u128), + BinaryOpKind::Greater => Ok((lhs > rhs) as u128), + BinaryOpKind::GreaterEqual => Ok((lhs >= rhs) as u128), + BinaryOpKind::And => Ok(lhs & rhs), + BinaryOpKind::Or => Ok(lhs | rhs), + BinaryOpKind::Xor => Ok(lhs ^ rhs), + BinaryOpKind::ShiftRight => Ok(lhs >> rhs), + BinaryOpKind::ShiftLeft => Ok(lhs << rhs), + BinaryOpKind::Modulo => Ok(lhs % rhs), + } + } _other => Err(Some(ResolverError::InvalidArrayLengthExpr { span })), } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 1158f4c229a..b8ed6fb73d2 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1214,6 +1214,18 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { assert_eq!(get_program_errors(src).len(), 0); } + #[test] + fn operators_in_global_used_in_type() { + let src = r#" + global ONE = 1; + global COUNT = ONE + 2; + fn main() { + let _array: [Field; COUNT] = [1, 2, 3]; + } + "#; + assert_eq!(get_program_errors(src).len(), 0); + } + // Regression for #4545 #[test] fn type_aliases_in_main() { From de4986eb74b28b2e1065fa6b413d02457ddf61b0 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 14 Mar 2024 11:18:23 -0500 Subject: [PATCH 080/416] fix: Make `nargo` the default binary for cargo run (#4554) # Description ## Problem\* The acvm binary was added recently which prevents developers from using `cargo run` without an additional `--bin nargo` to specify the nargo binary ## Summary\* Sets the default binary to run to `nargo` for `cargo run`. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/tooling/nargo_cli/Cargo.toml b/tooling/nargo_cli/Cargo.toml index f92a2421b0f..1629ae86edb 100644 --- a/tooling/nargo_cli/Cargo.toml +++ b/tooling/nargo_cli/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "nargo_cli" description = "Noir's package manager" +default-run = "nargo" version.workspace = true authors.workspace = true edition.workspace = true From fe9a437b6d7ddc3f78665df1a576236555880c51 Mon Sep 17 00:00:00 2001 From: Andy <35195301+Andy53@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:37:05 -0600 Subject: [PATCH 081/416] feat: remove curly braces with fmt (#4529) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/compare/master...Andy53:noir:fmt_remvoes_curly_braces_when_there_is_a_single_import?expand=1 Closes #4480 ## Summary\* Removes curly braces from imports when there is only a single import. `use dep::std::hash::{sha256};` becomes: `use dep::std::hash::sha256; ## Additional Context ## Documentation\* Check one: - [ X] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ X] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- noir_stdlib/src/array.nr | 2 +- tooling/nargo_fmt/src/rewrite/imports.rs | 9 ++++++++- tooling/nargo_fmt/tests/expected/contract.nr | 2 +- tooling/nargo_fmt/tests/expected/import_braces.nr | 1 + tooling/nargo_fmt/tests/input/contract.nr | 2 +- tooling/nargo_fmt/tests/input/import_braces.nr | 1 + 6 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 tooling/nargo_fmt/tests/expected/import_braces.nr create mode 100644 tooling/nargo_fmt/tests/input/import_braces.nr diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index 3da4b649174..baa4bef50cc 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -1,4 +1,4 @@ -use crate::cmp::{Ord}; +use crate::cmp::Ord; // TODO: Once we fully move to the new SSA pass this module can be removed and replaced // by the methods in the `slice` module diff --git a/tooling/nargo_fmt/src/rewrite/imports.rs b/tooling/nargo_fmt/src/rewrite/imports.rs index 2788f778140..55eb259bcdd 100644 --- a/tooling/nargo_fmt/src/rewrite/imports.rs +++ b/tooling/nargo_fmt/src/rewrite/imports.rs @@ -102,7 +102,14 @@ impl UseTree { let mut iter = self.path.iter().peekable(); while let Some(segment) = iter.next() { - let segment_str = segment.rewrite(visitor, shape); + let mut segment_str = segment.rewrite(visitor, shape); + if segment_str.contains('{') + && !segment_str.contains(',') + && !segment_str.contains("::") + { + let empty = ""; + segment_str = segment_str.replace(['{', '}'], empty); + } result.push_str(&segment_str); if iter.peek().is_some() { diff --git a/tooling/nargo_fmt/tests/expected/contract.nr b/tooling/nargo_fmt/tests/expected/contract.nr index c5b19a686d2..97a6ebd6b77 100644 --- a/tooling/nargo_fmt/tests/expected/contract.nr +++ b/tooling/nargo_fmt/tests/expected/contract.nr @@ -11,7 +11,7 @@ contract Benchmarking { context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - types::address::{AztecAddress} + types::address::AztecAddress }; struct Storage { diff --git a/tooling/nargo_fmt/tests/expected/import_braces.nr b/tooling/nargo_fmt/tests/expected/import_braces.nr new file mode 100644 index 00000000000..49c9d09001e --- /dev/null +++ b/tooling/nargo_fmt/tests/expected/import_braces.nr @@ -0,0 +1 @@ +use dep::std::hash::sha256; diff --git a/tooling/nargo_fmt/tests/input/contract.nr b/tooling/nargo_fmt/tests/input/contract.nr index c5b19a686d2..97a6ebd6b77 100644 --- a/tooling/nargo_fmt/tests/input/contract.nr +++ b/tooling/nargo_fmt/tests/input/contract.nr @@ -11,7 +11,7 @@ contract Benchmarking { context::Context, note::{note_getter_options::NoteGetterOptions, note_header::NoteHeader}, log::emit_unencrypted_log, state_vars::{Map, PublicMutable, PrivateSet}, types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, - types::address::{AztecAddress} + types::address::AztecAddress }; struct Storage { diff --git a/tooling/nargo_fmt/tests/input/import_braces.nr b/tooling/nargo_fmt/tests/input/import_braces.nr new file mode 100644 index 00000000000..88c7e9562a8 --- /dev/null +++ b/tooling/nargo_fmt/tests/input/import_braces.nr @@ -0,0 +1 @@ +use dep::std::hash::{sha256}; \ No newline at end of file From 3ad88696ab63d8b1838b4ca7e9a3a97f823ca976 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 14 Mar 2024 20:38:06 +0000 Subject: [PATCH 082/416] chore: separate tests for execution failures from compilation failures (#4559) # Description ## Problem\* Resolves ## Summary\* I just had a test pass when it should have failed in #4491 because despite compiling when it shouldn't have, it failed in execution (because I didn't provide a `Prover.toml` but the circuit took arguments). We're then masking potential test failures by not separating circuits which we expect to fail to compile vs fail to execute. I've created an `execution_failure` directory to hold all of the latter and updated the `compile_failure` tests to only compile the circuit rather than executing it. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../brillig_nested_slices/Prover.toml | 2 - .../custom_entry_not_found/Prover.toml | 1 - .../dep_impl_primitive/Prover.toml | 1 - .../compile_failure/depend_on_bin/Prover.toml | 1 - .../div_by_zero_modulo/Prover.toml | 0 .../dup_trait_implementation_4/Prover.toml | 0 .../dup_trait_implementation_5/Prover.toml | 0 .../dup_trait_items_1/Prover.toml | 0 .../dup_trait_items_2/Prover.toml | 0 .../dup_trait_items_3/Prover.toml | 0 .../dup_trait_items_4/Prover.toml | 0 .../dup_trait_items_5/Prover.toml | 0 .../dup_trait_items_6/Prover.toml | 0 .../hashmap_load_factor/Prover.toml | 26 ------------ .../negate_unsigned/Prover.toml | 0 .../orphaned_trait_impl/Prover.toml | 2 - .../slice_remove_failure/Prover.toml | 2 - .../crates/a/Prover.toml | 2 - .../crates/b/Prover.toml | 2 - .../assert_msg_runtime/Nargo.toml | 0 .../assert_msg_runtime/Prover.toml | 0 .../assert_msg_runtime/src/main.nr | 0 .../brillig_assert_fail/Nargo.toml | 0 .../brillig_assert_fail/Prover.toml | 0 .../brillig_assert_fail/src/main.nr | 0 .../brillig_assert_msg_runtime/Nargo.toml | 0 .../brillig_assert_msg_runtime/Prover.toml | 0 .../brillig_assert_msg_runtime/src/main.nr | 0 .../div_by_zero_constants/Nargo.toml | 0 .../div_by_zero_constants}/Prover.toml | 0 .../div_by_zero_constants/src/main.nr | 0 .../div_by_zero_modulo/Nargo.toml | 0 .../div_by_zero_modulo}/Prover.toml | 0 .../div_by_zero_modulo/src/main.nr | 0 .../div_by_zero_numerator_witness/Nargo.toml | 0 .../div_by_zero_numerator_witness/Prover.toml | 0 .../div_by_zero_numerator_witness/src/main.nr | 0 .../div_by_zero_witness/Nargo.toml | 0 .../div_by_zero_witness/Prover.toml | 0 .../div_by_zero_witness/src/main.nr | 0 .../dyn_index_fail_nested_array/Nargo.toml | 0 .../dyn_index_fail_nested_array/Prover.toml | 0 .../dyn_index_fail_nested_array/src/main.nr | 0 .../dynamic_index_failure/Nargo.toml | 0 .../dynamic_index_failure/Prover.toml | 0 .../dynamic_index_failure/src/main.nr | 0 .../option_expect/Nargo.toml | 0 .../option_expect/src/main.nr | 0 .../slice_access_failure/Nargo.toml | 0 .../slice_access_failure}/Prover.toml | 0 .../slice_access_failure/src/main.nr | 0 .../slice_insert_failure/Nargo.toml | 0 .../slice_insert_failure}/Prover.toml | 0 .../slice_insert_failure/src/main.nr | 0 .../slice_remove_failure/Nargo.toml | 0 .../slice_remove_failure}/Prover.toml | 0 .../slice_remove_failure/src/main.nr | 0 .../workspace_fail/Nargo.toml | 0 .../workspace_fail/crates/a/Nargo.toml | 0 .../workspace_fail/crates/a/Prover.toml | 0 .../workspace_fail/crates/a/src/main.nr | 0 .../workspace_fail/crates/b/Nargo.toml | 0 .../workspace_fail/crates/b/Prover.toml | 0 .../workspace_fail/crates/b/src/main.nr | 0 tooling/nargo_cli/build.rs | 41 ++++++++++++++++++- 65 files changed, 40 insertions(+), 40 deletions(-) delete mode 100644 test_programs/compile_failure/brillig_nested_slices/Prover.toml delete mode 100644 test_programs/compile_failure/custom_entry_not_found/Prover.toml delete mode 100644 test_programs/compile_failure/dep_impl_primitive/Prover.toml delete mode 100644 test_programs/compile_failure/depend_on_bin/Prover.toml delete mode 100644 test_programs/compile_failure/div_by_zero_modulo/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_implementation_4/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_implementation_5/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_1/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_2/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_3/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_4/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_5/Prover.toml delete mode 100644 test_programs/compile_failure/dup_trait_items_6/Prover.toml delete mode 100644 test_programs/compile_failure/hashmap_load_factor/Prover.toml delete mode 100644 test_programs/compile_failure/negate_unsigned/Prover.toml delete mode 100644 test_programs/compile_failure/orphaned_trait_impl/Prover.toml delete mode 100644 test_programs/compile_failure/slice_remove_failure/Prover.toml delete mode 100644 test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml delete mode 100644 test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml rename test_programs/{compile_failure => execution_failure}/assert_msg_runtime/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/assert_msg_runtime/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/assert_msg_runtime/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_fail/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_fail/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_fail/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_msg_runtime/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_msg_runtime/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/brillig_assert_msg_runtime/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_constants/Nargo.toml (100%) rename test_programs/{compile_failure/cyclic_dep => execution_failure/div_by_zero_constants}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_constants/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_modulo/Nargo.toml (100%) rename test_programs/{compile_failure/div_by_zero_constants => execution_failure/div_by_zero_modulo}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_modulo/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_numerator_witness/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_numerator_witness/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_numerator_witness/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_witness/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_witness/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/div_by_zero_witness/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/dyn_index_fail_nested_array/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/dyn_index_fail_nested_array/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/dyn_index_fail_nested_array/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/dynamic_index_failure/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/dynamic_index_failure/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/dynamic_index_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/option_expect/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/option_expect/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/slice_access_failure/Nargo.toml (100%) rename test_programs/{compile_failure/radix_non_constant_length => execution_failure/slice_access_failure}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/slice_access_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/slice_insert_failure/Nargo.toml (100%) rename test_programs/{compile_failure/slice_access_failure => execution_failure/slice_insert_failure}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/slice_insert_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/slice_remove_failure/Nargo.toml (100%) rename test_programs/{compile_failure/slice_insert_failure => execution_failure/slice_remove_failure}/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/slice_remove_failure/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/a/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/a/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/a/src/main.nr (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/b/Nargo.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/b/Prover.toml (100%) rename test_programs/{compile_failure => execution_failure}/workspace_fail/crates/b/src/main.nr (100%) diff --git a/test_programs/compile_failure/brillig_nested_slices/Prover.toml b/test_programs/compile_failure/brillig_nested_slices/Prover.toml deleted file mode 100644 index c52564de922..00000000000 --- a/test_programs/compile_failure/brillig_nested_slices/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -a = "5" -b = "10" diff --git a/test_programs/compile_failure/custom_entry_not_found/Prover.toml b/test_programs/compile_failure/custom_entry_not_found/Prover.toml deleted file mode 100644 index 4dd6b405159..00000000000 --- a/test_programs/compile_failure/custom_entry_not_found/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = "1" diff --git a/test_programs/compile_failure/dep_impl_primitive/Prover.toml b/test_programs/compile_failure/dep_impl_primitive/Prover.toml deleted file mode 100644 index 7d4290a117a..00000000000 --- a/test_programs/compile_failure/dep_impl_primitive/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = 1 diff --git a/test_programs/compile_failure/depend_on_bin/Prover.toml b/test_programs/compile_failure/depend_on_bin/Prover.toml deleted file mode 100644 index 7d4290a117a..00000000000 --- a/test_programs/compile_failure/depend_on_bin/Prover.toml +++ /dev/null @@ -1 +0,0 @@ -x = 1 diff --git a/test_programs/compile_failure/div_by_zero_modulo/Prover.toml b/test_programs/compile_failure/div_by_zero_modulo/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_implementation_4/Prover.toml b/test_programs/compile_failure/dup_trait_implementation_4/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_implementation_5/Prover.toml b/test_programs/compile_failure/dup_trait_implementation_5/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_1/Prover.toml b/test_programs/compile_failure/dup_trait_items_1/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_2/Prover.toml b/test_programs/compile_failure/dup_trait_items_2/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_3/Prover.toml b/test_programs/compile_failure/dup_trait_items_3/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_4/Prover.toml b/test_programs/compile_failure/dup_trait_items_4/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_5/Prover.toml b/test_programs/compile_failure/dup_trait_items_5/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/dup_trait_items_6/Prover.toml b/test_programs/compile_failure/dup_trait_items_6/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/hashmap_load_factor/Prover.toml b/test_programs/compile_failure/hashmap_load_factor/Prover.toml deleted file mode 100644 index e54319c61e9..00000000000 --- a/test_programs/compile_failure/hashmap_load_factor/Prover.toml +++ /dev/null @@ -1,26 +0,0 @@ -# Expected 6 key-value entries for hashmap capacity of 8. -# These must be distinct (both key-to-key, and value-to-value) for correct testing. - -[[input]] -key = 2 -value = 17 - -[[input]] -key = 3 -value = 19 - -[[input]] -key = 5 -value = 23 - -[[input]] -key = 7 -value = 29 - -[[input]] -key = 11 -value = 31 - -[[input]] -key = 41 -value = 43 \ No newline at end of file diff --git a/test_programs/compile_failure/negate_unsigned/Prover.toml b/test_programs/compile_failure/negate_unsigned/Prover.toml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test_programs/compile_failure/orphaned_trait_impl/Prover.toml b/test_programs/compile_failure/orphaned_trait_impl/Prover.toml deleted file mode 100644 index 2c1854573a4..00000000000 --- a/test_programs/compile_failure/orphaned_trait_impl/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = 1 -y = 2 diff --git a/test_programs/compile_failure/slice_remove_failure/Prover.toml b/test_programs/compile_failure/slice_remove_failure/Prover.toml deleted file mode 100644 index f28f2f8cc48..00000000000 --- a/test_programs/compile_failure/slice_remove_failure/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "5" -y = "10" diff --git a/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml b/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml deleted file mode 100644 index 465ef562de4..00000000000 --- a/test_programs/compile_failure/workspace_missing_toml/crates/a/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "1" -y = "1" diff --git a/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml b/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml deleted file mode 100644 index a0397e89477..00000000000 --- a/test_programs/compile_failure/workspace_missing_toml/crates/b/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "1" -y = "0" diff --git a/test_programs/compile_failure/assert_msg_runtime/Nargo.toml b/test_programs/execution_failure/assert_msg_runtime/Nargo.toml similarity index 100% rename from test_programs/compile_failure/assert_msg_runtime/Nargo.toml rename to test_programs/execution_failure/assert_msg_runtime/Nargo.toml diff --git a/test_programs/compile_failure/assert_msg_runtime/Prover.toml b/test_programs/execution_failure/assert_msg_runtime/Prover.toml similarity index 100% rename from test_programs/compile_failure/assert_msg_runtime/Prover.toml rename to test_programs/execution_failure/assert_msg_runtime/Prover.toml diff --git a/test_programs/compile_failure/assert_msg_runtime/src/main.nr b/test_programs/execution_failure/assert_msg_runtime/src/main.nr similarity index 100% rename from test_programs/compile_failure/assert_msg_runtime/src/main.nr rename to test_programs/execution_failure/assert_msg_runtime/src/main.nr diff --git a/test_programs/compile_failure/brillig_assert_fail/Nargo.toml b/test_programs/execution_failure/brillig_assert_fail/Nargo.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_fail/Nargo.toml rename to test_programs/execution_failure/brillig_assert_fail/Nargo.toml diff --git a/test_programs/compile_failure/brillig_assert_fail/Prover.toml b/test_programs/execution_failure/brillig_assert_fail/Prover.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_fail/Prover.toml rename to test_programs/execution_failure/brillig_assert_fail/Prover.toml diff --git a/test_programs/compile_failure/brillig_assert_fail/src/main.nr b/test_programs/execution_failure/brillig_assert_fail/src/main.nr similarity index 100% rename from test_programs/compile_failure/brillig_assert_fail/src/main.nr rename to test_programs/execution_failure/brillig_assert_fail/src/main.nr diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml b/test_programs/execution_failure/brillig_assert_msg_runtime/Nargo.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_msg_runtime/Nargo.toml rename to test_programs/execution_failure/brillig_assert_msg_runtime/Nargo.toml diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml b/test_programs/execution_failure/brillig_assert_msg_runtime/Prover.toml similarity index 100% rename from test_programs/compile_failure/brillig_assert_msg_runtime/Prover.toml rename to test_programs/execution_failure/brillig_assert_msg_runtime/Prover.toml diff --git a/test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr b/test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr similarity index 100% rename from test_programs/compile_failure/brillig_assert_msg_runtime/src/main.nr rename to test_programs/execution_failure/brillig_assert_msg_runtime/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_constants/Nargo.toml b/test_programs/execution_failure/div_by_zero_constants/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_constants/Nargo.toml rename to test_programs/execution_failure/div_by_zero_constants/Nargo.toml diff --git a/test_programs/compile_failure/cyclic_dep/Prover.toml b/test_programs/execution_failure/div_by_zero_constants/Prover.toml similarity index 100% rename from test_programs/compile_failure/cyclic_dep/Prover.toml rename to test_programs/execution_failure/div_by_zero_constants/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_constants/src/main.nr b/test_programs/execution_failure/div_by_zero_constants/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_constants/src/main.nr rename to test_programs/execution_failure/div_by_zero_constants/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_modulo/Nargo.toml b/test_programs/execution_failure/div_by_zero_modulo/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_modulo/Nargo.toml rename to test_programs/execution_failure/div_by_zero_modulo/Nargo.toml diff --git a/test_programs/compile_failure/div_by_zero_constants/Prover.toml b/test_programs/execution_failure/div_by_zero_modulo/Prover.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_constants/Prover.toml rename to test_programs/execution_failure/div_by_zero_modulo/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_modulo/src/main.nr b/test_programs/execution_failure/div_by_zero_modulo/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_modulo/src/main.nr rename to test_programs/execution_failure/div_by_zero_modulo/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_numerator_witness/Nargo.toml b/test_programs/execution_failure/div_by_zero_numerator_witness/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_numerator_witness/Nargo.toml rename to test_programs/execution_failure/div_by_zero_numerator_witness/Nargo.toml diff --git a/test_programs/compile_failure/div_by_zero_numerator_witness/Prover.toml b/test_programs/execution_failure/div_by_zero_numerator_witness/Prover.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_numerator_witness/Prover.toml rename to test_programs/execution_failure/div_by_zero_numerator_witness/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_numerator_witness/src/main.nr b/test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_numerator_witness/src/main.nr rename to test_programs/execution_failure/div_by_zero_numerator_witness/src/main.nr diff --git a/test_programs/compile_failure/div_by_zero_witness/Nargo.toml b/test_programs/execution_failure/div_by_zero_witness/Nargo.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_witness/Nargo.toml rename to test_programs/execution_failure/div_by_zero_witness/Nargo.toml diff --git a/test_programs/compile_failure/div_by_zero_witness/Prover.toml b/test_programs/execution_failure/div_by_zero_witness/Prover.toml similarity index 100% rename from test_programs/compile_failure/div_by_zero_witness/Prover.toml rename to test_programs/execution_failure/div_by_zero_witness/Prover.toml diff --git a/test_programs/compile_failure/div_by_zero_witness/src/main.nr b/test_programs/execution_failure/div_by_zero_witness/src/main.nr similarity index 100% rename from test_programs/compile_failure/div_by_zero_witness/src/main.nr rename to test_programs/execution_failure/div_by_zero_witness/src/main.nr diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/Nargo.toml b/test_programs/execution_failure/dyn_index_fail_nested_array/Nargo.toml similarity index 100% rename from test_programs/compile_failure/dyn_index_fail_nested_array/Nargo.toml rename to test_programs/execution_failure/dyn_index_fail_nested_array/Nargo.toml diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/Prover.toml b/test_programs/execution_failure/dyn_index_fail_nested_array/Prover.toml similarity index 100% rename from test_programs/compile_failure/dyn_index_fail_nested_array/Prover.toml rename to test_programs/execution_failure/dyn_index_fail_nested_array/Prover.toml diff --git a/test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr b/test_programs/execution_failure/dyn_index_fail_nested_array/src/main.nr similarity index 100% rename from test_programs/compile_failure/dyn_index_fail_nested_array/src/main.nr rename to test_programs/execution_failure/dyn_index_fail_nested_array/src/main.nr diff --git a/test_programs/compile_failure/dynamic_index_failure/Nargo.toml b/test_programs/execution_failure/dynamic_index_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/dynamic_index_failure/Nargo.toml rename to test_programs/execution_failure/dynamic_index_failure/Nargo.toml diff --git a/test_programs/compile_failure/dynamic_index_failure/Prover.toml b/test_programs/execution_failure/dynamic_index_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/dynamic_index_failure/Prover.toml rename to test_programs/execution_failure/dynamic_index_failure/Prover.toml diff --git a/test_programs/compile_failure/dynamic_index_failure/src/main.nr b/test_programs/execution_failure/dynamic_index_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/dynamic_index_failure/src/main.nr rename to test_programs/execution_failure/dynamic_index_failure/src/main.nr diff --git a/test_programs/compile_failure/option_expect/Nargo.toml b/test_programs/execution_failure/option_expect/Nargo.toml similarity index 100% rename from test_programs/compile_failure/option_expect/Nargo.toml rename to test_programs/execution_failure/option_expect/Nargo.toml diff --git a/test_programs/compile_failure/option_expect/src/main.nr b/test_programs/execution_failure/option_expect/src/main.nr similarity index 100% rename from test_programs/compile_failure/option_expect/src/main.nr rename to test_programs/execution_failure/option_expect/src/main.nr diff --git a/test_programs/compile_failure/slice_access_failure/Nargo.toml b/test_programs/execution_failure/slice_access_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/slice_access_failure/Nargo.toml rename to test_programs/execution_failure/slice_access_failure/Nargo.toml diff --git a/test_programs/compile_failure/radix_non_constant_length/Prover.toml b/test_programs/execution_failure/slice_access_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/radix_non_constant_length/Prover.toml rename to test_programs/execution_failure/slice_access_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_access_failure/src/main.nr b/test_programs/execution_failure/slice_access_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/slice_access_failure/src/main.nr rename to test_programs/execution_failure/slice_access_failure/src/main.nr diff --git a/test_programs/compile_failure/slice_insert_failure/Nargo.toml b/test_programs/execution_failure/slice_insert_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/slice_insert_failure/Nargo.toml rename to test_programs/execution_failure/slice_insert_failure/Nargo.toml diff --git a/test_programs/compile_failure/slice_access_failure/Prover.toml b/test_programs/execution_failure/slice_insert_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/slice_access_failure/Prover.toml rename to test_programs/execution_failure/slice_insert_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_insert_failure/src/main.nr b/test_programs/execution_failure/slice_insert_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/slice_insert_failure/src/main.nr rename to test_programs/execution_failure/slice_insert_failure/src/main.nr diff --git a/test_programs/compile_failure/slice_remove_failure/Nargo.toml b/test_programs/execution_failure/slice_remove_failure/Nargo.toml similarity index 100% rename from test_programs/compile_failure/slice_remove_failure/Nargo.toml rename to test_programs/execution_failure/slice_remove_failure/Nargo.toml diff --git a/test_programs/compile_failure/slice_insert_failure/Prover.toml b/test_programs/execution_failure/slice_remove_failure/Prover.toml similarity index 100% rename from test_programs/compile_failure/slice_insert_failure/Prover.toml rename to test_programs/execution_failure/slice_remove_failure/Prover.toml diff --git a/test_programs/compile_failure/slice_remove_failure/src/main.nr b/test_programs/execution_failure/slice_remove_failure/src/main.nr similarity index 100% rename from test_programs/compile_failure/slice_remove_failure/src/main.nr rename to test_programs/execution_failure/slice_remove_failure/src/main.nr diff --git a/test_programs/compile_failure/workspace_fail/Nargo.toml b/test_programs/execution_failure/workspace_fail/Nargo.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/Nargo.toml rename to test_programs/execution_failure/workspace_fail/Nargo.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/Nargo.toml b/test_programs/execution_failure/workspace_fail/crates/a/Nargo.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/a/Nargo.toml rename to test_programs/execution_failure/workspace_fail/crates/a/Nargo.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/Prover.toml b/test_programs/execution_failure/workspace_fail/crates/a/Prover.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/a/Prover.toml rename to test_programs/execution_failure/workspace_fail/crates/a/Prover.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/a/src/main.nr b/test_programs/execution_failure/workspace_fail/crates/a/src/main.nr similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/a/src/main.nr rename to test_programs/execution_failure/workspace_fail/crates/a/src/main.nr diff --git a/test_programs/compile_failure/workspace_fail/crates/b/Nargo.toml b/test_programs/execution_failure/workspace_fail/crates/b/Nargo.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/b/Nargo.toml rename to test_programs/execution_failure/workspace_fail/crates/b/Nargo.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/b/Prover.toml b/test_programs/execution_failure/workspace_fail/crates/b/Prover.toml similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/b/Prover.toml rename to test_programs/execution_failure/workspace_fail/crates/b/Prover.toml diff --git a/test_programs/compile_failure/workspace_fail/crates/b/src/main.nr b/test_programs/execution_failure/workspace_fail/crates/b/src/main.nr similarity index 100% rename from test_programs/compile_failure/workspace_fail/crates/b/src/main.nr rename to test_programs/execution_failure/workspace_fail/crates/b/src/main.nr diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 1ca12b75dfb..f68ccbfd50e 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -41,6 +41,7 @@ fn main() { println!("cargo:rerun-if-changed={}", test_dir.as_os_str().to_str().unwrap()); generate_execution_success_tests(&mut test_file, &test_dir); + generate_execution_failure_tests(&mut test_file, &test_dir); generate_noir_test_success_tests(&mut test_file, &test_dir); generate_noir_test_failure_tests(&mut test_file, &test_dir); generate_compile_success_empty_tests(&mut test_file, &test_dir); @@ -86,6 +87,44 @@ fn execution_success_{test_name}() {{ } } +fn generate_execution_failure_tests(test_file: &mut File, test_data_dir: &Path) { + let test_sub_dir = "execution_failure"; + let test_data_dir = test_data_dir.join(test_sub_dir); + + let test_case_dirs = + fs::read_dir(test_data_dir).unwrap().flatten().filter(|c| c.path().is_dir()); + + for test_dir in test_case_dirs { + let test_name = + test_dir.file_name().into_string().expect("Directory can't be converted to string"); + if test_name.contains('-') { + panic!( + "Invalid test directory: {test_name}. Cannot include `-`, please convert to `_`" + ); + }; + let test_dir = &test_dir.path(); + + write!( + test_file, + r#" +#[test] +fn execution_failure_{test_name}() {{ + let test_program_dir = PathBuf::from("{test_dir}"); + + let mut cmd = Command::cargo_bin("nargo").unwrap(); + cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); + cmd.arg("--program-dir").arg(test_program_dir); + cmd.arg("execute").arg("--force"); + + cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); +}} + "#, + test_dir = test_dir.display(), + ) + .expect("Could not write templated test file."); + } +} + fn generate_noir_test_success_tests(test_file: &mut File, test_data_dir: &Path) { let test_sub_dir = "noir_test_success"; let test_data_dir = test_data_dir.join(test_sub_dir); @@ -281,7 +320,7 @@ fn compile_failure_{test_name}() {{ let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.env("NARGO_BACKEND_PATH", path_to_mock_backend()); cmd.arg("--program-dir").arg(test_program_dir); - cmd.arg("execute").arg("--force"); + cmd.arg("compile").arg("--force"); cmd.assert().failure().stderr(predicate::str::contains("The application panicked (crashed).").not()); }} From 8396e2194533024aaed0b2de4679f031937da86e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Fri, 15 Mar 2024 12:26:55 +0000 Subject: [PATCH 083/416] chore: fixing some broken links (#4556) --- .../noir/standard_library/black_box_fns.md | 18 +++++++++--------- .../noir/standard_library/black_box_fns.md | 18 +++++++++--------- .../noir/standard_library/black_box_fns.md | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/docs/noir/standard_library/black_box_fns.md b/docs/docs/noir/standard_library/black_box_fns.md index eae8744abf0..be8c65679c3 100644 --- a/docs/docs/noir/standard_library/black_box_fns.md +++ b/docs/docs/noir/standard_library/black_box_fns.md @@ -12,18 +12,18 @@ The ACVM spec defines a set of blackbox functions which backends will be expecte Here is a list of the current black box functions: -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Blake3](./cryptographic_primitives/hashes#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) - [Recursive proof verification](./recursion) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md index eae8744abf0..be8c65679c3 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.24.0/noir/standard_library/black_box_fns.md @@ -12,18 +12,18 @@ The ACVM spec defines a set of blackbox functions which backends will be expecte Here is a list of the current black box functions: -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Blake3](./cryptographic_primitives/hashes#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) - [Recursive proof verification](./recursion) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. diff --git a/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md index eae8744abf0..be8c65679c3 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md +++ b/docs/versioned_docs/version-v0.25.0/noir/standard_library/black_box_fns.md @@ -12,18 +12,18 @@ The ACVM spec defines a set of blackbox functions which backends will be expecte Here is a list of the current black box functions: -- [SHA256](./cryptographic_primitives/hashes#sha256) -- [Schnorr signature verification](./cryptographic_primitives/schnorr) -- [Blake2s](./cryptographic_primitives/hashes#blake2s) -- [Blake3](./cryptographic_primitives/hashes#blake3) -- [Pedersen Hash](./cryptographic_primitives/hashes#pedersen_hash) -- [Pedersen Commitment](./cryptographic_primitives/hashes#pedersen_commitment) -- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification) -- [Fixed base scalar multiplication](./cryptographic_primitives/scalar) +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) - AND - XOR - RANGE -- [Keccak256](./cryptographic_primitives/hashes#keccak256) +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) - [Recursive proof verification](./recursion) Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. From 4cf700bcfe157ebc82cdf7321a16959b7a4add57 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 15 Mar 2024 08:40:43 -0500 Subject: [PATCH 084/416] feat: Add more impls on Option (#4549) # Description ## Problem\* Resolves ## Summary\* Adds more trait implementations on the Option type. ## Additional Context This PR depends on https://github.com/noir-lang/noir/pull/4470 to be merged first since it requires `bool: Hash` ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [x] **[Exceptional Case]** Documentation to be submitted in a separate PR. - Once both this and https://github.com/noir-lang/noir/pull/4470 are merged I think I'll add all the new impls to the docs at once. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/option.nr | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/noir_stdlib/src/option.nr b/noir_stdlib/src/option.nr index 1c32f758af7..c94a1cf836e 100644 --- a/noir_stdlib/src/option.nr +++ b/noir_stdlib/src/option.nr @@ -1,3 +1,7 @@ +use crate::hash::{Hash, Hasher}; +use crate::cmp::{Ordering, Ord, Eq}; +use crate::default::Default; + struct Option { _is_some: bool, _value: T, @@ -152,3 +156,51 @@ impl Option { } } } + +impl Default for Option { + fn default() -> Self { + Option::none() + } +} + +impl Eq for Option where T: Eq { + fn eq(self, other: Self) -> bool { + if self._is_some == other._is_some { + if self._is_some { + self._value == other._value + } else { + true + } + } else { + false + } + } +} + +impl Hash for Option where T: Hash { + fn hash(self, state: &mut H) where H: Hasher { + self._is_some.hash(state); + if self._is_some { + self._value.hash(state); + } + } +} + +// For this impl we're declaring Option::none < Option::some +impl Ord for Option where T: Ord { + fn cmp(self, other: Self) -> Ordering { + if self._is_some { + if other._is_some { + self._value.cmp(other._value) + } else { + Ordering::greater() + } + } else { + if other._is_some { + Ordering::less() + } else { + Ordering::equal() + } + } + } +} From a8b7cdb8a3698bc8923b6fa8714deebb8bf3923f Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:20:40 +0000 Subject: [PATCH 085/416] feat: allow usage of noir `#[test]` syntax in stdlib (#4553) # Description ## Problem\* Resolves ## Summary\* Running tests within the Noir stdlib is a little complicated as nargo will not accept it as a regular crate as the stdlib uses features which are disallowed in other crates. This means that in a number of cases we're placing stdlib unit tests inside of the `test_programs` instead of just using the `#[test]` format inside of the stdlib itself. This is suboptimal as it separates the implementation from the tests in the repository and once https://github.com/noir-lang/noir/pull/4491 is merged, it will be impossible to use this method to test any private functions within the stdlib. This PR then adds a new integration test which is equivalent to running `nargo test` on the stdlib and moves some tests over to the stdlib as an example. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/field/bn254.nr | 78 +++++++++++++++++ .../field_comparisons/Nargo.toml | 6 -- .../field_comparisons/Prover.toml | 1 - .../field_comparisons/src/main.nr | 86 ------------------- tooling/nargo_cli/tests/stdlib-tests.rs | 62 +++++++++++++ 5 files changed, 140 insertions(+), 93 deletions(-) delete mode 100644 test_programs/compile_success_empty/field_comparisons/Nargo.toml delete mode 100644 test_programs/compile_success_empty/field_comparisons/Prover.toml delete mode 100644 test_programs/compile_success_empty/field_comparisons/src/main.nr create mode 100644 tooling/nargo_cli/tests/stdlib-tests.rs diff --git a/noir_stdlib/src/field/bn254.nr b/noir_stdlib/src/field/bn254.nr index 9e1445fd3ba..765f8a9d849 100644 --- a/noir_stdlib/src/field/bn254.nr +++ b/noir_stdlib/src/field/bn254.nr @@ -101,3 +101,81 @@ pub fn gt(a: Field, b: Field) -> bool { pub fn lt(a: Field, b: Field) -> bool { gt(b, a) } + +mod tests { + // TODO: Allow imports from "super" + use crate::field::bn254::{decompose_unsafe, decompose, lt_unsafe, assert_gt, gt, lt, TWO_POW_128, lte_unsafe, PLO, PHI}; + + #[test] + fn check_decompose_unsafe() { + assert_eq(decompose_unsafe(TWO_POW_128), (0, 1)); + assert_eq(decompose_unsafe(TWO_POW_128 + 0x1234567890), (0x1234567890, 1)); + assert_eq(decompose_unsafe(0x1234567890), (0x1234567890, 0)); + } + + #[test] + fn check_decompose() { + assert_eq(decompose(TWO_POW_128), (0, 1)); + assert_eq(decompose(TWO_POW_128 + 0x1234567890), (0x1234567890, 1)); + assert_eq(decompose(0x1234567890), (0x1234567890, 0)); + } + + #[test] + fn check_lt_unsafe() { + assert(lt_unsafe(0, 1, 16)); + assert(lt_unsafe(0, 0x100, 16)); + assert(lt_unsafe(0x100, TWO_POW_128 - 1, 16)); + assert(!lt_unsafe(0, TWO_POW_128, 16)); + } + + #[test] + fn check_lte_unsafe() { + assert(lte_unsafe(0, 1, 16)); + assert(lte_unsafe(0, 0x100, 16)); + assert(lte_unsafe(0x100, TWO_POW_128 - 1, 16)); + assert(!lte_unsafe(0, TWO_POW_128, 16)); + + assert(lte_unsafe(0, 0, 16)); + assert(lte_unsafe(0x100, 0x100, 16)); + assert(lte_unsafe(TWO_POW_128 - 1, TWO_POW_128 - 1, 16)); + assert(lte_unsafe(TWO_POW_128, TWO_POW_128, 16)); + } + + #[test] + fn check_assert_gt() { + assert_gt(1, 0); + assert_gt(0x100, 0); + assert_gt((0 - 1), (0 - 2)); + assert_gt(TWO_POW_128, 0); + assert_gt(0 - 1, 0); + } + + #[test] + fn check_gt() { + assert(gt(1, 0)); + assert(gt(0x100, 0)); + assert(gt((0 - 1), (0 - 2))); + assert(gt(TWO_POW_128, 0)); + assert(!gt(0, 0)); + assert(!gt(0, 0x100)); + assert(gt(0 - 1, 0 - 2)); + assert(!gt(0 - 2, 0 - 1)); + } + + #[test] + fn check_plo_phi() { + assert_eq(PLO + PHI * TWO_POW_128, 0); + let p_bytes = crate::field::modulus_le_bytes(); + let mut p_low: Field = 0; + let mut p_high: Field = 0; + + let mut offset = 1; + for i in 0..16 { + p_low += (p_bytes[i] as Field) * offset; + p_high += (p_bytes[i + 16] as Field) * offset; + offset *= 256; + } + assert_eq(p_low, PLO); + assert_eq(p_high, PHI); + } +} diff --git a/test_programs/compile_success_empty/field_comparisons/Nargo.toml b/test_programs/compile_success_empty/field_comparisons/Nargo.toml deleted file mode 100644 index e8b06655c58..00000000000 --- a/test_programs/compile_success_empty/field_comparisons/Nargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "field_comparisons" -type = "bin" -authors = [""] - -[dependencies] diff --git a/test_programs/compile_success_empty/field_comparisons/Prover.toml b/test_programs/compile_success_empty/field_comparisons/Prover.toml deleted file mode 100644 index 8b137891791..00000000000 --- a/test_programs/compile_success_empty/field_comparisons/Prover.toml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/test_programs/compile_success_empty/field_comparisons/src/main.nr b/test_programs/compile_success_empty/field_comparisons/src/main.nr deleted file mode 100644 index 48cca6c89fc..00000000000 --- a/test_programs/compile_success_empty/field_comparisons/src/main.nr +++ /dev/null @@ -1,86 +0,0 @@ -use dep::std::field::bn254::{PLO, PHI, TWO_POW_128, decompose, decompose_unsafe, lt_unsafe, lte_unsafe, assert_gt, gt}; - -fn check_plo_phi() { - assert_eq(PLO + PHI * TWO_POW_128, 0); - let p_bytes = dep::std::field::modulus_le_bytes(); - let mut p_low: Field = 0; - let mut p_high: Field = 0; - - let mut offset = 1; - for i in 0..16 { - p_low += (p_bytes[i] as Field) * offset; - p_high += (p_bytes[i + 16] as Field) * offset; - offset *= 256; - } - assert_eq(p_low, PLO); - assert_eq(p_high, PHI); -} - -fn check_decompose_unsafe() { - assert_eq(decompose_unsafe(TWO_POW_128), (0, 1)); - assert_eq(decompose_unsafe(TWO_POW_128 + 0x1234567890), (0x1234567890, 1)); - assert_eq(decompose_unsafe(0x1234567890), (0x1234567890, 0)); -} - -fn check_decompose() { - assert_eq(decompose(TWO_POW_128), (0, 1)); - assert_eq(decompose(TWO_POW_128 + 0x1234567890), (0x1234567890, 1)); - assert_eq(decompose(0x1234567890), (0x1234567890, 0)); -} - -fn check_lt_unsafe() { - assert(lt_unsafe(0, 1, 16)); - assert(lt_unsafe(0, 0x100, 16)); - assert(lt_unsafe(0x100, TWO_POW_128 - 1, 16)); - assert(!lt_unsafe(0, TWO_POW_128, 16)); -} - -fn check_lte_unsafe() { - assert(lte_unsafe(0, 1, 16)); - assert(lte_unsafe(0, 0x100, 16)); - assert(lte_unsafe(0x100, TWO_POW_128 - 1, 16)); - assert(!lte_unsafe(0, TWO_POW_128, 16)); - - assert(lte_unsafe(0, 0, 16)); - assert(lte_unsafe(0x100, 0x100, 16)); - assert(lte_unsafe(TWO_POW_128 - 1, TWO_POW_128 - 1, 16)); - assert(lte_unsafe(TWO_POW_128, TWO_POW_128, 16)); -} - -fn check_assert_gt() { - assert_gt(1, 0); - assert_gt(0x100, 0); - assert_gt((0 - 1), (0 - 2)); - assert_gt(TWO_POW_128, 0); - assert_gt(0 - 1, 0); -} - -fn check_gt() { - assert(gt(1, 0)); - assert(gt(0x100, 0)); - assert(gt((0 - 1), (0 - 2))); - assert(gt(TWO_POW_128, 0)); - assert(!gt(0, 0)); - assert(!gt(0, 0x100)); - assert(gt(0 - 1, 0 - 2)); - assert(!gt(0 - 2, 0 - 1)); -} - -fn checks() { - check_plo_phi(); - check_decompose_unsafe(); - check_decompose(); - check_lt_unsafe(); - check_lte_unsafe(); - check_assert_gt(); - check_gt(); -} - -unconstrained fn checks_in_brillig() { - checks(); -} - -fn main() { - checks(); - checks_in_brillig(); -} diff --git a/tooling/nargo_cli/tests/stdlib-tests.rs b/tooling/nargo_cli/tests/stdlib-tests.rs new file mode 100644 index 00000000000..9d377cfaee9 --- /dev/null +++ b/tooling/nargo_cli/tests/stdlib-tests.rs @@ -0,0 +1,62 @@ +use std::{collections::BTreeMap, path::PathBuf}; + +use acvm::blackbox_solver::StubbedBlackBoxSolver; +use noirc_driver::{check_crate, file_manager_with_stdlib, CompileOptions}; +use noirc_frontend::hir::FunctionNameMatch; + +use nargo::{ + ops::{report_errors, run_test, TestStatus}, + package::{Package, PackageType}, + parse_all, prepare_package, +}; + +#[test] +fn stdlib_noir_tests() { + let mut file_manager = file_manager_with_stdlib(&PathBuf::from(".")); + file_manager.add_file_with_source_canonical_path(&PathBuf::from("main.nr"), "".to_owned()); + let parsed_files = parse_all(&file_manager); + + // We need a dummy package as we cannot compile the stdlib on its own. + let dummy_package = Package { + version: None, + compiler_required_version: None, + root_dir: PathBuf::from("."), + package_type: PackageType::Binary, + entry_path: PathBuf::from("main.nr"), + name: "dummy".parse().unwrap(), + dependencies: BTreeMap::new(), + }; + + let (mut context, dummy_crate_id) = + prepare_package(&file_manager, &parsed_files, &dummy_package); + + let result = check_crate(&mut context, dummy_crate_id, true, false); + report_errors(result, &context.file_manager, true, false) + .expect("Error encountered while compiling standard library"); + + // We can now search within the stdlib for any test functions to compile. + + let test_functions = context.get_all_test_functions_in_crate_matching( + context.stdlib_crate_id(), + FunctionNameMatch::Anything, + ); + + let test_report: Vec<(String, TestStatus)> = test_functions + .into_iter() + .map(|(test_name, test_function)| { + let status = run_test( + &StubbedBlackBoxSolver, + &mut context, + &test_function, + false, + None, + &CompileOptions::default(), + ); + + (test_name, status) + }) + .collect(); + + assert!(!test_report.is_empty(), "Could not find any tests within the stdlib"); + assert!(test_report.iter().all(|(_, status)| !status.failed())); +} From 4c3ac2958fcaaa90eddb60e92cd30eac50b5d2b5 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:20:53 +0000 Subject: [PATCH 086/416] chore: fix docker test workflows (#4566) # Description ## Problem\* Resolves ## Summary\* Docker tests have been broken for a while. This PR builds the types package so that the `noir_wasm` package can build. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/scripts/noir-wasm-build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/scripts/noir-wasm-build.sh b/.github/scripts/noir-wasm-build.sh index f799387b6f6..48e3ad73769 100755 --- a/.github/scripts/noir-wasm-build.sh +++ b/.github/scripts/noir-wasm-build.sh @@ -2,4 +2,5 @@ set -eu .github/scripts/wasm-pack-install.sh +yarn workspace @noir-lang/types build yarn workspace @noir-lang/noir_wasm build From 414194cdbeb65096c25152084d25ab79d80d2f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Pedro=20Sousa?= Date: Fri, 15 Mar 2024 18:54:04 +0000 Subject: [PATCH 087/416] chore: making docs build before cutting versions (#4568) # Description Release was broken because the CI doesn't build docs before cutting new versions. Instead of changing the CI I'm adding docs build to the `yarn version` command. I'm also adding the missing 0.25.0 docs (took a snapshot at tag 0.25.0) --- docs/package.json | 2 +- .../NoirJS/backend_barretenberg/.nojekyll | 1 + .../classes/BarretenbergBackend.md | 127 +++++++++++++++++ .../NoirJS/backend_barretenberg/index.md | 46 ++++++ .../interfaces/Backend.md | 132 ++++++++++++++++++ .../type-aliases/BackendOptions.md | 21 +++ .../type-aliases/CompiledCircuit.md | 20 +++ .../type-aliases/ProofData.md | 20 +++ .../backend_barretenberg/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 132 ++++++++++++++++++ .../reference/NoirJS/noir_js/functions/and.md | 22 +++ .../NoirJS/noir_js/functions/blake2s256.md | 21 +++ .../functions/ecdsa_secp256k1_verify.md | 28 ++++ .../functions/ecdsa_secp256r1_verify.md | 28 ++++ .../NoirJS/noir_js/functions/keccak256.md | 21 +++ .../NoirJS/noir_js/functions/sha256.md | 21 +++ .../reference/NoirJS/noir_js/functions/xor.md | 22 +++ .../reference/NoirJS/noir_js/index.md | 37 +++++ .../noir_js/type-aliases/CompiledCircuit.md | 20 +++ .../type-aliases/ForeignCallHandler.md | 24 ++++ .../noir_js/type-aliases/ForeignCallInput.md | 9 ++ .../noir_js/type-aliases/ForeignCallOutput.md | 9 ++ .../NoirJS/noir_js/type-aliases/InputMap.md | 13 ++ .../NoirJS/noir_js/type-aliases/ProofData.md | 20 +++ .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 ++ .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 +++++++ .../noir_wasm/functions/compile_contract.md | 51 +++++++ .../noir_wasm/functions/createFileManager.md | 21 +++ .../functions/inflateDebugSymbols.md | 21 +++ .../reference/NoirJS/noir_wasm/index.md | 49 +++++++ .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + 34 files changed, 1011 insertions(+), 1 deletion(-) create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/.nojekyll create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/.nojekyll create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/and.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/keccak256.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/sha256.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/xor.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/InputMap.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ProofData.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/index.md create mode 100644 docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs diff --git a/docs/package.json b/docs/package.json index 146c2a9800c..779b72149b1 100644 --- a/docs/package.json +++ b/docs/package.json @@ -8,7 +8,7 @@ "build": "yarn preprocess && yarn version::stables && docusaurus build", "version::stables": "ts-node ./scripts/setStable.ts", "serve": "serve build", - "version": "yarn preprocess && docusaurus docs:version" + "version": "yarn preprocess && docusaurus build && docusaurus docs:version" }, "dependencies": { "@docusaurus/core": "^3.0.1", diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..d60940df3ea --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,127 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../interfaces/Backend.md) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Implementation of + +[`Backend`](../interfaces/Backend.md).[`destroy`](../interfaces/Backend.md#destroy) + +#### Description + +Destroys the backend + +*** + +### generateProof() + +```ts +generateProof(compressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `compressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a proof + +*** + +### generateRecursiveProofArtifacts() + +```ts +generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +Generates artifacts that will be passed to a circuit that will verify this proof. + +Instead of passing the proof and verification key as a byte array, we pass them +as fields which makes it cheaper to verify in a circuit. + +The proof that is passed here will have been created using a circuit +that has the #[recursive] attribute on its `main` method. + +The number of public inputs denotes how many public inputs are in the inner proof. + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Example + +```typescript +const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/index.md new file mode 100644 index 00000000000..e32501acb71 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/index.md @@ -0,0 +1,46 @@ +# backend_barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Interfaces + +| Interface | Description | +| :------ | :------ | +| [Backend](interfaces/Backend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | + +## Functions + +### publicInputsToWitnessMap() + +```ts +publicInputsToWitnessMap(publicInputs, abi): WitnessMap +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `publicInputs` | `string`[] | +| `abi` | `Abi` | + +#### Returns + +`WitnessMap` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md new file mode 100644 index 00000000000..3eb9645c8d2 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/interfaces/Backend.md @@ -0,0 +1,132 @@ +# Backend + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the backend + +*** + +### generateFinalProof() + +```ts +generateFinalProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a final proof (not meant to be verified in another circuit) + +*** + +### generateIntermediateProof() + +```ts +generateIntermediateProof(decompressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `decompressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates an intermediate proof (meant to be verified in another circuit) + +*** + +### generateIntermediateProofArtifacts() + +```ts +generateIntermediateProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | +| `numOfPublicInputs` | `number` | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Retrieves the artifacts from a proof in the Field format + +*** + +### verifyFinalProof() + +```ts +verifyFinalProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a final proof + +*** + +### verifyIntermediateProof() + +```ts +verifyIntermediateProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies an intermediate proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..b49a479f4f4 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,21 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `memory` | `object` | - | +| `memory.maximum` | `number` | - | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md new file mode 100644 index 00000000000..05cebbc4e94 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..2aaa55bccf6 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Interfaces","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/interfaces/Backend","label":"Backend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/ProofData","label":"ProofData"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 00000000000..421d274fff8 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,132 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | [`CompiledCircuit`](../type-aliases/CompiledCircuit.md) | +| `backend`? | `Backend` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateProof() + +```ts +generateProof(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | [`InputMap`](../type-aliases/InputMap.md) | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<[`ProofData`](../type-aliases/ProofData.md)\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateProof(input) +``` + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | [`ProofData`](../type-aliases/ProofData.md) | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..5e3cd53e9d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/index.md new file mode 100644 index 00000000000..d600e21b299 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/index.md @@ -0,0 +1,37 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [CompiledCircuit](type-aliases/CompiledCircuit.md) | - | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [InputMap](type-aliases/InputMap.md) | - | +| [ProofData](type-aliases/ProofData.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md new file mode 100644 index 00000000000..34e0dd04205 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/CompiledCircuit.md @@ -0,0 +1,20 @@ +# CompiledCircuit + +```ts +type CompiledCircuit: object; +``` + +## Description + +The representation of a compiled circuit + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `abi` | `Abi` | **Description**

ABI representation of the circuit | +| `bytecode` | `string` | **Description**

The bytecode of the circuit | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/InputMap.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/InputMap.md new file mode 100644 index 00000000000..c714e999d93 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/InputMap.md @@ -0,0 +1,13 @@ +# InputMap + +```ts +type InputMap: object; +``` + +## Index signature + + \[`key`: `string`\]: `InputValue` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ProofData.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ProofData.md new file mode 100644 index 00000000000..05cebbc4e94 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/ProofData.md @@ -0,0 +1,20 @@ +# ProofData + +```ts +type ProofData: object; +``` + +## Description + +The representation of a proof + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `proof` | `Uint8Array` | **Description**

An byte array representing the proof | +| `publicInputs` | `string`[] | **Description**

Public inputs of a proof | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..fe2629ddc9f --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/CompiledCircuit","label":"CompiledCircuit"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/InputMap","label":"InputMap"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ProofData","label":"ProofData"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 00000000000..6faf763b37f --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 00000000000..7d0b39a43ef --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 00000000000..7e65c1d69c7 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 00000000000..fcea9275341 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 00000000000..b6e0f9d1bc0 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 00000000000..e0870710349 --- /dev/null +++ b/docs/versioned_docs/version-v0.25.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file From 6a9ea35c4f1578058179aa08eedf44eb18bad4a1 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Fri, 15 Mar 2024 16:35:37 -0400 Subject: [PATCH 088/416] feat: add as_slice builtin function, add execution test (#4523) # Description ## Problem\* Expected to resolve slowdown in https://github.com/noir-lang/noir/pull/4504/ ## Summary\* Adds builtin function for `as_slice`, allowing it to be much more efficient (`O(1)` instead of `O(n)`) ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: vezenovm --- .../src/brillig/brillig_gen/brillig_block.rs | 63 +++++++++++++------ .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 54 +++++++++++++++- .../noirc_evaluator/src/ssa/ir/instruction.rs | 4 ++ .../src/ssa/ir/instruction/call.rs | 10 +++ noir_stdlib/src/array.nr | 10 +-- .../array_to_slice/Nargo.toml | 7 +++ .../array_to_slice/Prover.toml | 2 + .../array_to_slice/src/main.nr | 33 ++++++++++ .../brillig_array_to_slice/Nargo.toml | 7 +++ .../brillig_array_to_slice/Prover.toml | 1 + .../brillig_array_to_slice/src/main.nr | 18 ++++++ 11 files changed, 181 insertions(+), 28 deletions(-) create mode 100644 test_programs/execution_success/array_to_slice/Nargo.toml create mode 100644 test_programs/execution_success/array_to_slice/Prover.toml create mode 100644 test_programs/execution_success/array_to_slice/src/main.nr create mode 100644 test_programs/execution_success/brillig_array_to_slice/Nargo.toml create mode 100644 test_programs/execution_success/brillig_array_to_slice/Prover.toml create mode 100644 test_programs/execution_success/brillig_array_to_slice/src/main.nr diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 938a80e87d1..99400b13528 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -453,6 +453,29 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_array_len(arguments[0], result_variable.address, dfg); } } + Value::Intrinsic(Intrinsic::AsSlice) => { + let source_variable = self.convert_ssa_value(arguments[0], dfg); + let result_ids = dfg.instruction_results(instruction_id); + let destination_len_variable = self.variables.define_single_addr_variable( + self.function_context, + self.brillig_context, + result_ids[0], + dfg, + ); + let destination_variable = self.variables.define_variable( + self.function_context, + self.brillig_context, + result_ids[1], + dfg, + ); + let source_size_as_register = + self.convert_ssa_array_set(source_variable, destination_variable, None); + + // we need to explicitly set the destination_len_variable + self.brillig_context + .mov_instruction(destination_len_variable.address, source_size_as_register); + self.brillig_context.deallocate_register(source_size_as_register); + } Value::Intrinsic( Intrinsic::SlicePushBack | Intrinsic::SlicePopBack @@ -612,13 +635,12 @@ impl<'block> BrilligBlock<'block> { dfg, ); self.validate_array_index(source_variable, index_register); - - self.convert_ssa_array_set( + let source_size_as_register = self.convert_ssa_array_set( source_variable, destination_variable, - index_register.address, - value_variable, + Some((index_register.address, value_variable)), ); + self.brillig_context.deallocate_register(source_size_as_register); } Instruction::RangeCheck { value, max_bit_size, assert_message } => { let value = self.convert_ssa_single_addr_value(*value, dfg); @@ -806,23 +828,25 @@ impl<'block> BrilligBlock<'block> { /// Array set operation in SSA returns a new array or slice that is a copy of the parameter array or slice /// With a specific value changed. + /// + /// Returns `source_size_as_register`, which is expected to be deallocated with: + /// `self.brillig_context.deallocate_register(source_size_as_register)` fn convert_ssa_array_set( &mut self, source_variable: BrilligVariable, destination_variable: BrilligVariable, - index_register: MemoryAddress, - value_variable: BrilligVariable, - ) { + opt_index_and_value: Option<(MemoryAddress, BrilligVariable)>, + ) -> MemoryAddress { let destination_pointer = match destination_variable { BrilligVariable::BrilligArray(BrilligArray { pointer, .. }) => pointer, BrilligVariable::BrilligVector(BrilligVector { pointer, .. }) => pointer, - _ => unreachable!("ICE: array set returns non-array"), + _ => unreachable!("ICE: array_set SSA returns non-array"), }; let reference_count = match source_variable { BrilligVariable::BrilligArray(BrilligArray { rc, .. }) | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, - _ => unreachable!("ICE: array set on non-array"), + _ => unreachable!("ICE: array_set SSA on non-array"), }; let (source_pointer, source_size_as_register) = match source_variable { @@ -836,7 +860,7 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.mov_instruction(source_size_register, size); (pointer, source_size_register) } - _ => unreachable!("ICE: array set on non-array"), + _ => unreachable!("ICE: array_set SSA on non-array"), }; // Here we want to compare the reference count against 1. @@ -879,18 +903,20 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.mov_instruction(target_size, source_size_as_register); self.brillig_context.usize_const(target_rc, 1_usize.into()); } - _ => unreachable!("ICE: array set on non-array"), + _ => unreachable!("ICE: array_set SSA on non-array"), } - // Then set the value in the newly created array - self.store_variable_in_array( - destination_pointer, - SingleAddrVariable::new_usize(index_register), - value_variable, - ); + if let Some((index_register, value_variable)) = opt_index_and_value { + // Then set the value in the newly created array + self.store_variable_in_array( + destination_pointer, + SingleAddrVariable::new_usize(index_register), + value_variable, + ); + } - self.brillig_context.deallocate_register(source_size_as_register); self.brillig_context.deallocate_register(condition); + source_size_as_register } pub(crate) fn store_variable_in_array_with_ctx( @@ -1319,6 +1345,7 @@ impl<'block> BrilligBlock<'block> { Value::Param { .. } | Value::Instruction { .. } => { // All block parameters and instruction results should have already been // converted to registers so we fetch from the cache. + self.variables.get_allocation(self.function_context, value_id, dfg) } Value::NumericConstant { constant, .. } => { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 4442efe286a..5a4fa021f1f 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1025,7 +1025,13 @@ impl Context { self.array_set_value(&store_value, result_block_id, &mut var_index)?; let element_type_sizes = if !can_omit_element_sizes_array(&array_typ) { - Some(self.init_element_type_sizes_array(&array_typ, array_id, None, dfg)?) + let acir_value = self.convert_value(array_id, dfg); + Some(self.init_element_type_sizes_array( + &array_typ, + array_id, + Some(&acir_value), + dfg, + )?) } else { None }; @@ -1246,7 +1252,8 @@ impl Context { let read = self.acir_context.read_from_memory(source, &index_var)?; Ok::(AcirValue::Var(read, AcirType::field())) })?; - self.initialize_array(destination, array_len, Some(AcirValue::Array(init_values.into())))?; + let array: im::Vector = init_values.into(); + self.initialize_array(destination, array_len, Some(AcirValue::Array(array)))?; Ok(()) } @@ -1663,6 +1670,49 @@ impl Context { }; Ok(vec![AcirValue::Var(self.acir_context.add_constant(len), AcirType::field())]) } + Intrinsic::AsSlice => { + let (slice_contents, slice_typ, block_id) = + self.check_array_is_initialized(arguments[0], dfg)?; + assert!(!slice_typ.is_nested_slice(), "ICE: Nested slice used in ACIR generation"); + + let result_block_id = self.block_id(&result_ids[1]); + let acir_value = self.convert_value(slice_contents, dfg); + + let array_len = if !slice_typ.contains_slice_element() { + slice_typ.flattened_size() + } else { + self.flattened_slice_size(slice_contents, dfg) + }; + let slice_length = self.acir_context.add_constant(array_len); + self.copy_dynamic_array(block_id, result_block_id, array_len)?; + + let element_type_sizes = if !can_omit_element_sizes_array(&slice_typ) { + Some(self.init_element_type_sizes_array( + &slice_typ, + slice_contents, + Some(&acir_value), + dfg, + )?) + } else { + None + }; + + let value_types = self.convert_value(slice_contents, dfg).flat_numeric_types(); + assert!( + array_len == value_types.len(), + "AsSlice: unexpected length difference: {:?} != {:?}", + array_len, + value_types.len() + ); + + let result = AcirValue::DynamicArray(AcirDynamicArray { + block_id: result_block_id, + len: value_types.len(), + value_types, + element_type_sizes, + }); + Ok(vec![AcirValue::Var(slice_length, AcirType::field()), result]) + } Intrinsic::SlicePushBack => { // arguments = [slice_length, slice_contents, ...elements_to_push] let slice_length = self.convert_value(arguments[0], dfg).into_var()?; diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index afade4b0616..dd190c112f3 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -37,6 +37,7 @@ pub(crate) type InstructionId = Id; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum Intrinsic { ArrayLen, + AsSlice, AssertConstant, SlicePushBack, SlicePushFront, @@ -57,6 +58,7 @@ impl std::fmt::Display for Intrinsic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Intrinsic::ArrayLen => write!(f, "array_len"), + Intrinsic::AsSlice => write!(f, "as_slice"), Intrinsic::AssertConstant => write!(f, "assert_constant"), Intrinsic::SlicePushBack => write!(f, "slice_push_back"), Intrinsic::SlicePushFront => write!(f, "slice_push_front"), @@ -89,6 +91,7 @@ impl Intrinsic { Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => true, Intrinsic::ArrayLen + | Intrinsic::AsSlice | Intrinsic::SlicePushBack | Intrinsic::SlicePushFront | Intrinsic::SlicePopBack @@ -109,6 +112,7 @@ impl Intrinsic { pub(crate) fn lookup(name: &str) -> Option { match name { "array_len" => Some(Intrinsic::ArrayLen), + "as_slice" => Some(Intrinsic::AsSlice), "assert_constant" => Some(Intrinsic::AssertConstant), "apply_range_constraint" => Some(Intrinsic::ApplyRangeConstraint), "slice_push_back" => Some(Intrinsic::SlicePushBack), diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 9349d58c4d9..8b800e0db54 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -84,6 +84,16 @@ pub(super) fn simplify_call( SimplifyResult::None } } + Intrinsic::AsSlice => { + let slice = dfg.get_array_constant(arguments[0]); + if let Some((slice, element_type)) = slice { + let slice_length = dfg.make_constant(slice.len().into(), Type::length_type()); + let new_slice = dfg.make_array(slice, element_type); + SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) + } else { + SimplifyResult::None + } + } Intrinsic::SlicePushBack => { let slice = dfg.get_array_constant(arguments[1]); if let Some((mut slice, element_type)) = slice { diff --git a/noir_stdlib/src/array.nr b/noir_stdlib/src/array.nr index baa4bef50cc..8a8a1fad01c 100644 --- a/noir_stdlib/src/array.nr +++ b/noir_stdlib/src/array.nr @@ -52,14 +52,8 @@ impl [T; N] { result } - // Converts an array into a slice. - pub fn as_slice(self) -> [T] { - let mut slice = []; - for elem in self { - slice = slice.push_back(elem); - } - slice - } + #[builtin(as_slice)] + pub fn as_slice(self) -> [T] {} // Apply a function to each element of an array, returning a new array // containing the mapped elements. diff --git a/test_programs/execution_success/array_to_slice/Nargo.toml b/test_programs/execution_success/array_to_slice/Nargo.toml new file mode 100644 index 00000000000..90c67b07b2b --- /dev/null +++ b/test_programs/execution_success/array_to_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "array_to_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.24.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/array_to_slice/Prover.toml b/test_programs/execution_success/array_to_slice/Prover.toml new file mode 100644 index 00000000000..26fdbc19975 --- /dev/null +++ b/test_programs/execution_success/array_to_slice/Prover.toml @@ -0,0 +1,2 @@ +x = "0" +y = "1" diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr new file mode 100644 index 00000000000..4f5594c6d11 --- /dev/null +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -0,0 +1,33 @@ +// Converts an array into a slice. +fn as_slice_push(xs: [T; N]) -> [T] { + let mut slice = []; + for elem in xs { + slice = slice.push_back(elem); + } + slice +} + +fn main(x: Field, y: pub Field) { + let xs: [Field; 0] = []; + let ys: [Field; 1] = [1]; + let zs: [Field; 2] = [1, 2]; + let ws: [Field; 3] = [1; 3]; + let qs: [Field; 4] = [3, 2, 1, 0]; + + let mut dynamic: [Field; 4] = [3, 2, 1, 0]; + let dynamic_expected: [Field; 4] = [1000, 2, 1, 0]; + dynamic[x] = 1000; + + assert(x != y); + assert(xs.as_slice() == as_slice_push(xs)); + assert(ys.as_slice() == as_slice_push(ys)); + assert(zs.as_slice() == as_slice_push(zs)); + assert(ws.as_slice() == as_slice_push(ws)); + assert(qs.as_slice() == as_slice_push(qs)); + + assert(dynamic.as_slice()[0] == dynamic_expected[0]); + assert(dynamic.as_slice()[1] == dynamic_expected[1]); + assert(dynamic.as_slice()[2] == dynamic_expected[2]); + assert(dynamic.as_slice()[3] == dynamic_expected[3]); + assert(dynamic.as_slice().len() == 4); +} diff --git a/test_programs/execution_success/brillig_array_to_slice/Nargo.toml b/test_programs/execution_success/brillig_array_to_slice/Nargo.toml new file mode 100644 index 00000000000..58157c38c26 --- /dev/null +++ b/test_programs/execution_success/brillig_array_to_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "brillig_array_to_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.25.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/brillig_array_to_slice/Prover.toml b/test_programs/execution_success/brillig_array_to_slice/Prover.toml new file mode 100644 index 00000000000..11497a473bc --- /dev/null +++ b/test_programs/execution_success/brillig_array_to_slice/Prover.toml @@ -0,0 +1 @@ +x = "0" diff --git a/test_programs/execution_success/brillig_array_to_slice/src/main.nr b/test_programs/execution_success/brillig_array_to_slice/src/main.nr new file mode 100644 index 00000000000..8f7fcf24bae --- /dev/null +++ b/test_programs/execution_success/brillig_array_to_slice/src/main.nr @@ -0,0 +1,18 @@ +unconstrained fn brillig_as_slice(x: Field) -> (u64, Field, Field) { + let mut dynamic: [Field; 1] = [1]; + dynamic[x] = 2; + assert(dynamic[0] == 2); + + let brillig_slice = dynamic.as_slice(); + assert(brillig_slice.len() == 1); + + (brillig_slice.len(), dynamic[0], brillig_slice[0]) +} + +fn main(x: Field) { + let (slice_len, dynamic_0, slice_0) = brillig_as_slice(x); + assert(slice_len == 1); + assert(dynamic_0 == 2); + assert(slice_0 == 2); +} + From dfa5126f2c65843c34701cacddf2cbcfb0d7ff11 Mon Sep 17 00:00:00 2001 From: jfecher Date: Mon, 18 Mar 2024 13:44:03 -0500 Subject: [PATCH 089/416] feat: RC optimization pass (#4560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* `inc_rc` and `dec_rc` instructions can bloat unconstrained code with unneeded rc changes on otherwise immutable arrays. ## Summary\* Adds an optimization pass to remove `inc_rc vN .. dec_rc vN` pairs as long as there are not `array_set` instructions in the same function which may mutate an array of the same type. ## Additional Context I thought of tracking all inc and dec instructions in the function originally but eventually limited it to finding just those in the function's entry block and exit block respectively. The later is the only place we currently issue dec_rc instructions anyway. This restriction greatly simplifies the code since we do not have to merge intermediate results across several blocks, nor do we have to handle inc/dec in loops. This pass applies to both acir and brillig functions since acir functions can still be called in an unconstrained context. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Álvaro Rodríguez --- compiler/noirc_evaluator/src/ssa.rs | 6 +- .../src/ssa/function_builder/mod.rs | 73 ++-- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 1 + compiler/noirc_evaluator/src/ssa/opt/rc.rs | 327 ++++++++++++++++++ .../src/monomorphization/debug.rs | 4 +- 5 files changed, 379 insertions(+), 32 deletions(-) create mode 100644 compiler/noirc_evaluator/src/ssa/opt/rc.rs diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 56cb76adbe4..808cf7533c9 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -48,6 +48,7 @@ pub(crate) fn optimize_into_acir( let ssa_gen_span_guard = ssa_gen_span.enter(); let ssa = SsaBuilder::new(program, print_ssa_passes, force_brillig_output)? .run_pass(Ssa::defunctionalize, "After Defunctionalization:") + .run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:") .run_pass(Ssa::inline_functions, "After Inlining:") // Run mem2reg with the CFG separated into blocks .run_pass(Ssa::mem2reg, "After Mem2Reg:") @@ -59,10 +60,7 @@ pub(crate) fn optimize_into_acir( // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") - .run_pass( - Ssa::fold_constants_using_constraints, - "After Constant Folding With Constraint Info:", - ) + .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") .finish(); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 2c39c83b342..aa5a7fedd92 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -195,12 +195,9 @@ impl FunctionBuilder { self.call_stack.clone() } - /// Insert a Load instruction at the end of the current block, loading from the given offset - /// of the given address which should point to a previous Allocate instruction. Note that - /// this is limited to loading a single value. Loading multiple values (such as a tuple) - /// will require multiple loads. - /// 'offset' is in units of FieldElements here. So loading the fourth FieldElement stored in - /// an array will have an offset of 3. + /// Insert a Load instruction at the end of the current block, loading from the given address + /// which should point to a previous Allocate instruction. Note that this is limited to loading + /// a single value. Loading multiple values (such as a tuple) will require multiple loads. /// Returns the element that was loaded. pub(crate) fn insert_load(&mut self, address: ValueId, type_to_load: Type) -> ValueId { self.insert_instruction(Instruction::Load { address }, Some(vec![type_to_load])).first() @@ -221,11 +218,9 @@ impl FunctionBuilder { operator: BinaryOp, rhs: ValueId, ) -> ValueId { - assert_eq!( - self.type_of_value(lhs), - self.type_of_value(rhs), - "ICE - Binary instruction operands must have the same type" - ); + let lhs_type = self.type_of_value(lhs); + let rhs_type = self.type_of_value(rhs); + assert_eq!(lhs_type, rhs_type, "ICE - Binary instruction operands must have the same type"); let instruction = Instruction::Binary(Binary { lhs, rhs, operator }); self.insert_instruction(instruction, None).first() } @@ -309,6 +304,18 @@ impl FunctionBuilder { self.insert_instruction(Instruction::ArraySet { array, index, value }, None).first() } + /// Insert an instruction to increment an array's reference count. This only has an effect + /// in unconstrained code where arrays are reference counted and copy on write. + pub(crate) fn insert_inc_rc(&mut self, value: ValueId) { + self.insert_instruction(Instruction::IncrementRc { value }, None); + } + + /// Insert an instruction to decrement an array's reference count. This only has an effect + /// in unconstrained code where arrays are reference counted and copy on write. + pub(crate) fn insert_dec_rc(&mut self, value: ValueId) { + self.insert_instruction(Instruction::DecrementRc { value }, None); + } + /// Terminates the current block with the given terminator instruction fn terminate_block_with(&mut self, terminator: TerminatorInstruction) { self.current_function.dfg.set_block_terminator(self.current_block, terminator); @@ -384,51 +391,65 @@ impl FunctionBuilder { /// within the given value. If the given value is not an array and does not contain /// any arrays, this does nothing. pub(crate) fn increment_array_reference_count(&mut self, value: ValueId) { - self.update_array_reference_count(value, true); + self.update_array_reference_count(value, true, None); } /// Insert instructions to decrement the reference count of any array(s) stored /// within the given value. If the given value is not an array and does not contain /// any arrays, this does nothing. pub(crate) fn decrement_array_reference_count(&mut self, value: ValueId) { - self.update_array_reference_count(value, false); + self.update_array_reference_count(value, false, None); } /// Increment or decrement the given value's reference count if it is an array. /// If it is not an array, this does nothing. Note that inc_rc and dec_rc instructions /// are ignored outside of unconstrained code. - pub(crate) fn update_array_reference_count(&mut self, value: ValueId, increment: bool) { + fn update_array_reference_count( + &mut self, + value: ValueId, + increment: bool, + load_address: Option, + ) { match self.type_of_value(value) { Type::Numeric(_) => (), Type::Function => (), Type::Reference(element) => { if element.contains_an_array() { - let value = self.insert_load(value, element.as_ref().clone()); - self.increment_array_reference_count(value); + let reference = value; + let value = self.insert_load(reference, element.as_ref().clone()); + self.update_array_reference_count(value, increment, Some(reference)); } } typ @ Type::Array(..) | typ @ Type::Slice(..) => { // If there are nested arrays or slices, we wait until ArrayGet // is issued to increment the count of that array. - let instruction = if increment { - Instruction::IncrementRc { value } - } else { - Instruction::DecrementRc { value } + let update_rc = |this: &mut Self, value| { + if increment { + this.insert_inc_rc(value); + } else { + this.insert_dec_rc(value); + } }; - self.insert_instruction(instruction, None); + + update_rc(self, value); + let dfg = &self.current_function.dfg; // This is a bit odd, but in brillig the inc_rc instruction operates on // a copy of the array's metadata, so we need to re-store a loaded array // even if there have been no other changes to it. - if let Value::Instruction { instruction, .. } = &self.current_function.dfg[value] { - let instruction = &self.current_function.dfg[*instruction]; + if let Some(address) = load_address { + // If we already have a load from the Type::Reference case, avoid inserting + // another load and rc update. + self.insert_store(address, value); + } else if let Value::Instruction { instruction, .. } = &dfg[value] { + let instruction = &dfg[*instruction]; if let Instruction::Load { address } = instruction { // We can't re-use `value` in case the original address was stored // to again in the meantime. So introduce another load. let address = *address; - let value = self.insert_load(address, typ); - self.insert_instruction(Instruction::IncrementRc { value }, None); - self.insert_store(address, value); + let new_load = self.insert_load(address, typ); + update_rc(self, new_load); + self.insert_store(address, new_load); } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index a315695f7db..8f98b3fb17f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -12,6 +12,7 @@ mod die; pub(crate) mod flatten_cfg; mod inlining; mod mem2reg; +mod rc; mod remove_bit_shifts; mod simplify_cfg; mod unrolling; diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs new file mode 100644 index 00000000000..4766bc3e8d2 --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -0,0 +1,327 @@ +use std::collections::{HashMap, HashSet}; + +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + function::Function, + instruction::{Instruction, InstructionId, TerminatorInstruction}, + types::Type, + value::ValueId, + }, + ssa_gen::Ssa, +}; + +impl Ssa { + /// This pass removes `inc_rc` and `dec_rc` instructions + /// as long as there are no `array_set` instructions to an array + /// of the same type in between. + /// + /// Note that this pass is very conservative since the array_set + /// instruction does not need to be to the same array. This is because + /// the given array may alias another array (e.g. function parameters or + /// a `load`ed array from a reference). + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn remove_paired_rc(mut self) -> Ssa { + for function in self.functions.values_mut() { + remove_paired_rc(function); + } + self + } +} + +#[derive(Default)] +struct Context { + // All inc_rc instructions encountered without a corresponding dec_rc. + // These are only searched for in the first block of a function. + // + // The type of the array being operated on is recorded. + // If an array_set to that array type is encountered, that is also recorded. + inc_rcs: HashMap>, +} + +struct IncRc { + id: InstructionId, + array: ValueId, + possibly_mutated: bool, +} + +/// This function is very simplistic for now. It takes advantage of the fact that dec_rc +/// instructions are currently issued only at the end of a function for parameters and will +/// only check the first and last block for inc & dec rc instructions to be removed. The rest +/// of the function is still checked for array_set instructions. +/// +/// This restriction lets this function largely ignore merging intermediate results from other +/// blocks and handling loops. +fn remove_paired_rc(function: &mut Function) { + // `dec_rc` is only issued for parameters currently so we can speed things + // up a bit by skipping any functions without them. + if !contains_array_parameter(function) { + return; + } + + let mut context = Context::default(); + + context.find_rcs_in_entry_block(function); + context.scan_for_array_sets(function); + let to_remove = context.find_rcs_to_remove(function); + remove_instructions(to_remove, function); +} + +fn contains_array_parameter(function: &mut Function) -> bool { + let mut parameters = function.parameters().iter(); + parameters.any(|parameter| function.dfg.type_of_value(*parameter).contains_an_array()) +} + +impl Context { + fn find_rcs_in_entry_block(&mut self, function: &Function) { + let entry = function.entry_block(); + + for instruction in function.dfg[entry].instructions() { + if let Instruction::IncrementRc { value } = &function.dfg[*instruction] { + let typ = function.dfg.type_of_value(*value); + + // We assume arrays aren't mutated until we find an array_set + let inc_rc = IncRc { id: *instruction, array: *value, possibly_mutated: false }; + self.inc_rcs.entry(typ).or_default().push(inc_rc); + } + } + } + + /// Find each array_set instruction in the function and mark any arrays used + /// by the inc_rc instructions as possibly mutated if they're the same type. + fn scan_for_array_sets(&mut self, function: &Function) { + for block in function.reachable_blocks() { + for instruction in function.dfg[block].instructions() { + if let Instruction::ArraySet { array, .. } = function.dfg[*instruction] { + let typ = function.dfg.type_of_value(array); + if let Some(inc_rcs) = self.inc_rcs.get_mut(&typ) { + for inc_rc in inc_rcs { + inc_rc.possibly_mutated = true; + } + } + } + } + } + } + + /// Find each dec_rc instruction and if the most recent inc_rc instruction for the same value + /// is not possibly mutated, then we can remove them both. Returns each such pair. + fn find_rcs_to_remove(&mut self, function: &Function) -> HashSet { + let last_block = Self::find_last_block(function); + let mut to_remove = HashSet::new(); + + for instruction in function.dfg[last_block].instructions() { + if let Instruction::DecrementRc { value } = &function.dfg[*instruction] { + if let Some(inc_rc) = self.pop_rc_for(*value, function) { + if !inc_rc.possibly_mutated { + to_remove.insert(inc_rc.id); + to_remove.insert(*instruction); + } + } + } + } + + to_remove + } + + /// Finds the block of the function with the Return instruction + fn find_last_block(function: &Function) -> BasicBlockId { + for block in function.reachable_blocks() { + if matches!( + function.dfg[block].terminator(), + Some(TerminatorInstruction::Return { .. }) + ) { + return block; + } + } + + unreachable!("SSA Function {} has no reachable return instruction!", function.id()) + } + + /// Finds and pops the IncRc for the given array value if possible. + fn pop_rc_for(&mut self, value: ValueId, function: &Function) -> Option { + let typ = function.dfg.type_of_value(value); + + let rcs = self.inc_rcs.get_mut(&typ)?; + let position = rcs.iter().position(|inc_rc| inc_rc.array == value)?; + + Some(rcs.remove(position)) + } +} + +fn remove_instructions(to_remove: HashSet, function: &mut Function) { + if !to_remove.is_empty() { + for block in function.reachable_blocks() { + function.dfg[block] + .instructions_mut() + .retain(|instruction| !to_remove.contains(instruction)); + } + } +} + +#[cfg(test)] +mod test { + use std::rc::Rc; + + use crate::ssa::{ + function_builder::FunctionBuilder, + ir::{ + basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, + instruction::Instruction, map::Id, types::Type, + }, + }; + + fn count_inc_rcs(block: BasicBlockId, dfg: &DataFlowGraph) -> usize { + dfg[block] + .instructions() + .iter() + .filter(|instruction_id| { + matches!(dfg[**instruction_id], Instruction::IncrementRc { .. }) + }) + .count() + } + + fn count_dec_rcs(block: BasicBlockId, dfg: &DataFlowGraph) -> usize { + dfg[block] + .instructions() + .iter() + .filter(|instruction_id| { + matches!(dfg[**instruction_id], Instruction::DecrementRc { .. }) + }) + .count() + } + + #[test] + fn single_block_fn_return_array() { + // This is the output for the program with a function: + // unconstrained fn foo(x: [Field; 2]) -> [[Field; 2]; 1] { + // [array] + // } + // + // fn foo { + // b0(v0: [Field; 2]): + // inc_rc v0 + // inc_rc v0 + // dec_rc v0 + // return [v0] + // } + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("foo".into(), main_id, RuntimeType::Brillig); + + let inner_array_type = Type::Array(Rc::new(vec![Type::field()]), 2); + let v0 = builder.add_parameter(inner_array_type.clone()); + + builder.insert_inc_rc(v0); + builder.insert_inc_rc(v0); + builder.insert_dec_rc(v0); + + let outer_array_type = Type::Array(Rc::new(vec![inner_array_type]), 1); + let array = builder.array_constant(vec![v0].into(), outer_array_type); + builder.terminate_with_return(vec![array]); + + let ssa = builder.finish().remove_paired_rc(); + let main = ssa.main(); + let entry = main.entry_block(); + + assert_eq!(count_inc_rcs(entry, &main.dfg), 1); + assert_eq!(count_dec_rcs(entry, &main.dfg), 0); + } + + #[test] + fn single_block_mutation() { + // fn mutator(mut array: [Field; 2]) { + // array[0] = 5; + // } + // + // fn mutator { + // b0(v0: [Field; 2]): + // v1 = allocate + // store v0 at v1 + // inc_rc v0 + // v2 = load v1 + // v7 = array_set v2, index u64 0, value Field 5 + // store v7 at v1 + // dec_rc v0 + // return + // } + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("mutator".into(), main_id, RuntimeType::Acir); + + let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); + let v0 = builder.add_parameter(array_type.clone()); + + let v1 = builder.insert_allocate(array_type.clone()); + builder.insert_store(v1, v0); + builder.insert_inc_rc(v0); + let v2 = builder.insert_load(v1, array_type); + + let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let five = builder.field_constant(5u128); + let v7 = builder.insert_array_set(v2, zero, five); + + builder.insert_store(v1, v7); + builder.insert_dec_rc(v0); + builder.terminate_with_return(vec![]); + + let ssa = builder.finish().remove_paired_rc(); + let main = ssa.main(); + let entry = main.entry_block(); + + // No changes, the array is possibly mutated + assert_eq!(count_inc_rcs(entry, &main.dfg), 1); + assert_eq!(count_dec_rcs(entry, &main.dfg), 1); + } + + // Similar to single_block_mutation but for a function which + // uses a mutable reference parameter. + #[test] + fn single_block_mutation_through_reference() { + // fn mutator2(array: &mut [Field; 2]) { + // array[0] = 5; + // } + // + // fn mutator2 { + // b0(v0: &mut [Field; 2]): + // v1 = load v0 + // inc_rc v1 + // store v1 at v0 + // v2 = load v0 + // v7 = array_set v2, index u64 0, value Field 5 + // store v7 at v0 + // v8 = load v0 + // dec_rc v8 + // store v8 at v0 + // return + // } + let main_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("mutator2".into(), main_id, RuntimeType::Acir); + + let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); + let reference_type = Type::Reference(Rc::new(array_type.clone())); + + let v0 = builder.add_parameter(reference_type); + + let v1 = builder.insert_load(v0, array_type.clone()); + builder.insert_inc_rc(v1); + builder.insert_store(v0, v1); + + let v2 = builder.insert_load(v1, array_type.clone()); + let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let five = builder.field_constant(5u128); + let v7 = builder.insert_array_set(v2, zero, five); + + builder.insert_store(v0, v7); + let v8 = builder.insert_load(v0, array_type); + builder.insert_dec_rc(v8); + builder.insert_store(v0, v8); + builder.terminate_with_return(vec![]); + + let ssa = builder.finish().remove_paired_rc(); + let main = ssa.main(); + let entry = main.entry_block(); + + // No changes, the array is possibly mutated + assert_eq!(count_inc_rcs(entry, &main.dfg), 1); + assert_eq!(count_dec_rcs(entry, &main.dfg), 1); + } +} diff --git a/compiler/noirc_frontend/src/monomorphization/debug.rs b/compiler/noirc_frontend/src/monomorphization/debug.rs index cf4e0ab792e..3a03177f8ec 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -195,8 +195,8 @@ fn element_type_at_index(ptype: &PrintableType, i: usize) -> &PrintableType { PrintableType::Tuple { types } => &types[i], PrintableType::Struct { name: _name, fields } => &fields[i].1, PrintableType::String { length: _length } => &PrintableType::UnsignedInteger { width: 8 }, - _ => { - panic!["expected type with sub-fields, found terminal type"] + other => { + panic!["expected type with sub-fields, found terminal type: {other:?}"] } } } From 265bd8b284e5acd572a3812a94a99fc102227ff2 Mon Sep 17 00:00:00 2001 From: Andy <35195301+Andy53@users.noreply.github.com> Date: Mon, 18 Mar 2024 13:11:52 -0600 Subject: [PATCH 090/416] fix: added error messages for passing oracles and references from unconstrained to constrained functions (#4570) # Description Added compiler errors for when a oracle or reference is passed from a unconstrained to a constrained function. ## Problem\* Currently the compiler panics when you pass a reference or an oracle from a unconstrained to constrained function. Resolves https://github.com/noir-lang/noir/issues/4565 Closes 4565 ## Summary\* A compiler error has been added for each condition with an appropriate message to aid the user. ## Documentation\* Check one: - [X ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ X] I have tested the changes locally. - [ X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Maxim Vezenov --- compiler/noirc_evaluator/src/errors.rs | 3 +++ .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 8 +++++--- .../src/hir/type_check/errors.rs | 5 +++++ .../noirc_frontend/src/hir/type_check/expr.rs | 20 +++++++++++-------- .../unconstrained_oracle/Nargo.toml | 5 +++++ .../unconstrained_oracle/src/main.nr | 9 +++++++++ .../unconstrained_ref/Nargo.toml | 5 +++++ .../unconstrained_ref/src/main.nr | 8 ++++++++ 8 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 test_programs/compile_failure/unconstrained_oracle/Nargo.toml create mode 100644 test_programs/compile_failure/unconstrained_oracle/src/main.nr create mode 100644 test_programs/compile_failure/unconstrained_ref/Nargo.toml create mode 100644 test_programs/compile_failure/unconstrained_ref/src/main.nr diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 40f4336e0b5..06259e06248 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -48,6 +48,8 @@ pub enum RuntimeError { BigIntModulus { call_stack: CallStack }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { call_stack: CallStack }, + #[error("All `oracle` methods should be wrapped in an unconstrained fn")] + UnconstrainedOracleReturnToConstrained { call_stack: CallStack }, } // We avoid showing the actual lhs and rhs since most of the time they are just 0 @@ -139,6 +141,7 @@ impl RuntimeError { | RuntimeError::NestedSlice { call_stack, .. } | RuntimeError::BigIntModulus { call_stack, .. } | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } => call_stack, + RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } => call_stack, } } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 5a4fa021f1f..8390f480e3a 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -612,9 +612,11 @@ impl Context { self.ssa_values.insert(*result, output); } } - Value::ForeignFunction(_) => unreachable!( - "All `oracle` methods should be wrapped in an unconstrained fn" - ), + Value::ForeignFunction(_) => { + return Err(RuntimeError::UnconstrainedOracleReturnToConstrained { + call_stack: self.acir_context.get_call_stack(), + }) + } _ => unreachable!("expected calling a function but got {function_value:?}"), } } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 7eacc8eb2d1..3d834128688 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -122,6 +122,10 @@ pub enum TypeCheckError { "Cannot pass a mutable reference from a constrained runtime to an unconstrained runtime" )] ConstrainedReferenceToUnconstrained { span: Span }, + #[error( + "Cannot pass a mutable reference from a unconstrained runtime to an constrained runtime" + )] + UnconstrainedReferenceToConstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, #[error("Only sized types may be used in the entry point to a program")] @@ -229,6 +233,7 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } + | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 7219f4d09c6..0b3dd022209 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -184,14 +184,18 @@ impl<'interner> TypeChecker<'interner> { let return_type = self.bind_function_type(function, args, span); // Check that we are not passing a slice from an unconstrained runtime to a constrained runtime - if is_current_func_constrained - && is_unconstrained_call - && return_type.contains_slice() - { - self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained { - span: self.interner.expr_span(expr_id), - }); - return Type::Error; + if is_current_func_constrained && is_unconstrained_call { + if return_type.contains_slice() { + self.errors.push(TypeCheckError::UnconstrainedSliceReturnToConstrained { + span: self.interner.expr_span(expr_id), + }); + return Type::Error; + } else if matches!(&return_type.follow_bindings(), Type::MutableReference(_)) { + self.errors.push(TypeCheckError::UnconstrainedReferenceToConstrained { + span: self.interner.expr_span(expr_id), + }); + return Type::Error; + } } return_type diff --git a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml new file mode 100644 index 00000000000..1081b5ab8e2 --- /dev/null +++ b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "unconstrained_oracle" +type = "bin" +authors = [""] +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_oracle/src/main.nr b/test_programs/compile_failure/unconstrained_oracle/src/main.nr new file mode 100644 index 00000000000..abc7bf51ab8 --- /dev/null +++ b/test_programs/compile_failure/unconstrained_oracle/src/main.nr @@ -0,0 +1,9 @@ +#[oracle(getNoun)] +unconstrained fn external_fn() -> Field { + 100 / 5 +} + +fn main() { + let x = anon(); + assert(x * 5 == 100); +} diff --git a/test_programs/compile_failure/unconstrained_ref/Nargo.toml b/test_programs/compile_failure/unconstrained_ref/Nargo.toml new file mode 100644 index 00000000000..b120a3b5532 --- /dev/null +++ b/test_programs/compile_failure/unconstrained_ref/Nargo.toml @@ -0,0 +1,5 @@ +[package] +name = "unconstrained_ref" +type = "bin" +authors = [""] +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_ref/src/main.nr b/test_programs/compile_failure/unconstrained_ref/src/main.nr new file mode 100644 index 00000000000..bb4b2090ddb --- /dev/null +++ b/test_programs/compile_failure/unconstrained_ref/src/main.nr @@ -0,0 +1,8 @@ +unconstrained fn uncon_ref() -> &mut Field { + let lr = &mut 7; + lr +} + +fn main() { + let e = uncon_ref(); +} \ No newline at end of file From c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Tue, 19 Mar 2024 05:25:45 -0400 Subject: [PATCH 091/416] feat: Sync from aztec-packages (#4573) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE feat: Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) chore(avm-simulator): update e2e test (https://github.com/AztecProtocol/aztec-packages/pull/5283) feat!: Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) feat: initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) feat: Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) feat: Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) feat(avm): brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) feat: New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) feat!: Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant --- .aztec-sync-commit | 2 +- .github/scripts/wasm-bindgen-install.sh | 2 +- acvm-repo/acir/codegen/acir.cpp | 246 +++- acvm-repo/acir/src/circuit/opcodes.rs | 16 + .../acvm/src/compiler/transformers/mod.rs | 1 + acvm-repo/acvm/src/pwg/mod.rs | 1 + acvm-repo/brillig/src/opcodes.rs | 10 +- acvm-repo/brillig_vm/src/arithmetic.rs | 80 +- aztec_macros/src/lib.rs | 9 - aztec_macros/src/transforms/functions.rs | 19 +- aztec_macros/src/utils/errors.rs | 6 - .../brillig/brillig_gen/brillig_black_box.rs | 4 +- .../src/brillig/brillig_gen/brillig_block.rs | 451 ++++--- .../brillig/brillig_gen/brillig_directive.rs | 2 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 165 +-- .../noirc_evaluator/src/brillig/brillig_ir.rs | 1095 +---------------- .../brillig/brillig_ir/brillig_variable.rs | 9 +- .../src/brillig/brillig_ir/codegen_binary.rs | 29 + .../src/brillig/brillig_ir/codegen_calls.rs | 102 ++ .../brillig_ir/codegen_control_flow.rs | 141 +++ .../brillig/brillig_ir/codegen_intrinsic.rs | 89 ++ .../src/brillig/brillig_ir/codegen_memory.rs | 255 ++++ .../src/brillig/brillig_ir/codegen_stack.rs | 26 + .../src/brillig/brillig_ir/debug_show.rs | 165 +-- .../src/brillig/brillig_ir/entry_point.rs | 115 +- .../src/brillig/brillig_ir/instructions.rs | 522 ++++++++ .../src/brillig/brillig_ir/registers.rs | 28 +- .../brillig_signed_div/Nargo.toml | 6 + .../brillig_signed_div/Prover.toml | 75 ++ .../brillig_signed_div/src/main.nr | 11 + tooling/bb_abstraction_leaks/build.rs | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 10 +- 33 files changed, 2042 insertions(+), 1654 deletions(-) create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs create mode 100644 compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs create mode 100644 test_programs/execution_success/brillig_signed_div/Nargo.toml create mode 100644 test_programs/execution_success/brillig_signed_div/Prover.toml create mode 100644 test_programs/execution_success/brillig_signed_div/src/main.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 4d9320c79ba..973bfd1534b 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -aa90f6ed7bfae06bdf6990816d154bbd24993689 +82f8cf5eba9deacdab43ad4ef95dbf27dd1c11c7 diff --git a/.github/scripts/wasm-bindgen-install.sh b/.github/scripts/wasm-bindgen-install.sh index a548372ee2c..20908003693 100755 --- a/.github/scripts/wasm-bindgen-install.sh +++ b/.github/scripts/wasm-bindgen-install.sh @@ -6,7 +6,7 @@ cd $(dirname "$0") ./cargo-binstall-install.sh # Install wasm-bindgen-cli. -if [ "$(wasm-bindgen --version | cut -d' ' -f2)" != "0.2.86" ]; then +if [ "$(wasm-bindgen --version &> /dev/null | cut -d' ' -f2)" != "0.2.86" ]; then echo "Building wasm-bindgen..." cargo binstall wasm-bindgen-cli@0.2.86 --force --no-confirm fi diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 0fc84d47a0f..11afd44ed6d 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -31,13 +31,31 @@ namespace Circuit { static Div bincodeDeserialize(std::vector); }; + struct IntegerDiv { + friend bool operator==(const IntegerDiv&, const IntegerDiv&); + std::vector bincodeSerialize() const; + static IntegerDiv bincodeDeserialize(std::vector); + }; + struct Equals { friend bool operator==(const Equals&, const Equals&); std::vector bincodeSerialize() const; static Equals bincodeDeserialize(std::vector); }; - std::variant value; + struct LessThan { + friend bool operator==(const LessThan&, const LessThan&); + std::vector bincodeSerialize() const; + static LessThan bincodeDeserialize(std::vector); + }; + + struct LessThanEquals { + friend bool operator==(const LessThanEquals&, const LessThanEquals&); + std::vector bincodeSerialize() const; + static LessThanEquals bincodeDeserialize(std::vector); + }; + + std::variant value; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -64,16 +82,10 @@ namespace Circuit { static Mul bincodeDeserialize(std::vector); }; - struct SignedDiv { - friend bool operator==(const SignedDiv&, const SignedDiv&); - std::vector bincodeSerialize() const; - static SignedDiv bincodeDeserialize(std::vector); - }; - - struct UnsignedDiv { - friend bool operator==(const UnsignedDiv&, const UnsignedDiv&); + struct Div { + friend bool operator==(const Div&, const Div&); std::vector bincodeSerialize() const; - static UnsignedDiv bincodeDeserialize(std::vector); + static Div bincodeDeserialize(std::vector); }; struct Equals { @@ -124,7 +136,7 @@ namespace Circuit { static Shr bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -1053,7 +1065,17 @@ namespace Circuit { static MemoryInit bincodeDeserialize(std::vector); }; - std::variant value; + struct Call { + uint32_t id; + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Call&, const Call&); + std::vector bincodeSerialize() const; + static Call bincodeDeserialize(std::vector); + }; + + std::variant value; friend bool operator==(const Opcode&, const Opcode&); std::vector bincodeSerialize() const; @@ -1317,6 +1339,41 @@ Circuit::BinaryFieldOp::Div serde::Deserializable:: return obj; } +namespace Circuit { + + inline bool operator==(const BinaryFieldOp::IntegerDiv &lhs, const BinaryFieldOp::IntegerDiv &rhs) { + return true; + } + + inline std::vector BinaryFieldOp::IntegerDiv::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BinaryFieldOp::IntegerDiv BinaryFieldOp::IntegerDiv::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BinaryFieldOp::IntegerDiv &obj, Serializer &serializer) { +} + +template <> +template +Circuit::BinaryFieldOp::IntegerDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryFieldOp::IntegerDiv obj; + return obj; +} + namespace Circuit { inline bool operator==(const BinaryFieldOp::Equals &lhs, const BinaryFieldOp::Equals &rhs) { @@ -1352,6 +1409,76 @@ Circuit::BinaryFieldOp::Equals serde::Deserializable BinaryFieldOp::LessThan::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BinaryFieldOp::LessThan BinaryFieldOp::LessThan::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BinaryFieldOp::LessThan &obj, Serializer &serializer) { +} + +template <> +template +Circuit::BinaryFieldOp::LessThan serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryFieldOp::LessThan obj; + return obj; +} + +namespace Circuit { + + inline bool operator==(const BinaryFieldOp::LessThanEquals &lhs, const BinaryFieldOp::LessThanEquals &rhs) { + return true; + } + + inline std::vector BinaryFieldOp::LessThanEquals::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BinaryFieldOp::LessThanEquals BinaryFieldOp::LessThanEquals::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::BinaryFieldOp::LessThanEquals &obj, Serializer &serializer) { +} + +template <> +template +Circuit::BinaryFieldOp::LessThanEquals serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryFieldOp::LessThanEquals obj; + return obj; +} + namespace Circuit { inline bool operator==(const BinaryIntOp &lhs, const BinaryIntOp &rhs) { @@ -1501,19 +1628,19 @@ Circuit::BinaryIntOp::Mul serde::Deserializable::dese namespace Circuit { - inline bool operator==(const BinaryIntOp::SignedDiv &lhs, const BinaryIntOp::SignedDiv &rhs) { + inline bool operator==(const BinaryIntOp::Div &lhs, const BinaryIntOp::Div &rhs) { return true; } - inline std::vector BinaryIntOp::SignedDiv::bincodeSerialize() const { + inline std::vector BinaryIntOp::Div::bincodeSerialize() const { auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); + serde::Serializable::serialize(*this, serializer); return std::move(serializer).bytes(); } - inline BinaryIntOp::SignedDiv BinaryIntOp::SignedDiv::bincodeDeserialize(std::vector input) { + inline BinaryIntOp::Div BinaryIntOp::Div::bincodeDeserialize(std::vector input) { auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); + auto value = serde::Deserializable::deserialize(deserializer); if (deserializer.get_buffer_offset() < input.size()) { throw serde::deserialization_error("Some input bytes were not read"); } @@ -1524,48 +1651,13 @@ namespace Circuit { template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::SignedDiv &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Circuit::BinaryIntOp::Div &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::SignedDiv serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::SignedDiv obj; - return obj; -} - -namespace Circuit { - - inline bool operator==(const BinaryIntOp::UnsignedDiv &lhs, const BinaryIntOp::UnsignedDiv &rhs) { - return true; - } - - inline std::vector BinaryIntOp::UnsignedDiv::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline BinaryIntOp::UnsignedDiv BinaryIntOp::UnsignedDiv::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Circuit - -template <> -template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::UnsignedDiv &obj, Serializer &serializer) { -} - -template <> -template -Circuit::BinaryIntOp::UnsignedDiv serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::UnsignedDiv obj; +Circuit::BinaryIntOp::Div serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::BinaryIntOp::Div obj; return obj; } @@ -6012,6 +6104,50 @@ Circuit::Opcode::MemoryInit serde::Deserializable:: return obj; } +namespace Circuit { + + inline bool operator==(const Opcode::Call &lhs, const Opcode::Call &rhs) { + if (!(lhs.id == rhs.id)) { return false; } + if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.outputs == rhs.outputs)) { return false; } + return true; + } + + inline std::vector Opcode::Call::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline Opcode::Call Opcode::Call::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Circuit + +template <> +template +void serde::Serializable::serialize(const Circuit::Opcode::Call &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.id, serializer); + serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.outputs, serializer); +} + +template <> +template +Circuit::Opcode::Call serde::Deserializable::deserialize(Deserializer &deserializer) { + Circuit::Opcode::Call obj; + obj.id = serde::Deserializable::deserialize(deserializer); + obj.inputs = serde::Deserializable::deserialize(deserializer); + obj.outputs = serde::Deserializable::deserialize(deserializer); + return obj; +} + namespace Circuit { inline bool operator==(const OpcodeLocation &lhs, const OpcodeLocation &rhs) { diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index f725ba8c32a..064a9d1244a 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -29,6 +29,17 @@ pub enum Opcode { block_id: BlockId, init: Vec, }, + /// Calls to functions represented as a separate circuit. A call opcode allows us + /// to build a call stack when executing the outer-most circuit. + Call { + /// Id for the function being called. It is the responsibility of the executor + /// to fetch the appropriate circuit from this id. + id: u32, + /// Inputs to the function call + inputs: Vec, + /// Outputs of the function call + outputs: Vec, + }, } impl std::fmt::Display for Opcode { @@ -86,6 +97,11 @@ impl std::fmt::Display for Opcode { write!(f, "INIT ")?; write!(f, "(id: {}, len: {}) ", block_id.0, init.len()) } + Opcode::Call { id, inputs, outputs } => { + write!(f, "CALL func {}: ", id)?; + writeln!(f, "inputs: {:?}", inputs)?; + writeln!(f, "outputs: {:?}", outputs) + } } } } diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index 214243d9360..2e549854521 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -145,6 +145,7 @@ pub(super) fn transform_internal( new_acir_opcode_positions.push(acir_opcode_positions[index]); transformed_opcodes.push(opcode); } + Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"), } } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index d8323e5ef5f..0fd733a6336 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -281,6 +281,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { Ok(Some(foreign_call)) => return self.wait_for_foreign_call(foreign_call), res => res.map(|_| ()), }, + Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"), }; self.handle_opcode_resolution(resolution) } diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index 51df1f90941..03a8e53e510 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -180,9 +180,16 @@ pub enum BinaryFieldOp { Add, Sub, Mul, + /// Field division Div, + /// Integer division + IntegerDiv, /// (==) equal Equals, + /// (<) Field less than + LessThan, + /// (<=) field less or equal + LessThanEquals, } /// Binary fixed-length integer expressions @@ -191,8 +198,7 @@ pub enum BinaryIntOp { Add, Sub, Mul, - SignedDiv, - UnsignedDiv, + Div, /// (==) equal Equals, /// (<) Field less than diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 9d7b6fe8f02..81103be582d 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,6 +1,6 @@ use acir::brillig::{BinaryFieldOp, BinaryIntOp}; use acir::FieldElement; -use num_bigint::{BigInt, BigUint}; +use num_bigint::BigUint; use num_traits::{One, ToPrimitive, Zero}; /// Evaluate a binary operation on two FieldElements and return the result as a FieldElement. @@ -15,7 +15,16 @@ pub(crate) fn evaluate_binary_field_op( BinaryFieldOp::Sub => a - b, BinaryFieldOp::Mul => a * b, BinaryFieldOp::Div => a / b, + BinaryFieldOp::IntegerDiv => { + let a_big = BigUint::from_bytes_be(&a.to_be_bytes()); + let b_big = BigUint::from_bytes_be(&b.to_be_bytes()); + + let result = a_big / b_big; + FieldElement::from_be_bytes_reduce(&result.to_bytes_be()) + } BinaryFieldOp::Equals => (a == b).into(), + BinaryFieldOp::LessThan => (a < b).into(), + BinaryFieldOp::LessThanEquals => (a <= b).into(), } } @@ -33,7 +42,7 @@ pub(crate) fn evaluate_binary_bigint_op( BinaryIntOp::Sub => (bit_modulo + a - b) % bit_modulo, BinaryIntOp::Mul => (a * b) % bit_modulo, // Perform unsigned division using the modulo operation on a and b. - BinaryIntOp::UnsignedDiv => { + BinaryIntOp::Div => { let b_mod = b % bit_modulo; if b_mod.is_zero() { BigUint::zero() @@ -41,16 +50,6 @@ pub(crate) fn evaluate_binary_bigint_op( (a % bit_modulo) / b_mod } } - // Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation. - BinaryIntOp::SignedDiv => { - let b_signed = to_big_signed(b, bit_size); - if b_signed.is_zero() { - BigUint::zero() - } else { - let signed_div = to_big_signed(a, bit_size) / b_signed; - to_big_unsigned(signed_div, bit_size) - } - } // Perform a == operation, returning 0 or 1 BinaryIntOp::Equals => { if (a % bit_modulo) == (b % bit_modulo) { @@ -94,23 +93,6 @@ pub(crate) fn evaluate_binary_bigint_op( Ok(result) } -fn to_big_signed(a: BigUint, bit_size: u32) -> BigInt { - let pow_2 = BigUint::from(2_u32).pow(bit_size - 1); - if a < pow_2 { - BigInt::from(a) - } else { - BigInt::from(a) - 2 * BigInt::from(pow_2) - } -} - -fn to_big_unsigned(a: BigInt, bit_size: u32) -> BigUint { - if a >= BigInt::zero() { - BigUint::from_bytes_le(&a.to_bytes_le().1) - } else { - BigUint::from(2_u32).pow(bit_size) - BigUint::from_bytes_le(&a.to_bytes_le().1) - } -} - #[cfg(test)] mod tests { use super::*; @@ -130,24 +112,6 @@ mod tests { result_value.to_u128().unwrap() } - fn to_signed(a: u128, bit_size: u32) -> i128 { - assert!(bit_size < 128); - let pow_2 = 2_u128.pow(bit_size - 1); - if a < pow_2 { - a as i128 - } else { - (a.wrapping_sub(2 * pow_2)) as i128 - } - } - - fn to_unsigned(a: i128, bit_size: u32) -> u128 { - if a >= 0 { - a as u128 - } else { - (a + 2_i128.pow(bit_size)) as u128 - } - } - fn to_negative(a: u128, bit_size: u32) -> u128 { assert!(a > 0); let two_pow = 2_u128.pow(bit_size); @@ -224,26 +188,6 @@ mod tests { let test_ops = vec![TestParams { a: 5, b: 3, result: 1 }, TestParams { a: 5, b: 10, result: 0 }]; - evaluate_int_ops(test_ops, BinaryIntOp::UnsignedDiv, bit_size); - } - - #[test] - fn to_signed_roundtrip() { - let bit_size = 32; - let minus_one = 2_u128.pow(bit_size) - 1; - assert_eq!(to_unsigned(to_signed(minus_one, bit_size), bit_size), minus_one); - } - - #[test] - fn signed_div_test() { - let bit_size = 32; - - let test_ops = vec![ - TestParams { a: 5, b: to_negative(10, bit_size), result: 0 }, - TestParams { a: 5, b: to_negative(1, bit_size), result: to_negative(5, bit_size) }, - TestParams { a: to_negative(5, bit_size), b: to_negative(1, bit_size), result: 5 }, - ]; - - evaluate_int_ops(test_ops, BinaryIntOp::SignedDiv, bit_size); + evaluate_int_ops(test_ops, BinaryIntOp::Div, bit_size); } } diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 1f3546cbb6a..e0100977eee 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -179,15 +179,6 @@ fn transform_module( crate_graph.root_file_id, )); } - - let constructor_defined = module.functions.iter().any(|func| func.name() == "constructor"); - if !constructor_defined { - let crate_graph = &context.crate_graph[crate_id]; - return Err(( - AztecMacroError::ContractConstructorMissing { span: Span::default() }, - crate_graph.root_file_id, - )); - } } Ok(has_transformed_module) diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index 09c11e173fe..c719651e10e 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -48,10 +48,9 @@ pub fn transform_function( func.def.body.0.insert(0, init_check); } - // Add assertion for initialization arguments + // Add assertion for initialization arguments and sender if is_initializer { - let assert_init_args = create_assert_init_args(); - func.def.body.0.insert(0, assert_init_args); + func.def.body.0.insert(0, create_assert_initializer()); } // Add access to the storage struct @@ -120,9 +119,6 @@ pub fn transform_vm_function( // We want the function to be seen as a public function func.def.is_unconstrained = true; - // NOTE: the line below is a temporary hack to trigger external transpilation tools - // It will be removed once the transpiler is integrated into the Noir compiler - func.def.name.0.contents = format!("avm_{}", func.def.name.0.contents); Ok(()) } @@ -211,18 +207,19 @@ fn create_internal_check(fname: &str) -> Statement { ))) } -/// Creates a call to assert_initialization_args_match_address_preimage to ensure -/// the initialization arguments used in the init call match the address preimage. +/// Creates a call to assert_initialization_matches_address_preimage to be inserted +/// in the initializer. Checks that the args and sender to the initializer match the +/// commitments from the address preimage. /// /// ```noir -/// assert_initialization_args_match_address_preimage(context); +/// assert_initialization_matches_address_preimage(context); /// ``` -fn create_assert_init_args() -> Statement { +fn create_assert_initializer() -> Statement { make_statement(StatementKind::Expression(call( variable_path(chained_dep!( "aztec", "initializer", - "assert_initialization_args_match_address_preimage" + "assert_initialization_matches_address_preimage" )), vec![variable("context")], ))) diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 63892b58af9..199473baec6 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -7,7 +7,6 @@ use super::constants::MAX_CONTRACT_PRIVATE_FUNCTIONS; pub enum AztecMacroError { AztecDepNotFound, ContractHasTooManyPrivateFunctions { span: Span }, - ContractConstructorMissing { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, @@ -29,11 +28,6 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, - AztecMacroError::ContractConstructorMissing { span } => MacroError { - primary_message: "Contract must have a constructor function".to_owned(), - secondary_message: None, - span: Some(span), - }, AztecMacroError::UnsupportedFunctionArgumentType { span, typ } => MacroError { primary_message: format!("Provided parameter type `{typ:?}` is not supported in Aztec contract interface"), secondary_message: None, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index d542240a40c..36e5c99a2ca 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -167,7 +167,7 @@ pub(crate) fn convert_black_box_call( ) = (function_arguments, function_results) { let message_hash = convert_array_or_vector(brillig_context, message, bb_func); - let signature = brillig_context.array_to_vector(signature); + let signature = brillig_context.array_to_vector_instruction(signature); brillig_context.black_box_op_instruction(BlackBoxOp::SchnorrVerify { public_key_x: public_key_x.address, public_key_y: public_key_y.address, @@ -368,7 +368,7 @@ fn convert_array_or_vector( bb_func: &BlackBoxFunc, ) -> BrilligVector { match array_or_vector { - BrilligVariable::BrilligArray(array) => brillig_context.array_to_vector(array), + BrilligVariable::BrilligArray(array) => brillig_context.array_to_vector_instruction(array), BrilligVariable::BrilligVector(vector) => *vector, _ => unreachable!( "ICE: {} expected an array or a vector, but got {:?}", diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 99400b13528..f808bfac43b 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -16,7 +16,7 @@ use crate::ssa::ir::{ types::{NumericType, Type}, value::{Value, ValueId}, }; -use acvm::acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, ValueOrArray}; +use acvm::acir::brillig::{MemoryAddress, ValueOrArray}; use acvm::brillig_vm::brillig::HeapVector; use acvm::FieldElement; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; @@ -155,7 +155,7 @@ impl<'block> BrilligBlock<'block> { return_variable.extract_registers() }) .collect(); - self.brillig_context.return_instruction(&return_registers); + self.brillig_context.codegen_return(&return_registers); } } } @@ -297,16 +297,15 @@ impl<'block> BrilligBlock<'block> { Type::Reference(element) => match *element { Type::Array(..) => { self.brillig_context - .allocate_array_reference_instruction(address_register.address); + .codegen_allocate_array_reference(address_register.address); } Type::Slice(..) => { self.brillig_context - .allocate_vector_reference_instruction(address_register.address); + .codegen_allocate_vector_reference(address_register.address); } _ => { - self.brillig_context.allocate_single_addr_reference_instruction( - address_register.address, - ); + self.brillig_context + .codegen_allocate_single_addr_reference(address_register.address); } }, _ => { @@ -318,8 +317,7 @@ impl<'block> BrilligBlock<'block> { let address_var = self.convert_ssa_single_addr_value(*address, dfg); let source_variable = self.convert_ssa_value(*value, dfg); - self.brillig_context - .store_variable_instruction(address_var.address, source_variable); + self.brillig_context.codegen_store_variable(address_var.address, source_variable); } Instruction::Load { address } => { let target_variable = self.variables.define_variable( @@ -332,7 +330,7 @@ impl<'block> BrilligBlock<'block> { let address_variable = self.convert_ssa_single_addr_value(*address, dfg); self.brillig_context - .load_variable_instruction(target_variable, address_variable.address); + .codegen_load_variable(target_variable, address_variable.address); } Instruction::Not(value) => { let condition_register = self.convert_ssa_single_addr_value(*value, dfg); @@ -374,16 +372,16 @@ impl<'block> BrilligBlock<'block> { if let ValueOrArray::HeapVector(HeapVector { size, .. }) = output_register { // Update the stack pointer so that we do not overwrite // dynamic memory returned from other external calls - self.brillig_context.update_stack_pointer(*size); + self.brillig_context.increase_free_memory_pointer_instruction(*size); // Update the dynamic slice length maintained in SSA if let ValueOrArray::MemoryAddress(len_index) = output_registers[i - 1] { let element_size = dfg[result_ids[i]].get_type().element_size(); self.brillig_context.mov_instruction(len_index, *size); - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( len_index, - BinaryIntOp::UnsignedDiv, + BrilligBinaryOp::UnsignedDiv, element_size, ); } else { @@ -518,7 +516,7 @@ impl<'block> BrilligBlock<'block> { // Update the user-facing slice length self.brillig_context.mov_instruction(target_len.address, limb_count.address); - self.brillig_context.radix_instruction( + self.brillig_context.codegen_to_radix( source, target_vector, radix, @@ -547,18 +545,18 @@ impl<'block> BrilligBlock<'block> { dfg, ) { BrilligVariable::BrilligArray(array) => { - self.brillig_context.array_to_vector(&array) + self.brillig_context.array_to_vector_instruction(&array) } BrilligVariable::BrilligVector(vector) => vector, BrilligVariable::SingleAddr(..) => unreachable!("ICE: ToBits on non-array"), }; - let radix = self.brillig_context.make_constant(2_usize.into(), 32); + let radix = self.brillig_context.make_constant_instruction(2_usize.into(), 32); // Update the user-facing slice length self.brillig_context.mov_instruction(target_len.address, limb_count.address); - self.brillig_context.radix_instruction( + self.brillig_context.codegen_to_radix( source, target_vector, radix, @@ -581,7 +579,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let source_register = self.convert_ssa_single_addr_value(*value, dfg); - self.brillig_context.truncate_instruction( + self.brillig_context.codegen_truncate( destination_register, source_register, *bit_size, @@ -655,20 +653,19 @@ impl<'block> BrilligBlock<'block> { // Create a field constant with the max let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); - let right = self.brillig_context.make_constant( + let right = self.brillig_context.make_constant_instruction( FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), FieldElement::max_num_bits(), ); // Check if lte max - let brillig_binary_op = BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals); let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); self.brillig_context.binary_instruction( left, right, condition, - brillig_binary_op, + BrilligBinaryOp::LessThanEquals, ); self.brillig_context.constrain_instruction(condition, assert_message.clone()); @@ -683,7 +680,11 @@ impl<'block> BrilligBlock<'block> { | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, other => unreachable!("ICE: increment rc on non-array: {other:?}"), }; - self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Add, 1); + self.brillig_context.codegen_usize_op_in_place( + rc_register, + BrilligBinaryOp::Add, + 1, + ); } Instruction::DecrementRc { value } => { let rc_register = match self.convert_ssa_value(*value, dfg) { @@ -691,7 +692,11 @@ impl<'block> BrilligBlock<'block> { | BrilligVariable::BrilligVector(BrilligVector { rc, .. }) => rc, other => unreachable!("ICE: decrement rc on non-array: {other:?}"), }; - self.brillig_context.usize_op_in_place(rc_register, BinaryIntOp::Sub, 1); + self.brillig_context.codegen_usize_op_in_place( + rc_register, + BrilligBinaryOp::Sub, + 1, + ); } Instruction::EnableSideEffects { .. } => { todo!("enable_side_effects not supported by brillig") @@ -733,7 +738,7 @@ impl<'block> BrilligBlock<'block> { let saved_registers = self .brillig_context - .pre_call_save_registers_prep_args(&argument_registers, &variables_to_save); + .codegen_pre_call_save_registers_prep_args(&argument_registers, &variables_to_save); // We don't save and restore constants, so we dump them before a external call since the callee might use the registers where they are allocated. self.variables.dump_constants(); @@ -767,7 +772,7 @@ impl<'block> BrilligBlock<'block> { // puts the returns into the returned_registers and restores saved_registers self.brillig_context - .post_call_prep_returns_load_registers(&returned_registers, &saved_registers); + .codegen_post_call_prep_returns_load_registers(&returned_registers, &saved_registers); } fn validate_array_index( @@ -777,7 +782,7 @@ impl<'block> BrilligBlock<'block> { ) { let (size_as_register, should_deallocate_size) = match array_variable { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { - (self.brillig_context.make_usize_constant(size.into()), true) + (self.brillig_context.make_usize_constant_instruction(size.into()), true) } BrilligVariable::BrilligVector(BrilligVector { size, .. }) => { (SingleAddrVariable::new_usize(size), false) @@ -787,11 +792,11 @@ impl<'block> BrilligBlock<'block> { let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( index_register.address, size_as_register.address, condition.address, - BinaryIntOp::LessThan, + BrilligBinaryOp::LessThan, ); self.brillig_context @@ -811,7 +816,7 @@ impl<'block> BrilligBlock<'block> { ) { match destination_variable { BrilligVariable::SingleAddr(destination_register) => { - self.brillig_context.array_get( + self.brillig_context.codegen_array_get( array_pointer, index_var, destination_register.address, @@ -819,8 +824,8 @@ impl<'block> BrilligBlock<'block> { } BrilligVariable::BrilligArray(..) | BrilligVariable::BrilligVector(..) => { let reference = self.brillig_context.allocate_register(); - self.brillig_context.array_get(array_pointer, index_var, reference); - self.brillig_context.load_variable_instruction(destination_variable, reference); + self.brillig_context.codegen_array_get(array_pointer, index_var, reference); + self.brillig_context.codegen_load_variable(destination_variable, reference); self.brillig_context.deallocate_register(reference); } } @@ -852,7 +857,7 @@ impl<'block> BrilligBlock<'block> { let (source_pointer, source_size_as_register) = match source_variable { BrilligVariable::BrilligArray(BrilligArray { size, pointer, rc: _ }) => { let source_size_register = self.brillig_context.allocate_register(); - self.brillig_context.usize_const(source_size_register, size.into()); + self.brillig_context.usize_const_instruction(source_size_register, size.into()); (pointer, source_size_register) } BrilligVariable::BrilligVector(BrilligVector { size, pointer, rc: _ }) => { @@ -864,23 +869,23 @@ impl<'block> BrilligBlock<'block> { }; // Here we want to compare the reference count against 1. - let one = self.brillig_context.make_usize_constant(1_usize.into()); + let one = self.brillig_context.make_usize_constant_instruction(1_usize.into()); let condition = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( reference_count, one.address, condition, - BinaryIntOp::Equals, + BrilligBinaryOp::Equals, ); - self.brillig_context.branch_instruction(condition, |ctx, cond| { + self.brillig_context.codegen_branch(condition, |ctx, cond| { if cond { // Reference count is 1, we can mutate the array directly ctx.mov_instruction(destination_pointer, source_pointer); } else { // First issue a array copy to the destination - ctx.allocate_array_instruction(destination_pointer, source_size_as_register); + ctx.codegen_allocate_array(destination_pointer, source_size_as_register); - ctx.copy_array_instruction( + ctx.codegen_copy_array( source_pointer, destination_pointer, SingleAddrVariable::new( @@ -893,7 +898,7 @@ impl<'block> BrilligBlock<'block> { match destination_variable { BrilligVariable::BrilligArray(BrilligArray { rc: target_rc, .. }) => { - self.brillig_context.usize_const(target_rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_rc, 1_usize.into()); } BrilligVariable::BrilligVector(BrilligVector { size: target_size, @@ -901,7 +906,7 @@ impl<'block> BrilligBlock<'block> { .. }) => { self.brillig_context.mov_instruction(target_size, source_size_as_register); - self.brillig_context.usize_const(target_rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_rc, 1_usize.into()); } _ => unreachable!("ICE: array_set SSA on non-array"), } @@ -927,20 +932,20 @@ impl<'block> BrilligBlock<'block> { ) { match value_variable { BrilligVariable::SingleAddr(value_variable) => { - ctx.array_set(destination_pointer, index_register, value_variable.address); + ctx.codegen_array_set(destination_pointer, index_register, value_variable.address); } BrilligVariable::BrilligArray(_) => { let reference: MemoryAddress = ctx.allocate_register(); - ctx.allocate_array_reference_instruction(reference); - ctx.store_variable_instruction(reference, value_variable); - ctx.array_set(destination_pointer, index_register, reference); + ctx.codegen_allocate_array_reference(reference); + ctx.codegen_store_variable(reference, value_variable); + ctx.codegen_array_set(destination_pointer, index_register, reference); ctx.deallocate_register(reference); } BrilligVariable::BrilligVector(_) => { let reference = ctx.allocate_register(); - ctx.allocate_vector_reference_instruction(reference); - ctx.store_variable_instruction(reference, value_variable); - ctx.array_set(destination_pointer, index_register, reference); + ctx.codegen_allocate_vector_reference(reference); + ctx.codegen_store_variable(reference, value_variable); + ctx.codegen_array_set(destination_pointer, index_register, reference); ctx.deallocate_register(reference); } } @@ -998,7 +1003,12 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_value(*arg, dfg) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Add, + ); self.slice_push_back_operation(target_vector, source_vector, &item_values); } @@ -1024,7 +1034,12 @@ impl<'block> BrilligBlock<'block> { self.convert_ssa_value(*arg, dfg) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Add, + ); self.slice_push_front_operation(target_vector, source_vector, &item_values); } @@ -1057,7 +1072,12 @@ impl<'block> BrilligBlock<'block> { ) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Sub); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Sub, + ); self.slice_pop_back_operation(target_vector, source_vector, &pop_variables); } @@ -1089,7 +1109,12 @@ impl<'block> BrilligBlock<'block> { ); let target_vector = target_variable.extract_vector(); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Sub); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Sub, + ); self.slice_pop_front_operation(target_vector, source_vector, &pop_variables); } @@ -1118,20 +1143,26 @@ impl<'block> BrilligBlock<'block> { // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 let user_index = self.convert_ssa_single_addr_value(arguments[2], dfg); - let converted_index = self.brillig_context.make_usize_constant(element_size.into()); + let converted_index = + self.brillig_context.make_usize_constant_instruction(element_size.into()); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( converted_index.address, user_index.address, converted_index.address, - BinaryIntOp::Mul, + BrilligBinaryOp::Mul, ); let items = vecmap(&arguments[3..element_size + 3], |arg| { self.convert_ssa_value(*arg, dfg) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Add); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Add, + ); self.slice_insert_operation(target_vector, source_vector, converted_index, &items); self.brillig_context.deallocate_single_addr(converted_index); @@ -1161,12 +1192,13 @@ impl<'block> BrilligBlock<'block> { // https://github.com/noir-lang/noir/issues/1889#issuecomment-1668048587 let user_index = self.convert_ssa_single_addr_value(arguments[2], dfg); - let converted_index = self.brillig_context.make_usize_constant(element_size.into()); - self.brillig_context.memory_op( + let converted_index = + self.brillig_context.make_usize_constant_instruction(element_size.into()); + self.brillig_context.memory_op_instruction( converted_index.address, user_index.address, converted_index.address, - BinaryIntOp::Mul, + BrilligBinaryOp::Mul, ); let removed_items = vecmap(&results[2..element_size + 2], |result| { @@ -1178,7 +1210,12 @@ impl<'block> BrilligBlock<'block> { ) }); - self.update_slice_length(target_len.address, arguments[0], dfg, BinaryIntOp::Sub); + self.update_slice_length( + target_len.address, + arguments[0], + dfg, + BrilligBinaryOp::Sub, + ); self.slice_remove_operation( target_vector, @@ -1207,12 +1244,12 @@ impl<'block> BrilligBlock<'block> { target_len: MemoryAddress, source_value: ValueId, dfg: &DataFlowGraph, - binary_op: BinaryIntOp, + binary_op: BrilligBinaryOp, ) { let source_len_variable = self.convert_ssa_value(source_value, dfg); let source_len = source_len_variable.extract_single_addr(); - self.brillig_context.usize_op(source_len.address, target_len, binary_op, 1); + self.brillig_context.codegen_usize_op(source_len.address, target_len, binary_op, 1); } /// Converts an SSA cast to a sequence of Brillig opcodes. @@ -1237,14 +1274,166 @@ impl<'block> BrilligBlock<'block> { let left = self.convert_ssa_single_addr_value(binary.lhs, dfg); let right = self.convert_ssa_single_addr_value(binary.rhs, dfg); - let (brillig_binary_op, is_signed) = - convert_ssa_binary_op_to_brillig_binary_op(binary.operator, &binary_type); + let (is_field, is_signed) = match binary_type { + Type::Numeric(numeric_type) => match numeric_type { + NumericType::Signed { .. } => (false, true), + NumericType::Unsigned { .. } => (false, false), + NumericType::NativeField => (true, false), + }, + _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), + }; + + let brillig_binary_op = match binary.operator { + BinaryOp::Div => { + if is_signed { + self.convert_signed_division(left, right, result_variable); + return; + } else if is_field { + BrilligBinaryOp::FieldDiv + } else { + BrilligBinaryOp::UnsignedDiv + } + } + BinaryOp::Mod => { + if is_signed { + self.convert_signed_modulo(left, right, result_variable); + return; + } else { + BrilligBinaryOp::Modulo + } + } + BinaryOp::Add => BrilligBinaryOp::Add, + BinaryOp::Sub => BrilligBinaryOp::Sub, + BinaryOp::Mul => BrilligBinaryOp::Mul, + BinaryOp::Eq => BrilligBinaryOp::Equals, + BinaryOp::Lt => BrilligBinaryOp::LessThan, + BinaryOp::And => BrilligBinaryOp::And, + BinaryOp::Or => BrilligBinaryOp::Or, + BinaryOp::Xor => BrilligBinaryOp::Xor, + BinaryOp::Shl => BrilligBinaryOp::Shl, + BinaryOp::Shr => BrilligBinaryOp::Shr, + }; self.brillig_context.binary_instruction(left, right, result_variable, brillig_binary_op); self.add_overflow_check(brillig_binary_op, left, right, result_variable, is_signed); } + /// Splits a two's complement signed integer in the sign bit and the absolute value. + /// For example, -6 i8 (11111010) is split to 00000110 (6, absolute value) and 1 (is_negative). + fn absolute_value( + &mut self, + num: SingleAddrVariable, + absolute_value: SingleAddrVariable, + result_is_negative: SingleAddrVariable, + ) { + let max_positive = self + .brillig_context + .make_constant_instruction(((1_u128 << (num.bit_size - 1)) - 1).into(), num.bit_size); + + // Compute if num is negative + self.brillig_context.binary_instruction( + max_positive, + num, + result_is_negative, + BrilligBinaryOp::LessThan, + ); + + self.brillig_context.codegen_branch(result_is_negative.address, |ctx, is_negative| { + if is_negative { + // Two's complement of num + let zero = ctx.make_constant_instruction(0_usize.into(), num.bit_size); + ctx.binary_instruction(zero, num, absolute_value, BrilligBinaryOp::Sub); + ctx.deallocate_single_addr(zero); + } else { + // Simply move the original num + ctx.mov_instruction(absolute_value.address, num.address); + } + }); + self.brillig_context.deallocate_single_addr(max_positive); + } + + fn convert_signed_division( + &mut self, + left: SingleAddrVariable, + right: SingleAddrVariable, + result: SingleAddrVariable, + ) { + let left_is_negative = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); + let left_abs_value = + SingleAddrVariable::new(self.brillig_context.allocate_register(), left.bit_size); + + let right_is_negative = + SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); + let right_abs_value = + SingleAddrVariable::new(self.brillig_context.allocate_register(), right.bit_size); + + let result_is_negative = + SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); + + // Compute both absolute values + self.absolute_value(left, left_abs_value, left_is_negative); + self.absolute_value(right, right_abs_value, right_is_negative); + + // Perform the division on the absolute values + self.brillig_context.binary_instruction( + left_abs_value, + right_abs_value, + result, + BrilligBinaryOp::UnsignedDiv, + ); + + // Compute result sign + self.brillig_context.binary_instruction( + left_is_negative, + right_is_negative, + result_is_negative, + BrilligBinaryOp::Xor, + ); + + // If result has to be negative, perform two's complement + self.brillig_context.codegen_if(result_is_negative.address, |ctx| { + let zero = ctx.make_constant_instruction(0_usize.into(), result.bit_size); + ctx.binary_instruction(zero, result, result, BrilligBinaryOp::Sub); + ctx.deallocate_single_addr(zero); + }); + + self.brillig_context.deallocate_single_addr(left_is_negative); + self.brillig_context.deallocate_single_addr(left_abs_value); + self.brillig_context.deallocate_single_addr(right_is_negative); + self.brillig_context.deallocate_single_addr(right_abs_value); + self.brillig_context.deallocate_single_addr(result_is_negative); + } + + fn convert_signed_modulo( + &mut self, + left: SingleAddrVariable, + right: SingleAddrVariable, + result: SingleAddrVariable, + ) { + let scratch_var_i = + SingleAddrVariable::new(self.brillig_context.allocate_register(), left.bit_size); + let scratch_var_j = + SingleAddrVariable::new(self.brillig_context.allocate_register(), left.bit_size); + + // i = left / right + self.convert_signed_division(left, right, scratch_var_i); + + // j = i * right + self.brillig_context.binary_instruction( + scratch_var_i, + right, + scratch_var_j, + BrilligBinaryOp::Mul, + ); + + // result_register = left - j + self.brillig_context.binary_instruction(left, scratch_var_j, result, BrilligBinaryOp::Sub); + // Free scratch registers + self.brillig_context.deallocate_single_addr(scratch_var_i); + self.brillig_context.deallocate_single_addr(scratch_var_j); + } + fn add_overflow_check( &mut self, binary_operation: BrilligBinaryOp, @@ -1253,15 +1442,14 @@ impl<'block> BrilligBlock<'block> { result: SingleAddrVariable, is_signed: bool, ) { - let (op, bit_size) = if let BrilligBinaryOp::Integer(op) = binary_operation { - // Bit size is checked at compile time to be equal for left and right - (op, left.bit_size) - } else { + let bit_size = left.bit_size; + + if bit_size == FieldElement::max_num_bits() { return; - }; + } - match (op, is_signed) { - (BinaryIntOp::Add, false) => { + match (binary_operation, is_signed) { + (BrilligBinaryOp::Add, false) => { let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); // Check that lhs <= result @@ -1269,7 +1457,7 @@ impl<'block> BrilligBlock<'block> { left, result, condition, - BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals), + BrilligBinaryOp::LessThanEquals, ); self.brillig_context.constrain_instruction( condition, @@ -1277,7 +1465,7 @@ impl<'block> BrilligBlock<'block> { ); self.brillig_context.deallocate_single_addr(condition); } - (BinaryIntOp::Sub, false) => { + (BrilligBinaryOp::Sub, false) => { let condition = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); // Check that rhs <= lhs @@ -1285,7 +1473,7 @@ impl<'block> BrilligBlock<'block> { right, left, condition, - BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals), + BrilligBinaryOp::LessThanEquals, ); self.brillig_context.constrain_instruction( condition, @@ -1293,19 +1481,20 @@ impl<'block> BrilligBlock<'block> { ); self.brillig_context.deallocate_single_addr(condition); } - (BinaryIntOp::Mul, false) => { + (BrilligBinaryOp::Mul, false) => { // Multiplication overflow is only possible for bit sizes > 1 if bit_size > 1 { let is_right_zero = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1); - let zero = self.brillig_context.make_constant(0_usize.into(), bit_size); + let zero = + self.brillig_context.make_constant_instruction(0_usize.into(), bit_size); self.brillig_context.binary_instruction( zero, right, is_right_zero, - BrilligBinaryOp::Integer(BinaryIntOp::Equals), + BrilligBinaryOp::Equals, ); - self.brillig_context.if_not_instruction(is_right_zero.address, |ctx| { + self.brillig_context.codegen_if_not(is_right_zero.address, |ctx| { let condition = SingleAddrVariable::new(ctx.allocate_register(), 1); let division = SingleAddrVariable::new(ctx.allocate_register(), bit_size); // Check that result / rhs == lhs @@ -1313,14 +1502,9 @@ impl<'block> BrilligBlock<'block> { result, right, division, - BrilligBinaryOp::Integer(BinaryIntOp::UnsignedDiv), - ); - ctx.binary_instruction( - division, - left, - condition, - BrilligBinaryOp::Integer(BinaryIntOp::Equals), + BrilligBinaryOp::UnsignedDiv, ); + ctx.binary_instruction(division, left, condition, BrilligBinaryOp::Equals); ctx.constrain_instruction( condition, Some("attempt to multiply with overflow".to_string()), @@ -1372,17 +1556,21 @@ impl<'block> BrilligBlock<'block> { // Initialize the variable let pointer = match new_variable { BrilligVariable::BrilligArray(brillig_array) => { + self.brillig_context.codegen_allocate_fixed_length_array( + brillig_array.pointer, + array.len(), + ); self.brillig_context - .allocate_fixed_length_array(brillig_array.pointer, array.len()); - self.brillig_context.usize_const(brillig_array.rc, 1_usize.into()); + .usize_const_instruction(brillig_array.rc, 1_usize.into()); brillig_array.pointer } BrilligVariable::BrilligVector(vector) => { - self.brillig_context.usize_const(vector.size, array.len().into()); self.brillig_context - .allocate_array_instruction(vector.pointer, vector.size); - self.brillig_context.usize_const(vector.rc, 1_usize.into()); + .usize_const_instruction(vector.size, array.len().into()); + self.brillig_context + .codegen_allocate_array(vector.pointer, vector.size); + self.brillig_context.usize_const_instruction(vector.rc, 1_usize.into()); vector.pointer } @@ -1395,16 +1583,16 @@ impl<'block> BrilligBlock<'block> { // Allocate a register for the iterator let iterator_register = - self.brillig_context.make_usize_constant(0_usize.into()); + self.brillig_context.make_usize_constant_instruction(0_usize.into()); for element_id in array.iter() { let element_variable = self.convert_ssa_value(*element_id, dfg); // Store the item in memory self.store_variable_in_array(pointer, iterator_register, element_variable); // Increment the iterator - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( iterator_register.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, 1, ); } @@ -1466,8 +1654,8 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.brillig_context.allocate_fixed_length_array(array.pointer, array.size); - self.brillig_context.usize_const(array.rc, 1_usize.into()); + self.brillig_context.codegen_allocate_fixed_length_array(array.pointer, array.size); + self.brillig_context.usize_const_instruction(array.rc, 1_usize.into()); variable } @@ -1483,8 +1671,8 @@ impl<'block> BrilligBlock<'block> { // Set the pointer to the current stack frame // The stack pointer will then be updated by the caller of this method // once the external call is resolved and the array size is known - self.brillig_context.set_array_pointer(vector.pointer); - self.brillig_context.usize_const(vector.rc, 1_usize.into()); + self.brillig_context.load_free_memory_pointer_instruction(vector.pointer); + self.brillig_context.usize_const_instruction(vector.rc, 1_usize.into()); variable } @@ -1508,13 +1696,14 @@ impl<'block> BrilligBlock<'block> { match array_variable { BrilligVariable::BrilligArray(BrilligArray { size, .. }) => { - self.brillig_context.usize_const(result_register, (size / element_size).into()); + self.brillig_context + .usize_const_instruction(result_register, (size / element_size).into()); } BrilligVariable::BrilligVector(BrilligVector { size, .. }) => { - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( size, result_register, - BinaryIntOp::UnsignedDiv, + BrilligBinaryOp::UnsignedDiv, element_size, ); } @@ -1551,67 +1740,3 @@ pub(crate) fn type_of_binary_operation(lhs_type: &Type, rhs_type: &Type) -> Type } } } - -/// Convert an SSA binary operation into: -/// - Brillig Binary Integer Op, if it is a integer type -/// - Brillig Binary Field Op, if it is a field type -pub(crate) fn convert_ssa_binary_op_to_brillig_binary_op( - ssa_op: BinaryOp, - typ: &Type, -) -> (BrilligBinaryOp, bool) { - // First get the bit size and whether its a signed integer, if it is a numeric type - // if it is not,then we return None, indicating that - // it is a Field. - let bit_size_signedness = match typ { - Type::Numeric(numeric_type) => match numeric_type { - NumericType::Signed { bit_size } => Some((bit_size, true)), - NumericType::Unsigned { bit_size } => Some((bit_size, false)), - NumericType::NativeField => None, - }, - _ => unreachable!("only numeric types are allowed in binary operations. References are handled separately"), - }; - - fn binary_op_to_field_op(op: BinaryOp) -> BrilligBinaryOp { - match op { - BinaryOp::Add => BrilligBinaryOp::Field(BinaryFieldOp::Add), - BinaryOp::Sub => BrilligBinaryOp::Field(BinaryFieldOp::Sub), - BinaryOp::Mul => BrilligBinaryOp::Field(BinaryFieldOp::Mul), - BinaryOp::Div => BrilligBinaryOp::Field(BinaryFieldOp::Div), - BinaryOp::Eq => BrilligBinaryOp::Field(BinaryFieldOp::Equals), - _ => unreachable!( - "Field type cannot be used with {op}. This should have been caught by the frontend" - ), - } - } - - fn binary_op_to_int_op(op: BinaryOp, is_signed: bool) -> BrilligBinaryOp { - let operation = match op { - BinaryOp::Add => BinaryIntOp::Add, - BinaryOp::Sub => BinaryIntOp::Sub, - BinaryOp::Mul => BinaryIntOp::Mul, - BinaryOp::Div => { - if is_signed { - BinaryIntOp::SignedDiv - } else { - BinaryIntOp::UnsignedDiv - } - } - BinaryOp::Mod => return BrilligBinaryOp::Modulo { is_signed_integer: is_signed }, - BinaryOp::Eq => BinaryIntOp::Equals, - BinaryOp::Lt => BinaryIntOp::LessThan, - BinaryOp::And => BinaryIntOp::And, - BinaryOp::Or => BinaryIntOp::Or, - BinaryOp::Xor => BinaryIntOp::Xor, - BinaryOp::Shl => BinaryIntOp::Shl, - BinaryOp::Shr => BinaryIntOp::Shr, - }; - - BrilligBinaryOp::Integer(operation) - } - - // If bit size is available then it is a binary integer operation - match bit_size_signedness { - Some((_, is_signed)) => (binary_op_to_int_op(ssa_op, is_signed), is_signed), - None => (binary_op_to_field_op(ssa_op), false), - } -} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index 93c4b1a5042..26b21e918ff 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -65,7 +65,7 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { }, //q = a/b is set into register (2) BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::UnsignedDiv, + op: BinaryIntOp::Div, lhs: MemoryAddress::from(0), rhs: MemoryAddress::from(1), destination: MemoryAddress::from(2), diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 969f95cff20..98dd17ce080 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -1,7 +1,6 @@ -use acvm::brillig_vm::brillig::BinaryIntOp; - -use crate::brillig::brillig_ir::brillig_variable::{ - BrilligVariable, BrilligVector, SingleAddrVariable, +use crate::brillig::brillig_ir::{ + brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, + BrilligBinaryOp, }; use super::brillig_block::BrilligBlock; @@ -14,30 +13,30 @@ impl<'block> BrilligBlock<'block> { variables_to_insert: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by variables_to_insert.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Add, + BrilligBinaryOp::Add, variables_to_insert.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we copy the source vector into the target vector - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, SingleAddrVariable::new_usize(source_vector.size), ); for (index, variable) in variables_to_insert.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); - self.brillig_context.memory_op( + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); + self.brillig_context.memory_op_instruction( target_index.address, source_vector.size, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.store_variable_in_array(target_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -51,27 +50,27 @@ impl<'block> BrilligBlock<'block> { variables_to_insert: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by variables_to_insert.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Add, + BrilligBinaryOp::Add, variables_to_insert.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we offset the target pointer by variables_to_insert.len() let destination_copy_pointer = self.brillig_context.allocate_register(); - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( target_vector.pointer, destination_copy_pointer, - BinaryIntOp::Add, + BrilligBinaryOp::Add, variables_to_insert.len(), ); // Now we copy the source vector into the target vector starting at index variables_to_insert.len() - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, destination_copy_pointer, SingleAddrVariable::new_usize(source_vector.size), @@ -79,7 +78,7 @@ impl<'block> BrilligBlock<'block> { // Then we write the items to insert at the start for (index, variable) in variables_to_insert.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); self.store_variable_in_array(target_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); } @@ -94,34 +93,34 @@ impl<'block> BrilligBlock<'block> { removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, removed_items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we offset the source pointer by removed_items.len() let source_copy_pointer = self.brillig_context.allocate_register(); - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.pointer, source_copy_pointer, - BinaryIntOp::Add, + BrilligBinaryOp::Add, removed_items.len(), ); // Now we copy the source vector starting at index removed_items.len() into the target vector - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_copy_pointer, target_vector.pointer, SingleAddrVariable::new_usize(target_vector.size), ); for (index, variable) in removed_items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); } @@ -136,30 +135,30 @@ impl<'block> BrilligBlock<'block> { removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, removed_items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Now we copy all elements except the last items into the target vector - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, SingleAddrVariable::new_usize(target_vector.size), ); for (index, variable) in removed_items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(index.into()); - self.brillig_context.memory_op( + let target_index = self.brillig_context.make_usize_constant_instruction(index.into()); + self.brillig_context.memory_op_instruction( target_index.address, target_vector.size, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -174,18 +173,18 @@ impl<'block> BrilligBlock<'block> { items: &[BrilligVariable], ) { // First we need to allocate the target vector incrementing the size by items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Add, + BrilligBinaryOp::Add, items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, index, @@ -193,38 +192,38 @@ impl<'block> BrilligBlock<'block> { // Compute the source pointer just at the index let source_pointer_at_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.pointer, index.address, source_pointer_at_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); // Compute the target pointer after the inserted elements let target_pointer_after_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( target_vector.pointer, index.address, target_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( target_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, items.len(), ); // Compute the number of elements to the right of the index let item_count = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.size, index.address, item_count, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, ); // Copy the elements to the right of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_pointer_at_index, target_pointer_after_index, SingleAddrVariable::new_usize(item_count), @@ -232,12 +231,13 @@ impl<'block> BrilligBlock<'block> { // Write the items to insert starting at the index for (subitem_index, variable) in items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(subitem_index.into()); - self.brillig_context.memory_op( + let target_index = + self.brillig_context.make_usize_constant_instruction(subitem_index.into()); + self.brillig_context.memory_op_instruction( target_index.address, index.address, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.store_variable_in_array(target_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -256,18 +256,18 @@ impl<'block> BrilligBlock<'block> { removed_items: &[BrilligVariable], ) { // First we need to allocate the target vector decrementing the size by removed_items.len() - self.brillig_context.usize_op( + self.brillig_context.codegen_usize_op( source_vector.size, target_vector.size, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, removed_items.len(), ); - self.brillig_context.allocate_array_instruction(target_vector.pointer, target_vector.size); + self.brillig_context.codegen_allocate_array(target_vector.pointer, target_vector.size); // We initialize the RC of the target vector to 1 - self.brillig_context.usize_const(target_vector.rc, 1_usize.into()); + self.brillig_context.usize_const_instruction(target_vector.rc, 1_usize.into()); // Copy the elements to the left of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_vector.pointer, target_vector.pointer, index, @@ -275,39 +275,43 @@ impl<'block> BrilligBlock<'block> { // Compute the source pointer after the removed items let source_pointer_after_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.pointer, index.address, source_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); - self.brillig_context.usize_op_in_place( + self.brillig_context.codegen_usize_op_in_place( source_pointer_after_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, removed_items.len(), ); // Compute the target pointer at the index let target_pointer_at_index = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( target_vector.pointer, index.address, target_pointer_at_index, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); // Compute the number of elements to the right of the index let item_count = self.brillig_context.allocate_register(); - self.brillig_context.memory_op( + self.brillig_context.memory_op_instruction( source_vector.size, index.address, item_count, - BinaryIntOp::Sub, + BrilligBinaryOp::Sub, + ); + self.brillig_context.codegen_usize_op_in_place( + item_count, + BrilligBinaryOp::Sub, + removed_items.len(), ); - self.brillig_context.usize_op_in_place(item_count, BinaryIntOp::Sub, removed_items.len()); // Copy the elements to the right of the index - self.brillig_context.copy_array_instruction( + self.brillig_context.codegen_copy_array( source_pointer_after_index, target_pointer_at_index, SingleAddrVariable::new_usize(item_count), @@ -315,12 +319,13 @@ impl<'block> BrilligBlock<'block> { // Get the removed items for (subitem_index, variable) in removed_items.iter().enumerate() { - let target_index = self.brillig_context.make_usize_constant(subitem_index.into()); - self.brillig_context.memory_op( + let target_index = + self.brillig_context.make_usize_constant_instruction(subitem_index.into()); + self.brillig_context.memory_op_instruction( target_index.address, index.address, target_index.address, - BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.retrieve_variable_from_array(source_vector.pointer, target_index, *variable); self.brillig_context.deallocate_single_addr(target_index); @@ -338,7 +343,7 @@ impl<'block> BrilligBlock<'block> { match source_variable { BrilligVariable::BrilligVector(source_vector) => source_vector, BrilligVariable::BrilligArray(source_array) => { - self.brillig_context.array_to_vector(&source_array) + self.brillig_context.array_to_vector_instruction(&source_array) } _ => unreachable!("ICE: unsupported slice push back source {:?}", source_variable), } @@ -425,7 +430,7 @@ mod tests { }; // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -450,7 +455,7 @@ mod tests { ); } - context.return_instruction(&[target_vector.pointer, target_vector.rc]); + context.codegen_return(&[target_vector.pointer, target_vector.rc]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let (vm, return_data_offset, return_data_size) = @@ -518,7 +523,7 @@ mod tests { }; // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -547,7 +552,7 @@ mod tests { ); } - context.return_instruction(&[ + context.codegen_return(&[ target_vector.pointer, target_vector.rc, removed_item.address, @@ -620,7 +625,7 @@ mod tests { ); // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -638,7 +643,7 @@ mod tests { &[BrilligVariable::SingleAddr(item_to_insert)], ); - context.return_instruction(&[target_vector.pointer, target_vector.rc]); + context.codegen_return(&[target_vector.pointer, target_vector.rc]); let calldata = array.into_iter().chain(vec![item]).chain(vec![index]).collect(); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; @@ -741,7 +746,7 @@ mod tests { ); // Cast the source array to a vector - let source_vector = context.array_to_vector(&array_variable); + let source_vector = context.array_to_vector_instruction(&array_variable); // Allocate the results let target_vector = BrilligVector { @@ -763,7 +768,7 @@ mod tests { &[BrilligVariable::SingleAddr(removed_item)], ); - context.return_instruction(&[ + context.codegen_return(&[ target_vector.pointer, target_vector.size, removed_item.address, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 2a96965171b..9138f57083a 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -4,28 +4,30 @@ //! `brillig_gen` is therefore the module which combines both //! ssa types and types in this module. //! A similar paradigm can be seen with the `acir_ir` module. +//! +//! The brillig ir provides instructions and codegens. +//! The instructions are low level operations that are printed via debug_show. +//! They should emit few opcodes. Codegens on the other hand orchestrate the +//! low level instructions to emit the desired high level operation. pub(crate) mod artifact; pub(crate) mod brillig_variable; pub(crate) mod debug_show; pub(crate) mod registers; +mod codegen_binary; +mod codegen_calls; +mod codegen_control_flow; +mod codegen_intrinsic; +mod codegen_memory; +mod codegen_stack; mod entry_point; +mod instructions; -use crate::ssa::ir::dfg::CallStack; +pub(crate) use instructions::BrilligBinaryOp; -use self::{ - artifact::{BrilligArtifact, UnresolvedJumpLocation}, - brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, - registers::BrilligRegistersContext, -}; -use acvm::{ - acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, MemoryAddress, Opcode as BrilligOpcode, Value, - ValueOrArray, - }, - brillig_vm::brillig::HeapValueType, - FieldElement, -}; +use self::{artifact::BrilligArtifact, registers::BrilligRegistersContext}; +use crate::ssa::ir::dfg::CallStack; +use acvm::acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}; use debug_show::DebugShow; /// The Brillig VM does not apply a limit to the memory address space, @@ -35,8 +37,8 @@ pub(crate) const BRILLIG_MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; // Registers reserved in runtime for special purposes. pub(crate) enum ReservedRegisters { - /// This register stores the stack pointer. Allocations must be done after this pointer. - StackPointer = 0, + /// This register stores the free memory pointer. Allocations must be done after this pointer. + FreeMemoryPointer = 0, /// This register stores the previous stack pointer. The registers of the caller are stored here. PreviousStackPointer = 1, } @@ -53,9 +55,9 @@ impl ReservedRegisters { Self::NUM_RESERVED_REGISTERS } - /// Returns the stack pointer register. This will get used to allocate memory in runtime. - pub(crate) fn stack_pointer() -> MemoryAddress { - MemoryAddress::from(ReservedRegisters::StackPointer as usize) + /// Returns the free memory pointer register. This will get used to allocate memory in runtime. + pub(crate) fn free_memory_pointer() -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::FreeMemoryPointer as usize) } /// Returns the previous stack pointer register. This will be used to restore the registers after a fn call. @@ -99,12 +101,8 @@ impl BrilligContext { } } - pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { - self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); - } - /// Adds a brillig instruction to the brillig byte code - pub(crate) fn push_opcode(&mut self, opcode: BrilligOpcode) { + fn push_opcode(&mut self, opcode: BrilligOpcode) { self.obj.push_opcode(opcode); } @@ -113,1054 +111,24 @@ impl BrilligContext { self.obj } - /// Allocates an array of size `size` and stores the pointer to the array - /// in `pointer_register` - pub(crate) fn allocate_fixed_length_array( - &mut self, - pointer_register: MemoryAddress, - size: usize, - ) { - // debug_show handled by allocate_array_instruction - let size_register = self.make_usize_constant(size.into()); - self.allocate_array_instruction(pointer_register, size_register.address); - self.deallocate_single_addr(size_register); - } - - /// Allocates an array of size contained in size_register and stores the - /// pointer to the array in `pointer_register` - pub(crate) fn allocate_array_instruction( - &mut self, - pointer_register: MemoryAddress, - size_register: MemoryAddress, - ) { - self.debug_show.allocate_array_instruction(pointer_register, size_register); - self.set_array_pointer(pointer_register); - self.update_stack_pointer(size_register); - } - - pub(crate) fn set_array_pointer(&mut self, pointer_register: MemoryAddress) { - self.debug_show.mov_instruction(pointer_register, ReservedRegisters::stack_pointer()); - self.push_opcode(BrilligOpcode::Mov { - destination: pointer_register, - source: ReservedRegisters::stack_pointer(), - }); - } - - pub(crate) fn update_stack_pointer(&mut self, size_register: MemoryAddress) { - self.memory_op( - ReservedRegisters::stack_pointer(), - size_register, - ReservedRegisters::stack_pointer(), - BinaryIntOp::Add, - ); - } - - /// Allocates a variable in memory and stores the - /// pointer to the array in `pointer_register` - fn allocate_variable_reference_instruction( - &mut self, - pointer_register: MemoryAddress, - size: usize, - ) { - self.debug_show.allocate_instruction(pointer_register); - // A variable can be stored in up to three values, so we reserve three values for that. - let size_register = self.make_usize_constant(size.into()); - self.push_opcode(BrilligOpcode::Mov { - destination: pointer_register, - source: ReservedRegisters::stack_pointer(), - }); - self.memory_op( - ReservedRegisters::stack_pointer(), - size_register.address, - ReservedRegisters::stack_pointer(), - BinaryIntOp::Add, - ); - self.deallocate_single_addr(size_register); - } - - pub(crate) fn allocate_single_addr_reference_instruction( - &mut self, - pointer_register: MemoryAddress, - ) { - self.allocate_variable_reference_instruction(pointer_register, 1); - } - - pub(crate) fn allocate_array_reference_instruction(&mut self, pointer_register: MemoryAddress) { - self.allocate_variable_reference_instruction( - pointer_register, - BrilligArray::registers_count(), - ); - } - - pub(crate) fn allocate_vector_reference_instruction( - &mut self, - pointer_register: MemoryAddress, - ) { - self.allocate_variable_reference_instruction( - pointer_register, - BrilligVector::registers_count(), - ); - } - - /// Gets the value in the array at index `index` and stores it in `result` - pub(crate) fn array_get( - &mut self, - array_ptr: MemoryAddress, - index: SingleAddrVariable, - result: MemoryAddress, - ) { - assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); - self.debug_show.array_get(array_ptr, index.address, result); - // Computes array_ptr + index, ie array[index] - let index_of_element_in_memory = self.allocate_register(); - self.memory_op(array_ptr, index.address, index_of_element_in_memory, BinaryIntOp::Add); - self.load_instruction(result, index_of_element_in_memory); - // Free up temporary register - self.deallocate_register(index_of_element_in_memory); - } - - /// Sets the item in the array at index `index` to `value` - pub(crate) fn array_set( - &mut self, - array_ptr: MemoryAddress, - index: SingleAddrVariable, - value: MemoryAddress, - ) { - assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); - self.debug_show.array_set(array_ptr, index.address, value); - // Computes array_ptr + index, ie array[index] - let index_of_element_in_memory = self.allocate_register(); - self.binary_instruction( - SingleAddrVariable::new_usize(array_ptr), - index, - SingleAddrVariable::new_usize(index_of_element_in_memory), - BrilligBinaryOp::Integer(BinaryIntOp::Add), - ); - - self.store_instruction(index_of_element_in_memory, value); - // Free up temporary register - self.deallocate_register(index_of_element_in_memory); - } - - /// Copies the values of an array pointed by source with length stored in `num_elements_register` - /// Into the array pointed by destination - pub(crate) fn copy_array_instruction( - &mut self, - source_pointer: MemoryAddress, - destination_pointer: MemoryAddress, - num_elements_variable: SingleAddrVariable, - ) { - assert!(num_elements_variable.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); - self.debug_show.copy_array_instruction( - source_pointer, - destination_pointer, - num_elements_variable.address, - ); - - let value_register = self.allocate_register(); - - self.loop_instruction(num_elements_variable.address, |ctx, iterator| { - ctx.array_get(source_pointer, iterator, value_register); - ctx.array_set(destination_pointer, iterator, value_register); - }); - - self.deallocate_register(value_register); - } - - /// This instruction will issue a loop that will iterate iteration_count times - /// The body of the loop should be issued by the caller in the on_iteration closure. - pub(crate) fn loop_instruction(&mut self, iteration_count: MemoryAddress, on_iteration: F) - where - F: FnOnce(&mut BrilligContext, SingleAddrVariable), - { - let iterator_register = self.make_usize_constant(0_u128.into()); - - let (loop_section, loop_label) = self.reserve_next_section_label(); - self.enter_section(loop_section); - - // Loop body - - // Check if iterator < iteration_count - let iterator_less_than_iterations = - SingleAddrVariable { address: self.allocate_register(), bit_size: 1 }; - - self.memory_op( - iterator_register.address, - iteration_count, - iterator_less_than_iterations.address, - BinaryIntOp::LessThan, - ); - - let (exit_loop_section, exit_loop_label) = self.reserve_next_section_label(); - - self.not_instruction(iterator_less_than_iterations, iterator_less_than_iterations); - - self.jump_if_instruction(iterator_less_than_iterations.address, exit_loop_label); - - // Call the on iteration function - on_iteration(self, iterator_register); - - // Increment the iterator register - self.usize_op_in_place(iterator_register.address, BinaryIntOp::Add, 1); - - self.jump_instruction(loop_label); - - // Exit the loop - self.enter_section(exit_loop_section); - - // Deallocate our temporary registers - self.deallocate_single_addr(iterator_less_than_iterations); - self.deallocate_single_addr(iterator_register); - } - - /// This instruction will issue an if-then branch that will check if the condition is true - /// and if so, perform the instructions given in `f(self, true)` and otherwise perform the - /// instructions given in `f(self, false)`. A boolean is passed instead of two separate - /// functions to allow the given function to mutably alias its environment. - pub(crate) fn branch_instruction( - &mut self, - condition: MemoryAddress, - mut f: impl FnMut(&mut BrilligContext, bool), - ) { - // Reserve 3 sections - let (then_section, then_label) = self.reserve_next_section_label(); - let (otherwise_section, otherwise_label) = self.reserve_next_section_label(); - let (end_section, end_label) = self.reserve_next_section_label(); - - self.jump_if_instruction(condition, then_label.clone()); - self.jump_instruction(otherwise_label.clone()); - - self.enter_section(then_section); - f(self, true); - self.jump_instruction(end_label.clone()); - - self.enter_section(otherwise_section); - f(self, false); - self.jump_instruction(end_label.clone()); - - self.enter_section(end_section); - } - - /// This instruction issues a branch that jumps over the code generated by the given function if the condition is truthy - pub(crate) fn if_not_instruction( - &mut self, - condition: MemoryAddress, - f: impl FnOnce(&mut BrilligContext), - ) { - let (end_section, end_label) = self.reserve_next_section_label(); - - self.jump_if_instruction(condition, end_label.clone()); - - f(self); - - self.enter_section(end_section); - } - - /// Adds a label to the next opcode - pub(crate) fn enter_context(&mut self, label: T) { - self.debug_show.enter_context(label.to_string()); - self.context_label = label.to_string(); - self.section_label = 0; - // Add a context label to the next opcode - self.obj.add_label_at_position(label.to_string(), self.obj.index_of_next_opcode()); - // Add a section label to the next opcode - self.obj - .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); - } - - /// Enter the given section - fn enter_section(&mut self, section: usize) { - self.section_label = section; - self.obj - .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); - } - - /// Create, reserve, and return a new section label. - fn reserve_next_section_label(&mut self) -> (usize, String) { - let section = self.next_section; - self.next_section += 1; - (section, self.compute_section_label(section)) - } - - /// Internal function used to compute the section labels - fn compute_section_label(&self, section: usize) -> String { - format!("{}-{}", self.context_label, section) - } - - /// Returns the current section label - fn current_section_label(&self) -> String { - self.compute_section_label(self.section_label) - } - - /// Adds a unresolved `Jump` instruction to the bytecode. - pub(crate) fn jump_instruction(&mut self, target_label: T) { - self.debug_show.jump_instruction(target_label.to_string()); - self.add_unresolved_jump(BrilligOpcode::Jump { location: 0 }, target_label.to_string()); - } - - /// Adds a unresolved `JumpIf` instruction to the bytecode. - pub(crate) fn jump_if_instruction( - &mut self, - condition: MemoryAddress, - target_label: T, - ) { - self.debug_show.jump_if_instruction(condition, target_label.to_string()); - self.add_unresolved_jump( - BrilligOpcode::JumpIf { condition, location: 0 }, - target_label.to_string(), - ); - } - - /// Adds a unresolved `Jump` instruction to the bytecode. - fn add_unresolved_jump( - &mut self, - jmp_instruction: BrilligOpcode, - destination: UnresolvedJumpLocation, - ) { - self.obj.add_unresolved_jump(jmp_instruction, destination); - } - - /// Allocates an unused register. - pub(crate) fn allocate_register(&mut self) -> MemoryAddress { - self.registers.allocate_register() - } - - /// Push a register to the deallocation list, ready for reuse. - pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { - self.registers.deallocate_register(register_index); - } - - /// Deallocates the address where the single address variable is stored - pub(crate) fn deallocate_single_addr(&mut self, var: SingleAddrVariable) { - self.deallocate_register(var.address); - } -} - -impl BrilligContext { - /// Emits brillig bytecode to jump to a trap condition if `condition` - /// is false. - pub(crate) fn constrain_instruction( - &mut self, - condition: SingleAddrVariable, - assert_message: Option, - ) { - assert!(condition.bit_size == 1); - self.debug_show.constrain_instruction(condition.address); - let (next_section, next_label) = self.reserve_next_section_label(); - self.add_unresolved_jump( - BrilligOpcode::JumpIf { condition: condition.address, location: 0 }, - next_label, - ); - self.push_opcode(BrilligOpcode::Trap); - if let Some(assert_message) = assert_message { - self.obj.add_assert_message_to_last_opcode(assert_message); - } - self.enter_section(next_section); - } - - /// Processes a return instruction. - /// - /// For Brillig, the return is implicit, since there is no explicit return instruction. - /// The caller will take `N` values from the Register starting at register index 0. - /// `N` indicates the number of return values expected. - /// - /// Brillig does not have an explicit return instruction, so this - /// method will move all register values to the first `N` values in - /// the VM. - pub(crate) fn return_instruction(&mut self, return_registers: &[MemoryAddress]) { - self.debug_show.return_instruction(return_registers); - let mut sources = Vec::with_capacity(return_registers.len()); - let mut destinations = Vec::with_capacity(return_registers.len()); - - for (destination_index, return_register) in return_registers.iter().enumerate() { - // In case we have fewer return registers than indices to write to, ensure we've allocated this register - let destination_register = ReservedRegisters::user_register_index(destination_index); - self.registers.ensure_register_is_allocated(destination_register); - sources.push(*return_register); - destinations.push(destination_register); - } - destinations - .iter() - .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); - self.mov_registers_to_registers_instruction(sources, destinations); - self.stop_instruction(); - } - - /// This function moves values from a set of registers to another set of registers. - /// It first moves all sources to new allocated registers to avoid overwriting. - pub(crate) fn mov_registers_to_registers_instruction( - &mut self, - sources: Vec, - destinations: Vec, - ) { - let new_sources: Vec<_> = sources - .iter() - .map(|source| { - let new_source = self.allocate_register(); - self.mov_instruction(new_source, *source); - new_source - }) - .collect(); - for (new_source, destination) in new_sources.iter().zip(destinations.iter()) { - self.mov_instruction(*destination, *new_source); - self.deallocate_register(*new_source); - } - } - - /// Emits a `mov` instruction. - /// - /// Copies the value at `source` into `destination` - pub(crate) fn mov_instruction(&mut self, destination: MemoryAddress, source: MemoryAddress) { - self.debug_show.mov_instruction(destination, source); - self.push_opcode(BrilligOpcode::Mov { destination, source }); - } - - /// Cast truncates the value to the given bit size and converts the type of the value in memory to that bit size. - pub(crate) fn cast_instruction( - &mut self, - destination: SingleAddrVariable, - source: SingleAddrVariable, - ) { - self.debug_show.cast_instruction(destination.address, source.address, destination.bit_size); - self.push_opcode(BrilligOpcode::Cast { - destination: destination.address, - source: source.address, - bit_size: destination.bit_size, - }); - } - - fn binary_result_bit_size(operation: BrilligBinaryOp, arguments_bit_size: u32) -> u32 { - match operation { - BrilligBinaryOp::Field(BinaryFieldOp::Equals) - | BrilligBinaryOp::Integer(BinaryIntOp::Equals) - | BrilligBinaryOp::Integer(BinaryIntOp::LessThan) - | BrilligBinaryOp::Integer(BinaryIntOp::LessThanEquals) => 1, - _ => arguments_bit_size, - } - } - - /// Processes a binary instruction according `operation`. - /// - /// This method will compute lhs rhs - /// and store the result in the `result` register. - pub(crate) fn binary_instruction( - &mut self, - lhs: SingleAddrVariable, - rhs: SingleAddrVariable, - result: SingleAddrVariable, - operation: BrilligBinaryOp, - ) { - assert!( - lhs.bit_size == rhs.bit_size, - "Not equal bit size for lhs and rhs: lhs {}, rhs {}", - lhs.bit_size, - rhs.bit_size - ); - let expected_result_bit_size = - BrilligContext::binary_result_bit_size(operation, lhs.bit_size); - assert!( - result.bit_size == expected_result_bit_size, - "Expected result bit size to be {}, got {} for operation {:?}", - expected_result_bit_size, - result.bit_size, - operation - ); - self.debug_show.binary_instruction(lhs.address, rhs.address, result.address, operation); - match operation { - BrilligBinaryOp::Field(op) => { - let opcode = BrilligOpcode::BinaryFieldOp { - op, - destination: result.address, - lhs: lhs.address, - rhs: rhs.address, - }; - self.push_opcode(opcode); - } - BrilligBinaryOp::Integer(op) => { - let opcode = BrilligOpcode::BinaryIntOp { - op, - destination: result.address, - bit_size: lhs.bit_size, - lhs: lhs.address, - rhs: rhs.address, - }; - self.push_opcode(opcode); - } - BrilligBinaryOp::Modulo { is_signed_integer } => { - self.modulo_instruction(result, lhs, rhs, is_signed_integer); - } - } - } - - /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { - self.debug_show.const_instruction(result.address, constant); - - self.push_opcode(BrilligOpcode::Const { - destination: result.address, - value: constant, - bit_size: result.bit_size, - }); - } - - pub(crate) fn usize_const(&mut self, result: MemoryAddress, constant: Value) { - self.const_instruction(SingleAddrVariable::new_usize(result), constant); - } - - /// Processes a not instruction. - /// - /// Not is computed using a subtraction operation as there is no native not instruction - /// in Brillig. - pub(crate) fn not_instruction( - &mut self, - input: SingleAddrVariable, - result: SingleAddrVariable, - ) { - self.debug_show.not_instruction(input.address, input.bit_size, result.address); - // Compile !x as ((-1) - x) - let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) - - FieldElement::one(); - let max = self.make_constant(Value::from(u_max), input.bit_size); - - let opcode = BrilligOpcode::BinaryIntOp { - destination: result.address, - op: BinaryIntOp::Sub, - bit_size: input.bit_size, - lhs: max.address, - rhs: input.address, - }; - self.push_opcode(opcode); - self.deallocate_single_addr(max); - } - - /// Processes a foreign call instruction. - /// - /// Note: the function being called is external and will - /// not be linked during brillig generation. - pub(crate) fn foreign_call_instruction( - &mut self, - func_name: String, - inputs: &[ValueOrArray], - input_value_types: &[HeapValueType], - outputs: &[ValueOrArray], - output_value_types: &[HeapValueType], - ) { - assert!(inputs.len() == input_value_types.len()); - assert!(outputs.len() == output_value_types.len()); - self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); - let opcode = BrilligOpcode::ForeignCall { - function: func_name, - destinations: outputs.to_vec(), - destination_value_types: output_value_types.to_vec(), - inputs: inputs.to_vec(), - input_value_types: input_value_types.to_vec(), - }; - self.push_opcode(opcode); - } - - /// Emits a load instruction - pub(crate) fn load_instruction( - &mut self, - destination: MemoryAddress, - source_pointer: MemoryAddress, - ) { - self.debug_show.load_instruction(destination, source_pointer); - self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); - } - - /// Loads a variable stored previously - pub(crate) fn load_variable_instruction( - &mut self, - destination: BrilligVariable, - variable_pointer: MemoryAddress, - ) { - match destination { - BrilligVariable::SingleAddr(single_addr) => { - self.load_instruction(single_addr.address, variable_pointer); - } - BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { - self.load_instruction(pointer, variable_pointer); - - let rc_pointer = self.allocate_register(); - self.mov_instruction(rc_pointer, variable_pointer); - self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); - - self.load_instruction(rc, rc_pointer); - self.deallocate_register(rc_pointer); - } - BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { - self.load_instruction(pointer, variable_pointer); - - let size_pointer = self.allocate_register(); - self.mov_instruction(size_pointer, variable_pointer); - self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); - - self.load_instruction(size, size_pointer); - self.deallocate_register(size_pointer); - - let rc_pointer = self.allocate_register(); - self.mov_instruction(rc_pointer, variable_pointer); - self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); - - self.load_instruction(rc, rc_pointer); - self.deallocate_register(rc_pointer); - } - } - } - - /// Emits a store instruction - pub(crate) fn store_instruction( - &mut self, - destination_pointer: MemoryAddress, - source: MemoryAddress, - ) { - self.debug_show.store_instruction(destination_pointer, source); - self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); - } - - /// Stores a variable by saving its registers to memory - pub(crate) fn store_variable_instruction( - &mut self, - variable_pointer: MemoryAddress, - source: BrilligVariable, - ) { - match source { - BrilligVariable::SingleAddr(single_addr) => { - self.store_instruction(variable_pointer, single_addr.address); - } - BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { - self.store_instruction(variable_pointer, pointer); - - let rc_pointer: MemoryAddress = self.allocate_register(); - self.mov_instruction(rc_pointer, variable_pointer); - self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 1_usize); - self.store_instruction(rc_pointer, rc); - self.deallocate_register(rc_pointer); - } - BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { - self.store_instruction(variable_pointer, pointer); - - let size_pointer = self.allocate_register(); - self.mov_instruction(size_pointer, variable_pointer); - self.usize_op_in_place(size_pointer, BinaryIntOp::Add, 1_usize); - self.store_instruction(size_pointer, size); - - let rc_pointer: MemoryAddress = self.allocate_register(); - self.mov_instruction(rc_pointer, variable_pointer); - self.usize_op_in_place(rc_pointer, BinaryIntOp::Add, 2_usize); - self.store_instruction(rc_pointer, rc); - - self.deallocate_register(size_pointer); - self.deallocate_register(rc_pointer); - } - } - } - - /// Emits a truncate instruction. - /// - /// Note: Truncation is used as an optimization in the SSA IR - /// for the ACIR generation pass; ACIR gen does not overflow - /// on every integer operation since it would be in-efficient. - /// Instead truncation instructions are emitted as to when a - /// truncation should be done. - /// For Brillig, all integer operations will overflow as its cheap. - /// We currently use cast to truncate: we cast to the required bit size - /// and back to the original bit size. - pub(crate) fn truncate_instruction( - &mut self, - destination_of_truncated_value: SingleAddrVariable, - value_to_truncate: SingleAddrVariable, - bit_size: u32, - ) { - self.debug_show.truncate_instruction( - destination_of_truncated_value.address, - value_to_truncate.address, - bit_size, - ); - assert!( - bit_size <= value_to_truncate.bit_size, - "tried to truncate to a bit size {} greater than the variable size {}", - bit_size, - value_to_truncate.bit_size - ); - - // We cast back and forth to ensure that the value is truncated. - let intermediate_register = - SingleAddrVariable { address: self.allocate_register(), bit_size }; - self.cast_instruction(intermediate_register, value_to_truncate); - self.cast_instruction(destination_of_truncated_value, intermediate_register); - self.deallocate_register(intermediate_register.address); - } - - /// Emits a stop instruction - pub(crate) fn stop_instruction(&mut self) { - self.debug_show.stop_instruction(); - self.push_opcode(BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }); - } - - /// Returns a register which holds the value of a constant - pub(crate) fn make_constant(&mut self, constant: Value, bit_size: u32) -> SingleAddrVariable { - let var = SingleAddrVariable::new(self.allocate_register(), bit_size); - self.const_instruction(var, constant); - var - } - - /// Returns a register which holds the value of an usize constant - pub(crate) fn make_usize_constant(&mut self, constant: Value) -> SingleAddrVariable { - let register = self.allocate_register(); - self.usize_const(register, constant); - SingleAddrVariable::new_usize(register) - } - - /// Computes left % right by emitting the necessary Brillig opcodes. - /// - /// This is done by using the following formula: - /// - /// a % b = a - (b * (a / b)) - /// - /// Brillig does not have an explicit modulo operation, - /// so we must emit multiple opcodes and process it differently - /// to other binary instructions. - pub(crate) fn modulo_instruction( - &mut self, - result: SingleAddrVariable, - left: SingleAddrVariable, - right: SingleAddrVariable, - signed: bool, - ) { - // no debug_show, shown in binary instruction - let scratch_register_i = self.allocate_register(); - let scratch_register_j = self.allocate_register(); - - assert!( - left.bit_size == right.bit_size, - "Not equal bitsize: lhs {}, rhs {}", - left.bit_size, - right.bit_size - ); - let bit_size = left.bit_size; - // i = left / right - self.push_opcode(BrilligOpcode::BinaryIntOp { - op: match signed { - true => BinaryIntOp::SignedDiv, - false => BinaryIntOp::UnsignedDiv, - }, - destination: scratch_register_i, - bit_size, - lhs: left.address, - rhs: right.address, - }); - - // j = i * right - self.push_opcode(BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Mul, - destination: scratch_register_j, - bit_size, - lhs: scratch_register_i, - rhs: right.address, - }); - - // result_register = left - j - self.push_opcode(BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Sub, - destination: result.address, - bit_size, - lhs: left.address, - rhs: scratch_register_j, - }); - // Free scratch registers - self.deallocate_register(scratch_register_i); - self.deallocate_register(scratch_register_j); - } - - /// Adds a unresolved external `Call` instruction to the bytecode. - /// This calls into another function compiled into this brillig artifact. - pub(crate) fn add_external_call_instruction(&mut self, func_label: T) { - self.debug_show.add_external_call_instruction(func_label.to_string()); - self.obj.add_unresolved_external_call( - BrilligOpcode::Call { location: 0 }, - func_label.to_string(), - ); - } - - /// Returns the i'th register after the reserved ones - pub(crate) fn register(&self, i: usize) -> MemoryAddress { - MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) - } - - /// Saves all of the registers that have been used up until this point. - fn save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { - // Save all of the used registers at this point in memory - // because the function call will/may overwrite them. - // - // Note that here it is important that the stack pointer register is at register 0, - // as after the first register save we add to the pointer. - let mut used_registers: Vec<_> = - vars.iter().flat_map(|var| var.extract_registers()).collect(); - - // Also dump the previous stack pointer - used_registers.push(ReservedRegisters::previous_stack_pointer()); - for register in used_registers.iter() { - self.store_instruction(ReservedRegisters::stack_pointer(), *register); - // Add one to our stack pointer - self.usize_op_in_place(ReservedRegisters::stack_pointer(), BinaryIntOp::Add, 1); - } - - // Store the location of our registers in the previous stack pointer - self.mov_instruction( - ReservedRegisters::previous_stack_pointer(), - ReservedRegisters::stack_pointer(), - ); - used_registers - } - - /// Loads all of the registers that have been save by save_all_used_registers. - fn load_all_saved_registers(&mut self, used_registers: &[MemoryAddress]) { - // Load all of the used registers that we saved. - // We do all the reverse operations of save_all_used_registers. - // Iterate our registers in reverse - let iterator_register = self.allocate_register(); - self.mov_instruction(iterator_register, ReservedRegisters::previous_stack_pointer()); - - for register in used_registers.iter().rev() { - // Subtract one from our stack pointer - self.usize_op_in_place(iterator_register, BinaryIntOp::Sub, 1); - self.load_instruction(*register, iterator_register); - } - } - - /// Utility method to perform a binary instruction with a constant value in place - pub(crate) fn usize_op_in_place( - &mut self, - destination: MemoryAddress, - op: BinaryIntOp, - constant: usize, - ) { - self.usize_op(destination, destination, op, constant); - } - - /// Utility method to perform a binary instruction with a constant value - pub(crate) fn usize_op( - &mut self, - operand: MemoryAddress, - destination: MemoryAddress, - op: BinaryIntOp, - constant: usize, - ) { - let const_register = self.make_usize_constant(Value::from(constant)); - self.memory_op(operand, const_register.address, destination, op); - // Mark as no longer used for this purpose, frees for reuse - self.deallocate_single_addr(const_register); - } - - /// Utility method to perform a binary instruction with a memory address - pub(crate) fn memory_op( - &mut self, - lhs: MemoryAddress, - rhs: MemoryAddress, - destination: MemoryAddress, - op: BinaryIntOp, - ) { - self.binary_instruction( - SingleAddrVariable::new_usize(lhs), - SingleAddrVariable::new_usize(rhs), - SingleAddrVariable::new( - destination, - BrilligContext::binary_result_bit_size( - BrilligBinaryOp::Integer(op), - BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - ), - ), - BrilligBinaryOp::Integer(op), - ); - } - - // Used before a call instruction. - // Save all the registers we have used to the stack. - // Move argument values to the front of the register indices. - pub(crate) fn pre_call_save_registers_prep_args( - &mut self, - arguments: &[MemoryAddress], - variables_to_save: &[BrilligVariable], - ) -> Vec { - // Save all the registers we have used to the stack. - let saved_registers = self.save_registers_of_vars(variables_to_save); - - // Move argument values to the front of the registers - // - // This means that the arguments will be in the first `n` registers after - // the number of reserved registers. - let (sources, destinations): (Vec<_>, Vec<_>) = - arguments.iter().enumerate().map(|(i, argument)| (*argument, self.register(i))).unzip(); - destinations - .iter() - .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); - self.mov_registers_to_registers_instruction(sources, destinations); - saved_registers - } - - // Used after a call instruction. - // Move return values to the front of the register indices. - // Load all the registers we have previous saved in save_registers_prep_args. - pub(crate) fn post_call_prep_returns_load_registers( - &mut self, - result_registers: &[MemoryAddress], - saved_registers: &[MemoryAddress], - ) { - // Allocate our result registers and write into them - // We assume the return values of our call are held in 0..num results register indices - let (sources, destinations): (Vec<_>, Vec<_>) = result_registers - .iter() - .enumerate() - .map(|(i, result_register)| (self.register(i), *result_register)) - .unzip(); - sources.iter().for_each(|source| self.registers.ensure_register_is_allocated(*source)); - self.mov_registers_to_registers_instruction(sources, destinations); - - // Restore all the same registers we have, in exact reverse order. - // Note that we have allocated some registers above, which we will not be handling here, - // only restoring registers that were used prior to the call finishing. - // After the call instruction, the stack frame pointer should be back to where we left off, - // so we do our instructions in reverse order. - self.load_all_saved_registers(saved_registers); - } - - /// Utility method to transform a HeapArray to a HeapVector by making a runtime constant with the size. - pub(crate) fn array_to_vector(&mut self, array: &BrilligArray) -> BrilligVector { - let size_register = self.make_usize_constant(array.size.into()); - BrilligVector { size: size_register.address, pointer: array.pointer, rc: array.rc } - } - - /// Issues a blackbox operation. - pub(crate) fn black_box_op_instruction(&mut self, op: BlackBoxOp) { - self.debug_show.black_box_op_instruction(&op); - self.push_opcode(BrilligOpcode::BlackBox(op)); - } - - /// Issues a to_radix instruction. This instruction will write the modulus of the source register - /// And the radix register limb_count times to the target vector. - pub(crate) fn radix_instruction( - &mut self, - source_field: SingleAddrVariable, - target_vector: BrilligVector, - radix: SingleAddrVariable, - limb_count: SingleAddrVariable, - big_endian: bool, - ) { - assert!(source_field.bit_size == FieldElement::max_num_bits()); - assert!(radix.bit_size == 32); - assert!(limb_count.bit_size == 32); - let radix_as_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - self.cast_instruction(radix_as_field, radix); - - self.cast_instruction(SingleAddrVariable::new_usize(target_vector.size), limb_count); - self.usize_const(target_vector.rc, 1_usize.into()); - self.allocate_array_instruction(target_vector.pointer, target_vector.size); - - let shifted_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - self.mov_instruction(shifted_field.address, source_field.address); - - let modulus_field = - SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); - - self.loop_instruction(target_vector.size, |ctx, iterator_register| { - // Compute the modulus - ctx.modulo_instruction(modulus_field, shifted_field, radix_as_field, false); - // Write it - ctx.array_set(target_vector.pointer, iterator_register, modulus_field.address); - // Integer div the field - ctx.binary_instruction( - shifted_field, - radix_as_field, - shifted_field, - BrilligBinaryOp::Integer(BinaryIntOp::UnsignedDiv), - ); - }); - - // Deallocate our temporary registers - self.deallocate_single_addr(shifted_field); - self.deallocate_single_addr(modulus_field); - self.deallocate_single_addr(radix_as_field); - - if big_endian { - self.reverse_vector_in_place_instruction(target_vector); - } - } - - /// This instruction will reverse the order of the elements in a vector. - pub(crate) fn reverse_vector_in_place_instruction(&mut self, vector: BrilligVector) { - let iteration_count = self.allocate_register(); - self.usize_op(vector.size, iteration_count, BinaryIntOp::UnsignedDiv, 2); - - let start_value_register = self.allocate_register(); - let index_at_end_of_array = self.allocate_register(); - let end_value_register = self.allocate_register(); - - self.loop_instruction(iteration_count, |ctx, iterator_register| { - // Load both values - ctx.array_get(vector.pointer, iterator_register, start_value_register); - - // The index at the end of array is size - 1 - iterator - ctx.mov_instruction(index_at_end_of_array, vector.size); - ctx.usize_op_in_place(index_at_end_of_array, BinaryIntOp::Sub, 1); - ctx.memory_op( - index_at_end_of_array, - iterator_register.address, - index_at_end_of_array, - BinaryIntOp::Sub, - ); - - ctx.array_get( - vector.pointer, - SingleAddrVariable::new_usize(index_at_end_of_array), - end_value_register, - ); - - // Write both values - ctx.array_set(vector.pointer, iterator_register, end_value_register); - ctx.array_set( - vector.pointer, - SingleAddrVariable::new_usize(index_at_end_of_array), - start_value_register, - ); - }); - - self.deallocate_register(iteration_count); - self.deallocate_register(start_value_register); - self.deallocate_register(end_value_register); - self.deallocate_register(index_at_end_of_array); - } - /// Sets a current call stack that the next pushed opcodes will be associated with. pub(crate) fn set_call_stack(&mut self, call_stack: CallStack) { self.obj.set_call_stack(call_stack); } } -/// Type to encapsulate the binary operation types in Brillig -#[derive(Clone, Copy, Debug)] -pub(crate) enum BrilligBinaryOp { - Field(BinaryFieldOp), - Integer(BinaryIntOp), - // Modulo operation requires more than one brillig opcode - Modulo { is_signed_integer: bool }, -} - #[cfg(test)] pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, - ValueOrArray, + ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; use acvm::brillig_vm::{VMStatus, VM}; use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; - use crate::brillig::brillig_ir::BrilligContext; + use crate::brillig::brillig_ir::{BrilligBinaryOp, BrilligContext}; use super::artifact::{BrilligParameter, GeneratedBrillig}; use super::{BrilligOpcode, ReservedRegisters}; @@ -1264,14 +232,14 @@ pub(crate) mod tests { // assert(the_sequence.len() == 12); // } let mut context = BrilligContext::new(true); - let r_stack = ReservedRegisters::stack_pointer(); + let r_stack = ReservedRegisters::free_memory_pointer(); // Start stack pointer at 0 - context.usize_const(r_stack, Value::from(ReservedRegisters::len() + 3)); + context.usize_const_instruction(r_stack, Value::from(ReservedRegisters::len() + 3)); let r_input_size = MemoryAddress::from(ReservedRegisters::len()); let r_array_ptr = MemoryAddress::from(ReservedRegisters::len() + 1); let r_output_size = MemoryAddress::from(ReservedRegisters::len() + 2); let r_equality = MemoryAddress::from(ReservedRegisters::len() + 3); - context.usize_const(r_input_size, Value::from(12_usize)); + context.usize_const_instruction(r_input_size, Value::from(12_usize)); // copy our stack frame to r_array_ptr context.mov_instruction(r_array_ptr, r_stack); context.foreign_call_instruction( @@ -1282,9 +250,14 @@ pub(crate) mod tests { &[HeapValueType::Vector { value_types: vec![HeapValueType::Simple] }], ); // push stack frame by r_returned_size - context.memory_op(r_stack, r_output_size, r_stack, BinaryIntOp::Add); + context.memory_op_instruction(r_stack, r_output_size, r_stack, BrilligBinaryOp::Add); // check r_input_size == r_output_size - context.memory_op(r_input_size, r_output_size, r_equality, BinaryIntOp::Equals); + context.memory_op_instruction( + r_input_size, + r_output_size, + r_equality, + BrilligBinaryOp::Equals, + ); // We push a JumpIf and Trap opcode directly as the constrain instruction // uses unresolved jumps which requires a block to be constructed in SSA and // we don't need this for Brillig IR tests diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index b94f8140ddd..b415421dd92 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -1,5 +1,6 @@ -use acvm::brillig_vm::brillig::{ - HeapArray, HeapValueType, HeapVector, MemoryAddress, ValueOrArray, +use acvm::{ + brillig_vm::brillig::{HeapArray, HeapValueType, HeapVector, MemoryAddress, ValueOrArray}, + FieldElement, }; use serde::{Deserialize, Serialize}; @@ -21,6 +22,10 @@ impl SingleAddrVariable { pub(crate) fn new_usize(address: MemoryAddress) -> Self { SingleAddrVariable { address, bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE } } + + pub(crate) fn new_field(address: MemoryAddress) -> Self { + SingleAddrVariable { address, bit_size: FieldElement::max_num_bits() } + } } /// The representation of a noir array in the Brillig IR diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs new file mode 100644 index 00000000000..248a304d820 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -0,0 +1,29 @@ +use acvm::acir::brillig::{MemoryAddress, Value}; + +use super::{instructions::BrilligBinaryOp, BrilligContext}; + +impl BrilligContext { + /// Utility method to perform a binary instruction with a constant value in place + pub(crate) fn codegen_usize_op_in_place( + &mut self, + destination: MemoryAddress, + op: BrilligBinaryOp, + constant: usize, + ) { + self.codegen_usize_op(destination, destination, op, constant); + } + + /// Utility method to perform a binary instruction with a constant value + pub(crate) fn codegen_usize_op( + &mut self, + operand: MemoryAddress, + destination: MemoryAddress, + op: BrilligBinaryOp, + constant: usize, + ) { + let const_register = self.make_usize_constant_instruction(Value::from(constant)); + self.memory_op_instruction(operand, const_register.address, destination, op); + // Mark as no longer used for this purpose, frees for reuse + self.deallocate_single_addr(const_register); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs new file mode 100644 index 00000000000..db65849a6b8 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_calls.rs @@ -0,0 +1,102 @@ +use acvm::acir::brillig::MemoryAddress; + +use super::{ + brillig_variable::BrilligVariable, BrilligBinaryOp, BrilligContext, ReservedRegisters, +}; + +impl BrilligContext { + /// Saves all of the registers that have been used up until this point. + fn codegen_save_registers_of_vars(&mut self, vars: &[BrilligVariable]) -> Vec { + // Save all of the used registers at this point in memory + // because the function call will/may overwrite them. + // + // Note that here it is important that the stack pointer register is at register 0, + // as after the first register save we add to the pointer. + let mut used_registers: Vec<_> = + vars.iter().flat_map(|var| var.extract_registers()).collect(); + + // Also dump the previous stack pointer + used_registers.push(ReservedRegisters::previous_stack_pointer()); + for register in used_registers.iter() { + self.store_instruction(ReservedRegisters::free_memory_pointer(), *register); + // Add one to our stack pointer + self.codegen_usize_op_in_place( + ReservedRegisters::free_memory_pointer(), + BrilligBinaryOp::Add, + 1, + ); + } + + // Store the location of our registers in the previous stack pointer + self.mov_instruction( + ReservedRegisters::previous_stack_pointer(), + ReservedRegisters::free_memory_pointer(), + ); + used_registers + } + + /// Loads all of the registers that have been save by save_all_used_registers. + fn codegen_load_all_saved_registers(&mut self, used_registers: &[MemoryAddress]) { + // Load all of the used registers that we saved. + // We do all the reverse operations of save_all_used_registers. + // Iterate our registers in reverse + let iterator_register = self.allocate_register(); + self.mov_instruction(iterator_register, ReservedRegisters::previous_stack_pointer()); + + for register in used_registers.iter().rev() { + // Subtract one from our stack pointer + self.codegen_usize_op_in_place(iterator_register, BrilligBinaryOp::Sub, 1); + self.load_instruction(*register, iterator_register); + } + } + + // Used before a call instruction. + // Save all the registers we have used to the stack. + // Move argument values to the front of the register indices. + pub(crate) fn codegen_pre_call_save_registers_prep_args( + &mut self, + arguments: &[MemoryAddress], + variables_to_save: &[BrilligVariable], + ) -> Vec { + // Save all the registers we have used to the stack. + let saved_registers = self.codegen_save_registers_of_vars(variables_to_save); + + // Move argument values to the front of the registers + // + // This means that the arguments will be in the first `n` registers after + // the number of reserved registers. + let (sources, destinations): (Vec<_>, Vec<_>) = + arguments.iter().enumerate().map(|(i, argument)| (*argument, self.register(i))).unzip(); + destinations + .iter() + .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); + self.codegen_mov_registers_to_registers(sources, destinations); + saved_registers + } + + // Used after a call instruction. + // Move return values to the front of the register indices. + // Load all the registers we have previous saved in save_registers_prep_args. + pub(crate) fn codegen_post_call_prep_returns_load_registers( + &mut self, + result_registers: &[MemoryAddress], + saved_registers: &[MemoryAddress], + ) { + // Allocate our result registers and write into them + // We assume the return values of our call are held in 0..num results register indices + let (sources, destinations): (Vec<_>, Vec<_>) = result_registers + .iter() + .enumerate() + .map(|(i, result_register)| (self.register(i), *result_register)) + .unzip(); + sources.iter().for_each(|source| self.registers.ensure_register_is_allocated(*source)); + self.codegen_mov_registers_to_registers(sources, destinations); + + // Restore all the same registers we have, in exact reverse order. + // Note that we have allocated some registers above, which we will not be handling here, + // only restoring registers that were used prior to the call finishing. + // After the call instruction, the stack frame pointer should be back to where we left off, + // so we do our instructions in reverse order. + self.codegen_load_all_saved_registers(saved_registers); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs new file mode 100644 index 00000000000..116eaa5103f --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_control_flow.rs @@ -0,0 +1,141 @@ +use acvm::acir::brillig::MemoryAddress; + +use super::{ + brillig_variable::SingleAddrVariable, BrilligBinaryOp, BrilligContext, ReservedRegisters, +}; + +impl BrilligContext { + /// Codegens a return from the current function. + /// + /// For Brillig, the return is implicit, since there is no explicit return instruction. + /// The caller will take `N` values from the Register starting at register index 0. + /// `N` indicates the number of return values expected. + /// + /// Brillig does not have an explicit return instruction, so this + /// method will move all register values to the first `N` values in + /// the VM. + pub(crate) fn codegen_return(&mut self, return_registers: &[MemoryAddress]) { + let mut sources = Vec::with_capacity(return_registers.len()); + let mut destinations = Vec::with_capacity(return_registers.len()); + + for (destination_index, return_register) in return_registers.iter().enumerate() { + // In case we have fewer return registers than indices to write to, ensure we've allocated this register + let destination_register = ReservedRegisters::user_register_index(destination_index); + self.registers.ensure_register_is_allocated(destination_register); + sources.push(*return_register); + destinations.push(destination_register); + } + destinations + .iter() + .for_each(|destination| self.registers.ensure_register_is_allocated(*destination)); + self.codegen_mov_registers_to_registers(sources, destinations); + self.stop_instruction(); + } + + /// This codegen will issue a loop that will iterate iteration_count times + /// The body of the loop should be issued by the caller in the on_iteration closure. + pub(crate) fn codegen_loop(&mut self, iteration_count: MemoryAddress, on_iteration: F) + where + F: FnOnce(&mut BrilligContext, SingleAddrVariable), + { + let iterator_register = self.make_usize_constant_instruction(0_u128.into()); + + let (loop_section, loop_label) = self.reserve_next_section_label(); + self.enter_section(loop_section); + + // Loop body + + // Check if iterator < iteration_count + let iterator_less_than_iterations = + SingleAddrVariable { address: self.allocate_register(), bit_size: 1 }; + + self.memory_op_instruction( + iterator_register.address, + iteration_count, + iterator_less_than_iterations.address, + BrilligBinaryOp::LessThan, + ); + + let (exit_loop_section, exit_loop_label) = self.reserve_next_section_label(); + + self.not_instruction(iterator_less_than_iterations, iterator_less_than_iterations); + + self.jump_if_instruction(iterator_less_than_iterations.address, exit_loop_label); + + // Call the on iteration function + on_iteration(self, iterator_register); + + // Increment the iterator register + self.codegen_usize_op_in_place(iterator_register.address, BrilligBinaryOp::Add, 1); + + self.jump_instruction(loop_label); + + // Exit the loop + self.enter_section(exit_loop_section); + + // Deallocate our temporary registers + self.deallocate_single_addr(iterator_less_than_iterations); + self.deallocate_single_addr(iterator_register); + } + + /// This codegen will issue an if-then branch that will check if the condition is true + /// and if so, perform the instructions given in `f(self, true)` and otherwise perform the + /// instructions given in `f(self, false)`. A boolean is passed instead of two separate + /// functions to allow the given function to mutably alias its environment. + pub(crate) fn codegen_branch( + &mut self, + condition: MemoryAddress, + mut f: impl FnMut(&mut BrilligContext, bool), + ) { + // Reserve 3 sections + let (then_section, then_label) = self.reserve_next_section_label(); + let (otherwise_section, otherwise_label) = self.reserve_next_section_label(); + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, then_label.clone()); + self.jump_instruction(otherwise_label.clone()); + + self.enter_section(then_section); + f(self, true); + self.jump_instruction(end_label.clone()); + + self.enter_section(otherwise_section); + f(self, false); + self.jump_instruction(end_label.clone()); + + self.enter_section(end_section); + } + + /// This codegen issues a branch that jumps over the code generated by the given function if the condition is false + pub(crate) fn codegen_if( + &mut self, + condition: MemoryAddress, + f: impl FnOnce(&mut BrilligContext), + ) { + let (end_section, end_label) = self.reserve_next_section_label(); + let (then_section, then_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, then_label.clone()); + self.jump_instruction(end_label.clone()); + + self.enter_section(then_section); + f(self); + + self.enter_section(end_section); + } + + /// This codegen issues a branch that jumps over the code generated by the given function if the condition is truthy + pub(crate) fn codegen_if_not( + &mut self, + condition: MemoryAddress, + f: impl FnOnce(&mut BrilligContext), + ) { + let (end_section, end_label) = self.reserve_next_section_label(); + + self.jump_if_instruction(condition, end_label.clone()); + + f(self); + + self.enter_section(end_section); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs new file mode 100644 index 00000000000..be262d9dee7 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -0,0 +1,89 @@ +use acvm::FieldElement; + +use crate::brillig::brillig_ir::BrilligBinaryOp; + +use super::{ + brillig_variable::{BrilligVector, SingleAddrVariable}, + BrilligContext, +}; + +impl BrilligContext { + /// Codegens a truncation of a value to the given bit size + pub(crate) fn codegen_truncate( + &mut self, + destination_of_truncated_value: SingleAddrVariable, + value_to_truncate: SingleAddrVariable, + bit_size: u32, + ) { + assert!( + bit_size <= value_to_truncate.bit_size, + "tried to truncate to a bit size {} greater than the variable size {}", + bit_size, + value_to_truncate.bit_size + ); + + // We cast back and forth to ensure that the value is truncated. + let intermediate_register = + SingleAddrVariable { address: self.allocate_register(), bit_size }; + self.cast_instruction(intermediate_register, value_to_truncate); + self.cast_instruction(destination_of_truncated_value, intermediate_register); + self.deallocate_single_addr(intermediate_register); + } + + /// Issues a to_radix instruction. This instruction will write the modulus of the source register + /// And the radix register limb_count times to the target vector. + pub(crate) fn codegen_to_radix( + &mut self, + source_field: SingleAddrVariable, + target_vector: BrilligVector, + radix: SingleAddrVariable, + limb_count: SingleAddrVariable, + big_endian: bool, + ) { + assert!(source_field.bit_size == FieldElement::max_num_bits()); + assert!(radix.bit_size == 32); + assert!(limb_count.bit_size == 32); + let radix_as_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + self.cast_instruction(radix_as_field, radix); + + self.cast_instruction(SingleAddrVariable::new_usize(target_vector.size), limb_count); + self.usize_const_instruction(target_vector.rc, 1_usize.into()); + self.codegen_allocate_array(target_vector.pointer, target_vector.size); + + let shifted_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + self.mov_instruction(shifted_field.address, source_field.address); + + let modulus_field = + SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + + self.codegen_loop(target_vector.size, |ctx, iterator_register| { + // Compute the modulus + ctx.binary_instruction( + shifted_field, + radix_as_field, + modulus_field, + BrilligBinaryOp::Modulo, + ); + // Write it + ctx.codegen_array_set(target_vector.pointer, iterator_register, modulus_field.address); + // Integer div the field + ctx.binary_instruction( + shifted_field, + radix_as_field, + shifted_field, + BrilligBinaryOp::UnsignedDiv, + ); + }); + + // Deallocate our temporary registers + self.deallocate_single_addr(shifted_field); + self.deallocate_single_addr(modulus_field); + self.deallocate_single_addr(radix_as_field); + + if big_endian { + self.codegen_reverse_vector_in_place(target_vector); + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs new file mode 100644 index 00000000000..15761113f51 --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_memory.rs @@ -0,0 +1,255 @@ +use acvm::acir::brillig::MemoryAddress; + +use crate::brillig::brillig_ir::BrilligBinaryOp; + +use super::{ + brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, + BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, +}; + +impl BrilligContext { + /// Allocates an array of size `size` and stores the pointer to the array + /// in `pointer_register` + pub(crate) fn codegen_allocate_fixed_length_array( + &mut self, + pointer_register: MemoryAddress, + size: usize, + ) { + let size_register = self.make_usize_constant_instruction(size.into()); + self.codegen_allocate_array(pointer_register, size_register.address); + self.deallocate_single_addr(size_register); + } + + /// Allocates an array of size contained in size_register and stores the + /// pointer to the array in `pointer_register` + pub(crate) fn codegen_allocate_array( + &mut self, + pointer_register: MemoryAddress, + size_register: MemoryAddress, + ) { + self.load_free_memory_pointer_instruction(pointer_register); + self.increase_free_memory_pointer_instruction(size_register); + } + + /// Allocates a variable in memory and stores the + /// pointer to the array in `pointer_register` + fn codegen_allocate_variable_reference( + &mut self, + pointer_register: MemoryAddress, + size: usize, + ) { + // A variable can be stored in up to three values, so we reserve three values for that. + let size_register = self.make_usize_constant_instruction(size.into()); + self.mov_instruction(pointer_register, ReservedRegisters::free_memory_pointer()); + self.memory_op_instruction( + ReservedRegisters::free_memory_pointer(), + size_register.address, + ReservedRegisters::free_memory_pointer(), + BrilligBinaryOp::Add, + ); + self.deallocate_single_addr(size_register); + } + + pub(crate) fn codegen_allocate_single_addr_reference( + &mut self, + pointer_register: MemoryAddress, + ) { + self.codegen_allocate_variable_reference(pointer_register, 1); + } + + pub(crate) fn codegen_allocate_array_reference(&mut self, pointer_register: MemoryAddress) { + self.codegen_allocate_variable_reference(pointer_register, BrilligArray::registers_count()); + } + + pub(crate) fn codegen_allocate_vector_reference(&mut self, pointer_register: MemoryAddress) { + self.codegen_allocate_variable_reference( + pointer_register, + BrilligVector::registers_count(), + ); + } + + /// Gets the value in the array at index `index` and stores it in `result` + pub(crate) fn codegen_array_get( + &mut self, + array_ptr: MemoryAddress, + index: SingleAddrVariable, + result: MemoryAddress, + ) { + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + // Computes array_ptr + index, ie array[index] + let index_of_element_in_memory = self.allocate_register(); + self.memory_op_instruction( + array_ptr, + index.address, + index_of_element_in_memory, + BrilligBinaryOp::Add, + ); + self.load_instruction(result, index_of_element_in_memory); + // Free up temporary register + self.deallocate_register(index_of_element_in_memory); + } + + /// Sets the item in the array at index `index` to `value` + pub(crate) fn codegen_array_set( + &mut self, + array_ptr: MemoryAddress, + index: SingleAddrVariable, + value: MemoryAddress, + ) { + assert!(index.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + // Computes array_ptr + index, ie array[index] + let index_of_element_in_memory = self.allocate_register(); + self.binary_instruction( + SingleAddrVariable::new_usize(array_ptr), + index, + SingleAddrVariable::new_usize(index_of_element_in_memory), + BrilligBinaryOp::Add, + ); + + self.store_instruction(index_of_element_in_memory, value); + // Free up temporary register + self.deallocate_register(index_of_element_in_memory); + } + + /// Copies the values of an array pointed by source with length stored in `num_elements_register` + /// Into the array pointed by destination + pub(crate) fn codegen_copy_array( + &mut self, + source_pointer: MemoryAddress, + destination_pointer: MemoryAddress, + num_elements_variable: SingleAddrVariable, + ) { + assert!(num_elements_variable.bit_size == BRILLIG_MEMORY_ADDRESSING_BIT_SIZE); + + let value_register = self.allocate_register(); + + self.codegen_loop(num_elements_variable.address, |ctx, iterator| { + ctx.codegen_array_get(source_pointer, iterator, value_register); + ctx.codegen_array_set(destination_pointer, iterator, value_register); + }); + + self.deallocate_register(value_register); + } + + /// Loads a variable stored previously + pub(crate) fn codegen_load_variable( + &mut self, + destination: BrilligVariable, + variable_pointer: MemoryAddress, + ) { + match destination { + BrilligVariable::SingleAddr(single_addr) => { + self.load_instruction(single_addr.address, variable_pointer); + } + BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { + self.load_instruction(pointer, variable_pointer); + + let rc_pointer = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 1_usize); + + self.load_instruction(rc, rc_pointer); + self.deallocate_register(rc_pointer); + } + BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { + self.load_instruction(pointer, variable_pointer); + + let size_pointer = self.allocate_register(); + self.mov_instruction(size_pointer, variable_pointer); + self.codegen_usize_op_in_place(size_pointer, BrilligBinaryOp::Add, 1_usize); + + self.load_instruction(size, size_pointer); + self.deallocate_register(size_pointer); + + let rc_pointer = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 2_usize); + + self.load_instruction(rc, rc_pointer); + self.deallocate_register(rc_pointer); + } + } + } + + /// Stores a variable by saving its registers to memory + pub(crate) fn codegen_store_variable( + &mut self, + variable_pointer: MemoryAddress, + source: BrilligVariable, + ) { + match source { + BrilligVariable::SingleAddr(single_addr) => { + self.store_instruction(variable_pointer, single_addr.address); + } + BrilligVariable::BrilligArray(BrilligArray { pointer, size: _, rc }) => { + self.store_instruction(variable_pointer, pointer); + + let rc_pointer: MemoryAddress = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 1_usize); + self.store_instruction(rc_pointer, rc); + self.deallocate_register(rc_pointer); + } + BrilligVariable::BrilligVector(BrilligVector { pointer, size, rc }) => { + self.store_instruction(variable_pointer, pointer); + + let size_pointer = self.allocate_register(); + self.mov_instruction(size_pointer, variable_pointer); + self.codegen_usize_op_in_place(size_pointer, BrilligBinaryOp::Add, 1_usize); + self.store_instruction(size_pointer, size); + + let rc_pointer: MemoryAddress = self.allocate_register(); + self.mov_instruction(rc_pointer, variable_pointer); + self.codegen_usize_op_in_place(rc_pointer, BrilligBinaryOp::Add, 2_usize); + self.store_instruction(rc_pointer, rc); + + self.deallocate_register(size_pointer); + self.deallocate_register(rc_pointer); + } + } + } + + /// This instruction will reverse the order of the elements in a vector. + pub(crate) fn codegen_reverse_vector_in_place(&mut self, vector: BrilligVector) { + let iteration_count = self.allocate_register(); + self.codegen_usize_op(vector.size, iteration_count, BrilligBinaryOp::UnsignedDiv, 2); + + let start_value_register = self.allocate_register(); + let index_at_end_of_array = self.allocate_register(); + let end_value_register = self.allocate_register(); + + self.codegen_loop(iteration_count, |ctx, iterator_register| { + // Load both values + ctx.codegen_array_get(vector.pointer, iterator_register, start_value_register); + + // The index at the end of array is size - 1 - iterator + ctx.mov_instruction(index_at_end_of_array, vector.size); + ctx.codegen_usize_op_in_place(index_at_end_of_array, BrilligBinaryOp::Sub, 1); + ctx.memory_op_instruction( + index_at_end_of_array, + iterator_register.address, + index_at_end_of_array, + BrilligBinaryOp::Sub, + ); + + ctx.codegen_array_get( + vector.pointer, + SingleAddrVariable::new_usize(index_at_end_of_array), + end_value_register, + ); + + // Write both values + ctx.codegen_array_set(vector.pointer, iterator_register, end_value_register); + ctx.codegen_array_set( + vector.pointer, + SingleAddrVariable::new_usize(index_at_end_of_array), + start_value_register, + ); + }); + + self.deallocate_register(iteration_count); + self.deallocate_register(start_value_register); + self.deallocate_register(end_value_register); + self.deallocate_register(index_at_end_of_array); + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs new file mode 100644 index 00000000000..1c30f0f848f --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_stack.rs @@ -0,0 +1,26 @@ +use acvm::acir::brillig::MemoryAddress; + +use super::BrilligContext; + +impl BrilligContext { + /// This function moves values from a set of registers to another set of registers. + /// It first moves all sources to new allocated registers to avoid overwriting. + pub(crate) fn codegen_mov_registers_to_registers( + &mut self, + sources: Vec, + destinations: Vec, + ) { + let new_sources: Vec<_> = sources + .iter() + .map(|source| { + let new_source = self.allocate_register(); + self.mov_instruction(new_source, *source); + new_source + }) + .collect(); + for (new_source, destination) in new_sources.iter().zip(destinations.iter()) { + self.mov_instruction(*destination, *new_source); + self.deallocate_register(*new_source); + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index e32ce6f6b92..fa99e968a31 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -2,10 +2,7 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; -use acvm::acir::brillig::{ - BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, - ValueOrArray, -}; +use acvm::acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, ValueOrArray}; /// Trait for converting values into debug-friendly strings. trait DebugToString { @@ -26,8 +23,8 @@ default_to_string_impl! { str usize u32 } impl DebugToString for MemoryAddress { fn debug_to_string(&self) -> String { - if *self == ReservedRegisters::stack_pointer() { - "Stack".into() + if *self == ReservedRegisters::free_memory_pointer() { + "FreeMem".into() } else if *self == ReservedRegisters::previous_stack_pointer() { "PrevStack".into() } else { @@ -48,47 +45,23 @@ impl DebugToString for HeapVector { } } -impl DebugToString for BinaryFieldOp { - fn debug_to_string(&self) -> String { - match self { - BinaryFieldOp::Add => "f+".into(), - BinaryFieldOp::Sub => "f-".into(), - BinaryFieldOp::Mul => "f*".into(), - BinaryFieldOp::Div => "f/".into(), - BinaryFieldOp::Equals => "f==".into(), - } - } -} - -impl DebugToString for BinaryIntOp { - fn debug_to_string(&self) -> String { - match self { - BinaryIntOp::Add => "+".into(), - BinaryIntOp::Sub => "-".into(), - BinaryIntOp::Mul => "*".into(), - BinaryIntOp::Equals => "==".into(), - BinaryIntOp::SignedDiv => "/".into(), - BinaryIntOp::UnsignedDiv => "//".into(), - BinaryIntOp::LessThan => "<".into(), - BinaryIntOp::LessThanEquals => "<=".into(), - BinaryIntOp::And => "&&".into(), - BinaryIntOp::Or => "||".into(), - BinaryIntOp::Xor => "^".into(), - BinaryIntOp::Shl => "<<".into(), - BinaryIntOp::Shr => ">>".into(), - } - } -} - impl DebugToString for BrilligBinaryOp { fn debug_to_string(&self) -> String { match self { - BrilligBinaryOp::Field(op) => op.debug_to_string(), - BrilligBinaryOp::Integer(op) => op.debug_to_string(), - BrilligBinaryOp::Modulo { is_signed_integer } => { - let op = if *is_signed_integer { "%" } else { "%%" }; - op.into() - } + BrilligBinaryOp::Add => "+".into(), + BrilligBinaryOp::Sub => "-".into(), + BrilligBinaryOp::Mul => "*".into(), + BrilligBinaryOp::Equals => "==".into(), + BrilligBinaryOp::FieldDiv => "f/".into(), + BrilligBinaryOp::UnsignedDiv => "/".into(), + BrilligBinaryOp::LessThan => "<".into(), + BrilligBinaryOp::LessThanEquals => "<=".into(), + BrilligBinaryOp::And => "&&".into(), + BrilligBinaryOp::Or => "||".into(), + BrilligBinaryOp::Xor => "^".into(), + BrilligBinaryOp::Shl => "<<".into(), + BrilligBinaryOp::Shr => ">>".into(), + BrilligBinaryOp::Modulo => "%".into(), } } } @@ -143,17 +116,6 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " ASSERT {} != 0", condition); } - /// Processes a return instruction. - pub(crate) fn return_instruction(&self, return_registers: &[MemoryAddress]) { - let registers_string = return_registers - .iter() - .map(MemoryAddress::debug_to_string) - .collect::>() - .join(", "); - - debug_println!(self.enable_debug_trace, " // return {};", registers_string); - } - /// Emits a `mov` instruction. pub(crate) fn mov_instruction(&self, destination: MemoryAddress, source: MemoryAddress) { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); @@ -240,64 +202,17 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " STOP"); } - /// Debug function for allocate_array_instruction - pub(crate) fn allocate_array_instruction( - &self, - pointer_register: MemoryAddress, - size_register: MemoryAddress, - ) { - debug_println!( - self.enable_debug_trace, - " ALLOCATE_ARRAY {} SIZE {}", - pointer_register, - size_register - ); - } - - /// Debug function for allocate_instruction - pub(crate) fn allocate_instruction(&self, pointer_register: MemoryAddress) { - debug_println!(self.enable_debug_trace, " ALLOCATE {} ", pointer_register); - } - - /// Debug function for array_get - pub(crate) fn array_get( - &self, - array_ptr: MemoryAddress, - index: MemoryAddress, - result: MemoryAddress, - ) { - debug_println!( - self.enable_debug_trace, - " ARRAY_GET {}[{}] -> {}", - array_ptr, - index, - result - ); - } - - /// Debug function for array_set - pub(crate) fn array_set( - &self, - array_ptr: MemoryAddress, - index: MemoryAddress, - value: MemoryAddress, - ) { - debug_println!(self.enable_debug_trace, " ARRAY_SET {}[{}] = {}", array_ptr, index, value); - } - - /// Debug function for copy_array_instruction - pub(crate) fn copy_array_instruction( + /// Emits a external stop instruction (returns data) + pub(crate) fn external_stop_instruction( &self, - source: MemoryAddress, - destination: MemoryAddress, - num_elements_register: MemoryAddress, + return_data_offset: usize, + return_data_size: usize, ) { debug_println!( self.enable_debug_trace, - " COPY_ARRAY {} -> {} ({} ELEMENTS)", - source, - destination, - num_elements_register + " EXT_STOP {}..{}", + return_data_offset, + return_data_offset + return_data_size ); } @@ -328,22 +243,6 @@ impl DebugShow { ); } - /// Debug function for cast_instruction - pub(crate) fn truncate_instruction( - &self, - destination: MemoryAddress, - source: MemoryAddress, - target_bit_size: u32, - ) { - debug_println!( - self.enable_debug_trace, - " TRUNCATE {} FROM {} TO {} BITS", - destination, - source, - target_bit_size - ); - } - /// Debug function for black_box_op pub(crate) fn black_box_op_instruction(&self, op: &BlackBoxOp) { match op { @@ -529,4 +428,20 @@ impl DebugShow { pub(crate) fn add_external_call_instruction(&self, func_label: String) { debug_println!(self.enable_debug_trace, " CALL {}", func_label); } + + /// Debug function for calldata_copy + pub(crate) fn calldata_copy_instruction( + &self, + destination: MemoryAddress, + calldata_size: usize, + offset: usize, + ) { + debug_println!( + self.enable_debug_trace, + " CALLDATA_COPY {} {}..{}", + destination, + offset, + offset + calldata_size + ); + } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 83440e4a51d..14c4ada8606 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -3,12 +3,9 @@ use super::{ brillig_variable::{BrilligArray, BrilligVariable, SingleAddrVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, - BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, -}; -use acvm::{ - acir::brillig::{MemoryAddress, Opcode as BrilligOpcode}, - FieldElement, + BrilligBinaryOp, BrilligContext, ReservedRegisters, }; +use acvm::{acir::brillig::MemoryAddress, FieldElement}; pub(crate) const MAX_STACK_SIZE: usize = 1024; @@ -28,18 +25,18 @@ impl BrilligContext { debug_show: DebugShow::new(false), }; - context.entry_point_instruction(&arguments, &return_parameters); + context.codegen_entry_point(&arguments, &return_parameters); context.add_external_call_instruction(target_function); - context.exit_point_instruction(&arguments, &return_parameters); + context.codegen_exit_point(&arguments, &return_parameters); context.artifact() } /// Adds the instructions needed to handle entry point parameters /// The runtime will leave the parameters in calldata. /// Arrays will be passed flattened. - fn entry_point_instruction( + fn codegen_entry_point( &mut self, arguments: &[BrilligParameter], return_parameters: &[BrilligParameter], @@ -48,11 +45,10 @@ impl BrilligContext { let return_data_size = BrilligContext::flattened_tuple_size(return_parameters); // Set initial value of stack pointer: MAX_STACK_SIZE + calldata_size + return_data_size - self.push_opcode(BrilligOpcode::Const { - destination: ReservedRegisters::stack_pointer(), - value: (MAX_STACK_SIZE + calldata_size + return_data_size).into(), - bit_size: BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - }); + self.const_instruction( + SingleAddrVariable::new_usize(ReservedRegisters::free_memory_pointer()), + (MAX_STACK_SIZE + calldata_size + return_data_size).into(), + ); // Copy calldata self.copy_and_cast_calldata(arguments); @@ -75,8 +71,8 @@ impl BrilligContext { } BrilligParameter::Array(_, _) => { let pointer_to_the_array_in_calldata = - self.make_usize_constant(current_calldata_pointer.into()); - let rc_register = self.make_usize_constant(1_usize.into()); + self.make_usize_constant_instruction(current_calldata_pointer.into()); + let rc_register = self.make_usize_constant_instruction(1_usize.into()); let flattened_size = BrilligContext::flattened_size(argument); let var = BrilligVariable::BrilligArray(BrilligArray { pointer: pointer_to_the_array_in_calldata.address, @@ -111,11 +107,7 @@ impl BrilligContext { fn copy_and_cast_calldata(&mut self, arguments: &[BrilligParameter]) { let calldata_size = BrilligContext::flattened_tuple_size(arguments); - self.push_opcode(BrilligOpcode::CalldataCopy { - destination_address: MemoryAddress(MAX_STACK_SIZE), - size: calldata_size, - offset: 0, - }); + self.calldata_copy_instruction(MemoryAddress(MAX_STACK_SIZE), calldata_size, 0); fn flat_bit_sizes(param: &BrilligParameter) -> Box + '_> { match param { @@ -130,11 +122,10 @@ impl BrilligContext { for (i, bit_size) in arguments.iter().flat_map(flat_bit_sizes).enumerate() { // Calldatacopy tags everything with field type, so when downcast when necessary if bit_size < FieldElement::max_num_bits() { - self.push_opcode(BrilligOpcode::Cast { - destination: MemoryAddress(MAX_STACK_SIZE + i), - source: MemoryAddress(MAX_STACK_SIZE + i), - bit_size, - }); + self.cast_instruction( + SingleAddrVariable::new(MemoryAddress(MAX_STACK_SIZE + i), bit_size), + SingleAddrVariable::new_field(MemoryAddress(MAX_STACK_SIZE + i)), + ); } } } @@ -178,7 +169,7 @@ impl BrilligContext { let target_item_size = item_type.len(); let source_item_size = BrilligContext::flattened_tuple_size(item_type); - self.allocate_fixed_length_array( + self.codegen_allocate_fixed_length_array( deflattened_array_pointer, item_count * target_item_size, ); @@ -190,20 +181,22 @@ impl BrilligContext { let mut source_offset = 0; for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_usize_constant((source_item_base_index + source_offset).into()); + let source_index = self.make_usize_constant_instruction( + (source_item_base_index + source_offset).into(), + ); - let target_index = - self.make_usize_constant((target_item_base_index + subitem_index).into()); + let target_index = self.make_usize_constant_instruction( + (target_item_base_index + subitem_index).into(), + ); match subitem { BrilligParameter::SingleAddr(_) => { - self.array_get( + self.codegen_array_get( flattened_array_pointer, source_index, movement_register, ); - self.array_set( + self.codegen_array_set( deflattened_array_pointer, target_index, movement_register, @@ -216,11 +209,11 @@ impl BrilligContext { ) => { let nested_array_pointer = self.allocate_register(); self.mov_instruction(nested_array_pointer, flattened_array_pointer); - self.memory_op( + self.memory_op_instruction( nested_array_pointer, source_index.address, nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, + BrilligBinaryOp::Add, ); let deflattened_nested_array_pointer = self.deflatten_array( nested_array_item_type, @@ -229,17 +222,21 @@ impl BrilligContext { ); let reference = self.allocate_register(); let rc = self.allocate_register(); - self.usize_const(rc, 1_usize.into()); + self.usize_const_instruction(rc, 1_usize.into()); - self.allocate_array_reference_instruction(reference); + self.codegen_allocate_array_reference(reference); let array_variable = BrilligVariable::BrilligArray(BrilligArray { pointer: deflattened_nested_array_pointer, size: nested_array_item_type.len() * nested_array_item_count, rc, }); - self.store_variable_instruction(reference, array_variable); + self.codegen_store_variable(reference, array_variable); - self.array_set(deflattened_array_pointer, target_index, reference); + self.codegen_array_set( + deflattened_array_pointer, + target_index, + reference, + ); self.deallocate_register(nested_array_pointer); self.deallocate_register(reference); @@ -272,7 +269,7 @@ impl BrilligContext { /// The runtime expects the results in a contiguous memory region. /// Arrays are expected to be returned with all the nested arrays flattened. /// However, the function called returns variables (that have extra data) and the returned arrays are deflattened. - fn exit_point_instruction( + fn codegen_exit_point( &mut self, arguments: &[BrilligParameter], return_parameters: &[BrilligParameter], @@ -318,7 +315,8 @@ impl BrilligContext { } BrilligParameter::Array(item_type, item_count) => { let returned_pointer = returned_variable.extract_array().pointer; - let pointer_to_return_data = self.make_usize_constant(return_data_index.into()); + let pointer_to_return_data = + self.make_usize_constant_instruction(return_data_index.into()); self.flatten_array( item_type, @@ -336,7 +334,7 @@ impl BrilligContext { } } - self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); + self.external_stop_instruction(return_data_offset, return_data_size); } // Flattens an array by recursively copying nested arrays and regular items. @@ -361,19 +359,21 @@ impl BrilligContext { let mut target_offset = 0; for (subitem_index, subitem) in item_type.iter().enumerate() { - let source_index = - self.make_usize_constant((source_item_base_index + subitem_index).into()); - let target_index = - self.make_usize_constant((target_item_base_index + target_offset).into()); + let source_index = self.make_usize_constant_instruction( + (source_item_base_index + subitem_index).into(), + ); + let target_index = self.make_usize_constant_instruction( + (target_item_base_index + target_offset).into(), + ); match subitem { BrilligParameter::SingleAddr(_) => { - self.array_get( + self.codegen_array_get( deflattened_array_pointer, source_index, movement_register, ); - self.array_set( + self.codegen_array_set( flattened_array_pointer, target_index, movement_register, @@ -385,7 +385,7 @@ impl BrilligContext { nested_array_item_count, ) => { let nested_array_reference = self.allocate_register(); - self.array_get( + self.codegen_array_get( deflattened_array_pointer, source_index, nested_array_reference, @@ -398,7 +398,7 @@ impl BrilligContext { rc: self.allocate_register(), }); - self.load_variable_instruction( + self.codegen_load_variable( nested_array_variable, nested_array_reference, ); @@ -410,11 +410,11 @@ impl BrilligContext { flattened_array_pointer, ); - self.memory_op( + self.memory_op_instruction( flattened_nested_array_pointer, target_index.address, flattened_nested_array_pointer, - acvm::brillig_vm::brillig::BinaryIntOp::Add, + BrilligBinaryOp::Add, ); self.flatten_array( @@ -443,12 +443,9 @@ impl BrilligContext { self.deallocate_register(movement_register); } else { - let item_count = self.make_usize_constant((item_count * item_type.len()).into()); - self.copy_array_instruction( - deflattened_array_pointer, - flattened_array_pointer, - item_count, - ); + let item_count = + self.make_usize_constant_instruction((item_count * item_type.len()).into()); + self.codegen_copy_array(deflattened_array_pointer, flattened_array_pointer, item_count); self.deallocate_single_addr(item_count); } } @@ -493,7 +490,7 @@ mod tests { context.load_instruction(array_pointer, array_pointer); context.load_instruction(array_value, array_pointer); - context.return_instruction(&[array_value]); + context.codegen_return(&[array_value]); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let (vm, return_data_offset, return_data_size) = @@ -531,7 +528,7 @@ mod tests { rc: context.allocate_register(), }; - context.return_instruction(&brillig_array.extract_registers()); + context.codegen_return(&brillig_array.extract_registers()); let bytecode = create_entry_point_bytecode(context, arguments, returns).byte_code; let (vm, return_data_pointer, return_data_size) = diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs new file mode 100644 index 00000000000..5c2a6bfacca --- /dev/null +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -0,0 +1,522 @@ +use acvm::{ + acir::brillig::{ + BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapValueType, MemoryAddress, + Opcode as BrilligOpcode, Value, ValueOrArray, + }, + FieldElement, +}; + +use super::{ + artifact::UnresolvedJumpLocation, + brillig_variable::{BrilligArray, BrilligVector, SingleAddrVariable}, + BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, +}; + +/// Low level instructions of the brillig IR, used by the brillig ir codegens and brillig_gen +/// Printed using debug_slow +impl BrilligContext { + /// Processes a binary instruction according `operation`. + /// + /// This method will compute lhs rhs + /// and store the result in the `result` register. + pub(crate) fn binary_instruction( + &mut self, + lhs: SingleAddrVariable, + rhs: SingleAddrVariable, + result: SingleAddrVariable, + operation: BrilligBinaryOp, + ) { + self.debug_show.binary_instruction(lhs.address, rhs.address, result.address, operation); + self.binary(lhs, rhs, result, operation); + } + + /// Processes a not instruction. + /// + /// Not is computed using a subtraction operation as there is no native not instruction + /// in Brillig. + pub(crate) fn not_instruction( + &mut self, + input: SingleAddrVariable, + result: SingleAddrVariable, + ) { + self.debug_show.not_instruction(input.address, input.bit_size, result.address); + // Compile !x as ((-1) - x) + let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) + - FieldElement::one(); + let max = self.make_constant(Value::from(u_max), input.bit_size); + + self.binary(max, input, result, BrilligBinaryOp::Sub); + self.deallocate_single_addr(max); + } + + /// Utility method to perform a binary instruction with a memory address + pub(crate) fn memory_op_instruction( + &mut self, + lhs: MemoryAddress, + rhs: MemoryAddress, + destination: MemoryAddress, + op: BrilligBinaryOp, + ) { + self.binary_instruction( + SingleAddrVariable::new_usize(lhs), + SingleAddrVariable::new_usize(rhs), + SingleAddrVariable::new( + destination, + BrilligContext::binary_result_bit_size(op, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE), + ), + op, + ); + } + + fn binary( + &mut self, + lhs: SingleAddrVariable, + rhs: SingleAddrVariable, + result: SingleAddrVariable, + operation: BrilligBinaryOp, + ) { + assert!( + lhs.bit_size == rhs.bit_size, + "Not equal bit size for lhs and rhs: lhs {}, rhs {}", + lhs.bit_size, + rhs.bit_size + ); + let is_field_op = lhs.bit_size == FieldElement::max_num_bits(); + let expected_result_bit_size = + BrilligContext::binary_result_bit_size(operation, lhs.bit_size); + assert!( + result.bit_size == expected_result_bit_size, + "Expected result bit size to be {}, got {} for operation {:?}", + expected_result_bit_size, + result.bit_size, + operation + ); + + if let BrilligBinaryOp::Modulo = operation { + self.modulo(result, lhs, rhs); + } else if is_field_op { + self.push_opcode(BrilligOpcode::BinaryFieldOp { + op: operation.into(), + destination: result.address, + lhs: lhs.address, + rhs: rhs.address, + }); + } else { + self.push_opcode(BrilligOpcode::BinaryIntOp { + op: operation.into(), + destination: result.address, + bit_size: lhs.bit_size, + lhs: lhs.address, + rhs: rhs.address, + }); + } + } + + /// Computes left % right by emitting the necessary Brillig opcodes. + /// + /// This is done by using the following formula: + /// + /// a % b = a - (b * (a / b)) + /// + /// Brillig does not have an explicit modulo operation, + /// so we must emit multiple opcodes and process it differently + /// to other binary instructions. + fn modulo( + &mut self, + result: SingleAddrVariable, + left: SingleAddrVariable, + right: SingleAddrVariable, + ) { + assert!( + left.bit_size == right.bit_size, + "Not equal bitsize: lhs {}, rhs {}", + left.bit_size, + right.bit_size + ); + let bit_size = left.bit_size; + + let scratch_var_i = SingleAddrVariable::new(self.allocate_register(), bit_size); + let scratch_var_j = SingleAddrVariable::new(self.allocate_register(), bit_size); + + // i = left / right + self.binary(left, right, scratch_var_i, BrilligBinaryOp::UnsignedDiv); + + // j = i * right + self.binary(scratch_var_i, right, scratch_var_j, BrilligBinaryOp::Mul); + + // result_register = left - j + self.binary(left, scratch_var_j, result, BrilligBinaryOp::Sub); + // Free scratch registers + self.deallocate_single_addr(scratch_var_i); + self.deallocate_single_addr(scratch_var_j); + } + + fn binary_result_bit_size(operation: BrilligBinaryOp, arguments_bit_size: u32) -> u32 { + match operation { + BrilligBinaryOp::Equals + | BrilligBinaryOp::LessThan + | BrilligBinaryOp::LessThanEquals => 1, + _ => arguments_bit_size, + } + } + + /// Processes a foreign call instruction. + /// + /// Note: the function being called is external and will + /// not be linked during brillig generation. + pub(crate) fn foreign_call_instruction( + &mut self, + func_name: String, + inputs: &[ValueOrArray], + input_value_types: &[HeapValueType], + outputs: &[ValueOrArray], + output_value_types: &[HeapValueType], + ) { + self.debug_show.foreign_call_instruction(func_name.clone(), inputs, outputs); + + assert!(inputs.len() == input_value_types.len()); + assert!(outputs.len() == output_value_types.len()); + + self.push_opcode(BrilligOpcode::ForeignCall { + function: func_name, + destinations: outputs.to_vec(), + destination_value_types: output_value_types.to_vec(), + inputs: inputs.to_vec(), + input_value_types: input_value_types.to_vec(), + }); + } + + /// Adds a unresolved external `Call` instruction to the bytecode. + /// This calls into another function compiled into this brillig artifact. + pub(crate) fn add_external_call_instruction(&mut self, func_label: T) { + self.debug_show.add_external_call_instruction(func_label.to_string()); + self.obj.add_unresolved_external_call( + BrilligOpcode::Call { location: 0 }, + func_label.to_string(), + ); + } + + /// Adds a unresolved `Jump` instruction to the bytecode. + pub(crate) fn jump_instruction(&mut self, target_label: T) { + self.debug_show.jump_instruction(target_label.to_string()); + self.add_unresolved_jump(BrilligOpcode::Jump { location: 0 }, target_label.to_string()); + } + + /// Adds a unresolved `JumpIf` instruction to the bytecode. + pub(crate) fn jump_if_instruction( + &mut self, + condition: MemoryAddress, + target_label: T, + ) { + self.debug_show.jump_if_instruction(condition, target_label.to_string()); + self.add_unresolved_jump( + BrilligOpcode::JumpIf { condition, location: 0 }, + target_label.to_string(), + ); + } + + /// Emits brillig bytecode to jump to a trap condition if `condition` + /// is false. + pub(crate) fn constrain_instruction( + &mut self, + condition: SingleAddrVariable, + assert_message: Option, + ) { + self.debug_show.constrain_instruction(condition.address); + + assert!(condition.bit_size == 1); + + let (next_section, next_label) = self.reserve_next_section_label(); + self.add_unresolved_jump( + BrilligOpcode::JumpIf { condition: condition.address, location: 0 }, + next_label, + ); + self.push_opcode(BrilligOpcode::Trap); + if let Some(assert_message) = assert_message { + self.obj.add_assert_message_to_last_opcode(assert_message); + } + self.enter_section(next_section); + } + + /// Adds a unresolved `Jump` to the bytecode. + fn add_unresolved_jump( + &mut self, + jmp_instruction: BrilligOpcode, + destination: UnresolvedJumpLocation, + ) { + self.obj.add_unresolved_jump(jmp_instruction, destination); + } + + /// Adds a label to the next opcode + pub(crate) fn enter_context(&mut self, label: T) { + self.debug_show.enter_context(label.to_string()); + self.context_label = label.to_string(); + self.section_label = 0; + // Add a context label to the next opcode + self.obj.add_label_at_position(label.to_string(), self.obj.index_of_next_opcode()); + // Add a section label to the next opcode + self.obj + .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); + } + + /// Enter the given section + pub(super) fn enter_section(&mut self, section: usize) { + self.section_label = section; + self.obj + .add_label_at_position(self.current_section_label(), self.obj.index_of_next_opcode()); + } + + /// Create, reserve, and return a new section label. + pub(super) fn reserve_next_section_label(&mut self) -> (usize, String) { + let section = self.next_section; + self.next_section += 1; + (section, self.compute_section_label(section)) + } + + /// Internal function used to compute the section labels + fn compute_section_label(&self, section: usize) -> String { + format!("{}-{}", self.context_label, section) + } + + /// Returns the current section label + fn current_section_label(&self) -> String { + self.compute_section_label(self.section_label) + } + + /// Emits a stop instruction + pub(crate) fn stop_instruction(&mut self) { + self.debug_show.stop_instruction(); + self.push_opcode(BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }); + } + + /// Emits a external stop instruction (returns data) + pub(crate) fn external_stop_instruction( + &mut self, + return_data_offset: usize, + return_data_size: usize, + ) { + self.debug_show.external_stop_instruction(return_data_offset, return_data_size); + self.push_opcode(BrilligOpcode::Stop { return_data_offset, return_data_size }); + } + + /// Issues a blackbox operation. + pub(crate) fn black_box_op_instruction(&mut self, op: BlackBoxOp) { + self.debug_show.black_box_op_instruction(&op); + self.push_opcode(BrilligOpcode::BlackBox(op)); + } + + pub(crate) fn load_free_memory_pointer_instruction(&mut self, pointer_register: MemoryAddress) { + self.debug_show.mov_instruction(pointer_register, ReservedRegisters::free_memory_pointer()); + self.push_opcode(BrilligOpcode::Mov { + destination: pointer_register, + source: ReservedRegisters::free_memory_pointer(), + }); + } + + pub(crate) fn increase_free_memory_pointer_instruction( + &mut self, + size_register: MemoryAddress, + ) { + self.memory_op_instruction( + ReservedRegisters::free_memory_pointer(), + size_register, + ReservedRegisters::free_memory_pointer(), + BrilligBinaryOp::Add, + ); + } + + /// Emits a store instruction + pub(crate) fn store_instruction( + &mut self, + destination_pointer: MemoryAddress, + source: MemoryAddress, + ) { + self.debug_show.store_instruction(destination_pointer, source); + self.push_opcode(BrilligOpcode::Store { destination_pointer, source }); + } + + /// Utility method to transform a HeapArray to a HeapVector by making a runtime constant with the size. + pub(crate) fn array_to_vector_instruction(&mut self, array: &BrilligArray) -> BrilligVector { + let size_register = self.make_usize_constant_instruction(array.size.into()); + BrilligVector { size: size_register.address, pointer: array.pointer, rc: array.rc } + } + + /// Emits a load instruction + pub(crate) fn load_instruction( + &mut self, + destination: MemoryAddress, + source_pointer: MemoryAddress, + ) { + self.debug_show.load_instruction(destination, source_pointer); + self.push_opcode(BrilligOpcode::Load { destination, source_pointer }); + } + + /// Emits a `mov` instruction. + /// + /// Copies the value at `source` into `destination` + pub(crate) fn mov_instruction(&mut self, destination: MemoryAddress, source: MemoryAddress) { + self.debug_show.mov_instruction(destination, source); + self.push_opcode(BrilligOpcode::Mov { destination, source }); + } + + /// Cast truncates the value to the given bit size and converts the type of the value in memory to that bit size. + pub(crate) fn cast_instruction( + &mut self, + destination: SingleAddrVariable, + source: SingleAddrVariable, + ) { + self.debug_show.cast_instruction(destination.address, source.address, destination.bit_size); + self.cast(destination, source); + } + + pub(crate) fn cast(&mut self, destination: SingleAddrVariable, source: SingleAddrVariable) { + self.push_opcode(BrilligOpcode::Cast { + destination: destination.address, + source: source.address, + bit_size: destination.bit_size, + }); + } + + /// Stores the value of `constant` in the `result` register + pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { + self.debug_show.const_instruction(result.address, constant); + self.constant(result, constant); + } + + fn constant(&mut self, result: SingleAddrVariable, constant: Value) { + if result.bit_size > 128 && !constant.to_field().fits_in_u128() { + let high = Value::from(FieldElement::from_be_bytes_reduce( + constant + .to_field() + .to_be_bytes() + .get(0..16) + .expect("FieldElement::to_be_bytes() too short!"), + )); + let low = Value::from(constant.to_u128()); + let high_register = SingleAddrVariable::new(self.allocate_register(), 254); + let low_register = SingleAddrVariable::new(self.allocate_register(), 254); + let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254); + self.constant(high_register, high); + self.constant(low_register, low); + // I want to multiply high by 2^128, but I can't get that big constant in. + // So I'll multiply by 2^64 twice. + self.constant(intermediate_register, Value::from(1_u128 << 64)); + self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); + self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); + // Now we can add. + self.binary(high_register, low_register, intermediate_register, BrilligBinaryOp::Add); + self.cast(result, intermediate_register); + self.deallocate_single_addr(high_register); + self.deallocate_single_addr(low_register); + self.deallocate_single_addr(intermediate_register); + } else { + self.push_opcode(BrilligOpcode::Const { + destination: result.address, + value: constant, + bit_size: result.bit_size, + }); + } + } + + pub(crate) fn usize_const_instruction(&mut self, result: MemoryAddress, constant: Value) { + self.const_instruction(SingleAddrVariable::new_usize(result), constant); + } + + /// Returns a register which holds the value of a constant + pub(crate) fn make_constant_instruction( + &mut self, + constant: Value, + bit_size: u32, + ) -> SingleAddrVariable { + let var = SingleAddrVariable::new(self.allocate_register(), bit_size); + self.const_instruction(var, constant); + var + } + + fn make_constant(&mut self, constant: Value, bit_size: u32) -> SingleAddrVariable { + let var = SingleAddrVariable::new(self.allocate_register(), bit_size); + self.constant(var, constant); + var + } + + /// Returns a register which holds the value of an usize constant + pub(crate) fn make_usize_constant_instruction( + &mut self, + constant: Value, + ) -> SingleAddrVariable { + let register = self.allocate_register(); + self.usize_const_instruction(register, constant); + SingleAddrVariable::new_usize(register) + } + + pub(super) fn calldata_copy_instruction( + &mut self, + destination: MemoryAddress, + calldata_size: usize, + offset: usize, + ) { + self.debug_show.calldata_copy_instruction(destination, calldata_size, offset); + + self.push_opcode(BrilligOpcode::CalldataCopy { + destination_address: destination, + size: calldata_size, + offset, + }); + } +} + +/// Type to encapsulate the binary operation types in Brillig +#[derive(Clone, Copy, Debug)] +pub(crate) enum BrilligBinaryOp { + Add, + Sub, + Mul, + FieldDiv, + UnsignedDiv, + Equals, + LessThan, + LessThanEquals, + And, + Or, + Xor, + Shl, + Shr, + // Modulo operation requires more than one brillig opcode + Modulo, +} + +impl From for BinaryFieldOp { + fn from(operation: BrilligBinaryOp) -> BinaryFieldOp { + match operation { + BrilligBinaryOp::Add => BinaryFieldOp::Add, + BrilligBinaryOp::Sub => BinaryFieldOp::Sub, + BrilligBinaryOp::Mul => BinaryFieldOp::Mul, + BrilligBinaryOp::FieldDiv => BinaryFieldOp::Div, + BrilligBinaryOp::UnsignedDiv => BinaryFieldOp::IntegerDiv, + BrilligBinaryOp::Equals => BinaryFieldOp::Equals, + BrilligBinaryOp::LessThan => BinaryFieldOp::LessThan, + BrilligBinaryOp::LessThanEquals => BinaryFieldOp::LessThanEquals, + _ => panic!("Unsupported operation: {:?} on a field", operation), + } + } +} + +impl From for BinaryIntOp { + fn from(operation: BrilligBinaryOp) -> BinaryIntOp { + match operation { + BrilligBinaryOp::Add => BinaryIntOp::Add, + BrilligBinaryOp::Sub => BinaryIntOp::Sub, + BrilligBinaryOp::Mul => BinaryIntOp::Mul, + BrilligBinaryOp::UnsignedDiv => BinaryIntOp::Div, + BrilligBinaryOp::Equals => BinaryIntOp::Equals, + BrilligBinaryOp::LessThan => BinaryIntOp::LessThan, + BrilligBinaryOp::LessThanEquals => BinaryIntOp::LessThanEquals, + BrilligBinaryOp::And => BinaryIntOp::And, + BrilligBinaryOp::Or => BinaryIntOp::Or, + BrilligBinaryOp::Xor => BinaryIntOp::Xor, + BrilligBinaryOp::Shl => BinaryIntOp::Shl, + BrilligBinaryOp::Shr => BinaryIntOp::Shr, + _ => panic!("Unsupported operation: {:?} on an integer", operation), + } + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs index 8c0e36215a9..f756f06aa69 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/registers.rs @@ -2,7 +2,7 @@ use acvm::acir::brillig::MemoryAddress; use crate::brillig::brillig_ir::entry_point::MAX_STACK_SIZE; -use super::ReservedRegisters; +use super::{brillig_variable::SingleAddrVariable, BrilligContext, ReservedRegisters}; /// Every brillig stack frame/call context has its own view of register space. /// This is maintained by copying these registers to the stack during calls and reading them back. @@ -81,3 +81,29 @@ impl BrilligRegistersContext { self.deallocated_registers.push(register_index); } } + +impl BrilligContext { + /// Returns the i'th register after the reserved ones + pub(crate) fn register(&self, i: usize) -> MemoryAddress { + MemoryAddress::from(ReservedRegisters::NUM_RESERVED_REGISTERS + i) + } + + /// Allocates an unused register. + pub(crate) fn allocate_register(&mut self) -> MemoryAddress { + self.registers.allocate_register() + } + + pub(crate) fn set_allocated_registers(&mut self, allocated_registers: Vec) { + self.registers = BrilligRegistersContext::from_preallocated_registers(allocated_registers); + } + + /// Push a register to the deallocation list, ready for reuse. + pub(crate) fn deallocate_register(&mut self, register_index: MemoryAddress) { + self.registers.deallocate_register(register_index); + } + + /// Deallocates the address where the single address variable is stored + pub(crate) fn deallocate_single_addr(&mut self, var: SingleAddrVariable) { + self.deallocate_register(var.address); + } +} diff --git a/test_programs/execution_success/brillig_signed_div/Nargo.toml b/test_programs/execution_success/brillig_signed_div/Nargo.toml new file mode 100644 index 00000000000..4bb9c5ecc1c --- /dev/null +++ b/test_programs/execution_success/brillig_signed_div/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "brillig_signed_div" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/brillig_signed_div/Prover.toml b/test_programs/execution_success/brillig_signed_div/Prover.toml new file mode 100644 index 00000000000..be93fec5cc3 --- /dev/null +++ b/test_programs/execution_success/brillig_signed_div/Prover.toml @@ -0,0 +1,75 @@ +[[ops]] +lhs = 4 +rhs = 255 # -1 +result = 252 # -4 + +[[ops]] +lhs = 4 +rhs = 254 # -2 +result = 254 # -2 + +[[ops]] +lhs = 4 +rhs = 253 # -3 +result = 255 # -1 + +[[ops]] +lhs = 4 +rhs = 252 # -4 +result = 255 # -1 + +[[ops]] +lhs = 4 +rhs = 251 # -5 +result = 0 + +[[ops]] +lhs = 252 # -4 +rhs = 255 # -1 +result = 4 + +[[ops]] +lhs = 252 # -4 +rhs = 254 # -2 +result = 2 + +[[ops]] +lhs = 252 # -4 +rhs = 253 # -3 +result = 1 + +[[ops]] +lhs = 252 # -4 +rhs = 252 # -4 +result = 1 + +[[ops]] +lhs = 252 # -4 +rhs = 251 # -5 +result = 0 + + +[[ops]] +lhs = 4 +rhs = 1 +result = 4 + +[[ops]] +lhs = 4 +rhs = 2 +result = 2 + +[[ops]] +lhs = 4 +rhs = 3 +result = 1 + +[[ops]] +lhs = 4 +rhs = 4 +result = 1 + +[[ops]] +lhs = 4 +rhs = 5 +result = 0 diff --git a/test_programs/execution_success/brillig_signed_div/src/main.nr b/test_programs/execution_success/brillig_signed_div/src/main.nr new file mode 100644 index 00000000000..bc3f5c3bdc0 --- /dev/null +++ b/test_programs/execution_success/brillig_signed_div/src/main.nr @@ -0,0 +1,11 @@ +struct SignedDivOp { + lhs: i8, + rhs: i8, + result: i8, +} + +unconstrained fn main(ops: [SignedDivOp; 15]) { + for i in 0..15 { + assert_eq(ops[i].lhs / ops[i].rhs, ops[i].result); + } +} diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index 0bd2b1c076a..2eae9e1f07e 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.26.3"; +const VERSION: &str = "0.29.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index d94999f324b..09673d7fd4e 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.26.3", + "@aztec/bb.js": "0.29.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index 49485193532..dc3253de21a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.26.3": - version: 0.26.3 - resolution: "@aztec/bb.js@npm:0.26.3" +"@aztec/bb.js@npm:0.29.0": + version: 0.29.0 + resolution: "@aztec/bb.js@npm:0.29.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 74c2b7ef5405f56472cf7c41d1c13261df07b1d5019e3ede9b63d218378e0fb73ccf5c52f1cc524505efad5799b347b497349d7c9b6fe82286014b1574f12309 + checksum: ae2bae0eed1a64f79f4694d0b02a51b87bb00ef1d9a331193dc9ace69a47819d93b923114e95e77b9b4d3de28c94bf5bdd1e839b0236979aaa0cfcda7a04fa97 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.26.3 + "@aztec/bb.js": 0.29.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 764f6a39c3b7321f7b7146fb0f486de9225b19c3 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 19 Mar 2024 12:02:25 +0000 Subject: [PATCH 092/416] chore: Add clarfiication on the difference in recursive aggregation circuits (#4567) # Description ## Problem\* Resolves comment here https://github.com/noir-lang/noir/pull/4563#discussion_r1526534631 ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../double_verify_nested_proof/src/main.nr | 14 +++++++++++--- .../double_verify_proof/src/main.nr | 1 + .../double_verify_proof_recursive/src/main.nr | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/test_programs/execution_success/double_verify_nested_proof/src/main.nr b/test_programs/execution_success/double_verify_nested_proof/src/main.nr index 0466f2a226d..95d4b6f6995 100644 --- a/test_programs/execution_success/double_verify_nested_proof/src/main.nr +++ b/test_programs/execution_success/double_verify_nested_proof/src/main.nr @@ -1,10 +1,18 @@ use dep::std; +// This circuit aggregates two recursive proofs from `double_verify_proof_recursive`. +// Recursive aggregation is a backend-specific process and it is expected for backends +// to attach any extra data they may need (e.g. aggregation objects) to their proofs. +// Whether the proof we are verifying itself contains a recursive proof is expected to be +// a circuit constant by the barretenberg. Barretenberg hides this circuit constant in the +// proof serialization. Thus, we must have separate circuits for verifying a normal proof and a recursive proof +// with two different proof sizes. fn main( verification_key: [Field; 114], - // This is the proof without public inputs attached. - // - // This means: the size of this does not change with the number of public inputs. + // This is the proof without user-specified public inputs attached. + // + // This means: the size of this does not change with the number of public inputs unless + // they have been attached by the backend. proof: [Field; 109], public_inputs: pub [Field; 1], // This is currently not public. It is fine given that the vk is a part of the circuit definition. diff --git a/test_programs/execution_success/double_verify_proof/src/main.nr b/test_programs/execution_success/double_verify_proof/src/main.nr index 4edf5c5af9f..d832ce0f049 100644 --- a/test_programs/execution_success/double_verify_proof/src/main.nr +++ b/test_programs/execution_success/double_verify_proof/src/main.nr @@ -1,5 +1,6 @@ use dep::std; +// This circuit aggregates two proofs from `assert_statement_recursive`. fn main( verification_key: [Field; 114], // This is the proof without public inputs attached. diff --git a/test_programs/execution_success/double_verify_proof_recursive/src/main.nr b/test_programs/execution_success/double_verify_proof_recursive/src/main.nr index e4c6926efbc..86b4971c3a6 100644 --- a/test_programs/execution_success/double_verify_proof_recursive/src/main.nr +++ b/test_programs/execution_success/double_verify_proof_recursive/src/main.nr @@ -1,5 +1,6 @@ use dep::std; +// This circuit aggregates two proofs from `assert_statement_recursive`. #[recursive] fn main( verification_key: [Field; 114], From 938d5e85eda00a05de5014e64d3dc9fc7c24936d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Tue, 19 Mar 2024 15:07:34 +0100 Subject: [PATCH 093/416] fix: Signed integer comparisons in brillig (#4579) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4286 ## Summary\* Fixes signed integer comparison by comparing biased versions of the two operands ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_block.rs | 39 ++++++++++++++++++- .../brillig_signed_cmp/Nargo.toml | 6 +++ .../brillig_signed_cmp/Prover.toml | 1 + .../brillig_signed_cmp/src/main.nr | 8 ++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test_programs/execution_success/brillig_signed_cmp/Nargo.toml create mode 100644 test_programs/execution_success/brillig_signed_cmp/Prover.toml create mode 100644 test_programs/execution_success/brillig_signed_cmp/src/main.nr diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index f808bfac43b..a9eea74ddd9 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1306,7 +1306,14 @@ impl<'block> BrilligBlock<'block> { BinaryOp::Sub => BrilligBinaryOp::Sub, BinaryOp::Mul => BrilligBinaryOp::Mul, BinaryOp::Eq => BrilligBinaryOp::Equals, - BinaryOp::Lt => BrilligBinaryOp::LessThan, + BinaryOp::Lt => { + if is_signed { + self.convert_signed_less_than(left, right, result_variable); + return; + } else { + BrilligBinaryOp::LessThan + } + } BinaryOp::And => BrilligBinaryOp::And, BinaryOp::Or => BrilligBinaryOp::Or, BinaryOp::Xor => BrilligBinaryOp::Xor, @@ -1434,6 +1441,36 @@ impl<'block> BrilligBlock<'block> { self.brillig_context.deallocate_single_addr(scratch_var_j); } + fn convert_signed_less_than( + &mut self, + left: SingleAddrVariable, + right: SingleAddrVariable, + result: SingleAddrVariable, + ) { + let biased_left = + SingleAddrVariable::new(self.brillig_context.allocate_register(), left.bit_size); + let biased_right = + SingleAddrVariable::new(self.brillig_context.allocate_register(), right.bit_size); + + let bias = self + .brillig_context + .make_constant_instruction((1_u128 << (left.bit_size - 1)).into(), left.bit_size); + + self.brillig_context.binary_instruction(left, bias, biased_left, BrilligBinaryOp::Add); + self.brillig_context.binary_instruction(right, bias, biased_right, BrilligBinaryOp::Add); + + self.brillig_context.binary_instruction( + biased_left, + biased_right, + result, + BrilligBinaryOp::LessThan, + ); + + self.brillig_context.deallocate_single_addr(biased_left); + self.brillig_context.deallocate_single_addr(biased_right); + self.brillig_context.deallocate_single_addr(bias); + } + fn add_overflow_check( &mut self, binary_operation: BrilligBinaryOp, diff --git a/test_programs/execution_success/brillig_signed_cmp/Nargo.toml b/test_programs/execution_success/brillig_signed_cmp/Nargo.toml new file mode 100644 index 00000000000..3f485df4a82 --- /dev/null +++ b/test_programs/execution_success/brillig_signed_cmp/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "brillig_signed_cmp" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/brillig_signed_cmp/Prover.toml b/test_programs/execution_success/brillig_signed_cmp/Prover.toml new file mode 100644 index 00000000000..4b719f83c16 --- /dev/null +++ b/test_programs/execution_success/brillig_signed_cmp/Prover.toml @@ -0,0 +1 @@ +minus_one = 255 diff --git a/test_programs/execution_success/brillig_signed_cmp/src/main.nr b/test_programs/execution_success/brillig_signed_cmp/src/main.nr new file mode 100644 index 00000000000..3e3ea0f4b0f --- /dev/null +++ b/test_programs/execution_success/brillig_signed_cmp/src/main.nr @@ -0,0 +1,8 @@ +unconstrained fn main(minus_one: i8) { + assert(minus_one < 0); + assert(0 < minus_one as u8); + assert(0 > minus_one); + let most_negative_number = minus_one * 127 - 1; + assert(most_negative_number < 0); + assert(127 > most_negative_number); +} From 3eb6acb9c1f23ecd552361ebdd82320c059bc4b9 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:58:51 +0000 Subject: [PATCH 094/416] chore: update deps (#4582) # Description ## Problem\* Resolves ## Summary\* This PR just bumps and deduplicates versions of various dependencies ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 176 +++++++++++++++-------------------------------------- 1 file changed, 50 insertions(+), 126 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b7c1b6e56e..37bf5135468 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,9 +124,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom 0.2.10", "once_cell", @@ -135,9 +135,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if 1.0.0", "getrandom 0.2.10", @@ -411,7 +411,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138985dd8aefbefeaa66b01b7f5b2b6b4c333fcef1cc5f32c63a2aabe37d6de3" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "lsp-types 0.94.1", "pin-project-lite", "rustix", @@ -967,14 +967,14 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode 0.3.6", "lazy_static", "libc", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -1781,9 +1781,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1796,9 +1796,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1806,15 +1806,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1824,15 +1824,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -1841,21 +1841,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures 0.1.31", "futures-channel", @@ -1999,7 +1999,7 @@ dependencies = [ "indexmap 2.0.0", "slab", "tokio", - "tokio-util 0.7.8", + "tokio-util 0.7.10", "tracing", ] @@ -2015,7 +2015,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -2024,7 +2024,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -2033,7 +2033,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", ] [[package]] @@ -2114,9 +2114,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -2129,7 +2129,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2", "tokio", "tower-service", "tracing", @@ -2138,9 +2138,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -2296,7 +2296,7 @@ version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fb7c1b80a1dfa604bb4a649a5c5aeef3d913f7c520cb42b40e534e8a61bcdfc" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", "indexmap 1.9.3", "is-terminal", "itoa", @@ -2401,7 +2401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a" dependencies = [ "derive_more", - "futures 0.3.28", + "futures 0.3.30", "jsonrpc-core", "jsonrpc-pubsub", "log", @@ -2416,7 +2416,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "futures-executor", "futures-util", "log", @@ -2431,7 +2431,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "jsonrpc-client-transports", ] @@ -2453,7 +2453,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "hyper", "jsonrpc-core", "jsonrpc-server-utils", @@ -2469,7 +2469,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011" dependencies = [ - "futures 0.3.28", + "futures 0.3.30", "jsonrpc-core", "lazy_static", "log", @@ -2485,7 +2485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes", - "futures 0.3.28", + "futures 0.3.30", "globset", "jsonrpc-core", "lazy_static", @@ -2814,7 +2814,7 @@ dependencies = [ "test-binary", "thiserror", "tokio", - "tokio-util 0.7.8", + "tokio-util 0.7.10", "toml 0.7.6", "tower", "tracing-appender", @@ -4559,16 +4559,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -4882,9 +4872,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -4892,7 +4882,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -4920,9 +4910,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -4945,9 +4935,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -5639,15 +5629,6 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -5666,21 +5647,6 @@ dependencies = [ "windows-targets 0.52.4", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.1" @@ -5711,12 +5677,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.4", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" @@ -5735,12 +5695,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" @@ -5759,12 +5713,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" @@ -5783,12 +5731,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" @@ -5807,12 +5749,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" @@ -5825,12 +5761,6 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" @@ -5849,12 +5779,6 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" From f2f827d51e6fe99fa3d17f125b22743da25e25be Mon Sep 17 00:00:00 2001 From: jfecher Date: Tue, 19 Mar 2024 12:37:23 -0500 Subject: [PATCH 095/416] feat: Add `break` and `continue` in unconstrained code (#4569) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4259 ## Summary\* Implements break & continue in for loops in unconstrained code only. When attempted to use outside of a loop or in a constrained function, an error is issued. ## Additional Context I didn't put a link to the docs in an error message, should I? It seems like this may be something we may want to do in other errors as well. This PR is currently missing documentation. The test failures are from the change to terminator instructions. This was needed since otherwise when break/continue set the terminator it was being overridden when `codegen_if` would set the terminator instruction for the same block afterward. One somewhat messy way to fix this is to switch to an empty, unreachable block. Then the if's terminator would set on that block instead. But if we push any instructions in between those would be lost too and we'd be none the wiser. Although pushing instructions after a break is usually a logic error in the compiler anyway. ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/function_builder/mod.rs | 11 ++-- compiler/noirc_evaluator/src/ssa/ir/cfg.rs | 4 -- .../noirc_evaluator/src/ssa/opt/inlining.rs | 13 ++-- .../src/ssa/ssa_gen/context.rs | 33 +++++++++- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 22 +++++++ compiler/noirc_frontend/src/ast/statement.rs | 6 ++ .../src/hir/resolution/errors.rs | 20 ++++++ .../src/hir/resolution/resolver.rs | 47 ++++++++++++-- .../noirc_frontend/src/hir/type_check/stmt.rs | 2 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 2 + compiler/noirc_frontend/src/lexer/token.rs | 6 ++ .../src/monomorphization/ast.rs | 2 + .../src/monomorphization/mod.rs | 2 + .../src/monomorphization/printer.rs | 2 + compiler/noirc_frontend/src/parser/parser.rs | 10 +++ compiler/noirc_frontend/src/tests.rs | 30 +++++++++ docs/docs/noir/concepts/control_flow.md | 62 ++++++++++++++----- docs/docs/noir/concepts/unconstrained.md | 4 ++ noir_stdlib/src/collections/map.nr | 18 +++--- .../break_and_continue/Nargo.toml | 7 +++ .../break_and_continue/src/main.nr | 15 +++++ tooling/nargo_fmt/src/visitor/stmt.rs | 2 + 22 files changed, 275 insertions(+), 45 deletions(-) create mode 100644 test_programs/execution_success/break_and_continue/Nargo.toml create mode 100644 test_programs/execution_success/break_and_continue/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index aa5a7fedd92..0726b557616 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -49,11 +49,10 @@ impl FunctionBuilder { ) -> Self { let mut new_function = Function::new(function_name, function_id); new_function.set_runtime(runtime); - let current_block = new_function.entry_block(); Self { + current_block: new_function.entry_block(), current_function: new_function, - current_block, finished_functions: Vec::new(), call_stack: CallStack::new(), } @@ -153,9 +152,10 @@ impl FunctionBuilder { instruction: Instruction, ctrl_typevars: Option>, ) -> InsertInstructionResult { + let block = self.current_block(); self.current_function.dfg.insert_instruction_and_results( instruction, - self.current_block, + block, ctrl_typevars, self.call_stack.clone(), ) @@ -317,8 +317,11 @@ impl FunctionBuilder { } /// Terminates the current block with the given terminator instruction + /// if the current block does not already have a terminator instruction. fn terminate_block_with(&mut self, terminator: TerminatorInstruction) { - self.current_function.dfg.set_block_terminator(self.current_block, terminator); + if self.current_function.dfg[self.current_block].terminator().is_none() { + self.current_function.dfg.set_block_terminator(self.current_block, terminator); + } } /// Terminate the current block with a jmp instruction to jmp to the given diff --git a/compiler/noirc_evaluator/src/ssa/ir/cfg.rs b/compiler/noirc_evaluator/src/ssa/ir/cfg.rs index ebfbf003ec4..5a3f07cd673 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/cfg.rs @@ -95,10 +95,6 @@ impl ControlFlowGraph { ); predecessor_node.successors.insert(to); let successor_node = self.data.entry(to).or_default(); - assert!( - successor_node.predecessors.len() < 2, - "ICE: A cfg node cannot have more than two predecessors" - ); successor_node.predecessors.insert(from); } diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 776f22b2877..aff06af9921 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -487,6 +487,13 @@ impl<'function> PerFunctionContext<'function> { } TerminatorInstruction::Return { return_values, call_stack } => { let return_values = vecmap(return_values, |value| self.translate_value(*value)); + + // Note that `translate_block` would take us back to the point at which the + // inlining of this source block began. Since additional blocks may have been + // inlined since, we are interested in the block representing the current program + // point, obtained via `current_block`. + let block_id = self.context.builder.current_block(); + if self.inlining_entry { let mut new_call_stack = self.context.call_stack.clone(); new_call_stack.append(call_stack.clone()); @@ -495,11 +502,7 @@ impl<'function> PerFunctionContext<'function> { .set_call_stack(new_call_stack) .terminate_with_return(return_values.clone()); } - // Note that `translate_block` would take us back to the point at which the - // inlining of this source block began. Since additional blocks may have been - // inlined since, we are interested in the block representing the current program - // point, obtained via `current_block`. - let block_id = self.context.builder.current_block(); + Some((block_id, return_values)) } } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 409b99958a9..a9a707ca8ed 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -39,6 +39,11 @@ pub(super) struct FunctionContext<'a> { pub(super) builder: FunctionBuilder, shared_context: &'a SharedContext, + + /// Contains any loops we're currently in the middle of translating. + /// These are ordered such that an inner loop is at the end of the vector and + /// outer loops are at the beginning. When a loop is finished, it is popped. + loops: Vec, } /// Shared context for all functions during ssa codegen. This is the only @@ -72,6 +77,13 @@ pub(super) struct SharedContext { pub(super) program: Program, } +#[derive(Copy, Clone)] +pub(super) struct Loop { + pub(super) loop_entry: BasicBlockId, + pub(super) loop_index: ValueId, + pub(super) loop_end: BasicBlockId, +} + /// The queue of functions remaining to compile type FunctionQueue = Vec<(ast::FuncId, IrFunctionId)>; @@ -97,7 +109,8 @@ impl<'a> FunctionContext<'a> { .1; let builder = FunctionBuilder::new(function_name, function_id, runtime); - let mut this = Self { definitions: HashMap::default(), builder, shared_context }; + let definitions = HashMap::default(); + let mut this = Self { definitions, builder, shared_context, loops: Vec::new() }; this.add_parameters_to_scope(parameters); this } @@ -1053,6 +1066,24 @@ impl<'a> FunctionContext<'a> { self.builder.decrement_array_reference_count(parameter); } } + + pub(crate) fn enter_loop( + &mut self, + loop_entry: BasicBlockId, + loop_index: ValueId, + loop_end: BasicBlockId, + ) { + self.loops.push(Loop { loop_entry, loop_index, loop_end }); + } + + pub(crate) fn exit_loop(&mut self) { + self.loops.pop(); + } + + pub(crate) fn current_loop(&self) -> Loop { + // The frontend should ensure break/continue are never used outside a loop + *self.loops.last().expect("current_loop: not in a loop!") + } } /// True if the given operator cannot be encoded directly and needs diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 3d8ae0bb3eb..5acb266b4c1 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -152,6 +152,8 @@ impl<'a> FunctionContext<'a> { } Expression::Assign(assign) => self.codegen_assign(assign), Expression::Semi(semi) => self.codegen_semi(semi), + Expression::Break => Ok(self.codegen_break()), + Expression::Continue => Ok(self.codegen_continue()), } } @@ -477,6 +479,10 @@ impl<'a> FunctionContext<'a> { let index_type = Self::convert_non_tuple_type(&for_expr.index_type); let loop_index = self.builder.add_block_parameter(loop_entry, index_type); + // Remember the blocks and variable used in case there are break/continue instructions + // within the loop which need to jump to them. + self.enter_loop(loop_entry, loop_index, loop_end); + self.builder.set_location(for_expr.start_range_location); let start_index = self.codegen_non_tuple_expression(&for_expr.start_range)?; @@ -507,6 +513,7 @@ impl<'a> FunctionContext<'a> { // Finish by switching back to the end of the loop self.builder.switch_to_block(loop_end); + self.exit_loop(); Ok(Self::unit_value()) } @@ -736,4 +743,19 @@ impl<'a> FunctionContext<'a> { self.codegen_expression(expr)?; Ok(Self::unit_value()) } + + fn codegen_break(&mut self) -> Values { + let loop_end = self.current_loop().loop_end; + self.builder.terminate_with_jmp(loop_end, Vec::new()); + Self::unit_value() + } + + fn codegen_continue(&mut self) -> Values { + let loop_ = self.current_loop(); + + // Must remember to increment i before jumping + let new_loop_index = self.make_offset(loop_.loop_index, 1); + self.builder.terminate_with_jmp(loop_.loop_entry, vec![new_loop_index]); + Self::unit_value() + } } diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index 387840b57c4..fb7f520ee71 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -35,6 +35,8 @@ pub enum StatementKind { Expression(Expression), Assign(AssignStatement), For(ForLoopStatement), + Break, + Continue, // This is an expression with a trailing semi-colon Semi(Expression), // This statement is the result of a recovered parse error. @@ -59,6 +61,8 @@ impl Statement { | StatementKind::Constrain(_) | StatementKind::Assign(_) | StatementKind::Semi(_) + | StatementKind::Break + | StatementKind::Continue | StatementKind::Error => { // To match rust, statements always require a semicolon, even at the end of a block if semi.is_none() { @@ -637,6 +641,8 @@ impl Display for StatementKind { StatementKind::Expression(expression) => expression.fmt(f), StatementKind::Assign(assign) => assign.fmt(f), StatementKind::For(for_loop) => for_loop.fmt(f), + StatementKind::Break => write!(f, "break"), + StatementKind::Continue => write!(f, "continue"), StatementKind::Semi(semi) => write!(f, "{semi};"), StatementKind::Error => write!(f, "Error"), } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 30a1ba2ee34..3c6c0582292 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -84,6 +84,10 @@ pub enum ResolverError { LowLevelFunctionOutsideOfStdlib { ident: Ident }, #[error("Dependency cycle found, '{item}' recursively depends on itself: {cycle} ")] DependencyCycle { span: Span, item: String, cycle: String }, + #[error("break/continue are only allowed in unconstrained functions")] + JumpInConstrainedFn { is_break: bool, span: Span }, + #[error("break/continue are only allowed within loops")] + JumpOutsideLoop { is_break: bool, span: Span }, } impl ResolverError { @@ -322,6 +326,22 @@ impl From for Diagnostic { span, ) }, + ResolverError::JumpInConstrainedFn { is_break, span } => { + let item = if is_break { "break" } else { "continue" }; + Diagnostic::simple_error( + format!("{item} is only allowed in unconstrained functions"), + "Constrained code must always have a known number of loop iterations".into(), + span, + ) + }, + ResolverError::JumpOutsideLoop { is_break, span } => { + let item = if is_break { "break" } else { "continue" }; + Diagnostic::simple_error( + format!("{item} is only allowed within loops"), + "".into(), + span, + ) + }, } } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index c33b83257b0..90716bb958d 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -39,7 +39,7 @@ use crate::{ use crate::{ ArrayLiteral, BinaryOpKind, Distinctness, ForRange, FunctionDefinition, FunctionReturnType, Generics, ItemVisibility, LValue, NoirStruct, NoirTypeAlias, Param, Path, PathKind, Pattern, - Shared, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, + Shared, Statement, StructType, Type, TypeAlias, TypeVariable, TypeVariableKind, UnaryOp, UnresolvedGenerics, UnresolvedTraitConstraint, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, Visibility, ERROR_IDENT, }; @@ -115,6 +115,13 @@ pub struct Resolver<'a> { /// that are captured. We do this in order to create the hidden environment /// parameter for the lambda function. lambda_stack: Vec, + + /// True if we're currently resolving an unconstrained function + in_unconstrained_fn: bool, + + /// How many loops we're currently within. + /// This increases by 1 at the start of a loop, and decreases by 1 when it ends. + nested_loops: u32, } /// ResolverMetas are tagged onto each definition to track how many times they are used @@ -155,6 +162,8 @@ impl<'a> Resolver<'a> { current_item: None, file, in_contract, + in_unconstrained_fn: false, + nested_loops: 0, } } @@ -416,6 +425,11 @@ impl<'a> Resolver<'a> { fn intern_function(&mut self, func: NoirFunction, id: FuncId) -> (HirFunction, FuncMeta) { let func_meta = self.extract_meta(&func, id); + + if func.def.is_unconstrained { + self.in_unconstrained_fn = true; + } + let hir_func = match func.kind { FunctionKind::Builtin | FunctionKind::LowLevel | FunctionKind::Oracle => { HirFunction::empty() @@ -1148,7 +1162,7 @@ impl<'a> Resolver<'a> { }) } - pub fn resolve_stmt(&mut self, stmt: StatementKind) -> HirStatement { + pub fn resolve_stmt(&mut self, stmt: StatementKind, span: Span) -> HirStatement { match stmt { StatementKind::Let(let_stmt) => { let expression = self.resolve_expression(let_stmt.expression); @@ -1188,6 +1202,8 @@ impl<'a> Resolver<'a> { let end_range = self.resolve_expression(end_range); let (identifier, block) = (for_loop.identifier, for_loop.block); + self.nested_loops += 1; + // TODO: For loop variables are currently mutable by default since we haven't // yet implemented syntax for them to be optionally mutable. let (identifier, block) = self.in_new_scope(|this| { @@ -1200,6 +1216,8 @@ impl<'a> Resolver<'a> { (decl, this.resolve_expression(block)) }); + self.nested_loops -= 1; + HirStatement::For(HirForStatement { start_range, end_range, @@ -1210,10 +1228,18 @@ impl<'a> Resolver<'a> { range @ ForRange::Array(_) => { let for_stmt = range.into_for(for_loop.identifier, for_loop.block, for_loop.span); - self.resolve_stmt(for_stmt) + self.resolve_stmt(for_stmt, for_loop.span) } } } + StatementKind::Break => { + self.check_break_continue(true, span); + HirStatement::Break + } + StatementKind::Continue => { + self.check_break_continue(false, span); + HirStatement::Continue + } StatementKind::Error => HirStatement::Error, } } @@ -1260,8 +1286,8 @@ impl<'a> Resolver<'a> { Some(self.resolve_expression(assert_msg_call_expr)) } - pub fn intern_stmt(&mut self, stmt: StatementKind) -> StmtId { - let hir_stmt = self.resolve_stmt(stmt); + pub fn intern_stmt(&mut self, stmt: Statement) -> StmtId { + let hir_stmt = self.resolve_stmt(stmt.kind, stmt.span); self.interner.push_stmt(hir_stmt) } @@ -1909,7 +1935,7 @@ impl<'a> Resolver<'a> { fn resolve_block(&mut self, block_expr: BlockExpression) -> HirExpression { let statements = - self.in_new_scope(|this| vecmap(block_expr.0, |stmt| this.intern_stmt(stmt.kind))); + self.in_new_scope(|this| vecmap(block_expr.0, |stmt| this.intern_stmt(stmt))); HirExpression::Block(HirBlockExpression(statements)) } @@ -2036,6 +2062,15 @@ impl<'a> Resolver<'a> { } HirLiteral::FmtStr(str, fmt_str_idents) } + + fn check_break_continue(&mut self, is_break: bool, span: Span) { + if !self.in_unconstrained_fn { + self.push_err(ResolverError::JumpInConstrainedFn { is_break, span }); + } + if self.nested_loops == 0 { + self.push_err(ResolverError::JumpOutsideLoop { is_break, span }); + } + } } /// Gives an error if a user tries to create a mutable reference diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 358bea86922..e90da555803 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -51,7 +51,7 @@ impl<'interner> TypeChecker<'interner> { HirStatement::Constrain(constrain_stmt) => self.check_constrain_stmt(constrain_stmt), HirStatement::Assign(assign_stmt) => self.check_assign_stmt(assign_stmt, stmt_id), HirStatement::For(for_loop) => self.check_for_loop(for_loop), - HirStatement::Error => (), + HirStatement::Break | HirStatement::Continue | HirStatement::Error => (), } Type::Unit } diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index b910be1fdda..4e5f718cf47 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -14,6 +14,8 @@ pub enum HirStatement { Constrain(HirConstrainStatement), Assign(HirAssignStatement), For(HirForStatement), + Break, + Continue, Expression(ExprId), Semi(ExprId), Error, diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 3dc9d05b15e..1e341d34510 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -644,10 +644,12 @@ pub enum Keyword { Assert, AssertEq, Bool, + Break, CallData, Char, CompTime, Constrain, + Continue, Contract, Crate, Dep, @@ -685,10 +687,12 @@ impl fmt::Display for Keyword { Keyword::Assert => write!(f, "assert"), Keyword::AssertEq => write!(f, "assert_eq"), Keyword::Bool => write!(f, "bool"), + Keyword::Break => write!(f, "break"), Keyword::Char => write!(f, "char"), Keyword::CallData => write!(f, "call_data"), Keyword::CompTime => write!(f, "comptime"), Keyword::Constrain => write!(f, "constrain"), + Keyword::Continue => write!(f, "continue"), Keyword::Contract => write!(f, "contract"), Keyword::Crate => write!(f, "crate"), Keyword::Dep => write!(f, "dep"), @@ -729,10 +733,12 @@ impl Keyword { "assert" => Keyword::Assert, "assert_eq" => Keyword::AssertEq, "bool" => Keyword::Bool, + "break" => Keyword::Break, "call_data" => Keyword::CallData, "char" => Keyword::Char, "comptime" => Keyword::CompTime, "constrain" => Keyword::Constrain, + "continue" => Keyword::Continue, "contract" => Keyword::Contract, "crate" => Keyword::Crate, "dep" => Keyword::Dep, diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 7fcf8e87792..21b77127360 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -38,6 +38,8 @@ pub enum Expression { Constrain(Box, Location, Option>), Assign(Assign), Semi(Box), + Break, + Continue, } /// A definition is either a local (variable), function, or is a built-in diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 4938d33aff9..a99a4e61d4d 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -594,6 +594,8 @@ impl<'interner> Monomorphizer<'interner> { HirStatement::Semi(expr) => { self.expr(expr).map(|expr| ast::Expression::Semi(Box::new(expr))) } + HirStatement::Break => Ok(ast::Expression::Break), + HirStatement::Continue => Ok(ast::Expression::Continue), HirStatement::Error => unreachable!(), } } diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index 7aec2193494..c3e34890ce0 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -73,6 +73,8 @@ impl AstPrinter { self.print_expr(expr, f)?; write!(f, ";") } + Expression::Break => write!(f, "break"), + Expression::Continue => write!(f, "continue"), } } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 383a1ffafc9..b2d9d7e6802 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -421,6 +421,8 @@ where declaration(expr_parser.clone()), assignment(expr_parser.clone()), for_loop(expr_no_constructors, statement), + break_statement(), + continue_statement(), return_statement(expr_parser.clone()), expr_parser.map(StatementKind::Expression), )) @@ -431,6 +433,14 @@ fn fresh_statement() -> impl NoirParser { statement(expression(), expression_no_constructors(expression())) } +fn break_statement() -> impl NoirParser { + keyword(Keyword::Break).to(StatementKind::Break) +} + +fn continue_statement() -> impl NoirParser { + keyword(Keyword::Continue).to(StatementKind::Continue) +} + fn declaration<'a, P>(expr_parser: P) -> impl NoirParser + 'a where P: ExprParser + 'a, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index b8ed6fb73d2..98dbc42adcd 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -778,6 +778,8 @@ mod test { HirStatement::Semi(semi_expr) => semi_expr, HirStatement::For(for_loop) => for_loop.block, HirStatement::Error => panic!("Invalid HirStatement!"), + HirStatement::Break => panic!("Unexpected break"), + HirStatement::Continue => panic!("Unexpected continue"), }; let expr = interner.expression(&expr_id); @@ -1226,6 +1228,34 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { assert_eq!(get_program_errors(src).len(), 0); } + #[test] + fn break_and_continue_in_constrained_fn() { + let src = r#" + fn main() { + for i in 0 .. 10 { + if i == 2 { + continue; + } + if i == 5 { + break; + } + } + } + "#; + assert_eq!(get_program_errors(src).len(), 2); + } + + #[test] + fn break_and_continue_outside_loop() { + let src = r#" + unconstrained fn main() { + continue; + break; + } + "#; + assert_eq!(get_program_errors(src).len(), 2); + } + // Regression for #4545 #[test] fn type_aliases_in_main() { diff --git a/docs/docs/noir/concepts/control_flow.md b/docs/docs/noir/concepts/control_flow.md index 4ce65236db3..045d3c3a5f5 100644 --- a/docs/docs/noir/concepts/control_flow.md +++ b/docs/docs/noir/concepts/control_flow.md @@ -7,21 +7,6 @@ keywords: [Noir programming language, loops, for loop, if-else statements, Rust sidebar_position: 2 --- -## Loops - -Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple -times. - -The following block of code between the braces is run 10 times. - -```rust -for i in 0..10 { - // do something -}; -``` - -The index for loops is of type `u64`. - ## If Expressions Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required @@ -43,3 +28,50 @@ if a == 0 { } assert(x == 2); ``` + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/docs/noir/concepts/unconstrained.md b/docs/docs/noir/concepts/unconstrained.md index 89d12c1c971..b8e71fe65f0 100644 --- a/docs/docs/noir/concepts/unconstrained.md +++ b/docs/docs/noir/concepts/unconstrained.md @@ -93,3 +93,7 @@ Backend circuit size: 2902 This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) diff --git a/noir_stdlib/src/collections/map.nr b/noir_stdlib/src/collections/map.nr index 2d76acf1f3a..5f8cc6dab62 100644 --- a/noir_stdlib/src/collections/map.nr +++ b/noir_stdlib/src/collections/map.nr @@ -288,10 +288,10 @@ impl HashMap { let mut result = Option::none(); let hash = self.hash(key); - let mut break = false; + let mut should_break = false; for attempt in 0..N { - if !break { + if !should_break { let index = self.quadratic_probe(hash, attempt as u64); let slot = self._table[index]; @@ -300,7 +300,7 @@ impl HashMap { let (current_key, value) = slot.key_value_unchecked(); if current_key == key { result = Option::some(value); - break = true; + should_break = true; } } } @@ -324,10 +324,10 @@ impl HashMap { self.assert_load_factor(); let hash = self.hash(key); - let mut break = false; + let mut should_break = false; for attempt in 0..N { - if !break { + if !should_break { let index = self.quadratic_probe(hash, attempt as u64); let mut slot = self._table[index]; let mut insert = false; @@ -346,7 +346,7 @@ impl HashMap { if insert { slot.set(key, value); self._table[index] = slot; - break = true; + should_break = true; } } } @@ -364,10 +364,10 @@ impl HashMap { H: Hasher { // docs:end:remove let hash = self.hash(key); - let mut break = false; + let mut should_break = false; for attempt in 0..N { - if !break { + if !should_break { let index = self.quadratic_probe(hash, attempt as u64); let mut slot = self._table[index]; @@ -378,7 +378,7 @@ impl HashMap { slot.mark_deleted(); self._table[index] = slot; self._len -= 1; - break = true; + should_break = true; } } } diff --git a/test_programs/execution_success/break_and_continue/Nargo.toml b/test_programs/execution_success/break_and_continue/Nargo.toml new file mode 100644 index 00000000000..483602478ba --- /dev/null +++ b/test_programs/execution_success/break_and_continue/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "break_and_continue" +type = "bin" +authors = [""] +compiler_version = ">=0.24.0" + +[dependencies] diff --git a/test_programs/execution_success/break_and_continue/src/main.nr b/test_programs/execution_success/break_and_continue/src/main.nr new file mode 100644 index 00000000000..67dce03ac64 --- /dev/null +++ b/test_programs/execution_success/break_and_continue/src/main.nr @@ -0,0 +1,15 @@ +unconstrained fn main() { + let mut count = 0; + + for i in 0..10 { + if i == 2 { + continue; + } + if i == 5 { + break; + } + count += 1; + } + + assert(count == 4); +} diff --git a/tooling/nargo_fmt/src/visitor/stmt.rs b/tooling/nargo_fmt/src/visitor/stmt.rs index 44c5dad6b5d..ee8cc990e0e 100644 --- a/tooling/nargo_fmt/src/visitor/stmt.rs +++ b/tooling/nargo_fmt/src/visitor/stmt.rs @@ -95,6 +95,8 @@ impl super::FmtVisitor<'_> { self.push_rewrite(self.slice(span).to_string(), span); } StatementKind::Error => unreachable!(), + StatementKind::Break => self.push_rewrite("break;".into(), span), + StatementKind::Continue => self.push_rewrite("continue;".into(), span), } self.last_position = span.end(); From b86300c3391cb89bf467dce044968c901fcb4bc0 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 19 Mar 2024 19:23:17 +0000 Subject: [PATCH 096/416] chore: Standardise workspace JS dependencies (#4583) # Description ## Problem\* ## Summary\* This PR standardizes the versions of various JS dependencies across the workspace ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../@yarnpkg/plugin-interactive-tools.cjs | 541 +++++++++ .yarnrc.yml | 9 +- acvm-repo/acvm_js/package.json | 12 +- compiler/integration-tests/package.json | 8 +- compiler/wasm/package.json | 12 +- docs/package.json | 6 +- package.json | 10 +- tooling/noir_codegen/package.json | 10 +- tooling/noir_js/package.json | 10 +- .../noir_js_backend_barretenberg/package.json | 10 +- .../tsconfig.cjs.json | 1 + tooling/noir_js_types/package.json | 8 +- tooling/noirc_abi_wasm/package.json | 4 +- yarn.lock | 1039 ++++------------- 14 files changed, 851 insertions(+), 829 deletions(-) create mode 100644 .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs diff --git a/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs b/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs new file mode 100644 index 00000000000..bc2ca19f116 --- /dev/null +++ b/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs @@ -0,0 +1,541 @@ +/* eslint-disable */ +//prettier-ignore +module.exports = { +name: "@yarnpkg/plugin-interactive-tools", +factory: function (require) { +var plugin=(()=>{var bF=Object.create;var D_=Object.defineProperty;var BF=Object.getOwnPropertyDescriptor;var UF=Object.getOwnPropertyNames;var jF=Object.getPrototypeOf,zF=Object.prototype.hasOwnProperty;var hi=(o=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(o,{get:(l,f)=>(typeof require<"u"?require:l)[f]}):o)(function(o){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+o+'" is not supported')});var nt=(o,l)=>()=>(l||o((l={exports:{}}).exports,l),l.exports),HF=(o,l)=>{for(var f in l)D_(o,f,{get:l[f],enumerable:!0})},j8=(o,l,f,h)=>{if(l&&typeof l=="object"||typeof l=="function")for(let E of UF(l))!zF.call(o,E)&&E!==f&&D_(o,E,{get:()=>l[E],enumerable:!(h=BF(l,E))||h.enumerable});return o};var V0=(o,l,f)=>(f=o!=null?bF(jF(o)):{},j8(l||!o||!o.__esModule?D_(f,"default",{value:o,enumerable:!0}):f,o)),qF=o=>j8(D_({},"__esModule",{value:!0}),o);var Py=nt((Xz,H8)=>{"use strict";var z8=Object.getOwnPropertySymbols,WF=Object.prototype.hasOwnProperty,VF=Object.prototype.propertyIsEnumerable;function GF(o){if(o==null)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(o)}function YF(){try{if(!Object.assign)return!1;var o=new String("abc");if(o[5]="de",Object.getOwnPropertyNames(o)[0]==="5")return!1;for(var l={},f=0;f<10;f++)l["_"+String.fromCharCode(f)]=f;var h=Object.getOwnPropertyNames(l).map(function(t){return l[t]});if(h.join("")!=="0123456789")return!1;var E={};return"abcdefghijklmnopqrst".split("").forEach(function(t){E[t]=t}),Object.keys(Object.assign({},E)).join("")==="abcdefghijklmnopqrst"}catch{return!1}}H8.exports=YF()?Object.assign:function(o,l){for(var f,h=GF(o),E,t=1;t{"use strict";var qE=Py(),Zf=typeof Symbol=="function"&&Symbol.for,Iy=Zf?Symbol.for("react.element"):60103,KF=Zf?Symbol.for("react.portal"):60106,XF=Zf?Symbol.for("react.fragment"):60107,QF=Zf?Symbol.for("react.strict_mode"):60108,JF=Zf?Symbol.for("react.profiler"):60114,ZF=Zf?Symbol.for("react.provider"):60109,$F=Zf?Symbol.for("react.context"):60110,eP=Zf?Symbol.for("react.forward_ref"):60112,tP=Zf?Symbol.for("react.suspense"):60113,nP=Zf?Symbol.for("react.memo"):60115,rP=Zf?Symbol.for("react.lazy"):60116,q8=typeof Symbol=="function"&&Symbol.iterator;function by(o){for(var l="https://reactjs.org/docs/error-decoder.html?invariant="+o,f=1;fw_.length&&w_.push(o)}function jE(o,l,f,h){var E=typeof o;(E==="undefined"||E==="boolean")&&(o=null);var t=!1;if(o===null)t=!0;else switch(E){case"string":case"number":t=!0;break;case"object":switch(o.$$typeof){case Iy:case KF:t=!0}}if(t)return f(h,o,l===""?"."+UE(o,0):l),1;if(t=0,l=l===""?".":l+":",Array.isArray(o))for(var N=0;N{"use strict";var aP="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";tS.exports=aP});var XE=nt((Zz,oS)=>{"use strict";var KE=function(){};process.env.NODE_ENV!=="production"&&(rS=nS(),S_={},iS=Function.call.bind(Object.prototype.hasOwnProperty),KE=function(o){var l="Warning: "+o;typeof console<"u"&&console.error(l);try{throw new Error(l)}catch{}});var rS,S_,iS;function uS(o,l,f,h,E){if(process.env.NODE_ENV!=="production"){for(var t in o)if(iS(o,t)){var N;try{if(typeof o[t]!="function"){var F=Error((h||"React class")+": "+f+" type `"+t+"` is invalid; it must be a function, usually from the `prop-types` package, but received `"+typeof o[t]+"`.");throw F.name="Invariant Violation",F}N=o[t](l,t,h,f,null,rS)}catch(x){N=x}if(N&&!(N instanceof Error)&&KE((h||"React class")+": type specification of "+f+" `"+t+"` is invalid; the type checker function must return `null` or an `Error` but returned a "+typeof N+". You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument)."),N instanceof Error&&!(N.message in S_)){S_[N.message]=!0;var k=E?E():"";KE("Failed "+f+" type: "+N.message+(k!=null?k:""))}}}}uS.resetWarningCache=function(){process.env.NODE_ENV!=="production"&&(S_={})};oS.exports=uS});var lS=nt(_u=>{"use strict";process.env.NODE_ENV!=="production"&&function(){"use strict";var o=Py(),l=XE(),f="16.13.1",h=typeof Symbol=="function"&&Symbol.for,E=h?Symbol.for("react.element"):60103,t=h?Symbol.for("react.portal"):60106,N=h?Symbol.for("react.fragment"):60107,F=h?Symbol.for("react.strict_mode"):60108,k=h?Symbol.for("react.profiler"):60114,x=h?Symbol.for("react.provider"):60109,j=h?Symbol.for("react.context"):60110,q=h?Symbol.for("react.concurrent_mode"):60111,V=h?Symbol.for("react.forward_ref"):60112,re=h?Symbol.for("react.suspense"):60113,y=h?Symbol.for("react.suspense_list"):60120,me=h?Symbol.for("react.memo"):60115,De=h?Symbol.for("react.lazy"):60116,ge=h?Symbol.for("react.block"):60121,ae=h?Symbol.for("react.fundamental"):60117,we=h?Symbol.for("react.responder"):60118,he=h?Symbol.for("react.scope"):60119,ve=typeof Symbol=="function"&&Symbol.iterator,ue="@@iterator";function Ae(Q){if(Q===null||typeof Q!="object")return null;var Se=ve&&Q[ve]||Q[ue];return typeof Se=="function"?Se:null}var ze={current:null},We={suspense:null},gt={current:null},_t=/^(.*)[\\\/]/;function Qe(Q,Se,Fe){var Le="";if(Se){var pt=Se.fileName,Yn=pt.replace(_t,"");if(/^index\./.test(Yn)){var Cn=pt.match(_t);if(Cn){var cr=Cn[1];if(cr){var Si=cr.replace(_t,"");Yn=Si+"/"+Yn}}}Le=" (at "+Yn+":"+Se.lineNumber+")"}else Fe&&(Le=" (created by "+Fe+")");return` + in `+(Q||"Unknown")+Le}var ot=1;function Ve(Q){return Q._status===ot?Q._result:null}function Pt(Q,Se,Fe){var Le=Se.displayName||Se.name||"";return Q.displayName||(Le!==""?Fe+"("+Le+")":Fe)}function Jt(Q){if(Q==null)return null;if(typeof Q.tag=="number"&&dt("Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."),typeof Q=="function")return Q.displayName||Q.name||null;if(typeof Q=="string")return Q;switch(Q){case N:return"Fragment";case t:return"Portal";case k:return"Profiler";case F:return"StrictMode";case re:return"Suspense";case y:return"SuspenseList"}if(typeof Q=="object")switch(Q.$$typeof){case j:return"Context.Consumer";case x:return"Context.Provider";case V:return Pt(Q,Q.render,"ForwardRef");case me:return Jt(Q.type);case ge:return Jt(Q.render);case De:{var Se=Q,Fe=Ve(Se);if(Fe)return Jt(Fe);break}}return null}var it={},J=null;function ce(Q){J=Q}it.getCurrentStack=null,it.getStackAddendum=function(){var Q="";if(J){var Se=Jt(J.type),Fe=J._owner;Q+=Qe(Se,J._source,Fe&&Jt(Fe.type))}var Le=it.getCurrentStack;return Le&&(Q+=Le()||""),Q};var Re={current:!1},le={ReactCurrentDispatcher:ze,ReactCurrentBatchConfig:We,ReactCurrentOwner:gt,IsSomeRendererActing:Re,assign:o};o(le,{ReactDebugCurrentFrame:it,ReactComponentTreeHook:{}});function He(Q){{for(var Se=arguments.length,Fe=new Array(Se>1?Se-1:0),Le=1;Le1?Se-1:0),Le=1;Le0&&typeof Fe[Fe.length-1]=="string"&&Fe[Fe.length-1].indexOf(` + in`)===0;if(!Le){var pt=le.ReactDebugCurrentFrame,Yn=pt.getStackAddendum();Yn!==""&&(Se+="%s",Fe=Fe.concat([Yn]))}var Cn=Fe.map(function(Ou){return""+Ou});Cn.unshift("Warning: "+Se),Function.prototype.apply.call(console[Q],console,Cn);try{var cr=0,Si="Warning: "+Se.replace(/%s/g,function(){return Fe[cr++]});throw new Error(Si)}catch{}}}var nn={};function an(Q,Se){{var Fe=Q.constructor,Le=Fe&&(Fe.displayName||Fe.name)||"ReactClass",pt=Le+"."+Se;if(nn[pt])return;dt("Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",Se,Le),nn[pt]=!0}}var On={isMounted:function(Q){return!1},enqueueForceUpdate:function(Q,Se,Fe){an(Q,"forceUpdate")},enqueueReplaceState:function(Q,Se,Fe,Le){an(Q,"replaceState")},enqueueSetState:function(Q,Se,Fe,Le){an(Q,"setState")}},lr={};Object.freeze(lr);function ln(Q,Se,Fe){this.props=Q,this.context=Se,this.refs=lr,this.updater=Fe||On}ln.prototype.isReactComponent={},ln.prototype.setState=function(Q,Se){if(!(typeof Q=="object"||typeof Q=="function"||Q==null))throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Q,Se,"setState")},ln.prototype.forceUpdate=function(Q){this.updater.enqueueForceUpdate(this,Q,"forceUpdate")};{var Vt={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]},Er=function(Q,Se){Object.defineProperty(ln.prototype,Q,{get:function(){He("%s(...) is deprecated in plain JavaScript React classes. %s",Se[0],Se[1])}})};for(var S in Vt)Vt.hasOwnProperty(S)&&Er(S,Vt[S])}function zt(){}zt.prototype=ln.prototype;function Xn(Q,Se,Fe){this.props=Q,this.context=Se,this.refs=lr,this.updater=Fe||On}var vr=Xn.prototype=new zt;vr.constructor=Xn,o(vr,ln.prototype),vr.isPureReactComponent=!0;function jr(){var Q={current:null};return Object.seal(Q),Q}var fr=Object.prototype.hasOwnProperty,zr={key:!0,ref:!0,__self:!0,__source:!0},Xt,Du,c0;c0={};function Ao(Q){if(fr.call(Q,"ref")){var Se=Object.getOwnPropertyDescriptor(Q,"ref").get;if(Se&&Se.isReactWarning)return!1}return Q.ref!==void 0}function Jo(Q){if(fr.call(Q,"key")){var Se=Object.getOwnPropertyDescriptor(Q,"key").get;if(Se&&Se.isReactWarning)return!1}return Q.key!==void 0}function Fs(Q,Se){var Fe=function(){Xt||(Xt=!0,dt("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)",Se))};Fe.isReactWarning=!0,Object.defineProperty(Q,"key",{get:Fe,configurable:!0})}function Zo(Q,Se){var Fe=function(){Du||(Du=!0,dt("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)",Se))};Fe.isReactWarning=!0,Object.defineProperty(Q,"ref",{get:Fe,configurable:!0})}function $o(Q){if(typeof Q.ref=="string"&>.current&&Q.__self&>.current.stateNode!==Q.__self){var Se=Jt(gt.current.type);c0[Se]||(dt('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://fb.me/react-strict-mode-string-ref',Jt(gt.current.type),Q.ref),c0[Se]=!0)}}var qt=function(Q,Se,Fe,Le,pt,Yn,Cn){var cr={$$typeof:E,type:Q,key:Se,ref:Fe,props:Cn,_owner:Yn};return cr._store={},Object.defineProperty(cr._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(cr,"_self",{configurable:!1,enumerable:!1,writable:!1,value:Le}),Object.defineProperty(cr,"_source",{configurable:!1,enumerable:!1,writable:!1,value:pt}),Object.freeze&&(Object.freeze(cr.props),Object.freeze(cr)),cr};function xi(Q,Se,Fe){var Le,pt={},Yn=null,Cn=null,cr=null,Si=null;if(Se!=null){Ao(Se)&&(Cn=Se.ref,$o(Se)),Jo(Se)&&(Yn=""+Se.key),cr=Se.__self===void 0?null:Se.__self,Si=Se.__source===void 0?null:Se.__source;for(Le in Se)fr.call(Se,Le)&&!zr.hasOwnProperty(Le)&&(pt[Le]=Se[Le])}var Ou=arguments.length-2;if(Ou===1)pt.children=Fe;else if(Ou>1){for(var ju=Array(Ou),zu=0;zu1){for(var wu=Array(zu),Ti=0;Ti is not supported and will be removed in a future major release. Did you mean to render instead?")),Fe.Provider},set:function(Cn){Fe.Provider=Cn}},_currentValue:{get:function(){return Fe._currentValue},set:function(Cn){Fe._currentValue=Cn}},_currentValue2:{get:function(){return Fe._currentValue2},set:function(Cn){Fe._currentValue2=Cn}},_threadCount:{get:function(){return Fe._threadCount},set:function(Cn){Fe._threadCount=Cn}},Consumer:{get:function(){return Le||(Le=!0,dt("Rendering is not supported and will be removed in a future major release. Did you mean to render instead?")),Fe.Consumer}}}),Fe.Consumer=Yn}return Fe._currentRenderer=null,Fe._currentRenderer2=null,Fe}function Wt(Q){var Se={$$typeof:De,_ctor:Q,_status:-1,_result:null};{var Fe,Le;Object.defineProperties(Se,{defaultProps:{configurable:!0,get:function(){return Fe},set:function(pt){dt("React.lazy(...): It is not supported to assign `defaultProps` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),Fe=pt,Object.defineProperty(Se,"defaultProps",{enumerable:!0})}},propTypes:{configurable:!0,get:function(){return Le},set:function(pt){dt("React.lazy(...): It is not supported to assign `propTypes` to a lazy component import. Either specify them where the component is defined, or create a wrapping component around it."),Le=pt,Object.defineProperty(Se,"propTypes",{enumerable:!0})}}})}return Se}function Ru(Q){return Q!=null&&Q.$$typeof===me?dt("forwardRef requires a render function but received a `memo` component. Instead of forwardRef(memo(...)), use memo(forwardRef(...))."):typeof Q!="function"?dt("forwardRef requires a render function but was given %s.",Q===null?"null":typeof Q):Q.length!==0&&Q.length!==2&&dt("forwardRef render functions accept exactly two parameters: props and ref. %s",Q.length===1?"Did you forget to use the ref parameter?":"Any additional parameter will be undefined."),Q!=null&&(Q.defaultProps!=null||Q.propTypes!=null)&&dt("forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?"),{$$typeof:V,render:Q}}function eu(Q){return typeof Q=="string"||typeof Q=="function"||Q===N||Q===q||Q===k||Q===F||Q===re||Q===y||typeof Q=="object"&&Q!==null&&(Q.$$typeof===De||Q.$$typeof===me||Q.$$typeof===x||Q.$$typeof===j||Q.$$typeof===V||Q.$$typeof===ae||Q.$$typeof===we||Q.$$typeof===he||Q.$$typeof===ge)}function Q0(Q,Se){return eu(Q)||dt("memo: The first argument must be a component. Instead received: %s",Q===null?"null":typeof Q),{$$typeof:me,type:Q,compare:Se===void 0?null:Se}}function Yi(){var Q=ze.current;if(Q===null)throw Error(`Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: +1. You might have mismatching versions of React and the renderer (such as React DOM) +2. You might be breaking the Rules of Hooks +3. You might have more than one copy of React in the same app +See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.`);return Q}function Xl(Q,Se){var Fe=Yi();if(Se!==void 0&&dt("useContext() second argument is reserved for future use in React. Passing it is not supported. You passed: %s.%s",Se,typeof Se=="number"&&Array.isArray(arguments[2])?` + +Did you call array.map(useContext)? Calling Hooks inside a loop is not supported. Learn more at https://fb.me/rules-of-hooks`:""),Q._context!==void 0){var Le=Q._context;Le.Consumer===Q?dt("Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):Le.Provider===Q&&dt("Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")}return Fe.useContext(Q,Se)}function ko(Q){var Se=Yi();return Se.useState(Q)}function li(Q,Se,Fe){var Le=Yi();return Le.useReducer(Q,Se,Fe)}function ao(Q){var Se=Yi();return Se.useRef(Q)}function Ql(Q,Se){var Fe=Yi();return Fe.useEffect(Q,Se)}function No(Q,Se){var Fe=Yi();return Fe.useLayoutEffect(Q,Se)}function Is(Q,Se){var Fe=Yi();return Fe.useCallback(Q,Se)}function $n(Q,Se){var Fe=Yi();return Fe.useMemo(Q,Se)}function tl(Q,Se,Fe){var Le=Yi();return Le.useImperativeHandle(Q,Se,Fe)}function fo(Q,Se){{var Fe=Yi();return Fe.useDebugValue(Q,Se)}}var I0;I0=!1;function Sl(){if(gt.current){var Q=Jt(gt.current.type);if(Q)return` + +Check the render method of \``+Q+"`."}return""}function Lo(Q){if(Q!==void 0){var Se=Q.fileName.replace(/^.*[\\\/]/,""),Fe=Q.lineNumber;return` + +Check your code at `+Se+":"+Fe+"."}return""}function St(Q){return Q!=null?Lo(Q.__source):""}var Bt={};function Hn(Q){var Se=Sl();if(!Se){var Fe=typeof Q=="string"?Q:Q.displayName||Q.name;Fe&&(Se=` + +Check the top-level render call using <`+Fe+">.")}return Se}function qr(Q,Se){if(!(!Q._store||Q._store.validated||Q.key!=null)){Q._store.validated=!0;var Fe=Hn(Se);if(!Bt[Fe]){Bt[Fe]=!0;var Le="";Q&&Q._owner&&Q._owner!==gt.current&&(Le=" It was passed a child from "+Jt(Q._owner.type)+"."),ce(Q),dt('Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.',Fe,Le),ce(null)}}}function Ki(Q,Se){if(typeof Q=="object"){if(Array.isArray(Q))for(var Fe=0;Fe",pt=" Did you accidentally export a JSX literal instead of a component?"):Cn=typeof Q,dt("React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",Cn,pt)}var cr=xi.apply(this,arguments);if(cr==null)return cr;if(Le)for(var Si=2;Si{"use strict";process.env.NODE_ENV==="production"?QE.exports=eS():QE.exports=lS()});var sS=nt((Wv,By)=>{(function(){var o,l="4.17.21",f=200,h="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",E="Expected a function",t="Invalid `variable` option passed into `_.template`",N="__lodash_hash_undefined__",F=500,k="__lodash_placeholder__",x=1,j=2,q=4,V=1,re=2,y=1,me=2,De=4,ge=8,ae=16,we=32,he=64,ve=128,ue=256,Ae=512,ze=30,We="...",gt=800,_t=16,Qe=1,ot=2,Ve=3,Pt=1/0,Jt=9007199254740991,it=17976931348623157e292,J=0/0,ce=4294967295,Re=ce-1,le=ce>>>1,He=[["ary",ve],["bind",y],["bindKey",me],["curry",ge],["curryRight",ae],["flip",Ae],["partial",we],["partialRight",he],["rearg",ue]],dt="[object Arguments]",At="[object Array]",nn="[object AsyncFunction]",an="[object Boolean]",On="[object Date]",lr="[object DOMException]",ln="[object Error]",Vt="[object Function]",Er="[object GeneratorFunction]",S="[object Map]",zt="[object Number]",Xn="[object Null]",vr="[object Object]",jr="[object Promise]",fr="[object Proxy]",zr="[object RegExp]",Xt="[object Set]",Du="[object String]",c0="[object Symbol]",Ao="[object Undefined]",Jo="[object WeakMap]",Fs="[object WeakSet]",Zo="[object ArrayBuffer]",$o="[object DataView]",qt="[object Float32Array]",xi="[object Float64Array]",lu="[object Int8Array]",vi="[object Int16Array]",Dr="[object Int32Array]",el="[object Uint8Array]",Y0="[object Uint8ClampedArray]",Bu="[object Uint16Array]",K0="[object Uint32Array]",Kr=/\b__p \+= '';/g,Oo=/\b(__p \+=) '' \+/g,Mo=/(__e\(.*?\)|\b__t\)) \+\n'';/g,F0=/&(?:amp|lt|gt|quot|#39);/g,su=/[&<>"']/g,ki=RegExp(F0.source),Ps=RegExp(su.source),Kl=/<%-([\s\S]+?)%>/g,P0=/<%([\s\S]+?)%>/g,d0=/<%=([\s\S]+?)%>/g,Hr=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,Ri=/^\w*$/,X0=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,mi=/[\\^$.*+?()[\]{}|]/g,en=RegExp(mi.source),In=/^\s+/,Ai=/\s/,yi=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,Wt=/\{\n\/\* \[wrapped with (.+)\] \*/,Ru=/,? & /,eu=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,Q0=/[()=,{}\[\]\/\s]/,Yi=/\\(\\)?/g,Xl=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,ko=/\w*$/,li=/^[-+]0x[0-9a-f]+$/i,ao=/^0b[01]+$/i,Ql=/^\[object .+?Constructor\]$/,No=/^0o[0-7]+$/i,Is=/^(?:0|[1-9]\d*)$/,$n=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,tl=/($^)/,fo=/['\n\r\u2028\u2029\\]/g,I0="\\ud800-\\udfff",Sl="\\u0300-\\u036f",Lo="\\ufe20-\\ufe2f",St="\\u20d0-\\u20ff",Bt=Sl+Lo+St,Hn="\\u2700-\\u27bf",qr="a-z\\xdf-\\xf6\\xf8-\\xff",Ki="\\xac\\xb1\\xd7\\xf7",Xr="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",Au="\\u2000-\\u206f",p0=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Ni="A-Z\\xc0-\\xd6\\xd8-\\xde",h0="\\ufe0e\\ufe0f",hs=Ki+Xr+Au+p0,Ct="['\u2019]",co="["+I0+"]",nl="["+hs+"]",Jl="["+Bt+"]",Uu="\\d+",vs="["+Hn+"]",b0="["+qr+"]",Q="[^"+I0+hs+Uu+Hn+qr+Ni+"]",Se="\\ud83c[\\udffb-\\udfff]",Fe="(?:"+Jl+"|"+Se+")",Le="[^"+I0+"]",pt="(?:\\ud83c[\\udde6-\\uddff]){2}",Yn="[\\ud800-\\udbff][\\udc00-\\udfff]",Cn="["+Ni+"]",cr="\\u200d",Si="(?:"+b0+"|"+Q+")",Ou="(?:"+Cn+"|"+Q+")",ju="(?:"+Ct+"(?:d|ll|m|re|s|t|ve))?",zu="(?:"+Ct+"(?:D|LL|M|RE|S|T|VE))?",wu=Fe+"?",Ti="["+h0+"]?",Fo="(?:"+cr+"(?:"+[Le,pt,Yn].join("|")+")"+Ti+wu+")*",Mu="\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",po="\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])",Hu=Ti+wu+Fo,Pa="(?:"+[vs,pt,Yn].join("|")+")"+Hu,v0="(?:"+[Le+Jl+"?",Jl,pt,Yn,co].join("|")+")",ia=RegExp(Ct,"g"),J0=RegExp(Jl,"g"),ua=RegExp(Se+"(?="+Se+")|"+v0+Hu,"g"),Ia=RegExp([Cn+"?"+b0+"+"+ju+"(?="+[nl,Cn,"$"].join("|")+")",Ou+"+"+zu+"(?="+[nl,Cn+Si,"$"].join("|")+")",Cn+"?"+Si+"+"+ju,Cn+"+"+zu,po,Mu,Uu,Pa].join("|"),"g"),ms=RegExp("["+cr+I0+Bt+h0+"]"),S0=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Qn=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],ac=-1,si={};si[qt]=si[xi]=si[lu]=si[vi]=si[Dr]=si[el]=si[Y0]=si[Bu]=si[K0]=!0,si[dt]=si[At]=si[Zo]=si[an]=si[$o]=si[On]=si[ln]=si[Vt]=si[S]=si[zt]=si[vr]=si[zr]=si[Xt]=si[Du]=si[Jo]=!1;var Jr={};Jr[dt]=Jr[At]=Jr[Zo]=Jr[$o]=Jr[an]=Jr[On]=Jr[qt]=Jr[xi]=Jr[lu]=Jr[vi]=Jr[Dr]=Jr[S]=Jr[zt]=Jr[vr]=Jr[zr]=Jr[Xt]=Jr[Du]=Jr[c0]=Jr[el]=Jr[Y0]=Jr[Bu]=Jr[K0]=!0,Jr[ln]=Jr[Vt]=Jr[Jo]=!1;var Zl={\u00C0:"A",\u00C1:"A",\u00C2:"A",\u00C3:"A",\u00C4:"A",\u00C5:"A",\u00E0:"a",\u00E1:"a",\u00E2:"a",\u00E3:"a",\u00E4:"a",\u00E5:"a",\u00C7:"C",\u00E7:"c",\u00D0:"D",\u00F0:"d",\u00C8:"E",\u00C9:"E",\u00CA:"E",\u00CB:"E",\u00E8:"e",\u00E9:"e",\u00EA:"e",\u00EB:"e",\u00CC:"I",\u00CD:"I",\u00CE:"I",\u00CF:"I",\u00EC:"i",\u00ED:"i",\u00EE:"i",\u00EF:"i",\u00D1:"N",\u00F1:"n",\u00D2:"O",\u00D3:"O",\u00D4:"O",\u00D5:"O",\u00D6:"O",\u00D8:"O",\u00F2:"o",\u00F3:"o",\u00F4:"o",\u00F5:"o",\u00F6:"o",\u00F8:"o",\u00D9:"U",\u00DA:"U",\u00DB:"U",\u00DC:"U",\u00F9:"u",\u00FA:"u",\u00FB:"u",\u00FC:"u",\u00DD:"Y",\u00FD:"y",\u00FF:"y",\u00C6:"Ae",\u00E6:"ae",\u00DE:"Th",\u00FE:"th",\u00DF:"ss",\u0100:"A",\u0102:"A",\u0104:"A",\u0101:"a",\u0103:"a",\u0105:"a",\u0106:"C",\u0108:"C",\u010A:"C",\u010C:"C",\u0107:"c",\u0109:"c",\u010B:"c",\u010D:"c",\u010E:"D",\u0110:"D",\u010F:"d",\u0111:"d",\u0112:"E",\u0114:"E",\u0116:"E",\u0118:"E",\u011A:"E",\u0113:"e",\u0115:"e",\u0117:"e",\u0119:"e",\u011B:"e",\u011C:"G",\u011E:"G",\u0120:"G",\u0122:"G",\u011D:"g",\u011F:"g",\u0121:"g",\u0123:"g",\u0124:"H",\u0126:"H",\u0125:"h",\u0127:"h",\u0128:"I",\u012A:"I",\u012C:"I",\u012E:"I",\u0130:"I",\u0129:"i",\u012B:"i",\u012D:"i",\u012F:"i",\u0131:"i",\u0134:"J",\u0135:"j",\u0136:"K",\u0137:"k",\u0138:"k",\u0139:"L",\u013B:"L",\u013D:"L",\u013F:"L",\u0141:"L",\u013A:"l",\u013C:"l",\u013E:"l",\u0140:"l",\u0142:"l",\u0143:"N",\u0145:"N",\u0147:"N",\u014A:"N",\u0144:"n",\u0146:"n",\u0148:"n",\u014B:"n",\u014C:"O",\u014E:"O",\u0150:"O",\u014D:"o",\u014F:"o",\u0151:"o",\u0154:"R",\u0156:"R",\u0158:"R",\u0155:"r",\u0157:"r",\u0159:"r",\u015A:"S",\u015C:"S",\u015E:"S",\u0160:"S",\u015B:"s",\u015D:"s",\u015F:"s",\u0161:"s",\u0162:"T",\u0164:"T",\u0166:"T",\u0163:"t",\u0165:"t",\u0167:"t",\u0168:"U",\u016A:"U",\u016C:"U",\u016E:"U",\u0170:"U",\u0172:"U",\u0169:"u",\u016B:"u",\u016D:"u",\u016F:"u",\u0171:"u",\u0173:"u",\u0174:"W",\u0175:"w",\u0176:"Y",\u0177:"y",\u0178:"Y",\u0179:"Z",\u017B:"Z",\u017D:"Z",\u017A:"z",\u017C:"z",\u017E:"z",\u0132:"IJ",\u0133:"ij",\u0152:"Oe",\u0153:"oe",\u0149:"'n",\u017F:"s"},oa={"&":"&","<":"<",">":">",'"':""","'":"'"},pf={"&":"&","<":"<",">":">",""":'"',"'":"'"},bs={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},ba=parseFloat,Bs=parseInt,m0=typeof global=="object"&&global&&global.Object===Object&&global,Us=typeof self=="object"&&self&&self.Object===Object&&self,zi=m0||Us||Function("return this")(),U=typeof Wv=="object"&&Wv&&!Wv.nodeType&&Wv,H=U&&typeof By=="object"&&By&&!By.nodeType&&By,Y=H&&H.exports===U,ee=Y&&m0.process,Ce=function(){try{var xe=H&&H.require&&H.require("util").types;return xe||ee&&ee.binding&&ee.binding("util")}catch{}}(),_e=Ce&&Ce.isArrayBuffer,Oe=Ce&&Ce.isDate,$=Ce&&Ce.isMap,Ne=Ce&&Ce.isRegExp,Je=Ce&&Ce.isSet,vt=Ce&&Ce.isTypedArray;function oe(xe,tt,Ke){switch(Ke.length){case 0:return xe.call(tt);case 1:return xe.call(tt,Ke[0]);case 2:return xe.call(tt,Ke[0],Ke[1]);case 3:return xe.call(tt,Ke[0],Ke[1],Ke[2])}return xe.apply(tt,Ke)}function qe(xe,tt,Ke,Yt){for(var Kt=-1,pr=xe==null?0:xe.length;++Kt-1}function rn(xe,tt,Ke){for(var Yt=-1,Kt=xe==null?0:xe.length;++Yt-1;);return Ke}function Tl(xe,tt){for(var Ke=xe.length;Ke--&&wt(tt,xe[Ke],0)>-1;);return Ke}function vf(xe,tt){for(var Ke=xe.length,Yt=0;Ke--;)xe[Ke]===tt&&++Yt;return Yt}var Io=Jn(Zl),ys=Jn(oa);function js(xe){return"\\"+bs[xe]}function bo(xe,tt){return xe==null?o:xe[tt]}function Bo(xe){return ms.test(xe)}function gs(xe){return S0.test(xe)}function Xu(xe){for(var tt,Ke=[];!(tt=xe.next()).done;)Ke.push(tt.value);return Ke}function Su(xe){var tt=-1,Ke=Array(xe.size);return xe.forEach(function(Yt,Kt){Ke[++tt]=[Kt,Yt]}),Ke}function _i(xe,tt){return function(Ke){return xe(tt(Ke))}}function C0(xe,tt){for(var Ke=-1,Yt=xe.length,Kt=0,pr=[];++Ke-1}function fa(p,m){var R=this.__data__,I=ts(R,p);return I<0?(++this.size,R.push([p,m])):R[I][1]=m,this}io.prototype.clear=Ba,io.prototype.delete=_f,io.prototype.get=fc,io.prototype.has=Ds,io.prototype.set=fa;function U0(p){var m=-1,R=p==null?0:p.length;for(this.clear();++m=m?p:m)),p}function j0(p,m,R,I,W,te){var pe,Ee=m&x,be=m&j,Dt=m&q;if(R&&(pe=W?R(p,I,W,te):R(p)),pe!==o)return pe;if(!Iu(p))return p;var Tt=tr(p);if(Tt){if(pe=Cs(p),!Ee)return iu(p,pe)}else{var Ot=Pu(p),on=Ot==Vt||Ot==Er;if(Js(p))return vc(p,Ee);if(Ot==vr||Ot==dt||on&&!W){if(pe=be||on?{}:Ec(p),!Ee)return be?ns(p,ol(pe,p)):u0(p,Ef(pe,p))}else{if(!Jr[Ot])return W?p:{};pe=Th(p,Ot,Ee)}}te||(te=new ul);var Mn=te.get(p);if(Mn)return Mn;te.set(p,pe),Pd(p)?p.forEach(function(ar){pe.add(j0(ar,m,R,ar,p,te))}):_p(p)&&p.forEach(function(ar,ri){pe.set(ri,j0(ar,m,R,ri,p,te))});var rr=Dt?be?sr:r1:be?dn:N0,br=Tt?o:rr(p);return rt(br||p,function(ar,ri){br&&(ri=ar,ar=p[ri]),Ss(pe,ri,j0(ar,m,R,ri,p,te))}),pe}function Df(p){var m=N0(p);return function(R){return Wc(R,p,m)}}function Wc(p,m,R){var I=R.length;if(p==null)return!I;for(p=bn(p);I--;){var W=R[I],te=m[W],pe=p[W];if(pe===o&&!(W in p)||!te(pe))return!1}return!0}function dc(p,m,R){if(typeof p!="function")throw new $r(E);return Qa(function(){p.apply(o,R)},m)}function Ol(p,m,R,I){var W=-1,te=sn,pe=!0,Ee=p.length,be=[],Dt=m.length;if(!Ee)return be;R&&(m=Ft(m,gi(R))),I?(te=rn,pe=!1):m.length>=f&&(te=rl,pe=!1,m=new yo(m));e:for(;++WW?0:W+R),I=I===o||I>W?W:Mr(I),I<0&&(I+=W),I=R>I?0:Dp(I);R0&&R(Ee)?m>1?Wi(Ee,m-1,R,I,W):Dn(W,Ee):I||(W[W.length]=Ee)}return W}var _=yc(),g=yc(!0);function A(p,m){return p&&_(p,m,N0)}function P(p,m){return p&&g(p,m,N0)}function B(p,m){return bt(m,function(R){return xa(p[R])})}function Z(p,m){m=Ws(m,p);for(var R=0,I=m.length;p!=null&&Rm}function Nt(p,m){return p!=null&&ui.call(p,m)}function xr(p,m){return p!=null&&m in bn(p)}function r0(p,m,R){return p>=Kn(m,R)&&p=120&&Tt.length>=120)?new yo(pe&&Tt):o}Tt=p[0];var Ot=-1,on=Ee[0];e:for(;++Ot-1;)Ee!==p&&O0.call(Ee,be,1),O0.call(p,be,1);return p}function sd(p,m){for(var R=p?m.length:0,I=R-1;R--;){var W=m[R];if(R==I||W!==te){var te=W;Do(W)?O0.call(p,W,1):x2(p,W)}}return p}function ad(p,m){return p+Es(_0()*(m-p+1))}function S2(p,m,R,I){for(var W=-1,te=ei(Zu((m-p)/(R||1)),0),pe=Ke(te);te--;)pe[I?te:++W]=p,p+=R;return pe}function Yc(p,m){var R="";if(!p||m<1||m>Jt)return R;do m%2&&(R+=p),m=Es(m/2),m&&(p+=p);while(m);return R}function Ir(p,m){return l1(L2(p,m,l0),p+"")}function fd(p){return za(Nc(p))}function cd(p,m){var R=Nc(p);return wc(R,n0(m,0,R.length))}function Ga(p,m,R,I){if(!Iu(p))return p;m=Ws(m,p);for(var W=-1,te=m.length,pe=te-1,Ee=p;Ee!=null&&++WW?0:W+m),R=R>W?W:R,R<0&&(R+=W),W=m>R?0:R-m>>>0,m>>>=0;for(var te=Ke(W);++I>>1,pe=p[te];pe!==null&&!Bl(pe)&&(R?pe<=m:pe=f){var Dt=m?null:am(p);if(Dt)return $0(Dt);pe=!1,W=rl,be=new yo}else be=m?[]:Ee;e:for(;++I=I?p:sl(p,m,R)}var Zc=_s||function(p){return zi.clearTimeout(p)};function vc(p,m){if(m)return p.slice();var R=p.length,I=qi?qi(R):new p.constructor(R);return p.copy(I),I}function mc(p){var m=new p.constructor(p.byteLength);return new A0(m).set(new A0(p)),m}function pd(p,m){var R=m?mc(p.buffer):p.buffer;return new p.constructor(R,p.byteOffset,p.byteLength)}function Eh(p){var m=new p.constructor(p.source,ko.exec(p));return m.lastIndex=p.lastIndex,m}function Tf(p){return Ar?bn(Ar.call(p)):{}}function $c(p,m){var R=m?mc(p.buffer):p.buffer;return new p.constructor(R,p.byteOffset,p.length)}function Dh(p,m){if(p!==m){var R=p!==o,I=p===null,W=p===p,te=Bl(p),pe=m!==o,Ee=m===null,be=m===m,Dt=Bl(m);if(!Ee&&!Dt&&!te&&p>m||te&&pe&&be&&!Ee&&!Dt||I&&pe&&be||!R&&be||!W)return 1;if(!I&&!te&&!Dt&&p=Ee)return be;var Dt=R[I];return be*(Dt=="desc"?-1:1)}}return p.index-m.index}function Vs(p,m,R,I){for(var W=-1,te=p.length,pe=R.length,Ee=-1,be=m.length,Dt=ei(te-pe,0),Tt=Ke(be+Dt),Ot=!I;++Ee1?R[W-1]:o,pe=W>2?R[2]:o;for(te=p.length>3&&typeof te=="function"?(W--,te):o,pe&&lo(R[0],R[1],pe)&&(te=W<3?o:te,W=1),m=bn(m);++I-1?W[te?m[pe]:pe]:o}}function t1(p){return cl(function(m){var R=m.length,I=R,W=Wr.prototype.thru;for(p&&m.reverse();I--;){var te=m[I];if(typeof te!="function")throw new $r(E);if(W&&!pe&&qo(te)=="wrapper")var pe=new Wr([],!0)}for(I=pe?I:R;++I1&&fi.reverse(),Tt&&beEe))return!1;var Dt=te.get(p),Tt=te.get(m);if(Dt&&Tt)return Dt==m&&Tt==p;var Ot=-1,on=!0,Mn=R&re?new yo:o;for(te.set(p,m),te.set(m,p);++Ot1?"& ":"")+m[I],m=m.join(R>2?", ":" "),p.replace(yi,`{ +/* [wrapped with `+m+`] */ +`)}function is(p){return tr(p)||pl(p)||!!(vo&&p&&p[vo])}function Do(p,m){var R=typeof p;return m=m==null?Jt:m,!!m&&(R=="number"||R!="symbol"&&Is.test(p))&&p>-1&&p%1==0&&p0){if(++m>=gt)return arguments[0]}else m=0;return p.apply(o,arguments)}}function wc(p,m){var R=-1,I=p.length,W=I-1;for(m=m===o?I:m;++R1?p[m-1]:o;return R=typeof R=="function"?(p.pop(),R):o,wd(p,R)});function zh(p){var m=K(p);return m.__chain__=!0,m}function Hh(p,m){return m(p),p}function g1(p,m){return m(p)}var J2=cl(function(p){var m=p.length,R=m?p[0]:0,I=this.__wrapped__,W=function(te){return qa(te,p)};return m>1||this.__actions__.length||!(I instanceof ft)||!Do(R)?this.thru(W):(I=I.slice(R,+R+(m?1:0)),I.__actions__.push({func:g1,args:[W],thisArg:o}),new Wr(I,this.__chain__).thru(function(te){return m&&!te.length&&te.push(o),te}))});function qh(){return zh(this)}function Z2(){return new Wr(this.value(),this.__chain__)}function Wh(){this.__values__===o&&(this.__values__=fv(this.value()));var p=this.__index__>=this.__values__.length,m=p?o:this.__values__[this.__index__++];return{done:p,value:m}}function _m(){return this}function Em(p){for(var m,R=this;R instanceof ni;){var I=P2(R);I.__index__=0,I.__values__=o,m?W.__wrapped__=I:m=I;var W=I;R=R.__wrapped__}return W.__wrapped__=p,m}function Pf(){var p=this.__wrapped__;if(p instanceof ft){var m=p;return this.__actions__.length&&(m=new ft(this)),m=m.reverse(),m.__actions__.push({func:g1,args:[W2],thisArg:o}),new Wr(m,this.__chain__)}return this.thru(W2)}function If(){return _h(this.__wrapped__,this.__actions__)}var Sd=Ya(function(p,m,R){ui.call(p,R)?++p[R]:Vu(p,R,1)});function Dm(p,m,R){var I=tr(p)?kt:ud;return R&&lo(p,m,R)&&(m=o),I(p,Vn(m,3))}function $2(p,m){var R=tr(p)?bt:Vc;return R(p,Vn(m,3))}var Td=Nl(U2),ep=Nl(a1);function Vh(p,m){return Wi(_1(p,m),1)}function tp(p,m){return Wi(_1(p,m),Pt)}function Gh(p,m,R){return R=R===o?1:Mr(R),Wi(_1(p,m),R)}function Yh(p,m){var R=tr(p)?rt:Ts;return R(p,Vn(m,3))}function np(p,m){var R=tr(p)?xt:da;return R(p,Vn(m,3))}var wm=Ya(function(p,m,R){ui.call(p,R)?p[R].push(m):Vu(p,R,[m])});function Sm(p,m,R,I){p=hl(p)?p:Nc(p),R=R&&!I?Mr(R):0;var W=p.length;return R<0&&(R=ei(W+R,0)),S1(p)?R<=W&&p.indexOf(m,R)>-1:!!W&&wt(p,m,R)>-1}var Tm=Ir(function(p,m,R){var I=-1,W=typeof m=="function",te=hl(p)?Ke(p.length):[];return Ts(p,function(pe){te[++I]=W?oe(m,pe,R):Ml(pe,m,R)}),te}),Kh=Ya(function(p,m,R){Vu(p,R,m)});function _1(p,m){var R=tr(p)?Ft:D2;return R(p,Vn(m,3))}function Cm(p,m,R,I){return p==null?[]:(tr(m)||(m=m==null?[]:[m]),R=I?o:R,tr(R)||(R=R==null?[]:[R]),go(p,m,R))}var rp=Ya(function(p,m,R){p[R?0:1].push(m)},function(){return[[],[]]});function ip(p,m,R){var I=tr(p)?dr:wr,W=arguments.length<3;return I(p,Vn(m,4),R,W,Ts)}function xm(p,m,R){var I=tr(p)?er:wr,W=arguments.length<3;return I(p,Vn(m,4),R,W,da)}function Rm(p,m){var R=tr(p)?bt:Vc;return R(p,Rd(Vn(m,3)))}function Xh(p){var m=tr(p)?za:fd;return m(p)}function Am(p,m,R){(R?lo(p,m,R):m===o)?m=1:m=Mr(m);var I=tr(p)?Ha:cd;return I(p,m)}function Om(p){var m=tr(p)?ca:ll;return m(p)}function up(p){if(p==null)return 0;if(hl(p))return S1(p)?tu(p):p.length;var m=Pu(p);return m==S||m==Xt?p.size:Wa(p).length}function op(p,m,R){var I=tr(p)?Cr:yh;return R&&lo(p,m,R)&&(m=o),I(p,Vn(m,3))}var Ta=Ir(function(p,m){if(p==null)return[];var R=m.length;return R>1&&lo(p,m[0],m[1])?m=[]:R>2&&lo(m[0],m[1],m[2])&&(m=[m[0]]),go(p,Wi(m,1),[])}),E1=aa||function(){return zi.Date.now()};function lp(p,m){if(typeof m!="function")throw new $r(E);return p=Mr(p),function(){if(--p<1)return m.apply(this,arguments)}}function Qh(p,m,R){return m=R?o:m,m=p&&m==null?p.length:m,hn(p,ve,o,o,o,o,m)}function Cd(p,m){var R;if(typeof m!="function")throw new $r(E);return p=Mr(p),function(){return--p>0&&(R=m.apply(this,arguments)),p<=1&&(m=o),R}}var D1=Ir(function(p,m,R){var I=y;if(R.length){var W=C0(R,yr(D1));I|=we}return hn(p,I,m,R,W)}),Jh=Ir(function(p,m,R){var I=y|me;if(R.length){var W=C0(R,yr(Jh));I|=we}return hn(m,I,p,R,W)});function sp(p,m,R){m=R?o:m;var I=hn(p,ge,o,o,o,o,o,m);return I.placeholder=sp.placeholder,I}function Zh(p,m,R){m=R?o:m;var I=hn(p,ae,o,o,o,o,o,m);return I.placeholder=Zh.placeholder,I}function ap(p,m,R){var I,W,te,pe,Ee,be,Dt=0,Tt=!1,Ot=!1,on=!0;if(typeof p!="function")throw new $r(E);m=vl(m)||0,Iu(R)&&(Tt=!!R.leading,Ot="maxWait"in R,te=Ot?ei(vl(R.maxWait)||0,m):te,on="trailing"in R?!!R.trailing:on);function Mn(s0){var Os=I,Co=W;return I=W=o,Dt=s0,pe=p.apply(Co,Os),pe}function rr(s0){return Dt=s0,Ee=Qa(ri,m),Tt?Mn(s0):pe}function br(s0){var Os=s0-be,Co=s0-Dt,kv=m-Os;return Ot?Kn(kv,te-Co):kv}function ar(s0){var Os=s0-be,Co=s0-Dt;return be===o||Os>=m||Os<0||Ot&&Co>=te}function ri(){var s0=E1();if(ar(s0))return fi(s0);Ee=Qa(ri,br(s0))}function fi(s0){return Ee=o,on&&I?Mn(s0):(I=W=o,pe)}function zl(){Ee!==o&&Zc(Ee),Dt=0,I=be=W=Ee=o}function Zi(){return Ee===o?pe:fi(E1())}function so(){var s0=E1(),Os=ar(s0);if(I=arguments,W=this,be=s0,Os){if(Ee===o)return rr(be);if(Ot)return Zc(Ee),Ee=Qa(ri,m),Mn(be)}return Ee===o&&(Ee=Qa(ri,m)),pe}return so.cancel=zl,so.flush=Zi,so}var $h=Ir(function(p,m){return dc(p,1,m)}),ev=Ir(function(p,m,R){return dc(p,vl(m)||0,R)});function fp(p){return hn(p,Ae)}function xd(p,m){if(typeof p!="function"||m!=null&&typeof m!="function")throw new $r(E);var R=function(){var I=arguments,W=m?m.apply(this,I):I[0],te=R.cache;if(te.has(W))return te.get(W);var pe=p.apply(this,I);return R.cache=te.set(W,pe)||te,pe};return R.cache=new(xd.Cache||U0),R}xd.Cache=U0;function Rd(p){if(typeof p!="function")throw new $r(E);return function(){var m=arguments;switch(m.length){case 0:return!p.call(this);case 1:return!p.call(this,m[0]);case 2:return!p.call(this,m[0],m[1]);case 3:return!p.call(this,m[0],m[1],m[2])}return!p.apply(this,m)}}function H0(p){return Cd(2,p)}var Ad=O2(function(p,m){m=m.length==1&&tr(m[0])?Ft(m[0],gi(Vn())):Ft(Wi(m,1),gi(Vn()));var R=m.length;return Ir(function(I){for(var W=-1,te=Kn(I.length,R);++W=m}),pl=i0(function(){return arguments}())?i0:function(p){return Gu(p)&&ui.call(p,"callee")&&!B0.call(p,"callee")},tr=Ke.isArray,Qs=_e?gi(_e):Ge;function hl(p){return p!=null&&Ld(p.length)&&!xa(p)}function o0(p){return Gu(p)&&hl(p)}function rv(p){return p===!0||p===!1||Gu(p)&&yt(p)==an}var Js=no||Bp,vp=Oe?gi(Oe):je;function Fm(p){return Gu(p)&&p.nodeType===1&&!Cc(p)}function iv(p){if(p==null)return!0;if(hl(p)&&(tr(p)||typeof p=="string"||typeof p.splice=="function"||Js(p)||Ra(p)||pl(p)))return!p.length;var m=Pu(p);if(m==S||m==Xt)return!p.size;if(Nf(p))return!Wa(p).length;for(var R in p)if(ui.call(p,R))return!1;return!0}function mp(p,m){return st(p,m)}function Pm(p,m,R){R=typeof R=="function"?R:o;var I=R?R(p,m):o;return I===o?st(p,m,o,R):!!I}function yp(p){if(!Gu(p))return!1;var m=yt(p);return m==ln||m==lr||typeof p.message=="string"&&typeof p.name=="string"&&!Cc(p)}function Tc(p){return typeof p=="number"&&nu(p)}function xa(p){if(!Iu(p))return!1;var m=yt(p);return m==Vt||m==Er||m==nn||m==fr}function gp(p){return typeof p=="number"&&p==Mr(p)}function Ld(p){return typeof p=="number"&&p>-1&&p%1==0&&p<=Jt}function Iu(p){var m=typeof p;return p!=null&&(m=="object"||m=="function")}function Gu(p){return p!=null&&typeof p=="object"}var _p=$?gi($):Wn;function Ep(p,m){return p===m||oi(p,m,jn(m))}function uv(p,m,R){return R=typeof R=="function"?R:o,oi(p,m,jn(m),R)}function Im(p){return ov(p)&&p!=+p}function bm(p){if(Ll(p))throw new Kt(h);return ur(p)}function Bm(p){return p===null}function Fd(p){return p==null}function ov(p){return typeof p=="number"||Gu(p)&&yt(p)==zt}function Cc(p){if(!Gu(p)||yt(p)!=vr)return!1;var m=il(p);if(m===null)return!0;var R=ui.call(m,"constructor")&&m.constructor;return typeof R=="function"&&R instanceof R&&Lu.call(R)==sa}var w1=Ne?gi(Ne):ai;function Um(p){return gp(p)&&p>=-Jt&&p<=Jt}var Pd=Je?gi(Je):Qi;function S1(p){return typeof p=="string"||!tr(p)&&Gu(p)&&yt(p)==Du}function Bl(p){return typeof p=="symbol"||Gu(p)&&yt(p)==c0}var Ra=vt?gi(vt):Vr;function lv(p){return p===o}function jm(p){return Gu(p)&&Pu(p)==Jo}function sv(p){return Gu(p)&&yt(p)==Fs}var av=md(od),zm=md(function(p,m){return p<=m});function fv(p){if(!p)return[];if(hl(p))return S1(p)?Zr(p):iu(p);if(Fu&&p[Fu])return Xu(p[Fu]());var m=Pu(p),R=m==S?Su:m==Xt?$0:Nc;return R(p)}function Aa(p){if(!p)return p===0?p:0;if(p=vl(p),p===Pt||p===-Pt){var m=p<0?-1:1;return m*it}return p===p?p:0}function Mr(p){var m=Aa(p),R=m%1;return m===m?R?m-R:m:0}function Dp(p){return p?n0(Mr(p),0,ce):0}function vl(p){if(typeof p=="number")return p;if(Bl(p))return J;if(Iu(p)){var m=typeof p.valueOf=="function"?p.valueOf():p;p=Iu(m)?m+"":m}if(typeof p!="string")return p===0?p:+p;p=Nu(p);var R=ao.test(p);return R||No.test(p)?Bs(p.slice(2),R?2:8):li.test(p)?J:+p}function yu(p){return M0(p,dn(p))}function T1(p){return p?n0(Mr(p),-Jt,Jt):p===0?p:0}function Ui(p){return p==null?"":al(p)}var wp=uo(function(p,m){if(Nf(m)||hl(m)){M0(m,N0(m),p);return}for(var R in m)ui.call(m,R)&&Ss(p,R,m[R])}),Id=uo(function(p,m){M0(m,dn(m),p)}),To=uo(function(p,m,R,I){M0(m,dn(m),p,I)}),As=uo(function(p,m,R,I){M0(m,N0(m),p,I)}),bf=cl(qa);function bd(p,m){var R=ti(p);return m==null?R:Ef(R,m)}var Sp=Ir(function(p,m){p=bn(p);var R=-1,I=m.length,W=I>2?m[2]:o;for(W&&lo(m[0],m[1],W)&&(I=1);++R1),te}),M0(p,sr(p),R),I&&(R=j0(R,x|j|q,fm));for(var W=m.length;W--;)x2(R,m[W]);return R});function A1(p,m){return ef(p,Rd(Vn(m)))}var xp=cl(function(p,m){return p==null?{}:vh(p,m)});function ef(p,m){if(p==null)return{};var R=Ft(sr(p),function(I){return[I]});return m=Vn(m),mh(p,R,function(I,W){return m(I,W[0])})}function Hm(p,m,R){m=Ws(m,p);var I=-1,W=m.length;for(W||(W=1,p=o);++Im){var I=p;p=m,m=I}if(R||p%1||m%1){var W=_0();return Kn(p+W*(m-p+ba("1e-"+((W+"").length-1))),m)}return ad(p,m)}var Wd=Cf(function(p,m,R){return m=m.toLowerCase(),p+(R?Wo(m):m)});function Wo(p){return Op(Ui(p).toLowerCase())}function Vd(p){return p=Ui(p),p&&p.replace($n,Io).replace(J0,"")}function Wm(p,m,R){p=Ui(p),m=al(m);var I=p.length;R=R===o?I:n0(Mr(R),0,I);var W=R;return R-=m.length,R>=0&&p.slice(R,W)==m}function k1(p){return p=Ui(p),p&&Ps.test(p)?p.replace(su,ys):p}function Vm(p){return p=Ui(p),p&&en.test(p)?p.replace(mi,"\\$&"):p}var Gm=Cf(function(p,m,R){return p+(R?"-":"")+m.toLowerCase()}),dv=Cf(function(p,m,R){return p+(R?" ":"")+m.toLowerCase()}),Ym=wh("toLowerCase");function pv(p,m,R){p=Ui(p),m=Mr(m);var I=m?tu(p):0;if(!m||I>=m)return p;var W=(m-I)/2;return ga(Es(W),R)+p+ga(Zu(W),R)}function Km(p,m,R){p=Ui(p),m=Mr(m);var I=m?tu(p):0;return m&&I>>0,R?(p=Ui(p),p&&(typeof m=="string"||m!=null&&!w1(m))&&(m=al(m),!m&&Bo(p))?va(Zr(p),0,R):p.split(m,R)):[]}var zf=Cf(function(p,m,R){return p+(R?" ":"")+Op(m)});function vv(p,m,R){return p=Ui(p),R=R==null?0:n0(Mr(R),0,p.length),m=al(m),p.slice(R,R+m.length)==m}function mv(p,m,R){var I=K.templateSettings;R&&lo(p,m,R)&&(m=o),p=Ui(p),m=To({},m,I,Rf);var W=To({},m.imports,I.imports,Rf),te=N0(W),pe=Po(W,te),Ee,be,Dt=0,Tt=m.interpolate||tl,Ot="__p += '",on=mu((m.escape||tl).source+"|"+Tt.source+"|"+(Tt===d0?Xl:tl).source+"|"+(m.evaluate||tl).source+"|$","g"),Mn="//# sourceURL="+(ui.call(m,"sourceURL")?(m.sourceURL+"").replace(/\s/g," "):"lodash.templateSources["+ ++ac+"]")+` +`;p.replace(on,function(ar,ri,fi,zl,Zi,so){return fi||(fi=zl),Ot+=p.slice(Dt,so).replace(fo,js),ri&&(Ee=!0,Ot+=`' + +__e(`+ri+`) + +'`),Zi&&(be=!0,Ot+=`'; +`+Zi+`; +__p += '`),fi&&(Ot+=`' + +((__t = (`+fi+`)) == null ? '' : __t) + +'`),Dt=so+ar.length,ar}),Ot+=`'; +`;var rr=ui.call(m,"variable")&&m.variable;if(!rr)Ot=`with (obj) { +`+Ot+` +} +`;else if(Q0.test(rr))throw new Kt(t);Ot=(be?Ot.replace(Kr,""):Ot).replace(Oo,"$1").replace(Mo,"$1;"),Ot="function("+(rr||"obj")+`) { +`+(rr?"":`obj || (obj = {}); +`)+"var __t, __p = ''"+(Ee?", __e = _.escape":"")+(be?`, __j = Array.prototype.join; +function print() { __p += __j.call(arguments, '') } +`:`; +`)+Ot+`return __p +}`;var br=wv(function(){return pr(te,Mn+"return "+Ot).apply(o,pe)});if(br.source=Ot,yp(br))throw br;return br}function yv(p){return Ui(p).toLowerCase()}function Gd(p){return Ui(p).toUpperCase()}function Yd(p,m,R){if(p=Ui(p),p&&(R||m===o))return Nu(p);if(!p||!(m=al(m)))return p;var I=Zr(p),W=Zr(m),te=hf(I,W),pe=Tl(I,W)+1;return va(I,te,pe).join("")}function Ap(p,m,R){if(p=Ui(p),p&&(R||m===o))return p.slice(0,ho(p)+1);if(!p||!(m=al(m)))return p;var I=Zr(p),W=Tl(I,Zr(m))+1;return va(I,0,W).join("")}function gv(p,m,R){if(p=Ui(p),p&&(R||m===o))return p.replace(In,"");if(!p||!(m=al(m)))return p;var I=Zr(p),W=hf(I,Zr(m));return va(I,W).join("")}function Kd(p,m){var R=ze,I=We;if(Iu(m)){var W="separator"in m?m.separator:W;R="length"in m?Mr(m.length):R,I="omission"in m?al(m.omission):I}p=Ui(p);var te=p.length;if(Bo(p)){var pe=Zr(p);te=pe.length}if(R>=te)return p;var Ee=R-tu(I);if(Ee<1)return I;var be=pe?va(pe,0,Ee).join(""):p.slice(0,Ee);if(W===o)return be+I;if(pe&&(Ee+=be.length-Ee),w1(W)){if(p.slice(Ee).search(W)){var Dt,Tt=be;for(W.global||(W=mu(W.source,Ui(ko.exec(W))+"g")),W.lastIndex=0;Dt=W.exec(Tt);)var Ot=Dt.index;be=be.slice(0,Ot===o?Ee:Ot)}}else if(p.indexOf(al(W),Ee)!=Ee){var on=be.lastIndexOf(W);on>-1&&(be=be.slice(0,on))}return be+I}function _v(p){return p=Ui(p),p&&ki.test(p)?p.replace(F0,Bi):p}var Ev=Cf(function(p,m,R){return p+(R?" ":"")+m.toUpperCase()}),Op=wh("toUpperCase");function Dv(p,m,R){return p=Ui(p),m=R?o:m,m===o?gs(p)?yf(p):y0(p):p.match(m)||[]}var wv=Ir(function(p,m){try{return oe(p,o,m)}catch(R){return yp(R)?R:new Kt(R)}}),$m=cl(function(p,m){return rt(m,function(R){R=Fl(R),Vu(p,R,D1(p[R],p))}),p});function Sv(p){var m=p==null?0:p.length,R=Vn();return p=m?Ft(p,function(I){if(typeof I[1]!="function")throw new $r(E);return[R(I[0]),I[1]]}):[],Ir(function(I){for(var W=-1;++WJt)return[];var R=ce,I=Kn(p,ce);m=Vn(m),p-=ce;for(var W=T0(I,m);++R0||m<0)?new ft(R):(p<0?R=R.takeRight(-p):p&&(R=R.drop(p)),m!==o&&(m=Mr(m),R=m<0?R.dropRight(-m):R.take(m-p)),R)},ft.prototype.takeRightWhile=function(p){return this.reverse().takeWhile(p).reverse()},ft.prototype.toArray=function(){return this.take(ce)},A(ft.prototype,function(p,m){var R=/^(?:filter|find|map|reject)|While$/.test(m),I=/^(?:head|last)$/.test(m),W=K[I?"take"+(m=="last"?"Right":""):m],te=I||/^find/.test(m);!W||(K.prototype[m]=function(){var pe=this.__wrapped__,Ee=I?[1]:arguments,be=pe instanceof ft,Dt=Ee[0],Tt=be||tr(pe),Ot=function(ri){var fi=W.apply(K,Dn([ri],Ee));return I&&on?fi[0]:fi};Tt&&R&&typeof Dt=="function"&&Dt.length!=1&&(be=Tt=!1);var on=this.__chain__,Mn=!!this.__actions__.length,rr=te&&!on,br=be&&!Mn;if(!te&&Tt){pe=br?pe:new ft(this);var ar=p.apply(pe,Ee);return ar.__actions__.push({func:g1,args:[Ot],thisArg:o}),new Wr(ar,on)}return rr&&br?p.apply(this,Ee):(ar=this.thru(Ot),rr?I?ar.value()[0]:ar.value():ar)})}),rt(["pop","push","shift","sort","splice","unshift"],function(p){var m=Qr[p],R=/^(?:push|sort|unshift)$/.test(p)?"tap":"thru",I=/^(?:pop|shift)$/.test(p);K.prototype[p]=function(){var W=arguments;if(I&&!this.__chain__){var te=this.value();return m.apply(tr(te)?te:[],W)}return this[R](function(pe){return m.apply(tr(pe)?pe:[],W)})}}),A(ft.prototype,function(p,m){var R=K[m];if(R){var I=R.name+"";ui.call(An,I)||(An[I]=[]),An[I].push({name:m,func:R})}}),An[ya(o,me).name]=[{name:"wrapper",func:o}],ft.prototype.clone=Di,ft.prototype.reverse=ru,ft.prototype.value=E0,K.prototype.at=J2,K.prototype.chain=qh,K.prototype.commit=Z2,K.prototype.next=Wh,K.prototype.plant=Em,K.prototype.reverse=Pf,K.prototype.toJSON=K.prototype.valueOf=K.prototype.value=If,K.prototype.first=K.prototype.head,Fu&&(K.prototype[Fu]=_m),K},to=eo();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(zi._=to,define(function(){return to})):H?((H.exports=to)._=to,U._=to):zi._=to}).call(Wv)});var ZE=nt((tH,JE)=>{"use strict";var Pi=JE.exports;JE.exports.default=Pi;var Eu="\x1B[",Uy="\x1B]",Vv="\x07",T_=";",aS=process.env.TERM_PROGRAM==="Apple_Terminal";Pi.cursorTo=(o,l)=>{if(typeof o!="number")throw new TypeError("The `x` argument is required");return typeof l!="number"?Eu+(o+1)+"G":Eu+(l+1)+";"+(o+1)+"H"};Pi.cursorMove=(o,l)=>{if(typeof o!="number")throw new TypeError("The `x` argument is required");let f="";return o<0?f+=Eu+-o+"D":o>0&&(f+=Eu+o+"C"),l<0?f+=Eu+-l+"A":l>0&&(f+=Eu+l+"B"),f};Pi.cursorUp=(o=1)=>Eu+o+"A";Pi.cursorDown=(o=1)=>Eu+o+"B";Pi.cursorForward=(o=1)=>Eu+o+"C";Pi.cursorBackward=(o=1)=>Eu+o+"D";Pi.cursorLeft=Eu+"G";Pi.cursorSavePosition=aS?"\x1B7":Eu+"s";Pi.cursorRestorePosition=aS?"\x1B8":Eu+"u";Pi.cursorGetPosition=Eu+"6n";Pi.cursorNextLine=Eu+"E";Pi.cursorPrevLine=Eu+"F";Pi.cursorHide=Eu+"?25l";Pi.cursorShow=Eu+"?25h";Pi.eraseLines=o=>{let l="";for(let f=0;f[Uy,"8",T_,T_,l,Vv,o,Uy,"8",T_,T_,Vv].join("");Pi.image=(o,l={})=>{let f=`${Uy}1337;File=inline=1`;return l.width&&(f+=`;width=${l.width}`),l.height&&(f+=`;height=${l.height}`),l.preserveAspectRatio===!1&&(f+=";preserveAspectRatio=0"),f+":"+o.toString("base64")+Vv};Pi.iTerm={setCwd:(o=process.cwd())=>`${Uy}50;CurrentDir=${o}${Vv}`,annotation:(o,l={})=>{let f=`${Uy}1337;`,h=typeof l.x<"u",E=typeof l.y<"u";if((h||E)&&!(h&&E&&typeof l.length<"u"))throw new Error("`x`, `y` and `length` must be defined when `x` or `y` is defined");return o=o.replace(/\|/g,""),f+=l.isHidden?"AddHiddenAnnotation=":"AddAnnotation=",l.length>0?f+=(h?[o,l.length,l.x,l.y]:[l.length,o]).join("|"):f+=o,f+Vv}}});var cS=nt((nH,$E)=>{"use strict";var fS=(o,l)=>{for(let f of Reflect.ownKeys(l))Object.defineProperty(o,f,Object.getOwnPropertyDescriptor(l,f));return o};$E.exports=fS;$E.exports.default=fS});var pS=nt((rH,x_)=>{"use strict";var fP=cS(),C_=new WeakMap,dS=(o,l={})=>{if(typeof o!="function")throw new TypeError("Expected a function");let f,h=0,E=o.displayName||o.name||"",t=function(...N){if(C_.set(t,++h),h===1)f=o.apply(this,N),o=null;else if(l.throw===!0)throw new Error(`Function \`${E}\` can only be called once`);return f};return fP(t,o),C_.set(t,h),t};x_.exports=dS;x_.exports.default=dS;x_.exports.callCount=o=>{if(!C_.has(o))throw new Error(`The given function \`${o.name}\` is not wrapped by the \`onetime\` package`);return C_.get(o)}});var hS=nt((iH,R_)=>{R_.exports=["SIGABRT","SIGALRM","SIGHUP","SIGINT","SIGTERM"];process.platform!=="win32"&&R_.exports.push("SIGVTALRM","SIGXCPU","SIGXFSZ","SIGUSR2","SIGTRAP","SIGSYS","SIGQUIT","SIGIOT");process.platform==="linux"&&R_.exports.push("SIGIO","SIGPOLL","SIGPWR","SIGSTKFLT","SIGUNUSED")});var nD=nt((uH,Kv)=>{var w0=global.process,Jp=function(o){return o&&typeof o=="object"&&typeof o.removeListener=="function"&&typeof o.emit=="function"&&typeof o.reallyExit=="function"&&typeof o.listeners=="function"&&typeof o.kill=="function"&&typeof o.pid=="number"&&typeof o.on=="function"};Jp(w0)?(vS=hi("assert"),Gv=hS(),mS=/^win/i.test(w0.platform),jy=hi("events"),typeof jy!="function"&&(jy=jy.EventEmitter),w0.__signal_exit_emitter__?wl=w0.__signal_exit_emitter__:(wl=w0.__signal_exit_emitter__=new jy,wl.count=0,wl.emitted={}),wl.infinite||(wl.setMaxListeners(1/0),wl.infinite=!0),Kv.exports=function(o,l){if(!Jp(global.process))return function(){};vS.equal(typeof o,"function","a callback must be provided for exit handler"),Yv===!1&&eD();var f="exit";l&&l.alwaysLast&&(f="afterexit");var h=function(){wl.removeListener(f,o),wl.listeners("exit").length===0&&wl.listeners("afterexit").length===0&&A_()};return wl.on(f,o),h},A_=function(){!Yv||!Jp(global.process)||(Yv=!1,Gv.forEach(function(l){try{w0.removeListener(l,O_[l])}catch{}}),w0.emit=M_,w0.reallyExit=tD,wl.count-=1)},Kv.exports.unload=A_,Zp=function(l,f,h){wl.emitted[l]||(wl.emitted[l]=!0,wl.emit(l,f,h))},O_={},Gv.forEach(function(o){O_[o]=function(){if(!!Jp(global.process)){var f=w0.listeners(o);f.length===wl.count&&(A_(),Zp("exit",null,o),Zp("afterexit",null,o),mS&&o==="SIGHUP"&&(o="SIGINT"),w0.kill(w0.pid,o))}}}),Kv.exports.signals=function(){return Gv},Yv=!1,eD=function(){Yv||!Jp(global.process)||(Yv=!0,wl.count+=1,Gv=Gv.filter(function(l){try{return w0.on(l,O_[l]),!0}catch{return!1}}),w0.emit=gS,w0.reallyExit=yS)},Kv.exports.load=eD,tD=w0.reallyExit,yS=function(l){!Jp(global.process)||(w0.exitCode=l||0,Zp("exit",w0.exitCode,null),Zp("afterexit",w0.exitCode,null),tD.call(w0,w0.exitCode))},M_=w0.emit,gS=function(l,f){if(l==="exit"&&Jp(global.process)){f!==void 0&&(w0.exitCode=f);var h=M_.apply(this,arguments);return Zp("exit",w0.exitCode,null),Zp("afterexit",w0.exitCode,null),h}else return M_.apply(this,arguments)}):Kv.exports=function(){return function(){}};var vS,Gv,mS,jy,wl,A_,Zp,O_,Yv,eD,tD,yS,M_,gS});var ES=nt((oH,_S)=>{"use strict";var cP=pS(),dP=nD();_S.exports=cP(()=>{dP(()=>{process.stderr.write("\x1B[?25h")},{alwaysLast:!0})})});var rD=nt(Xv=>{"use strict";var pP=ES(),k_=!1;Xv.show=(o=process.stderr)=>{!o.isTTY||(k_=!1,o.write("\x1B[?25h"))};Xv.hide=(o=process.stderr)=>{!o.isTTY||(pP(),k_=!0,o.write("\x1B[?25l"))};Xv.toggle=(o,l)=>{o!==void 0&&(k_=o),k_?Xv.show(l):Xv.hide(l)}});var TS=nt(zy=>{"use strict";var SS=zy&&zy.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(zy,"__esModule",{value:!0});var DS=SS(ZE()),wS=SS(rD()),hP=(o,{showCursor:l=!1}={})=>{let f=0,h="",E=!1,t=N=>{!l&&!E&&(wS.default.hide(),E=!0);let F=N+` +`;F!==h&&(h=F,o.write(DS.default.eraseLines(f)+F),f=F.split(` +`).length)};return t.clear=()=>{o.write(DS.default.eraseLines(f)),h="",f=0},t.done=()=>{h="",f=0,l||(wS.default.show(),E=!1)},t};zy.default={create:hP}});var CS=nt((aH,vP)=>{vP.exports=[{name:"AppVeyor",constant:"APPVEYOR",env:"APPVEYOR",pr:"APPVEYOR_PULL_REQUEST_NUMBER"},{name:"Azure Pipelines",constant:"AZURE_PIPELINES",env:"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",pr:"SYSTEM_PULLREQUEST_PULLREQUESTID"},{name:"Bamboo",constant:"BAMBOO",env:"bamboo_planKey"},{name:"Bitbucket Pipelines",constant:"BITBUCKET",env:"BITBUCKET_COMMIT",pr:"BITBUCKET_PR_ID"},{name:"Bitrise",constant:"BITRISE",env:"BITRISE_IO",pr:"BITRISE_PULL_REQUEST"},{name:"Buddy",constant:"BUDDY",env:"BUDDY_WORKSPACE_ID",pr:"BUDDY_EXECUTION_PULL_REQUEST_ID"},{name:"Buildkite",constant:"BUILDKITE",env:"BUILDKITE",pr:{env:"BUILDKITE_PULL_REQUEST",ne:"false"}},{name:"CircleCI",constant:"CIRCLE",env:"CIRCLECI",pr:"CIRCLE_PULL_REQUEST"},{name:"Cirrus CI",constant:"CIRRUS",env:"CIRRUS_CI",pr:"CIRRUS_PR"},{name:"AWS CodeBuild",constant:"CODEBUILD",env:"CODEBUILD_BUILD_ARN"},{name:"Codeship",constant:"CODESHIP",env:{CI_NAME:"codeship"}},{name:"Drone",constant:"DRONE",env:"DRONE",pr:{DRONE_BUILD_EVENT:"pull_request"}},{name:"dsari",constant:"DSARI",env:"DSARI"},{name:"GitLab CI",constant:"GITLAB",env:"GITLAB_CI"},{name:"GoCD",constant:"GOCD",env:"GO_PIPELINE_LABEL"},{name:"Hudson",constant:"HUDSON",env:"HUDSON_URL"},{name:"Jenkins",constant:"JENKINS",env:["JENKINS_URL","BUILD_ID"],pr:{any:["ghprbPullId","CHANGE_ID"]}},{name:"Magnum CI",constant:"MAGNUM",env:"MAGNUM"},{name:"Netlify CI",constant:"NETLIFY",env:"NETLIFY_BUILD_BASE",pr:{env:"PULL_REQUEST",ne:"false"}},{name:"Sail CI",constant:"SAIL",env:"SAILCI",pr:"SAIL_PULL_REQUEST_NUMBER"},{name:"Semaphore",constant:"SEMAPHORE",env:"SEMAPHORE",pr:"PULL_REQUEST_NUMBER"},{name:"Shippable",constant:"SHIPPABLE",env:"SHIPPABLE",pr:{IS_PULL_REQUEST:"true"}},{name:"Solano CI",constant:"SOLANO",env:"TDDIUM",pr:"TDDIUM_PR_ID"},{name:"Strider CD",constant:"STRIDER",env:"STRIDER"},{name:"TaskCluster",constant:"TASKCLUSTER",env:["TASK_ID","RUN_ID"]},{name:"TeamCity",constant:"TEAMCITY",env:"TEAMCITY_VERSION"},{name:"Travis CI",constant:"TRAVIS",env:"TRAVIS",pr:{env:"TRAVIS_PULL_REQUEST",ne:"false"}}]});var AS=nt(Fa=>{"use strict";var RS=CS(),Uc=process.env;Object.defineProperty(Fa,"_vendors",{value:RS.map(function(o){return o.constant})});Fa.name=null;Fa.isPR=null;RS.forEach(function(o){var l=Array.isArray(o.env)?o.env:[o.env],f=l.every(function(h){return xS(h)});if(Fa[o.constant]=f,f)switch(Fa.name=o.name,typeof o.pr){case"string":Fa.isPR=!!Uc[o.pr];break;case"object":"env"in o.pr?Fa.isPR=o.pr.env in Uc&&Uc[o.pr.env]!==o.pr.ne:"any"in o.pr?Fa.isPR=o.pr.any.some(function(h){return!!Uc[h]}):Fa.isPR=xS(o.pr);break;default:Fa.isPR=null}});Fa.isCI=!!(Uc.CI||Uc.CONTINUOUS_INTEGRATION||Uc.BUILD_NUMBER||Uc.RUN_ID||Fa.name);function xS(o){return typeof o=="string"?!!Uc[o]:Object.keys(o).every(function(l){return Uc[l]===o[l]})}});var MS=nt((cH,OS)=>{"use strict";OS.exports=AS().isCI});var NS=nt((dH,kS)=>{"use strict";var mP=o=>{let l=new Set;do for(let f of Reflect.ownKeys(o))l.add([o,f]);while((o=Reflect.getPrototypeOf(o))&&o!==Object.prototype);return l};kS.exports=(o,{include:l,exclude:f}={})=>{let h=E=>{let t=N=>typeof N=="string"?E===N:N.test(E);return l?l.some(t):f?!f.some(t):!0};for(let[E,t]of mP(o.constructor.prototype)){if(t==="constructor"||!h(t))continue;let N=Reflect.getOwnPropertyDescriptor(E,t);N&&typeof N.value=="function"&&(o[t]=o[t].bind(o))}return o}});var jS=nt(ou=>{"use strict";Object.defineProperty(ou,"__esModule",{value:!0});var Jv,Wy,I_,b_,fD;typeof window>"u"||typeof MessageChannel!="function"?(Qv=null,iD=null,uD=function(){if(Qv!==null)try{var o=ou.unstable_now();Qv(!0,o),Qv=null}catch(l){throw setTimeout(uD,0),l}},LS=Date.now(),ou.unstable_now=function(){return Date.now()-LS},Jv=function(o){Qv!==null?setTimeout(Jv,0,o):(Qv=o,setTimeout(uD,0))},Wy=function(o,l){iD=setTimeout(o,l)},I_=function(){clearTimeout(iD)},b_=function(){return!1},fD=ou.unstable_forceFrameRate=function(){}):(N_=window.performance,oD=window.Date,PS=window.setTimeout,IS=window.clearTimeout,typeof console<"u"&&(bS=window.cancelAnimationFrame,typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),typeof bS!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")),typeof N_=="object"&&typeof N_.now=="function"?ou.unstable_now=function(){return N_.now()}:(BS=oD.now(),ou.unstable_now=function(){return oD.now()-BS}),Hy=!1,qy=null,L_=-1,lD=5,sD=0,b_=function(){return ou.unstable_now()>=sD},fD=function(){},ou.unstable_forceFrameRate=function(o){0>o||125P_(N,f))k!==void 0&&0>P_(k,N)?(o[h]=k,o[F]=f,h=F):(o[h]=N,o[t]=f,h=t);else if(k!==void 0&&0>P_(k,f))o[h]=k,o[F]=f,h=F;else break e}}return l}return null}function P_(o,l){var f=o.sortIndex-l.sortIndex;return f!==0?f:o.id-l.id}var $f=[],f2=[],yP=1,Ls=null,ds=3,U_=!1,$p=!1,Vy=!1;function j_(o){for(var l=cf(f2);l!==null;){if(l.callback===null)B_(f2);else if(l.startTime<=o)B_(f2),l.sortIndex=l.expirationTime,cD($f,l);else break;l=cf(f2)}}function dD(o){if(Vy=!1,j_(o),!$p)if(cf($f)!==null)$p=!0,Jv(pD);else{var l=cf(f2);l!==null&&Wy(dD,l.startTime-o)}}function pD(o,l){$p=!1,Vy&&(Vy=!1,I_()),U_=!0;var f=ds;try{for(j_(l),Ls=cf($f);Ls!==null&&(!(Ls.expirationTime>l)||o&&!b_());){var h=Ls.callback;if(h!==null){Ls.callback=null,ds=Ls.priorityLevel;var E=h(Ls.expirationTime<=l);l=ou.unstable_now(),typeof E=="function"?Ls.callback=E:Ls===cf($f)&&B_($f),j_(l)}else B_($f);Ls=cf($f)}if(Ls!==null)var t=!0;else{var N=cf(f2);N!==null&&Wy(dD,N.startTime-l),t=!1}return t}finally{Ls=null,ds=f,U_=!1}}function US(o){switch(o){case 1:return-1;case 2:return 250;case 5:return 1073741823;case 4:return 1e4;default:return 5e3}}var gP=fD;ou.unstable_ImmediatePriority=1;ou.unstable_UserBlockingPriority=2;ou.unstable_NormalPriority=3;ou.unstable_IdlePriority=5;ou.unstable_LowPriority=4;ou.unstable_runWithPriority=function(o,l){switch(o){case 1:case 2:case 3:case 4:case 5:break;default:o=3}var f=ds;ds=o;try{return l()}finally{ds=f}};ou.unstable_next=function(o){switch(ds){case 1:case 2:case 3:var l=3;break;default:l=ds}var f=ds;ds=l;try{return o()}finally{ds=f}};ou.unstable_scheduleCallback=function(o,l,f){var h=ou.unstable_now();if(typeof f=="object"&&f!==null){var E=f.delay;E=typeof E=="number"&&0h?(o.sortIndex=E,cD(f2,o),cf($f)===null&&o===cf(f2)&&(Vy?I_():Vy=!0,Wy(dD,E-h))):(o.sortIndex=f,cD($f,o),$p||U_||($p=!0,Jv(pD))),o};ou.unstable_cancelCallback=function(o){o.callback=null};ou.unstable_wrapCallback=function(o){var l=ds;return function(){var f=ds;ds=l;try{return o.apply(this,arguments)}finally{ds=f}}};ou.unstable_getCurrentPriorityLevel=function(){return ds};ou.unstable_shouldYield=function(){var o=ou.unstable_now();j_(o);var l=cf($f);return l!==Ls&&Ls!==null&&l!==null&&l.callback!==null&&l.startTime<=o&&l.expirationTime{"use strict";process.env.NODE_ENV!=="production"&&function(){"use strict";Object.defineProperty(Ii,"__esModule",{value:!0});var o=!1,l=!1,f=!0,h,E,t,N,F;if(typeof window>"u"||typeof MessageChannel!="function"){var k=null,x=null,j=function(){if(k!==null)try{var St=Ii.unstable_now(),Bt=!0;k(Bt,St),k=null}catch(Hn){throw setTimeout(j,0),Hn}},q=Date.now();Ii.unstable_now=function(){return Date.now()-q},h=function(St){k!==null?setTimeout(h,0,St):(k=St,setTimeout(j,0))},E=function(St,Bt){x=setTimeout(St,Bt)},t=function(){clearTimeout(x)},N=function(){return!1},F=Ii.unstable_forceFrameRate=function(){}}else{var V=window.performance,re=window.Date,y=window.setTimeout,me=window.clearTimeout;if(typeof console<"u"){var De=window.requestAnimationFrame,ge=window.cancelAnimationFrame;typeof De!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),typeof ge!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")}if(typeof V=="object"&&typeof V.now=="function")Ii.unstable_now=function(){return V.now()};else{var ae=re.now();Ii.unstable_now=function(){return re.now()-ae}}var we=!1,he=null,ve=-1,ue=5,Ae=0,ze=300,We=!1;if(l&&navigator!==void 0&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0){var gt=navigator.scheduling;N=function(){var St=Ii.unstable_now();return St>=Ae?We||gt.isInputPending()?!0:St>=ze:!1},F=function(){We=!0}}else N=function(){return Ii.unstable_now()>=Ae},F=function(){};Ii.unstable_forceFrameRate=function(St){if(St<0||St>125){console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not unsupported");return}St>0?ue=Math.floor(1e3/St):ue=5};var _t=function(){if(he!==null){var St=Ii.unstable_now();Ae=St+ue;var Bt=!0;try{var Hn=he(Bt,St);Hn?ot.postMessage(null):(we=!1,he=null)}catch(qr){throw ot.postMessage(null),qr}}else we=!1;We=!1},Qe=new MessageChannel,ot=Qe.port2;Qe.port1.onmessage=_t,h=function(St){he=St,we||(we=!0,ot.postMessage(null))},E=function(St,Bt){ve=y(function(){St(Ii.unstable_now())},Bt)},t=function(){me(ve),ve=-1}}function Ve(St,Bt){var Hn=St.length;St.push(Bt),it(St,Bt,Hn)}function Pt(St){var Bt=St[0];return Bt===void 0?null:Bt}function Jt(St){var Bt=St[0];if(Bt!==void 0){var Hn=St.pop();return Hn!==Bt&&(St[0]=Hn,J(St,Hn,0)),Bt}else return null}function it(St,Bt,Hn){for(var qr=Hn;;){var Ki=Math.floor((qr-1)/2),Xr=St[Ki];if(Xr!==void 0&&ce(Xr,Bt)>0)St[Ki]=Bt,St[qr]=Xr,qr=Ki;else return}}function J(St,Bt,Hn){for(var qr=Hn,Ki=St.length;qrfr){if(fr*=2,fr>jr){console.error("Scheduler Profiling: Event log exceeded maximum size. Don't forget to call `stopLoggingProfilingEvents()`."),Dr();return}var Hn=new Int32Array(fr*4);Hn.set(Xt),zr=Hn.buffer,Xt=Hn}Xt.set(St,Bt)}}function vi(){fr=vr,zr=new ArrayBuffer(fr*4),Xt=new Int32Array(zr),Du=0}function Dr(){var St=zr;return fr=0,zr=null,Xt=null,Du=0,St}function el(St,Bt){f&&(Vt[Xn]++,Xt!==null&&lu([c0,Bt*1e3,St.id,St.priorityLevel]))}function Y0(St,Bt){f&&(Vt[Er]=Re,Vt[S]=0,Vt[Xn]--,Xt!==null&&lu([Ao,Bt*1e3,St.id]))}function Bu(St,Bt){f&&(Vt[Xn]--,Xt!==null&&lu([Fs,Bt*1e3,St.id]))}function K0(St,Bt){f&&(Vt[Er]=Re,Vt[S]=0,Vt[Xn]--,Xt!==null&&lu([Jo,Bt*1e3,St.id]))}function Kr(St,Bt){f&&(an++,Vt[Er]=St.priorityLevel,Vt[S]=St.id,Vt[zt]=an,Xt!==null&&lu([Zo,Bt*1e3,St.id,an]))}function Oo(St,Bt){f&&(Vt[Er]=Re,Vt[S]=0,Vt[zt]=0,Xt!==null&&lu([$o,Bt*1e3,St.id,an]))}function Mo(St){f&&(On++,Xt!==null&&lu([qt,St*1e3,On]))}function F0(St){f&&Xt!==null&&lu([xi,St*1e3,On])}var su=1073741823,ki=-1,Ps=250,Kl=5e3,P0=1e4,d0=su,Hr=[],Ri=[],X0=1,mi=!1,en=null,In=dt,Ai=!1,yi=!1,Wt=!1;function Ru(St){for(var Bt=Pt(Ri);Bt!==null;){if(Bt.callback===null)Jt(Ri);else if(Bt.startTime<=St)Jt(Ri),Bt.sortIndex=Bt.expirationTime,Ve(Hr,Bt),f&&(el(Bt,St),Bt.isQueued=!0);else return;Bt=Pt(Ri)}}function eu(St){if(Wt=!1,Ru(St),!yi)if(Pt(Hr)!==null)yi=!0,h(Q0);else{var Bt=Pt(Ri);Bt!==null&&E(eu,Bt.startTime-St)}}function Q0(St,Bt){f&&F0(Bt),yi=!1,Wt&&(Wt=!1,t()),Ai=!0;var Hn=In;try{if(f)try{return Yi(St,Bt)}catch(Xr){if(en!==null){var qr=Ii.unstable_now();K0(en,qr),en.isQueued=!1}throw Xr}else return Yi(St,Bt)}finally{if(en=null,In=Hn,Ai=!1,f){var Ki=Ii.unstable_now();Mo(Ki)}}}function Yi(St,Bt){var Hn=Bt;for(Ru(Hn),en=Pt(Hr);en!==null&&!(o&&mi)&&!(en.expirationTime>Hn&&(!St||N()));){var qr=en.callback;if(qr!==null){en.callback=null,In=en.priorityLevel;var Ki=en.expirationTime<=Hn;Kr(en,Hn);var Xr=qr(Ki);Hn=Ii.unstable_now(),typeof Xr=="function"?(en.callback=Xr,Oo(en,Hn)):(f&&(Y0(en,Hn),en.isQueued=!1),en===Pt(Hr)&&Jt(Hr)),Ru(Hn)}else Jt(Hr);en=Pt(Hr)}if(en!==null)return!0;var Au=Pt(Ri);return Au!==null&&E(eu,Au.startTime-Hn),!1}function Xl(St,Bt){switch(St){case le:case He:case dt:case At:case nn:break;default:St=dt}var Hn=In;In=St;try{return Bt()}finally{In=Hn}}function ko(St){var Bt;switch(In){case le:case He:case dt:Bt=dt;break;default:Bt=In;break}var Hn=In;In=Bt;try{return St()}finally{In=Hn}}function li(St){var Bt=In;return function(){var Hn=In;In=Bt;try{return St.apply(this,arguments)}finally{In=Hn}}}function ao(St){switch(St){case le:return ki;case He:return Ps;case nn:return d0;case At:return P0;case dt:default:return Kl}}function Ql(St,Bt,Hn){var qr=Ii.unstable_now(),Ki,Xr;if(typeof Hn=="object"&&Hn!==null){var Au=Hn.delay;typeof Au=="number"&&Au>0?Ki=qr+Au:Ki=qr,Xr=typeof Hn.timeout=="number"?Hn.timeout:ao(St)}else Xr=ao(St),Ki=qr;var p0=Ki+Xr,Ni={id:X0++,callback:Bt,priorityLevel:St,startTime:Ki,expirationTime:p0,sortIndex:-1};return f&&(Ni.isQueued=!1),Ki>qr?(Ni.sortIndex=Ki,Ve(Ri,Ni),Pt(Hr)===null&&Ni===Pt(Ri)&&(Wt?t():Wt=!0,E(eu,Ki-qr))):(Ni.sortIndex=p0,Ve(Hr,Ni),f&&(el(Ni,qr),Ni.isQueued=!0),!yi&&!Ai&&(yi=!0,h(Q0))),Ni}function No(){mi=!0}function Is(){mi=!1,!yi&&!Ai&&(yi=!0,h(Q0))}function $n(){return Pt(Hr)}function tl(St){if(f&&St.isQueued){var Bt=Ii.unstable_now();Bu(St,Bt),St.isQueued=!1}St.callback=null}function fo(){return In}function I0(){var St=Ii.unstable_now();Ru(St);var Bt=Pt(Hr);return Bt!==en&&en!==null&&Bt!==null&&Bt.callback!==null&&Bt.startTime<=St&&Bt.expirationTime{"use strict";process.env.NODE_ENV==="production"?hD.exports=jS():hD.exports=zS()});var HS=nt((mH,Gy)=>{Gy.exports=function o(l){"use strict";var f=Py(),h=Mi(),E=z_();function t(_){for(var g="https://reactjs.org/docs/error-decoder.html?invariant="+_,A=1;AX0||(_.current=Ri[X0],Ri[X0]=null,X0--)}function en(_,g){X0++,Ri[X0]=_.current,_.current=g}var In={},Ai={current:In},yi={current:!1},Wt=In;function Ru(_,g){var A=_.type.contextTypes;if(!A)return In;var P=_.stateNode;if(P&&P.__reactInternalMemoizedUnmaskedChildContext===g)return P.__reactInternalMemoizedMaskedChildContext;var B={},Z;for(Z in A)B[Z]=g[Z];return P&&(_=_.stateNode,_.__reactInternalMemoizedUnmaskedChildContext=g,_.__reactInternalMemoizedMaskedChildContext=B),B}function eu(_){return _=_.childContextTypes,_!=null}function Q0(_){mi(yi,_),mi(Ai,_)}function Yi(_){mi(yi,_),mi(Ai,_)}function Xl(_,g,A){if(Ai.current!==In)throw Error(t(168));en(Ai,g,_),en(yi,A,_)}function ko(_,g,A){var P=_.stateNode;if(_=g.childContextTypes,typeof P.getChildContext!="function")return A;P=P.getChildContext();for(var B in P)if(!(B in _))throw Error(t(108,ze(g)||"Unknown",B));return f({},A,{},P)}function li(_){var g=_.stateNode;return g=g&&g.__reactInternalMemoizedMergedChildContext||In,Wt=Ai.current,en(Ai,g,_),en(yi,yi.current,_),!0}function ao(_,g,A){var P=_.stateNode;if(!P)throw Error(t(169));A?(g=ko(_,g,Wt),P.__reactInternalMemoizedMergedChildContext=g,mi(yi,_),mi(Ai,_),en(Ai,g,_)):mi(yi,_),en(yi,A,_)}var Ql=E.unstable_runWithPriority,No=E.unstable_scheduleCallback,Is=E.unstable_cancelCallback,$n=E.unstable_shouldYield,tl=E.unstable_requestPaint,fo=E.unstable_now,I0=E.unstable_getCurrentPriorityLevel,Sl=E.unstable_ImmediatePriority,Lo=E.unstable_UserBlockingPriority,St=E.unstable_NormalPriority,Bt=E.unstable_LowPriority,Hn=E.unstable_IdlePriority,qr={},Ki=tl!==void 0?tl:function(){},Xr=null,Au=null,p0=!1,Ni=fo(),h0=1e4>Ni?fo:function(){return fo()-Ni};function hs(){switch(I0()){case Sl:return 99;case Lo:return 98;case St:return 97;case Bt:return 96;case Hn:return 95;default:throw Error(t(332))}}function Ct(_){switch(_){case 99:return Sl;case 98:return Lo;case 97:return St;case 96:return Bt;case 95:return Hn;default:throw Error(t(332))}}function co(_,g){return _=Ct(_),Ql(_,g)}function nl(_,g,A){return _=Ct(_),No(_,g,A)}function Jl(_){return Xr===null?(Xr=[_],Au=No(Sl,vs)):Xr.push(_),qr}function Uu(){if(Au!==null){var _=Au;Au=null,Is(_)}vs()}function vs(){if(!p0&&Xr!==null){p0=!0;var _=0;try{var g=Xr;co(99,function(){for(;_=g&&(ho=!0),_.firstContext=null)}function Mu(_,g){if(Ou!==_&&g!==!1&&g!==0)if((typeof g!="number"||g===1073741823)&&(Ou=_,g=1073741823),g={context:_,observedBits:g,next:null},Si===null){if(cr===null)throw Error(t(308));Si=g,cr.dependencies={expirationTime:0,firstContext:g,responders:null}}else Si=Si.next=g;return ln?_._currentValue:_._currentValue2}var po=!1;function Hu(_){return{baseState:_,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Pa(_){return{baseState:_.baseState,firstUpdate:_.firstUpdate,lastUpdate:_.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function v0(_,g){return{expirationTime:_,suspenseConfig:g,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function ia(_,g){_.lastUpdate===null?_.firstUpdate=_.lastUpdate=g:(_.lastUpdate.next=g,_.lastUpdate=g)}function J0(_,g){var A=_.alternate;if(A===null){var P=_.updateQueue,B=null;P===null&&(P=_.updateQueue=Hu(_.memoizedState))}else P=_.updateQueue,B=A.updateQueue,P===null?B===null?(P=_.updateQueue=Hu(_.memoizedState),B=A.updateQueue=Hu(A.memoizedState)):P=_.updateQueue=Pa(B):B===null&&(B=A.updateQueue=Pa(P));B===null||P===B?ia(P,g):P.lastUpdate===null||B.lastUpdate===null?(ia(P,g),ia(B,g)):(ia(P,g),B.lastUpdate=g)}function ua(_,g){var A=_.updateQueue;A=A===null?_.updateQueue=Hu(_.memoizedState):Ia(_,A),A.lastCapturedUpdate===null?A.firstCapturedUpdate=A.lastCapturedUpdate=g:(A.lastCapturedUpdate.next=g,A.lastCapturedUpdate=g)}function Ia(_,g){var A=_.alternate;return A!==null&&g===A.updateQueue&&(g=_.updateQueue=Pa(g)),g}function ms(_,g,A,P,B,Z){switch(A.tag){case 1:return _=A.payload,typeof _=="function"?_.call(Z,P,B):_;case 3:_.effectTag=_.effectTag&-4097|64;case 0:if(_=A.payload,B=typeof _=="function"?_.call(Z,P,B):_,B==null)break;return f({},P,B);case 2:po=!0}return P}function S0(_,g,A,P,B){po=!1,g=Ia(_,g);for(var Z=g.baseState,de=null,yt=0,Rt=g.firstUpdate,Nt=Z;Rt!==null;){var xr=Rt.expirationTime;xrai?(Qi=ur,ur=null):Qi=ur.sibling;var Vr=cu(Ge,ur,st[ai],$t);if(Vr===null){ur===null&&(ur=Qi);break}_&&ur&&Vr.alternate===null&&g(Ge,ur),je=Z(Vr,je,ai),oi===null?Wn=Vr:oi.sibling=Vr,oi=Vr,ur=Qi}if(ai===st.length)return A(Ge,ur),Wn;if(ur===null){for(;aiai?(Qi=ur,ur=null):Qi=ur.sibling;var Tu=cu(Ge,ur,Vr.value,$t);if(Tu===null){ur===null&&(ur=Qi);break}_&&ur&&Tu.alternate===null&&g(Ge,ur),je=Z(Tu,je,ai),oi===null?Wn=Tu:oi.sibling=Tu,oi=Tu,ur=Qi}if(Vr.done)return A(Ge,ur),Wn;if(ur===null){for(;!Vr.done;ai++,Vr=st.next())Vr=r0(Ge,Vr.value,$t),Vr!==null&&(je=Z(Vr,je,ai),oi===null?Wn=Vr:oi.sibling=Vr,oi=Vr);return Wn}for(ur=P(Ge,ur);!Vr.done;ai++,Vr=st.next())Vr=z0(ur,Ge,ai,Vr.value,$t),Vr!==null&&(_&&Vr.alternate!==null&&ur.delete(Vr.key===null?ai:Vr.key),je=Z(Vr,je,ai),oi===null?Wn=Vr:oi.sibling=Vr,oi=Vr);return _&&ur.forEach(function(Wa){return g(Ge,Wa)}),Wn}return function(Ge,je,st,$t){var Wn=typeof st=="object"&&st!==null&&st.type===j&&st.key===null;Wn&&(st=st.props.children);var oi=typeof st=="object"&&st!==null;if(oi)switch(st.$$typeof){case k:e:{for(oi=st.key,Wn=je;Wn!==null;){if(Wn.key===oi)if(Wn.tag===7?st.type===j:Wn.elementType===st.type){A(Ge,Wn.sibling),je=B(Wn,st.type===j?st.props.children:st.props,$t),je.ref=Us(Ge,Wn,st),je.return=Ge,Ge=je;break e}else{A(Ge,Wn);break}else g(Ge,Wn);Wn=Wn.sibling}st.type===j?(je=n0(st.props.children,Ge.mode,$t,st.key),je.return=Ge,Ge=je):($t=qa(st.type,st.key,st.props,null,Ge.mode,$t),$t.ref=Us(Ge,je,st),$t.return=Ge,Ge=$t)}return de(Ge);case x:e:{for(Wn=st.key;je!==null;){if(je.key===Wn)if(je.tag===4&&je.stateNode.containerInfo===st.containerInfo&&je.stateNode.implementation===st.implementation){A(Ge,je.sibling),je=B(je,st.children||[],$t),je.return=Ge,Ge=je;break e}else{A(Ge,je);break}else g(Ge,je);je=je.sibling}je=Df(st,Ge.mode,$t),je.return=Ge,Ge=je}return de(Ge)}if(typeof st=="string"||typeof st=="number")return st=""+st,je!==null&&je.tag===6?(A(Ge,je.sibling),je=B(je,st,$t),je.return=Ge,Ge=je):(A(Ge,je),je=j0(st,Ge.mode,$t),je.return=Ge,Ge=je),de(Ge);if(m0(st))return Ml(Ge,je,st,$t);if(ue(st))return i0(Ge,je,st,$t);if(oi&&zi(Ge,st),typeof st>"u"&&!Wn)switch(Ge.tag){case 1:case 0:throw Ge=Ge.type,Error(t(152,Ge.displayName||Ge.name||"Component"))}return A(Ge,je)}}var H=U(!0),Y=U(!1),ee={},Ce={current:ee},_e={current:ee},Oe={current:ee};function $(_){if(_===ee)throw Error(t(174));return _}function Ne(_,g){en(Oe,g,_),en(_e,_,_),en(Ce,ee,_),g=Pt(g),mi(Ce,_),en(Ce,g,_)}function Je(_){mi(Ce,_),mi(_e,_),mi(Oe,_)}function vt(_){var g=$(Oe.current),A=$(Ce.current);g=Jt(A,_.type,g),A!==g&&(en(_e,_,_),en(Ce,g,_))}function oe(_){_e.current===_&&(mi(Ce,_),mi(_e,_))}var qe={current:0};function rt(_){for(var g=_;g!==null;){if(g.tag===13){var A=g.memoizedState;if(A!==null&&(A=A.dehydrated,A===null||Kr(A)||Oo(A)))return g}else if(g.tag===19&&g.memoizedProps.revealOrder!==void 0){if((g.effectTag&64)!==0)return g}else if(g.child!==null){g.child.return=g,g=g.child;continue}if(g===_)break;for(;g.sibling===null;){if(g.return===null||g.return===_)return null;g=g.return}g.sibling.return=g.return,g=g.sibling}return null}function xt(_,g){return{responder:_,props:g}}var kt=N.ReactCurrentDispatcher,bt=N.ReactCurrentBatchConfig,sn=0,rn=null,Ft=null,Dn=null,dr=null,er=null,Cr=null,Rn=0,Nr=null,y0=0,Lr=!1,ut=null,wt=0;function et(){throw Error(t(321))}function It(_,g){if(g===null)return!1;for(var A=0;ARn&&(Rn=xr,Ua(Rn))):(cc(xr,Rt.suspenseConfig),Z=Rt.eagerReducer===_?Rt.eagerState:_(Z,Rt.action)),de=Rt,Rt=Rt.next}while(Rt!==null&&Rt!==P);Nt||(yt=de,B=Z),Fe(Z,g.memoizedState)||(ho=!0),g.memoizedState=Z,g.baseUpdate=yt,g.baseState=B,A.lastRenderedState=Z}return[g.memoizedState,A.dispatch]}function T0(_){var g=Jn();return typeof _=="function"&&(_=_()),g.memoizedState=g.baseState=_,_=g.queue={last:null,dispatch:null,lastRenderedReducer:au,lastRenderedState:_},_=_.dispatch=js.bind(null,rn,_),[g.memoizedState,_]}function Z0(_){return ku(au,_)}function Nu(_,g,A,P){return _={tag:_,create:g,destroy:A,deps:P,next:null},Nr===null?(Nr={lastEffect:null},Nr.lastEffect=_.next=_):(g=Nr.lastEffect,g===null?Nr.lastEffect=_.next=_:(A=g.next,g.next=_,_.next=A,Nr.lastEffect=_)),_}function gi(_,g,A,P){var B=Jn();y0|=_,B.memoizedState=Nu(g,A,void 0,P===void 0?null:P)}function Po(_,g,A,P){var B=wr();P=P===void 0?null:P;var Z=void 0;if(Ft!==null){var de=Ft.memoizedState;if(Z=de.destroy,P!==null&&It(P,de.deps)){Nu(0,A,Z,P);return}}y0|=_,B.memoizedState=Nu(g,A,Z,P)}function rl(_,g){return gi(516,192,_,g)}function hf(_,g){return Po(516,192,_,g)}function Tl(_,g){if(typeof g=="function")return _=_(),g(_),function(){g(null)};if(g!=null)return _=_(),g.current=_,function(){g.current=null}}function vf(){}function Io(_,g){return Jn().memoizedState=[_,g===void 0?null:g],_}function ys(_,g){var A=wr();g=g===void 0?null:g;var P=A.memoizedState;return P!==null&&g!==null&&It(g,P[1])?P[0]:(A.memoizedState=[_,g],_)}function js(_,g,A){if(!(25>wt))throw Error(t(301));var P=_.alternate;if(_===rn||P!==null&&P===rn)if(Lr=!0,_={expirationTime:sn,suspenseConfig:null,action:A,eagerReducer:null,eagerState:null,next:null},ut===null&&(ut=new Map),A=ut.get(g),A===void 0)ut.set(g,_);else{for(g=A;g.next!==null;)g=g.next;g.next=_}else{var B=E0(),Z=si.suspense;B=Un(B,_,Z),Z={expirationTime:B,suspenseConfig:Z,action:A,eagerReducer:null,eagerState:null,next:null};var de=g.last;if(de===null)Z.next=Z;else{var yt=de.next;yt!==null&&(Z.next=yt),de.next=Z}if(g.last=Z,_.expirationTime===0&&(P===null||P.expirationTime===0)&&(P=g.lastRenderedReducer,P!==null))try{var Rt=g.lastRenderedState,Nt=P(Rt,A);if(Z.eagerReducer=P,Z.eagerState=Nt,Fe(Nt,Rt))return}catch{}finally{}e0(_,B)}}var bo={readContext:Mu,useCallback:et,useContext:et,useEffect:et,useImperativeHandle:et,useLayoutEffect:et,useMemo:et,useReducer:et,useRef:et,useState:et,useDebugValue:et,useResponder:et,useDeferredValue:et,useTransition:et},Bo={readContext:Mu,useCallback:Io,useContext:Mu,useEffect:rl,useImperativeHandle:function(_,g,A){return A=A!=null?A.concat([_]):null,gi(4,36,Tl.bind(null,g,_),A)},useLayoutEffect:function(_,g){return gi(4,36,_,g)},useMemo:function(_,g){var A=Jn();return g=g===void 0?null:g,_=_(),A.memoizedState=[_,g],_},useReducer:function(_,g,A){var P=Jn();return g=A!==void 0?A(g):g,P.memoizedState=P.baseState=g,_=P.queue={last:null,dispatch:null,lastRenderedReducer:_,lastRenderedState:g},_=_.dispatch=js.bind(null,rn,_),[P.memoizedState,_]},useRef:function(_){var g=Jn();return _={current:_},g.memoizedState=_},useState:T0,useDebugValue:vf,useResponder:xt,useDeferredValue:function(_,g){var A=T0(_),P=A[0],B=A[1];return rl(function(){E.unstable_next(function(){var Z=bt.suspense;bt.suspense=g===void 0?null:g;try{B(_)}finally{bt.suspense=Z}})},[_,g]),P},useTransition:function(_){var g=T0(!1),A=g[0],P=g[1];return[Io(function(B){P(!0),E.unstable_next(function(){var Z=bt.suspense;bt.suspense=_===void 0?null:_;try{P(!1),B()}finally{bt.suspense=Z}})},[_,A]),A]}},gs={readContext:Mu,useCallback:ys,useContext:Mu,useEffect:hf,useImperativeHandle:function(_,g,A){return A=A!=null?A.concat([_]):null,Po(4,36,Tl.bind(null,g,_),A)},useLayoutEffect:function(_,g){return Po(4,36,_,g)},useMemo:function(_,g){var A=wr();g=g===void 0?null:g;var P=A.memoizedState;return P!==null&&g!==null&&It(g,P[1])?P[0]:(_=_(),A.memoizedState=[_,g],_)},useReducer:ku,useRef:function(){return wr().memoizedState},useState:Z0,useDebugValue:vf,useResponder:xt,useDeferredValue:function(_,g){var A=Z0(_),P=A[0],B=A[1];return hf(function(){E.unstable_next(function(){var Z=bt.suspense;bt.suspense=g===void 0?null:g;try{B(_)}finally{bt.suspense=Z}})},[_,g]),P},useTransition:function(_){var g=Z0(!1),A=g[0],P=g[1];return[ys(function(B){P(!0),E.unstable_next(function(){var Z=bt.suspense;bt.suspense=_===void 0?null:_;try{P(!1),B()}finally{bt.suspense=Z}})},[_,A]),A]}},Xu=null,Su=null,_i=!1;function C0(_,g){var A=Ho(5,null,null,0);A.elementType="DELETED",A.type="DELETED",A.stateNode=g,A.return=_,A.effectTag=8,_.lastEffect!==null?(_.lastEffect.nextEffect=A,_.lastEffect=A):_.firstEffect=_.lastEffect=A}function $0(_,g){switch(_.tag){case 5:return g=Bu(g,_.type,_.pendingProps),g!==null?(_.stateNode=g,!0):!1;case 6:return g=K0(g,_.pendingProps),g!==null?(_.stateNode=g,!0):!1;case 13:return!1;default:return!1}}function Uo(_){if(_i){var g=Su;if(g){var A=g;if(!$0(_,g)){if(g=Mo(A),!g||!$0(_,g)){_.effectTag=_.effectTag&-1025|2,_i=!1,Xu=_;return}C0(Xu,A)}Xu=_,Su=F0(g)}else _.effectTag=_.effectTag&-1025|2,_i=!1,Xu=_}}function la(_){for(_=_.return;_!==null&&_.tag!==5&&_.tag!==3&&_.tag!==13;)_=_.return;Xu=_}function $l(_){if(!S||_!==Xu)return!1;if(!_i)return la(_),_i=!0,!1;var g=_.type;if(_.tag!==5||g!=="head"&&g!=="body"&&!dt(g,_.memoizedProps))for(g=Su;g;)C0(_,g),g=Mo(g);if(la(_),_.tag===13){if(!S)throw Error(t(316));if(_=_.memoizedState,_=_!==null?_.dehydrated:null,!_)throw Error(t(317));Su=Ps(_)}else Su=Xu?Mo(_.stateNode):null;return!0}function tu(){S&&(Su=Xu=null,_i=!1)}var Zr=N.ReactCurrentOwner,ho=!1;function Bi(_,g,A,P){g.child=_===null?Y(g,null,A,P):H(g,_.child,A,P)}function Ci(_,g,A,P,B){A=A.render;var Z=g.ref;return Fo(g,B),P=un(_,g,A,P,Z,B),_!==null&&!ho?(g.updateQueue=_.updateQueue,g.effectTag&=-517,_.expirationTime<=B&&(_.expirationTime=0),mu(_,g,B)):(g.effectTag|=1,Bi(_,g,P,B),g.child)}function mf(_,g,A,P,B,Z){if(_===null){var de=A.type;return typeof de=="function"&&!Ef(de)&&de.defaultProps===void 0&&A.compare===null&&A.defaultProps===void 0?(g.tag=15,g.type=de,yf(_,g,de,P,B,Z)):(_=qa(A.type,null,P,null,g.mode,Z),_.ref=g.ref,_.return=g,g.child=_)}return de=_.child,Bg)&&Wr.set(_,g)))}}function ro(_,g){_.expirationTime_?g:_)}function t0(_){if(_.lastExpiredTime!==0)_.callbackExpirationTime=1073741823,_.callbackPriority=99,_.callbackNode=Jl(io.bind(null,_));else{var g=mo(_),A=_.callbackNode;if(g===0)A!==null&&(_.callbackNode=null,_.callbackExpirationTime=0,_.callbackPriority=90);else{var P=E0();if(g===1073741823?P=99:g===1||g===2?P=95:(P=10*(1073741821-g)-10*(1073741821-P),P=0>=P?99:250>=P?98:5250>=P?97:95),A!==null){var B=_.callbackPriority;if(_.callbackExpirationTime===g&&B>=P)return;A!==qr&&Is(A)}_.callbackExpirationTime=g,_.callbackPriority=P,g=g===1073741823?Jl(io.bind(null,_)):nl(P,jo.bind(null,_),{timeout:10*(1073741821-g)-h0()}),_.callbackNode=g}}}function jo(_,g){if(ru=0,g)return g=E0(),da(_,g),t0(_),null;var A=mo(_);if(A!==0){if(g=_.callbackNode,(Ln&(nu|fu))!==Rr)throw Error(t(327));if(qs(),_===fe&&A===Pe||Ds(_,A),ie!==null){var P=Ln;Ln|=nu;var B=U0(_);do try{nd();break}catch(yt){fa(_,yt)}while(1);if(ju(),Ln=P,Zu.current=B,Me===ei)throw g=at,Ds(_,A),Ol(_,A),t0(_),g;if(ie===null)switch(B=_.finishedWork=_.current.alternate,_.finishedExpirationTime=A,P=Me,fe=null,P){case Li:case ei:throw Error(t(345));case Kn:da(_,2=A){_.lastPingedTime=A,Ds(_,A);break}}if(Z=mo(_),Z!==0&&Z!==A)break;if(P!==0&&P!==A){_.lastPingedTime=P;break}_.timeoutHandle=an(Rl.bind(null,_),B);break}Rl(_);break;case g0:if(Ol(_,A),P=_.lastSuspendedTime,A===P&&(_.nextKnownPendingLevel=qc(B)),_n&&(B=_.lastPingedTime,B===0||B>=A)){_.lastPingedTime=A,Ds(_,A);break}if(B=mo(_),B!==0&&B!==A)break;if(P!==0&&P!==A){_.lastPingedTime=P;break}if(Qt!==1073741823?P=10*(1073741821-Qt)-h0():mt===1073741823?P=0:(P=10*(1073741821-mt)-5e3,B=h0(),A=10*(1073741821-A)-B,P=B-P,0>P&&(P=0),P=(120>P?120:480>P?480:1080>P?1080:1920>P?1920:3e3>P?3e3:4320>P?4320:1960*gf(P/1960))-P,A=P?P=0:(B=de.busyDelayMs|0,Z=h0()-(10*(1073741821-Z)-(de.timeoutMs|0||5e3)),P=Z<=B?0:B+P-Z),10 component higher in the tree to provide a loading indicator or placeholder to display.`+Hr(B))}Me!==_0&&(Me=Kn),Z=Cl(Z,B),Rt=P;do{switch(Rt.tag){case 3:de=Z,Rt.effectTag|=4096,Rt.expirationTime=g;var je=_s(Rt,de,g);ua(Rt,je);break e;case 1:de=Z;var st=Rt.type,$t=Rt.stateNode;if((Rt.effectTag&64)===0&&(typeof st.getDerivedStateFromError=="function"||$t!==null&&typeof $t.componentDidCatch=="function"&&(mr===null||!mr.has($t)))){Rt.effectTag|=4096,Rt.expirationTime=g;var Wn=aa(Rt,de,g);ua(Rt,Wn);break e}}Rt=Rt.return}while(Rt!==null)}ie=yo(ie)}catch(oi){g=oi;continue}break}while(1)}function U0(){var _=Zu.current;return Zu.current=bo,_===null?bo:_}function cc(_,g){_Sn&&(Sn=_)}function _2(){for(;ie!==null;)ie=rd(ie)}function nd(){for(;ie!==null&&!$n();)ie=rd(ie)}function rd(_){var g=Ha(_.alternate,_,Pe);return _.memoizedProps=_.pendingProps,g===null&&(g=yo(_)),Es.current=null,g}function yo(_){ie=_;do{var g=ie.alternate;if(_=ie.return,(ie.effectTag&2048)===0){e:{var A=g;g=ie;var P=Pe,B=g.pendingProps;switch(g.tag){case 2:break;case 16:break;case 15:case 0:break;case 1:eu(g.type)&&Q0(g);break;case 3:Je(g),Yi(g),B=g.stateNode,B.pendingContext&&(B.context=B.pendingContext,B.pendingContext=null),(A===null||A.child===null)&&$l(g)&&Qu(g),Qr(g);break;case 5:oe(g);var Z=$(Oe.current);if(P=g.type,A!==null&&g.stateNode!=null)qu(A,g,P,B,Z),A.ref!==g.ref&&(g.effectTag|=128);else if(B){if(A=$(Ce.current),$l(g)){if(B=g,!S)throw Error(t(175));A=su(B.stateNode,B.type,B.memoizedProps,Z,A,B),B.updateQueue=A,A=A!==null,A&&Qu(g)}else{var de=ce(P,B,Z,A,g);$r(de,g,!1,!1),g.stateNode=de,le(de,P,B,Z,A)&&Qu(g)}g.ref!==null&&(g.effectTag|=128)}else if(g.stateNode===null)throw Error(t(166));break;case 6:if(A&&g.stateNode!=null)xn(A,g,A.memoizedProps,B);else{if(typeof B!="string"&&g.stateNode===null)throw Error(t(166));if(A=$(Oe.current),Z=$(Ce.current),$l(g)){if(A=g,!S)throw Error(t(176));(A=ki(A.stateNode,A.memoizedProps,A))&&Qu(g)}else g.stateNode=nn(B,A,Z,g)}break;case 11:break;case 13:if(mi(qe,g),B=g.memoizedState,(g.effectTag&64)!==0){g.expirationTime=P;break e}B=B!==null,Z=!1,A===null?g.memoizedProps.fallback!==void 0&&$l(g):(P=A.memoizedState,Z=P!==null,B||P===null||(P=A.child.sibling,P!==null&&(de=g.firstEffect,de!==null?(g.firstEffect=P,P.nextEffect=de):(g.firstEffect=g.lastEffect=P,P.nextEffect=null),P.effectTag=8))),B&&!Z&&(g.mode&2)!==0&&(A===null&&g.memoizedProps.unstable_avoidThisFallback!==!0||(qe.current&1)!==0?Me===Li&&(Me=$u):((Me===Li||Me===$u)&&(Me=g0),Sn!==0&&fe!==null&&(Ol(fe,Pe),Ts(fe,Sn)))),Er&&B&&(g.effectTag|=4),Vt&&(B||Z)&&(g.effectTag|=4);break;case 7:break;case 8:break;case 12:break;case 4:Je(g),Qr(g);break;case 10:wu(g);break;case 9:break;case 14:break;case 17:eu(g.type)&&Q0(g);break;case 19:if(mi(qe,g),B=g.memoizedState,B===null)break;if(Z=(g.effectTag&64)!==0,de=B.rendering,de===null){if(Z)Lu(B,!1);else if(Me!==Li||A!==null&&(A.effectTag&64)!==0)for(A=g.child;A!==null;){if(de=rt(A),de!==null){for(g.effectTag|=64,Lu(B,!1),A=de.updateQueue,A!==null&&(g.updateQueue=A,g.effectTag|=4),B.lastEffect===null&&(g.firstEffect=null),g.lastEffect=B.lastEffect,A=P,B=g.child;B!==null;)Z=B,P=A,Z.effectTag&=2,Z.nextEffect=null,Z.firstEffect=null,Z.lastEffect=null,de=Z.alternate,de===null?(Z.childExpirationTime=0,Z.expirationTime=P,Z.child=null,Z.memoizedProps=null,Z.memoizedState=null,Z.updateQueue=null,Z.dependencies=null):(Z.childExpirationTime=de.childExpirationTime,Z.expirationTime=de.expirationTime,Z.child=de.child,Z.memoizedProps=de.memoizedProps,Z.memoizedState=de.memoizedState,Z.updateQueue=de.updateQueue,P=de.dependencies,Z.dependencies=P===null?null:{expirationTime:P.expirationTime,firstContext:P.firstContext,responders:P.responders}),B=B.sibling;en(qe,qe.current&1|2,g),g=g.child;break e}A=A.sibling}}else{if(!Z)if(A=rt(de),A!==null){if(g.effectTag|=64,Z=!0,A=A.updateQueue,A!==null&&(g.updateQueue=A,g.effectTag|=4),Lu(B,!0),B.tail===null&&B.tailMode==="hidden"&&!de.alternate){g=g.lastEffect=B.lastEffect,g!==null&&(g.nextEffect=null);break}}else h0()>B.tailExpiration&&1B&&(B=P),de>B&&(B=de),Z=Z.sibling;A.childExpirationTime=B}if(g!==null)return g;_!==null&&(_.effectTag&2048)===0&&(_.firstEffect===null&&(_.firstEffect=ie.firstEffect),ie.lastEffect!==null&&(_.lastEffect!==null&&(_.lastEffect.nextEffect=ie.firstEffect),_.lastEffect=ie.lastEffect),1_?g:_}function Rl(_){var g=hs();return co(99,ul.bind(null,_,g)),null}function ul(_,g){do qs();while(ti!==null);if((Ln&(nu|fu))!==Rr)throw Error(t(327));var A=_.finishedWork,P=_.finishedExpirationTime;if(A===null)return null;if(_.finishedWork=null,_.finishedExpirationTime=0,A===_.current)throw Error(t(177));_.callbackNode=null,_.callbackExpirationTime=0,_.callbackPriority=90,_.nextKnownPendingLevel=0;var B=qc(A);if(_.firstPendingTime=B,P<=_.lastSuspendedTime?_.firstSuspendedTime=_.lastSuspendedTime=_.nextKnownPendingLevel=0:P<=_.firstSuspendedTime&&(_.firstSuspendedTime=P-1),P<=_.lastPingedTime&&(_.lastPingedTime=0),P<=_.lastExpiredTime&&(_.lastExpiredTime=0),_===fe&&(ie=fe=null,Pe=0),1=A?Kt(_,g,A):(en(qe,qe.current&1,g),g=mu(_,g,A),g!==null?g.sibling:null);en(qe,qe.current&1,g);break;case 19:if(P=g.childExpirationTime>=A,(_.effectTag&64)!==0){if(P)return bn(_,g,A);g.effectTag|=64}if(B=g.memoizedState,B!==null&&(B.rendering=null,B.tail=null),en(qe,qe.current,g),!P)return null}return mu(_,g,A)}ho=!1}}else ho=!1;switch(g.expirationTime=0,g.tag){case 2:if(P=g.type,_!==null&&(_.alternate=null,g.alternate=null,g.effectTag|=2),_=g.pendingProps,B=Ru(g,Ai.current),Fo(g,A),B=un(null,g,P,_,B,A),g.effectTag|=1,typeof B=="object"&&B!==null&&typeof B.render=="function"&&B.$$typeof===void 0){if(g.tag=1,fn(),eu(P)){var Z=!0;li(g)}else Z=!1;g.memoizedState=B.state!==null&&B.state!==void 0?B.state:null;var de=P.getDerivedStateFromProps;typeof de=="function"&&Zl(g,P,de,_),B.updater=oa,g.stateNode=B,B._reactInternalFiber=g,Bs(g,P,_,A),g=tt(null,g,P,!0,Z,A)}else g.tag=0,Bi(null,g,B,A),g=g.child;return g;case 16:if(B=g.elementType,_!==null&&(_.alternate=null,g.alternate=null,g.effectTag|=2),_=g.pendingProps,Ae(B),B._status!==1)throw B._result;switch(B=B._result,g.type=B,Z=g.tag=ol(B),_=Yn(B,_),Z){case 0:g=to(null,g,B,_,A);break;case 1:g=xe(null,g,B,_,A);break;case 11:g=Ci(null,g,B,_,A);break;case 14:g=mf(null,g,B,Yn(B.type,_),P,A);break;default:throw Error(t(306,B,""))}return g;case 0:return P=g.type,B=g.pendingProps,B=g.elementType===P?B:Yn(P,B),to(_,g,P,B,A);case 1:return P=g.type,B=g.pendingProps,B=g.elementType===P?B:Yn(P,B),xe(_,g,P,B,A);case 3:if(Ke(g),P=g.updateQueue,P===null)throw Error(t(282));if(B=g.memoizedState,B=B!==null?B.element:null,S0(g,P,g.pendingProps,null,A),P=g.memoizedState.element,P===B)tu(),g=mu(_,g,A);else{if((B=g.stateNode.hydrate)&&(S?(Su=F0(g.stateNode.containerInfo),Xu=g,B=_i=!0):B=!1),B)for(A=Y(g,null,P,A),g.child=A;A;)A.effectTag=A.effectTag&-3|1024,A=A.sibling;else Bi(_,g,P,A),tu();g=g.child}return g;case 5:return vt(g),_===null&&Uo(g),P=g.type,B=g.pendingProps,Z=_!==null?_.memoizedProps:null,de=B.children,dt(P,B)?de=null:Z!==null&&dt(P,Z)&&(g.effectTag|=16),eo(_,g),g.mode&4&&A!==1&&At(P,B)?(g.expirationTime=g.childExpirationTime=1,g=null):(Bi(_,g,de,A),g=g.child),g;case 6:return _===null&&Uo(g),null;case 13:return Kt(_,g,A);case 4:return Ne(g,g.stateNode.containerInfo),P=g.pendingProps,_===null?g.child=H(g,null,P,A):Bi(_,g,P,A),g.child;case 11:return P=g.type,B=g.pendingProps,B=g.elementType===P?B:Yn(P,B),Ci(_,g,P,B,A);case 7:return Bi(_,g,g.pendingProps,A),g.child;case 8:return Bi(_,g,g.pendingProps.children,A),g.child;case 12:return Bi(_,g,g.pendingProps.children,A),g.child;case 10:e:{if(P=g.type._context,B=g.pendingProps,de=g.memoizedProps,Z=B.value,zu(g,Z),de!==null){var yt=de.value;if(Z=Fe(yt,Z)?0:(typeof P._calculateChangedBits=="function"?P._calculateChangedBits(yt,Z):1073741823)|0,Z===0){if(de.children===B.children&&!yi.current){g=mu(_,g,A);break e}}else for(yt=g.child,yt!==null&&(yt.return=g);yt!==null;){var Rt=yt.dependencies;if(Rt!==null){de=yt.child;for(var Nt=Rt.firstContext;Nt!==null;){if(Nt.context===P&&(Nt.observedBits&Z)!==0){yt.tag===1&&(Nt=v0(A,null),Nt.tag=2,J0(yt,Nt)),yt.expirationTime"u")return!1;var g=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(g.isDisabled||!g.supportsFiber)return!0;try{var A=g.inject(_);ca=function(P){try{g.onCommitFiberRoot(A,P,void 0,(P.current.effectTag&64)===64)}catch{}},ws=function(P){try{g.onCommitFiberUnmount(A,P)}catch{}}}catch{}return!0}function ts(_,g,A,P){this.tag=_,this.key=A,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=g,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=P,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.childExpirationTime=this.expirationTime=0,this.alternate=null}function Ho(_,g,A,P){return new ts(_,g,A,P)}function Ef(_){return _=_.prototype,!(!_||!_.isReactComponent)}function ol(_){if(typeof _=="function")return Ef(_)?1:0;if(_!=null){if(_=_.$$typeof,_===De)return 11;if(_===we)return 14}return 2}function Vu(_,g){var A=_.alternate;return A===null?(A=Ho(_.tag,g,_.key,_.mode),A.elementType=_.elementType,A.type=_.type,A.stateNode=_.stateNode,A.alternate=_,_.alternate=A):(A.pendingProps=g,A.effectTag=0,A.nextEffect=null,A.firstEffect=null,A.lastEffect=null),A.childExpirationTime=_.childExpirationTime,A.expirationTime=_.expirationTime,A.child=_.child,A.memoizedProps=_.memoizedProps,A.memoizedState=_.memoizedState,A.updateQueue=_.updateQueue,g=_.dependencies,A.dependencies=g===null?null:{expirationTime:g.expirationTime,firstContext:g.firstContext,responders:g.responders},A.sibling=_.sibling,A.index=_.index,A.ref=_.ref,A}function qa(_,g,A,P,B,Z){var de=2;if(P=_,typeof _=="function")Ef(_)&&(de=1);else if(typeof _=="string")de=5;else e:switch(_){case j:return n0(A.children,B,Z,g);case me:de=8,B|=7;break;case q:de=8,B|=1;break;case V:return _=Ho(12,A,g,B|8),_.elementType=V,_.type=V,_.expirationTime=Z,_;case ge:return _=Ho(13,A,g,B),_.type=ge,_.elementType=ge,_.expirationTime=Z,_;case ae:return _=Ho(19,A,g,B),_.elementType=ae,_.expirationTime=Z,_;default:if(typeof _=="object"&&_!==null)switch(_.$$typeof){case re:de=10;break e;case y:de=9;break e;case De:de=11;break e;case we:de=14;break e;case he:de=16,P=null;break e}throw Error(t(130,_==null?_:typeof _,""))}return g=Ho(de,A,g,B),g.elementType=_,g.type=P,g.expirationTime=Z,g}function n0(_,g,A,P){return _=Ho(7,_,P,g),_.expirationTime=A,_}function j0(_,g,A){return _=Ho(6,_,null,g),_.expirationTime=A,_}function Df(_,g,A){return g=Ho(4,_.children!==null?_.children:[],_.key,g),g.expirationTime=A,g.stateNode={containerInfo:_.containerInfo,pendingChildren:null,implementation:_.implementation},g}function Wc(_,g,A){this.tag=g,this.current=null,this.containerInfo=_,this.pingCache=this.pendingChildren=null,this.finishedExpirationTime=0,this.finishedWork=null,this.timeoutHandle=lr,this.pendingContext=this.context=null,this.hydrate=A,this.callbackNode=null,this.callbackPriority=90,this.lastExpiredTime=this.lastPingedTime=this.nextKnownPendingLevel=this.lastSuspendedTime=this.firstSuspendedTime=this.firstPendingTime=0}function dc(_,g){var A=_.firstSuspendedTime;return _=_.lastSuspendedTime,A!==0&&A>=g&&_<=g}function Ol(_,g){var A=_.firstSuspendedTime,P=_.lastSuspendedTime;Ag||A===0)&&(_.lastSuspendedTime=g),g<=_.lastPingedTime&&(_.lastPingedTime=0),g<=_.lastExpiredTime&&(_.lastExpiredTime=0)}function Ts(_,g){g>_.firstPendingTime&&(_.firstPendingTime=g);var A=_.firstSuspendedTime;A!==0&&(g>=A?_.firstSuspendedTime=_.lastSuspendedTime=_.nextKnownPendingLevel=0:g>=_.lastSuspendedTime&&(_.lastSuspendedTime=g+1),g>_.nextKnownPendingLevel&&(_.nextKnownPendingLevel=g))}function da(_,g){var A=_.lastExpiredTime;(A===0||A>g)&&(_.lastExpiredTime=g)}function ud(_){var g=_._reactInternalFiber;if(g===void 0)throw typeof _.render=="function"?Error(t(188)):Error(t(268,Object.keys(_)));return _=Qe(g),_===null?null:_.stateNode}function pa(_,g){_=_.memoizedState,_!==null&&_.dehydrated!==null&&_.retryTime{"use strict";Object.defineProperty(ec,"__esModule",{value:!0});var _P=0;ec.__interactionsRef=null;ec.__subscriberRef=null;ec.unstable_clear=function(o){return o()};ec.unstable_getCurrent=function(){return null};ec.unstable_getThreadID=function(){return++_P};ec.unstable_trace=function(o,l,f){return f()};ec.unstable_wrap=function(o){return o};ec.unstable_subscribe=function(){};ec.unstable_unsubscribe=function(){}});var WS=nt(vu=>{"use strict";process.env.NODE_ENV!=="production"&&function(){"use strict";Object.defineProperty(vu,"__esModule",{value:!0});var o=!0,l=0,f=0,h=0;vu.__interactionsRef=null,vu.__subscriberRef=null,o&&(vu.__interactionsRef={current:new Set},vu.__subscriberRef={current:null});function E(ae){if(!o)return ae();var we=vu.__interactionsRef.current;vu.__interactionsRef.current=new Set;try{return ae()}finally{vu.__interactionsRef.current=we}}function t(){return o?vu.__interactionsRef.current:null}function N(){return++h}function F(ae,we,he){var ve=arguments.length>3&&arguments[3]!==void 0?arguments[3]:l;if(!o)return he();var ue={__count:1,id:f++,name:ae,timestamp:we},Ae=vu.__interactionsRef.current,ze=new Set(Ae);ze.add(ue),vu.__interactionsRef.current=ze;var We=vu.__subscriberRef.current,gt;try{We!==null&&We.onInteractionTraced(ue)}finally{try{We!==null&&We.onWorkStarted(ze,ve)}finally{try{gt=he()}finally{vu.__interactionsRef.current=Ae;try{We!==null&&We.onWorkStopped(ze,ve)}finally{ue.__count--,We!==null&&ue.__count===0&&We.onInteractionScheduledWorkCompleted(ue)}}}}return gt}function k(ae){var we=arguments.length>1&&arguments[1]!==void 0?arguments[1]:l;if(!o)return ae;var he=vu.__interactionsRef.current,ve=vu.__subscriberRef.current;ve!==null&&ve.onWorkScheduled(he,we),he.forEach(function(ze){ze.__count++});var ue=!1;function Ae(){var ze=vu.__interactionsRef.current;vu.__interactionsRef.current=he,ve=vu.__subscriberRef.current;try{var We;try{ve!==null&&ve.onWorkStarted(he,we)}finally{try{We=ae.apply(void 0,arguments)}finally{vu.__interactionsRef.current=ze,ve!==null&&ve.onWorkStopped(he,we)}}return We}finally{ue||(ue=!0,he.forEach(function(gt){gt.__count--,ve!==null&>.__count===0&&ve.onInteractionScheduledWorkCompleted(gt)}))}}return Ae.cancel=function(){ve=vu.__subscriberRef.current;try{ve!==null&&ve.onWorkCanceled(he,we)}finally{he.forEach(function(We){We.__count--,ve&&We.__count===0&&ve.onInteractionScheduledWorkCompleted(We)})}},Ae}var x=null;o&&(x=new Set);function j(ae){o&&(x.add(ae),x.size===1&&(vu.__subscriberRef.current={onInteractionScheduledWorkCompleted:re,onInteractionTraced:V,onWorkCanceled:ge,onWorkScheduled:y,onWorkStarted:me,onWorkStopped:De}))}function q(ae){o&&(x.delete(ae),x.size===0&&(vu.__subscriberRef.current=null))}function V(ae){var we=!1,he=null;if(x.forEach(function(ve){try{ve.onInteractionTraced(ae)}catch(ue){we||(we=!0,he=ue)}}),we)throw he}function re(ae){var we=!1,he=null;if(x.forEach(function(ve){try{ve.onInteractionScheduledWorkCompleted(ae)}catch(ue){we||(we=!0,he=ue)}}),we)throw he}function y(ae,we){var he=!1,ve=null;if(x.forEach(function(ue){try{ue.onWorkScheduled(ae,we)}catch(Ae){he||(he=!0,ve=Ae)}}),he)throw ve}function me(ae,we){var he=!1,ve=null;if(x.forEach(function(ue){try{ue.onWorkStarted(ae,we)}catch(Ae){he||(he=!0,ve=Ae)}}),he)throw ve}function De(ae,we){var he=!1,ve=null;if(x.forEach(function(ue){try{ue.onWorkStopped(ae,we)}catch(Ae){he||(he=!0,ve=Ae)}}),he)throw ve}function ge(ae,we){var he=!1,ve=null;if(x.forEach(function(ue){try{ue.onWorkCanceled(ae,we)}catch(Ae){he||(he=!0,ve=Ae)}}),he)throw ve}vu.unstable_clear=E,vu.unstable_getCurrent=t,vu.unstable_getThreadID=N,vu.unstable_trace=F,vu.unstable_wrap=k,vu.unstable_subscribe=j,vu.unstable_unsubscribe=q}()});var VS=nt((_H,vD)=>{"use strict";process.env.NODE_ENV==="production"?vD.exports=qS():vD.exports=WS()});var GS=nt((EH,Yy)=>{"use strict";process.env.NODE_ENV!=="production"&&(Yy.exports=function o(l){"use strict";var f=Py(),h=Mi(),E=XE(),t=z_(),N=VS(),F=0,k=1,x=2,j=3,q=4,V=5,re=6,y=7,me=8,De=9,ge=10,ae=11,we=12,he=13,ve=14,ue=15,Ae=16,ze=17,We=18,gt=19,_t=20,Qe=21,ot=function(){};ot=function(c,d){for(var D=arguments.length,C=new Array(D>2?D-2:0),O=2;O8)throw new Error("warningWithoutStack() currently supports at most 8 arguments.");if(!c){if(typeof console<"u"){var z=C.map(function(se){return""+se});z.unshift("Warning: "+d),Function.prototype.apply.call(console.error,console,z)}try{var G=0,ne="Warning: "+d.replace(/%s/g,function(){return C[G++]});throw new Error(ne)}catch{}}};var Ve=ot;function Pt(c){return c._reactInternalFiber}function Jt(c,d){c._reactInternalFiber=d}var it=h.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;it.hasOwnProperty("ReactCurrentDispatcher")||(it.ReactCurrentDispatcher={current:null}),it.hasOwnProperty("ReactCurrentBatchConfig")||(it.ReactCurrentBatchConfig={suspense:null});var J=typeof Symbol=="function"&&Symbol.for,ce=J?Symbol.for("react.element"):60103,Re=J?Symbol.for("react.portal"):60106,le=J?Symbol.for("react.fragment"):60107,He=J?Symbol.for("react.strict_mode"):60108,dt=J?Symbol.for("react.profiler"):60114,At=J?Symbol.for("react.provider"):60109,nn=J?Symbol.for("react.context"):60110,an=J?Symbol.for("react.concurrent_mode"):60111,On=J?Symbol.for("react.forward_ref"):60112,lr=J?Symbol.for("react.suspense"):60113,ln=J?Symbol.for("react.suspense_list"):60120,Vt=J?Symbol.for("react.memo"):60115,Er=J?Symbol.for("react.lazy"):60116,S=J?Symbol.for("react.fundamental"):60117,zt=J?Symbol.for("react.responder"):60118,Xn=J?Symbol.for("react.scope"):60119,vr=typeof Symbol=="function"&&Symbol.iterator,jr="@@iterator";function fr(c){if(c===null||typeof c!="object")return null;var d=vr&&c[vr]||c[jr];return typeof d=="function"?d:null}var zr=Ve;zr=function(c,d){if(!c){for(var D=it.ReactDebugCurrentFrame,C=D.getStackAddendum(),O=arguments.length,z=new Array(O>2?O-2:0),G=2;G import('./MyComponent'))`,C),c._status=Ao,c._result=O}},function(C){c._status===c0&&(c._status=Jo,c._result=C)})}}function $o(c,d,D){var C=d.displayName||d.name||"";return c.displayName||(C!==""?D+"("+C+")":D)}function qt(c){if(c==null)return null;if(typeof c.tag=="number"&&Ve(!1,"Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."),typeof c=="function")return c.displayName||c.name||null;if(typeof c=="string")return c;switch(c){case le:return"Fragment";case Re:return"Portal";case dt:return"Profiler";case He:return"StrictMode";case lr:return"Suspense";case ln:return"SuspenseList"}if(typeof c=="object")switch(c.$$typeof){case nn:return"Context.Consumer";case At:return"Context.Provider";case On:return $o(c,c.render,"ForwardRef");case Vt:return qt(c.type);case Er:{var d=c,D=Fs(d);if(D)return qt(D);break}}return null}var xi=0,lu=1,vi=2,Dr=4,el=6,Y0=8,Bu=16,K0=32,Kr=64,Oo=128,Mo=256,F0=512,su=1024,ki=1028,Ps=932,Kl=2047,P0=2048,d0=4096,Hr=!0,Ri=!0,X0=!0,mi=!0,en=!0,In=!0,Ai=!1,yi=!1,Wt=!1,Ru=!1,eu=!1,Q0=!0,Yi=!1,Xl=!1,ko=!1,li=!1,ao=!1,Ql=it.ReactCurrentOwner;function No(c){var d=c,D=c;if(c.alternate)for(;d.return;)d=d.return;else{var C=d;do d=C,(d.effectTag&(vi|su))!==xi&&(D=d.return),C=d.return;while(C)}return d.tag===j?D:null}function Is(c){return No(c)===c}function $n(c){{var d=Ql.current;if(d!==null&&d.tag===k){var D=d,C=D.stateNode;C._warnedAboutRefsInRender||Ve(!1,"%s is accessing isMounted inside its render() function. render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",qt(D.type)||"A component"),C._warnedAboutRefsInRender=!0}}var O=Pt(c);return O?No(O)===O:!1}function tl(c){if(No(c)!==c)throw Error("Unable to find node on an unmounted component.")}function fo(c){var d=c.alternate;if(!d){var D=No(c);if(D===null)throw Error("Unable to find node on an unmounted component.");return D!==c?null:c}for(var C=c,O=d;;){var z=C.return;if(z===null)break;var G=z.alternate;if(G===null){var ne=z.return;if(ne!==null){C=O=ne;continue}break}if(z.child===G.child){for(var se=z.child;se;){if(se===C)return tl(z),c;if(se===O)return tl(z),d;se=se.sibling}throw Error("Unable to find node on an unmounted component.")}if(C.return!==O.return)C=z,O=G;else{for(var Ue=!1,Xe=z.child;Xe;){if(Xe===C){Ue=!0,C=z,O=G;break}if(Xe===O){Ue=!0,O=z,C=G;break}Xe=Xe.sibling}if(!Ue){for(Xe=G.child;Xe;){if(Xe===C){Ue=!0,C=G,O=z;break}if(Xe===O){Ue=!0,O=G,C=z;break}Xe=Xe.sibling}if(!Ue)throw Error("Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.")}}if(C.alternate!==O)throw Error("Return fibers should always be each others' alternates. This error is likely caused by a bug in React. Please file an issue.")}if(C.tag!==j)throw Error("Unable to find node on an unmounted component.");return C.stateNode.current===C?c:d}function I0(c){var d=fo(c);if(!d)return null;for(var D=d;;){if(D.tag===V||D.tag===re)return D;if(D.child){D.child.return=D,D=D.child;continue}if(D===d)return null;for(;!D.sibling;){if(!D.return||D.return===d)return null;D=D.return}D.sibling.return=D.return,D=D.sibling}return null}function Sl(c){var d=fo(c);if(!d)return null;for(var D=d;;){if(D.tag===V||D.tag===re||Wt&&D.tag===_t)return D;if(D.child&&D.tag!==q){D.child.return=D,D=D.child;continue}if(D===d)return null;for(;!D.sibling;){if(!D.return||D.return===d)return null;D=D.return}D.sibling.return=D.return,D=D.sibling}return null}var Lo=l.getPublicInstance,St=l.getRootHostContext,Bt=l.getChildHostContext,Hn=l.prepareForCommit,qr=l.resetAfterCommit,Ki=l.createInstance,Xr=l.appendInitialChild,Au=l.finalizeInitialChildren,p0=l.prepareUpdate,Ni=l.shouldSetTextContent,h0=l.shouldDeprioritizeSubtree,hs=l.createTextInstance,Ct=l.setTimeout,co=l.clearTimeout,nl=l.noTimeout,Jl=l.now,Uu=l.isPrimaryRenderer,vs=l.warnsIfNotActing,b0=l.supportsMutation,Q=l.supportsPersistence,Se=l.supportsHydration,Fe=l.mountResponderInstance,Le=l.unmountResponderInstance,pt=l.getFundamentalComponentInstance,Yn=l.mountFundamentalComponent,Cn=l.shouldUpdateFundamentalComponent,cr=l.getInstanceFromNode,Si=l.appendChild,Ou=l.appendChildToContainer,ju=l.commitTextUpdate,zu=l.commitMount,wu=l.commitUpdate,Ti=l.insertBefore,Fo=l.insertInContainerBefore,Mu=l.removeChild,po=l.removeChildFromContainer,Hu=l.resetTextContent,Pa=l.hideInstance,v0=l.hideTextInstance,ia=l.unhideInstance,J0=l.unhideTextInstance,ua=l.updateFundamentalComponent,Ia=l.unmountFundamentalComponent,ms=l.cloneInstance,S0=l.createContainerChildSet,Qn=l.appendChildToContainerChildSet,ac=l.finalizeContainerChildren,si=l.replaceContainerChildren,Jr=l.cloneHiddenInstance,Zl=l.cloneHiddenTextInstance,oa=l.cloneInstance,pf=l.canHydrateInstance,bs=l.canHydrateTextInstance,ba=l.canHydrateSuspenseInstance,Bs=l.isSuspenseInstancePending,m0=l.isSuspenseInstanceFallback,Us=l.registerSuspenseInstanceRetry,zi=l.getNextHydratableSibling,U=l.getFirstHydratableChild,H=l.hydrateInstance,Y=l.hydrateTextInstance,ee=l.hydrateSuspenseInstance,Ce=l.getNextHydratableInstanceAfterSuspenseInstance,_e=l.commitHydratedContainer,Oe=l.commitHydratedSuspenseInstance,$=l.clearSuspenseBoundary,Ne=l.clearSuspenseBoundaryFromContainer,Je=l.didNotMatchHydratedContainerTextInstance,vt=l.didNotMatchHydratedTextInstance,oe=l.didNotHydrateContainerInstance,qe=l.didNotHydrateInstance,rt=l.didNotFindHydratableContainerInstance,xt=l.didNotFindHydratableContainerTextInstance,kt=l.didNotFindHydratableContainerSuspenseInstance,bt=l.didNotFindHydratableInstance,sn=l.didNotFindHydratableTextInstance,rn=l.didNotFindHydratableSuspenseInstance,Ft=/^(.*)[\\\/]/,Dn=function(c,d,D){var C="";if(d){var O=d.fileName,z=O.replace(Ft,"");if(/^index\./.test(z)){var G=O.match(Ft);if(G){var ne=G[1];if(ne){var se=ne.replace(Ft,"");z=se+"/"+z}}}C=" (at "+z+":"+d.lineNumber+")"}else D&&(C=" (created by "+D+")");return` + in `+(c||"Unknown")+C},dr=it.ReactDebugCurrentFrame;function er(c){switch(c.tag){case j:case q:case re:case y:case ge:case De:return"";default:var d=c._debugOwner,D=c._debugSource,C=qt(c.type),O=null;return d&&(O=qt(d.type)),Dn(C,D,O)}}function Cr(c){var d="",D=c;do d+=er(D),D=D.return;while(D);return d}var Rn=null,Nr=null;function y0(){{if(Rn===null)return null;var c=Rn._debugOwner;if(c!==null&&typeof c<"u")return qt(c.type)}return null}function Lr(){return Rn===null?"":Cr(Rn)}function ut(){dr.getCurrentStack=null,Rn=null,Nr=null}function wt(c){dr.getCurrentStack=Lr,Rn=c,Nr=null}function et(c){Nr=c}var It="\u269B",un="\u26D4",fn=typeof performance<"u"&&typeof performance.mark=="function"&&typeof performance.clearMarks=="function"&&typeof performance.measure=="function"&&typeof performance.clearMeasures=="function",Jn=null,wr=null,au=null,ku=!1,T0=!1,Z0=!1,Nu=0,gi=0,Po=new Set,rl=function(c){return It+" "+c},hf=function(c,d){var D=d?un+" ":It+" ",C=d?" Warning: "+d:"";return""+D+c+C},Tl=function(c){performance.mark(rl(c))},vf=function(c){performance.clearMarks(rl(c))},Io=function(c,d,D){var C=rl(d),O=hf(c,D);try{performance.measure(O,C)}catch{}performance.clearMarks(C),performance.clearMeasures(O)},ys=function(c,d){return c+" (#"+d+")"},js=function(c,d,D){return D===null?c+" ["+(d?"update":"mount")+"]":c+"."+D},bo=function(c,d){var D=qt(c.type)||"Unknown",C=c._debugID,O=c.alternate!==null,z=js(D,O,d);if(ku&&Po.has(z))return!1;Po.add(z);var G=ys(z,C);return Tl(G),!0},Bo=function(c,d){var D=qt(c.type)||"Unknown",C=c._debugID,O=c.alternate!==null,z=js(D,O,d),G=ys(z,C);vf(G)},gs=function(c,d,D){var C=qt(c.type)||"Unknown",O=c._debugID,z=c.alternate!==null,G=js(C,z,d),ne=ys(G,O);Io(G,ne,D)},Xu=function(c){switch(c.tag){case j:case V:case re:case q:case y:case ge:case De:case me:return!0;default:return!1}},Su=function(){wr!==null&&au!==null&&Bo(au,wr),au=null,wr=null,Z0=!1},_i=function(){for(var c=Jn;c;)c._debugIsCurrentlyTiming&&gs(c,null,null),c=c.return},C0=function(c){c.return!==null&&C0(c.return),c._debugIsCurrentlyTiming&&bo(c,null)},$0=function(){Jn!==null&&C0(Jn)};function Uo(){Hr&&gi++}function la(){Hr&&(ku&&(T0=!0),wr!==null&&wr!=="componentWillMount"&&wr!=="componentWillReceiveProps"&&(Z0=!0))}function $l(c){if(Hr){if(!fn||Xu(c)||(Jn=c,!bo(c,null)))return;c._debugIsCurrentlyTiming=!0}}function tu(c){if(Hr){if(!fn||Xu(c))return;c._debugIsCurrentlyTiming=!1,Bo(c,null)}}function Zr(c){if(Hr){if(!fn||Xu(c)||(Jn=c.return,!c._debugIsCurrentlyTiming))return;c._debugIsCurrentlyTiming=!1,gs(c,null,null)}}function ho(c){if(Hr){if(!fn||Xu(c)||(Jn=c.return,!c._debugIsCurrentlyTiming))return;c._debugIsCurrentlyTiming=!1;var d=c.tag===he?"Rendering was suspended":"An error was thrown inside this error boundary";gs(c,null,d)}}function Bi(c,d){if(Hr){if(!fn||(Su(),!bo(c,d)))return;au=c,wr=d}}function Ci(){if(Hr){if(!fn)return;if(wr!==null&&au!==null){var c=Z0?"Scheduled a cascading update":null;gs(au,wr,c)}wr=null,au=null}}function mf(c){if(Hr){if(Jn=c,!fn)return;Nu=0,Tl("(React Tree Reconciliation)"),$0()}}function yf(c,d){if(Hr){if(!fn)return;var D=null;if(c!==null)if(c.tag===j)D="A top-level update interrupted the previous render";else{var C=qt(c.type)||"Unknown";D="An update to "+C+" interrupted the previous render"}else Nu>1&&(D="There were cascading updates");Nu=0;var O=d?"(React Tree Reconciliation: Completed Root)":"(React Tree Reconciliation: Yielded)";_i(),Io(O,"(React Tree Reconciliation)",D)}}function eo(){if(Hr){if(!fn)return;ku=!0,T0=!1,Po.clear(),Tl("(Committing Changes)")}}function to(){if(Hr){if(!fn)return;var c=null;T0?c="Lifecycle hook scheduled a cascading update":Nu>0&&(c="Caused by a cascading update in earlier commit"),T0=!1,Nu++,ku=!1,Po.clear(),Io("(Committing Changes)","(Committing Changes)",c)}}function xe(){if(Hr){if(!fn)return;gi=0,Tl("(Committing Snapshot Effects)")}}function tt(){if(Hr){if(!fn)return;var c=gi;gi=0,Io("(Committing Snapshot Effects: "+c+" Total)","(Committing Snapshot Effects)",null)}}function Ke(){if(Hr){if(!fn)return;gi=0,Tl("(Committing Host Effects)")}}function Yt(){if(Hr){if(!fn)return;var c=gi;gi=0,Io("(Committing Host Effects: "+c+" Total)","(Committing Host Effects)",null)}}function Kt(){if(Hr){if(!fn)return;gi=0,Tl("(Calling Lifecycle Methods)")}}function pr(){if(Hr){if(!fn)return;var c=gi;gi=0,Io("(Calling Lifecycle Methods: "+c+" Total)","(Calling Lifecycle Methods)",null)}}var Ei=[],bn;bn=[];var mu=-1;function Qu(c){return{current:c}}function $r(c,d){if(mu<0){Ve(!1,"Unexpected pop.");return}d!==bn[mu]&&Ve(!1,"Unexpected Fiber popped."),c.current=Ei[mu],Ei[mu]=null,bn[mu]=null,mu--}function Qr(c,d,D){mu++,Ei[mu]=c.current,bn[mu]=D,c.current=d}var qu;qu={};var xn={};Object.freeze(xn);var x0=Qu(xn),Lu=Qu(!1),ui=xn;function Cl(c,d,D){return li?xn:D&&Xi(d)?ui:x0.current}function zs(c,d,D){if(!li){var C=c.stateNode;C.__reactInternalMemoizedUnmaskedChildContext=d,C.__reactInternalMemoizedMaskedChildContext=D}}function Wu(c,d){if(li)return xn;var D=c.type,C=D.contextTypes;if(!C)return xn;var O=c.stateNode;if(O&&O.__reactInternalMemoizedUnmaskedChildContext===d)return O.__reactInternalMemoizedMaskedChildContext;var z={};for(var G in C)z[G]=d[G];{var ne=qt(D)||"Unknown";E(C,z,"context",ne,Lr)}return O&&zs(c,d,z),z}function sa(){return li?!1:Lu.current}function Xi(c){if(li)return!1;var d=c.childContextTypes;return d!=null}function Hs(c){li||($r(Lu,c),$r(x0,c))}function R0(c){li||($r(Lu,c),$r(x0,c))}function Hi(c,d,D){if(!li){if(x0.current!==xn)throw Error("Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue.");Qr(x0,d,c),Qr(Lu,D,c)}}function A0(c,d,D){if(li)return D;var C=c.stateNode,O=d.childContextTypes;if(typeof C.getChildContext!="function"){{var z=qt(d)||"Unknown";qu[z]||(qu[z]=!0,Ve(!1,"%s.childContextTypes is specified but there is no getChildContext() method on the instance. You can either define getChildContext() on %s or remove childContextTypes from it.",z,z))}return D}var G;et("getChildContext"),Bi(c,"getChildContext"),G=C.getChildContext(),Ci(),et(null);for(var ne in G)if(!(ne in O))throw Error((qt(d)||"Unknown")+'.getChildContext(): key "'+ne+'" is not defined in childContextTypes.');{var se=qt(d)||"Unknown";E(O,G,"child context",se,Lr)}return f({},D,{},G)}function qi(c){if(li)return!1;var d=c.stateNode,D=d&&d.__reactInternalMemoizedMergedChildContext||xn;return ui=x0.current,Qr(x0,D,c),Qr(Lu,Lu.current,c),!0}function il(c,d,D){if(!li){var C=c.stateNode;if(!C)throw Error("Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue.");if(D){var O=A0(c,d,ui);C.__reactInternalMemoizedMergedChildContext=O,$r(Lu,c),$r(x0,c),Qr(x0,O,c),Qr(Lu,D,c)}else $r(Lu,c),Qr(Lu,D,c)}}function xl(c){if(li)return xn;if(!(Is(c)&&c.tag===k))throw Error("Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue.");var d=c;do{switch(d.tag){case j:return d.stateNode.context;case k:{var D=d.type;if(Xi(D))return d.stateNode.__reactInternalMemoizedMergedChildContext;break}}d=d.return}while(d!==null);throw Error("Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue.")}var B0=1,O0=2,vo=t.unstable_runWithPriority,Fu=t.unstable_scheduleCallback,Ju=t.unstable_cancelCallback,es=t.unstable_shouldYield,_s=t.unstable_requestPaint,aa=t.unstable_now,gf=t.unstable_getCurrentPriorityLevel,Zu=t.unstable_ImmediatePriority,Es=t.unstable_UserBlockingPriority,Rr=t.unstable_NormalPriority,no=t.unstable_LowPriority,nu=t.unstable_IdlePriority;if(In&&!(N.__interactionsRef!=null&&N.__interactionsRef.current!=null))throw Error("It is not supported to run the profiling version of a renderer (for example, `react-dom/profiling`) without also replacing the `scheduler/tracing` module with `scheduler/tracing-profiling`. Your bundler might have a setting for aliasing both modules. Learn more at http://fb.me/react-profiling");var fu={},Li=99,ei=98,Kn=97,$u=96,g0=95,_0=90,Ln=es,fe=_s!==void 0?_s:function(){},ie=null,Pe=null,Me=!1,at=aa(),mt=at<1e4?aa:function(){return aa()-at};function Qt(){switch(gf()){case Zu:return Li;case Es:return ei;case Rr:return Kn;case no:return $u;case nu:return g0;default:throw Error("Unknown priority level.")}}function An(c){switch(c){case Li:return Zu;case ei:return Es;case Kn:return Rr;case $u:return no;case g0:return nu;default:throw Error("Unknown priority level.")}}function Sn(c,d){var D=An(c);return vo(D,d)}function _n(c,d,D){var C=An(c);return Fu(C,d,D)}function Tn(c){return ie===null?(ie=[c],Pe=Fu(Zu,Fi)):ie.push(c),fu}function ir(c){c!==fu&&Ju(c)}function Ut(){if(Pe!==null){var c=Pe;Pe=null,Ju(c)}Fi()}function Fi(){if(!Me&&ie!==null){Me=!0;var c=0;try{var d=!0,D=ie;Sn(Li,function(){for(;c1?d-1:0),C=1;C2?D-2:0),O=2;O0&&(ja.forEach(function(Lt){c.add(qt(Lt.type)||"Component"),ts.add(Lt.type)}),ja=[]);var d=new Set;za.length>0&&(za.forEach(function(Lt){d.add(qt(Lt.type)||"Component"),ts.add(Lt.type)}),za=[]);var D=new Set;Ha.length>0&&(Ha.forEach(function(Lt){D.add(qt(Lt.type)||"Component"),ts.add(Lt.type)}),Ha=[]);var C=new Set;ca.length>0&&(ca.forEach(function(Lt){C.add(qt(Lt.type)||"Component"),ts.add(Lt.type)}),ca=[]);var O=new Set;ws.length>0&&(ws.forEach(function(Lt){O.add(qt(Lt.type)||"Component"),ts.add(Lt.type)}),ws=[]);var z=new Set;if(Ss.length>0&&(Ss.forEach(function(Lt){z.add(qt(Lt.type)||"Component"),ts.add(Lt.type)}),Ss=[]),d.size>0){var G=zo(d);Ve(!1,`Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details. + +* Move code with side effects to componentDidMount, and set initial state in the constructor. + +Please update the following components: %s`,G)}if(C.size>0){var ne=zo(C);Ve(!1,`Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details. + +* Move data fetching code or side effects to componentDidUpdate. +* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state + +Please update the following components: %s`,ne)}if(z.size>0){var se=zo(z);Ve(!1,`Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details. + +* Move data fetching code or side effects to componentDidUpdate. + +Please update the following components: %s`,se)}if(c.size>0){var Ue=zo(c);qs(!1,`componentWillMount has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details. + +* Move code with side effects to componentDidMount, and set initial state in the constructor. +* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder. + +Please update the following components: %s`,Ue)}if(D.size>0){var Xe=zo(D);qs(!1,`componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details. + +* Move data fetching code or side effects to componentDidUpdate. +* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state +* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder. + +Please update the following components: %s`,Xe)}if(O.size>0){var ht=zo(O);qs(!1,`componentWillUpdate has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details. + +* Move data fetching code or side effects to componentDidUpdate. +* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder. + +Please update the following components: %s`,ht)}};var Ho=new Map,Ef=new Set;Al.recordLegacyContextWarning=function(c,d){var D=id(c);if(D===null){Ve(!1,"Expected to find a StrictMode component in a strict mode tree. This error is likely caused by a bug in React. Please file an issue.");return}if(!Ef.has(c.type)){var C=Ho.get(D);(c.type.contextTypes!=null||c.type.childContextTypes!=null||d!==null&&typeof d.getChildContext=="function")&&(C===void 0&&(C=[],Ho.set(D,C)),C.push(c))}},Al.flushLegacyContextWarning=function(){Ho.forEach(function(c,d){var D=new Set;c.forEach(function(z){D.add(qt(z.type)||"Component"),Ef.add(z.type)});var C=zo(D),O=Cr(d);Ve(!1,`Legacy context API has been detected within a strict-mode tree. + +The old API will be supported in all 16.x releases, but applications using it should migrate to the new version. + +Please update the following components: %s + +Learn more about this warning here: https://fb.me/react-legacy-context%s`,C,O)})},Al.discardPendingWarnings=function(){ja=[],za=[],Ha=[],ca=[],ws=[],Ss=[],Ho=new Map}}var ol=null,Vu=null,qa=function(c){ol=c};function n0(c){{if(ol===null)return c;var d=ol(c);return d===void 0?c:d.current}}function j0(c){return n0(c)}function Df(c){{if(ol===null)return c;var d=ol(c);if(d===void 0){if(c!=null&&typeof c.render=="function"){var D=n0(c.render);if(c.render!==D){var C={$$typeof:On,render:D};return c.displayName!==void 0&&(C.displayName=c.displayName),C}}return c}return d.current}}function Wc(c,d){{if(ol===null)return!1;var D=c.elementType,C=d.type,O=!1,z=typeof C=="object"&&C!==null?C.$$typeof:null;switch(c.tag){case k:{typeof C=="function"&&(O=!0);break}case F:{(typeof C=="function"||z===Er)&&(O=!0);break}case ae:{(z===On||z===Er)&&(O=!0);break}case ve:case ue:{(z===Vt||z===Er)&&(O=!0);break}default:return!1}if(O){var G=ol(D);if(G!==void 0&&G===ol(C))return!0}return!1}}function dc(c){{if(ol===null||typeof WeakSet!="function")return;Vu===null&&(Vu=new WeakSet),Vu.add(c)}}var Ol=function(c,d){{if(ol===null)return;var D=d.staleFamilies,C=d.updatedFamilies;tf(),Rp(function(){da(c.current,C,D)})}},Ts=function(c,d){{if(c.context!==xn)return;tf(),pv(function(){Xg(d,c,null,null)})}};function da(c,d,D){{var C=c.alternate,O=c.child,z=c.sibling,G=c.tag,ne=c.type,se=null;switch(G){case F:case ue:case k:se=ne;break;case ae:se=ne.render;break;default:break}if(ol===null)throw new Error("Expected resolveFamily to be set during hot reload.");var Ue=!1,Xe=!1;if(se!==null){var ht=ol(se);ht!==void 0&&(D.has(ht)?Xe=!0:d.has(ht)&&(G===k?Xe=!0:Ue=!0))}Vu!==null&&(Vu.has(c)||C!==null&&Vu.has(C))&&(Xe=!0),Xe&&(c._debugNeedsRemount=!0),(Xe||Ue)&&yl(c,Un),O!==null&&!Xe&&da(O,d,D),z!==null&&da(z,d,D)}}var ud=function(c,d){{var D=new Set,C=new Set(d.map(function(O){return O.current}));return pa(c.current,C,D),D}};function pa(c,d,D){{var C=c.child,O=c.sibling,z=c.tag,G=c.type,ne=null;switch(z){case F:case ue:case k:ne=G;break;case ae:ne=G.render;break;default:break}var se=!1;ne!==null&&d.has(ne)&&(se=!0),se?pc(c,D):C!==null&&pa(C,d,D),O!==null&&pa(O,d,D)}}function pc(c,d){{var D=Vc(c,d);if(D)return;for(var C=c;;){switch(C.tag){case V:d.add(C.stateNode);return;case q:d.add(C.stateNode.containerInfo);return;case j:d.add(C.stateNode.containerInfo);return}if(C.return===null)throw new Error("Expected to reach root first.");C=C.return}}}function Vc(c,d){for(var D=c,C=!1;;){if(D.tag===V)C=!0,d.add(D.stateNode);else if(D.child!==null){D.child.return=D,D=D.child;continue}if(D===c)return C;for(;D.sibling===null;){if(D.return===null||D.return===c)return C;D=D.return}D.sibling.return=D.return,D=D.sibling}return!1}function Wi(c,d){if(c&&c.defaultProps){var D=f({},d),C=c.defaultProps;for(var O in C)D[O]===void 0&&(D[O]=C[O]);return D}return d}function _(c){if(Zo(c),c._status!==Ao)throw c._result;return c._result}var g=Qu(null),A;A={};var P=null,B=null,Z=null,de=!1;function yt(){P=null,B=null,Z=null,de=!1}function Rt(){de=!0}function Nt(){de=!1}function xr(c,d){var D=c.type._context;Uu?(Qr(g,D._currentValue,c),D._currentValue=d,D._currentRenderer===void 0||D._currentRenderer===null||D._currentRenderer===A||Ve(!1,"Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported."),D._currentRenderer=A):(Qr(g,D._currentValue2,c),D._currentValue2=d,D._currentRenderer2===void 0||D._currentRenderer2===null||D._currentRenderer2===A||Ve(!1,"Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported."),D._currentRenderer2=A)}function r0(c){var d=g.current;$r(g,c);var D=c.type._context;Uu?D._currentValue=d:D._currentValue2=d}function cu(c,d,D){if(yo(D,d))return 0;var C=typeof c._calculateChangedBits=="function"?c._calculateChangedBits(D,d):Wr;return(C&Wr)!==C&&Xt(!1,"calculateChangedBits: Expected the return value to be a 31-bit integer. Instead received: %s",C),C|0}function z0(c,d){for(var D=c;D!==null;){var C=D.alternate;if(D.childExpirationTime=d&&op(),D.firstContext=null)}}function Ge(c,d){if(de&&Xt(!1,"Context can only be read while React is rendering. In classes, you can read it in the render method or getDerivedStateFromProps. In function components, you can read it directly in the function body, but not inside Hooks like useReducer() or useMemo()."),Z!==c){if(!(d===!1||d===0)){var D;typeof d!="number"||d===Wr?(Z=c,D=Wr):D=d;var C={context:c,observedBits:D,next:null};if(B===null){if(P===null)throw Error("Context can only be read while React is rendering. In classes, you can read it in the render method or getDerivedStateFromProps. In function components, you can read it directly in the function body, but not inside Hooks like useReducer() or useMemo().");B=C,P.dependencies={expirationTime:ft,firstContext:C,responders:null}}else B=B.next=C}}return Uu?c._currentValue:c._currentValue2}var je=0,st=1,$t=2,Wn=3,oi=!1,ur,ai;ur=!1,ai=null;function Qi(c){var d={baseState:c,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null};return d}function Vr(c){var d={baseState:c.baseState,firstUpdate:c.firstUpdate,lastUpdate:c.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null};return d}function Tu(c,d){var D={expirationTime:c,suspenseConfig:d,tag:je,payload:null,callback:null,next:null,nextEffect:null};return D.priority=Qt(),D}function Wa(c,d){c.lastUpdate===null?c.firstUpdate=c.lastUpdate=d:(c.lastUpdate.next=d,c.lastUpdate=d)}function Va(c,d){var D=c.alternate,C,O;D===null?(C=c.updateQueue,O=null,C===null&&(C=c.updateQueue=Qi(c.memoizedState))):(C=c.updateQueue,O=D.updateQueue,C===null?O===null?(C=c.updateQueue=Qi(c.memoizedState),O=D.updateQueue=Qi(D.memoizedState)):C=c.updateQueue=Vr(O):O===null&&(O=D.updateQueue=Vr(C))),O===null||C===O?Wa(C,d):C.lastUpdate===null||O.lastUpdate===null?(Wa(C,d),Wa(O,d)):(Wa(C,d),O.lastUpdate=d),c.tag===k&&(ai===C||O!==null&&ai===O)&&!ur&&(Ve(!1,"An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback."),ur=!0)}function od(c,d){var D=c.updateQueue;D===null?D=c.updateQueue=Qi(c.memoizedState):D=D2(c,D),D.lastCapturedUpdate===null?D.firstCapturedUpdate=D.lastCapturedUpdate=d:(D.lastCapturedUpdate.next=d,D.lastCapturedUpdate=d)}function D2(c,d){var D=c.alternate;return D!==null&&d===D.updateQueue&&(d=c.updateQueue=Vr(d)),d}function w2(c,d,D,C,O,z){switch(D.tag){case st:{var G=D.payload;if(typeof G=="function"){Rt(),Ri&&c.mode&mr&&G.call(z,C,O);var ne=G.call(z,C,O);return Nt(),ne}return G}case Wn:c.effectTag=c.effectTag&~d0|Kr;case je:{var se=D.payload,Ue;return typeof se=="function"?(Rt(),Ri&&c.mode&mr&&se.call(z,C,O),Ue=se.call(z,C,O),Nt()):Ue=se,Ue==null?C:f({},C,Ue)}case $t:return oi=!0,C}return C}function wf(c,d,D,C,O){oi=!1,d=D2(c,d),ai=d;for(var z=d.baseState,G=null,ne=ft,se=d.firstUpdate,Ue=z;se!==null;){var Xe=se.expirationTime;if(Xe from render. Or maybe you meant to call this function rather than return it."))}function Eh(c){function d(lt,Mt){if(!!c){var $e=lt.lastEffect;$e!==null?($e.nextEffect=Mt,lt.lastEffect=Mt):lt.firstEffect=lt.lastEffect=Mt,Mt.nextEffect=null,Mt.effectTag=Y0}}function D(lt,Mt){if(!c)return null;for(var $e=Mt;$e!==null;)d(lt,$e),$e=$e.sibling;return null}function C(lt,Mt){for(var $e=new Map,jt=Mt;jt!==null;)jt.key!==null?$e.set(jt.key,jt):$e.set(jt.index,jt),jt=jt.sibling;return $e}function O(lt,Mt,$e){var jt=Co(lt,Mt,$e);return jt.index=0,jt.sibling=null,jt}function z(lt,Mt,$e){if(lt.index=$e,!c)return Mt;var jt=lt.alternate;if(jt!==null){var Fn=jt.index;return FnYr?(Cu=hr,hr=null):Cu=hr.sibling;var D0=Lt(lt,hr,$e[Yr],jt);if(D0===null){hr===null&&(hr=Cu);break}c&&hr&&D0.alternate===null&&d(lt,hr),pu=z(D0,pu,Yr),Yu===null?ci=D0:Yu.sibling=D0,Yu=D0,hr=Cu}if(Yr===$e.length)return D(lt,hr),ci;if(hr===null){for(;Yr<$e.length;Yr++){var W0=ht(lt,$e[Yr],jt);W0!==null&&(pu=z(W0,pu,Yr),Yu===null?ci=W0:Yu.sibling=W0,Yu=W0)}return ci}for(var Ms=C(lt,hr);Yr<$e.length;Yr++){var Ku=Gt(Ms,lt,Yr,$e[Yr],jt);Ku!==null&&(c&&Ku.alternate!==null&&Ms.delete(Ku.key===null?Yr:Ku.key),pu=z(Ku,pu,Yr),Yu===null?ci=Ku:Yu.sibling=Ku,Yu=Ku)}return c&&Ms.forEach(function(gl){return d(lt,gl)}),ci}function kr(lt,Mt,$e,jt){var Fn=fr($e);if(typeof Fn!="function")throw Error("An object is not an iterable. This error is likely caused by a bug in React. Please file an issue.");{typeof Symbol=="function"&&$e[Symbol.toStringTag]==="Generator"&&(Qc||Xt(!1,"Using Generators as children is unsupported and will likely yield unexpected results because enumerating a generator mutates it. You may convert it to an array with `Array.from()` or the `[...spread]` operator before rendering. Keep in mind you might need to polyfill these features for older browsers."),Qc=!0),$e.entries===Fn&&(dd||Xt(!1,"Using Maps as children is unsupported and will likely yield unexpected results. Convert it to a sequence/iterable of keyed ReactElements instead."),dd=!0);var vn=Fn.call($e);if(vn)for(var Vi=null,ci=vn.next();!ci.done;ci=vn.next()){var Yu=ci.value;Vi=Ht(Yu,Vi)}}var hr=Fn.call($e);if(hr==null)throw Error("An iterable object provided no iterator.");for(var pu=null,Yr=null,Cu=Mt,D0=0,W0=0,Ms=null,Ku=hr.next();Cu!==null&&!Ku.done;W0++,Ku=hr.next()){Cu.index>W0?(Ms=Cu,Cu=null):Ms=Cu.sibling;var gl=Lt(lt,Cu,Ku.value,jt);if(gl===null){Cu===null&&(Cu=Ms);break}c&&Cu&&gl.alternate===null&&d(lt,Cu),D0=z(gl,D0,W0),Yr===null?pu=gl:Yr.sibling=gl,Yr=gl,Cu=Ms}if(Ku.done)return D(lt,Cu),pu;if(Cu===null){for(;!Ku.done;W0++,Ku=hr.next()){var rf=ht(lt,Ku.value,jt);rf!==null&&(D0=z(rf,D0,W0),Yr===null?pu=rf:Yr.sibling=rf,Yr=rf)}return pu}for(var Vo=C(lt,Cu);!Ku.done;W0++,Ku=hr.next()){var ks=Gt(Vo,lt,W0,Ku.value,jt);ks!==null&&(c&&ks.alternate!==null&&Vo.delete(ks.key===null?W0:ks.key),D0=z(ks,D0,W0),Yr===null?pu=ks:Yr.sibling=ks,Yr=ks)}return c&&Vo.forEach(function(Jd){return d(lt,Jd)}),pu}function ii(lt,Mt,$e,jt){if(Mt!==null&&Mt.tag===re){D(lt,Mt.sibling);var Fn=O(Mt,$e,jt);return Fn.return=lt,Fn}D(lt,Mt);var vn=_y($e,lt.mode,jt);return vn.return=lt,vn}function Oi(lt,Mt,$e,jt){for(var Fn=$e.key,vn=Mt;vn!==null;){if(vn.key===Fn)if(vn.tag===y?$e.type===le:vn.elementType===$e.type||Wc(vn,$e)){D(lt,vn.sibling);var Vi=O(vn,$e.type===le?$e.props.children:$e.props,jt);return Vi.ref=vc(lt,vn,$e),Vi.return=lt,Vi._debugSource=$e._source,Vi._debugOwner=$e._owner,Vi}else{D(lt,vn);break}else d(lt,vn);vn=vn.sibling}if($e.type===le){var ci=nf($e.props.children,lt.mode,jt,$e.key);return ci.return=lt,ci}else{var Yu=gy($e,lt.mode,jt);return Yu.ref=vc(lt,Mt,$e),Yu.return=lt,Yu}}function L0(lt,Mt,$e,jt){for(var Fn=$e.key,vn=Mt;vn!==null;){if(vn.key===Fn)if(vn.tag===q&&vn.stateNode.containerInfo===$e.containerInfo&&vn.stateNode.implementation===$e.implementation){D(lt,vn.sibling);var Vi=O(vn,$e.children||[],jt);return Vi.return=lt,Vi}else{D(lt,vn);break}else d(lt,vn);vn=vn.sibling}var ci=Ey($e,lt.mode,jt);return ci.return=lt,ci}function $i(lt,Mt,$e,jt){var Fn=typeof $e=="object"&&$e!==null&&$e.type===le&&$e.key===null;Fn&&($e=$e.props.children);var vn=typeof $e=="object"&&$e!==null;if(vn)switch($e.$$typeof){case ce:return G(Oi(lt,Mt,$e,jt));case Re:return G(L0(lt,Mt,$e,jt))}if(typeof $e=="string"||typeof $e=="number")return G(ii(lt,Mt,""+$e,jt));if(Zc($e))return yn(lt,Mt,$e,jt);if(fr($e))return kr(lt,Mt,$e,jt);if(vn&&mc(lt,$e),typeof $e=="function"&&pd(),typeof $e>"u"&&!Fn)switch(lt.tag){case k:{var Vi=lt.stateNode;if(Vi.render._isMockFunction)break}case F:{var ci=lt.type;throw Error((ci.displayName||ci.name||"Component")+"(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.")}}return D(lt,Mt)}return $i}var Tf=Eh(!0),$c=Eh(!1);function Dh(c,d){if(!(c===null||d.child===c.child))throw Error("Resuming work not yet implemented.");if(d.child!==null){var D=d.child,C=Co(D,D.pendingProps,D.expirationTime);for(d.child=C,C.return=d;D.sibling!==null;)D=D.sibling,C=C.sibling=Co(D,D.pendingProps,D.expirationTime),C.return=d;C.sibling=null}}function sm(c,d){for(var D=c.child;D!==null;)kv(D,d),D=D.sibling}var Vs={},ma=Qu(Vs),iu=Qu(Vs),M0=Qu(Vs);function u0(c){if(c===Vs)throw Error("Expected host context to exist. This error is likely caused by a bug in React. Please file an issue.");return c}function ns(){var c=u0(M0.current);return c}function Ya(c,d){Qr(M0,d,c),Qr(iu,c,c),Qr(ma,Vs,c);var D=St(d);$r(ma,c),Qr(ma,D,c)}function uo(c){$r(ma,c),$r(iu,c),$r(M0,c)}function fl(){var c=u0(ma.current);return c}function yc(c){var d=u0(M0.current),D=u0(ma.current),C=Bt(D,c.type,d);D!==C&&(Qr(iu,c,c),Qr(ma,C,c))}function M2(c){iu.current===c&&($r(ma,c),$r(iu,c))}var wh=0,Cf=1,xf=1,e1=2,Nl=Qu(wh);function t1(c,d){return(c&d)!==0}function ya(c){return c&Cf}function hd(c,d){return c&Cf|d}function vd(c,d){return c|d}function Fr(c,d){Qr(Nl,d,c)}function ga(c){$r(Nl,c)}function k2(c,d){var D=c.memoizedState;if(D!==null)return D.dehydrated!==null;var C=c.memoizedProps;return C.fallback===void 0?!1:C.unstable_avoidThisFallback!==!0?!0:!d}function n1(c){for(var d=c;d!==null;){if(d.tag===he){var D=d.memoizedState;if(D!==null){var C=D.dehydrated;if(C===null||Bs(C)||m0(C))return d}}else if(d.tag===gt&&d.memoizedProps.revealOrder!==void 0){var O=(d.effectTag&Kr)!==xi;if(O)return d}else if(d.child!==null){d.child.return=d,d=d.child;continue}if(d===c)return null;for(;d.sibling===null;){if(d.return===null||d.return===c)return null;d=d.return}d.sibling.return=d.return,d=d.sibling}return null}var md={},wi=Array.isArray;function N2(c,d,D,C){return{fiber:C,props:d,responder:c,rootEventTypes:null,state:D}}function am(c,d,D,C,O){var z=md,G=c.getInitialState;G!==null&&(z=G(d));var ne=N2(c,d,z,D);if(!O)for(var se=D;se!==null;){var Ue=se.tag;if(Ue===V){O=se.stateNode;break}else if(Ue===j){O=se.stateNode.containerInfo;break}se=se.return}Fe(c,ne,d,z,O),C.set(c,ne)}function yd(c,d,D,C,O){var z,G;if(c&&(z=c.responder,G=c.props),!(z&&z.$$typeof===zt))throw Error("An invalid value was used as an event listener. Expect one or many event listeners created via React.unstable_useResponder().");var ne=G;if(D.has(z)){Xt(!1,'Duplicate event responder "%s" found in event listeners. Event listeners passed to elements cannot use the same event responder more than once.',z.displayName);return}D.add(z);var se=C.get(z);se===void 0?am(z,ne,d,C,O):(se.props=ne,se.fiber=d)}function hn(c,d,D){var C=new Set,O=d.dependencies;if(c!=null){O===null&&(O=d.dependencies={expirationTime:ft,firstContext:null,responders:new Map});var z=O.responders;if(z===null&&(z=new Map),wi(c))for(var G=0,ne=c.length;G0){var z=O.dispatch;if(Cs!==null){var G=Cs.get(O);if(G!==void 0){Cs.delete(O);var ne=C.memoizedState,se=G;do{var Ue=se.action;ne=c(ne,Ue),se=se.next}while(se!==null);return yo(ne,C.memoizedState)||op(),C.memoizedState=ne,C.baseUpdate===O.last&&(C.baseState=ne),O.lastRenderedState=ne,[ne,z]}}return[C.memoizedState,z]}var Xe=O.last,ht=C.baseUpdate,Lt=C.baseState,Gt;if(ht!==null?(Xe!==null&&(Xe.next=null),Gt=ht.next):Gt=Xe!==null?Xe.next:null,Gt!==null){var Ht=Lt,yn=null,kr=null,ii=ht,Oi=Gt,L0=!1;do{var $i=Oi.expirationTime;if($iPu&&(Pu=$i,Kd(Pu));else if(gv($i,Oi.suspenseConfig),Oi.eagerReducer===c)Ht=Oi.eagerState;else{var lt=Oi.action;Ht=c(Ht,lt)}ii=Oi,Oi=Oi.next}while(Oi!==null&&Oi!==Gt);L0||(kr=ii,yn=Ht),yo(Ht,C.memoizedState)||op(),C.memoizedState=Ht,C.baseUpdate=kr,C.baseState=yn,O.lastRenderedState=Ht}var Mt=O.dispatch;return[C.memoizedState,Mt]}function Ff(c){var d=Dc();typeof c=="function"&&(c=c()),d.memoizedState=d.baseState=c;var D=d.queue={last:null,dispatch:null,lastRenderedReducer:L2,lastRenderedState:c},C=D.dispatch=a1.bind(null,dl,D);return[d.memoizedState,C]}function o1(c){return u1(L2,c)}function Qa(c,d,D,C){var O={tag:c,create:d,destroy:D,deps:C,next:null};if(rs===null)rs=Xa(),rs.lastEffect=O.next=O;else{var z=rs.lastEffect;if(z===null)rs.lastEffect=O.next=O;else{var G=z.next;z.next=O,O.next=G,rs.lastEffect=O}}return O}function l1(c){var d=Dc(),D={current:c};return Object.seal(D),d.memoizedState=D,D}function F2(c){var d=i1();return d.memoizedState}function Dd(c,d,D,C){var O=Dc(),z=C===void 0?null:C;Mf|=c,O.memoizedState=Qa(d,D,void 0,z)}function wc(c,d,D,C){var O=i1(),z=C===void 0?null:C,G=void 0;if(jn!==null){var ne=jn.memoizedState;if(G=ne.destroy,z!==null){var se=ne.deps;if(Nf(z,se)){Qa(Af,D,G,z);return}}}Mf|=c,O.memoizedState=Qa(d,D,G,z)}function s1(c,d){return typeof jest<"u"&&Mv(dl),Dd(Dr|F0,sr|r1,c,d)}function Fl(c,d){return typeof jest<"u"&&Mv(dl),wc(Dr|F0,sr|r1,c,d)}function Ea(c,d){return Dd(Dr,Of|cl,c,d)}function Ch(c,d){return wc(Dr,Of|cl,c,d)}function P2(c,d){if(typeof d=="function"){var D=d,C=c();return D(C),function(){D(null)}}else if(d!=null){var O=d;O.hasOwnProperty("current")||Xt(!1,"Expected useImperativeHandle() first argument to either be a ref callback or React.createRef() object. Instead received: %s.","an object with keys {"+Object.keys(O).join(", ")+"}");var z=c();return O.current=z,function(){O.current=null}}}function I2(c,d,D){typeof d!="function"&&Xt(!1,"Expected useImperativeHandle() second argument to be a function that creates a handle. Instead received: %s.",d!==null?typeof d:"null");var C=D!=null?D.concat([c]):null;return Dd(Dr,Of|cl,P2.bind(null,d,c),C)}function xh(c,d,D){typeof d!="function"&&Xt(!1,"Expected useImperativeHandle() second argument to be a function that creates a handle. Instead received: %s.",d!==null?typeof d:"null");var C=D!=null?D.concat([c]):null;return wc(Dr,Of|cl,P2.bind(null,d,c),C)}function pm(c,d){}var Rh=pm;function Pl(c,d){var D=Dc(),C=d===void 0?null:d;return D.memoizedState=[c,C],c}function us(c,d){var D=i1(),C=d===void 0?null:d,O=D.memoizedState;if(O!==null&&C!==null){var z=O[1];if(Nf(C,z))return O[0]}return D.memoizedState=[c,C],c}function xs(c,d){var D=Dc(),C=d===void 0?null:d,O=c();return D.memoizedState=[O,C],O}function Gs(c,d){var D=i1(),C=d===void 0?null:d,O=D.memoizedState;if(O!==null&&C!==null){var z=O[1];if(Nf(C,z))return O[0]}var G=c();return D.memoizedState=[G,C],G}function b2(c,d){var D=Ff(c),C=D[0],O=D[1];return s1(function(){t.unstable_next(function(){var z=qo.suspense;qo.suspense=d===void 0?null:d;try{O(c)}finally{qo.suspense=z}})},[c,d]),C}function Ah(c,d){var D=o1(c),C=D[0],O=D[1];return Fl(function(){t.unstable_next(function(){var z=qo.suspense;qo.suspense=d===void 0?null:d;try{O(c)}finally{qo.suspense=z}})},[c,d]),C}function B2(c){var d=Ff(!1),D=d[0],C=d[1],O=Pl(function(z){C(!0),t.unstable_next(function(){var G=qo.suspense;qo.suspense=c===void 0?null:c;try{C(!1),z()}finally{qo.suspense=G}})},[c,D]);return[O,D]}function U2(c){var d=o1(!1),D=d[0],C=d[1],O=us(function(z){C(!0),t.unstable_next(function(){var G=qo.suspense;qo.suspense=c===void 0?null:c;try{C(!1),z()}finally{qo.suspense=G}})},[c,D]);return[O,D]}function a1(c,d,D){if(!(Ec=0){var D=c1()-d1;c.actualDuration+=D,d&&(c.selfBaseDuration=D),d1=-1}}var bl=null,Za=null,Da=!1;function q2(){Da&&Xt(!1,"We should not be hydrating here. This is a bug in React. Please file a bug.")}function W2(c){if(!Se)return!1;var d=c.stateNode.containerInfo;return Za=U(d),bl=c,Da=!0,!0}function hm(c,d){return Se?(Za=zi(d),Y2(c),Da=!0,!0):!1}function V2(c,d){switch(c.tag){case j:oe(c.stateNode.containerInfo,d);break;case V:qe(c.type,c.memoizedProps,c.stateNode,d);break}var D=U4();D.stateNode=d,D.return=c,D.effectTag=Y0,c.lastEffect!==null?(c.lastEffect.nextEffect=D,c.lastEffect=D):c.firstEffect=c.lastEffect=D}function Fh(c,d){switch(d.effectTag=d.effectTag&~su|vi,c.tag){case j:{var D=c.stateNode.containerInfo;switch(d.tag){case V:var C=d.type,O=d.pendingProps;rt(D,C,O);break;case re:var z=d.pendingProps;xt(D,z);break;case he:kt(D);break}break}case V:{var G=c.type,ne=c.memoizedProps,se=c.stateNode;switch(d.tag){case V:var Ue=d.type,Xe=d.pendingProps;bt(G,ne,se,Ue,Xe);break;case re:var ht=d.pendingProps;sn(G,ne,se,ht);break;case he:rn(G,ne,se);break}break}default:return}}function Ph(c,d){switch(c.tag){case V:{var D=c.type,C=c.pendingProps,O=pf(d,D,C);return O!==null?(c.stateNode=O,!0):!1}case re:{var z=c.pendingProps,G=bs(d,z);return G!==null?(c.stateNode=G,!0):!1}case he:{if(Ai){var ne=ba(d);if(ne!==null){var se={dehydrated:ne,retryTime:Di};c.memoizedState=se;var Ue=j4(ne);return Ue.return=c,c.child=Ue,!0}}return!1}default:return!1}}function G2(c){if(!!Da){var d=Za;if(!d){Fh(bl,c),Da=!1,bl=c;return}var D=d;if(!Ph(c,d)){if(d=zi(D),!d||!Ph(c,d)){Fh(bl,c),Da=!1,bl=c;return}V2(bl,D)}bl=c,Za=U(d)}}function vm(c,d,D){if(!Se)throw Error("Expected prepareToHydrateHostInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var C=c.stateNode,O=H(C,c.type,c.memoizedProps,d,D,c);return c.updateQueue=O,O!==null}function mm(c){if(!Se)throw Error("Expected prepareToHydrateHostTextInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var d=c.stateNode,D=c.memoizedProps,C=Y(d,D,c);if(C){var O=bl;if(O!==null)switch(O.tag){case j:{var z=O.stateNode.containerInfo;Je(z,d,D);break}case V:{var G=O.type,ne=O.memoizedProps,se=O.stateNode;vt(G,ne,se,d,D);break}}}return C}function Ih(c){if(!Se)throw Error("Expected prepareToHydrateHostSuspenseInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var d=c.memoizedState,D=d!==null?d.dehydrated:null;if(!D)throw Error("Expected to have a hydrated suspense instance. This error is likely caused by a bug in React. Please file an issue.");ee(D,c)}function ym(c){if(!Se)throw Error("Expected skipPastDehydratedSuspenseInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.");var d=c.memoizedState,D=d!==null?d.dehydrated:null;if(!D)throw Error("Expected to have a hydrated suspense instance. This error is likely caused by a bug in React. Please file an issue.");return Ce(D)}function Y2(c){for(var d=c.return;d!==null&&d.tag!==V&&d.tag!==j&&d.tag!==he;)d=d.return;bl=d}function h1(c){if(!Se||c!==bl)return!1;if(!Da)return Y2(c),Da=!0,!1;var d=c.type;if(c.tag!==V||d!=="head"&&d!=="body"&&!Ni(d,c.memoizedProps))for(var D=Za;D;)V2(c,D),D=zi(D);return Y2(c),c.tag===he?Za=ym(c):Za=bl?zi(c.stateNode):null,!0}function v1(){!Se||(bl=null,Za=null,Da=!1)}var m1=it.ReactCurrentOwner,wa=!1,K2,Ys,Ks,Xs,X2,Sa,y1,wd,Sc,Q2;K2={},Ys={},Ks={},Xs={},X2={},Sa=!1,y1=!1,wd={},Sc={},Q2={};function wo(c,d,D,C){c===null?d.child=$c(d,null,D,C):d.child=Tf(d,c.child,D,C)}function bh(c,d,D,C){d.child=Tf(d,c.child,null,C),d.child=Tf(d,null,D,C)}function Bh(c,d,D,C,O){if(d.type!==d.elementType){var z=D.propTypes;z&&E(z,C,"prop",qt(D),Lr)}var G=D.render,ne=d.ref,se;return i0(d,O),m1.current=d,et("render"),se=Lf(c,d,G,C,ne,O),Ri&&d.mode&mr&&d.memoizedState!==null&&(se=Lf(c,d,G,C,ne,O)),et(null),c!==null&&!wa?(gd(c,d,O),Ta(c,d,O)):(d.effectTag|=lu,wo(c,d,se,O),d.child)}function Uh(c,d,D,C,O,z){if(c===null){var G=D.type;if(s0(G)&&D.compare===null&&D.defaultProps===void 0){var ne=G;return ne=n0(G),d.tag=ue,d.type=ne,$2(d,G),jh(c,d,ne,C,O,z)}{var se=G.propTypes;se&&E(se,C,"prop",qt(G),Lr)}var Ue=yy(D.type,null,C,null,d.mode,z);return Ue.ref=d.ref,Ue.return=d,d.child=Ue,Ue}{var Xe=D.type,ht=Xe.propTypes;ht&&E(ht,C,"prop",qt(Xe),Lr)}var Lt=c.child;if(O component appears to have a render method, but doesn't extend React.Component. This is likely to cause errors. Change %s to extend React.Component instead.",se,se),K2[se]=!0)}d.mode&mr&&Al.recordLegacyContextWarning(d,null),m1.current=d,ne=Lf(null,d,D,O,z,C)}if(d.effectTag|=lu,typeof ne=="object"&&ne!==null&&typeof ne.render=="function"&&ne.$$typeof===void 0){{var Ue=qt(D)||"Unknown";Ys[Ue]||(Ve(!1,"The <%s /> component appears to be a function component that returns a class instance. Change %s to a class that extends React.Component instead. If you can't use a class try assigning the prototype on the function as a workaround. `%s.prototype = React.Component.prototype`. Don't use an arrow function since it cannot be called with `new` by React.",Ue,Ue,Ue),Ys[Ue]=!0)}d.tag=k,_d();var Xe=!1;Xi(D)?(Xe=!0,qi(d)):Xe=!1,d.memoizedState=ne.state!==null&&ne.state!==void 0?ne.state:null;var ht=D.getDerivedStateFromProps;return typeof ht=="function"&&Sf(d,D,ht,O),al(d,ne),hc(d,D,O,C),Z2(null,d,D,!0,Xe,C)}else return d.tag=F,li&&D.contextTypes&&Ve(!1,"%s uses the legacy contextTypes API which is no longer supported. Use React.createContext() with React.useContext() instead.",qt(D)||"Unknown"),Ri&&d.mode&mr&&d.memoizedState!==null&&(ne=Lf(null,d,D,O,z,C)),wo(null,d,ne,C),$2(d,D),d.child}function $2(c,d){if(d&&d.childContextTypes&&Ve(!1,"%s(...): childContextTypes cannot be defined on a function component.",d.displayName||d.name||"Component"),c.ref!==null){var D="",C=y0();C&&(D+=` + +Check the render method of \``+C+"`.");var O=C||c._debugID||"",z=c._debugSource;z&&(O=z.fileName+":"+z.lineNumber),X2[O]||(X2[O]=!0,Xt(!1,"Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s",D))}if(Xl&&d.defaultProps!==void 0){var G=qt(d)||"Unknown";Q2[G]||(Ve(!1,"%s: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.",G),Q2[G]=!0)}if(typeof d.getDerivedStateFromProps=="function"){var ne=qt(d)||"Unknown";Xs[ne]||(Ve(!1,"%s: Function components do not support getDerivedStateFromProps.",ne),Xs[ne]=!0)}if(typeof d.contextType=="object"&&d.contextType!==null){var se=qt(d)||"Unknown";Ks[se]||(Ve(!1,"%s: Function components do not support contextType.",se),Ks[se]=!0)}}var Td={dehydrated:null,retryTime:ft};function ep(c,d,D){return t1(c,e1)&&(d===null||d.memoizedState!==null)}function Vh(c,d,D){var C=d.mode,O=d.pendingProps;Jg(d)&&(d.effectTag|=Kr);var z=Nl.current,G=!1,ne=(d.effectTag&Kr)!==xi;if(ne||ep(z,c,d)?(G=!0,d.effectTag&=~Kr):(c===null||c.memoizedState!==null)&&O.fallback!==void 0&&O.unstable_avoidThisFallback!==!0&&(z=vd(z,xf)),z=ya(z),Fr(d,z),"maxDuration"in O&&(y1||(y1=!0,Xt(!1,"maxDuration has been removed from React. Remove the maxDuration prop."))),c===null){if(O.fallback!==void 0&&(G2(d),Ai)){var se=d.memoizedState;if(se!==null){var Ue=se.dehydrated;if(Ue!==null)return Gh(d,Ue,D)}}if(G){var Xe=O.fallback,ht=nf(null,C,ft,null);if(ht.return=d,(d.mode&K)===Ar){var Lt=d.memoizedState,Gt=Lt!==null?d.child.child:d.child;ht.child=Gt;for(var Ht=Gt;Ht!==null;)Ht.return=ht,Ht=Ht.sibling}var yn=nf(Xe,C,D,null);return yn.return=d,ht.sibling=yn,d.memoizedState=Td,d.child=ht,yn}else{var kr=O.children;return d.memoizedState=null,d.child=$c(d,null,kr,D)}}else{var ii=c.memoizedState;if(ii!==null){if(Ai){var Oi=ii.dehydrated;if(Oi!==null)if(ne){if(d.memoizedState!==null)return d.child=c.child,d.effectTag|=Kr,null;var L0=O.fallback,$i=nf(null,C,ft,null);if($i.return=d,$i.child=null,(d.mode&K)===Ar)for(var lt=$i.child=d.child;lt!==null;)lt.return=$i,lt=lt.sibling;else Tf(d,c.child,null,D);if(en&&d.mode&ni){for(var Mt=0,$e=$i.child;$e!==null;)Mt+=$e.treeBaseDuration,$e=$e.sibling;$i.treeBaseDuration=Mt}var jt=nf(L0,C,D,null);return jt.return=d,$i.sibling=jt,jt.effectTag|=vi,$i.childExpirationTime=ft,d.memoizedState=Td,d.child=$i,jt}else return Yh(c,d,Oi,ii,D)}var Fn=c.child,vn=Fn.sibling;if(G){var Vi=O.fallback,ci=Co(Fn,Fn.pendingProps,ft);if(ci.return=d,(d.mode&K)===Ar){var Yu=d.memoizedState,hr=Yu!==null?d.child.child:d.child;if(hr!==Fn.child){ci.child=hr;for(var pu=hr;pu!==null;)pu.return=ci,pu=pu.sibling}}if(en&&d.mode&ni){for(var Yr=0,Cu=ci.child;Cu!==null;)Yr+=Cu.treeBaseDuration,Cu=Cu.sibling;ci.treeBaseDuration=Yr}var D0=Co(vn,Vi,vn.expirationTime);return D0.return=d,ci.sibling=D0,ci.childExpirationTime=ft,d.memoizedState=Td,d.child=ci,D0}else{var W0=O.children,Ms=Fn.child,Ku=Tf(d,Ms,W0,D);return d.memoizedState=null,d.child=Ku}}else{var gl=c.child;if(G){var rf=O.fallback,Vo=nf(null,C,ft,null);if(Vo.return=d,Vo.child=gl,gl!==null&&(gl.return=Vo),(d.mode&K)===Ar){var ks=d.memoizedState,Jd=ks!==null?d.child.child:d.child;Vo.child=Jd;for(var Vf=Jd;Vf!==null;)Vf.return=Vo,Vf=Vf.sibling}if(en&&d.mode&ni){for(var Lc=0,Hl=Vo.child;Hl!==null;)Lc+=Hl.treeBaseDuration,Hl=Hl.sibling;Vo.treeBaseDuration=Lc}var Go=nf(rf,C,D,null);return Go.return=d,Vo.sibling=Go,Go.effectTag|=vi,Vo.childExpirationTime=ft,d.memoizedState=Td,d.child=Vo,Go}else{d.memoizedState=null;var L1=O.children;return d.child=Tf(d,gl,L1,D)}}}}function tp(c,d,D){d.memoizedState=null;var C=d.pendingProps,O=C.children;return wo(c,d,O,D),d.child}function Gh(c,d,D){if((c.mode&K)===Ar)Xt(!1,"Cannot hydrate Suspense in legacy mode. Switch from ReactDOM.hydrate(element, container) to ReactDOM.createBlockingRoot(container, { hydrate: true }).render(element) or remove the Suspense components from the server rendered components."),c.expirationTime=Un;else if(m0(d)){var C=jl(),O=Ds(C);In&&R(O),c.expirationTime=O}else c.expirationTime=Di,In&&R(Di);return null}function Yh(c,d,D,C,O){if(q2(),(d.mode&K)===Ar||m0(D))return tp(c,d,O);var z=c.childExpirationTime>=O;if(wa||z){if(O. Use lowercase "%s" instead.',c,c.toLowerCase());break}case"forward":case"backward":{Xt(!1,'"%s" is not a valid value for revealOrder on . React uses the -s suffix in the spelling. Use "%ss" instead.',c,c.toLowerCase());break}default:Xt(!1,'"%s" is not a supported revealOrder on . Did you mean "together", "forwards" or "backwards"?',c);break}else Xt(!1,'%s is not a supported value for revealOrder on . Did you mean "together", "forwards" or "backwards"?',c)}function Kh(c,d){c!==void 0&&!Sc[c]&&(c!=="collapsed"&&c!=="hidden"?(Sc[c]=!0,Xt(!1,'"%s" is not a supported value for tail on . Did you mean "collapsed" or "hidden"?',c)):d!=="forwards"&&d!=="backwards"&&(Sc[c]=!0,Xt(!1,' is only valid if revealOrder is "forwards" or "backwards". Did you mean to specify revealOrder="forwards"?',c)))}function _1(c,d){{var D=Array.isArray(c),C=!D&&typeof fr(c)=="function";if(D||C){var O=D?"array":"iterable";return Xt(!1,"A nested %s was passed to row #%s in . Wrap it in an additional SuspenseList to configure its revealOrder: ... {%s} ... ",O,d,O),!1}}return!0}function Cm(c,d){if((d==="forwards"||d==="backwards")&&c!==void 0&&c!==null&&c!==!1)if(Array.isArray(c)){for(var D=0;D. This is not useful since it needs multiple rows. Did you mean to pass multiple children or an array?',d)}}function rp(c,d,D,C,O,z){var G=c.memoizedState;G===null?c.memoizedState={isBackwards:d,rendering:null,last:C,tail:D,tailExpiration:0,tailMode:O,lastEffect:z}:(G.isBackwards=d,G.rendering=null,G.last=C,G.tail=D,G.tailExpiration=0,G.tailMode=O,G.lastEffect=z)}function ip(c,d,D){var C=d.pendingProps,O=C.revealOrder,z=C.tail,G=C.children;Tm(O),Kh(z,O),Cm(G,O),wo(c,d,G,D);var ne=Nl.current,se=t1(ne,e1);if(se)ne=hd(ne,e1),d.effectTag|=Kr;else{var Ue=c!==null&&(c.effectTag&Kr)!==xi;Ue&&wm(d,d.child,D),ne=ya(ne)}if(Fr(d,ne),(d.mode&K)===Ar)d.memoizedState=null;else switch(O){case"forwards":{var Xe=Sm(d.child),ht;Xe===null?(ht=d.child,d.child=null):(ht=Xe.sibling,Xe.sibling=null),rp(d,!1,ht,Xe,z,d.lastEffect);break}case"backwards":{var Lt=null,Gt=d.child;for(d.child=null;Gt!==null;){var Ht=Gt.alternate;if(Ht!==null&&n1(Ht)===null){d.child=Gt;break}var yn=Gt.sibling;Gt.sibling=Lt,Lt=Gt,Gt=yn}rp(d,!0,Lt,null,z,d.lastEffect);break}case"together":{rp(d,!1,null,null,void 0,d.lastEffect);break}default:d.memoizedState=null}return d.child}function xm(c,d,D){Ya(d,d.stateNode.containerInfo);var C=d.pendingProps;return c===null?d.child=Tf(d,null,C,D):wo(c,d,C,D),d.child}function Rm(c,d,D){var C=d.type,O=C._context,z=d.pendingProps,G=d.memoizedProps,ne=z.value;{var se=d.type.propTypes;se&&E(se,z,"prop","Context.Provider",Lr)}if(xr(d,ne),G!==null){var Ue=G.value,Xe=cu(O,ne,Ue);if(Xe===0){if(G.children===z.children&&!sa())return Ta(c,d,D)}else Ml(d,O,Xe,D)}var ht=z.children;return wo(c,d,ht,D),d.child}var Xh=!1;function Am(c,d,D){var C=d.type;C._context===void 0?C!==C.Consumer&&(Xh||(Xh=!0,Xt(!1,"Rendering directly is not supported and will be removed in a future major release. Did you mean to render instead?"))):C=C._context;var O=d.pendingProps,z=O.children;typeof z!="function"&&Ve(!1,"A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it."),i0(d,D);var G=Ge(C,O.unstable_observedBits),ne;return m1.current=d,et("render"),ne=z(G),et(null),d.effectTag|=lu,wo(c,d,ne,D),d.child}function Om(c,d,D){var C=d.type.impl;if(C.reconcileChildren===!1)return null;var O=d.pendingProps,z=O.children;return wo(c,d,z,D),d.child}function up(c,d,D){var C=d.pendingProps,O=C.children;return wo(c,d,O,D),d.child}function op(){wa=!0}function Ta(c,d,D){tu(d),c!==null&&(d.dependencies=c.dependencies),en&&Lh(d);var C=d.expirationTime;C!==ft&&Kd(C);var O=d.childExpirationTime;return O=D;se&&(d.effectTag|=Dr)}break;case he:{var Ue=d.memoizedState;if(Ue!==null){if(Ai&&Ue.dehydrated!==null){Fr(d,ya(Nl.current)),d.effectTag|=Kr;break}var Xe=d.child,ht=Xe.childExpirationTime;if(ht!==ft&&ht>=D)return Vh(c,d,D);Fr(d,ya(Nl.current));var Lt=Ta(c,d,D);return Lt!==null?Lt.sibling:null}else Fr(d,ya(Nl.current));break}case gt:{var Gt=(c.effectTag&Kr)!==xi,Ht=d.childExpirationTime>=D;if(Gt){if(Ht)return ip(c,d,D);d.effectTag|=Kr}var yn=d.memoizedState;if(yn!==null&&(yn.rendering=null,yn.tail=null),Fr(d,Nl.current),Ht)break;return null}}return Ta(c,d,D)}else wa=!1}else wa=!1;switch(d.expirationTime=ft,d.tag){case x:return Dm(c,d,d.type,D);case Ae:{var kr=d.elementType;return If(c,d,kr,C,D)}case F:{var ii=d.type,Oi=d.pendingProps,L0=d.elementType===ii?Oi:Wi(ii,Oi);return J2(c,d,ii,L0,D)}case k:{var $i=d.type,lt=d.pendingProps,Mt=d.elementType===$i?lt:Wi($i,lt);return qh(c,d,$i,Mt,D)}case j:return _m(c,d,D);case V:return Em(c,d,D);case re:return Pf(c,d);case he:return Vh(c,d,D);case q:return xm(c,d,D);case ae:{var $e=d.type,jt=d.pendingProps,Fn=d.elementType===$e?jt:Wi($e,jt);return Bh(c,d,$e,Fn,D)}case y:return gm(c,d,D);case me:return zh(c,d,D);case we:return Hh(c,d,D);case ge:return Rm(c,d,D);case De:return Am(c,d,D);case ve:{var vn=d.type,Vi=d.pendingProps,ci=Wi(vn,Vi);if(d.type!==d.elementType){var Yu=vn.propTypes;Yu&&E(Yu,ci,"prop",qt(vn),Lr)}return ci=Wi(vn.type,ci),Uh(c,d,vn,ci,C,D)}case ue:return jh(c,d,d.type,d.pendingProps,C,D);case ze:{var hr=d.type,pu=d.pendingProps,Yr=d.elementType===hr?pu:Wi(hr,pu);return Sd(c,d,hr,Yr,D)}case gt:return ip(c,d,D);case _t:{if(Wt)return Om(c,d,D);break}case Qe:{if(Ru)return up(c,d,D);break}}throw Error("Unknown unit of work tag ("+d.tag+"). This error is likely caused by a bug in React. Please file an issue.")}function Qh(c,d,D,C){return{currentFiber:c,impl:D,instance:null,prevProps:null,props:d,state:C}}function Cd(c){return c.tag===he&&c.memoizedState!==null}function D1(c){return c.child.sibling.child}var Jh={};function sp(c,d,D){if(Ru){if(c.tag===V){var C=c.type,O=c.memoizedProps,z=c.stateNode,G=Lo(z);G!==null&&d(C,O||Jh,G)===!0&&D.push(G)}var ne=c.child;Cd(c)&&(ne=D1(c)),ne!==null&&ap(ne,d,D)}}function Zh(c,d){if(Ru){if(c.tag===V){var D=c.type,C=c.memoizedProps,O=c.stateNode,z=Lo(O);if(z!==null&&d(D,C,z)===!0)return z}var G=c.child;if(Cd(c)&&(G=D1(c)),G!==null)return $h(G,d)}return null}function ap(c,d,D){for(var C=c;C!==null;)sp(C,d,D),C=C.sibling}function $h(c,d){for(var D=c;D!==null;){var C=Zh(D,d);if(C!==null)return C;D=D.sibling}return null}function ev(c,d,D){if(xd(c,d))D.push(c.stateNode.methods);else{var C=c.child;Cd(c)&&(C=D1(c)),C!==null&&fp(C,d,D)}}function fp(c,d,D){for(var C=c;C!==null;)ev(C,d,D),C=C.sibling}function xd(c,d){return c.tag===Qe&&c.type===d&&c.stateNode!==null}function Rd(c,d){return{getChildren:function(){var D=d.fiber,C=D.child,O=[];return C!==null&&fp(C,c,O),O.length===0?null:O},getChildrenFromRoot:function(){for(var D=d.fiber,C=D;C!==null;){var O=C.return;if(O===null||(C=O,C.tag===Qe&&C.type===c))break}var z=[];return fp(C.child,c,z),z.length===0?null:z},getParent:function(){for(var D=d.fiber.return;D!==null;){if(D.tag===Qe&&D.type===c)return D.stateNode.methods;D=D.return}return null},getProps:function(){var D=d.fiber;return D.memoizedProps},queryAllNodes:function(D){var C=d.fiber,O=C.child,z=[];return O!==null&&ap(O,D,z),z.length===0?null:z},queryFirstNode:function(D){var C=d.fiber,O=C.child;return O!==null?$h(O,D):null},containsNode:function(D){for(var C=cr(D);C!==null;){if(C.tag===Qe&&C.type===c&&C.stateNode===d)return!0;C=C.return}return!1}}}function H0(c){c.effectTag|=Dr}function Ad(c){c.effectTag|=Oo}var Ca,$a,Od,Md;if(b0)Ca=function(c,d,D,C){for(var O=d.child;O!==null;){if(O.tag===V||O.tag===re)Xr(c,O.stateNode);else if(Wt&&O.tag===_t)Xr(c,O.stateNode.instance);else if(O.tag!==q){if(O.child!==null){O.child.return=O,O=O.child;continue}}if(O===d)return;for(;O.sibling===null;){if(O.return===null||O.return===d)return;O=O.return}O.sibling.return=O.return,O=O.sibling}},$a=function(c){},Od=function(c,d,D,C,O){var z=c.memoizedProps;if(z!==C){var G=d.stateNode,ne=fl(),se=p0(G,D,z,C,O,ne);d.updateQueue=se,se&&H0(d)}},Md=function(c,d,D,C){D!==C&&H0(d)};else if(Q){Ca=function(c,d,D,C){for(var O=d.child;O!==null;){e:if(O.tag===V){var z=O.stateNode;if(D&&C){var G=O.memoizedProps,ne=O.type;z=Jr(z,ne,G,O)}Xr(c,z)}else if(O.tag===re){var se=O.stateNode;if(D&&C){var Ue=O.memoizedProps;se=Zl(se,Ue,O)}Xr(c,se)}else if(Wt&&O.tag===_t){var Xe=O.stateNode.instance;if(D&&C){var ht=O.memoizedProps,Lt=O.type;Xe=Jr(Xe,Lt,ht,O)}Xr(c,Xe)}else if(O.tag!==q){if(O.tag===he){if((O.effectTag&Dr)!==xi){var Gt=O.memoizedState!==null;if(Gt){var Ht=O.child;if(Ht!==null){Ht.child!==null&&(Ht.child.return=Ht,Ca(c,Ht,!0,Gt));var yn=Ht.sibling;if(yn!==null){yn.return=O,O=yn;continue}}}}if(O.child!==null){O.child.return=O,O=O.child;continue}}else if(O.child!==null){O.child.return=O,O=O.child;continue}}if(O=O,O===d)return;for(;O.sibling===null;){if(O.return===null||O.return===d)return;O=O.return}O.sibling.return=O.return,O=O.sibling}};var cp=function(c,d,D,C){for(var O=d.child;O!==null;){e:if(O.tag===V){var z=O.stateNode;if(D&&C){var G=O.memoizedProps,ne=O.type;z=Jr(z,ne,G,O)}Qn(c,z)}else if(O.tag===re){var se=O.stateNode;if(D&&C){var Ue=O.memoizedProps;se=Zl(se,Ue,O)}Qn(c,se)}else if(Wt&&O.tag===_t){var Xe=O.stateNode.instance;if(D&&C){var ht=O.memoizedProps,Lt=O.type;Xe=Jr(Xe,Lt,ht,O)}Qn(c,Xe)}else if(O.tag!==q){if(O.tag===he){if((O.effectTag&Dr)!==xi){var Gt=O.memoizedState!==null;if(Gt){var Ht=O.child;if(Ht!==null){Ht.child!==null&&(Ht.child.return=Ht,cp(c,Ht,!0,Gt));var yn=Ht.sibling;if(yn!==null){yn.return=O,O=yn;continue}}}}if(O.child!==null){O.child.return=O,O=O.child;continue}}else if(O.child!==null){O.child.return=O,O=O.child;continue}}if(O=O,O===d)return;for(;O.sibling===null;){if(O.return===null||O.return===d)return;O=O.return}O.sibling.return=O.return,O=O.sibling}};$a=function(c){var d=c.stateNode,D=c.firstEffect===null;if(!D){var C=d.containerInfo,O=S0(C);cp(O,c,!1,!1),d.pendingChildren=O,H0(c),ac(C,O)}},Od=function(c,d,D,C,O){var z=c.stateNode,G=c.memoizedProps,ne=d.firstEffect===null;if(ne&&G===C){d.stateNode=z;return}var se=d.stateNode,Ue=fl(),Xe=null;if(G!==C&&(Xe=p0(se,D,G,C,O,Ue)),ne&&Xe===null){d.stateNode=z;return}var ht=ms(z,Xe,D,G,C,d,ne,se);Au(ht,D,C,O,Ue)&&H0(d),d.stateNode=ht,ne?H0(d):Ca(ht,d,!1,!1)},Md=function(c,d,D,C){if(D!==C){var O=ns(),z=fl();d.stateNode=hs(C,O,z,d),H0(d)}}}else $a=function(c){},Od=function(c,d,D,C,O){},Md=function(c,d,D,C){};function kd(c,d){switch(c.tailMode){case"hidden":{for(var D=c.tail,C=null;D!==null;)D.alternate!==null&&(C=D),D=D.sibling;C===null?c.tail=null:C.sibling=null;break}case"collapsed":{for(var O=c.tail,z=null;O!==null;)O.alternate!==null&&(z=O),O=O.sibling;z===null?!d&&c.tail!==null?c.tail.sibling=null:c.tail=null:z.sibling=null;break}}}function tv(c,d,D){var C=d.pendingProps;switch(d.tag){case x:break;case Ae:break;case ue:case F:break;case k:{var O=d.type;Xi(O)&&Hs(d);break}case j:{uo(d),R0(d);var z=d.stateNode;if(z.pendingContext&&(z.context=z.pendingContext,z.pendingContext=null),c===null||c.child===null){var G=h1(d);G&&H0(d)}$a(d);break}case V:{M2(d);var ne=ns(),se=d.type;if(c!==null&&d.stateNode!=null){if(Od(c,d,se,C,ne),yi){var Ue=c.memoizedProps.listeners,Xe=C.listeners;Ue!==Xe&&H0(d)}c.ref!==d.ref&&Ad(d)}else{if(!C){if(d.stateNode===null)throw Error("We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.");break}var ht=fl(),Lt=h1(d);if(Lt){if(vm(d,ne,ht)&&H0(d),yi){var Gt=C.listeners;Gt!=null&&hn(Gt,d,ne)}}else{var Ht=Ki(se,C,ne,ht,d);if(Ca(Ht,d,!1,!1),d.stateNode=Ht,yi){var yn=C.listeners;yn!=null&&hn(yn,d,ne)}Au(Ht,se,C,ne,ht)&&H0(d)}d.ref!==null&&Ad(d)}break}case re:{var kr=C;if(c&&d.stateNode!=null){var ii=c.memoizedProps;Md(c,d,ii,kr)}else{if(typeof kr!="string"&&d.stateNode===null)throw Error("We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.");var Oi=ns(),L0=fl(),$i=h1(d);$i?mm(d)&&H0(d):d.stateNode=hs(kr,Oi,L0,d)}break}case ae:break;case he:{ga(d);var lt=d.memoizedState;if(Ai&<!==null&<.dehydrated!==null)if(c===null){var Mt=h1(d);if(!Mt)throw Error("A dehydrated suspense component was completed without a hydrated node. This is probably a bug in React.");return Ih(d),In&&R(Di),null}else return v1(),(d.effectTag&Kr)===xi&&(d.memoizedState=null),d.effectTag|=Dr,null;if((d.effectTag&Kr)!==xi)return d.expirationTime=D,d;var $e=lt!==null,jt=!1;if(c===null)d.memoizedProps.fallback!==void 0&&h1(d);else{var Fn=c.memoizedState;if(jt=Fn!==null,!$e&&Fn!==null){var vn=c.child.sibling;if(vn!==null){var Vi=d.firstEffect;Vi!==null?(d.firstEffect=vn,vn.nextEffect=Vi):(d.firstEffect=d.lastEffect=vn,vn.nextEffect=null),vn.effectTag=Y0}}}if($e&&!jt&&(d.mode&K)!==Ar){var ci=c===null&&d.memoizedProps.unstable_avoidThisFallback!==!0;ci||t1(Nl.current,xf)?_v():Ev()}Q&&$e&&(d.effectTag|=Dr),b0&&($e||jt)&&(d.effectTag|=Dr),Yi&&d.updateQueue!==null&&d.memoizedProps.suspenseCallback!=null&&(d.effectTag|=Dr);break}case y:break;case me:break;case we:break;case q:uo(d),$a(d);break;case ge:r0(d);break;case De:break;case ve:break;case ze:{var Yu=d.type;Xi(Yu)&&Hs(d);break}case gt:{ga(d);var hr=d.memoizedState;if(hr===null)break;var pu=(d.effectTag&Kr)!==xi,Yr=hr.rendering;if(Yr===null)if(pu)kd(hr,!1);else{var Cu=Dv()&&(c===null||(c.effectTag&Kr)===xi);if(!Cu)for(var D0=d.child;D0!==null;){var W0=n1(D0);if(W0!==null){pu=!0,d.effectTag|=Kr,kd(hr,!1);var Ms=W0.updateQueue;return Ms!==null&&(d.updateQueue=Ms,d.effectTag|=Dr),hr.lastEffect===null&&(d.firstEffect=null),d.lastEffect=hr.lastEffect,sm(d,D),Fr(d,hd(Nl.current,e1)),d.child}D0=D0.sibling}}else{if(!pu){var Ku=n1(Yr);if(Ku!==null){d.effectTag|=Kr,pu=!0;var gl=Ku.updateQueue;if(gl!==null&&(d.updateQueue=gl,d.effectTag|=Dr),kd(hr,!0),hr.tail===null&&hr.tailMode==="hidden"&&!Yr.alternate){var rf=d.lastEffect=hr.lastEffect;return rf!==null&&(rf.nextEffect=null),null}}else if(mt()>hr.tailExpiration&&D>Di){d.effectTag|=Kr,pu=!0,kd(hr,!1);var Vo=D-1;d.expirationTime=d.childExpirationTime=Vo,In&&R(Vo)}}if(hr.isBackwards)Yr.sibling=d.child,d.child=Yr;else{var ks=hr.last;ks!==null?ks.sibling=Yr:d.child=Yr,hr.last=Yr}}if(hr.tail!==null){if(hr.tailExpiration===0){var Jd=500;hr.tailExpiration=mt()+Jd}var Vf=hr.tail;hr.rendering=Vf,hr.tail=Vf.sibling,hr.lastEffect=d.lastEffect,Vf.sibling=null;var Lc=Nl.current;return pu?Lc=hd(Lc,e1):Lc=ya(Lc),Fr(d,Lc),Vf}break}case _t:{if(Wt){var Hl=d.type.impl,Go=d.stateNode;if(Go===null){var L1=Hl.getInitialState,i_;L1!==void 0&&(i_=L1(C)),Go=d.stateNode=Qh(d,C,Hl,i_||{});var u_=pt(Go);if(Go.instance=u_,Hl.reconcileChildren===!1)return null;Ca(u_,d,!1,!1),Yn(Go)}else{var nE=Go.props;if(Go.prevProps=nE,Go.props=C,Go.currentFiber=d,Q){var o_=oa(Go);Go.instance=o_,Ca(o_,d,!1,!1)}var rE=Cn(Go);rE&&H0(d)}}break}case Qe:{if(Ru)if(c===null){var iE=d.type,Ry={fiber:d,methods:null};if(d.stateNode=Ry,Ry.methods=Rd(iE,Ry),yi){var l_=C.listeners;if(l_!=null){var uE=ns();hn(l_,d,uE)}}d.ref!==null&&(Ad(d),H0(d))}else{if(yi){var oE=c.memoizedProps.listeners,lE=C.listeners;(oE!==lE||d.ref!==null)&&H0(d)}else d.ref!==null&&H0(d);c.ref!==d.ref&&Ad(d)}break}default:throw Error("Unknown unit of work tag ("+d.tag+"). This error is likely caused by a bug in React. Please file an issue.")}return null}function Mm(c,d){switch(c.tag){case k:{var D=c.type;Xi(D)&&Hs(c);var C=c.effectTag;return C&d0?(c.effectTag=C&~d0|Kr,c):null}case j:{uo(c),R0(c);var O=c.effectTag;if((O&Kr)!==xi)throw Error("The root failed to unmount after an error. This is likely a bug in React. Please file an issue.");return c.effectTag=O&~d0|Kr,c}case V:return M2(c),null;case he:{if(ga(c),Ai){var z=c.memoizedState;if(z!==null&&z.dehydrated!==null){if(c.alternate===null)throw Error("Threw in newly mounted dehydrated component. This is likely a bug in React. Please file an issue.");v1()}}var G=c.effectTag;return G&d0?(c.effectTag=G&~d0|Kr,c):null}case gt:return ga(c),null;case q:return uo(c),null;case ge:return r0(c),null;default:return null}}function nv(c){switch(c.tag){case k:{var d=c.type.childContextTypes;d!=null&&Hs(c);break}case j:{uo(c),R0(c);break}case V:{M2(c);break}case q:uo(c);break;case he:ga(c);break;case gt:ga(c);break;case ge:r0(c);break;default:break}}function dp(c,d){return{value:c,source:d,stack:Cr(d)}}var pp=function(c,d,D,C,O,z,G,ne,se){var Ue=Array.prototype.slice.call(arguments,3);try{d.apply(D,Ue)}catch(Xe){this.onError(Xe)}};if(typeof window<"u"&&typeof window.dispatchEvent=="function"&&typeof document<"u"&&typeof document.createEvent=="function"){var hp=document.createElement("react"),km=function(c,d,D,C,O,z,G,ne,se){if(!(typeof document<"u"))throw Error("The `document` global was defined when React was initialized, but is not defined anymore. This can happen in a test environment if a component schedules an update from an asynchronous callback, but the test has already finished running. To solve this, you can either unmount the component at the end of your test (and ensure that any asynchronous operations get canceled in `componentWillUnmount`), or you can change the test itself to be asynchronous.");var Ue=document.createEvent("Event"),Xe=!0,ht=window.event,Lt=Object.getOwnPropertyDescriptor(window,"event"),Gt=Array.prototype.slice.call(arguments,3);function Ht(){hp.removeEventListener(L0,Ht,!1),typeof window.event<"u"&&window.hasOwnProperty("event")&&(window.event=ht),d.apply(D,Gt),Xe=!1}var yn,kr=!1,ii=!1;function Oi($i){if(yn=$i.error,kr=!0,yn===null&&$i.colno===0&&$i.lineno===0&&(ii=!0),$i.defaultPrevented&&yn!=null&&typeof yn=="object")try{yn._suppressLogging=!0}catch{}}var L0="react-"+(c||"invokeguardedcallback");window.addEventListener("error",Oi),hp.addEventListener(L0,Ht,!1),Ue.initEvent(L0,!1,!1),hp.dispatchEvent(Ue),Lt&&Object.defineProperty(window,"event",Lt),Xe&&(kr?ii&&(yn=new Error("A cross-origin error was thrown. React doesn't have access to the actual error object in development. See https://fb.me/react-crossorigin-error for more information.")):yn=new Error(`An error was thrown inside one of your components, but React doesn't know what it was. This is likely due to browser flakiness. React does its best to preserve the "Pause on exceptions" behavior of the DevTools, which requires some DEV-mode only tricks. It's possible that these don't work in your browser. Try triggering the error in production mode, or switching to a modern browser. If you suspect that this is actually an issue with React, please file an issue.`),this.onError(yn)),window.removeEventListener("error",Oi)};pp=km}var Nm=pp,So=!1,Nd=null,Lm={onError:function(c){So=!0,Nd=c}};function pl(c,d,D,C,O,z,G,ne,se){So=!1,Nd=null,Nm.apply(Lm,arguments)}function tr(){return So}function Qs(){if(So){var c=Nd;return So=!1,Nd=null,c}else throw Error("clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue.")}function hl(c){return!0}function o0(c){var d=hl(c);if(d!==!1){var D=c.error;{var C=c.componentName,O=c.componentStack,z=c.errorBoundaryName,G=c.errorBoundaryFound,ne=c.willRetry;if(D!=null&&D._suppressLogging){if(G&&ne)return;console.error(D)}var se=C?"The above error occurred in the <"+C+"> component:":"The above error occurred in one of your React components:",Ue;G&&z?ne?Ue="React will try to recreate this component tree from scratch "+("using the error boundary you provided, "+z+"."):Ue="This error was initially handled by the error boundary "+z+`. +Recreating the tree from scratch failed so React will unmount the tree.`:Ue=`Consider adding an error boundary to your tree to customize error handling behavior. +Visit https://fb.me/react-error-boundaries to learn more about error boundaries.`;var Xe=""+se+O+` + +`+(""+Ue);console.error(Xe)}}}var rv=null;rv=new Set;var Js=typeof WeakSet=="function"?WeakSet:Set;function vp(c,d){var D=d.source,C=d.stack;C===null&&D!==null&&(C=Cr(D));var O={componentName:D!==null?qt(D.type):null,componentStack:C!==null?C:"",error:d.value,errorBoundary:null,errorBoundaryName:null,errorBoundaryFound:!1,willRetry:!1};c!==null&&c.tag===k&&(O.errorBoundary=c.stateNode,O.errorBoundaryName=qt(c.type),O.errorBoundaryFound=!0,O.willRetry=!0);try{o0(O)}catch(z){setTimeout(function(){throw z})}}var Fm=function(c,d){Bi(c,"componentWillUnmount"),d.props=c.memoizedProps,d.state=c.memoizedState,d.componentWillUnmount(),Ci()};function iv(c,d){if(pl(null,Fm,null,c,d),tr()){var D=Qs();Hf(c,D)}}function mp(c){var d=c.ref;if(d!==null)if(typeof d=="function"){if(pl(null,d,null,null),tr()){var D=Qs();Hf(c,D)}}else d.current=null}function Pm(c,d){if(pl(null,d,null),tr()){var D=Qs();Hf(c,D)}}function yp(c,d){switch(d.tag){case F:case ae:case ue:{Tc(fm,Af,d);return}case k:{if(d.effectTag&Mo&&c!==null){var D=c.memoizedProps,C=c.memoizedState;Bi(d,"getSnapshotBeforeUpdate");var O=d.stateNode;d.type===d.elementType&&!Sa&&(O.props!==d.memoizedProps&&Xt(!1,"Expected %s props to match memoized props before getSnapshotBeforeUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(d.type)||"instance"),O.state!==d.memoizedState&&Xt(!1,"Expected %s state to match memoized state before getSnapshotBeforeUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(d.type)||"instance"));var z=O.getSnapshotBeforeUpdate(d.elementType===d.type?D:Wi(d.type,D),C);{var G=rv;z===void 0&&!G.has(d.type)&&(G.add(d.type),Ve(!1,"%s.getSnapshotBeforeUpdate(): A snapshot value (or null) must be returned. You have returned undefined.",qt(d.type)))}O.__reactInternalSnapshotBeforeUpdate=z,Ci()}return}case j:case V:case re:case q:case ze:return;default:throw Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}}function Tc(c,d,D){var C=D.updateQueue,O=C!==null?C.lastEffect:null;if(O!==null){var z=O.next,G=z;do{if((G.tag&c)!==Af){var ne=G.destroy;G.destroy=void 0,ne!==void 0&&ne()}if((G.tag&d)!==Af){var se=G.create;G.destroy=se();{var Ue=G.destroy;if(Ue!==void 0&&typeof Ue!="function"){var Xe=void 0;Ue===null?Xe=" You returned null. If your effect does not require clean up, return undefined (or nothing).":typeof Ue.then=="function"?Xe=` + +It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately: + +useEffect(() => { + async function fetchData() { + // You can await here + const response = await MyAPI.getData(someId); + // ... + } + fetchData(); +}, [someId]); // Or [] if effect doesn't need props or state + +Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching`:Xe=" You returned: "+Ue,Ve(!1,"An effect function must not return anything besides a function, which is used for clean-up.%s%s",Xe,Cr(D))}}}G=G.next}while(G!==z)}}function xa(c){if((c.effectTag&F0)!==xi)switch(c.tag){case F:case ae:case ue:{Tc(sr,Af,c),Tc(Af,r1,c);break}default:break}}function gp(c,d,D,C){switch(D.tag){case F:case ae:case ue:{Tc(cm,cl,D);break}case k:{var O=D.stateNode;if(D.effectTag&Dr)if(d===null)Bi(D,"componentDidMount"),D.type===D.elementType&&!Sa&&(O.props!==D.memoizedProps&&Xt(!1,"Expected %s props to match memoized props before componentDidMount. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(D.type)||"instance"),O.state!==D.memoizedState&&Xt(!1,"Expected %s state to match memoized state before componentDidMount. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(D.type)||"instance")),O.componentDidMount(),Ci();else{var z=D.elementType===D.type?d.memoizedProps:Wi(D.type,d.memoizedProps),G=d.memoizedState;Bi(D,"componentDidUpdate"),D.type===D.elementType&&!Sa&&(O.props!==D.memoizedProps&&Xt(!1,"Expected %s props to match memoized props before componentDidUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(D.type)||"instance"),O.state!==D.memoizedState&&Xt(!1,"Expected %s state to match memoized state before componentDidUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(D.type)||"instance")),O.componentDidUpdate(z,G,O.__reactInternalSnapshotBeforeUpdate),Ci()}var ne=D.updateQueue;ne!==null&&(D.type===D.elementType&&!Sa&&(O.props!==D.memoizedProps&&Xt(!1,"Expected %s props to match memoized props before processing the update queue. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(D.type)||"instance"),O.state!==D.memoizedState&&Xt(!1,"Expected %s state to match memoized state before processing the update queue. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(D.type)||"instance")),go(D,ne,O,C));return}case j:{var se=D.updateQueue;if(se!==null){var Ue=null;if(D.child!==null)switch(D.child.tag){case V:Ue=Lo(D.child.stateNode);break;case k:Ue=D.child.stateNode;break}go(D,se,Ue,C)}return}case V:{var Xe=D.stateNode;if(d===null&&D.effectTag&Dr){var ht=D.type,Lt=D.memoizedProps;zu(Xe,ht,Lt,D)}return}case re:return;case q:return;case we:{if(en){var Gt=D.memoizedProps.onRender;typeof Gt=="function"&&(In?Gt(D.memoizedProps.id,d===null?"mount":"update",D.actualDuration,D.treeBaseDuration,D.actualStartTime,Il(),c.memoizedInteractions):Gt(D.memoizedProps.id,d===null?"mount":"update",D.actualDuration,D.treeBaseDuration,D.actualStartTime,Il()))}return}case he:{Bl(c,D);return}case gt:case ze:case _t:case Qe:return;default:throw Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}}function Ld(c,d){if(b0)for(var D=c;;){if(D.tag===V){var C=D.stateNode;d?Pa(C):ia(D.stateNode,D.memoizedProps)}else if(D.tag===re){var O=D.stateNode;d?v0(O):J0(O,D.memoizedProps)}else if(D.tag===he&&D.memoizedState!==null&&D.memoizedState.dehydrated===null){var z=D.child.sibling;z.return=D,D=z;continue}else if(D.child!==null){D.child.return=D,D=D.child;continue}if(D===c)return;for(;D.sibling===null;){if(D.return===null||D.return===c)return;D=D.return}D.sibling.return=D.return,D=D.sibling}}function Iu(c){var d=c.ref;if(d!==null){var D=c.stateNode,C;switch(c.tag){case V:C=Lo(D);break;default:C=D}Ru&&c.tag===Qe&&(C=D.methods),typeof d=="function"?d(C):(d.hasOwnProperty("current")||Ve(!1,"Unexpected ref object provided for %s. Use either a ref-setter function or React.createRef().%s",qt(c.type),Cr(c)),d.current=C)}}function Gu(c){var d=c.ref;d!==null&&(typeof d=="function"?d(null):d.current=null)}function _p(c,d,D){switch(Mn(d),d.tag){case F:case ae:case ve:case ue:{var C=d.updateQueue;if(C!==null){var O=C.lastEffect;if(O!==null){var z=O.next,G=D>Kn?Kn:D;Sn(G,function(){var ii=z;do{var Oi=ii.destroy;Oi!==void 0&&Pm(d,Oi),ii=ii.next}while(ii!==z)})}}break}case k:{mp(d);var ne=d.stateNode;typeof ne.componentWillUnmount=="function"&&iv(d,ne);return}case V:{if(yi){var se=d.dependencies;if(se!==null){var Ue=se.responders;if(Ue!==null){for(var Xe=Array.from(Ue.values()),ht=0,Lt=Xe.length;ht component higher in the tree to provide a loading indicator or placeholder to display.`+Cr(D))}Op(),C=dp(C,D);var Lt=d;do{switch(Lt.tag){case j:{var Gt=C;Lt.effectTag|=d0,Lt.expirationTime=O;var Ht=sv(Lt,Gt,O);od(Lt,Ht);return}case k:var yn=C,kr=Lt.type,ii=Lt.stateNode;if((Lt.effectTag&Kr)===xi&&(typeof kr.getDerivedStateFromError=="function"||ii!==null&&typeof ii.componentDidCatch=="function"&&!Fp(ii))){Lt.effectTag|=d0,Lt.expirationTime=O;var Oi=av(Lt,yn,O);od(Lt,Oi);return}break;default:break}Lt=Lt.return}while(Lt!==null)}var Aa=Math.ceil,Mr=it.ReactCurrentDispatcher,Dp=it.ReactCurrentOwner,vl=it.IsSomeRendererActing,yu=0,T1=1,Ui=2,wp=4,Id=8,To=16,As=32,bf=0,bd=1,Sp=2,C1=3,x1=4,Tp=5,nr=yu,ml=null,Gn=null,q0=ft,k0=bf,Bd=null,Ul=Un,R1=Un,xc=null,Rc=ft,Ud=!1,Cp=0,N0=500,dn=null,jd=!1,zd=null,Ac=null,Oc=!1,Mc=null,A1=_0,xp=ft,ef=null,Hm=50,kc=0,Hd=null,cv=50,O1=0,Bf=null,Uf=null,M1=ft;function jl(){return(nr&(To|As))!==yu?t0(mt()):(M1!==ft||(M1=t0(mt())),M1)}function Nc(){return t0(mt())}function jf(c,d,D){var C=d.mode;if((C&K)===Ar)return Un;var O=Qt();if((C&ti)===Ar)return O===Li?Un:e0;if((nr&To)!==yu)return q0;var z;if(D!==null)z=fa(c,D.timeoutMs|0||_f);else switch(O){case Li:z=Un;break;case ei:z=Ua(c);break;case Kn:case $u:z=Ds(c);break;case g0:z=ru;break;default:throw Error("Expected a valid priority level")}return ml!==null&&z===q0&&(z-=1),z}function qm(c,d){sy(),dy(c);var D=qd(c,d);if(D===null){fy(c);return}jp(c,d),la();var C=Qt();if(d===Un?(nr&Id)!==yu&&(nr&(To|As))===yu?(W(D,d),k1(D)):(Wo(D),W(D,d),nr===yu&&Ut()):(Wo(D),W(D,d)),(nr&wp)!==yu&&(C===ei||C===Li))if(ef===null)ef=new Map([[D,d]]);else{var O=ef.get(D);(O===void 0||O>d)&&ef.set(D,d)}}var yl=qm;function qd(c,d){c.expirationTimeO?C:O}function Wo(c){var d=c.lastExpiredTime;if(d!==ft){c.callbackExpirationTime=Un,c.callbackPriority=Li,c.callbackNode=Tn(k1.bind(null,c));return}var D=Wd(c),C=c.callbackNode;if(D===ft){C!==null&&(c.callbackNode=null,c.callbackExpirationTime=ft,c.callbackPriority=_0);return}var O=jl(),z=nd(O,D);if(C!==null){var G=c.callbackPriority,ne=c.callbackExpirationTime;if(ne===D&&G>=z)return;ir(C)}c.callbackExpirationTime=D,c.callbackPriority=z;var se;D===Un?se=Tn(k1.bind(null,c)):ao?se=_n(z,Vd.bind(null,c)):se=_n(z,Vd.bind(null,c),{timeout:jo(D)-mt()}),c.callbackNode=se}function Vd(c,d){if(M1=ft,d){var D=jl();return qp(c,D),Wo(c),null}var C=Wd(c);if(C!==ft){var O=c.callbackNode;if((nr&(To|As))!==yu)throw Error("Should not already be working.");if(tf(),(c!==ml||C!==q0)&&(zf(c,C),te(c,C)),Gn!==null){var z=nr;nr|=To;var G=mv(c),ne=Gd(c);mf(Gn);do try{ey();break}catch(Xe){vv(c,Xe)}while(!0);if(yt(),nr=z,yv(G),In&&Yd(ne),k0===bd){var se=Bd;throw Up(),zf(c,C),Wf(c,C),Wo(c),se}if(Gn!==null)Up();else{Rv();var Ue=c.finishedWork=c.current.alternate;c.finishedExpirationTime=C,Wm(c,Ue,k0,C)}if(Wo(c),c.callbackNode===O)return Vd.bind(null,c)}}return null}function Wm(c,d,D,C){switch(ml=null,D){case bf:case bd:throw Error("Root did not complete. This is a bug in React.");case Sp:{qp(c,C>ru?ru:C);break}case C1:{Wf(c,C);var O=c.lastSuspendedTime;C===O&&(c.nextKnownPendingLevel=Mp(d)),p();var z=Ul===Un;if(z&&!(Q0&&qf.current)){var G=Cp+N0-mt();if(G>10){if(Ud){var ne=c.lastPingedTime;if(ne===ft||ne>=C){c.lastPingedTime=C,zf(c,C);break}}var se=Wd(c);if(se!==ft&&se!==C)break;if(O!==ft&&O!==C){c.lastPingedTime=O;break}c.timeoutHandle=Ct(l0.bind(null,c),G);break}}l0(c);break}case x1:{Wf(c,C);var Ue=c.lastSuspendedTime;if(C===Ue&&(c.nextKnownPendingLevel=Mp(d)),p(),!(Q0&&qf.current)){if(Ud){var Xe=c.lastPingedTime;if(Xe===ft||Xe>=C){c.lastPingedTime=C,zf(c,C);break}}var ht=Wd(c);if(ht!==ft&&ht!==C)break;if(Ue!==ft&&Ue!==C){c.lastPingedTime=Ue;break}var Lt;if(R1!==Un)Lt=jo(R1)-mt();else if(Ul===Un)Lt=0;else{var Gt=wv(Ul),Ht=mt(),yn=jo(C)-Ht,kr=Ht-Gt;kr<0&&(kr=0),Lt=bp(kr)-kr,yn10){c.timeoutHandle=Ct(l0.bind(null,c),Lt);break}}l0(c);break}case Tp:{if(!(Q0&&qf.current)&&Ul!==Un&&xc!==null){var ii=Bp(Ul,C,xc);if(ii>10){Wf(c,C),c.timeoutHandle=Ct(l0.bind(null,c),ii);break}}l0(c);break}default:throw Error("Unknown root exit status.")}}function k1(c){var d=c.lastExpiredTime,D=d!==ft?d:Un;if(c.finishedExpirationTime===D)l0(c);else{if((nr&(To|As))!==yu)throw Error("Should not already be working.");if(tf(),(c!==ml||D!==q0)&&(zf(c,D),te(c,D)),Gn!==null){var C=nr;nr|=To;var O=mv(c),z=Gd(c);mf(Gn);do try{Sv();break}catch(ne){vv(c,ne)}while(!0);if(yt(),nr=C,yv(O),In&&Yd(z),k0===bd){var G=Bd;throw Up(),zf(c,D),Wf(c,D),Wo(c),G}if(Gn!==null)throw Error("Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue.");Rv(),c.finishedWork=c.current.alternate,c.finishedExpirationTime=D,Vm(c,k0,D),Wo(c)}}return null}function Vm(c,d,D){ml=null,(d===C1||d===x1)&&p(),l0(c)}function Gm(c,d){qp(c,d),Wo(c),(nr&(To|As))===yu&&Ut()}function dv(){if((nr&(T1|To|As))!==yu){(nr&To)!==yu&&Xt(!1,"unstable_flushDiscreteUpdates: Cannot flush updates when React is already rendering.");return}Km(),tf()}function Ym(c){return Sn(Kn,c)}function pv(c,d,D,C){return Sn(Li,c.bind(null,d,D,C))}function Km(){if(ef!==null){var c=ef;ef=null,c.forEach(function(d,D){qp(D,d),Wo(D)}),Ut()}}function Xm(c,d){var D=nr;nr|=T1;try{return c(d)}finally{nr=D,nr===yu&&Ut()}}function Qm(c,d){var D=nr;nr|=Ui;try{return c(d)}finally{nr=D,nr===yu&&Ut()}}function hv(c,d,D,C){var O=nr;nr|=wp;try{return Sn(ei,c.bind(null,d,D,C))}finally{nr=O,nr===yu&&Ut()}}function Jm(c,d){var D=nr;nr&=~T1,nr|=Id;try{return c(d)}finally{nr=D,nr===yu&&Ut()}}function Rp(c,d){if((nr&(To|As))!==yu)throw Error("flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering.");var D=nr;nr|=T1;try{return Sn(Li,c.bind(null,d))}finally{nr=D,Ut()}}function Zm(c){var d=nr;nr|=T1;try{Sn(Li,c)}finally{nr=d,nr===yu&&Ut()}}function zf(c,d){c.finishedWork=null,c.finishedExpirationTime=ft;var D=c.timeoutHandle;if(D!==nl&&(c.timeoutHandle=nl,co(D)),Gn!==null)for(var C=Gn.return;C!==null;)nv(C),C=C.return;ml=c,Gn=Co(c.current,null,d),q0=d,k0=bf,Bd=null,Ul=Un,R1=Un,xc=null,Rc=ft,Ud=!1,In&&(Uf=null),Al.discardPendingWarnings(),Zs=null}function vv(c,d){do{try{if(yt(),_d(),ut(),Gn===null||Gn.return===null)return k0=bd,Bd=d,null;en&&Gn.mode&ni&&p1(Gn,!0),fv(c,Gn.return,Gn,d,q0),Gn=Tv(Gn)}catch(D){d=D;continue}return}while(!0)}function mv(c){var d=Mr.current;return Mr.current=f1,d===null?f1:d}function yv(c){Mr.current=c}function Gd(c){if(In){var d=N.__interactionsRef.current;return N.__interactionsRef.current=c.memoizedInteractions,d}return null}function Yd(c){In&&(N.__interactionsRef.current=c)}function Ap(){Cp=mt()}function gv(c,d){cru&&(Ul=c),d!==null&&cru&&(R1=c,xc=d)}function Kd(c){c>Rc&&(Rc=c)}function _v(){k0===bf&&(k0=C1)}function Ev(){(k0===bf||k0===C1)&&(k0=x1),Rc!==ft&&ml!==null&&(Wf(ml,q0),Kg(ml,Rc))}function Op(){k0!==Tp&&(k0=Sp)}function Dv(){return k0===bf}function wv(c){var d=jo(c);return d-_f}function $m(c,d){var D=jo(c);return D-(d.timeoutMs|0||_f)}function Sv(){for(;Gn!==null;)Gn=Xd(Gn)}function ey(){for(;Gn!==null&&!Ln();)Gn=Xd(Gn)}function Xd(c){var d=c.alternate;$l(c),wt(c);var D;return en&&(c.mode&ni)!==Ar?(H2(c),D=N1(d,c,q0),p1(c,!0)):D=N1(d,c,q0),ut(),c.memoizedProps=c.pendingProps,D===null&&(D=Tv(c)),Dp.current=null,D}function Tv(c){Gn=c;do{var d=Gn.alternate,D=Gn.return;if((Gn.effectTag&P0)===xi){wt(Gn);var C=void 0;if(!en||(Gn.mode&ni)===Ar?C=tv(d,Gn,q0):(H2(Gn),C=tv(d,Gn,q0),p1(Gn,!1)),Zr(Gn),ut(),ty(Gn),C!==null)return C;if(D!==null&&(D.effectTag&P0)===xi){D.firstEffect===null&&(D.firstEffect=Gn.firstEffect),Gn.lastEffect!==null&&(D.lastEffect!==null&&(D.lastEffect.nextEffect=Gn.firstEffect),D.lastEffect=Gn.lastEffect);var O=Gn.effectTag;O>lu&&(D.lastEffect!==null?D.lastEffect.nextEffect=Gn:D.firstEffect=Gn,D.lastEffect=Gn)}}else{var z=Mm(Gn,q0);if(en&&(Gn.mode&ni)!==Ar){p1(Gn,!1);for(var G=Gn.actualDuration,ne=Gn.child;ne!==null;)G+=ne.actualDuration,ne=ne.sibling;Gn.actualDuration=G}if(z!==null)return ho(Gn),z.effectTag&=Kl,z;Zr(Gn),D!==null&&(D.firstEffect=D.lastEffect=null,D.effectTag|=P0)}var se=Gn.sibling;if(se!==null)return se;Gn=D}while(Gn!==null);return k0===bf&&(k0=Tp),null}function Mp(c){var d=c.expirationTime,D=c.childExpirationTime;return d>D?d:D}function ty(c){if(!(q0!==Di&&c.childExpirationTime===Di)){var d=ft;if(en&&(c.mode&ni)!==Ar){for(var D=c.actualDuration,C=c.selfBaseDuration,O=c.alternate===null||c.child!==c.alternate.child,z=c.child;z!==null;){var G=z.expirationTime,ne=z.childExpirationTime;G>d&&(d=G),ne>d&&(d=ne),O&&(D+=z.actualDuration),C+=z.treeBaseDuration,z=z.sibling}c.actualDuration=D,c.treeBaseDuration=C}else for(var se=c.child;se!==null;){var Ue=se.expirationTime,Xe=se.childExpirationTime;Ue>d&&(d=Ue),Xe>d&&(d=Xe),se=se.sibling}c.childExpirationTime=d}}function l0(c){var d=Qt();return Sn(Li,kp.bind(null,c,d)),null}function kp(c,d){do tf();while(Mc!==null);if(ay(),(nr&(To|As))!==yu)throw Error("Should not already be working.");var D=c.finishedWork,C=c.finishedExpirationTime;if(D===null)return null;if(c.finishedWork=null,c.finishedExpirationTime=ft,D===c.current)throw Error("Cannot commit the same tree as before. This error is likely caused by a bug in React. Please file an issue.");c.callbackNode=null,c.callbackExpirationTime=ft,c.callbackPriority=_0,c.nextKnownPendingLevel=ft,eo();var O=Mp(D);q4(c,C,O),c===ml&&(ml=null,Gn=null,q0=ft);var z;if(D.effectTag>lu?D.lastEffect!==null?(D.lastEffect.nextEffect=D,z=D.firstEffect):z=D:z=D.firstEffect,z!==null){var G=nr;nr|=As;var ne=Gd(c);Dp.current=null,xe(),Hn(c.containerInfo),dn=z;do if(pl(null,ny,null),tr()){if(dn===null)throw Error("Should be working on an effect.");var se=Qs();Hf(dn,se),dn=dn.nextEffect}while(dn!==null);tt(),en&&Nh(),Ke(),dn=z;do if(pl(null,ry,null,c,d),tr()){if(dn===null)throw Error("Should be working on an effect.");var Ue=Qs();Hf(dn,Ue),dn=dn.nextEffect}while(dn!==null);Yt(),qr(c.containerInfo),c.current=D,Kt(),dn=z;do if(pl(null,Np,null,c,C),tr()){if(dn===null)throw Error("Should be working on an effect.");var Xe=Qs();Hf(dn,Xe),dn=dn.nextEffect}while(dn!==null);pr(),dn=null,fe(),In&&Yd(ne),nr=G}else c.current=D,xe(),tt(),en&&Nh(),Ke(),Yt(),Kt(),pr();to();var ht=Oc;if(Oc)Oc=!1,Mc=c,xp=C,A1=d;else for(dn=z;dn!==null;){var Lt=dn.nextEffect;dn.nextEffect=null,dn=Lt}var Gt=c.firstPendingTime;if(Gt!==ft){if(In){if(Uf!==null){var Ht=Uf;Uf=null;for(var yn=0;ynKn?Kn:A1;return A1=_0,Sn(c,Lp)}}function Lp(){if(Mc===null)return!1;var c=Mc,d=xp;if(Mc=null,xp=ft,(nr&(To|As))!==yu)throw Error("Cannot flush passive effects while already rendering.");var D=nr;nr|=As;for(var C=Gd(c),O=c.current.firstEffect;O!==null;){{if(wt(O),pl(null,xa,null,O),tr()){if(O===null)throw Error("Should be working on an effect.");var z=Qs();Hf(O,z)}ut()}var G=O.nextEffect;O.nextEffect=null,O=G}return In&&(Yd(C),pe(c,d)),nr=D,Ut(),O1=Mc===null?0:O1+1,!0}function Fp(c){return Ac!==null&&Ac.has(c)}function Pp(c){Ac===null?Ac=new Set([c]):Ac.add(c)}function iy(c){jd||(jd=!0,zd=c)}var uy=iy;function Cv(c,d,D){var C=dp(D,d),O=sv(c,C,Un);Va(c,O);var z=qd(c,Un);z!==null&&(Wo(z),W(z,Un))}function Hf(c,d){if(c.tag===j){Cv(c,c,d);return}for(var D=c.return;D!==null;){if(D.tag===j){Cv(D,c,d);return}else if(D.tag===k){var C=D.type,O=D.stateNode;if(typeof C.getDerivedStateFromError=="function"||typeof O.componentDidCatch=="function"&&!Fp(O)){var z=dp(d,c),G=av(D,z,Un);Va(D,G);var ne=qd(D,Un);ne!==null&&(Wo(ne),W(ne,Un));return}}D=D.return}}function Ip(c,d,D){var C=c.pingCache;if(C!==null&&C.delete(d),ml===c&&q0===D){k0===x1||k0===C1&&Ul===Un&&mt()-CpHm)throw kc=0,Hd=null,Error("Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.");O1>cv&&(O1=0,Xt(!1,"Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render."))}function ay(){Al.flushLegacyContextWarning(),mi&&Al.flushPendingUnsafeLifecycleWarnings()}function Rv(){var c=!0;yf(Bf,c),Bf=null}function Up(){var c=!1;yf(Bf,c),Bf=null}function jp(c,d){Hr&&ml!==null&&d>q0&&(Bf=c)}var Qd=null;function fy(c){{var d=c.tag;if(d!==j&&d!==k&&d!==F&&d!==ae&&d!==ve&&d!==ue)return;var D=qt(c.type)||"ReactComponent";if(Qd!==null){if(Qd.has(D))return;Qd.add(D)}else Qd=new Set([D]);Ve(!1,"Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s",d===k?"the componentWillUnmount method":"a useEffect cleanup function",Cr(c))}}var N1;if(X0){var cy=null;N1=function(c,d,D){var C=Gg(cy,d);try{return lp(c,d,D)}catch(z){if(z!==null&&typeof z=="object"&&typeof z.then=="function")throw z;if(yt(),_d(),nv(d),Gg(d,C),en&&d.mode&ni&&H2(d),pl(null,lp,null,c,d,D),tr()){var O=Qs();throw O}else throw z}}}else N1=lp;var Av=!1,Ov=!1;function dy(c){if(c.tag===k)switch(Nr){case"getChildContext":if(Ov)return;Ve(!1,"setState(...): Cannot call setState() inside getChildContext()"),Ov=!0;break;case"render":if(Av)return;Ve(!1,"Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state."),Av=!0;break}}var qf={current:!1};function zp(c){vs===!0&&vl.current===!0&&qf.current!==!0&&Ve(!1,`It looks like you're using the wrong act() around your test interactions. +Be sure to use the matching version of act() corresponding to your renderer: + +// for react-dom: +import {act} from 'react-dom/test-utils'; +// ... +act(() => ...); + +// for react-test-renderer: +import TestRenderer from 'react-test-renderer'; +const {act} = TestRenderer; +// ... +act(() => ...);%s`,Cr(c))}function Mv(c){vs===!0&&(c.mode&mr)!==Ar&&vl.current===!1&&qf.current===!1&&Ve(!1,`An update to %s ran an effect, but was not wrapped in act(...). + +When testing, code that causes React state updates should be wrapped into act(...): + +act(() => { + /* fire events that update state */ +}); +/* assert on the output */ + +This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act%s`,qt(c.type),Cr(c))}function py(c){vs===!0&&nr===yu&&vl.current===!1&&qf.current===!1&&Ve(!1,`An update to %s inside a test was not wrapped in act(...). + +When testing, code that causes React state updates should be wrapped into act(...): + +act(() => { + /* fire events that update state */ +}); +/* assert on the output */ + +This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act%s`,qt(c.type),Cr(c))}var hy=py,Hp=!1;function vy(c){Hp===!1&&t.unstable_flushAllWithoutAsserting===void 0&&(c.mode&K||c.mode&ti?(Hp=!0,Ve(!1,`In Concurrent or Sync modes, the "scheduler" module needs to be mocked to guarantee consistent behaviour across tests and browsers. For example, with jest: +jest.mock('scheduler', () => require('scheduler/unstable_mock')); + +For more info, visit https://fb.me/react-mock-scheduler`)):eu===!0&&(Hp=!0,Ve(!1,`Starting from React v17, the "scheduler" module will need to be mocked to guarantee consistent behaviour across tests and browsers. For example, with jest: +jest.mock('scheduler', () => require('scheduler/unstable_mock')); + +For more info, visit https://fb.me/react-mock-scheduler`)))}var Zs=null;function my(c){{var d=Qt();if((c.mode&ti)!==xi&&(d===ei||d===Li))for(var D=c;D!==null;){var C=D.alternate;if(C!==null)switch(D.tag){case k:var O=C.updateQueue;if(O!==null)for(var z=O.firstUpdate;z!==null;){var G=z.priority;if(G===ei||G===Li){Zs===null?Zs=new Set([qt(D.type)]):Zs.add(qt(D.type));break}z=z.next}break;case F:case ae:case ue:if(D.memoizedState!==null&&D.memoizedState.baseUpdate!==null)for(var ne=D.memoizedState.baseUpdate;ne!==null;){var se=ne.priority;if(se===ei||se===Li){Zs===null?Zs=new Set([qt(D.type)]):Zs.add(qt(D.type));break}if(ne.next===D.memoizedState.baseUpdate)break;ne=ne.next}break;default:break}D=D.return}}}function p(){if(Zs!==null){var c=[];Zs.forEach(function(d){return c.push(d)}),Zs=null,c.length>0&&Ve(!1,`%s triggered a user-blocking update that suspended. + +The fix is to split the update into multiple parts: a user-blocking update to provide immediate feedback, and another update that triggers the bulk of the changes. + +Refer to the documentation for useTransition to learn how to implement this pattern.`,c.sort().join(", "))}}function m(c,d){return d*1e3+c.interactionThreadID}function R(c){!In||(Uf===null?Uf=[c]:Uf.push(c))}function I(c,d,D){if(!!In&&D.size>0){var C=c.pendingInteractionMap,O=C.get(d);O!=null?D.forEach(function(ne){O.has(ne)||ne.__count++,O.add(ne)}):(C.set(d,new Set(D)),D.forEach(function(ne){ne.__count++}));var z=N.__subscriberRef.current;if(z!==null){var G=m(c,d);z.onWorkScheduled(D,G)}}}function W(c,d){!In||I(c,d,N.__interactionsRef.current)}function te(c,d){if(!!In){var D=new Set;if(c.pendingInteractionMap.forEach(function(z,G){G>=d&&z.forEach(function(ne){return D.add(ne)})}),c.memoizedInteractions=D,D.size>0){var C=N.__subscriberRef.current;if(C!==null){var O=m(c,d);try{C.onWorkStarted(D,O)}catch(z){_n(Li,function(){throw z})}}}}}function pe(c,d){if(!!In){var D=c.firstPendingTime,C;try{if(C=N.__subscriberRef.current,C!==null&&c.memoizedInteractions.size>0){var O=m(c,d);C.onWorkStopped(c.memoizedInteractions,O)}}catch(G){_n(Li,function(){throw G})}finally{var z=c.pendingInteractionMap;z.forEach(function(G,ne){ne>D&&(z.delete(ne),G.forEach(function(se){if(se.__count--,C!==null&&se.__count===0)try{C.onInteractionScheduledWorkCompleted(se)}catch(Ue){_n(Li,function(){throw Ue})}}))})}}}var Ee=null,be=null,Dt=!1,Tt=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u";function Ot(c){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u")return!1;var d=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(d.isDisabled)return!0;if(!d.supportsFiber)return Ve(!1,"The installed version of React DevTools is too old and will not work with the current version of React. Please update React DevTools. https://fb.me/react-devtools"),!0;try{var D=d.inject(c);Ee=function(C,O){try{var z=(C.current.effectTag&Kr)===Kr;if(en){var G=Nc(),ne=nd(G,O);d.onCommitFiberRoot(D,C,ne,z)}else d.onCommitFiberRoot(D,C,void 0,z)}catch(se){Dt||(Dt=!0,Ve(!1,"React DevTools encountered an error: %s",se))}},be=function(C){try{d.onCommitFiberUnmount(D,C)}catch(O){Dt||(Dt=!0,Ve(!1,"React DevTools encountered an error: %s",O))}}}catch(C){Ve(!1,"React DevTools encountered an error: %s.",C)}return!0}function on(c,d){typeof Ee=="function"&&Ee(c,d)}function Mn(c){typeof be=="function"&&be(c)}var rr;{rr=!1;try{var br=Object.preventExtensions({}),ar=new Map([[br,null]]),ri=new Set([br]);ar.set(0,0),ri.add(0)}catch{rr=!0}}var fi=1;function zl(c,d,D,C){this.tag=c,this.key=D,this.elementType=null,this.type=null,this.stateNode=null,this.return=null,this.child=null,this.sibling=null,this.index=0,this.ref=null,this.pendingProps=d,this.memoizedProps=null,this.updateQueue=null,this.memoizedState=null,this.dependencies=null,this.mode=C,this.effectTag=xi,this.nextEffect=null,this.firstEffect=null,this.lastEffect=null,this.expirationTime=ft,this.childExpirationTime=ft,this.alternate=null,en&&(this.actualDuration=Number.NaN,this.actualStartTime=Number.NaN,this.selfBaseDuration=Number.NaN,this.treeBaseDuration=Number.NaN,this.actualDuration=0,this.actualStartTime=-1,this.selfBaseDuration=0,this.treeBaseDuration=0),Hr&&(this._debugID=fi++,this._debugIsCurrentlyTiming=!1),this._debugSource=null,this._debugOwner=null,this._debugNeedsRemount=!1,this._debugHookTypes=null,!rr&&typeof Object.preventExtensions=="function"&&Object.preventExtensions(this)}var Zi=function(c,d,D,C){return new zl(c,d,D,C)};function so(c){var d=c.prototype;return!!(d&&d.isReactComponent)}function s0(c){return typeof c=="function"&&!so(c)&&c.defaultProps===void 0}function Os(c){if(typeof c=="function")return so(c)?k:F;if(c!=null){var d=c.$$typeof;if(d===On)return ae;if(d===Vt)return ve}return x}function Co(c,d,D){var C=c.alternate;C===null?(C=Zi(c.tag,d,c.key,c.mode),C.elementType=c.elementType,C.type=c.type,C.stateNode=c.stateNode,C._debugID=c._debugID,C._debugSource=c._debugSource,C._debugOwner=c._debugOwner,C._debugHookTypes=c._debugHookTypes,C.alternate=c,c.alternate=C):(C.pendingProps=d,C.effectTag=xi,C.nextEffect=null,C.firstEffect=null,C.lastEffect=null,en&&(C.actualDuration=0,C.actualStartTime=-1)),C.childExpirationTime=c.childExpirationTime,C.expirationTime=c.expirationTime,C.child=c.child,C.memoizedProps=c.memoizedProps,C.memoizedState=c.memoizedState,C.updateQueue=c.updateQueue;var O=c.dependencies;switch(C.dependencies=O===null?null:{expirationTime:O.expirationTime,firstContext:O.firstContext,responders:O.responders},C.sibling=c.sibling,C.index=c.index,C.ref=c.ref,en&&(C.selfBaseDuration=c.selfBaseDuration,C.treeBaseDuration=c.treeBaseDuration),C._debugNeedsRemount=c._debugNeedsRemount,C.tag){case x:case F:case ue:C.type=n0(c.type);break;case k:C.type=j0(c.type);break;case ae:C.type=Df(c.type);break;default:break}return C}function kv(c,d){c.effectTag&=vi,c.nextEffect=null,c.firstEffect=null,c.lastEffect=null;var D=c.alternate;if(D===null)c.childExpirationTime=ft,c.expirationTime=d,c.child=null,c.memoizedProps=null,c.memoizedState=null,c.updateQueue=null,c.dependencies=null,en&&(c.selfBaseDuration=0,c.treeBaseDuration=0);else{c.childExpirationTime=D.childExpirationTime,c.expirationTime=D.expirationTime,c.child=D.child,c.memoizedProps=D.memoizedProps,c.memoizedState=D.memoizedState,c.updateQueue=D.updateQueue;var C=D.dependencies;c.dependencies=C===null?null:{expirationTime:C.expirationTime,firstContext:C.firstContext,responders:C.responders},en&&(c.selfBaseDuration=D.selfBaseDuration,c.treeBaseDuration=D.treeBaseDuration)}return c}function F4(c){var d;return c===O0?d=ti|K|mr:c===B0?d=K|mr:d=Ar,en&&Tt&&(d|=ni),Zi(j,null,null,d)}function yy(c,d,D,C,O,z){var G,ne=x,se=c;if(typeof c=="function")so(c)?(ne=k,se=j0(se)):se=n0(se);else if(typeof c=="string")ne=V;else{e:switch(c){case le:return nf(D.children,O,z,d);case an:ne=me,O|=ti|K|mr;break;case He:ne=me,O|=mr;break;case dt:return I4(D,O,z,d);case lr:return b4(D,O,z,d);case ln:return B4(D,O,z,d);default:{if(typeof c=="object"&&c!==null)switch(c.$$typeof){case At:ne=ge;break e;case nn:ne=De;break e;case On:ne=ae,se=Df(se);break e;case Vt:ne=ve;break e;case Er:ne=Ae,se=null;break e;case S:if(Wt)return Vg(c,D,O,z,d);break;case Xn:if(Ru)return P4(c,D,O,z,d)}var Ue="";{(c===void 0||typeof c=="object"&&c!==null&&Object.keys(c).length===0)&&(Ue+=" You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");var Xe=C?qt(C.type):null;Xe&&(Ue+=` + +Check the render method of \``+Xe+"`.")}throw Error("Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: "+(c==null?c:typeof c)+"."+Ue)}}}return G=Zi(ne,D,d,O),G.elementType=c,G.type=se,G.expirationTime=z,G}function gy(c,d,D){var C=null;C=c._owner;var O=c.type,z=c.key,G=c.props,ne=yy(O,z,G,C,d,D);return ne._debugSource=c._source,ne._debugOwner=c._owner,ne}function nf(c,d,D,C){var O=Zi(y,c,C,d);return O.expirationTime=D,O}function Vg(c,d,D,C,O){var z=Zi(_t,d,O,D);return z.elementType=c,z.type=c,z.expirationTime=C,z}function P4(c,d,D,C,O){var z=Zi(Qe,d,O,D);return z.type=c,z.elementType=c,z.expirationTime=C,z}function I4(c,d,D,C){(typeof c.id!="string"||typeof c.onRender!="function")&&Ve(!1,'Profiler must specify an "id" string and "onRender" function as props');var O=Zi(we,c,C,d|ni);return O.elementType=dt,O.type=dt,O.expirationTime=D,O}function b4(c,d,D,C){var O=Zi(he,c,C,d);return O.type=lr,O.elementType=lr,O.expirationTime=D,O}function B4(c,d,D,C){var O=Zi(gt,c,C,d);return O.type=ln,O.elementType=ln,O.expirationTime=D,O}function _y(c,d,D){var C=Zi(re,c,null,d);return C.expirationTime=D,C}function U4(){var c=Zi(V,null,null,Ar);return c.elementType="DELETED",c.type="DELETED",c}function j4(c){var d=Zi(We,null,null,Ar);return d.stateNode=c,d}function Ey(c,d,D){var C=c.children!==null?c.children:[],O=Zi(q,C,c.key,d);return O.expirationTime=D,O.stateNode={containerInfo:c.containerInfo,pendingChildren:null,implementation:c.implementation},O}function Gg(c,d){return c===null&&(c=Zi(x,null,null,Ar)),c.tag=d.tag,c.key=d.key,c.elementType=d.elementType,c.type=d.type,c.stateNode=d.stateNode,c.return=d.return,c.child=d.child,c.sibling=d.sibling,c.index=d.index,c.ref=d.ref,c.pendingProps=d.pendingProps,c.memoizedProps=d.memoizedProps,c.updateQueue=d.updateQueue,c.memoizedState=d.memoizedState,c.dependencies=d.dependencies,c.mode=d.mode,c.effectTag=d.effectTag,c.nextEffect=d.nextEffect,c.firstEffect=d.firstEffect,c.lastEffect=d.lastEffect,c.expirationTime=d.expirationTime,c.childExpirationTime=d.childExpirationTime,c.alternate=d.alternate,en&&(c.actualDuration=d.actualDuration,c.actualStartTime=d.actualStartTime,c.selfBaseDuration=d.selfBaseDuration,c.treeBaseDuration=d.treeBaseDuration),c._debugID=d._debugID,c._debugSource=d._debugSource,c._debugOwner=d._debugOwner,c._debugIsCurrentlyTiming=d._debugIsCurrentlyTiming,c._debugNeedsRemount=d._debugNeedsRemount,c._debugHookTypes=d._debugHookTypes,c}function z4(c,d,D){this.tag=d,this.current=null,this.containerInfo=c,this.pendingChildren=null,this.pingCache=null,this.finishedExpirationTime=ft,this.finishedWork=null,this.timeoutHandle=nl,this.context=null,this.pendingContext=null,this.hydrate=D,this.callbackNode=null,this.callbackPriority=_0,this.firstPendingTime=ft,this.firstSuspendedTime=ft,this.lastSuspendedTime=ft,this.nextKnownPendingLevel=ft,this.lastPingedTime=ft,this.lastExpiredTime=ft,In&&(this.interactionThreadID=N.unstable_getThreadID(),this.memoizedInteractions=new Set,this.pendingInteractionMap=new Map),Yi&&(this.hydrationCallbacks=null)}function H4(c,d,D,C){var O=new z4(c,d,D);Yi&&(O.hydrationCallbacks=C);var z=F4(d);return O.current=z,z.stateNode=O,O}function Yg(c,d){var D=c.firstSuspendedTime,C=c.lastSuspendedTime;return D!==ft&&D>=d&&C<=d}function Wf(c,d){var D=c.firstSuspendedTime,C=c.lastSuspendedTime;Dd||D===ft)&&(c.lastSuspendedTime=d),d<=c.lastPingedTime&&(c.lastPingedTime=ft),d<=c.lastExpiredTime&&(c.lastExpiredTime=ft)}function Kg(c,d){var D=c.firstPendingTime;d>D&&(c.firstPendingTime=d);var C=c.firstSuspendedTime;C!==ft&&(d>=C?c.firstSuspendedTime=c.lastSuspendedTime=c.nextKnownPendingLevel=ft:d>=c.lastSuspendedTime&&(c.lastSuspendedTime=d+1),d>c.nextKnownPendingLevel&&(c.nextKnownPendingLevel=d))}function q4(c,d,D){c.firstPendingTime=D,d<=c.lastSuspendedTime?c.firstSuspendedTime=c.lastSuspendedTime=c.nextKnownPendingLevel=ft:d<=c.firstSuspendedTime&&(c.firstSuspendedTime=d-1),d<=c.lastPingedTime&&(c.lastPingedTime=ft),d<=c.lastExpiredTime&&(c.lastExpiredTime=ft)}function qp(c,d){var D=c.lastExpiredTime;(D===ft||D>d)&&(c.lastExpiredTime=d)}var W4={debugTool:null},Nv=W4,Dy,wy;Dy=!1,wy={};function V4(c){if(!c)return xn;var d=Pt(c),D=xl(d);if(d.tag===k){var C=d.type;if(Xi(C))return A0(d,C,D)}return D}function Sy(c){var d=Pt(c);if(d===void 0)throw typeof c.render=="function"?Error("Unable to find node on an unmounted component."):Error("Argument appears to not be a ReactComponent. Keys: "+Object.keys(c));var D=I0(d);return D===null?null:D.stateNode}function G4(c,d){{var D=Pt(c);if(D===void 0)throw typeof c.render=="function"?Error("Unable to find node on an unmounted component."):Error("Argument appears to not be a ReactComponent. Keys: "+Object.keys(c));var C=I0(D);if(C===null)return null;if(C.mode&mr){var O=qt(D.type)||"Component";wy[O]||(wy[O]=!0,D.mode&mr?Ve(!1,"%s is deprecated in StrictMode. %s was passed an instance of %s which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node%s",d,d,O,Cr(C)):Ve(!1,"%s is deprecated in StrictMode. %s was passed an instance of %s which renders StrictMode children. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://fb.me/react-strict-mode-find-node%s",d,d,O,Cr(C)))}return C.stateNode}return Sy(c)}function Y4(c,d,D,C){return H4(c,d,D,C)}function Xg(c,d,D,C){var O=d.current,z=jl();typeof jest<"u"&&(vy(O),zp(O));var G=_o(),ne=jf(z,O,G);Nv.debugTool&&(O.alternate===null?Nv.debugTool.onMountContainer(d):c===null?Nv.debugTool.onUnmountContainer(d):Nv.debugTool.onUpdateContainer(d));var se=V4(D);d.context===null?d.context=se:d.pendingContext=se,Nr==="render"&&Rn!==null&&!Dy&&(Dy=!0,Ve(!1,`Render methods should be a pure function of props and state; triggering nested component updates from render is not allowed. If necessary, trigger nested updates in componentDidUpdate. + +Check the render method of %s.`,qt(Rn.type)||"Unknown"));var Ue=Tu(ne,G);return Ue.payload={element:c},C=C===void 0?null:C,C!==null&&(typeof C!="function"&&Ve(!1,"render(...): Expected the last optional `callback` argument to be a function. Instead received: %s.",C),Ue.callback=C),Va(O,Ue),yl(O,ne),ne}function K4(c){var d=c.current;if(!d.child)return null;switch(d.child.tag){case V:return Lo(d.child.stateNode);default:return d.child.stateNode}}function X4(c){switch(c.tag){case j:var d=c.stateNode;d.hydrate&&Gm(d,d.firstPendingTime);break;case he:Rp(function(){return yl(c,Un)});var D=Ua(jl());Lv(c,D);break}}function Qg(c,d){var D=c.memoizedState;D!==null&&D.dehydrated!==null&&D.retryTime=d.length)return C;var O=d[D],z=Array.isArray(c)?c.slice():f({},c);return z[O]=xy(c[O],d,D+1,C),z},n_=function(c,d,D){return xy(c,d,0,D)};Zg=function(c,d,D,C){for(var O=c.memoizedState;O!==null&&d>0;)O=O.next,d--;if(O!==null){var z=n_(O.memoizedState,D,C);O.memoizedState=z,O.baseState=z,c.memoizedProps=f({},c.memoizedProps),yl(c,Un)}},$g=function(c,d,D){c.pendingProps=n_(c.memoizedProps,d,D),c.alternate&&(c.alternate.pendingProps=c.pendingProps),yl(c,Un)},e_=function(c){yl(c,Un)},t_=function(c){Cy=c}}function $4(c){var d=c.findFiberByHostInstance,D=it.ReactCurrentDispatcher;return Ot(f({},c,{overrideHookState:Zg,overrideProps:$g,setSuspenseHandler:t_,scheduleUpdate:e_,currentDispatcherRef:D,findHostInstanceByFiber:function(C){var O=I0(C);return O===null?null:O.stateNode},findFiberByHostInstance:function(C){return d?d(C):null},findHostInstancesForRefresh:ud,scheduleRefresh:Ol,scheduleRoot:Ts,setRefreshHandler:qa,getCurrentFiber:function(){return Rn}}))}var r_=Object.freeze({createContainer:Y4,updateContainer:Xg,batchedEventUpdates:Qm,batchedUpdates:Xm,unbatchedUpdates:Jm,deferredUpdates:Ym,syncUpdates:pv,discreteUpdates:hv,flushDiscreteUpdates:dv,flushControlled:Zm,flushSync:Rp,flushPassiveEffects:tf,IsThisRendererActing:qf,getPublicRootInstance:K4,attemptSynchronousHydration:X4,attemptUserBlockingHydration:Q4,attemptContinuousHydration:Ty,attemptHydrationAtCurrentPriority:J4,findHostInstance:Sy,findHostInstanceWithWarning:G4,findHostInstanceWithNoPortals:Z4,shouldSuspend:Jg,injectIntoDevTools:$4}),eE=r_.default||r_;Yy.exports=eE;var tE=Yy.exports;return Yy.exports=o,tE})});var YS=nt((DH,mD)=>{"use strict";process.env.NODE_ENV==="production"?mD.exports=HS():mD.exports=GS()});var XS=nt((wH,KS)=>{"use strict";var EP={ALIGN_COUNT:8,ALIGN_AUTO:0,ALIGN_FLEX_START:1,ALIGN_CENTER:2,ALIGN_FLEX_END:3,ALIGN_STRETCH:4,ALIGN_BASELINE:5,ALIGN_SPACE_BETWEEN:6,ALIGN_SPACE_AROUND:7,DIMENSION_COUNT:2,DIMENSION_WIDTH:0,DIMENSION_HEIGHT:1,DIRECTION_COUNT:3,DIRECTION_INHERIT:0,DIRECTION_LTR:1,DIRECTION_RTL:2,DISPLAY_COUNT:2,DISPLAY_FLEX:0,DISPLAY_NONE:1,EDGE_COUNT:9,EDGE_LEFT:0,EDGE_TOP:1,EDGE_RIGHT:2,EDGE_BOTTOM:3,EDGE_START:4,EDGE_END:5,EDGE_HORIZONTAL:6,EDGE_VERTICAL:7,EDGE_ALL:8,EXPERIMENTAL_FEATURE_COUNT:1,EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS:0,FLEX_DIRECTION_COUNT:4,FLEX_DIRECTION_COLUMN:0,FLEX_DIRECTION_COLUMN_REVERSE:1,FLEX_DIRECTION_ROW:2,FLEX_DIRECTION_ROW_REVERSE:3,JUSTIFY_COUNT:6,JUSTIFY_FLEX_START:0,JUSTIFY_CENTER:1,JUSTIFY_FLEX_END:2,JUSTIFY_SPACE_BETWEEN:3,JUSTIFY_SPACE_AROUND:4,JUSTIFY_SPACE_EVENLY:5,LOG_LEVEL_COUNT:6,LOG_LEVEL_ERROR:0,LOG_LEVEL_WARN:1,LOG_LEVEL_INFO:2,LOG_LEVEL_DEBUG:3,LOG_LEVEL_VERBOSE:4,LOG_LEVEL_FATAL:5,MEASURE_MODE_COUNT:3,MEASURE_MODE_UNDEFINED:0,MEASURE_MODE_EXACTLY:1,MEASURE_MODE_AT_MOST:2,NODE_TYPE_COUNT:2,NODE_TYPE_DEFAULT:0,NODE_TYPE_TEXT:1,OVERFLOW_COUNT:3,OVERFLOW_VISIBLE:0,OVERFLOW_HIDDEN:1,OVERFLOW_SCROLL:2,POSITION_TYPE_COUNT:2,POSITION_TYPE_RELATIVE:0,POSITION_TYPE_ABSOLUTE:1,PRINT_OPTIONS_COUNT:3,PRINT_OPTIONS_LAYOUT:1,PRINT_OPTIONS_STYLE:2,PRINT_OPTIONS_CHILDREN:4,UNIT_COUNT:4,UNIT_UNDEFINED:0,UNIT_POINT:1,UNIT_PERCENT:2,UNIT_AUTO:3,WRAP_COUNT:3,WRAP_NO_WRAP:0,WRAP_WRAP:1,WRAP_WRAP_REVERSE:2};KS.exports=EP});var $S=nt((SH,ZS)=>{"use strict";var DP=Object.assign||function(o){for(var l=1;l"}}]),o}(),QS=function(){H_(o,null,[{key:"fromJS",value:function(f){var h=f.width,E=f.height;return new o(h,E)}}]);function o(l,f){gD(this,o),this.width=l,this.height=f}return H_(o,[{key:"fromJS",value:function(f){f(this.width,this.height)}},{key:"toString",value:function(){return""}}]),o}(),JS=function(){function o(l,f){gD(this,o),this.unit=l,this.value=f}return H_(o,[{key:"fromJS",value:function(f){f(this.unit,this.value)}},{key:"toString",value:function(){switch(this.unit){case tc.UNIT_POINT:return String(this.value);case tc.UNIT_PERCENT:return this.value+"%";case tc.UNIT_AUTO:return"auto";default:return this.value+"?"}}},{key:"valueOf",value:function(){return this.value}}]),o}();ZS.exports=function(o,l){function f(N,F,k){var x=N[F];N[F]=function(){for(var j=arguments.length,q=Array(j),V=0;V1?q-1:0),re=1;re1&&arguments[1]!==void 0?arguments[1]:NaN,k=arguments.length>2&&arguments[2]!==void 0?arguments[2]:NaN,x=arguments.length>3&&arguments[3]!==void 0?arguments[3]:tc.DIRECTION_LTR;return N.call(this,F,k,x)}),DP({Config:l.Config,Node:l.Node,Layout:o("Layout",wP),Size:o("Size",QS),Value:o("Value",JS),getInstanceCount:function(){return l.getInstanceCount.apply(l,arguments)}},tc)}});var eT=nt((exports,module)=>{(function(o,l){typeof define=="function"&&define.amd?define([],function(){return l}):typeof module=="object"&&module.exports?module.exports=l:(o.nbind=o.nbind||{}).init=l})(exports,function(Module,cb){typeof Module=="function"&&(cb=Module,Module={}),Module.onRuntimeInitialized=function(o,l){return function(){o&&o.apply(this,arguments);try{Module.ccall("nbind_init")}catch(f){l(f);return}l(null,{bind:Module._nbind_value,reflect:Module.NBind.reflect,queryType:Module.NBind.queryType,toggleLightGC:Module.toggleLightGC,lib:Module})}}(Module.onRuntimeInitialized,cb);var Module;Module||(Module=(typeof Module<"u"?Module:null)||{});var moduleOverrides={};for(var key in Module)Module.hasOwnProperty(key)&&(moduleOverrides[key]=Module[key]);var ENVIRONMENT_IS_WEB=!1,ENVIRONMENT_IS_WORKER=!1,ENVIRONMENT_IS_NODE=!1,ENVIRONMENT_IS_SHELL=!1;if(Module.ENVIRONMENT)if(Module.ENVIRONMENT==="WEB")ENVIRONMENT_IS_WEB=!0;else if(Module.ENVIRONMENT==="WORKER")ENVIRONMENT_IS_WORKER=!0;else if(Module.ENVIRONMENT==="NODE")ENVIRONMENT_IS_NODE=!0;else if(Module.ENVIRONMENT==="SHELL")ENVIRONMENT_IS_SHELL=!0;else throw new Error("The provided Module['ENVIRONMENT'] value is not valid. It must be one of: WEB|WORKER|NODE|SHELL.");else ENVIRONMENT_IS_WEB=typeof window=="object",ENVIRONMENT_IS_WORKER=typeof importScripts=="function",ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof hi=="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER,ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){Module.print||(Module.print=console.log),Module.printErr||(Module.printErr=console.warn);var nodeFS,nodePath;Module.read=function(l,f){nodeFS||(nodeFS={}("")),nodePath||(nodePath={}("")),l=nodePath.normalize(l);var h=nodeFS.readFileSync(l);return f?h:h.toString()},Module.readBinary=function(l){var f=Module.read(l,!0);return f.buffer||(f=new Uint8Array(f)),assert(f.buffer),f},Module.load=function(l){globalEval(read(l))},Module.thisProgram||(process.argv.length>1?Module.thisProgram=process.argv[1].replace(/\\/g,"/"):Module.thisProgram="unknown-program"),Module.arguments=process.argv.slice(2),typeof module<"u"&&(module.exports=Module),Module.inspect=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL)Module.print||(Module.print=print),typeof printErr<"u"&&(Module.printErr=printErr),typeof read<"u"?Module.read=read:Module.read=function(){throw"no read() available"},Module.readBinary=function(l){if(typeof readbuffer=="function")return new Uint8Array(readbuffer(l));var f=read(l,"binary");return assert(typeof f=="object"),f},typeof scriptArgs<"u"?Module.arguments=scriptArgs:typeof arguments<"u"&&(Module.arguments=arguments),typeof quit=="function"&&(Module.quit=function(o,l){quit(o)});else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(Module.read=function(l){var f=new XMLHttpRequest;return f.open("GET",l,!1),f.send(null),f.responseText},ENVIRONMENT_IS_WORKER&&(Module.readBinary=function(l){var f=new XMLHttpRequest;return f.open("GET",l,!1),f.responseType="arraybuffer",f.send(null),new Uint8Array(f.response)}),Module.readAsync=function(l,f,h){var E=new XMLHttpRequest;E.open("GET",l,!0),E.responseType="arraybuffer",E.onload=function(){E.status==200||E.status==0&&E.response?f(E.response):h()},E.onerror=h,E.send(null)},typeof arguments<"u"&&(Module.arguments=arguments),typeof console<"u")Module.print||(Module.print=function(l){console.log(l)}),Module.printErr||(Module.printErr=function(l){console.warn(l)});else{var TRY_USE_DUMP=!1;Module.print||(Module.print=TRY_USE_DUMP&&typeof dump<"u"?function(o){dump(o)}:function(o){})}ENVIRONMENT_IS_WORKER&&(Module.load=importScripts),typeof Module.setWindowTitle>"u"&&(Module.setWindowTitle=function(o){document.title=o})}else throw"Unknown runtime environment. Where are we?";function globalEval(o){eval.call(null,o)}!Module.load&&Module.read&&(Module.load=function(l){globalEval(Module.read(l))}),Module.print||(Module.print=function(){}),Module.printErr||(Module.printErr=Module.print),Module.arguments||(Module.arguments=[]),Module.thisProgram||(Module.thisProgram="./this.program"),Module.quit||(Module.quit=function(o,l){throw l}),Module.print=Module.print,Module.printErr=Module.printErr,Module.preRun=[],Module.postRun=[];for(var key in moduleOverrides)moduleOverrides.hasOwnProperty(key)&&(Module[key]=moduleOverrides[key]);moduleOverrides=void 0;var Runtime={setTempRet0:function(o){return tempRet0=o,o},getTempRet0:function(){return tempRet0},stackSave:function(){return STACKTOP},stackRestore:function(o){STACKTOP=o},getNativeTypeSize:function(o){switch(o){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(o[o.length-1]==="*")return Runtime.QUANTUM_SIZE;if(o[0]==="i"){var l=parseInt(o.substr(1));return assert(l%8===0),l/8}else return 0}}},getNativeFieldSize:function(o){return Math.max(Runtime.getNativeTypeSize(o),Runtime.QUANTUM_SIZE)},STACK_ALIGN:16,prepVararg:function(o,l){return l==="double"||l==="i64"?o&7&&(assert((o&7)===4),o+=4):assert((o&3)===0),o},getAlignSize:function(o,l,f){return!f&&(o=="i64"||o=="double")?8:o?Math.min(l||(o?Runtime.getNativeFieldSize(o):0),Runtime.QUANTUM_SIZE):Math.min(l,8)},dynCall:function(o,l,f){return f&&f.length?Module["dynCall_"+o].apply(null,[l].concat(f)):Module["dynCall_"+o].call(null,l)},functionPointers:[],addFunction:function(o){for(var l=0;l>2],f=(l+o+15|0)&-16;if(HEAP32[DYNAMICTOP_PTR>>2]=f,f>=TOTAL_MEMORY){var h=enlargeMemory();if(!h)return HEAP32[DYNAMICTOP_PTR>>2]=l,0}return l},alignMemory:function(o,l){var f=o=Math.ceil(o/(l||16))*(l||16);return f},makeBigInt:function(o,l,f){var h=f?+(o>>>0)+ +(l>>>0)*4294967296:+(o>>>0)+ +(l|0)*4294967296;return h},GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};Module.Runtime=Runtime;var ABORT=0,EXITSTATUS=0;function assert(o,l){o||abort("Assertion failed: "+l)}function getCFunc(ident){var func=Module["_"+ident];if(!func)try{func=eval("_"+ident)}catch(o){}return assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)"),func}var cwrap,ccall;(function(){var JSfuncs={stackSave:function(){Runtime.stackSave()},stackRestore:function(){Runtime.stackRestore()},arrayToC:function(o){var l=Runtime.stackAlloc(o.length);return writeArrayToMemory(o,l),l},stringToC:function(o){var l=0;if(o!=null&&o!==0){var f=(o.length<<2)+1;l=Runtime.stackAlloc(f),stringToUTF8(o,l,f)}return l}},toC={string:JSfuncs.stringToC,array:JSfuncs.arrayToC};ccall=function(l,f,h,E,t){var N=getCFunc(l),F=[],k=0;if(E)for(var x=0;x>0]=l;break;case"i8":HEAP8[o>>0]=l;break;case"i16":HEAP16[o>>1]=l;break;case"i32":HEAP32[o>>2]=l;break;case"i64":tempI64=[l>>>0,(tempDouble=l,+Math_abs(tempDouble)>=1?tempDouble>0?(Math_min(+Math_floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math_ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[o>>2]=tempI64[0],HEAP32[o+4>>2]=tempI64[1];break;case"float":HEAPF32[o>>2]=l;break;case"double":HEAPF64[o>>3]=l;break;default:abort("invalid type for setValue: "+f)}}Module.setValue=setValue;function getValue(o,l,f){switch(l=l||"i8",l.charAt(l.length-1)==="*"&&(l="i32"),l){case"i1":return HEAP8[o>>0];case"i8":return HEAP8[o>>0];case"i16":return HEAP16[o>>1];case"i32":return HEAP32[o>>2];case"i64":return HEAP32[o>>2];case"float":return HEAPF32[o>>2];case"double":return HEAPF64[o>>3];default:abort("invalid type for setValue: "+l)}return null}Module.getValue=getValue;var ALLOC_NORMAL=0,ALLOC_STACK=1,ALLOC_STATIC=2,ALLOC_DYNAMIC=3,ALLOC_NONE=4;Module.ALLOC_NORMAL=ALLOC_NORMAL,Module.ALLOC_STACK=ALLOC_STACK,Module.ALLOC_STATIC=ALLOC_STATIC,Module.ALLOC_DYNAMIC=ALLOC_DYNAMIC,Module.ALLOC_NONE=ALLOC_NONE;function allocate(o,l,f,h){var E,t;typeof o=="number"?(E=!0,t=o):(E=!1,t=o.length);var N=typeof l=="string"?l:null,F;if(f==ALLOC_NONE?F=h:F=[typeof _malloc=="function"?_malloc:Runtime.staticAlloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][f===void 0?ALLOC_STATIC:f](Math.max(t,N?1:l.length)),E){var h=F,k;for(assert((F&3)==0),k=F+(t&-4);h>2]=0;for(k=F+t;h>0]=0;return F}if(N==="i8")return o.subarray||o.slice?HEAPU8.set(o,F):HEAPU8.set(new Uint8Array(o),F),F;for(var x=0,j,q,V;x>0],f|=h,!(h==0&&!l||(E++,l&&E==l)););l||(l=E);var t="";if(f<128){for(var N=1024,F;l>0;)F=String.fromCharCode.apply(String,HEAPU8.subarray(o,o+Math.min(l,N))),t=t?t+F:F,o+=N,l-=N;return t}return Module.UTF8ToString(o)}Module.Pointer_stringify=Pointer_stringify;function AsciiToString(o){for(var l="";;){var f=HEAP8[o++>>0];if(!f)return l;l+=String.fromCharCode(f)}}Module.AsciiToString=AsciiToString;function stringToAscii(o,l){return writeAsciiToMemory(o,l,!1)}Module.stringToAscii=stringToAscii;var UTF8Decoder=typeof TextDecoder<"u"?new TextDecoder("utf8"):void 0;function UTF8ArrayToString(o,l){for(var f=l;o[f];)++f;if(f-l>16&&o.subarray&&UTF8Decoder)return UTF8Decoder.decode(o.subarray(l,f));for(var h,E,t,N,F,k,x="";;){if(h=o[l++],!h)return x;if(!(h&128)){x+=String.fromCharCode(h);continue}if(E=o[l++]&63,(h&224)==192){x+=String.fromCharCode((h&31)<<6|E);continue}if(t=o[l++]&63,(h&240)==224?h=(h&15)<<12|E<<6|t:(N=o[l++]&63,(h&248)==240?h=(h&7)<<18|E<<12|t<<6|N:(F=o[l++]&63,(h&252)==248?h=(h&3)<<24|E<<18|t<<12|N<<6|F:(k=o[l++]&63,h=(h&1)<<30|E<<24|t<<18|N<<12|F<<6|k))),h<65536)x+=String.fromCharCode(h);else{var j=h-65536;x+=String.fromCharCode(55296|j>>10,56320|j&1023)}}}Module.UTF8ArrayToString=UTF8ArrayToString;function UTF8ToString(o){return UTF8ArrayToString(HEAPU8,o)}Module.UTF8ToString=UTF8ToString;function stringToUTF8Array(o,l,f,h){if(!(h>0))return 0;for(var E=f,t=f+h-1,N=0;N=55296&&F<=57343&&(F=65536+((F&1023)<<10)|o.charCodeAt(++N)&1023),F<=127){if(f>=t)break;l[f++]=F}else if(F<=2047){if(f+1>=t)break;l[f++]=192|F>>6,l[f++]=128|F&63}else if(F<=65535){if(f+2>=t)break;l[f++]=224|F>>12,l[f++]=128|F>>6&63,l[f++]=128|F&63}else if(F<=2097151){if(f+3>=t)break;l[f++]=240|F>>18,l[f++]=128|F>>12&63,l[f++]=128|F>>6&63,l[f++]=128|F&63}else if(F<=67108863){if(f+4>=t)break;l[f++]=248|F>>24,l[f++]=128|F>>18&63,l[f++]=128|F>>12&63,l[f++]=128|F>>6&63,l[f++]=128|F&63}else{if(f+5>=t)break;l[f++]=252|F>>30,l[f++]=128|F>>24&63,l[f++]=128|F>>18&63,l[f++]=128|F>>12&63,l[f++]=128|F>>6&63,l[f++]=128|F&63}}return l[f]=0,f-E}Module.stringToUTF8Array=stringToUTF8Array;function stringToUTF8(o,l,f){return stringToUTF8Array(o,HEAPU8,l,f)}Module.stringToUTF8=stringToUTF8;function lengthBytesUTF8(o){for(var l=0,f=0;f=55296&&h<=57343&&(h=65536+((h&1023)<<10)|o.charCodeAt(++f)&1023),h<=127?++l:h<=2047?l+=2:h<=65535?l+=3:h<=2097151?l+=4:h<=67108863?l+=5:l+=6}return l}Module.lengthBytesUTF8=lengthBytesUTF8;var UTF16Decoder=typeof TextDecoder<"u"?new TextDecoder("utf-16le"):void 0;function demangle(o){var l=Module.___cxa_demangle||Module.__cxa_demangle;if(l){try{var f=o.substr(1),h=lengthBytesUTF8(f)+1,E=_malloc(h);stringToUTF8(f,E,h);var t=_malloc(4),N=l(E,0,0,t);if(getValue(t,"i32")===0&&N)return Pointer_stringify(N)}catch{}finally{E&&_free(E),t&&_free(t),N&&_free(N)}return o}return Runtime.warnOnce("warning: build with -s DEMANGLE_SUPPORT=1 to link in libcxxabi demangling"),o}function demangleAll(o){var l=/__Z[\w\d_]+/g;return o.replace(l,function(f){var h=demangle(f);return f===h?f:f+" ["+h+"]"})}function jsStackTrace(){var o=new Error;if(!o.stack){try{throw new Error(0)}catch(l){o=l}if(!o.stack)return"(no stack trace available)"}return o.stack.toString()}function stackTrace(){var o=jsStackTrace();return Module.extraStackTrace&&(o+=` +`+Module.extraStackTrace()),demangleAll(o)}Module.stackTrace=stackTrace;var HEAP,buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferViews(){Module.HEAP8=HEAP8=new Int8Array(buffer),Module.HEAP16=HEAP16=new Int16Array(buffer),Module.HEAP32=HEAP32=new Int32Array(buffer),Module.HEAPU8=HEAPU8=new Uint8Array(buffer),Module.HEAPU16=HEAPU16=new Uint16Array(buffer),Module.HEAPU32=HEAPU32=new Uint32Array(buffer),Module.HEAPF32=HEAPF32=new Float32Array(buffer),Module.HEAPF64=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed,STACK_BASE,STACKTOP,STACK_MAX,DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0,staticSealed=!1;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module.TOTAL_STACK||5242880,TOTAL_MEMORY=Module.TOTAL_MEMORY||134217728;TOTAL_MEMORY0;){var l=o.shift();if(typeof l=="function"){l();continue}var f=l.func;typeof f=="number"?l.arg===void 0?Module.dynCall_v(f):Module.dynCall_vi(f,l.arg):f(l.arg===void 0?null:l.arg)}}var __ATPRERUN__=[],__ATINIT__=[],__ATMAIN__=[],__ATEXIT__=[],__ATPOSTRUN__=[],runtimeInitialized=!1,runtimeExited=!1;function preRun(){if(Module.preRun)for(typeof Module.preRun=="function"&&(Module.preRun=[Module.preRun]);Module.preRun.length;)addOnPreRun(Module.preRun.shift());callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){runtimeInitialized||(runtimeInitialized=!0,callRuntimeCallbacks(__ATINIT__))}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__),runtimeExited=!0}function postRun(){if(Module.postRun)for(typeof Module.postRun=="function"&&(Module.postRun=[Module.postRun]);Module.postRun.length;)addOnPostRun(Module.postRun.shift());callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(o){__ATPRERUN__.unshift(o)}Module.addOnPreRun=addOnPreRun;function addOnInit(o){__ATINIT__.unshift(o)}Module.addOnInit=addOnInit;function addOnPreMain(o){__ATMAIN__.unshift(o)}Module.addOnPreMain=addOnPreMain;function addOnExit(o){__ATEXIT__.unshift(o)}Module.addOnExit=addOnExit;function addOnPostRun(o){__ATPOSTRUN__.unshift(o)}Module.addOnPostRun=addOnPostRun;function intArrayFromString(o,l,f){var h=f>0?f:lengthBytesUTF8(o)+1,E=new Array(h),t=stringToUTF8Array(o,E,0,E.length);return l&&(E.length=t),E}Module.intArrayFromString=intArrayFromString;function intArrayToString(o){for(var l=[],f=0;f255&&(h&=255),l.push(String.fromCharCode(h))}return l.join("")}Module.intArrayToString=intArrayToString;function writeStringToMemory(o,l,f){Runtime.warnOnce("writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!");var h,E;f&&(E=l+lengthBytesUTF8(o),h=HEAP8[E]),stringToUTF8(o,l,1/0),f&&(HEAP8[E]=h)}Module.writeStringToMemory=writeStringToMemory;function writeArrayToMemory(o,l){HEAP8.set(o,l)}Module.writeArrayToMemory=writeArrayToMemory;function writeAsciiToMemory(o,l,f){for(var h=0;h>0]=o.charCodeAt(h);f||(HEAP8[l>>0]=0)}if(Module.writeAsciiToMemory=writeAsciiToMemory,(!Math.imul||Math.imul(4294967295,5)!==-5)&&(Math.imul=function o(l,f){var h=l>>>16,E=l&65535,t=f>>>16,N=f&65535;return E*N+(h*N+E*t<<16)|0}),Math.imul=Math.imul,!Math.fround){var froundBuffer=new Float32Array(1);Math.fround=function(o){return froundBuffer[0]=o,froundBuffer[0]}}Math.fround=Math.fround,Math.clz32||(Math.clz32=function(o){o=o>>>0;for(var l=0;l<32;l++)if(o&1<<31-l)return l;return 32}),Math.clz32=Math.clz32,Math.trunc||(Math.trunc=function(o){return o<0?Math.ceil(o):Math.floor(o)}),Math.trunc=Math.trunc;var Math_abs=Math.abs,Math_cos=Math.cos,Math_sin=Math.sin,Math_tan=Math.tan,Math_acos=Math.acos,Math_asin=Math.asin,Math_atan=Math.atan,Math_atan2=Math.atan2,Math_exp=Math.exp,Math_log=Math.log,Math_sqrt=Math.sqrt,Math_ceil=Math.ceil,Math_floor=Math.floor,Math_pow=Math.pow,Math_imul=Math.imul,Math_fround=Math.fround,Math_round=Math.round,Math_min=Math.min,Math_clz32=Math.clz32,Math_trunc=Math.trunc,runDependencies=0,runDependencyWatcher=null,dependenciesFulfilled=null;function getUniqueRunDependency(o){return o}function addRunDependency(o){runDependencies++,Module.monitorRunDependencies&&Module.monitorRunDependencies(runDependencies)}Module.addRunDependency=addRunDependency;function removeRunDependency(o){if(runDependencies--,Module.monitorRunDependencies&&Module.monitorRunDependencies(runDependencies),runDependencies==0&&(runDependencyWatcher!==null&&(clearInterval(runDependencyWatcher),runDependencyWatcher=null),dependenciesFulfilled)){var l=dependenciesFulfilled;dependenciesFulfilled=null,l()}}Module.removeRunDependency=removeRunDependency,Module.preloadedImages={},Module.preloadedAudios={};var ASM_CONSTS=[function(o,l,f,h,E,t,N,F){return _nbind.callbackSignatureList[o].apply(this,arguments)}];function _emscripten_asm_const_iiiiiiii(o,l,f,h,E,t,N,F){return ASM_CONSTS[o](l,f,h,E,t,N,F)}function _emscripten_asm_const_iiiii(o,l,f,h,E){return ASM_CONSTS[o](l,f,h,E)}function _emscripten_asm_const_iiidddddd(o,l,f,h,E,t,N,F,k){return ASM_CONSTS[o](l,f,h,E,t,N,F,k)}function _emscripten_asm_const_iiididi(o,l,f,h,E,t,N){return ASM_CONSTS[o](l,f,h,E,t,N)}function _emscripten_asm_const_iiii(o,l,f,h){return ASM_CONSTS[o](l,f,h)}function _emscripten_asm_const_iiiid(o,l,f,h,E){return ASM_CONSTS[o](l,f,h,E)}function _emscripten_asm_const_iiiiii(o,l,f,h,E,t){return ASM_CONSTS[o](l,f,h,E,t)}STATIC_BASE=Runtime.GLOBAL_BASE,STATICTOP=STATIC_BASE+12800,__ATINIT__.push({func:function(){__GLOBAL__sub_I_Yoga_cpp()}},{func:function(){__GLOBAL__sub_I_nbind_cc()}},{func:function(){__GLOBAL__sub_I_common_cc()}},{func:function(){__GLOBAL__sub_I_Binding_cc()}}),allocate([0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,192,127,0,0,192,127,0,0,192,127,3,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,3,0,0,0,0,0,192,127,3,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,192,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,192,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,127,0,0,192,127,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,0,0,128,191,0,0,128,191,0,0,192,127,0,0,0,0,0,0,0,0,0,0,128,63,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,3,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,190,12,0,0,200,12,0,0,208,12,0,0,216,12,0,0,230,12,0,0,242,12,0,0,1,0,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,192,127,3,0,0,0,180,45,0,0,181,45,0,0,182,45,0,0,181,45,0,0,182,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,3,0,0,0,1,0,0,0,4,0,0,0,183,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,181,45,0,0,184,45,0,0,185,45,0,0,181,45,0,0,181,45,0,0,182,45,0,0,186,45,0,0,185,45,0,0,148,4,0,0,3,0,0,0,187,45,0,0,164,4,0,0,188,45,0,0,2,0,0,0,189,45,0,0,164,4,0,0,188,45,0,0,185,45,0,0,164,4,0,0,185,45,0,0,164,4,0,0,188,45,0,0,181,45,0,0,182,45,0,0,181,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,5,0,0,0,6,0,0,0,1,0,0,0,7,0,0,0,183,45,0,0,182,45,0,0,181,45,0,0,190,45,0,0,190,45,0,0,182,45,0,0,182,45,0,0,185,45,0,0,181,45,0,0,185,45,0,0,182,45,0,0,181,45,0,0,185,45,0,0,182,45,0,0,185,45,0,0,48,5,0,0,3,0,0,0,56,5,0,0,1,0,0,0,189,45,0,0,185,45,0,0,164,4,0,0,76,5,0,0,2,0,0,0,191,45,0,0,186,45,0,0,182,45,0,0,185,45,0,0,192,45,0,0,185,45,0,0,182,45,0,0,186,45,0,0,185,45,0,0,76,5,0,0,76,5,0,0,136,5,0,0,182,45,0,0,181,45,0,0,2,0,0,0,190,45,0,0,136,5,0,0,56,19,0,0,156,5,0,0,2,0,0,0,184,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,8,0,0,0,9,0,0,0,1,0,0,0,10,0,0,0,204,5,0,0,181,45,0,0,181,45,0,0,2,0,0,0,180,45,0,0,204,5,0,0,2,0,0,0,195,45,0,0,236,5,0,0,97,19,0,0,198,45,0,0,211,45,0,0,212,45,0,0,213,45,0,0,214,45,0,0,215,45,0,0,188,45,0,0,182,45,0,0,216,45,0,0,217,45,0,0,218,45,0,0,219,45,0,0,192,45,0,0,181,45,0,0,0,0,0,0,185,45,0,0,110,19,0,0,186,45,0,0,115,19,0,0,221,45,0,0,120,19,0,0,148,4,0,0,132,19,0,0,96,6,0,0,145,19,0,0,222,45,0,0,164,19,0,0,223,45,0,0,173,19,0,0,0,0,0,0,3,0,0,0,104,6,0,0,1,0,0,0,187,45,0,0,0,0,0,0,0,0,0,0,1,0,0,0,11,0,0,0,12,0,0,0,1,0,0,0,13,0,0,0,185,45,0,0,224,45,0,0,164,6,0,0,188,45,0,0,172,6,0,0,180,6,0,0,2,0,0,0,188,6,0,0,7,0,0,0,224,45,0,0,7,0,0,0,164,6,0,0,1,0,0,0,213,45,0,0,185,45,0,0,224,45,0,0,172,6,0,0,185,45,0,0,224,45,0,0,164,6,0,0,185,45,0,0,224,45,0,0,211,45,0,0,211,45,0,0,222,45,0,0,211,45,0,0,224,45,0,0,222,45,0,0,211,45,0,0,224,45,0,0,172,6,0,0,222,45,0,0,211,45,0,0,224,45,0,0,188,45,0,0,222,45,0,0,211,45,0,0,40,7,0,0,188,45,0,0,2,0,0,0,224,45,0,0,185,45,0,0,188,45,0,0,188,45,0,0,188,45,0,0,188,45,0,0,222,45,0,0,224,45,0,0,148,4,0,0,185,45,0,0,148,4,0,0,148,4,0,0,148,4,0,0,148,4,0,0,148,4,0,0,185,45,0,0,164,6,0,0,148,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,14,0,0,0,15,0,0,0,1,0,0,0,16,0,0,0,148,7,0,0,2,0,0,0,225,45,0,0,183,45,0,0,188,45,0,0,168,7,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,234,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,9,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,2,0,0,0,242,45,0,0,0,4,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,111,117,108,100,32,110,111,116,32,97,108,108,111,99,97,116,101,32,109,101,109,111,114,121,32,102,111,114,32,110,111,100,101,0,67,97,110,110,111,116,32,114,101,115,101,116,32,97,32,110,111,100,101,32,119,104,105,99,104,32,115,116,105,108,108,32,104,97,115,32,99,104,105,108,100,114,101,110,32,97,116,116,97,99,104,101,100,0,67,97,110,110,111,116,32,114,101,115,101,116,32,97,32,110,111,100,101,32,115,116,105,108,108,32,97,116,116,97,99,104,101,100,32,116,111,32,97,32,112,97,114,101,110,116,0,67,111,117,108,100,32,110,111,116,32,97,108,108,111,99,97,116,101,32,109,101,109,111,114,121,32,102,111,114,32,99,111,110,102,105,103,0,67,97,110,110,111,116,32,115,101,116,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,58,32,78,111,100,101,115,32,119,105,116,104,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,115,32,99,97,110,110,111,116,32,104,97,118,101,32,99,104,105,108,100,114,101,110,46,0,67,104,105,108,100,32,97,108,114,101,97,100,121,32,104,97,115,32,97,32,112,97,114,101,110,116,44,32,105,116,32,109,117,115,116,32,98,101,32,114,101,109,111,118,101,100,32,102,105,114,115,116,46,0,67,97,110,110,111,116,32,97,100,100,32,99,104,105,108,100,58,32,78,111,100,101,115,32,119,105,116,104,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,115,32,99,97,110,110,111,116,32,104,97,118,101,32,99,104,105,108,100,114,101,110,46,0,79,110,108,121,32,108,101,97,102,32,110,111,100,101,115,32,119,105,116,104,32,99,117,115,116,111,109,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,115,115,104,111,117,108,100,32,109,97,110,117,97,108,108,121,32,109,97,114,107,32,116,104,101,109,115,101,108,118,101,115,32,97,115,32,100,105,114,116,121,0,67,97,110,110,111,116,32,103,101,116,32,108,97,121,111,117,116,32,112,114,111,112,101,114,116,105,101,115,32,111,102,32,109,117,108,116,105,45,101,100,103,101,32,115,104,111,114,116,104,97,110,100,115,0,37,115,37,100,46,123,91,115,107,105,112,112,101,100,93,32,0,119,109,58,32,37,115,44,32,104,109,58,32,37,115,44,32,97,119,58,32,37,102,32,97,104,58,32,37,102,32,61,62,32,100,58,32,40,37,102,44,32,37,102,41,32,37,115,10,0,37,115,37,100,46,123,37,115,0,42,0,119,109,58,32,37,115,44,32,104,109,58,32,37,115,44,32,97,119,58,32,37,102,32,97,104,58,32,37,102,32,37,115,10,0,37,115,37,100,46,125,37,115,0,119,109,58,32,37,115,44,32,104,109,58,32,37,115,44,32,100,58,32,40,37,102,44,32,37,102,41,32,37,115,10,0,79,117,116,32,111,102,32,99,97,99,104,101,32,101,110,116,114,105,101,115,33,10,0,83,99,97,108,101,32,102,97,99,116,111,114,32,115,104,111,117,108,100,32,110,111,116,32,98,101,32,108,101,115,115,32,116,104,97,110,32,122,101,114,111,0,105,110,105,116,105,97,108,0,37,115,10,0,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,0,85,78,68,69,70,73,78,69,68,0,69,88,65,67,84,76,89,0,65,84,95,77,79,83,84,0,76,65,89,95,85,78,68,69,70,73,78,69,68,0,76,65,89,95,69,88,65,67,84,76,89,0,76,65,89,95,65,84,95,77,79,83,84,0,97,118,97,105,108,97,98,108,101,87,105,100,116,104,32,105,115,32,105,110,100,101,102,105,110,105,116,101,32,115,111,32,119,105,100,116,104,77,101,97,115,117,114,101,77,111,100,101,32,109,117,115,116,32,98,101,32,89,71,77,101,97,115,117,114,101,77,111,100,101,85,110,100,101,102,105,110,101,100,0,97,118,97,105,108,97,98,108,101,72,101,105,103,104,116,32,105,115,32,105,110,100,101,102,105,110,105,116,101,32,115,111,32,104,101,105,103,104,116,77,101,97,115,117,114,101,77,111,100,101,32,109,117,115,116,32,98,101,32,89,71,77,101,97,115,117,114,101,77,111,100,101,85,110,100,101,102,105,110,101,100,0,102,108,101,120,0,115,116,114,101,116,99,104,0,109,117,108,116,105,108,105,110,101,45,115,116,114,101,116,99,104,0,69,120,112,101,99,116,101,100,32,110,111,100,101,32,116,111,32,104,97,118,101,32,99,117,115,116,111,109,32,109,101,97,115,117,114,101,32,102,117,110,99,116,105,111,110,0,109,101,97,115,117,114,101,0,69,120,112,101,99,116,32,99,117,115,116,111,109,32,98,97,115,101,108,105,110,101,32,102,117,110,99,116,105,111,110,32,116,111,32,110,111,116,32,114,101,116,117,114,110,32,78,97,78,0,97,98,115,45,109,101,97,115,117,114,101,0,97,98,115,45,108,97,121,111,117,116,0,78,111,100,101,0,99,114,101,97,116,101,68,101,102,97,117,108,116,0,99,114,101,97,116,101,87,105,116,104,67,111,110,102,105,103,0,100,101,115,116,114,111,121,0,114,101,115,101,116,0,99,111,112,121,83,116,121,108,101,0,115,101,116,80,111,115,105,116,105,111,110,84,121,112,101,0,115,101,116,80,111,115,105,116,105,111,110,0,115,101,116,80,111,115,105,116,105,111,110,80,101,114,99,101,110,116,0,115,101,116,65,108,105,103,110,67,111,110,116,101,110,116,0,115,101,116,65,108,105,103,110,73,116,101,109,115,0,115,101,116,65,108,105,103,110,83,101,108,102,0,115,101,116,70,108,101,120,68,105,114,101,99,116,105,111,110,0,115,101,116,70,108,101,120,87,114,97,112,0,115,101,116,74,117,115,116,105,102,121,67,111,110,116,101,110,116,0,115,101,116,77,97,114,103,105,110,0,115,101,116,77,97,114,103,105,110,80,101,114,99,101,110,116,0,115,101,116,77,97,114,103,105,110,65,117,116,111,0,115,101,116,79,118,101,114,102,108,111,119,0,115,101,116,68,105,115,112,108,97,121,0,115,101,116,70,108,101,120,0,115,101,116,70,108,101,120,66,97,115,105,115,0,115,101,116,70,108,101,120,66,97,115,105,115,80,101,114,99,101,110,116,0,115,101,116,70,108,101,120,71,114,111,119,0,115,101,116,70,108,101,120,83,104,114,105,110,107,0,115,101,116,87,105,100,116,104,0,115,101,116,87,105,100,116,104,80,101,114,99,101,110,116,0,115,101,116,87,105,100,116,104,65,117,116,111,0,115,101,116,72,101,105,103,104,116,0,115,101,116,72,101,105,103,104,116,80,101,114,99,101,110,116,0,115,101,116,72,101,105,103,104,116,65,117,116,111,0,115,101,116,77,105,110,87,105,100,116,104,0,115,101,116,77,105,110,87,105,100,116,104,80,101,114,99,101,110,116,0,115,101,116,77,105,110,72,101,105,103,104,116,0,115,101,116,77,105,110,72,101,105,103,104,116,80,101,114,99,101,110,116,0,115,101,116,77,97,120,87,105,100,116,104,0,115,101,116,77,97,120,87,105,100,116,104,80,101,114,99,101,110,116,0,115,101,116,77,97,120,72,101,105,103,104,116,0,115,101,116,77,97,120,72,101,105,103,104,116,80,101,114,99,101,110,116,0,115,101,116,65,115,112,101,99,116,82,97,116,105,111,0,115,101,116,66,111,114,100,101,114,0,115,101,116,80,97,100,100,105,110,103,0,115,101,116,80,97,100,100,105,110,103,80,101,114,99,101,110,116,0,103,101,116,80,111,115,105,116,105,111,110,84,121,112,101,0,103,101,116,80,111,115,105,116,105,111,110,0,103,101,116,65,108,105,103,110,67,111,110,116,101,110,116,0,103,101,116,65,108,105,103,110,73,116,101,109,115,0,103,101,116,65,108,105,103,110,83,101,108,102,0,103,101,116,70,108,101,120,68,105,114,101,99,116,105,111,110,0,103,101,116,70,108,101,120,87,114,97,112,0,103,101,116,74,117,115,116,105,102,121,67,111,110,116,101,110,116,0,103,101,116,77,97,114,103,105,110,0,103,101,116,70,108,101,120,66,97,115,105,115,0,103,101,116,70,108,101,120,71,114,111,119,0,103,101,116,70,108,101,120,83,104,114,105,110,107,0,103,101,116,87,105,100,116,104,0,103,101,116,72,101,105,103,104,116,0,103,101,116,77,105,110,87,105,100,116,104,0,103,101,116,77,105,110,72,101,105,103,104,116,0,103,101,116,77,97,120,87,105,100,116,104,0,103,101,116,77,97,120,72,101,105,103,104,116,0,103,101,116,65,115,112,101,99,116,82,97,116,105,111,0,103,101,116,66,111,114,100,101,114,0,103,101,116,79,118,101,114,102,108,111,119,0,103,101,116,68,105,115,112,108,97,121,0,103,101,116,80,97,100,100,105,110,103,0,105,110,115,101,114,116,67,104,105,108,100,0,114,101,109,111,118,101,67,104,105,108,100,0,103,101,116,67,104,105,108,100,67,111,117,110,116,0,103,101,116,80,97,114,101,110,116,0,103,101,116,67,104,105,108,100,0,115,101,116,77,101,97,115,117,114,101,70,117,110,99,0,117,110,115,101,116,77,101,97,115,117,114,101,70,117,110,99,0,109,97,114,107,68,105,114,116,121,0,105,115,68,105,114,116,121,0,99,97,108,99,117,108,97,116,101,76,97,121,111,117,116,0,103,101,116,67,111,109,112,117,116,101,100,76,101,102,116,0,103,101,116,67,111,109,112,117,116,101,100,82,105,103,104,116,0,103,101,116,67,111,109,112,117,116,101,100,84,111,112,0,103,101,116,67,111,109,112,117,116,101,100,66,111,116,116,111,109,0,103,101,116,67,111,109,112,117,116,101,100,87,105,100,116,104,0,103,101,116,67,111,109,112,117,116,101,100,72,101,105,103,104,116,0,103,101,116,67,111,109,112,117,116,101,100,76,97,121,111,117,116,0,103,101,116,67,111,109,112,117,116,101,100,77,97,114,103,105,110,0,103,101,116,67,111,109,112,117,116,101,100,66,111,114,100,101,114,0,103,101,116,67,111,109,112,117,116,101,100,80,97,100,100,105,110,103,0,67,111,110,102,105,103,0,99,114,101,97,116,101,0,115,101,116,69,120,112,101,114,105,109,101,110,116,97,108,70,101,97,116,117,114,101,69,110,97,98,108,101,100,0,115,101,116,80,111,105,110,116,83,99,97,108,101,70,97,99,116,111,114,0,105,115,69,120,112,101,114,105,109,101,110,116,97,108,70,101,97,116,117,114,101,69,110,97,98,108,101,100,0,86,97,108,117,101,0,76,97,121,111,117,116,0,83,105,122,101,0,103,101,116,73,110,115,116,97,110,99,101,67,111,117,110,116,0,73,110,116,54,52,0,1,1,1,2,2,4,4,4,4,8,8,4,8,118,111,105,100,0,98,111,111,108,0,115,116,100,58,58,115,116,114,105,110,103,0,99,98,70,117,110,99,116,105,111,110,32,38,0,99,111,110,115,116,32,99,98,70,117,110,99,116,105,111,110,32,38,0,69,120,116,101,114,110,97,108,0,66,117,102,102,101,114,0,78,66,105,110,100,73,68,0,78,66,105,110,100,0,98,105,110,100,95,118,97,108,117,101,0,114,101,102,108,101,99,116,0,113,117,101,114,121,84,121,112,101,0,108,97,108,108,111,99,0,108,114,101,115,101,116,0,123,114,101,116,117,114,110,40,95,110,98,105,110,100,46,99,97,108,108,98,97,99,107,83,105,103,110,97,116,117,114,101,76,105,115,116,91,36,48,93,46,97,112,112,108,121,40,116,104,105,115,44,97,114,103,117,109,101,110,116,115,41,41,59,125,0,95,110,98,105,110,100,95,110,101,119,0,17,0,10,0,17,17,17,0,0,0,0,5,0,0,0,0,0,0,9,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,15,10,17,17,17,3,10,7,0,1,19,9,11,11,0,0,9,6,11,0,0,11,0,6,17,0,0,0,17,17,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,17,0,10,10,17,17,17,0,10,0,0,2,0,9,11,0,0,0,9,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,4,13,0,0,0,0,9,14,0,0,0,0,0,14,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,15,0,0,0,0,9,16,0,0,0,0,0,16,0,0,16,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,18,18,18,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,10,0,0,0,0,9,11,0,0,0,0,0,11,0,0,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,12,0,0,0,0,9,12,0,0,0,0,0,12,0,0,12,0,0,45,43,32,32,32,48,88,48,120,0,40,110,117,108,108,41,0,45,48,88,43,48,88,32,48,88,45,48,120,43,48,120,32,48,120,0,105,110,102,0,73,78,70,0,110,97,110,0,78,65,78,0,48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,46,0,84,33,34,25,13,1,2,3,17,75,28,12,16,4,11,29,18,30,39,104,110,111,112,113,98,32,5,6,15,19,20,21,26,8,22,7,40,36,23,24,9,10,14,27,31,37,35,131,130,125,38,42,43,60,61,62,63,67,71,74,77,88,89,90,91,92,93,94,95,96,97,99,100,101,102,103,105,106,107,108,114,115,116,121,122,123,124,0,73,108,108,101,103,97,108,32,98,121,116,101,32,115,101,113,117,101,110,99,101,0,68,111,109,97,105,110,32,101,114,114,111,114,0,82,101,115,117,108,116,32,110,111,116,32,114,101,112,114,101,115,101,110,116,97,98,108,101,0,78,111,116,32,97,32,116,116,121,0,80,101,114,109,105,115,115,105,111,110,32,100,101,110,105,101,100,0,79,112,101,114,97,116,105,111,110,32,110,111,116,32,112,101,114,109,105,116,116,101,100,0,78,111,32,115,117,99,104,32,102,105,108,101,32,111,114,32,100,105,114,101,99,116,111,114,121,0,78,111,32,115,117,99,104,32,112,114,111,99,101,115,115,0,70,105,108,101,32,101,120,105,115,116,115,0,86,97,108,117,101,32,116,111,111,32,108,97,114,103,101,32,102,111,114,32,100,97,116,97,32,116,121,112,101,0,78,111,32,115,112,97,99,101,32,108,101,102,116,32,111,110,32,100,101,118,105,99,101,0,79,117,116,32,111,102,32,109,101,109,111,114,121,0,82,101,115,111,117,114,99,101,32,98,117,115,121,0,73,110,116,101,114,114,117,112,116,101,100,32,115,121,115,116,101,109,32,99,97,108,108,0,82,101,115,111,117,114,99,101,32,116,101,109,112,111,114,97,114,105,108,121,32,117,110,97,118,97,105,108,97,98,108,101,0,73,110,118,97,108,105,100,32,115,101,101,107,0,67,114,111,115,115,45,100,101,118,105,99,101,32,108,105,110,107,0,82,101,97,100,45,111,110,108,121,32,102,105,108,101,32,115,121,115,116,101,109,0,68,105,114,101,99,116,111,114,121,32,110,111,116,32,101,109,112,116,121,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,112,101,101,114,0,79,112,101,114,97,116,105,111,110,32,116,105,109,101,100,32,111,117,116,0,67,111,110,110,101,99,116,105,111,110,32,114,101,102,117,115,101,100,0,72,111,115,116,32,105,115,32,100,111,119,110,0,72,111,115,116,32,105,115,32,117,110,114,101,97,99,104,97,98,108,101,0,65,100,100,114,101,115,115,32,105,110,32,117,115,101,0,66,114,111,107,101,110,32,112,105,112,101,0,73,47,79,32,101,114,114,111,114,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,32,111,114,32,97,100,100,114,101,115,115,0,66,108,111,99,107,32,100,101,118,105,99,101,32,114,101,113,117,105,114,101,100,0,78,111,32,115,117,99,104,32,100,101,118,105,99,101,0,78,111,116,32,97,32,100,105,114,101,99,116,111,114,121,0,73,115,32,97,32,100,105,114,101,99,116,111,114,121,0,84,101,120,116,32,102,105,108,101,32,98,117,115,121,0,69,120,101,99,32,102,111,114,109,97,116,32,101,114,114,111,114,0,73,110,118,97,108,105,100,32,97,114,103,117,109,101,110,116,0,65,114,103,117,109,101,110,116,32,108,105,115,116,32,116,111,111,32,108,111,110,103,0,83,121,109,98,111,108,105,99,32,108,105,110,107,32,108,111,111,112,0,70,105,108,101,110,97,109,101,32,116,111,111,32,108,111,110,103,0,84,111,111,32,109,97,110,121,32,111,112,101,110,32,102,105,108,101,115,32,105,110,32,115,121,115,116,101,109,0,78,111,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,115,32,97,118,97,105,108,97,98,108,101,0,66,97,100,32,102,105,108,101,32,100,101,115,99,114,105,112,116,111,114,0,78,111,32,99,104,105,108,100,32,112,114,111,99,101,115,115,0,66,97,100,32,97,100,100,114,101,115,115,0,70,105,108,101,32,116,111,111,32,108,97,114,103,101,0,84,111,111,32,109,97,110,121,32,108,105,110,107,115,0,78,111,32,108,111,99,107,115,32,97,118,97,105,108,97,98,108,101,0,82,101,115,111,117,114,99,101,32,100,101,97,100,108,111,99,107,32,119,111,117,108,100,32,111,99,99,117,114,0,83,116,97,116,101,32,110,111,116,32,114,101,99,111,118,101,114,97,98,108,101,0,80,114,101,118,105,111,117,115,32,111,119,110,101,114,32,100,105,101,100,0,79,112,101,114,97,116,105,111,110,32,99,97,110,99,101,108,101,100,0,70,117,110,99,116,105,111,110,32,110,111,116,32,105,109,112,108,101,109,101,110,116,101,100,0,78,111,32,109,101,115,115,97,103,101,32,111,102,32,100,101,115,105,114,101,100,32,116,121,112,101,0,73,100,101,110,116,105,102,105,101,114,32,114,101,109,111,118,101,100,0,68,101,118,105,99,101,32,110,111,116,32,97,32,115,116,114,101,97,109,0,78,111,32,100,97,116,97,32,97,118,97,105,108,97,98,108,101,0,68,101,118,105,99,101,32,116,105,109,101,111,117,116,0,79,117,116,32,111,102,32,115,116,114,101,97,109,115,32,114,101,115,111,117,114,99,101,115,0,76,105,110,107,32,104,97,115,32,98,101,101,110,32,115,101,118,101,114,101,100,0,80,114,111,116,111,99,111,108,32,101,114,114,111,114,0,66,97,100,32,109,101,115,115,97,103,101,0,70,105,108,101,32,100,101,115,99,114,105,112,116,111,114,32,105,110,32,98,97,100,32,115,116,97,116,101,0,78,111,116,32,97,32,115,111,99,107,101,116,0,68,101,115,116,105,110,97,116,105,111,110,32,97,100,100,114,101,115,115,32,114,101,113,117,105,114,101,100,0,77,101,115,115,97,103,101,32,116,111,111,32,108,97,114,103,101,0,80,114,111,116,111,99,111,108,32,119,114,111,110,103,32,116,121,112,101,32,102,111,114,32,115,111,99,107,101,116,0,80,114,111,116,111,99,111,108,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,80,114,111,116,111,99,111,108,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,83,111,99,107,101,116,32,116,121,112,101,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,78,111,116,32,115,117,112,112,111,114,116,101,100,0,80,114,111,116,111,99,111,108,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,0,65,100,100,114,101,115,115,32,102,97,109,105,108,121,32,110,111,116,32,115,117,112,112,111,114,116,101,100,32,98,121,32,112,114,111,116,111,99,111,108,0,65,100,100,114,101,115,115,32,110,111,116,32,97,118,97,105,108,97,98,108,101,0,78,101,116,119,111,114,107,32,105,115,32,100,111,119,110,0,78,101,116,119,111,114,107,32,117,110,114,101,97,99,104,97,98,108,101,0,67,111,110,110,101,99,116,105,111,110,32,114,101,115,101,116,32,98,121,32,110,101,116,119,111,114,107,0,67,111,110,110,101,99,116,105,111,110,32,97,98,111,114,116,101,100,0,78,111,32,98,117,102,102,101,114,32,115,112,97,99,101,32,97,118,97,105,108,97,98,108,101,0,83,111,99,107,101,116,32,105,115,32,99,111,110,110,101,99,116,101,100,0,83,111,99,107,101,116,32,110,111,116,32,99,111,110,110,101,99,116,101,100,0,67,97,110,110,111,116,32,115,101,110,100,32,97,102,116,101,114,32,115,111,99,107,101,116,32,115,104,117,116,100,111,119,110,0,79,112,101,114,97,116,105,111,110,32,97,108,114,101,97,100,121,32,105,110,32,112,114,111,103,114,101,115,115,0,79,112,101,114,97,116,105,111,110,32,105,110,32,112,114,111,103,114,101,115,115,0,83,116,97,108,101,32,102,105,108,101,32,104,97,110,100,108,101,0,82,101,109,111,116,101,32,73,47,79,32,101,114,114,111,114,0,81,117,111,116,97,32,101,120,99,101,101,100,101,100,0,78,111,32,109,101,100,105,117,109,32,102,111,117,110,100,0,87,114,111,110,103,32,109,101,100,105,117,109,32,116,121,112,101,0,78,111,32,101,114,114,111,114,32,105,110,102,111,114,109,97,116,105,111,110,0,0],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=STATICTOP;STATICTOP+=16;function _atexit(o,l){__ATEXIT__.unshift({func:o,arg:l})}function ___cxa_atexit(){return _atexit.apply(null,arguments)}function _abort(){Module.abort()}function __ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj(){Module.printErr("missing function: _ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj"),abort(-1)}function __decorate(o,l,f,h){var E=arguments.length,t=E<3?l:h===null?h=Object.getOwnPropertyDescriptor(l,f):h,N;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")t=Reflect.decorate(o,l,f,h);else for(var F=o.length-1;F>=0;F--)(N=o[F])&&(t=(E<3?N(t):E>3?N(l,f,t):N(l,f))||t);return E>3&&t&&Object.defineProperty(l,f,t),t}function _defineHidden(o){return function(l,f){Object.defineProperty(l,f,{configurable:!1,enumerable:!1,value:o,writable:!0})}}var _nbind={};function __nbind_free_external(o){_nbind.externalList[o].dereference(o)}function __nbind_reference_external(o){_nbind.externalList[o].reference()}function _llvm_stackrestore(o){var l=_llvm_stacksave,f=l.LLVM_SAVEDSTACKS[o];l.LLVM_SAVEDSTACKS.splice(o,1),Runtime.stackRestore(f)}function __nbind_register_pool(o,l,f,h){_nbind.Pool.pageSize=o,_nbind.Pool.usedPtr=l/4,_nbind.Pool.rootPtr=f,_nbind.Pool.pagePtr=h/4,HEAP32[l/4]=16909060,HEAP8[l]==1&&(_nbind.bigEndian=!0),HEAP32[l/4]=0,_nbind.makeTypeKindTbl=(t={},t[1024]=_nbind.PrimitiveType,t[64]=_nbind.Int64Type,t[2048]=_nbind.BindClass,t[3072]=_nbind.BindClassPtr,t[4096]=_nbind.SharedClassPtr,t[5120]=_nbind.ArrayType,t[6144]=_nbind.ArrayType,t[7168]=_nbind.CStringType,t[9216]=_nbind.CallbackType,t[10240]=_nbind.BindType,t),_nbind.makeTypeNameTbl={Buffer:_nbind.BufferType,External:_nbind.ExternalType,Int64:_nbind.Int64Type,_nbind_new:_nbind.CreateValueType,bool:_nbind.BooleanType,"cbFunction &":_nbind.CallbackType,"const cbFunction &":_nbind.CallbackType,"const std::string &":_nbind.StringType,"std::string":_nbind.StringType},Module.toggleLightGC=_nbind.toggleLightGC,_nbind.callUpcast=Module.dynCall_ii;var E=_nbind.makeType(_nbind.constructType,{flags:2048,id:0,name:""});E.proto=Module,_nbind.BindClass.list.push(E);var t}function _emscripten_set_main_loop_timing(o,l){if(Browser.mainLoop.timingMode=o,Browser.mainLoop.timingValue=l,!Browser.mainLoop.func)return 1;if(o==0)Browser.mainLoop.scheduler=function(){var N=Math.max(0,Browser.mainLoop.tickStartTime+l-_emscripten_get_now())|0;setTimeout(Browser.mainLoop.runner,N)},Browser.mainLoop.method="timeout";else if(o==1)Browser.mainLoop.scheduler=function(){Browser.requestAnimationFrame(Browser.mainLoop.runner)},Browser.mainLoop.method="rAF";else if(o==2){if(!window.setImmediate){let t=function(N){N.source===window&&N.data===h&&(N.stopPropagation(),f.shift()())};var E=t,f=[],h="setimmediate";window.addEventListener("message",t,!0),window.setImmediate=function(F){f.push(F),ENVIRONMENT_IS_WORKER?(Module.setImmediates===void 0&&(Module.setImmediates=[]),Module.setImmediates.push(F),window.postMessage({target:h})):window.postMessage(h,"*")}}Browser.mainLoop.scheduler=function(){window.setImmediate(Browser.mainLoop.runner)},Browser.mainLoop.method="immediate"}return 0}function _emscripten_get_now(){abort()}function _emscripten_set_main_loop(o,l,f,h,E){Module.noExitRuntime=!0,assert(!Browser.mainLoop.func,"emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters."),Browser.mainLoop.func=o,Browser.mainLoop.arg=h;var t;typeof h<"u"?t=function(){Module.dynCall_vi(o,h)}:t=function(){Module.dynCall_v(o)};var N=Browser.mainLoop.currentlyRunningMainloop;if(Browser.mainLoop.runner=function(){if(!ABORT){if(Browser.mainLoop.queue.length>0){var k=Date.now(),x=Browser.mainLoop.queue.shift();if(x.func(x.arg),Browser.mainLoop.remainingBlockers){var j=Browser.mainLoop.remainingBlockers,q=j%1==0?j-1:Math.floor(j);x.counted?Browser.mainLoop.remainingBlockers=q:(q=q+.5,Browser.mainLoop.remainingBlockers=(8*j+q)/9)}if(console.log('main loop blocker "'+x.name+'" took '+(Date.now()-k)+" ms"),Browser.mainLoop.updateStatus(),N1&&Browser.mainLoop.currentFrameNumber%Browser.mainLoop.timingValue!=0){Browser.mainLoop.scheduler();return}else Browser.mainLoop.timingMode==0&&(Browser.mainLoop.tickStartTime=_emscripten_get_now());Browser.mainLoop.method==="timeout"&&Module.ctx&&(Module.printErr("Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!"),Browser.mainLoop.method=""),Browser.mainLoop.runIter(t),!(N0?_emscripten_set_main_loop_timing(0,1e3/l):_emscripten_set_main_loop_timing(1,1),Browser.mainLoop.scheduler()),f)throw"SimulateInfiniteLoop"}var Browser={mainLoop:{scheduler:null,method:"",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function(){Browser.mainLoop.scheduler=null,Browser.mainLoop.currentlyRunningMainloop++},resume:function(){Browser.mainLoop.currentlyRunningMainloop++;var o=Browser.mainLoop.timingMode,l=Browser.mainLoop.timingValue,f=Browser.mainLoop.func;Browser.mainLoop.func=null,_emscripten_set_main_loop(f,0,!1,Browser.mainLoop.arg,!0),_emscripten_set_main_loop_timing(o,l),Browser.mainLoop.scheduler()},updateStatus:function(){if(Module.setStatus){var o=Module.statusMessage||"Please wait...",l=Browser.mainLoop.remainingBlockers,f=Browser.mainLoop.expectedBlockers;l?l"u"&&(console.log("warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available."),Module.noImageDecoding=!0);var o={};o.canHandle=function(t){return!Module.noImageDecoding&&/\.(jpg|jpeg|png|bmp)$/i.test(t)},o.handle=function(t,N,F,k){var x=null;if(Browser.hasBlobConstructor)try{x=new Blob([t],{type:Browser.getMimetype(N)}),x.size!==t.length&&(x=new Blob([new Uint8Array(t).buffer],{type:Browser.getMimetype(N)}))}catch(re){Runtime.warnOnce("Blob constructor present but fails: "+re+"; falling back to blob builder")}if(!x){var j=new Browser.BlobBuilder;j.append(new Uint8Array(t).buffer),x=j.getBlob()}var q=Browser.URLObject.createObjectURL(x),V=new Image;V.onload=function(){assert(V.complete,"Image "+N+" could not be decoded");var y=document.createElement("canvas");y.width=V.width,y.height=V.height;var me=y.getContext("2d");me.drawImage(V,0,0),Module.preloadedImages[N]=y,Browser.URLObject.revokeObjectURL(q),F&&F(t)},V.onerror=function(y){console.log("Image "+q+" could not be decoded"),k&&k()},V.src=q},Module.preloadPlugins.push(o);var l={};l.canHandle=function(t){return!Module.noAudioDecoding&&t.substr(-4)in{".ogg":1,".wav":1,".mp3":1}},l.handle=function(t,N,F,k){var x=!1;function j(me){x||(x=!0,Module.preloadedAudios[N]=me,F&&F(t))}function q(){x||(x=!0,Module.preloadedAudios[N]=new Audio,k&&k())}if(Browser.hasBlobConstructor){try{var V=new Blob([t],{type:Browser.getMimetype(N)})}catch{return q()}var re=Browser.URLObject.createObjectURL(V),y=new Audio;y.addEventListener("canplaythrough",function(){j(y)},!1),y.onerror=function(De){if(x)return;console.log("warning: browser could not fully decode audio "+N+", trying slower base64 approach");function ge(ae){for(var we="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",he="=",ve="",ue=0,Ae=0,ze=0;ze=6;){var We=ue>>Ae-6&63;Ae-=6,ve+=we[We]}return Ae==2?(ve+=we[(ue&3)<<4],ve+=he+he):Ae==4&&(ve+=we[(ue&15)<<2],ve+=he),ve}y.src="data:audio/x-"+N.substr(-3)+";base64,"+ge(t),j(y)},y.src=re,Browser.safeSetTimeout(function(){j(y)},1e4)}else return q()},Module.preloadPlugins.push(l);function f(){Browser.pointerLock=document.pointerLockElement===Module.canvas||document.mozPointerLockElement===Module.canvas||document.webkitPointerLockElement===Module.canvas||document.msPointerLockElement===Module.canvas}var h=Module.canvas;h&&(h.requestPointerLock=h.requestPointerLock||h.mozRequestPointerLock||h.webkitRequestPointerLock||h.msRequestPointerLock||function(){},h.exitPointerLock=document.exitPointerLock||document.mozExitPointerLock||document.webkitExitPointerLock||document.msExitPointerLock||function(){},h.exitPointerLock=h.exitPointerLock.bind(document),document.addEventListener("pointerlockchange",f,!1),document.addEventListener("mozpointerlockchange",f,!1),document.addEventListener("webkitpointerlockchange",f,!1),document.addEventListener("mspointerlockchange",f,!1),Module.elementPointerLock&&h.addEventListener("click",function(E){!Browser.pointerLock&&Module.canvas.requestPointerLock&&(Module.canvas.requestPointerLock(),E.preventDefault())},!1))},createContext:function(o,l,f,h){if(l&&Module.ctx&&o==Module.canvas)return Module.ctx;var E,t;if(l){var N={antialias:!1,alpha:!1};if(h)for(var F in h)N[F]=h[F];t=GL.createContext(o,N),t&&(E=GL.getContext(t).GLctx)}else E=o.getContext("2d");return E?(f&&(l||assert(typeof GLctx>"u","cannot set in module if GLctx is used, but we are a non-GL context that would replace it"),Module.ctx=E,l&&GL.makeContextCurrent(t),Module.useWebGL=l,Browser.moduleContextCreatedCallbacks.forEach(function(k){k()}),Browser.init()),E):null},destroyContext:function(o,l,f){},fullscreenHandlersInstalled:!1,lockPointer:void 0,resizeCanvas:void 0,requestFullscreen:function(o,l,f){Browser.lockPointer=o,Browser.resizeCanvas=l,Browser.vrDevice=f,typeof Browser.lockPointer>"u"&&(Browser.lockPointer=!0),typeof Browser.resizeCanvas>"u"&&(Browser.resizeCanvas=!1),typeof Browser.vrDevice>"u"&&(Browser.vrDevice=null);var h=Module.canvas;function E(){Browser.isFullscreen=!1;var N=h.parentNode;(document.fullscreenElement||document.mozFullScreenElement||document.msFullscreenElement||document.webkitFullscreenElement||document.webkitCurrentFullScreenElement)===N?(h.exitFullscreen=document.exitFullscreen||document.cancelFullScreen||document.mozCancelFullScreen||document.msExitFullscreen||document.webkitCancelFullScreen||function(){},h.exitFullscreen=h.exitFullscreen.bind(document),Browser.lockPointer&&h.requestPointerLock(),Browser.isFullscreen=!0,Browser.resizeCanvas&&Browser.setFullscreenCanvasSize()):(N.parentNode.insertBefore(h,N),N.parentNode.removeChild(N),Browser.resizeCanvas&&Browser.setWindowedCanvasSize()),Module.onFullScreen&&Module.onFullScreen(Browser.isFullscreen),Module.onFullscreen&&Module.onFullscreen(Browser.isFullscreen),Browser.updateCanvasDimensions(h)}Browser.fullscreenHandlersInstalled||(Browser.fullscreenHandlersInstalled=!0,document.addEventListener("fullscreenchange",E,!1),document.addEventListener("mozfullscreenchange",E,!1),document.addEventListener("webkitfullscreenchange",E,!1),document.addEventListener("MSFullscreenChange",E,!1));var t=document.createElement("div");h.parentNode.insertBefore(t,h),t.appendChild(h),t.requestFullscreen=t.requestFullscreen||t.mozRequestFullScreen||t.msRequestFullscreen||(t.webkitRequestFullscreen?function(){t.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)}:null)||(t.webkitRequestFullScreen?function(){t.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT)}:null),f?t.requestFullscreen({vrDisplay:f}):t.requestFullscreen()},requestFullScreen:function(o,l,f){return Module.printErr("Browser.requestFullScreen() is deprecated. Please call Browser.requestFullscreen instead."),Browser.requestFullScreen=function(h,E,t){return Browser.requestFullscreen(h,E,t)},Browser.requestFullscreen(o,l,f)},nextRAF:0,fakeRequestAnimationFrame:function(o){var l=Date.now();if(Browser.nextRAF===0)Browser.nextRAF=l+1e3/60;else for(;l+2>=Browser.nextRAF;)Browser.nextRAF+=1e3/60;var f=Math.max(Browser.nextRAF-l,0);setTimeout(o,f)},requestAnimationFrame:function o(l){typeof window>"u"?Browser.fakeRequestAnimationFrame(l):(window.requestAnimationFrame||(window.requestAnimationFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame||Browser.fakeRequestAnimationFrame),window.requestAnimationFrame(l))},safeCallback:function(o){return function(){if(!ABORT)return o.apply(null,arguments)}},allowAsyncCallbacks:!0,queuedAsyncCallbacks:[],pauseAsyncCallbacks:function(){Browser.allowAsyncCallbacks=!1},resumeAsyncCallbacks:function(){if(Browser.allowAsyncCallbacks=!0,Browser.queuedAsyncCallbacks.length>0){var o=Browser.queuedAsyncCallbacks;Browser.queuedAsyncCallbacks=[],o.forEach(function(l){l()})}},safeRequestAnimationFrame:function(o){return Browser.requestAnimationFrame(function(){ABORT||(Browser.allowAsyncCallbacks?o():Browser.queuedAsyncCallbacks.push(o))})},safeSetTimeout:function(o,l){return Module.noExitRuntime=!0,setTimeout(function(){ABORT||(Browser.allowAsyncCallbacks?o():Browser.queuedAsyncCallbacks.push(o))},l)},safeSetInterval:function(o,l){return Module.noExitRuntime=!0,setInterval(function(){ABORT||Browser.allowAsyncCallbacks&&o()},l)},getMimetype:function(o){return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",bmp:"image/bmp",ogg:"audio/ogg",wav:"audio/wav",mp3:"audio/mpeg"}[o.substr(o.lastIndexOf(".")+1)]},getUserMedia:function(o){window.getUserMedia||(window.getUserMedia=navigator.getUserMedia||navigator.mozGetUserMedia),window.getUserMedia(o)},getMovementX:function(o){return o.movementX||o.mozMovementX||o.webkitMovementX||0},getMovementY:function(o){return o.movementY||o.mozMovementY||o.webkitMovementY||0},getMouseWheelDelta:function(o){var l=0;switch(o.type){case"DOMMouseScroll":l=o.detail;break;case"mousewheel":l=o.wheelDelta;break;case"wheel":l=o.deltaY;break;default:throw"unrecognized mouse wheel event: "+o.type}return l},mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function(o){if(Browser.pointerLock)o.type!="mousemove"&&"mozMovementX"in o?Browser.mouseMovementX=Browser.mouseMovementY=0:(Browser.mouseMovementX=Browser.getMovementX(o),Browser.mouseMovementY=Browser.getMovementY(o)),typeof SDL<"u"?(Browser.mouseX=SDL.mouseX+Browser.mouseMovementX,Browser.mouseY=SDL.mouseY+Browser.mouseMovementY):(Browser.mouseX+=Browser.mouseMovementX,Browser.mouseY+=Browser.mouseMovementY);else{var l=Module.canvas.getBoundingClientRect(),f=Module.canvas.width,h=Module.canvas.height,E=typeof window.scrollX<"u"?window.scrollX:window.pageXOffset,t=typeof window.scrollY<"u"?window.scrollY:window.pageYOffset;if(o.type==="touchstart"||o.type==="touchend"||o.type==="touchmove"){var N=o.touch;if(N===void 0)return;var F=N.pageX-(E+l.left),k=N.pageY-(t+l.top);F=F*(f/l.width),k=k*(h/l.height);var x={x:F,y:k};if(o.type==="touchstart")Browser.lastTouches[N.identifier]=x,Browser.touches[N.identifier]=x;else if(o.type==="touchend"||o.type==="touchmove"){var j=Browser.touches[N.identifier];j||(j=x),Browser.lastTouches[N.identifier]=j,Browser.touches[N.identifier]=x}return}var q=o.pageX-(E+l.left),V=o.pageY-(t+l.top);q=q*(f/l.width),V=V*(h/l.height),Browser.mouseMovementX=q-Browser.mouseX,Browser.mouseMovementY=V-Browser.mouseY,Browser.mouseX=q,Browser.mouseY=V}},asyncLoad:function(o,l,f,h){var E=h?"":"al "+o;Module.readAsync(o,function(t){assert(t,'Loading data file "'+o+'" failed (no arrayBuffer).'),l(new Uint8Array(t)),E&&removeRunDependency(E)},function(t){if(f)f();else throw'Loading data file "'+o+'" failed.'}),E&&addRunDependency(E)},resizeListeners:[],updateResizeListeners:function(){var o=Module.canvas;Browser.resizeListeners.forEach(function(l){l(o.width,o.height)})},setCanvasSize:function(o,l,f){var h=Module.canvas;Browser.updateCanvasDimensions(h,o,l),f||Browser.updateResizeListeners()},windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function(){if(typeof SDL<"u"){var o=HEAPU32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2];o=o|8388608,HEAP32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2]=o}Browser.updateResizeListeners()},setWindowedCanvasSize:function(){if(typeof SDL<"u"){var o=HEAPU32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2];o=o&-8388609,HEAP32[SDL.screen+Runtime.QUANTUM_SIZE*0>>2]=o}Browser.updateResizeListeners()},updateCanvasDimensions:function(o,l,f){l&&f?(o.widthNative=l,o.heightNative=f):(l=o.widthNative,f=o.heightNative);var h=l,E=f;if(Module.forcedAspectRatio&&Module.forcedAspectRatio>0&&(h/E>2];return l},getStr:function(){var o=Pointer_stringify(SYSCALLS.get());return o},get64:function(){var o=SYSCALLS.get(),l=SYSCALLS.get();return o>=0?assert(l===0):assert(l===-1),o},getZero:function(){assert(SYSCALLS.get()===0)}};function ___syscall6(o,l){SYSCALLS.varargs=l;try{var f=SYSCALLS.getStreamFromFD();return FS.close(f),0}catch(h){return(typeof FS>"u"||!(h instanceof FS.ErrnoError))&&abort(h),-h.errno}}function ___syscall54(o,l){SYSCALLS.varargs=l;try{return 0}catch(f){return(typeof FS>"u"||!(f instanceof FS.ErrnoError))&&abort(f),-f.errno}}function _typeModule(o){var l=[[0,1,"X"],[1,1,"const X"],[128,1,"X *"],[256,1,"X &"],[384,1,"X &&"],[512,1,"std::shared_ptr"],[640,1,"std::unique_ptr"],[5120,1,"std::vector"],[6144,2,"std::array"],[9216,-1,"std::function"]];function f(k,x,j,q,V,re){if(x==1){var y=q&896;(y==128||y==256||y==384)&&(k="X const")}var me;return re?me=j.replace("X",k).replace("Y",V):me=k.replace("X",j).replace("Y",V),me.replace(/([*&]) (?=[*&])/g,"$1")}function h(k,x,j,q,V){throw new Error(k+" type "+j.replace("X",x+"?")+(q?" with flag "+q:"")+" in "+V)}function E(k,x,j,q,V,re,y,me){re===void 0&&(re="X"),me===void 0&&(me=1);var De=j(k);if(De)return De;var ge=q(k),ae=ge.placeholderFlag,we=l[ae];y&&we&&(re=f(y[2],y[0],re,we[0],"?",!0));var he;ae==0&&(he="Unbound"),ae>=10&&(he="Corrupt"),me>20&&(he="Deeply nested"),he&&h(he,k,re,ae,V||"?");var ve=ge.paramList[0],ue=E(ve,x,j,q,V,re,we,me+1),Ae,ze={flags:we[0],id:k,name:"",paramList:[ue]},We=[],gt="?";switch(ge.placeholderFlag){case 1:Ae=ue.spec;break;case 2:if((ue.flags&15360)==1024&&ue.spec.ptrSize==1){ze.flags=7168;break}case 3:case 6:case 5:Ae=ue.spec,ue.flags&15360;break;case 8:gt=""+ge.paramList[1],ze.paramList.push(ge.paramList[1]);break;case 9:for(var _t=0,Qe=ge.paramList[1];_t>2]=o),o}function _llvm_stacksave(){var o=_llvm_stacksave;return o.LLVM_SAVEDSTACKS||(o.LLVM_SAVEDSTACKS=[]),o.LLVM_SAVEDSTACKS.push(Runtime.stackSave()),o.LLVM_SAVEDSTACKS.length-1}function ___syscall140(o,l){SYSCALLS.varargs=l;try{var f=SYSCALLS.getStreamFromFD(),h=SYSCALLS.get(),E=SYSCALLS.get(),t=SYSCALLS.get(),N=SYSCALLS.get(),F=E;return FS.llseek(f,F,N),HEAP32[t>>2]=f.position,f.getdents&&F===0&&N===0&&(f.getdents=null),0}catch(k){return(typeof FS>"u"||!(k instanceof FS.ErrnoError))&&abort(k),-k.errno}}function ___syscall146(o,l){SYSCALLS.varargs=l;try{var f=SYSCALLS.get(),h=SYSCALLS.get(),E=SYSCALLS.get(),t=0;___syscall146.buffer||(___syscall146.buffers=[null,[],[]],___syscall146.printChar=function(j,q){var V=___syscall146.buffers[j];assert(V),q===0||q===10?((j===1?Module.print:Module.printErr)(UTF8ArrayToString(V,0)),V.length=0):V.push(q)});for(var N=0;N>2],k=HEAP32[h+(N*8+4)>>2],x=0;x"u"||!(j instanceof FS.ErrnoError))&&abort(j),-j.errno}}function __nbind_finish(){for(var o=0,l=_nbind.BindClass.list;oo.pageSize/2||l>o.pageSize-f){var h=_nbind.typeNameTbl.NBind.proto;return h.lalloc(l)}else return HEAPU32[o.usedPtr]=f+l,o.rootPtr+f},o.lreset=function(l,f){var h=HEAPU32[o.pagePtr];if(h){var E=_nbind.typeNameTbl.NBind.proto;E.lreset(l,f)}else HEAPU32[o.usedPtr]=l},o}();_nbind.Pool=Pool;function constructType(o,l){var f=o==10240?_nbind.makeTypeNameTbl[l.name]||_nbind.BindType:_nbind.makeTypeKindTbl[o],h=new f(l);return typeIdTbl[l.id]=h,_nbind.typeNameTbl[l.name]=h,h}_nbind.constructType=constructType;function getType(o){return typeIdTbl[o]}_nbind.getType=getType;function queryType(o){var l=HEAPU8[o],f=_nbind.structureList[l][1];o/=4,f<0&&(++o,f=HEAPU32[o]+1);var h=Array.prototype.slice.call(HEAPU32.subarray(o+1,o+1+f));return l==9&&(h=[h[0],h.slice(1)]),{paramList:h,placeholderFlag:l}}_nbind.queryType=queryType;function getTypes(o,l){return o.map(function(f){return typeof f=="number"?_nbind.getComplexType(f,constructType,getType,queryType,l):_nbind.typeNameTbl[f]})}_nbind.getTypes=getTypes;function readTypeIdList(o,l){return Array.prototype.slice.call(HEAPU32,o/4,o/4+l)}_nbind.readTypeIdList=readTypeIdList;function readAsciiString(o){for(var l=o;HEAPU8[l++];);return String.fromCharCode.apply("",HEAPU8.subarray(o,l-1))}_nbind.readAsciiString=readAsciiString;function readPolicyList(o){var l={};if(o)for(;;){var f=HEAPU32[o/4];if(!f)break;l[readAsciiString(f)]=!0,o+=4}return l}_nbind.readPolicyList=readPolicyList;function getDynCall(o,l){var f={float32_t:"d",float64_t:"d",int64_t:"d",uint64_t:"d",void:"v"},h=o.map(function(t){return f[t.name]||"i"}).join(""),E=Module["dynCall_"+h];if(!E)throw new Error("dynCall_"+h+" not found for "+l+"("+o.map(function(t){return t.name}).join(", ")+")");return E}_nbind.getDynCall=getDynCall;function addMethod(o,l,f,h){var E=o[l];o.hasOwnProperty(l)&&E?((E.arity||E.arity===0)&&(E=_nbind.makeOverloader(E,E.arity),o[l]=E),E.addMethod(f,h)):(f.arity=h,o[l]=f)}_nbind.addMethod=addMethod;function throwError(o){throw new Error(o)}_nbind.throwError=throwError,_nbind.bigEndian=!1,_a=_typeModule(_typeModule),_nbind.Type=_a.Type,_nbind.makeType=_a.makeType,_nbind.getComplexType=_a.getComplexType,_nbind.structureList=_a.structureList;var BindType=function(o){__extends(l,o);function l(){var f=o!==null&&o.apply(this,arguments)||this;return f.heap=HEAPU32,f.ptrSize=4,f}return l.prototype.needsWireRead=function(f){return!!this.wireRead||!!this.makeWireRead},l.prototype.needsWireWrite=function(f){return!!this.wireWrite||!!this.makeWireWrite},l}(_nbind.Type);_nbind.BindType=BindType;var PrimitiveType=function(o){__extends(l,o);function l(f){var h=o.call(this,f)||this,E=f.flags&32?{32:HEAPF32,64:HEAPF64}:f.flags&8?{8:HEAPU8,16:HEAPU16,32:HEAPU32}:{8:HEAP8,16:HEAP16,32:HEAP32};return h.heap=E[f.ptrSize*8],h.ptrSize=f.ptrSize,h}return l.prototype.needsWireWrite=function(f){return!!f&&!!f.Strict},l.prototype.makeWireWrite=function(f,h){return h&&h.Strict&&function(E){if(typeof E=="number")return E;throw new Error("Type mismatch")}},l}(BindType);_nbind.PrimitiveType=PrimitiveType;function pushCString(o,l){if(o==null){if(l&&l.Nullable)return 0;throw new Error("Type mismatch")}if(l&&l.Strict){if(typeof o!="string")throw new Error("Type mismatch")}else o=o.toString();var f=Module.lengthBytesUTF8(o)+1,h=_nbind.Pool.lalloc(f);return Module.stringToUTF8Array(o,HEAPU8,h,f),h}_nbind.pushCString=pushCString;function popCString(o){return o===0?null:Module.Pointer_stringify(o)}_nbind.popCString=popCString;var CStringType=function(o){__extends(l,o);function l(){var f=o!==null&&o.apply(this,arguments)||this;return f.wireRead=popCString,f.wireWrite=pushCString,f.readResources=[_nbind.resources.pool],f.writeResources=[_nbind.resources.pool],f}return l.prototype.makeWireWrite=function(f,h){return function(E){return pushCString(E,h)}},l}(BindType);_nbind.CStringType=CStringType;var BooleanType=function(o){__extends(l,o);function l(){var f=o!==null&&o.apply(this,arguments)||this;return f.wireRead=function(h){return!!h},f}return l.prototype.needsWireWrite=function(f){return!!f&&!!f.Strict},l.prototype.makeWireRead=function(f){return"!!("+f+")"},l.prototype.makeWireWrite=function(f,h){return h&&h.Strict&&function(E){if(typeof E=="boolean")return E;throw new Error("Type mismatch")}||f},l}(BindType);_nbind.BooleanType=BooleanType;var Wrapper=function(){function o(){}return o.prototype.persist=function(){this.__nbindState|=1},o}();_nbind.Wrapper=Wrapper;function makeBound(o,l){var f=function(h){__extends(E,h);function E(t,N,F,k){var x=h.call(this)||this;if(!(x instanceof E))return new(Function.prototype.bind.apply(E,Array.prototype.concat.apply([null],arguments)));var j=N,q=F,V=k;if(t!==_nbind.ptrMarker){var re=x.__nbindConstructor.apply(x,arguments);j=4608,V=HEAPU32[re/4],q=HEAPU32[re/4+1]}var y={configurable:!0,enumerable:!1,value:null,writable:!1},me={__nbindFlags:j,__nbindPtr:q};V&&(me.__nbindShared=V,_nbind.mark(x));for(var De=0,ge=Object.keys(me);De>=1;var f=_nbind.valueList[o];return _nbind.valueList[o]=firstFreeValue,firstFreeValue=o,f}else{if(l)return _nbind.popShared(o,l);throw new Error("Invalid value slot "+o)}}_nbind.popValue=popValue;var valueBase=18446744073709552e3;function push64(o){return typeof o=="number"?o:pushValue(o)*4096+valueBase}function pop64(o){return o=3?N=Buffer.from(t):N=new Buffer(t),N.copy(h)}else getBuffer(h).set(t)}}_nbind.commitBuffer=commitBuffer;var dirtyList=[],gcTimer=0;function sweep(){for(var o=0,l=dirtyList;o>2]=DYNAMIC_BASE,staticSealed=!0;function invoke_viiiii(o,l,f,h,E,t){try{Module.dynCall_viiiii(o,l,f,h,E,t)}catch(N){if(typeof N!="number"&&N!=="longjmp")throw N;Module.setThrew(1,0)}}function invoke_vif(o,l,f){try{Module.dynCall_vif(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_vid(o,l,f){try{Module.dynCall_vid(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_fiff(o,l,f,h){try{return Module.dynCall_fiff(o,l,f,h)}catch(E){if(typeof E!="number"&&E!=="longjmp")throw E;Module.setThrew(1,0)}}function invoke_vi(o,l){try{Module.dynCall_vi(o,l)}catch(f){if(typeof f!="number"&&f!=="longjmp")throw f;Module.setThrew(1,0)}}function invoke_vii(o,l,f){try{Module.dynCall_vii(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_ii(o,l){try{return Module.dynCall_ii(o,l)}catch(f){if(typeof f!="number"&&f!=="longjmp")throw f;Module.setThrew(1,0)}}function invoke_viddi(o,l,f,h,E){try{Module.dynCall_viddi(o,l,f,h,E)}catch(t){if(typeof t!="number"&&t!=="longjmp")throw t;Module.setThrew(1,0)}}function invoke_vidd(o,l,f,h){try{Module.dynCall_vidd(o,l,f,h)}catch(E){if(typeof E!="number"&&E!=="longjmp")throw E;Module.setThrew(1,0)}}function invoke_iiii(o,l,f,h){try{return Module.dynCall_iiii(o,l,f,h)}catch(E){if(typeof E!="number"&&E!=="longjmp")throw E;Module.setThrew(1,0)}}function invoke_diii(o,l,f,h){try{return Module.dynCall_diii(o,l,f,h)}catch(E){if(typeof E!="number"&&E!=="longjmp")throw E;Module.setThrew(1,0)}}function invoke_di(o,l){try{return Module.dynCall_di(o,l)}catch(f){if(typeof f!="number"&&f!=="longjmp")throw f;Module.setThrew(1,0)}}function invoke_iid(o,l,f){try{return Module.dynCall_iid(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_iii(o,l,f){try{return Module.dynCall_iii(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_viiddi(o,l,f,h,E,t){try{Module.dynCall_viiddi(o,l,f,h,E,t)}catch(N){if(typeof N!="number"&&N!=="longjmp")throw N;Module.setThrew(1,0)}}function invoke_viiiiii(o,l,f,h,E,t,N){try{Module.dynCall_viiiiii(o,l,f,h,E,t,N)}catch(F){if(typeof F!="number"&&F!=="longjmp")throw F;Module.setThrew(1,0)}}function invoke_dii(o,l,f){try{return Module.dynCall_dii(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_i(o){try{return Module.dynCall_i(o)}catch(l){if(typeof l!="number"&&l!=="longjmp")throw l;Module.setThrew(1,0)}}function invoke_iiiiii(o,l,f,h,E,t){try{return Module.dynCall_iiiiii(o,l,f,h,E,t)}catch(N){if(typeof N!="number"&&N!=="longjmp")throw N;Module.setThrew(1,0)}}function invoke_viiid(o,l,f,h,E){try{Module.dynCall_viiid(o,l,f,h,E)}catch(t){if(typeof t!="number"&&t!=="longjmp")throw t;Module.setThrew(1,0)}}function invoke_viififi(o,l,f,h,E,t,N){try{Module.dynCall_viififi(o,l,f,h,E,t,N)}catch(F){if(typeof F!="number"&&F!=="longjmp")throw F;Module.setThrew(1,0)}}function invoke_viii(o,l,f,h){try{Module.dynCall_viii(o,l,f,h)}catch(E){if(typeof E!="number"&&E!=="longjmp")throw E;Module.setThrew(1,0)}}function invoke_v(o){try{Module.dynCall_v(o)}catch(l){if(typeof l!="number"&&l!=="longjmp")throw l;Module.setThrew(1,0)}}function invoke_viid(o,l,f,h){try{Module.dynCall_viid(o,l,f,h)}catch(E){if(typeof E!="number"&&E!=="longjmp")throw E;Module.setThrew(1,0)}}function invoke_idd(o,l,f){try{return Module.dynCall_idd(o,l,f)}catch(h){if(typeof h!="number"&&h!=="longjmp")throw h;Module.setThrew(1,0)}}function invoke_viiii(o,l,f,h,E){try{Module.dynCall_viiii(o,l,f,h,E)}catch(t){if(typeof t!="number"&&t!=="longjmp")throw t;Module.setThrew(1,0)}}Module.asmGlobalArg={Math,Int8Array,Int16Array,Int32Array,Uint8Array,Uint16Array,Uint32Array,Float32Array,Float64Array,NaN:NaN,Infinity:1/0},Module.asmLibraryArg={abort,assert,enlargeMemory,getTotalMemory,abortOnCannotGrowMemory,invoke_viiiii,invoke_vif,invoke_vid,invoke_fiff,invoke_vi,invoke_vii,invoke_ii,invoke_viddi,invoke_vidd,invoke_iiii,invoke_diii,invoke_di,invoke_iid,invoke_iii,invoke_viiddi,invoke_viiiiii,invoke_dii,invoke_i,invoke_iiiiii,invoke_viiid,invoke_viififi,invoke_viii,invoke_v,invoke_viid,invoke_idd,invoke_viiii,_emscripten_asm_const_iiiii,_emscripten_asm_const_iiidddddd,_emscripten_asm_const_iiiid,__nbind_reference_external,_emscripten_asm_const_iiiiiiii,_removeAccessorPrefix,_typeModule,__nbind_register_pool,__decorate,_llvm_stackrestore,___cxa_atexit,__extends,__nbind_get_value_object,__ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj,_emscripten_set_main_loop_timing,__nbind_register_primitive,__nbind_register_type,_emscripten_memcpy_big,__nbind_register_function,___setErrNo,__nbind_register_class,__nbind_finish,_abort,_nbind_value,_llvm_stacksave,___syscall54,_defineHidden,_emscripten_set_main_loop,_emscripten_get_now,__nbind_register_callback_signature,_emscripten_asm_const_iiiiii,__nbind_free_external,_emscripten_asm_const_iiii,_emscripten_asm_const_iiididi,___syscall6,_atexit,___syscall140,___syscall146,DYNAMICTOP_PTR,tempDoublePtr,ABORT,STACKTOP,STACK_MAX,cttz_i8,___dso_handle};var asm=function(o,l,f){var h=new o.Int8Array(f),E=new o.Int16Array(f),t=new o.Int32Array(f),N=new o.Uint8Array(f),F=new o.Uint16Array(f),k=new o.Uint32Array(f),x=new o.Float32Array(f),j=new o.Float64Array(f),q=l.DYNAMICTOP_PTR|0,V=l.tempDoublePtr|0,re=l.ABORT|0,y=l.STACKTOP|0,me=l.STACK_MAX|0,De=l.cttz_i8|0,ge=l.___dso_handle|0,ae=0,we=0,he=0,ve=0,ue=o.NaN,Ae=o.Infinity,ze=0,We=0,gt=0,_t=0,Qe=0,ot=0,Ve=o.Math.floor,Pt=o.Math.abs,Jt=o.Math.sqrt,it=o.Math.pow,J=o.Math.cos,ce=o.Math.sin,Re=o.Math.tan,le=o.Math.acos,He=o.Math.asin,dt=o.Math.atan,At=o.Math.atan2,nn=o.Math.exp,an=o.Math.log,On=o.Math.ceil,lr=o.Math.imul,ln=o.Math.min,Vt=o.Math.max,Er=o.Math.clz32,S=o.Math.fround,zt=l.abort,Xn=l.assert,vr=l.enlargeMemory,jr=l.getTotalMemory,fr=l.abortOnCannotGrowMemory,zr=l.invoke_viiiii,Xt=l.invoke_vif,Du=l.invoke_vid,c0=l.invoke_fiff,Ao=l.invoke_vi,Jo=l.invoke_vii,Fs=l.invoke_ii,Zo=l.invoke_viddi,$o=l.invoke_vidd,qt=l.invoke_iiii,xi=l.invoke_diii,lu=l.invoke_di,vi=l.invoke_iid,Dr=l.invoke_iii,el=l.invoke_viiddi,Y0=l.invoke_viiiiii,Bu=l.invoke_dii,K0=l.invoke_i,Kr=l.invoke_iiiiii,Oo=l.invoke_viiid,Mo=l.invoke_viififi,F0=l.invoke_viii,su=l.invoke_v,ki=l.invoke_viid,Ps=l.invoke_idd,Kl=l.invoke_viiii,P0=l._emscripten_asm_const_iiiii,d0=l._emscripten_asm_const_iiidddddd,Hr=l._emscripten_asm_const_iiiid,Ri=l.__nbind_reference_external,X0=l._emscripten_asm_const_iiiiiiii,mi=l._removeAccessorPrefix,en=l._typeModule,In=l.__nbind_register_pool,Ai=l.__decorate,yi=l._llvm_stackrestore,Wt=l.___cxa_atexit,Ru=l.__extends,eu=l.__nbind_get_value_object,Q0=l.__ZN8facebook4yoga14YGNodeToStringEPNSt3__212basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEP6YGNode14YGPrintOptionsj,Yi=l._emscripten_set_main_loop_timing,Xl=l.__nbind_register_primitive,ko=l.__nbind_register_type,li=l._emscripten_memcpy_big,ao=l.__nbind_register_function,Ql=l.___setErrNo,No=l.__nbind_register_class,Is=l.__nbind_finish,$n=l._abort,tl=l._nbind_value,fo=l._llvm_stacksave,I0=l.___syscall54,Sl=l._defineHidden,Lo=l._emscripten_set_main_loop,St=l._emscripten_get_now,Bt=l.__nbind_register_callback_signature,Hn=l._emscripten_asm_const_iiiiii,qr=l.__nbind_free_external,Ki=l._emscripten_asm_const_iiii,Xr=l._emscripten_asm_const_iiididi,Au=l.___syscall6,p0=l._atexit,Ni=l.___syscall140,h0=l.___syscall146,hs=S(0);let Ct=S(0);function co(e){e=e|0;var n=0;return n=y,y=y+e|0,y=y+15&-16,n|0}function nl(){return y|0}function Jl(e){e=e|0,y=e}function Uu(e,n){e=e|0,n=n|0,y=e,me=n}function vs(e,n){e=e|0,n=n|0,ae||(ae=e,we=n)}function b0(e){e=e|0,ot=e}function Q(){return ot|0}function Se(){var e=0,n=0;gr(8104,8,400)|0,gr(8504,408,540)|0,e=9044,n=e+44|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));h[9088]=0,h[9089]=1,t[2273]=0,t[2274]=948,t[2275]=948,Wt(17,8104,ge|0)|0}function Fe(e){e=e|0,ac(e+948|0)}function Le(e){return e=S(e),((mr(e)|0)&2147483647)>>>0>2139095040|0}function pt(e,n,r){e=e|0,n=n|0,r=r|0;e:do if(t[e+(n<<3)+4>>2]|0)e=e+(n<<3)|0;else{if((n|2|0)==3&&t[e+60>>2]|0){e=e+56|0;break}switch(n|0){case 0:case 2:case 4:case 5:{if(t[e+52>>2]|0){e=e+48|0;break e}break}default:}if(t[e+68>>2]|0){e=e+64|0;break}else{e=(n|1|0)==5?948:r;break}}while(0);return e|0}function Yn(e){e=e|0;var n=0;return n=p_(1e3)|0,Cn(e,(n|0)!=0,2456),t[2276]=(t[2276]|0)+1,gr(n|0,8104,1e3)|0,h[e+2>>0]|0&&(t[n+4>>2]=2,t[n+12>>2]=4),t[n+976>>2]=e,n|0}function Cn(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;s=y,y=y+16|0,u=s,n||(t[u>>2]=r,Cl(e,5,3197,u)),y=s}function cr(){return Yn(956)|0}function Si(e){e=e|0;var n=0;return n=pn(1e3)|0,Ou(n,e),Cn(t[e+976>>2]|0,1,2456),t[2276]=(t[2276]|0)+1,t[n+944>>2]=0,n|0}function Ou(e,n){e=e|0,n=n|0;var r=0;gr(e|0,n|0,948)|0,sa(e+948|0,n+948|0),r=e+960|0,e=n+960|0,n=r+40|0;do t[r>>2]=t[e>>2],r=r+4|0,e=e+4|0;while((r|0)<(n|0))}function ju(e){e=e|0;var n=0,r=0,u=0,s=0;if(n=e+944|0,r=t[n>>2]|0,r|0&&(zu(r+948|0,e)|0,t[n>>2]=0),r=wu(e)|0,r|0){n=0;do t[(Ti(e,n)|0)+944>>2]=0,n=n+1|0;while((n|0)!=(r|0))}r=e+948|0,u=t[r>>2]|0,s=e+952|0,n=t[s>>2]|0,(n|0)!=(u|0)&&(t[s>>2]=n+(~((n+-4-u|0)>>>2)<<2)),Fo(r),h_(e),t[2276]=(t[2276]|0)+-1}function zu(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0;u=t[e>>2]|0,w=e+4|0,r=t[w>>2]|0,a=r;e:do if((u|0)==(r|0))s=u,v=4;else for(e=u;;){if((t[e>>2]|0)==(n|0)){s=e,v=4;break e}if(e=e+4|0,(e|0)==(r|0)){e=0;break}}while(0);return(v|0)==4&&((s|0)!=(r|0)?(u=s+4|0,e=a-u|0,n=e>>2,n&&(ky(s|0,u|0,e|0)|0,r=t[w>>2]|0),e=s+(n<<2)|0,(r|0)==(e|0)||(t[w>>2]=r+(~((r+-4-e|0)>>>2)<<2)),e=1):e=0),e|0}function wu(e){return e=e|0,(t[e+952>>2]|0)-(t[e+948>>2]|0)>>2|0}function Ti(e,n){e=e|0,n=n|0;var r=0;return r=t[e+948>>2]|0,(t[e+952>>2]|0)-r>>2>>>0>n>>>0?e=t[r+(n<<2)>>2]|0:e=0,e|0}function Fo(e){e=e|0;var n=0,r=0,u=0,s=0;u=y,y=y+32|0,n=u,s=t[e>>2]|0,r=(t[e+4>>2]|0)-s|0,((t[e+8>>2]|0)-s|0)>>>0>r>>>0&&(s=r>>2,K(n,s,s,e+8|0),ti(e,n),ni(n)),y=u}function Mu(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0;L=wu(e)|0;do if(L|0){if((t[(Ti(e,0)|0)+944>>2]|0)==(e|0)){if(!(zu(e+948|0,n)|0))break;gr(n+400|0,8504,540)|0,t[n+944>>2]=0,Qn(e);break}v=t[(t[e+976>>2]|0)+12>>2]|0,w=e+948|0,T=(v|0)==0,r=0,a=0;do u=t[(t[w>>2]|0)+(a<<2)>>2]|0,(u|0)==(n|0)?Qn(e):(s=Si(u)|0,t[(t[w>>2]|0)+(r<<2)>>2]=s,t[s+944>>2]=e,T||BE[v&15](u,s,e,r),r=r+1|0),a=a+1|0;while((a|0)!=(L|0));if(r>>>0>>0){T=e+948|0,w=e+952|0,v=r,r=t[w>>2]|0;do a=(t[T>>2]|0)+(v<<2)|0,u=a+4|0,s=r-u|0,n=s>>2,n&&(ky(a|0,u|0,s|0)|0,r=t[w>>2]|0),s=r,u=a+(n<<2)|0,(s|0)!=(u|0)&&(r=s+(~((s+-4-u|0)>>>2)<<2)|0,t[w>>2]=r),v=v+1|0;while((v|0)!=(L|0))}}while(0)}function po(e){e=e|0;var n=0,r=0,u=0,s=0;Hu(e,(wu(e)|0)==0,2491),Hu(e,(t[e+944>>2]|0)==0,2545),n=e+948|0,r=t[n>>2]|0,u=e+952|0,s=t[u>>2]|0,(s|0)!=(r|0)&&(t[u>>2]=s+(~((s+-4-r|0)>>>2)<<2)),Fo(n),n=e+976|0,r=t[n>>2]|0,gr(e|0,8104,1e3)|0,h[r+2>>0]|0&&(t[e+4>>2]=2,t[e+12>>2]=4),t[n>>2]=r}function Hu(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;s=y,y=y+16|0,u=s,n||(t[u>>2]=r,pr(e,5,3197,u)),y=s}function Pa(){return t[2276]|0}function v0(){var e=0;return e=p_(20)|0,ia((e|0)!=0,2592),t[2277]=(t[2277]|0)+1,t[e>>2]=t[239],t[e+4>>2]=t[240],t[e+8>>2]=t[241],t[e+12>>2]=t[242],t[e+16>>2]=t[243],e|0}function ia(e,n){e=e|0,n=n|0;var r=0,u=0;u=y,y=y+16|0,r=u,e||(t[r>>2]=n,pr(0,5,3197,r)),y=u}function J0(e){e=e|0,h_(e),t[2277]=(t[2277]|0)+-1}function ua(e,n){e=e|0,n=n|0;var r=0;n?(Hu(e,(wu(e)|0)==0,2629),r=1):(r=0,n=0),t[e+964>>2]=n,t[e+988>>2]=r}function Ia(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,a=u+8|0,s=u+4|0,v=u,t[s>>2]=n,Hu(e,(t[n+944>>2]|0)==0,2709),Hu(e,(t[e+964>>2]|0)==0,2763),ms(e),n=e+948|0,t[v>>2]=(t[n>>2]|0)+(r<<2),t[a>>2]=t[v>>2],S0(n,a,s)|0,t[(t[s>>2]|0)+944>>2]=e,Qn(e),y=u}function ms(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0;if(r=wu(e)|0,r|0&&(t[(Ti(e,0)|0)+944>>2]|0)!=(e|0)){u=t[(t[e+976>>2]|0)+12>>2]|0,s=e+948|0,a=(u|0)==0,n=0;do v=t[(t[s>>2]|0)+(n<<2)>>2]|0,w=Si(v)|0,t[(t[s>>2]|0)+(n<<2)>>2]=w,t[w+944>>2]=e,a||BE[u&15](v,w,e,n),n=n+1|0;while((n|0)!=(r|0))}}function S0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0,Ze=0,Ye=0;Ze=y,y=y+64|0,b=Ze+52|0,w=Ze+48|0,X=Ze+28|0,Be=Ze+24|0,Te=Ze+20|0,ye=Ze,u=t[e>>2]|0,a=u,n=u+((t[n>>2]|0)-a>>2<<2)|0,u=e+4|0,s=t[u>>2]|0,v=e+8|0;do if(s>>>0<(t[v>>2]|0)>>>0){if((n|0)==(s|0)){t[n>>2]=t[r>>2],t[u>>2]=(t[u>>2]|0)+4;break}Wr(e,n,s,n+4|0),n>>>0<=r>>>0&&(r=(t[u>>2]|0)>>>0>r>>>0?r+4|0:r),t[n>>2]=t[r>>2]}else{u=(s-a>>2)+1|0,s=R0(e)|0,s>>>0>>0&&di(e),M=t[e>>2]|0,L=(t[v>>2]|0)-M|0,a=L>>1,K(ye,L>>2>>>0>>1>>>0?a>>>0>>0?u:a:s,n-M>>2,e+8|0),M=ye+8|0,u=t[M>>2]|0,a=ye+12|0,L=t[a>>2]|0,v=L,T=u;do if((u|0)==(L|0)){if(L=ye+4|0,u=t[L>>2]|0,Ye=t[ye>>2]|0,s=Ye,u>>>0<=Ye>>>0){u=v-s>>1,u=(u|0)==0?1:u,K(X,u,u>>>2,t[ye+16>>2]|0),t[Be>>2]=t[L>>2],t[Te>>2]=t[M>>2],t[w>>2]=t[Be>>2],t[b>>2]=t[Te>>2],Di(X,w,b),u=t[ye>>2]|0,t[ye>>2]=t[X>>2],t[X>>2]=u,u=X+4|0,Ye=t[L>>2]|0,t[L>>2]=t[u>>2],t[u>>2]=Ye,u=X+8|0,Ye=t[M>>2]|0,t[M>>2]=t[u>>2],t[u>>2]=Ye,u=X+12|0,Ye=t[a>>2]|0,t[a>>2]=t[u>>2],t[u>>2]=Ye,ni(X),u=t[M>>2]|0;break}a=u,v=((a-s>>2)+1|0)/-2|0,w=u+(v<<2)|0,s=T-a|0,a=s>>2,a&&(ky(w|0,u|0,s|0)|0,u=t[L>>2]|0),Ye=w+(a<<2)|0,t[M>>2]=Ye,t[L>>2]=u+(v<<2),u=Ye}while(0);t[u>>2]=t[r>>2],t[M>>2]=(t[M>>2]|0)+4,n=ft(e,ye,n)|0,ni(ye)}while(0);return y=Ze,n|0}function Qn(e){e=e|0;var n=0;do{if(n=e+984|0,h[n>>0]|0)break;h[n>>0]=1,x[e+504>>2]=S(ue),e=t[e+944>>2]|0}while((e|0)!=0)}function ac(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-4-u|0)>>>2)<<2)),Et(r))}function si(e){return e=e|0,t[e+944>>2]|0}function Jr(e){e=e|0,Hu(e,(t[e+964>>2]|0)!=0,2832),Qn(e)}function Zl(e){return e=e|0,(h[e+984>>0]|0)!=0|0}function oa(e,n){e=e|0,n=n|0,vL(e,n,400)|0&&(gr(e|0,n|0,400)|0,Qn(e))}function pf(e){e=e|0;var n=Ct;return n=S(x[e+44>>2]),e=Le(n)|0,S(e?S(0):n)}function bs(e){e=e|0;var n=Ct;return n=S(x[e+48>>2]),Le(n)|0&&(n=h[(t[e+976>>2]|0)+2>>0]|0?S(1):S(0)),S(n)}function ba(e,n){e=e|0,n=n|0,t[e+980>>2]=n}function Bs(e){return e=e|0,t[e+980>>2]|0}function m0(e,n){e=e|0,n=n|0;var r=0;r=e+4|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function Us(e){return e=e|0,t[e+4>>2]|0}function zi(e,n){e=e|0,n=n|0;var r=0;r=e+8|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function U(e){return e=e|0,t[e+8>>2]|0}function H(e,n){e=e|0,n=n|0;var r=0;r=e+12|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function Y(e){return e=e|0,t[e+12>>2]|0}function ee(e,n){e=e|0,n=n|0;var r=0;r=e+16|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function Ce(e){return e=e|0,t[e+16>>2]|0}function _e(e,n){e=e|0,n=n|0;var r=0;r=e+20|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function Oe(e){return e=e|0,t[e+20>>2]|0}function $(e,n){e=e|0,n=n|0;var r=0;r=e+24|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function Ne(e){return e=e|0,t[e+24>>2]|0}function Je(e,n){e=e|0,n=n|0;var r=0;r=e+28|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function vt(e){return e=e|0,t[e+28>>2]|0}function oe(e,n){e=e|0,n=n|0;var r=0;r=e+32|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function qe(e){return e=e|0,t[e+32>>2]|0}function rt(e,n){e=e|0,n=n|0;var r=0;r=e+36|0,(t[r>>2]|0)!=(n|0)&&(t[r>>2]=n,Qn(e))}function xt(e){return e=e|0,t[e+36>>2]|0}function kt(e,n){e=e|0,n=S(n);var r=0;r=e+40|0,S(x[r>>2])!=n&&(x[r>>2]=n,Qn(e))}function bt(e,n){e=e|0,n=S(n);var r=0;r=e+44|0,S(x[r>>2])!=n&&(x[r>>2]=n,Qn(e))}function sn(e,n){e=e|0,n=S(n);var r=0;r=e+48|0,S(x[r>>2])!=n&&(x[r>>2]=n,Qn(e))}function rn(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+52|0,s=e+56|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function Ft(e,n){e=e|0,n=S(n);var r=0,u=0;u=e+52|0,r=e+56|0,S(x[u>>2])==n&&(t[r>>2]|0)==2||(x[u>>2]=n,u=Le(n)|0,t[r>>2]=u?3:2,Qn(e))}function Dn(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+52|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function dr(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=(a^1)&1,s=e+132+(n<<3)|0,n=e+132+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function er(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=a?0:2,s=e+132+(n<<3)|0,n=e+132+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function Cr(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=n+132+(r<<3)|0,n=t[u+4>>2]|0,r=e,t[r>>2]=t[u>>2],t[r+4>>2]=n}function Rn(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=(a^1)&1,s=e+60+(n<<3)|0,n=e+60+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function Nr(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=a?0:2,s=e+60+(n<<3)|0,n=e+60+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function y0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=n+60+(r<<3)|0,n=t[u+4>>2]|0,r=e,t[r>>2]=t[u>>2],t[r+4>>2]=n}function Lr(e,n){e=e|0,n=n|0;var r=0;r=e+60+(n<<3)+4|0,(t[r>>2]|0)!=3&&(x[e+60+(n<<3)>>2]=S(ue),t[r>>2]=3,Qn(e))}function ut(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=(a^1)&1,s=e+204+(n<<3)|0,n=e+204+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function wt(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=a?0:2,s=e+204+(n<<3)|0,n=e+204+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function et(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=n+204+(r<<3)|0,n=t[u+4>>2]|0,r=e,t[r>>2]=t[u>>2],t[r+4>>2]=n}function It(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0,a=0;a=Le(r)|0,u=(a^1)&1,s=e+276+(n<<3)|0,n=e+276+(n<<3)+4|0,a|S(x[s>>2])==r&&(t[n>>2]|0)==(u|0)||(x[s>>2]=r,t[n>>2]=u,Qn(e))}function un(e,n){return e=e|0,n=n|0,S(x[e+276+(n<<3)>>2])}function fn(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+348|0,s=e+352|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function Jn(e,n){e=e|0,n=S(n);var r=0,u=0;u=e+348|0,r=e+352|0,S(x[u>>2])==n&&(t[r>>2]|0)==2||(x[u>>2]=n,u=Le(n)|0,t[r>>2]=u?3:2,Qn(e))}function wr(e){e=e|0;var n=0;n=e+352|0,(t[n>>2]|0)!=3&&(x[e+348>>2]=S(ue),t[n>>2]=3,Qn(e))}function au(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+348|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function ku(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+356|0,s=e+360|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function T0(e,n){e=e|0,n=S(n);var r=0,u=0;u=e+356|0,r=e+360|0,S(x[u>>2])==n&&(t[r>>2]|0)==2||(x[u>>2]=n,u=Le(n)|0,t[r>>2]=u?3:2,Qn(e))}function Z0(e){e=e|0;var n=0;n=e+360|0,(t[n>>2]|0)!=3&&(x[e+356>>2]=S(ue),t[n>>2]=3,Qn(e))}function Nu(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+356|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function gi(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+364|0,s=e+368|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function Po(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=a?0:2,u=e+364|0,s=e+368|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function rl(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+364|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function hf(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+372|0,s=e+376|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function Tl(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=a?0:2,u=e+372|0,s=e+376|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function vf(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+372|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function Io(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+380|0,s=e+384|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function ys(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=a?0:2,u=e+380|0,s=e+384|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function js(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+380|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function bo(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=(a^1)&1,u=e+388|0,s=e+392|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function Bo(e,n){e=e|0,n=S(n);var r=0,u=0,s=0,a=0;a=Le(n)|0,r=a?0:2,u=e+388|0,s=e+392|0,a|S(x[u>>2])==n&&(t[s>>2]|0)==(r|0)||(x[u>>2]=n,t[s>>2]=r,Qn(e))}function gs(e,n){e=e|0,n=n|0;var r=0,u=0;u=n+388|0,r=t[u+4>>2]|0,n=e,t[n>>2]=t[u>>2],t[n+4>>2]=r}function Xu(e,n){e=e|0,n=S(n);var r=0;r=e+396|0,S(x[r>>2])!=n&&(x[r>>2]=n,Qn(e))}function Su(e){return e=e|0,S(x[e+396>>2])}function _i(e){return e=e|0,S(x[e+400>>2])}function C0(e){return e=e|0,S(x[e+404>>2])}function $0(e){return e=e|0,S(x[e+408>>2])}function Uo(e){return e=e|0,S(x[e+412>>2])}function la(e){return e=e|0,S(x[e+416>>2])}function $l(e){return e=e|0,S(x[e+420>>2])}function tu(e,n){switch(e=e|0,n=n|0,Hu(e,(n|0)<6,2918),n|0){case 0:{n=(t[e+496>>2]|0)==2?5:4;break}case 2:{n=(t[e+496>>2]|0)==2?4:5;break}default:}return S(x[e+424+(n<<2)>>2])}function Zr(e,n){switch(e=e|0,n=n|0,Hu(e,(n|0)<6,2918),n|0){case 0:{n=(t[e+496>>2]|0)==2?5:4;break}case 2:{n=(t[e+496>>2]|0)==2?4:5;break}default:}return S(x[e+448+(n<<2)>>2])}function ho(e,n){switch(e=e|0,n=n|0,Hu(e,(n|0)<6,2918),n|0){case 0:{n=(t[e+496>>2]|0)==2?5:4;break}case 2:{n=(t[e+496>>2]|0)==2?4:5;break}default:}return S(x[e+472+(n<<2)>>2])}function Bi(e,n){e=e|0,n=n|0;var r=0,u=Ct;return r=t[e+4>>2]|0,(r|0)==(t[n+4>>2]|0)?r?(u=S(x[e>>2]),e=S(Pt(S(u-S(x[n>>2]))))>2]=0,t[u+4>>2]=0,t[u+8>>2]=0,Q0(u|0,e|0,n|0,0),pr(e,3,(h[u+11>>0]|0)<0?t[u>>2]|0:u,r),BL(u),y=r}function eo(e,n,r,u){e=S(e),n=S(n),r=r|0,u=u|0;var s=Ct;e=S(e*n),s=S(NE(e,S(1)));do if(Ci(s,S(0))|0)e=S(e-s);else{if(e=S(e-s),Ci(s,S(1))|0){e=S(e+S(1));break}if(r){e=S(e+S(1));break}u||(s>S(.5)?s=S(1):(u=Ci(s,S(.5))|0,s=S(u?1:0)),e=S(e+s))}while(0);return S(e/n)}function to(e,n,r,u,s,a,v,w,T,L,M,b,X){e=e|0,n=S(n),r=r|0,u=S(u),s=s|0,a=S(a),v=v|0,w=S(w),T=S(T),L=S(L),M=S(M),b=S(b),X=X|0;var Be=0,Te=Ct,ye=Ct,Ze=Ct,Ye=Ct,ct=Ct,ke=Ct;return T>2]),Te!=S(0))?(Ze=S(eo(n,Te,0,0)),Ye=S(eo(u,Te,0,0)),ye=S(eo(a,Te,0,0)),Te=S(eo(w,Te,0,0))):(ye=a,Ze=n,Te=w,Ye=u),(s|0)==(e|0)?Be=Ci(ye,Ze)|0:Be=0,(v|0)==(r|0)?X=Ci(Te,Ye)|0:X=0,!Be&&(ct=S(n-M),!(xe(e,ct,T)|0))&&!(tt(e,ct,s,T)|0)?Be=Ke(e,ct,s,a,T)|0:Be=1,!X&&(ke=S(u-b),!(xe(r,ke,L)|0))&&!(tt(r,ke,v,L)|0)?X=Ke(r,ke,v,w,L)|0:X=1,X=Be&X),X|0}function xe(e,n,r){return e=e|0,n=S(n),r=S(r),(e|0)==1?e=Ci(n,r)|0:e=0,e|0}function tt(e,n,r,u){return e=e|0,n=S(n),r=r|0,u=S(u),(e|0)==2&(r|0)==0?n>=u?e=1:e=Ci(n,u)|0:e=0,e|0}function Ke(e,n,r,u,s){return e=e|0,n=S(n),r=r|0,u=S(u),s=S(s),(e|0)==2&(r|0)==2&u>n?s<=n?e=1:e=Ci(n,s)|0:e=0,e|0}function Yt(e,n,r,u,s,a,v,w,T,L,M){e=e|0,n=S(n),r=S(r),u=u|0,s=s|0,a=a|0,v=S(v),w=S(w),T=T|0,L=L|0,M=M|0;var b=0,X=0,Be=0,Te=0,ye=Ct,Ze=Ct,Ye=0,ct=0,ke=0,Ie=0,Zt=0,Br=0,Pn=0,gn=0,_r=0,Pr=0,kn=0,uu=Ct,os=Ct,ls=Ct,ss=0,ea=0;kn=y,y=y+160|0,gn=kn+152|0,Pn=kn+120|0,Br=kn+104|0,ke=kn+72|0,Te=kn+56|0,Zt=kn+8|0,ct=kn,Ie=(t[2279]|0)+1|0,t[2279]=Ie,_r=e+984|0,(h[_r>>0]|0)!=0&&(t[e+512>>2]|0)!=(t[2278]|0)?Ye=4:(t[e+516>>2]|0)==(u|0)?Pr=0:Ye=4,(Ye|0)==4&&(t[e+520>>2]=0,t[e+924>>2]=-1,t[e+928>>2]=-1,x[e+932>>2]=S(-1),x[e+936>>2]=S(-1),Pr=1);e:do if(t[e+964>>2]|0)if(ye=S(Kt(e,2,v)),Ze=S(Kt(e,0,v)),b=e+916|0,ls=S(x[b>>2]),os=S(x[e+920>>2]),uu=S(x[e+932>>2]),to(s,n,a,r,t[e+924>>2]|0,ls,t[e+928>>2]|0,os,uu,S(x[e+936>>2]),ye,Ze,M)|0)Ye=22;else if(Be=t[e+520>>2]|0,!Be)Ye=21;else for(X=0;;){if(b=e+524+(X*24|0)|0,uu=S(x[b>>2]),os=S(x[e+524+(X*24|0)+4>>2]),ls=S(x[e+524+(X*24|0)+16>>2]),to(s,n,a,r,t[e+524+(X*24|0)+8>>2]|0,uu,t[e+524+(X*24|0)+12>>2]|0,os,ls,S(x[e+524+(X*24|0)+20>>2]),ye,Ze,M)|0){Ye=22;break e}if(X=X+1|0,X>>>0>=Be>>>0){Ye=21;break}}else{if(T){if(b=e+916|0,!(Ci(S(x[b>>2]),n)|0)){Ye=21;break}if(!(Ci(S(x[e+920>>2]),r)|0)){Ye=21;break}if((t[e+924>>2]|0)!=(s|0)){Ye=21;break}b=(t[e+928>>2]|0)==(a|0)?b:0,Ye=22;break}if(Be=t[e+520>>2]|0,!Be)Ye=21;else for(X=0;;){if(b=e+524+(X*24|0)|0,Ci(S(x[b>>2]),n)|0&&Ci(S(x[e+524+(X*24|0)+4>>2]),r)|0&&(t[e+524+(X*24|0)+8>>2]|0)==(s|0)&&(t[e+524+(X*24|0)+12>>2]|0)==(a|0)){Ye=22;break e}if(X=X+1|0,X>>>0>=Be>>>0){Ye=21;break}}}while(0);do if((Ye|0)==21)h[11697]|0?(b=0,Ye=28):(b=0,Ye=31);else if((Ye|0)==22){if(X=(h[11697]|0)!=0,!((b|0)!=0&(Pr^1)))if(X){Ye=28;break}else{Ye=31;break}Te=b+16|0,t[e+908>>2]=t[Te>>2],Be=b+20|0,t[e+912>>2]=t[Be>>2],(h[11698]|0)==0|X^1||(t[ct>>2]=Ei(Ie)|0,t[ct+4>>2]=Ie,pr(e,4,2972,ct),X=t[e+972>>2]|0,X|0&&P1[X&127](e),s=bn(s,T)|0,a=bn(a,T)|0,ea=+S(x[Te>>2]),ss=+S(x[Be>>2]),t[Zt>>2]=s,t[Zt+4>>2]=a,j[Zt+8>>3]=+n,j[Zt+16>>3]=+r,j[Zt+24>>3]=ea,j[Zt+32>>3]=ss,t[Zt+40>>2]=L,pr(e,4,2989,Zt))}while(0);return(Ye|0)==28&&(X=Ei(Ie)|0,t[Te>>2]=X,t[Te+4>>2]=Ie,t[Te+8>>2]=Pr?3047:11699,pr(e,4,3038,Te),X=t[e+972>>2]|0,X|0&&P1[X&127](e),Zt=bn(s,T)|0,Ye=bn(a,T)|0,t[ke>>2]=Zt,t[ke+4>>2]=Ye,j[ke+8>>3]=+n,j[ke+16>>3]=+r,t[ke+24>>2]=L,pr(e,4,3049,ke),Ye=31),(Ye|0)==31&&(mu(e,n,r,u,s,a,v,w,T,M),h[11697]|0&&(X=t[2279]|0,Zt=Ei(X)|0,t[Br>>2]=Zt,t[Br+4>>2]=X,t[Br+8>>2]=Pr?3047:11699,pr(e,4,3083,Br),X=t[e+972>>2]|0,X|0&&P1[X&127](e),Zt=bn(s,T)|0,Br=bn(a,T)|0,ss=+S(x[e+908>>2]),ea=+S(x[e+912>>2]),t[Pn>>2]=Zt,t[Pn+4>>2]=Br,j[Pn+8>>3]=ss,j[Pn+16>>3]=ea,t[Pn+24>>2]=L,pr(e,4,3092,Pn)),t[e+516>>2]=u,b||(X=e+520|0,b=t[X>>2]|0,(b|0)==16&&(h[11697]|0&&pr(e,4,3124,gn),t[X>>2]=0,b=0),T?b=e+916|0:(t[X>>2]=b+1,b=e+524+(b*24|0)|0),x[b>>2]=n,x[b+4>>2]=r,t[b+8>>2]=s,t[b+12>>2]=a,t[b+16>>2]=t[e+908>>2],t[b+20>>2]=t[e+912>>2],b=0)),T&&(t[e+416>>2]=t[e+908>>2],t[e+420>>2]=t[e+912>>2],h[e+985>>0]=1,h[_r>>0]=0),t[2279]=(t[2279]|0)+-1,t[e+512>>2]=t[2278],y=kn,Pr|(b|0)==0|0}function Kt(e,n,r){e=e|0,n=n|0,r=S(r);var u=Ct;return u=S(Hi(e,n,r)),S(u+S(A0(e,n,r)))}function pr(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=y,y=y+16|0,s=a,t[s>>2]=u,e?u=t[e+976>>2]|0:u=0,zs(u,e,n,r,s),y=a}function Ei(e){return e=e|0,(e>>>0>60?3201:3201+(60-e)|0)|0}function bn(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;return s=y,y=y+32|0,r=s+12|0,u=s,t[r>>2]=t[254],t[r+4>>2]=t[255],t[r+8>>2]=t[256],t[u>>2]=t[257],t[u+4>>2]=t[258],t[u+8>>2]=t[259],(e|0)>2?e=11699:e=t[(n?u:r)+(e<<2)>>2]|0,y=s,e|0}function mu(e,n,r,u,s,a,v,w,T,L){e=e|0,n=S(n),r=S(r),u=u|0,s=s|0,a=a|0,v=S(v),w=S(w),T=T|0,L=L|0;var M=0,b=0,X=0,Be=0,Te=Ct,ye=Ct,Ze=Ct,Ye=Ct,ct=Ct,ke=Ct,Ie=Ct,Zt=0,Br=0,Pn=0,gn=Ct,_r=Ct,Pr=0,kn=Ct,uu=0,os=0,ls=0,ss=0,ea=0,t2=0,n2=0,uf=0,r2=0,Fc=0,Pc=0,i2=0,u2=0,o2=0,pi=0,of=0,l2=0,Yf=0,s2=Ct,a2=Ct,Ic=Ct,bc=Ct,Kf=Ct,ql=0,La=0,Ns=0,lf=0,b1=0,B1=Ct,Bc=Ct,U1=Ct,j1=Ct,Wl=Ct,El=Ct,sf=0,hu=Ct,z1=Ct,as=Ct,Xf=Ct,fs=Ct,Qf=Ct,H1=0,q1=0,Jf=Ct,Vl=Ct,af=0,W1=0,V1=0,G1=0,Sr=Ct,bu=0,Dl=0,cs=0,Gl=0,Or=0,Bn=0,ff=0,mn=Ct,Y1=0,a0=0;ff=y,y=y+16|0,ql=ff+12|0,La=ff+8|0,Ns=ff+4|0,lf=ff,Hu(e,(s|0)==0|(Le(n)|0)^1,3326),Hu(e,(a|0)==0|(Le(r)|0)^1,3406),Dl=xl(e,u)|0,t[e+496>>2]=Dl,Or=B0(2,Dl)|0,Bn=B0(0,Dl)|0,x[e+440>>2]=S(Hi(e,Or,v)),x[e+444>>2]=S(A0(e,Or,v)),x[e+428>>2]=S(Hi(e,Bn,v)),x[e+436>>2]=S(A0(e,Bn,v)),x[e+464>>2]=S(O0(e,Or)),x[e+468>>2]=S(vo(e,Or)),x[e+452>>2]=S(O0(e,Bn)),x[e+460>>2]=S(vo(e,Bn)),x[e+488>>2]=S(Fu(e,Or,v)),x[e+492>>2]=S(Ju(e,Or,v)),x[e+476>>2]=S(Fu(e,Bn,v)),x[e+484>>2]=S(Ju(e,Bn,v));do if(t[e+964>>2]|0)es(e,n,r,s,a,v,w);else{if(cs=e+948|0,Gl=(t[e+952>>2]|0)-(t[cs>>2]|0)>>2,!Gl){_s(e,n,r,s,a,v,w);break}if(!T&&aa(e,n,r,s,a,v,w)|0)break;ms(e),of=e+508|0,h[of>>0]=0,Or=B0(t[e+4>>2]|0,Dl)|0,Bn=gf(Or,Dl)|0,bu=qi(Or)|0,l2=t[e+8>>2]|0,W1=e+28|0,Yf=(t[W1>>2]|0)!=0,fs=bu?v:w,Jf=bu?w:v,s2=S(Zu(e,Or,v)),a2=S(Es(e,Or,v)),Te=S(Zu(e,Bn,v)),Qf=S(Rr(e,Or,v)),Vl=S(Rr(e,Bn,v)),Pn=bu?s:a,af=bu?a:s,Sr=bu?Qf:Vl,ct=bu?Vl:Qf,Xf=S(Kt(e,2,v)),Ye=S(Kt(e,0,v)),ye=S(S(xn(e+364|0,v))-Sr),Ze=S(S(xn(e+380|0,v))-Sr),ke=S(S(xn(e+372|0,w))-ct),Ie=S(S(xn(e+388|0,w))-ct),Ic=bu?ye:ke,bc=bu?Ze:Ie,Xf=S(n-Xf),n=S(Xf-Sr),Le(n)|0?Sr=n:Sr=S(xu(S(Kp(n,Ze)),ye)),z1=S(r-Ye),n=S(z1-ct),Le(n)|0?as=n:as=S(xu(S(Kp(n,Ie)),ke)),ye=bu?Sr:as,hu=bu?as:Sr;e:do if((Pn|0)==1)for(u=0,b=0;;){if(M=Ti(e,b)|0,!u)S(nu(M))>S(0)&&S(fu(M))>S(0)?u=M:u=0;else if(no(M)|0){Be=0;break e}if(b=b+1|0,b>>>0>=Gl>>>0){Be=u;break}}else Be=0;while(0);Zt=Be+500|0,Br=Be+504|0,u=0,M=0,n=S(0),X=0;do{if(b=t[(t[cs>>2]|0)+(X<<2)>>2]|0,(t[b+36>>2]|0)==1)Li(b),h[b+985>>0]=1,h[b+984>>0]=0;else{Qr(b),T&&x0(b,xl(b,Dl)|0,ye,hu,Sr);do if((t[b+24>>2]|0)!=1)if((b|0)==(Be|0)){t[Zt>>2]=t[2278],x[Br>>2]=S(0);break}else{ei(e,b,Sr,s,as,Sr,as,a,Dl,L);break}else M|0&&(t[M+960>>2]=b),t[b+960>>2]=0,M=b,u=(u|0)==0?b:u;while(0);El=S(x[b+504>>2]),n=S(n+S(El+S(Kt(b,Or,Sr))))}X=X+1|0}while((X|0)!=(Gl|0));for(ls=n>ye,sf=Yf&((Pn|0)==2&ls)?1:Pn,uu=(af|0)==1,ea=uu&(T^1),t2=(sf|0)==1,n2=(sf|0)==2,uf=976+(Or<<2)|0,r2=(af|2|0)==2,o2=uu&(Yf^1),Fc=1040+(Bn<<2)|0,Pc=1040+(Or<<2)|0,i2=976+(Bn<<2)|0,u2=(af|0)!=1,ls=Yf&((Pn|0)!=0&ls),os=e+976|0,uu=uu^1,n=ye,Pr=0,ss=0,El=S(0),Kf=S(0);;){e:do if(Pr>>>0>>0)for(Br=t[cs>>2]|0,X=0,Ie=S(0),ke=S(0),Ze=S(0),ye=S(0),b=0,M=0,Be=Pr;;){if(Zt=t[Br+(Be<<2)>>2]|0,(t[Zt+36>>2]|0)!=1&&(t[Zt+940>>2]=ss,(t[Zt+24>>2]|0)!=1)){if(Ye=S(Kt(Zt,Or,Sr)),pi=t[uf>>2]|0,r=S(xn(Zt+380+(pi<<3)|0,fs)),ct=S(x[Zt+504>>2]),r=S(Kp(r,ct)),r=S(xu(S(xn(Zt+364+(pi<<3)|0,fs)),r)),Yf&(X|0)!=0&S(Ye+S(ke+r))>n){a=X,Ye=Ie,Pn=Be;break e}Ye=S(Ye+r),r=S(ke+Ye),Ye=S(Ie+Ye),no(Zt)|0&&(Ze=S(Ze+S(nu(Zt))),ye=S(ye-S(ct*S(fu(Zt))))),M|0&&(t[M+960>>2]=Zt),t[Zt+960>>2]=0,X=X+1|0,M=Zt,b=(b|0)==0?Zt:b}else Ye=Ie,r=ke;if(Be=Be+1|0,Be>>>0>>0)Ie=Ye,ke=r;else{a=X,Pn=Be;break}}else a=0,Ye=S(0),Ze=S(0),ye=S(0),b=0,Pn=Pr;while(0);pi=Ze>S(0)&ZeS(0)&yebc&((Le(bc)|0)^1))n=bc,pi=51;else if(h[(t[os>>2]|0)+3>>0]|0)pi=51;else{if(gn!=S(0)&&S(nu(e))!=S(0)){pi=53;break}n=Ye,pi=53}while(0);if((pi|0)==51&&(pi=0,Le(n)|0?pi=53:(_r=S(n-Ye),kn=n)),(pi|0)==53&&(pi=0,Ye>2]|0,Be=_rS(0),ke=S(_r/gn),Ze=S(0),Ye=S(0),n=S(0),M=b;do r=S(xn(M+380+(X<<3)|0,fs)),ye=S(xn(M+364+(X<<3)|0,fs)),ye=S(Kp(r,S(xu(ye,S(x[M+504>>2]))))),Be?(r=S(ye*S(fu(M))),r!=S(-0)&&(mn=S(ye-S(ct*r)),B1=S(Kn(M,Or,mn,kn,Sr)),mn!=B1)&&(Ze=S(Ze-S(B1-ye)),n=S(n+r))):Zt&&(Bc=S(nu(M)),Bc!=S(0))&&(mn=S(ye+S(ke*Bc)),U1=S(Kn(M,Or,mn,kn,Sr)),mn!=U1)&&(Ze=S(Ze-S(U1-ye)),Ye=S(Ye-Bc)),M=t[M+960>>2]|0;while((M|0)!=0);if(n=S(Ie+n),ye=S(_r+Ze),b1)n=S(0);else{ct=S(gn+Ye),Be=t[uf>>2]|0,Zt=yeS(0),ct=S(ye/ct),n=S(0);do{mn=S(xn(b+380+(Be<<3)|0,fs)),Ze=S(xn(b+364+(Be<<3)|0,fs)),Ze=S(Kp(mn,S(xu(Ze,S(x[b+504>>2]))))),Zt?(mn=S(Ze*S(fu(b))),ye=S(-mn),mn!=S(-0)?(mn=S(ke*ye),ye=S(Kn(b,Or,S(Ze+(Br?ye:mn)),kn,Sr))):ye=Ze):X&&(j1=S(nu(b)),j1!=S(0))?ye=S(Kn(b,Or,S(Ze+S(ct*j1)),kn,Sr)):ye=Ze,n=S(n-S(ye-Ze)),Ye=S(Kt(b,Or,Sr)),r=S(Kt(b,Bn,Sr)),ye=S(ye+Ye),x[La>>2]=ye,t[lf>>2]=1,Ze=S(x[b+396>>2]);e:do if(Le(Ze)|0){M=Le(hu)|0;do if(!M){if(ls|(qu(b,Bn,hu)|0|uu)||($u(e,b)|0)!=4||(t[(g0(b,Bn)|0)+4>>2]|0)==3||(t[(_0(b,Bn)|0)+4>>2]|0)==3)break;x[ql>>2]=hu,t[Ns>>2]=1;break e}while(0);if(qu(b,Bn,hu)|0){M=t[b+992+(t[i2>>2]<<2)>>2]|0,mn=S(r+S(xn(M,hu))),x[ql>>2]=mn,M=u2&(t[M+4>>2]|0)==2,t[Ns>>2]=((Le(mn)|0|M)^1)&1;break}else{x[ql>>2]=hu,t[Ns>>2]=M?0:2;break}}else mn=S(ye-Ye),gn=S(mn/Ze),mn=S(Ze*mn),t[Ns>>2]=1,x[ql>>2]=S(r+(bu?gn:mn));while(0);Ln(b,Or,kn,Sr,lf,La),Ln(b,Bn,hu,Sr,Ns,ql);do if(!(qu(b,Bn,hu)|0)&&($u(e,b)|0)==4){if((t[(g0(b,Bn)|0)+4>>2]|0)==3){M=0;break}M=(t[(_0(b,Bn)|0)+4>>2]|0)!=3}else M=0;while(0);mn=S(x[La>>2]),gn=S(x[ql>>2]),Y1=t[lf>>2]|0,a0=t[Ns>>2]|0,Yt(b,bu?mn:gn,bu?gn:mn,Dl,bu?Y1:a0,bu?a0:Y1,Sr,as,T&(M^1),3488,L)|0,h[of>>0]=h[of>>0]|h[b+508>>0],b=t[b+960>>2]|0}while((b|0)!=0)}}else n=S(0);if(n=S(_r+n),a0=n>0]=a0|N[of>>0],n2&n>S(0)?(M=t[uf>>2]|0,(t[e+364+(M<<3)+4>>2]|0)!=0&&(Wl=S(xn(e+364+(M<<3)|0,fs)),Wl>=S(0))?ye=S(xu(S(0),S(Wl-S(kn-n)))):ye=S(0)):ye=n,Zt=Pr>>>0>>0,Zt){Be=t[cs>>2]|0,X=Pr,M=0;do b=t[Be+(X<<2)>>2]|0,t[b+24>>2]|0||(M=((t[(g0(b,Or)|0)+4>>2]|0)==3&1)+M|0,M=M+((t[(_0(b,Or)|0)+4>>2]|0)==3&1)|0),X=X+1|0;while((X|0)!=(Pn|0));M?(Ye=S(0),r=S(0)):pi=101}else pi=101;e:do if((pi|0)==101)switch(pi=0,l2|0){case 1:{M=0,Ye=S(ye*S(.5)),r=S(0);break e}case 2:{M=0,Ye=ye,r=S(0);break e}case 3:{if(a>>>0<=1){M=0,Ye=S(0),r=S(0);break e}r=S((a+-1|0)>>>0),M=0,Ye=S(0),r=S(S(xu(ye,S(0)))/r);break e}case 5:{r=S(ye/S((a+1|0)>>>0)),M=0,Ye=r;break e}case 4:{r=S(ye/S(a>>>0)),M=0,Ye=S(r*S(.5));break e}default:{M=0,Ye=S(0),r=S(0);break e}}while(0);if(n=S(s2+Ye),Zt){Ze=S(ye/S(M|0)),X=t[cs>>2]|0,b=Pr,ye=S(0);do{M=t[X+(b<<2)>>2]|0;e:do if((t[M+36>>2]|0)!=1){switch(t[M+24>>2]|0){case 1:{if(fe(M,Or)|0){if(!T)break e;mn=S(ie(M,Or,kn)),mn=S(mn+S(O0(e,Or))),mn=S(mn+S(Hi(M,Or,Sr))),x[M+400+(t[Pc>>2]<<2)>>2]=mn;break e}break}case 0:if(a0=(t[(g0(M,Or)|0)+4>>2]|0)==3,mn=S(Ze+n),n=a0?mn:n,T&&(a0=M+400+(t[Pc>>2]<<2)|0,x[a0>>2]=S(n+S(x[a0>>2]))),a0=(t[(_0(M,Or)|0)+4>>2]|0)==3,mn=S(Ze+n),n=a0?mn:n,ea){mn=S(r+S(Kt(M,Or,Sr))),ye=hu,n=S(n+S(mn+S(x[M+504>>2])));break e}else{n=S(n+S(r+S(Pe(M,Or,Sr)))),ye=S(xu(ye,S(Pe(M,Bn,Sr))));break e}default:}T&&(mn=S(Ye+S(O0(e,Or))),a0=M+400+(t[Pc>>2]<<2)|0,x[a0>>2]=S(mn+S(x[a0>>2])))}while(0);b=b+1|0}while((b|0)!=(Pn|0))}else ye=S(0);if(r=S(a2+n),r2?Ye=S(S(Kn(e,Bn,S(Vl+ye),Jf,v))-Vl):Ye=hu,Ze=S(S(Kn(e,Bn,S(Vl+(o2?hu:ye)),Jf,v))-Vl),Zt&T){b=Pr;do{X=t[(t[cs>>2]|0)+(b<<2)>>2]|0;do if((t[X+36>>2]|0)!=1){if((t[X+24>>2]|0)==1){if(fe(X,Bn)|0){if(mn=S(ie(X,Bn,hu)),mn=S(mn+S(O0(e,Bn))),mn=S(mn+S(Hi(X,Bn,Sr))),M=t[Fc>>2]|0,x[X+400+(M<<2)>>2]=mn,!(Le(mn)|0))break}else M=t[Fc>>2]|0;mn=S(O0(e,Bn)),x[X+400+(M<<2)>>2]=S(mn+S(Hi(X,Bn,Sr)));break}M=$u(e,X)|0;do if((M|0)==4){if((t[(g0(X,Bn)|0)+4>>2]|0)==3){pi=139;break}if((t[(_0(X,Bn)|0)+4>>2]|0)==3){pi=139;break}if(qu(X,Bn,hu)|0){n=Te;break}Y1=t[X+908+(t[uf>>2]<<2)>>2]|0,t[ql>>2]=Y1,n=S(x[X+396>>2]),a0=Le(n)|0,ye=(t[V>>2]=Y1,S(x[V>>2])),a0?n=Ze:(_r=S(Kt(X,Bn,Sr)),mn=S(ye/n),n=S(n*ye),n=S(_r+(bu?mn:n))),x[La>>2]=n,x[ql>>2]=S(S(Kt(X,Or,Sr))+ye),t[Ns>>2]=1,t[lf>>2]=1,Ln(X,Or,kn,Sr,Ns,ql),Ln(X,Bn,hu,Sr,lf,La),n=S(x[ql>>2]),_r=S(x[La>>2]),mn=bu?n:_r,n=bu?_r:n,a0=((Le(mn)|0)^1)&1,Yt(X,mn,n,Dl,a0,((Le(n)|0)^1)&1,Sr,as,1,3493,L)|0,n=Te}else pi=139;while(0);e:do if((pi|0)==139){pi=0,n=S(Ye-S(Pe(X,Bn,Sr)));do if((t[(g0(X,Bn)|0)+4>>2]|0)==3){if((t[(_0(X,Bn)|0)+4>>2]|0)!=3)break;n=S(Te+S(xu(S(0),S(n*S(.5)))));break e}while(0);if((t[(_0(X,Bn)|0)+4>>2]|0)==3){n=Te;break}if((t[(g0(X,Bn)|0)+4>>2]|0)==3){n=S(Te+S(xu(S(0),n)));break}switch(M|0){case 1:{n=Te;break e}case 2:{n=S(Te+S(n*S(.5)));break e}default:{n=S(Te+n);break e}}}while(0);mn=S(El+n),a0=X+400+(t[Fc>>2]<<2)|0,x[a0>>2]=S(mn+S(x[a0>>2]))}while(0);b=b+1|0}while((b|0)!=(Pn|0))}if(El=S(El+Ze),Kf=S(xu(Kf,r)),a=ss+1|0,Pn>>>0>=Gl>>>0)break;n=kn,Pr=Pn,ss=a}do if(T){if(M=a>>>0>1,!M&&!(Me(e)|0))break;if(!(Le(hu)|0)){n=S(hu-El);e:do switch(t[e+12>>2]|0){case 3:{Te=S(Te+n),ke=S(0);break}case 2:{Te=S(Te+S(n*S(.5))),ke=S(0);break}case 4:{hu>El?ke=S(n/S(a>>>0)):ke=S(0);break}case 7:if(hu>El){Te=S(Te+S(n/S(a<<1>>>0))),ke=S(n/S(a>>>0)),ke=M?ke:S(0);break e}else{Te=S(Te+S(n*S(.5))),ke=S(0);break e}case 6:{ke=S(n/S(ss>>>0)),ke=hu>El&M?ke:S(0);break}default:ke=S(0)}while(0);if(a|0)for(Zt=1040+(Bn<<2)|0,Br=976+(Bn<<2)|0,Be=0,b=0;;){e:do if(b>>>0>>0)for(ye=S(0),Ze=S(0),n=S(0),X=b;;){M=t[(t[cs>>2]|0)+(X<<2)>>2]|0;do if((t[M+36>>2]|0)!=1&&(t[M+24>>2]|0)==0){if((t[M+940>>2]|0)!=(Be|0))break e;if(at(M,Bn)|0&&(mn=S(x[M+908+(t[Br>>2]<<2)>>2]),n=S(xu(n,S(mn+S(Kt(M,Bn,Sr)))))),($u(e,M)|0)!=5)break;Wl=S(mt(M)),Wl=S(Wl+S(Hi(M,0,Sr))),mn=S(x[M+912>>2]),mn=S(S(mn+S(Kt(M,0,Sr)))-Wl),Wl=S(xu(Ze,Wl)),mn=S(xu(ye,mn)),ye=mn,Ze=Wl,n=S(xu(n,S(Wl+mn)))}while(0);if(M=X+1|0,M>>>0>>0)X=M;else{X=M;break}}else Ze=S(0),n=S(0),X=b;while(0);if(ct=S(ke+n),r=Te,Te=S(Te+ct),b>>>0>>0){Ye=S(r+Ze),M=b;do{b=t[(t[cs>>2]|0)+(M<<2)>>2]|0;e:do if((t[b+36>>2]|0)!=1&&(t[b+24>>2]|0)==0)switch($u(e,b)|0){case 1:{mn=S(r+S(Hi(b,Bn,Sr))),x[b+400+(t[Zt>>2]<<2)>>2]=mn;break e}case 3:{mn=S(S(Te-S(A0(b,Bn,Sr)))-S(x[b+908+(t[Br>>2]<<2)>>2])),x[b+400+(t[Zt>>2]<<2)>>2]=mn;break e}case 2:{mn=S(r+S(S(ct-S(x[b+908+(t[Br>>2]<<2)>>2]))*S(.5))),x[b+400+(t[Zt>>2]<<2)>>2]=mn;break e}case 4:{if(mn=S(r+S(Hi(b,Bn,Sr))),x[b+400+(t[Zt>>2]<<2)>>2]=mn,qu(b,Bn,hu)|0||(bu?(ye=S(x[b+908>>2]),n=S(ye+S(Kt(b,Or,Sr))),Ze=ct):(Ze=S(x[b+912>>2]),Ze=S(Ze+S(Kt(b,Bn,Sr))),n=ct,ye=S(x[b+908>>2])),Ci(n,ye)|0&&Ci(Ze,S(x[b+912>>2]))|0))break e;Yt(b,n,Ze,Dl,1,1,Sr,as,1,3501,L)|0;break e}case 5:{x[b+404>>2]=S(S(Ye-S(mt(b)))+S(ie(b,0,hu)));break e}default:break e}while(0);M=M+1|0}while((M|0)!=(X|0))}if(Be=Be+1|0,(Be|0)==(a|0))break;b=X}}}while(0);if(x[e+908>>2]=S(Kn(e,2,Xf,v,v)),x[e+912>>2]=S(Kn(e,0,z1,w,v)),(sf|0)!=0&&(H1=t[e+32>>2]|0,q1=(sf|0)==2,!(q1&(H1|0)!=2))?q1&(H1|0)==2&&(n=S(Qf+kn),n=S(xu(S(Kp(n,S(Qt(e,Or,Kf,fs)))),Qf)),pi=198):(n=S(Kn(e,Or,Kf,fs,v)),pi=198),(pi|0)==198&&(x[e+908+(t[976+(Or<<2)>>2]<<2)>>2]=n),(af|0)!=0&&(V1=t[e+32>>2]|0,G1=(af|0)==2,!(G1&(V1|0)!=2))?G1&(V1|0)==2&&(n=S(Vl+hu),n=S(xu(S(Kp(n,S(Qt(e,Bn,S(Vl+El),Jf)))),Vl)),pi=204):(n=S(Kn(e,Bn,S(Vl+El),Jf,v)),pi=204),(pi|0)==204&&(x[e+908+(t[976+(Bn<<2)>>2]<<2)>>2]=n),T){if((t[W1>>2]|0)==2){b=976+(Bn<<2)|0,X=1040+(Bn<<2)|0,M=0;do Be=Ti(e,M)|0,t[Be+24>>2]|0||(Y1=t[b>>2]|0,mn=S(x[e+908+(Y1<<2)>>2]),a0=Be+400+(t[X>>2]<<2)|0,mn=S(mn-S(x[a0>>2])),x[a0>>2]=S(mn-S(x[Be+908+(Y1<<2)>>2]))),M=M+1|0;while((M|0)!=(Gl|0))}if(u|0){M=bu?sf:s;do An(e,u,Sr,M,as,Dl,L),u=t[u+960>>2]|0;while((u|0)!=0)}if(M=(Or|2|0)==3,b=(Bn|2|0)==3,M|b){u=0;do X=t[(t[cs>>2]|0)+(u<<2)>>2]|0,(t[X+36>>2]|0)!=1&&(M&&Sn(e,X,Or),b&&Sn(e,X,Bn)),u=u+1|0;while((u|0)!=(Gl|0))}}}while(0);y=ff}function Qu(e,n){e=e|0,n=S(n);var r=0;Cn(e,n>=S(0),3147),r=n==S(0),x[e+4>>2]=r?S(0):n}function $r(e,n,r,u){e=e|0,n=S(n),r=S(r),u=u|0;var s=Ct,a=Ct,v=0,w=0,T=0;t[2278]=(t[2278]|0)+1,Qr(e),qu(e,2,n)|0?(s=S(xn(t[e+992>>2]|0,n)),T=1,s=S(s+S(Kt(e,2,n)))):(s=S(xn(e+380|0,n)),s>=S(0)?T=2:(T=((Le(n)|0)^1)&1,s=n)),qu(e,0,r)|0?(a=S(xn(t[e+996>>2]|0,r)),w=1,a=S(a+S(Kt(e,0,n)))):(a=S(xn(e+388|0,r)),a>=S(0)?w=2:(w=((Le(r)|0)^1)&1,a=r)),v=e+976|0,Yt(e,s,a,u,T,w,n,r,1,3189,t[v>>2]|0)|0&&(x0(e,t[e+496>>2]|0,n,r,n),Lu(e,S(x[(t[v>>2]|0)+4>>2]),S(0),S(0)),h[11696]|0)&&mf(e,7)}function Qr(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;w=y,y=y+32|0,v=w+24|0,a=w+16|0,u=w+8|0,s=w,r=0;do n=e+380+(r<<3)|0,(t[e+380+(r<<3)+4>>2]|0)!=0&&(T=n,L=t[T+4>>2]|0,M=u,t[M>>2]=t[T>>2],t[M+4>>2]=L,M=e+364+(r<<3)|0,L=t[M+4>>2]|0,T=s,t[T>>2]=t[M>>2],t[T+4>>2]=L,t[a>>2]=t[u>>2],t[a+4>>2]=t[u+4>>2],t[v>>2]=t[s>>2],t[v+4>>2]=t[s+4>>2],Bi(a,v)|0)||(n=e+348+(r<<3)|0),t[e+992+(r<<2)>>2]=n,r=r+1|0;while((r|0)!=2);y=w}function qu(e,n,r){e=e|0,n=n|0,r=S(r);var u=0;switch(e=t[e+992+(t[976+(n<<2)>>2]<<2)>>2]|0,t[e+4>>2]|0){case 0:case 3:{e=0;break}case 1:{S(x[e>>2])>2])>2]|0){case 2:{n=S(S(S(x[e>>2])*n)/S(100));break}case 1:{n=S(x[e>>2]);break}default:n=S(ue)}return S(n)}function x0(e,n,r,u,s){e=e|0,n=n|0,r=S(r),u=S(u),s=S(s);var a=0,v=Ct;n=t[e+944>>2]|0?n:1,a=B0(t[e+4>>2]|0,n)|0,n=gf(a,n)|0,r=S(Ar(e,a,r)),u=S(Ar(e,n,u)),v=S(r+S(Hi(e,a,s))),x[e+400+(t[1040+(a<<2)>>2]<<2)>>2]=v,r=S(r+S(A0(e,a,s))),x[e+400+(t[1e3+(a<<2)>>2]<<2)>>2]=r,r=S(u+S(Hi(e,n,s))),x[e+400+(t[1040+(n<<2)>>2]<<2)>>2]=r,s=S(u+S(A0(e,n,s))),x[e+400+(t[1e3+(n<<2)>>2]<<2)>>2]=s}function Lu(e,n,r,u){e=e|0,n=S(n),r=S(r),u=S(u);var s=0,a=0,v=Ct,w=Ct,T=0,L=0,M=Ct,b=0,X=Ct,Be=Ct,Te=Ct,ye=Ct;if(n!=S(0)&&(s=e+400|0,ye=S(x[s>>2]),a=e+404|0,Te=S(x[a>>2]),b=e+416|0,Be=S(x[b>>2]),L=e+420|0,v=S(x[L>>2]),X=S(ye+r),M=S(Te+u),u=S(X+Be),w=S(M+v),T=(t[e+988>>2]|0)==1,x[s>>2]=S(eo(ye,n,0,T)),x[a>>2]=S(eo(Te,n,0,T)),r=S(NE(S(Be*n),S(1))),Ci(r,S(0))|0?a=0:a=(Ci(r,S(1))|0)^1,r=S(NE(S(v*n),S(1))),Ci(r,S(0))|0?s=0:s=(Ci(r,S(1))|0)^1,ye=S(eo(u,n,T&a,T&(a^1))),x[b>>2]=S(ye-S(eo(X,n,0,T))),ye=S(eo(w,n,T&s,T&(s^1))),x[L>>2]=S(ye-S(eo(M,n,0,T))),a=(t[e+952>>2]|0)-(t[e+948>>2]|0)>>2,a|0)){s=0;do Lu(Ti(e,s)|0,n,X,M),s=s+1|0;while((s|0)!=(a|0))}}function ui(e,n,r,u,s){switch(e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,r|0){case 5:case 0:{e=v8(t[489]|0,u,s)|0;break}default:e=FL(u,s)|0}return e|0}function Cl(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;s=y,y=y+16|0,a=s,t[a>>2]=u,zs(e,0,n,r,a),y=s}function zs(e,n,r,u,s){if(e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,e=e|0?e:956,I8[t[e+8>>2]&1](e,n,r,u,s)|0,(r|0)==5)$n();else return}function Wu(e,n,r){e=e|0,n=n|0,r=r|0,h[e+n>>0]=r&1}function sa(e,n){e=e|0,n=n|0;var r=0,u=0;t[e>>2]=0,t[e+4>>2]=0,t[e+8>>2]=0,r=n+4|0,u=(t[r>>2]|0)-(t[n>>2]|0)>>2,u|0&&(Xi(e,u),Hs(e,t[n>>2]|0,t[r>>2]|0,u))}function Xi(e,n){e=e|0,n=n|0;var r=0;if((R0(e)|0)>>>0>>0&&di(e),n>>>0>1073741823)$n();else{r=pn(n<<2)|0,t[e+4>>2]=r,t[e>>2]=r,t[e+8>>2]=r+(n<<2);return}}function Hs(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,u=e+4|0,e=r-n|0,(e|0)>0&&(gr(t[u>>2]|0,n|0,e|0)|0,t[u>>2]=(t[u>>2]|0)+(e>>>2<<2))}function R0(e){return e=e|0,1073741823}function Hi(e,n,r){return e=e|0,n=n|0,r=S(r),qi(n)|0&&(t[e+96>>2]|0)!=0?e=e+92|0:e=pt(e+60|0,t[1040+(n<<2)>>2]|0,992)|0,S(il(e,r))}function A0(e,n,r){return e=e|0,n=n|0,r=S(r),qi(n)|0&&(t[e+104>>2]|0)!=0?e=e+100|0:e=pt(e+60|0,t[1e3+(n<<2)>>2]|0,992)|0,S(il(e,r))}function qi(e){return e=e|0,(e|1|0)==3|0}function il(e,n){return e=e|0,n=S(n),(t[e+4>>2]|0)==3?n=S(0):n=S(xn(e,n)),S(n)}function xl(e,n){return e=e|0,n=n|0,e=t[e>>2]|0,((e|0)==0?(n|0)>1?n:1:e)|0}function B0(e,n){e=e|0,n=n|0;var r=0;e:do if((n|0)==2){switch(e|0){case 2:{e=3;break e}case 3:break;default:{r=4;break e}}e=2}else r=4;while(0);return e|0}function O0(e,n){e=e|0,n=n|0;var r=Ct;return qi(n)|0&&(t[e+312>>2]|0)!=0&&(r=S(x[e+308>>2]),r>=S(0))||(r=S(xu(S(x[(pt(e+276|0,t[1040+(n<<2)>>2]|0,992)|0)>>2]),S(0)))),S(r)}function vo(e,n){e=e|0,n=n|0;var r=Ct;return qi(n)|0&&(t[e+320>>2]|0)!=0&&(r=S(x[e+316>>2]),r>=S(0))||(r=S(xu(S(x[(pt(e+276|0,t[1e3+(n<<2)>>2]|0,992)|0)>>2]),S(0)))),S(r)}function Fu(e,n,r){e=e|0,n=n|0,r=S(r);var u=Ct;return qi(n)|0&&(t[e+240>>2]|0)!=0&&(u=S(xn(e+236|0,r)),u>=S(0))||(u=S(xu(S(xn(pt(e+204|0,t[1040+(n<<2)>>2]|0,992)|0,r)),S(0)))),S(u)}function Ju(e,n,r){e=e|0,n=n|0,r=S(r);var u=Ct;return qi(n)|0&&(t[e+248>>2]|0)!=0&&(u=S(xn(e+244|0,r)),u>=S(0))||(u=S(xu(S(xn(pt(e+204|0,t[1e3+(n<<2)>>2]|0,992)|0,r)),S(0)))),S(u)}function es(e,n,r,u,s,a,v){e=e|0,n=S(n),r=S(r),u=u|0,s=s|0,a=S(a),v=S(v);var w=Ct,T=Ct,L=Ct,M=Ct,b=Ct,X=Ct,Be=0,Te=0,ye=0;ye=y,y=y+16|0,Be=ye,Te=e+964|0,Hu(e,(t[Te>>2]|0)!=0,3519),w=S(Rr(e,2,n)),T=S(Rr(e,0,n)),L=S(Kt(e,2,n)),M=S(Kt(e,0,n)),Le(n)|0?b=n:b=S(xu(S(0),S(S(n-L)-w))),Le(r)|0?X=r:X=S(xu(S(0),S(S(r-M)-T))),(u|0)==1&(s|0)==1?(x[e+908>>2]=S(Kn(e,2,S(n-L),a,a)),n=S(Kn(e,0,S(r-M),v,a))):(b8[t[Te>>2]&1](Be,e,b,u,X,s),b=S(w+S(x[Be>>2])),X=S(n-L),x[e+908>>2]=S(Kn(e,2,(u|2|0)==2?b:X,a,a)),X=S(T+S(x[Be+4>>2])),n=S(r-M),n=S(Kn(e,0,(s|2|0)==2?X:n,v,a))),x[e+912>>2]=n,y=ye}function _s(e,n,r,u,s,a,v){e=e|0,n=S(n),r=S(r),u=u|0,s=s|0,a=S(a),v=S(v);var w=Ct,T=Ct,L=Ct,M=Ct;L=S(Rr(e,2,a)),w=S(Rr(e,0,a)),M=S(Kt(e,2,a)),T=S(Kt(e,0,a)),n=S(n-M),x[e+908>>2]=S(Kn(e,2,(u|2|0)==2?L:n,a,a)),r=S(r-T),x[e+912>>2]=S(Kn(e,0,(s|2|0)==2?w:r,v,a))}function aa(e,n,r,u,s,a,v){e=e|0,n=S(n),r=S(r),u=u|0,s=s|0,a=S(a),v=S(v);var w=0,T=Ct,L=Ct;return w=(u|0)==2,!(n<=S(0)&w)&&!(r<=S(0)&(s|0)==2)&&!((u|0)==1&(s|0)==1)?e=0:(T=S(Kt(e,0,a)),L=S(Kt(e,2,a)),w=n>2]=S(Kn(e,2,w?S(0):n,a,a)),n=S(r-T),w=r>2]=S(Kn(e,0,w?S(0):n,v,a)),e=1),e|0}function gf(e,n){return e=e|0,n=n|0,_n(e)|0?e=B0(2,n)|0:e=0,e|0}function Zu(e,n,r){return e=e|0,n=n|0,r=S(r),r=S(Fu(e,n,r)),S(r+S(O0(e,n)))}function Es(e,n,r){return e=e|0,n=n|0,r=S(r),r=S(Ju(e,n,r)),S(r+S(vo(e,n)))}function Rr(e,n,r){e=e|0,n=n|0,r=S(r);var u=Ct;return u=S(Zu(e,n,r)),S(u+S(Es(e,n,r)))}function no(e){return e=e|0,t[e+24>>2]|0?e=0:S(nu(e))!=S(0)?e=1:e=S(fu(e))!=S(0),e|0}function nu(e){e=e|0;var n=Ct;if(t[e+944>>2]|0){if(n=S(x[e+44>>2]),Le(n)|0)return n=S(x[e+40>>2]),e=n>S(0)&((Le(n)|0)^1),S(e?n:S(0))}else n=S(0);return S(n)}function fu(e){e=e|0;var n=Ct,r=0,u=Ct;do if(t[e+944>>2]|0){if(n=S(x[e+48>>2]),Le(n)|0){if(r=h[(t[e+976>>2]|0)+2>>0]|0,r<<24>>24==0&&(u=S(x[e+40>>2]),u>24?S(1):S(0)}}else n=S(0);while(0);return S(n)}function Li(e){e=e|0;var n=0,r=0;if(jv(e+400|0,0,540)|0,h[e+985>>0]=1,ms(e),r=wu(e)|0,r|0){n=e+948|0,e=0;do Li(t[(t[n>>2]|0)+(e<<2)>>2]|0),e=e+1|0;while((e|0)!=(r|0))}}function ei(e,n,r,u,s,a,v,w,T,L){e=e|0,n=n|0,r=S(r),u=u|0,s=S(s),a=S(a),v=S(v),w=w|0,T=T|0,L=L|0;var M=0,b=Ct,X=0,Be=0,Te=Ct,ye=Ct,Ze=0,Ye=Ct,ct=0,ke=Ct,Ie=0,Zt=0,Br=0,Pn=0,gn=0,_r=0,Pr=0,kn=0,uu=0,os=0;uu=y,y=y+16|0,Br=uu+12|0,Pn=uu+8|0,gn=uu+4|0,_r=uu,kn=B0(t[e+4>>2]|0,T)|0,Ie=qi(kn)|0,b=S(xn(Tn(n)|0,Ie?a:v)),Zt=qu(n,2,a)|0,Pr=qu(n,0,v)|0;do if(!(Le(b)|0)&&!(Le(Ie?r:s)|0)){if(M=n+504|0,!(Le(S(x[M>>2]))|0)&&(!(ir(t[n+976>>2]|0,0)|0)||(t[n+500>>2]|0)==(t[2278]|0)))break;x[M>>2]=S(xu(b,S(Rr(n,kn,a))))}else X=7;while(0);do if((X|0)==7){if(ct=Ie^1,!(ct|Zt^1)){v=S(xn(t[n+992>>2]|0,a)),x[n+504>>2]=S(xu(v,S(Rr(n,2,a))));break}if(!(Ie|Pr^1)){v=S(xn(t[n+996>>2]|0,v)),x[n+504>>2]=S(xu(v,S(Rr(n,0,a))));break}x[Br>>2]=S(ue),x[Pn>>2]=S(ue),t[gn>>2]=0,t[_r>>2]=0,Ye=S(Kt(n,2,a)),ke=S(Kt(n,0,a)),Zt?(Te=S(Ye+S(xn(t[n+992>>2]|0,a))),x[Br>>2]=Te,t[gn>>2]=1,Be=1):(Be=0,Te=S(ue)),Pr?(b=S(ke+S(xn(t[n+996>>2]|0,v))),x[Pn>>2]=b,t[_r>>2]=1,M=1):(M=0,b=S(ue)),X=t[e+32>>2]|0,Ie&(X|0)==2?X=2:Le(Te)|0&&!(Le(r)|0)&&(x[Br>>2]=r,t[gn>>2]=2,Be=2,Te=r),!((X|0)==2&ct)&&Le(b)|0&&!(Le(s)|0)&&(x[Pn>>2]=s,t[_r>>2]=2,M=2,b=s),ye=S(x[n+396>>2]),Ze=Le(ye)|0;do if(Ze)X=Be;else{if((Be|0)==1&ct){x[Pn>>2]=S(S(Te-Ye)/ye),t[_r>>2]=1,M=1,X=1;break}Ie&(M|0)==1?(x[Br>>2]=S(ye*S(b-ke)),t[gn>>2]=1,M=1,X=1):X=Be}while(0);os=Le(r)|0,Be=($u(e,n)|0)!=4,!(Ie|Zt|((u|0)!=1|os)|(Be|(X|0)==1))&&(x[Br>>2]=r,t[gn>>2]=1,!Ze)&&(x[Pn>>2]=S(S(r-Ye)/ye),t[_r>>2]=1,M=1),!(Pr|ct|((w|0)!=1|(Le(s)|0))|(Be|(M|0)==1))&&(x[Pn>>2]=s,t[_r>>2]=1,!Ze)&&(x[Br>>2]=S(ye*S(s-ke)),t[gn>>2]=1),Ln(n,2,a,a,gn,Br),Ln(n,0,v,a,_r,Pn),r=S(x[Br>>2]),s=S(x[Pn>>2]),Yt(n,r,s,T,t[gn>>2]|0,t[_r>>2]|0,a,v,0,3565,L)|0,v=S(x[n+908+(t[976+(kn<<2)>>2]<<2)>>2]),x[n+504>>2]=S(xu(v,S(Rr(n,kn,a))))}while(0);t[n+500>>2]=t[2278],y=uu}function Kn(e,n,r,u,s){return e=e|0,n=n|0,r=S(r),u=S(u),s=S(s),u=S(Qt(e,n,r,u)),S(xu(u,S(Rr(e,n,s))))}function $u(e,n){return e=e|0,n=n|0,n=n+20|0,n=t[((t[n>>2]|0)==0?e+16|0:n)>>2]|0,(n|0)==5&&_n(t[e+4>>2]|0)|0&&(n=1),n|0}function g0(e,n){return e=e|0,n=n|0,qi(n)|0&&(t[e+96>>2]|0)!=0?n=4:n=t[1040+(n<<2)>>2]|0,e+60+(n<<3)|0}function _0(e,n){return e=e|0,n=n|0,qi(n)|0&&(t[e+104>>2]|0)!=0?n=5:n=t[1e3+(n<<2)>>2]|0,e+60+(n<<3)|0}function Ln(e,n,r,u,s,a){switch(e=e|0,n=n|0,r=S(r),u=S(u),s=s|0,a=a|0,r=S(xn(e+380+(t[976+(n<<2)>>2]<<3)|0,r)),r=S(r+S(Kt(e,n,u))),t[s>>2]|0){case 2:case 1:{s=Le(r)|0,u=S(x[a>>2]),x[a>>2]=s|u>2]=2,x[a>>2]=r);break}default:}}function fe(e,n){return e=e|0,n=n|0,e=e+132|0,qi(n)|0&&(t[(pt(e,4,948)|0)+4>>2]|0)!=0?e=1:e=(t[(pt(e,t[1040+(n<<2)>>2]|0,948)|0)+4>>2]|0)!=0,e|0}function ie(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0;return e=e+132|0,qi(n)|0&&(u=pt(e,4,948)|0,(t[u+4>>2]|0)!=0)?s=4:(u=pt(e,t[1040+(n<<2)>>2]|0,948)|0,t[u+4>>2]|0?s=4:r=S(0)),(s|0)==4&&(r=S(xn(u,r))),S(r)}function Pe(e,n,r){e=e|0,n=n|0,r=S(r);var u=Ct;return u=S(x[e+908+(t[976+(n<<2)>>2]<<2)>>2]),u=S(u+S(Hi(e,n,r))),S(u+S(A0(e,n,r)))}function Me(e){e=e|0;var n=0,r=0,u=0;e:do if(_n(t[e+4>>2]|0)|0)n=0;else if((t[e+16>>2]|0)!=5)if(r=wu(e)|0,!r)n=0;else for(n=0;;){if(u=Ti(e,n)|0,(t[u+24>>2]|0)==0&&(t[u+20>>2]|0)==5){n=1;break e}if(n=n+1|0,n>>>0>=r>>>0){n=0;break}}else n=1;while(0);return n|0}function at(e,n){e=e|0,n=n|0;var r=Ct;return r=S(x[e+908+(t[976+(n<<2)>>2]<<2)>>2]),r>=S(0)&((Le(r)|0)^1)|0}function mt(e){e=e|0;var n=Ct,r=0,u=0,s=0,a=0,v=0,w=0,T=Ct;if(r=t[e+968>>2]|0,r)T=S(x[e+908>>2]),n=S(x[e+912>>2]),n=S(N8[r&0](e,T,n)),Hu(e,(Le(n)|0)^1,3573);else{a=wu(e)|0;do if(a|0){for(r=0,s=0;;){if(u=Ti(e,s)|0,t[u+940>>2]|0){v=8;break}if((t[u+24>>2]|0)!=1)if(w=($u(e,u)|0)==5,w){r=u;break}else r=(r|0)==0?u:r;if(s=s+1|0,s>>>0>=a>>>0){v=8;break}}if((v|0)==8&&!r)break;return n=S(mt(r)),S(n+S(x[r+404>>2]))}while(0);n=S(x[e+912>>2])}return S(n)}function Qt(e,n,r,u){e=e|0,n=n|0,r=S(r),u=S(u);var s=Ct,a=0;return _n(n)|0?(n=1,a=3):qi(n)|0?(n=0,a=3):(u=S(ue),s=S(ue)),(a|0)==3&&(s=S(xn(e+364+(n<<3)|0,u)),u=S(xn(e+380+(n<<3)|0,u))),a=u=S(0)&((Le(u)|0)^1)),r=a?u:r,a=s>=S(0)&((Le(s)|0)^1)&r>2]|0,a)|0,Te=gf(Ze,a)|0,ye=qi(Ze)|0,b=S(Kt(n,2,r)),X=S(Kt(n,0,r)),qu(n,2,r)|0?w=S(b+S(xn(t[n+992>>2]|0,r))):fe(n,2)|0&&Ut(n,2)|0?(w=S(x[e+908>>2]),T=S(O0(e,2)),T=S(w-S(T+S(vo(e,2)))),w=S(ie(n,2,r)),w=S(Kn(n,2,S(T-S(w+S(Fi(n,2,r)))),r,r))):w=S(ue),qu(n,0,s)|0?T=S(X+S(xn(t[n+996>>2]|0,s))):fe(n,0)|0&&Ut(n,0)|0?(T=S(x[e+912>>2]),ct=S(O0(e,0)),ct=S(T-S(ct+S(vo(e,0)))),T=S(ie(n,0,s)),T=S(Kn(n,0,S(ct-S(T+S(Fi(n,0,s)))),s,r))):T=S(ue),L=Le(w)|0,M=Le(T)|0;do if(L^M&&(Be=S(x[n+396>>2]),!(Le(Be)|0)))if(L){w=S(b+S(S(T-X)*Be));break}else{ct=S(X+S(S(w-b)/Be)),T=M?ct:T;break}while(0);M=Le(w)|0,L=Le(T)|0,M|L&&(ke=(M^1)&1,u=r>S(0)&((u|0)!=0&M),w=ye?w:u?r:w,Yt(n,w,T,a,ye?ke:u?2:ke,M&(L^1)&1,w,T,0,3623,v)|0,w=S(x[n+908>>2]),w=S(w+S(Kt(n,2,r))),T=S(x[n+912>>2]),T=S(T+S(Kt(n,0,r)))),Yt(n,w,T,a,1,1,w,T,1,3635,v)|0,Ut(n,Ze)|0&&!(fe(n,Ze)|0)?(ke=t[976+(Ze<<2)>>2]|0,ct=S(x[e+908+(ke<<2)>>2]),ct=S(ct-S(x[n+908+(ke<<2)>>2])),ct=S(ct-S(vo(e,Ze))),ct=S(ct-S(A0(n,Ze,r))),ct=S(ct-S(Fi(n,Ze,ye?r:s))),x[n+400+(t[1040+(Ze<<2)>>2]<<2)>>2]=ct):Ye=21;do if((Ye|0)==21){if(!(fe(n,Ze)|0)&&(t[e+8>>2]|0)==1){ke=t[976+(Ze<<2)>>2]|0,ct=S(x[e+908+(ke<<2)>>2]),ct=S(S(ct-S(x[n+908+(ke<<2)>>2]))*S(.5)),x[n+400+(t[1040+(Ze<<2)>>2]<<2)>>2]=ct;break}!(fe(n,Ze)|0)&&(t[e+8>>2]|0)==2&&(ke=t[976+(Ze<<2)>>2]|0,ct=S(x[e+908+(ke<<2)>>2]),ct=S(ct-S(x[n+908+(ke<<2)>>2])),x[n+400+(t[1040+(Ze<<2)>>2]<<2)>>2]=ct)}while(0);Ut(n,Te)|0&&!(fe(n,Te)|0)?(ke=t[976+(Te<<2)>>2]|0,ct=S(x[e+908+(ke<<2)>>2]),ct=S(ct-S(x[n+908+(ke<<2)>>2])),ct=S(ct-S(vo(e,Te))),ct=S(ct-S(A0(n,Te,r))),ct=S(ct-S(Fi(n,Te,ye?s:r))),x[n+400+(t[1040+(Te<<2)>>2]<<2)>>2]=ct):Ye=30;do if((Ye|0)==30&&!(fe(n,Te)|0)){if(($u(e,n)|0)==2){ke=t[976+(Te<<2)>>2]|0,ct=S(x[e+908+(ke<<2)>>2]),ct=S(S(ct-S(x[n+908+(ke<<2)>>2]))*S(.5)),x[n+400+(t[1040+(Te<<2)>>2]<<2)>>2]=ct;break}ke=($u(e,n)|0)==3,ke^(t[e+28>>2]|0)==2&&(ke=t[976+(Te<<2)>>2]|0,ct=S(x[e+908+(ke<<2)>>2]),ct=S(ct-S(x[n+908+(ke<<2)>>2])),x[n+400+(t[1040+(Te<<2)>>2]<<2)>>2]=ct)}while(0)}function Sn(e,n,r){e=e|0,n=n|0,r=r|0;var u=Ct,s=0;s=t[976+(r<<2)>>2]|0,u=S(x[n+908+(s<<2)>>2]),u=S(S(x[e+908+(s<<2)>>2])-u),u=S(u-S(x[n+400+(t[1040+(r<<2)>>2]<<2)>>2])),x[n+400+(t[1e3+(r<<2)>>2]<<2)>>2]=u}function _n(e){return e=e|0,(e|1|0)==1|0}function Tn(e){e=e|0;var n=Ct;switch(t[e+56>>2]|0){case 0:case 3:{n=S(x[e+40>>2]),n>S(0)&((Le(n)|0)^1)?e=h[(t[e+976>>2]|0)+2>>0]|0?1056:992:e=1056;break}default:e=e+52|0}return e|0}function ir(e,n){return e=e|0,n=n|0,(h[e+n>>0]|0)!=0|0}function Ut(e,n){return e=e|0,n=n|0,e=e+132|0,qi(n)|0&&(t[(pt(e,5,948)|0)+4>>2]|0)!=0?e=1:e=(t[(pt(e,t[1e3+(n<<2)>>2]|0,948)|0)+4>>2]|0)!=0,e|0}function Fi(e,n,r){e=e|0,n=n|0,r=S(r);var u=0,s=0;return e=e+132|0,qi(n)|0&&(u=pt(e,5,948)|0,(t[u+4>>2]|0)!=0)?s=4:(u=pt(e,t[1e3+(n<<2)>>2]|0,948)|0,t[u+4>>2]|0?s=4:r=S(0)),(s|0)==4&&(r=S(xn(u,r))),S(r)}function Ar(e,n,r){return e=e|0,n=n|0,r=S(r),fe(e,n)|0?r=S(ie(e,n,r)):r=S(-S(Fi(e,n,r))),S(r)}function mr(e){return e=S(e),x[V>>2]=e,t[V>>2]|0|0}function K(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>1073741823)$n();else{s=pn(n<<2)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<2)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<2)}function ti(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>2)<<2)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function ni(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-4-n|0)>>>2)<<2)),e=t[e>>2]|0,e|0&&Et(e)}function Wr(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;if(v=e+4|0,w=t[v>>2]|0,s=w-u|0,a=s>>2,e=n+(a<<2)|0,e>>>0>>0){u=w;do t[u>>2]=t[e>>2],e=e+4|0,u=(t[v>>2]|0)+4|0,t[v>>2]=u;while(e>>>0>>0)}a|0&&ky(w+(0-a<<2)|0,n|0,s|0)|0}function ft(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0;return w=n+4|0,T=t[w>>2]|0,s=t[e>>2]|0,v=r,a=v-s|0,u=T+(0-(a>>2)<<2)|0,t[w>>2]=u,(a|0)>0&&gr(u|0,s|0,a|0)|0,s=e+4|0,a=n+8|0,u=(t[s>>2]|0)-v|0,(u|0)>0&&(gr(t[a>>2]|0,r|0,u|0)|0,t[a>>2]=(t[a>>2]|0)+(u>>>2<<2)),v=t[e>>2]|0,t[e>>2]=t[w>>2],t[w>>2]=v,v=t[s>>2]|0,t[s>>2]=t[a>>2],t[a>>2]=v,v=e+8|0,r=n+12|0,e=t[v>>2]|0,t[v>>2]=t[r>>2],t[r>>2]=e,t[n>>2]=t[w>>2],T|0}function Di(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;if(v=t[n>>2]|0,a=t[r>>2]|0,(v|0)!=(a|0)){s=e+8|0,r=((a+-4-v|0)>>>2)+1|0,e=v,u=t[s>>2]|0;do t[u>>2]=t[e>>2],u=(t[s>>2]|0)+4|0,t[s>>2]=u,e=e+4|0;while((e|0)!=(a|0));t[n>>2]=v+(r<<2)}}function ru(){Se()}function E0(){var e=0;return e=pn(4)|0,Un(e),e|0}function Un(e){e=e|0,t[e>>2]=v0()|0}function e0(e){e=e|0,e|0&&(ro(e),Et(e))}function ro(e){e=e|0,J0(t[e>>2]|0)}function mo(e,n,r){e=e|0,n=n|0,r=r|0,Wu(t[e>>2]|0,n,r)}function t0(e,n){e=e|0,n=S(n),Qu(t[e>>2]|0,n)}function jo(e,n){return e=e|0,n=n|0,ir(t[e>>2]|0,n)|0}function io(){var e=0;return e=pn(8)|0,Ba(e,0),e|0}function Ba(e,n){e=e|0,n=n|0,n?n=Yn(t[n>>2]|0)|0:n=cr()|0,t[e>>2]=n,t[e+4>>2]=0,ba(n,e)}function _f(e){e=e|0;var n=0;return n=pn(8)|0,Ba(n,e),n|0}function fc(e){e=e|0,e|0&&(Ds(e),Et(e))}function Ds(e){e=e|0;var n=0;ju(t[e>>2]|0),n=e+4|0,e=t[n>>2]|0,t[n>>2]=0,e|0&&(fa(e),Et(e))}function fa(e){e=e|0,U0(e)}function U0(e){e=e|0,e=t[e>>2]|0,e|0&&qr(e|0)}function cc(e){return e=e|0,Bs(e)|0}function Ua(e){e=e|0;var n=0,r=0;r=e+4|0,n=t[r>>2]|0,t[r>>2]=0,n|0&&(fa(n),Et(n)),po(t[e>>2]|0)}function _2(e,n){e=e|0,n=n|0,oa(t[e>>2]|0,t[n>>2]|0)}function nd(e,n){e=e|0,n=n|0,$(t[e>>2]|0,n)}function rd(e,n,r){e=e|0,n=n|0,r=+r,dr(t[e>>2]|0,n,S(r))}function yo(e,n,r){e=e|0,n=n|0,r=+r,er(t[e>>2]|0,n,S(r))}function qc(e,n){e=e|0,n=n|0,H(t[e>>2]|0,n)}function Rl(e,n){e=e|0,n=n|0,ee(t[e>>2]|0,n)}function ul(e,n){e=e|0,n=n|0,_e(t[e>>2]|0,n)}function E2(e,n){e=e|0,n=n|0,m0(t[e>>2]|0,n)}function qs(e,n){e=e|0,n=n|0,Je(t[e>>2]|0,n)}function Al(e,n){e=e|0,n=n|0,zi(t[e>>2]|0,n)}function id(e,n,r){e=e|0,n=n|0,r=+r,Rn(t[e>>2]|0,n,S(r))}function zo(e,n,r){e=e|0,n=n|0,r=+r,Nr(t[e>>2]|0,n,S(r))}function ja(e,n){e=e|0,n=n|0,Lr(t[e>>2]|0,n)}function za(e,n){e=e|0,n=n|0,oe(t[e>>2]|0,n)}function Ha(e,n){e=e|0,n=n|0,rt(t[e>>2]|0,n)}function ca(e,n){e=e|0,n=+n,kt(t[e>>2]|0,S(n))}function ws(e,n){e=e|0,n=+n,rn(t[e>>2]|0,S(n))}function Ss(e,n){e=e|0,n=+n,Ft(t[e>>2]|0,S(n))}function ts(e,n){e=e|0,n=+n,bt(t[e>>2]|0,S(n))}function Ho(e,n){e=e|0,n=+n,sn(t[e>>2]|0,S(n))}function Ef(e,n){e=e|0,n=+n,fn(t[e>>2]|0,S(n))}function ol(e,n){e=e|0,n=+n,Jn(t[e>>2]|0,S(n))}function Vu(e){e=e|0,wr(t[e>>2]|0)}function qa(e,n){e=e|0,n=+n,ku(t[e>>2]|0,S(n))}function n0(e,n){e=e|0,n=+n,T0(t[e>>2]|0,S(n))}function j0(e){e=e|0,Z0(t[e>>2]|0)}function Df(e,n){e=e|0,n=+n,gi(t[e>>2]|0,S(n))}function Wc(e,n){e=e|0,n=+n,Po(t[e>>2]|0,S(n))}function dc(e,n){e=e|0,n=+n,hf(t[e>>2]|0,S(n))}function Ol(e,n){e=e|0,n=+n,Tl(t[e>>2]|0,S(n))}function Ts(e,n){e=e|0,n=+n,Io(t[e>>2]|0,S(n))}function da(e,n){e=e|0,n=+n,ys(t[e>>2]|0,S(n))}function ud(e,n){e=e|0,n=+n,bo(t[e>>2]|0,S(n))}function pa(e,n){e=e|0,n=+n,Bo(t[e>>2]|0,S(n))}function pc(e,n){e=e|0,n=+n,Xu(t[e>>2]|0,S(n))}function Vc(e,n,r){e=e|0,n=n|0,r=+r,It(t[e>>2]|0,n,S(r))}function Wi(e,n,r){e=e|0,n=n|0,r=+r,ut(t[e>>2]|0,n,S(r))}function _(e,n,r){e=e|0,n=n|0,r=+r,wt(t[e>>2]|0,n,S(r))}function g(e){return e=e|0,Ne(t[e>>2]|0)|0}function A(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;u=y,y=y+16|0,s=u,Cr(s,t[n>>2]|0,r),P(e,s),y=u}function P(e,n){e=e|0,n=n|0,B(e,t[n+4>>2]|0,+S(x[n>>2]))}function B(e,n,r){e=e|0,n=n|0,r=+r,t[e>>2]=n,j[e+8>>3]=r}function Z(e){return e=e|0,Y(t[e>>2]|0)|0}function de(e){return e=e|0,Ce(t[e>>2]|0)|0}function yt(e){return e=e|0,Oe(t[e>>2]|0)|0}function Rt(e){return e=e|0,Us(t[e>>2]|0)|0}function Nt(e){return e=e|0,vt(t[e>>2]|0)|0}function xr(e){return e=e|0,U(t[e>>2]|0)|0}function r0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;u=y,y=y+16|0,s=u,y0(s,t[n>>2]|0,r),P(e,s),y=u}function cu(e){return e=e|0,qe(t[e>>2]|0)|0}function z0(e){return e=e|0,xt(t[e>>2]|0)|0}function Ml(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,Dn(u,t[n>>2]|0),P(e,u),y=r}function i0(e){return e=e|0,+ +S(pf(t[e>>2]|0))}function Ge(e){return e=e|0,+ +S(bs(t[e>>2]|0))}function je(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,au(u,t[n>>2]|0),P(e,u),y=r}function st(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,Nu(u,t[n>>2]|0),P(e,u),y=r}function $t(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,rl(u,t[n>>2]|0),P(e,u),y=r}function Wn(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,vf(u,t[n>>2]|0),P(e,u),y=r}function oi(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,js(u,t[n>>2]|0),P(e,u),y=r}function ur(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,gs(u,t[n>>2]|0),P(e,u),y=r}function ai(e){return e=e|0,+ +S(Su(t[e>>2]|0))}function Qi(e,n){return e=e|0,n=n|0,+ +S(un(t[e>>2]|0,n))}function Vr(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;u=y,y=y+16|0,s=u,et(s,t[n>>2]|0,r),P(e,s),y=u}function Tu(e,n,r){e=e|0,n=n|0,r=r|0,Ia(t[e>>2]|0,t[n>>2]|0,r)}function Wa(e,n){e=e|0,n=n|0,Mu(t[e>>2]|0,t[n>>2]|0)}function Va(e){return e=e|0,wu(t[e>>2]|0)|0}function od(e){return e=e|0,e=si(t[e>>2]|0)|0,e?e=cc(e)|0:e=0,e|0}function D2(e,n){return e=e|0,n=n|0,e=Ti(t[e>>2]|0,n)|0,e?e=cc(e)|0:e=0,e|0}function w2(e,n){e=e|0,n=n|0;var r=0,u=0;u=pn(4)|0,wf(u,n),r=e+4|0,n=t[r>>2]|0,t[r>>2]=u,n|0&&(fa(n),Et(n)),ua(t[e>>2]|0,1)}function wf(e,n){e=e|0,n=n|0,sl(e,n)}function ld(e,n,r,u,s,a){e=e|0,n=n|0,r=S(r),u=u|0,s=S(s),a=a|0;var v=0,w=0;v=y,y=y+16|0,w=v,hh(w,Bs(n)|0,+r,u,+s,a),x[e>>2]=S(+j[w>>3]),x[e+4>>2]=S(+j[w+8>>3]),y=v}function hh(e,n,r,u,s,a){e=e|0,n=n|0,r=+r,u=u|0,s=+s,a=a|0;var v=0,w=0,T=0,L=0,M=0;v=y,y=y+32|0,M=v+8|0,L=v+20|0,T=v,w=v+16|0,j[M>>3]=r,t[L>>2]=u,j[T>>3]=s,t[w>>2]=a,Gc(e,t[n+4>>2]|0,M,L,T,w),y=v}function Gc(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0;var v=0,w=0;v=y,y=y+16|0,w=v,Ma(w),n=go(n)|0,vh(e,n,+j[r>>3],t[u>>2]|0,+j[s>>3],t[a>>2]|0),ka(w),y=v}function go(e){return e=e|0,t[e>>2]|0}function vh(e,n,r,u,s,a){e=e|0,n=n|0,r=+r,u=u|0,s=+s,a=a|0;var v=0;v=_o(mh()|0)|0,r=+kl(r),u=sd(u)|0,s=+kl(s),ad(e,Xr(0,v|0,n|0,+r,u|0,+s,sd(a)|0)|0)}function mh(){var e=0;return h[7608]|0||(Kc(9120),e=7608,t[e>>2]=1,t[e+4>>2]=0),9120}function _o(e){return e=e|0,t[e+8>>2]|0}function kl(e){return e=+e,+ +Ga(e)}function sd(e){return e=e|0,cd(e)|0}function ad(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;s=y,y=y+32|0,r=s,u=n,u&1?(S2(r,0),eu(u|0,r|0)|0,Yc(e,r),Ir(r)):(t[e>>2]=t[n>>2],t[e+4>>2]=t[n+4>>2],t[e+8>>2]=t[n+8>>2],t[e+12>>2]=t[n+12>>2]),y=s}function S2(e,n){e=e|0,n=n|0,fd(e,n),t[e+8>>2]=0,h[e+24>>0]=0}function Yc(e,n){e=e|0,n=n|0,n=n+8|0,t[e>>2]=t[n>>2],t[e+4>>2]=t[n+4>>2],t[e+8>>2]=t[n+8>>2],t[e+12>>2]=t[n+12>>2]}function Ir(e){e=e|0,h[e+24>>0]=0}function fd(e,n){e=e|0,n=n|0,t[e>>2]=n}function cd(e){return e=e|0,e|0}function Ga(e){return e=+e,+e}function Kc(e){e=e|0,ll(e,T2()|0,4)}function T2(){return 1064}function ll(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r,t[e+8>>2]=Bt(n|0,r+1|0)|0}function sl(e,n){e=e|0,n=n|0,n=t[n>>2]|0,t[e>>2]=n,Ri(n|0)}function yh(e){e=e|0;var n=0,r=0;r=e+4|0,n=t[r>>2]|0,t[r>>2]=0,n|0&&(fa(n),Et(n)),ua(t[e>>2]|0,0)}function Sf(e){e=e|0,Jr(t[e>>2]|0)}function Xc(e){return e=e|0,Zl(t[e>>2]|0)|0}function C2(e,n,r,u){e=e|0,n=+n,r=+r,u=u|0,$r(t[e>>2]|0,S(n),S(r),u)}function gh(e){return e=e|0,+ +S(_i(t[e>>2]|0))}function al(e){return e=e|0,+ +S($0(t[e>>2]|0))}function ha(e){return e=e|0,+ +S(C0(t[e>>2]|0))}function x2(e){return e=e|0,+ +S(Uo(t[e>>2]|0))}function R2(e){return e=e|0,+ +S(la(t[e>>2]|0))}function hc(e){return e=e|0,+ +S($l(t[e>>2]|0))}function _h(e,n){e=e|0,n=n|0,j[e>>3]=+S(_i(t[n>>2]|0)),j[e+8>>3]=+S($0(t[n>>2]|0)),j[e+16>>3]=+S(C0(t[n>>2]|0)),j[e+24>>3]=+S(Uo(t[n>>2]|0)),j[e+32>>3]=+S(la(t[n>>2]|0)),j[e+40>>3]=+S($l(t[n>>2]|0))}function A2(e,n){return e=e|0,n=n|0,+ +S(tu(t[e>>2]|0,n))}function dd(e,n){return e=e|0,n=n|0,+ +S(Zr(t[e>>2]|0,n))}function Qc(e,n){return e=e|0,n=n|0,+ +S(ho(t[e>>2]|0,n))}function Jc(){return Pa()|0}function Ws(){O2(),va(),Zc(),vc(),mc(),pd()}function O2(){E7(11713,4938,1)}function va(){UA(10448)}function Zc(){EA(10408)}function vc(){qR(10324)}function mc(){nE(10096)}function pd(){Eh(9132)}function Eh(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0,Ze=0,Ye=0,ct=0,ke=0,Ie=0,Zt=0,Br=0,Pn=0,gn=0,_r=0,Pr=0,kn=0,uu=0,os=0,ls=0,ss=0,ea=0,t2=0,n2=0,uf=0,r2=0,Fc=0,Pc=0,i2=0,u2=0,o2=0,pi=0,of=0,l2=0,Yf=0,s2=0,a2=0,Ic=0,bc=0,Kf=0,ql=0,La=0,Ns=0,lf=0,b1=0,B1=0,Bc=0,U1=0,j1=0,Wl=0,El=0,sf=0,hu=0,z1=0,as=0,Xf=0,fs=0,Qf=0,H1=0,q1=0,Jf=0,Vl=0,af=0,W1=0,V1=0,G1=0,Sr=0,bu=0,Dl=0,cs=0,Gl=0,Or=0,Bn=0,ff=0;n=y,y=y+672|0,r=n+656|0,ff=n+648|0,Bn=n+640|0,Or=n+632|0,Gl=n+624|0,cs=n+616|0,Dl=n+608|0,bu=n+600|0,Sr=n+592|0,G1=n+584|0,V1=n+576|0,W1=n+568|0,af=n+560|0,Vl=n+552|0,Jf=n+544|0,q1=n+536|0,H1=n+528|0,Qf=n+520|0,fs=n+512|0,Xf=n+504|0,as=n+496|0,z1=n+488|0,hu=n+480|0,sf=n+472|0,El=n+464|0,Wl=n+456|0,j1=n+448|0,U1=n+440|0,Bc=n+432|0,B1=n+424|0,b1=n+416|0,lf=n+408|0,Ns=n+400|0,La=n+392|0,ql=n+384|0,Kf=n+376|0,bc=n+368|0,Ic=n+360|0,a2=n+352|0,s2=n+344|0,Yf=n+336|0,l2=n+328|0,of=n+320|0,pi=n+312|0,o2=n+304|0,u2=n+296|0,i2=n+288|0,Pc=n+280|0,Fc=n+272|0,r2=n+264|0,uf=n+256|0,n2=n+248|0,t2=n+240|0,ea=n+232|0,ss=n+224|0,ls=n+216|0,os=n+208|0,uu=n+200|0,kn=n+192|0,Pr=n+184|0,_r=n+176|0,gn=n+168|0,Pn=n+160|0,Br=n+152|0,Zt=n+144|0,Ie=n+136|0,ke=n+128|0,ct=n+120|0,Ye=n+112|0,Ze=n+104|0,ye=n+96|0,Te=n+88|0,Be=n+80|0,X=n+72|0,b=n+64|0,M=n+56|0,L=n+48|0,T=n+40|0,w=n+32|0,v=n+24|0,a=n+16|0,s=n+8|0,u=n,Tf(e,3646),$c(e,3651,2)|0,Dh(e,3665,2)|0,sm(e,3682,18)|0,t[ff>>2]=19,t[ff+4>>2]=0,t[r>>2]=t[ff>>2],t[r+4>>2]=t[ff+4>>2],Vs(e,3690,r)|0,t[Bn>>2]=1,t[Bn+4>>2]=0,t[r>>2]=t[Bn>>2],t[r+4>>2]=t[Bn+4>>2],ma(e,3696,r)|0,t[Or>>2]=2,t[Or+4>>2]=0,t[r>>2]=t[Or>>2],t[r+4>>2]=t[Or+4>>2],iu(e,3706,r)|0,t[Gl>>2]=1,t[Gl+4>>2]=0,t[r>>2]=t[Gl>>2],t[r+4>>2]=t[Gl+4>>2],M0(e,3722,r)|0,t[cs>>2]=2,t[cs+4>>2]=0,t[r>>2]=t[cs>>2],t[r+4>>2]=t[cs+4>>2],M0(e,3734,r)|0,t[Dl>>2]=3,t[Dl+4>>2]=0,t[r>>2]=t[Dl>>2],t[r+4>>2]=t[Dl+4>>2],iu(e,3753,r)|0,t[bu>>2]=4,t[bu+4>>2]=0,t[r>>2]=t[bu>>2],t[r+4>>2]=t[bu+4>>2],iu(e,3769,r)|0,t[Sr>>2]=5,t[Sr+4>>2]=0,t[r>>2]=t[Sr>>2],t[r+4>>2]=t[Sr+4>>2],iu(e,3783,r)|0,t[G1>>2]=6,t[G1+4>>2]=0,t[r>>2]=t[G1>>2],t[r+4>>2]=t[G1+4>>2],iu(e,3796,r)|0,t[V1>>2]=7,t[V1+4>>2]=0,t[r>>2]=t[V1>>2],t[r+4>>2]=t[V1+4>>2],iu(e,3813,r)|0,t[W1>>2]=8,t[W1+4>>2]=0,t[r>>2]=t[W1>>2],t[r+4>>2]=t[W1+4>>2],iu(e,3825,r)|0,t[af>>2]=3,t[af+4>>2]=0,t[r>>2]=t[af>>2],t[r+4>>2]=t[af+4>>2],M0(e,3843,r)|0,t[Vl>>2]=4,t[Vl+4>>2]=0,t[r>>2]=t[Vl>>2],t[r+4>>2]=t[Vl+4>>2],M0(e,3853,r)|0,t[Jf>>2]=9,t[Jf+4>>2]=0,t[r>>2]=t[Jf>>2],t[r+4>>2]=t[Jf+4>>2],iu(e,3870,r)|0,t[q1>>2]=10,t[q1+4>>2]=0,t[r>>2]=t[q1>>2],t[r+4>>2]=t[q1+4>>2],iu(e,3884,r)|0,t[H1>>2]=11,t[H1+4>>2]=0,t[r>>2]=t[H1>>2],t[r+4>>2]=t[H1+4>>2],iu(e,3896,r)|0,t[Qf>>2]=1,t[Qf+4>>2]=0,t[r>>2]=t[Qf>>2],t[r+4>>2]=t[Qf+4>>2],u0(e,3907,r)|0,t[fs>>2]=2,t[fs+4>>2]=0,t[r>>2]=t[fs>>2],t[r+4>>2]=t[fs+4>>2],u0(e,3915,r)|0,t[Xf>>2]=3,t[Xf+4>>2]=0,t[r>>2]=t[Xf>>2],t[r+4>>2]=t[Xf+4>>2],u0(e,3928,r)|0,t[as>>2]=4,t[as+4>>2]=0,t[r>>2]=t[as>>2],t[r+4>>2]=t[as+4>>2],u0(e,3948,r)|0,t[z1>>2]=5,t[z1+4>>2]=0,t[r>>2]=t[z1>>2],t[r+4>>2]=t[z1+4>>2],u0(e,3960,r)|0,t[hu>>2]=6,t[hu+4>>2]=0,t[r>>2]=t[hu>>2],t[r+4>>2]=t[hu+4>>2],u0(e,3974,r)|0,t[sf>>2]=7,t[sf+4>>2]=0,t[r>>2]=t[sf>>2],t[r+4>>2]=t[sf+4>>2],u0(e,3983,r)|0,t[El>>2]=20,t[El+4>>2]=0,t[r>>2]=t[El>>2],t[r+4>>2]=t[El+4>>2],Vs(e,3999,r)|0,t[Wl>>2]=8,t[Wl+4>>2]=0,t[r>>2]=t[Wl>>2],t[r+4>>2]=t[Wl+4>>2],u0(e,4012,r)|0,t[j1>>2]=9,t[j1+4>>2]=0,t[r>>2]=t[j1>>2],t[r+4>>2]=t[j1+4>>2],u0(e,4022,r)|0,t[U1>>2]=21,t[U1+4>>2]=0,t[r>>2]=t[U1>>2],t[r+4>>2]=t[U1+4>>2],Vs(e,4039,r)|0,t[Bc>>2]=10,t[Bc+4>>2]=0,t[r>>2]=t[Bc>>2],t[r+4>>2]=t[Bc+4>>2],u0(e,4053,r)|0,t[B1>>2]=11,t[B1+4>>2]=0,t[r>>2]=t[B1>>2],t[r+4>>2]=t[B1+4>>2],u0(e,4065,r)|0,t[b1>>2]=12,t[b1+4>>2]=0,t[r>>2]=t[b1>>2],t[r+4>>2]=t[b1+4>>2],u0(e,4084,r)|0,t[lf>>2]=13,t[lf+4>>2]=0,t[r>>2]=t[lf>>2],t[r+4>>2]=t[lf+4>>2],u0(e,4097,r)|0,t[Ns>>2]=14,t[Ns+4>>2]=0,t[r>>2]=t[Ns>>2],t[r+4>>2]=t[Ns+4>>2],u0(e,4117,r)|0,t[La>>2]=15,t[La+4>>2]=0,t[r>>2]=t[La>>2],t[r+4>>2]=t[La+4>>2],u0(e,4129,r)|0,t[ql>>2]=16,t[ql+4>>2]=0,t[r>>2]=t[ql>>2],t[r+4>>2]=t[ql+4>>2],u0(e,4148,r)|0,t[Kf>>2]=17,t[Kf+4>>2]=0,t[r>>2]=t[Kf>>2],t[r+4>>2]=t[Kf+4>>2],u0(e,4161,r)|0,t[bc>>2]=18,t[bc+4>>2]=0,t[r>>2]=t[bc>>2],t[r+4>>2]=t[bc+4>>2],u0(e,4181,r)|0,t[Ic>>2]=5,t[Ic+4>>2]=0,t[r>>2]=t[Ic>>2],t[r+4>>2]=t[Ic+4>>2],M0(e,4196,r)|0,t[a2>>2]=6,t[a2+4>>2]=0,t[r>>2]=t[a2>>2],t[r+4>>2]=t[a2+4>>2],M0(e,4206,r)|0,t[s2>>2]=7,t[s2+4>>2]=0,t[r>>2]=t[s2>>2],t[r+4>>2]=t[s2+4>>2],M0(e,4217,r)|0,t[Yf>>2]=3,t[Yf+4>>2]=0,t[r>>2]=t[Yf>>2],t[r+4>>2]=t[Yf+4>>2],ns(e,4235,r)|0,t[l2>>2]=1,t[l2+4>>2]=0,t[r>>2]=t[l2>>2],t[r+4>>2]=t[l2+4>>2],Ya(e,4251,r)|0,t[of>>2]=4,t[of+4>>2]=0,t[r>>2]=t[of>>2],t[r+4>>2]=t[of+4>>2],ns(e,4263,r)|0,t[pi>>2]=5,t[pi+4>>2]=0,t[r>>2]=t[pi>>2],t[r+4>>2]=t[pi+4>>2],ns(e,4279,r)|0,t[o2>>2]=6,t[o2+4>>2]=0,t[r>>2]=t[o2>>2],t[r+4>>2]=t[o2+4>>2],ns(e,4293,r)|0,t[u2>>2]=7,t[u2+4>>2]=0,t[r>>2]=t[u2>>2],t[r+4>>2]=t[u2+4>>2],ns(e,4306,r)|0,t[i2>>2]=8,t[i2+4>>2]=0,t[r>>2]=t[i2>>2],t[r+4>>2]=t[i2+4>>2],ns(e,4323,r)|0,t[Pc>>2]=9,t[Pc+4>>2]=0,t[r>>2]=t[Pc>>2],t[r+4>>2]=t[Pc+4>>2],ns(e,4335,r)|0,t[Fc>>2]=2,t[Fc+4>>2]=0,t[r>>2]=t[Fc>>2],t[r+4>>2]=t[Fc+4>>2],Ya(e,4353,r)|0,t[r2>>2]=12,t[r2+4>>2]=0,t[r>>2]=t[r2>>2],t[r+4>>2]=t[r2+4>>2],uo(e,4363,r)|0,t[uf>>2]=1,t[uf+4>>2]=0,t[r>>2]=t[uf>>2],t[r+4>>2]=t[uf+4>>2],fl(e,4376,r)|0,t[n2>>2]=2,t[n2+4>>2]=0,t[r>>2]=t[n2>>2],t[r+4>>2]=t[n2+4>>2],fl(e,4388,r)|0,t[t2>>2]=13,t[t2+4>>2]=0,t[r>>2]=t[t2>>2],t[r+4>>2]=t[t2+4>>2],uo(e,4402,r)|0,t[ea>>2]=14,t[ea+4>>2]=0,t[r>>2]=t[ea>>2],t[r+4>>2]=t[ea+4>>2],uo(e,4411,r)|0,t[ss>>2]=15,t[ss+4>>2]=0,t[r>>2]=t[ss>>2],t[r+4>>2]=t[ss+4>>2],uo(e,4421,r)|0,t[ls>>2]=16,t[ls+4>>2]=0,t[r>>2]=t[ls>>2],t[r+4>>2]=t[ls+4>>2],uo(e,4433,r)|0,t[os>>2]=17,t[os+4>>2]=0,t[r>>2]=t[os>>2],t[r+4>>2]=t[os+4>>2],uo(e,4446,r)|0,t[uu>>2]=18,t[uu+4>>2]=0,t[r>>2]=t[uu>>2],t[r+4>>2]=t[uu+4>>2],uo(e,4458,r)|0,t[kn>>2]=3,t[kn+4>>2]=0,t[r>>2]=t[kn>>2],t[r+4>>2]=t[kn+4>>2],fl(e,4471,r)|0,t[Pr>>2]=1,t[Pr+4>>2]=0,t[r>>2]=t[Pr>>2],t[r+4>>2]=t[Pr+4>>2],yc(e,4486,r)|0,t[_r>>2]=10,t[_r+4>>2]=0,t[r>>2]=t[_r>>2],t[r+4>>2]=t[_r+4>>2],ns(e,4496,r)|0,t[gn>>2]=11,t[gn+4>>2]=0,t[r>>2]=t[gn>>2],t[r+4>>2]=t[gn+4>>2],ns(e,4508,r)|0,t[Pn>>2]=3,t[Pn+4>>2]=0,t[r>>2]=t[Pn>>2],t[r+4>>2]=t[Pn+4>>2],Ya(e,4519,r)|0,t[Br>>2]=4,t[Br+4>>2]=0,t[r>>2]=t[Br>>2],t[r+4>>2]=t[Br+4>>2],M2(e,4530,r)|0,t[Zt>>2]=19,t[Zt+4>>2]=0,t[r>>2]=t[Zt>>2],t[r+4>>2]=t[Zt+4>>2],wh(e,4542,r)|0,t[Ie>>2]=12,t[Ie+4>>2]=0,t[r>>2]=t[Ie>>2],t[r+4>>2]=t[Ie+4>>2],Cf(e,4554,r)|0,t[ke>>2]=13,t[ke+4>>2]=0,t[r>>2]=t[ke>>2],t[r+4>>2]=t[ke+4>>2],xf(e,4568,r)|0,t[ct>>2]=2,t[ct+4>>2]=0,t[r>>2]=t[ct>>2],t[r+4>>2]=t[ct+4>>2],e1(e,4578,r)|0,t[Ye>>2]=20,t[Ye+4>>2]=0,t[r>>2]=t[Ye>>2],t[r+4>>2]=t[Ye+4>>2],Nl(e,4587,r)|0,t[Ze>>2]=22,t[Ze+4>>2]=0,t[r>>2]=t[Ze>>2],t[r+4>>2]=t[Ze+4>>2],Vs(e,4602,r)|0,t[ye>>2]=23,t[ye+4>>2]=0,t[r>>2]=t[ye>>2],t[r+4>>2]=t[ye+4>>2],Vs(e,4619,r)|0,t[Te>>2]=14,t[Te+4>>2]=0,t[r>>2]=t[Te>>2],t[r+4>>2]=t[Te+4>>2],t1(e,4629,r)|0,t[Be>>2]=1,t[Be+4>>2]=0,t[r>>2]=t[Be>>2],t[r+4>>2]=t[Be+4>>2],ya(e,4637,r)|0,t[X>>2]=4,t[X+4>>2]=0,t[r>>2]=t[X>>2],t[r+4>>2]=t[X+4>>2],fl(e,4653,r)|0,t[b>>2]=5,t[b+4>>2]=0,t[r>>2]=t[b>>2],t[r+4>>2]=t[b+4>>2],fl(e,4669,r)|0,t[M>>2]=6,t[M+4>>2]=0,t[r>>2]=t[M>>2],t[r+4>>2]=t[M+4>>2],fl(e,4686,r)|0,t[L>>2]=7,t[L+4>>2]=0,t[r>>2]=t[L>>2],t[r+4>>2]=t[L+4>>2],fl(e,4701,r)|0,t[T>>2]=8,t[T+4>>2]=0,t[r>>2]=t[T>>2],t[r+4>>2]=t[T+4>>2],fl(e,4719,r)|0,t[w>>2]=9,t[w+4>>2]=0,t[r>>2]=t[w>>2],t[r+4>>2]=t[w+4>>2],fl(e,4736,r)|0,t[v>>2]=21,t[v+4>>2]=0,t[r>>2]=t[v>>2],t[r+4>>2]=t[v+4>>2],hd(e,4754,r)|0,t[a>>2]=2,t[a+4>>2]=0,t[r>>2]=t[a>>2],t[r+4>>2]=t[a+4>>2],yc(e,4772,r)|0,t[s>>2]=3,t[s+4>>2]=0,t[r>>2]=t[s>>2],t[r+4>>2]=t[s+4>>2],yc(e,4790,r)|0,t[u>>2]=4,t[u+4>>2]=0,t[r>>2]=t[u>>2],t[r+4>>2]=t[u+4>>2],yc(e,4808,r)|0,y=n}function Tf(e,n){e=e|0,n=n|0;var r=0;r=rf()|0,t[e>>2]=r,Vo(r,n),Zd(t[e>>2]|0)}function $c(e,n,r){return e=e|0,n=n|0,r=r|0,Mt(e,Fr(n)|0,r,0),e|0}function Dh(e,n,r){return e=e|0,n=n|0,r=r|0,d(e,Fr(n)|0,r,0),e|0}function sm(e,n,r){return e=e|0,n=n|0,r=r|0,Q4(e,Fr(n)|0,r,0),e|0}function Vs(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],H4(e,n,s),y=u,e|0}function ma(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],zl(e,n,s),y=u,e|0}function iu(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],p(e,n,s),y=u,e|0}function M0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Rv(e,n,s),y=u,e|0}function u0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],ny(e,n,s),y=u,e|0}function ns(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Gd(e,n,s),y=u,e|0}function Ya(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Vd(e,n,s),y=u,e|0}function uo(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],k0(e,n,s),y=u,e|0}function fl(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Dp(e,n,s),y=u,e|0}function yc(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],bm(e,n,s),y=u,e|0}function M2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],o0(e,n,s),y=u,e|0}function wh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Ad(e,n,s),y=u,e|0}function Cf(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Am(e,n,s),y=u,e|0}function xf(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],$2(e,n,s),y=u,e|0}function e1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],y1(e,n,s),y=u,e|0}function Nl(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Za(e,n,s),y=u,e|0}function t1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],B2(e,n,s),y=u,e|0}function ya(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],L2(e,n,s),y=u,e|0}function hd(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],vd(e,n,s),y=u,e|0}function vd(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],ga(e,r,s,1),y=u}function Fr(e){return e=e|0,e|0}function ga(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=k2()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=n1(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,md(a,u)|0,u),y=s}function k2(){var e=0,n=0;if(h[7616]|0||(cl(9136),Wt(24,9136,ge|0)|0,n=7616,t[n>>2]=1,t[n+4>>2]=0),!(sr(9136)|0)){e=9136,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));cl(9136)}return 9136}function n1(e){return e=e|0,0}function md(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=k2()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],Rf(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Af(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function wi(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0;var v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0;v=y,y=y+32|0,X=v+24|0,b=v+20|0,T=v+16|0,M=v+12|0,L=v+8|0,w=v+4|0,Be=v,t[b>>2]=n,t[T>>2]=r,t[M>>2]=u,t[L>>2]=s,t[w>>2]=a,a=e+28|0,t[Be>>2]=t[a>>2],t[X>>2]=t[Be>>2],N2(e+24|0,X,b,M,L,T,w)|0,t[a>>2]=t[t[a>>2]>>2],y=v}function N2(e,n,r,u,s,a,v){return e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,v=v|0,e=am(n)|0,n=pn(24)|0,yd(n+4|0,t[r>>2]|0,t[u>>2]|0,t[s>>2]|0,t[a>>2]|0,t[v>>2]|0),t[n>>2]=t[e>>2],t[e>>2]=n,n|0}function am(e){return e=e|0,t[e>>2]|0}function yd(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,t[e>>2]=n,t[e+4>>2]=r,t[e+8>>2]=u,t[e+12>>2]=s,t[e+16>>2]=a}function hn(e,n){return e=e|0,n=n|0,n|e|0}function Rf(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Af(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=fm(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Of(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],Rf(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Sh(e,w),cm(w),y=L;return}}function fm(e){return e=e|0,357913941}function Of(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Sh(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function cm(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function cl(e){e=e|0,qo(e)}function r1(e){e=e|0,qn(e+24|0)}function sr(e){return e=e|0,t[e>>2]|0}function qn(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function qo(e){e=e|0;var n=0;n=yr()|0,jn(e,2,3,n,Vn()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function yr(){return 9228}function Vn(){return 1140}function dl(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;return r=y,y=y+16|0,u=r+8|0,s=r,a=Eo(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],n=gc(n,u)|0,y=r,n|0}function jn(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,t[e>>2]=n,t[e+4>>2]=r,t[e+8>>2]=u,t[e+12>>2]=s,t[e+16>>2]=a}function Eo(e){return e=e|0,(t[(k2()|0)+24>>2]|0)+(e*12|0)|0}function gc(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;return s=y,y=y+48|0,u=s,r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),I1[r&31](u,e),u=oo(u)|0,y=s,u|0}function oo(e){e=e|0;var n=0,r=0,u=0,s=0;return s=y,y=y+32|0,n=s+12|0,r=s,u=Pu(Ka()|0)|0,u?(rs(n,u),Mf(r,n),_c(e,r),e=Cs(n)|0):e=Ec(e)|0,y=s,e|0}function Ka(){var e=0;return h[7632]|0||(Nf(9184),Wt(25,9184,ge|0)|0,e=7632,t[e>>2]=1,t[e+4>>2]=0),9184}function Pu(e){return e=e|0,t[e+36>>2]|0}function rs(e,n){e=e|0,n=n|0,t[e>>2]=n,t[e+4>>2]=e,t[e+8>>2]=0}function Mf(e,n){e=e|0,n=n|0,t[e>>2]=t[n>>2],t[e+4>>2]=t[n+4>>2],t[e+8>>2]=0}function _c(e,n){e=e|0,n=n|0,lo(n,e,e+8|0,e+16|0,e+24|0,e+32|0,e+40|0)|0}function Cs(e){return e=e|0,t[(t[e+4>>2]|0)+8>>2]|0}function Ec(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0,T=0;T=y,y=y+16|0,r=T+4|0,u=T,s=Oa(8)|0,a=s,v=pn(48)|0,w=v,n=w+48|0;do t[w>>2]=t[e>>2],w=w+4|0,e=e+4|0;while((w|0)<(n|0));return n=a+4|0,t[n>>2]=v,w=pn(8)|0,v=t[n>>2]|0,t[u>>2]=0,t[r>>2]=t[u>>2],Th(w,v,r),t[s>>2]=w,y=T,a|0}function Th(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,r=pn(16)|0,t[r+4>>2]=0,t[r+8>>2]=0,t[r>>2]=1092,t[r+12>>2]=n,t[e+4>>2]=r}function cn(e){e=e|0,Uv(e),Et(e)}function is(e){e=e|0,e=t[e+12>>2]|0,e|0&&Et(e)}function Do(e){e=e|0,Et(e)}function lo(e,n,r,u,s,a,v){return e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,v=v|0,a=Ji(t[e>>2]|0,n,r,u,s,a,v)|0,v=e+4|0,t[(t[v>>2]|0)+8>>2]=a,t[(t[v>>2]|0)+8>>2]|0}function Ji(e,n,r,u,s,a,v){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,v=v|0;var w=0,T=0;return w=y,y=y+16|0,T=w,Ma(T),e=go(e)|0,v=Gr(e,+j[n>>3],+j[r>>3],+j[u>>3],+j[s>>3],+j[a>>3],+j[v>>3])|0,ka(T),y=w,v|0}function Gr(e,n,r,u,s,a,v){e=e|0,n=+n,r=+r,u=+u,s=+s,a=+a,v=+v;var w=0;return w=_o(kf()|0)|0,n=+kl(n),r=+kl(r),u=+kl(u),s=+kl(s),a=+kl(a),d0(0,w|0,e|0,+n,+r,+u,+s,+a,+ +kl(v))|0}function kf(){var e=0;return h[7624]|0||(dm(9172),e=7624,t[e>>2]=1,t[e+4>>2]=0),9172}function dm(e){e=e|0,ll(e,Ll()|0,6)}function Ll(){return 1112}function Nf(e){e=e|0,Xa(e)}function Lf(e){e=e|0,gd(e+24|0),_d(e+16|0)}function gd(e){e=e|0,i1(e)}function _d(e){e=e|0,Dc(e)}function Dc(e){e=e|0;var n=0,r=0;if(n=t[e>>2]|0,n|0)do r=n,n=t[n>>2]|0,Et(r);while((n|0)!=0);t[e>>2]=0}function i1(e){e=e|0;var n=0,r=0;if(n=t[e>>2]|0,n|0)do r=n,n=t[n>>2]|0,Et(r);while((n|0)!=0);t[e>>2]=0}function Xa(e){e=e|0;var n=0;t[e+16>>2]=0,t[e+20>>2]=0,n=e+24|0,t[n>>2]=0,t[e+28>>2]=n,t[e+36>>2]=0,h[e+40>>0]=0,h[e+41>>0]=0}function L2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Ed(e,r,s,0),y=u}function Ed(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=u1()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Ff(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,o1(a,u)|0,u),y=s}function u1(){var e=0,n=0;if(h[7640]|0||(Fl(9232),Wt(26,9232,ge|0)|0,n=7640,t[n>>2]=1,t[n+4>>2]=0),!(sr(9232)|0)){e=9232,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Fl(9232)}return 9232}function Ff(e){return e=e|0,0}function o1(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=u1()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],Qa(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(l1(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function Qa(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function l1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=F2(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Dd(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],Qa(a,u,r),t[T>>2]=(t[T>>2]|0)+12,wc(e,w),s1(w),y=L;return}}function F2(e){return e=e|0,357913941}function Dd(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function wc(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function s1(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Fl(e){e=e|0,P2(e)}function Ea(e){e=e|0,Ch(e+24|0)}function Ch(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function P2(e){e=e|0;var n=0;n=yr()|0,jn(e,2,1,n,I2()|0,3),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function I2(){return 1144}function xh(e,n,r,u,s){e=e|0,n=n|0,r=+r,u=+u,s=s|0;var a=0,v=0,w=0,T=0;a=y,y=y+16|0,v=a+8|0,w=a,T=pm(e)|0,e=t[T+4>>2]|0,t[w>>2]=t[T>>2],t[w+4>>2]=e,t[v>>2]=t[w>>2],t[v+4>>2]=t[w+4>>2],Rh(n,v,r,u,s),y=a}function pm(e){return e=e|0,(t[(u1()|0)+24>>2]|0)+(e*12|0)|0}function Rh(e,n,r,u,s){e=e|0,n=n|0,r=+r,u=+u,s=s|0;var a=0,v=0,w=0,T=0,L=0;L=y,y=y+16|0,v=L+2|0,w=L+1|0,T=L,a=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(a=t[(t[e>>2]|0)+a>>2]|0),Pl(v,r),r=+us(v,r),Pl(w,u),u=+us(w,u),xs(T,s),T=Gs(T,s)|0,L8[a&1](e,r,u,T),y=L}function Pl(e,n){e=e|0,n=+n}function us(e,n){return e=e|0,n=+n,+ +Ah(n)}function xs(e,n){e=e|0,n=n|0}function Gs(e,n){return e=e|0,n=n|0,b2(n)|0}function b2(e){return e=e|0,e|0}function Ah(e){return e=+e,+e}function B2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],U2(e,r,s,1),y=u}function U2(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=a1()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=f1(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Oh(a,u)|0,u),y=s}function a1(){var e=0,n=0;if(h[7648]|0||(c1(9268),Wt(27,9268,ge|0)|0,n=7648,t[n>>2]=1,t[n+4>>2]=0),!(sr(9268)|0)){e=9268,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));c1(9268)}return 9268}function f1(e){return e=e|0,0}function Oh(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=a1()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],j2(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(z2(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function j2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function z2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Rs(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Ja(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],j2(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Mh(e,w),du(w),y=L;return}}function Rs(e){return e=e|0,357913941}function Ja(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Mh(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function du(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function c1(e){e=e|0,Il(e)}function kh(e){e=e|0,d1(e+24|0)}function d1(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Il(e){e=e|0;var n=0;n=yr()|0,jn(e,2,4,n,Nh()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function Nh(){return 1160}function H2(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;return r=y,y=y+16|0,u=r+8|0,s=r,a=Lh(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],n=p1(n,u)|0,y=r,n|0}function Lh(e){return e=e|0,(t[(a1()|0)+24>>2]|0)+(e*12|0)|0}function p1(e,n){e=e|0,n=n|0;var r=0;return r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),bl(Qp[r&31](e)|0)|0}function bl(e){return e=e|0,e&1|0}function Za(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Da(e,r,s,0),y=u}function Da(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=q2()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=W2(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,hm(a,u)|0,u),y=s}function q2(){var e=0,n=0;if(h[7656]|0||(Ih(9304),Wt(28,9304,ge|0)|0,n=7656,t[n>>2]=1,t[n+4>>2]=0),!(sr(9304)|0)){e=9304,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Ih(9304)}return 9304}function W2(e){return e=e|0,0}function hm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=q2()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],V2(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Fh(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function V2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Fh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Ph(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,G2(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],V2(a,u,r),t[T>>2]=(t[T>>2]|0)+12,vm(e,w),mm(w),y=L;return}}function Ph(e){return e=e|0,357913941}function G2(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function vm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function mm(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Ih(e){e=e|0,h1(e)}function ym(e){e=e|0,Y2(e+24|0)}function Y2(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function h1(e){e=e|0;var n=0;n=yr()|0,jn(e,2,5,n,v1()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function v1(){return 1164}function m1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,s=u+8|0,a=u,v=wa(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],K2(n,s,r),y=u}function wa(e){return e=e|0,(t[(q2()|0)+24>>2]|0)+(e*12|0)|0}function K2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),Ys(s,r),r=Ks(s,r)|0,I1[u&31](e,r),Xs(s),y=a}function Ys(e,n){e=e|0,n=n|0,X2(e,n)}function Ks(e,n){return e=e|0,n=n|0,e|0}function Xs(e){e=e|0,fa(e)}function X2(e,n){e=e|0,n=n|0,Sa(e,n)}function Sa(e,n){e=e|0,n=n|0,t[e>>2]=n}function y1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],wd(e,r,s,0),y=u}function wd(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Sc()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Q2(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,wo(a,u)|0,u),y=s}function Sc(){var e=0,n=0;if(h[7664]|0||(Hh(9340),Wt(29,9340,ge|0)|0,n=7664,t[n>>2]=1,t[n+4>>2]=0),!(sr(9340)|0)){e=9340,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Hh(9340)}return 9340}function Q2(e){return e=e|0,0}function wo(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Sc()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],bh(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Bh(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function bh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Bh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Uh(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,jh(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],bh(a,u,r),t[T>>2]=(t[T>>2]|0)+12,gm(e,w),zh(w),y=L;return}}function Uh(e){return e=e|0,357913941}function jh(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function gm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function zh(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Hh(e){e=e|0,qh(e)}function g1(e){e=e|0,J2(e+24|0)}function J2(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function qh(e){e=e|0;var n=0;n=yr()|0,jn(e,2,4,n,Z2()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function Z2(){return 1180}function Wh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=_m(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],r=Em(n,s,r)|0,y=u,r|0}function _m(e){return e=e|0,(t[(Sc()|0)+24>>2]|0)+(e*12|0)|0}function Em(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;return a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),Pf(s,r),s=If(s,r)|0,s=Sd(bE[u&15](e,s)|0)|0,y=a,s|0}function Pf(e,n){e=e|0,n=n|0}function If(e,n){return e=e|0,n=n|0,Dm(n)|0}function Sd(e){return e=e|0,e|0}function Dm(e){return e=e|0,e|0}function $2(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Td(e,r,s,0),y=u}function Td(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=ep()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Vh(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,tp(a,u)|0,u),y=s}function ep(){var e=0,n=0;if(h[7672]|0||(Kh(9376),Wt(30,9376,ge|0)|0,n=7672,t[n>>2]=1,t[n+4>>2]=0),!(sr(9376)|0)){e=9376,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Kh(9376)}return 9376}function Vh(e){return e=e|0,0}function tp(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=ep()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],Gh(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Yh(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function Gh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Yh(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=np(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,wm(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],Gh(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Sm(e,w),Tm(w),y=L;return}}function np(e){return e=e|0,357913941}function wm(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Sm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Tm(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Kh(e){e=e|0,rp(e)}function _1(e){e=e|0,Cm(e+24|0)}function Cm(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function rp(e){e=e|0;var n=0;n=yr()|0,jn(e,2,5,n,ip()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function ip(){return 1196}function xm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;return r=y,y=y+16|0,u=r+8|0,s=r,a=Rm(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],n=Xh(n,u)|0,y=r,n|0}function Rm(e){return e=e|0,(t[(ep()|0)+24>>2]|0)+(e*12|0)|0}function Xh(e,n){e=e|0,n=n|0;var r=0;return r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),Sd(Qp[r&31](e)|0)|0}function Am(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Om(e,r,s,1),y=u}function Om(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=up()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=op(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Ta(a,u)|0,u),y=s}function up(){var e=0,n=0;if(h[7680]|0||(sp(9412),Wt(31,9412,ge|0)|0,n=7680,t[n>>2]=1,t[n+4>>2]=0),!(sr(9412)|0)){e=9412,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));sp(9412)}return 9412}function op(e){return e=e|0,0}function Ta(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=up()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],E1(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(lp(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function E1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function lp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Qh(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Cd(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],E1(a,u,r),t[T>>2]=(t[T>>2]|0)+12,D1(e,w),Jh(w),y=L;return}}function Qh(e){return e=e|0,357913941}function Cd(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function D1(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Jh(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function sp(e){e=e|0,$h(e)}function Zh(e){e=e|0,ap(e+24|0)}function ap(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function $h(e){e=e|0;var n=0;n=yr()|0,jn(e,2,6,n,ev()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function ev(){return 1200}function fp(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;return r=y,y=y+16|0,u=r+8|0,s=r,a=xd(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],n=Rd(n,u)|0,y=r,n|0}function xd(e){return e=e|0,(t[(up()|0)+24>>2]|0)+(e*12|0)|0}function Rd(e,n){e=e|0,n=n|0;var r=0;return r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),H0(Qp[r&31](e)|0)|0}function H0(e){return e=e|0,e|0}function Ad(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Ca(e,r,s,0),y=u}function Ca(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=$a()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Od(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Md(a,u)|0,u),y=s}function $a(){var e=0,n=0;if(h[7688]|0||(pp(9448),Wt(32,9448,ge|0)|0,n=7688,t[n>>2]=1,t[n+4>>2]=0),!(sr(9448)|0)){e=9448,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));pp(9448)}return 9448}function Od(e){return e=e|0,0}function Md(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=$a()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],cp(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(kd(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function cp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function kd(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=tv(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Mm(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],cp(a,u,r),t[T>>2]=(t[T>>2]|0)+12,nv(e,w),dp(w),y=L;return}}function tv(e){return e=e|0,357913941}function Mm(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function nv(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function dp(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function pp(e){e=e|0,Nm(e)}function hp(e){e=e|0,km(e+24|0)}function km(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Nm(e){e=e|0;var n=0;n=yr()|0,jn(e,2,6,n,So()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function So(){return 1204}function Nd(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,s=u+8|0,a=u,v=Lm(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],pl(n,s,r),y=u}function Lm(e){return e=e|0,(t[($a()|0)+24>>2]|0)+(e*12|0)|0}function pl(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),tr(s,r),s=Qs(s,r)|0,I1[u&31](e,s),y=a}function tr(e,n){e=e|0,n=n|0}function Qs(e,n){return e=e|0,n=n|0,hl(n)|0}function hl(e){return e=e|0,e|0}function o0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],rv(e,r,s,0),y=u}function rv(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Js()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=vp(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Fm(a,u)|0,u),y=s}function Js(){var e=0,n=0;if(h[7696]|0||(gp(9484),Wt(33,9484,ge|0)|0,n=7696,t[n>>2]=1,t[n+4>>2]=0),!(sr(9484)|0)){e=9484,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));gp(9484)}return 9484}function vp(e){return e=e|0,0}function Fm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Js()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],iv(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(mp(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function iv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function mp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Pm(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,yp(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],iv(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Tc(e,w),xa(w),y=L;return}}function Pm(e){return e=e|0,357913941}function yp(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Tc(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function xa(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function gp(e){e=e|0,Gu(e)}function Ld(e){e=e|0,Iu(e+24|0)}function Iu(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Gu(e){e=e|0;var n=0;n=yr()|0,jn(e,2,1,n,_p()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function _p(){return 1212}function Ep(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;s=y,y=y+16|0,a=s+8|0,v=s,w=uv(e)|0,e=t[w+4>>2]|0,t[v>>2]=t[w>>2],t[v+4>>2]=e,t[a>>2]=t[v>>2],t[a+4>>2]=t[v+4>>2],Im(n,a,r,u),y=s}function uv(e){return e=e|0,(t[(Js()|0)+24>>2]|0)+(e*12|0)|0}function Im(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;w=y,y=y+16|0,a=w+1|0,v=w,s=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(s=t[(t[e>>2]|0)+s>>2]|0),tr(a,r),a=Qs(a,r)|0,Pf(v,u),v=If(v,u)|0,Fy[s&15](e,a,v),y=w}function bm(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Bm(e,r,s,1),y=u}function Bm(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Fd()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=ov(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Cc(a,u)|0,u),y=s}function Fd(){var e=0,n=0;if(h[7704]|0||(lv(9520),Wt(34,9520,ge|0)|0,n=7704,t[n>>2]=1,t[n+4>>2]=0),!(sr(9520)|0)){e=9520,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));lv(9520)}return 9520}function ov(e){return e=e|0,0}function Cc(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Fd()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],w1(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Um(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function w1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Um(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Pd(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,S1(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],w1(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Bl(e,w),Ra(w),y=L;return}}function Pd(e){return e=e|0,357913941}function S1(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Bl(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Ra(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function lv(e){e=e|0,av(e)}function jm(e){e=e|0,sv(e+24|0)}function sv(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function av(e){e=e|0;var n=0;n=yr()|0,jn(e,2,1,n,zm()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function zm(){return 1224}function fv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;return s=y,y=y+16|0,a=s+8|0,v=s,w=Aa(e)|0,e=t[w+4>>2]|0,t[v>>2]=t[w>>2],t[v+4>>2]=e,t[a>>2]=t[v>>2],t[a+4>>2]=t[v+4>>2],u=+Mr(n,a,r),y=s,+u}function Aa(e){return e=e|0,(t[(Fd()|0)+24>>2]|0)+(e*12|0)|0}function Mr(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),xs(s,r),s=Gs(s,r)|0,v=+Ga(+P8[u&7](e,s)),y=a,+v}function Dp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],vl(e,r,s,1),y=u}function vl(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=yu()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=T1(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Ui(a,u)|0,u),y=s}function yu(){var e=0,n=0;if(h[7712]|0||(Sp(9556),Wt(35,9556,ge|0)|0,n=7712,t[n>>2]=1,t[n+4>>2]=0),!(sr(9556)|0)){e=9556,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Sp(9556)}return 9556}function T1(e){return e=e|0,0}function Ui(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=yu()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],wp(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Id(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function wp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Id(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=To(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,As(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],wp(a,u,r),t[T>>2]=(t[T>>2]|0)+12,bf(e,w),bd(w),y=L;return}}function To(e){return e=e|0,357913941}function As(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function bf(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function bd(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Sp(e){e=e|0,Tp(e)}function C1(e){e=e|0,x1(e+24|0)}function x1(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Tp(e){e=e|0;var n=0;n=yr()|0,jn(e,2,5,n,nr()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function nr(){return 1232}function ml(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=Gn(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],r=+q0(n,s),y=u,+r}function Gn(e){return e=e|0,(t[(yu()|0)+24>>2]|0)+(e*12|0)|0}function q0(e,n){e=e|0,n=n|0;var r=0;return r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),+ +Ga(+F8[r&15](e))}function k0(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Bd(e,r,s,1),y=u}function Bd(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Ul()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=R1(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,xc(a,u)|0,u),y=s}function Ul(){var e=0,n=0;if(h[7720]|0||(zd(9592),Wt(36,9592,ge|0)|0,n=7720,t[n>>2]=1,t[n+4>>2]=0),!(sr(9592)|0)){e=9592,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));zd(9592)}return 9592}function R1(e){return e=e|0,0}function xc(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Ul()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],Rc(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Ud(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function Rc(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Ud(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Cp(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,N0(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],Rc(a,u,r),t[T>>2]=(t[T>>2]|0)+12,dn(e,w),jd(w),y=L;return}}function Cp(e){return e=e|0,357913941}function N0(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function dn(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function jd(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function zd(e){e=e|0,Mc(e)}function Ac(e){e=e|0,Oc(e+24|0)}function Oc(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Mc(e){e=e|0;var n=0;n=yr()|0,jn(e,2,7,n,A1()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function A1(){return 1276}function xp(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;return r=y,y=y+16|0,u=r+8|0,s=r,a=ef(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],n=Hm(n,u)|0,y=r,n|0}function ef(e){return e=e|0,(t[(Ul()|0)+24>>2]|0)+(e*12|0)|0}function Hm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;return s=y,y=y+16|0,u=s,r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),I1[r&31](u,e),u=kc(u)|0,y=s,u|0}function kc(e){e=e|0;var n=0,r=0,u=0,s=0;return s=y,y=y+32|0,n=s+12|0,r=s,u=Pu(Hd()|0)|0,u?(rs(n,u),Mf(r,n),cv(e,r),e=Cs(n)|0):e=O1(e)|0,y=s,e|0}function Hd(){var e=0;return h[7736]|0||(Wo(9640),Wt(25,9640,ge|0)|0,e=7736,t[e>>2]=1,t[e+4>>2]=0),9640}function cv(e,n){e=e|0,n=n|0,Nc(n,e,e+8|0)|0}function O1(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0;return r=y,y=y+16|0,s=r+4|0,v=r,u=Oa(8)|0,n=u,w=pn(16)|0,t[w>>2]=t[e>>2],t[w+4>>2]=t[e+4>>2],t[w+8>>2]=t[e+8>>2],t[w+12>>2]=t[e+12>>2],a=n+4|0,t[a>>2]=w,e=pn(8)|0,a=t[a>>2]|0,t[v>>2]=0,t[s>>2]=t[v>>2],Bf(e,a,s),t[u>>2]=e,y=r,n|0}function Bf(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,r=pn(16)|0,t[r+4>>2]=0,t[r+8>>2]=0,t[r>>2]=1244,t[r+12>>2]=n,t[e+4>>2]=r}function Uf(e){e=e|0,Uv(e),Et(e)}function M1(e){e=e|0,e=t[e+12>>2]|0,e|0&&Et(e)}function jl(e){e=e|0,Et(e)}function Nc(e,n,r){return e=e|0,n=n|0,r=r|0,n=jf(t[e>>2]|0,n,r)|0,r=e+4|0,t[(t[r>>2]|0)+8>>2]=n,t[(t[r>>2]|0)+8>>2]|0}function jf(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;return u=y,y=y+16|0,s=u,Ma(s),e=go(e)|0,r=qm(e,t[n>>2]|0,+j[r>>3])|0,ka(s),y=u,r|0}function qm(e,n,r){e=e|0,n=n|0,r=+r;var u=0;return u=_o(yl()|0)|0,n=sd(n)|0,Hr(0,u|0,e|0,n|0,+ +kl(r))|0}function yl(){var e=0;return h[7728]|0||(qd(9628),e=7728,t[e>>2]=1,t[e+4>>2]=0),9628}function qd(e){e=e|0,ll(e,Wd()|0,2)}function Wd(){return 1264}function Wo(e){e=e|0,Xa(e)}function Vd(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Wm(e,r,s,1),y=u}function Wm(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=k1()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Vm(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Gm(a,u)|0,u),y=s}function k1(){var e=0,n=0;if(h[7744]|0||(hv(9684),Wt(37,9684,ge|0)|0,n=7744,t[n>>2]=1,t[n+4>>2]=0),!(sr(9684)|0)){e=9684,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));hv(9684)}return 9684}function Vm(e){return e=e|0,0}function Gm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=k1()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],dv(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Ym(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function dv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Ym(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=pv(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Km(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],dv(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Xm(e,w),Qm(w),y=L;return}}function pv(e){return e=e|0,357913941}function Km(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Xm(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Qm(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function hv(e){e=e|0,Zm(e)}function Jm(e){e=e|0,Rp(e+24|0)}function Rp(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Zm(e){e=e|0;var n=0;n=yr()|0,jn(e,2,5,n,zf()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function zf(){return 1280}function vv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=mv(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],r=yv(n,s,r)|0,y=u,r|0}function mv(e){return e=e|0,(t[(k1()|0)+24>>2]|0)+(e*12|0)|0}function yv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return v=y,y=y+32|0,s=v,a=v+16|0,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),xs(a,r),a=Gs(a,r)|0,Fy[u&15](s,e,a),a=kc(s)|0,y=v,a|0}function Gd(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Yd(e,r,s,1),y=u}function Yd(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Ap()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=gv(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Kd(a,u)|0,u),y=s}function Ap(){var e=0,n=0;if(h[7752]|0||(Sv(9720),Wt(38,9720,ge|0)|0,n=7752,t[n>>2]=1,t[n+4>>2]=0),!(sr(9720)|0)){e=9720,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Sv(9720)}return 9720}function gv(e){return e=e|0,0}function Kd(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Ap()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],_v(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Ev(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function _v(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Ev(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Op(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Dv(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],_v(a,u,r),t[T>>2]=(t[T>>2]|0)+12,wv(e,w),$m(w),y=L;return}}function Op(e){return e=e|0,357913941}function Dv(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function wv(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function $m(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Sv(e){e=e|0,Tv(e)}function ey(e){e=e|0,Xd(e+24|0)}function Xd(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Tv(e){e=e|0;var n=0;n=yr()|0,jn(e,2,8,n,Mp()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function Mp(){return 1288}function ty(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;return r=y,y=y+16|0,u=r+8|0,s=r,a=l0(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],n=kp(n,u)|0,y=r,n|0}function l0(e){return e=e|0,(t[(Ap()|0)+24>>2]|0)+(e*12|0)|0}function kp(e,n){e=e|0,n=n|0;var r=0;return r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),cd(Qp[r&31](e)|0)|0}function ny(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],ry(e,r,s,0),y=u}function ry(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Np()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=tf(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Lp(a,u)|0,u),y=s}function Np(){var e=0,n=0;if(h[7760]|0||(Ip(9756),Wt(39,9756,ge|0)|0,n=7760,t[n>>2]=1,t[n+4>>2]=0),!(sr(9756)|0)){e=9756,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Ip(9756)}return 9756}function tf(e){return e=e|0,0}function Lp(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Np()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],Fp(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(Pp(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function Fp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function Pp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=iy(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,uy(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],Fp(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Cv(e,w),Hf(w),y=L;return}}function iy(e){return e=e|0,357913941}function uy(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Cv(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Hf(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Ip(e){e=e|0,ly(e)}function xv(e){e=e|0,oy(e+24|0)}function oy(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function ly(e){e=e|0;var n=0;n=yr()|0,jn(e,2,8,n,bp()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function bp(){return 1292}function Bp(e,n,r){e=e|0,n=n|0,r=+r;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,s=u+8|0,a=u,v=sy(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],ay(n,s,r),y=u}function sy(e){return e=e|0,(t[(Np()|0)+24>>2]|0)+(e*12|0)|0}function ay(e,n,r){e=e|0,n=n|0,r=+r;var u=0,s=0,a=0;a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),Pl(s,r),r=+us(s,r),k8[u&31](e,r),y=a}function Rv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Up(e,r,s,0),y=u}function Up(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=jp()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Qd(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,fy(a,u)|0,u),y=s}function jp(){var e=0,n=0;if(h[7768]|0||(zp(9792),Wt(40,9792,ge|0)|0,n=7768,t[n>>2]=1,t[n+4>>2]=0),!(sr(9792)|0)){e=9792,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));zp(9792)}return 9792}function Qd(e){return e=e|0,0}function fy(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=jp()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],N1(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(cy(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function N1(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function cy(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Av(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Ov(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],N1(a,u,r),t[T>>2]=(t[T>>2]|0)+12,dy(e,w),qf(w),y=L;return}}function Av(e){return e=e|0,357913941}function Ov(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function dy(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function qf(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function zp(e){e=e|0,hy(e)}function Mv(e){e=e|0,py(e+24|0)}function py(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function hy(e){e=e|0;var n=0;n=yr()|0,jn(e,2,1,n,Hp()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function Hp(){return 1300}function vy(e,n,r,u){e=e|0,n=n|0,r=r|0,u=+u;var s=0,a=0,v=0,w=0;s=y,y=y+16|0,a=s+8|0,v=s,w=Zs(e)|0,e=t[w+4>>2]|0,t[v>>2]=t[w>>2],t[v+4>>2]=e,t[a>>2]=t[v>>2],t[a+4>>2]=t[v+4>>2],my(n,a,r,u),y=s}function Zs(e){return e=e|0,(t[(jp()|0)+24>>2]|0)+(e*12|0)|0}function my(e,n,r,u){e=e|0,n=n|0,r=r|0,u=+u;var s=0,a=0,v=0,w=0;w=y,y=y+16|0,a=w+1|0,v=w,s=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(s=t[(t[e>>2]|0)+s>>2]|0),xs(a,r),a=Gs(a,r)|0,Pl(v,u),u=+us(v,u),U8[s&15](e,a,u),y=w}function p(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],m(e,r,s,0),y=u}function m(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=R()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=I(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,W(a,u)|0,u),y=s}function R(){var e=0,n=0;if(h[7776]|0||(Ot(9828),Wt(41,9828,ge|0)|0,n=7776,t[n>>2]=1,t[n+4>>2]=0),!(sr(9828)|0)){e=9828,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Ot(9828)}return 9828}function I(e){return e=e|0,0}function W(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=R()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],te(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(pe(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function te(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function pe(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Ee(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,be(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],te(a,u,r),t[T>>2]=(t[T>>2]|0)+12,Dt(e,w),Tt(w),y=L;return}}function Ee(e){return e=e|0,357913941}function be(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function Dt(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Tt(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Ot(e){e=e|0,rr(e)}function on(e){e=e|0,Mn(e+24|0)}function Mn(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function rr(e){e=e|0;var n=0;n=yr()|0,jn(e,2,7,n,br()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function br(){return 1312}function ar(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,s=u+8|0,a=u,v=ri(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],fi(n,s,r),y=u}function ri(e){return e=e|0,(t[(R()|0)+24>>2]|0)+(e*12|0)|0}function fi(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),xs(s,r),s=Gs(s,r)|0,I1[u&31](e,s),y=a}function zl(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Zi(e,r,s,0),y=u}function Zi(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=so()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=s0(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,Os(a,u)|0,u),y=s}function so(){var e=0,n=0;if(h[7784]|0||(Vg(9864),Wt(42,9864,ge|0)|0,n=7784,t[n>>2]=1,t[n+4>>2]=0),!(sr(9864)|0)){e=9864,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Vg(9864)}return 9864}function s0(e){return e=e|0,0}function Os(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=so()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],Co(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(kv(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function Co(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function kv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=F4(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,yy(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],Co(a,u,r),t[T>>2]=(t[T>>2]|0)+12,gy(e,w),nf(w),y=L;return}}function F4(e){return e=e|0,357913941}function yy(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function gy(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function nf(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Vg(e){e=e|0,b4(e)}function P4(e){e=e|0,I4(e+24|0)}function I4(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function b4(e){e=e|0;var n=0;n=yr()|0,jn(e,2,8,n,B4()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function B4(){return 1320}function _y(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,s=u+8|0,a=u,v=U4(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],j4(n,s,r),y=u}function U4(e){return e=e|0,(t[(so()|0)+24>>2]|0)+(e*12|0)|0}function j4(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),Ey(s,r),s=Gg(s,r)|0,I1[u&31](e,s),y=a}function Ey(e,n){e=e|0,n=n|0}function Gg(e,n){return e=e|0,n=n|0,z4(n)|0}function z4(e){return e=e|0,e|0}function H4(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],Yg(e,r,s,0),y=u}function Yg(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=Wf()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=Kg(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,q4(a,u)|0,u),y=s}function Wf(){var e=0,n=0;if(h[7792]|0||(Sy(9900),Wt(43,9900,ge|0)|0,n=7792,t[n>>2]=1,t[n+4>>2]=0),!(sr(9900)|0)){e=9900,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Sy(9900)}return 9900}function Kg(e){return e=e|0,0}function q4(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=Wf()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],qp(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(W4(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function qp(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function W4(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=Nv(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,Dy(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],qp(a,u,r),t[T>>2]=(t[T>>2]|0)+12,wy(e,w),V4(w),y=L;return}}function Nv(e){return e=e|0,357913941}function Dy(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function wy(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function V4(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function Sy(e){e=e|0,Xg(e)}function G4(e){e=e|0,Y4(e+24|0)}function Y4(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Xg(e){e=e|0;var n=0;n=yr()|0,jn(e,2,22,n,K4()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function K4(){return 1344}function X4(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0;r=y,y=y+16|0,u=r+8|0,s=r,a=Qg(e)|0,e=t[a+4>>2]|0,t[s>>2]=t[a>>2],t[s+4>>2]=e,t[u>>2]=t[s>>2],t[u+4>>2]=t[s+4>>2],Lv(n,u),y=r}function Qg(e){return e=e|0,(t[(Wf()|0)+24>>2]|0)+(e*12|0)|0}function Lv(e,n){e=e|0,n=n|0;var r=0;r=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(r=t[(t[e>>2]|0)+r>>2]|0),P1[r&127](e)}function Q4(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=Ty()|0,e=J4(r)|0,wi(a,n,s,e,Z4(r,u)|0,u)}function Ty(){var e=0,n=0;if(h[7800]|0||(xy(9936),Wt(44,9936,ge|0)|0,n=7800,t[n>>2]=1,t[n+4>>2]=0),!(sr(9936)|0)){e=9936,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));xy(9936)}return 9936}function J4(e){return e=e|0,e|0}function Z4(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=Ty()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(Cy(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(Jg(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function Cy(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function Jg(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=Zg(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,$g(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,Cy(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,e_(e,s),t_(s),y=w;return}}function Zg(e){return e=e|0,536870911}function $g(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function e_(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function t_(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function xy(e){e=e|0,r_(e)}function n_(e){e=e|0,$4(e+24|0)}function $4(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function r_(e){e=e|0;var n=0;n=yr()|0,jn(e,1,23,n,So()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function eE(e,n){e=e|0,n=n|0,c(t[(tE(e)|0)>>2]|0,n)}function tE(e){return e=e|0,(t[(Ty()|0)+24>>2]|0)+(e<<3)|0}function c(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,tr(u,n),n=Qs(u,n)|0,P1[e&127](n),y=r}function d(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=D()|0,e=C(r)|0,wi(a,n,s,e,O(r,u)|0,u)}function D(){var e=0,n=0;if(h[7808]|0||(ht(9972),Wt(45,9972,ge|0)|0,n=7808,t[n>>2]=1,t[n+4>>2]=0),!(sr(9972)|0)){e=9972,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));ht(9972)}return 9972}function C(e){return e=e|0,e|0}function O(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=D()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(z(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(G(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function z(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function G(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=ne(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,se(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,z(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,Ue(e,s),Xe(s),y=w;return}}function ne(e){return e=e|0,536870911}function se(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function Ue(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Xe(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function ht(e){e=e|0,Ht(e)}function Lt(e){e=e|0,Gt(e+24|0)}function Gt(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function Ht(e){e=e|0;var n=0;n=yr()|0,jn(e,1,9,n,yn()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function yn(){return 1348}function kr(e,n){return e=e|0,n=n|0,Oi(t[(ii(e)|0)>>2]|0,n)|0}function ii(e){return e=e|0,(t[(D()|0)+24>>2]|0)+(e<<3)|0}function Oi(e,n){e=e|0,n=n|0;var r=0,u=0;return r=y,y=y+16|0,u=r,L0(u,n),n=$i(u,n)|0,n=Sd(Qp[e&31](n)|0)|0,y=r,n|0}function L0(e,n){e=e|0,n=n|0}function $i(e,n){return e=e|0,n=n|0,lt(n)|0}function lt(e){return e=e|0,e|0}function Mt(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=$e()|0,e=jt(r)|0,wi(a,n,s,e,Fn(r,u)|0,u)}function $e(){var e=0,n=0;if(h[7816]|0||(Yr(10008),Wt(46,10008,ge|0)|0,n=7816,t[n>>2]=1,t[n+4>>2]=0),!(sr(10008)|0)){e=10008,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Yr(10008)}return 10008}function jt(e){return e=e|0,e|0}function Fn(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=$e()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(vn(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(Vi(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function vn(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function Vi(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=ci(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,Yu(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,vn(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,hr(e,s),pu(s),y=w;return}}function ci(e){return e=e|0,536870911}function Yu(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function hr(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function pu(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function Yr(e){e=e|0,W0(e)}function Cu(e){e=e|0,D0(e+24|0)}function D0(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function W0(e){e=e|0;var n=0;n=yr()|0,jn(e,1,15,n,ip()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function Ms(e){return e=e|0,gl(t[(Ku(e)|0)>>2]|0)|0}function Ku(e){return e=e|0,(t[($e()|0)+24>>2]|0)+(e<<3)|0}function gl(e){return e=e|0,Sd(E_[e&7]()|0)|0}function rf(){var e=0;return h[7832]|0||(u_(10052),Wt(25,10052,ge|0)|0,e=7832,t[e>>2]=1,t[e+4>>2]=0),10052}function Vo(e,n){e=e|0,n=n|0,t[e>>2]=ks()|0,t[e+4>>2]=Jd()|0,t[e+12>>2]=n,t[e+8>>2]=Vf()|0,t[e+32>>2]=2}function ks(){return 11709}function Jd(){return 1188}function Vf(){return L1()|0}function Lc(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(Go(r),Et(r)):n|0&&(Ds(n),Et(n))}function Hl(e,n){return e=e|0,n=n|0,n&e|0}function Go(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function L1(){var e=0;return h[7824]|0||(t[2511]=i_()|0,t[2512]=0,e=7824,t[e>>2]=1,t[e+4>>2]=0),10044}function i_(){return 0}function u_(e){e=e|0,Xa(e)}function nE(e){e=e|0;var n=0,r=0,u=0,s=0,a=0;n=y,y=y+32|0,r=n+24|0,a=n+16|0,s=n+8|0,u=n,o_(e,4827),rE(e,4834,3)|0,iE(e,3682,47)|0,t[a>>2]=9,t[a+4>>2]=0,t[r>>2]=t[a>>2],t[r+4>>2]=t[a+4>>2],Ry(e,4841,r)|0,t[s>>2]=1,t[s+4>>2]=0,t[r>>2]=t[s>>2],t[r+4>>2]=t[s+4>>2],l_(e,4871,r)|0,t[u>>2]=10,t[u+4>>2]=0,t[r>>2]=t[u>>2],t[r+4>>2]=t[u+4>>2],uE(e,4891,r)|0,y=n}function o_(e,n){e=e|0,n=n|0;var r=0;r=PR()|0,t[e>>2]=r,IR(r,n),Zd(t[e>>2]|0)}function rE(e,n,r){return e=e|0,n=n|0,r=r|0,_R(e,Fr(n)|0,r,0),e|0}function iE(e,n,r){return e=e|0,n=n|0,r=r|0,iR(e,Fr(n)|0,r,0),e|0}function Ry(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],U9(e,n,s),y=u,e|0}function l_(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],E9(e,n,s),y=u,e|0}function uE(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=t[r+4>>2]|0,t[a>>2]=t[r>>2],t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],oE(e,n,s),y=u,e|0}function oE(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],lE(e,r,s,1),y=u}function lE(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=sE()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=o9(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,l9(a,u)|0,u),y=s}function sE(){var e=0,n=0;if(h[7840]|0||(hw(10100),Wt(48,10100,ge|0)|0,n=7840,t[n>>2]=1,t[n+4>>2]=0),!(sr(10100)|0)){e=10100,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));hw(10100)}return 10100}function o9(e){return e=e|0,0}function l9(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=sE()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],pw(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(s9(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function pw(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function s9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=a9(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,f9(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],pw(a,u,r),t[T>>2]=(t[T>>2]|0)+12,c9(e,w),d9(w),y=L;return}}function a9(e){return e=e|0,357913941}function f9(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function c9(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function d9(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function hw(e){e=e|0,v9(e)}function p9(e){e=e|0,h9(e+24|0)}function h9(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function v9(e){e=e|0;var n=0;n=yr()|0,jn(e,2,6,n,m9()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function m9(){return 1364}function y9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;return u=y,y=y+16|0,s=u+8|0,a=u,v=g9(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],r=_9(n,s,r)|0,y=u,r|0}function g9(e){return e=e|0,(t[(sE()|0)+24>>2]|0)+(e*12|0)|0}function _9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;return a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),xs(s,r),s=Gs(s,r)|0,s=bl(bE[u&15](e,s)|0)|0,y=a,s|0}function E9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],D9(e,r,s,0),y=u}function D9(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=aE()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=w9(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,S9(a,u)|0,u),y=s}function aE(){var e=0,n=0;if(h[7848]|0||(mw(10136),Wt(49,10136,ge|0)|0,n=7848,t[n>>2]=1,t[n+4>>2]=0),!(sr(10136)|0)){e=10136,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));mw(10136)}return 10136}function w9(e){return e=e|0,0}function S9(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=aE()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],vw(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(T9(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function vw(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function T9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=C9(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,x9(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],vw(a,u,r),t[T>>2]=(t[T>>2]|0)+12,R9(e,w),A9(w),y=L;return}}function C9(e){return e=e|0,357913941}function x9(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function R9(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function A9(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function mw(e){e=e|0,k9(e)}function O9(e){e=e|0,M9(e+24|0)}function M9(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function k9(e){e=e|0;var n=0;n=yr()|0,jn(e,2,9,n,N9()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function N9(){return 1372}function L9(e,n,r){e=e|0,n=n|0,r=+r;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,s=u+8|0,a=u,v=F9(e)|0,e=t[v+4>>2]|0,t[a>>2]=t[v>>2],t[a+4>>2]=e,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],P9(n,s,r),y=u}function F9(e){return e=e|0,(t[(aE()|0)+24>>2]|0)+(e*12|0)|0}function P9(e,n,r){e=e|0,n=n|0,r=+r;var u=0,s=0,a=0,v=Ct;a=y,y=y+16|0,s=a,u=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(u=t[(t[e>>2]|0)+u>>2]|0),I9(s,r),v=S(b9(s,r)),M8[u&1](e,v),y=a}function I9(e,n){e=e|0,n=+n}function b9(e,n){return e=e|0,n=+n,S(B9(n))}function B9(e){return e=+e,S(e)}function U9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,s=u+8|0,a=u,w=t[r>>2]|0,v=t[r+4>>2]|0,r=Fr(n)|0,t[a>>2]=w,t[a+4>>2]=v,t[s>>2]=t[a>>2],t[s+4>>2]=t[a+4>>2],j9(e,r,s,0),y=u}function j9(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0,T=0,L=0,M=0;s=y,y=y+32|0,a=s+16|0,M=s+8|0,w=s,L=t[r>>2]|0,T=t[r+4>>2]|0,v=t[e>>2]|0,e=fE()|0,t[M>>2]=L,t[M+4>>2]=T,t[a>>2]=t[M>>2],t[a+4>>2]=t[M+4>>2],r=z9(a)|0,t[w>>2]=L,t[w+4>>2]=T,t[a>>2]=t[w>>2],t[a+4>>2]=t[w+4>>2],wi(v,n,e,r,H9(a,u)|0,u),y=s}function fE(){var e=0,n=0;if(h[7856]|0||(gw(10172),Wt(50,10172,ge|0)|0,n=7856,t[n>>2]=1,t[n+4>>2]=0),!(sr(10172)|0)){e=10172,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));gw(10172)}return 10172}function z9(e){return e=e|0,0}function H9(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0;return M=y,y=y+32|0,s=M+24|0,v=M+16|0,w=M,T=M+8|0,a=t[e>>2]|0,u=t[e+4>>2]|0,t[w>>2]=a,t[w+4>>2]=u,b=fE()|0,L=b+24|0,e=hn(n,4)|0,t[T>>2]=e,n=b+28|0,r=t[n>>2]|0,r>>>0<(t[b+32>>2]|0)>>>0?(t[v>>2]=a,t[v+4>>2]=u,t[s>>2]=t[v>>2],t[s+4>>2]=t[v+4>>2],yw(r,s,e),e=(t[n>>2]|0)+12|0,t[n>>2]=e):(q9(L,w,T),e=t[n>>2]|0),y=M,((e-(t[L>>2]|0)|0)/12|0)+-1|0}function yw(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=t[n+4>>2]|0,t[e>>2]=t[n>>2],t[e+4>>2]=u,t[e+8>>2]=r}function q9(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;if(L=y,y=y+48|0,u=L+32|0,v=L+24|0,w=L,T=e+4|0,s=(((t[T>>2]|0)-(t[e>>2]|0)|0)/12|0)+1|0,a=W9(e)|0,a>>>0>>0)di(e);else{M=t[e>>2]|0,X=((t[e+8>>2]|0)-M|0)/12|0,b=X<<1,V9(w,X>>>0>>1>>>0?b>>>0>>0?s:b:a,((t[T>>2]|0)-M|0)/12|0,e+8|0),T=w+8|0,a=t[T>>2]|0,s=t[n+4>>2]|0,r=t[r>>2]|0,t[v>>2]=t[n>>2],t[v+4>>2]=s,t[u>>2]=t[v>>2],t[u+4>>2]=t[v+4>>2],yw(a,u,r),t[T>>2]=(t[T>>2]|0)+12,G9(e,w),Y9(w),y=L;return}}function W9(e){return e=e|0,357913941}function V9(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>357913941)$n();else{s=pn(n*12|0)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r*12|0)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n*12|0)}function G9(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(((s|0)/-12|0)*12|0)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Y9(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~(((u+-12-n|0)>>>0)/12|0)*12|0)),e=t[e>>2]|0,e|0&&Et(e)}function gw(e){e=e|0,Q9(e)}function K9(e){e=e|0,X9(e+24|0)}function X9(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~(((n+-12-u|0)>>>0)/12|0)*12|0)),Et(r))}function Q9(e){e=e|0;var n=0;n=yr()|0,jn(e,2,3,n,J9()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function J9(){return 1380}function Z9(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;s=y,y=y+16|0,a=s+8|0,v=s,w=$9(e)|0,e=t[w+4>>2]|0,t[v>>2]=t[w>>2],t[v+4>>2]=e,t[a>>2]=t[v>>2],t[a+4>>2]=t[v+4>>2],eR(n,a,r,u),y=s}function $9(e){return e=e|0,(t[(fE()|0)+24>>2]|0)+(e*12|0)|0}function eR(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;w=y,y=y+16|0,a=w+1|0,v=w,s=t[n>>2]|0,n=t[n+4>>2]|0,e=e+(n>>1)|0,n&1&&(s=t[(t[e>>2]|0)+s>>2]|0),xs(a,r),a=Gs(a,r)|0,tR(v,u),v=nR(v,u)|0,Fy[s&15](e,a,v),y=w}function tR(e,n){e=e|0,n=n|0}function nR(e,n){return e=e|0,n=n|0,rR(n)|0}function rR(e){return e=e|0,(e|0)!=0|0}function iR(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=cE()|0,e=uR(r)|0,wi(a,n,s,e,oR(r,u)|0,u)}function cE(){var e=0,n=0;if(h[7864]|0||(Ew(10208),Wt(51,10208,ge|0)|0,n=7864,t[n>>2]=1,t[n+4>>2]=0),!(sr(10208)|0)){e=10208,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Ew(10208)}return 10208}function uR(e){return e=e|0,e|0}function oR(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=cE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(_w(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(lR(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function _w(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function lR(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=sR(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,aR(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,_w(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,fR(e,s),cR(s),y=w;return}}function sR(e){return e=e|0,536870911}function aR(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function fR(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function cR(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function Ew(e){e=e|0,hR(e)}function dR(e){e=e|0,pR(e+24|0)}function pR(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function hR(e){e=e|0;var n=0;n=yr()|0,jn(e,1,24,n,vR()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function vR(){return 1392}function mR(e,n){e=e|0,n=n|0,gR(t[(yR(e)|0)>>2]|0,n)}function yR(e){return e=e|0,(t[(cE()|0)+24>>2]|0)+(e<<3)|0}function gR(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,L0(u,n),n=$i(u,n)|0,P1[e&127](n),y=r}function _R(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=dE()|0,e=ER(r)|0,wi(a,n,s,e,DR(r,u)|0,u)}function dE(){var e=0,n=0;if(h[7872]|0||(ww(10244),Wt(52,10244,ge|0)|0,n=7872,t[n>>2]=1,t[n+4>>2]=0),!(sr(10244)|0)){e=10244,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));ww(10244)}return 10244}function ER(e){return e=e|0,e|0}function DR(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=dE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(Dw(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(wR(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function Dw(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function wR(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=SR(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,TR(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,Dw(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,CR(e,s),xR(s),y=w;return}}function SR(e){return e=e|0,536870911}function TR(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function CR(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function xR(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function ww(e){e=e|0,OR(e)}function RR(e){e=e|0,AR(e+24|0)}function AR(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function OR(e){e=e|0;var n=0;n=yr()|0,jn(e,1,16,n,MR()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function MR(){return 1400}function kR(e){return e=e|0,LR(t[(NR(e)|0)>>2]|0)|0}function NR(e){return e=e|0,(t[(dE()|0)+24>>2]|0)+(e<<3)|0}function LR(e){return e=e|0,FR(E_[e&7]()|0)|0}function FR(e){return e=e|0,e|0}function PR(){var e=0;return h[7880]|0||(HR(10280),Wt(25,10280,ge|0)|0,e=7880,t[e>>2]=1,t[e+4>>2]=0),10280}function IR(e,n){e=e|0,n=n|0,t[e>>2]=bR()|0,t[e+4>>2]=BR()|0,t[e+12>>2]=n,t[e+8>>2]=UR()|0,t[e+32>>2]=4}function bR(){return 11711}function BR(){return 1356}function UR(){return L1()|0}function jR(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(zR(r),Et(r)):n|0&&(ro(n),Et(n))}function zR(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function HR(e){e=e|0,Xa(e)}function qR(e){e=e|0,WR(e,4920),VR(e)|0,GR(e)|0}function WR(e,n){e=e|0,n=n|0;var r=0;r=Hd()|0,t[e>>2]=r,pA(r,n),Zd(t[e>>2]|0)}function VR(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,rA()|0),e|0}function GR(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,YR()|0),e|0}function YR(){var e=0;return h[7888]|0||(Sw(10328),Wt(53,10328,ge|0)|0,e=7888,t[e>>2]=1,t[e+4>>2]=0),sr(10328)|0||Sw(10328),10328}function Wp(e,n){e=e|0,n=n|0,wi(e,0,n,0,0,0)}function Sw(e){e=e|0,QR(e),Vp(e,10)}function KR(e){e=e|0,XR(e+24|0)}function XR(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function QR(e){e=e|0;var n=0;n=yr()|0,jn(e,5,1,n,eA()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function JR(e,n,r){e=e|0,n=n|0,r=+r,ZR(e,n,r)}function Vp(e,n){e=e|0,n=n|0,t[e+20>>2]=n}function ZR(e,n,r){e=e|0,n=n|0,r=+r;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+16|0,a=u+8|0,w=u+13|0,s=u,v=u+12|0,xs(w,n),t[a>>2]=Gs(w,n)|0,Pl(v,r),j[s>>3]=+us(v,r),$R(e,a,s),y=u}function $R(e,n,r){e=e|0,n=n|0,r=r|0,B(e+8|0,t[n>>2]|0,+j[r>>3]),h[e+24>>0]=1}function eA(){return 1404}function tA(e,n){return e=e|0,n=+n,nA(e,n)|0}function nA(e,n){e=e|0,n=+n;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return u=y,y=y+16|0,a=u+4|0,v=u+8|0,w=u,s=Oa(8)|0,r=s,T=pn(16)|0,xs(a,e),e=Gs(a,e)|0,Pl(v,n),B(T,e,+us(v,n)),v=r+4|0,t[v>>2]=T,e=pn(8)|0,v=t[v>>2]|0,t[w>>2]=0,t[a>>2]=t[w>>2],Bf(e,v,a),t[s>>2]=e,y=u,r|0}function rA(){var e=0;return h[7896]|0||(Tw(10364),Wt(54,10364,ge|0)|0,e=7896,t[e>>2]=1,t[e+4>>2]=0),sr(10364)|0||Tw(10364),10364}function Tw(e){e=e|0,oA(e),Vp(e,55)}function iA(e){e=e|0,uA(e+24|0)}function uA(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function oA(e){e=e|0;var n=0;n=yr()|0,jn(e,5,4,n,fA()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function lA(e){e=e|0,sA(e)}function sA(e){e=e|0,aA(e)}function aA(e){e=e|0,Cw(e+8|0),h[e+24>>0]=1}function Cw(e){e=e|0,t[e>>2]=0,j[e+8>>3]=0}function fA(){return 1424}function cA(){return dA()|0}function dA(){var e=0,n=0,r=0,u=0,s=0,a=0,v=0;return n=y,y=y+16|0,s=n+4|0,v=n,r=Oa(8)|0,e=r,u=pn(16)|0,Cw(u),a=e+4|0,t[a>>2]=u,u=pn(8)|0,a=t[a>>2]|0,t[v>>2]=0,t[s>>2]=t[v>>2],Bf(u,a,s),t[r>>2]=u,y=n,e|0}function pA(e,n){e=e|0,n=n|0,t[e>>2]=hA()|0,t[e+4>>2]=vA()|0,t[e+12>>2]=n,t[e+8>>2]=mA()|0,t[e+32>>2]=5}function hA(){return 11710}function vA(){return 1416}function mA(){return s_()|0}function yA(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(gA(r),Et(r)):n|0&&Et(n)}function gA(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function s_(){var e=0;return h[7904]|0||(t[2600]=_A()|0,t[2601]=0,e=7904,t[e>>2]=1,t[e+4>>2]=0),10400}function _A(){return t[357]|0}function EA(e){e=e|0,DA(e,4926),wA(e)|0}function DA(e,n){e=e|0,n=n|0;var r=0;r=Ka()|0,t[e>>2]=r,LA(r,n),Zd(t[e>>2]|0)}function wA(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,SA()|0),e|0}function SA(){var e=0;return h[7912]|0||(xw(10412),Wt(56,10412,ge|0)|0,e=7912,t[e>>2]=1,t[e+4>>2]=0),sr(10412)|0||xw(10412),10412}function xw(e){e=e|0,xA(e),Vp(e,57)}function TA(e){e=e|0,CA(e+24|0)}function CA(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function xA(e){e=e|0;var n=0;n=yr()|0,jn(e,5,5,n,MA()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function RA(e){e=e|0,AA(e)}function AA(e){e=e|0,OA(e)}function OA(e){e=e|0;var n=0,r=0;n=e+8|0,r=n+48|0;do t[n>>2]=0,n=n+4|0;while((n|0)<(r|0));h[e+56>>0]=1}function MA(){return 1432}function kA(){return NA()|0}function NA(){var e=0,n=0,r=0,u=0,s=0,a=0,v=0,w=0;v=y,y=y+16|0,e=v+4|0,n=v,r=Oa(8)|0,u=r,s=pn(48)|0,a=s,w=a+48|0;do t[a>>2]=0,a=a+4|0;while((a|0)<(w|0));return a=u+4|0,t[a>>2]=s,w=pn(8)|0,a=t[a>>2]|0,t[n>>2]=0,t[e>>2]=t[n>>2],Th(w,a,e),t[r>>2]=w,y=v,u|0}function LA(e,n){e=e|0,n=n|0,t[e>>2]=FA()|0,t[e+4>>2]=PA()|0,t[e+12>>2]=n,t[e+8>>2]=IA()|0,t[e+32>>2]=6}function FA(){return 11704}function PA(){return 1436}function IA(){return s_()|0}function bA(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(BA(r),Et(r)):n|0&&Et(n)}function BA(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function UA(e){e=e|0,jA(e,4933),zA(e)|0,HA(e)|0}function jA(e,n){e=e|0,n=n|0;var r=0;r=d7()|0,t[e>>2]=r,p7(r,n),Zd(t[e>>2]|0)}function zA(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,n7()|0),e|0}function HA(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,qA()|0),e|0}function qA(){var e=0;return h[7920]|0||(Rw(10452),Wt(58,10452,ge|0)|0,e=7920,t[e>>2]=1,t[e+4>>2]=0),sr(10452)|0||Rw(10452),10452}function Rw(e){e=e|0,GA(e),Vp(e,1)}function WA(e){e=e|0,VA(e+24|0)}function VA(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function GA(e){e=e|0;var n=0;n=yr()|0,jn(e,5,1,n,QA()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function YA(e,n,r){e=e|0,n=+n,r=+r,KA(e,n,r)}function KA(e,n,r){e=e|0,n=+n,r=+r;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+32|0,a=u+8|0,w=u+17|0,s=u,v=u+16|0,Pl(w,n),j[a>>3]=+us(w,n),Pl(v,r),j[s>>3]=+us(v,r),XA(e,a,s),y=u}function XA(e,n,r){e=e|0,n=n|0,r=r|0,Aw(e+8|0,+j[n>>3],+j[r>>3]),h[e+24>>0]=1}function Aw(e,n,r){e=e|0,n=+n,r=+r,j[e>>3]=n,j[e+8>>3]=r}function QA(){return 1472}function JA(e,n){return e=+e,n=+n,ZA(e,n)|0}function ZA(e,n){e=+e,n=+n;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return u=y,y=y+16|0,v=u+4|0,w=u+8|0,T=u,s=Oa(8)|0,r=s,a=pn(16)|0,Pl(v,e),e=+us(v,e),Pl(w,n),Aw(a,e,+us(w,n)),w=r+4|0,t[w>>2]=a,a=pn(8)|0,w=t[w>>2]|0,t[T>>2]=0,t[v>>2]=t[T>>2],Ow(a,w,v),t[s>>2]=a,y=u,r|0}function Ow(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,r=pn(16)|0,t[r+4>>2]=0,t[r+8>>2]=0,t[r>>2]=1452,t[r+12>>2]=n,t[e+4>>2]=r}function $A(e){e=e|0,Uv(e),Et(e)}function e7(e){e=e|0,e=t[e+12>>2]|0,e|0&&Et(e)}function t7(e){e=e|0,Et(e)}function n7(){var e=0;return h[7928]|0||(Mw(10488),Wt(59,10488,ge|0)|0,e=7928,t[e>>2]=1,t[e+4>>2]=0),sr(10488)|0||Mw(10488),10488}function Mw(e){e=e|0,u7(e),Vp(e,60)}function r7(e){e=e|0,i7(e+24|0)}function i7(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function u7(e){e=e|0;var n=0;n=yr()|0,jn(e,5,6,n,a7()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function o7(e){e=e|0,l7(e)}function l7(e){e=e|0,s7(e)}function s7(e){e=e|0,kw(e+8|0),h[e+24>>0]=1}function kw(e){e=e|0,t[e>>2]=0,t[e+4>>2]=0,t[e+8>>2]=0,t[e+12>>2]=0}function a7(){return 1492}function f7(){return c7()|0}function c7(){var e=0,n=0,r=0,u=0,s=0,a=0,v=0;return n=y,y=y+16|0,s=n+4|0,v=n,r=Oa(8)|0,e=r,u=pn(16)|0,kw(u),a=e+4|0,t[a>>2]=u,u=pn(8)|0,a=t[a>>2]|0,t[v>>2]=0,t[s>>2]=t[v>>2],Ow(u,a,s),t[r>>2]=u,y=n,e|0}function d7(){var e=0;return h[7936]|0||(_7(10524),Wt(25,10524,ge|0)|0,e=7936,t[e>>2]=1,t[e+4>>2]=0),10524}function p7(e,n){e=e|0,n=n|0,t[e>>2]=h7()|0,t[e+4>>2]=v7()|0,t[e+12>>2]=n,t[e+8>>2]=m7()|0,t[e+32>>2]=7}function h7(){return 11700}function v7(){return 1484}function m7(){return s_()|0}function y7(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(g7(r),Et(r)):n|0&&Et(n)}function g7(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function _7(e){e=e|0,Xa(e)}function E7(e,n,r){e=e|0,n=n|0,r=r|0,e=Fr(n)|0,n=D7(r)|0,r=w7(r,0)|0,Z7(e,n,r,pE()|0,0)}function D7(e){return e=e|0,e|0}function w7(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=pE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(Lw(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(O7(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function pE(){var e=0,n=0;if(h[7944]|0||(Nw(10568),Wt(61,10568,ge|0)|0,n=7944,t[n>>2]=1,t[n+4>>2]=0),!(sr(10568)|0)){e=10568,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));Nw(10568)}return 10568}function Nw(e){e=e|0,C7(e)}function S7(e){e=e|0,T7(e+24|0)}function T7(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function C7(e){e=e|0;var n=0;n=yr()|0,jn(e,1,17,n,ev()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function x7(e){return e=e|0,A7(t[(R7(e)|0)>>2]|0)|0}function R7(e){return e=e|0,(t[(pE()|0)+24>>2]|0)+(e<<3)|0}function A7(e){return e=e|0,H0(E_[e&7]()|0)|0}function Lw(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function O7(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=M7(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,k7(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,Lw(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,N7(e,s),L7(s),y=w;return}}function M7(e){return e=e|0,536870911}function k7(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function N7(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function L7(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function F7(){P7()}function P7(){I7(10604)}function I7(e){e=e|0,b7(e,4955)}function b7(e,n){e=e|0,n=n|0;var r=0;r=B7()|0,t[e>>2]=r,U7(r,n),Zd(t[e>>2]|0)}function B7(){var e=0;return h[7952]|0||(K7(10612),Wt(25,10612,ge|0)|0,e=7952,t[e>>2]=1,t[e+4>>2]=0),10612}function U7(e,n){e=e|0,n=n|0,t[e>>2]=q7()|0,t[e+4>>2]=W7()|0,t[e+12>>2]=n,t[e+8>>2]=V7()|0,t[e+32>>2]=8}function Zd(e){e=e|0;var n=0,r=0;n=y,y=y+16|0,r=n,Fv()|0,t[r>>2]=e,j7(10608,r),y=n}function Fv(){return h[11714]|0||(t[2652]=0,Wt(62,10608,ge|0)|0,h[11714]=1),10608}function j7(e,n){e=e|0,n=n|0;var r=0;r=pn(8)|0,t[r+4>>2]=t[n>>2],t[r>>2]=t[e>>2],t[e>>2]=r}function z7(e){e=e|0,H7(e)}function H7(e){e=e|0;var n=0,r=0;if(n=t[e>>2]|0,n|0)do r=n,n=t[n>>2]|0,Et(r);while((n|0)!=0);t[e>>2]=0}function q7(){return 11715}function W7(){return 1496}function V7(){return L1()|0}function G7(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(Y7(r),Et(r)):n|0&&Et(n)}function Y7(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function K7(e){e=e|0,Xa(e)}function X7(e,n){e=e|0,n=n|0;var r=0,u=0;Fv()|0,r=t[2652]|0;e:do if(r|0){for(;u=t[r+4>>2]|0,!(u|0&&(h8(hE(u)|0,e)|0)==0);)if(r=t[r>>2]|0,!r)break e;Q7(u,n)}while(0)}function hE(e){return e=e|0,t[e+12>>2]|0}function Q7(e,n){e=e|0,n=n|0;var r=0;e=e+36|0,r=t[e>>2]|0,r|0&&(fa(r),Et(r)),r=pn(4)|0,wf(r,n),t[e>>2]=r}function vE(){return h[11716]|0||(t[2664]=0,Wt(63,10656,ge|0)|0,h[11716]=1),10656}function Fw(){var e=0;return h[11717]|0?e=t[2665]|0:(J7(),t[2665]=1504,h[11717]=1,e=1504),e|0}function J7(){h[11740]|0||(h[11718]=hn(hn(8,0)|0,0)|0,h[11719]=hn(hn(0,0)|0,0)|0,h[11720]=hn(hn(0,16)|0,0)|0,h[11721]=hn(hn(8,0)|0,0)|0,h[11722]=hn(hn(0,0)|0,0)|0,h[11723]=hn(hn(8,0)|0,0)|0,h[11724]=hn(hn(0,0)|0,0)|0,h[11725]=hn(hn(8,0)|0,0)|0,h[11726]=hn(hn(0,0)|0,0)|0,h[11727]=hn(hn(8,0)|0,0)|0,h[11728]=hn(hn(0,0)|0,0)|0,h[11729]=hn(hn(0,0)|0,32)|0,h[11730]=hn(hn(0,0)|0,32)|0,h[11740]=1)}function Pw(){return 1572}function Z7(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0,L=0,M=0;a=y,y=y+32|0,M=a+16|0,L=a+12|0,T=a+8|0,w=a+4|0,v=a,t[M>>2]=e,t[L>>2]=n,t[T>>2]=r,t[w>>2]=u,t[v>>2]=s,vE()|0,$7(10656,M,L,T,w,v),y=a}function $7(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0;var v=0;v=pn(24)|0,yd(v+4|0,t[n>>2]|0,t[r>>2]|0,t[u>>2]|0,t[s>>2]|0,t[a>>2]|0),t[v>>2]=t[e>>2],t[e>>2]=v}function Iw(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0,Ze=0,Ye=0,ct=0;if(ct=y,y=y+32|0,Te=ct+20|0,ye=ct+8|0,Ze=ct+4|0,Ye=ct,n=t[n>>2]|0,n|0){Be=Te+4|0,T=Te+8|0,L=ye+4|0,M=ye+8|0,b=ye+8|0,X=Te+8|0;do{if(v=n+4|0,w=mE(v)|0,w|0){if(s=Ay(w)|0,t[Te>>2]=0,t[Be>>2]=0,t[T>>2]=0,u=(Oy(w)|0)+1|0,eO(Te,u),u|0)for(;u=u+-1|0,Gf(ye,t[s>>2]|0),a=t[Be>>2]|0,a>>>0<(t[X>>2]|0)>>>0?(t[a>>2]=t[ye>>2],t[Be>>2]=(t[Be>>2]|0)+4):yE(Te,ye),u;)s=s+4|0;u=My(w)|0,t[ye>>2]=0,t[L>>2]=0,t[M>>2]=0;e:do if(t[u>>2]|0)for(s=0,a=0;;){if((s|0)==(a|0)?tO(ye,u):(t[s>>2]=t[u>>2],t[L>>2]=(t[L>>2]|0)+4),u=u+4|0,!(t[u>>2]|0))break e;s=t[L>>2]|0,a=t[b>>2]|0}while(0);t[Ze>>2]=a_(v)|0,t[Ye>>2]=sr(w)|0,nO(r,e,Ze,Ye,Te,ye),gE(ye),F1(Te)}n=t[n>>2]|0}while((n|0)!=0)}y=ct}function mE(e){return e=e|0,t[e+12>>2]|0}function Ay(e){return e=e|0,t[e+12>>2]|0}function Oy(e){return e=e|0,t[e+16>>2]|0}function eO(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;s=y,y=y+32|0,r=s,u=t[e>>2]|0,(t[e+8>>2]|0)-u>>2>>>0>>0&&(Ww(r,n,(t[e+4>>2]|0)-u>>2,e+8|0),Vw(e,r),Gw(r)),y=s}function yE(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0;if(v=y,y=y+32|0,r=v,u=e+4|0,s=((t[u>>2]|0)-(t[e>>2]|0)>>2)+1|0,a=qw(e)|0,a>>>0>>0)di(e);else{w=t[e>>2]|0,L=(t[e+8>>2]|0)-w|0,T=L>>1,Ww(r,L>>2>>>0>>1>>>0?T>>>0>>0?s:T:a,(t[u>>2]|0)-w>>2,e+8|0),a=r+8|0,t[t[a>>2]>>2]=t[n>>2],t[a>>2]=(t[a>>2]|0)+4,Vw(e,r),Gw(r),y=v;return}}function My(e){return e=e|0,t[e+8>>2]|0}function tO(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0;if(v=y,y=y+32|0,r=v,u=e+4|0,s=((t[u>>2]|0)-(t[e>>2]|0)>>2)+1|0,a=Hw(e)|0,a>>>0>>0)di(e);else{w=t[e>>2]|0,L=(t[e+8>>2]|0)-w|0,T=L>>1,DO(r,L>>2>>>0>>1>>>0?T>>>0>>0?s:T:a,(t[u>>2]|0)-w>>2,e+8|0),a=r+8|0,t[t[a>>2]>>2]=t[n>>2],t[a>>2]=(t[a>>2]|0)+4,wO(e,r),SO(r),y=v;return}}function a_(e){return e=e|0,t[e>>2]|0}function nO(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,rO(e,n,r,u,s,a)}function gE(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-4-u|0)>>>2)<<2)),Et(r))}function F1(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-4-u|0)>>>2)<<2)),Et(r))}function rO(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0;var v=0,w=0,T=0,L=0,M=0,b=0;v=y,y=y+48|0,M=v+40|0,w=v+32|0,b=v+24|0,T=v+12|0,L=v,Ma(w),e=go(e)|0,t[b>>2]=t[n>>2],r=t[r>>2]|0,u=t[u>>2]|0,_E(T,s),iO(L,a),t[M>>2]=t[b>>2],uO(e,M,r,u,T,L),gE(L),F1(T),ka(w),y=v}function _E(e,n){e=e|0,n=n|0;var r=0,u=0;t[e>>2]=0,t[e+4>>2]=0,t[e+8>>2]=0,r=n+4|0,u=(t[r>>2]|0)-(t[n>>2]|0)>>2,u|0&&(_O(e,u),EO(e,t[n>>2]|0,t[r>>2]|0,u))}function iO(e,n){e=e|0,n=n|0;var r=0,u=0;t[e>>2]=0,t[e+4>>2]=0,t[e+8>>2]=0,r=n+4|0,u=(t[r>>2]|0)-(t[n>>2]|0)>>2,u|0&&(yO(e,u),gO(e,t[n>>2]|0,t[r>>2]|0,u))}function uO(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0;var v=0,w=0,T=0,L=0,M=0,b=0;v=y,y=y+32|0,M=v+28|0,b=v+24|0,w=v+12|0,T=v,L=_o(oO()|0)|0,t[b>>2]=t[n>>2],t[M>>2]=t[b>>2],n=Gp(M)|0,r=bw(r)|0,u=EE(u)|0,t[w>>2]=t[s>>2],M=s+4|0,t[w+4>>2]=t[M>>2],b=s+8|0,t[w+8>>2]=t[b>>2],t[b>>2]=0,t[M>>2]=0,t[s>>2]=0,s=DE(w)|0,t[T>>2]=t[a>>2],M=a+4|0,t[T+4>>2]=t[M>>2],b=a+8|0,t[T+8>>2]=t[b>>2],t[b>>2]=0,t[M>>2]=0,t[a>>2]=0,X0(0,L|0,e|0,n|0,r|0,u|0,s|0,lO(T)|0)|0,gE(T),F1(w),y=v}function oO(){var e=0;return h[7968]|0||(vO(10708),e=7968,t[e>>2]=1,t[e+4>>2]=0),10708}function Gp(e){return e=e|0,Uw(e)|0}function bw(e){return e=e|0,Bw(e)|0}function EE(e){return e=e|0,H0(e)|0}function DE(e){return e=e|0,aO(e)|0}function lO(e){return e=e|0,sO(e)|0}function sO(e){e=e|0;var n=0,r=0,u=0;if(u=(t[e+4>>2]|0)-(t[e>>2]|0)|0,r=u>>2,u=Oa(u+4|0)|0,t[u>>2]=r,r|0){n=0;do t[u+4+(n<<2)>>2]=Bw(t[(t[e>>2]|0)+(n<<2)>>2]|0)|0,n=n+1|0;while((n|0)!=(r|0))}return u|0}function Bw(e){return e=e|0,e|0}function aO(e){e=e|0;var n=0,r=0,u=0;if(u=(t[e+4>>2]|0)-(t[e>>2]|0)|0,r=u>>2,u=Oa(u+4|0)|0,t[u>>2]=r,r|0){n=0;do t[u+4+(n<<2)>>2]=Uw((t[e>>2]|0)+(n<<2)|0)|0,n=n+1|0;while((n|0)!=(r|0))}return u|0}function Uw(e){e=e|0;var n=0,r=0,u=0,s=0;return s=y,y=y+32|0,n=s+12|0,r=s,u=Pu(jw()|0)|0,u?(rs(n,u),Mf(r,n),VN(e,r),e=Cs(n)|0):e=fO(e)|0,y=s,e|0}function jw(){var e=0;return h[7960]|0||(hO(10664),Wt(25,10664,ge|0)|0,e=7960,t[e>>2]=1,t[e+4>>2]=0),10664}function fO(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0;return r=y,y=y+16|0,s=r+4|0,v=r,u=Oa(8)|0,n=u,w=pn(4)|0,t[w>>2]=t[e>>2],a=n+4|0,t[a>>2]=w,e=pn(8)|0,a=t[a>>2]|0,t[v>>2]=0,t[s>>2]=t[v>>2],zw(e,a,s),t[u>>2]=e,y=r,n|0}function zw(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,r=pn(16)|0,t[r+4>>2]=0,t[r+8>>2]=0,t[r>>2]=1656,t[r+12>>2]=n,t[e+4>>2]=r}function cO(e){e=e|0,Uv(e),Et(e)}function dO(e){e=e|0,e=t[e+12>>2]|0,e|0&&Et(e)}function pO(e){e=e|0,Et(e)}function hO(e){e=e|0,Xa(e)}function vO(e){e=e|0,ll(e,mO()|0,5)}function mO(){return 1676}function yO(e,n){e=e|0,n=n|0;var r=0;if((Hw(e)|0)>>>0>>0&&di(e),n>>>0>1073741823)$n();else{r=pn(n<<2)|0,t[e+4>>2]=r,t[e>>2]=r,t[e+8>>2]=r+(n<<2);return}}function gO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,u=e+4|0,e=r-n|0,(e|0)>0&&(gr(t[u>>2]|0,n|0,e|0)|0,t[u>>2]=(t[u>>2]|0)+(e>>>2<<2))}function Hw(e){return e=e|0,1073741823}function _O(e,n){e=e|0,n=n|0;var r=0;if((qw(e)|0)>>>0>>0&&di(e),n>>>0>1073741823)$n();else{r=pn(n<<2)|0,t[e+4>>2]=r,t[e>>2]=r,t[e+8>>2]=r+(n<<2);return}}function EO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,u=e+4|0,e=r-n|0,(e|0)>0&&(gr(t[u>>2]|0,n|0,e|0)|0,t[u>>2]=(t[u>>2]|0)+(e>>>2<<2))}function qw(e){return e=e|0,1073741823}function DO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>1073741823)$n();else{s=pn(n<<2)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<2)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<2)}function wO(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>2)<<2)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function SO(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-4-n|0)>>>2)<<2)),e=t[e>>2]|0,e|0&&Et(e)}function Ww(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>1073741823)$n();else{s=pn(n<<2)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<2)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<2)}function Vw(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>2)<<2)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Gw(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-4-n|0)>>>2)<<2)),e=t[e>>2]|0,e|0&&Et(e)}function TO(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0;if(ye=y,y=y+32|0,M=ye+20|0,b=ye+12|0,L=ye+16|0,X=ye+4|0,Be=ye,Te=ye+8|0,w=Fw()|0,a=t[w>>2]|0,v=t[a>>2]|0,v|0)for(T=t[w+8>>2]|0,w=t[w+4>>2]|0;Gf(M,v),CO(e,M,w,T),a=a+4|0,v=t[a>>2]|0,v;)T=T+1|0,w=w+1|0;if(a=Pw()|0,v=t[a>>2]|0,v|0)do Gf(M,v),t[b>>2]=t[a+4>>2],xO(n,M,b),a=a+8|0,v=t[a>>2]|0;while((v|0)!=0);if(a=t[(Fv()|0)>>2]|0,a|0)do n=t[a+4>>2]|0,Gf(M,t[(Pv(n)|0)>>2]|0),t[b>>2]=hE(n)|0,RO(r,M,b),a=t[a>>2]|0;while((a|0)!=0);if(Gf(L,0),a=vE()|0,t[M>>2]=t[L>>2],Iw(M,a,s),a=t[(Fv()|0)>>2]|0,a|0){e=M+4|0,n=M+8|0,r=M+8|0;do{if(T=t[a+4>>2]|0,Gf(b,t[(Pv(T)|0)>>2]|0),AO(X,Yw(T)|0),v=t[X>>2]|0,v|0){t[M>>2]=0,t[e>>2]=0,t[n>>2]=0;do Gf(Be,t[(Pv(t[v+4>>2]|0)|0)>>2]|0),w=t[e>>2]|0,w>>>0<(t[r>>2]|0)>>>0?(t[w>>2]=t[Be>>2],t[e>>2]=(t[e>>2]|0)+4):yE(M,Be),v=t[v>>2]|0;while((v|0)!=0);OO(u,b,M),F1(M)}t[Te>>2]=t[b>>2],L=Kw(T)|0,t[M>>2]=t[Te>>2],Iw(M,L,s),_d(X),a=t[a>>2]|0}while((a|0)!=0)}y=ye}function CO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,zO(e,n,r,u)}function xO(e,n,r){e=e|0,n=n|0,r=r|0,jO(e,n,r)}function Pv(e){return e=e|0,e|0}function RO(e,n,r){e=e|0,n=n|0,r=r|0,IO(e,n,r)}function Yw(e){return e=e|0,e+16|0}function AO(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;if(a=y,y=y+16|0,s=a+8|0,r=a,t[e>>2]=0,u=t[n>>2]|0,t[s>>2]=u,t[r>>2]=e,r=PO(r)|0,u|0){if(u=pn(12)|0,v=(Xw(s)|0)+4|0,e=t[v+4>>2]|0,n=u+4|0,t[n>>2]=t[v>>2],t[n+4>>2]=e,n=t[t[s>>2]>>2]|0,t[s>>2]=n,!n)e=u;else for(n=u;e=pn(12)|0,T=(Xw(s)|0)+4|0,w=t[T+4>>2]|0,v=e+4|0,t[v>>2]=t[T>>2],t[v+4>>2]=w,t[n>>2]=e,v=t[t[s>>2]>>2]|0,t[s>>2]=v,v;)n=e;t[e>>2]=t[r>>2],t[r>>2]=u}y=a}function OO(e,n,r){e=e|0,n=n|0,r=r|0,MO(e,n,r)}function Kw(e){return e=e|0,e+24|0}function MO(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+32|0,v=u+24|0,s=u+16|0,w=u+12|0,a=u,Ma(s),e=go(e)|0,t[w>>2]=t[n>>2],_E(a,r),t[v>>2]=t[w>>2],kO(e,v,a),F1(a),ka(s),y=u}function kO(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=y,y=y+32|0,v=u+16|0,w=u+12|0,s=u,a=_o(NO()|0)|0,t[w>>2]=t[n>>2],t[v>>2]=t[w>>2],n=Gp(v)|0,t[s>>2]=t[r>>2],v=r+4|0,t[s+4>>2]=t[v>>2],w=r+8|0,t[s+8>>2]=t[w>>2],t[w>>2]=0,t[v>>2]=0,t[r>>2]=0,P0(0,a|0,e|0,n|0,DE(s)|0)|0,F1(s),y=u}function NO(){var e=0;return h[7976]|0||(LO(10720),e=7976,t[e>>2]=1,t[e+4>>2]=0),10720}function LO(e){e=e|0,ll(e,FO()|0,2)}function FO(){return 1732}function PO(e){return e=e|0,t[e>>2]|0}function Xw(e){return e=e|0,t[e>>2]|0}function IO(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+32|0,a=u+16|0,s=u+8|0,v=u,Ma(s),e=go(e)|0,t[v>>2]=t[n>>2],r=t[r>>2]|0,t[a>>2]=t[v>>2],Qw(e,a,r),ka(s),y=u}function Qw(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+16|0,a=u+4|0,v=u,s=_o(bO()|0)|0,t[v>>2]=t[n>>2],t[a>>2]=t[v>>2],n=Gp(a)|0,P0(0,s|0,e|0,n|0,bw(r)|0)|0,y=u}function bO(){var e=0;return h[7984]|0||(BO(10732),e=7984,t[e>>2]=1,t[e+4>>2]=0),10732}function BO(e){e=e|0,ll(e,UO()|0,2)}function UO(){return 1744}function jO(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;u=y,y=y+32|0,a=u+16|0,s=u+8|0,v=u,Ma(s),e=go(e)|0,t[v>>2]=t[n>>2],r=t[r>>2]|0,t[a>>2]=t[v>>2],Qw(e,a,r),ka(s),y=u}function zO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;s=y,y=y+32|0,v=s+16|0,a=s+8|0,w=s,Ma(a),e=go(e)|0,t[w>>2]=t[n>>2],r=h[r>>0]|0,u=h[u>>0]|0,t[v>>2]=t[w>>2],HO(e,v,r,u),ka(a),y=s}function HO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;s=y,y=y+16|0,v=s+4|0,w=s,a=_o(qO()|0)|0,t[w>>2]=t[n>>2],t[v>>2]=t[w>>2],n=Gp(v)|0,r=Iv(r)|0,Hn(0,a|0,e|0,n|0,r|0,Iv(u)|0)|0,y=s}function qO(){var e=0;return h[7992]|0||(VO(10744),e=7992,t[e>>2]=1,t[e+4>>2]=0),10744}function Iv(e){return e=e|0,WO(e)|0}function WO(e){return e=e|0,e&255|0}function VO(e){e=e|0,ll(e,GO()|0,3)}function GO(){return 1756}function YO(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;switch(X=y,y=y+32|0,w=X+8|0,T=X+4|0,L=X+20|0,M=X,Sa(e,0),u=WN(n)|0,t[w>>2]=0,b=w+4|0,t[b>>2]=0,t[w+8>>2]=0,u<<24>>24){case 0:{h[L>>0]=0,KO(T,r,L),f_(e,T)|0,U0(T);break}case 8:{b=RE(n)|0,h[L>>0]=8,Gf(M,t[b+4>>2]|0),XO(T,r,L,M,b+8|0),f_(e,T)|0,U0(T);break}case 9:{if(a=RE(n)|0,n=t[a+4>>2]|0,n|0)for(v=w+8|0,s=a+12|0;n=n+-1|0,Gf(T,t[s>>2]|0),u=t[b>>2]|0,u>>>0<(t[v>>2]|0)>>>0?(t[u>>2]=t[T>>2],t[b>>2]=(t[b>>2]|0)+4):yE(w,T),n;)s=s+4|0;h[L>>0]=9,Gf(M,t[a+8>>2]|0),QO(T,r,L,M,w),f_(e,T)|0,U0(T);break}default:b=RE(n)|0,h[L>>0]=u,Gf(M,t[b+4>>2]|0),JO(T,r,L,M),f_(e,T)|0,U0(T)}F1(w),y=X}function KO(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;u=y,y=y+16|0,s=u,Ma(s),n=go(n)|0,fM(e,n,h[r>>0]|0),ka(s),y=u}function f_(e,n){e=e|0,n=n|0;var r=0;return r=t[e>>2]|0,r|0&&qr(r|0),t[e>>2]=t[n>>2],t[n>>2]=0,e|0}function XO(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0;a=y,y=y+32|0,w=a+16|0,v=a+8|0,T=a,Ma(v),n=go(n)|0,r=h[r>>0]|0,t[T>>2]=t[u>>2],s=t[s>>2]|0,t[w>>2]=t[T>>2],oM(e,n,r,w,s),ka(v),y=a}function QO(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0,L=0;a=y,y=y+32|0,T=a+24|0,v=a+16|0,L=a+12|0,w=a,Ma(v),n=go(n)|0,r=h[r>>0]|0,t[L>>2]=t[u>>2],_E(w,s),t[T>>2]=t[L>>2],nM(e,n,r,T,w),F1(w),ka(v),y=a}function JO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;s=y,y=y+32|0,v=s+16|0,a=s+8|0,w=s,Ma(a),n=go(n)|0,r=h[r>>0]|0,t[w>>2]=t[u>>2],t[v>>2]=t[w>>2],ZO(e,n,r,v),ka(a),y=s}function ZO(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0,v=0,w=0;s=y,y=y+16|0,a=s+4|0,w=s,v=_o($O()|0)|0,r=Iv(r)|0,t[w>>2]=t[u>>2],t[a>>2]=t[w>>2],c_(e,P0(0,v|0,n|0,r|0,Gp(a)|0)|0),y=s}function $O(){var e=0;return h[8e3]|0||(eM(10756),e=8e3,t[e>>2]=1,t[e+4>>2]=0),10756}function c_(e,n){e=e|0,n=n|0,Sa(e,n)}function eM(e){e=e|0,ll(e,tM()|0,2)}function tM(){return 1772}function nM(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0,L=0;a=y,y=y+32|0,T=a+16|0,L=a+12|0,v=a,w=_o(rM()|0)|0,r=Iv(r)|0,t[L>>2]=t[u>>2],t[T>>2]=t[L>>2],u=Gp(T)|0,t[v>>2]=t[s>>2],T=s+4|0,t[v+4>>2]=t[T>>2],L=s+8|0,t[v+8>>2]=t[L>>2],t[L>>2]=0,t[T>>2]=0,t[s>>2]=0,c_(e,Hn(0,w|0,n|0,r|0,u|0,DE(v)|0)|0),F1(v),y=a}function rM(){var e=0;return h[8008]|0||(iM(10768),e=8008,t[e>>2]=1,t[e+4>>2]=0),10768}function iM(e){e=e|0,ll(e,uM()|0,3)}function uM(){return 1784}function oM(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0;a=y,y=y+16|0,w=a+4|0,T=a,v=_o(lM()|0)|0,r=Iv(r)|0,t[T>>2]=t[u>>2],t[w>>2]=t[T>>2],u=Gp(w)|0,c_(e,Hn(0,v|0,n|0,r|0,u|0,EE(s)|0)|0),y=a}function lM(){var e=0;return h[8016]|0||(sM(10780),e=8016,t[e>>2]=1,t[e+4>>2]=0),10780}function sM(e){e=e|0,ll(e,aM()|0,3)}function aM(){return 1800}function fM(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;u=_o(cM()|0)|0,c_(e,Ki(0,u|0,n|0,Iv(r)|0)|0)}function cM(){var e=0;return h[8024]|0||(dM(10792),e=8024,t[e>>2]=1,t[e+4>>2]=0),10792}function dM(e){e=e|0,ll(e,pM()|0,1)}function pM(){return 1816}function hM(){vM(),mM(),yM()}function vM(){t[2702]=T8(65536)|0}function mM(){bM(10856)}function yM(){gM(10816)}function gM(e){e=e|0,_M(e,5044),EM(e)|0}function _M(e,n){e=e|0,n=n|0;var r=0;r=jw()|0,t[e>>2]=r,kM(r,n),Zd(t[e>>2]|0)}function EM(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,DM()|0),e|0}function DM(){var e=0;return h[8032]|0||(Jw(10820),Wt(64,10820,ge|0)|0,e=8032,t[e>>2]=1,t[e+4>>2]=0),sr(10820)|0||Jw(10820),10820}function Jw(e){e=e|0,TM(e),Vp(e,25)}function wM(e){e=e|0,SM(e+24|0)}function SM(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function TM(e){e=e|0;var n=0;n=yr()|0,jn(e,5,18,n,AM()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function CM(e,n){e=e|0,n=n|0,xM(e,n)}function xM(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;r=y,y=y+16|0,u=r,s=r+4|0,Pf(s,n),t[u>>2]=If(s,n)|0,RM(e,u),y=r}function RM(e,n){e=e|0,n=n|0,Zw(e+4|0,t[n>>2]|0),h[e+8>>0]=1}function Zw(e,n){e=e|0,n=n|0,t[e>>2]=n}function AM(){return 1824}function OM(e){return e=e|0,MM(e)|0}function MM(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0;return r=y,y=y+16|0,s=r+4|0,v=r,u=Oa(8)|0,n=u,w=pn(4)|0,Pf(s,e),Zw(w,If(s,e)|0),a=n+4|0,t[a>>2]=w,e=pn(8)|0,a=t[a>>2]|0,t[v>>2]=0,t[s>>2]=t[v>>2],zw(e,a,s),t[u>>2]=e,y=r,n|0}function Oa(e){e=e|0;var n=0,r=0;return e=e+7&-8,e>>>0<=32768&&(n=t[2701]|0,e>>>0<=(65536-n|0)>>>0)?(r=(t[2702]|0)+n|0,t[2701]=n+e,e=r):(e=T8(e+8|0)|0,t[e>>2]=t[2703],t[2703]=e,e=e+8|0),e|0}function kM(e,n){e=e|0,n=n|0,t[e>>2]=NM()|0,t[e+4>>2]=LM()|0,t[e+12>>2]=n,t[e+8>>2]=FM()|0,t[e+32>>2]=9}function NM(){return 11744}function LM(){return 1832}function FM(){return s_()|0}function PM(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(IM(r),Et(r)):n|0&&Et(n)}function IM(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function bM(e){e=e|0,BM(e,5052),UM(e)|0,jM(e,5058,26)|0,zM(e,5069,1)|0,HM(e,5077,10)|0,qM(e,5087,19)|0,WM(e,5094,27)|0}function BM(e,n){e=e|0,n=n|0;var r=0;r=IN()|0,t[e>>2]=r,bN(r,n),Zd(t[e>>2]|0)}function UM(e){e=e|0;var n=0;return n=t[e>>2]|0,Wp(n,wN()|0),e|0}function jM(e,n,r){return e=e|0,n=n|0,r=r|0,iN(e,Fr(n)|0,r,0),e|0}function zM(e,n,r){return e=e|0,n=n|0,r=r|0,qk(e,Fr(n)|0,r,0),e|0}function HM(e,n,r){return e=e|0,n=n|0,r=r|0,Dk(e,Fr(n)|0,r,0),e|0}function qM(e,n,r){return e=e|0,n=n|0,r=r|0,ok(e,Fr(n)|0,r,0),e|0}function $w(e,n){e=e|0,n=n|0;var r=0,u=0;e:for(;;){for(r=t[2703]|0;;){if((r|0)==(n|0))break e;if(u=t[r>>2]|0,t[2703]=u,!r)r=u;else break}Et(r)}t[2701]=e}function WM(e,n,r){return e=e|0,n=n|0,r=r|0,VM(e,Fr(n)|0,r,0),e|0}function VM(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=wE()|0,e=GM(r)|0,wi(a,n,s,e,YM(r,u)|0,u)}function wE(){var e=0,n=0;if(h[8040]|0||(t8(10860),Wt(65,10860,ge|0)|0,n=8040,t[n>>2]=1,t[n+4>>2]=0),!(sr(10860)|0)){e=10860,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));t8(10860)}return 10860}function GM(e){return e=e|0,e|0}function YM(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=wE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(e8(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(KM(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function e8(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function KM(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=XM(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,QM(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,e8(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,JM(e,s),ZM(s),y=w;return}}function XM(e){return e=e|0,536870911}function QM(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function JM(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function ZM(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function t8(e){e=e|0,tk(e)}function $M(e){e=e|0,ek(e+24|0)}function ek(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function tk(e){e=e|0;var n=0;n=yr()|0,jn(e,1,11,n,nk()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function nk(){return 1840}function rk(e,n,r){e=e|0,n=n|0,r=r|0,uk(t[(ik(e)|0)>>2]|0,n,r)}function ik(e){return e=e|0,(t[(wE()|0)+24>>2]|0)+(e<<3)|0}function uk(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;u=y,y=y+16|0,a=u+1|0,s=u,Pf(a,n),n=If(a,n)|0,Pf(s,r),r=If(s,r)|0,I1[e&31](n,r),y=u}function ok(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=SE()|0,e=lk(r)|0,wi(a,n,s,e,sk(r,u)|0,u)}function SE(){var e=0,n=0;if(h[8048]|0||(r8(10896),Wt(66,10896,ge|0)|0,n=8048,t[n>>2]=1,t[n+4>>2]=0),!(sr(10896)|0)){e=10896,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));r8(10896)}return 10896}function lk(e){return e=e|0,e|0}function sk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=SE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(n8(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(ak(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function n8(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function ak(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=fk(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,ck(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,n8(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,dk(e,s),pk(s),y=w;return}}function fk(e){return e=e|0,536870911}function ck(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function dk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function pk(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function r8(e){e=e|0,mk(e)}function hk(e){e=e|0,vk(e+24|0)}function vk(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function mk(e){e=e|0;var n=0;n=yr()|0,jn(e,1,11,n,yk()|0,1),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function yk(){return 1852}function gk(e,n){return e=e|0,n=n|0,Ek(t[(_k(e)|0)>>2]|0,n)|0}function _k(e){return e=e|0,(t[(SE()|0)+24>>2]|0)+(e<<3)|0}function Ek(e,n){e=e|0,n=n|0;var r=0,u=0;return r=y,y=y+16|0,u=r,Pf(u,n),n=If(u,n)|0,n=H0(Qp[e&31](n)|0)|0,y=r,n|0}function Dk(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=TE()|0,e=wk(r)|0,wi(a,n,s,e,Sk(r,u)|0,u)}function TE(){var e=0,n=0;if(h[8056]|0||(u8(10932),Wt(67,10932,ge|0)|0,n=8056,t[n>>2]=1,t[n+4>>2]=0),!(sr(10932)|0)){e=10932,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));u8(10932)}return 10932}function wk(e){return e=e|0,e|0}function Sk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=TE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(i8(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(Tk(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function i8(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function Tk(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=Ck(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,xk(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,i8(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,Rk(e,s),Ak(s),y=w;return}}function Ck(e){return e=e|0,536870911}function xk(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function Rk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Ak(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function u8(e){e=e|0,kk(e)}function Ok(e){e=e|0,Mk(e+24|0)}function Mk(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function kk(e){e=e|0;var n=0;n=yr()|0,jn(e,1,7,n,Nk()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function Nk(){return 1860}function Lk(e,n,r){return e=e|0,n=n|0,r=r|0,Pk(t[(Fk(e)|0)>>2]|0,n,r)|0}function Fk(e){return e=e|0,(t[(TE()|0)+24>>2]|0)+(e<<3)|0}function Pk(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0;return u=y,y=y+32|0,v=u+12|0,a=u+8|0,w=u,T=u+16|0,s=u+4|0,Ik(T,n),bk(w,T,n),Ys(s,r),r=Ks(s,r)|0,t[v>>2]=t[w>>2],Fy[e&15](a,v,r),r=Bk(a)|0,U0(a),Xs(s),y=u,r|0}function Ik(e,n){e=e|0,n=n|0}function bk(e,n,r){e=e|0,n=n|0,r=r|0,Uk(e,r)}function Bk(e){return e=e|0,go(e)|0}function Uk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0;s=y,y=y+16|0,r=s,u=n,u&1?(jk(r,0),eu(u|0,r|0)|0,zk(e,r),Hk(r)):t[e>>2]=t[n>>2],y=s}function jk(e,n){e=e|0,n=n|0,fd(e,n),t[e+4>>2]=0,h[e+8>>0]=0}function zk(e,n){e=e|0,n=n|0,t[e>>2]=t[n+4>>2]}function Hk(e){e=e|0,h[e+8>>0]=0}function qk(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=CE()|0,e=Wk(r)|0,wi(a,n,s,e,Vk(r,u)|0,u)}function CE(){var e=0,n=0;if(h[8064]|0||(l8(10968),Wt(68,10968,ge|0)|0,n=8064,t[n>>2]=1,t[n+4>>2]=0),!(sr(10968)|0)){e=10968,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));l8(10968)}return 10968}function Wk(e){return e=e|0,e|0}function Vk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=CE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(o8(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(Gk(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function o8(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function Gk(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=Yk(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,Kk(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,o8(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,Xk(e,s),Qk(s),y=w;return}}function Yk(e){return e=e|0,536870911}function Kk(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function Xk(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function Qk(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function l8(e){e=e|0,$k(e)}function Jk(e){e=e|0,Zk(e+24|0)}function Zk(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function $k(e){e=e|0;var n=0;n=yr()|0,jn(e,1,1,n,eN()|0,5),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function eN(){return 1872}function tN(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,rN(t[(nN(e)|0)>>2]|0,n,r,u,s,a)}function nN(e){return e=e|0,(t[(CE()|0)+24>>2]|0)+(e<<3)|0}function rN(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0;var v=0,w=0,T=0,L=0,M=0,b=0;v=y,y=y+32|0,w=v+16|0,T=v+12|0,L=v+8|0,M=v+4|0,b=v,Ys(w,n),n=Ks(w,n)|0,Ys(T,r),r=Ks(T,r)|0,Ys(L,u),u=Ks(L,u)|0,Ys(M,s),s=Ks(M,s)|0,Ys(b,a),a=Ks(b,a)|0,O8[e&1](n,r,u,s,a),Xs(b),Xs(M),Xs(L),Xs(T),Xs(w),y=v}function iN(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;a=t[e>>2]|0,s=xE()|0,e=uN(r)|0,wi(a,n,s,e,oN(r,u)|0,u)}function xE(){var e=0,n=0;if(h[8072]|0||(a8(11004),Wt(69,11004,ge|0)|0,n=8072,t[n>>2]=1,t[n+4>>2]=0),!(sr(11004)|0)){e=11004,n=e+36|0;do t[e>>2]=0,e=e+4|0;while((e|0)<(n|0));a8(11004)}return 11004}function uN(e){return e=e|0,e|0}function oN(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0,w=0,T=0;return w=y,y=y+16|0,s=w,a=w+4|0,t[s>>2]=e,T=xE()|0,v=T+24|0,n=hn(n,4)|0,t[a>>2]=n,r=T+28|0,u=t[r>>2]|0,u>>>0<(t[T+32>>2]|0)>>>0?(s8(u,e,n),n=(t[r>>2]|0)+8|0,t[r>>2]=n):(lN(v,s,a),n=t[r>>2]|0),y=w,(n-(t[v>>2]|0)>>3)+-1|0}function s8(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,t[e+4>>2]=r}function lN(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0;if(w=y,y=y+32|0,s=w,a=e+4|0,v=((t[a>>2]|0)-(t[e>>2]|0)>>3)+1|0,u=sN(e)|0,u>>>0>>0)di(e);else{T=t[e>>2]|0,M=(t[e+8>>2]|0)-T|0,L=M>>2,aN(s,M>>3>>>0>>1>>>0?L>>>0>>0?v:L:u,(t[a>>2]|0)-T>>3,e+8|0),v=s+8|0,s8(t[v>>2]|0,t[n>>2]|0,t[r>>2]|0),t[v>>2]=(t[v>>2]|0)+8,fN(e,s),cN(s),y=w;return}}function sN(e){return e=e|0,536870911}function aN(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0;t[e+12>>2]=0,t[e+16>>2]=u;do if(n)if(n>>>0>536870911)$n();else{s=pn(n<<3)|0;break}else s=0;while(0);t[e>>2]=s,u=s+(r<<3)|0,t[e+8>>2]=u,t[e+4>>2]=u,t[e+12>>2]=s+(n<<3)}function fN(e,n){e=e|0,n=n|0;var r=0,u=0,s=0,a=0,v=0;u=t[e>>2]|0,v=e+4|0,a=n+4|0,s=(t[v>>2]|0)-u|0,r=(t[a>>2]|0)+(0-(s>>3)<<3)|0,t[a>>2]=r,(s|0)>0?(gr(r|0,u|0,s|0)|0,u=a,r=t[a>>2]|0):u=a,a=t[e>>2]|0,t[e>>2]=r,t[u>>2]=a,a=n+8|0,s=t[v>>2]|0,t[v>>2]=t[a>>2],t[a>>2]=s,a=e+8|0,v=n+12|0,e=t[a>>2]|0,t[a>>2]=t[v>>2],t[v>>2]=e,t[n>>2]=t[u>>2]}function cN(e){e=e|0;var n=0,r=0,u=0;n=t[e+4>>2]|0,r=e+8|0,u=t[r>>2]|0,(u|0)!=(n|0)&&(t[r>>2]=u+(~((u+-8-n|0)>>>3)<<3)),e=t[e>>2]|0,e|0&&Et(e)}function a8(e){e=e|0,hN(e)}function dN(e){e=e|0,pN(e+24|0)}function pN(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function hN(e){e=e|0;var n=0;n=yr()|0,jn(e,1,12,n,vN()|0,2),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function vN(){return 1896}function mN(e,n,r){e=e|0,n=n|0,r=r|0,gN(t[(yN(e)|0)>>2]|0,n,r)}function yN(e){return e=e|0,(t[(xE()|0)+24>>2]|0)+(e<<3)|0}function gN(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;u=y,y=y+16|0,a=u+4|0,s=u,_N(a,n),n=EN(a,n)|0,Ys(s,r),r=Ks(s,r)|0,I1[e&31](n,r),Xs(s),y=u}function _N(e,n){e=e|0,n=n|0}function EN(e,n){return e=e|0,n=n|0,DN(n)|0}function DN(e){return e=e|0,e|0}function wN(){var e=0;return h[8080]|0||(f8(11040),Wt(70,11040,ge|0)|0,e=8080,t[e>>2]=1,t[e+4>>2]=0),sr(11040)|0||f8(11040),11040}function f8(e){e=e|0,CN(e),Vp(e,71)}function SN(e){e=e|0,TN(e+24|0)}function TN(e){e=e|0;var n=0,r=0,u=0;r=t[e>>2]|0,u=r,r|0&&(e=e+4|0,n=t[e>>2]|0,(n|0)!=(r|0)&&(t[e>>2]=n+(~((n+-8-u|0)>>>3)<<3)),Et(r))}function CN(e){e=e|0;var n=0;n=yr()|0,jn(e,5,7,n,ON()|0,0),t[e+24>>2]=0,t[e+28>>2]=0,t[e+32>>2]=0}function xN(e){e=e|0,RN(e)}function RN(e){e=e|0,AN(e)}function AN(e){e=e|0,h[e+8>>0]=1}function ON(){return 1936}function MN(){return kN()|0}function kN(){var e=0,n=0,r=0,u=0,s=0,a=0,v=0;return n=y,y=y+16|0,s=n+4|0,v=n,r=Oa(8)|0,e=r,a=e+4|0,t[a>>2]=pn(1)|0,u=pn(8)|0,a=t[a>>2]|0,t[v>>2]=0,t[s>>2]=t[v>>2],NN(u,a,s),t[r>>2]=u,y=n,e|0}function NN(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]=n,r=pn(16)|0,t[r+4>>2]=0,t[r+8>>2]=0,t[r>>2]=1916,t[r+12>>2]=n,t[e+4>>2]=r}function LN(e){e=e|0,Uv(e),Et(e)}function FN(e){e=e|0,e=t[e+12>>2]|0,e|0&&Et(e)}function PN(e){e=e|0,Et(e)}function IN(){var e=0;return h[8088]|0||(qN(11076),Wt(25,11076,ge|0)|0,e=8088,t[e>>2]=1,t[e+4>>2]=0),11076}function bN(e,n){e=e|0,n=n|0,t[e>>2]=BN()|0,t[e+4>>2]=UN()|0,t[e+12>>2]=n,t[e+8>>2]=jN()|0,t[e+32>>2]=10}function BN(){return 11745}function UN(){return 1940}function jN(){return L1()|0}function zN(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,(Hl(u,896)|0)==512?r|0&&(HN(r),Et(r)):n|0&&Et(n)}function HN(e){e=e|0,e=t[e+4>>2]|0,e|0&&$d(e)}function qN(e){e=e|0,Xa(e)}function Gf(e,n){e=e|0,n=n|0,t[e>>2]=n}function RE(e){return e=e|0,t[e>>2]|0}function WN(e){return e=e|0,h[t[e>>2]>>0]|0}function VN(e,n){e=e|0,n=n|0;var r=0,u=0;r=y,y=y+16|0,u=r,t[u>>2]=t[e>>2],GN(n,u)|0,y=r}function GN(e,n){e=e|0,n=n|0;var r=0;return r=YN(t[e>>2]|0,n)|0,n=e+4|0,t[(t[n>>2]|0)+8>>2]=r,t[(t[n>>2]|0)+8>>2]|0}function YN(e,n){e=e|0,n=n|0;var r=0,u=0;return r=y,y=y+16|0,u=r,Ma(u),e=go(e)|0,n=KN(e,t[n>>2]|0)|0,ka(u),y=r,n|0}function Ma(e){e=e|0,t[e>>2]=t[2701],t[e+4>>2]=t[2703]}function KN(e,n){e=e|0,n=n|0;var r=0;return r=_o(XN()|0)|0,Ki(0,r|0,e|0,EE(n)|0)|0}function ka(e){e=e|0,$w(t[e>>2]|0,t[e+4>>2]|0)}function XN(){var e=0;return h[8096]|0||(QN(11120),e=8096,t[e>>2]=1,t[e+4>>2]=0),11120}function QN(e){e=e|0,ll(e,JN()|0,1)}function JN(){return 1948}function ZN(){$N()}function $N(){var e=0,n=0,r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0;if(Te=y,y=y+16|0,M=Te+4|0,b=Te,In(65536,10804,t[2702]|0,10812),r=Fw()|0,n=t[r>>2]|0,e=t[n>>2]|0,e|0)for(u=t[r+8>>2]|0,r=t[r+4>>2]|0;Xl(e|0,N[r>>0]|0|0,h[u>>0]|0),n=n+4|0,e=t[n>>2]|0,e;)u=u+1|0,r=r+1|0;if(e=Pw()|0,n=t[e>>2]|0,n|0)do ko(n|0,t[e+4>>2]|0),e=e+8|0,n=t[e>>2]|0;while((n|0)!=0);ko(eL()|0,5167),L=Fv()|0,e=t[L>>2]|0;e:do if(e|0){do tL(t[e+4>>2]|0),e=t[e>>2]|0;while((e|0)!=0);if(e=t[L>>2]|0,e|0){T=L;do{for(;s=e,e=t[e>>2]|0,s=t[s+4>>2]|0,!!(nL(s)|0);)if(t[b>>2]=T,t[M>>2]=t[b>>2],rL(L,M)|0,!e)break e;if(iL(s),T=t[T>>2]|0,n=c8(s)|0,a=fo()|0,v=y,y=y+((1*(n<<2)|0)+15&-16)|0,w=y,y=y+((1*(n<<2)|0)+15&-16)|0,n=t[(Yw(s)|0)>>2]|0,n|0)for(r=v,u=w;t[r>>2]=t[(Pv(t[n+4>>2]|0)|0)>>2],t[u>>2]=t[n+8>>2],n=t[n>>2]|0,n;)r=r+4|0,u=u+4|0;ye=Pv(s)|0,n=uL(s)|0,r=c8(s)|0,u=oL(s)|0,No(ye|0,n|0,v|0,w|0,r|0,u|0,hE(s)|0),yi(a|0)}while((e|0)!=0)}}while(0);if(e=t[(vE()|0)>>2]|0,e|0)do ye=e+4|0,L=mE(ye)|0,s=My(L)|0,a=Ay(L)|0,v=(Oy(L)|0)+1|0,w=d_(L)|0,T=d8(ye)|0,L=sr(L)|0,M=a_(ye)|0,b=AE(ye)|0,ao(0,s|0,a|0,v|0,w|0,T|0,L|0,M|0,b|0,OE(ye)|0),e=t[e>>2]|0;while((e|0)!=0);e=t[(Fv()|0)>>2]|0;e:do if(e|0){t:for(;;){if(n=t[e+4>>2]|0,n|0&&(X=t[(Pv(n)|0)>>2]|0,Be=t[(Kw(n)|0)>>2]|0,Be|0)){r=Be;do{n=r+4|0,u=mE(n)|0;n:do if(u|0)switch(sr(u)|0){case 0:break t;case 4:case 3:case 2:{w=My(u)|0,T=Ay(u)|0,L=(Oy(u)|0)+1|0,M=d_(u)|0,b=sr(u)|0,ye=a_(n)|0,ao(X|0,w|0,T|0,L|0,M|0,0,b|0,ye|0,AE(n)|0,OE(n)|0);break n}case 1:{v=My(u)|0,w=Ay(u)|0,T=(Oy(u)|0)+1|0,L=d_(u)|0,M=d8(n)|0,b=sr(u)|0,ye=a_(n)|0,ao(X|0,v|0,w|0,T|0,L|0,M|0,b|0,ye|0,AE(n)|0,OE(n)|0);break n}case 5:{L=My(u)|0,M=Ay(u)|0,b=(Oy(u)|0)+1|0,ye=d_(u)|0,ao(X|0,L|0,M|0,b|0,ye|0,lL(u)|0,sr(u)|0,0,0,0);break n}default:break n}while(0);r=t[r>>2]|0}while((r|0)!=0)}if(e=t[e>>2]|0,!e)break e}$n()}while(0);Is(),y=Te}function eL(){return 11703}function tL(e){e=e|0,h[e+40>>0]=0}function nL(e){return e=e|0,(h[e+40>>0]|0)!=0|0}function rL(e,n){return e=e|0,n=n|0,n=sL(n)|0,e=t[n>>2]|0,t[n>>2]=t[e>>2],Et(e),t[n>>2]|0}function iL(e){e=e|0,h[e+40>>0]=1}function c8(e){return e=e|0,t[e+20>>2]|0}function uL(e){return e=e|0,t[e+8>>2]|0}function oL(e){return e=e|0,t[e+32>>2]|0}function d_(e){return e=e|0,t[e+4>>2]|0}function d8(e){return e=e|0,t[e+4>>2]|0}function AE(e){return e=e|0,t[e+8>>2]|0}function OE(e){return e=e|0,t[e+16>>2]|0}function lL(e){return e=e|0,t[e+20>>2]|0}function sL(e){return e=e|0,t[e>>2]|0}function p_(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0,Ze=0,Ye=0,ct=0,ke=0,Ie=0,Zt=0;Zt=y,y=y+16|0,X=Zt;do if(e>>>0<245){if(L=e>>>0<11?16:e+11&-8,e=L>>>3,b=t[2783]|0,r=b>>>e,r&3|0)return n=(r&1^1)+e|0,e=11172+(n<<1<<2)|0,r=e+8|0,u=t[r>>2]|0,s=u+8|0,a=t[s>>2]|0,(e|0)==(a|0)?t[2783]=b&~(1<>2]=e,t[r>>2]=a),Ie=n<<3,t[u+4>>2]=Ie|3,Ie=u+Ie+4|0,t[Ie>>2]=t[Ie>>2]|1,Ie=s,y=Zt,Ie|0;if(M=t[2785]|0,L>>>0>M>>>0){if(r|0)return n=2<>>12&16,n=n>>>v,r=n>>>5&8,n=n>>>r,s=n>>>2&4,n=n>>>s,e=n>>>1&2,n=n>>>e,u=n>>>1&1,u=(r|v|s|e|u)+(n>>>u)|0,n=11172+(u<<1<<2)|0,e=n+8|0,s=t[e>>2]|0,v=s+8|0,r=t[v>>2]|0,(n|0)==(r|0)?(e=b&~(1<>2]=n,t[e>>2]=r,e=b),a=(u<<3)-L|0,t[s+4>>2]=L|3,u=s+L|0,t[u+4>>2]=a|1,t[u+a>>2]=a,M|0&&(s=t[2788]|0,n=M>>>3,r=11172+(n<<1<<2)|0,n=1<>2]|0):(t[2783]=e|n,n=r,e=r+8|0),t[e>>2]=s,t[n+12>>2]=s,t[s+8>>2]=n,t[s+12>>2]=r),t[2785]=a,t[2788]=u,Ie=v,y=Zt,Ie|0;if(w=t[2784]|0,w){if(r=(w&0-w)+-1|0,v=r>>>12&16,r=r>>>v,a=r>>>5&8,r=r>>>a,T=r>>>2&4,r=r>>>T,u=r>>>1&2,r=r>>>u,e=r>>>1&1,e=t[11436+((a|v|T|u|e)+(r>>>e)<<2)>>2]|0,r=(t[e+4>>2]&-8)-L|0,u=t[e+16+(((t[e+16>>2]|0)==0&1)<<2)>>2]|0,!u)T=e,a=r;else{do v=(t[u+4>>2]&-8)-L|0,T=v>>>0>>0,r=T?v:r,e=T?u:e,u=t[u+16+(((t[u+16>>2]|0)==0&1)<<2)>>2]|0;while((u|0)!=0);T=e,a=r}if(v=T+L|0,T>>>0>>0){s=t[T+24>>2]|0,n=t[T+12>>2]|0;do if((n|0)==(T|0)){if(e=T+20|0,n=t[e>>2]|0,!n&&(e=T+16|0,n=t[e>>2]|0,!n)){r=0;break}for(;;){if(r=n+20|0,u=t[r>>2]|0,u|0){n=u,e=r;continue}if(r=n+16|0,u=t[r>>2]|0,u)n=u,e=r;else break}t[e>>2]=0,r=n}else r=t[T+8>>2]|0,t[r+12>>2]=n,t[n+8>>2]=r,r=n;while(0);do if(s|0){if(n=t[T+28>>2]|0,e=11436+(n<<2)|0,(T|0)==(t[e>>2]|0)){if(t[e>>2]=r,!r){t[2784]=w&~(1<>2]|0)!=(T|0)&1)<<2)>>2]=r,!r)break;t[r+24>>2]=s,n=t[T+16>>2]|0,n|0&&(t[r+16>>2]=n,t[n+24>>2]=r),n=t[T+20>>2]|0,n|0&&(t[r+20>>2]=n,t[n+24>>2]=r)}while(0);return a>>>0<16?(Ie=a+L|0,t[T+4>>2]=Ie|3,Ie=T+Ie+4|0,t[Ie>>2]=t[Ie>>2]|1):(t[T+4>>2]=L|3,t[v+4>>2]=a|1,t[v+a>>2]=a,M|0&&(u=t[2788]|0,n=M>>>3,r=11172+(n<<1<<2)|0,n=1<>2]|0):(t[2783]=b|n,n=r,e=r+8|0),t[e>>2]=u,t[n+12>>2]=u,t[u+8>>2]=n,t[u+12>>2]=r),t[2785]=a,t[2788]=v),Ie=T+8|0,y=Zt,Ie|0}else b=L}else b=L}else b=L}else if(e>>>0<=4294967231)if(e=e+11|0,L=e&-8,T=t[2784]|0,T){u=0-L|0,e=e>>>8,e?L>>>0>16777215?w=31:(b=(e+1048320|0)>>>16&8,ke=e<>>16&4,ke=ke<>>16&2,w=14-(M|b|w)+(ke<>>15)|0,w=L>>>(w+7|0)&1|w<<1):w=0,r=t[11436+(w<<2)>>2]|0;e:do if(!r)r=0,e=0,ke=57;else for(e=0,v=L<<((w|0)==31?0:25-(w>>>1)|0),a=0;;){if(s=(t[r+4>>2]&-8)-L|0,s>>>0>>0)if(s)e=r,u=s;else{e=r,u=0,s=r,ke=61;break e}if(s=t[r+20>>2]|0,r=t[r+16+(v>>>31<<2)>>2]|0,a=(s|0)==0|(s|0)==(r|0)?a:s,s=(r|0)==0,s){r=a,ke=57;break}else v=v<<((s^1)&1)}while(0);if((ke|0)==57){if((r|0)==0&(e|0)==0){if(e=2<>>12&16,b=b>>>v,a=b>>>5&8,b=b>>>a,w=b>>>2&4,b=b>>>w,M=b>>>1&2,b=b>>>M,r=b>>>1&1,e=0,r=t[11436+((a|v|w|M|r)+(b>>>r)<<2)>>2]|0}r?(s=r,ke=61):(w=e,v=u)}if((ke|0)==61)for(;;)if(ke=0,r=(t[s+4>>2]&-8)-L|0,b=r>>>0>>0,r=b?r:u,e=b?s:e,s=t[s+16+(((t[s+16>>2]|0)==0&1)<<2)>>2]|0,s)u=r,ke=61;else{w=e,v=r;break}if((w|0)!=0&&v>>>0<((t[2785]|0)-L|0)>>>0){if(a=w+L|0,w>>>0>=a>>>0)return Ie=0,y=Zt,Ie|0;s=t[w+24>>2]|0,n=t[w+12>>2]|0;do if((n|0)==(w|0)){if(e=w+20|0,n=t[e>>2]|0,!n&&(e=w+16|0,n=t[e>>2]|0,!n)){n=0;break}for(;;){if(r=n+20|0,u=t[r>>2]|0,u|0){n=u,e=r;continue}if(r=n+16|0,u=t[r>>2]|0,u)n=u,e=r;else break}t[e>>2]=0}else Ie=t[w+8>>2]|0,t[Ie+12>>2]=n,t[n+8>>2]=Ie;while(0);do if(s){if(e=t[w+28>>2]|0,r=11436+(e<<2)|0,(w|0)==(t[r>>2]|0)){if(t[r>>2]=n,!n){u=T&~(1<>2]|0)!=(w|0)&1)<<2)>>2]=n,!n){u=T;break}t[n+24>>2]=s,e=t[w+16>>2]|0,e|0&&(t[n+16>>2]=e,t[e+24>>2]=n),e=t[w+20>>2]|0,e&&(t[n+20>>2]=e,t[e+24>>2]=n),u=T}else u=T;while(0);do if(v>>>0>=16){if(t[w+4>>2]=L|3,t[a+4>>2]=v|1,t[a+v>>2]=v,n=v>>>3,v>>>0<256){r=11172+(n<<1<<2)|0,e=t[2783]|0,n=1<>2]|0):(t[2783]=e|n,n=r,e=r+8|0),t[e>>2]=a,t[n+12>>2]=a,t[a+8>>2]=n,t[a+12>>2]=r;break}if(n=v>>>8,n?v>>>0>16777215?n=31:(ke=(n+1048320|0)>>>16&8,Ie=n<>>16&4,Ie=Ie<>>16&2,n=14-(ct|ke|n)+(Ie<>>15)|0,n=v>>>(n+7|0)&1|n<<1):n=0,r=11436+(n<<2)|0,t[a+28>>2]=n,e=a+16|0,t[e+4>>2]=0,t[e>>2]=0,e=1<>2]=a,t[a+24>>2]=r,t[a+12>>2]=a,t[a+8>>2]=a;break}for(e=v<<((n|0)==31?0:25-(n>>>1)|0),r=t[r>>2]|0;;){if((t[r+4>>2]&-8|0)==(v|0)){ke=97;break}if(u=r+16+(e>>>31<<2)|0,n=t[u>>2]|0,n)e=e<<1,r=n;else{ke=96;break}}if((ke|0)==96){t[u>>2]=a,t[a+24>>2]=r,t[a+12>>2]=a,t[a+8>>2]=a;break}else if((ke|0)==97){ke=r+8|0,Ie=t[ke>>2]|0,t[Ie+12>>2]=a,t[ke>>2]=a,t[a+8>>2]=Ie,t[a+12>>2]=r,t[a+24>>2]=0;break}}else Ie=v+L|0,t[w+4>>2]=Ie|3,Ie=w+Ie+4|0,t[Ie>>2]=t[Ie>>2]|1;while(0);return Ie=w+8|0,y=Zt,Ie|0}else b=L}else b=L;else b=-1;while(0);if(r=t[2785]|0,r>>>0>=b>>>0)return n=r-b|0,e=t[2788]|0,n>>>0>15?(Ie=e+b|0,t[2788]=Ie,t[2785]=n,t[Ie+4>>2]=n|1,t[Ie+n>>2]=n,t[e+4>>2]=b|3):(t[2785]=0,t[2788]=0,t[e+4>>2]=r|3,Ie=e+r+4|0,t[Ie>>2]=t[Ie>>2]|1),Ie=e+8|0,y=Zt,Ie|0;if(v=t[2786]|0,v>>>0>b>>>0)return ct=v-b|0,t[2786]=ct,Ie=t[2789]|0,ke=Ie+b|0,t[2789]=ke,t[ke+4>>2]=ct|1,t[Ie+4>>2]=b|3,Ie=Ie+8|0,y=Zt,Ie|0;if(t[2901]|0?e=t[2903]|0:(t[2903]=4096,t[2902]=4096,t[2904]=-1,t[2905]=-1,t[2906]=0,t[2894]=0,e=X&-16^1431655768,t[X>>2]=e,t[2901]=e,e=4096),w=b+48|0,T=b+47|0,a=e+T|0,s=0-e|0,L=a&s,L>>>0<=b>>>0||(e=t[2893]|0,e|0&&(M=t[2891]|0,X=M+L|0,X>>>0<=M>>>0|X>>>0>e>>>0)))return Ie=0,y=Zt,Ie|0;e:do if(t[2894]&4)n=0,ke=133;else{r=t[2789]|0;t:do if(r){for(u=11580;e=t[u>>2]|0,!(e>>>0<=r>>>0&&(ye=u+4|0,(e+(t[ye>>2]|0)|0)>>>0>r>>>0));)if(e=t[u+8>>2]|0,e)u=e;else{ke=118;break t}if(n=a-v&s,n>>>0<2147483647)if(e=e2(n|0)|0,(e|0)==((t[u>>2]|0)+(t[ye>>2]|0)|0)){if((e|0)!=-1){v=n,a=e,ke=135;break e}}else u=e,ke=126;else n=0}else ke=118;while(0);do if((ke|0)==118)if(r=e2(0)|0,(r|0)!=-1&&(n=r,Be=t[2902]|0,Te=Be+-1|0,n=((Te&n|0)==0?0:(Te+n&0-Be)-n|0)+L|0,Be=t[2891]|0,Te=n+Be|0,n>>>0>b>>>0&n>>>0<2147483647)){if(ye=t[2893]|0,ye|0&&Te>>>0<=Be>>>0|Te>>>0>ye>>>0){n=0;break}if(e=e2(n|0)|0,(e|0)==(r|0)){v=n,a=r,ke=135;break e}else u=e,ke=126}else n=0;while(0);do if((ke|0)==126){if(r=0-n|0,!(w>>>0>n>>>0&(n>>>0<2147483647&(u|0)!=-1)))if((u|0)==-1){n=0;break}else{v=n,a=u,ke=135;break e}if(e=t[2903]|0,e=T-n+e&0-e,e>>>0>=2147483647){v=n,a=u,ke=135;break e}if((e2(e|0)|0)==-1){e2(r|0)|0,n=0;break}else{v=e+n|0,a=u,ke=135;break e}}while(0);t[2894]=t[2894]|4,ke=133}while(0);if((ke|0)==133&&L>>>0<2147483647&&(ct=e2(L|0)|0,ye=e2(0)|0,Ze=ye-ct|0,Ye=Ze>>>0>(b+40|0)>>>0,!((ct|0)==-1|Ye^1|ct>>>0>>0&((ct|0)!=-1&(ye|0)!=-1)^1))&&(v=Ye?Ze:n,a=ct,ke=135),(ke|0)==135){n=(t[2891]|0)+v|0,t[2891]=n,n>>>0>(t[2892]|0)>>>0&&(t[2892]=n),T=t[2789]|0;do if(T){for(n=11580;;){if(e=t[n>>2]|0,r=n+4|0,u=t[r>>2]|0,(a|0)==(e+u|0)){ke=145;break}if(s=t[n+8>>2]|0,s)n=s;else break}if((ke|0)==145&&(t[n+12>>2]&8|0)==0&&T>>>0>>0&T>>>0>=e>>>0){t[r>>2]=u+v,Ie=T+8|0,Ie=(Ie&7|0)==0?0:0-Ie&7,ke=T+Ie|0,Ie=(t[2786]|0)+(v-Ie)|0,t[2789]=ke,t[2786]=Ie,t[ke+4>>2]=Ie|1,t[ke+Ie+4>>2]=40,t[2790]=t[2905];break}for(a>>>0<(t[2787]|0)>>>0&&(t[2787]=a),r=a+v|0,n=11580;;){if((t[n>>2]|0)==(r|0)){ke=153;break}if(e=t[n+8>>2]|0,e)n=e;else break}if((ke|0)==153&&(t[n+12>>2]&8|0)==0){t[n>>2]=a,M=n+4|0,t[M>>2]=(t[M>>2]|0)+v,M=a+8|0,M=a+((M&7|0)==0?0:0-M&7)|0,n=r+8|0,n=r+((n&7|0)==0?0:0-n&7)|0,L=M+b|0,w=n-M-b|0,t[M+4>>2]=b|3;do if((n|0)!=(T|0)){if((n|0)==(t[2788]|0)){Ie=(t[2785]|0)+w|0,t[2785]=Ie,t[2788]=L,t[L+4>>2]=Ie|1,t[L+Ie>>2]=Ie;break}if(e=t[n+4>>2]|0,(e&3|0)==1){v=e&-8,u=e>>>3;e:do if(e>>>0<256)if(e=t[n+8>>2]|0,r=t[n+12>>2]|0,(r|0)==(e|0)){t[2783]=t[2783]&~(1<>2]=r,t[r+8>>2]=e;break}else{a=t[n+24>>2]|0,e=t[n+12>>2]|0;do if((e|0)==(n|0)){if(u=n+16|0,r=u+4|0,e=t[r>>2]|0,!e)if(e=t[u>>2]|0,e)r=u;else{e=0;break}for(;;){if(u=e+20|0,s=t[u>>2]|0,s|0){e=s,r=u;continue}if(u=e+16|0,s=t[u>>2]|0,s)e=s,r=u;else break}t[r>>2]=0}else Ie=t[n+8>>2]|0,t[Ie+12>>2]=e,t[e+8>>2]=Ie;while(0);if(!a)break;r=t[n+28>>2]|0,u=11436+(r<<2)|0;do if((n|0)!=(t[u>>2]|0)){if(t[a+16+(((t[a+16>>2]|0)!=(n|0)&1)<<2)>>2]=e,!e)break e}else{if(t[u>>2]=e,e|0)break;t[2784]=t[2784]&~(1<>2]=a,r=n+16|0,u=t[r>>2]|0,u|0&&(t[e+16>>2]=u,t[u+24>>2]=e),r=t[r+4>>2]|0,!r)break;t[e+20>>2]=r,t[r+24>>2]=e}while(0);n=n+v|0,s=v+w|0}else s=w;if(n=n+4|0,t[n>>2]=t[n>>2]&-2,t[L+4>>2]=s|1,t[L+s>>2]=s,n=s>>>3,s>>>0<256){r=11172+(n<<1<<2)|0,e=t[2783]|0,n=1<>2]|0):(t[2783]=e|n,n=r,e=r+8|0),t[e>>2]=L,t[n+12>>2]=L,t[L+8>>2]=n,t[L+12>>2]=r;break}n=s>>>8;do if(!n)n=0;else{if(s>>>0>16777215){n=31;break}ke=(n+1048320|0)>>>16&8,Ie=n<>>16&4,Ie=Ie<>>16&2,n=14-(ct|ke|n)+(Ie<>>15)|0,n=s>>>(n+7|0)&1|n<<1}while(0);if(u=11436+(n<<2)|0,t[L+28>>2]=n,e=L+16|0,t[e+4>>2]=0,t[e>>2]=0,e=t[2784]|0,r=1<>2]=L,t[L+24>>2]=u,t[L+12>>2]=L,t[L+8>>2]=L;break}for(e=s<<((n|0)==31?0:25-(n>>>1)|0),r=t[u>>2]|0;;){if((t[r+4>>2]&-8|0)==(s|0)){ke=194;break}if(u=r+16+(e>>>31<<2)|0,n=t[u>>2]|0,n)e=e<<1,r=n;else{ke=193;break}}if((ke|0)==193){t[u>>2]=L,t[L+24>>2]=r,t[L+12>>2]=L,t[L+8>>2]=L;break}else if((ke|0)==194){ke=r+8|0,Ie=t[ke>>2]|0,t[Ie+12>>2]=L,t[ke>>2]=L,t[L+8>>2]=Ie,t[L+12>>2]=r,t[L+24>>2]=0;break}}else Ie=(t[2786]|0)+w|0,t[2786]=Ie,t[2789]=L,t[L+4>>2]=Ie|1;while(0);return Ie=M+8|0,y=Zt,Ie|0}for(n=11580;e=t[n>>2]|0,!(e>>>0<=T>>>0&&(Ie=e+(t[n+4>>2]|0)|0,Ie>>>0>T>>>0));)n=t[n+8>>2]|0;s=Ie+-47|0,e=s+8|0,e=s+((e&7|0)==0?0:0-e&7)|0,s=T+16|0,e=e>>>0>>0?T:e,n=e+8|0,r=a+8|0,r=(r&7|0)==0?0:0-r&7,ke=a+r|0,r=v+-40-r|0,t[2789]=ke,t[2786]=r,t[ke+4>>2]=r|1,t[ke+r+4>>2]=40,t[2790]=t[2905],r=e+4|0,t[r>>2]=27,t[n>>2]=t[2895],t[n+4>>2]=t[2896],t[n+8>>2]=t[2897],t[n+12>>2]=t[2898],t[2895]=a,t[2896]=v,t[2898]=0,t[2897]=n,n=e+24|0;do ke=n,n=n+4|0,t[n>>2]=7;while((ke+8|0)>>>0>>0);if((e|0)!=(T|0)){if(a=e-T|0,t[r>>2]=t[r>>2]&-2,t[T+4>>2]=a|1,t[e>>2]=a,n=a>>>3,a>>>0<256){r=11172+(n<<1<<2)|0,e=t[2783]|0,n=1<>2]|0):(t[2783]=e|n,n=r,e=r+8|0),t[e>>2]=T,t[n+12>>2]=T,t[T+8>>2]=n,t[T+12>>2]=r;break}if(n=a>>>8,n?a>>>0>16777215?r=31:(ke=(n+1048320|0)>>>16&8,Ie=n<>>16&4,Ie=Ie<>>16&2,r=14-(ct|ke|r)+(Ie<>>15)|0,r=a>>>(r+7|0)&1|r<<1):r=0,u=11436+(r<<2)|0,t[T+28>>2]=r,t[T+20>>2]=0,t[s>>2]=0,n=t[2784]|0,e=1<>2]=T,t[T+24>>2]=u,t[T+12>>2]=T,t[T+8>>2]=T;break}for(e=a<<((r|0)==31?0:25-(r>>>1)|0),r=t[u>>2]|0;;){if((t[r+4>>2]&-8|0)==(a|0)){ke=216;break}if(u=r+16+(e>>>31<<2)|0,n=t[u>>2]|0,n)e=e<<1,r=n;else{ke=215;break}}if((ke|0)==215){t[u>>2]=T,t[T+24>>2]=r,t[T+12>>2]=T,t[T+8>>2]=T;break}else if((ke|0)==216){ke=r+8|0,Ie=t[ke>>2]|0,t[Ie+12>>2]=T,t[ke>>2]=T,t[T+8>>2]=Ie,t[T+12>>2]=r,t[T+24>>2]=0;break}}}else{Ie=t[2787]|0,(Ie|0)==0|a>>>0>>0&&(t[2787]=a),t[2895]=a,t[2896]=v,t[2898]=0,t[2792]=t[2901],t[2791]=-1,n=0;do Ie=11172+(n<<1<<2)|0,t[Ie+12>>2]=Ie,t[Ie+8>>2]=Ie,n=n+1|0;while((n|0)!=32);Ie=a+8|0,Ie=(Ie&7|0)==0?0:0-Ie&7,ke=a+Ie|0,Ie=v+-40-Ie|0,t[2789]=ke,t[2786]=Ie,t[ke+4>>2]=Ie|1,t[ke+Ie+4>>2]=40,t[2790]=t[2905]}while(0);if(n=t[2786]|0,n>>>0>b>>>0)return ct=n-b|0,t[2786]=ct,Ie=t[2789]|0,ke=Ie+b|0,t[2789]=ke,t[ke+4>>2]=ct|1,t[Ie+4>>2]=b|3,Ie=Ie+8|0,y=Zt,Ie|0}return t[(bv()|0)>>2]=12,Ie=0,y=Zt,Ie|0}function h_(e){e=e|0;var n=0,r=0,u=0,s=0,a=0,v=0,w=0,T=0;if(!!e){r=e+-8|0,s=t[2787]|0,e=t[e+-4>>2]|0,n=e&-8,T=r+n|0;do if(e&1)w=r,v=r;else{if(u=t[r>>2]|0,!(e&3)||(v=r+(0-u)|0,a=u+n|0,v>>>0>>0))return;if((v|0)==(t[2788]|0)){if(e=T+4|0,n=t[e>>2]|0,(n&3|0)!=3){w=v,n=a;break}t[2785]=a,t[e>>2]=n&-2,t[v+4>>2]=a|1,t[v+a>>2]=a;return}if(r=u>>>3,u>>>0<256)if(e=t[v+8>>2]|0,n=t[v+12>>2]|0,(n|0)==(e|0)){t[2783]=t[2783]&~(1<>2]=n,t[n+8>>2]=e,w=v,n=a;break}s=t[v+24>>2]|0,e=t[v+12>>2]|0;do if((e|0)==(v|0)){if(r=v+16|0,n=r+4|0,e=t[n>>2]|0,!e)if(e=t[r>>2]|0,e)n=r;else{e=0;break}for(;;){if(r=e+20|0,u=t[r>>2]|0,u|0){e=u,n=r;continue}if(r=e+16|0,u=t[r>>2]|0,u)e=u,n=r;else break}t[n>>2]=0}else w=t[v+8>>2]|0,t[w+12>>2]=e,t[e+8>>2]=w;while(0);if(s){if(n=t[v+28>>2]|0,r=11436+(n<<2)|0,(v|0)==(t[r>>2]|0)){if(t[r>>2]=e,!e){t[2784]=t[2784]&~(1<>2]|0)!=(v|0)&1)<<2)>>2]=e,!e){w=v,n=a;break}t[e+24>>2]=s,n=v+16|0,r=t[n>>2]|0,r|0&&(t[e+16>>2]=r,t[r+24>>2]=e),n=t[n+4>>2]|0,n?(t[e+20>>2]=n,t[n+24>>2]=e,w=v,n=a):(w=v,n=a)}else w=v,n=a}while(0);if(!(v>>>0>=T>>>0)&&(e=T+4|0,u=t[e>>2]|0,!!(u&1))){if(u&2)t[e>>2]=u&-2,t[w+4>>2]=n|1,t[v+n>>2]=n,s=n;else{if(e=t[2788]|0,(T|0)==(t[2789]|0)){if(T=(t[2786]|0)+n|0,t[2786]=T,t[2789]=w,t[w+4>>2]=T|1,(w|0)!=(e|0))return;t[2788]=0,t[2785]=0;return}if((T|0)==(e|0)){T=(t[2785]|0)+n|0,t[2785]=T,t[2788]=v,t[w+4>>2]=T|1,t[v+T>>2]=T;return}s=(u&-8)+n|0,r=u>>>3;do if(u>>>0<256)if(n=t[T+8>>2]|0,e=t[T+12>>2]|0,(e|0)==(n|0)){t[2783]=t[2783]&~(1<>2]=e,t[e+8>>2]=n;break}else{a=t[T+24>>2]|0,e=t[T+12>>2]|0;do if((e|0)==(T|0)){if(r=T+16|0,n=r+4|0,e=t[n>>2]|0,!e)if(e=t[r>>2]|0,e)n=r;else{r=0;break}for(;;){if(r=e+20|0,u=t[r>>2]|0,u|0){e=u,n=r;continue}if(r=e+16|0,u=t[r>>2]|0,u)e=u,n=r;else break}t[n>>2]=0,r=e}else r=t[T+8>>2]|0,t[r+12>>2]=e,t[e+8>>2]=r,r=e;while(0);if(a|0){if(e=t[T+28>>2]|0,n=11436+(e<<2)|0,(T|0)==(t[n>>2]|0)){if(t[n>>2]=r,!r){t[2784]=t[2784]&~(1<>2]|0)!=(T|0)&1)<<2)>>2]=r,!r)break;t[r+24>>2]=a,e=T+16|0,n=t[e>>2]|0,n|0&&(t[r+16>>2]=n,t[n+24>>2]=r),e=t[e+4>>2]|0,e|0&&(t[r+20>>2]=e,t[e+24>>2]=r)}}while(0);if(t[w+4>>2]=s|1,t[v+s>>2]=s,(w|0)==(t[2788]|0)){t[2785]=s;return}}if(e=s>>>3,s>>>0<256){r=11172+(e<<1<<2)|0,n=t[2783]|0,e=1<>2]|0):(t[2783]=n|e,e=r,n=r+8|0),t[n>>2]=w,t[e+12>>2]=w,t[w+8>>2]=e,t[w+12>>2]=r;return}e=s>>>8,e?s>>>0>16777215?e=31:(v=(e+1048320|0)>>>16&8,T=e<>>16&4,T=T<>>16&2,e=14-(a|v|e)+(T<>>15)|0,e=s>>>(e+7|0)&1|e<<1):e=0,u=11436+(e<<2)|0,t[w+28>>2]=e,t[w+20>>2]=0,t[w+16>>2]=0,n=t[2784]|0,r=1<>>1)|0),r=t[u>>2]|0;;){if((t[r+4>>2]&-8|0)==(s|0)){e=73;break}if(u=r+16+(n>>>31<<2)|0,e=t[u>>2]|0,e)n=n<<1,r=e;else{e=72;break}}if((e|0)==72){t[u>>2]=w,t[w+24>>2]=r,t[w+12>>2]=w,t[w+8>>2]=w;break}else if((e|0)==73){v=r+8|0,T=t[v>>2]|0,t[T+12>>2]=w,t[v>>2]=w,t[w+8>>2]=T,t[w+12>>2]=r,t[w+24>>2]=0;break}}else t[2784]=n|r,t[u>>2]=w,t[w+24>>2]=u,t[w+12>>2]=w,t[w+8>>2]=w;while(0);if(T=(t[2791]|0)+-1|0,t[2791]=T,!T)e=11588;else return;for(;e=t[e>>2]|0,e;)e=e+8|0;t[2791]=-1}}}function aL(){return 11628}function fL(e){e=e|0;var n=0,r=0;return n=y,y=y+16|0,r=n,t[r>>2]=pL(t[e+60>>2]|0)|0,e=v_(Au(6,r|0)|0)|0,y=n,e|0}function p8(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0;b=y,y=y+48|0,L=b+16|0,a=b,s=b+32|0,w=e+28|0,u=t[w>>2]|0,t[s>>2]=u,T=e+20|0,u=(t[T>>2]|0)-u|0,t[s+4>>2]=u,t[s+8>>2]=n,t[s+12>>2]=r,u=u+r|0,v=e+60|0,t[a>>2]=t[v>>2],t[a+4>>2]=s,t[a+8>>2]=2,a=v_(h0(146,a|0)|0)|0;e:do if((u|0)!=(a|0)){for(n=2;!((a|0)<0);)if(u=u-a|0,Be=t[s+4>>2]|0,X=a>>>0>Be>>>0,s=X?s+8|0:s,n=(X<<31>>31)+n|0,Be=a-(X?Be:0)|0,t[s>>2]=(t[s>>2]|0)+Be,X=s+4|0,t[X>>2]=(t[X>>2]|0)-Be,t[L>>2]=t[v>>2],t[L+4>>2]=s,t[L+8>>2]=n,a=v_(h0(146,L|0)|0)|0,(u|0)==(a|0)){M=3;break e}t[e+16>>2]=0,t[w>>2]=0,t[T>>2]=0,t[e>>2]=t[e>>2]|32,(n|0)==2?r=0:r=r-(t[s+4>>2]|0)|0}else M=3;while(0);return(M|0)==3&&(Be=t[e+44>>2]|0,t[e+16>>2]=Be+(t[e+48>>2]|0),t[w>>2]=Be,t[T>>2]=Be),y=b,r|0}function cL(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;return s=y,y=y+32|0,a=s,u=s+20|0,t[a>>2]=t[e+60>>2],t[a+4>>2]=0,t[a+8>>2]=n,t[a+12>>2]=u,t[a+16>>2]=r,(v_(Ni(140,a|0)|0)|0)<0?(t[u>>2]=-1,e=-1):e=t[u>>2]|0,y=s,e|0}function v_(e){return e=e|0,e>>>0>4294963200&&(t[(bv()|0)>>2]=0-e,e=-1),e|0}function bv(){return(dL()|0)+64|0}function dL(){return ME()|0}function ME(){return 2084}function pL(e){return e=e|0,e|0}function hL(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;return s=y,y=y+32|0,u=s,t[e+36>>2]=1,(t[e>>2]&64|0)==0&&(t[u>>2]=t[e+60>>2],t[u+4>>2]=21523,t[u+8>>2]=s+16,I0(54,u|0)|0)&&(h[e+75>>0]=-1),u=p8(e,n,r)|0,y=s,u|0}function h8(e,n){e=e|0,n=n|0;var r=0,u=0;if(r=h[e>>0]|0,u=h[n>>0]|0,r<<24>>24==0||r<<24>>24!=u<<24>>24)e=u;else{do e=e+1|0,n=n+1|0,r=h[e>>0]|0,u=h[n>>0]|0;while(!(r<<24>>24==0||r<<24>>24!=u<<24>>24));e=u}return(r&255)-(e&255)|0}function vL(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0;e:do if(!r)e=0;else{for(;u=h[e>>0]|0,s=h[n>>0]|0,u<<24>>24==s<<24>>24;)if(r=r+-1|0,r)e=e+1|0,n=n+1|0;else{e=0;break e}e=(u&255)-(s&255)|0}while(0);return e|0}function v8(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0;ye=y,y=y+224|0,M=ye+120|0,b=ye+80|0,Be=ye,Te=ye+136|0,u=b,s=u+40|0;do t[u>>2]=0,u=u+4|0;while((u|0)<(s|0));return t[M>>2]=t[r>>2],(kE(0,n,M,Be,b)|0)<0?r=-1:((t[e+76>>2]|0)>-1?X=mL(e)|0:X=0,r=t[e>>2]|0,L=r&32,(h[e+74>>0]|0)<1&&(t[e>>2]=r&-33),u=e+48|0,t[u>>2]|0?r=kE(e,n,M,Be,b)|0:(s=e+44|0,a=t[s>>2]|0,t[s>>2]=Te,v=e+28|0,t[v>>2]=Te,w=e+20|0,t[w>>2]=Te,t[u>>2]=80,T=e+16|0,t[T>>2]=Te+80,r=kE(e,n,M,Be,b)|0,a&&(__[t[e+36>>2]&7](e,0,0)|0,r=(t[w>>2]|0)==0?-1:r,t[s>>2]=a,t[u>>2]=0,t[T>>2]=0,t[v>>2]=0,t[w>>2]=0)),u=t[e>>2]|0,t[e>>2]=u|L,X|0&&yL(e),r=(u&32|0)==0?r:-1),y=ye,r|0}function kE(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0,Ze=0,Ye=0,ct=0,ke=0,Ie=0,Zt=0,Br=0,Pn=0,gn=0,_r=0,Pr=0,kn=0;kn=y,y=y+64|0,Pn=kn+16|0,gn=kn,Zt=kn+24|0,_r=kn+8|0,Pr=kn+20|0,t[Pn>>2]=n,ct=(e|0)!=0,ke=Zt+40|0,Ie=ke,Zt=Zt+39|0,Br=_r+4|0,v=0,a=0,M=0;e:for(;;){do if((a|0)>-1)if((v|0)>(2147483647-a|0)){t[(bv()|0)>>2]=75,a=-1;break}else{a=v+a|0;break}while(0);if(v=h[n>>0]|0,v<<24>>24)w=n;else{Ye=87;break}t:for(;;){switch(v<<24>>24){case 37:{v=w,Ye=9;break t}case 0:{v=w;break t}default:}Ze=w+1|0,t[Pn>>2]=Ze,v=h[Ze>>0]|0,w=Ze}t:do if((Ye|0)==9)for(;;){if(Ye=0,(h[w+1>>0]|0)!=37)break t;if(v=v+1|0,w=w+2|0,t[Pn>>2]=w,(h[w>>0]|0)==37)Ye=9;else break}while(0);if(v=v-n|0,ct&&Yo(e,n,v),v|0){n=w;continue}T=w+1|0,v=(h[T>>0]|0)+-48|0,v>>>0<10?(Ze=(h[w+2>>0]|0)==36,ye=Ze?v:-1,M=Ze?1:M,T=Ze?w+3|0:T):ye=-1,t[Pn>>2]=T,v=h[T>>0]|0,w=(v<<24>>24)+-32|0;t:do if(w>>>0<32)for(L=0,b=v;;){if(v=1<>2]=T,v=h[T>>0]|0,w=(v<<24>>24)+-32|0,w>>>0>=32)break;b=v}else L=0;while(0);if(v<<24>>24==42){if(w=T+1|0,v=(h[w>>0]|0)+-48|0,v>>>0<10&&(h[T+2>>0]|0)==36)t[s+(v<<2)>>2]=10,v=t[u+((h[w>>0]|0)+-48<<3)>>2]|0,M=1,T=T+3|0;else{if(M|0){a=-1;break}ct?(M=(t[r>>2]|0)+(4-1)&~(4-1),v=t[M>>2]|0,t[r>>2]=M+4,M=0,T=w):(v=0,M=0,T=w)}t[Pn>>2]=T,Ze=(v|0)<0,v=Ze?0-v|0:v,L=Ze?L|8192:L}else{if(v=m8(Pn)|0,(v|0)<0){a=-1;break}T=t[Pn>>2]|0}do if((h[T>>0]|0)==46){if((h[T+1>>0]|0)!=42){t[Pn>>2]=T+1,w=m8(Pn)|0,T=t[Pn>>2]|0;break}if(b=T+2|0,w=(h[b>>0]|0)+-48|0,w>>>0<10&&(h[T+3>>0]|0)==36){t[s+(w<<2)>>2]=10,w=t[u+((h[b>>0]|0)+-48<<3)>>2]|0,T=T+4|0,t[Pn>>2]=T;break}if(M|0){a=-1;break e}ct?(Ze=(t[r>>2]|0)+(4-1)&~(4-1),w=t[Ze>>2]|0,t[r>>2]=Ze+4):w=0,t[Pn>>2]=b,T=b}else w=-1;while(0);for(Te=0;;){if(((h[T>>0]|0)+-65|0)>>>0>57){a=-1;break e}if(Ze=T+1|0,t[Pn>>2]=Ze,b=h[(h[T>>0]|0)+-65+(5178+(Te*58|0))>>0]|0,X=b&255,(X+-1|0)>>>0<8)Te=X,T=Ze;else break}if(!(b<<24>>24)){a=-1;break}Be=(ye|0)>-1;do if(b<<24>>24==19)if(Be){a=-1;break e}else Ye=49;else{if(Be){t[s+(ye<<2)>>2]=X,Be=u+(ye<<3)|0,ye=t[Be+4>>2]|0,Ye=gn,t[Ye>>2]=t[Be>>2],t[Ye+4>>2]=ye,Ye=49;break}if(!ct){a=0;break e}y8(gn,X,r)}while(0);if((Ye|0)==49&&(Ye=0,!ct)){v=0,n=Ze;continue}T=h[T>>0]|0,T=(Te|0)!=0&(T&15|0)==3?T&-33:T,Be=L&-65537,ye=(L&8192|0)==0?L:Be;t:do switch(T|0){case 110:switch((Te&255)<<24>>24){case 0:{t[t[gn>>2]>>2]=a,v=0,n=Ze;continue e}case 1:{t[t[gn>>2]>>2]=a,v=0,n=Ze;continue e}case 2:{v=t[gn>>2]|0,t[v>>2]=a,t[v+4>>2]=((a|0)<0)<<31>>31,v=0,n=Ze;continue e}case 3:{E[t[gn>>2]>>1]=a,v=0,n=Ze;continue e}case 4:{h[t[gn>>2]>>0]=a,v=0,n=Ze;continue e}case 6:{t[t[gn>>2]>>2]=a,v=0,n=Ze;continue e}case 7:{v=t[gn>>2]|0,t[v>>2]=a,t[v+4>>2]=((a|0)<0)<<31>>31,v=0,n=Ze;continue e}default:{v=0,n=Ze;continue e}}case 112:{T=120,w=w>>>0>8?w:8,n=ye|8,Ye=61;break}case 88:case 120:{n=ye,Ye=61;break}case 111:{T=gn,n=t[T>>2]|0,T=t[T+4>>2]|0,X=_L(n,T,ke)|0,Be=Ie-X|0,L=0,b=5642,w=(ye&8|0)==0|(w|0)>(Be|0)?w:Be+1|0,Be=ye,Ye=67;break}case 105:case 100:if(T=gn,n=t[T>>2]|0,T=t[T+4>>2]|0,(T|0)<0){n=m_(0,0,n|0,T|0)|0,T=ot,L=gn,t[L>>2]=n,t[L+4>>2]=T,L=1,b=5642,Ye=66;break t}else{L=(ye&2049|0)!=0&1,b=(ye&2048|0)==0?(ye&1|0)==0?5642:5644:5643,Ye=66;break t}case 117:{T=gn,L=0,b=5642,n=t[T>>2]|0,T=t[T+4>>2]|0,Ye=66;break}case 99:{h[Zt>>0]=t[gn>>2],n=Zt,L=0,b=5642,X=ke,T=1,w=Be;break}case 109:{T=EL(t[(bv()|0)>>2]|0)|0,Ye=71;break}case 115:{T=t[gn>>2]|0,T=T|0?T:5652,Ye=71;break}case 67:{t[_r>>2]=t[gn>>2],t[Br>>2]=0,t[gn>>2]=_r,X=-1,T=_r,Ye=75;break}case 83:{n=t[gn>>2]|0,w?(X=w,T=n,Ye=75):(_l(e,32,v,0,ye),n=0,Ye=84);break}case 65:case 71:case 70:case 69:case 97:case 103:case 102:case 101:{v=wL(e,+j[gn>>3],v,w,ye,T)|0,n=Ze;continue e}default:L=0,b=5642,X=ke,T=w,w=ye}while(0);t:do if((Ye|0)==61)ye=gn,Te=t[ye>>2]|0,ye=t[ye+4>>2]|0,X=gL(Te,ye,ke,T&32)|0,b=(n&8|0)==0|(Te|0)==0&(ye|0)==0,L=b?0:2,b=b?5642:5642+(T>>4)|0,Be=n,n=Te,T=ye,Ye=67;else if((Ye|0)==66)X=Bv(n,T,ke)|0,Be=ye,Ye=67;else if((Ye|0)==71)Ye=0,ye=DL(T,0,w)|0,Te=(ye|0)==0,n=T,L=0,b=5642,X=Te?T+w|0:ye,T=Te?w:ye-T|0,w=Be;else if((Ye|0)==75){for(Ye=0,b=T,n=0,w=0;L=t[b>>2]|0,!(!L||(w=g8(Pr,L)|0,(w|0)<0|w>>>0>(X-n|0)>>>0));)if(n=w+n|0,X>>>0>n>>>0)b=b+4|0;else break;if((w|0)<0){a=-1;break e}if(_l(e,32,v,n,ye),!n)n=0,Ye=84;else for(L=0;;){if(w=t[T>>2]|0,!w){Ye=84;break t}if(w=g8(Pr,w)|0,L=w+L|0,(L|0)>(n|0)){Ye=84;break t}if(Yo(e,Pr,w),L>>>0>=n>>>0){Ye=84;break}else T=T+4|0}}while(0);if((Ye|0)==67)Ye=0,T=(n|0)!=0|(T|0)!=0,ye=(w|0)!=0|T,T=((T^1)&1)+(Ie-X)|0,n=ye?X:ke,X=ke,T=ye?(w|0)>(T|0)?w:T:w,w=(w|0)>-1?Be&-65537:Be;else if((Ye|0)==84){Ye=0,_l(e,32,v,n,ye^8192),v=(v|0)>(n|0)?v:n,n=Ze;continue}Te=X-n|0,Be=(T|0)<(Te|0)?Te:T,ye=Be+L|0,v=(v|0)<(ye|0)?ye:v,_l(e,32,v,ye,w),Yo(e,b,L),_l(e,48,v,ye,w^65536),_l(e,48,Be,Te,0),Yo(e,n,Te),_l(e,32,v,ye,w^8192),n=Ze}e:do if((Ye|0)==87&&!e)if(!M)a=0;else{for(a=1;n=t[s+(a<<2)>>2]|0,!!n;)if(y8(u+(a<<3)|0,n,r),a=a+1|0,(a|0)>=10){a=1;break e}for(;;){if(t[s+(a<<2)>>2]|0){a=-1;break e}if(a=a+1|0,(a|0)>=10){a=1;break}}}while(0);return y=kn,a|0}function mL(e){return e=e|0,0}function yL(e){e=e|0}function Yo(e,n,r){e=e|0,n=n|0,r=r|0,t[e>>2]&32||kL(n,r,e)|0}function m8(e){e=e|0;var n=0,r=0,u=0;if(r=t[e>>2]|0,u=(h[r>>0]|0)+-48|0,u>>>0<10){n=0;do n=u+(n*10|0)|0,r=r+1|0,t[e>>2]=r,u=(h[r>>0]|0)+-48|0;while(u>>>0<10)}else n=0;return n|0}function y8(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;e:do if(n>>>0<=20)do switch(n|0){case 9:{u=(t[r>>2]|0)+(4-1)&~(4-1),n=t[u>>2]|0,t[r>>2]=u+4,t[e>>2]=n;break e}case 10:{u=(t[r>>2]|0)+(4-1)&~(4-1),n=t[u>>2]|0,t[r>>2]=u+4,u=e,t[u>>2]=n,t[u+4>>2]=((n|0)<0)<<31>>31;break e}case 11:{u=(t[r>>2]|0)+(4-1)&~(4-1),n=t[u>>2]|0,t[r>>2]=u+4,u=e,t[u>>2]=n,t[u+4>>2]=0;break e}case 12:{u=(t[r>>2]|0)+(8-1)&~(8-1),n=u,s=t[n>>2]|0,n=t[n+4>>2]|0,t[r>>2]=u+8,u=e,t[u>>2]=s,t[u+4>>2]=n;break e}case 13:{s=(t[r>>2]|0)+(4-1)&~(4-1),u=t[s>>2]|0,t[r>>2]=s+4,u=(u&65535)<<16>>16,s=e,t[s>>2]=u,t[s+4>>2]=((u|0)<0)<<31>>31;break e}case 14:{s=(t[r>>2]|0)+(4-1)&~(4-1),u=t[s>>2]|0,t[r>>2]=s+4,s=e,t[s>>2]=u&65535,t[s+4>>2]=0;break e}case 15:{s=(t[r>>2]|0)+(4-1)&~(4-1),u=t[s>>2]|0,t[r>>2]=s+4,u=(u&255)<<24>>24,s=e,t[s>>2]=u,t[s+4>>2]=((u|0)<0)<<31>>31;break e}case 16:{s=(t[r>>2]|0)+(4-1)&~(4-1),u=t[s>>2]|0,t[r>>2]=s+4,s=e,t[s>>2]=u&255,t[s+4>>2]=0;break e}case 17:{s=(t[r>>2]|0)+(8-1)&~(8-1),a=+j[s>>3],t[r>>2]=s+8,j[e>>3]=a;break e}case 18:{s=(t[r>>2]|0)+(8-1)&~(8-1),a=+j[s>>3],t[r>>2]=s+8,j[e>>3]=a;break e}default:break e}while(0);while(0)}function gL(e,n,r,u){if(e=e|0,n=n|0,r=r|0,u=u|0,!((e|0)==0&(n|0)==0))do r=r+-1|0,h[r>>0]=N[5694+(e&15)>>0]|0|u,e=y_(e|0,n|0,4)|0,n=ot;while(!((e|0)==0&(n|0)==0));return r|0}function _L(e,n,r){if(e=e|0,n=n|0,r=r|0,!((e|0)==0&(n|0)==0))do r=r+-1|0,h[r>>0]=e&7|48,e=y_(e|0,n|0,3)|0,n=ot;while(!((e|0)==0&(n|0)==0));return r|0}function Bv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;if(n>>>0>0|(n|0)==0&e>>>0>4294967295){for(;u=PE(e|0,n|0,10,0)|0,r=r+-1|0,h[r>>0]=u&255|48,u=e,e=FE(e|0,n|0,10,0)|0,n>>>0>9|(n|0)==9&u>>>0>4294967295;)n=ot;n=e}else n=e;if(n)for(;r=r+-1|0,h[r>>0]=(n>>>0)%10|0|48,!(n>>>0<10);)n=(n>>>0)/10|0;return r|0}function EL(e){return e=e|0,RL(e,t[(xL()|0)+188>>2]|0)|0}function DL(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;a=n&255,u=(r|0)!=0;e:do if(u&(e&3|0)!=0)for(s=n&255;;){if((h[e>>0]|0)==s<<24>>24){v=6;break e}if(e=e+1|0,r=r+-1|0,u=(r|0)!=0,!(u&(e&3|0)!=0)){v=5;break}}else v=5;while(0);(v|0)==5&&(u?v=6:r=0);e:do if((v|0)==6&&(s=n&255,(h[e>>0]|0)!=s<<24>>24)){u=lr(a,16843009)|0;t:do if(r>>>0>3){for(;a=t[e>>2]^u,!((a&-2139062144^-2139062144)&a+-16843009|0);)if(e=e+4|0,r=r+-4|0,r>>>0<=3){v=11;break t}}else v=11;while(0);if((v|0)==11&&!r){r=0;break}for(;;){if((h[e>>0]|0)==s<<24>>24)break e;if(e=e+1|0,r=r+-1|0,!r){r=0;break}}}while(0);return(r|0?e:0)|0}function _l(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0;if(v=y,y=y+256|0,a=v,(r|0)>(u|0)&(s&73728|0)==0){if(s=r-u|0,jv(a|0,n|0,(s>>>0<256?s:256)|0)|0,s>>>0>255){n=r-u|0;do Yo(e,a,256),s=s+-256|0;while(s>>>0>255);s=n&255}Yo(e,a,s)}y=v}function g8(e,n){return e=e|0,n=n|0,e?e=TL(e,n,0)|0:e=0,e|0}function wL(e,n,r,u,s,a){e=e|0,n=+n,r=r|0,u=u|0,s=s|0,a=a|0;var v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0,ye=0,Ze=0,Ye=0,ct=0,ke=0,Ie=0,Zt=0,Br=0,Pn=0,gn=0,_r=0,Pr=0,kn=0,uu=0;uu=y,y=y+560|0,T=uu+8|0,Ze=uu,kn=uu+524|0,Pr=kn,L=uu+512|0,t[Ze>>2]=0,_r=L+12|0,_8(n)|0,(ot|0)<0?(n=-n,Pn=1,Br=5659):(Pn=(s&2049|0)!=0&1,Br=(s&2048|0)==0?(s&1|0)==0?5660:5665:5662),_8(n)|0,gn=ot&2146435072;do if(gn>>>0<2146435072|(gn|0)==2146435072&0<0){if(Be=+SL(n,Ze)*2,v=Be!=0,v&&(t[Ze>>2]=(t[Ze>>2]|0)+-1),ct=a|32,(ct|0)==97){Te=a&32,X=(Te|0)==0?Br:Br+9|0,b=Pn|2,v=12-u|0;do if(u>>>0>11|(v|0)==0)n=Be;else{n=8;do v=v+-1|0,n=n*16;while((v|0)!=0);if((h[X>>0]|0)==45){n=-(n+(-Be-n));break}else{n=Be+n-n;break}}while(0);w=t[Ze>>2]|0,v=(w|0)<0?0-w|0:w,v=Bv(v,((v|0)<0)<<31>>31,_r)|0,(v|0)==(_r|0)&&(v=L+11|0,h[v>>0]=48),h[v+-1>>0]=(w>>31&2)+43,M=v+-2|0,h[M>>0]=a+15,L=(u|0)<1,T=(s&8|0)==0,v=kn;do gn=~~n,w=v+1|0,h[v>>0]=N[5694+gn>>0]|Te,n=(n-+(gn|0))*16,(w-Pr|0)==1&&!(T&(L&n==0))?(h[w>>0]=46,v=v+2|0):v=w;while(n!=0);gn=v-Pr|0,Pr=_r-M|0,_r=(u|0)!=0&(gn+-2|0)<(u|0)?u+2|0:gn,v=Pr+b+_r|0,_l(e,32,r,v,s),Yo(e,X,b),_l(e,48,r,v,s^65536),Yo(e,kn,gn),_l(e,48,_r-gn|0,0,0),Yo(e,M,Pr),_l(e,32,r,v,s^8192);break}w=(u|0)<0?6:u,v?(v=(t[Ze>>2]|0)+-28|0,t[Ze>>2]=v,n=Be*268435456):(n=Be,v=t[Ze>>2]|0),gn=(v|0)<0?T:T+288|0,T=gn;do Ie=~~n>>>0,t[T>>2]=Ie,T=T+4|0,n=(n-+(Ie>>>0))*1e9;while(n!=0);if((v|0)>0)for(L=gn,b=T;;){if(M=(v|0)<29?v:29,v=b+-4|0,v>>>0>=L>>>0){T=0;do ke=C8(t[v>>2]|0,0,M|0)|0,ke=LE(ke|0,ot|0,T|0,0)|0,Ie=ot,Ye=PE(ke|0,Ie|0,1e9,0)|0,t[v>>2]=Ye,T=FE(ke|0,Ie|0,1e9,0)|0,v=v+-4|0;while(v>>>0>=L>>>0);T&&(L=L+-4|0,t[L>>2]=T)}for(T=b;!(T>>>0<=L>>>0);)if(v=T+-4|0,!(t[v>>2]|0))T=v;else break;if(v=(t[Ze>>2]|0)-M|0,t[Ze>>2]=v,(v|0)>0)b=T;else break}else L=gn;if((v|0)<0){u=((w+25|0)/9|0)+1|0,ye=(ct|0)==102;do{if(Te=0-v|0,Te=(Te|0)<9?Te:9,L>>>0>>0){M=(1<>>Te,X=0,v=L;do Ie=t[v>>2]|0,t[v>>2]=(Ie>>>Te)+X,X=lr(Ie&M,b)|0,v=v+4|0;while(v>>>0>>0);v=(t[L>>2]|0)==0?L+4|0:L,X?(t[T>>2]=X,L=v,v=T+4|0):(L=v,v=T)}else L=(t[L>>2]|0)==0?L+4|0:L,v=T;T=ye?gn:L,T=(v-T>>2|0)>(u|0)?T+(u<<2)|0:v,v=(t[Ze>>2]|0)+Te|0,t[Ze>>2]=v}while((v|0)<0);v=L,u=T}else v=L,u=T;if(Ie=gn,v>>>0>>0){if(T=(Ie-v>>2)*9|0,M=t[v>>2]|0,M>>>0>=10){L=10;do L=L*10|0,T=T+1|0;while(M>>>0>=L>>>0)}}else T=0;if(ye=(ct|0)==103,Ye=(w|0)!=0,L=w-((ct|0)!=102?T:0)+((Ye&ye)<<31>>31)|0,(L|0)<(((u-Ie>>2)*9|0)+-9|0)){if(L=L+9216|0,Te=gn+4+(((L|0)/9|0)+-1024<<2)|0,L=((L|0)%9|0)+1|0,(L|0)<9){M=10;do M=M*10|0,L=L+1|0;while((L|0)!=9)}else M=10;if(b=t[Te>>2]|0,X=(b>>>0)%(M>>>0)|0,L=(Te+4|0)==(u|0),L&(X|0)==0)L=Te;else if(Be=(((b>>>0)/(M>>>0)|0)&1|0)==0?9007199254740992:9007199254740994,ke=(M|0)/2|0,n=X>>>0>>0?.5:L&(X|0)==(ke|0)?1:1.5,Pn&&(ke=(h[Br>>0]|0)==45,n=ke?-n:n,Be=ke?-Be:Be),L=b-X|0,t[Te>>2]=L,Be+n!=Be){if(ke=L+M|0,t[Te>>2]=ke,ke>>>0>999999999)for(T=Te;L=T+-4|0,t[T>>2]=0,L>>>0>>0&&(v=v+-4|0,t[v>>2]=0),ke=(t[L>>2]|0)+1|0,t[L>>2]=ke,ke>>>0>999999999;)T=L;else L=Te;if(T=(Ie-v>>2)*9|0,b=t[v>>2]|0,b>>>0>=10){M=10;do M=M*10|0,T=T+1|0;while(b>>>0>=M>>>0)}}else L=Te;L=L+4|0,L=u>>>0>L>>>0?L:u,ke=v}else L=u,ke=v;for(ct=L;;){if(ct>>>0<=ke>>>0){Ze=0;break}if(v=ct+-4|0,!(t[v>>2]|0))ct=v;else{Ze=1;break}}u=0-T|0;do if(ye)if(v=((Ye^1)&1)+w|0,(v|0)>(T|0)&(T|0)>-5?(M=a+-1|0,w=v+-1-T|0):(M=a+-2|0,w=v+-1|0),v=s&8,v)Te=v;else{if(Ze&&(Zt=t[ct+-4>>2]|0,(Zt|0)!=0))if((Zt>>>0)%10|0)L=0;else{L=0,v=10;do v=v*10|0,L=L+1|0;while(!((Zt>>>0)%(v>>>0)|0|0))}else L=9;if(v=((ct-Ie>>2)*9|0)+-9|0,(M|32|0)==102){Te=v-L|0,Te=(Te|0)>0?Te:0,w=(w|0)<(Te|0)?w:Te,Te=0;break}else{Te=v+T-L|0,Te=(Te|0)>0?Te:0,w=(w|0)<(Te|0)?w:Te,Te=0;break}}else M=a,Te=s&8;while(0);if(ye=w|Te,b=(ye|0)!=0&1,X=(M|32|0)==102,X)Ye=0,v=(T|0)>0?T:0;else{if(v=(T|0)<0?u:T,v=Bv(v,((v|0)<0)<<31>>31,_r)|0,L=_r,(L-v|0)<2)do v=v+-1|0,h[v>>0]=48;while((L-v|0)<2);h[v+-1>>0]=(T>>31&2)+43,v=v+-2|0,h[v>>0]=M,Ye=v,v=L-v|0}if(v=Pn+1+w+b+v|0,_l(e,32,r,v,s),Yo(e,Br,Pn),_l(e,48,r,v,s^65536),X){M=ke>>>0>gn>>>0?gn:ke,Te=kn+9|0,b=Te,X=kn+8|0,L=M;do{if(T=Bv(t[L>>2]|0,0,Te)|0,(L|0)==(M|0))(T|0)==(Te|0)&&(h[X>>0]=48,T=X);else if(T>>>0>kn>>>0){jv(kn|0,48,T-Pr|0)|0;do T=T+-1|0;while(T>>>0>kn>>>0)}Yo(e,T,b-T|0),L=L+4|0}while(L>>>0<=gn>>>0);if(ye|0&&Yo(e,5710,1),L>>>0>>0&(w|0)>0)for(;;){if(T=Bv(t[L>>2]|0,0,Te)|0,T>>>0>kn>>>0){jv(kn|0,48,T-Pr|0)|0;do T=T+-1|0;while(T>>>0>kn>>>0)}if(Yo(e,T,(w|0)<9?w:9),L=L+4|0,T=w+-9|0,L>>>0>>0&(w|0)>9)w=T;else{w=T;break}}_l(e,48,w+9|0,9,0)}else{if(ye=Ze?ct:ke+4|0,(w|0)>-1){Ze=kn+9|0,Te=(Te|0)==0,u=Ze,b=0-Pr|0,X=kn+8|0,M=ke;do{T=Bv(t[M>>2]|0,0,Ze)|0,(T|0)==(Ze|0)&&(h[X>>0]=48,T=X);do if((M|0)==(ke|0)){if(L=T+1|0,Yo(e,T,1),Te&(w|0)<1){T=L;break}Yo(e,5710,1),T=L}else{if(T>>>0<=kn>>>0)break;jv(kn|0,48,T+b|0)|0;do T=T+-1|0;while(T>>>0>kn>>>0)}while(0);Pr=u-T|0,Yo(e,T,(w|0)>(Pr|0)?Pr:w),w=w-Pr|0,M=M+4|0}while(M>>>0>>0&(w|0)>-1)}_l(e,48,w+18|0,18,0),Yo(e,Ye,_r-Ye|0)}_l(e,32,r,v,s^8192)}else kn=(a&32|0)!=0,v=Pn+3|0,_l(e,32,r,v,s&-65537),Yo(e,Br,Pn),Yo(e,n!=n|!1?kn?5686:5690:kn?5678:5682,3),_l(e,32,r,v,s^8192);while(0);return y=uu,((v|0)<(r|0)?r:v)|0}function _8(e){e=+e;var n=0;return j[V>>3]=e,n=t[V>>2]|0,ot=t[V+4>>2]|0,n|0}function SL(e,n){return e=+e,n=n|0,+ +E8(e,n)}function E8(e,n){e=+e,n=n|0;var r=0,u=0,s=0;switch(j[V>>3]=e,r=t[V>>2]|0,u=t[V+4>>2]|0,s=y_(r|0,u|0,52)|0,s&2047){case 0:{e!=0?(e=+E8(e*18446744073709552e3,n),r=(t[n>>2]|0)+-64|0):r=0,t[n>>2]=r;break}case 2047:break;default:t[n>>2]=(s&2047)+-1022,t[V>>2]=r,t[V+4>>2]=u&-2146435073|1071644672,e=+j[V>>3]}return+e}function TL(e,n,r){e=e|0,n=n|0,r=r|0;do if(e){if(n>>>0<128){h[e>>0]=n,e=1;break}if(!(t[t[(CL()|0)+188>>2]>>2]|0))if((n&-128|0)==57216){h[e>>0]=n,e=1;break}else{t[(bv()|0)>>2]=84,e=-1;break}if(n>>>0<2048){h[e>>0]=n>>>6|192,h[e+1>>0]=n&63|128,e=2;break}if(n>>>0<55296|(n&-8192|0)==57344){h[e>>0]=n>>>12|224,h[e+1>>0]=n>>>6&63|128,h[e+2>>0]=n&63|128,e=3;break}if((n+-65536|0)>>>0<1048576){h[e>>0]=n>>>18|240,h[e+1>>0]=n>>>12&63|128,h[e+2>>0]=n>>>6&63|128,h[e+3>>0]=n&63|128,e=4;break}else{t[(bv()|0)>>2]=84,e=-1;break}}else e=1;while(0);return e|0}function CL(){return ME()|0}function xL(){return ME()|0}function RL(e,n){e=e|0,n=n|0;var r=0,u=0;for(u=0;;){if((N[5712+u>>0]|0)==(e|0)){e=2;break}if(r=u+1|0,(r|0)==87){r=5800,u=87,e=5;break}else u=r}if((e|0)==2&&(u?(r=5800,e=5):r=5800),(e|0)==5)for(;;){do e=r,r=r+1|0;while((h[e>>0]|0)!=0);if(u=u+-1|0,u)e=5;else break}return AL(r,t[n+20>>2]|0)|0}function AL(e,n){return e=e|0,n=n|0,OL(e,n)|0}function OL(e,n){return e=e|0,n=n|0,n?n=ML(t[n>>2]|0,t[n+4>>2]|0,e)|0:n=0,(n|0?n:e)|0}function ML(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0;X=(t[e>>2]|0)+1794895138|0,a=Yp(t[e+8>>2]|0,X)|0,u=Yp(t[e+12>>2]|0,X)|0,s=Yp(t[e+16>>2]|0,X)|0;e:do if(a>>>0>>2>>>0&&(b=n-(a<<2)|0,u>>>0>>0&s>>>0>>0)&&((s|u)&3|0)==0){for(b=u>>>2,M=s>>>2,L=0;;){if(w=a>>>1,T=L+w|0,v=T<<1,s=v+b|0,u=Yp(t[e+(s<<2)>>2]|0,X)|0,s=Yp(t[e+(s+1<<2)>>2]|0,X)|0,!(s>>>0>>0&u>>>0<(n-s|0)>>>0)){u=0;break e}if(h[e+(s+u)>>0]|0){u=0;break e}if(u=h8(r,e+s|0)|0,!u)break;if(u=(u|0)<0,(a|0)==1){u=0;break e}else L=u?L:T,a=u?w:a-w|0}u=v+M|0,s=Yp(t[e+(u<<2)>>2]|0,X)|0,u=Yp(t[e+(u+1<<2)>>2]|0,X)|0,u>>>0>>0&s>>>0<(n-u|0)>>>0?u=(h[e+(u+s)>>0]|0)==0?e+u|0:0:u=0}else u=0;while(0);return u|0}function Yp(e,n){e=e|0,n=n|0;var r=0;return r=A8(e|0)|0,((n|0)==0?e:r)|0}function kL(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0,w=0;u=r+16|0,s=t[u>>2]|0,s?a=5:NL(r)|0?u=0:(s=t[u>>2]|0,a=5);e:do if((a|0)==5){if(w=r+20|0,v=t[w>>2]|0,u=v,(s-v|0)>>>0>>0){u=__[t[r+36>>2]&7](r,e,n)|0;break}t:do if((h[r+75>>0]|0)>-1){for(v=n;;){if(!v){a=0,s=e;break t}if(s=v+-1|0,(h[e+s>>0]|0)==10)break;v=s}if(u=__[t[r+36>>2]&7](r,e,v)|0,u>>>0>>0)break e;a=v,s=e+v|0,n=n-v|0,u=t[w>>2]|0}else a=0,s=e;while(0);gr(u|0,s|0,n|0)|0,t[w>>2]=(t[w>>2]|0)+n,u=a+n|0}while(0);return u|0}function NL(e){e=e|0;var n=0,r=0;return n=e+74|0,r=h[n>>0]|0,h[n>>0]=r+255|r,n=t[e>>2]|0,n&8?(t[e>>2]=n|32,e=-1):(t[e+8>>2]=0,t[e+4>>2]=0,r=t[e+44>>2]|0,t[e+28>>2]=r,t[e+20>>2]=r,t[e+16>>2]=r+(t[e+48>>2]|0),e=0),e|0}function xu(e,n){e=S(e),n=S(n);var r=0,u=0;r=D8(e)|0;do if((r&2147483647)>>>0<=2139095040){if(u=D8(n)|0,(u&2147483647)>>>0<=2139095040)if((u^r|0)<0){e=(r|0)<0?n:e;break}else{e=e>2]=e,t[V>>2]|0|0}function Kp(e,n){e=S(e),n=S(n);var r=0,u=0;r=w8(e)|0;do if((r&2147483647)>>>0<=2139095040){if(u=w8(n)|0,(u&2147483647)>>>0<=2139095040)if((u^r|0)<0){e=(r|0)<0?e:n;break}else{e=e>2]=e,t[V>>2]|0|0}function NE(e,n){e=S(e),n=S(n);var r=0,u=0,s=0,a=0,v=0,w=0,T=0,L=0;a=(x[V>>2]=e,t[V>>2]|0),w=(x[V>>2]=n,t[V>>2]|0),r=a>>>23&255,v=w>>>23&255,T=a&-2147483648,s=w<<1;e:do if((s|0)!=0&&!((r|0)==255|((LL(n)|0)&2147483647)>>>0>2139095040)){if(u=a<<1,u>>>0<=s>>>0)return n=S(e*S(0)),S((u|0)==(s|0)?n:e);if(r)u=a&8388607|8388608;else{if(r=a<<9,(r|0)>-1){u=r,r=0;do r=r+-1|0,u=u<<1;while((u|0)>-1)}else r=0;u=a<<1-r}if(v)w=w&8388607|8388608;else{if(a=w<<9,(a|0)>-1){s=0;do s=s+-1|0,a=a<<1;while((a|0)>-1)}else s=0;v=s,w=w<<1-s}s=u-w|0,a=(s|0)>-1;t:do if((r|0)>(v|0)){for(;;){if(a)if(s)u=s;else break;if(u=u<<1,r=r+-1|0,s=u-w|0,a=(s|0)>-1,(r|0)<=(v|0))break t}n=S(e*S(0));break e}while(0);if(a)if(s)u=s;else{n=S(e*S(0));break}if(u>>>0<8388608)do u=u<<1,r=r+-1|0;while(u>>>0<8388608);(r|0)>0?r=u+-8388608|r<<23:r=u>>>(1-r|0),n=(t[V>>2]=r|T,S(x[V>>2]))}else L=3;while(0);return(L|0)==3&&(n=S(e*n),n=S(n/n)),S(n)}function LL(e){return e=S(e),x[V>>2]=e,t[V>>2]|0|0}function FL(e,n){return e=e|0,n=n|0,v8(t[582]|0,e,n)|0}function di(e){e=e|0,$n()}function Uv(e){e=e|0}function PL(e,n){return e=e|0,n=n|0,0}function IL(e){return e=e|0,(S8(e+4|0)|0)==-1?(P1[t[(t[e>>2]|0)+8>>2]&127](e),e=1):e=0,e|0}function S8(e){e=e|0;var n=0;return n=t[e>>2]|0,t[e>>2]=n+-1,n+-1|0}function $d(e){e=e|0,IL(e)|0&&bL(e)}function bL(e){e=e|0;var n=0;n=e+8|0,(t[n>>2]|0)!=0&&(S8(n)|0)!=-1||P1[t[(t[e>>2]|0)+16>>2]&127](e)}function pn(e){e=e|0;var n=0;for(n=(e|0)==0?1:e;e=p_(n)|0,!(e|0);){if(e=UL()|0,!e){e=0;break}B8[e&0]()}return e|0}function T8(e){return e=e|0,pn(e)|0}function Et(e){e=e|0,h_(e)}function BL(e){e=e|0,(h[e+11>>0]|0)<0&&Et(t[e>>2]|0)}function UL(){var e=0;return e=t[2923]|0,t[2923]=e+0,e|0}function jL(){}function m_(e,n,r,u){return e=e|0,n=n|0,r=r|0,u=u|0,u=n-u-(r>>>0>e>>>0|0)>>>0,ot=u,e-r>>>0|0|0}function LE(e,n,r,u){return e=e|0,n=n|0,r=r|0,u=u|0,r=e+r>>>0,ot=n+u+(r>>>0>>0|0)>>>0,r|0|0}function jv(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0,v=0;if(a=e+r|0,n=n&255,(r|0)>=67){for(;e&3;)h[e>>0]=n,e=e+1|0;for(u=a&-4|0,s=u-64|0,v=n|n<<8|n<<16|n<<24;(e|0)<=(s|0);)t[e>>2]=v,t[e+4>>2]=v,t[e+8>>2]=v,t[e+12>>2]=v,t[e+16>>2]=v,t[e+20>>2]=v,t[e+24>>2]=v,t[e+28>>2]=v,t[e+32>>2]=v,t[e+36>>2]=v,t[e+40>>2]=v,t[e+44>>2]=v,t[e+48>>2]=v,t[e+52>>2]=v,t[e+56>>2]=v,t[e+60>>2]=v,e=e+64|0;for(;(e|0)<(u|0);)t[e>>2]=v,e=e+4|0}for(;(e|0)<(a|0);)h[e>>0]=n,e=e+1|0;return a-r|0}function C8(e,n,r){return e=e|0,n=n|0,r=r|0,(r|0)<32?(ot=n<>>32-r,e<>>r,e>>>r|(n&(1<>>r-32|0)}function gr(e,n,r){e=e|0,n=n|0,r=r|0;var u=0,s=0,a=0;if((r|0)>=8192)return li(e|0,n|0,r|0)|0;if(a=e|0,s=e+r|0,(e&3)==(n&3)){for(;e&3;){if(!r)return a|0;h[e>>0]=h[n>>0]|0,e=e+1|0,n=n+1|0,r=r-1|0}for(r=s&-4|0,u=r-64|0;(e|0)<=(u|0);)t[e>>2]=t[n>>2],t[e+4>>2]=t[n+4>>2],t[e+8>>2]=t[n+8>>2],t[e+12>>2]=t[n+12>>2],t[e+16>>2]=t[n+16>>2],t[e+20>>2]=t[n+20>>2],t[e+24>>2]=t[n+24>>2],t[e+28>>2]=t[n+28>>2],t[e+32>>2]=t[n+32>>2],t[e+36>>2]=t[n+36>>2],t[e+40>>2]=t[n+40>>2],t[e+44>>2]=t[n+44>>2],t[e+48>>2]=t[n+48>>2],t[e+52>>2]=t[n+52>>2],t[e+56>>2]=t[n+56>>2],t[e+60>>2]=t[n+60>>2],e=e+64|0,n=n+64|0;for(;(e|0)<(r|0);)t[e>>2]=t[n>>2],e=e+4|0,n=n+4|0}else for(r=s-4|0;(e|0)<(r|0);)h[e>>0]=h[n>>0]|0,h[e+1>>0]=h[n+1>>0]|0,h[e+2>>0]=h[n+2>>0]|0,h[e+3>>0]=h[n+3>>0]|0,e=e+4|0,n=n+4|0;for(;(e|0)<(s|0);)h[e>>0]=h[n>>0]|0,e=e+1|0,n=n+1|0;return a|0}function x8(e){e=e|0;var n=0;return n=h[De+(e&255)>>0]|0,(n|0)<8?n|0:(n=h[De+(e>>8&255)>>0]|0,(n|0)<8?n+8|0:(n=h[De+(e>>16&255)>>0]|0,(n|0)<8?n+16|0:(h[De+(e>>>24)>>0]|0)+24|0))}function R8(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0;var a=0,v=0,w=0,T=0,L=0,M=0,b=0,X=0,Be=0,Te=0;if(M=e,T=n,L=T,v=r,X=u,w=X,!L)return a=(s|0)!=0,w?a?(t[s>>2]=e|0,t[s+4>>2]=n&0,X=0,s=0,ot=X,s|0):(X=0,s=0,ot=X,s|0):(a&&(t[s>>2]=(M>>>0)%(v>>>0),t[s+4>>2]=0),X=0,s=(M>>>0)/(v>>>0)>>>0,ot=X,s|0);a=(w|0)==0;do if(v){if(!a){if(a=(Er(w|0)|0)-(Er(L|0)|0)|0,a>>>0<=31){b=a+1|0,w=31-a|0,n=a-31>>31,v=b,e=M>>>(b>>>0)&n|L<>>(b>>>0)&n,a=0,w=M<>2]=e|0,t[s+4>>2]=T|n&0,X=0,s=0,ot=X,s|0):(X=0,s=0,ot=X,s|0)}if(a=v-1|0,a&v|0){w=(Er(v|0)|0)+33-(Er(L|0)|0)|0,Te=64-w|0,b=32-w|0,T=b>>31,Be=w-32|0,n=Be>>31,v=w,e=b-1>>31&L>>>(Be>>>0)|(L<>>(w>>>0))&n,n=n&L>>>(w>>>0),a=M<>>(Be>>>0))&T|M<>31;break}return s|0&&(t[s>>2]=a&M,t[s+4>>2]=0),(v|0)==1?(Be=T|n&0,Te=e|0|0,ot=Be,Te|0):(Te=x8(v|0)|0,Be=L>>>(Te>>>0)|0,Te=L<<32-Te|M>>>(Te>>>0)|0,ot=Be,Te|0)}else{if(a)return s|0&&(t[s>>2]=(L>>>0)%(v>>>0),t[s+4>>2]=0),Be=0,Te=(L>>>0)/(v>>>0)>>>0,ot=Be,Te|0;if(!M)return s|0&&(t[s>>2]=0,t[s+4>>2]=(L>>>0)%(w>>>0)),Be=0,Te=(L>>>0)/(w>>>0)>>>0,ot=Be,Te|0;if(a=w-1|0,!(a&w))return s|0&&(t[s>>2]=e|0,t[s+4>>2]=a&L|n&0),Be=0,Te=L>>>((x8(w|0)|0)>>>0),ot=Be,Te|0;if(a=(Er(w|0)|0)-(Er(L|0)|0)|0,a>>>0<=30){n=a+1|0,w=31-a|0,v=n,e=L<>>(n>>>0),n=L>>>(n>>>0),a=0,w=M<>2]=e|0,t[s+4>>2]=T|n&0,Be=0,Te=0,ot=Be,Te|0):(Be=0,Te=0,ot=Be,Te|0)}while(0);if(!v)L=w,T=0,w=0;else{b=r|0|0,M=X|u&0,L=LE(b|0,M|0,-1,-1)|0,r=ot,T=w,w=0;do u=T,T=a>>>31|T<<1,a=w|a<<1,u=e<<1|u>>>31|0,X=e>>>31|n<<1|0,m_(L|0,r|0,u|0,X|0)|0,Te=ot,Be=Te>>31|((Te|0)<0?-1:0)<<1,w=Be&1,e=m_(u|0,X|0,Be&b|0,(((Te|0)<0?-1:0)>>31|((Te|0)<0?-1:0)<<1)&M|0)|0,n=ot,v=v-1|0;while((v|0)!=0);L=T,T=0}return v=0,s|0&&(t[s>>2]=e,t[s+4>>2]=n),Be=(a|0)>>>31|(L|v)<<1|(v<<1|a>>>31)&0|T,Te=(a<<1|0>>>31)&-2|w,ot=Be,Te|0}function FE(e,n,r,u){return e=e|0,n=n|0,r=r|0,u=u|0,R8(e,n,r,u,0)|0}function e2(e){e=e|0;var n=0,r=0;return r=e+15&-16|0,n=t[q>>2]|0,e=n+r|0,(r|0)>0&(e|0)<(n|0)|(e|0)<0?(fr()|0,Ql(12),-1):(t[q>>2]=e,(e|0)>(jr()|0)&&(vr()|0)==0?(t[q>>2]=n,Ql(12),-1):n|0)}function ky(e,n,r){e=e|0,n=n|0,r=r|0;var u=0;if((n|0)<(e|0)&(e|0)<(n+r|0)){for(u=e,n=n+r|0,e=e+r|0;(r|0)>0;)e=e-1|0,n=n-1|0,r=r-1|0,h[e>>0]=h[n>>0]|0;e=u}else gr(e,n,r)|0;return e|0}function PE(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0;var s=0,a=0;return a=y,y=y+16|0,s=a|0,R8(e,n,r,u,s)|0,y=a,ot=t[s+4>>2]|0,t[s>>2]|0|0}function A8(e){return e=e|0,(e&255)<<24|(e>>8&255)<<16|(e>>16&255)<<8|e>>>24|0}function zL(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,O8[e&1](n|0,r|0,u|0,s|0,a|0)}function HL(e,n,r){e=e|0,n=n|0,r=S(r),M8[e&1](n|0,S(r))}function qL(e,n,r){e=e|0,n=n|0,r=+r,k8[e&31](n|0,+r)}function WL(e,n,r,u){return e=e|0,n=n|0,r=S(r),u=S(u),S(N8[e&0](n|0,S(r),S(u)))}function VL(e,n){e=e|0,n=n|0,P1[e&127](n|0)}function YL(e,n,r){e=e|0,n=n|0,r=r|0,I1[e&31](n|0,r|0)}function KL(e,n){return e=e|0,n=n|0,Qp[e&31](n|0)|0}function XL(e,n,r,u,s){e=e|0,n=n|0,r=+r,u=+u,s=s|0,L8[e&1](n|0,+r,+u,s|0)}function QL(e,n,r,u){e=e|0,n=n|0,r=+r,u=+u,MF[e&1](n|0,+r,+u)}function JL(e,n,r,u){return e=e|0,n=n|0,r=r|0,u=u|0,__[e&7](n|0,r|0,u|0)|0}function ZL(e,n,r,u){return e=e|0,n=n|0,r=r|0,u=u|0,+kF[e&1](n|0,r|0,u|0)}function $L(e,n){return e=e|0,n=n|0,+F8[e&15](n|0)}function eF(e,n,r){return e=e|0,n=n|0,r=+r,NF[e&1](n|0,+r)|0}function tF(e,n,r){return e=e|0,n=n|0,r=r|0,bE[e&15](n|0,r|0)|0}function nF(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=+u,s=+s,a=a|0,LF[e&1](n|0,r|0,+u,+s,a|0)}function rF(e,n,r,u,s,a,v){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,v=v|0,FF[e&1](n|0,r|0,u|0,s|0,a|0,v|0)}function iF(e,n,r){return e=e|0,n=n|0,r=r|0,+P8[e&7](n|0,r|0)}function uF(e){return e=e|0,E_[e&7]()|0}function oF(e,n,r,u,s,a){return e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,I8[e&1](n|0,r|0,u|0,s|0,a|0)|0}function lF(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=+s,PF[e&1](n|0,r|0,u|0,+s)}function sF(e,n,r,u,s,a,v){e=e|0,n=n|0,r=r|0,u=S(u),s=s|0,a=S(a),v=v|0,b8[e&1](n|0,r|0,S(u),s|0,S(a),v|0)}function aF(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,Fy[e&15](n|0,r|0,u|0)}function fF(e){e=e|0,B8[e&0]()}function cF(e,n,r,u){e=e|0,n=n|0,r=r|0,u=+u,U8[e&15](n|0,r|0,+u)}function dF(e,n,r){return e=e|0,n=+n,r=+r,IF[e&1](+n,+r)|0}function pF(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,BE[e&15](n|0,r|0,u|0,s|0)}function hF(e,n,r,u,s){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,zt(0)}function vF(e,n){e=e|0,n=S(n),zt(1)}function $s(e,n){e=e|0,n=+n,zt(2)}function mF(e,n,r){return e=e|0,n=S(n),r=S(r),zt(3),Ct}function Zn(e){e=e|0,zt(4)}function Ny(e,n){e=e|0,n=n|0,zt(5)}function Na(e){return e=e|0,zt(6),0}function yF(e,n,r,u){e=e|0,n=+n,r=+r,u=u|0,zt(7)}function gF(e,n,r){e=e|0,n=+n,r=+r,zt(8)}function _F(e,n,r){return e=e|0,n=n|0,r=r|0,zt(9),0}function EF(e,n,r){return e=e|0,n=n|0,r=r|0,zt(10),0}function Xp(e){return e=e|0,zt(11),0}function DF(e,n){return e=e|0,n=+n,zt(12),0}function Ly(e,n){return e=e|0,n=n|0,zt(13),0}function wF(e,n,r,u,s){e=e|0,n=n|0,r=+r,u=+u,s=s|0,zt(14)}function SF(e,n,r,u,s,a){e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,a=a|0,zt(15)}function IE(e,n){return e=e|0,n=n|0,zt(16),0}function TF(){return zt(17),0}function CF(e,n,r,u,s){return e=e|0,n=n|0,r=r|0,u=u|0,s=s|0,zt(18),0}function xF(e,n,r,u){e=e|0,n=n|0,r=r|0,u=+u,zt(19)}function RF(e,n,r,u,s,a){e=e|0,n=n|0,r=S(r),u=u|0,s=S(s),a=a|0,zt(20)}function g_(e,n,r){e=e|0,n=n|0,r=r|0,zt(21)}function AF(){zt(22)}function zv(e,n,r){e=e|0,n=n|0,r=+r,zt(23)}function OF(e,n){return e=+e,n=+n,zt(24),0}function Hv(e,n,r,u){e=e|0,n=n|0,r=r|0,u=u|0,zt(25)}var O8=[hF,TO],M8=[vF,t0],k8=[$s,ca,ws,Ss,ts,Ho,Ef,ol,qa,n0,Df,Wc,dc,Ol,Ts,da,ud,pa,pc,$s,$s,$s,$s,$s,$s,$s,$s,$s,$s,$s,$s,$s],N8=[mF],P1=[Zn,Uv,cn,is,Do,Uf,M1,jl,$A,e7,t7,cO,dO,pO,LN,FN,PN,Fe,fc,Ua,Vu,j0,yh,Sf,r1,Lf,Ea,kh,ym,g1,_1,Zh,hp,Ld,jm,C1,Ac,Jm,ey,xv,Mv,on,P4,G4,n_,Lt,Cu,e0,p9,O9,K9,dR,RR,KR,iA,lA,TA,RA,WA,r7,o7,S7,z7,gd,wM,$M,hk,Ok,Jk,dN,SN,xN,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn,Zn],I1=[Ny,_2,nd,qc,Rl,ul,E2,qs,Al,ja,za,Ha,Ml,je,st,$t,Wn,oi,ur,Wa,w2,_h,X4,eE,mR,CM,X7,$w,Ny,Ny,Ny,Ny],Qp=[Na,fL,_f,g,Z,de,yt,Rt,Nt,xr,cu,z0,Va,od,Xc,Ms,kR,x7,OM,Oa,Na,Na,Na,Na,Na,Na,Na,Na,Na,Na,Na,Na],L8=[yF,C2],MF=[gF,YA],__=[_F,p8,cL,hL,Wh,vv,y9,Lk],kF=[EF,fv],F8=[Xp,i0,Ge,ai,gh,al,ha,x2,R2,hc,Xp,Xp,Xp,Xp,Xp,Xp],NF=[DF,tA],bE=[Ly,PL,D2,dl,H2,xm,fp,xp,ty,kr,jo,gk,Ly,Ly,Ly,Ly],LF=[wF,xh],FF=[SF,tN],P8=[IE,Qi,A2,dd,Qc,ml,IE,IE],E_=[TF,Jc,io,E0,cA,kA,f7,MN],I8=[CF,ui],PF=[xF,vy],b8=[RF,ld],Fy=[g_,A,r0,Vr,Tu,m1,Nd,ar,_y,mo,YO,rk,mN,g_,g_,g_],B8=[AF],U8=[zv,rd,yo,id,zo,Vc,Wi,_,Bp,L9,JR,zv,zv,zv,zv,zv],IF=[OF,JA],BE=[Hv,Ep,Lc,Z9,jR,yA,bA,y7,G7,PM,zN,Hv,Hv,Hv,Hv,Hv];return{_llvm_bswap_i32:A8,dynCall_idd:dF,dynCall_i:uF,_i64Subtract:m_,___udivdi3:FE,dynCall_vif:HL,setThrew:vs,dynCall_viii:aF,_bitshift64Lshr:y_,_bitshift64Shl:C8,dynCall_vi:VL,dynCall_viiddi:nF,dynCall_diii:ZL,dynCall_iii:tF,_memset:jv,_sbrk:e2,_memcpy:gr,__GLOBAL__sub_I_Yoga_cpp:ru,dynCall_vii:YL,___uremdi3:PE,dynCall_vid:qL,stackAlloc:co,_nbind_init:ZN,getTempRet0:Q,dynCall_di:$L,dynCall_iid:eF,setTempRet0:b0,_i64Add:LE,dynCall_fiff:WL,dynCall_iiii:JL,_emscripten_get_global_libc:aL,dynCall_viid:cF,dynCall_viiid:lF,dynCall_viififi:sF,dynCall_ii:KL,__GLOBAL__sub_I_Binding_cc:hM,dynCall_viiii:pF,dynCall_iiiiii:oF,stackSave:nl,dynCall_viiiii:zL,__GLOBAL__sub_I_nbind_cc:Ws,dynCall_vidd:QL,_free:h_,runPostSets:jL,dynCall_viiiiii:rF,establishStackSpace:Uu,_memmove:ky,stackRestore:Jl,_malloc:p_,__GLOBAL__sub_I_common_cc:F7,dynCall_viddi:XL,dynCall_dii:iF,dynCall_v:fF}}(Module.asmGlobalArg,Module.asmLibraryArg,buffer),_llvm_bswap_i32=Module._llvm_bswap_i32=asm._llvm_bswap_i32,getTempRet0=Module.getTempRet0=asm.getTempRet0,___udivdi3=Module.___udivdi3=asm.___udivdi3,setThrew=Module.setThrew=asm.setThrew,_bitshift64Lshr=Module._bitshift64Lshr=asm._bitshift64Lshr,_bitshift64Shl=Module._bitshift64Shl=asm._bitshift64Shl,_memset=Module._memset=asm._memset,_sbrk=Module._sbrk=asm._sbrk,_memcpy=Module._memcpy=asm._memcpy,stackAlloc=Module.stackAlloc=asm.stackAlloc,___uremdi3=Module.___uremdi3=asm.___uremdi3,_nbind_init=Module._nbind_init=asm._nbind_init,_i64Subtract=Module._i64Subtract=asm._i64Subtract,setTempRet0=Module.setTempRet0=asm.setTempRet0,_i64Add=Module._i64Add=asm._i64Add,_emscripten_get_global_libc=Module._emscripten_get_global_libc=asm._emscripten_get_global_libc,__GLOBAL__sub_I_Yoga_cpp=Module.__GLOBAL__sub_I_Yoga_cpp=asm.__GLOBAL__sub_I_Yoga_cpp,__GLOBAL__sub_I_Binding_cc=Module.__GLOBAL__sub_I_Binding_cc=asm.__GLOBAL__sub_I_Binding_cc,stackSave=Module.stackSave=asm.stackSave,__GLOBAL__sub_I_nbind_cc=Module.__GLOBAL__sub_I_nbind_cc=asm.__GLOBAL__sub_I_nbind_cc,_free=Module._free=asm._free,runPostSets=Module.runPostSets=asm.runPostSets,establishStackSpace=Module.establishStackSpace=asm.establishStackSpace,_memmove=Module._memmove=asm._memmove,stackRestore=Module.stackRestore=asm.stackRestore,_malloc=Module._malloc=asm._malloc,__GLOBAL__sub_I_common_cc=Module.__GLOBAL__sub_I_common_cc=asm.__GLOBAL__sub_I_common_cc,dynCall_viiiii=Module.dynCall_viiiii=asm.dynCall_viiiii,dynCall_vif=Module.dynCall_vif=asm.dynCall_vif,dynCall_vid=Module.dynCall_vid=asm.dynCall_vid,dynCall_fiff=Module.dynCall_fiff=asm.dynCall_fiff,dynCall_vi=Module.dynCall_vi=asm.dynCall_vi,dynCall_vii=Module.dynCall_vii=asm.dynCall_vii,dynCall_ii=Module.dynCall_ii=asm.dynCall_ii,dynCall_viddi=Module.dynCall_viddi=asm.dynCall_viddi,dynCall_vidd=Module.dynCall_vidd=asm.dynCall_vidd,dynCall_iiii=Module.dynCall_iiii=asm.dynCall_iiii,dynCall_diii=Module.dynCall_diii=asm.dynCall_diii,dynCall_di=Module.dynCall_di=asm.dynCall_di,dynCall_iid=Module.dynCall_iid=asm.dynCall_iid,dynCall_iii=Module.dynCall_iii=asm.dynCall_iii,dynCall_viiddi=Module.dynCall_viiddi=asm.dynCall_viiddi,dynCall_viiiiii=Module.dynCall_viiiiii=asm.dynCall_viiiiii,dynCall_dii=Module.dynCall_dii=asm.dynCall_dii,dynCall_i=Module.dynCall_i=asm.dynCall_i,dynCall_iiiiii=Module.dynCall_iiiiii=asm.dynCall_iiiiii,dynCall_viiid=Module.dynCall_viiid=asm.dynCall_viiid,dynCall_viififi=Module.dynCall_viififi=asm.dynCall_viififi,dynCall_viii=Module.dynCall_viii=asm.dynCall_viii,dynCall_v=Module.dynCall_v=asm.dynCall_v,dynCall_viid=Module.dynCall_viid=asm.dynCall_viid,dynCall_idd=Module.dynCall_idd=asm.dynCall_idd,dynCall_viiii=Module.dynCall_viiii=asm.dynCall_viiii;Runtime.stackAlloc=Module.stackAlloc,Runtime.stackSave=Module.stackSave,Runtime.stackRestore=Module.stackRestore,Runtime.establishStackSpace=Module.establishStackSpace,Runtime.setTempRet0=Module.setTempRet0,Runtime.getTempRet0=Module.getTempRet0,Module.asm=asm;function ExitStatus(o){this.name="ExitStatus",this.message="Program terminated with exit("+o+")",this.status=o}ExitStatus.prototype=new Error,ExitStatus.prototype.constructor=ExitStatus;var initialStackTop,preloadStartTime=null,calledMain=!1;dependenciesFulfilled=function o(){Module.calledRun||run(),Module.calledRun||(dependenciesFulfilled=o)},Module.callMain=Module.callMain=function o(l){l=l||[],ensureInitRuntime();var f=l.length+1;function h(){for(var k=0;k<4-1;k++)E.push(0)}var E=[allocate(intArrayFromString(Module.thisProgram),"i8",ALLOC_NORMAL)];h();for(var t=0;t0||(preRun(),runDependencies>0)||Module.calledRun)return;function l(){Module.calledRun||(Module.calledRun=!0,!ABORT&&(ensureInitRuntime(),preMain(),Module.onRuntimeInitialized&&Module.onRuntimeInitialized(),Module._main&&shouldRunNow&&Module.callMain(o),postRun()))}Module.setStatus?(Module.setStatus("Running..."),setTimeout(function(){setTimeout(function(){Module.setStatus("")},1),l()},1)):l()}Module.run=Module.run=run;function exit(o,l){l&&Module.noExitRuntime||(Module.noExitRuntime||(ABORT=!0,EXITSTATUS=o,STACKTOP=initialStackTop,exitRuntime(),Module.onExit&&Module.onExit(o)),ENVIRONMENT_IS_NODE&&process.exit(o),Module.quit(o,new ExitStatus(o)))}Module.exit=Module.exit=exit;var abortDecorators=[];function abort(o){Module.onAbort&&Module.onAbort(o),o!==void 0?(Module.print(o),Module.printErr(o),o=JSON.stringify(o)):o="",ABORT=!0,EXITSTATUS=1;var l=` +If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.`,f="abort("+o+") at "+stackTrace()+l;throw abortDecorators&&abortDecorators.forEach(function(h){f=h(f,o)}),f}if(Module.abort=Module.abort=abort,Module.preInit)for(typeof Module.preInit=="function"&&(Module.preInit=[Module.preInit]);Module.preInit.length>0;)Module.preInit.pop()();var shouldRunNow=!0;Module.noInitialRun&&(shouldRunNow=!1),run()})});var eh=nt((CH,tT)=>{"use strict";var SP=$S(),TP=eT(),_D=!1,ED=null;TP({},function(o,l){if(!_D){if(_D=!0,o)throw o;ED=l}});if(!_D)throw new Error("Failed to load the yoga module - it needed to be loaded synchronously, but didn't");tT.exports=SP(ED.bind,ED.lib)});var rT=nt((xH,nT)=>{"use strict";nT.exports=({onlyFirst:o=!1}={})=>{let l=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(l,o?void 0:"g")}});var DD=nt((RH,iT)=>{"use strict";var CP=rT();iT.exports=o=>typeof o=="string"?o.replace(CP(),""):o});var SD=nt((AH,wD)=>{"use strict";var uT=o=>Number.isNaN(o)?!1:o>=4352&&(o<=4447||o===9001||o===9002||11904<=o&&o<=12871&&o!==12351||12880<=o&&o<=19903||19968<=o&&o<=42182||43360<=o&&o<=43388||44032<=o&&o<=55203||63744<=o&&o<=64255||65040<=o&&o<=65049||65072<=o&&o<=65131||65281<=o&&o<=65376||65504<=o&&o<=65510||110592<=o&&o<=110593||127488<=o&&o<=127569||131072<=o&&o<=262141);wD.exports=uT;wD.exports.default=uT});var lT=nt((OH,oT)=>{"use strict";oT.exports=function(){return/\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g}});var q_=nt((MH,TD)=>{"use strict";var xP=DD(),RP=SD(),AP=lT(),sT=o=>{if(typeof o!="string"||o.length===0||(o=xP(o),o.length===0))return 0;o=o.replace(AP()," ");let l=0;for(let f=0;f=127&&h<=159||h>=768&&h<=879||(h>65535&&f++,l+=RP(h)?2:1)}return l};TD.exports=sT;TD.exports.default=sT});var xD=nt((kH,CD)=>{"use strict";var OP=q_(),aT=o=>{let l=0;for(let f of o.split(` +`))l=Math.max(l,OP(f));return l};CD.exports=aT;CD.exports.default=aT});var fT=nt(Ky=>{"use strict";var MP=Ky&&Ky.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Ky,"__esModule",{value:!0});var kP=MP(xD()),RD={};Ky.default=o=>{if(o.length===0)return{width:0,height:0};if(RD[o])return RD[o];let l=kP.default(o),f=o.split(` +`).length;return RD[o]={width:l,height:f},{width:l,height:f}}});var cT=nt(Xy=>{"use strict";var NP=Xy&&Xy.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Xy,"__esModule",{value:!0});var Gi=NP(eh()),LP=(o,l)=>{"position"in l&&o.setPositionType(l.position==="absolute"?Gi.default.POSITION_TYPE_ABSOLUTE:Gi.default.POSITION_TYPE_RELATIVE)},FP=(o,l)=>{"marginLeft"in l&&o.setMargin(Gi.default.EDGE_START,l.marginLeft||0),"marginRight"in l&&o.setMargin(Gi.default.EDGE_END,l.marginRight||0),"marginTop"in l&&o.setMargin(Gi.default.EDGE_TOP,l.marginTop||0),"marginBottom"in l&&o.setMargin(Gi.default.EDGE_BOTTOM,l.marginBottom||0)},PP=(o,l)=>{"paddingLeft"in l&&o.setPadding(Gi.default.EDGE_LEFT,l.paddingLeft||0),"paddingRight"in l&&o.setPadding(Gi.default.EDGE_RIGHT,l.paddingRight||0),"paddingTop"in l&&o.setPadding(Gi.default.EDGE_TOP,l.paddingTop||0),"paddingBottom"in l&&o.setPadding(Gi.default.EDGE_BOTTOM,l.paddingBottom||0)},IP=(o,l)=>{var f;"flexGrow"in l&&o.setFlexGrow((f=l.flexGrow)!==null&&f!==void 0?f:0),"flexShrink"in l&&o.setFlexShrink(typeof l.flexShrink=="number"?l.flexShrink:1),"flexDirection"in l&&(l.flexDirection==="row"&&o.setFlexDirection(Gi.default.FLEX_DIRECTION_ROW),l.flexDirection==="row-reverse"&&o.setFlexDirection(Gi.default.FLEX_DIRECTION_ROW_REVERSE),l.flexDirection==="column"&&o.setFlexDirection(Gi.default.FLEX_DIRECTION_COLUMN),l.flexDirection==="column-reverse"&&o.setFlexDirection(Gi.default.FLEX_DIRECTION_COLUMN_REVERSE)),"flexBasis"in l&&(typeof l.flexBasis=="number"?o.setFlexBasis(l.flexBasis):typeof l.flexBasis=="string"?o.setFlexBasisPercent(Number.parseInt(l.flexBasis,10)):o.setFlexBasis(NaN)),"alignItems"in l&&((l.alignItems==="stretch"||!l.alignItems)&&o.setAlignItems(Gi.default.ALIGN_STRETCH),l.alignItems==="flex-start"&&o.setAlignItems(Gi.default.ALIGN_FLEX_START),l.alignItems==="center"&&o.setAlignItems(Gi.default.ALIGN_CENTER),l.alignItems==="flex-end"&&o.setAlignItems(Gi.default.ALIGN_FLEX_END)),"alignSelf"in l&&((l.alignSelf==="auto"||!l.alignSelf)&&o.setAlignSelf(Gi.default.ALIGN_AUTO),l.alignSelf==="flex-start"&&o.setAlignSelf(Gi.default.ALIGN_FLEX_START),l.alignSelf==="center"&&o.setAlignSelf(Gi.default.ALIGN_CENTER),l.alignSelf==="flex-end"&&o.setAlignSelf(Gi.default.ALIGN_FLEX_END)),"justifyContent"in l&&((l.justifyContent==="flex-start"||!l.justifyContent)&&o.setJustifyContent(Gi.default.JUSTIFY_FLEX_START),l.justifyContent==="center"&&o.setJustifyContent(Gi.default.JUSTIFY_CENTER),l.justifyContent==="flex-end"&&o.setJustifyContent(Gi.default.JUSTIFY_FLEX_END),l.justifyContent==="space-between"&&o.setJustifyContent(Gi.default.JUSTIFY_SPACE_BETWEEN),l.justifyContent==="space-around"&&o.setJustifyContent(Gi.default.JUSTIFY_SPACE_AROUND))},bP=(o,l)=>{var f,h;"width"in l&&(typeof l.width=="number"?o.setWidth(l.width):typeof l.width=="string"?o.setWidthPercent(Number.parseInt(l.width,10)):o.setWidthAuto()),"height"in l&&(typeof l.height=="number"?o.setHeight(l.height):typeof l.height=="string"?o.setHeightPercent(Number.parseInt(l.height,10)):o.setHeightAuto()),"minWidth"in l&&(typeof l.minWidth=="string"?o.setMinWidthPercent(Number.parseInt(l.minWidth,10)):o.setMinWidth((f=l.minWidth)!==null&&f!==void 0?f:0)),"minHeight"in l&&(typeof l.minHeight=="string"?o.setMinHeightPercent(Number.parseInt(l.minHeight,10)):o.setMinHeight((h=l.minHeight)!==null&&h!==void 0?h:0))},BP=(o,l)=>{"display"in l&&o.setDisplay(l.display==="flex"?Gi.default.DISPLAY_FLEX:Gi.default.DISPLAY_NONE)},UP=(o,l)=>{if("borderStyle"in l){let f=typeof l.borderStyle=="string"?1:0;o.setBorder(Gi.default.EDGE_TOP,f),o.setBorder(Gi.default.EDGE_BOTTOM,f),o.setBorder(Gi.default.EDGE_LEFT,f),o.setBorder(Gi.default.EDGE_RIGHT,f)}};Xy.default=(o,l={})=>{LP(o,l),FP(o,l),PP(o,l),IP(o,l),bP(o,l),BP(o,l),UP(o,l)}});var pT=nt((FH,dT)=>{"use strict";dT.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]}});var AD=nt((PH,vT)=>{var Qy=pT(),hT={};for(let o of Object.keys(Qy))hT[Qy[o]]=o;var zn={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};vT.exports=zn;for(let o of Object.keys(zn)){if(!("channels"in zn[o]))throw new Error("missing channels property: "+o);if(!("labels"in zn[o]))throw new Error("missing channel labels property: "+o);if(zn[o].labels.length!==zn[o].channels)throw new Error("channel and label counts mismatch: "+o);let{channels:l,labels:f}=zn[o];delete zn[o].channels,delete zn[o].labels,Object.defineProperty(zn[o],"channels",{value:l}),Object.defineProperty(zn[o],"labels",{value:f})}zn.rgb.hsl=function(o){let l=o[0]/255,f=o[1]/255,h=o[2]/255,E=Math.min(l,f,h),t=Math.max(l,f,h),N=t-E,F,k;t===E?F=0:l===t?F=(f-h)/N:f===t?F=2+(h-l)/N:h===t&&(F=4+(l-f)/N),F=Math.min(F*60,360),F<0&&(F+=360);let x=(E+t)/2;return t===E?k=0:x<=.5?k=N/(t+E):k=N/(2-t-E),[F,k*100,x*100]};zn.rgb.hsv=function(o){let l,f,h,E,t,N=o[0]/255,F=o[1]/255,k=o[2]/255,x=Math.max(N,F,k),j=x-Math.min(N,F,k),q=function(V){return(x-V)/6/j+1/2};return j===0?(E=0,t=0):(t=j/x,l=q(N),f=q(F),h=q(k),N===x?E=h-f:F===x?E=1/3+l-h:k===x&&(E=2/3+f-l),E<0?E+=1:E>1&&(E-=1)),[E*360,t*100,x*100]};zn.rgb.hwb=function(o){let l=o[0],f=o[1],h=o[2],E=zn.rgb.hsl(o)[0],t=1/255*Math.min(l,Math.min(f,h));return h=1-1/255*Math.max(l,Math.max(f,h)),[E,t*100,h*100]};zn.rgb.cmyk=function(o){let l=o[0]/255,f=o[1]/255,h=o[2]/255,E=Math.min(1-l,1-f,1-h),t=(1-l-E)/(1-E)||0,N=(1-f-E)/(1-E)||0,F=(1-h-E)/(1-E)||0;return[t*100,N*100,F*100,E*100]};function jP(o,l){return(o[0]-l[0])**2+(o[1]-l[1])**2+(o[2]-l[2])**2}zn.rgb.keyword=function(o){let l=hT[o];if(l)return l;let f=1/0,h;for(let E of Object.keys(Qy)){let t=Qy[E],N=jP(o,t);N.04045?((l+.055)/1.055)**2.4:l/12.92,f=f>.04045?((f+.055)/1.055)**2.4:f/12.92,h=h>.04045?((h+.055)/1.055)**2.4:h/12.92;let E=l*.4124+f*.3576+h*.1805,t=l*.2126+f*.7152+h*.0722,N=l*.0193+f*.1192+h*.9505;return[E*100,t*100,N*100]};zn.rgb.lab=function(o){let l=zn.rgb.xyz(o),f=l[0],h=l[1],E=l[2];f/=95.047,h/=100,E/=108.883,f=f>.008856?f**(1/3):7.787*f+16/116,h=h>.008856?h**(1/3):7.787*h+16/116,E=E>.008856?E**(1/3):7.787*E+16/116;let t=116*h-16,N=500*(f-h),F=200*(h-E);return[t,N,F]};zn.hsl.rgb=function(o){let l=o[0]/360,f=o[1]/100,h=o[2]/100,E,t,N;if(f===0)return N=h*255,[N,N,N];h<.5?E=h*(1+f):E=h+f-h*f;let F=2*h-E,k=[0,0,0];for(let x=0;x<3;x++)t=l+1/3*-(x-1),t<0&&t++,t>1&&t--,6*t<1?N=F+(E-F)*6*t:2*t<1?N=E:3*t<2?N=F+(E-F)*(2/3-t)*6:N=F,k[x]=N*255;return k};zn.hsl.hsv=function(o){let l=o[0],f=o[1]/100,h=o[2]/100,E=f,t=Math.max(h,.01);h*=2,f*=h<=1?h:2-h,E*=t<=1?t:2-t;let N=(h+f)/2,F=h===0?2*E/(t+E):2*f/(h+f);return[l,F*100,N*100]};zn.hsv.rgb=function(o){let l=o[0]/60,f=o[1]/100,h=o[2]/100,E=Math.floor(l)%6,t=l-Math.floor(l),N=255*h*(1-f),F=255*h*(1-f*t),k=255*h*(1-f*(1-t));switch(h*=255,E){case 0:return[h,k,N];case 1:return[F,h,N];case 2:return[N,h,k];case 3:return[N,F,h];case 4:return[k,N,h];case 5:return[h,N,F]}};zn.hsv.hsl=function(o){let l=o[0],f=o[1]/100,h=o[2]/100,E=Math.max(h,.01),t,N;N=(2-f)*h;let F=(2-f)*E;return t=f*E,t/=F<=1?F:2-F,t=t||0,N/=2,[l,t*100,N*100]};zn.hwb.rgb=function(o){let l=o[0]/360,f=o[1]/100,h=o[2]/100,E=f+h,t;E>1&&(f/=E,h/=E);let N=Math.floor(6*l),F=1-h;t=6*l-N,(N&1)!==0&&(t=1-t);let k=f+t*(F-f),x,j,q;switch(N){default:case 6:case 0:x=F,j=k,q=f;break;case 1:x=k,j=F,q=f;break;case 2:x=f,j=F,q=k;break;case 3:x=f,j=k,q=F;break;case 4:x=k,j=f,q=F;break;case 5:x=F,j=f,q=k;break}return[x*255,j*255,q*255]};zn.cmyk.rgb=function(o){let l=o[0]/100,f=o[1]/100,h=o[2]/100,E=o[3]/100,t=1-Math.min(1,l*(1-E)+E),N=1-Math.min(1,f*(1-E)+E),F=1-Math.min(1,h*(1-E)+E);return[t*255,N*255,F*255]};zn.xyz.rgb=function(o){let l=o[0]/100,f=o[1]/100,h=o[2]/100,E,t,N;return E=l*3.2406+f*-1.5372+h*-.4986,t=l*-.9689+f*1.8758+h*.0415,N=l*.0557+f*-.204+h*1.057,E=E>.0031308?1.055*E**(1/2.4)-.055:E*12.92,t=t>.0031308?1.055*t**(1/2.4)-.055:t*12.92,N=N>.0031308?1.055*N**(1/2.4)-.055:N*12.92,E=Math.min(Math.max(0,E),1),t=Math.min(Math.max(0,t),1),N=Math.min(Math.max(0,N),1),[E*255,t*255,N*255]};zn.xyz.lab=function(o){let l=o[0],f=o[1],h=o[2];l/=95.047,f/=100,h/=108.883,l=l>.008856?l**(1/3):7.787*l+16/116,f=f>.008856?f**(1/3):7.787*f+16/116,h=h>.008856?h**(1/3):7.787*h+16/116;let E=116*f-16,t=500*(l-f),N=200*(f-h);return[E,t,N]};zn.lab.xyz=function(o){let l=o[0],f=o[1],h=o[2],E,t,N;t=(l+16)/116,E=f/500+t,N=t-h/200;let F=t**3,k=E**3,x=N**3;return t=F>.008856?F:(t-16/116)/7.787,E=k>.008856?k:(E-16/116)/7.787,N=x>.008856?x:(N-16/116)/7.787,E*=95.047,t*=100,N*=108.883,[E,t,N]};zn.lab.lch=function(o){let l=o[0],f=o[1],h=o[2],E;E=Math.atan2(h,f)*360/2/Math.PI,E<0&&(E+=360);let N=Math.sqrt(f*f+h*h);return[l,N,E]};zn.lch.lab=function(o){let l=o[0],f=o[1],E=o[2]/360*2*Math.PI,t=f*Math.cos(E),N=f*Math.sin(E);return[l,t,N]};zn.rgb.ansi16=function(o,l=null){let[f,h,E]=o,t=l===null?zn.rgb.hsv(o)[2]:l;if(t=Math.round(t/50),t===0)return 30;let N=30+(Math.round(E/255)<<2|Math.round(h/255)<<1|Math.round(f/255));return t===2&&(N+=60),N};zn.hsv.ansi16=function(o){return zn.rgb.ansi16(zn.hsv.rgb(o),o[2])};zn.rgb.ansi256=function(o){let l=o[0],f=o[1],h=o[2];return l===f&&f===h?l<8?16:l>248?231:Math.round((l-8)/247*24)+232:16+36*Math.round(l/255*5)+6*Math.round(f/255*5)+Math.round(h/255*5)};zn.ansi16.rgb=function(o){let l=o%10;if(l===0||l===7)return o>50&&(l+=3.5),l=l/10.5*255,[l,l,l];let f=(~~(o>50)+1)*.5,h=(l&1)*f*255,E=(l>>1&1)*f*255,t=(l>>2&1)*f*255;return[h,E,t]};zn.ansi256.rgb=function(o){if(o>=232){let t=(o-232)*10+8;return[t,t,t]}o-=16;let l,f=Math.floor(o/36)/5*255,h=Math.floor((l=o%36)/6)/5*255,E=l%6/5*255;return[f,h,E]};zn.rgb.hex=function(o){let f=(((Math.round(o[0])&255)<<16)+((Math.round(o[1])&255)<<8)+(Math.round(o[2])&255)).toString(16).toUpperCase();return"000000".substring(f.length)+f};zn.hex.rgb=function(o){let l=o.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!l)return[0,0,0];let f=l[0];l[0].length===3&&(f=f.split("").map(F=>F+F).join(""));let h=parseInt(f,16),E=h>>16&255,t=h>>8&255,N=h&255;return[E,t,N]};zn.rgb.hcg=function(o){let l=o[0]/255,f=o[1]/255,h=o[2]/255,E=Math.max(Math.max(l,f),h),t=Math.min(Math.min(l,f),h),N=E-t,F,k;return N<1?F=t/(1-N):F=0,N<=0?k=0:E===l?k=(f-h)/N%6:E===f?k=2+(h-l)/N:k=4+(l-f)/N,k/=6,k%=1,[k*360,N*100,F*100]};zn.hsl.hcg=function(o){let l=o[1]/100,f=o[2]/100,h=f<.5?2*l*f:2*l*(1-f),E=0;return h<1&&(E=(f-.5*h)/(1-h)),[o[0],h*100,E*100]};zn.hsv.hcg=function(o){let l=o[1]/100,f=o[2]/100,h=l*f,E=0;return h<1&&(E=(f-h)/(1-h)),[o[0],h*100,E*100]};zn.hcg.rgb=function(o){let l=o[0]/360,f=o[1]/100,h=o[2]/100;if(f===0)return[h*255,h*255,h*255];let E=[0,0,0],t=l%1*6,N=t%1,F=1-N,k=0;switch(Math.floor(t)){case 0:E[0]=1,E[1]=N,E[2]=0;break;case 1:E[0]=F,E[1]=1,E[2]=0;break;case 2:E[0]=0,E[1]=1,E[2]=N;break;case 3:E[0]=0,E[1]=F,E[2]=1;break;case 4:E[0]=N,E[1]=0,E[2]=1;break;default:E[0]=1,E[1]=0,E[2]=F}return k=(1-f)*h,[(f*E[0]+k)*255,(f*E[1]+k)*255,(f*E[2]+k)*255]};zn.hcg.hsv=function(o){let l=o[1]/100,f=o[2]/100,h=l+f*(1-l),E=0;return h>0&&(E=l/h),[o[0],E*100,h*100]};zn.hcg.hsl=function(o){let l=o[1]/100,h=o[2]/100*(1-l)+.5*l,E=0;return h>0&&h<.5?E=l/(2*h):h>=.5&&h<1&&(E=l/(2*(1-h))),[o[0],E*100,h*100]};zn.hcg.hwb=function(o){let l=o[1]/100,f=o[2]/100,h=l+f*(1-l);return[o[0],(h-l)*100,(1-h)*100]};zn.hwb.hcg=function(o){let l=o[1]/100,h=1-o[2]/100,E=h-l,t=0;return E<1&&(t=(h-E)/(1-E)),[o[0],E*100,t*100]};zn.apple.rgb=function(o){return[o[0]/65535*255,o[1]/65535*255,o[2]/65535*255]};zn.rgb.apple=function(o){return[o[0]/255*65535,o[1]/255*65535,o[2]/255*65535]};zn.gray.rgb=function(o){return[o[0]/100*255,o[0]/100*255,o[0]/100*255]};zn.gray.hsl=function(o){return[0,0,o[0]]};zn.gray.hsv=zn.gray.hsl;zn.gray.hwb=function(o){return[0,100,o[0]]};zn.gray.cmyk=function(o){return[0,0,0,o[0]]};zn.gray.lab=function(o){return[o[0],0,0]};zn.gray.hex=function(o){let l=Math.round(o[0]/100*255)&255,h=((l<<16)+(l<<8)+l).toString(16).toUpperCase();return"000000".substring(h.length)+h};zn.rgb.gray=function(o){return[(o[0]+o[1]+o[2])/3/255*100]}});var yT=nt((IH,mT)=>{var W_=AD();function zP(){let o={},l=Object.keys(W_);for(let f=l.length,h=0;h{var OD=AD(),VP=yT(),Zv={},GP=Object.keys(OD);function YP(o){let l=function(...f){let h=f[0];return h==null?h:(h.length>1&&(f=h),o(f))};return"conversion"in o&&(l.conversion=o.conversion),l}function KP(o){let l=function(...f){let h=f[0];if(h==null)return h;h.length>1&&(f=h);let E=o(f);if(typeof E=="object")for(let t=E.length,N=0;N{Zv[o]={},Object.defineProperty(Zv[o],"channels",{value:OD[o].channels}),Object.defineProperty(Zv[o],"labels",{value:OD[o].labels});let l=VP(o);Object.keys(l).forEach(h=>{let E=l[h];Zv[o][h]=KP(E),Zv[o][h].raw=YP(E)})});gT.exports=Zv});var G_=nt((BH,TT)=>{"use strict";var ET=(o,l)=>(...f)=>`\x1B[${o(...f)+l}m`,DT=(o,l)=>(...f)=>{let h=o(...f);return`\x1B[${38+l};5;${h}m`},wT=(o,l)=>(...f)=>{let h=o(...f);return`\x1B[${38+l};2;${h[0]};${h[1]};${h[2]}m`},V_=o=>o,ST=(o,l,f)=>[o,l,f],$v=(o,l,f)=>{Object.defineProperty(o,l,{get:()=>{let h=f();return Object.defineProperty(o,l,{value:h,enumerable:!0,configurable:!0}),h},enumerable:!0,configurable:!0})},MD,em=(o,l,f,h)=>{MD===void 0&&(MD=_T());let E=h?10:0,t={};for(let[N,F]of Object.entries(MD)){let k=N==="ansi16"?"ansi":N;N===l?t[k]=o(f,E):typeof F=="object"&&(t[k]=o(F[l],E))}return t};function XP(){let o=new Map,l={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};l.color.gray=l.color.blackBright,l.bgColor.bgGray=l.bgColor.bgBlackBright,l.color.grey=l.color.blackBright,l.bgColor.bgGrey=l.bgColor.bgBlackBright;for(let[f,h]of Object.entries(l)){for(let[E,t]of Object.entries(h))l[E]={open:`\x1B[${t[0]}m`,close:`\x1B[${t[1]}m`},h[E]=l[E],o.set(t[0],t[1]);Object.defineProperty(l,f,{value:h,enumerable:!1})}return Object.defineProperty(l,"codes",{value:o,enumerable:!1}),l.color.close="\x1B[39m",l.bgColor.close="\x1B[49m",$v(l.color,"ansi",()=>em(ET,"ansi16",V_,!1)),$v(l.color,"ansi256",()=>em(DT,"ansi256",V_,!1)),$v(l.color,"ansi16m",()=>em(wT,"rgb",ST,!1)),$v(l.bgColor,"ansi",()=>em(ET,"ansi16",V_,!0)),$v(l.bgColor,"ansi256",()=>em(DT,"ansi256",V_,!0)),$v(l.bgColor,"ansi16m",()=>em(wT,"rgb",ST,!0)),l}Object.defineProperty(TT,"exports",{enumerable:!0,get:XP})});var RT=nt((UH,xT)=>{"use strict";var Jy=q_(),QP=DD(),JP=G_(),ND=new Set(["\x1B","\x9B"]),ZP=39,CT=o=>`${ND.values().next().value}[${o}m`,$P=o=>o.split(" ").map(l=>Jy(l)),kD=(o,l,f)=>{let h=[...l],E=!1,t=Jy(QP(o[o.length-1]));for(let[N,F]of h.entries()){let k=Jy(F);if(t+k<=f?o[o.length-1]+=F:(o.push(F),t=0),ND.has(F))E=!0;else if(E&&F==="m"){E=!1;continue}E||(t+=k,t===f&&N0&&o.length>1&&(o[o.length-2]+=o.pop())},eI=o=>{let l=o.split(" "),f=l.length;for(;f>0&&!(Jy(l[f-1])>0);)f--;return f===l.length?o:l.slice(0,f).join(" ")+l.slice(f).join("")},tI=(o,l,f={})=>{if(f.trim!==!1&&o.trim()==="")return"";let h="",E="",t,N=$P(o),F=[""];for(let[k,x]of o.split(" ").entries()){f.trim!==!1&&(F[F.length-1]=F[F.length-1].trimLeft());let j=Jy(F[F.length-1]);if(k!==0&&(j>=l&&(f.wordWrap===!1||f.trim===!1)&&(F.push(""),j=0),(j>0||f.trim===!1)&&(F[F.length-1]+=" ",j++)),f.hard&&N[k]>l){let q=l-j,V=1+Math.floor((N[k]-q-1)/l);Math.floor((N[k]-1)/l)l&&j>0&&N[k]>0){if(f.wordWrap===!1&&jl&&f.wordWrap===!1){kD(F,x,l);continue}F[F.length-1]+=x}f.trim!==!1&&(F=F.map(eI)),h=F.join(` +`);for(let[k,x]of[...h].entries()){if(E+=x,ND.has(x)){let q=parseFloat(/\d[^m]*/.exec(h.slice(k,k+4)));t=q===ZP?null:q}let j=JP.codes.get(Number(t));t&&j&&(h[k+1]===` +`?E+=CT(j):x===` +`&&(E+=CT(t)))}return E};xT.exports=(o,l,f)=>String(o).normalize().replace(/\r\n/g,` +`).split(` +`).map(h=>tI(h,l,f)).join(` +`)});var MT=nt((jH,OT)=>{"use strict";var AT="[\uD800-\uDBFF][\uDC00-\uDFFF]",nI=o=>o&&o.exact?new RegExp(`^${AT}$`):new RegExp(AT,"g");OT.exports=nI});var LD=nt((zH,FT)=>{"use strict";var rI=SD(),iI=MT(),kT=G_(),LT=["\x1B","\x9B"],Y_=o=>`${LT[0]}[${o}m`,NT=(o,l,f)=>{let h=[];o=[...o];for(let E of o){let t=E;E.match(";")&&(E=E.split(";")[0][0]+"0");let N=kT.codes.get(parseInt(E,10));if(N){let F=o.indexOf(N.toString());F>=0?o.splice(F,1):h.push(Y_(l?N:t))}else if(l){h.push(Y_(0));break}else h.push(Y_(t))}if(l&&(h=h.filter((E,t)=>h.indexOf(E)===t),f!==void 0)){let E=Y_(kT.codes.get(parseInt(f,10)));h=h.reduce((t,N)=>N===E?[N,...t]:[...t,N],[])}return h.join("")};FT.exports=(o,l,f)=>{let h=[...o.normalize()],E=[];f=typeof f=="number"?f:h.length;let t=!1,N,F=0,k="";for(let[x,j]of h.entries()){let q=!1;if(LT.includes(j)){let V=/\d[^m]*/.exec(o.slice(x,x+18));N=V&&V.length>0?V[0]:void 0,Fl&&F<=f)k+=j;else if(F===l&&!t&&N!==void 0)k=NT(E);else if(F>=f){k+=NT(E,!0,N);break}}return k}});var IT=nt((HH,PT)=>{"use strict";var c2=LD(),uI=q_();function K_(o,l,f){if(o.charAt(l)===" ")return l;for(let h=1;h<=3;h++)if(f){if(o.charAt(l+h)===" ")return l+h}else if(o.charAt(l-h)===" ")return l-h;return l}PT.exports=(o,l,f)=>{f={position:"end",preferTruncationOnSpace:!1,...f};let{position:h,space:E,preferTruncationOnSpace:t}=f,N="\u2026",F=1;if(typeof o!="string")throw new TypeError(`Expected \`input\` to be a string, got ${typeof o}`);if(typeof l!="number")throw new TypeError(`Expected \`columns\` to be a number, got ${typeof l}`);if(l<1)return"";if(l===1)return N;let k=uI(o);if(k<=l)return o;if(h==="start"){if(t){let x=K_(o,k-l+1,!0);return N+c2(o,x,k).trim()}return E===!0&&(N+=" ",F=2),N+c2(o,k-l+F,k)}if(h==="middle"){E===!0&&(N=" "+N+" ",F=3);let x=Math.floor(l/2);if(t){let j=K_(o,x),q=K_(o,k-(l-x)+1,!0);return c2(o,0,j)+N+c2(o,q,k).trim()}return c2(o,0,x)+N+c2(o,k-(l-x)+F,k)}if(h==="end"){if(t){let x=K_(o,l-1);return c2(o,0,x)+N}return E===!0&&(N=" "+N,F=2),c2(o,0,l-F)+N}throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${h}`)}});var PD=nt(Zy=>{"use strict";var bT=Zy&&Zy.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Zy,"__esModule",{value:!0});var oI=bT(RT()),lI=bT(IT()),FD={};Zy.default=(o,l,f)=>{let h=o+String(l)+String(f);if(FD[h])return FD[h];let E=o;if(f==="wrap"&&(E=oI.default(o,l,{trim:!1,hard:!0})),f.startsWith("truncate")){let t="end";f==="truncate-middle"&&(t="middle"),f==="truncate-start"&&(t="start"),E=lI.default(o,l,{position:t})}return FD[h]=E,E}});var bD=nt(ID=>{"use strict";Object.defineProperty(ID,"__esModule",{value:!0});var BT=o=>{let l="";if(o.childNodes.length>0)for(let f of o.childNodes){let h="";f.nodeName==="#text"?h=f.nodeValue:((f.nodeName==="ink-text"||f.nodeName==="ink-virtual-text")&&(h=BT(f)),h.length>0&&typeof f.internal_transform=="function"&&(h=f.internal_transform(h))),l+=h}return l};ID.default=BT});var BD=nt(f0=>{"use strict";var $y=f0&&f0.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(f0,"__esModule",{value:!0});f0.setTextNodeValue=f0.createTextNode=f0.setStyle=f0.setAttribute=f0.removeChildNode=f0.insertBeforeNode=f0.appendChildNode=f0.createNode=f0.TEXT_NAME=void 0;var sI=$y(eh()),UT=$y(fT()),aI=$y(cT()),fI=$y(PD()),cI=$y(bD());f0.TEXT_NAME="#text";f0.createNode=o=>{var l;let f={nodeName:o,style:{},attributes:{},childNodes:[],parentNode:null,yogaNode:o==="ink-virtual-text"?void 0:sI.default.Node.create()};return o==="ink-text"&&((l=f.yogaNode)===null||l===void 0||l.setMeasureFunc(dI.bind(null,f))),f};f0.appendChildNode=(o,l)=>{var f;l.parentNode&&f0.removeChildNode(l.parentNode,l),l.parentNode=o,o.childNodes.push(l),l.yogaNode&&((f=o.yogaNode)===null||f===void 0||f.insertChild(l.yogaNode,o.yogaNode.getChildCount())),(o.nodeName==="ink-text"||o.nodeName==="ink-virtual-text")&&X_(o)};f0.insertBeforeNode=(o,l,f)=>{var h,E;l.parentNode&&f0.removeChildNode(l.parentNode,l),l.parentNode=o;let t=o.childNodes.indexOf(f);if(t>=0){o.childNodes.splice(t,0,l),l.yogaNode&&((h=o.yogaNode)===null||h===void 0||h.insertChild(l.yogaNode,t));return}o.childNodes.push(l),l.yogaNode&&((E=o.yogaNode)===null||E===void 0||E.insertChild(l.yogaNode,o.yogaNode.getChildCount())),(o.nodeName==="ink-text"||o.nodeName==="ink-virtual-text")&&X_(o)};f0.removeChildNode=(o,l)=>{var f,h;l.yogaNode&&((h=(f=l.parentNode)===null||f===void 0?void 0:f.yogaNode)===null||h===void 0||h.removeChild(l.yogaNode)),l.parentNode=null;let E=o.childNodes.indexOf(l);E>=0&&o.childNodes.splice(E,1),(o.nodeName==="ink-text"||o.nodeName==="ink-virtual-text")&&X_(o)};f0.setAttribute=(o,l,f)=>{o.attributes[l]=f};f0.setStyle=(o,l)=>{o.style=l,o.yogaNode&&aI.default(o.yogaNode,l)};f0.createTextNode=o=>{let l={nodeName:"#text",nodeValue:o,yogaNode:void 0,parentNode:null,style:{}};return f0.setTextNodeValue(l,o),l};var dI=function(o,l){var f,h;let E=o.nodeName==="#text"?o.nodeValue:cI.default(o),t=UT.default(E);if(t.width<=l||t.width>=1&&l>0&&l<1)return t;let N=(h=(f=o.style)===null||f===void 0?void 0:f.textWrap)!==null&&h!==void 0?h:"wrap",F=fI.default(E,l,N);return UT.default(F)},jT=o=>{var l;if(!(!o||!o.parentNode))return(l=o.yogaNode)!==null&&l!==void 0?l:jT(o.parentNode)},X_=o=>{let l=jT(o);l==null||l.markDirty()};f0.setTextNodeValue=(o,l)=>{typeof l!="string"&&(l=String(l)),o.nodeValue=l,X_(o)}});var th=nt((GH,zT)=>{"use strict";zT.exports={BINARY_TYPES:["nodebuffer","arraybuffer","fragments"],GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),EMPTY_BUFFER:Buffer.alloc(0),NOOP:()=>{}}});var eg=nt((YH,UD)=>{"use strict";var{EMPTY_BUFFER:pI}=th();function HT(o,l){if(o.length===0)return pI;if(o.length===1)return o[0];let f=Buffer.allocUnsafe(l),h=0;for(let E=0;E{"use strict";var GT=Symbol("kDone"),jD=Symbol("kRun"),zD=class{constructor(l){this[GT]=()=>{this.pending--,this[jD]()},this.concurrency=l||1/0,this.jobs=[],this.pending=0}add(l){this.jobs.push(l),this[jD]()}[jD](){if(this.pending!==this.concurrency&&this.jobs.length){let l=this.jobs.shift();this.pending++,l(this[GT])}}};YT.exports=zD});var rg=nt((XH,ZT)=>{"use strict";var tg=hi("zlib"),XT=eg(),hI=KT(),{kStatusCode:QT,NOOP:vI}=th(),mI=Buffer.from([0,0,255,255]),Z_=Symbol("permessage-deflate"),X1=Symbol("total-length"),ng=Symbol("callback"),d2=Symbol("buffers"),HD=Symbol("error"),J_,qD=class{constructor(l,f,h){if(this._maxPayload=h|0,this._options=l||{},this._threshold=this._options.threshold!==void 0?this._options.threshold:1024,this._isServer=!!f,this._deflate=null,this._inflate=null,this.params=null,!J_){let E=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;J_=new hI(E)}}static get extensionName(){return"permessage-deflate"}offer(){let l={};return this._options.serverNoContextTakeover&&(l.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(l.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(l.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?l.client_max_window_bits=this._options.clientMaxWindowBits:this._options.clientMaxWindowBits==null&&(l.client_max_window_bits=!0),l}accept(l){return l=this.normalizeParams(l),this.params=this._isServer?this.acceptAsServer(l):this.acceptAsClient(l),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){let l=this._deflate[ng];this._deflate.close(),this._deflate=null,l&&l(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(l){let f=this._options,h=l.find(E=>!(f.serverNoContextTakeover===!1&&E.server_no_context_takeover||E.server_max_window_bits&&(f.serverMaxWindowBits===!1||typeof f.serverMaxWindowBits=="number"&&f.serverMaxWindowBits>E.server_max_window_bits)||typeof f.clientMaxWindowBits=="number"&&!E.client_max_window_bits));if(!h)throw new Error("None of the extension offers can be accepted");return f.serverNoContextTakeover&&(h.server_no_context_takeover=!0),f.clientNoContextTakeover&&(h.client_no_context_takeover=!0),typeof f.serverMaxWindowBits=="number"&&(h.server_max_window_bits=f.serverMaxWindowBits),typeof f.clientMaxWindowBits=="number"?h.client_max_window_bits=f.clientMaxWindowBits:(h.client_max_window_bits===!0||f.clientMaxWindowBits===!1)&&delete h.client_max_window_bits,h}acceptAsClient(l){let f=l[0];if(this._options.clientNoContextTakeover===!1&&f.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(!f.client_max_window_bits)typeof this._options.clientMaxWindowBits=="number"&&(f.client_max_window_bits=this._options.clientMaxWindowBits);else if(this._options.clientMaxWindowBits===!1||typeof this._options.clientMaxWindowBits=="number"&&f.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"');return f}normalizeParams(l){return l.forEach(f=>{Object.keys(f).forEach(h=>{let E=f[h];if(E.length>1)throw new Error(`Parameter "${h}" must have only a single value`);if(E=E[0],h==="client_max_window_bits"){if(E!==!0){let t=+E;if(!Number.isInteger(t)||t<8||t>15)throw new TypeError(`Invalid value for parameter "${h}": ${E}`);E=t}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${h}": ${E}`)}else if(h==="server_max_window_bits"){let t=+E;if(!Number.isInteger(t)||t<8||t>15)throw new TypeError(`Invalid value for parameter "${h}": ${E}`);E=t}else if(h==="client_no_context_takeover"||h==="server_no_context_takeover"){if(E!==!0)throw new TypeError(`Invalid value for parameter "${h}": ${E}`)}else throw new Error(`Unknown parameter "${h}"`);f[h]=E})}),l}decompress(l,f,h){J_.add(E=>{this._decompress(l,f,(t,N)=>{E(),h(t,N)})})}compress(l,f,h){J_.add(E=>{this._compress(l,f,(t,N)=>{E(),h(t,N)})})}_decompress(l,f,h){let E=this._isServer?"client":"server";if(!this._inflate){let t=`${E}_max_window_bits`,N=typeof this.params[t]!="number"?tg.Z_DEFAULT_WINDOWBITS:this.params[t];this._inflate=tg.createInflateRaw({...this._options.zlibInflateOptions,windowBits:N}),this._inflate[Z_]=this,this._inflate[X1]=0,this._inflate[d2]=[],this._inflate.on("error",gI),this._inflate.on("data",JT)}this._inflate[ng]=h,this._inflate.write(l),f&&this._inflate.write(mI),this._inflate.flush(()=>{let t=this._inflate[HD];if(t){this._inflate.close(),this._inflate=null,h(t);return}let N=XT.concat(this._inflate[d2],this._inflate[X1]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[X1]=0,this._inflate[d2]=[],f&&this.params[`${E}_no_context_takeover`]&&this._inflate.reset()),h(null,N)})}_compress(l,f,h){let E=this._isServer?"server":"client";if(!this._deflate){let t=`${E}_max_window_bits`,N=typeof this.params[t]!="number"?tg.Z_DEFAULT_WINDOWBITS:this.params[t];this._deflate=tg.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:N}),this._deflate[X1]=0,this._deflate[d2]=[],this._deflate.on("error",vI),this._deflate.on("data",yI)}this._deflate[ng]=h,this._deflate.write(l),this._deflate.flush(tg.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let t=XT.concat(this._deflate[d2],this._deflate[X1]);f&&(t=t.slice(0,t.length-4)),this._deflate[ng]=null,this._deflate[X1]=0,this._deflate[d2]=[],f&&this.params[`${E}_no_context_takeover`]&&this._deflate.reset(),h(null,t)})}};ZT.exports=qD;function yI(o){this[d2].push(o),this[X1]+=o.length}function JT(o){if(this[X1]+=o.length,this[Z_]._maxPayload<1||this[X1]<=this[Z_]._maxPayload){this[d2].push(o);return}this[HD]=new RangeError("Max payload size exceeded"),this[HD][QT]=1009,this.removeListener("data",JT),this.reset()}function gI(o){this[Z_]._inflate=null,o[QT]=1007,this[ng](o)}});var VD=nt((QH,WD)=>{"use strict";function $T(o){return o>=1e3&&o<=1014&&o!==1004&&o!==1005&&o!==1006||o>=3e3&&o<=4999}function eC(o){let l=o.length,f=0;for(;f=l||(o[f+1]&192)!==128||(o[f+2]&192)!==128||o[f]===224&&(o[f+1]&224)===128||o[f]===237&&(o[f+1]&224)===160)return!1;f+=3}else if((o[f]&248)===240){if(f+3>=l||(o[f+1]&192)!==128||(o[f+2]&192)!==128||(o[f+3]&192)!==128||o[f]===240&&(o[f+1]&240)===128||o[f]===244&&o[f+1]>143||o[f]>244)return!1;f+=4}else return!1;return!0}try{let o=hi("utf-8-validate");typeof o=="object"&&(o=o.Validation.isValidUTF8),WD.exports={isValidStatusCode:$T,isValidUTF8(l){return l.length<150?eC(l):o(l)}}}catch{WD.exports={isValidStatusCode:$T,isValidUTF8:eC}}});var XD=nt((JH,oC)=>{"use strict";var{Writable:_I}=hi("stream"),tC=rg(),{BINARY_TYPES:EI,EMPTY_BUFFER:DI,kStatusCode:wI,kWebSocket:SI}=th(),{concat:GD,toArrayBuffer:TI,unmask:CI}=eg(),{isValidStatusCode:xI,isValidUTF8:nC}=VD(),ig=0,rC=1,iC=2,uC=3,YD=4,RI=5,KD=class extends _I{constructor(l,f,h,E){super(),this._binaryType=l||EI[0],this[SI]=void 0,this._extensions=f||{},this._isServer=!!h,this._maxPayload=E|0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._state=ig,this._loop=!1}_write(l,f,h){if(this._opcode===8&&this._state==ig)return h();this._bufferedBytes+=l.length,this._buffers.push(l),this.startLoop(h)}consume(l){if(this._bufferedBytes-=l,l===this._buffers[0].length)return this._buffers.shift();if(l=h.length?f.set(this._buffers.shift(),E):(f.set(new Uint8Array(h.buffer,h.byteOffset,l),E),this._buffers[0]=h.slice(l)),l-=h.length}while(l>0);return f}startLoop(l){let f;this._loop=!0;do switch(this._state){case ig:f=this.getInfo();break;case rC:f=this.getPayloadLength16();break;case iC:f=this.getPayloadLength64();break;case uC:this.getMask();break;case YD:f=this.getData(l);break;default:this._loop=!1;return}while(this._loop);l(f)}getInfo(){if(this._bufferedBytes<2){this._loop=!1;return}let l=this.consume(2);if((l[0]&48)!==0)return this._loop=!1,Ko(RangeError,"RSV2 and RSV3 must be clear",!0,1002);let f=(l[0]&64)===64;if(f&&!this._extensions[tC.extensionName])return this._loop=!1,Ko(RangeError,"RSV1 must be clear",!0,1002);if(this._fin=(l[0]&128)===128,this._opcode=l[0]&15,this._payloadLength=l[1]&127,this._opcode===0){if(f)return this._loop=!1,Ko(RangeError,"RSV1 must be clear",!0,1002);if(!this._fragmented)return this._loop=!1,Ko(RangeError,"invalid opcode 0",!0,1002);this._opcode=this._fragmented}else if(this._opcode===1||this._opcode===2){if(this._fragmented)return this._loop=!1,Ko(RangeError,`invalid opcode ${this._opcode}`,!0,1002);this._compressed=f}else if(this._opcode>7&&this._opcode<11){if(!this._fin)return this._loop=!1,Ko(RangeError,"FIN must be set",!0,1002);if(f)return this._loop=!1,Ko(RangeError,"RSV1 must be clear",!0,1002);if(this._payloadLength>125)return this._loop=!1,Ko(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002)}else return this._loop=!1,Ko(RangeError,`invalid opcode ${this._opcode}`,!0,1002);if(!this._fin&&!this._fragmented&&(this._fragmented=this._opcode),this._masked=(l[1]&128)===128,this._isServer){if(!this._masked)return this._loop=!1,Ko(RangeError,"MASK must be set",!0,1002)}else if(this._masked)return this._loop=!1,Ko(RangeError,"MASK must be clear",!0,1002);if(this._payloadLength===126)this._state=rC;else if(this._payloadLength===127)this._state=iC;else return this.haveLength()}getPayloadLength16(){if(this._bufferedBytes<2){this._loop=!1;return}return this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength()}getPayloadLength64(){if(this._bufferedBytes<8){this._loop=!1;return}let l=this.consume(8),f=l.readUInt32BE(0);return f>Math.pow(2,53-32)-1?(this._loop=!1,Ko(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009)):(this._payloadLength=f*Math.pow(2,32)+l.readUInt32BE(4),this.haveLength())}haveLength(){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0))return this._loop=!1,Ko(RangeError,"Max payload size exceeded",!1,1009);this._masked?this._state=uC:this._state=YD}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=YD}getData(l){let f=DI;if(this._payloadLength){if(this._bufferedBytes7)return this.controlMessage(f);if(this._compressed){this._state=RI,this.decompress(f,l);return}return f.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(f)),this.dataMessage()}decompress(l,f){this._extensions[tC.extensionName].decompress(l,this._fin,(E,t)=>{if(E)return f(E);if(t.length){if(this._messageLength+=t.length,this._messageLength>this._maxPayload&&this._maxPayload>0)return f(Ko(RangeError,"Max payload size exceeded",!1,1009));this._fragments.push(t)}let N=this.dataMessage();if(N)return f(N);this.startLoop(f)})}dataMessage(){if(this._fin){let l=this._messageLength,f=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],this._opcode===2){let h;this._binaryType==="nodebuffer"?h=GD(f,l):this._binaryType==="arraybuffer"?h=TI(GD(f,l)):h=f,this.emit("message",h)}else{let h=GD(f,l);if(!nC(h))return this._loop=!1,Ko(Error,"invalid UTF-8 sequence",!0,1007);this.emit("message",h.toString())}}this._state=ig}controlMessage(l){if(this._opcode===8)if(this._loop=!1,l.length===0)this.emit("conclude",1005,""),this.end();else{if(l.length===1)return Ko(RangeError,"invalid payload length 1",!0,1002);{let f=l.readUInt16BE(0);if(!xI(f))return Ko(RangeError,`invalid status code ${f}`,!0,1002);let h=l.slice(2);if(!nC(h))return Ko(Error,"invalid UTF-8 sequence",!0,1007);this.emit("conclude",f,h.toString()),this.end()}}else this._opcode===9?this.emit("ping",l):this.emit("pong",l);this._state=ig}};oC.exports=KD;function Ko(o,l,f,h){let E=new o(f?`Invalid WebSocket frame: ${l}`:l);return Error.captureStackTrace(E,Ko),E[wI]=h,E}});var QD=nt((ZH,aC)=>{"use strict";var{randomFillSync:AI}=hi("crypto"),lC=rg(),{EMPTY_BUFFER:OI}=th(),{isValidStatusCode:MI}=VD(),{mask:sC,toBuffer:Q1}=eg(),nh=Buffer.alloc(4),jc=class{constructor(l,f){this._extensions=f||{},this._socket=l,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._deflating=!1,this._queue=[]}static frame(l,f){let h=f.mask&&f.readOnly,E=f.mask?6:2,t=l.length;l.length>=65536?(E+=8,t=127):l.length>125&&(E+=2,t=126);let N=Buffer.allocUnsafe(h?l.length+E:E);return N[0]=f.fin?f.opcode|128:f.opcode,f.rsv1&&(N[0]|=64),N[1]=t,t===126?N.writeUInt16BE(l.length,2):t===127&&(N.writeUInt32BE(0,2),N.writeUInt32BE(l.length,6)),f.mask?(AI(nh,0,4),N[1]|=128,N[E-4]=nh[0],N[E-3]=nh[1],N[E-2]=nh[2],N[E-1]=nh[3],h?(sC(l,nh,N,E,l.length),[N]):(sC(l,nh,l,0,l.length),[N,l])):[N,l]}close(l,f,h,E){let t;if(l===void 0)t=OI;else{if(typeof l!="number"||!MI(l))throw new TypeError("First argument must be a valid error code number");if(f===void 0||f==="")t=Buffer.allocUnsafe(2),t.writeUInt16BE(l,0);else{let N=Buffer.byteLength(f);if(N>123)throw new RangeError("The message must not be greater than 123 bytes");t=Buffer.allocUnsafe(2+N),t.writeUInt16BE(l,0),t.write(f,2)}}this._deflating?this.enqueue([this.doClose,t,h,E]):this.doClose(t,h,E)}doClose(l,f,h){this.sendFrame(jc.frame(l,{fin:!0,rsv1:!1,opcode:8,mask:f,readOnly:!1}),h)}ping(l,f,h){let E=Q1(l);if(E.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPing,E,f,Q1.readOnly,h]):this.doPing(E,f,Q1.readOnly,h)}doPing(l,f,h,E){this.sendFrame(jc.frame(l,{fin:!0,rsv1:!1,opcode:9,mask:f,readOnly:h}),E)}pong(l,f,h){let E=Q1(l);if(E.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPong,E,f,Q1.readOnly,h]):this.doPong(E,f,Q1.readOnly,h)}doPong(l,f,h,E){this.sendFrame(jc.frame(l,{fin:!0,rsv1:!1,opcode:10,mask:f,readOnly:h}),E)}send(l,f,h){let E=Q1(l),t=this._extensions[lC.extensionName],N=f.binary?2:1,F=f.compress;if(this._firstFragment?(this._firstFragment=!1,F&&t&&(F=E.length>=t._threshold),this._compress=F):(F=!1,N=0),f.fin&&(this._firstFragment=!0),t){let k={fin:f.fin,rsv1:F,opcode:N,mask:f.mask,readOnly:Q1.readOnly};this._deflating?this.enqueue([this.dispatch,E,this._compress,k,h]):this.dispatch(E,this._compress,k,h)}else this.sendFrame(jc.frame(E,{fin:f.fin,rsv1:!1,opcode:N,mask:f.mask,readOnly:Q1.readOnly}),h)}dispatch(l,f,h,E){if(!f){this.sendFrame(jc.frame(l,h),E);return}let t=this._extensions[lC.extensionName];this._bufferedBytes+=l.length,this._deflating=!0,t.compress(l,h.fin,(N,F)=>{if(this._socket.destroyed){let k=new Error("The socket was closed while data was being compressed");typeof E=="function"&&E(k);for(let x=0;x{"use strict";var tm=class{constructor(l,f){this.target=f,this.type=l}},JD=class extends tm{constructor(l,f){super("message",f),this.data=l}},ZD=class extends tm{constructor(l,f,h){super("close",h),this.wasClean=h._closeFrameReceived&&h._closeFrameSent,this.reason=f,this.code=l}},$D=class extends tm{constructor(l){super("open",l)}},e3=class extends tm{constructor(l,f){super("error",f),this.message=l.message,this.error=l}},kI={addEventListener(o,l,f){if(typeof l!="function")return;function h(k){l.call(this,new JD(k,this))}function E(k,x){l.call(this,new ZD(k,x,this))}function t(k){l.call(this,new e3(k,this))}function N(){l.call(this,new $D(this))}let F=f&&f.once?"once":"on";o==="message"?(h._listener=l,this[F](o,h)):o==="close"?(E._listener=l,this[F](o,E)):o==="error"?(t._listener=l,this[F](o,t)):o==="open"?(N._listener=l,this[F](o,N)):this[F](o,l)},removeEventListener(o,l){let f=this.listeners(o);for(let h=0;h{"use strict";var ug=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function zc(o,l,f){o[l]===void 0?o[l]=[f]:o[l].push(f)}function NI(o){let l=Object.create(null);if(o===void 0||o==="")return l;let f=Object.create(null),h=!1,E=!1,t=!1,N,F,k=-1,x=-1,j=0;for(;j{let f=o[l];return Array.isArray(f)||(f=[f]),f.map(h=>[l].concat(Object.keys(h).map(E=>{let t=h[E];return Array.isArray(t)||(t=[t]),t.map(N=>N===!0?E:`${E}=${N}`).join("; ")})).join("; ")).join(", ")}).join(", ")}dC.exports={format:LI,parse:NI}});var o3=nt((tq,wC)=>{"use strict";var FI=hi("events"),PI=hi("https"),II=hi("http"),vC=hi("net"),bI=hi("tls"),{randomBytes:BI,createHash:UI}=hi("crypto"),{URL:n3}=hi("url"),p2=rg(),jI=XD(),zI=QD(),{BINARY_TYPES:pC,EMPTY_BUFFER:r3,GUID:HI,kStatusCode:qI,kWebSocket:ta,NOOP:mC}=th(),{addEventListener:WI,removeEventListener:VI}=cC(),{format:GI,parse:YI}=t3(),{toBuffer:KI}=eg(),yC=["CONNECTING","OPEN","CLOSING","CLOSED"],i3=[8,13],XI=30*1e3,ji=class extends FI{constructor(l,f,h){super(),this._binaryType=pC[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage="",this._closeTimer=null,this._extensions={},this._protocol="",this._readyState=ji.CONNECTING,this._receiver=null,this._sender=null,this._socket=null,l!==null?(this._bufferedAmount=0,this._isServer=!1,this._redirects=0,Array.isArray(f)?f=f.join(", "):typeof f=="object"&&f!==null&&(h=f,f=void 0),gC(this,l,f,h)):this._isServer=!0}get binaryType(){return this._binaryType}set binaryType(l){!pC.includes(l)||(this._binaryType=l,this._receiver&&(this._receiver._binaryType=l))}get bufferedAmount(){return this._socket?this._socket._writableState.length+this._sender._bufferedBytes:this._bufferedAmount}get extensions(){return Object.keys(this._extensions).join()}get protocol(){return this._protocol}get readyState(){return this._readyState}get url(){return this._url}setSocket(l,f,h){let E=new jI(this.binaryType,this._extensions,this._isServer,h);this._sender=new zI(l,this._extensions),this._receiver=E,this._socket=l,E[ta]=this,l[ta]=this,E.on("conclude",ZI),E.on("drain",$I),E.on("error",eb),E.on("message",tb),E.on("ping",nb),E.on("pong",rb),l.setTimeout(0),l.setNoDelay(),f.length>0&&l.unshift(f),l.on("close",_C),l.on("data",$_),l.on("end",EC),l.on("error",DC),this._readyState=ji.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=ji.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[p2.extensionName]&&this._extensions[p2.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=ji.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(l,f){if(this.readyState!==ji.CLOSED){if(this.readyState===ji.CONNECTING){let h="WebSocket was closed before the connection was established";return J1(this,this._req,h)}if(this.readyState===ji.CLOSING){this._closeFrameSent&&this._closeFrameReceived&&this._socket.end();return}this._readyState=ji.CLOSING,this._sender.close(l,f,!this._isServer,h=>{h||(this._closeFrameSent=!0,this._closeFrameReceived&&this._socket.end())}),this._closeTimer=setTimeout(this._socket.destroy.bind(this._socket),XI)}}ping(l,f,h){if(this.readyState===ji.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof l=="function"?(h=l,l=f=void 0):typeof f=="function"&&(h=f,f=void 0),typeof l=="number"&&(l=l.toString()),this.readyState!==ji.OPEN){u3(this,l,h);return}f===void 0&&(f=!this._isServer),this._sender.ping(l||r3,f,h)}pong(l,f,h){if(this.readyState===ji.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof l=="function"?(h=l,l=f=void 0):typeof f=="function"&&(h=f,f=void 0),typeof l=="number"&&(l=l.toString()),this.readyState!==ji.OPEN){u3(this,l,h);return}f===void 0&&(f=!this._isServer),this._sender.pong(l||r3,f,h)}send(l,f,h){if(this.readyState===ji.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof f=="function"&&(h=f,f={}),typeof l=="number"&&(l=l.toString()),this.readyState!==ji.OPEN){u3(this,l,h);return}let E={binary:typeof l!="string",mask:!this._isServer,compress:!0,fin:!0,...f};this._extensions[p2.extensionName]||(E.compress=!1),this._sender.send(l||r3,E,h)}terminate(){if(this.readyState!==ji.CLOSED){if(this.readyState===ji.CONNECTING){let l="WebSocket was closed before the connection was established";return J1(this,this._req,l)}this._socket&&(this._readyState=ji.CLOSING,this._socket.destroy())}}};yC.forEach((o,l)=>{let f={enumerable:!0,value:l};Object.defineProperty(ji.prototype,o,f),Object.defineProperty(ji,o,f)});["binaryType","bufferedAmount","extensions","protocol","readyState","url"].forEach(o=>{Object.defineProperty(ji.prototype,o,{enumerable:!0})});["open","error","close","message"].forEach(o=>{Object.defineProperty(ji.prototype,`on${o}`,{configurable:!0,enumerable:!0,get(){let l=this.listeners(o);for(let f=0;f{J1(o,V,"Opening handshake has timed out")}),V.on("error",re=>{V===null||V.aborted||(V=o._req=null,o._readyState=ji.CLOSING,o.emit("error",re),o.emitClose())}),V.on("response",re=>{let y=re.headers.location,me=re.statusCode;if(y&&E.followRedirects&&me>=300&&me<400){if(++o._redirects>E.maxRedirects){J1(o,V,"Maximum redirects exceeded");return}V.abort();let De=new n3(y,l);gC(o,De,f,h)}else o.emit("unexpected-response",V,re)||J1(o,V,`Unexpected server response: ${re.statusCode}`)}),V.on("upgrade",(re,y,me)=>{if(o.emit("upgrade",re),o.readyState!==ji.CONNECTING)return;V=o._req=null;let De=UI("sha1").update(x+HI).digest("base64");if(re.headers["sec-websocket-accept"]!==De){J1(o,y,"Invalid Sec-WebSocket-Accept header");return}let ge=re.headers["sec-websocket-protocol"],ae=(f||"").split(/, */),we;if(!f&&ge?we="Server sent a subprotocol but none was requested":f&&!ge?we="Server sent no subprotocol":ge&&!ae.includes(ge)&&(we="Server sent an invalid subprotocol"),we){J1(o,y,we);return}if(ge&&(o._protocol=ge),q)try{let he=YI(re.headers["sec-websocket-extensions"]);he[p2.extensionName]&&(q.accept(he[p2.extensionName]),o._extensions[p2.extensionName]=q)}catch{J1(o,y,"Invalid Sec-WebSocket-Extensions header");return}o.setSocket(y,me,E.maxPayload)})}function QI(o){return o.path=o.socketPath,vC.connect(o)}function JI(o){return o.path=void 0,!o.servername&&o.servername!==""&&(o.servername=vC.isIP(o.host)?"":o.host),bI.connect(o)}function J1(o,l,f){o._readyState=ji.CLOSING;let h=new Error(f);Error.captureStackTrace(h,J1),l.setHeader?(l.abort(),l.socket&&!l.socket.destroyed&&l.socket.destroy(),l.once("abort",o.emitClose.bind(o)),o.emit("error",h)):(l.destroy(h),l.once("error",o.emit.bind(o,"error")),l.once("close",o.emitClose.bind(o)))}function u3(o,l,f){if(l){let h=KI(l).length;o._socket?o._sender._bufferedBytes+=h:o._bufferedAmount+=h}if(f){let h=new Error(`WebSocket is not open: readyState ${o.readyState} (${yC[o.readyState]})`);f(h)}}function ZI(o,l){let f=this[ta];f._socket.removeListener("data",$_),f._socket.resume(),f._closeFrameReceived=!0,f._closeMessage=l,f._closeCode=o,o===1005?f.close():f.close(o,l)}function $I(){this[ta]._socket.resume()}function eb(o){let l=this[ta];l._socket.removeListener("data",$_),l._readyState=ji.CLOSING,l._closeCode=o[qI],l.emit("error",o),l._socket.destroy()}function hC(){this[ta].emitClose()}function tb(o){this[ta].emit("message",o)}function nb(o){let l=this[ta];l.pong(o,!l._isServer,mC),l.emit("ping",o)}function rb(o){this[ta].emit("pong",o)}function _C(){let o=this[ta];this.removeListener("close",_C),this.removeListener("end",EC),o._readyState=ji.CLOSING,o._socket.read(),o._receiver.end(),this.removeListener("data",$_),this[ta]=void 0,clearTimeout(o._closeTimer),o._receiver._writableState.finished||o._receiver._writableState.errorEmitted?o.emitClose():(o._receiver.on("error",hC),o._receiver.on("finish",hC))}function $_(o){this[ta]._receiver.write(o)||this.pause()}function EC(){let o=this[ta];o._readyState=ji.CLOSING,o._receiver.end(),this.end()}function DC(){let o=this[ta];this.removeListener("error",DC),this.on("error",mC),o&&(o._readyState=ji.CLOSING,this.destroy())}});var xC=nt((nq,CC)=>{"use strict";var{Duplex:ib}=hi("stream");function SC(o){o.emit("close")}function ub(){!this.destroyed&&this._writableState.finished&&this.destroy()}function TC(o){this.removeListener("error",TC),this.destroy(),this.listenerCount("error")===0&&this.emit("error",o)}function ob(o,l){let f=!0;function h(){f&&o._socket.resume()}o.readyState===o.CONNECTING?o.once("open",function(){o._receiver.removeAllListeners("drain"),o._receiver.on("drain",h)}):(o._receiver.removeAllListeners("drain"),o._receiver.on("drain",h));let E=new ib({...l,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return o.on("message",function(N){E.push(N)||(f=!1,o._socket.pause())}),o.once("error",function(N){E.destroyed||E.destroy(N)}),o.once("close",function(){E.destroyed||E.push(null)}),E._destroy=function(t,N){if(o.readyState===o.CLOSED){N(t),process.nextTick(SC,E);return}let F=!1;o.once("error",function(x){F=!0,N(x)}),o.once("close",function(){F||N(t),process.nextTick(SC,E)}),o.terminate()},E._final=function(t){if(o.readyState===o.CONNECTING){o.once("open",function(){E._final(t)});return}o._socket!==null&&(o._socket._writableState.finished?(t(),E._readableState.endEmitted&&E.destroy()):(o._socket.once("finish",function(){t()}),o.close()))},E._read=function(){o.readyState===o.OPEN&&!f&&(f=!0,o._receiver._writableState.needDrain||o._socket.resume())},E._write=function(t,N,F){if(o.readyState===o.CONNECTING){o.once("open",function(){E._write(t,N,F)});return}o.send(t,F)},E.on("end",ub),E.on("error",TC),E}CC.exports=ob});var AC=nt((rq,RC)=>{"use strict";var lb=hi("events"),{createHash:sb}=hi("crypto"),{createServer:ab,STATUS_CODES:l3}=hi("http"),rh=rg(),fb=o3(),{format:db,parse:pb}=t3(),{GUID:hb,kWebSocket:vb}=th(),mb=/^[+/0-9A-Za-z]{22}==$/,s3=class extends lb{constructor(l,f){if(super(),l={maxPayload:100*1024*1024,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,...l},l.port==null&&!l.server&&!l.noServer)throw new TypeError('One of the "port", "server", or "noServer" options must be specified');if(l.port!=null?(this._server=ab((h,E)=>{let t=l3[426];E.writeHead(426,{"Content-Length":t.length,"Content-Type":"text/plain"}),E.end(t)}),this._server.listen(l.port,l.host,l.backlog,f)):l.server&&(this._server=l.server),this._server){let h=this.emit.bind(this,"connection");this._removeListeners=yb(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(E,t,N)=>{this.handleUpgrade(E,t,N,h)}})}l.perMessageDeflate===!0&&(l.perMessageDeflate={}),l.clientTracking&&(this.clients=new Set),this.options=l}address(){if(this.options.noServer)throw new Error('The server is operating in "noServer" mode');return this._server?this._server.address():null}close(l){if(l&&this.once("close",l),this.clients)for(let h of this.clients)h.terminate();let f=this._server;if(f&&(this._removeListeners(),this._removeListeners=this._server=null,this.options.port!=null)){f.close(()=>this.emit("close"));return}process.nextTick(gb,this)}shouldHandle(l){if(this.options.path){let f=l.url.indexOf("?");if((f!==-1?l.url.slice(0,f):l.url)!==this.options.path)return!1}return!0}handleUpgrade(l,f,h,E){f.on("error",a3);let t=l.headers["sec-websocket-key"]!==void 0?l.headers["sec-websocket-key"].trim():!1,N=+l.headers["sec-websocket-version"],F={};if(l.method!=="GET"||l.headers.upgrade.toLowerCase()!=="websocket"||!t||!mb.test(t)||N!==8&&N!==13||!this.shouldHandle(l))return e4(f,400);if(this.options.perMessageDeflate){let k=new rh(this.options.perMessageDeflate,!0,this.options.maxPayload);try{let x=pb(l.headers["sec-websocket-extensions"]);x[rh.extensionName]&&(k.accept(x[rh.extensionName]),F[rh.extensionName]=k)}catch{return e4(f,400)}}if(this.options.verifyClient){let k={origin:l.headers[`${N===8?"sec-websocket-origin":"origin"}`],secure:!!(l.socket.authorized||l.socket.encrypted),req:l};if(this.options.verifyClient.length===2){this.options.verifyClient(k,(x,j,q,V)=>{if(!x)return e4(f,j||401,q,V);this.completeUpgrade(t,F,l,f,h,E)});return}if(!this.options.verifyClient(k))return e4(f,401)}this.completeUpgrade(t,F,l,f,h,E)}completeUpgrade(l,f,h,E,t,N){if(!E.readable||!E.writable)return E.destroy();if(E[vb])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");let k=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${sb("sha1").update(l+hb).digest("base64")}`],x=new fb(null),j=h.headers["sec-websocket-protocol"];if(j&&(j=j.split(",").map(_b),this.options.handleProtocols?j=this.options.handleProtocols(j,h):j=j[0],j&&(k.push(`Sec-WebSocket-Protocol: ${j}`),x._protocol=j)),f[rh.extensionName]){let q=f[rh.extensionName].params,V=db({[rh.extensionName]:[q]});k.push(`Sec-WebSocket-Extensions: ${V}`),x._extensions=f}this.emit("headers",k,h),E.write(k.concat(`\r +`).join(`\r +`)),E.removeListener("error",a3),x.setSocket(E,t,this.options.maxPayload),this.clients&&(this.clients.add(x),x.on("close",()=>this.clients.delete(x))),N(x,h)}};RC.exports=s3;function yb(o,l){for(let f of Object.keys(l))o.on(f,l[f]);return function(){for(let h of Object.keys(l))o.removeListener(h,l[h])}}function gb(o){o.emit("close")}function a3(){this.destroy()}function e4(o,l,f,h){o.writable&&(f=f||l3[l],h={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(f),...h},o.write(`HTTP/1.1 ${l} ${l3[l]}\r +`+Object.keys(h).map(E=>`${E}: ${h[E]}`).join(`\r +`)+`\r +\r +`+f)),o.removeListener("error",a3),o.destroy()}function _b(o){return o.trim()}});var MC=nt((iq,OC)=>{"use strict";var og=o3();og.createWebSocketStream=xC();og.Server=AC();og.Receiver=XD();og.Sender=QD();OC.exports=og});var kC=nt(t4=>{"use strict";var Eb=t4&&t4.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(t4,"__esModule",{value:!0});var Db=Eb(MC()),lg=global;lg.WebSocket||(lg.WebSocket=Db.default);lg.window||(lg.window=global);lg.window.__REACT_DEVTOOLS_COMPONENT_FILTERS__=[{type:1,value:7,isEnabled:!0},{type:2,value:"InternalApp",isEnabled:!0,isValid:!0},{type:2,value:"InternalAppContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStdoutContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStderrContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStdinContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalFocusContext",isEnabled:!0,isValid:!0}]});var NC=nt((n4,f3)=>{(function(o,l){typeof n4=="object"&&typeof f3=="object"?f3.exports=l():typeof define=="function"&&define.amd?define([],l):typeof n4=="object"?n4.ReactDevToolsBackend=l():o.ReactDevToolsBackend=l()})(window,function(){return function(o){var l={};function f(h){if(l[h])return l[h].exports;var E=l[h]={i:h,l:!1,exports:{}};return o[h].call(E.exports,E,E.exports,f),E.l=!0,E.exports}return f.m=o,f.c=l,f.d=function(h,E,t){f.o(h,E)||Object.defineProperty(h,E,{enumerable:!0,get:t})},f.r=function(h){typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(h,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(h,"__esModule",{value:!0})},f.t=function(h,E){if(1&E&&(h=f(h)),8&E||4&E&&typeof h=="object"&&h&&h.__esModule)return h;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:h}),2&E&&typeof h!="string")for(var N in h)f.d(t,N,function(F){return h[F]}.bind(null,N));return t},f.n=function(h){var E=h&&h.__esModule?function(){return h.default}:function(){return h};return f.d(E,"a",E),E},f.o=function(h,E){return Object.prototype.hasOwnProperty.call(h,E)},f.p="",f(f.s=20)}([function(o,l,f){"use strict";o.exports=f(12)},function(o,l,f){"use strict";var h=Object.getOwnPropertySymbols,E=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable;function N(F){if(F==null)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(F)}o.exports=function(){try{if(!Object.assign)return!1;var F=new String("abc");if(F[5]="de",Object.getOwnPropertyNames(F)[0]==="5")return!1;for(var k={},x=0;x<10;x++)k["_"+String.fromCharCode(x)]=x;if(Object.getOwnPropertyNames(k).map(function(q){return k[q]}).join("")!=="0123456789")return!1;var j={};return"abcdefghijklmnopqrst".split("").forEach(function(q){j[q]=q}),Object.keys(Object.assign({},j)).join("")==="abcdefghijklmnopqrst"}catch{return!1}}()?Object.assign:function(F,k){for(var x,j,q=N(F),V=1;V"u"?"undefined":E(self))=="object"&&self&&self.Object===Object&&self,V=j||q||Function("return this")(),re=Object.prototype.toString,y=Math.max,me=Math.min,De=function(){return V.Date.now()};function ge(ve,ue,Ae){var ze,We,gt,_t,Qe,ot,Ve=0,Pt=!1,Jt=!1,it=!0;if(typeof ve!="function")throw new TypeError("Expected a function");function J(At){var nn=ze,an=We;return ze=We=void 0,Ve=At,_t=ve.apply(an,nn)}function ce(At){return Ve=At,Qe=setTimeout(le,ue),Pt?J(At):_t}function Re(At){var nn=At-ot;return ot===void 0||nn>=ue||nn<0||Jt&&At-Ve>=gt}function le(){var At=De();if(Re(At))return He(At);Qe=setTimeout(le,function(nn){var an=ue-(nn-ot);return Jt?me(an,gt-(nn-Ve)):an}(At))}function He(At){return Qe=void 0,it&&ze?J(At):(ze=We=void 0,_t)}function dt(){var At=De(),nn=Re(At);if(ze=arguments,We=this,ot=At,nn){if(Qe===void 0)return ce(ot);if(Jt)return Qe=setTimeout(le,ue),J(ot)}return Qe===void 0&&(Qe=setTimeout(le,ue)),_t}return ue=he(ue)||0,ae(Ae)&&(Pt=!!Ae.leading,gt=(Jt="maxWait"in Ae)?y(he(Ae.maxWait)||0,ue):gt,it="trailing"in Ae?!!Ae.trailing:it),dt.cancel=function(){Qe!==void 0&&clearTimeout(Qe),Ve=0,ze=ot=We=Qe=void 0},dt.flush=function(){return Qe===void 0?_t:He(De())},dt}function ae(ve){var ue=E(ve);return!!ve&&(ue=="object"||ue=="function")}function we(ve){return E(ve)=="symbol"||function(ue){return!!ue&&E(ue)=="object"}(ve)&&re.call(ve)=="[object Symbol]"}function he(ve){if(typeof ve=="number")return ve;if(we(ve))return NaN;if(ae(ve)){var ue=typeof ve.valueOf=="function"?ve.valueOf():ve;ve=ae(ue)?ue+"":ue}if(typeof ve!="string")return ve===0?ve:+ve;ve=ve.replace(t,"");var Ae=F.test(ve);return Ae||k.test(ve)?x(ve.slice(2),Ae?2:8):N.test(ve)?NaN:+ve}o.exports=function(ve,ue,Ae){var ze=!0,We=!0;if(typeof ve!="function")throw new TypeError("Expected a function");return ae(Ae)&&(ze="leading"in Ae?!!Ae.leading:ze,We="trailing"in Ae?!!Ae.trailing:We),ge(ve,ue,{leading:ze,maxWait:ue,trailing:We})}}).call(this,f(4))},function(o,l,f){(function(h){function E(J){return(E=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(ce){return typeof ce}:function(ce){return ce&&typeof Symbol=="function"&&ce.constructor===Symbol&&ce!==Symbol.prototype?"symbol":typeof ce})(J)}var t;l=o.exports=y,t=(h===void 0?"undefined":E(h))==="object"&&h.env&&h.env.NODE_DEBUG&&/\bsemver\b/i.test(h.env.NODE_DEBUG)?function(){var J=Array.prototype.slice.call(arguments,0);J.unshift("SEMVER"),console.log.apply(console,J)}:function(){},l.SEMVER_SPEC_VERSION="2.0.0";var N=Number.MAX_SAFE_INTEGER||9007199254740991,F=l.re=[],k=l.src=[],x=l.tokens={},j=0;function q(J){x[J]=j++}q("NUMERICIDENTIFIER"),k[x.NUMERICIDENTIFIER]="0|[1-9]\\d*",q("NUMERICIDENTIFIERLOOSE"),k[x.NUMERICIDENTIFIERLOOSE]="[0-9]+",q("NONNUMERICIDENTIFIER"),k[x.NONNUMERICIDENTIFIER]="\\d*[a-zA-Z-][a-zA-Z0-9-]*",q("MAINVERSION"),k[x.MAINVERSION]="("+k[x.NUMERICIDENTIFIER]+")\\.("+k[x.NUMERICIDENTIFIER]+")\\.("+k[x.NUMERICIDENTIFIER]+")",q("MAINVERSIONLOOSE"),k[x.MAINVERSIONLOOSE]="("+k[x.NUMERICIDENTIFIERLOOSE]+")\\.("+k[x.NUMERICIDENTIFIERLOOSE]+")\\.("+k[x.NUMERICIDENTIFIERLOOSE]+")",q("PRERELEASEIDENTIFIER"),k[x.PRERELEASEIDENTIFIER]="(?:"+k[x.NUMERICIDENTIFIER]+"|"+k[x.NONNUMERICIDENTIFIER]+")",q("PRERELEASEIDENTIFIERLOOSE"),k[x.PRERELEASEIDENTIFIERLOOSE]="(?:"+k[x.NUMERICIDENTIFIERLOOSE]+"|"+k[x.NONNUMERICIDENTIFIER]+")",q("PRERELEASE"),k[x.PRERELEASE]="(?:-("+k[x.PRERELEASEIDENTIFIER]+"(?:\\."+k[x.PRERELEASEIDENTIFIER]+")*))",q("PRERELEASELOOSE"),k[x.PRERELEASELOOSE]="(?:-?("+k[x.PRERELEASEIDENTIFIERLOOSE]+"(?:\\."+k[x.PRERELEASEIDENTIFIERLOOSE]+")*))",q("BUILDIDENTIFIER"),k[x.BUILDIDENTIFIER]="[0-9A-Za-z-]+",q("BUILD"),k[x.BUILD]="(?:\\+("+k[x.BUILDIDENTIFIER]+"(?:\\."+k[x.BUILDIDENTIFIER]+")*))",q("FULL"),q("FULLPLAIN"),k[x.FULLPLAIN]="v?"+k[x.MAINVERSION]+k[x.PRERELEASE]+"?"+k[x.BUILD]+"?",k[x.FULL]="^"+k[x.FULLPLAIN]+"$",q("LOOSEPLAIN"),k[x.LOOSEPLAIN]="[v=\\s]*"+k[x.MAINVERSIONLOOSE]+k[x.PRERELEASELOOSE]+"?"+k[x.BUILD]+"?",q("LOOSE"),k[x.LOOSE]="^"+k[x.LOOSEPLAIN]+"$",q("GTLT"),k[x.GTLT]="((?:<|>)?=?)",q("XRANGEIDENTIFIERLOOSE"),k[x.XRANGEIDENTIFIERLOOSE]=k[x.NUMERICIDENTIFIERLOOSE]+"|x|X|\\*",q("XRANGEIDENTIFIER"),k[x.XRANGEIDENTIFIER]=k[x.NUMERICIDENTIFIER]+"|x|X|\\*",q("XRANGEPLAIN"),k[x.XRANGEPLAIN]="[v=\\s]*("+k[x.XRANGEIDENTIFIER]+")(?:\\.("+k[x.XRANGEIDENTIFIER]+")(?:\\.("+k[x.XRANGEIDENTIFIER]+")(?:"+k[x.PRERELEASE]+")?"+k[x.BUILD]+"?)?)?",q("XRANGEPLAINLOOSE"),k[x.XRANGEPLAINLOOSE]="[v=\\s]*("+k[x.XRANGEIDENTIFIERLOOSE]+")(?:\\.("+k[x.XRANGEIDENTIFIERLOOSE]+")(?:\\.("+k[x.XRANGEIDENTIFIERLOOSE]+")(?:"+k[x.PRERELEASELOOSE]+")?"+k[x.BUILD]+"?)?)?",q("XRANGE"),k[x.XRANGE]="^"+k[x.GTLT]+"\\s*"+k[x.XRANGEPLAIN]+"$",q("XRANGELOOSE"),k[x.XRANGELOOSE]="^"+k[x.GTLT]+"\\s*"+k[x.XRANGEPLAINLOOSE]+"$",q("COERCE"),k[x.COERCE]="(^|[^\\d])(\\d{1,16})(?:\\.(\\d{1,16}))?(?:\\.(\\d{1,16}))?(?:$|[^\\d])",q("COERCERTL"),F[x.COERCERTL]=new RegExp(k[x.COERCE],"g"),q("LONETILDE"),k[x.LONETILDE]="(?:~>?)",q("TILDETRIM"),k[x.TILDETRIM]="(\\s*)"+k[x.LONETILDE]+"\\s+",F[x.TILDETRIM]=new RegExp(k[x.TILDETRIM],"g"),q("TILDE"),k[x.TILDE]="^"+k[x.LONETILDE]+k[x.XRANGEPLAIN]+"$",q("TILDELOOSE"),k[x.TILDELOOSE]="^"+k[x.LONETILDE]+k[x.XRANGEPLAINLOOSE]+"$",q("LONECARET"),k[x.LONECARET]="(?:\\^)",q("CARETTRIM"),k[x.CARETTRIM]="(\\s*)"+k[x.LONECARET]+"\\s+",F[x.CARETTRIM]=new RegExp(k[x.CARETTRIM],"g"),q("CARET"),k[x.CARET]="^"+k[x.LONECARET]+k[x.XRANGEPLAIN]+"$",q("CARETLOOSE"),k[x.CARETLOOSE]="^"+k[x.LONECARET]+k[x.XRANGEPLAINLOOSE]+"$",q("COMPARATORLOOSE"),k[x.COMPARATORLOOSE]="^"+k[x.GTLT]+"\\s*("+k[x.LOOSEPLAIN]+")$|^$",q("COMPARATOR"),k[x.COMPARATOR]="^"+k[x.GTLT]+"\\s*("+k[x.FULLPLAIN]+")$|^$",q("COMPARATORTRIM"),k[x.COMPARATORTRIM]="(\\s*)"+k[x.GTLT]+"\\s*("+k[x.LOOSEPLAIN]+"|"+k[x.XRANGEPLAIN]+")",F[x.COMPARATORTRIM]=new RegExp(k[x.COMPARATORTRIM],"g"),q("HYPHENRANGE"),k[x.HYPHENRANGE]="^\\s*("+k[x.XRANGEPLAIN]+")\\s+-\\s+("+k[x.XRANGEPLAIN]+")\\s*$",q("HYPHENRANGELOOSE"),k[x.HYPHENRANGELOOSE]="^\\s*("+k[x.XRANGEPLAINLOOSE]+")\\s+-\\s+("+k[x.XRANGEPLAINLOOSE]+")\\s*$",q("STAR"),k[x.STAR]="(<|>)?=?\\s*\\*";for(var V=0;V256||!(ce.loose?F[x.LOOSE]:F[x.FULL]).test(J))return null;try{return new y(J,ce)}catch{return null}}function y(J,ce){if(ce&&E(ce)==="object"||(ce={loose:!!ce,includePrerelease:!1}),J instanceof y){if(J.loose===ce.loose)return J;J=J.version}else if(typeof J!="string")throw new TypeError("Invalid Version: "+J);if(J.length>256)throw new TypeError("version is longer than 256 characters");if(!(this instanceof y))return new y(J,ce);t("SemVer",J,ce),this.options=ce,this.loose=!!ce.loose;var Re=J.trim().match(ce.loose?F[x.LOOSE]:F[x.FULL]);if(!Re)throw new TypeError("Invalid Version: "+J);if(this.raw=J,this.major=+Re[1],this.minor=+Re[2],this.patch=+Re[3],this.major>N||this.major<0)throw new TypeError("Invalid major version");if(this.minor>N||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>N||this.patch<0)throw new TypeError("Invalid patch version");Re[4]?this.prerelease=Re[4].split(".").map(function(le){if(/^[0-9]+$/.test(le)){var He=+le;if(He>=0&&He=0;)typeof this.prerelease[Re]=="number"&&(this.prerelease[Re]++,Re=-2);Re===-1&&this.prerelease.push(0)}ce&&(this.prerelease[0]===ce?isNaN(this.prerelease[1])&&(this.prerelease=[ce,0]):this.prerelease=[ce,0]);break;default:throw new Error("invalid increment argument: "+J)}return this.format(),this.raw=this.version,this},l.inc=function(J,ce,Re,le){typeof Re=="string"&&(le=Re,Re=void 0);try{return new y(J,Re).inc(ce,le).version}catch{return null}},l.diff=function(J,ce){if(he(J,ce))return null;var Re=re(J),le=re(ce),He="";if(Re.prerelease.length||le.prerelease.length){He="pre";var dt="prerelease"}for(var At in Re)if((At==="major"||At==="minor"||At==="patch")&&Re[At]!==le[At])return He+At;return dt},l.compareIdentifiers=De;var me=/^[0-9]+$/;function De(J,ce){var Re=me.test(J),le=me.test(ce);return Re&&le&&(J=+J,ce=+ce),J===ce?0:Re&&!le?-1:le&&!Re?1:J0}function we(J,ce,Re){return ge(J,ce,Re)<0}function he(J,ce,Re){return ge(J,ce,Re)===0}function ve(J,ce,Re){return ge(J,ce,Re)!==0}function ue(J,ce,Re){return ge(J,ce,Re)>=0}function Ae(J,ce,Re){return ge(J,ce,Re)<=0}function ze(J,ce,Re,le){switch(ce){case"===":return E(J)==="object"&&(J=J.version),E(Re)==="object"&&(Re=Re.version),J===Re;case"!==":return E(J)==="object"&&(J=J.version),E(Re)==="object"&&(Re=Re.version),J!==Re;case"":case"=":case"==":return he(J,Re,le);case"!=":return ve(J,Re,le);case">":return ae(J,Re,le);case">=":return ue(J,Re,le);case"<":return we(J,Re,le);case"<=":return Ae(J,Re,le);default:throw new TypeError("Invalid operator: "+ce)}}function We(J,ce){if(ce&&E(ce)==="object"||(ce={loose:!!ce,includePrerelease:!1}),J instanceof We){if(J.loose===!!ce.loose)return J;J=J.value}if(!(this instanceof We))return new We(J,ce);t("comparator",J,ce),this.options=ce,this.loose=!!ce.loose,this.parse(J),this.semver===gt?this.value="":this.value=this.operator+this.semver.version,t("comp",this)}l.rcompareIdentifiers=function(J,ce){return De(ce,J)},l.major=function(J,ce){return new y(J,ce).major},l.minor=function(J,ce){return new y(J,ce).minor},l.patch=function(J,ce){return new y(J,ce).patch},l.compare=ge,l.compareLoose=function(J,ce){return ge(J,ce,!0)},l.compareBuild=function(J,ce,Re){var le=new y(J,Re),He=new y(ce,Re);return le.compare(He)||le.compareBuild(He)},l.rcompare=function(J,ce,Re){return ge(ce,J,Re)},l.sort=function(J,ce){return J.sort(function(Re,le){return l.compareBuild(Re,le,ce)})},l.rsort=function(J,ce){return J.sort(function(Re,le){return l.compareBuild(le,Re,ce)})},l.gt=ae,l.lt=we,l.eq=he,l.neq=ve,l.gte=ue,l.lte=Ae,l.cmp=ze,l.Comparator=We;var gt={};function _t(J,ce){if(ce&&E(ce)==="object"||(ce={loose:!!ce,includePrerelease:!1}),J instanceof _t)return J.loose===!!ce.loose&&J.includePrerelease===!!ce.includePrerelease?J:new _t(J.raw,ce);if(J instanceof We)return new _t(J.value,ce);if(!(this instanceof _t))return new _t(J,ce);if(this.options=ce,this.loose=!!ce.loose,this.includePrerelease=!!ce.includePrerelease,this.raw=J,this.set=J.split(/\s*\|\|\s*/).map(function(Re){return this.parseRange(Re.trim())},this).filter(function(Re){return Re.length}),!this.set.length)throw new TypeError("Invalid SemVer Range: "+J);this.format()}function Qe(J,ce){for(var Re=!0,le=J.slice(),He=le.pop();Re&&le.length;)Re=le.every(function(dt){return He.intersects(dt,ce)}),He=le.pop();return Re}function ot(J){return!J||J.toLowerCase()==="x"||J==="*"}function Ve(J,ce,Re,le,He,dt,At,nn,an,On,lr,ln,Vt){return((ce=ot(Re)?"":ot(le)?">="+Re+".0.0":ot(He)?">="+Re+"."+le+".0":">="+ce)+" "+(nn=ot(an)?"":ot(On)?"<"+(+an+1)+".0.0":ot(lr)?"<"+an+"."+(+On+1)+".0":ln?"<="+an+"."+On+"."+lr+"-"+ln:"<="+nn)).trim()}function Pt(J,ce,Re){for(var le=0;le0){var He=J[le].semver;if(He.major===ce.major&&He.minor===ce.minor&&He.patch===ce.patch)return!0}return!1}return!0}function Jt(J,ce,Re){try{ce=new _t(ce,Re)}catch{return!1}return ce.test(J)}function it(J,ce,Re,le){var He,dt,At,nn,an;switch(J=new y(J,le),ce=new _t(ce,le),Re){case">":He=ae,dt=Ae,At=we,nn=">",an=">=";break;case"<":He=we,dt=ue,At=ae,nn="<",an="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(Jt(J,ce,le))return!1;for(var On=0;On=0.0.0")),ln=ln||Er,Vt=Vt||Er,He(Er.semver,ln.semver,le)?ln=Er:At(Er.semver,Vt.semver,le)&&(Vt=Er)}),ln.operator===nn||ln.operator===an||(!Vt.operator||Vt.operator===nn)&&dt(J,Vt.semver)||Vt.operator===an&&At(J,Vt.semver))return!1}return!0}We.prototype.parse=function(J){var ce=this.options.loose?F[x.COMPARATORLOOSE]:F[x.COMPARATOR],Re=J.match(ce);if(!Re)throw new TypeError("Invalid comparator: "+J);this.operator=Re[1]!==void 0?Re[1]:"",this.operator==="="&&(this.operator=""),Re[2]?this.semver=new y(Re[2],this.options.loose):this.semver=gt},We.prototype.toString=function(){return this.value},We.prototype.test=function(J){if(t("Comparator.test",J,this.options.loose),this.semver===gt||J===gt)return!0;if(typeof J=="string")try{J=new y(J,this.options)}catch{return!1}return ze(J,this.operator,this.semver,this.options)},We.prototype.intersects=function(J,ce){if(!(J instanceof We))throw new TypeError("a Comparator is required");var Re;if(ce&&E(ce)==="object"||(ce={loose:!!ce,includePrerelease:!1}),this.operator==="")return this.value===""||(Re=new _t(J.value,ce),Jt(this.value,Re,ce));if(J.operator==="")return J.value===""||(Re=new _t(this.value,ce),Jt(J.semver,Re,ce));var le=!(this.operator!==">="&&this.operator!==">"||J.operator!==">="&&J.operator!==">"),He=!(this.operator!=="<="&&this.operator!=="<"||J.operator!=="<="&&J.operator!=="<"),dt=this.semver.version===J.semver.version,At=!(this.operator!==">="&&this.operator!=="<="||J.operator!==">="&&J.operator!=="<="),nn=ze(this.semver,"<",J.semver,ce)&&(this.operator===">="||this.operator===">")&&(J.operator==="<="||J.operator==="<"),an=ze(this.semver,">",J.semver,ce)&&(this.operator==="<="||this.operator==="<")&&(J.operator===">="||J.operator===">");return le||He||dt&&At||nn||an},l.Range=_t,_t.prototype.format=function(){return this.range=this.set.map(function(J){return J.join(" ").trim()}).join("||").trim(),this.range},_t.prototype.toString=function(){return this.range},_t.prototype.parseRange=function(J){var ce=this.options.loose;J=J.trim();var Re=ce?F[x.HYPHENRANGELOOSE]:F[x.HYPHENRANGE];J=J.replace(Re,Ve),t("hyphen replace",J),J=J.replace(F[x.COMPARATORTRIM],"$1$2$3"),t("comparator trim",J,F[x.COMPARATORTRIM]),J=(J=(J=J.replace(F[x.TILDETRIM],"$1~")).replace(F[x.CARETTRIM],"$1^")).split(/\s+/).join(" ");var le=ce?F[x.COMPARATORLOOSE]:F[x.COMPARATOR],He=J.split(" ").map(function(dt){return function(At,nn){return t("comp",At,nn),At=function(an,On){return an.trim().split(/\s+/).map(function(lr){return function(ln,Vt){t("caret",ln,Vt);var Er=Vt.loose?F[x.CARETLOOSE]:F[x.CARET];return ln.replace(Er,function(S,zt,Xn,vr,jr){var fr;return t("caret",ln,S,zt,Xn,vr,jr),ot(zt)?fr="":ot(Xn)?fr=">="+zt+".0.0 <"+(+zt+1)+".0.0":ot(vr)?fr=zt==="0"?">="+zt+"."+Xn+".0 <"+zt+"."+(+Xn+1)+".0":">="+zt+"."+Xn+".0 <"+(+zt+1)+".0.0":jr?(t("replaceCaret pr",jr),fr=zt==="0"?Xn==="0"?">="+zt+"."+Xn+"."+vr+"-"+jr+" <"+zt+"."+Xn+"."+(+vr+1):">="+zt+"."+Xn+"."+vr+"-"+jr+" <"+zt+"."+(+Xn+1)+".0":">="+zt+"."+Xn+"."+vr+"-"+jr+" <"+(+zt+1)+".0.0"):(t("no pr"),fr=zt==="0"?Xn==="0"?">="+zt+"."+Xn+"."+vr+" <"+zt+"."+Xn+"."+(+vr+1):">="+zt+"."+Xn+"."+vr+" <"+zt+"."+(+Xn+1)+".0":">="+zt+"."+Xn+"."+vr+" <"+(+zt+1)+".0.0"),t("caret return",fr),fr})}(lr,On)}).join(" ")}(At,nn),t("caret",At),At=function(an,On){return an.trim().split(/\s+/).map(function(lr){return function(ln,Vt){var Er=Vt.loose?F[x.TILDELOOSE]:F[x.TILDE];return ln.replace(Er,function(S,zt,Xn,vr,jr){var fr;return t("tilde",ln,S,zt,Xn,vr,jr),ot(zt)?fr="":ot(Xn)?fr=">="+zt+".0.0 <"+(+zt+1)+".0.0":ot(vr)?fr=">="+zt+"."+Xn+".0 <"+zt+"."+(+Xn+1)+".0":jr?(t("replaceTilde pr",jr),fr=">="+zt+"."+Xn+"."+vr+"-"+jr+" <"+zt+"."+(+Xn+1)+".0"):fr=">="+zt+"."+Xn+"."+vr+" <"+zt+"."+(+Xn+1)+".0",t("tilde return",fr),fr})}(lr,On)}).join(" ")}(At,nn),t("tildes",At),At=function(an,On){return t("replaceXRanges",an,On),an.split(/\s+/).map(function(lr){return function(ln,Vt){ln=ln.trim();var Er=Vt.loose?F[x.XRANGELOOSE]:F[x.XRANGE];return ln.replace(Er,function(S,zt,Xn,vr,jr,fr){t("xRange",ln,S,zt,Xn,vr,jr,fr);var zr=ot(Xn),Xt=zr||ot(vr),Du=Xt||ot(jr),c0=Du;return zt==="="&&c0&&(zt=""),fr=Vt.includePrerelease?"-0":"",zr?S=zt===">"||zt==="<"?"<0.0.0-0":"*":zt&&c0?(Xt&&(vr=0),jr=0,zt===">"?(zt=">=",Xt?(Xn=+Xn+1,vr=0,jr=0):(vr=+vr+1,jr=0)):zt==="<="&&(zt="<",Xt?Xn=+Xn+1:vr=+vr+1),S=zt+Xn+"."+vr+"."+jr+fr):Xt?S=">="+Xn+".0.0"+fr+" <"+(+Xn+1)+".0.0"+fr:Du&&(S=">="+Xn+"."+vr+".0"+fr+" <"+Xn+"."+(+vr+1)+".0"+fr),t("xRange return",S),S})}(lr,On)}).join(" ")}(At,nn),t("xrange",At),At=function(an,On){return t("replaceStars",an,On),an.trim().replace(F[x.STAR],"")}(At,nn),t("stars",At),At}(dt,this.options)},this).join(" ").split(/\s+/);return this.options.loose&&(He=He.filter(function(dt){return!!dt.match(le)})),He=He.map(function(dt){return new We(dt,this.options)},this)},_t.prototype.intersects=function(J,ce){if(!(J instanceof _t))throw new TypeError("a Range is required");return this.set.some(function(Re){return Qe(Re,ce)&&J.set.some(function(le){return Qe(le,ce)&&Re.every(function(He){return le.every(function(dt){return He.intersects(dt,ce)})})})})},l.toComparators=function(J,ce){return new _t(J,ce).set.map(function(Re){return Re.map(function(le){return le.value}).join(" ").trim().split(" ")})},_t.prototype.test=function(J){if(!J)return!1;if(typeof J=="string")try{J=new y(J,this.options)}catch{return!1}for(var ce=0;ce":dt.prerelease.length===0?dt.patch++:dt.prerelease.push(0),dt.raw=dt.format();case"":case">=":Re&&!ae(Re,dt)||(Re=dt);break;case"<":case"<=":break;default:throw new Error("Unexpected operation: "+He.operator)}});return Re&&J.test(Re)?Re:null},l.validRange=function(J,ce){try{return new _t(J,ce).range||"*"}catch{return null}},l.ltr=function(J,ce,Re){return it(J,ce,"<",Re)},l.gtr=function(J,ce,Re){return it(J,ce,">",Re)},l.outside=it,l.prerelease=function(J,ce){var Re=re(J,ce);return Re&&Re.prerelease.length?Re.prerelease:null},l.intersects=function(J,ce,Re){return J=new _t(J,Re),ce=new _t(ce,Re),J.intersects(ce)},l.coerce=function(J,ce){if(J instanceof y)return J;if(typeof J=="number"&&(J=String(J)),typeof J!="string")return null;var Re=null;if((ce=ce||{}).rtl){for(var le;(le=F[x.COERCERTL].exec(J))&&(!Re||Re.index+Re[0].length!==J.length);)Re&&le.index+le[0].length===Re.index+Re[0].length||(Re=le),F[x.COERCERTL].lastIndex=le.index+le[1].length+le[2].length;F[x.COERCERTL].lastIndex=-1}else Re=J.match(F[x.COERCE]);return Re===null?null:re(Re[2]+"."+(Re[3]||"0")+"."+(Re[4]||"0"),ce)}}).call(this,f(5))},function(o,l){function f(E){return(f=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(E)}var h;h=function(){return this}();try{h=h||new Function("return this")()}catch{(typeof window>"u"?"undefined":f(window))==="object"&&(h=window)}o.exports=h},function(o,l){var f,h,E=o.exports={};function t(){throw new Error("setTimeout has not been defined")}function N(){throw new Error("clearTimeout has not been defined")}function F(De){if(f===setTimeout)return setTimeout(De,0);if((f===t||!f)&&setTimeout)return f=setTimeout,setTimeout(De,0);try{return f(De,0)}catch{try{return f.call(null,De,0)}catch{return f.call(this,De,0)}}}(function(){try{f=typeof setTimeout=="function"?setTimeout:t}catch{f=t}try{h=typeof clearTimeout=="function"?clearTimeout:N}catch{h=N}})();var k,x=[],j=!1,q=-1;function V(){j&&k&&(j=!1,k.length?x=k.concat(x):q=-1,x.length&&re())}function re(){if(!j){var De=F(V);j=!0;for(var ge=x.length;ge;){for(k=x,x=[];++q1)for(var ae=1;aethis[N])return ve(this,this[y].get(Qe)),!1;var it=this[y].get(Qe).value;return this[q]&&(this[V]||this[q](Qe,it.value)),it.now=Pt,it.maxAge=Ve,it.value=ot,this[F]+=Jt-it.length,it.length=Jt,this.get(Qe),he(this),!0}var J=new ue(Qe,ot,Jt,Pt,Ve);return J.length>this[N]?(this[q]&&this[q](Qe,ot),!1):(this[F]+=J.length,this[re].unshift(J),this[y].set(Qe,this[re].head),he(this),!0)}},{key:"has",value:function(Qe){if(!this[y].has(Qe))return!1;var ot=this[y].get(Qe).value;return!we(this,ot)}},{key:"get",value:function(Qe){return ae(this,Qe,!0)}},{key:"peek",value:function(Qe){return ae(this,Qe,!1)}},{key:"pop",value:function(){var Qe=this[re].tail;return Qe?(ve(this,Qe),Qe.value):null}},{key:"del",value:function(Qe){ve(this,this[y].get(Qe))}},{key:"load",value:function(Qe){this.reset();for(var ot=Date.now(),Ve=Qe.length-1;Ve>=0;Ve--){var Pt=Qe[Ve],Jt=Pt.e||0;if(Jt===0)this.set(Pt.k,Pt.v);else{var it=Jt-ot;it>0&&this.set(Pt.k,Pt.v,it)}}}},{key:"prune",value:function(){var Qe=this;this[y].forEach(function(ot,Ve){return ae(Qe,Ve,!1)})}},{key:"max",set:function(Qe){if(typeof Qe!="number"||Qe<0)throw new TypeError("max must be a non-negative number");this[N]=Qe||1/0,he(this)},get:function(){return this[N]}},{key:"allowStale",set:function(Qe){this[x]=!!Qe},get:function(){return this[x]}},{key:"maxAge",set:function(Qe){if(typeof Qe!="number")throw new TypeError("maxAge must be a non-negative number");this[j]=Qe,he(this)},get:function(){return this[j]}},{key:"lengthCalculator",set:function(Qe){var ot=this;typeof Qe!="function"&&(Qe=De),Qe!==this[k]&&(this[k]=Qe,this[F]=0,this[re].forEach(function(Ve){Ve.length=ot[k](Ve.value,Ve.key),ot[F]+=Ve.length})),he(this)},get:function(){return this[k]}},{key:"length",get:function(){return this[F]}},{key:"itemCount",get:function(){return this[re].length}}])&&E(We.prototype,gt),_t&&E(We,_t),ze}(),ae=function(ze,We,gt){var _t=ze[y].get(We);if(_t){var Qe=_t.value;if(we(ze,Qe)){if(ve(ze,_t),!ze[x])return}else gt&&(ze[me]&&(_t.value.now=Date.now()),ze[re].unshiftNode(_t));return Qe.value}},we=function(ze,We){if(!We||!We.maxAge&&!ze[j])return!1;var gt=Date.now()-We.now;return We.maxAge?gt>We.maxAge:ze[j]&>>ze[j]},he=function(ze){if(ze[F]>ze[N])for(var We=ze[re].tail;ze[F]>ze[N]&&We!==null;){var gt=We.prev;ve(ze,We),We=gt}},ve=function(ze,We){if(We){var gt=We.value;ze[q]&&ze[q](gt.key,gt.value),ze[F]-=gt.length,ze[y].delete(gt.key),ze[re].removeNode(We)}},ue=function ze(We,gt,_t,Qe,ot){h(this,ze),this.key=We,this.value=gt,this.length=_t,this.now=Qe,this.maxAge=ot||0},Ae=function(ze,We,gt,_t){var Qe=gt.value;we(ze,Qe)&&(ve(ze,gt),ze[x]||(Qe=void 0)),Qe&&We.call(_t,Qe.value,Qe.key,ze)};o.exports=ge},function(o,l,f){(function(h){function E(t){return(E=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(N){return typeof N}:function(N){return N&&typeof Symbol=="function"&&N.constructor===Symbol&&N!==Symbol.prototype?"symbol":typeof N})(t)}o.exports=function(){if(typeof document>"u"||!document.addEventListener)return null;var t,N,F,k={};return k.copy=function(){var x=!1,j=null,q=!1;function V(){x=!1,j=null,q&&window.getSelection().removeAllRanges(),q=!1}return document.addEventListener("copy",function(re){if(x){for(var y in j)re.clipboardData.setData(y,j[y]);re.preventDefault()}}),function(re){return new Promise(function(y,me){x=!0,typeof re=="string"?j={"text/plain":re}:re instanceof Node?j={"text/html":new XMLSerializer().serializeToString(re)}:re instanceof Object?j=re:me("Invalid data type. Must be string, DOM node, or an object mapping MIME types to strings."),function De(ge){try{if(document.execCommand("copy"))V(),y();else{if(ge)throw V(),new Error("Unable to copy. Perhaps it's not available in your browser?");(function(){var ae=document.getSelection();if(!document.queryCommandEnabled("copy")&&ae.isCollapsed){var we=document.createRange();we.selectNodeContents(document.body),ae.removeAllRanges(),ae.addRange(we),q=!0}})(),De(!0)}}catch(ae){V(),me(ae)}}(!1)})}}(),k.paste=(F=!1,document.addEventListener("paste",function(x){if(F){F=!1,x.preventDefault();var j=t;t=null,j(x.clipboardData.getData(N))}}),function(x){return new Promise(function(j,q){F=!0,t=j,N=x||"text/plain";try{document.execCommand("paste")||(F=!1,q(new Error("Unable to paste. Pasting only works in Internet Explorer at the moment.")))}catch(V){F=!1,q(new Error(V))}})}),typeof ClipboardEvent>"u"&&window.clipboardData!==void 0&&window.clipboardData.setData!==void 0&&(function(x){function j(he,ve){return function(){he.apply(ve,arguments)}}function q(he){if(E(this)!="object")throw new TypeError("Promises must be constructed via new");if(typeof he!="function")throw new TypeError("not a function");this._state=null,this._value=null,this._deferreds=[],ge(he,j(re,this),j(y,this))}function V(he){var ve=this;return this._state===null?void this._deferreds.push(he):void ae(function(){var ue=ve._state?he.onFulfilled:he.onRejected;if(ue!==null){var Ae;try{Ae=ue(ve._value)}catch(ze){return void he.reject(ze)}he.resolve(Ae)}else(ve._state?he.resolve:he.reject)(ve._value)})}function re(he){try{if(he===this)throw new TypeError("A promise cannot be resolved with itself.");if(he&&(E(he)=="object"||typeof he=="function")){var ve=he.then;if(typeof ve=="function")return void ge(j(ve,he),j(re,this),j(y,this))}this._state=!0,this._value=he,me.call(this)}catch(ue){y.call(this,ue)}}function y(he){this._state=!1,this._value=he,me.call(this)}function me(){for(var he=0,ve=this._deferreds.length;ve>he;he++)V.call(this,this._deferreds[he]);this._deferreds=null}function De(he,ve,ue,Ae){this.onFulfilled=typeof he=="function"?he:null,this.onRejected=typeof ve=="function"?ve:null,this.resolve=ue,this.reject=Ae}function ge(he,ve,ue){var Ae=!1;try{he(function(ze){Ae||(Ae=!0,ve(ze))},function(ze){Ae||(Ae=!0,ue(ze))})}catch(ze){if(Ae)return;Ae=!0,ue(ze)}}var ae=q.immediateFn||typeof h=="function"&&h||function(he){setTimeout(he,1)},we=Array.isArray||function(he){return Object.prototype.toString.call(he)==="[object Array]"};q.prototype.catch=function(he){return this.then(null,he)},q.prototype.then=function(he,ve){var ue=this;return new q(function(Ae,ze){V.call(ue,new De(he,ve,Ae,ze))})},q.all=function(){var he=Array.prototype.slice.call(arguments.length===1&&we(arguments[0])?arguments[0]:arguments);return new q(function(ve,ue){function Ae(gt,_t){try{if(_t&&(E(_t)=="object"||typeof _t=="function")){var Qe=_t.then;if(typeof Qe=="function")return void Qe.call(_t,function(ot){Ae(gt,ot)},ue)}he[gt]=_t,--ze==0&&ve(he)}catch(ot){ue(ot)}}if(he.length===0)return ve([]);for(var ze=he.length,We=0;WeAe;Ae++)he[Ae].then(ve,ue)})},o.exports?o.exports=q:x.Promise||(x.Promise=q)}(this),k.copy=function(x){return new Promise(function(j,q){if(typeof x!="string"&&!("text/plain"in x))throw new Error("You must provide a text/plain type.");var V=typeof x=="string"?x:x["text/plain"];window.clipboardData.setData("Text",V)?j():q(new Error("Copying was rejected."))})},k.paste=function(){return new Promise(function(x,j){var q=window.clipboardData.getData("Text");q?x(q):j(new Error("Pasting was rejected."))})}),k}()}).call(this,f(13).setImmediate)},function(o,l,f){"use strict";o.exports=f(15)},function(o,l,f){"use strict";f.r(l),l.default=`:root { + /** + * IMPORTANT: When new theme variables are added below\u2013 also add them to SettingsContext updateThemeVariables() + */ + + /* Light theme */ + --light-color-attribute-name: #ef6632; + --light-color-attribute-name-not-editable: #23272f; + --light-color-attribute-name-inverted: rgba(255, 255, 255, 0.7); + --light-color-attribute-value: #1a1aa6; + --light-color-attribute-value-inverted: #ffffff; + --light-color-attribute-editable-value: #1a1aa6; + --light-color-background: #ffffff; + --light-color-background-hover: rgba(0, 136, 250, 0.1); + --light-color-background-inactive: #e5e5e5; + --light-color-background-invalid: #fff0f0; + --light-color-background-selected: #0088fa; + --light-color-button-background: #ffffff; + --light-color-button-background-focus: #ededed; + --light-color-button: #5f6673; + --light-color-button-disabled: #cfd1d5; + --light-color-button-active: #0088fa; + --light-color-button-focus: #23272f; + --light-color-button-hover: #23272f; + --light-color-border: #eeeeee; + --light-color-commit-did-not-render-fill: #cfd1d5; + --light-color-commit-did-not-render-fill-text: #000000; + --light-color-commit-did-not-render-pattern: #cfd1d5; + --light-color-commit-did-not-render-pattern-text: #333333; + --light-color-commit-gradient-0: #37afa9; + --light-color-commit-gradient-1: #63b19e; + --light-color-commit-gradient-2: #80b393; + --light-color-commit-gradient-3: #97b488; + --light-color-commit-gradient-4: #abb67d; + --light-color-commit-gradient-5: #beb771; + --light-color-commit-gradient-6: #cfb965; + --light-color-commit-gradient-7: #dfba57; + --light-color-commit-gradient-8: #efbb49; + --light-color-commit-gradient-9: #febc38; + --light-color-commit-gradient-text: #000000; + --light-color-component-name: #6a51b2; + --light-color-component-name-inverted: #ffffff; + --light-color-component-badge-background: rgba(0, 0, 0, 0.1); + --light-color-component-badge-background-inverted: rgba(255, 255, 255, 0.25); + --light-color-component-badge-count: #777d88; + --light-color-component-badge-count-inverted: rgba(255, 255, 255, 0.7); + --light-color-context-background: rgba(0,0,0,.9); + --light-color-context-background-hover: rgba(255, 255, 255, 0.1); + --light-color-context-background-selected: #178fb9; + --light-color-context-border: #3d424a; + --light-color-context-text: #ffffff; + --light-color-context-text-selected: #ffffff; + --light-color-dim: #777d88; + --light-color-dimmer: #cfd1d5; + --light-color-dimmest: #eff0f1; + --light-color-error-background: hsl(0, 100%, 97%); + --light-color-error-border: hsl(0, 100%, 92%); + --light-color-error-text: #ff0000; + --light-color-expand-collapse-toggle: #777d88; + --light-color-link: #0000ff; + --light-color-modal-background: rgba(255, 255, 255, 0.75); + --light-color-record-active: #fc3a4b; + --light-color-record-hover: #3578e5; + --light-color-record-inactive: #0088fa; + --light-color-scroll-thumb: #c2c2c2; + --light-color-scroll-track: #fafafa; + --light-color-search-match: yellow; + --light-color-search-match-current: #f7923b; + --light-color-selected-tree-highlight-active: rgba(0, 136, 250, 0.1); + --light-color-selected-tree-highlight-inactive: rgba(0, 0, 0, 0.05); + --light-color-shadow: rgba(0, 0, 0, 0.25); + --light-color-tab-selected-border: #0088fa; + --light-color-text: #000000; + --light-color-text-invalid: #ff0000; + --light-color-text-selected: #ffffff; + --light-color-toggle-background-invalid: #fc3a4b; + --light-color-toggle-background-on: #0088fa; + --light-color-toggle-background-off: #cfd1d5; + --light-color-toggle-text: #ffffff; + --light-color-tooltip-background: rgba(0, 0, 0, 0.9); + --light-color-tooltip-text: #ffffff; + + /* Dark theme */ + --dark-color-attribute-name: #9d87d2; + --dark-color-attribute-name-not-editable: #ededed; + --dark-color-attribute-name-inverted: #282828; + --dark-color-attribute-value: #cedae0; + --dark-color-attribute-value-inverted: #ffffff; + --dark-color-attribute-editable-value: yellow; + --dark-color-background: #282c34; + --dark-color-background-hover: rgba(255, 255, 255, 0.1); + --dark-color-background-inactive: #3d424a; + --dark-color-background-invalid: #5c0000; + --dark-color-background-selected: #178fb9; + --dark-color-button-background: #282c34; + --dark-color-button-background-focus: #3d424a; + --dark-color-button: #afb3b9; + --dark-color-button-active: #61dafb; + --dark-color-button-disabled: #4f5766; + --dark-color-button-focus: #a2e9fc; + --dark-color-button-hover: #ededed; + --dark-color-border: #3d424a; + --dark-color-commit-did-not-render-fill: #777d88; + --dark-color-commit-did-not-render-fill-text: #000000; + --dark-color-commit-did-not-render-pattern: #666c77; + --dark-color-commit-did-not-render-pattern-text: #ffffff; + --dark-color-commit-gradient-0: #37afa9; + --dark-color-commit-gradient-1: #63b19e; + --dark-color-commit-gradient-2: #80b393; + --dark-color-commit-gradient-3: #97b488; + --dark-color-commit-gradient-4: #abb67d; + --dark-color-commit-gradient-5: #beb771; + --dark-color-commit-gradient-6: #cfb965; + --dark-color-commit-gradient-7: #dfba57; + --dark-color-commit-gradient-8: #efbb49; + --dark-color-commit-gradient-9: #febc38; + --dark-color-commit-gradient-text: #000000; + --dark-color-component-name: #61dafb; + --dark-color-component-name-inverted: #282828; + --dark-color-component-badge-background: rgba(255, 255, 255, 0.25); + --dark-color-component-badge-background-inverted: rgba(0, 0, 0, 0.25); + --dark-color-component-badge-count: #8f949d; + --dark-color-component-badge-count-inverted: rgba(255, 255, 255, 0.7); + --dark-color-context-background: rgba(255,255,255,.9); + --dark-color-context-background-hover: rgba(0, 136, 250, 0.1); + --dark-color-context-background-selected: #0088fa; + --dark-color-context-border: #eeeeee; + --dark-color-context-text: #000000; + --dark-color-context-text-selected: #ffffff; + --dark-color-dim: #8f949d; + --dark-color-dimmer: #777d88; + --dark-color-dimmest: #4f5766; + --dark-color-error-background: #200; + --dark-color-error-border: #900; + --dark-color-error-text: #f55; + --dark-color-expand-collapse-toggle: #8f949d; + --dark-color-link: #61dafb; + --dark-color-modal-background: rgba(0, 0, 0, 0.75); + --dark-color-record-active: #fc3a4b; + --dark-color-record-hover: #a2e9fc; + --dark-color-record-inactive: #61dafb; + --dark-color-scroll-thumb: #afb3b9; + --dark-color-scroll-track: #313640; + --dark-color-search-match: yellow; + --dark-color-search-match-current: #f7923b; + --dark-color-selected-tree-highlight-active: rgba(23, 143, 185, 0.15); + --dark-color-selected-tree-highlight-inactive: rgba(255, 255, 255, 0.05); + --dark-color-shadow: rgba(0, 0, 0, 0.5); + --dark-color-tab-selected-border: #178fb9; + --dark-color-text: #ffffff; + --dark-color-text-invalid: #ff8080; + --dark-color-text-selected: #ffffff; + --dark-color-toggle-background-invalid: #fc3a4b; + --dark-color-toggle-background-on: #178fb9; + --dark-color-toggle-background-off: #777d88; + --dark-color-toggle-text: #ffffff; + --dark-color-tooltip-background: rgba(255, 255, 255, 0.9); + --dark-color-tooltip-text: #000000; + + /* Font smoothing */ + --light-font-smoothing: auto; + --dark-font-smoothing: antialiased; + --font-smoothing: auto; + + /* Compact density */ + --compact-font-size-monospace-small: 9px; + --compact-font-size-monospace-normal: 11px; + --compact-font-size-monospace-large: 15px; + --compact-font-size-sans-small: 10px; + --compact-font-size-sans-normal: 12px; + --compact-font-size-sans-large: 14px; + --compact-line-height-data: 18px; + --compact-root-font-size: 16px; + + /* Comfortable density */ + --comfortable-font-size-monospace-small: 10px; + --comfortable-font-size-monospace-normal: 13px; + --comfortable-font-size-monospace-large: 17px; + --comfortable-font-size-sans-small: 12px; + --comfortable-font-size-sans-normal: 14px; + --comfortable-font-size-sans-large: 16px; + --comfortable-line-height-data: 22px; + --comfortable-root-font-size: 20px; + + /* GitHub.com system fonts */ + --font-family-monospace: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, + Courier, monospace; + --font-family-sans: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, + Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + + /* Constant values shared between JS and CSS */ + --interaction-commit-size: 10px; + --interaction-label-width: 200px; +} +`},function(o,l,f){"use strict";function h(k){var x=this;if(x instanceof h||(x=new h),x.tail=null,x.head=null,x.length=0,k&&typeof k.forEach=="function")k.forEach(function(V){x.push(V)});else if(arguments.length>0)for(var j=0,q=arguments.length;j1)j=x;else{if(!this.head)throw new TypeError("Reduce of empty list with no initial value");q=this.head.next,j=this.head.value}for(var V=0;q!==null;V++)j=k(j,q.value,V),q=q.next;return j},h.prototype.reduceReverse=function(k,x){var j,q=this.tail;if(arguments.length>1)j=x;else{if(!this.tail)throw new TypeError("Reduce of empty list with no initial value");q=this.tail.prev,j=this.tail.value}for(var V=this.length-1;q!==null;V--)j=k(j,q.value,V),q=q.prev;return j},h.prototype.toArray=function(){for(var k=new Array(this.length),x=0,j=this.head;j!==null;x++)k[x]=j.value,j=j.next;return k},h.prototype.toArrayReverse=function(){for(var k=new Array(this.length),x=0,j=this.tail;j!==null;x++)k[x]=j.value,j=j.prev;return k},h.prototype.slice=function(k,x){(x=x||this.length)<0&&(x+=this.length),(k=k||0)<0&&(k+=this.length);var j=new h;if(xthis.length&&(x=this.length);for(var q=0,V=this.head;V!==null&&qthis.length&&(x=this.length);for(var q=this.length,V=this.tail;V!==null&&q>x;q--)V=V.prev;for(;V!==null&&q>k;q--,V=V.prev)j.push(V.value);return j},h.prototype.splice=function(k,x){k>this.length&&(k=this.length-1),k<0&&(k=this.length+k);for(var j=0,q=this.head;q!==null&&j=0&&(F._idleTimeoutId=setTimeout(function(){F._onTimeout&&F._onTimeout()},k))},f(14),l.setImmediate=typeof self<"u"&&self.setImmediate||h!==void 0&&h.setImmediate||this&&this.setImmediate,l.clearImmediate=typeof self<"u"&&self.clearImmediate||h!==void 0&&h.clearImmediate||this&&this.clearImmediate}).call(this,f(4))},function(o,l,f){(function(h,E){(function(t,N){"use strict";if(!t.setImmediate){var F,k,x,j,q,V=1,re={},y=!1,me=t.document,De=Object.getPrototypeOf&&Object.getPrototypeOf(t);De=De&&De.setTimeout?De:t,{}.toString.call(t.process)==="[object process]"?F=function(we){E.nextTick(function(){ae(we)})}:function(){if(t.postMessage&&!t.importScripts){var we=!0,he=t.onmessage;return t.onmessage=function(){we=!1},t.postMessage("","*"),t.onmessage=he,we}}()?(j="setImmediate$"+Math.random()+"$",q=function(we){we.source===t&&typeof we.data=="string"&&we.data.indexOf(j)===0&&ae(+we.data.slice(j.length))},t.addEventListener?t.addEventListener("message",q,!1):t.attachEvent("onmessage",q),F=function(we){t.postMessage(j+we,"*")}):t.MessageChannel?((x=new MessageChannel).port1.onmessage=function(we){ae(we.data)},F=function(we){x.port2.postMessage(we)}):me&&"onreadystatechange"in me.createElement("script")?(k=me.documentElement,F=function(we){var he=me.createElement("script");he.onreadystatechange=function(){ae(we),he.onreadystatechange=null,k.removeChild(he),he=null},k.appendChild(he)}):F=function(we){setTimeout(ae,0,we)},De.setImmediate=function(we){typeof we!="function"&&(we=new Function(""+we));for(var he=new Array(arguments.length-1),ve=0;ve"u"?h===void 0?this:h:self)}).call(this,f(4),f(5))},function(o,l,f){"use strict";function h(ue){return(h=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(Ae){return typeof Ae}:function(Ae){return Ae&&typeof Symbol=="function"&&Ae.constructor===Symbol&&Ae!==Symbol.prototype?"symbol":typeof Ae})(ue)}var E=f(1),t=f(16),N=f(18).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,F=60128;if(typeof Symbol=="function"&&Symbol.for){var k=Symbol.for;F=k("react.opaque.id")}var x=[],j=null,q=null;function V(){if(j===null){var ue=new Map;try{me.useContext({_currentValue:null}),me.useState(null),me.useReducer(function(gt){return gt},null),me.useRef(null),me.useLayoutEffect(function(){}),me.useEffect(function(){}),me.useImperativeHandle(void 0,function(){return null}),me.useDebugValue(null),me.useCallback(function(){}),me.useMemo(function(){return null})}finally{var Ae=x;x=[]}for(var ze=0;zece;ce++)if((J=ge(it,Pt,ce))!==-1){De=ce,Pt=J;break e}Pt=-1}}e:{if(it=Jt,(J=V().get(Ve.primitive))!==void 0){for(ce=0;cePt-it?null:Jt.slice(it,Pt-1))!==null){if(Pt=0,We!==null){for(;PtPt;We--)gt=Qe.pop()}for(We=Jt.length-Pt-1;1<=We;We--)Pt=[],gt.push({id:null,isStateEditable:!1,name:we(Jt[We-1].functionName),value:void 0,subHooks:Pt}),Qe.push(gt),gt=Pt;We=Jt}Pt=(Jt=Ve.primitive)==="Context"||Jt==="DebugValue"?null:_t++,gt.push({id:Pt,isStateEditable:Jt==="Reducer"||Jt==="State",name:Jt,value:Ve.value,subHooks:[]})}return function Re(le,He){for(var dt=[],At=0;At-1&&(re=re.replace(/eval code/g,"eval").replace(/(\(eval at [^()]*)|(\),.*$)/g,""));var y=re.replace(/^\s+/,"").replace(/\(eval code/g,"("),me=y.match(/ (\((.+):(\d+):(\d+)\)$)/),De=(y=me?y.replace(me[0],""):y).split(/\s+/).slice(1),ge=this.extractLocation(me?me[1]:De.pop()),ae=De.join(" ")||void 0,we=["eval",""].indexOf(ge[0])>-1?void 0:ge[0];return new k({functionName:ae,fileName:we,lineNumber:ge[1],columnNumber:ge[2],source:re})},this)},parseFFOrSafari:function(V){return V.stack.split(` +`).filter(function(re){return!re.match(q)},this).map(function(re){if(re.indexOf(" > eval")>-1&&(re=re.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,":$1")),re.indexOf("@")===-1&&re.indexOf(":")===-1)return new k({functionName:re});var y=/((.*".+"[^@]*)?[^@]*)(?:@)/,me=re.match(y),De=me&&me[1]?me[1]:void 0,ge=this.extractLocation(re.replace(y,""));return new k({functionName:De,fileName:ge[0],lineNumber:ge[1],columnNumber:ge[2],source:re})},this)},parseOpera:function(V){return!V.stacktrace||V.message.indexOf(` +`)>-1&&V.message.split(` +`).length>V.stacktrace.split(` +`).length?this.parseOpera9(V):V.stack?this.parseOpera11(V):this.parseOpera10(V)},parseOpera9:function(V){for(var re=/Line (\d+).*script (?:in )?(\S+)/i,y=V.message.split(` +`),me=[],De=2,ge=y.length;De/,"$2").replace(/\([^)]*\)/g,"")||void 0;ge.match(/\(([^)]*)\)/)&&(y=ge.replace(/^[^(]+\(([^)]*)\)$/,"$1"));var we=y===void 0||y==="[arguments not available]"?void 0:y.split(",");return new k({functionName:ae,args:we,fileName:De[0],lineNumber:De[1],columnNumber:De[2],source:re})},this)}}})=="function"?h.apply(l,E):h)===void 0||(o.exports=t)})()},function(o,l,f){var h,E,t;(function(N,F){"use strict";E=[],(t=typeof(h=function(){function k(ae){return ae.charAt(0).toUpperCase()+ae.substring(1)}function x(ae){return function(){return this[ae]}}var j=["isConstructor","isEval","isNative","isToplevel"],q=["columnNumber","lineNumber"],V=["fileName","functionName","source"],re=j.concat(q,V,["args"]);function y(ae){if(ae)for(var we=0;we1?Oe-1:0),Ne=1;Ne=0&&Oe.splice($,1)}}}])&&h(H.prototype,Y),ee&&h(H,ee),U}(),t=f(2),N=f.n(t);try{var F=f(9).default,k=function(U){var H=new RegExp("".concat(U,": ([0-9]+)")),Y=F.match(H);return parseInt(Y[1],10)};k("comfortable-line-height-data"),k("compact-line-height-data")}catch{}function x(U){try{return sessionStorage.getItem(U)}catch{return null}}function j(U){try{sessionStorage.removeItem(U)}catch{}}function q(U,H){try{return sessionStorage.setItem(U,H)}catch{}}var V=function(U,H){return U===H},re=f(1),y=f.n(re);function me(U){return U.ownerDocument?U.ownerDocument.defaultView:null}function De(U){var H=me(U);return H?H.frameElement:null}function ge(U){var H=he(U);return ae([U.getBoundingClientRect(),{top:H.borderTop,left:H.borderLeft,bottom:H.borderBottom,right:H.borderRight,width:0,height:0}])}function ae(U){return U.reduce(function(H,Y){return H==null?Y:{top:H.top+Y.top,left:H.left+Y.left,width:H.width,height:H.height,bottom:H.bottom+Y.bottom,right:H.right+Y.right}})}function we(U,H){var Y=De(U);if(Y&&Y!==H){for(var ee=[U.getBoundingClientRect()],Ce=Y,_e=!1;Ce;){var Oe=ge(Ce);if(ee.push(Oe),Ce=De(Ce),_e)break;Ce&&me(Ce)===H&&(_e=!0)}return ae(ee)}return U.getBoundingClientRect()}function he(U){var H=window.getComputedStyle(U);return{borderLeft:parseInt(H.borderLeftWidth,10),borderRight:parseInt(H.borderRightWidth,10),borderTop:parseInt(H.borderTopWidth,10),borderBottom:parseInt(H.borderBottomWidth,10),marginLeft:parseInt(H.marginLeft,10),marginRight:parseInt(H.marginRight,10),marginTop:parseInt(H.marginTop,10),marginBottom:parseInt(H.marginBottom,10),paddingLeft:parseInt(H.paddingLeft,10),paddingRight:parseInt(H.paddingRight,10),paddingTop:parseInt(H.paddingTop,10),paddingBottom:parseInt(H.paddingBottom,10)}}function ve(U,H){var Y;if(typeof Symbol>"u"||U[Symbol.iterator]==null){if(Array.isArray(U)||(Y=function(Ne,Je){if(!!Ne){if(typeof Ne=="string")return ue(Ne,Je);var vt=Object.prototype.toString.call(Ne).slice(8,-1);if(vt==="Object"&&Ne.constructor&&(vt=Ne.constructor.name),vt==="Map"||vt==="Set")return Array.from(Ne);if(vt==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(vt))return ue(Ne,Je)}}(U))||H&&U&&typeof U.length=="number"){Y&&(U=Y);var ee=0,Ce=function(){};return{s:Ce,n:function(){return ee>=U.length?{done:!0}:{done:!1,value:U[ee++]}},e:function(Ne){throw Ne},f:Ce}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var _e,Oe=!0,$=!1;return{s:function(){Y=U[Symbol.iterator]()},n:function(){var Ne=Y.next();return Oe=Ne.done,Ne},e:function(Ne){$=!0,_e=Ne},f:function(){try{Oe||Y.return==null||Y.return()}finally{if($)throw _e}}}}function ue(U,H){(H==null||H>U.length)&&(H=U.length);for(var Y=0,ee=new Array(H);YOe.left+Oe.width&&(oe=Oe.left+Oe.width-vt-5),{style:{top:Ne+="px",left:oe+="px"}}}(H,Y,{width:ee.width,height:ee.height});y()(this.tip.style,Ce.style)}}]),U}(),Qe=function(){function U(){Ae(this,U);var H=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;this.window=H;var Y=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;this.tipBoundsWindow=Y;var ee=H.document;this.container=ee.createElement("div"),this.container.style.zIndex="10000000",this.tip=new _t(ee,this.container),this.rects=[],ee.body.appendChild(this.container)}return We(U,[{key:"remove",value:function(){this.tip.remove(),this.rects.forEach(function(H){H.remove()}),this.rects.length=0,this.container.parentNode&&this.container.parentNode.removeChild(this.container)}},{key:"inspect",value:function(H,Y){for(var ee=this,Ce=H.filter(function(xt){return xt.nodeType===Node.ELEMENT_NODE});this.rects.length>Ce.length;)this.rects.pop().remove();if(Ce.length!==0){for(;this.rects.length1&&arguments[1]!==void 0?arguments[1]:V,rt=void 0,xt=[],kt=void 0,bt=!1,sn=function(Ft,Dn){return qe(Ft,xt[Dn])},rn=function(){for(var Ft=arguments.length,Dn=Array(Ft),dr=0;dr"u"?"undefined":At(performance))==="object"&&typeof performance.now=="function"?function(){return performance.now()}:function(){return Date.now()},an=new Map,On=null,lr=!1,ln=null;function Vt(U){(lr=U)||(an.clear(),On!==null&&(cancelAnimationFrame(On),On=null),ln!==null&&(clearTimeout(ln),ln=null),He!==null&&(He.parentNode!=null&&He.parentNode.removeChild(He),He=null))}function Er(U){lr&&(U.forEach(function(H){var Y=an.get(H),ee=nn(),Ce=Y!=null?Y.lastMeasuredAt:0,_e=Y!=null?Y.rect:null;(_e===null||Ce+2505&&arguments[5]!==void 0?arguments[5]:0,$=Mo(U);switch($){case"html_element":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:U.tagName,type:$};case"function":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:typeof U.name!="function"&&U.name?U.name:"function",type:$};case"string":return U.length<=500?U:U.slice(0,500)+"...";case"bigint":case"symbol":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:U.toString(),type:$};case"react_element":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:F0(U)||"Unknown",type:$};case"array_buffer":case"data_view":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:$==="data_view"?"DataView":"ArrayBuffer",size:U.byteLength,type:$};case"array":return _e=Ce(ee),Oe>=2&&!_e?c0($,!0,U,H,ee):U.map(function(vt,oe){return Ao(vt,H,Y,ee.concat([oe]),Ce,_e?1:Oe+1)});case"html_all_collection":case"typed_array":case"iterator":if(_e=Ce(ee),Oe>=2&&!_e)return c0($,!0,U,H,ee);var Ne={unserializable:!0,type:$,readonly:!0,size:$==="typed_array"?U.length:void 0,preview_short:ki(U,!1),preview_long:ki(U,!0),name:U.constructor&&U.constructor.name!=="Object"?U.constructor.name:""};return Xt(U[Symbol.iterator])&&Array.from(U).forEach(function(vt,oe){return Ne[oe]=Ao(vt,H,Y,ee.concat([oe]),Ce,_e?1:Oe+1)}),Y.push(ee),Ne;case"opaque_iterator":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:U[Symbol.toStringTag],type:$};case"date":case"regexp":return H.push(ee),{inspectable:!1,preview_short:ki(U,!1),preview_long:ki(U,!0),name:U.toString(),type:$};case"object":if(_e=Ce(ee),Oe>=2&&!_e)return c0($,!0,U,H,ee);var Je={};return lu(U).forEach(function(vt){var oe=vt.toString();Je[oe]=Ao(U[vt],H,Y,ee.concat([oe]),Ce,_e?1:Oe+1)}),Je;case"infinity":case"nan":case"undefined":return H.push(ee),{type:$};default:return U}}function Jo(U){return(Jo=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(H){return typeof H}:function(H){return H&&typeof Symbol=="function"&&H.constructor===Symbol&&H!==Symbol.prototype?"symbol":typeof H})(U)}function Fs(U){return function(H){if(Array.isArray(H))return Zo(H)}(U)||function(H){if(typeof Symbol<"u"&&Symbol.iterator in Object(H))return Array.from(H)}(U)||function(H,Y){if(!!H){if(typeof H=="string")return Zo(H,Y);var ee=Object.prototype.toString.call(H).slice(8,-1);if(ee==="Object"&&H.constructor&&(ee=H.constructor.name),ee==="Map"||ee==="Set")return Array.from(H);if(ee==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(ee))return Zo(H,Y)}}(U)||function(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Zo(U,H){(H==null||H>U.length)&&(H=U.length);for(var Y=0,ee=new Array(H);YH.toString()?1:H.toString()>U.toString()?-1:0}function lu(U){for(var H=[],Y=U,ee=function(){var Ce=[].concat(Fs(Object.keys(Y)),Fs(Object.getOwnPropertySymbols(Y))),_e=Object.getOwnPropertyDescriptors(Y);Ce.forEach(function(Oe){_e[Oe].enumerable&&H.push(Oe)}),Y=Object.getPrototypeOf(Y)};Y!=null;)ee();return H}function vi(U){var H=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Anonymous",Y=$o.get(U);if(Y!=null)return Y;var ee=H;return typeof U.displayName=="string"?ee=U.displayName:typeof U.name=="string"&&U.name!==""&&(ee=U.name),$o.set(U,ee),ee}var Dr=0;function el(){return++Dr}function Y0(U){var H=qt.get(U);if(H!==void 0)return H;for(var Y=new Array(U.length),ee=0;ee1&&arguments[1]!==void 0?arguments[1]:50;return U.length>H?U.substr(0,H)+"\u2026":U}function ki(U,H){if(U!=null&&hasOwnProperty.call(U,Du.type))return H?U[Du.preview_long]:U[Du.preview_short];switch(Mo(U)){case"html_element":return"<".concat(su(U.tagName.toLowerCase())," />");case"function":return su("\u0192 ".concat(typeof U.name=="function"?"":U.name,"() {}"));case"string":return'"'.concat(U,'"');case"bigint":return su(U.toString()+"n");case"regexp":case"symbol":return su(U.toString());case"react_element":return"<".concat(su(F0(U)||"Unknown")," />");case"array_buffer":return"ArrayBuffer(".concat(U.byteLength,")");case"data_view":return"DataView(".concat(U.buffer.byteLength,")");case"array":if(H){for(var Y="",ee=0;ee0&&(Y+=", "),!((Y+=ki(U[ee],!1)).length>50));ee++);return"[".concat(su(Y),"]")}var Ce=hasOwnProperty.call(U,Du.size)?U[Du.size]:U.length;return"Array(".concat(Ce,")");case"typed_array":var _e="".concat(U.constructor.name,"(").concat(U.length,")");if(H){for(var Oe="",$=0;$0&&(Oe+=", "),!((Oe+=U[$]).length>50));$++);return"".concat(_e," [").concat(su(Oe),"]")}return _e;case"iterator":var Ne=U.constructor.name;if(H){for(var Je=Array.from(U),vt="",oe=0;oe0&&(vt+=", "),Array.isArray(qe)){var rt=ki(qe[0],!0),xt=ki(qe[1],!1);vt+="".concat(rt," => ").concat(xt)}else vt+=ki(qe,!1);if(vt.length>50)break}return"".concat(Ne,"(").concat(U.size,") {").concat(su(vt),"}")}return"".concat(Ne,"(").concat(U.size,")");case"opaque_iterator":return U[Symbol.toStringTag];case"date":return U.toString();case"object":if(H){for(var kt=lu(U).sort(xi),bt="",sn=0;sn0&&(bt+=", "),(bt+="".concat(rn.toString(),": ").concat(ki(U[rn],!1))).length>50)break}return"{".concat(su(bt),"}")}return"{\u2026}";case"boolean":case"number":case"infinity":case"nan":case"null":case"undefined":return U;default:try{return su(""+U)}catch{return"unserializable"}}}var Ps=f(7);function Kl(U){return(Kl=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(H){return typeof H}:function(H){return H&&typeof Symbol=="function"&&H.constructor===Symbol&&H!==Symbol.prototype?"symbol":typeof H})(U)}function P0(U,H){var Y=Object.keys(U);if(Object.getOwnPropertySymbols){var ee=Object.getOwnPropertySymbols(U);H&&(ee=ee.filter(function(Ce){return Object.getOwnPropertyDescriptor(U,Ce).enumerable})),Y.push.apply(Y,ee)}return Y}function d0(U){for(var H=1;H2&&arguments[2]!==void 0?arguments[2]:[];if(U!==null){var ee=[],Ce=[],_e=Ao(U,ee,Ce,Y,H);return{data:_e,cleaned:ee,unserializable:Ce}}return null}function X0(U){var H,Y,ee=(H=U,Y=new Set,JSON.stringify(H,function(Oe,$){if(Kl($)==="object"&&$!==null){if(Y.has($))return;Y.add($)}return typeof $=="bigint"?$.toString()+"n":$})),Ce=ee===void 0?"undefined":ee,_e=window.__REACT_DEVTOOLS_GLOBAL_HOOK__.clipboardCopyText;typeof _e=="function"?_e(Ce).catch(function(Oe){}):Object(Ps.copy)(Ce)}function mi(U,H){var Y=arguments.length>2&&arguments[2]!==void 0?arguments[2]:0,ee=H[Y],Ce=Array.isArray(U)?U.slice():d0({},U);return Y+1===H.length?Array.isArray(Ce)?Ce.splice(ee,1):delete Ce[ee]:Ce[ee]=mi(U[ee],H,Y+1),Ce}function en(U,H,Y){var ee=arguments.length>3&&arguments[3]!==void 0?arguments[3]:0,Ce=H[ee],_e=Array.isArray(U)?U.slice():d0({},U);if(ee+1===H.length){var Oe=Y[ee];_e[Oe]=_e[Ce],Array.isArray(_e)?_e.splice(Ce,1):delete _e[Ce]}else _e[Ce]=en(U[Ce],H,Y,ee+1);return _e}function In(U,H,Y){var ee=arguments.length>3&&arguments[3]!==void 0?arguments[3]:0;if(ee>=H.length)return Y;var Ce=H[ee],_e=Array.isArray(U)?U.slice():d0({},U);return _e[Ce]=In(U[Ce],H,Y,ee+1),_e}var Ai=f(8);function yi(U,H){var Y=Object.keys(U);if(Object.getOwnPropertySymbols){var ee=Object.getOwnPropertySymbols(U);H&&(ee=ee.filter(function(Ce){return Object.getOwnPropertyDescriptor(U,Ce).enumerable})),Y.push.apply(Y,ee)}return Y}function Wt(U){for(var H=1;H"u"||!(Symbol.iterator in Object(Y)))){var Ce=[],_e=!0,Oe=!1,$=void 0;try{for(var Ne,Je=Y[Symbol.iterator]();!(_e=(Ne=Je.next()).done)&&(Ce.push(Ne.value),!ee||Ce.length!==ee);_e=!0);}catch(vt){Oe=!0,$=vt}finally{try{_e||Je.return==null||Je.return()}finally{if(Oe)throw $}}return Ce}}(U,H)||Xl(U,H)||function(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Yi(U,H){var Y;if(typeof Symbol>"u"||U[Symbol.iterator]==null){if(Array.isArray(U)||(Y=Xl(U))||H&&U&&typeof U.length=="number"){Y&&(U=Y);var ee=0,Ce=function(){};return{s:Ce,n:function(){return ee>=U.length?{done:!0}:{done:!1,value:U[ee++]}},e:function(Ne){throw Ne},f:Ce}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var _e,Oe=!0,$=!1;return{s:function(){Y=U[Symbol.iterator]()},n:function(){var Ne=Y.next();return Oe=Ne.done,Ne},e:function(Ne){$=!0,_e=Ne},f:function(){try{Oe||Y.return==null||Y.return()}finally{if($)throw _e}}}}function Xl(U,H){if(U){if(typeof U=="string")return ko(U,H);var Y=Object.prototype.toString.call(U).slice(8,-1);return Y==="Object"&&U.constructor&&(Y=U.constructor.name),Y==="Map"||Y==="Set"?Array.from(U):Y==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(Y)?ko(U,H):void 0}}function ko(U,H){(H==null||H>U.length)&&(H=U.length);for(var Y=0,ee=new Array(H);Y"u"?"undefined":li(performance))==="object"&&typeof performance.now=="function"?function(){return performance.now()}:function(){return Date.now()};function No(U){var H=null;function Y(rn){var Ft=li(rn)==="object"&&rn!==null?rn.$$typeof:rn;return li(Ft)==="symbol"?Ft.toString():Ft}var ee=H=Object(zt.gte)(U,"17.0.0-alpha")?{Block:22,ClassComponent:1,ContextConsumer:9,ContextProvider:10,CoroutineComponent:-1,CoroutineHandlerPhase:-1,DehydratedSuspenseComponent:18,ForwardRef:11,Fragment:7,FunctionComponent:0,HostComponent:5,HostPortal:4,HostRoot:3,HostText:6,IncompleteClassComponent:17,IndeterminateComponent:2,LazyComponent:16,MemoComponent:14,Mode:8,OffscreenComponent:23,Profiler:12,SimpleMemoComponent:15,SuspenseComponent:13,SuspenseListComponent:19,YieldComponent:-1}:Object(zt.gte)(U,"16.6.0-beta.0")?{Block:22,ClassComponent:1,ContextConsumer:9,ContextProvider:10,CoroutineComponent:-1,CoroutineHandlerPhase:-1,DehydratedSuspenseComponent:18,ForwardRef:11,Fragment:7,FunctionComponent:0,HostComponent:5,HostPortal:4,HostRoot:3,HostText:6,IncompleteClassComponent:17,IndeterminateComponent:2,LazyComponent:16,MemoComponent:14,Mode:8,OffscreenComponent:-1,Profiler:12,SimpleMemoComponent:15,SuspenseComponent:13,SuspenseListComponent:19,YieldComponent:-1}:Object(zt.gte)(U,"16.4.3-alpha")?{Block:-1,ClassComponent:2,ContextConsumer:11,ContextProvider:12,CoroutineComponent:-1,CoroutineHandlerPhase:-1,DehydratedSuspenseComponent:-1,ForwardRef:13,Fragment:9,FunctionComponent:0,HostComponent:7,HostPortal:6,HostRoot:5,HostText:8,IncompleteClassComponent:-1,IndeterminateComponent:4,LazyComponent:-1,MemoComponent:-1,Mode:10,OffscreenComponent:-1,Profiler:15,SimpleMemoComponent:-1,SuspenseComponent:16,SuspenseListComponent:-1,YieldComponent:-1}:{Block:-1,ClassComponent:2,ContextConsumer:12,ContextProvider:13,CoroutineComponent:7,CoroutineHandlerPhase:8,DehydratedSuspenseComponent:-1,ForwardRef:14,Fragment:10,FunctionComponent:1,HostComponent:5,HostPortal:4,HostRoot:3,HostText:6,IncompleteClassComponent:-1,IndeterminateComponent:0,LazyComponent:-1,MemoComponent:-1,Mode:11,OffscreenComponent:-1,Profiler:15,SimpleMemoComponent:-1,SuspenseComponent:16,SuspenseListComponent:-1,YieldComponent:9},Ce=ee.ClassComponent,_e=ee.IncompleteClassComponent,Oe=ee.FunctionComponent,$=ee.IndeterminateComponent,Ne=ee.ForwardRef,Je=ee.HostRoot,vt=ee.HostComponent,oe=ee.HostPortal,qe=ee.HostText,rt=ee.Fragment,xt=ee.MemoComponent,kt=ee.SimpleMemoComponent,bt=ee.SuspenseComponent,sn=ee.SuspenseListComponent;return{getDisplayNameForFiber:function(rn){var Ft=rn.type,Dn=rn.tag,dr=Ft;li(Ft)==="object"&&Ft!==null&&(dr=function Cr(Rn){switch(Y(Rn)){case 60115:case"Symbol(react.memo)":return Cr(Rn.type);case 60112:case"Symbol(react.forward_ref)":return Rn.render;default:return Rn}}(Ft));var er=null;switch(Dn){case Ce:case _e:return vi(dr);case Oe:case $:return vi(dr);case Ne:return Ft&&Ft.displayName||vi(dr,"Anonymous");case Je:return null;case vt:return Ft;case oe:case qe:case rt:return null;case xt:case kt:return vi(dr,"Anonymous");case bt:return"Suspense";case sn:return"SuspenseList";default:switch(Y(Ft)){case 60111:case"Symbol(react.concurrent_mode)":case"Symbol(react.async_mode)":return null;case 60109:case"Symbol(react.provider)":return er=rn.type._context||rn.type.context,"".concat(er.displayName||"Context",".Provider");case 60110:case"Symbol(react.context)":return er=rn.type._context||rn.type,"".concat(er.displayName||"Context",".Consumer");case 60108:case"Symbol(react.strict_mode)":return null;case 60114:case"Symbol(react.profiler)":return"Profiler(".concat(rn.memoizedProps.id,")");case 60119:case"Symbol(react.scope)":return"Scope";default:return null}}},getTypeSymbol:Y,ReactPriorityLevels:{ImmediatePriority:99,UserBlockingPriority:98,NormalPriority:97,LowPriority:96,IdlePriority:95,NoPriority:90},ReactTypeOfWork:H,ReactTypeOfSideEffect:{NoFlags:0,PerformedWork:1,Placement:2}}}function Is(U,H,Y,ee){var Ce=No(Y.version),_e=Ce.getDisplayNameForFiber,Oe=Ce.getTypeSymbol,$=Ce.ReactPriorityLevels,Ne=Ce.ReactTypeOfWork,Je=Ce.ReactTypeOfSideEffect,vt=Je.NoFlags,oe=Je.PerformedWork,qe=Je.Placement,rt=Ne.FunctionComponent,xt=Ne.ClassComponent,kt=Ne.ContextConsumer,bt=Ne.DehydratedSuspenseComponent,sn=Ne.Fragment,rn=Ne.ForwardRef,Ft=Ne.HostRoot,Dn=Ne.HostPortal,dr=Ne.HostComponent,er=Ne.HostText,Cr=Ne.IncompleteClassComponent,Rn=Ne.IndeterminateComponent,Nr=Ne.MemoComponent,y0=Ne.OffscreenComponent,Lr=Ne.SimpleMemoComponent,ut=Ne.SuspenseComponent,wt=Ne.SuspenseListComponent,et=$.ImmediatePriority,It=$.UserBlockingPriority,un=$.NormalPriority,fn=$.LowPriority,Jn=$.IdlePriority,wr=$.NoPriority,au=Y.overrideHookState,ku=Y.overrideHookStateDeletePath,T0=Y.overrideHookStateRenamePath,Z0=Y.overrideProps,Nu=Y.overridePropsDeletePath,gi=Y.overridePropsRenamePath,Po=Y.setSuspenseHandler,rl=Y.scheduleUpdate,hf=typeof Po=="function"&&typeof rl=="function";co(Y);var Tl=window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__!==!1,vf=window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__===!0;(Tl||vf)&&Jl({appendComponentStack:Tl,breakOnConsoleErrors:vf});var Io=new Set,ys=new Set,js=new Set,bo=!1,Bo=new Set;function gs(fe){js.clear(),Io.clear(),ys.clear(),fe.forEach(function(ie){if(ie.isEnabled)switch(ie.type){case 2:ie.isValid&&ie.value!==""&&Io.add(new RegExp(ie.value,"i"));break;case 1:js.add(ie.value);break;case 3:ie.isValid&&ie.value!==""&&ys.add(new RegExp(ie.value,"i"));break;case 4:Io.add(new RegExp("\\("));break;default:console.warn('Invalid component filter type "'.concat(ie.type,'"'))}})}function Xu(fe){var ie=fe._debugSource,Pe=fe.tag,Me=fe.type;switch(Pe){case bt:return!0;case Dn:case er:case sn:case y0:return!0;case Ft:return!1;default:switch(Oe(Me)){case 60111:case"Symbol(react.concurrent_mode)":case"Symbol(react.async_mode)":case 60108:case"Symbol(react.strict_mode)":return!0}}var at=Su(fe);if(js.has(at))return!0;if(Io.size>0){var mt=_e(fe);if(mt!=null){var Qt,An=Yi(Io);try{for(An.s();!(Qt=An.n()).done;)if(Qt.value.test(mt))return!0}catch(ir){An.e(ir)}finally{An.f()}}}if(ie!=null&&ys.size>0){var Sn,_n=ie.fileName,Tn=Yi(ys);try{for(Tn.s();!(Sn=Tn.n()).done;)if(Sn.value.test(_n))return!0}catch(ir){Tn.e(ir)}finally{Tn.f()}}return!1}function Su(fe){var ie=fe.type;switch(fe.tag){case xt:case Cr:return 1;case rt:case Rn:return 5;case rn:return 6;case Ft:return 11;case dr:return 7;case Dn:case er:case sn:return 9;case Nr:case Lr:return 8;case ut:return 12;case wt:return 13;default:switch(Oe(ie)){case 60111:case"Symbol(react.concurrent_mode)":case"Symbol(react.async_mode)":return 9;case 60109:case"Symbol(react.provider)":return 2;case 60110:case"Symbol(react.context)":return 2;case 60108:case"Symbol(react.strict_mode)":return 9;case 60114:case"Symbol(react.profiler)":return 10;default:return 9}}}function _i(fe){if(Uo.has(fe))return fe;var ie=fe.alternate;return ie!=null&&Uo.has(ie)?ie:(Uo.add(fe),fe)}window.__REACT_DEVTOOLS_COMPONENT_FILTERS__!=null?gs(window.__REACT_DEVTOOLS_COMPONENT_FILTERS__):gs([{type:1,value:7,isEnabled:!0}]);var C0=new Map,$0=new Map,Uo=new Set,la=new Map,$l=new Map,tu=-1;function Zr(fe){if(!C0.has(fe)){var ie=el();C0.set(fe,ie),$0.set(ie,fe)}return C0.get(fe)}function ho(fe){switch(Su(fe)){case 1:if(B0!==null){var ie=Zr(_i(fe)),Pe=Ci(fe);Pe!==null&&B0.set(ie,Pe)}}}var Bi={};function Ci(fe){switch(Su(fe)){case 1:var ie=fe.stateNode,Pe=Bi,Me=Bi;return ie!=null&&(ie.constructor&&ie.constructor.contextType!=null?Me=ie.context:(Pe=ie.context)&&Object.keys(Pe).length===0&&(Pe=Bi)),[Pe,Me];default:return null}}function mf(fe){switch(Su(fe)){case 1:if(B0!==null){var ie=Zr(_i(fe)),Pe=B0.has(ie)?B0.get(ie):null,Me=Ci(fe);if(Pe==null||Me==null)return null;var at=Q0(Pe,2),mt=at[0],Qt=at[1],An=Q0(Me,2),Sn=An[0],_n=An[1];if(Sn!==Bi)return eo(mt,Sn);if(_n!==Bi)return Qt!==_n}}return null}function yf(fe,ie){if(fe==null||ie==null)return!1;if(ie.hasOwnProperty("baseState")&&ie.hasOwnProperty("memoizedState")&&ie.hasOwnProperty("next")&&ie.hasOwnProperty("queue"))for(;ie!==null;){if(ie.memoizedState!==fe.memoizedState)return!0;ie=ie.next,fe=fe.next}return!1}function eo(fe,ie){if(fe==null||ie==null||ie.hasOwnProperty("baseState")&&ie.hasOwnProperty("memoizedState")&&ie.hasOwnProperty("next")&&ie.hasOwnProperty("queue"))return null;var Pe,Me=[],at=Yi(new Set([].concat(eu(Object.keys(fe)),eu(Object.keys(ie)))));try{for(at.s();!(Pe=at.n()).done;){var mt=Pe.value;fe[mt]!==ie[mt]&&Me.push(mt)}}catch(Qt){at.e(Qt)}finally{at.f()}return Me}function to(fe,ie){switch(ie.tag){case xt:case rt:case kt:case Nr:case Lr:return(ao(ie)&oe)===oe;default:return fe.memoizedProps!==ie.memoizedProps||fe.memoizedState!==ie.memoizedState||fe.ref!==ie.ref}}var xe=[],tt=[],Ke=[],Yt=[],Kt=new Map,pr=0,Ei=null;function bn(fe){xe.push(fe)}function mu(fe){if(xe.length!==0||tt.length!==0||Ke.length!==0||Ei!==null||Fu){var ie=tt.length+Ke.length+(Ei===null?0:1),Pe=new Array(3+pr+(ie>0?2+ie:0)+xe.length),Me=0;if(Pe[Me++]=H,Pe[Me++]=tu,Pe[Me++]=pr,Kt.forEach(function(An,Sn){Pe[Me++]=Sn.length;for(var _n=Y0(Sn),Tn=0;Tn<_n.length;Tn++)Pe[Me+Tn]=_n[Tn];Me+=Sn.length}),ie>0){Pe[Me++]=2,Pe[Me++]=ie;for(var at=tt.length-1;at>=0;at--)Pe[Me++]=tt[at];for(var mt=0;mt0?fe.forEach(function(ie){U.emit("operations",ie)}):(Rr!==null&&(fu=!0),U.getFiberRoots(H).forEach(function(ie){$u(tu=Zr(_i(ie.current)),ie.current),Fu&&ie.memoizedInteractions!=null&&(il={changeDescriptions:es?new Map:null,durations:[],commitTime:Ql()-Ju,interactions:Array.from(ie.memoizedInteractions).map(function(Pe){return Wt(Wt({},Pe),{},{timestamp:Pe.timestamp-Ju})}),maxActualDuration:0,priorityLevel:null}),Qr(ie.current,null,!1,!1),mu(),tu=-1}))},getBestMatchForTrackedPath:function(){if(Rr===null||no===null)return null;for(var fe=no;fe!==null&&Xu(fe);)fe=fe.return;return fe===null?null:{id:Zr(_i(fe)),isFullMatch:nu===Rr.length-1}},getDisplayNameForFiberID:function(fe){var ie=$0.get(fe);return ie!=null?_e(ie):null},getFiberIDForNative:function(fe){var ie=arguments.length>1&&arguments[1]!==void 0&&arguments[1],Pe=Y.findFiberByHostInstance(fe);if(Pe!=null){if(ie)for(;Pe!==null&&Xu(Pe);)Pe=Pe.return;return Zr(_i(Pe))}return null},getInstanceAndStyle:function(fe){var ie=null,Pe=null,Me=Wu(fe);return Me!==null&&(ie=Me.stateNode,Me.memoizedProps!==null&&(Pe=Me.memoizedProps.style)),{instance:ie,style:Pe}},getOwnersList:function(fe){var ie=Wu(fe);if(ie==null)return null;var Pe=ie._debugOwner,Me=[{displayName:_e(ie)||"Anonymous",id:fe,type:Su(ie)}];if(Pe)for(var at=Pe;at!==null;)Me.unshift({displayName:_e(at)||"Anonymous",id:Zr(_i(at)),type:Su(at)}),at=at._debugOwner||null;return Me},getPathForElement:function(fe){var ie=$0.get(fe);if(ie==null)return null;for(var Pe=[];ie!==null;)Pe.push(_0(ie)),ie=ie.return;return Pe.reverse(),Pe},getProfilingData:function(){var fe=[];if(_s===null)throw Error("getProfilingData() called before any profiling data was recorded");return _s.forEach(function(ie,Pe){var Me=[],at=[],mt=new Map,Qt=new Map,An=xl!==null&&xl.get(Pe)||"Unknown";O0!=null&&O0.forEach(function(Sn,_n){vo!=null&&vo.get(_n)===Pe&&at.push([_n,Sn])}),ie.forEach(function(Sn,_n){var Tn=Sn.changeDescriptions,ir=Sn.durations,Ut=Sn.interactions,Fi=Sn.maxActualDuration,Ar=Sn.priorityLevel,mr=Sn.commitTime,K=[];Ut.forEach(function(Di){mt.has(Di.id)||mt.set(Di.id,Di),K.push(Di.id);var ru=Qt.get(Di.id);ru!=null?ru.push(_n):Qt.set(Di.id,[_n])});for(var ti=[],ni=[],Wr=0;Wr1?Kn.set(Tn,ir-1):Kn.delete(Tn),ei.delete(Sn)}(tu),$r(Pe,!1))}else $u(tu,Pe),Qr(Pe,null,!1,!1);if(Fu&&at){var An=_s.get(tu);An!=null?An.push(il):_s.set(tu,[il])}mu(),bo&&U.emit("traceUpdates",Bo),tu=-1},handleCommitFiberUnmount:function(fe){$r(fe,!1)},inspectElement:function(fe,ie){if(Hi(fe)){if(ie!=null){A0(ie);var Pe=null;return ie[0]==="hooks"&&(Pe="hooks"),{id:fe,type:"hydrated-path",path:ie,value:Ri(Bu(Xi,ie),qi(null,Pe),ie)}}return{id:fe,type:"no-change"}}if(Hs=!1,Xi!==null&&Xi.id===fe||(R0={}),(Xi=sa(fe))===null)return{id:fe,type:"not-found"};ie!=null&&A0(ie),function(at){var mt=at.hooks,Qt=at.id,An=at.props,Sn=$0.get(Qt);if(Sn!=null){var _n=Sn.elementType,Tn=Sn.stateNode,ir=Sn.tag,Ut=Sn.type;switch(ir){case xt:case Cr:case Rn:ee.$r=Tn;break;case rt:ee.$r={hooks:mt,props:An,type:Ut};break;case rn:ee.$r={props:An,type:Ut.render};break;case Nr:case Lr:ee.$r={props:An,type:_n!=null&&_n.type!=null?_n.type:Ut};break;default:ee.$r=null}}else console.warn('Could not find Fiber with id "'.concat(Qt,'"'))}(Xi);var Me=Wt({},Xi);return Me.context=Ri(Me.context,qi("context",null)),Me.hooks=Ri(Me.hooks,qi("hooks","hooks")),Me.props=Ri(Me.props,qi("props",null)),Me.state=Ri(Me.state,qi("state",null)),{id:fe,type:"full-data",value:Me}},logElementToConsole:function(fe){var ie=Hi(fe)?Xi:sa(fe);if(ie!==null){var Pe=typeof console.groupCollapsed=="function";Pe&&console.groupCollapsed("[Click to expand] %c<".concat(ie.displayName||"Component"," />"),"color: var(--dom-tag-name-color); font-weight: normal;"),ie.props!==null&&console.log("Props:",ie.props),ie.state!==null&&console.log("State:",ie.state),ie.hooks!==null&&console.log("Hooks:",ie.hooks);var Me=Cl(fe);Me!==null&&console.log("Nodes:",Me),ie.source!==null&&console.log("Location:",ie.source),(window.chrome||/firefox/i.test(navigator.userAgent))&&console.log("Right-click any value to save it as a global variable for further inspection."),Pe&&console.groupEnd()}else console.warn('Could not find Fiber with id "'.concat(fe,'"'))},prepareViewAttributeSource:function(fe,ie){Hi(fe)&&(window.$attribute=Bu(Xi,ie))},prepareViewElementSource:function(fe){var ie=$0.get(fe);if(ie!=null){var Pe=ie.elementType,Me=ie.tag,at=ie.type;switch(Me){case xt:case Cr:case Rn:case rt:ee.$type=at;break;case rn:ee.$type=at.render;break;case Nr:case Lr:ee.$type=Pe!=null&&Pe.type!=null?Pe.type:at;break;default:ee.$type=null}}else console.warn('Could not find Fiber with id "'.concat(fe,'"'))},overrideSuspense:function(fe,ie){if(typeof Po!="function"||typeof rl!="function")throw new Error("Expected overrideSuspense() to not get called for earlier React versions.");ie?(Zu.add(fe),Zu.size===1&&Po(Es)):(Zu.delete(fe),Zu.size===0&&Po(gf));var Pe=$0.get(fe);Pe!=null&&rl(Pe)},overrideValueAtPath:function(fe,ie,Pe,Me,at){var mt=Wu(ie);if(mt!==null){var Qt=mt.stateNode;switch(fe){case"context":switch(Me=Me.slice(1),mt.tag){case xt:Me.length===0?Qt.context=at:Oo(Qt.context,Me,at),Qt.forceUpdate()}break;case"hooks":typeof au=="function"&&au(mt,Pe,Me,at);break;case"props":switch(mt.tag){case xt:mt.pendingProps=In(Qt.props,Me,at),Qt.forceUpdate();break;default:typeof Z0=="function"&&Z0(mt,Me,at)}break;case"state":switch(mt.tag){case xt:Oo(Qt.state,Me,at),Qt.forceUpdate()}}}},renamePath:function(fe,ie,Pe,Me,at){var mt=Wu(ie);if(mt!==null){var Qt=mt.stateNode;switch(fe){case"context":switch(Me=Me.slice(1),at=at.slice(1),mt.tag){case xt:Me.length===0||Kr(Qt.context,Me,at),Qt.forceUpdate()}break;case"hooks":typeof T0=="function"&&T0(mt,Pe,Me,at);break;case"props":Qt===null?typeof gi=="function"&&gi(mt,Me,at):(mt.pendingProps=en(Qt.props,Me,at),Qt.forceUpdate());break;case"state":Kr(Qt.state,Me,at),Qt.forceUpdate()}}},renderer:Y,setTraceUpdatesEnabled:function(fe){bo=fe},setTrackedPath:Li,startProfiling:aa,stopProfiling:function(){Fu=!1,es=!1},storeAsGlobal:function(fe,ie,Pe){if(Hi(fe)){var Me=Bu(Xi,ie),at="$reactTemp".concat(Pe);window[at]=Me,console.log(at),console.log(Me)}},updateComponentFilters:function(fe){if(Fu)throw Error("Cannot modify filter preferences while profiling");U.getFiberRoots(H).forEach(function(ie){tu=Zr(_i(ie.current)),qu(ie.current),$r(ie.current,!1),tu=-1}),gs(fe),Kn.clear(),U.getFiberRoots(H).forEach(function(ie){$u(tu=Zr(_i(ie.current)),ie.current),Qr(ie.current,null,!1,!1),mu(ie),tu=-1})}}}var $n;function tl(U){return(tl=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(H){return typeof H}:function(H){return H&&typeof Symbol=="function"&&H.constructor===Symbol&&H!==Symbol.prototype?"symbol":typeof H})(U)}function fo(U,H,Y){if($n===void 0)try{throw Error()}catch(Ce){var ee=Ce.stack.trim().match(/\n( *(at )?)/);$n=ee&&ee[1]||""}return` +`+$n+U}var I0=!1;function Sl(U,H,Y){if(!U||I0)return"";var ee,Ce=Error.prepareStackTrace;Error.prepareStackTrace=void 0,I0=!0;var _e=Y.current;Y.current=null;try{if(H){var Oe=function(){throw Error()};if(Object.defineProperty(Oe.prototype,"props",{set:function(){throw Error()}}),(typeof Reflect>"u"?"undefined":tl(Reflect))==="object"&&Reflect.construct){try{Reflect.construct(Oe,[])}catch(qe){ee=qe}Reflect.construct(U,[],Oe)}else{try{Oe.call()}catch(qe){ee=qe}U.call(Oe.prototype)}}else{try{throw Error()}catch(qe){ee=qe}U()}}catch(qe){if(qe&&ee&&typeof qe.stack=="string"){for(var $=qe.stack.split(` +`),Ne=ee.stack.split(` +`),Je=$.length-1,vt=Ne.length-1;Je>=1&&vt>=0&&$[Je]!==Ne[vt];)vt--;for(;Je>=1&&vt>=0;Je--,vt--)if($[Je]!==Ne[vt]){if(Je!==1||vt!==1)do if(Je--,--vt<0||$[Je]!==Ne[vt])return` +`+$[Je].replace(" at new "," at ");while(Je>=1&&vt>=0);break}}}finally{I0=!1,Error.prepareStackTrace=Ce,Y.current=_e}var oe=U?U.displayName||U.name:"";return oe?fo(oe):""}function Lo(U,H,Y,ee){return Sl(U,!1,ee)}function St(U,H,Y){var ee=U.HostComponent,Ce=U.LazyComponent,_e=U.SuspenseComponent,Oe=U.SuspenseListComponent,$=U.FunctionComponent,Ne=U.IndeterminateComponent,Je=U.SimpleMemoComponent,vt=U.ForwardRef,oe=U.Block,qe=U.ClassComponent;switch(H.tag){case ee:return fo(H.type);case Ce:return fo("Lazy");case _e:return fo("Suspense");case Oe:return fo("SuspenseList");case $:case Ne:case Je:return Lo(H.type,0,0,Y);case vt:return Lo(H.type.render,0,0,Y);case oe:return Lo(H.type._render,0,0,Y);case qe:return function(rt,xt,kt,bt){return Sl(rt,!0,bt)}(H.type,0,0,Y);default:return""}}function Bt(U,H,Y){try{var ee="",Ce=H;do ee+=St(U,Ce,Y),Ce=Ce.return;while(Ce);return ee}catch(_e){return` +Error generating stack: `+_e.message+` +`+_e.stack}}function Hn(U,H){var Y;if(typeof Symbol>"u"||U[Symbol.iterator]==null){if(Array.isArray(U)||(Y=function(Ne,Je){if(!!Ne){if(typeof Ne=="string")return qr(Ne,Je);var vt=Object.prototype.toString.call(Ne).slice(8,-1);if(vt==="Object"&&Ne.constructor&&(vt=Ne.constructor.name),vt==="Map"||vt==="Set")return Array.from(Ne);if(vt==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(vt))return qr(Ne,Je)}}(U))||H&&U&&typeof U.length=="number"){Y&&(U=Y);var ee=0,Ce=function(){};return{s:Ce,n:function(){return ee>=U.length?{done:!0}:{done:!1,value:U[ee++]}},e:function(Ne){throw Ne},f:Ce}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var _e,Oe=!0,$=!1;return{s:function(){Y=U[Symbol.iterator]()},n:function(){var Ne=Y.next();return Oe=Ne.done,Ne},e:function(Ne){$=!0,_e=Ne},f:function(){try{Oe||Y.return==null||Y.return()}finally{if($)throw _e}}}}function qr(U,H){(H==null||H>U.length)&&(H=U.length);for(var Y=0,ee=new Array(H);Y0?Je[Je.length-1]:null,qe=oe!==null&&(Xr.test(oe)||Au.test(oe));if(!qe){var rt,xt=Hn(p0.values());try{for(xt.s();!(rt=xt.n()).done;){var kt=rt.value,bt=kt.currentDispatcherRef,sn=kt.getCurrentFiber,rn=kt.workTagMap,Ft=sn();if(Ft!=null){var Dn=Bt(rn,Ft,bt);Dn!==""&&Je.push(Dn);break}}}catch(dr){xt.e(dr)}finally{xt.f()}}}catch{}_e.apply(void 0,Je)};Oe.__REACT_DEVTOOLS_ORIGINAL_METHOD__=_e,Ni[Ce]=Oe}catch{}})}}function Uu(U){return(Uu=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(H){return typeof H}:function(H){return H&&typeof Symbol=="function"&&H.constructor===Symbol&&H!==Symbol.prototype?"symbol":typeof H})(U)}function vs(U,H){for(var Y=0;Y"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch{return!1}}();return function(){var Y,ee=Le(U);if(H){var Ce=Le(this).constructor;Y=Reflect.construct(ee,arguments,Ce)}else Y=ee.apply(this,arguments);return Se(this,Y)}}function Se(U,H){return!H||Uu(H)!=="object"&&typeof H!="function"?Fe(U):H}function Fe(U){if(U===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return U}function Le(U){return(Le=Object.setPrototypeOf?Object.getPrototypeOf:function(H){return H.__proto__||Object.getPrototypeOf(H)})(U)}function pt(U,H,Y){return H in U?Object.defineProperty(U,H,{value:Y,enumerable:!0,configurable:!0,writable:!0}):U[H]=Y,U}var Yn=function(U){(function(Oe,$){if(typeof $!="function"&&$!==null)throw new TypeError("Super expression must either be null or a function");Oe.prototype=Object.create($&&$.prototype,{constructor:{value:Oe,writable:!0,configurable:!0}}),$&&b0(Oe,$)})(_e,U);var H,Y,ee,Ce=Q(_e);function _e(Oe){var $;(function(oe,qe){if(!(oe instanceof qe))throw new TypeError("Cannot call a class as a function")})(this,_e),pt(Fe($=Ce.call(this)),"_isProfiling",!1),pt(Fe($),"_recordChangeDescriptions",!1),pt(Fe($),"_rendererInterfaces",{}),pt(Fe($),"_persistedSelection",null),pt(Fe($),"_persistedSelectionMatch",null),pt(Fe($),"_traceUpdatesEnabled",!1),pt(Fe($),"copyElementPath",function(oe){var qe=oe.id,rt=oe.path,xt=oe.rendererID,kt=$._rendererInterfaces[xt];kt==null?console.warn('Invalid renderer id "'.concat(xt,'" for element "').concat(qe,'"')):kt.copyElementPath(qe,rt)}),pt(Fe($),"deletePath",function(oe){var qe=oe.hookID,rt=oe.id,xt=oe.path,kt=oe.rendererID,bt=oe.type,sn=$._rendererInterfaces[kt];sn==null?console.warn('Invalid renderer id "'.concat(kt,'" for element "').concat(rt,'"')):sn.deletePath(bt,rt,qe,xt)}),pt(Fe($),"getProfilingData",function(oe){var qe=oe.rendererID,rt=$._rendererInterfaces[qe];rt==null&&console.warn('Invalid renderer id "'.concat(qe,'"')),$._bridge.send("profilingData",rt.getProfilingData())}),pt(Fe($),"getProfilingStatus",function(){$._bridge.send("profilingStatus",$._isProfiling)}),pt(Fe($),"getOwnersList",function(oe){var qe=oe.id,rt=oe.rendererID,xt=$._rendererInterfaces[rt];if(xt==null)console.warn('Invalid renderer id "'.concat(rt,'" for element "').concat(qe,'"'));else{var kt=xt.getOwnersList(qe);$._bridge.send("ownersList",{id:qe,owners:kt})}}),pt(Fe($),"inspectElement",function(oe){var qe=oe.id,rt=oe.path,xt=oe.rendererID,kt=$._rendererInterfaces[xt];kt==null?console.warn('Invalid renderer id "'.concat(xt,'" for element "').concat(qe,'"')):($._bridge.send("inspectedElement",kt.inspectElement(qe,rt)),$._persistedSelectionMatch!==null&&$._persistedSelectionMatch.id===qe||($._persistedSelection=null,$._persistedSelectionMatch=null,kt.setTrackedPath(null),$._throttledPersistSelection(xt,qe)))}),pt(Fe($),"logElementToConsole",function(oe){var qe=oe.id,rt=oe.rendererID,xt=$._rendererInterfaces[rt];xt==null?console.warn('Invalid renderer id "'.concat(rt,'" for element "').concat(qe,'"')):xt.logElementToConsole(qe)}),pt(Fe($),"overrideSuspense",function(oe){var qe=oe.id,rt=oe.rendererID,xt=oe.forceFallback,kt=$._rendererInterfaces[rt];kt==null?console.warn('Invalid renderer id "'.concat(rt,'" for element "').concat(qe,'"')):kt.overrideSuspense(qe,xt)}),pt(Fe($),"overrideValueAtPath",function(oe){var qe=oe.hookID,rt=oe.id,xt=oe.path,kt=oe.rendererID,bt=oe.type,sn=oe.value,rn=$._rendererInterfaces[kt];rn==null?console.warn('Invalid renderer id "'.concat(kt,'" for element "').concat(rt,'"')):rn.overrideValueAtPath(bt,rt,qe,xt,sn)}),pt(Fe($),"overrideContext",function(oe){var qe=oe.id,rt=oe.path,xt=oe.rendererID,kt=oe.wasForwarded,bt=oe.value;kt||$.overrideValueAtPath({id:qe,path:rt,rendererID:xt,type:"context",value:bt})}),pt(Fe($),"overrideHookState",function(oe){var qe=oe.id,rt=(oe.hookID,oe.path),xt=oe.rendererID,kt=oe.wasForwarded,bt=oe.value;kt||$.overrideValueAtPath({id:qe,path:rt,rendererID:xt,type:"hooks",value:bt})}),pt(Fe($),"overrideProps",function(oe){var qe=oe.id,rt=oe.path,xt=oe.rendererID,kt=oe.wasForwarded,bt=oe.value;kt||$.overrideValueAtPath({id:qe,path:rt,rendererID:xt,type:"props",value:bt})}),pt(Fe($),"overrideState",function(oe){var qe=oe.id,rt=oe.path,xt=oe.rendererID,kt=oe.wasForwarded,bt=oe.value;kt||$.overrideValueAtPath({id:qe,path:rt,rendererID:xt,type:"state",value:bt})}),pt(Fe($),"reloadAndProfile",function(oe){q("React::DevTools::reloadAndProfile","true"),q("React::DevTools::recordChangeDescriptions",oe?"true":"false"),$._bridge.send("reloadAppForProfiling")}),pt(Fe($),"renamePath",function(oe){var qe=oe.hookID,rt=oe.id,xt=oe.newPath,kt=oe.oldPath,bt=oe.rendererID,sn=oe.type,rn=$._rendererInterfaces[bt];rn==null?console.warn('Invalid renderer id "'.concat(bt,'" for element "').concat(rt,'"')):rn.renamePath(sn,rt,qe,kt,xt)}),pt(Fe($),"setTraceUpdatesEnabled",function(oe){for(var qe in $._traceUpdatesEnabled=oe,Vt(oe),$._rendererInterfaces)$._rendererInterfaces[qe].setTraceUpdatesEnabled(oe)}),pt(Fe($),"syncSelectionFromNativeElementsPanel",function(){var oe=window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0;oe!=null&&$.selectNode(oe)}),pt(Fe($),"shutdown",function(){$.emit("shutdown")}),pt(Fe($),"startProfiling",function(oe){for(var qe in $._recordChangeDescriptions=oe,$._isProfiling=!0,$._rendererInterfaces)$._rendererInterfaces[qe].startProfiling(oe);$._bridge.send("profilingStatus",$._isProfiling)}),pt(Fe($),"stopProfiling",function(){for(var oe in $._isProfiling=!1,$._recordChangeDescriptions=!1,$._rendererInterfaces)$._rendererInterfaces[oe].stopProfiling();$._bridge.send("profilingStatus",$._isProfiling)}),pt(Fe($),"storeAsGlobal",function(oe){var qe=oe.count,rt=oe.id,xt=oe.path,kt=oe.rendererID,bt=$._rendererInterfaces[kt];bt==null?console.warn('Invalid renderer id "'.concat(kt,'" for element "').concat(rt,'"')):bt.storeAsGlobal(rt,xt,qe)}),pt(Fe($),"updateConsolePatchSettings",function(oe){var qe=oe.appendComponentStack,rt=oe.breakOnConsoleErrors;qe||rt?Jl({appendComponentStack:qe,breakOnConsoleErrors:rt}):Ct!==null&&(Ct(),Ct=null)}),pt(Fe($),"updateComponentFilters",function(oe){for(var qe in $._rendererInterfaces)$._rendererInterfaces[qe].updateComponentFilters(oe)}),pt(Fe($),"viewAttributeSource",function(oe){var qe=oe.id,rt=oe.path,xt=oe.rendererID,kt=$._rendererInterfaces[xt];kt==null?console.warn('Invalid renderer id "'.concat(xt,'" for element "').concat(qe,'"')):kt.prepareViewAttributeSource(qe,rt)}),pt(Fe($),"viewElementSource",function(oe){var qe=oe.id,rt=oe.rendererID,xt=$._rendererInterfaces[rt];xt==null?console.warn('Invalid renderer id "'.concat(rt,'" for element "').concat(qe,'"')):xt.prepareViewElementSource(qe)}),pt(Fe($),"onTraceUpdates",function(oe){$.emit("traceUpdates",oe)}),pt(Fe($),"onHookOperations",function(oe){if($._bridge.send("operations",oe),$._persistedSelection!==null){var qe=oe[0];if($._persistedSelection.rendererID===qe){var rt=$._rendererInterfaces[qe];if(rt==null)console.warn('Invalid renderer id "'.concat(qe,'"'));else{var xt=$._persistedSelectionMatch,kt=rt.getBestMatchForTrackedPath();$._persistedSelectionMatch=kt;var bt=xt!==null?xt.id:null,sn=kt!==null?kt.id:null;bt!==sn&&sn!==null&&$._bridge.send("selectFiber",sn),kt!==null&&kt.isFullMatch&&($._persistedSelection=null,$._persistedSelectionMatch=null,rt.setTrackedPath(null))}}}}),pt(Fe($),"_throttledPersistSelection",N()(function(oe,qe){var rt=$._rendererInterfaces[oe],xt=rt!=null?rt.getPathForElement(qe):null;xt!==null?q("React::DevTools::lastSelection",JSON.stringify({rendererID:oe,path:xt})):j("React::DevTools::lastSelection")},1e3)),x("React::DevTools::reloadAndProfile")==="true"&&($._recordChangeDescriptions=x("React::DevTools::recordChangeDescriptions")==="true",$._isProfiling=!0,j("React::DevTools::recordChangeDescriptions"),j("React::DevTools::reloadAndProfile"));var Ne=x("React::DevTools::lastSelection");Ne!=null&&($._persistedSelection=JSON.parse(Ne)),$._bridge=Oe,Oe.addListener("copyElementPath",$.copyElementPath),Oe.addListener("deletePath",$.deletePath),Oe.addListener("getProfilingData",$.getProfilingData),Oe.addListener("getProfilingStatus",$.getProfilingStatus),Oe.addListener("getOwnersList",$.getOwnersList),Oe.addListener("inspectElement",$.inspectElement),Oe.addListener("logElementToConsole",$.logElementToConsole),Oe.addListener("overrideSuspense",$.overrideSuspense),Oe.addListener("overrideValueAtPath",$.overrideValueAtPath),Oe.addListener("reloadAndProfile",$.reloadAndProfile),Oe.addListener("renamePath",$.renamePath),Oe.addListener("setTraceUpdatesEnabled",$.setTraceUpdatesEnabled),Oe.addListener("startProfiling",$.startProfiling),Oe.addListener("stopProfiling",$.stopProfiling),Oe.addListener("storeAsGlobal",$.storeAsGlobal),Oe.addListener("syncSelectionFromNativeElementsPanel",$.syncSelectionFromNativeElementsPanel),Oe.addListener("shutdown",$.shutdown),Oe.addListener("updateConsolePatchSettings",$.updateConsolePatchSettings),Oe.addListener("updateComponentFilters",$.updateComponentFilters),Oe.addListener("viewAttributeSource",$.viewAttributeSource),Oe.addListener("viewElementSource",$.viewElementSource),Oe.addListener("overrideContext",$.overrideContext),Oe.addListener("overrideHookState",$.overrideHookState),Oe.addListener("overrideProps",$.overrideProps),Oe.addListener("overrideState",$.overrideState),$._isProfiling&&Oe.send("profilingStatus",!0);var Je,vt=!1;try{localStorage.getItem("test"),vt=!0}catch{}return Oe.send("isBackendStorageAPISupported",vt),Re(Oe,Fe($)),Je=Fe($),Je.addListener("traceUpdates",Er),$}return H=_e,(Y=[{key:"getInstanceAndStyle",value:function(Oe){var $=Oe.id,Ne=Oe.rendererID,Je=this._rendererInterfaces[Ne];return Je==null?(console.warn('Invalid renderer id "'.concat(Ne,'"')),null):Je.getInstanceAndStyle($)}},{key:"getIDForNode",value:function(Oe){for(var $ in this._rendererInterfaces){var Ne=this._rendererInterfaces[$];try{var Je=Ne.getFiberIDForNative(Oe,!0);if(Je!==null)return Je}catch{}}return null}},{key:"selectNode",value:function(Oe){var $=this.getIDForNode(Oe);$!==null&&this._bridge.send("selectFiber",$)}},{key:"setRendererInterface",value:function(Oe,$){this._rendererInterfaces[Oe]=$,this._isProfiling&&$.startProfiling(this._recordChangeDescriptions),$.setTraceUpdatesEnabled(this._traceUpdatesEnabled);var Ne=this._persistedSelection;Ne!==null&&Ne.rendererID===Oe&&$.setTrackedPath(Ne.path)}},{key:"onUnsupportedRenderer",value:function(Oe){this._bridge.send("unsupportedRendererVersion",Oe)}},{key:"rendererInterfaces",get:function(){return this._rendererInterfaces}}])&&vs(H.prototype,Y),ee&&vs(H,ee),_e}(E);function Cn(U){return(Cn=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(H){return typeof H}:function(H){return H&&typeof Symbol=="function"&&H.constructor===Symbol&&H!==Symbol.prototype?"symbol":typeof H})(U)}function cr(U){return function(H){if(Array.isArray(H))return Si(H)}(U)||function(H){if(typeof Symbol<"u"&&Symbol.iterator in Object(H))return Array.from(H)}(U)||function(H,Y){if(!!H){if(typeof H=="string")return Si(H,Y);var ee=Object.prototype.toString.call(H).slice(8,-1);if(ee==="Object"&&H.constructor&&(ee=H.constructor.name),ee==="Map"||ee==="Set")return Array.from(H);if(ee==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(ee))return Si(H,Y)}}(U)||function(){throw new TypeError(`Invalid attempt to spread non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Si(U,H){(H==null||H>U.length)&&(H=U.length);for(var Y=0,ee=new Array(H);Y"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch{return!1}}();return function(){var Y,ee=Fo(U);if(H){var Ce=Fo(this).constructor;Y=Reflect.construct(ee,arguments,Ce)}else Y=ee.apply(this,arguments);return wu(this,Y)}}function wu(U,H){return!H||Cn(H)!=="object"&&typeof H!="function"?Ti(U):H}function Ti(U){if(U===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return U}function Fo(U){return(Fo=Object.setPrototypeOf?Object.getPrototypeOf:function(H){return H.__proto__||Object.getPrototypeOf(H)})(U)}function Mu(U,H,Y){return H in U?Object.defineProperty(U,H,{value:Y,enumerable:!0,configurable:!0,writable:!0}):U[H]=Y,U}var po=function(U){(function(Oe,$){if(typeof $!="function"&&$!==null)throw new TypeError("Super expression must either be null or a function");Oe.prototype=Object.create($&&$.prototype,{constructor:{value:Oe,writable:!0,configurable:!0}}),$&&ju(Oe,$)})(_e,U);var H,Y,ee,Ce=zu(_e);function _e(Oe){var $;return function(Ne,Je){if(!(Ne instanceof Je))throw new TypeError("Cannot call a class as a function")}(this,_e),Mu(Ti($=Ce.call(this)),"_isShutdown",!1),Mu(Ti($),"_messageQueue",[]),Mu(Ti($),"_timeoutID",null),Mu(Ti($),"_wallUnlisten",null),Mu(Ti($),"_flush",function(){if($._timeoutID!==null&&(clearTimeout($._timeoutID),$._timeoutID=null),$._messageQueue.length){for(var Ne=0;Ne<$._messageQueue.length;Ne+=2){var Je;(Je=$._wall).send.apply(Je,[$._messageQueue[Ne]].concat(cr($._messageQueue[Ne+1])))}$._messageQueue.length=0,$._timeoutID=setTimeout($._flush,100)}}),Mu(Ti($),"overrideValueAtPath",function(Ne){var Je=Ne.id,vt=Ne.path,oe=Ne.rendererID,qe=Ne.type,rt=Ne.value;switch(qe){case"context":$.send("overrideContext",{id:Je,path:vt,rendererID:oe,wasForwarded:!0,value:rt});break;case"hooks":$.send("overrideHookState",{id:Je,path:vt,rendererID:oe,wasForwarded:!0,value:rt});break;case"props":$.send("overrideProps",{id:Je,path:vt,rendererID:oe,wasForwarded:!0,value:rt});break;case"state":$.send("overrideState",{id:Je,path:vt,rendererID:oe,wasForwarded:!0,value:rt})}}),$._wall=Oe,$._wallUnlisten=Oe.listen(function(Ne){Ti($).emit(Ne.event,Ne.payload)})||null,$.addListener("overrideValueAtPath",$.overrideValueAtPath),$}return H=_e,(Y=[{key:"send",value:function(Oe){if(this._isShutdown)console.warn('Cannot send message "'.concat(Oe,'" through a Bridge that has been shutdown.'));else{for(var $=arguments.length,Ne=new Array($>1?$-1:0),Je=1;Je<$;Je++)Ne[Je-1]=arguments[Je];this._messageQueue.push(Oe,Ne),this._timeoutID||(this._timeoutID=setTimeout(this._flush,0))}}},{key:"shutdown",value:function(){if(this._isShutdown)console.warn("Bridge was already shutdown.");else{this.send("shutdown"),this._isShutdown=!0,this.addListener=function(){},this.emit=function(){},this.removeAllListeners();var Oe=this._wallUnlisten;Oe&&Oe();do this._flush();while(this._messageQueue.length);this._timeoutID!==null&&(clearTimeout(this._timeoutID),this._timeoutID=null)}}},{key:"wall",get:function(){return this._wall}}])&&Ou(H.prototype,Y),ee&&Ou(H,ee),_e}(E);function Hu(U,H,Y){var ee=U[H];return U[H]=function(Ce){return Y.call(this,ee,arguments)},ee}function Pa(U,H){for(var Y in H)U[Y]=H[Y]}function v0(U){typeof U.forceUpdate=="function"?U.forceUpdate():U.updater!=null&&typeof U.updater.enqueueForceUpdate=="function"&&U.updater.enqueueForceUpdate(this,function(){},"forceUpdate")}function ia(U,H){var Y=Object.keys(U);if(Object.getOwnPropertySymbols){var ee=Object.getOwnPropertySymbols(U);H&&(ee=ee.filter(function(Ce){return Object.getOwnPropertyDescriptor(U,Ce).enumerable})),Y.push.apply(Y,ee)}return Y}function J0(U){for(var H=1;H0?oe[oe.length-1]:0),oe.push(un),$.set(et,Je(It._topLevelWrapper));try{var fn=ut.apply(this,wt);return oe.pop(),fn}catch(wr){throw oe=[],wr}finally{if(oe.length===0){var Jn=$.get(et);if(Jn===void 0)throw new Error("Expected to find root ID.");dr(Jn)}}},performUpdateIfNecessary:function(ut,wt){var et=wt[0];if(S0(et)===9)return ut.apply(this,wt);var It=Je(et);oe.push(It);var un=Qn(et);try{var fn=ut.apply(this,wt),Jn=Qn(et);return vt(un,Jn)||xt(et,It,Jn),oe.pop(),fn}catch(au){throw oe=[],au}finally{if(oe.length===0){var wr=$.get(et);if(wr===void 0)throw new Error("Expected to find root ID.");dr(wr)}}},receiveComponent:function(ut,wt){var et=wt[0];if(S0(et)===9)return ut.apply(this,wt);var It=Je(et);oe.push(It);var un=Qn(et);try{var fn=ut.apply(this,wt),Jn=Qn(et);return vt(un,Jn)||xt(et,It,Jn),oe.pop(),fn}catch(au){throw oe=[],au}finally{if(oe.length===0){var wr=$.get(et);if(wr===void 0)throw new Error("Expected to find root ID.");dr(wr)}}},unmountComponent:function(ut,wt){var et=wt[0];if(S0(et)===9)return ut.apply(this,wt);var It=Je(et);oe.push(It);try{var un=ut.apply(this,wt);return oe.pop(),function(Jn,wr){rn.push(wr),_e.delete(wr)}(0,It),un}catch(Jn){throw oe=[],Jn}finally{if(oe.length===0){var fn=$.get(et);if(fn===void 0)throw new Error("Expected to find root ID.");dr(fn)}}}}));var bt=[],sn=new Map,rn=[],Ft=0,Dn=null;function dr(ut){if(bt.length!==0||rn.length!==0||Dn!==null){var wt=rn.length+(Dn===null?0:1),et=new Array(3+Ft+(wt>0?2+wt:0)+bt.length),It=0;if(et[It++]=H,et[It++]=ut,et[It++]=Ft,sn.forEach(function(Jn,wr){et[It++]=wr.length;for(var au=Y0(wr),ku=0;ku0){et[It++]=2,et[It++]=wt;for(var un=0;un"),"color: var(--dom-tag-name-color); font-weight: normal;"),wt.props!==null&&console.log("Props:",wt.props),wt.state!==null&&console.log("State:",wt.state),wt.context!==null&&console.log("Context:",wt.context);var It=Ce(ut);It!==null&&console.log("Node:",It),(window.chrome||/firefox/i.test(navigator.userAgent))&&console.log("Right-click any value to save it as a global variable for further inspection."),et&&console.groupEnd()}else console.warn('Could not find element with id "'.concat(ut,'"'))},overrideSuspense:function(){throw new Error("overrideSuspense not supported by this renderer")},overrideValueAtPath:function(ut,wt,et,It,un){var fn=_e.get(wt);if(fn!=null){var Jn=fn._instance;if(Jn!=null)switch(ut){case"context":Oo(Jn.context,It,un),v0(Jn);break;case"hooks":throw new Error("Hooks not supported by this renderer");case"props":var wr=fn._currentElement;fn._currentElement=J0(J0({},wr),{},{props:In(wr.props,It,un)}),v0(Jn);break;case"state":Oo(Jn.state,It,un),v0(Jn)}}},renamePath:function(ut,wt,et,It,un){var fn=_e.get(wt);if(fn!=null){var Jn=fn._instance;if(Jn!=null)switch(ut){case"context":Kr(Jn.context,It,un),v0(Jn);break;case"hooks":throw new Error("Hooks not supported by this renderer");case"props":var wr=fn._currentElement;fn._currentElement=J0(J0({},wr),{},{props:en(wr.props,It,un)}),v0(Jn);break;case"state":Kr(Jn.state,It,un),v0(Jn)}}},prepareViewAttributeSource:function(ut,wt){var et=Lr(ut);et!==null&&(window.$attribute=Bu(et,wt))},prepareViewElementSource:function(ut){var wt=_e.get(ut);if(wt!=null){var et=wt._currentElement;et!=null?ee.$type=et.type:console.warn('Could not find element with id "'.concat(ut,'"'))}else console.warn('Could not find instance with id "'.concat(ut,'"'))},renderer:Y,setTraceUpdatesEnabled:function(ut){},setTrackedPath:function(ut){},startProfiling:function(){},stopProfiling:function(){},storeAsGlobal:function(ut,wt,et){var It=Lr(ut);if(It!==null){var un=Bu(It,wt),fn="$reactTemp".concat(et);window[fn]=un,console.log(fn),console.log(un)}},updateComponentFilters:function(ut){}}}function si(U,H){var Y=!1,ee={bottom:0,left:0,right:0,top:0},Ce=H[U];if(Ce!=null){for(var _e=0,Oe=Object.keys(ee);_e0?"development":"production";var bt=Function.prototype.toString;if(kt.Mount&&kt.Mount._renderNewRootComponent){var sn=bt.call(kt.Mount._renderNewRootComponent);return sn.indexOf("function")!==0?"production":sn.indexOf("storedMeasure")!==-1?"development":sn.indexOf("should be a pure function")!==-1?sn.indexOf("NODE_ENV")!==-1||sn.indexOf("development")!==-1||sn.indexOf("true")!==-1?"development":sn.indexOf("nextElement")!==-1||sn.indexOf("nextComponent")!==-1?"unminified":"development":sn.indexOf("nextElement")!==-1||sn.indexOf("nextComponent")!==-1?"unminified":"outdated"}}catch{}return"production"}(Ne);try{var oe=window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__!==!1,qe=window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__===!0;(oe||qe)&&(co(Ne),Jl({appendComponentStack:oe,breakOnConsoleErrors:qe}))}catch{}var rt=U.__REACT_DEVTOOLS_ATTACH__;if(typeof rt=="function"){var xt=rt($,Je,Ne,U);$.rendererInterfaces.set(Je,xt)}return $.emit("renderer",{id:Je,renderer:Ne,reactBuildType:vt}),Je},on:function(Ne,Je){_e[Ne]||(_e[Ne]=[]),_e[Ne].push(Je)},off:function(Ne,Je){if(_e[Ne]){var vt=_e[Ne].indexOf(Je);vt!==-1&&_e[Ne].splice(vt,1),_e[Ne].length||delete _e[Ne]}},sub:function(Ne,Je){return $.on(Ne,Je),function(){return $.off(Ne,Je)}},supportsFiber:!0,checkDCE:function(Ne){try{Function.prototype.toString.call(Ne).indexOf("^_^")>-1&&(Y=!0,setTimeout(function(){throw new Error("React is running in production mode, but dead code elimination has not been applied. Read how to correctly configure React for production: https://reactjs.org/link/perf-use-production-build")}))}catch{}},onCommitFiberUnmount:function(Ne,Je){var vt=Ce.get(Ne);vt!=null&&vt.handleCommitFiberUnmount(Je)},onCommitFiberRoot:function(Ne,Je,vt){var oe=$.getFiberRoots(Ne),qe=Je.current,rt=oe.has(Je),xt=qe.memoizedState==null||qe.memoizedState.element==null;rt||xt?rt&&xt&&oe.delete(Je):oe.add(Je);var kt=Ce.get(Ne);kt!=null&&kt.handleCommitFiberRoot(Je,vt)}};Object.defineProperty(U,"__REACT_DEVTOOLS_GLOBAL_HOOK__",{configurable:!1,enumerable:!1,get:function(){return $}})})(window);var m0=window.__REACT_DEVTOOLS_GLOBAL_HOOK__,Us=[{type:1,value:7,isEnabled:!0}];function zi(U){if(m0!=null){var H=U||{},Y=H.host,ee=Y===void 0?"localhost":Y,Ce=H.nativeStyleEditorValidAttributes,_e=H.useHttps,Oe=_e!==void 0&&_e,$=H.port,Ne=$===void 0?8097:$,Je=H.websocket,vt=H.resolveRNStyle,oe=vt===void 0?null:vt,qe=H.isAppActive,rt=Oe?"wss":"ws",xt=null;if((qe===void 0?function(){return!0}:qe)()){var kt=null,bt=[],sn=rt+"://"+ee+":"+Ne,rn=Je||new window.WebSocket(sn);rn.onclose=function(){kt!==null&&kt.emit("shutdown"),Ft()},rn.onerror=function(){Ft()},rn.onmessage=function(Dn){var dr;try{if(typeof Dn.data!="string")throw Error();dr=JSON.parse(Dn.data)}catch{return void console.error("[React DevTools] Failed to parse JSON: "+Dn.data)}bt.forEach(function(er){try{er(dr)}catch(Cr){throw console.log("[React DevTools] Error calling listener",dr),console.log("error:",Cr),Cr}})},rn.onopen=function(){(kt=new po({listen:function(Rn){return bt.push(Rn),function(){var Nr=bt.indexOf(Rn);Nr>=0&&bt.splice(Nr,1)}},send:function(Rn,Nr,y0){rn.readyState===rn.OPEN?rn.send(JSON.stringify({event:Rn,payload:Nr})):(kt!==null&&kt.shutdown(),Ft())}})).addListener("inspectElement",function(Rn){var Nr=Rn.id,y0=Rn.rendererID,Lr=Dn.rendererInterfaces[y0];if(Lr!=null){var ut=Lr.findNativeNodesForFiberID(Nr);ut!=null&&ut[0]!=null&&Dn.emit("showNativeHighlight",ut[0])}}),kt.addListener("updateComponentFilters",function(Rn){Us=Rn}),window.__REACT_DEVTOOLS_COMPONENT_FILTERS__==null&&kt.send("overrideComponentFilters",Us);var Dn=new Yn(kt);if(Dn.addListener("shutdown",function(){m0.emit("shutdown")}),function(Rn,Nr,y0){if(Rn==null)return function(){};var Lr=[Rn.sub("renderer-attached",function(et){var It=et.id,un=(et.renderer,et.rendererInterface);Nr.setRendererInterface(It,un),un.flushInitialOperations()}),Rn.sub("unsupported-renderer-version",function(et){Nr.onUnsupportedRenderer(et)}),Rn.sub("operations",Nr.onHookOperations),Rn.sub("traceUpdates",Nr.onTraceUpdates)],ut=function(et,It){var un=Rn.rendererInterfaces.get(et);un==null&&(typeof It.findFiberByHostInstance=="function"?un=Is(Rn,et,It,y0):It.ComponentTree&&(un=ac(Rn,et,It,y0)),un!=null&&Rn.rendererInterfaces.set(et,un)),un!=null?Rn.emit("renderer-attached",{id:et,renderer:It,rendererInterface:un}):Rn.emit("unsupported-renderer-version",et)};Rn.renderers.forEach(function(et,It){ut(It,et)}),Lr.push(Rn.sub("renderer",function(et){var It=et.id,un=et.renderer;ut(It,un)})),Rn.emit("react-devtools",Nr),Rn.reactDevtoolsAgent=Nr;var wt=function(){Lr.forEach(function(et){return et()}),Rn.rendererInterfaces.forEach(function(et){et.cleanup()}),Rn.reactDevtoolsAgent=null};Nr.addListener("shutdown",wt),Lr.push(function(){Nr.removeListener("shutdown",wt)})}(m0,Dn,window),oe!=null||m0.resolveRNStyle!=null)oa(kt,Dn,oe||m0.resolveRNStyle,Ce||m0.nativeStyleEditorValidAttributes||null);else{var dr,er,Cr=function(){kt!==null&&oa(kt,Dn,dr,er)};m0.hasOwnProperty("resolveRNStyle")||Object.defineProperty(m0,"resolveRNStyle",{enumerable:!1,get:function(){return dr},set:function(Rn){dr=Rn,Cr()}}),m0.hasOwnProperty("nativeStyleEditorValidAttributes")||Object.defineProperty(m0,"nativeStyleEditorValidAttributes",{enumerable:!1,get:function(){return er},set:function(Rn){er=Rn,Cr()}})}}}else Ft()}function Ft(){xt===null&&(xt=setTimeout(function(){return zi(U)},2e3))}}}])})});var FC=nt(LC=>{"use strict";Object.defineProperty(LC,"__esModule",{value:!0});kC();var wb=NC();wb.connectToDevTools()});var UC=nt(sg=>{"use strict";var BC=sg&&sg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(sg,"__esModule",{value:!0});var PC=z_(),Sb=BC(YS()),IC=BC(eh()),ps=BD();process.env.DEV==="true"&&FC();var bC=o=>{o==null||o.unsetMeasureFunc(),o==null||o.freeRecursive()};sg.default=Sb.default({schedulePassiveEffects:PC.unstable_scheduleCallback,cancelPassiveEffects:PC.unstable_cancelCallback,now:Date.now,getRootHostContext:()=>({isInsideText:!1}),prepareForCommit:()=>{},resetAfterCommit:o=>{if(o.isStaticDirty){o.isStaticDirty=!1,typeof o.onImmediateRender=="function"&&o.onImmediateRender();return}typeof o.onRender=="function"&&o.onRender()},getChildHostContext:(o,l)=>{let f=o.isInsideText,h=l==="ink-text"||l==="ink-virtual-text";return f===h?o:{isInsideText:h}},shouldSetTextContent:()=>!1,createInstance:(o,l,f,h)=>{if(h.isInsideText&&o==="ink-box")throw new Error(" can\u2019t be nested inside component");let E=o==="ink-text"&&h.isInsideText?"ink-virtual-text":o,t=ps.createNode(E);for(let[N,F]of Object.entries(l))N!=="children"&&(N==="style"?ps.setStyle(t,F):N==="internal_transform"?t.internal_transform=F:N==="internal_static"?t.internal_static=!0:ps.setAttribute(t,N,F));return t},createTextInstance:(o,l,f)=>{if(!f.isInsideText)throw new Error(`Text string "${o}" must be rendered inside component`);return ps.createTextNode(o)},resetTextContent:()=>{},hideTextInstance:o=>{ps.setTextNodeValue(o,"")},unhideTextInstance:(o,l)=>{ps.setTextNodeValue(o,l)},getPublicInstance:o=>o,hideInstance:o=>{var l;(l=o.yogaNode)===null||l===void 0||l.setDisplay(IC.default.DISPLAY_NONE)},unhideInstance:o=>{var l;(l=o.yogaNode)===null||l===void 0||l.setDisplay(IC.default.DISPLAY_FLEX)},appendInitialChild:ps.appendChildNode,appendChild:ps.appendChildNode,insertBefore:ps.insertBeforeNode,finalizeInitialChildren:(o,l,f,h)=>(o.internal_static&&(h.isStaticDirty=!0,h.staticNode=o),!1),supportsMutation:!0,appendChildToContainer:ps.appendChildNode,insertInContainerBefore:ps.insertBeforeNode,removeChildFromContainer:(o,l)=>{ps.removeChildNode(o,l),bC(l.yogaNode)},prepareUpdate:(o,l,f,h,E)=>{o.internal_static&&(E.isStaticDirty=!0);let t={},N=Object.keys(h);for(let F of N)if(h[F]!==f[F]){if(F==="style"&&typeof h.style=="object"&&typeof f.style=="object"){let x=h.style,j=f.style,q=Object.keys(x);for(let V of q){if(V==="borderStyle"||V==="borderColor"){if(typeof t.style!="object"){let re={};t.style=re}t.style.borderStyle=x.borderStyle,t.style.borderColor=x.borderColor}if(x[V]!==j[V]){if(typeof t.style!="object"){let re={};t.style=re}t.style[V]=x[V]}}continue}t[F]=h[F]}return t},commitUpdate:(o,l)=>{for(let[f,h]of Object.entries(l))f!=="children"&&(f==="style"?ps.setStyle(o,h):f==="internal_transform"?o.internal_transform=h:f==="internal_static"?o.internal_static=!0:ps.setAttribute(o,f,h))},commitTextUpdate:(o,l,f)=>{ps.setTextNodeValue(o,f)},removeChild:(o,l)=>{ps.removeChildNode(o,l),bC(l.yogaNode)}})});var zC=nt((sq,jC)=>{"use strict";jC.exports=(o,l=1,f)=>{if(f={indent:" ",includeEmptyLines:!1,...f},typeof o!="string")throw new TypeError(`Expected \`input\` to be a \`string\`, got \`${typeof o}\``);if(typeof l!="number")throw new TypeError(`Expected \`count\` to be a \`number\`, got \`${typeof l}\``);if(typeof f.indent!="string")throw new TypeError(`Expected \`options.indent\` to be a \`string\`, got \`${typeof f.indent}\``);if(l===0)return o;let h=f.includeEmptyLines?/^/gm:/^(?!\s*$)/gm;return o.replace(h,f.indent.repeat(l))}});var HC=nt(ag=>{"use strict";var Tb=ag&&ag.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(ag,"__esModule",{value:!0});var r4=Tb(eh());ag.default=o=>o.getComputedWidth()-o.getComputedPadding(r4.default.EDGE_LEFT)-o.getComputedPadding(r4.default.EDGE_RIGHT)-o.getComputedBorder(r4.default.EDGE_LEFT)-o.getComputedBorder(r4.default.EDGE_RIGHT)});var qC=nt((fq,Cb)=>{Cb.exports={single:{topLeft:"\u250C",topRight:"\u2510",bottomRight:"\u2518",bottomLeft:"\u2514",vertical:"\u2502",horizontal:"\u2500"},double:{topLeft:"\u2554",topRight:"\u2557",bottomRight:"\u255D",bottomLeft:"\u255A",vertical:"\u2551",horizontal:"\u2550"},round:{topLeft:"\u256D",topRight:"\u256E",bottomRight:"\u256F",bottomLeft:"\u2570",vertical:"\u2502",horizontal:"\u2500"},bold:{topLeft:"\u250F",topRight:"\u2513",bottomRight:"\u251B",bottomLeft:"\u2517",vertical:"\u2503",horizontal:"\u2501"},singleDouble:{topLeft:"\u2553",topRight:"\u2556",bottomRight:"\u255C",bottomLeft:"\u2559",vertical:"\u2551",horizontal:"\u2500"},doubleSingle:{topLeft:"\u2552",topRight:"\u2555",bottomRight:"\u255B",bottomLeft:"\u2558",vertical:"\u2502",horizontal:"\u2550"},classic:{topLeft:"+",topRight:"+",bottomRight:"+",bottomLeft:"+",vertical:"|",horizontal:"-"}}});var VC=nt((cq,c3)=>{"use strict";var WC=qC();c3.exports=WC;c3.exports.default=WC});var YC=nt((dq,GC)=>{"use strict";GC.exports=(o,l=process.argv)=>{let f=o.startsWith("-")?"":o.length===1?"-":"--",h=l.indexOf(f+o),E=l.indexOf("--");return h!==-1&&(E===-1||h{"use strict";var xb=hi("os"),KC=hi("tty"),df=YC(),{env:Xo}=process,h2;df("no-color")||df("no-colors")||df("color=false")||df("color=never")?h2=0:(df("color")||df("colors")||df("color=true")||df("color=always"))&&(h2=1);"FORCE_COLOR"in Xo&&(Xo.FORCE_COLOR==="true"?h2=1:Xo.FORCE_COLOR==="false"?h2=0:h2=Xo.FORCE_COLOR.length===0?1:Math.min(parseInt(Xo.FORCE_COLOR,10),3));function d3(o){return o===0?!1:{level:o,hasBasic:!0,has256:o>=2,has16m:o>=3}}function p3(o,l){if(h2===0)return 0;if(df("color=16m")||df("color=full")||df("color=truecolor"))return 3;if(df("color=256"))return 2;if(o&&!l&&h2===void 0)return 0;let f=h2||0;if(Xo.TERM==="dumb")return f;if(process.platform==="win32"){let h=xb.release().split(".");return Number(h[0])>=10&&Number(h[2])>=10586?Number(h[2])>=14931?3:2:1}if("CI"in Xo)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some(h=>h in Xo)||Xo.CI_NAME==="codeship"?1:f;if("TEAMCITY_VERSION"in Xo)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(Xo.TEAMCITY_VERSION)?1:0;if("GITHUB_ACTIONS"in Xo)return 1;if(Xo.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in Xo){let h=parseInt((Xo.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(Xo.TERM_PROGRAM){case"iTerm.app":return h>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(Xo.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(Xo.TERM)||"COLORTERM"in Xo?1:f}function Rb(o){let l=p3(o,o&&o.isTTY);return d3(l)}XC.exports={supportsColor:Rb,stdout:d3(p3(!0,KC.isatty(1))),stderr:d3(p3(!0,KC.isatty(2)))}});var ZC=nt((hq,JC)=>{"use strict";var Ab=(o,l,f)=>{let h=o.indexOf(l);if(h===-1)return o;let E=l.length,t=0,N="";do N+=o.substr(t,h-t)+l+f,t=h+E,h=o.indexOf(l,t);while(h!==-1);return N+=o.substr(t),N},Ob=(o,l,f,h)=>{let E=0,t="";do{let N=o[h-1]==="\r";t+=o.substr(E,(N?h-1:h)-E)+l+(N?`\r +`:` +`)+f,E=h+1,h=o.indexOf(` +`,E)}while(h!==-1);return t+=o.substr(E),t};JC.exports={stringReplaceAll:Ab,stringEncaseCRLFWithFirstIndex:Ob}});var r6=nt((vq,n6)=>{"use strict";var Mb=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,$C=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,kb=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,Nb=/\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi,Lb=new Map([["n",` +`],["r","\r"],["t"," "],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e","\x1B"],["a","\x07"]]);function t6(o){let l=o[0]==="u",f=o[1]==="{";return l&&!f&&o.length===5||o[0]==="x"&&o.length===3?String.fromCharCode(parseInt(o.slice(1),16)):l&&f?String.fromCodePoint(parseInt(o.slice(2,-1),16)):Lb.get(o)||o}function Fb(o,l){let f=[],h=l.trim().split(/\s*,\s*/g),E;for(let t of h){let N=Number(t);if(!Number.isNaN(N))f.push(N);else if(E=t.match(kb))f.push(E[2].replace(Nb,(F,k,x)=>k?t6(k):x));else throw new Error(`Invalid Chalk template style argument: ${t} (in style '${o}')`)}return f}function Pb(o){$C.lastIndex=0;let l=[],f;for(;(f=$C.exec(o))!==null;){let h=f[1];if(f[2]){let E=Fb(h,f[2]);l.push([h].concat(E))}else l.push([h])}return l}function e6(o,l){let f={};for(let E of l)for(let t of E.styles)f[t[0]]=E.inverse?null:t.slice(1);let h=o;for(let[E,t]of Object.entries(f))if(!!Array.isArray(t)){if(!(E in h))throw new Error(`Unknown Chalk style: ${E}`);h=t.length>0?h[E](...t):h[E]}return h}n6.exports=(o,l)=>{let f=[],h=[],E=[];if(l.replace(Mb,(t,N,F,k,x,j)=>{if(N)E.push(t6(N));else if(k){let q=E.join("");E=[],h.push(f.length===0?q:e6(o,f)(q)),f.push({inverse:F,styles:Pb(k)})}else if(x){if(f.length===0)throw new Error("Found extraneous } in Chalk template literal");h.push(e6(o,f)(E.join(""))),E=[],f.pop()}else E.push(j)}),h.push(E.join("")),f.length>0){let t=`Chalk template literal is missing ${f.length} closing bracket${f.length===1?"":"s"} (\`}\`)`;throw new Error(t)}return h.join("")}});var s4=nt((mq,a6)=>{"use strict";var fg=G_(),{stdout:v3,stderr:m3}=QC(),{stringReplaceAll:Ib,stringEncaseCRLFWithFirstIndex:bb}=ZC(),{isArray:i4}=Array,u6=["ansi","ansi","ansi256","ansi16m"],nm=Object.create(null),Bb=(o,l={})=>{if(l.level&&!(Number.isInteger(l.level)&&l.level>=0&&l.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let f=v3?v3.level:0;o.level=l.level===void 0?f:l.level},y3=class{constructor(l){return o6(l)}},o6=o=>{let l={};return Bb(l,o),l.template=(...f)=>s6(l.template,...f),Object.setPrototypeOf(l,u4.prototype),Object.setPrototypeOf(l.template,l),l.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},l.template.Instance=y3,l.template};function u4(o){return o6(o)}for(let[o,l]of Object.entries(fg))nm[o]={get(){let f=o4(this,g3(l.open,l.close,this._styler),this._isEmpty);return Object.defineProperty(this,o,{value:f}),f}};nm.visible={get(){let o=o4(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:o}),o}};var l6=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(let o of l6)nm[o]={get(){let{level:l}=this;return function(...f){let h=g3(fg.color[u6[l]][o](...f),fg.color.close,this._styler);return o4(this,h,this._isEmpty)}}};for(let o of l6){let l="bg"+o[0].toUpperCase()+o.slice(1);nm[l]={get(){let{level:f}=this;return function(...h){let E=g3(fg.bgColor[u6[f]][o](...h),fg.bgColor.close,this._styler);return o4(this,E,this._isEmpty)}}}}var Ub=Object.defineProperties(()=>{},{...nm,level:{enumerable:!0,get(){return this._generator.level},set(o){this._generator.level=o}}}),g3=(o,l,f)=>{let h,E;return f===void 0?(h=o,E=l):(h=f.openAll+o,E=l+f.closeAll),{open:o,close:l,openAll:h,closeAll:E,parent:f}},o4=(o,l,f)=>{let h=(...E)=>i4(E[0])&&i4(E[0].raw)?i6(h,s6(h,...E)):i6(h,E.length===1?""+E[0]:E.join(" "));return Object.setPrototypeOf(h,Ub),h._generator=o,h._styler=l,h._isEmpty=f,h},i6=(o,l)=>{if(o.level<=0||!l)return o._isEmpty?"":l;let f=o._styler;if(f===void 0)return l;let{openAll:h,closeAll:E}=f;if(l.indexOf("\x1B")!==-1)for(;f!==void 0;)l=Ib(l,f.close,f.open),f=f.parent;let t=l.indexOf(` +`);return t!==-1&&(l=bb(l,E,h,t)),h+l+E},h3,s6=(o,...l)=>{let[f]=l;if(!i4(f)||!i4(f.raw))return l.join(" ");let h=l.slice(1),E=[f.raw[0]];for(let t=1;t{"use strict";var jb=dg&&dg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(dg,"__esModule",{value:!0});var cg=jb(s4()),zb=/^(rgb|hsl|hsv|hwb)\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/,Hb=/^(ansi|ansi256)\(\s?(\d+)\s?\)$/,a4=(o,l)=>l==="foreground"?o:"bg"+o[0].toUpperCase()+o.slice(1);dg.default=(o,l,f)=>{if(!l)return o;if(l in cg.default){let E=a4(l,f);return cg.default[E](o)}if(l.startsWith("#")){let E=a4("hex",f);return cg.default[E](l)(o)}if(l.startsWith("ansi")){let E=Hb.exec(l);if(!E)return o;let t=a4(E[1],f),N=Number(E[2]);return cg.default[t](N)(o)}if(l.startsWith("rgb")||l.startsWith("hsl")||l.startsWith("hsv")||l.startsWith("hwb")){let E=zb.exec(l);if(!E)return o;let t=a4(E[1],f),N=Number(E[2]),F=Number(E[3]),k=Number(E[4]);return cg.default[t](N,F,k)(o)}return o}});var c6=nt(pg=>{"use strict";var f6=pg&&pg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(pg,"__esModule",{value:!0});var qb=f6(VC()),E3=f6(_3());pg.default=(o,l,f,h)=>{if(typeof f.style.borderStyle=="string"){let E=f.yogaNode.getComputedWidth(),t=f.yogaNode.getComputedHeight(),N=f.style.borderColor,F=qb.default[f.style.borderStyle],k=E3.default(F.topLeft+F.horizontal.repeat(E-2)+F.topRight,N,"foreground"),x=(E3.default(F.vertical,N,"foreground")+` +`).repeat(t-2),j=E3.default(F.bottomLeft+F.horizontal.repeat(E-2)+F.bottomRight,N,"foreground");h.write(o,l,k,{transformers:[]}),h.write(o,l+1,x,{transformers:[]}),h.write(o+E-1,l+1,x,{transformers:[]}),h.write(o,l+t-1,j,{transformers:[]})}}});var p6=nt(hg=>{"use strict";var ih=hg&&hg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(hg,"__esModule",{value:!0});var Wb=ih(eh()),Vb=ih(xD()),Gb=ih(zC()),Yb=ih(PD()),Kb=ih(HC()),Xb=ih(bD()),Qb=ih(c6()),Jb=(o,l)=>{var f;let h=(f=o.childNodes[0])===null||f===void 0?void 0:f.yogaNode;if(h){let E=h.getComputedLeft(),t=h.getComputedTop();l=` +`.repeat(t)+Gb.default(l,E)}return l},d6=(o,l,f)=>{var h;let{offsetX:E=0,offsetY:t=0,transformers:N=[],skipStaticElements:F}=f;if(F&&o.internal_static)return;let{yogaNode:k}=o;if(k){if(k.getDisplay()===Wb.default.DISPLAY_NONE)return;let x=E+k.getComputedLeft(),j=t+k.getComputedTop(),q=N;if(typeof o.internal_transform=="function"&&(q=[o.internal_transform,...N]),o.nodeName==="ink-text"){let V=Xb.default(o);if(V.length>0){let re=Vb.default(V),y=Kb.default(k);if(re>y){let me=(h=o.style.textWrap)!==null&&h!==void 0?h:"wrap";V=Yb.default(V,y,me)}V=Jb(o,V),l.write(x,j,V,{transformers:q})}return}if(o.nodeName==="ink-box"&&Qb.default(x,j,o,l),o.nodeName==="ink-root"||o.nodeName==="ink-box")for(let V of o.childNodes)d6(V,l,{offsetX:x,offsetY:j,transformers:q,skipStaticElements:F})}};hg.default=d6});var v6=nt((Eq,h6)=>{"use strict";h6.exports=o=>{o=Object.assign({onlyFirst:!1},o);let l=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(l,o.onlyFirst?void 0:"g")}});var y6=nt((Dq,D3)=>{"use strict";var Zb=v6(),m6=o=>typeof o=="string"?o.replace(Zb(),""):o;D3.exports=m6;D3.exports.default=m6});var E6=nt((wq,_6)=>{"use strict";var g6="[\uD800-\uDBFF][\uDC00-\uDFFF]";_6.exports=o=>o&&o.exact?new RegExp(`^${g6}$`):new RegExp(g6,"g")});var w6=nt((Sq,w3)=>{"use strict";var $b=y6(),eB=E6(),D6=o=>$b(o).replace(eB()," ").length;w3.exports=D6;w3.exports.default=D6});var C6=nt(vg=>{"use strict";var T6=vg&&vg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(vg,"__esModule",{value:!0});var S6=T6(LD()),tB=T6(w6()),S3=class{constructor(l){this.writes=[];let{width:f,height:h}=l;this.width=f,this.height=h}write(l,f,h,E){let{transformers:t}=E;!h||this.writes.push({x:l,y:f,text:h,transformers:t})}get(){let l=[];for(let h=0;hh.trimRight()).join(` +`),height:l.length}}};vg.default=S3});var A6=nt(mg=>{"use strict";var T3=mg&&mg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(mg,"__esModule",{value:!0});var nB=T3(eh()),x6=T3(p6()),R6=T3(C6());mg.default=(o,l)=>{var f;if(o.yogaNode.setWidth(l),o.yogaNode){o.yogaNode.calculateLayout(void 0,void 0,nB.default.DIRECTION_LTR);let h=new R6.default({width:o.yogaNode.getComputedWidth(),height:o.yogaNode.getComputedHeight()});x6.default(o,h,{skipStaticElements:!0});let E;!((f=o.staticNode)===null||f===void 0)&&f.yogaNode&&(E=new R6.default({width:o.staticNode.yogaNode.getComputedWidth(),height:o.staticNode.yogaNode.getComputedHeight()}),x6.default(o.staticNode,E,{skipStaticElements:!1}));let{output:t,height:N}=h.get();return{output:t,outputHeight:N,staticOutput:E?`${E.get().output} +`:""}}return{output:"",outputHeight:0,staticOutput:""}}});var N6=nt((xq,k6)=>{"use strict";var O6=hi("stream"),M6=["assert","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"],C3={},rB=o=>{let l=new O6.PassThrough,f=new O6.PassThrough;l.write=E=>o("stdout",E),f.write=E=>o("stderr",E);let h=new console.Console(l,f);for(let E of M6)C3[E]=console[E],console[E]=h[E];return()=>{for(let E of M6)console[E]=C3[E];C3={}}};k6.exports=rB});var R3=nt(x3=>{"use strict";Object.defineProperty(x3,"__esModule",{value:!0});x3.default=new WeakMap});var O3=nt(A3=>{"use strict";Object.defineProperty(A3,"__esModule",{value:!0});var iB=Mi(),L6=iB.createContext({exit:()=>{}});L6.displayName="InternalAppContext";A3.default=L6});var k3=nt(M3=>{"use strict";Object.defineProperty(M3,"__esModule",{value:!0});var uB=Mi(),F6=uB.createContext({stdin:void 0,setRawMode:()=>{},isRawModeSupported:!1,internal_exitOnCtrlC:!0});F6.displayName="InternalStdinContext";M3.default=F6});var L3=nt(N3=>{"use strict";Object.defineProperty(N3,"__esModule",{value:!0});var oB=Mi(),P6=oB.createContext({stdout:void 0,write:()=>{}});P6.displayName="InternalStdoutContext";N3.default=P6});var P3=nt(F3=>{"use strict";Object.defineProperty(F3,"__esModule",{value:!0});var lB=Mi(),I6=lB.createContext({stderr:void 0,write:()=>{}});I6.displayName="InternalStderrContext";F3.default=I6});var f4=nt(I3=>{"use strict";Object.defineProperty(I3,"__esModule",{value:!0});var sB=Mi(),b6=sB.createContext({activeId:void 0,add:()=>{},remove:()=>{},activate:()=>{},deactivate:()=>{},enableFocus:()=>{},disableFocus:()=>{},focusNext:()=>{},focusPrevious:()=>{}});b6.displayName="InternalFocusContext";I3.default=b6});var U6=nt((Lq,B6)=>{"use strict";var aB=/[|\\{}()[\]^$+*?.-]/g;B6.exports=o=>{if(typeof o!="string")throw new TypeError("Expected a string");return o.replace(aB,"\\$&")}});var q6=nt((Fq,H6)=>{"use strict";var fB=U6(),z6=[].concat(hi("module").builtinModules,"bootstrap_node","node").map(o=>new RegExp(`(?:\\(${o}\\.js:\\d+:\\d+\\)$|^\\s*at ${o}\\.js:\\d+:\\d+$)`));z6.push(/\(internal\/[^:]+:\d+:\d+\)$/,/\s*at internal\/[^:]+:\d+:\d+$/,/\/\.node-spawn-wrap-\w+-\w+\/node:\d+:\d+\)?$/);var yg=class{constructor(l){l={ignoredPackages:[],...l},"internals"in l||(l.internals=yg.nodeInternals()),"cwd"in l||(l.cwd=process.cwd()),this._cwd=l.cwd.replace(/\\/g,"/"),this._internals=[].concat(l.internals,cB(l.ignoredPackages)),this._wrapCallSite=l.wrapCallSite||!1}static nodeInternals(){return[...z6]}clean(l,f=0){f=" ".repeat(f),Array.isArray(l)||(l=l.split(` +`)),!/^\s*at /.test(l[0])&&/^\s*at /.test(l[1])&&(l=l.slice(1));let h=!1,E=null,t=[];return l.forEach(N=>{if(N=N.replace(/\\/g,"/"),this._internals.some(k=>k.test(N)))return;let F=/^\s*at /.test(N);h?N=N.trimEnd().replace(/^(\s+)at /,"$1"):(N=N.trim(),F&&(N=N.slice(3))),N=N.replace(`${this._cwd}/`,""),N&&(F?(E&&(t.push(E),E=null),t.push(N)):(h=!0,E=N))}),t.map(N=>`${f}${N} +`).join("")}captureString(l,f=this.captureString){typeof l=="function"&&(f=l,l=1/0);let{stackTraceLimit:h}=Error;l&&(Error.stackTraceLimit=l);let E={};Error.captureStackTrace(E,f);let{stack:t}=E;return Error.stackTraceLimit=h,this.clean(t)}capture(l,f=this.capture){typeof l=="function"&&(f=l,l=1/0);let{prepareStackTrace:h,stackTraceLimit:E}=Error;Error.prepareStackTrace=(F,k)=>this._wrapCallSite?k.map(this._wrapCallSite):k,l&&(Error.stackTraceLimit=l);let t={};Error.captureStackTrace(t,f);let{stack:N}=t;return Object.assign(Error,{prepareStackTrace:h,stackTraceLimit:E}),N}at(l=this.at){let[f]=this.capture(1,l);if(!f)return{};let h={line:f.getLineNumber(),column:f.getColumnNumber()};j6(h,f.getFileName(),this._cwd),f.isConstructor()&&(h.constructor=!0),f.isEval()&&(h.evalOrigin=f.getEvalOrigin()),f.isNative()&&(h.native=!0);let E;try{E=f.getTypeName()}catch{}E&&E!=="Object"&&E!=="[object Object]"&&(h.type=E);let t=f.getFunctionName();t&&(h.function=t);let N=f.getMethodName();return N&&t!==N&&(h.method=N),h}parseLine(l){let f=l&&l.match(dB);if(!f)return null;let h=f[1]==="new",E=f[2],t=f[3],N=f[4],F=Number(f[5]),k=Number(f[6]),x=f[7],j=f[8],q=f[9],V=f[10]==="native",re=f[11]===")",y,me={};if(j&&(me.line=Number(j)),q&&(me.column=Number(q)),re&&x){let De=0;for(let ge=x.length-1;ge>0;ge--)if(x.charAt(ge)===")")De++;else if(x.charAt(ge)==="("&&x.charAt(ge-1)===" "&&(De--,De===-1&&x.charAt(ge-1)===" ")){let ae=x.slice(0,ge-1);x=x.slice(ge+1),E+=` (${ae}`;break}}if(E){let De=E.match(pB);De&&(E=De[1],y=De[2])}return j6(me,x,this._cwd),h&&(me.constructor=!0),t&&(me.evalOrigin=t,me.evalLine=F,me.evalColumn=k,me.evalFile=N&&N.replace(/\\/g,"/")),V&&(me.native=!0),E&&(me.function=E),y&&E!==y&&(me.method=y),me}};function j6(o,l,f){l&&(l=l.replace(/\\/g,"/"),l.startsWith(`${f}/`)&&(l=l.slice(f.length+1)),o.file=l)}function cB(o){if(o.length===0)return[];let l=o.map(f=>fB(f));return new RegExp(`[/\\\\]node_modules[/\\\\](?:${l.join("|")})[/\\\\][^:]+:\\d+:\\d+`)}var dB=new RegExp("^(?:\\s*at )?(?:(new) )?(?:(.*?) \\()?(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?(?:(.+?):(\\d+):(\\d+)|(native))(\\)?)$"),pB=/^(.*?) \[as (.*?)\]$/;H6.exports=yg});var V6=nt((Pq,W6)=>{"use strict";W6.exports=(o,l)=>o.replace(/^\t+/gm,f=>" ".repeat(f.length*(l||2)))});var Y6=nt((Iq,G6)=>{"use strict";var hB=V6(),vB=(o,l)=>{let f=[],h=o-l,E=o+l;for(let t=h;t<=E;t++)f.push(t);return f};G6.exports=(o,l,f)=>{if(typeof o!="string")throw new TypeError("Source code is missing.");if(!l||l<1)throw new TypeError("Line number must start from `1`.");if(o=hB(o).split(/\r?\n/),!(l>o.length))return f={around:3,...f},vB(l,f.around).filter(h=>o[h-1]!==void 0).map(h=>({line:h,value:o[h-1]}))}});var c4=nt(nc=>{"use strict";var mB=nc&&nc.__createBinding||(Object.create?function(o,l,f,h){h===void 0&&(h=f),Object.defineProperty(o,h,{enumerable:!0,get:function(){return l[f]}})}:function(o,l,f,h){h===void 0&&(h=f),o[h]=l[f]}),yB=nc&&nc.__setModuleDefault||(Object.create?function(o,l){Object.defineProperty(o,"default",{enumerable:!0,value:l})}:function(o,l){o.default=l}),gB=nc&&nc.__importStar||function(o){if(o&&o.__esModule)return o;var l={};if(o!=null)for(var f in o)f!=="default"&&Object.hasOwnProperty.call(o,f)&&mB(l,o,f);return yB(l,o),l},_B=nc&&nc.__rest||function(o,l){var f={};for(var h in o)Object.prototype.hasOwnProperty.call(o,h)&&l.indexOf(h)<0&&(f[h]=o[h]);if(o!=null&&typeof Object.getOwnPropertySymbols=="function")for(var E=0,h=Object.getOwnPropertySymbols(o);E{var{children:f}=o,h=_B(o,["children"]);let E=Object.assign(Object.assign({},h),{marginLeft:h.marginLeft||h.marginX||h.margin||0,marginRight:h.marginRight||h.marginX||h.margin||0,marginTop:h.marginTop||h.marginY||h.margin||0,marginBottom:h.marginBottom||h.marginY||h.margin||0,paddingLeft:h.paddingLeft||h.paddingX||h.padding||0,paddingRight:h.paddingRight||h.paddingX||h.padding||0,paddingTop:h.paddingTop||h.paddingY||h.padding||0,paddingBottom:h.paddingBottom||h.paddingY||h.padding||0});return K6.default.createElement("ink-box",{ref:l,style:E},f)});b3.displayName="Box";b3.defaultProps={flexDirection:"row",flexGrow:0,flexShrink:1};nc.default=b3});var j3=nt(gg=>{"use strict";var B3=gg&&gg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(gg,"__esModule",{value:!0});var EB=B3(Mi()),rm=B3(s4()),X6=B3(_3()),U3=({color:o,backgroundColor:l,dimColor:f,bold:h,italic:E,underline:t,strikethrough:N,inverse:F,wrap:k,children:x})=>{if(x==null)return null;let j=q=>(f&&(q=rm.default.dim(q)),o&&(q=X6.default(q,o,"foreground")),l&&(q=X6.default(q,l,"background")),h&&(q=rm.default.bold(q)),E&&(q=rm.default.italic(q)),t&&(q=rm.default.underline(q)),N&&(q=rm.default.strikethrough(q)),F&&(q=rm.default.inverse(q)),q);return EB.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row",textWrap:k},internal_transform:j},x)};U3.displayName="Text";U3.defaultProps={dimColor:!1,bold:!1,italic:!1,underline:!1,strikethrough:!1,wrap:"wrap"};gg.default=U3});var $6=nt(rc=>{"use strict";var DB=rc&&rc.__createBinding||(Object.create?function(o,l,f,h){h===void 0&&(h=f),Object.defineProperty(o,h,{enumerable:!0,get:function(){return l[f]}})}:function(o,l,f,h){h===void 0&&(h=f),o[h]=l[f]}),wB=rc&&rc.__setModuleDefault||(Object.create?function(o,l){Object.defineProperty(o,"default",{enumerable:!0,value:l})}:function(o,l){o.default=l}),SB=rc&&rc.__importStar||function(o){if(o&&o.__esModule)return o;var l={};if(o!=null)for(var f in o)f!=="default"&&Object.hasOwnProperty.call(o,f)&&DB(l,o,f);return wB(l,o),l},_g=rc&&rc.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(rc,"__esModule",{value:!0});var Q6=SB(hi("fs")),Qo=_g(Mi()),J6=_g(q6()),TB=_g(Y6()),Z1=_g(c4()),Hc=_g(j3()),Z6=new J6.default({cwd:process.cwd(),internals:J6.default.nodeInternals()}),CB=({error:o})=>{let l=o.stack?o.stack.split(` +`).slice(1):void 0,f=l?Z6.parseLine(l[0]):void 0,h,E=0;if((f==null?void 0:f.file)&&(f==null?void 0:f.line)&&Q6.existsSync(f.file)){let t=Q6.readFileSync(f.file,"utf8");if(h=TB.default(t,f.line),h)for(let{line:N}of h)E=Math.max(E,String(N).length)}return Qo.default.createElement(Z1.default,{flexDirection:"column",padding:1},Qo.default.createElement(Z1.default,null,Qo.default.createElement(Hc.default,{backgroundColor:"red",color:"white"}," ","ERROR"," "),Qo.default.createElement(Hc.default,null," ",o.message)),f&&Qo.default.createElement(Z1.default,{marginTop:1},Qo.default.createElement(Hc.default,{dimColor:!0},f.file,":",f.line,":",f.column)),f&&h&&Qo.default.createElement(Z1.default,{marginTop:1,flexDirection:"column"},h.map(({line:t,value:N})=>Qo.default.createElement(Z1.default,{key:t},Qo.default.createElement(Z1.default,{width:E+1},Qo.default.createElement(Hc.default,{dimColor:t!==f.line,backgroundColor:t===f.line?"red":void 0,color:t===f.line?"white":void 0},String(t).padStart(E," "),":")),Qo.default.createElement(Hc.default,{key:t,backgroundColor:t===f.line?"red":void 0,color:t===f.line?"white":void 0}," "+N)))),o.stack&&Qo.default.createElement(Z1.default,{marginTop:1,flexDirection:"column"},o.stack.split(` +`).slice(1).map(t=>{let N=Z6.parseLine(t);return N?Qo.default.createElement(Z1.default,{key:t},Qo.default.createElement(Hc.default,{dimColor:!0},"- "),Qo.default.createElement(Hc.default,{dimColor:!0,bold:!0},N.function),Qo.default.createElement(Hc.default,{dimColor:!0,color:"gray"}," ","(",N.file,":",N.line,":",N.column,")")):Qo.default.createElement(Z1.default,{key:t},Qo.default.createElement(Hc.default,{dimColor:!0},"- "),Qo.default.createElement(Hc.default,{dimColor:!0,bold:!0},t))})))};rc.default=CB});var tx=nt(ic=>{"use strict";var xB=ic&&ic.__createBinding||(Object.create?function(o,l,f,h){h===void 0&&(h=f),Object.defineProperty(o,h,{enumerable:!0,get:function(){return l[f]}})}:function(o,l,f,h){h===void 0&&(h=f),o[h]=l[f]}),RB=ic&&ic.__setModuleDefault||(Object.create?function(o,l){Object.defineProperty(o,"default",{enumerable:!0,value:l})}:function(o,l){o.default=l}),AB=ic&&ic.__importStar||function(o){if(o&&o.__esModule)return o;var l={};if(o!=null)for(var f in o)f!=="default"&&Object.hasOwnProperty.call(o,f)&&xB(l,o,f);return RB(l,o),l},oh=ic&&ic.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(ic,"__esModule",{value:!0});var uh=AB(Mi()),ex=oh(rD()),OB=oh(O3()),MB=oh(k3()),kB=oh(L3()),NB=oh(P3()),LB=oh(f4()),FB=oh($6()),PB=" ",IB="\x1B[Z",bB="\x1B",d4=class extends uh.PureComponent{constructor(){super(...arguments),this.state={isFocusEnabled:!0,activeFocusId:void 0,focusables:[],error:void 0},this.rawModeEnabledCount=0,this.handleSetRawMode=l=>{let{stdin:f}=this.props;if(!this.isRawModeSupported())throw f===process.stdin?new Error(`Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default. +Read about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported`):new Error(`Raw mode is not supported on the stdin provided to Ink. +Read about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported`);if(f.setEncoding("utf8"),l){this.rawModeEnabledCount===0&&(f.addListener("data",this.handleInput),f.resume(),f.setRawMode(!0)),this.rawModeEnabledCount++;return}--this.rawModeEnabledCount===0&&(f.setRawMode(!1),f.removeListener("data",this.handleInput),f.pause())},this.handleInput=l=>{l===""&&this.props.exitOnCtrlC&&this.handleExit(),l===bB&&this.state.activeFocusId&&this.setState({activeFocusId:void 0}),this.state.isFocusEnabled&&this.state.focusables.length>0&&(l===PB&&this.focusNext(),l===IB&&this.focusPrevious())},this.handleExit=l=>{this.isRawModeSupported()&&this.handleSetRawMode(!1),this.props.onExit(l)},this.enableFocus=()=>{this.setState({isFocusEnabled:!0})},this.disableFocus=()=>{this.setState({isFocusEnabled:!1})},this.focusNext=()=>{this.setState(l=>{let f=l.focusables[0].id;return{activeFocusId:this.findNextFocusable(l)||f}})},this.focusPrevious=()=>{this.setState(l=>{let f=l.focusables[l.focusables.length-1].id;return{activeFocusId:this.findPreviousFocusable(l)||f}})},this.addFocusable=(l,{autoFocus:f})=>{this.setState(h=>{let E=h.activeFocusId;return!E&&f&&(E=l),{activeFocusId:E,focusables:[...h.focusables,{id:l,isActive:!0}]}})},this.removeFocusable=l=>{this.setState(f=>({activeFocusId:f.activeFocusId===l?void 0:f.activeFocusId,focusables:f.focusables.filter(h=>h.id!==l)}))},this.activateFocusable=l=>{this.setState(f=>({focusables:f.focusables.map(h=>h.id!==l?h:{id:l,isActive:!0})}))},this.deactivateFocusable=l=>{this.setState(f=>({activeFocusId:f.activeFocusId===l?void 0:f.activeFocusId,focusables:f.focusables.map(h=>h.id!==l?h:{id:l,isActive:!1})}))},this.findNextFocusable=l=>{let f=l.focusables.findIndex(h=>h.id===l.activeFocusId);for(let h=f+1;h{let f=l.focusables.findIndex(h=>h.id===l.activeFocusId);for(let h=f-1;h>=0;h--)if(l.focusables[h].isActive)return l.focusables[h].id}}static getDerivedStateFromError(l){return{error:l}}isRawModeSupported(){return this.props.stdin.isTTY}render(){return uh.default.createElement(OB.default.Provider,{value:{exit:this.handleExit}},uh.default.createElement(MB.default.Provider,{value:{stdin:this.props.stdin,setRawMode:this.handleSetRawMode,isRawModeSupported:this.isRawModeSupported(),internal_exitOnCtrlC:this.props.exitOnCtrlC}},uh.default.createElement(kB.default.Provider,{value:{stdout:this.props.stdout,write:this.props.writeToStdout}},uh.default.createElement(NB.default.Provider,{value:{stderr:this.props.stderr,write:this.props.writeToStderr}},uh.default.createElement(LB.default.Provider,{value:{activeId:this.state.activeFocusId,add:this.addFocusable,remove:this.removeFocusable,activate:this.activateFocusable,deactivate:this.deactivateFocusable,enableFocus:this.enableFocus,disableFocus:this.disableFocus,focusNext:this.focusNext,focusPrevious:this.focusPrevious}},this.state.error?uh.default.createElement(FB.default,{error:this.state.error}):this.props.children)))))}componentDidMount(){ex.default.hide(this.props.stdout)}componentWillUnmount(){ex.default.show(this.props.stdout),this.isRawModeSupported()&&this.handleSetRawMode(!1)}componentDidCatch(l){this.handleExit(l)}};ic.default=d4;d4.displayName="InternalApp"});var ix=nt(uc=>{"use strict";var BB=uc&&uc.__createBinding||(Object.create?function(o,l,f,h){h===void 0&&(h=f),Object.defineProperty(o,h,{enumerable:!0,get:function(){return l[f]}})}:function(o,l,f,h){h===void 0&&(h=f),o[h]=l[f]}),UB=uc&&uc.__setModuleDefault||(Object.create?function(o,l){Object.defineProperty(o,"default",{enumerable:!0,value:l})}:function(o,l){o.default=l}),jB=uc&&uc.__importStar||function(o){if(o&&o.__esModule)return o;var l={};if(o!=null)for(var f in o)f!=="default"&&Object.hasOwnProperty.call(o,f)&&BB(l,o,f);return UB(l,o),l},oc=uc&&uc.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(uc,"__esModule",{value:!0});var zB=oc(Mi()),nx=sS(),HB=oc(TS()),qB=oc(ZE()),WB=oc(MS()),VB=oc(NS()),p4=oc(UC()),GB=oc(A6()),YB=oc(nD()),KB=oc(N6()),XB=jB(BD()),QB=oc(R3()),JB=oc(tx()),im=process.env.CI==="false"?!1:WB.default,rx=()=>{},z3=class{constructor(l){this.resolveExitPromise=()=>{},this.rejectExitPromise=()=>{},this.unsubscribeExit=()=>{},this.onRender=()=>{if(this.isUnmounted)return;let{output:f,outputHeight:h,staticOutput:E}=GB.default(this.rootNode,this.options.stdout.columns||80),t=E&&E!==` +`;if(this.options.debug){t&&(this.fullStaticOutput+=E),this.options.stdout.write(this.fullStaticOutput+f);return}if(im){t&&this.options.stdout.write(E),this.lastOutput=f;return}if(t&&(this.fullStaticOutput+=E),h>=this.options.stdout.rows){this.options.stdout.write(qB.default.clearTerminal+this.fullStaticOutput+f),this.lastOutput=f;return}t&&(this.log.clear(),this.options.stdout.write(E),this.log(f)),!t&&f!==this.lastOutput&&this.throttledLog(f),this.lastOutput=f},VB.default(this),this.options=l,this.rootNode=XB.createNode("ink-root"),this.rootNode.onRender=l.debug?this.onRender:nx.throttle(this.onRender,32,{leading:!0,trailing:!0}),this.rootNode.onImmediateRender=this.onRender,this.log=HB.default.create(l.stdout),this.throttledLog=l.debug?this.log:nx.throttle(this.log,void 0,{leading:!0,trailing:!0}),this.isUnmounted=!1,this.lastOutput="",this.fullStaticOutput="",this.container=p4.default.createContainer(this.rootNode,!1,!1),this.unsubscribeExit=YB.default(this.unmount,{alwaysLast:!1}),process.env.DEV==="true"&&p4.default.injectIntoDevTools({bundleType:0,version:"16.13.1",rendererPackageName:"ink"}),l.patchConsole&&this.patchConsole(),im||(l.stdout.on("resize",this.onRender),this.unsubscribeResize=()=>{l.stdout.off("resize",this.onRender)})}render(l){let f=zB.default.createElement(JB.default,{stdin:this.options.stdin,stdout:this.options.stdout,stderr:this.options.stderr,writeToStdout:this.writeToStdout,writeToStderr:this.writeToStderr,exitOnCtrlC:this.options.exitOnCtrlC,onExit:this.unmount},l);p4.default.updateContainer(f,this.container,null,rx)}writeToStdout(l){if(!this.isUnmounted){if(this.options.debug){this.options.stdout.write(l+this.fullStaticOutput+this.lastOutput);return}if(im){this.options.stdout.write(l);return}this.log.clear(),this.options.stdout.write(l),this.log(this.lastOutput)}}writeToStderr(l){if(!this.isUnmounted){if(this.options.debug){this.options.stderr.write(l),this.options.stdout.write(this.fullStaticOutput+this.lastOutput);return}if(im){this.options.stderr.write(l);return}this.log.clear(),this.options.stderr.write(l),this.log(this.lastOutput)}}unmount(l){this.isUnmounted||(this.onRender(),this.unsubscribeExit(),typeof this.restoreConsole=="function"&&this.restoreConsole(),typeof this.unsubscribeResize=="function"&&this.unsubscribeResize(),im?this.options.stdout.write(this.lastOutput+` +`):this.options.debug||this.log.done(),this.isUnmounted=!0,p4.default.updateContainer(null,this.container,null,rx),QB.default.delete(this.options.stdout),l instanceof Error?this.rejectExitPromise(l):this.resolveExitPromise())}waitUntilExit(){return this.exitPromise||(this.exitPromise=new Promise((l,f)=>{this.resolveExitPromise=l,this.rejectExitPromise=f})),this.exitPromise}clear(){!im&&!this.options.debug&&this.log.clear()}patchConsole(){this.options.debug||(this.restoreConsole=KB.default((l,f)=>{l==="stdout"&&this.writeToStdout(f),l==="stderr"&&(f.startsWith("The above error occurred")||this.writeToStderr(f))}))}};uc.default=z3});var ox=nt(Eg=>{"use strict";var ux=Eg&&Eg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Eg,"__esModule",{value:!0});var ZB=ux(ix()),h4=ux(R3()),$B=hi("stream"),eU=(o,l)=>{let f=Object.assign({stdout:process.stdout,stdin:process.stdin,stderr:process.stderr,debug:!1,exitOnCtrlC:!0,patchConsole:!0},tU(l)),h=nU(f.stdout,()=>new ZB.default(f));return h.render(o),{rerender:h.render,unmount:()=>h.unmount(),waitUntilExit:h.waitUntilExit,cleanup:()=>h4.default.delete(f.stdout),clear:h.clear}};Eg.default=eU;var tU=(o={})=>o instanceof $B.Stream?{stdout:o,stdin:process.stdin}:o,nU=(o,l)=>{let f;return h4.default.has(o)?f=h4.default.get(o):(f=l(),h4.default.set(o,f)),f}});var sx=nt($1=>{"use strict";var rU=$1&&$1.__createBinding||(Object.create?function(o,l,f,h){h===void 0&&(h=f),Object.defineProperty(o,h,{enumerable:!0,get:function(){return l[f]}})}:function(o,l,f,h){h===void 0&&(h=f),o[h]=l[f]}),iU=$1&&$1.__setModuleDefault||(Object.create?function(o,l){Object.defineProperty(o,"default",{enumerable:!0,value:l})}:function(o,l){o.default=l}),uU=$1&&$1.__importStar||function(o){if(o&&o.__esModule)return o;var l={};if(o!=null)for(var f in o)f!=="default"&&Object.hasOwnProperty.call(o,f)&&rU(l,o,f);return iU(l,o),l};Object.defineProperty($1,"__esModule",{value:!0});var Dg=uU(Mi()),lx=o=>{let{items:l,children:f,style:h}=o,[E,t]=Dg.useState(0),N=Dg.useMemo(()=>l.slice(E),[l,E]);Dg.useLayoutEffect(()=>{t(l.length)},[l.length]);let F=N.map((x,j)=>f(x,E+j)),k=Dg.useMemo(()=>Object.assign({position:"absolute",flexDirection:"column"},h),[h]);return Dg.default.createElement("ink-box",{internal_static:!0,style:k},F)};lx.displayName="Static";$1.default=lx});var fx=nt(wg=>{"use strict";var oU=wg&&wg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(wg,"__esModule",{value:!0});var lU=oU(Mi()),ax=({children:o,transform:l})=>o==null?null:lU.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row"},internal_transform:l},o);ax.displayName="Transform";wg.default=ax});var dx=nt(Sg=>{"use strict";var sU=Sg&&Sg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Sg,"__esModule",{value:!0});var aU=sU(Mi()),cx=({count:o=1})=>aU.default.createElement("ink-text",null,` +`.repeat(o));cx.displayName="Newline";Sg.default=cx});var vx=nt(Tg=>{"use strict";var px=Tg&&Tg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Tg,"__esModule",{value:!0});var fU=px(Mi()),cU=px(c4()),hx=()=>fU.default.createElement(cU.default,{flexGrow:1});hx.displayName="Spacer";Tg.default=hx});var v4=nt(Cg=>{"use strict";var dU=Cg&&Cg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Cg,"__esModule",{value:!0});var pU=Mi(),hU=dU(k3()),vU=()=>pU.useContext(hU.default);Cg.default=vU});var yx=nt(xg=>{"use strict";var mU=xg&&xg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(xg,"__esModule",{value:!0});var mx=Mi(),yU=mU(v4()),gU=(o,l={})=>{let{stdin:f,setRawMode:h,internal_exitOnCtrlC:E}=yU.default();mx.useEffect(()=>{if(l.isActive!==!1)return h(!0),()=>{h(!1)}},[l.isActive,h]),mx.useEffect(()=>{if(l.isActive===!1)return;let t=N=>{let F=String(N),k={upArrow:F==="\x1B[A",downArrow:F==="\x1B[B",leftArrow:F==="\x1B[D",rightArrow:F==="\x1B[C",pageDown:F==="\x1B[6~",pageUp:F==="\x1B[5~",return:F==="\r",escape:F==="\x1B",ctrl:!1,shift:!1,tab:F===" "||F==="\x1B[Z",backspace:F==="\b",delete:F==="\x7F"||F==="\x1B[3~",meta:!1};F<=""&&!k.return&&(F=String.fromCharCode(F.charCodeAt(0)+"a".charCodeAt(0)-1),k.ctrl=!0),F.startsWith("\x1B")&&(F=F.slice(1),k.meta=!0);let x=F>="A"&&F<="Z",j=F>="\u0410"&&F<="\u042F";F.length===1&&(x||j)&&(k.shift=!0),k.tab&&F==="[Z"&&(k.shift=!0),(k.tab||k.backspace||k.delete)&&(F=""),(!(F==="c"&&k.ctrl)||!E)&&o(F,k)};return f==null||f.on("data",t),()=>{f==null||f.off("data",t)}},[l.isActive,f,E,o])};xg.default=gU});var gx=nt(Rg=>{"use strict";var _U=Rg&&Rg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Rg,"__esModule",{value:!0});var EU=Mi(),DU=_U(O3()),wU=()=>EU.useContext(DU.default);Rg.default=wU});var _x=nt(Ag=>{"use strict";var SU=Ag&&Ag.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Ag,"__esModule",{value:!0});var TU=Mi(),CU=SU(L3()),xU=()=>TU.useContext(CU.default);Ag.default=xU});var Ex=nt(Og=>{"use strict";var RU=Og&&Og.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Og,"__esModule",{value:!0});var AU=Mi(),OU=RU(P3()),MU=()=>AU.useContext(OU.default);Og.default=MU});var wx=nt(kg=>{"use strict";var Dx=kg&&kg.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(kg,"__esModule",{value:!0});var Mg=Mi(),kU=Dx(f4()),NU=Dx(v4()),LU=({isActive:o=!0,autoFocus:l=!1}={})=>{let{isRawModeSupported:f,setRawMode:h}=NU.default(),{activeId:E,add:t,remove:N,activate:F,deactivate:k}=Mg.useContext(kU.default),x=Mg.useMemo(()=>Math.random().toString().slice(2,7),[]);return Mg.useEffect(()=>(t(x,{autoFocus:l}),()=>{N(x)}),[x,l]),Mg.useEffect(()=>{o?F(x):k(x)},[o,x]),Mg.useEffect(()=>{if(!(!f||!o))return h(!0),()=>{h(!1)}},[o]),{isFocused:Boolean(x)&&E===x}};kg.default=LU});var Sx=nt(Ng=>{"use strict";var FU=Ng&&Ng.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(Ng,"__esModule",{value:!0});var PU=Mi(),IU=FU(f4()),bU=()=>{let o=PU.useContext(IU.default);return{enableFocus:o.enableFocus,disableFocus:o.disableFocus,focusNext:o.focusNext,focusPrevious:o.focusPrevious}};Ng.default=bU});var Tx=nt(H3=>{"use strict";Object.defineProperty(H3,"__esModule",{value:!0});H3.default=o=>{var l,f,h,E;return{width:(f=(l=o.yogaNode)===null||l===void 0?void 0:l.getComputedWidth())!==null&&f!==void 0?f:0,height:(E=(h=o.yogaNode)===null||h===void 0?void 0:h.getComputedHeight())!==null&&E!==void 0?E:0}}});var lc=nt(Yl=>{"use strict";Object.defineProperty(Yl,"__esModule",{value:!0});var BU=ox();Object.defineProperty(Yl,"render",{enumerable:!0,get:function(){return BU.default}});var UU=c4();Object.defineProperty(Yl,"Box",{enumerable:!0,get:function(){return UU.default}});var jU=j3();Object.defineProperty(Yl,"Text",{enumerable:!0,get:function(){return jU.default}});var zU=sx();Object.defineProperty(Yl,"Static",{enumerable:!0,get:function(){return zU.default}});var HU=fx();Object.defineProperty(Yl,"Transform",{enumerable:!0,get:function(){return HU.default}});var qU=dx();Object.defineProperty(Yl,"Newline",{enumerable:!0,get:function(){return qU.default}});var WU=vx();Object.defineProperty(Yl,"Spacer",{enumerable:!0,get:function(){return WU.default}});var VU=yx();Object.defineProperty(Yl,"useInput",{enumerable:!0,get:function(){return VU.default}});var GU=gx();Object.defineProperty(Yl,"useApp",{enumerable:!0,get:function(){return GU.default}});var YU=v4();Object.defineProperty(Yl,"useStdin",{enumerable:!0,get:function(){return YU.default}});var KU=_x();Object.defineProperty(Yl,"useStdout",{enumerable:!0,get:function(){return KU.default}});var XU=Ex();Object.defineProperty(Yl,"useStderr",{enumerable:!0,get:function(){return XU.default}});var QU=wx();Object.defineProperty(Yl,"useFocus",{enumerable:!0,get:function(){return QU.default}});var JU=Sx();Object.defineProperty(Yl,"useFocusManager",{enumerable:!0,get:function(){return JU.default}});var ZU=Tx();Object.defineProperty(Yl,"measureElement",{enumerable:!0,get:function(){return ZU.default}})});var Fx=nt(Lg=>{"use strict";Object.defineProperty(Lg,"__esModule",{value:!0});Lg.UncontrolledTextInput=void 0;var Nx=Mi(),V3=Mi(),kx=lc(),ah=s4(),Lx=({value:o,placeholder:l="",focus:f=!0,mask:h,highlightPastedText:E=!1,showCursor:t=!0,onChange:N,onSubmit:F})=>{let[{cursorOffset:k,cursorWidth:x},j]=V3.useState({cursorOffset:(o||"").length,cursorWidth:0});V3.useEffect(()=>{j(me=>{if(!f||!t)return me;let De=o||"";return me.cursorOffset>De.length-1?{cursorOffset:De.length,cursorWidth:0}:me})},[o,f,t]);let q=E?x:0,V=h?h.repeat(o.length):o,re=V,y=l?ah.grey(l):void 0;if(t&&f){y=l.length>0?ah.inverse(l[0])+ah.grey(l.slice(1)):ah.inverse(" "),re=V.length>0?"":ah.inverse(" ");let me=0;for(let De of V)me>=k-q&&me<=k?re+=ah.inverse(De):re+=De,me++;V.length>0&&k===V.length&&(re+=ah.inverse(" "))}return kx.useInput((me,De)=>{if(De.upArrow||De.downArrow||De.ctrl&&me==="c"||De.tab||De.shift&&De.tab)return;if(De.return){F&&F(o);return}let ge=k,ae=o,we=0;De.leftArrow?t&&ge--:De.rightArrow?t&&ge++:De.backspace||De.delete?k>0&&(ae=o.slice(0,k-1)+o.slice(k,o.length),ge--):(ae=o.slice(0,k)+me+o.slice(k,o.length),ge+=me.length,me.length>1&&(we=me.length)),k<0&&(ge=0),k>o.length&&(ge=o.length),j({cursorOffset:ge,cursorWidth:we}),ae!==o&&N(ae)},{isActive:f}),Nx.createElement(kx.Text,null,l?V.length>0?re:y:re)};Lg.default=Lx;Lg.UncontrolledTextInput=o=>{let[l,f]=V3.useState("");return Nx.createElement(Lx,Object.assign({},o,{value:l,onChange:f}))}});var Ix=nt(S4=>{"use strict";Object.defineProperty(S4,"__esModule",{value:!0});function Fg(o){let l=[...o.caches],f=l.shift();return f===void 0?Px():{get(h,E,t={miss:()=>Promise.resolve()}){return f.get(h,E,t).catch(()=>Fg({caches:l}).get(h,E,t))},set(h,E){return f.set(h,E).catch(()=>Fg({caches:l}).set(h,E))},delete(h){return f.delete(h).catch(()=>Fg({caches:l}).delete(h))},clear(){return f.clear().catch(()=>Fg({caches:l}).clear())}}}function Px(){return{get(o,l,f={miss:()=>Promise.resolve()}){return l().then(E=>Promise.all([E,f.miss(E)])).then(([E])=>E)},set(o,l){return Promise.resolve(l)},delete(o){return Promise.resolve()},clear(){return Promise.resolve()}}}S4.createFallbackableCache=Fg;S4.createNullCache=Px});var Bx=nt((EW,bx)=>{bx.exports=Ix()});var Ux=nt(G3=>{"use strict";Object.defineProperty(G3,"__esModule",{value:!0});function $U(o={serializable:!0}){let l={};return{get(f,h,E={miss:()=>Promise.resolve()}){let t=JSON.stringify(f);if(t in l)return Promise.resolve(o.serializable?JSON.parse(l[t]):l[t]);let N=h(),F=E&&E.miss||(()=>Promise.resolve());return N.then(k=>F(k)).then(()=>N)},set(f,h){return l[JSON.stringify(f)]=o.serializable?JSON.stringify(h):h,Promise.resolve(h)},delete(f){return delete l[JSON.stringify(f)],Promise.resolve()},clear(){return l={},Promise.resolve()}}}G3.createInMemoryCache=$U});var zx=nt((wW,jx)=>{jx.exports=Ux()});var qx=nt(sc=>{"use strict";Object.defineProperty(sc,"__esModule",{value:!0});function ej(o,l,f){let h={"x-algolia-api-key":f,"x-algolia-application-id":l};return{headers(){return o===Y3.WithinHeaders?h:{}},queryParameters(){return o===Y3.WithinQueryParameters?h:{}}}}function tj(o){let l=0,f=()=>(l++,new Promise(h=>{setTimeout(()=>{h(o(f))},Math.min(100*l,1e3))}));return o(f)}function Hx(o,l=(f,h)=>Promise.resolve()){return Object.assign(o,{wait(f){return Hx(o.then(h=>Promise.all([l(h,f),h])).then(h=>h[1]))}})}function nj(o){let l=o.length-1;for(l;l>0;l--){let f=Math.floor(Math.random()*(l+1)),h=o[l];o[l]=o[f],o[f]=h}return o}function rj(o,l){return Object.keys(l!==void 0?l:{}).forEach(f=>{o[f]=l[f](o)}),o}function ij(o,...l){let f=0;return o.replace(/%s/g,()=>encodeURIComponent(l[f++]))}var uj="4.2.0",oj=o=>()=>o.transporter.requester.destroy(),Y3={WithinQueryParameters:0,WithinHeaders:1};sc.AuthMode=Y3;sc.addMethods=rj;sc.createAuth=ej;sc.createRetryablePromise=tj;sc.createWaitablePromise=Hx;sc.destroy=oj;sc.encode=ij;sc.shuffle=nj;sc.version=uj});var Pg=nt((TW,Wx)=>{Wx.exports=qx()});var Vx=nt(K3=>{"use strict";Object.defineProperty(K3,"__esModule",{value:!0});var lj={Delete:"DELETE",Get:"GET",Post:"POST",Put:"PUT"};K3.MethodEnum=lj});var Ig=nt((xW,Gx)=>{Gx.exports=Vx()});var l5=nt(G0=>{"use strict";Object.defineProperty(G0,"__esModule",{value:!0});var Kx=Ig();function X3(o,l){let f=o||{},h=f.data||{};return Object.keys(f).forEach(E=>{["timeout","headers","queryParameters","data","cacheable"].indexOf(E)===-1&&(h[E]=f[E])}),{data:Object.entries(h).length>0?h:void 0,timeout:f.timeout||l,headers:f.headers||{},queryParameters:f.queryParameters||{},cacheable:f.cacheable}}var T4={Read:1,Write:2,Any:3},um={Up:1,Down:2,Timeouted:3},Xx=2*60*1e3;function J3(o,l=um.Up){return{...o,status:l,lastUpdate:Date.now()}}function Qx(o){return o.status===um.Up||Date.now()-o.lastUpdate>Xx}function Jx(o){return o.status===um.Timeouted&&Date.now()-o.lastUpdate<=Xx}function Z3(o){return{protocol:o.protocol||"https",url:o.url,accept:o.accept||T4.Any}}function sj(o,l){return Promise.all(l.map(f=>o.get(f,()=>Promise.resolve(J3(f))))).then(f=>{let h=f.filter(F=>Qx(F)),E=f.filter(F=>Jx(F)),t=[...h,...E],N=t.length>0?t.map(F=>Z3(F)):l;return{getTimeout(F,k){return(E.length===0&&F===0?1:E.length+3+F)*k},statelessHosts:N}})}var aj=({isTimedOut:o,status:l})=>!o&&~~l===0,fj=o=>{let l=o.status;return o.isTimedOut||aj(o)||~~(l/100)!==2&&~~(l/100)!==4},cj=({status:o})=>~~(o/100)===2,dj=(o,l)=>fj(o)?l.onRetry(o):cj(o)?l.onSucess(o):l.onFail(o);function Yx(o,l,f,h){let E=[],t=n5(f,h),N=r5(o,h),F=f.method,k=f.method!==Kx.MethodEnum.Get?{}:{...f.data,...h.data},x={"x-algolia-agent":o.userAgent.value,...o.queryParameters,...k,...h.queryParameters},j=0,q=(V,re)=>{let y=V.pop();if(y===void 0)throw o5(Q3(E));let me={data:t,headers:N,method:F,url:e5(y,f.path,x),connectTimeout:re(j,o.timeouts.connect),responseTimeout:re(j,h.timeout)},De=ae=>{let we={request:me,response:ae,host:y,triesLeft:V.length};return E.push(we),we},ge={onSucess:ae=>Zx(ae),onRetry(ae){let we=De(ae);return ae.isTimedOut&&j++,Promise.all([o.logger.info("Retryable failure",$3(we)),o.hostsCache.set(y,J3(y,ae.isTimedOut?um.Timeouted:um.Down))]).then(()=>q(V,re))},onFail(ae){throw De(ae),$x(ae,Q3(E))}};return o.requester.send(me).then(ae=>dj(ae,ge))};return sj(o.hostsCache,l).then(V=>q([...V.statelessHosts].reverse(),V.getTimeout))}function pj(o){let{hostsCache:l,logger:f,requester:h,requestsCache:E,responsesCache:t,timeouts:N,userAgent:F,hosts:k,queryParameters:x,headers:j}=o,q={hostsCache:l,logger:f,requester:h,requestsCache:E,responsesCache:t,timeouts:N,userAgent:F,headers:j,queryParameters:x,hosts:k.map(V=>Z3(V)),read(V,re){let y=X3(re,q.timeouts.read),me=()=>Yx(q,q.hosts.filter(ae=>(ae.accept&T4.Read)!==0),V,y);if((y.cacheable!==void 0?y.cacheable:V.cacheable)!==!0)return me();let ge={request:V,mappedRequestOptions:y,transporter:{queryParameters:q.queryParameters,headers:q.headers}};return q.responsesCache.get(ge,()=>q.requestsCache.get(ge,()=>q.requestsCache.set(ge,me()).then(ae=>Promise.all([q.requestsCache.delete(ge),ae]),ae=>Promise.all([q.requestsCache.delete(ge),Promise.reject(ae)])).then(([ae,we])=>we)),{miss:ae=>q.responsesCache.set(ge,ae)})},write(V,re){return Yx(q,q.hosts.filter(y=>(y.accept&T4.Write)!==0),V,X3(re,q.timeouts.write))}};return q}function hj(o){let l={value:`Algolia for JavaScript (${o})`,add(f){let h=`; ${f.segment}${f.version!==void 0?` (${f.version})`:""}`;return l.value.indexOf(h)===-1&&(l.value=`${l.value}${h}`),l}};return l}function Zx(o){try{return JSON.parse(o.content)}catch(l){throw u5(l.message,o)}}function $x({content:o,status:l},f){let h=o;try{h=JSON.parse(o).message}catch{}return i5(h,l,f)}function vj(o,...l){let f=0;return o.replace(/%s/g,()=>encodeURIComponent(l[f++]))}function e5(o,l,f){let h=t5(f),E=`${o.protocol}://${o.url}/${l.charAt(0)==="/"?l.substr(1):l}`;return h.length&&(E+=`?${h}`),E}function t5(o){let l=f=>Object.prototype.toString.call(f)==="[object Object]"||Object.prototype.toString.call(f)==="[object Array]";return Object.keys(o).map(f=>vj("%s=%s",f,l(o[f])?JSON.stringify(o[f]):o[f])).join("&")}function n5(o,l){if(o.method===Kx.MethodEnum.Get||o.data===void 0&&l.data===void 0)return;let f=Array.isArray(o.data)?o.data:{...o.data,...l.data};return JSON.stringify(f)}function r5(o,l){let f={...o.headers,...l.headers},h={};return Object.keys(f).forEach(E=>{let t=f[E];h[E.toLowerCase()]=t}),h}function Q3(o){return o.map(l=>$3(l))}function $3(o){let l=o.request.headers["x-algolia-api-key"]?{"x-algolia-api-key":"*****"}:{};return{...o,request:{...o.request,headers:{...o.request.headers,...l}}}}function i5(o,l,f){return{name:"ApiError",message:o,status:l,transporterStackTrace:f}}function u5(o,l){return{name:"DeserializationError",message:o,response:l}}function o5(o){return{name:"RetryError",message:"Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.",transporterStackTrace:o}}G0.CallEnum=T4;G0.HostStatusEnum=um;G0.createApiError=i5;G0.createDeserializationError=u5;G0.createMappedRequestOptions=X3;G0.createRetryError=o5;G0.createStatefulHost=J3;G0.createStatelessHost=Z3;G0.createTransporter=pj;G0.createUserAgent=hj;G0.deserializeFailure=$x;G0.deserializeSuccess=Zx;G0.isStatefulHostTimeouted=Jx;G0.isStatefulHostUp=Qx;G0.serializeData=n5;G0.serializeHeaders=r5;G0.serializeQueryParameters=t5;G0.serializeUrl=e5;G0.stackFrameWithoutCredentials=$3;G0.stackTraceWithoutCredentials=Q3});var bg=nt((AW,s5)=>{s5.exports=l5()});var a5=nt(y2=>{"use strict";Object.defineProperty(y2,"__esModule",{value:!0});var om=Pg(),mj=bg(),Bg=Ig(),yj=o=>{let l=o.region||"us",f=om.createAuth(om.AuthMode.WithinHeaders,o.appId,o.apiKey),h=mj.createTransporter({hosts:[{url:`analytics.${l}.algolia.com`}],...o,headers:{...f.headers(),"content-type":"application/json",...o.headers},queryParameters:{...f.queryParameters(),...o.queryParameters}}),E=o.appId;return om.addMethods({appId:E,transporter:h},o.methods)},gj=o=>(l,f)=>o.transporter.write({method:Bg.MethodEnum.Post,path:"2/abtests",data:l},f),_j=o=>(l,f)=>o.transporter.write({method:Bg.MethodEnum.Delete,path:om.encode("2/abtests/%s",l)},f),Ej=o=>(l,f)=>o.transporter.read({method:Bg.MethodEnum.Get,path:om.encode("2/abtests/%s",l)},f),Dj=o=>l=>o.transporter.read({method:Bg.MethodEnum.Get,path:"2/abtests"},l),wj=o=>(l,f)=>o.transporter.write({method:Bg.MethodEnum.Post,path:om.encode("2/abtests/%s/stop",l)},f);y2.addABTest=gj;y2.createAnalyticsClient=yj;y2.deleteABTest=_j;y2.getABTest=Ej;y2.getABTests=Dj;y2.stopABTest=wj});var c5=nt((MW,f5)=>{f5.exports=a5()});var p5=nt(Ug=>{"use strict";Object.defineProperty(Ug,"__esModule",{value:!0});var ew=Pg(),Sj=bg(),d5=Ig(),Tj=o=>{let l=o.region||"us",f=ew.createAuth(ew.AuthMode.WithinHeaders,o.appId,o.apiKey),h=Sj.createTransporter({hosts:[{url:`recommendation.${l}.algolia.com`}],...o,headers:{...f.headers(),"content-type":"application/json",...o.headers},queryParameters:{...f.queryParameters(),...o.queryParameters}});return ew.addMethods({appId:o.appId,transporter:h},o.methods)},Cj=o=>l=>o.transporter.read({method:d5.MethodEnum.Get,path:"1/strategies/personalization"},l),xj=o=>(l,f)=>o.transporter.write({method:d5.MethodEnum.Post,path:"1/strategies/personalization",data:l},f);Ug.createRecommendationClient=Tj;Ug.getPersonalizationStrategy=Cj;Ug.setPersonalizationStrategy=xj});var v5=nt((NW,h5)=>{h5.exports=p5()});var A5=nt(tn=>{"use strict";Object.defineProperty(tn,"__esModule",{value:!0});var Nn=Pg(),ra=bg(),Ur=Ig(),Rj=hi("crypto");function C4(o){let l=f=>o.request(f).then(h=>{if(o.batch!==void 0&&o.batch(h.hits),!o.shouldStop(h))return h.cursor?l({cursor:h.cursor}):l({page:(f.page||0)+1})});return l({})}var Aj=o=>{let l=o.appId,f=Nn.createAuth(o.authMode!==void 0?o.authMode:Nn.AuthMode.WithinHeaders,l,o.apiKey),h=ra.createTransporter({hosts:[{url:`${l}-dsn.algolia.net`,accept:ra.CallEnum.Read},{url:`${l}.algolia.net`,accept:ra.CallEnum.Write}].concat(Nn.shuffle([{url:`${l}-1.algolianet.com`},{url:`${l}-2.algolianet.com`},{url:`${l}-3.algolianet.com`}])),...o,headers:{...f.headers(),"content-type":"application/x-www-form-urlencoded",...o.headers},queryParameters:{...f.queryParameters(),...o.queryParameters}}),E={transporter:h,appId:l,addAlgoliaAgent(t,N){h.userAgent.add({segment:t,version:N})},clearCache(){return Promise.all([h.requestsCache.clear(),h.responsesCache.clear()]).then(()=>{})}};return Nn.addMethods(E,o.methods)};function m5(){return{name:"MissingObjectIDError",message:"All objects must have an unique objectID (like a primary key) to be valid. Algolia is also able to generate objectIDs automatically but *it's not recommended*. To do it, use the `{'autoGenerateObjectIDIfNotExist': true}` option."}}function y5(){return{name:"ObjectNotFoundError",message:"Object not found."}}function g5(){return{name:"ValidUntilNotFoundError",message:"ValidUntil not found in given secured api key."}}var Oj=o=>(l,f)=>{let{queryParameters:h,...E}=f||{},t={acl:l,...h!==void 0?{queryParameters:h}:{}},N=(F,k)=>Nn.createRetryablePromise(x=>jg(o)(F.key,k).catch(j=>{if(j.status!==404)throw j;return x()}));return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:"1/keys",data:t},E),N)},Mj=o=>(l,f,h)=>{let E=ra.createMappedRequestOptions(h);return E.queryParameters["X-Algolia-User-ID"]=l,o.transporter.write({method:Ur.MethodEnum.Post,path:"1/clusters/mapping",data:{cluster:f}},E)},kj=o=>(l,f,h)=>o.transporter.write({method:Ur.MethodEnum.Post,path:"1/clusters/mapping/batch",data:{users:l,cluster:f}},h),x4=o=>(l,f,h)=>{let E=(t,N)=>zg(o)(l,{methods:{waitTask:xo}}).waitTask(t.taskID,N);return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/operation",l),data:{operation:"copy",destination:f}},h),E)},Nj=o=>(l,f,h)=>x4(o)(l,f,{...h,scope:[A4.Rules]}),Lj=o=>(l,f,h)=>x4(o)(l,f,{...h,scope:[A4.Settings]}),Fj=o=>(l,f,h)=>x4(o)(l,f,{...h,scope:[A4.Synonyms]}),Pj=o=>(l,f)=>{let h=(E,t)=>Nn.createRetryablePromise(N=>jg(o)(l,t).then(N).catch(F=>{if(F.status!==404)throw F}));return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Delete,path:Nn.encode("1/keys/%s",l)},f),h)},Ij=()=>(o,l)=>{let f=ra.serializeQueryParameters(l),h=Rj.createHmac("sha256",o).update(f).digest("hex");return Buffer.from(h+f).toString("base64")},jg=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/keys/%s",l)},f),bj=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:"1/logs"},l),Bj=()=>o=>{let l=Buffer.from(o,"base64").toString("ascii"),f=/validUntil=(\d+)/,h=l.match(f);if(h===null)throw g5();return parseInt(h[1],10)-Math.round(new Date().getTime()/1e3)},Uj=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:"1/clusters/mapping/top"},l),jj=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/clusters/mapping/%s",l)},f),zj=o=>l=>{let{retrieveMappings:f,...h}=l||{};return f===!0&&(h.getClusters=!0),o.transporter.read({method:Ur.MethodEnum.Get,path:"1/clusters/mapping/pending"},h)},zg=o=>(l,f={})=>{let h={transporter:o.transporter,appId:o.appId,indexName:l};return Nn.addMethods(h,f.methods)},Hj=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:"1/keys"},l),qj=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:"1/clusters"},l),Wj=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:"1/indexes"},l),Vj=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:"1/clusters/mapping"},l),Gj=o=>(l,f,h)=>{let E=(t,N)=>zg(o)(l,{methods:{waitTask:xo}}).waitTask(t.taskID,N);return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/operation",l),data:{operation:"move",destination:f}},h),E)},Yj=o=>(l,f)=>{let h=(E,t)=>Promise.all(Object.keys(E.taskID).map(N=>zg(o)(N,{methods:{waitTask:xo}}).waitTask(E.taskID[N],t)));return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:"1/indexes/*/batch",data:{requests:l}},f),h)},Kj=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Post,path:"1/indexes/*/objects",data:{requests:l}},f),Xj=o=>(l,f)=>{let h=l.map(E=>({...E,params:ra.serializeQueryParameters(E.params||{})}));return o.transporter.read({method:Ur.MethodEnum.Post,path:"1/indexes/*/queries",data:{requests:h},cacheable:!0},f)},Qj=o=>(l,f)=>Promise.all(l.map(h=>{let{facetName:E,facetQuery:t,...N}=h.params;return zg(o)(h.indexName,{methods:{searchForFacetValues:C5}}).searchForFacetValues(E,t,{...f,...N})})),Jj=o=>(l,f)=>{let h=ra.createMappedRequestOptions(f);return h.queryParameters["X-Algolia-User-ID"]=l,o.transporter.write({method:Ur.MethodEnum.Delete,path:"1/clusters/mapping"},h)},Zj=o=>(l,f)=>{let h=(E,t)=>Nn.createRetryablePromise(N=>jg(o)(l,t).catch(F=>{if(F.status!==404)throw F;return N()}));return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/keys/%s/restore",l)},f),h)},$j=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Post,path:"1/clusters/mapping/search",data:{query:l}},f),ez=o=>(l,f)=>{let h=Object.assign({},f),{queryParameters:E,...t}=f||{},N=E?{queryParameters:E}:{},F=["acl","indexes","referers","restrictSources","queryParameters","description","maxQueriesPerIPPerHour","maxHitsPerQuery"],k=j=>Object.keys(h).filter(q=>F.indexOf(q)!==-1).every(q=>j[q]===h[q]),x=(j,q)=>Nn.createRetryablePromise(V=>jg(o)(l,q).then(re=>k(re)?Promise.resolve():V()));return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Put,path:Nn.encode("1/keys/%s",l),data:N},t),x)},_5=o=>(l,f)=>{let h=(E,t)=>xo(o)(E.taskID,t);return Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/batch",o.indexName),data:{requests:l}},f),h)},tz=o=>l=>C4({...l,shouldStop:f=>f.cursor===void 0,request:f=>o.transporter.read({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/browse",o.indexName),data:f},l)}),nz=o=>l=>{let f={hitsPerPage:1e3,...l};return C4({...f,shouldStop:h=>h.hits.length({...E,hits:E.hits.map(t=>(delete t._highlightResult,t))}))}})},rz=o=>l=>{let f={hitsPerPage:1e3,...l};return C4({...f,shouldStop:h=>h.hits.length({...E,hits:E.hits.map(t=>(delete t._highlightResult,t))}))}})},R4=o=>(l,f,h)=>{let{batchSize:E,...t}=h||{},N={taskIDs:[],objectIDs:[]},F=(k=0)=>{let x=[],j;for(j=k;j({action:f,body:q})),t).then(q=>(N.objectIDs=N.objectIDs.concat(q.objectIDs),N.taskIDs.push(q.taskID),j++,F(j)))};return Nn.createWaitablePromise(F(),(k,x)=>Promise.all(k.taskIDs.map(j=>xo(o)(j,x))))},iz=o=>l=>Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/clear",o.indexName)},l),(f,h)=>xo(o)(f.taskID,h)),uz=o=>l=>{let{forwardToReplicas:f,...h}=l||{},E=ra.createMappedRequestOptions(h);return f&&(E.queryParameters.forwardToReplicas=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/rules/clear",o.indexName)},E),(t,N)=>xo(o)(t.taskID,N))},oz=o=>l=>{let{forwardToReplicas:f,...h}=l||{},E=ra.createMappedRequestOptions(h);return f&&(E.queryParameters.forwardToReplicas=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/synonyms/clear",o.indexName)},E),(t,N)=>xo(o)(t.taskID,N))},lz=o=>(l,f)=>Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/deleteByQuery",o.indexName),data:l},f),(h,E)=>xo(o)(h.taskID,E)),sz=o=>l=>Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Delete,path:Nn.encode("1/indexes/%s",o.indexName)},l),(f,h)=>xo(o)(f.taskID,h)),az=o=>(l,f)=>Nn.createWaitablePromise(E5(o)([l],f).then(h=>({taskID:h.taskIDs[0]})),(h,E)=>xo(o)(h.taskID,E)),E5=o=>(l,f)=>{let h=l.map(E=>({objectID:E}));return R4(o)(h,fh.DeleteObject,f)},fz=o=>(l,f)=>{let{forwardToReplicas:h,...E}=f||{},t=ra.createMappedRequestOptions(E);return h&&(t.queryParameters.forwardToReplicas=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Delete,path:Nn.encode("1/indexes/%s/rules/%s",o.indexName,l)},t),(N,F)=>xo(o)(N.taskID,F))},cz=o=>(l,f)=>{let{forwardToReplicas:h,...E}=f||{},t=ra.createMappedRequestOptions(E);return h&&(t.queryParameters.forwardToReplicas=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Delete,path:Nn.encode("1/indexes/%s/synonyms/%s",o.indexName,l)},t),(N,F)=>xo(o)(N.taskID,F))},dz=o=>l=>D5(o)(l).then(()=>!0).catch(f=>{if(f.status!==404)throw f;return!1}),pz=o=>(l,f)=>{let{query:h,paginate:E,...t}=f||{},N=0,F=()=>T5(o)(h||"",{...t,page:N}).then(k=>{for(let[x,j]of Object.entries(k.hits))if(l(j))return{object:j,position:parseInt(x,10),page:N};if(N++,E===!1||N>=k.nbPages)throw y5();return F()});return F()},hz=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/indexes/%s/%s",o.indexName,l)},f),vz=()=>(o,l)=>{for(let[f,h]of Object.entries(o.hits))if(h.objectID===l)return parseInt(f,10);return-1},mz=o=>(l,f)=>{let{attributesToRetrieve:h,...E}=f||{},t=l.map(N=>({indexName:o.indexName,objectID:N,...h?{attributesToRetrieve:h}:{}}));return o.transporter.read({method:Ur.MethodEnum.Post,path:"1/indexes/*/objects",data:{requests:t}},E)},yz=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/indexes/%s/rules/%s",o.indexName,l)},f),D5=o=>l=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/indexes/%s/settings",o.indexName),data:{getVersion:2}},l),gz=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/indexes/%s/synonyms/%s",o.indexName,l)},f),w5=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Get,path:Nn.encode("1/indexes/%s/task/%s",o.indexName,l.toString())},f),_z=o=>(l,f)=>Nn.createWaitablePromise(S5(o)([l],f).then(h=>({objectID:h.objectIDs[0],taskID:h.taskIDs[0]})),(h,E)=>xo(o)(h.taskID,E)),S5=o=>(l,f)=>{let{createIfNotExists:h,...E}=f||{},t=h?fh.PartialUpdateObject:fh.PartialUpdateObjectNoCreate;return R4(o)(l,t,E)},Ez=o=>(l,f)=>{let{safe:h,autoGenerateObjectIDIfNotExist:E,batchSize:t,...N}=f||{},F=(y,me,De,ge)=>Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/operation",y),data:{operation:De,destination:me}},ge),(ae,we)=>xo(o)(ae.taskID,we)),k=Math.random().toString(36).substring(7),x=`${o.indexName}_tmp_${k}`,j=tw({appId:o.appId,transporter:o.transporter,indexName:x}),q=[],V=F(o.indexName,x,"copy",{...N,scope:["settings","synonyms","rules"]});q.push(V);let re=(h?V.wait(N):V).then(()=>{let y=j(l,{...N,autoGenerateObjectIDIfNotExist:E,batchSize:t});return q.push(y),h?y.wait(N):y}).then(()=>{let y=F(x,o.indexName,"move",N);return q.push(y),h?y.wait(N):y}).then(()=>Promise.all(q)).then(([y,me,De])=>({objectIDs:me.objectIDs,taskIDs:[y.taskID,...me.taskIDs,De.taskID]}));return Nn.createWaitablePromise(re,(y,me)=>Promise.all(q.map(De=>De.wait(me))))},Dz=o=>(l,f)=>nw(o)(l,{...f,clearExistingRules:!0}),wz=o=>(l,f)=>rw(o)(l,{...f,replaceExistingSynonyms:!0}),Sz=o=>(l,f)=>Nn.createWaitablePromise(tw(o)([l],f).then(h=>({objectID:h.objectIDs[0],taskID:h.taskIDs[0]})),(h,E)=>xo(o)(h.taskID,E)),tw=o=>(l,f)=>{let{autoGenerateObjectIDIfNotExist:h,...E}=f||{},t=h?fh.AddObject:fh.UpdateObject;if(t===fh.UpdateObject){for(let N of l)if(N.objectID===void 0)return Nn.createWaitablePromise(Promise.reject(m5()))}return R4(o)(l,t,E)},Tz=o=>(l,f)=>nw(o)([l],f),nw=o=>(l,f)=>{let{forwardToReplicas:h,clearExistingRules:E,...t}=f||{},N=ra.createMappedRequestOptions(t);return h&&(N.queryParameters.forwardToReplicas=1),E&&(N.queryParameters.clearExistingRules=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/rules/batch",o.indexName),data:l},N),(F,k)=>xo(o)(F.taskID,k))},Cz=o=>(l,f)=>rw(o)([l],f),rw=o=>(l,f)=>{let{forwardToReplicas:h,replaceExistingSynonyms:E,...t}=f||{},N=ra.createMappedRequestOptions(t);return h&&(N.queryParameters.forwardToReplicas=1),E&&(N.queryParameters.replaceExistingSynonyms=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/synonyms/batch",o.indexName),data:l},N),(F,k)=>xo(o)(F.taskID,k))},T5=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/query",o.indexName),data:{query:l},cacheable:!0},f),C5=o=>(l,f,h)=>o.transporter.read({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/facets/%s/query",o.indexName,l),data:{facetQuery:f},cacheable:!0},h),x5=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/rules/search",o.indexName),data:{query:l}},f),R5=o=>(l,f)=>o.transporter.read({method:Ur.MethodEnum.Post,path:Nn.encode("1/indexes/%s/synonyms/search",o.indexName),data:{query:l}},f),xz=o=>(l,f)=>{let{forwardToReplicas:h,...E}=f||{},t=ra.createMappedRequestOptions(E);return h&&(t.queryParameters.forwardToReplicas=1),Nn.createWaitablePromise(o.transporter.write({method:Ur.MethodEnum.Put,path:Nn.encode("1/indexes/%s/settings",o.indexName),data:l},t),(N,F)=>xo(o)(N.taskID,F))},xo=o=>(l,f)=>Nn.createRetryablePromise(h=>w5(o)(l,f).then(E=>E.status!=="published"?h():void 0)),Rz={AddObject:"addObject",Analytics:"analytics",Browser:"browse",DeleteIndex:"deleteIndex",DeleteObject:"deleteObject",EditSettings:"editSettings",ListIndexes:"listIndexes",Logs:"logs",Recommendation:"recommendation",Search:"search",SeeUnretrievableAttributes:"seeUnretrievableAttributes",Settings:"settings",Usage:"usage"},fh={AddObject:"addObject",UpdateObject:"updateObject",PartialUpdateObject:"partialUpdateObject",PartialUpdateObjectNoCreate:"partialUpdateObjectNoCreate",DeleteObject:"deleteObject"},A4={Settings:"settings",Synonyms:"synonyms",Rules:"rules"},Az={None:"none",StopIfEnoughMatches:"stopIfEnoughMatches"},Oz={Synonym:"synonym",OneWaySynonym:"oneWaySynonym",AltCorrection1:"altCorrection1",AltCorrection2:"altCorrection2",Placeholder:"placeholder"};tn.ApiKeyACLEnum=Rz;tn.BatchActionEnum=fh;tn.ScopeEnum=A4;tn.StrategyEnum=Az;tn.SynonymEnum=Oz;tn.addApiKey=Oj;tn.assignUserID=Mj;tn.assignUserIDs=kj;tn.batch=_5;tn.browseObjects=tz;tn.browseRules=nz;tn.browseSynonyms=rz;tn.chunkedBatch=R4;tn.clearObjects=iz;tn.clearRules=uz;tn.clearSynonyms=oz;tn.copyIndex=x4;tn.copyRules=Nj;tn.copySettings=Lj;tn.copySynonyms=Fj;tn.createBrowsablePromise=C4;tn.createMissingObjectIDError=m5;tn.createObjectNotFoundError=y5;tn.createSearchClient=Aj;tn.createValidUntilNotFoundError=g5;tn.deleteApiKey=Pj;tn.deleteBy=lz;tn.deleteIndex=sz;tn.deleteObject=az;tn.deleteObjects=E5;tn.deleteRule=fz;tn.deleteSynonym=cz;tn.exists=dz;tn.findObject=pz;tn.generateSecuredApiKey=Ij;tn.getApiKey=jg;tn.getLogs=bj;tn.getObject=hz;tn.getObjectPosition=vz;tn.getObjects=mz;tn.getRule=yz;tn.getSecuredApiKeyRemainingValidity=Bj;tn.getSettings=D5;tn.getSynonym=gz;tn.getTask=w5;tn.getTopUserIDs=Uj;tn.getUserID=jj;tn.hasPendingMappings=zj;tn.initIndex=zg;tn.listApiKeys=Hj;tn.listClusters=qj;tn.listIndices=Wj;tn.listUserIDs=Vj;tn.moveIndex=Gj;tn.multipleBatch=Yj;tn.multipleGetObjects=Kj;tn.multipleQueries=Xj;tn.multipleSearchForFacetValues=Qj;tn.partialUpdateObject=_z;tn.partialUpdateObjects=S5;tn.removeUserID=Jj;tn.replaceAllObjects=Ez;tn.replaceAllRules=Dz;tn.replaceAllSynonyms=wz;tn.restoreApiKey=Zj;tn.saveObject=Sz;tn.saveObjects=tw;tn.saveRule=Tz;tn.saveRules=nw;tn.saveSynonym=Cz;tn.saveSynonyms=rw;tn.search=T5;tn.searchForFacetValues=C5;tn.searchRules=x5;tn.searchSynonyms=R5;tn.searchUserIDs=$j;tn.setSettings=xz;tn.updateApiKey=ez;tn.waitTask=xo});var M5=nt((FW,O5)=>{O5.exports=A5()});var k5=nt(O4=>{"use strict";Object.defineProperty(O4,"__esModule",{value:!0});function Mz(){return{debug(o,l){return Promise.resolve()},info(o,l){return Promise.resolve()},error(o,l){return Promise.resolve()}}}var kz={Debug:1,Info:2,Error:3};O4.LogLevelEnum=kz;O4.createNullLogger=Mz});var L5=nt((IW,N5)=>{N5.exports=k5()});var I5=nt(iw=>{"use strict";Object.defineProperty(iw,"__esModule",{value:!0});var F5=hi("http"),P5=hi("https"),Nz=hi("url");function Lz(){let o={keepAlive:!0},l=new F5.Agent(o),f=new P5.Agent(o);return{send(h){return new Promise(E=>{let t=Nz.parse(h.url),N=t.query===null?t.pathname:`${t.pathname}?${t.query}`,F={agent:t.protocol==="https:"?f:l,hostname:t.hostname,path:N,method:h.method,headers:h.headers,...t.port!==void 0?{port:t.port||""}:{}},k=(t.protocol==="https:"?P5:F5).request(F,V=>{let re="";V.on("data",y=>re+=y),V.on("end",()=>{clearTimeout(j),clearTimeout(q),E({status:V.statusCode||0,content:re,isTimedOut:!1})})}),x=(V,re)=>setTimeout(()=>{k.abort(),E({status:0,content:re,isTimedOut:!0})},V*1e3),j=x(h.connectTimeout,"Connection timeout"),q;k.on("error",V=>{clearTimeout(j),clearTimeout(q),E({status:0,content:V.message,isTimedOut:!1})}),k.once("response",()=>{clearTimeout(j),q=x(h.responseTimeout,"Socket timeout")}),h.data!==void 0&&k.write(h.data),k.end()})},destroy(){return l.destroy(),f.destroy(),Promise.resolve()}}}iw.createNodeHttpRequester=Lz});var B5=nt((BW,b5)=>{b5.exports=I5()});var H5=nt((UW,z5)=>{"use strict";var U5=Bx(),Fz=zx(),lm=c5(),ow=Pg(),uw=v5(),wn=M5(),Pz=L5(),Iz=B5(),bz=bg();function j5(o,l,f){let h={appId:o,apiKey:l,timeouts:{connect:2,read:5,write:30},requester:Iz.createNodeHttpRequester(),logger:Pz.createNullLogger(),responsesCache:U5.createNullCache(),requestsCache:U5.createNullCache(),hostsCache:Fz.createInMemoryCache(),userAgent:bz.createUserAgent(ow.version).add({segment:"Node.js",version:process.versions.node})};return wn.createSearchClient({...h,...f,methods:{search:wn.multipleQueries,searchForFacetValues:wn.multipleSearchForFacetValues,multipleBatch:wn.multipleBatch,multipleGetObjects:wn.multipleGetObjects,multipleQueries:wn.multipleQueries,copyIndex:wn.copyIndex,copySettings:wn.copySettings,copyRules:wn.copyRules,copySynonyms:wn.copySynonyms,moveIndex:wn.moveIndex,listIndices:wn.listIndices,getLogs:wn.getLogs,listClusters:wn.listClusters,multipleSearchForFacetValues:wn.multipleSearchForFacetValues,getApiKey:wn.getApiKey,addApiKey:wn.addApiKey,listApiKeys:wn.listApiKeys,updateApiKey:wn.updateApiKey,deleteApiKey:wn.deleteApiKey,restoreApiKey:wn.restoreApiKey,assignUserID:wn.assignUserID,assignUserIDs:wn.assignUserIDs,getUserID:wn.getUserID,searchUserIDs:wn.searchUserIDs,listUserIDs:wn.listUserIDs,getTopUserIDs:wn.getTopUserIDs,removeUserID:wn.removeUserID,hasPendingMappings:wn.hasPendingMappings,generateSecuredApiKey:wn.generateSecuredApiKey,getSecuredApiKeyRemainingValidity:wn.getSecuredApiKeyRemainingValidity,destroy:ow.destroy,initIndex:E=>t=>wn.initIndex(E)(t,{methods:{batch:wn.batch,delete:wn.deleteIndex,getObject:wn.getObject,getObjects:wn.getObjects,saveObject:wn.saveObject,saveObjects:wn.saveObjects,search:wn.search,searchForFacetValues:wn.searchForFacetValues,waitTask:wn.waitTask,setSettings:wn.setSettings,getSettings:wn.getSettings,partialUpdateObject:wn.partialUpdateObject,partialUpdateObjects:wn.partialUpdateObjects,deleteObject:wn.deleteObject,deleteObjects:wn.deleteObjects,deleteBy:wn.deleteBy,clearObjects:wn.clearObjects,browseObjects:wn.browseObjects,getObjectPosition:wn.getObjectPosition,findObject:wn.findObject,exists:wn.exists,saveSynonym:wn.saveSynonym,saveSynonyms:wn.saveSynonyms,getSynonym:wn.getSynonym,searchSynonyms:wn.searchSynonyms,browseSynonyms:wn.browseSynonyms,deleteSynonym:wn.deleteSynonym,clearSynonyms:wn.clearSynonyms,replaceAllObjects:wn.replaceAllObjects,replaceAllSynonyms:wn.replaceAllSynonyms,searchRules:wn.searchRules,getRule:wn.getRule,deleteRule:wn.deleteRule,saveRule:wn.saveRule,saveRules:wn.saveRules,replaceAllRules:wn.replaceAllRules,browseRules:wn.browseRules,clearRules:wn.clearRules}}),initAnalytics:()=>E=>lm.createAnalyticsClient({...h,...E,methods:{addABTest:lm.addABTest,getABTest:lm.getABTest,getABTests:lm.getABTests,stopABTest:lm.stopABTest,deleteABTest:lm.deleteABTest}}),initRecommendation:()=>E=>uw.createRecommendationClient({...h,...E,methods:{getPersonalizationStrategy:uw.getPersonalizationStrategy,setPersonalizationStrategy:uw.setPersonalizationStrategy}})}})}j5.version=ow.version;z5.exports=j5});var W5=nt((jW,lw)=>{var q5=H5();lw.exports=q5;lw.exports.default=q5});var Yz={};HF(Yz,{default:()=>Gz});var G5=hi("@yarnpkg/cli"),ch=hi("@yarnpkg/core");var Cx=V0(lc()),lh=V0(Mi()),m4=(0,lh.memo)(({active:o})=>{let l=(0,lh.useMemo)(()=>o?"\u25C9":"\u25EF",[o]),f=(0,lh.useMemo)(()=>o?"green":"yellow",[o]);return lh.default.createElement(Cx.Text,{color:f},l)});var m2=V0(lc()),na=V0(Mi());var xx=V0(lc()),y4=V0(Mi());function v2({active:o},l,f){let{stdin:h}=(0,xx.useStdin)(),E=(0,y4.useCallback)((t,N)=>l(t,N),f);(0,y4.useEffect)(()=>{if(!(!o||!h))return h.on("keypress",E),()=>{h.off("keypress",E)}},[o,E,h])}var Rx=function({active:o},l,f){v2({active:o},(h,E)=>{E.name==="tab"&&(E.shift?l("before"):l("after"))},f)};var g4=function(o,l,{active:f,minus:h,plus:E,set:t,loop:N=!0}){v2({active:f},(F,k)=>{let x=l.indexOf(o);switch(k.name){case h:{let j=x-1;if(N){t(l[(l.length+j)%l.length]);return}if(j<0)return;t(l[j])}break;case E:{let j=x+1;if(N){t(l[j%l.length]);return}if(j>=l.length)return;t(l[j])}break}},[l,o,E,t,N])};var _4=({active:o=!0,children:l=[],radius:f=10,size:h=1,loop:E=!0,onFocusRequest:t,willReachEnd:N})=>{let F=De=>{if(De.key===null)throw new Error("Expected all children to have a key");return De.key},k=na.default.Children.map(l,De=>F(De)),x=k[0],[j,q]=(0,na.useState)(x),V=k.indexOf(j);(0,na.useEffect)(()=>{k.includes(j)||q(x)},[l]),(0,na.useEffect)(()=>{N&&V>=k.length-2&&N()},[V]),Rx({active:o&&!!t},De=>{t==null||t(De)},[t]),g4(j,k,{active:o,minus:"up",plus:"down",set:q,loop:E});let re=V-f,y=V+f;y>k.length&&(re-=y-k.length,y=k.length),re<0&&(y+=-re,re=0),y>=k.length&&(y=k.length-1);let me=[];for(let De=re;De<=y;++De){let ge=k[De],ae=o&&ge===j;me.push(na.default.createElement(m2.Box,{key:ge,height:h},na.default.createElement(m2.Box,{marginLeft:1,marginRight:1},na.default.createElement(m2.Text,null,ae?na.default.createElement(m2.Text,{color:"cyan",bold:!0},">"):" ")),na.default.createElement(m2.Box,null,na.default.cloneElement(l[De],{active:ae}))))}return na.default.createElement(m2.Box,{flexDirection:"column",width:"100%"},me)};var E4=V0(Mi());var Ax=V0(lc()),ed=V0(Mi()),Ox=hi("readline"),q3=ed.default.createContext(null),Mx=({children:o})=>{let{stdin:l,setRawMode:f}=(0,Ax.useStdin)();(0,ed.useEffect)(()=>{f&&f(!0),l&&(0,Ox.emitKeypressEvents)(l)},[l,f]);let[h,E]=(0,ed.useState)(new Map),t=(0,ed.useMemo)(()=>({getAll:()=>h,get:N=>h.get(N),set:(N,F)=>E(new Map([...h,[N,F]]))}),[h,E]);return ed.default.createElement(q3.Provider,{value:t,children:o})};function sh(o,l){let f=(0,E4.useContext)(q3);if(f===null)throw new Error("Expected this hook to run with a ministore context attached");if(typeof o>"u")return f.getAll();let h=(0,E4.useCallback)(t=>{f.set(o,t)},[o,f.set]),E=f.get(o);return typeof E>"u"&&(E=l),[E,h]}var D4=V0(lc()),W3=V0(Mi());async function w4(o,l,{stdin:f,stdout:h,stderr:E}={}){let t,N=k=>{let{exit:x}=(0,D4.useApp)();v2({active:!0},(j,q)=>{q.name==="return"&&(t=k,x())},[x,k])},{waitUntilExit:F}=(0,D4.render)(W3.default.createElement(Mx,null,W3.default.createElement(o,{...l,useSubmit:N})),{stdin:f,stdout:h,stderr:E});return await F(),t}var Y5=hi("clipanion"),K5=V0(Fx()),or=V0(lc()),En=V0(Mi());var V5=V0(W5()),sw={appId:"OFCNCOG2CU",apiKey:"6fe4476ee5a1832882e326b506d14126",indexName:"npm-search"},Bz=(0,V5.default)(sw.appId,sw.apiKey).initIndex(sw.indexName),aw=async(o,l=0)=>await Bz.search(o,{analyticsTags:["yarn-plugin-interactive-tools"],attributesToRetrieve:["name","version","owner","repository","humanDownloadsLast30Days"],page:l,hitsPerPage:10});var Hg=["regular","dev","peer"],dh=class extends G5.BaseCommand{async execute(){let l=await ch.Configuration.find(this.context.cwd,this.context.plugins),f=()=>En.default.createElement(or.Box,{flexDirection:"row"},En.default.createElement(or.Box,{flexDirection:"column",width:48},En.default.createElement(or.Box,null,En.default.createElement(or.Text,null,"Press ",En.default.createElement(or.Text,{bold:!0,color:"cyanBright"},""),"/",En.default.createElement(or.Text,{bold:!0,color:"cyanBright"},"")," to move between packages.")),En.default.createElement(or.Box,null,En.default.createElement(or.Text,null,"Press ",En.default.createElement(or.Text,{bold:!0,color:"cyanBright"},"")," to select a package.")),En.default.createElement(or.Box,null,En.default.createElement(or.Text,null,"Press ",En.default.createElement(or.Text,{bold:!0,color:"cyanBright"},"")," again to change the target."))),En.default.createElement(or.Box,{flexDirection:"column"},En.default.createElement(or.Box,{marginLeft:1},En.default.createElement(or.Text,null,"Press ",En.default.createElement(or.Text,{bold:!0,color:"cyanBright"},"")," to install the selected packages.")),En.default.createElement(or.Box,{marginLeft:1},En.default.createElement(or.Text,null,"Press ",En.default.createElement(or.Text,{bold:!0,color:"cyanBright"},"")," to abort.")))),h=()=>En.default.createElement(En.default.Fragment,null,En.default.createElement(or.Box,{width:15},En.default.createElement(or.Text,{bold:!0,underline:!0,color:"gray"},"Owner")),En.default.createElement(or.Box,{width:11},En.default.createElement(or.Text,{bold:!0,underline:!0,color:"gray"},"Version")),En.default.createElement(or.Box,{width:10},En.default.createElement(or.Text,{bold:!0,underline:!0,color:"gray"},"Downloads"))),E=()=>En.default.createElement(or.Box,{width:17},En.default.createElement(or.Text,{bold:!0,underline:!0,color:"gray"},"Target")),t=({hit:re,active:y})=>{let[me,De]=sh(re.name,null);v2({active:y},(we,he)=>{if(he.name!=="space")return;if(!me){De(Hg[0]);return}let ve=Hg.indexOf(me)+1;ve===Hg.length?De(null):De(Hg[ve])},[me,De]);let ge=ch.structUtils.parseIdent(re.name),ae=ch.structUtils.prettyIdent(l,ge);return En.default.createElement(or.Box,null,En.default.createElement(or.Box,{width:45},En.default.createElement(or.Text,{bold:!0,wrap:"wrap"},ae)),En.default.createElement(or.Box,{width:14,marginLeft:1},En.default.createElement(or.Text,{bold:!0,wrap:"truncate"},re.owner.name)),En.default.createElement(or.Box,{width:10,marginLeft:1},En.default.createElement(or.Text,{italic:!0,wrap:"truncate"},re.version)),En.default.createElement(or.Box,{width:16,marginLeft:1},En.default.createElement(or.Text,null,re.humanDownloadsLast30Days)))},N=({name:re,active:y})=>{let[me]=sh(re,null),De=ch.structUtils.parseIdent(re);return En.default.createElement(or.Box,null,En.default.createElement(or.Box,{width:47},En.default.createElement(or.Text,{bold:!0}," - ",ch.structUtils.prettyIdent(l,De))),Hg.map(ge=>En.default.createElement(or.Box,{key:ge,width:14,marginLeft:1},En.default.createElement(or.Text,null," ",En.default.createElement(m4,{active:me===ge})," ",En.default.createElement(or.Text,{bold:!0},ge)))))},F=()=>En.default.createElement(or.Box,{marginTop:1},En.default.createElement(or.Text,null,"Powered by Algolia.")),x=await w4(({useSubmit:re})=>{let y=sh();re(y);let me=Array.from(y.keys()).filter(We=>y.get(We)!==null),[De,ge]=(0,En.useState)(""),[ae,we]=(0,En.useState)(0),[he,ve]=(0,En.useState)([]),ue=We=>{We.match(/\t| /)||ge(We)},Ae=async()=>{we(0);let We=await aw(De);We.query===De&&ve(We.hits)},ze=async()=>{let We=await aw(De,ae+1);We.query===De&&We.page-1===ae&&(we(We.page),ve([...he,...We.hits]))};return(0,En.useEffect)(()=>{De?Ae():ve([])},[De]),En.default.createElement(or.Box,{flexDirection:"column"},En.default.createElement(f,null),En.default.createElement(or.Box,{flexDirection:"row",marginTop:1},En.default.createElement(or.Text,{bold:!0},"Search: "),En.default.createElement(or.Box,{width:41},En.default.createElement(K5.default,{value:De,onChange:ue,placeholder:"i.e. babel, webpack, react...",showCursor:!1})),En.default.createElement(h,null)),he.length?En.default.createElement(_4,{radius:2,loop:!1,children:he.map(We=>En.default.createElement(t,{key:We.name,hit:We,active:!1})),willReachEnd:ze}):En.default.createElement(or.Text,{color:"gray"},"Start typing..."),En.default.createElement(or.Box,{flexDirection:"row",marginTop:1},En.default.createElement(or.Box,{width:49},En.default.createElement(or.Text,{bold:!0},"Selected:")),En.default.createElement(E,null)),me.length?me.map(We=>En.default.createElement(N,{key:We,name:We,active:!1})):En.default.createElement(or.Text,{color:"gray"},"No selected packages..."),En.default.createElement(F,null))},{},{stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr});if(typeof x>"u")return 1;let j=Array.from(x.keys()).filter(re=>x.get(re)==="regular"),q=Array.from(x.keys()).filter(re=>x.get(re)==="dev"),V=Array.from(x.keys()).filter(re=>x.get(re)==="peer");return j.length&&await this.cli.run(["add",...j]),q.length&&await this.cli.run(["add","--dev",...q]),V&&await this.cli.run(["add","--peer",...V]),0}};dh.paths=[["search"]],dh.usage=Y5.Command.Usage({category:"Interactive commands",description:"open the search interface",details:` + This command opens a fullscreen terminal interface where you can search for and install packages from the npm registry. + `,examples:[["Open the search window","yarn search"]]});var N4=hi("@yarnpkg/cli"),Ro=hi("@yarnpkg/core");var qg=V0(lc()),g2=V0(Mi());var X5=V0(lc()),Q5=V0(Mi()),M4=({length:o,active:l})=>{if(o===0)return null;let f=o>1?` ${"-".repeat(o-1)}`:" ";return Q5.default.createElement(X5.Text,{dimColor:!l},f)};var J5=function({active:o,skewer:l,options:f,value:h,onChange:E,sizes:t=[]}){let N=f.filter(({label:k})=>!!k).map(({value:k})=>k),F=f.findIndex(k=>k.value===h&&k.label!="");return g4(h,N,{active:o,minus:"left",plus:"right",set:E}),g2.default.createElement(g2.default.Fragment,null,f.map(({label:k},x)=>{let j=x===F,q=t[x]-1||0,V=k.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,""),re=Math.max(0,q-V.length-2);return k?g2.default.createElement(qg.Box,{key:k,width:q,marginLeft:1},g2.default.createElement(qg.Text,{wrap:"truncate"},g2.default.createElement(m4,{active:j})," ",k),l?g2.default.createElement(M4,{active:o,length:re}):null):g2.default.createElement(qg.Box,{key:`spacer-${x}`,width:q,marginLeft:1})}))};var r9=hi("@yarnpkg/plugin-essentials"),L4=hi("clipanion");function td(){}td.prototype={diff:function(l,f){var h=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},E=h.callback;typeof h=="function"&&(E=h,h={}),this.options=h;var t=this;function N(me){return E?(setTimeout(function(){E(void 0,me)},0),!0):me}l=this.castInput(l),f=this.castInput(f),l=this.removeEmpty(this.tokenize(l)),f=this.removeEmpty(this.tokenize(f));var F=f.length,k=l.length,x=1,j=F+k;h.maxEditLength&&(j=Math.min(j,h.maxEditLength));var q=[{newPos:-1,components:[]}],V=this.extractCommon(q[0],f,l,0);if(q[0].newPos+1>=F&&V+1>=k)return N([{value:this.join(f),count:f.length}]);function re(){for(var me=-1*x;me<=x;me+=2){var De=void 0,ge=q[me-1],ae=q[me+1],we=(ae?ae.newPos:0)-me;ge&&(q[me-1]=void 0);var he=ge&&ge.newPos+1=F&&we+1>=k)return N(Uz(t,De.components,f,l,t.useLongestToken));q[me]=De}x++}if(E)(function me(){setTimeout(function(){if(x>j)return E();re()||me()},0)})();else for(;x<=j;){var y=re();if(y)return y}},pushComponent:function(l,f,h){var E=l[l.length-1];E&&E.added===f&&E.removed===h?l[l.length-1]={count:E.count+1,added:f,removed:h}:l.push({count:1,added:f,removed:h})},extractCommon:function(l,f,h,E){for(var t=f.length,N=h.length,F=l.newPos,k=F-E,x=0;F+1re.length?me:re}),x.value=o.join(j)}else x.value=o.join(f.slice(F,F+x.count));F+=x.count,x.added||(k+=x.count)}}var V=l[N-1];return N>1&&typeof V.value=="string"&&(V.added||V.removed)&&o.equals("",V.value)&&(l[N-2].value+=V.value,l.pop()),l}function jz(o){return{newPos:o.newPos,components:o.components.slice(0)}}var rV=new td;function zz(o,l){if(typeof o=="function")l.callback=o;else if(o)for(var f in o)o.hasOwnProperty(f)&&(l[f]=o[f]);return l}var Z5=/^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/,$5=/\S/,dw=new td;dw.equals=function(o,l){return this.options.ignoreCase&&(o=o.toLowerCase(),l=l.toLowerCase()),o===l||this.options.ignoreWhitespace&&!$5.test(o)&&!$5.test(l)};dw.tokenize=function(o){for(var l=o.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/),f=0;f"u"?f:N}:h;return typeof o=="string"?o:JSON.stringify(fw(o,null,null,E),E," ")};Wg.equals=function(o,l){return td.prototype.equals.call(Wg,o.replace(/,([\r\n])/g,"$1"),l.replace(/,([\r\n])/g,"$1"))};function fw(o,l,f,h,E){l=l||[],f=f||[],h&&(o=h(E,o));var t;for(t=0;t=?)?)([0-9]+)(\.[0-9]+)(\.[0-9]+)((?:-\S+)?)$/,u9=(o,l)=>o.length>0?[o.slice(0,l)].concat(u9(o.slice(l),l)):[],ph=class extends N4.BaseCommand{async execute(){if(!this.context.stdout.isTTY)throw new L4.UsageError("This command can only be run in a TTY environment");let l=await Ro.Configuration.find(this.context.cwd,this.context.plugins),{project:f,workspace:h}=await Ro.Project.find(l,this.context.cwd),E=await Ro.Cache.find(l);if(!h)throw new N4.WorkspaceRequiredError(f.cwd,this.context.cwd);await f.restoreInstallState({restoreResolutions:!1});let t=this.context.stdout.rows-7,N=(ae,we)=>{let he=e9(ae,we),ve="";for(let ue of he)ue.added?ve+=Ro.formatUtils.pretty(l,ue.value,"green"):ue.removed||(ve+=ue.value);return ve},F=(ae,we)=>{if(ae===we)return we;let he=Ro.structUtils.parseRange(ae),ve=Ro.structUtils.parseRange(we),ue=he.selector.match(n9),Ae=ve.selector.match(n9);if(!ue||!Ae)return N(ae,we);let ze=["gray","red","yellow","green","magenta"],We=null,gt="";for(let _t=1;_t{let ve=await r9.suggestUtils.fetchDescriptorFrom(ae,he,{project:f,cache:E,preserveModifier:we,workspace:h});return ve!==null?ve.range:ae.range},x=async ae=>{let we=i9.default.valid(ae.range)?`^${ae.range}`:ae.range,[he,ve]=await Promise.all([k(ae,ae.range,we).catch(()=>null),k(ae,ae.range,"latest").catch(()=>null)]),ue=[{value:null,label:ae.range}];return he&&he!==ae.range?ue.push({value:he,label:F(ae.range,he)}):ue.push({value:null,label:""}),ve&&ve!==he&&ve!==ae.range?ue.push({value:ve,label:F(ae.range,ve)}):ue.push({value:null,label:""}),ue},j=()=>Tr.default.createElement(bi.Box,{flexDirection:"row"},Tr.default.createElement(bi.Box,{flexDirection:"column",width:49},Tr.default.createElement(bi.Box,{marginLeft:1},Tr.default.createElement(bi.Text,null,"Press ",Tr.default.createElement(bi.Text,{bold:!0,color:"cyanBright"},""),"/",Tr.default.createElement(bi.Text,{bold:!0,color:"cyanBright"},"")," to select packages.")),Tr.default.createElement(bi.Box,{marginLeft:1},Tr.default.createElement(bi.Text,null,"Press ",Tr.default.createElement(bi.Text,{bold:!0,color:"cyanBright"},""),"/",Tr.default.createElement(bi.Text,{bold:!0,color:"cyanBright"},"")," to select versions."))),Tr.default.createElement(bi.Box,{flexDirection:"column"},Tr.default.createElement(bi.Box,{marginLeft:1},Tr.default.createElement(bi.Text,null,"Press ",Tr.default.createElement(bi.Text,{bold:!0,color:"cyanBright"},"")," to install.")),Tr.default.createElement(bi.Box,{marginLeft:1},Tr.default.createElement(bi.Text,null,"Press ",Tr.default.createElement(bi.Text,{bold:!0,color:"cyanBright"},"")," to abort.")))),q=()=>Tr.default.createElement(bi.Box,{flexDirection:"row",paddingTop:1,paddingBottom:1},Tr.default.createElement(bi.Box,{width:50},Tr.default.createElement(bi.Text,{bold:!0},Tr.default.createElement(bi.Text,{color:"greenBright"},"?")," Pick the packages you want to upgrade.")),Tr.default.createElement(bi.Box,{width:17},Tr.default.createElement(bi.Text,{bold:!0,underline:!0,color:"gray"},"Current")),Tr.default.createElement(bi.Box,{width:17},Tr.default.createElement(bi.Text,{bold:!0,underline:!0,color:"gray"},"Range")),Tr.default.createElement(bi.Box,{width:17},Tr.default.createElement(bi.Text,{bold:!0,underline:!0,color:"gray"},"Latest"))),V=({active:ae,descriptor:we,suggestions:he})=>{let[ve,ue]=sh(we.descriptorHash,null),Ae=Ro.structUtils.stringifyIdent(we),ze=Math.max(0,45-Ae.length);return Tr.default.createElement(Tr.default.Fragment,null,Tr.default.createElement(bi.Box,null,Tr.default.createElement(bi.Box,{width:45},Tr.default.createElement(bi.Text,{bold:!0},Ro.structUtils.prettyIdent(l,we)),Tr.default.createElement(M4,{active:ae,length:ze})),Tr.default.createElement(J5,{active:ae,options:he,value:ve,skewer:!0,onChange:ue,sizes:[17,17,17]})))},re=({dependencies:ae})=>{let[we,he]=(0,Tr.useState)(ae.map(()=>null)),ve=(0,Tr.useRef)(!0),ue=async Ae=>{let ze=await x(Ae);return ze.filter(We=>We.label!=="").length<=1?null:{descriptor:Ae,suggestions:ze}};return(0,Tr.useEffect)(()=>()=>{ve.current=!1},[]),(0,Tr.useEffect)(()=>{let Ae=Math.trunc(t*1.75),ze=ae.slice(0,Ae),We=ae.slice(Ae),gt=u9(We,t),_t=ze.map(ue).reduce(async(Qe,ot)=>{await Qe;let Ve=await ot;Ve!==null&&(!ve.current||he(Pt=>{let Jt=Pt.findIndex(J=>J===null),it=[...Pt];return it[Jt]=Ve,it}))},Promise.resolve());gt.reduce((Qe,ot)=>Promise.all(ot.map(Ve=>Promise.resolve().then(()=>ue(Ve)))).then(async Ve=>{Ve=Ve.filter(Pt=>Pt!==null),await Qe,ve.current&&he(Pt=>{let Jt=Pt.findIndex(it=>it===null);return Pt.slice(0,Jt).concat(Ve).concat(Pt.slice(Jt+Ve.length))})}),_t).then(()=>{ve.current&&he(Qe=>Qe.filter(ot=>ot!==null))})},[]),we.length?Tr.default.createElement(_4,{radius:t>>1,children:we.map((Ae,ze)=>Ae!==null?Tr.default.createElement(V,{key:ze,active:!1,descriptor:Ae.descriptor,suggestions:Ae.suggestions}):Tr.default.createElement(bi.Text,{key:ze},"Loading..."))}):Tr.default.createElement(bi.Text,null,"No upgrades found")},me=await w4(({useSubmit:ae})=>{ae(sh());let we=new Map;for(let ve of f.workspaces)for(let ue of["dependencies","devDependencies"])for(let Ae of ve.manifest[ue].values())f.tryWorkspaceByDescriptor(Ae)===null&&(Ae.range.startsWith("link:")||we.set(Ae.descriptorHash,Ae));let he=Ro.miscUtils.sortMap(we.values(),ve=>Ro.structUtils.stringifyDescriptor(ve));return Tr.default.createElement(bi.Box,{flexDirection:"column"},Tr.default.createElement(j,null),Tr.default.createElement(q,null),Tr.default.createElement(re,{dependencies:he}))},{},{stdin:this.context.stdin,stdout:this.context.stdout,stderr:this.context.stderr});if(typeof me>"u")return 1;let De=!1;for(let ae of f.workspaces)for(let we of["dependencies","devDependencies"]){let he=ae.manifest[we];for(let ve of he.values()){let ue=me.get(ve.descriptorHash);typeof ue<"u"&&ue!==null&&(he.set(ve.identHash,Ro.structUtils.makeDescriptor(ve,ue)),De=!0)}}return De?(await Ro.StreamReport.start({configuration:l,stdout:this.context.stdout,includeLogs:!this.context.quiet},async ae=>{await f.install({cache:E,report:ae})})).exitCode():0}};ph.paths=[["upgrade-interactive"]],ph.usage=L4.Command.Usage({category:"Interactive commands",description:"open the upgrade interface",details:` + This command opens a fullscreen terminal interface where you can see any out of date packages used by your application, their status compared to the latest versions available on the remote registry, and select packages to upgrade. + `,examples:[["Open the upgrade window","yarn upgrade-interactive"]]});var Vz={commands:[dh,ph]},Gz=Vz;return qF(Yz);})(); +/* +object-assign +(c) Sindre Sorhus +@license MIT +*/ +/** + * @license + * Lodash + * Copyright OpenJS Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ +/** @license React v0.0.0-experimental-51a3aa6af + * react-debug-tools.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.0.0-experimental-51a3aa6af + * react-is.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.0.0-experimental-51a3aa6af + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.18.0 + * scheduler-tracing.development.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.18.0 + * scheduler-tracing.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.18.0 + * scheduler.development.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.18.0 + * scheduler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.24.0 + * react-reconciler.development.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v0.24.0 + * react-reconciler.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v16.13.1 + * react.development.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/** @license React v16.13.1 + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +return plugin; +} +}; diff --git a/.yarnrc.yml b/.yarnrc.yml index 6d27afaac27..c513a5dbaaa 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1,7 @@ +logFilters: + - code: YN0013 + level: discard + nodeLinker: node-modules plugins: @@ -5,8 +9,7 @@ plugins: spec: "@yarnpkg/plugin-typescript" - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs spec: "@yarnpkg/plugin-workspace-tools" + - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs + spec: "@yarnpkg/plugin-interactive-tools" yarnPath: .yarn/releases/yarn-3.6.3.cjs -logFilters: - - code: YN0013 - level: discard diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index 0a9cd7235f5..2f444ba565a 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -41,14 +41,14 @@ "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", "@web/dev-server-esbuild": "^0.3.6", - "@web/test-runner": "^0.15.3", + "@web/test-runner": "^0.18.1", "@web/test-runner-playwright": "^0.10.0", - "chai": "^4.3.7", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "chai": "^4.4.1", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "mocha": "^10.2.0", - "prettier": "3.0.3", + "prettier": "3.2.5", "ts-node": "^10.9.1", - "typescript": "^5.0.4" + "typescript": "^5.4.2" } } diff --git a/compiler/integration-tests/package.json b/compiler/integration-tests/package.json index 798b7c55312..431fe408a8b 100644 --- a/compiler/integration-tests/package.json +++ b/compiler/integration-tests/package.json @@ -20,13 +20,13 @@ "@nomicfoundation/hardhat-ethers": "^3.0.0", "@web/dev-server-esbuild": "^0.3.6", "@web/dev-server-import-maps": "^0.2.0", - "@web/test-runner": "^0.15.3", + "@web/test-runner": "^0.18.1", "@web/test-runner-playwright": "^0.10.0", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "ethers": "^6.7.1", "hardhat": "^2.17.4", - "prettier": "3.0.3", + "prettier": "3.2.5", "smol-toml": "^1.1.2", "toml": "^3.0.0", "tslog": "^4.9.2" diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index 6dfa3215483..8db3876a753 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -55,27 +55,27 @@ "@types/sinon": "^17", "@wasm-tool/wasm-pack-plugin": "^1.7.0", "@web/dev-server-esbuild": "^0.3.6", - "@web/test-runner": "^0.18.0", + "@web/test-runner": "^0.18.1", "@web/test-runner-playwright": "^0.11.0", "adm-zip": "^0.5.0", "assert": "^2.1.0", "browserify-fs": "^1.0.0", - "chai": "^4.3.10", + "chai": "^4.4.1", "copy-webpack-plugin": "^12.0.2", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "html-webpack-plugin": "^5.6.0", "memfs": "^4.6.0", "mocha": "^10.2.0", "mocha-each": "^2.0.1", "path-browserify": "^1.0.1", - "prettier": "3.0.3", + "prettier": "3.2.5", "process": "^0.11.10", "readable-stream": "^4.4.2", "sinon": "^17.0.1", "ts-loader": "^9.5.1", "ts-node": "^10.9.1", - "typescript": "~5.2.2", + "typescript": "^5.4.2", "unzipit": "^1.4.3", "url": "^0.11.3", "webpack": "^5.90.1", diff --git a/docs/package.json b/docs/package.json index 779b72149b1..661a0a0dcab 100644 --- a/docs/package.json +++ b/docs/package.json @@ -34,15 +34,15 @@ "@docusaurus/types": "^3.0.1", "@types/prettier": "^3", "docusaurus-plugin-typedoc": "1.0.0-next.18", - "eslint-plugin-prettier": "^5.0.0", - "prettier": "3.0.3", + "eslint-plugin-prettier": "^5.1.3", + "prettier": "3.2.5", "serve": "^14.2.1", "ts-node": "^10.9.1", "typedoc": "^0.25.0", "typedoc-plugin-frontmatter": "^0.0.2", "typedoc-plugin-markdown": "4.0.0-next.25", "typedoc-plugin-merge-modules": "^5.1.0", - "typescript": "~5.2.2" + "typescript": "^5.4.2" }, "browserslist": { "production": [ diff --git a/package.json b/package.json index fcf36c9b969..049c1634dd2 100644 --- a/package.json +++ b/package.json @@ -36,14 +36,14 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", - "chai": "^4.3.7", + "chai": "^4.4.1", "cspell": "^8.3.2", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "mocha": "^10.2.0", - "prettier": "3.0.3", + "prettier": "3.2.5", "ts-node": "^10.9.1", - "typescript": "^5.0.4" + "typescript": "^5.4.2" }, "packageManager": "yarn@3.6.4" } diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json index 68ee7184928..485f4252e29 100644 --- a/tooling/noir_codegen/package.json +++ b/tooling/noir_codegen/package.json @@ -51,13 +51,13 @@ "@types/mocha": "^10.0.1", "@types/node": "^20.6.2", "@types/prettier": "^3", - "chai": "^4.3.8", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "chai": "^4.4.1", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "mocha": "^10.2.0", - "prettier": "3.0.3", + "prettier": "3.2.5", "ts-node": "^10.9.1", "tsx": "^4.6.2", - "typescript": "^5.2.2" + "typescript": "^5.4.2" } } diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index 0a40d300ea4..0da6e695202 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -53,14 +53,14 @@ "@types/mocha": "^10.0.1", "@types/node": "^20.6.2", "@types/prettier": "^3", - "chai": "^4.3.8", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "chai": "^4.4.1", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "mocha": "^10.2.0", - "prettier": "3.0.3", + "prettier": "3.2.5", "ts-node": "^10.9.1", "tsc-multi": "^1.1.0", "tsx": "^4.6.2", - "typescript": "^5.2.2" + "typescript": "^5.4.2" } } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 09673d7fd4e..6223f8d2969 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -49,12 +49,12 @@ "devDependencies": { "@types/node": "^20.6.2", "@types/prettier": "^3", - "chai": "^4.3.8", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", + "chai": "^4.4.1", + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", "mocha": "^10.2.0", - "prettier": "3.0.3", + "prettier": "3.2.5", "ts-node": "^10.9.1", - "typescript": "5.1.5" + "typescript": "5.4.2" } } diff --git a/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json b/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json index 15d273af62e..ac1e3784480 100644 --- a/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json +++ b/tooling/noir_js_backend_barretenberg/tsconfig.cjs.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", + "moduleResolution": "Node", "outDir": "./lib/cjs" }, } diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index d853813303d..d7a08debb54 100644 --- a/tooling/noir_js_types/package.json +++ b/tooling/noir_js_types/package.json @@ -40,9 +40,9 @@ }, "devDependencies": { "@types/prettier": "^3", - "eslint": "^8.56.0", - "eslint-plugin-prettier": "^5.0.0", - "prettier": "3.0.3", - "typescript": "^5.2.2" + "eslint": "^8.57.0", + "eslint-plugin-prettier": "^5.1.3", + "prettier": "3.2.5", + "typescript": "^5.4.2" } } diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index 8fb9eb314a5..a6cc5503242 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -43,9 +43,9 @@ "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", "@web/dev-server-esbuild": "^0.3.6", - "@web/test-runner": "^0.15.3", + "@web/test-runner": "^0.18.1", "@web/test-runner-playwright": "^0.10.0", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "mocha": "^10.2.0" } } diff --git a/yarn.lock b/yarn.lock index dc3253de21a..ee120fbb18e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3485,10 +3485,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.56.0": - version: 8.56.0 - resolution: "@eslint/js@npm:8.56.0" - checksum: 5804130574ef810207bdf321c265437814e7a26f4e6fac9b496de3206afd52f533e09ec002a3be06cd9adcc9da63e727f1883938e663c4e4751c007d5b58e539 +"@eslint/js@npm:8.57.0": + version: 8.57.0 + resolution: "@eslint/js@npm:8.57.0" + checksum: 315dc65b0e9893e2bff139bddace7ea601ad77ed47b4550e73da8c9c2d2766c7a575c3cddf17ef85b8fd6a36ff34f91729d0dcca56e73ca887c10df91a41b0bb languageName: node linkType: hard @@ -3926,14 +3926,14 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.11.13": - version: 0.11.13 - resolution: "@humanwhocodes/config-array@npm:0.11.13" +"@humanwhocodes/config-array@npm:^0.11.14": + version: 0.11.14 + resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: - "@humanwhocodes/object-schema": ^2.0.1 - debug: ^4.1.1 + "@humanwhocodes/object-schema": ^2.0.2 + debug: ^4.3.1 minimatch: ^3.0.5 - checksum: f8ea57b0d7ed7f2d64cd3944654976829d9da91c04d9c860e18804729a33f7681f78166ef4c761850b8c324d362f7d53f14c5c44907a6b38b32c703ff85e4805 + checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 languageName: node linkType: hard @@ -3944,10 +3944,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.1": - version: 2.0.1 - resolution: "@humanwhocodes/object-schema@npm:2.0.1" - checksum: 24929487b1ed48795d2f08346a0116cc5ee4634848bce64161fb947109352c562310fd159fc64dda0e8b853307f5794605191a9547f7341158559ca3c8262a45 +"@humanwhocodes/object-schema@npm:^2.0.2": + version: 2.0.2 + resolution: "@humanwhocodes/object-schema@npm:2.0.2" + checksum: 2fc11503361b5fb4f14714c700c02a3f4c7c93e9acd6b87a29f62c522d90470f364d6161b03d1cc618b979f2ae02aed1106fd29d302695d8927e2fc8165ba8ee languageName: node linkType: hard @@ -4380,15 +4380,15 @@ __metadata: dependencies: "@esm-bundle/chai": ^4.3.4-fix.0 "@web/dev-server-esbuild": ^0.3.6 - "@web/test-runner": ^0.15.3 + "@web/test-runner": ^0.18.1 "@web/test-runner-playwright": ^0.10.0 - chai: ^4.3.7 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + chai: ^4.4.1 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 mocha: ^10.2.0 - prettier: 3.0.3 + prettier: 3.2.5 ts-node: ^10.9.1 - typescript: ^5.0.4 + typescript: ^5.4.2 languageName: unknown linkType: soft @@ -4400,14 +4400,14 @@ __metadata: "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 - chai: ^4.3.8 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + chai: ^4.4.1 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 fflate: ^0.8.0 mocha: ^10.2.0 - prettier: 3.0.3 + prettier: 3.2.5 ts-node: ^10.9.1 - typescript: 5.1.5 + typescript: 5.4.2 languageName: unknown linkType: soft @@ -4421,16 +4421,16 @@ __metadata: "@types/mocha": ^10.0.1 "@types/node": ^20.6.2 "@types/prettier": ^3 - chai: ^4.3.8 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + chai: ^4.4.1 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 glob: ^10.3.10 mocha: ^10.2.0 - prettier: 3.0.3 + prettier: 3.2.5 ts-command-line-args: ^2.5.1 ts-node: ^10.9.1 tsx: ^4.6.2 - typescript: ^5.2.2 + typescript: ^5.4.2 bin: noir-codegen: lib/main.js languageName: unknown @@ -4447,15 +4447,15 @@ __metadata: "@types/mocha": ^10.0.1 "@types/node": ^20.6.2 "@types/prettier": ^3 - chai: ^4.3.8 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + chai: ^4.4.1 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 mocha: ^10.2.0 - prettier: 3.0.3 + prettier: 3.2.5 ts-node: ^10.9.1 tsc-multi: ^1.1.0 tsx: ^4.6.2 - typescript: ^5.2.2 + typescript: ^5.4.2 languageName: unknown linkType: soft @@ -4477,28 +4477,28 @@ __metadata: "@types/sinon": ^17 "@wasm-tool/wasm-pack-plugin": ^1.7.0 "@web/dev-server-esbuild": ^0.3.6 - "@web/test-runner": ^0.18.0 + "@web/test-runner": ^0.18.1 "@web/test-runner-playwright": ^0.11.0 adm-zip: ^0.5.0 assert: ^2.1.0 browserify-fs: ^1.0.0 - chai: ^4.3.10 + chai: ^4.4.1 copy-webpack-plugin: ^12.0.2 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 html-webpack-plugin: ^5.6.0 memfs: ^4.6.0 mocha: ^10.2.0 mocha-each: ^2.0.1 pako: ^2.1.0 path-browserify: ^1.0.1 - prettier: 3.0.3 + prettier: 3.2.5 process: ^0.11.10 readable-stream: ^4.4.2 sinon: ^17.0.1 ts-loader: ^9.5.1 ts-node: ^10.9.1 - typescript: ~5.2.2 + typescript: ^5.4.2 unzipit: ^1.4.3 url: ^0.11.3 webpack: ^5.90.1 @@ -4514,9 +4514,9 @@ __metadata: "@esm-bundle/chai": ^4.3.4-fix.0 "@noir-lang/types": "workspace:*" "@web/dev-server-esbuild": ^0.3.6 - "@web/test-runner": ^0.15.3 + "@web/test-runner": ^0.18.1 "@web/test-runner-playwright": ^0.10.0 - eslint: ^8.56.0 + eslint: ^8.57.0 mocha: ^10.2.0 languageName: unknown linkType: soft @@ -4527,14 +4527,14 @@ __metadata: dependencies: "@typescript-eslint/eslint-plugin": ^6.7.3 "@typescript-eslint/parser": ^6.7.3 - chai: ^4.3.7 + chai: ^4.4.1 cspell: ^8.3.2 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 mocha: ^10.2.0 - prettier: 3.0.3 + prettier: 3.2.5 ts-node: ^10.9.1 - typescript: ^5.0.4 + typescript: ^5.4.2 languageName: unknown linkType: soft @@ -4543,10 +4543,10 @@ __metadata: resolution: "@noir-lang/types@workspace:tooling/noir_js_types" dependencies: "@types/prettier": ^3 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 - prettier: 3.0.3 - typescript: ^5.2.2 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 + prettier: 3.2.5 + typescript: ^5.4.2 languageName: unknown linkType: soft @@ -4876,17 +4876,10 @@ __metadata: languageName: node linkType: hard -"@pkgr/utils@npm:^2.4.2": - version: 2.4.2 - resolution: "@pkgr/utils@npm:2.4.2" - dependencies: - cross-spawn: ^7.0.3 - fast-glob: ^3.3.0 - is-glob: ^4.0.3 - open: ^9.1.0 - picocolors: ^1.0.0 - tslib: ^2.6.0 - checksum: 24e04c121269317d259614cd32beea3af38277151c4002df5883c4be920b8e3490bb897748e844f9d46bf68230f86dabd4e8f093773130e7e60529a769a132fc +"@pkgr/core@npm:^0.1.0": + version: 0.1.1 + resolution: "@pkgr/core@npm:0.1.1" + checksum: 6f25fd2e3008f259c77207ac9915b02f1628420403b2630c92a07ff963129238c9262afc9e84344c7a23b5cc1f3965e2cd17e3798219f5fd78a63d144d3cceba languageName: node linkType: hard @@ -4924,64 +4917,21 @@ __metadata: languageName: node linkType: hard -"@puppeteer/browsers@npm:0.5.0": - version: 0.5.0 - resolution: "@puppeteer/browsers@npm:0.5.0" - dependencies: - debug: 4.3.4 - extract-zip: 2.0.1 - https-proxy-agent: 5.0.1 - progress: 2.0.3 - proxy-from-env: 1.1.0 - tar-fs: 2.1.1 - unbzip2-stream: 1.4.3 - yargs: 17.7.1 - peerDependencies: - typescript: ">= 4.7.4" - peerDependenciesMeta: - typescript: - optional: true - bin: - browsers: lib/cjs/main-cli.js - checksum: d75fde03be4be106ca907834739251c2bb0b33a09fa23315c5dbe8b8b4cfed2f1b26af62e1dbe5fccc227e9bc87b51da0815461b982477eb01439bfdd6e7b01a - languageName: node - linkType: hard - -"@puppeteer/browsers@npm:1.4.6": - version: 1.4.6 - resolution: "@puppeteer/browsers@npm:1.4.6" +"@puppeteer/browsers@npm:2.1.0": + version: 2.1.0 + resolution: "@puppeteer/browsers@npm:2.1.0" dependencies: debug: 4.3.4 extract-zip: 2.0.1 progress: 2.0.3 - proxy-agent: 6.3.0 - tar-fs: 3.0.4 + proxy-agent: 6.4.0 + semver: 7.6.0 + tar-fs: 3.0.5 unbzip2-stream: 1.4.3 - yargs: 17.7.1 - peerDependencies: - typescript: ">= 4.7.4" - peerDependenciesMeta: - typescript: - optional: true + yargs: 17.7.2 bin: browsers: lib/cjs/main-cli.js - checksum: 29569dd8a8a41737bb0dd40cce6279cfc8764afc6242d2f9d8ae610bed7e466fc77eeb27b9b3ac53dd04927a1a0e26389f282f6ba057210979b36ab455009d64 - languageName: node - linkType: hard - -"@rollup/plugin-node-resolve@npm:^13.0.4": - version: 13.3.0 - resolution: "@rollup/plugin-node-resolve@npm:13.3.0" - dependencies: - "@rollup/pluginutils": ^3.1.0 - "@types/resolve": 1.17.1 - deepmerge: ^4.2.2 - is-builtin-module: ^3.1.0 - is-module: ^1.0.0 - resolve: ^1.19.0 - peerDependencies: - rollup: ^2.42.0 - checksum: ec5418e6b3c23a9e30683056b3010e9d325316dcfae93fbc673ae64dad8e56a2ce761c15c48f5e2dcfe0c822fdc4a4905ee6346e3dcf90603ba2260afef5a5e6 + checksum: 318740056fc716cf26179f053eb47e119bc01658f59382a19fb7d39e5b9232a7ad7d82e33445e0519683c13e22b328193fc9952c99d09cdc09f6539391d4749c languageName: node linkType: hard @@ -5004,19 +4954,6 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^3.1.0": - version: 3.1.0 - resolution: "@rollup/pluginutils@npm:3.1.0" - dependencies: - "@types/estree": 0.0.39 - estree-walker: ^1.0.1 - picomatch: ^2.2.2 - peerDependencies: - rollup: ^1.20.0||^2.0.0 - checksum: 8be16e27863c219edbb25a4e6ec2fe0e1e451d9e917b6a43cf2ae5bc025a6b8faaa40f82a6e53b66d0de37b58ff472c6c3d57a83037ae635041f8df959d6d9aa - languageName: node - linkType: hard - "@rollup/pluginutils@npm:^5.0.1": version: 5.1.0 resolution: "@rollup/pluginutils@npm:5.1.0" @@ -5785,13 +5722,6 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:0.0.39": - version: 0.0.39 - resolution: "@types/estree@npm:0.0.39" - checksum: 412fb5b9868f2c418126451821833414189b75cc6bf84361156feed733e3d92ec220b9d74a89e52722e03d5e241b2932732711b7497374a404fad49087adc248 - languageName: node - linkType: hard - "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": version: 4.17.41 resolution: "@types/express-serve-static-core@npm:4.17.41" @@ -6027,13 +5957,6 @@ __metadata: languageName: node linkType: hard -"@types/mocha@npm:^8.2.0": - version: 8.2.3 - resolution: "@types/mocha@npm:8.2.3" - checksum: b43ed1b642a2ee62bf10792a07d5d21d66ab8b4d2cf5d822c8a7643e77b90009aecc000eefab5f6ddc9eb69004192f84119a6f97a8499e1a13ea082e7a5e71bf - languageName: node - linkType: hard - "@types/ms@npm:*": version: 0.7.34 resolution: "@types/ms@npm:0.7.34" @@ -6226,15 +6149,6 @@ __metadata: languageName: node linkType: hard -"@types/resolve@npm:1.17.1": - version: 1.17.1 - resolution: "@types/resolve@npm:1.17.1" - dependencies: - "@types/node": "*" - checksum: dc6a6df507656004e242dcb02c784479deca516d5f4b58a1707e708022b269ae147e1da0521f3e8ad0d63638869d87e0adc023f0bd5454aa6f72ac66c7525cf5 - languageName: node - linkType: hard - "@types/resolve@npm:1.20.2": version: 1.20.2 resolution: "@types/resolve@npm:1.20.2" @@ -6550,15 +6464,6 @@ __metadata: languageName: node linkType: hard -"@web/browser-logs@npm:^0.2.6": - version: 0.2.6 - resolution: "@web/browser-logs@npm:0.2.6" - dependencies: - errorstacks: ^2.2.0 - checksum: 82693e37a7e5a3c3df255e1e4feef6e6c2f2b7f5f883e1a9fd233d09a22c4f3e9e3dfd2ec809d7a02f0894156f26b89f1759bf4e9317640ee3630e9a3d9ec2a8 - languageName: node - linkType: hard - "@web/browser-logs@npm:^0.3.4": version: 0.3.4 resolution: "@web/browser-logs@npm:0.3.4" @@ -6577,15 +6482,6 @@ __metadata: languageName: node linkType: hard -"@web/config-loader@npm:^0.1.3": - version: 0.1.3 - resolution: "@web/config-loader@npm:0.1.3" - dependencies: - semver: ^7.3.4 - checksum: 278554bd00b757eaf296ba904a224c61d4698df1a5d6c04931c40bc6bb308e81e767055cbf283b763cc530aae6b200bb950aa19eb41aa8979a3a2b29e5f0ac7a - languageName: node - linkType: hard - "@web/config-loader@npm:^0.3.0": version: 0.3.1 resolution: "@web/config-loader@npm:0.3.1" @@ -6698,20 +6594,6 @@ __metadata: languageName: node linkType: hard -"@web/dev-server-rollup@npm:^0.4.1": - version: 0.4.1 - resolution: "@web/dev-server-rollup@npm:0.4.1" - dependencies: - "@rollup/plugin-node-resolve": ^13.0.4 - "@web/dev-server-core": ^0.4.1 - nanocolors: ^0.2.1 - parse5: ^6.0.1 - rollup: ^2.67.0 - whatwg-url: ^11.0.0 - checksum: a0c3566f67b5a5ead3822431302ddcaa9d043b18fdcf1190056a4e0539e5d5b545ebfecaf6021412eb4b5b6e074c2b1eff35c71e859195623c7c07e065f9df58 - languageName: node - linkType: hard - "@web/dev-server-rollup@npm:^0.6.1": version: 0.6.1 resolution: "@web/dev-server-rollup@npm:0.6.1" @@ -6726,31 +6608,6 @@ __metadata: languageName: node linkType: hard -"@web/dev-server@npm:^0.1.38": - version: 0.1.38 - resolution: "@web/dev-server@npm:0.1.38" - dependencies: - "@babel/code-frame": ^7.12.11 - "@types/command-line-args": ^5.0.0 - "@web/config-loader": ^0.1.3 - "@web/dev-server-core": ^0.4.1 - "@web/dev-server-rollup": ^0.4.1 - camelcase: ^6.2.0 - command-line-args: ^5.1.1 - command-line-usage: ^7.0.1 - debounce: ^1.2.0 - deepmerge: ^4.2.2 - ip: ^1.1.5 - nanocolors: ^0.2.1 - open: ^8.0.2 - portfinder: ^1.0.32 - bin: - wds: dist/bin.js - web-dev-server: dist/bin.js - checksum: eeaf34f8744f58cfb9493155ad8548a87cae4e445a2fa894610b070f66cb303614d247bb609e378b9df342935ad980a259630317c444d19f9796abfcfb20bb13 - languageName: node - linkType: hard - "@web/dev-server@npm:^0.4.0": version: 0.4.1 resolution: "@web/dev-server@npm:0.4.1" @@ -6796,38 +6653,16 @@ __metadata: languageName: node linkType: hard -"@web/test-runner-chrome@npm:^0.12.1": - version: 0.12.1 - resolution: "@web/test-runner-chrome@npm:0.12.1" - dependencies: - "@web/test-runner-core": ^0.10.29 - "@web/test-runner-coverage-v8": ^0.5.0 - chrome-launcher: ^0.15.0 - puppeteer-core: ^19.8.1 - checksum: 08964e4c22c286231a6bdc003393316a7e0c7878560a9394d8bb9212c3c2e37799bc0aaf4732921213647c3dc89169146f65746d381feaa9c281c0373bf9da59 - languageName: node - linkType: hard - -"@web/test-runner-chrome@npm:^0.15.0": - version: 0.15.0 - resolution: "@web/test-runner-chrome@npm:0.15.0" +"@web/test-runner-chrome@npm:^0.16.0": + version: 0.16.0 + resolution: "@web/test-runner-chrome@npm:0.16.0" dependencies: "@web/test-runner-core": ^0.13.0 "@web/test-runner-coverage-v8": ^0.8.0 async-mutex: 0.4.0 chrome-launcher: ^0.15.0 - puppeteer-core: ^20.0.0 - checksum: 091aa83707aa1a6ade8074c37050f9a0fae2729f223b5e7d756f86ccdadcd85e738cc47d0a4ae8ac6ea930142cc20e341f5d3ad30a3a81d6666b353a7e8c2dd4 - languageName: node - linkType: hard - -"@web/test-runner-commands@npm:^0.6.6": - version: 0.6.6 - resolution: "@web/test-runner-commands@npm:0.6.6" - dependencies: - "@web/test-runner-core": ^0.10.29 - mkdirp: ^1.0.4 - checksum: b25533edd9ec59aeec28756a52ae4c6730388c336fa94e0c21eedf208012efd9aedf96c47ebad9a98cce9d87c4ee539b318a571e1e2bfb1bf8e5f1f6889c98e4 + puppeteer-core: ^22.0.0 + checksum: 99cfa93d12e8854fb2d104de1f8c5a73403e39fc56e5ed78e24f63e903299521750c7b475253dfb75293a2e604ed83d5cfdc1e593a72e9208a99a511bdb6169a languageName: node linkType: hard @@ -6841,40 +6676,6 @@ __metadata: languageName: node linkType: hard -"@web/test-runner-core@npm:^0.10.20, @web/test-runner-core@npm:^0.10.29": - version: 0.10.29 - resolution: "@web/test-runner-core@npm:0.10.29" - dependencies: - "@babel/code-frame": ^7.12.11 - "@types/babel__code-frame": ^7.0.2 - "@types/co-body": ^6.1.0 - "@types/convert-source-map": ^2.0.0 - "@types/debounce": ^1.2.0 - "@types/istanbul-lib-coverage": ^2.0.3 - "@types/istanbul-reports": ^3.0.0 - "@web/browser-logs": ^0.2.6 - "@web/dev-server-core": ^0.4.1 - chokidar: ^3.4.3 - cli-cursor: ^3.1.0 - co-body: ^6.1.0 - convert-source-map: ^2.0.0 - debounce: ^1.2.0 - dependency-graph: ^0.11.0 - globby: ^11.0.1 - ip: ^1.1.5 - istanbul-lib-coverage: ^3.0.0 - istanbul-lib-report: ^3.0.0 - istanbul-reports: ^3.0.2 - log-update: ^4.0.0 - nanocolors: ^0.2.1 - nanoid: ^3.1.25 - open: ^8.0.2 - picomatch: ^2.2.2 - source-map: ^0.7.3 - checksum: 635a510442bea3bce97596a2aed1c58a6154b4b83a44bf3e9c9497a751f42426cae5f67555916c4fd63064a4e91a5e26755e3090887ebac38ec0ab2691e1fe6c - languageName: node - linkType: hard - "@web/test-runner-core@npm:^0.12.0": version: 0.12.0 resolution: "@web/test-runner-core@npm:0.12.0" @@ -6943,18 +6744,6 @@ __metadata: languageName: node linkType: hard -"@web/test-runner-coverage-v8@npm:^0.5.0": - version: 0.5.0 - resolution: "@web/test-runner-coverage-v8@npm:0.5.0" - dependencies: - "@web/test-runner-core": ^0.10.20 - istanbul-lib-coverage: ^3.0.0 - picomatch: ^2.2.2 - v8-to-istanbul: ^9.0.1 - checksum: e69dc6379cff24f28bd21cc37a661945fbf7f3fd532da813e74f4042efe17fc191cdb7c09f1e1ea276167952b0116478ba0fe7af0966fa4867278c3a2cd772df - languageName: node - linkType: hard - "@web/test-runner-coverage-v8@npm:^0.7.3": version: 0.7.3 resolution: "@web/test-runner-coverage-v8@npm:0.7.3" @@ -6981,16 +6770,6 @@ __metadata: languageName: node linkType: hard -"@web/test-runner-mocha@npm:^0.7.5": - version: 0.7.5 - resolution: "@web/test-runner-mocha@npm:0.7.5" - dependencies: - "@types/mocha": ^8.2.0 - "@web/test-runner-core": ^0.10.20 - checksum: 12f87299945d230815bb783de2953ac4239306c1a67145ef5b78cfb0b361ae7f659e5d3e5150af2cedc6f2c55adf10652b761f016430a7ac2d7f77b91ecb9cd1 - languageName: node - linkType: hard - "@web/test-runner-mocha@npm:^0.9.0": version: 0.9.0 resolution: "@web/test-runner-mocha@npm:0.9.0" @@ -7022,41 +6801,14 @@ __metadata: languageName: node linkType: hard -"@web/test-runner@npm:^0.15.3": - version: 0.15.3 - resolution: "@web/test-runner@npm:0.15.3" - dependencies: - "@web/browser-logs": ^0.2.6 - "@web/config-loader": ^0.1.3 - "@web/dev-server": ^0.1.38 - "@web/test-runner-chrome": ^0.12.1 - "@web/test-runner-commands": ^0.6.6 - "@web/test-runner-core": ^0.10.29 - "@web/test-runner-mocha": ^0.7.5 - camelcase: ^6.2.0 - command-line-args: ^5.1.1 - command-line-usage: ^7.0.1 - convert-source-map: ^2.0.0 - diff: ^5.0.0 - globby: ^11.0.1 - nanocolors: ^0.2.1 - portfinder: ^1.0.32 - source-map: ^0.7.3 - bin: - web-test-runner: dist/bin.js - wtr: dist/bin.js - checksum: 75d00d4f15f9977ff4e8fca84e1c7f9d834073688df06d9e4b62bf43cad65a36b2ae21b9ebab5706e4d3b07fc639bb90758b1be1df036c8b80137ec3407a8f08 - languageName: node - linkType: hard - -"@web/test-runner@npm:^0.18.0": - version: 0.18.0 - resolution: "@web/test-runner@npm:0.18.0" +"@web/test-runner@npm:^0.18.1": + version: 0.18.1 + resolution: "@web/test-runner@npm:0.18.1" dependencies: "@web/browser-logs": ^0.4.0 "@web/config-loader": ^0.3.0 "@web/dev-server": ^0.4.0 - "@web/test-runner-chrome": ^0.15.0 + "@web/test-runner-chrome": ^0.16.0 "@web/test-runner-commands": ^0.9.0 "@web/test-runner-core": ^0.13.0 "@web/test-runner-mocha": ^0.9.0 @@ -7072,7 +6824,7 @@ __metadata: bin: web-test-runner: dist/bin.js wtr: dist/bin.js - checksum: d5e410f08cb954f9854a3d837f5f704b578376ee8b0452cff66aeca2eb3cb98e50556ca3b958bda567b42af2ef2cd0a7424eaea40f9b3e80362ae788fbd33118 + checksum: eb21fc5978da2ccbfd6ffac0f61b519036d68a9cdcf211c1dbafa9c6b6f92bb10a9267dec02f4d71dfc45e636166d98712ff9a347af951eabff99b0d7e5899b5 languageName: node linkType: hard @@ -7958,6 +7710,41 @@ __metadata: languageName: node linkType: hard +"bare-events@npm:^2.0.0, bare-events@npm:^2.2.0": + version: 2.2.1 + resolution: "bare-events@npm:2.2.1" + checksum: f4f830fe780b105fce189180761cf69ac60848212133ca7d29eb94f8888f813bf70f339e4e651b200aa8304fc9dc77ca7443756cc68b43294367b5867ad4536b + languageName: node + linkType: hard + +"bare-fs@npm:^2.1.1": + version: 2.2.2 + resolution: "bare-fs@npm:2.2.2" + dependencies: + bare-events: ^2.0.0 + bare-os: ^2.0.0 + bare-path: ^2.0.0 + streamx: ^2.13.0 + checksum: 5b6d26690ee4de93b559f6a1187b6ff553224fe4faea5ef9cbd235b13e033ef96a598dc28eb10aad17d1f35baed24e14e18436534041913f905a0c50ed27713a + languageName: node + linkType: hard + +"bare-os@npm:^2.0.0, bare-os@npm:^2.1.0": + version: 2.2.1 + resolution: "bare-os@npm:2.2.1" + checksum: 7d870d8955531809253dfbceeda5b68e8396ef640166f8ff6c4c5e344f18a6bc9253f6d5e7d9ae2841426b66e9b7b1a39b2a102e6b23e1ddff26ad8a8981af81 + languageName: node + linkType: hard + +"bare-path@npm:^2.0.0, bare-path@npm:^2.1.0": + version: 2.1.0 + resolution: "bare-path@npm:2.1.0" + dependencies: + bare-os: ^2.1.0 + checksum: 03f260e72bd0ae0df4cd712322a2d3c8c16701ffaa55cf2d517ae62b7f78c64b7ec5bba81ec579367f966472481f5160db282e6663bd0fc8cfb09ebe272d8bba + languageName: node + linkType: hard + "base-x@npm:^3.0.2": version: 3.0.9 resolution: "base-x@npm:3.0.9" @@ -7995,13 +7782,6 @@ __metadata: languageName: node linkType: hard -"big-integer@npm:^1.6.44": - version: 1.6.52 - resolution: "big-integer@npm:1.6.52" - checksum: 6e86885787a20fed96521958ae9086960e4e4b5e74d04f3ef7513d4d0ad631a9f3bde2730fc8aaa4b00419fc865f6ec573e5320234531ef37505da7da192c40b - languageName: node - linkType: hard - "big.js@npm:^5.2.2": version: 5.2.2 resolution: "big.js@npm:5.2.2" @@ -8023,17 +7803,6 @@ __metadata: languageName: node linkType: hard -"bl@npm:^4.0.3": - version: 4.1.0 - resolution: "bl@npm:4.1.0" - dependencies: - buffer: ^5.5.0 - inherits: ^2.0.4 - readable-stream: ^3.4.0 - checksum: 9e8521fa7e83aa9427c6f8ccdcba6e8167ef30cc9a22df26effcc5ab682ef91d2cbc23a239f945d099289e4bbcfae7a192e9c28c84c6202e710a0dfec3722662 - languageName: node - linkType: hard - "bl@npm:~0.8.1": version: 0.8.2 resolution: "bl@npm:0.8.2" @@ -8177,15 +7946,6 @@ __metadata: languageName: node linkType: hard -"bplist-parser@npm:^0.2.0": - version: 0.2.0 - resolution: "bplist-parser@npm:0.2.0" - dependencies: - big-integer: ^1.6.44 - checksum: d5339dd16afc51de6c88f88f58a45b72ed6a06aa31f5557d09877575f220b7c1d3fbe375da0b62e6a10d4b8ed80523567e351f24014f5bc886ad523758142cdd - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -8320,7 +8080,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.2.1, buffer@npm:^5.5.0": +"buffer@npm:^5.2.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -8347,15 +8107,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^3.0.0": - version: 3.0.0 - resolution: "bundle-name@npm:3.0.0" - dependencies: - run-applescript: ^5.0.0 - checksum: edf2b1fbe6096ed32e7566947ace2ea937ee427391744d7510a2880c4b9a5b3543d3f6c551236a29e5c87d3195f8e2912516290e638c15bcbede7b37cc375615 - languageName: node - linkType: hard - "bundle-name@npm:^4.1.0": version: 4.1.0 resolution: "bundle-name@npm:4.1.0" @@ -8553,9 +8304,9 @@ __metadata: languageName: node linkType: hard -"chai@npm:^4.3.10, chai@npm:^4.3.7, chai@npm:^4.3.8": - version: 4.3.10 - resolution: "chai@npm:4.3.10" +"chai@npm:^4.4.1": + version: 4.4.1 + resolution: "chai@npm:4.4.1" dependencies: assertion-error: ^1.1.0 check-error: ^1.0.3 @@ -8564,7 +8315,7 @@ __metadata: loupe: ^2.3.6 pathval: ^1.1.1 type-detect: ^4.0.8 - checksum: 536668c60a0d985a0fbd94418028e388d243a925d7c5e858c7443e334753511614a3b6a124bac9ca077dfc4c37acc367d62f8c294960f440749536dc181dfc6d + checksum: 9ab84f36eb8e0b280c56c6c21ca4da5933132cd8a0c89c384f1497f77953640db0bc151edd47f81748240a9fab57b78f7d925edfeedc8e8fc98016d71f40c36e languageName: node linkType: hard @@ -8753,13 +8504,6 @@ __metadata: languageName: node linkType: hard -"chownr@npm:^1.1.1": - version: 1.1.4 - resolution: "chownr@npm:1.1.4" - checksum: 115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d - languageName: node - linkType: hard - "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -8788,25 +8532,15 @@ __metadata: languageName: node linkType: hard -"chromium-bidi@npm:0.4.16": - version: 0.4.16 - resolution: "chromium-bidi@npm:0.4.16" - dependencies: - mitt: 3.0.0 - peerDependencies: - devtools-protocol: "*" - checksum: 9cbb362fdf589dbdfd1618499c5bbdac45a3aa1291c1d2faa2f1ea3768738677985175d1bb1511dfe3e188bc78e6ea2acb453564ece7e09f535bbcd2253ce06a - languageName: node - linkType: hard - -"chromium-bidi@npm:0.4.7": - version: 0.4.7 - resolution: "chromium-bidi@npm:0.4.7" +"chromium-bidi@npm:0.5.12": + version: 0.5.12 + resolution: "chromium-bidi@npm:0.5.12" dependencies: - mitt: 3.0.0 + mitt: 3.0.1 + urlpattern-polyfill: 10.0.0 peerDependencies: devtools-protocol: "*" - checksum: eec7581e2eddd2c95014c6edc5aae0b036c79bbeadee05166436b16139b6932c902c5ce21d95ed919a592f58d3a47c5469dc5f3de2a300700b2748ab119ad65e + checksum: c14aedc9725a813d82ce66423750757383af6d7580b4f22f8756d31a340b8d6d77dd30a4de0e213f2f89e63faf54a333431acc07675791f1c176d2275e499525 languageName: node linkType: hard @@ -9543,15 +9277,6 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:3.1.5": - version: 3.1.5 - resolution: "cross-fetch@npm:3.1.5" - dependencies: - node-fetch: 2.6.7 - checksum: f6b8c6ee3ef993ace6277fd789c71b6acf1b504fd5f5c7128df4ef2f125a429e29cd62dc8c127523f04a5f2fa4771ed80e3f3d9695617f441425045f505cf3bb - languageName: node - linkType: hard - "cross-fetch@npm:4.0.0": version: 4.0.0 resolution: "cross-fetch@npm:4.0.0" @@ -9947,7 +9672,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.2.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -10039,16 +9764,6 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^3.0.0": - version: 3.0.0 - resolution: "default-browser-id@npm:3.0.0" - dependencies: - bplist-parser: ^0.2.0 - untildify: ^4.0.0 - checksum: 279c7ad492542e5556336b6c254a4eaf31b2c63a5433265655ae6e47301197b6cfb15c595a6fdc6463b2ff8e1a1a1ed3cba56038a60e1527ba4ab1628c6b9941 - languageName: node - linkType: hard - "default-browser-id@npm:^5.0.0": version: 5.0.0 resolution: "default-browser-id@npm:5.0.0" @@ -10056,18 +9771,6 @@ __metadata: languageName: node linkType: hard -"default-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "default-browser@npm:4.0.0" - dependencies: - bundle-name: ^3.0.0 - default-browser-id: ^3.0.0 - execa: ^7.1.1 - titleize: ^3.0.0 - checksum: 40c5af984799042b140300be5639c9742599bda76dc9eba5ac9ad5943c83dd36cebc4471eafcfddf8e0ec817166d5ba89d56f08e66a126c7c7908a179cead1a7 - languageName: node - linkType: hard - "default-browser@npm:^5.2.1": version: 5.2.1 resolution: "default-browser@npm:5.2.1" @@ -10273,17 +9976,10 @@ __metadata: languageName: node linkType: hard -"devtools-protocol@npm:0.0.1107588": - version: 0.0.1107588 - resolution: "devtools-protocol@npm:0.0.1107588" - checksum: 9064fd643f39ae0adabb8f425b746899ff24371d89a5047d38752653259e6afcb6bcb2d9759ff727eb5885cfc0f9ba8eb384850a2af00694135622e88080e3e5 - languageName: node - linkType: hard - -"devtools-protocol@npm:0.0.1147663": - version: 0.0.1147663 - resolution: "devtools-protocol@npm:0.0.1147663" - checksum: 0631f2b6c6cd7f56e7d62a85bfc291f7e167f0f2de90969ef61fb24e2bd546b2e9530043d2bc3fe6c4d7a9e00473004272d2c2832a10a05e4b75c03a22f549fc +"devtools-protocol@npm:0.0.1249869": + version: 0.0.1249869 + resolution: "devtools-protocol@npm:0.0.1249869" + checksum: 549dda02f6d778741930e5abdde3f8e5d39e16fdbe8af38597a974e9c239798dd464250bd7ab4360d77e3d059620e95be7cf05150142473bf9b661ed28a616ed languageName: node linkType: hard @@ -10351,9 +10047,9 @@ __metadata: axios: ^1.4.0 clsx: ^1.2.1 docusaurus-plugin-typedoc: 1.0.0-next.18 - eslint-plugin-prettier: ^5.0.0 + eslint-plugin-prettier: ^5.1.3 hast-util-is-element: ^1.1.0 - prettier: 3.0.3 + prettier: 3.2.5 prism-react-renderer: ^2.1.0 react: ^18.2.0 react-dom: ^18.2.0 @@ -10366,7 +10062,7 @@ __metadata: typedoc-plugin-frontmatter: ^0.0.2 typedoc-plugin-markdown: 4.0.0-next.25 typedoc-plugin-merge-modules: ^5.1.0 - typescript: ~5.2.2 + typescript: ^5.4.2 languageName: unknown linkType: soft @@ -10604,7 +10300,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": +"end-of-stream@npm:^1.1.0": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -10925,22 +10621,23 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-prettier@npm:^5.0.0": - version: 5.0.1 - resolution: "eslint-plugin-prettier@npm:5.0.1" +"eslint-plugin-prettier@npm:^5.1.3": + version: 5.1.3 + resolution: "eslint-plugin-prettier@npm:5.1.3" dependencies: prettier-linter-helpers: ^1.0.0 - synckit: ^0.8.5 + synckit: ^0.8.6 peerDependencies: "@types/eslint": ">=8.0.0" eslint: ">=8.0.0" + eslint-config-prettier: "*" prettier: ">=3.0.0" peerDependenciesMeta: "@types/eslint": optional: true eslint-config-prettier: optional: true - checksum: c2261033b97bafe99ccb7cc47c2fac6fa85b8bbc8b128042e52631f906b69e12afed2cdd9d7e3021cc892ee8dd4204a3574e1f32a0b718b4bb3b440944b6983b + checksum: eb2a7d46a1887e1b93788ee8f8eb81e0b6b2a6f5a66a62bc6f375b033fc4e7ca16448da99380be800042786e76cf5c0df9c87a51a2c9b960ed47acbd7c0b9381 languageName: node linkType: hard @@ -10971,15 +10668,15 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.56.0": - version: 8.56.0 - resolution: "eslint@npm:8.56.0" +"eslint@npm:^8.57.0": + version: 8.57.0 + resolution: "eslint@npm:8.57.0" dependencies: "@eslint-community/eslint-utils": ^4.2.0 "@eslint-community/regexpp": ^4.6.1 "@eslint/eslintrc": ^2.1.4 - "@eslint/js": 8.56.0 - "@humanwhocodes/config-array": ^0.11.13 + "@eslint/js": 8.57.0 + "@humanwhocodes/config-array": ^0.11.14 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 "@ungap/structured-clone": ^1.2.0 @@ -11015,7 +10712,7 @@ __metadata: text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 883436d1e809b4a25d9eb03d42f584b84c408dbac28b0019f6ea07b5177940bf3cca86208f749a6a1e0039b63e085ee47aca1236c30721e91f0deef5cc5a5136 + checksum: 3a48d7ff85ab420a8447e9810d8087aea5b1df9ef68c9151732b478de698389ee656fd895635b5f2871c89ee5a2652b3f343d11e9db6f8486880374ebc74a2d9 languageName: node linkType: hard @@ -11131,13 +10828,6 @@ __metadata: languageName: node linkType: hard -"estree-walker@npm:^1.0.1": - version: 1.0.1 - resolution: "estree-walker@npm:1.0.1" - checksum: 7e70da539691f6db03a08e7ce94f394ce2eef4180e136d251af299d41f92fb2d28ebcd9a6e393e3728d7970aeb5358705ddf7209d52fbcb2dd4693f95dcf925f - languageName: node - linkType: hard - "estree-walker@npm:^2.0.2": version: 2.0.2 resolution: "estree-walker@npm:2.0.2" @@ -11357,23 +11047,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:^7.1.1": - version: 7.2.0 - resolution: "execa@npm:7.2.0" - dependencies: - cross-spawn: ^7.0.3 - get-stream: ^6.0.1 - human-signals: ^4.3.0 - is-stream: ^3.0.0 - merge-stream: ^2.0.0 - npm-run-path: ^5.1.0 - onetime: ^6.0.0 - signal-exit: ^3.0.7 - strip-final-newline: ^3.0.0 - checksum: 14fd17ba0ca8c87b277584d93b1d9fc24f2a65e5152b31d5eb159a3b814854283eaae5f51efa9525e304447e2f757c691877f7adff8fde5746aae67eb1edd1cc - languageName: node - linkType: hard - "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -11887,13 +11560,6 @@ __metadata: languageName: node linkType: hard -"fs-constants@npm:^1.0.0": - version: 1.0.0 - resolution: "fs-constants@npm:1.0.0" - checksum: 18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d - languageName: node - linkType: hard - "fs-extra@npm:^0.30.0": version: 0.30.0 resolution: "fs-extra@npm:0.30.0" @@ -13130,6 +12796,16 @@ __metadata: languageName: node linkType: hard +"http-proxy-agent@npm:^7.0.1": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: ^7.1.0 + debug: ^4.3.4 + checksum: 670858c8f8f3146db5889e1fa117630910101db601fff7d5a8aa637da0abedf68c899f03d3451cac2f83bcc4c3d2dabf339b3aa00ff8080571cceb02c3ce02f3 + languageName: node + linkType: hard + "http-proxy-middleware@npm:^2.0.3": version: 2.0.6 resolution: "http-proxy-middleware@npm:2.0.6" @@ -13169,7 +12845,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5.0.1, https-proxy-agent@npm:^5.0.0": +"https-proxy-agent@npm:^5.0.0": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" dependencies: @@ -13179,7 +12855,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "https-proxy-agent@npm:7.0.2" dependencies: @@ -13189,6 +12865,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^7.0.3": + version: 7.0.4 + resolution: "https-proxy-agent@npm:7.0.4" + dependencies: + agent-base: ^7.0.2 + debug: 4 + checksum: daaab857a967a2519ddc724f91edbbd388d766ff141b9025b629f92b9408fc83cee8a27e11a907aede392938e9c398e240d643e178408a59e4073539cde8cfe9 + languageName: node + linkType: hard + "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -13196,13 +12882,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^4.3.0": - version: 4.3.1 - resolution: "human-signals@npm:4.3.1" - checksum: 6f12958df3f21b6fdaf02d90896c271df00636a31e2bbea05bddf817a35c66b38a6fdac5863e2df85bd52f34958997f1f50350ff97249e1dff8452865d5235d1 - languageName: node - linkType: hard - "hyperdyperid@npm:^1.2.0": version: 1.2.0 resolution: "hyperdyperid@npm:1.2.0" @@ -13438,13 +13117,13 @@ __metadata: "@nomicfoundation/hardhat-ethers": ^3.0.0 "@web/dev-server-esbuild": ^0.3.6 "@web/dev-server-import-maps": ^0.2.0 - "@web/test-runner": ^0.15.3 + "@web/test-runner": ^0.18.1 "@web/test-runner-playwright": ^0.10.0 - eslint: ^8.56.0 - eslint-plugin-prettier: ^5.0.0 + eslint: ^8.57.0 + eslint-plugin-prettier: ^5.1.3 ethers: ^6.7.1 hardhat: ^2.17.4 - prettier: 3.0.3 + prettier: 3.2.5 smol-toml: ^1.1.2 toml: ^3.0.0 tslog: ^4.9.2 @@ -13578,7 +13257,7 @@ __metadata: languageName: node linkType: hard -"is-builtin-module@npm:^3.1.0, is-builtin-module@npm:^3.2.1": +"is-builtin-module@npm:^3.2.1": version: 3.2.1 resolution: "is-builtin-module@npm:3.2.1" dependencies: @@ -13899,13 +13578,6 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^3.0.0": - version: 3.0.0 - resolution: "is-stream@npm:3.0.0" - checksum: 172093fe99119ffd07611ab6d1bcccfe8bc4aa80d864b15f43e63e54b7abc71e779acd69afdb854c4e2a67fdc16ae710e370eda40088d1cfc956a50ed82d8f16 - languageName: node - linkType: hard - "is-typed-array@npm:^1.1.3": version: 1.1.12 resolution: "is-typed-array@npm:1.1.12" @@ -16034,13 +15706,6 @@ __metadata: languageName: node linkType: hard -"mimic-fn@npm:^4.0.0": - version: 4.0.0 - resolution: "mimic-fn@npm:4.0.0" - checksum: 995dcece15ee29aa16e188de6633d43a3db4611bcf93620e7e62109ec41c79c0f34277165b8ce5e361205049766e371851264c21ac64ca35499acb5421c2ba56 - languageName: node - linkType: hard - "mimic-response@npm:^1.0.0, mimic-response@npm:^1.0.1": version: 1.0.1 resolution: "mimic-response@npm:1.0.1" @@ -16205,17 +15870,10 @@ __metadata: languageName: node linkType: hard -"mitt@npm:3.0.0": - version: 3.0.0 - resolution: "mitt@npm:3.0.0" - checksum: f7be5049d27d18b1dbe9408452d66376fa60ae4a79fe9319869d1b90ae8cbaedadc7e9dab30b32d781411256d468be5538996bb7368941c09009ef6bbfa6bfc7 - languageName: node - linkType: hard - -"mkdirp-classic@npm:^0.5.2": - version: 0.5.3 - resolution: "mkdirp-classic@npm:0.5.3" - checksum: 3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac +"mitt@npm:3.0.1": + version: 3.0.1 + resolution: "mitt@npm:3.0.1" + checksum: b55a489ac9c2949ab166b7f060601d3b6d893a852515ae9eca4e11df01c013876df777ea109317622b5c1c60e8aae252558e33c8c94e14124db38f64a39614b1 languageName: node linkType: hard @@ -16449,20 +16107,6 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.6.7": - version: 2.6.7 - resolution: "node-fetch@npm:2.6.7" - dependencies: - whatwg-url: ^5.0.0 - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 8d816ffd1ee22cab8301c7756ef04f3437f18dace86a1dae22cf81db8ef29c0bf6655f3215cb0cdb22b420b6fe141e64b26905e7f33f9377a7fa59135ea3e10b - languageName: node - linkType: hard - "node-fetch@npm:^2.6.12": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" @@ -16577,15 +16221,6 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^5.1.0": - version: 5.1.0 - resolution: "npm-run-path@npm:5.1.0" - dependencies: - path-key: ^4.0.0 - checksum: dc184eb5ec239d6a2b990b43236845332ef12f4e0beaa9701de724aa797fe40b6bbd0157fb7639d24d3ab13f5d5cf22d223a19c6300846b8126f335f788bee66 - languageName: node - linkType: hard - "nprogress@npm:^0.2.0": version: 0.2.0 resolution: "nprogress@npm:0.2.0" @@ -16718,15 +16353,6 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^6.0.0": - version: 6.0.0 - resolution: "onetime@npm:6.0.0" - dependencies: - mimic-fn: ^4.0.0 - checksum: 0846ce78e440841335d4e9182ef69d5762e9f38aa7499b19f42ea1c4cd40f0b4446094c455c713f9adac3f4ae86f613bb5e30c99e52652764d06a89f709b3788 - languageName: node - linkType: hard - "only@npm:~0.0.2": version: 0.0.2 resolution: "only@npm:0.0.2" @@ -16757,18 +16383,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^9.1.0": - version: 9.1.0 - resolution: "open@npm:9.1.0" - dependencies: - default-browser: ^4.0.0 - define-lazy-prop: ^3.0.0 - is-inside-container: ^1.0.0 - is-wsl: ^2.2.0 - checksum: 3993c0f61d51fed8ac290e99c9c3cf45d3b6cfb3e2aa2b74cafd312c3486c22fd81df16ac8f3ab91dd8a4e3e729a16fc2480cfc406c4833416cf908acf1ae7c9 - languageName: node - linkType: hard - "opener@npm:^1.5.2": version: 1.5.2 resolution: "opener@npm:1.5.2" @@ -16954,7 +16568,7 @@ __metadata: languageName: node linkType: hard -"pac-proxy-agent@npm:^7.0.0": +"pac-proxy-agent@npm:^7.0.1": version: 7.0.1 resolution: "pac-proxy-agent@npm:7.0.1" dependencies: @@ -17181,13 +16795,6 @@ __metadata: languageName: node linkType: hard -"path-key@npm:^4.0.0": - version: 4.0.0 - resolution: "path-key@npm:4.0.0" - checksum: 8e6c314ae6d16b83e93032c61020129f6f4484590a777eed709c4a01b50e498822b00f76ceaf94bc64dbd90b327df56ceadce27da3d83393790f1219e07721d7 - languageName: node - linkType: hard - "path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" @@ -17833,12 +17440,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:3.0.3": - version: 3.0.3 - resolution: "prettier@npm:3.0.3" +"prettier@npm:3.2.5": + version: 3.2.5 + resolution: "prettier@npm:3.2.5" bin: prettier: bin/prettier.cjs - checksum: e10b9af02b281f6c617362ebd2571b1d7fc9fb8a3bd17e371754428cda992e5e8d8b7a046e8f7d3e2da1dcd21aa001e2e3c797402ebb6111b5cd19609dd228e0 + checksum: 2ee4e1417572372afb7a13bb446b34f20f1bf1747db77cf6ccaf57a9be005f2f15c40f903d41a6b79eec3f57fff14d32a20fb6dee1f126da48908926fe43c311 languageName: node linkType: hard @@ -17970,23 +17577,23 @@ __metadata: languageName: node linkType: hard -"proxy-agent@npm:6.3.0": - version: 6.3.0 - resolution: "proxy-agent@npm:6.3.0" +"proxy-agent@npm:6.4.0": + version: 6.4.0 + resolution: "proxy-agent@npm:6.4.0" dependencies: agent-base: ^7.0.2 debug: ^4.3.4 - http-proxy-agent: ^7.0.0 - https-proxy-agent: ^7.0.0 + http-proxy-agent: ^7.0.1 + https-proxy-agent: ^7.0.3 lru-cache: ^7.14.1 - pac-proxy-agent: ^7.0.0 + pac-proxy-agent: ^7.0.1 proxy-from-env: ^1.1.0 - socks-proxy-agent: ^8.0.1 - checksum: e3fb0633d665e352ed4efe23ae5616b8301423dfa4ff1c5975d093da8a636181a97391f7a91c6a7ffae17c1a305df855e95507f73bcdafda8876198c64b88f5b + socks-proxy-agent: ^8.0.2 + checksum: 4d3794ad5e07486298902f0a7f250d0f869fa0e92d790767ca3f793a81374ce0ab6c605f8ab8e791c4d754da96656b48d1c24cb7094bfd310a15867e4a0841d7 languageName: node linkType: hard -"proxy-from-env@npm:1.1.0, proxy-from-env@npm:^1.1.0": +"proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 @@ -18049,46 +17656,17 @@ __metadata: languageName: node linkType: hard -"puppeteer-core@npm:^19.8.1": - version: 19.11.1 - resolution: "puppeteer-core@npm:19.11.1" - dependencies: - "@puppeteer/browsers": 0.5.0 - chromium-bidi: 0.4.7 - cross-fetch: 3.1.5 - debug: 4.3.4 - devtools-protocol: 0.0.1107588 - extract-zip: 2.0.1 - https-proxy-agent: 5.0.1 - proxy-from-env: 1.1.0 - tar-fs: 2.1.1 - unbzip2-stream: 1.4.3 - ws: 8.13.0 - peerDependencies: - typescript: ">= 4.7.4" - peerDependenciesMeta: - typescript: - optional: true - checksum: 06126e478b8b653e83b98b51cec35dceef8ab576abd1369afd45360c5bac3711443e58ebe3b852d40801a118e4cb7ddf5d3154518b5a9294ee93f7a42d9f22d4 - languageName: node - linkType: hard - -"puppeteer-core@npm:^20.0.0": - version: 20.9.0 - resolution: "puppeteer-core@npm:20.9.0" +"puppeteer-core@npm:^22.0.0": + version: 22.4.1 + resolution: "puppeteer-core@npm:22.4.1" dependencies: - "@puppeteer/browsers": 1.4.6 - chromium-bidi: 0.4.16 + "@puppeteer/browsers": 2.1.0 + chromium-bidi: 0.5.12 cross-fetch: 4.0.0 debug: 4.3.4 - devtools-protocol: 0.0.1147663 - ws: 8.13.0 - peerDependencies: - typescript: ">= 4.7.4" - peerDependenciesMeta: - typescript: - optional: true - checksum: d298598445b0f2032c02d0ed7d1d18a8d2d2fcaf6fc31fc96e93e2669a7fc6fbee0338bd9b8c8f8822887f18a8fb680b77bb56e96fe1928baadb52292bbd93b4 + devtools-protocol: 0.0.1249869 + ws: 8.16.0 + checksum: 01c28c4f0f66a4ed6c7fee14871920fd25c60ae3507c1b8aed37968b240021f8d0cdd37cb863cebb88fde792ae9fea453e3b943931ae075c99f8e90303d2625f languageName: node linkType: hard @@ -18411,7 +17989,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.0.6, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -18929,7 +18507,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.3.2": +"resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.3.2": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -18951,7 +18529,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.3.2#~builtin": +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.3.2#~builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -19067,20 +18645,6 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^2.67.0": - version: 2.79.1 - resolution: "rollup@npm:2.79.1" - dependencies: - fsevents: ~2.3.2 - dependenciesMeta: - fsevents: - optional: true - bin: - rollup: dist/bin/rollup - checksum: 6a2bf167b3587d4df709b37d149ad0300692cc5deb510f89ac7bdc77c8738c9546ae3de9322b0968e1ed2b0e984571f5f55aae28fa7de4cfcb1bc5402a4e2be6 - languageName: node - linkType: hard - "rollup@npm:^4.4.0": version: 4.9.4 resolution: "rollup@npm:4.9.4" @@ -19156,15 +18720,6 @@ __metadata: languageName: node linkType: hard -"run-applescript@npm:^5.0.0": - version: 5.0.0 - resolution: "run-applescript@npm:5.0.0" - dependencies: - execa: ^5.0.0 - checksum: d00c2dbfa5b2d774de7451194b8b125f40f65fc183de7d9dcae97f57f59433586d3c39b9001e111c38bfa24c3436c99df1bb4066a2a0c90d39a8c4cd6889af77 - languageName: node - linkType: hard - "run-applescript@npm:^7.0.0": version: 7.0.0 resolution: "run-applescript@npm:7.0.0" @@ -19352,6 +18907,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:7.6.0": + version: 7.6.0 + resolution: "semver@npm:7.6.0" + dependencies: + lru-cache: ^6.0.0 + bin: + semver: bin/semver.js + checksum: 7427f05b70786c696640edc29fdd4bc33b2acf3bbe1740b955029044f80575fc664e1a512e4113c3af21e767154a94b4aa214bf6cd6e42a1f6dba5914e0b208c + languageName: node + linkType: hard + "semver@npm:^5.4.1, semver@npm:^5.5.0": version: 5.7.2 resolution: "semver@npm:5.7.2" @@ -19955,6 +19521,20 @@ __metadata: languageName: node linkType: hard +"streamx@npm:^2.13.0": + version: 2.16.1 + resolution: "streamx@npm:2.16.1" + dependencies: + bare-events: ^2.2.0 + fast-fifo: ^1.1.0 + queue-tick: ^1.0.1 + dependenciesMeta: + bare-events: + optional: true + checksum: 6bbb4c38c0ab6ddbe0857d55e72f71288f308f2a9f4413b7b07391cdf9f94232ffc2bbe40a1212d2e09634ecdbd5052b444c73cc8d67ae1c97e2b7e553dad559 + languageName: node + linkType: hard + "streamx@npm:^2.15.0": version: 2.15.6 resolution: "streamx@npm:2.15.6" @@ -20088,13 +19668,6 @@ __metadata: languageName: node linkType: hard -"strip-final-newline@npm:^3.0.0": - version: 3.0.0 - resolution: "strip-final-newline@npm:3.0.0" - checksum: 23ee263adfa2070cd0f23d1ac14e2ed2f000c9b44229aec9c799f1367ec001478469560abefd00c5c99ee6f0b31c137d53ec6029c53e9f32a93804e18c201050 - languageName: node - linkType: hard - "strip-hex-prefix@npm:1.0.0": version: 1.0.0 resolution: "strip-hex-prefix@npm:1.0.0" @@ -20222,13 +19795,13 @@ __metadata: languageName: node linkType: hard -"synckit@npm:^0.8.5": - version: 0.8.6 - resolution: "synckit@npm:0.8.6" +"synckit@npm:^0.8.6": + version: 0.8.8 + resolution: "synckit@npm:0.8.8" dependencies: - "@pkgr/utils": ^2.4.2 + "@pkgr/core": ^0.1.0 tslib: ^2.6.2 - checksum: 7c1f4991d0afd63c090c0537f1cf8619dd5777a40cf83bf46beadbf4eb0f9e400d92044e90a177a305df4bcb56efbaf1b689877f301f2672d865b6eecf1be75a + checksum: 9ed5d33abb785f5f24e2531efd53b2782ca77abf7912f734d170134552b99001915531be5a50297aa45c5701b5c9041e8762e6cd7a38e41e2461c1e7fccdedf8 languageName: node linkType: hard @@ -20275,39 +19848,20 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:2.1.1": - version: 2.1.1 - resolution: "tar-fs@npm:2.1.1" - dependencies: - chownr: ^1.1.1 - mkdirp-classic: ^0.5.2 - pump: ^3.0.0 - tar-stream: ^2.1.4 - checksum: f5b9a70059f5b2969e65f037b4e4da2daf0fa762d3d232ffd96e819e3f94665dbbbe62f76f084f1acb4dbdcce16c6e4dac08d12ffc6d24b8d76720f4d9cf032d - languageName: node - linkType: hard - -"tar-fs@npm:3.0.4": - version: 3.0.4 - resolution: "tar-fs@npm:3.0.4" +"tar-fs@npm:3.0.5": + version: 3.0.5 + resolution: "tar-fs@npm:3.0.5" dependencies: - mkdirp-classic: ^0.5.2 + bare-fs: ^2.1.1 + bare-path: ^2.1.0 pump: ^3.0.0 tar-stream: ^3.1.5 - checksum: dcf4054f9e92ca0efe61c2b3f612914fb259a47900aa908a63106513a6d006c899b426ada53eb88d9dbbf089b5724c8e90b96a2c4ca6171845fa14203d734e30 - languageName: node - linkType: hard - -"tar-stream@npm:^2.1.4": - version: 2.2.0 - resolution: "tar-stream@npm:2.2.0" - dependencies: - bl: ^4.0.3 - end-of-stream: ^1.4.1 - fs-constants: ^1.0.0 - inherits: ^2.0.3 - readable-stream: ^3.1.1 - checksum: 699831a8b97666ef50021c767f84924cfee21c142c2eb0e79c63254e140e6408d6d55a065a2992548e72b06de39237ef2b802b99e3ece93ca3904a37622a66f3 + dependenciesMeta: + bare-fs: + optional: true + bare-path: + optional: true + checksum: e31c7e3e525fec0afecdec1cac58071809e396187725f2eba442f08a4c5649c8cd6b7ce25982f9a91bb0f055628df47c08177dd2ea4f5dafd3c22f42f8da8f00 languageName: node linkType: hard @@ -20452,13 +20006,6 @@ __metadata: languageName: node linkType: hard -"titleize@npm:^3.0.0": - version: 3.0.0 - resolution: "titleize@npm:3.0.0" - checksum: 71fbbeabbfb36ccd840559f67f21e356e1d03da2915b32d2ae1a60ddcc13a124be2739f696d2feb884983441d159a18649e8d956648d591bdad35c430a6b6d28 - languageName: node - linkType: hard - "tmp@npm:0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -20861,63 +20408,23 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.1.5": - version: 5.1.5 - resolution: "typescript@npm:5.1.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 0eef8699e05ae767096924dbed633c340b4d36e953bb8ed87fb12e9dd9dcea5055ceac7182c614a556dbd346a8a82df799d330e1e286ae66e17c84e1710f6a6f - languageName: node - linkType: hard - -"typescript@npm:^5.0.4, typescript@npm:^5.2.2": - version: 5.3.3 - resolution: "typescript@npm:5.3.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 2007ccb6e51bbbf6fde0a78099efe04dc1c3dfbdff04ca3b6a8bc717991862b39fd6126c0c3ebf2d2d98ac5e960bcaa873826bb2bb241f14277034148f41f6a2 - languageName: node - linkType: hard - -"typescript@npm:~5.2.2": - version: 5.2.2 - resolution: "typescript@npm:5.2.2" +"typescript@npm:5.4.2, typescript@npm:^5.4.2": + version: 5.4.2 + resolution: "typescript@npm:5.4.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c + checksum: 96d80fde25a09bcb04d399082fb27a808a9e17c2111e43849d2aafbd642d835e4f4ef0de09b0ba795ec2a700be6c4c2c3f62bf4660c05404c948727b5bbfb32a languageName: node linkType: hard -"typescript@patch:typescript@5.1.5#~builtin": - version: 5.1.5 - resolution: "typescript@patch:typescript@npm%3A5.1.5#~builtin::version=5.1.5&hash=5da071" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 12ff5d14888805f24479e54bc8a3f83647107a6345f6c29dffcd429fb345be55f584a37e262cca58a0105203e41d4cb4e31b1b9096c9abeca0e2ace8eb00935e - languageName: node - linkType: hard - -"typescript@patch:typescript@^5.0.4#~builtin, typescript@patch:typescript@^5.2.2#~builtin": - version: 5.3.3 - resolution: "typescript@patch:typescript@npm%3A5.3.3#~builtin::version=5.3.3&hash=f3b441" +"typescript@patch:typescript@5.4.2#~builtin, typescript@patch:typescript@^5.4.2#~builtin": + version: 5.4.2 + resolution: "typescript@patch:typescript@npm%3A5.4.2#~builtin::version=5.4.2&hash=f3b441" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: f61375590b3162599f0f0d5b8737877ac0a7bc52761dbb585d67e7b8753a3a4c42d9a554c4cc929f591ffcf3a2b0602f65ae3ce74714fd5652623a816862b610 - languageName: node - linkType: hard - -"typescript@patch:typescript@~5.2.2#~builtin": - version: 5.2.2 - resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=f3b441" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 0f4da2f15e6f1245e49db15801dbee52f2bbfb267e1c39225afdab5afee1a72839cd86000e65ee9d7e4dfaff12239d28beaf5ee431357fcced15fb08583d72ca + checksum: c1b669146bca5529873aae60870e243fa8140c85f57ca32c42f898f586d73ce4a6b4f6bb02ae312729e214d7f5859a0c70da3e527a116fdf5ad00c9fc733ecc6 languageName: node linkType: hard @@ -21283,13 +20790,6 @@ __metadata: languageName: node linkType: hard -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 - languageName: node - linkType: hard - "unzipit@npm:^1.4.3": version: 1.4.3 resolution: "unzipit@npm:1.4.3" @@ -21412,6 +20912,13 @@ __metadata: languageName: node linkType: hard +"urlpattern-polyfill@npm:10.0.0": + version: 10.0.0 + resolution: "urlpattern-polyfill@npm:10.0.0" + checksum: 61d890f151ea4ecf34a3dcab32c65ad1f3cda857c9d154af198260c6e5b2ad96d024593409baaa6d4428dd1ab206c14799bf37fe011117ac93a6a44913ac5aa4 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -22139,9 +21646,9 @@ __metadata: languageName: node linkType: hard -"ws@npm:8.13.0": - version: 8.13.0 - resolution: "ws@npm:8.13.0" +"ws@npm:8.16.0, ws@npm:^8.16.0": + version: 8.16.0 + resolution: "ws@npm:8.16.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -22150,7 +21657,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b languageName: node linkType: hard @@ -22199,21 +21706,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.16.0": - version: 8.16.0 - resolution: "ws@npm:8.16.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: feb3eecd2bae82fa8a8beef800290ce437d8b8063bdc69712725f21aef77c49cb2ff45c6e5e7fce622248f9c7abaee506bae0a9064067ffd6935460c7357321b - languageName: node - linkType: hard - "xdg-basedir@npm:^4.0.0": version: 4.0.0 resolution: "xdg-basedir@npm:4.0.0" @@ -22362,22 +21854,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.1": - version: 17.7.1 - resolution: "yargs@npm:17.7.1" - dependencies: - cliui: ^8.0.1 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.3 - y18n: ^5.0.5 - yargs-parser: ^21.1.1 - checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 - languageName: node - linkType: hard - -"yargs@npm:^17.7.1": +"yargs@npm:17.7.2, yargs@npm:^17.7.1": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 30dbe2ea32e05d2d360ccac398621245f42a8484 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 19 Mar 2024 20:22:25 +0000 Subject: [PATCH 097/416] chore: fix docs builds (#4593) --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index 661a0a0dcab..78560707795 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "scripts": { - "preprocess": "./scripts/codegen_nargo_reference.sh && yarn node ./scripts/preprocess/index.js", + "preprocess": "yarn workspace @noir-lang/acvm_js build && ./scripts/codegen_nargo_reference.sh && yarn node ./scripts/preprocess/index.js", "start": "yarn preprocess && docusaurus start", "build": "yarn preprocess && yarn version::stables && docusaurus build", "version::stables": "ts-node ./scripts/setStable.ts", From 9a241f9622b342cd9d56bf8481219cfc374c0510 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Tue, 19 Mar 2024 16:03:29 -0400 Subject: [PATCH 098/416] feat!: separating out array and slice types in the AST (#4504) # Description Replacement for https://github.com/noir-lang/noir/pull/4232. Holding off on closing it to possibly port fixes from it, but wanted to start fresh because this approach is quite different. ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4220 ## Summary\* - Removes `NotConstant` - Separate syntax for slice literals: `&[..]` - Separate AST/type constructors for slices Notes: - I removed several broken links that were blocking updating the docs: + A few were to a missing `reference` docs folder, likely from an in-progress branch + One was to some `base_order_curve`, which has been moved to reference the `BigInt` struct ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Jake Fecher Co-authored-by: jfecher --- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 14 +- compiler/noirc_frontend/src/ast/expression.rs | 19 +++ compiler/noirc_frontend/src/ast/mod.rs | 9 +- .../src/hir/resolution/resolver.rs | 57 +++++--- .../src/hir/type_check/errors.rs | 3 + .../noirc_frontend/src/hir/type_check/expr.rs | 130 ++++++++++-------- .../noirc_frontend/src/hir/type_check/stmt.rs | 1 + compiler/noirc_frontend/src/hir_def/expr.rs | 1 + compiler/noirc_frontend/src/hir_def/types.rs | 113 ++++++--------- .../src/monomorphization/ast.rs | 1 + .../src/monomorphization/debug.rs | 1 + .../src/monomorphization/mod.rs | 49 ++++--- .../src/monomorphization/printer.rs | 5 + compiler/noirc_frontend/src/node_interner.rs | 3 +- compiler/noirc_frontend/src/parser/parser.rs | 87 +++++++++++- .../noirc_frontend/src/parser/parser/types.rs | 13 +- compiler/noirc_printable_type/src/lib.rs | 38 +++-- docs/docs/how_to/merkle-proof.mdx | 4 +- docs/docs/noir/concepts/data_types/arrays.md | 2 +- docs/docs/noir/concepts/data_types/fields.md | 4 +- docs/docs/noir/concepts/data_types/slices.mdx | 31 ++++- docs/docs/noir/concepts/functions.md | 2 +- docs/docs/noir/standard_library/bigint.md | 4 +- .../noir/standard_library/containers/vec.mdx | 12 +- .../ecdsa_sig_verification.mdx | 24 +++- .../cryptographic_primitives/hashes.mdx | 62 ++++++++- .../cryptographic_primitives/schnorr.mdx | 10 ++ .../noir/standard_library/merkle_trees.md | 4 +- docs/docs/noir/standard_library/recursion.md | 4 +- docs/docs/noir/standard_library/traits.md | 11 +- docs/docs/noir/standard_library/zeroed.md | 1 + noir_stdlib/src/bigint.nr | 29 ++-- noir_stdlib/src/cmp.nr | 30 ++++ noir_stdlib/src/collections/vec.nr | 2 +- noir_stdlib/src/default.nr | 6 + noir_stdlib/src/ecdsa_secp256k1.nr | 12 ++ noir_stdlib/src/ecdsa_secp256r1.nr | 11 ++ noir_stdlib/src/hash.nr | 90 +++++++++--- noir_stdlib/src/hash/mimc.nr | 2 +- noir_stdlib/src/hash/pedersen.nr | 6 +- noir_stdlib/src/hash/poseidon.nr | 2 +- noir_stdlib/src/hash/poseidon2.nr | 2 +- noir_stdlib/src/schnorr.nr | 12 ++ noir_stdlib/src/slice.nr | 3 + .../compile_success_empty/vectors/src/main.nr | 2 +- .../array_to_slice/src/main.nr | 2 +- .../execution_success/bigint/src/main.nr | 8 +- .../brillig_hash_to_field/src/main.nr | 2 +- .../brillig_keccak/src/main.nr | 2 +- .../brillig_schnorr/src/main.nr | 2 +- .../brillig_slices/src/main.nr | 8 +- .../hash_to_field/src/main.nr | 2 +- .../nested_array_in_slice/src/main.nr | 2 +- .../regression_4202/src/main.nr | 2 +- .../execution_success/schnorr/src/main.nr | 2 +- .../slice_dynamic_index/src/main.nr | 2 +- .../execution_success/slices/src/main.nr | 35 +++-- tooling/debugger/tests/debug.rs | 4 +- tooling/nargo/src/artifacts/debug_vars.rs | 27 +++- tooling/nargo_cli/build.rs | 4 +- tooling/nargo_fmt/src/rewrite/array.rs | 10 +- tooling/nargo_fmt/src/rewrite/expr.rs | 11 +- tooling/nargo_fmt/src/rewrite/typ.rs | 12 +- tooling/noirc_abi/src/lib.rs | 2 +- 64 files changed, 763 insertions(+), 304 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 5acb266b4c1..a39df14d60a 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -199,6 +199,15 @@ impl<'a> FunctionContext<'a> { ast::Type::Array(_, _) => { self.codegen_array_checked(elements, typ[0].clone())? } + _ => unreachable!("ICE: unexpected array literal type, got {}", array.typ), + }) + } + ast::Literal::Slice(array) => { + let elements = + try_vecmap(&array.contents, |element| self.codegen_expression(element))?; + + let typ = Self::convert_type(&array.typ).flatten(); + Ok(match array.typ { ast::Type::Slice(_) => { let slice_length = self.builder.length_constant(array.contents.len() as u128); @@ -206,10 +215,7 @@ impl<'a> FunctionContext<'a> { self.codegen_array_checked(elements, typ[1].clone())?; Tree::Branch(vec![slice_length.into(), slice_contents]) } - _ => unreachable!( - "ICE: array literal type must be an array or a slice, but got {}", - array.typ - ), + _ => unreachable!("ICE: unexpected slice literal type, got {}", array.typ), }) } ast::Literal::Integer(value, typ, location) => { diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index a9ecc1a53e5..09c09daf9b9 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -70,6 +70,17 @@ impl ExpressionKind { })) } + pub fn slice(contents: Vec) -> ExpressionKind { + ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Standard(contents))) + } + + pub fn repeated_slice(repeated_element: Expression, length: Expression) -> ExpressionKind { + ExpressionKind::Literal(Literal::Slice(ArrayLiteral::Repeated { + repeated_element: Box::new(repeated_element), + length: Box::new(length), + })) + } + pub fn integer(contents: FieldElement) -> ExpressionKind { ExpressionKind::Literal(Literal::Integer(contents, false)) } @@ -319,6 +330,7 @@ impl UnaryOp { #[derive(Debug, PartialEq, Eq, Clone)] pub enum Literal { Array(ArrayLiteral), + Slice(ArrayLiteral), Bool(bool), Integer(FieldElement, /*sign*/ bool), // false for positive integer and true for negative Str(String), @@ -498,6 +510,13 @@ impl Display for Literal { Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { write!(f, "[{repeated_element}; {length}]") } + Literal::Slice(ArrayLiteral::Standard(elements)) => { + let contents = vecmap(elements, ToString::to_string); + write!(f, "&[{}]", contents.join(", ")) + } + Literal::Slice(ArrayLiteral::Repeated { repeated_element, length }) => { + write!(f, "&[{repeated_element}; {length}]") + } Literal::Bool(boolean) => write!(f, "{}", if *boolean { "true" } else { "false" }), Literal::Integer(integer, sign) => { if *sign { diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 8a420c32fb8..4547dc2a176 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -83,7 +83,8 @@ impl core::fmt::Display for IntegerBitSize { #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub enum UnresolvedTypeData { FieldElement, - Array(Option, Box), // [4]Witness = Array(4, Witness) + Array(UnresolvedTypeExpression, Box), // [Field; 4] = Array(4, Field) + Slice(Box), Integer(Signedness, IntegerBitSize), // u32 = Integer(unsigned, ThirtyTwo) Bool, Expression(UnresolvedTypeExpression), @@ -151,10 +152,8 @@ impl std::fmt::Display for UnresolvedTypeData { use UnresolvedTypeData::*; match self { FieldElement => write!(f, "Field"), - Array(len, typ) => match len { - None => write!(f, "[{typ}]"), - Some(len) => write!(f, "[{typ}; {len}]"), - }, + Array(len, typ) => write!(f, "[{typ}; {len}]"), + Slice(typ) => write!(f, "[{typ}]"), Integer(sign, num_bits) => match sign { Signedness::Signed => write!(f, "i{num_bits}"), Signedness::Unsigned => write!(f, "u{num_bits}"), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 90716bb958d..c6606386f7b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -481,13 +481,13 @@ impl<'a> Resolver<'a> { FieldElement => Type::FieldElement, Array(size, elem) => { let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); - let size = if size.is_none() { - Type::NotConstant - } else { - self.resolve_array_size(size, new_variables) - }; + let size = self.resolve_array_size(Some(size), new_variables); Type::Array(Box::new(size), elem) } + Slice(elem) => { + let elem = Box::new(self.resolve_type_inner(*elem, new_variables)); + Type::Slice(elem) + } Expression(expr) => self.convert_expression_type(expr), Integer(sign, bits) => Type::Integer(sign, bits), Bool => Type::Bool, @@ -1084,7 +1084,6 @@ impl<'a> Resolver<'a> { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::NotConstant | Type::TraitAsType(..) | Type::Forall(_, _) => (), @@ -1095,6 +1094,10 @@ impl<'a> Resolver<'a> { Self::find_numeric_generics_in_type(element_type, found); } + Type::Slice(element_type) => { + Self::find_numeric_generics_in_type(element_type, found); + } + Type::Tuple(fields) => { for field in fields { Self::find_numeric_generics_in_type(field, found); @@ -1402,27 +1405,37 @@ impl<'a> Resolver<'a> { } } + fn resolve_array_literal(&mut self, array_literal: ArrayLiteral) -> HirArrayLiteral { + match array_literal { + ArrayLiteral::Standard(elements) => { + let elements = vecmap(elements, |elem| self.resolve_expression(elem)); + HirArrayLiteral::Standard(elements) + } + ArrayLiteral::Repeated { repeated_element, length } => { + let span = length.span; + let length = + UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else(|error| { + self.errors.push(ResolverError::ParserError(Box::new(error))); + UnresolvedTypeExpression::Constant(0, span) + }); + + let length = self.convert_expression_type(length); + let repeated_element = self.resolve_expression(*repeated_element); + + HirArrayLiteral::Repeated { repeated_element, length } + } + } + } + pub fn resolve_expression(&mut self, expr: Expression) -> ExprId { let hir_expr = match expr.kind { ExpressionKind::Literal(literal) => HirExpression::Literal(match literal { Literal::Bool(b) => HirLiteral::Bool(b), - Literal::Array(ArrayLiteral::Standard(elements)) => { - let elements = vecmap(elements, |elem| self.resolve_expression(elem)); - HirLiteral::Array(HirArrayLiteral::Standard(elements)) + Literal::Array(array_literal) => { + HirLiteral::Array(self.resolve_array_literal(array_literal)) } - Literal::Array(ArrayLiteral::Repeated { repeated_element, length }) => { - let span = length.span; - let length = UnresolvedTypeExpression::from_expr(*length, span).unwrap_or_else( - |error| { - self.errors.push(ResolverError::ParserError(Box::new(error))); - UnresolvedTypeExpression::Constant(0, span) - }, - ); - - let length = self.convert_expression_type(length); - let repeated_element = self.resolve_expression(*repeated_element); - - HirLiteral::Array(HirArrayLiteral::Repeated { repeated_element, length }) + Literal::Slice(array_literal) => { + HirLiteral::Slice(self.resolve_array_literal(array_literal)) } Literal::Integer(integer, sign) => HirLiteral::Integer(integer, sign), Literal::Str(str) => HirLiteral::Str(str), diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 3d834128688..4e2a3a8b4a2 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -128,6 +128,8 @@ pub enum TypeCheckError { UnconstrainedReferenceToConstrained { span: Span }, #[error("Slices cannot be returned from an unconstrained runtime to a constrained runtime")] UnconstrainedSliceReturnToConstrained { span: Span }, + #[error("Slices must have constant length")] + NonConstantSliceLength { span: Span }, #[error("Only sized types may be used in the entry point to a program")] InvalidTypeForEntryPoint { span: Span }, #[error("Mismatched number of parameters in trait implementation")] @@ -233,6 +235,7 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } + | TypeCheckError::NonConstantSliceLength { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 0b3dd022209..eb998b59755 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -48,6 +48,49 @@ impl<'interner> TypeChecker<'interner> { false } + fn check_hir_array_literal( + &mut self, + hir_array_literal: HirArrayLiteral, + ) -> (Result>, Box) { + match hir_array_literal { + HirArrayLiteral::Standard(arr) => { + let elem_types = vecmap(&arr, |arg| self.check_expression(arg)); + + let first_elem_type = elem_types + .first() + .cloned() + .unwrap_or_else(|| self.interner.next_type_variable()); + + // Check if the array is homogeneous + for (index, elem_type) in elem_types.iter().enumerate().skip(1) { + let location = self.interner.expr_location(&arr[index]); + + elem_type.unify(&first_elem_type, &mut self.errors, || { + TypeCheckError::NonHomogeneousArray { + first_span: self.interner.expr_location(&arr[0]).span, + first_type: first_elem_type.to_string(), + first_index: index, + second_span: location.span, + second_type: elem_type.to_string(), + second_index: index + 1, + } + .add_context("elements in an array must have the same type") + }); + } + + (Ok(arr.len() as u64), Box::new(first_elem_type.clone())) + } + HirArrayLiteral::Repeated { repeated_element, length } => { + let elem_type = self.check_expression(&repeated_element); + let length = match length { + Type::Constant(length) => Ok(length), + other => Err(Box::new(other)), + }; + (length, Box::new(elem_type)) + } + } + } + /// Infers a type for a given expression, and return this type. /// As a side-effect, this function will also remember this type in the NodeInterner /// for the given expr_id key. @@ -59,64 +102,42 @@ impl<'interner> TypeChecker<'interner> { pub(crate) fn check_expression(&mut self, expr_id: &ExprId) -> Type { let typ = match self.interner.expression(expr_id) { HirExpression::Ident(ident) => self.check_ident(ident, expr_id), - HirExpression::Literal(literal) => { - match literal { - HirLiteral::Array(HirArrayLiteral::Standard(arr)) => { - let elem_types = vecmap(&arr, |arg| self.check_expression(arg)); - - let first_elem_type = elem_types - .first() - .cloned() - .unwrap_or_else(|| self.interner.next_type_variable()); - - let arr_type = Type::Array( - Box::new(Type::constant_variable(arr.len() as u64, self.interner)), - Box::new(first_elem_type.clone()), - ); - - // Check if the array is homogeneous - for (index, elem_type) in elem_types.iter().enumerate().skip(1) { - let location = self.interner.expr_location(&arr[index]); - - elem_type.unify(&first_elem_type, &mut self.errors, || { - TypeCheckError::NonHomogeneousArray { - first_span: self.interner.expr_location(&arr[0]).span, - first_type: first_elem_type.to_string(), - first_index: index, - second_span: location.span, - second_type: elem_type.to_string(), - second_index: index + 1, - } - .add_context("elements in an array must have the same type") + HirExpression::Literal(literal) => match literal { + HirLiteral::Array(hir_array_literal) => { + let (length, elem_type) = self.check_hir_array_literal(hir_array_literal); + Type::Array( + length.map_or_else( + |typ| typ, + |constant| Box::new(Type::constant_variable(constant, self.interner)), + ), + elem_type, + ) + } + HirLiteral::Slice(hir_array_literal) => { + let (length_type, elem_type) = self.check_hir_array_literal(hir_array_literal); + match length_type { + Ok(_length) => Type::Slice(elem_type), + Err(_non_constant) => { + self.errors.push(TypeCheckError::NonConstantSliceLength { + span: self.interner.expr_span(expr_id), }); + Type::Error } - - arr_type - } - HirLiteral::Array(HirArrayLiteral::Repeated { repeated_element, length }) => { - let elem_type = self.check_expression(&repeated_element); - let length = match length { - Type::Constant(length) => { - Type::constant_variable(length, self.interner) - } - other => other, - }; - Type::Array(Box::new(length), Box::new(elem_type)) } - HirLiteral::Bool(_) => Type::Bool, - HirLiteral::Integer(_, _) => Type::polymorphic_integer_or_field(self.interner), - HirLiteral::Str(string) => { - let len = Type::Constant(string.len() as u64); - Type::String(Box::new(len)) - } - HirLiteral::FmtStr(string, idents) => { - let len = Type::Constant(string.len() as u64); - let types = vecmap(&idents, |elem| self.check_expression(elem)); - Type::FmtString(Box::new(len), Box::new(Type::Tuple(types))) - } - HirLiteral::Unit => Type::Unit, } - } + HirLiteral::Bool(_) => Type::Bool, + HirLiteral::Integer(_, _) => Type::polymorphic_integer_or_field(self.interner), + HirLiteral::Str(string) => { + let len = Type::Constant(string.len() as u64); + Type::String(Box::new(len)) + } + HirLiteral::FmtStr(string, idents) => { + let len = Type::Constant(string.len() as u64); + let types = vecmap(&idents, |elem| self.check_expression(elem)); + Type::FmtString(Box::new(len), Box::new(Type::Tuple(types))) + } + HirLiteral::Unit => Type::Unit, + }, HirExpression::Infix(infix_expr) => { // The type of the infix expression must be looked up from a type table let lhs_type = self.check_expression(&infix_expr.lhs); @@ -557,6 +578,7 @@ impl<'interner> TypeChecker<'interner> { // XXX: We can check the array bounds here also, but it may be better to constant fold first // and have ConstId instead of ExprId for constants Type::Array(_, base_type) => *base_type, + Type::Slice(base_type) => *base_type, Type::Error => Type::Error, typ => { let span = self.interner.expr_span(&new_lhs); diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index e90da555803..49ba3244dc9 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -256,6 +256,7 @@ impl<'interner> TypeChecker<'interner> { let typ = match lvalue_type.follow_bindings() { Type::Array(_, elem_type) => *elem_type, + Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, other => { // TODO: Need a better span here diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index b4c590de491..6a1b4385f0d 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -99,6 +99,7 @@ impl HirBinaryOp { #[derive(Debug, Clone)] pub enum HirLiteral { Array(HirArrayLiteral), + Slice(HirArrayLiteral), Bool(bool), Integer(FieldElement, bool), //true for negative integer and false for positive Str(String), diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 60bc5b2470f..7fb26aa3879 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -27,6 +27,9 @@ pub enum Type { /// is either a type variable of some kind or a Type::Constant. Array(Box, Box), + /// Slice(E) is a slice of elements of type E. + Slice(Box), + /// A primitive integer type with the given sign and bit count. /// E.g. `u32` would be `Integer(Unsigned, ThirtyTwo)` Integer(Signedness, IntegerBitSize), @@ -98,11 +101,6 @@ pub enum Type { /// bind to an integer without special checks to bind it to a non-type. Constant(u64), - /// The type of a slice is an array of size NotConstant. - /// The size of an array literal is resolved to this if it ever uses operations - /// involving slices. - NotConstant, - /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with /// an invalid type would otherwise issue a new error each time it is called @@ -146,27 +144,23 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) - | Type::NotConstant + | Type::Slice(_) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } } pub(crate) fn is_nested_slice(&self) -> bool { match self { - Type::Array(size, elem) => { - if let Type::NotConstant = size.as_ref() { - elem.as_ref().contains_slice() - } else { - false - } - } + Type::Slice(elem) => elem.as_ref().contains_slice(), + Type::Array(_, elem) => elem.as_ref().contains_slice(), + Type::Alias(alias, generics) => alias.borrow().get_type(generics).is_nested_slice(), _ => false, } } pub(crate) fn contains_slice(&self) -> bool { match self { - Type::Array(size, _) => matches!(size.as_ref(), Type::NotConstant), + Type::Slice(_) => true, Type::Struct(struct_typ, generics) => { let fields = struct_typ.borrow().get_fields(generics); for field in fields.iter() { @@ -453,7 +447,7 @@ pub enum TypeVariableKind { /// that can only be bound to Type::Integer, or other polymorphic integers. Integer, - /// A potentially constant array size. This will only bind to itself, Type::NotConstant, or + /// A potentially constant array size. This will only bind to itself or /// Type::Constant(n) with a matching size. This defaults to Type::Constant(n) if still unbound /// during monomorphization. Constant(u64), @@ -631,14 +625,13 @@ impl Type { | Type::TypeVariable(_, _) | Type::Constant(_) | Type::NamedGeneric(_, _) - | Type::NotConstant | Type::Forall(_, _) | Type::TraitAsType(..) => false, Type::Array(length, elem) => { elem.contains_numeric_typevar(target_id) || named_generic_id_matches_target(length) } - + Type::Slice(elem) => elem.contains_numeric_typevar(target_id), Type::Tuple(fields) => { fields.iter().any(|field| field.contains_numeric_typevar(target_id)) } @@ -696,8 +689,8 @@ impl Type { | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) - | Type::TraitAsType(..) - | Type::NotConstant => false, + | Type::Slice(_) + | Type::TraitAsType(..) => false, Type::Alias(alias, generics) => { let alias = alias.borrow(); @@ -766,11 +759,10 @@ impl std::fmt::Display for Type { write!(f, "Field") } Type::Array(len, typ) => { - if matches!(len.follow_bindings(), Type::NotConstant) { - write!(f, "[{typ}]") - } else { - write!(f, "[{typ}; {len}]") - } + write!(f, "[{typ}; {len}]") + } + Type::Slice(typ) => { + write!(f, "[{typ}]") } Type::Integer(sign, num_bits) => match sign { Signedness::Signed => write!(f, "i{num_bits}"), @@ -860,7 +852,6 @@ impl std::fmt::Display for Type { Type::MutableReference(element) => { write!(f, "&mut {element}") } - Type::NotConstant => write!(f, "_"), } } } @@ -916,10 +907,6 @@ impl Type { bindings.insert(target_id, (var.clone(), this)); Ok(()) } - Type::NotConstant => { - bindings.insert(target_id, (var.clone(), Type::NotConstant)); - Ok(()) - } // A TypeVariable is less specific than a MaybeConstant, so we bind // to the other type variable instead. Type::TypeVariable(new_var, kind) => { @@ -947,14 +934,8 @@ impl Type { bindings.insert(*new_target_id, (new_var.clone(), clone)); Ok(()) } - // The lengths don't match, but neither are set in stone so we can - // just set them both to NotConstant. See issue 2370 - TypeVariableKind::Constant(_) => { - // *length != target_length - bindings.insert(target_id, (var.clone(), Type::NotConstant)); - bindings.insert(*new_target_id, (new_var.clone(), Type::NotConstant)); - Ok(()) - } + // *length != target_length + TypeVariableKind::Constant(_) => Err(UnificationError), TypeVariableKind::IntegerOrField => Err(UnificationError), TypeVariableKind::Integer => Err(UnificationError), }, @@ -1164,6 +1145,8 @@ impl Type { elem_a.try_unify(elem_b, bindings) } + (Slice(elem_a), Slice(elem_b)) => elem_a.try_unify(elem_b, bindings), + (String(len_a), String(len_b)) => len_a.try_unify(len_b, bindings), (FmtString(len_a, elements_a), FmtString(len_b, elements_b)) => { @@ -1308,20 +1291,14 @@ impl Type { let this = self.follow_bindings(); let target = target.follow_bindings(); - if let (Type::Array(size1, element1), Type::Array(size2, element2)) = (&this, &target) { - let size1 = size1.follow_bindings(); - let size2 = size2.follow_bindings(); - - // If we have an array and our target is a slice - if matches!(size1, Type::Constant(_)) && matches!(size2, Type::NotConstant) { - // Still have to ensure the element types match. - // Don't need to issue an error here if not, it will be done in unify_with_coercions - let mut bindings = TypeBindings::new(); - if element1.try_unify(element2, &mut bindings).is_ok() { - convert_array_expression_to_slice(expression, this, target, interner); - Self::apply_type_bindings(bindings); - return true; - } + if let (Type::Array(_size, element1), Type::Slice(element2)) = (&this, &target) { + // Still have to ensure the element types match. + // Don't need to issue an error here if not, it will be done in unify_with_coercions + let mut bindings = TypeBindings::new(); + if element1.try_unify(element2, &mut bindings).is_ok() { + convert_array_expression_to_slice(expression, this, target, interner); + Self::apply_type_bindings(bindings); + return true; } } false @@ -1489,6 +1466,10 @@ impl Type { let element = element.substitute_helper(type_bindings, substitute_bound_typevars); Type::Array(Box::new(size), Box::new(element)) } + Type::Slice(element) => { + let element = element.substitute_helper(type_bindings, substitute_bound_typevars); + Type::Slice(Box::new(element)) + } Type::String(size) => { let size = size.substitute_helper(type_bindings, substitute_bound_typevars); Type::String(Box::new(size)) @@ -1548,7 +1529,6 @@ impl Type { | Type::Constant(_) | Type::TraitAsType(..) | Type::Error - | Type::NotConstant | Type::Unit => self.clone(), } } @@ -1557,6 +1537,7 @@ impl Type { pub fn occurs(&self, target_id: TypeVariableId) -> bool { match self { Type::Array(len, elem) => len.occurs(target_id) || elem.occurs(target_id), + Type::Slice(elem) => elem.occurs(target_id), Type::String(len) => len.occurs(target_id), Type::FmtString(len, fields) => { let len_occurs = len.occurs(target_id); @@ -1589,7 +1570,6 @@ impl Type { | Type::Constant(_) | Type::TraitAsType(..) | Type::Error - | Type::NotConstant | Type::Unit => false, } } @@ -1606,6 +1586,7 @@ impl Type { Array(size, elem) => { Array(Box::new(size.follow_bindings()), Box::new(elem.follow_bindings())) } + Slice(elem) => Slice(Box::new(elem.follow_bindings())), String(size) => String(Box::new(size.follow_bindings())), FmtString(size, args) => { let size = Box::new(size.follow_bindings()); @@ -1640,14 +1621,9 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - TraitAsType(..) - | FieldElement - | Integer(_, _) - | Bool - | Constant(_) - | Unit - | Error - | NotConstant => self.clone(), + TraitAsType(..) | FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Error => { + self.clone() + } } } @@ -1727,6 +1703,10 @@ impl From<&Type> for PrintableType { let typ = typ.as_ref(); PrintableType::Array { length, typ: Box::new(typ.into()) } } + Type::Slice(typ) => { + let typ = typ.as_ref(); + PrintableType::Slice { typ: Box::new(typ.into()) } + } Type::Integer(sign, bit_width) => match sign { Signedness::Unsigned => { PrintableType::UnsignedInteger { width: (*bit_width).into() } @@ -1772,7 +1752,6 @@ impl From<&Type> for PrintableType { Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } - Type::NotConstant => unreachable!(), } } } @@ -1784,11 +1763,10 @@ impl std::fmt::Debug for Type { write!(f, "Field") } Type::Array(len, typ) => { - if matches!(len.follow_bindings(), Type::NotConstant) { - write!(f, "[{typ:?}]") - } else { - write!(f, "[{typ:?}; {len:?}]") - } + write!(f, "[{typ:?}; {len:?}]") + } + Type::Slice(typ) => { + write!(f, "[{typ:?}]") } Type::Integer(sign, num_bits) => match sign { Signedness::Signed => write!(f, "i{num_bits}"), @@ -1858,7 +1836,6 @@ impl std::fmt::Debug for Type { Type::MutableReference(element) => { write!(f, "&mut {element:?}") } - Type::NotConstant => write!(f, "NotConstant"), } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 21b77127360..4f633cbb350 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -89,6 +89,7 @@ pub struct For { #[derive(Debug, Clone, Hash)] pub enum Literal { Array(ArrayLiteral), + Slice(ArrayLiteral), Integer(FieldElement, Type, Location), Bool(bool), Str(String), diff --git a/compiler/noirc_frontend/src/monomorphization/debug.rs b/compiler/noirc_frontend/src/monomorphization/debug.rs index 3a03177f8ec..88943be727f 100644 --- a/compiler/noirc_frontend/src/monomorphization/debug.rs +++ b/compiler/noirc_frontend/src/monomorphization/debug.rs @@ -192,6 +192,7 @@ impl<'interner> Monomorphizer<'interner> { fn element_type_at_index(ptype: &PrintableType, i: usize) -> &PrintableType { match ptype { PrintableType::Array { length: _length, typ } => typ.as_ref(), + PrintableType::Slice { typ } => typ.as_ref(), PrintableType::Tuple { types } => &types[i], PrintableType::Struct { name: _name, fields } => &fields[i].1, PrintableType::String { length: _length } => &PrintableType::UnsignedInteger { width: 8 }, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index a99a4e61d4d..d4fe2e0a2aa 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -418,9 +418,15 @@ impl<'interner> Monomorphizer<'interner> { } } HirExpression::Literal(HirLiteral::Array(array)) => match array { - HirArrayLiteral::Standard(array) => self.standard_array(expr, array)?, + HirArrayLiteral::Standard(array) => self.standard_array(expr, array, false)?, HirArrayLiteral::Repeated { repeated_element, length } => { - self.repeated_array(expr, repeated_element, length)? + self.repeated_array(expr, repeated_element, length, false)? + } + }, + HirExpression::Literal(HirLiteral::Slice(array)) => match array { + HirArrayLiteral::Standard(array) => self.standard_array(expr, array, true)?, + HirArrayLiteral::Repeated { repeated_element, length } => { + self.repeated_array(expr, repeated_element, length, true)? } }, HirExpression::Literal(HirLiteral::Unit) => ast::Expression::Block(vec![]), @@ -519,10 +525,15 @@ impl<'interner> Monomorphizer<'interner> { &mut self, array: node_interner::ExprId, array_elements: Vec, + is_slice: bool, ) -> Result { let typ = self.convert_type(&self.interner.id_type(array)); let contents = try_vecmap(array_elements, |id| self.expr(id))?; - Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + if is_slice { + Ok(ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents, typ }))) + } else { + Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + } } fn repeated_array( @@ -530,6 +541,7 @@ impl<'interner> Monomorphizer<'interner> { array: node_interner::ExprId, repeated_element: node_interner::ExprId, length: HirType, + is_slice: bool, ) -> Result { let typ = self.convert_type(&self.interner.id_type(array)); @@ -539,7 +551,11 @@ impl<'interner> Monomorphizer<'interner> { })?; let contents = try_vecmap(0..length, |_| self.expr(repeated_element))?; - Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + if is_slice { + Ok(ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents, typ }))) + } else { + Ok(ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { contents, typ }))) + } } fn index( @@ -854,12 +870,13 @@ impl<'interner> Monomorphizer<'interner> { HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { let element = Box::new(self.convert_type(element.as_ref())); - - if let Some(length) = length.evaluate_to_u64() { - ast::Type::Array(length, element) - } else { - ast::Type::Slice(element) - } + // TODO: convert to MonomorphizationError + let length = length.evaluate_to_u64().unwrap_or(0); + ast::Type::Array(length, element) + } + HirType::Slice(element) => { + let element = Box::new(self.convert_type(element.as_ref())); + ast::Type::Slice(element) } HirType::TraitAsType(..) => { unreachable!("All TraitAsType should be replaced before calling convert_type"); @@ -934,10 +951,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Type::MutableReference(Box::new(element)) } - HirType::Forall(_, _) - | HirType::Constant(_) - | HirType::NotConstant - | HirType::Error => { + HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } } @@ -1152,11 +1166,7 @@ impl<'interner> Monomorphizer<'interner> { fn append_printable_type_info_inner(typ: &Type, arguments: &mut Vec) { // Disallow printing slices and mutable references for consistency, // since they cannot be passed from ACIR into Brillig - if let HirType::Array(size, _) = typ { - if let HirType::NotConstant = **size { - unreachable!("println and format strings do not support slices. Convert the slice to an array before passing it to println"); - } - } else if matches!(typ, HirType::MutableReference(_)) { + if matches!(typ, HirType::MutableReference(_)) { unreachable!("println and format strings do not support mutable references."); } @@ -1224,6 +1234,7 @@ impl<'interner> Monomorphizer<'interner> { location: Location, ) -> ast::Expression { use ast::*; + let int_type = Type::Integer(crate::Signedness::Unsigned, arr_elem_bits); let bytes_as_expr = vecmap(bytes, |byte| { diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index c3e34890ce0..c253bfe7930 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -97,6 +97,11 @@ impl AstPrinter { self.print_comma_separated(&array.contents, f)?; write!(f, "]") } + super::ast::Literal::Slice(array) => { + write!(f, "&[")?; + self.print_comma_separated(&array.contents, f)?; + write!(f, "]") + } super::ast::Literal::Integer(x, _, _) => x.fmt(f), super::ast::Literal::Bool(x) => x.fmt(f), super::ast::Literal::Str(s) => s.fmt(f), diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index b83d2008530..4e184e1391b 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1664,6 +1664,7 @@ enum TypeMethodKey { /// accept only fields or integers, it is just that their names may not clash. FieldOrInt, Array, + Slice, Bool, String, FmtString, @@ -1679,6 +1680,7 @@ fn get_type_method_key(typ: &Type) -> Option { match &typ { Type::FieldElement => Some(FieldOrInt), Type::Array(_, _) => Some(Array), + Type::Slice(_) => Some(Slice), Type::Integer(_, _) => Some(FieldOrInt), Type::TypeVariable(_, TypeVariableKind::IntegerOrField) => Some(FieldOrInt), Type::TypeVariable(_, TypeVariableKind::Integer) => Some(FieldOrInt), @@ -1697,7 +1699,6 @@ fn get_type_method_key(typ: &Type) -> Option { | Type::Forall(_, _) | Type::Constant(_) | Type::Error - | Type::NotConstant | Type::Struct(_, _) | Type::TraitAsType(..) => None, } diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index b2d9d7e6802..f2e22da3c2c 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -575,6 +575,7 @@ fn parse_type_inner( format_string_type(recursive_type_parser.clone()), named_type(recursive_type_parser.clone()), named_trait(recursive_type_parser.clone()), + slice_type(recursive_type_parser.clone()), array_type(recursive_type_parser.clone()), parenthesized_type(recursive_type_parser.clone()), tuple_type(recursive_type_parser.clone()), @@ -711,13 +712,22 @@ fn generic_type_args( fn array_type(type_parser: impl NoirParser) -> impl NoirParser { just(Token::LeftBracket) .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression()).or_not()) + .then(just(Token::Semicolon).ignore_then(type_expression())) .then_ignore(just(Token::RightBracket)) .map_with_span(|(element_type, size), span| { UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) }) } +fn slice_type(type_parser: impl NoirParser) -> impl NoirParser { + just(Token::LeftBracket) + .ignore_then(type_parser) + .then_ignore(just(Token::RightBracket)) + .map_with_span(|element_type, span| { + UnresolvedTypeData::Slice(Box::new(element_type)).with_span(span) + }) +} + fn type_expression() -> impl NoirParser { recursive(|expr| { expression_with_precedence( @@ -1079,6 +1089,36 @@ where .map(|(lhs, count)| ExpressionKind::repeated_array(lhs, count)) } +fn slice_expr

(expr_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + just(Token::Ampersand) + .ignore_then(standard_slice(expr_parser.clone()).or(slice_sugar(expr_parser))) +} + +/// &[a, b, c, ...] +fn standard_slice

(expr_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + expression_list(expr_parser) + .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) + .validate(|elements, _span, _emit| ExpressionKind::slice(elements)) +} + +/// &[a; N] +fn slice_sugar

(expr_parser: P) -> impl NoirParser +where + P: ExprParser, +{ + expr_parser + .clone() + .then(just(Token::Semicolon).ignore_then(expr_parser)) + .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) + .map(|(lhs, count)| ExpressionKind::repeated_slice(lhs, count)) +} + fn expression_list

(expr_parser: P) -> impl NoirParser> where P: ExprParser, @@ -1102,6 +1142,7 @@ where { choice(( if_expr(expr_no_constructors, statement.clone()), + slice_expr(expr_parser.clone()), array_expr(expr_parser.clone()), if allow_constructors { constructor(expr_parser.clone()).boxed() @@ -1293,6 +1334,50 @@ mod test { parse_all_failing(array_expr(expression()), invalid); } + fn expr_to_slice(expr: ExpressionKind) -> ArrayLiteral { + let lit = match expr { + ExpressionKind::Literal(literal) => literal, + _ => unreachable!("expected a literal"), + }; + + match lit { + Literal::Slice(arr) => arr, + _ => unreachable!("expected a slice: {:?}", lit), + } + } + + #[test] + fn parse_slice() { + let valid = vec![ + "&[0, 1, 2,3, 4]", + "&[0,1,2,3,4,]", // Trailing commas are valid syntax + "&[0;5]", + ]; + + for expr in parse_all(slice_expr(expression()), valid) { + match expr_to_slice(expr) { + ArrayLiteral::Standard(elements) => assert_eq!(elements.len(), 5), + ArrayLiteral::Repeated { length, .. } => { + assert_eq!(length.kind, ExpressionKind::integer(5i128.into())); + } + } + } + + parse_all_failing( + slice_expr(expression()), + vec!["0,1,2,3,4]", "&[[0,1,2,3,4]", "&[0,1,2,,]", "&[0,1,2,3,4"], + ); + } + + #[test] + fn parse_slice_sugar() { + let valid = vec!["&[0;7]", "&[(1, 2); 4]", "&[0;Four]", "&[2;1+3-a]"]; + parse_all(slice_expr(expression()), valid); + + let invalid = vec!["&[0;;4]", "&[1, 2; 3]"]; + parse_all_failing(slice_expr(expression()), invalid); + } + #[test] fn parse_block() { parse_with(block(fresh_statement()), "{ [0,1,2,3,4] }").unwrap(); diff --git a/compiler/noirc_frontend/src/parser/parser/types.rs b/compiler/noirc_frontend/src/parser/parser/types.rs index 572397d6527..d8a63d161b9 100644 --- a/compiler/noirc_frontend/src/parser/parser/types.rs +++ b/compiler/noirc_frontend/src/parser/parser/types.rs @@ -88,13 +88,24 @@ pub(super) fn array_type( ) -> impl NoirParser { just(Token::LeftBracket) .ignore_then(type_parser) - .then(just(Token::Semicolon).ignore_then(type_expression()).or_not()) + .then(just(Token::Semicolon).ignore_then(type_expression())) .then_ignore(just(Token::RightBracket)) .map_with_span(|(element_type, size), span| { UnresolvedTypeData::Array(size, Box::new(element_type)).with_span(span) }) } +pub(super) fn slice_type( + type_parser: impl NoirParser, +) -> impl NoirParser { + just(Token::LeftBracket) + .ignore_then(type_parser) + .then_ignore(just(Token::RightBracket)) + .map_with_span(|element_type, span| { + UnresolvedTypeData::Slice(Box::new(element_type)).with_span(span) + }) +} + pub(super) fn type_expression() -> impl NoirParser { recursive(|expr| { expression_with_precedence( diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index 60f233cd86d..d3d20654adc 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -15,6 +15,10 @@ pub enum PrintableType { #[serde(rename = "type")] typ: Box, }, + Slice { + #[serde(rename = "type")] + typ: Box, + }, Tuple { types: Vec, }, @@ -50,7 +54,7 @@ pub enum PrintableType { pub enum PrintableValue { Field(FieldElement), String(String), - Vec(Vec), + Vec { array_elements: Vec, is_slice: bool }, Struct(BTreeMap), Other, } @@ -184,9 +188,12 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { (_, PrintableType::MutableReference { .. }) => { output.push_str("<>"); } - (PrintableValue::Vec(vector), PrintableType::Array { typ, .. }) => { + (PrintableValue::Vec { array_elements, is_slice }, PrintableType::Array { typ, .. }) => { + if *is_slice { + output.push('&') + } output.push('['); - let mut values = vector.iter().peekable(); + let mut values = array_elements.iter().peekable(); while let Some(value) = values.next() { output.push_str(&format!( "{}", @@ -221,9 +228,9 @@ fn to_string(value: &PrintableValue, typ: &PrintableType) -> Option { output.push_str(" }"); } - (PrintableValue::Vec(values), PrintableType::Tuple { types }) => { + (PrintableValue::Vec { array_elements, .. }, PrintableType::Tuple { types }) => { output.push('('); - let mut elems = values.iter().zip(types).peekable(); + let mut elems = array_elements.iter().zip(types).peekable(); while let Some((value, typ)) = elems.next() { output.push_str( &PrintableValueDisplay::Plain(value.clone(), typ.clone()).to_string(), @@ -322,7 +329,7 @@ pub fn decode_value( array_elements.push(decode_value(field_iterator, typ)); } - PrintableValue::Vec(array_elements) + PrintableValue::Vec { array_elements, is_slice: false } } PrintableType::Array { length: Some(length), typ } => { let length = *length as usize; @@ -331,11 +338,24 @@ pub fn decode_value( array_elements.push(decode_value(field_iterator, typ)); } - PrintableValue::Vec(array_elements) + PrintableValue::Vec { array_elements, is_slice: false } } - PrintableType::Tuple { types } => { - PrintableValue::Vec(vecmap(types, |typ| decode_value(field_iterator, typ))) + PrintableType::Slice { typ } => { + let length = field_iterator + .next() + .expect("not enough data to decode variable array length") + .to_u128() as usize; + let mut array_elements = Vec::with_capacity(length); + for _ in 0..length { + array_elements.push(decode_value(field_iterator, typ)); + } + + PrintableValue::Vec { array_elements, is_slice: true } } + PrintableType::Tuple { types } => PrintableValue::Vec { + array_elements: vecmap(types, |typ| decode_value(field_iterator, typ)), + is_slice: false, + }, PrintableType::String { length } => { let field_elements: Vec = field_iterator.take(*length as usize).collect(); diff --git a/docs/docs/how_to/merkle-proof.mdx b/docs/docs/how_to/merkle-proof.mdx index 34074659ac1..003c7019a93 100644 --- a/docs/docs/how_to/merkle-proof.mdx +++ b/docs/docs/how_to/merkle-proof.mdx @@ -14,7 +14,7 @@ in a merkle tree. use dep::std; fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { - let leaf = std::hash::hash_to_field(message); + let leaf = std::hash::hash_to_field(message.as_slice()); let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); assert(merkle_root == root); } @@ -27,7 +27,7 @@ random oracle. If only collision resistance is needed, then one can call `std::h instead. ```rust -let leaf = std::hash::hash_to_field(message); +let leaf = std::hash::hash_to_field(message.as_slice()); ``` The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. diff --git a/docs/docs/noir/concepts/data_types/arrays.md b/docs/docs/noir/concepts/data_types/arrays.md index a8bd338e736..efce3e95d32 100644 --- a/docs/docs/noir/concepts/data_types/arrays.md +++ b/docs/docs/noir/concepts/data_types/arrays.md @@ -72,7 +72,7 @@ let element = array[0][0]; ``` However, multidimensional slices are not supported. For example, the following code will error at compile time: ```rust -let slice : [[Field]] = []; +let slice : [[Field]] = &[]; ``` ## Types diff --git a/docs/docs/noir/concepts/data_types/fields.md b/docs/docs/noir/concepts/data_types/fields.md index 99b4aa63549..a10a4810788 100644 --- a/docs/docs/noir/concepts/data_types/fields.md +++ b/docs/docs/noir/concepts/data_types/fields.md @@ -42,7 +42,7 @@ After declaring a Field, you can use these common methods on it: Transforms the field into an array of bits, Little Endian. ```rust -fn to_le_bits(_x : Field, _bit_size: u32) -> [u1; N] +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] ``` example: @@ -59,7 +59,7 @@ fn main() { Transforms the field into an array of bits, Big Endian. ```rust -fn to_be_bits(_x : Field, _bit_size: u32) -> [u1; N] +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1] ``` example: diff --git a/docs/docs/noir/concepts/data_types/slices.mdx b/docs/docs/noir/concepts/data_types/slices.mdx index 4a6ee816aa2..828faf4a8f8 100644 --- a/docs/docs/noir/concepts/data_types/slices.mdx +++ b/docs/docs/noir/concepts/data_types/slices.mdx @@ -15,13 +15,19 @@ A slice is a dynamically-sized view into a sequence of elements. They can be res use dep::std::slice; fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; + let mut slice: [Field] = &[0; 2]; let mut new_slice = slice.push_back(6); new_slice.len() } ``` +To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + View the corresponding test file [here][test-file]. [test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr @@ -42,7 +48,7 @@ example: ```rust fn main() -> pub Field { - let mut slice: [Field] = [0; 2]; + let mut slice: [Field] = &[0; 2]; let mut new_slice = slice.push_back(6); new_slice.len() @@ -62,7 +68,7 @@ fn push_front(_self: Self, _elem: T) -> Self Example: ```rust -let mut new_slice: [Field] = []; +let mut new_slice: [Field] = &[]; new_slice = new_slice.push_front(20); assert(new_slice[0] == 20); // returns true ``` @@ -112,7 +118,7 @@ fn append(mut self, other: Self) -> Self Example: ```rust -let append = [1, 2].append([3, 4, 5]); +let append = &[1, 2].append(&[3, 4, 5]); ``` ### insert @@ -145,3 +151,20 @@ Example: ```rust let (remove_slice, removed_elem) = slice.remove(3); ``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` diff --git a/docs/docs/noir/concepts/functions.md b/docs/docs/noir/concepts/functions.md index 48aba9cd058..2c9bc33fdfc 100644 --- a/docs/docs/noir/concepts/functions.md +++ b/docs/docs/noir/concepts/functions.md @@ -71,7 +71,7 @@ fn main(x : [Field]) { #[test] fn test_one() { - main([1, 2]); + main(&[1, 2]); } ``` diff --git a/docs/docs/noir/standard_library/bigint.md b/docs/docs/noir/standard_library/bigint.md index 7d7931840cf..9aa4fb77112 100644 --- a/docs/docs/noir/standard_library/bigint.md +++ b/docs/docs/noir/standard_library/bigint.md @@ -31,7 +31,7 @@ For instance the big integer 'Secpk1Fq' in the standard library refers to intege Feel free to explore the source code for the other primes: -#include_code curve_order_base noir_stdlib/src/bigint.nr rust +#include_code big_int_definition noir_stdlib/src/bigint.nr rust ## Example usage @@ -48,7 +48,7 @@ The available operations for each big integer are: Construct a big integer from its little-endian bytes representation. Example: ```rust - let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]); + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); ``` Sure, here's the formatted version of the remaining methods: diff --git a/docs/docs/noir/standard_library/containers/vec.mdx b/docs/docs/noir/standard_library/containers/vec.mdx index 1954f05bc76..fcfd7e07aa0 100644 --- a/docs/docs/noir/standard_library/containers/vec.mdx +++ b/docs/docs/noir/standard_library/containers/vec.mdx @@ -49,8 +49,8 @@ pub fn from_slice(slice: [T]) -> Self Example: ```rust -let arr: [Field] = [1, 2, 3]; -let vector_from_slice = Vec::from_slice(arr); +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); assert(vector_from_slice.len() == 3); ``` @@ -80,7 +80,7 @@ pub fn get(self, index: Field) -> T Example: ```rust -let vector: Vec = Vec::from_slice([10, 20, 30]); +let vector: Vec = Vec::from_slice(&[10, 20, 30]); assert(vector.get(1) == 20); ``` @@ -111,7 +111,7 @@ pub fn pop(&mut self) -> T Example: ```rust -let mut vector = Vec::from_slice([10, 20]); +let mut vector = Vec::from_slice(&[10, 20]); let popped_elem = vector.pop(); assert(popped_elem == 20); assert(vector.len() == 1); @@ -128,7 +128,7 @@ pub fn insert(&mut self, index: Field, elem: T) Example: ```rust -let mut vector = Vec::from_slice([10, 30]); +let mut vector = Vec::from_slice(&[10, 30]); vector.insert(1, 20); assert(vector.get(1) == 20); ``` @@ -144,7 +144,7 @@ pub fn remove(&mut self, index: Field) -> T Example: ```rust -let mut vector = Vec::from_slice([10, 20, 30]); +let mut vector = Vec::from_slice(&[10, 20, 30]); let removed_elem = vector.remove(1); assert(removed_elem == 20); assert(vector.len() == 2); diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx index d67a1ac94df..6787c9f46a1 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -11,7 +11,8 @@ Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 cur ## ecdsa_secp256k1::verify_signature -Verifier for ECDSA Secp256k1 signatures +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. #include_code ecdsa_secp256k1 noir_stdlib/src/ecdsa_secp256k1.nr rust @@ -24,9 +25,20 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign } ``` + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +#include_code ecdsa_secp256k1_slice noir_stdlib/src/ecdsa_secp256k1.nr rust + + + ## ecdsa_secp256r1::verify_signature -Verifier for ECDSA Secp256r1 signatures +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. #include_code ecdsa_secp256r1 noir_stdlib/src/ecdsa_secp256r1.nr rust @@ -40,3 +52,11 @@ fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], sign ``` + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +#include_code ecdsa_secp256r1_slice noir_stdlib/src/ecdsa_secp256r1.nr rust + + diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx index b9239f822e8..f98c90a97c8 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -13,6 +13,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## sha256 Given an array of bytes, returns the resulting sha256 hash. +See sha256_slice for a version that works directly on slices. #include_code sha256 noir_stdlib/src/hash.nr rust @@ -27,9 +28,18 @@ fn main() { +## sha256_slice + +A version of sha256 specialized to slices: + +#include_code sha256_slice noir_stdlib/src/hash.nr rust + + + ## blake2s Given an array of bytes, returns an array with the Blake2 hash +See blake2s_slice for a version that works directly on slices. #include_code blake2s noir_stdlib/src/hash.nr rust @@ -44,9 +54,18 @@ fn main() { +## blake2s_slice + +A version of blake2s specialized to slices: + +#include_code blake2s_slice noir_stdlib/src/hash.nr rust + + + ## blake3 Given an array of bytes, returns an array with the Blake3 hash +See blake3_slice for a version that works directly on slices. #include_code blake3 noir_stdlib/src/hash.nr rust @@ -61,9 +80,18 @@ fn main() { +## blake3_slice + +A version of blake3 specialized to slices: + +#include_code blake3_slice noir_stdlib/src/hash.nr rust + + + ## pedersen_hash Given an array of Fields, returns the Pedersen hash. +See pedersen_hash_slice for a version that works directly on slices. #include_code pedersen_hash noir_stdlib/src/hash.nr rust @@ -73,10 +101,18 @@ example: +## pedersen_hash_slice + +Given a slice of Fields, returns the Pedersen hash. + +#include_code pedersen_hash_slice noir_stdlib/src/hash.nr rust + + ## pedersen_commitment Given an array of Fields, returns the Pedersen commitment. +See pedersen_commitment_slice for a version that works directly on slices. #include_code pedersen_commitment noir_stdlib/src/hash.nr rust @@ -86,11 +122,20 @@ example: +## pedersen_commitment_slice + +Given a slice of Fields, returns the Pedersen commitment. + +#include_code pedersen_commitment_slice noir_stdlib/src/hash.nr rust + + + ## keccak256 -Given an array of bytes (`u8`), returns the resulting keccak hash as an array of 32 bytes -(`[u8; 32]`). Specify a message_size to hash only the first `message_size` bytes -of the input. +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. See keccak256_slice for a version that works +directly on slices. #include_code keccak256 noir_stdlib/src/hash.nr rust @@ -100,6 +145,15 @@ example: +## keccak256_slice + +Given a slice of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). + +#include_code keccak256_slice noir_stdlib/src/hash.nr rust + + + ## poseidon Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify @@ -156,7 +210,7 @@ fn main() { ## hash_to_field ```rust -fn hash_to_field(_input : [Field; N]) -> Field {} +fn hash_to_field(_input : [Field]) -> Field {} ``` Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return diff --git a/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx index 0e0c358c6e1..2c9eb18cd34 100644 --- a/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx +++ b/docs/docs/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -10,6 +10,7 @@ import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; ## schnorr::verify_signature Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). +See schnorr::verify_signature_slice for a version that works directly on slices. #include_code schnorr_verify noir_stdlib/src/schnorr.nr rust @@ -34,3 +35,12 @@ const signature = Array.from( ``` + +## schnorr::verify_signature_slice + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) +where the message is a slice. + +#include_code schnorr_verify_slice noir_stdlib/src/schnorr.nr rust + + diff --git a/docs/docs/noir/standard_library/merkle_trees.md b/docs/docs/noir/standard_library/merkle_trees.md index fa488677884..6a9ebf72ada 100644 --- a/docs/docs/noir/standard_library/merkle_trees.md +++ b/docs/docs/noir/standard_library/merkle_trees.md @@ -42,9 +42,9 @@ fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3] let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); let pubkey_x = pubkey[0]; let pubkey_y = pubkey[1]; - let note_commitment = std::hash::pedersen([pubkey_x, pubkey_y, secret]); + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); - let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path); + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); println(root); } ``` diff --git a/docs/docs/noir/standard_library/recursion.md b/docs/docs/noir/standard_library/recursion.md index 9337499dac8..a93894043dc 100644 --- a/docs/docs/noir/standard_library/recursion.md +++ b/docs/docs/noir/standard_library/recursion.md @@ -29,8 +29,8 @@ By incorporating this attribute directly in the circuit's definition, tooling li ## Verifying Recursive Proofs ```rust -#[foreign(verify_proof)] -fn verify_proof(_verification_key : [Field], _proof : [Field], _public_input : Field, _key_hash : Field) {} +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} ``` :::info diff --git a/docs/docs/noir/standard_library/traits.md b/docs/docs/noir/standard_library/traits.md index fb6d5ae1c48..e6e7e6d40cb 100644 --- a/docs/docs/noir/standard_library/traits.md +++ b/docs/docs/noir/standard_library/traits.md @@ -32,6 +32,8 @@ impl Default for bool { .. } impl Default for [T; N] where T: Default { .. } +impl Default for [T] { .. } + impl Default for (A, B) where A: Default, B: Default { .. } @@ -46,7 +48,8 @@ impl Default for (A, B, C, D, E) ``` For primitive integer types, the return value of `default` is `0`. Container -types such as arrays are filled with default values of their element type. +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. ## `std::convert` @@ -112,6 +115,9 @@ impl Eq for bool { .. } impl Eq for [T; N] where T: Eq { .. } +impl Eq for [T] + where T: Eq { .. } + impl Eq for (A, B) where A: Eq, B: Eq { .. } @@ -154,6 +160,9 @@ impl Ord for bool { .. } impl Ord for [T; N] where T: Ord { .. } +impl Ord for [T] + where T: Ord { .. } + impl Ord for (A, B) where A: Ord, B: Ord { .. } diff --git a/docs/docs/noir/standard_library/zeroed.md b/docs/docs/noir/standard_library/zeroed.md index 97dab02dac2..f450fecdd36 100644 --- a/docs/docs/noir/standard_library/zeroed.md +++ b/docs/docs/noir/standard_library/zeroed.md @@ -18,6 +18,7 @@ This function currently supports the following types: - Bool - Uint - Array +- Slice - String - Tuple - Function diff --git a/noir_stdlib/src/bigint.nr b/noir_stdlib/src/bigint.nr index e7fc28ba39b..39ec40a1480 100644 --- a/noir_stdlib/src/bigint.nr +++ b/noir_stdlib/src/bigint.nr @@ -1,25 +1,24 @@ use crate::ops::{Add, Sub, Mul, Div}; use crate::cmp::Eq; -// docs:start:curve_order_base -global bn254_fq = [0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, - 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; -global bn254_fr = [0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28, - 0x5D, 0x58, 0x81, 0x81, 0xB6, 0x45, 0x50, 0xB8, 0x29, 0xA0, 0x31, 0xE1, 0x72, 0x4E, 0x64, 0x30]; -global secpk1_fr = [0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, - 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; -global secpk1_fq = [0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; -global secpr1_fq = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; -global secpr1_fr = [0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; -// docs:end:curve_order_base - +global bn254_fq = &[0x47, 0xFD, 0x7C, 0xD8, 0x16, 0x8C, 0x20, 0x3C, 0x8d, 0xca, 0x71, 0x68, 0x91, 0x6a, 0x81, 0x97, + 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30]; +global bn254_fr = &[0x01, 0x00, 0x00, 0x00, 0x3F, 0x59, 0x1F, 0x43, 0x09, 0x97, 0xB9, 0x79, 0x48, 0xE8, 0x33, 0x28, + 0x5D, 0x58, 0x81, 0x81, 0xB6, 0x45, 0x50, 0xB8, 0x29, 0xA0, 0x31, 0xE1, 0x72, 0x4E, 0x64, 0x30]; +global secpk1_fr = &[0x41, 0x41, 0x36, 0xD0, 0x8C, 0x5E, 0xD2, 0xBF, 0x3B, 0xA0, 0x48, 0xAF, 0xE6, 0xDC, 0xAE, 0xBA, + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; +global secpk1_fq = &[0x2F, 0xFC, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; +global secpr1_fq = &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF]; +global secpr1_fr = &[0x51, 0x25, 0x63, 0xFC, 0xC2, 0xCA, 0xB9, 0xF3, 0x84, 0x9E, 0x17, 0xA7, 0xAD, 0xFA, 0xE6, 0xBC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,0xFF, 0xFF, 0xFF, 0xFF]; +// docs:start:big_int_definition struct BigInt { pointer: u32, modulus: u32, } +// docs:end:big_int_definition impl BigInt { #[builtin(bigint_add)] diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index 38316e5d6a8..0eed50eb42b 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -28,6 +28,16 @@ impl Eq for [T; N] where T: Eq { } } +impl Eq for [T] where T: Eq { + fn eq(self, other: [T]) -> bool { + let mut result = self.len() == other.len(); + for i in 0 .. self.len() { + result &= self[i].eq(other[i]); + } + result + } +} + impl Eq for str { fn eq(self, other: str) -> bool { let self_bytes = self.as_bytes(); @@ -213,6 +223,26 @@ impl Ord for [T; N] where T: Ord { } } +impl Ord for [T] where T: Ord { + // The first non-equal element of both arrays determines + // the ordering for the whole array. + fn cmp(self, other: [T]) -> Ordering { + let mut result = self.len().cmp(other.len()); + for i in 0 .. self.len() { + if result == Ordering::equal() { + let result_i = self[i].cmp(other[i]); + + if result_i == Ordering::less() { + result = result_i; + } else if result_i == Ordering::greater() { + result = result_i; + } + } + } + result + } +} + impl Ord for (A, B) where A: Ord, B: Ord { fn cmp(self, other: (A, B)) -> Ordering { let result = self.0.cmp(other.0); diff --git a/noir_stdlib/src/collections/vec.nr b/noir_stdlib/src/collections/vec.nr index deec98185ff..56354438c89 100644 --- a/noir_stdlib/src/collections/vec.nr +++ b/noir_stdlib/src/collections/vec.nr @@ -5,7 +5,7 @@ struct Vec { // A separate type is technically not needed but helps differentiate which operations are mutable. impl Vec { pub fn new() -> Self { - Self { slice: [] } + Self { slice: &[] } } // Create a Vec containing each element from the given slice. diff --git a/noir_stdlib/src/default.nr b/noir_stdlib/src/default.nr index 32c4f3f3b48..bd2f1ce0cd2 100644 --- a/noir_stdlib/src/default.nr +++ b/noir_stdlib/src/default.nr @@ -23,6 +23,12 @@ impl Default for [T; N] where T: Default { } } +impl Default for [T] { + fn default() -> [T] { + &[] + } +} + impl Default for (A, B) where A: Default, B: Default { fn default() -> (A, B) { (A::default(), B::default()) diff --git a/noir_stdlib/src/ecdsa_secp256k1.nr b/noir_stdlib/src/ecdsa_secp256k1.nr index b72a1acd041..f84e2221f57 100644 --- a/noir_stdlib/src/ecdsa_secp256k1.nr +++ b/noir_stdlib/src/ecdsa_secp256k1.nr @@ -8,3 +8,15 @@ pub fn verify_signature( ) -> bool // docs:end:ecdsa_secp256k1 {} + +#[foreign(ecdsa_secp256k1)] +// docs:start:ecdsa_secp256k1_slice +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +// docs:end:ecdsa_secp256k1_slice +{} + diff --git a/noir_stdlib/src/ecdsa_secp256r1.nr b/noir_stdlib/src/ecdsa_secp256r1.nr index ef92bf24ae4..76e68aeeafa 100644 --- a/noir_stdlib/src/ecdsa_secp256r1.nr +++ b/noir_stdlib/src/ecdsa_secp256r1.nr @@ -8,3 +8,14 @@ pub fn verify_signature( ) -> bool // docs:end:ecdsa_secp256r1 {} + +#[foreign(ecdsa_secp256r1)] +// docs:start:ecdsa_secp256r1_slice +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +// docs:end:ecdsa_secp256r1_slice +{} diff --git a/noir_stdlib/src/hash.nr b/noir_stdlib/src/hash.nr index 896dae15371..1a61b5e084e 100644 --- a/noir_stdlib/src/hash.nr +++ b/noir_stdlib/src/hash.nr @@ -12,38 +12,69 @@ pub fn sha256(input: [u8; N]) -> [u8; 32] // docs:end:sha256 {} +#[foreign(sha256)] +// docs:start:sha256_slice +pub fn sha256_slice(input: [u8]) -> [u8; 32] +// docs:end:sha256_slice +{} + #[foreign(blake2s)] // docs:start:blake2s pub fn blake2s(input: [u8; N]) -> [u8; 32] // docs:end:blake2s {} +#[foreign(blake2s)] +// docs:start:blake2s_slice +pub fn blake2s_slice(input: [u8]) -> [u8; 32] +// docs:end:blake2s_slice +{} + #[foreign(blake3)] // docs:start:blake3 pub fn blake3(input: [u8; N]) -> [u8; 32] // docs:end:blake3 {} +#[foreign(blake3)] +// docs:start:blake3_slice +pub fn blake3_slice(input: [u8]) -> [u8; 32] +// docs:end:blake3_slice +{} + // docs:start:pedersen_commitment struct PedersenPoint { x : Field, y : Field, } -pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint -// docs:end:pedersen_commitment -{ +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { + // docs:end:pedersen_commitment pedersen_commitment_with_separator(input, 0) } +// docs:start:pedersen_commitment_slice +pub fn pedersen_commitment_slice(input: [Field]) -> PedersenPoint { + pedersen_commitment_with_separator_slice(input, 0) +} +// docs:end:pedersen_commitment_slice + #[foreign(pedersen_commitment)] pub fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} +#[foreign(pedersen_commitment)] +pub fn __pedersen_commitment_with_separator_slice(input: [Field], separator: u32) -> [Field; 2] {} + pub fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> PedersenPoint { let values = __pedersen_commitment_with_separator(input, separator); PedersenPoint { x: values[0], y: values[1] } } +pub fn pedersen_commitment_with_separator_slice(input: [Field], separator: u32) -> PedersenPoint { + let values = __pedersen_commitment_with_separator_slice(input, separator); + PedersenPoint { x: values[0], y: values[1] } +} + // docs:start:pedersen_hash pub fn pedersen_hash(input: [Field; N]) -> Field // docs:end:pedersen_hash @@ -51,20 +82,30 @@ pub fn pedersen_hash(input: [Field; N]) -> Field pedersen_hash_with_separator(input, 0) } +// docs:start:pedersen_hash_slice +pub fn pedersen_hash_slice(input: [Field]) -> Field +// docs:end:pedersen_hash_slice +{ + pedersen_hash_with_separator_slice(input, 0) +} + #[foreign(pedersen_hash)] pub fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} -pub fn hash_to_field(input: [Field; N]) -> Field { - let mut inputs_as_bytes = []; +#[foreign(pedersen_hash)] +pub fn pedersen_hash_with_separator_slice(input: [Field], separator: u32) -> Field {} + +pub fn hash_to_field(inputs: [Field]) -> Field { + let mut inputs_as_bytes = &[]; - for i in 0..N { - let input_bytes = input[i].to_le_bytes(32); + for input in inputs { + let input_bytes = input.to_le_bytes(32); for i in 0..32 { inputs_as_bytes = inputs_as_bytes.push_back(input_bytes[i]); } } - let hashed_input = blake2s(inputs_as_bytes); + let hashed_input = blake2s_slice(inputs_as_bytes); crate::field::bytes32_to_field(hashed_input) } @@ -74,6 +115,12 @@ pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] // docs:end:keccak256 {} +#[foreign(keccak256)] +// docs:start:keccak256_slice +pub fn keccak256_slice(input: [u8], message_size: u32) -> [u8; 32] +// docs:end:keccak256_slice +{} + #[foreign(poseidon2_permutation)] pub fn poseidon2_permutation(_input: [Field; N], _state_length: u32) -> [Field; N] {} @@ -123,49 +170,49 @@ where impl Hash for Field { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self]); + H::write(state, &[self]); } } impl Hash for u8 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for u32 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for u64 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for i8 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for i32 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for i64 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } impl Hash for bool { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self as Field]); + H::write(state, &[self as Field]); } } @@ -175,7 +222,7 @@ impl Hash for () { impl Hash for U128 { fn hash(self, state: &mut H) where H: Hasher{ - H::write(state, [self.lo as Field, self.hi as Field]); + H::write(state, &[self.lo as Field, self.hi as Field]); } } @@ -187,6 +234,15 @@ impl Hash for [T; N] where T: Hash { } } +impl Hash for [T] where T: Hash { + fn hash(self, state: &mut H) where H: Hasher{ + self.len().hash(state); + for elem in self { + elem.hash(state); + } + } +} + impl Hash for (A, B) where A: Hash, B: Hash { fn hash(self, state: &mut H) where H: Hasher{ self.0.hash(state); diff --git a/noir_stdlib/src/hash/mimc.nr b/noir_stdlib/src/hash/mimc.nr index db8a32d7909..1fb53701013 100644 --- a/noir_stdlib/src/hash/mimc.nr +++ b/noir_stdlib/src/hash/mimc.nr @@ -152,7 +152,7 @@ impl Hasher for MimcHasher { impl Default for MimcHasher{ fn default() -> Self{ MimcHasher{ - _state: [], + _state: &[], _len: 0, } } diff --git a/noir_stdlib/src/hash/pedersen.nr b/noir_stdlib/src/hash/pedersen.nr index ace6851099d..ad21e728945 100644 --- a/noir_stdlib/src/hash/pedersen.nr +++ b/noir_stdlib/src/hash/pedersen.nr @@ -1,4 +1,4 @@ -use crate::hash::{Hasher, pedersen_hash}; +use crate::hash::{Hasher, pedersen_hash_slice}; use crate::default::Default; struct PedersenHasher{ @@ -7,7 +7,7 @@ struct PedersenHasher{ impl Hasher for PedersenHasher { fn finish(self) -> Field { - pedersen_hash(self._state) + pedersen_hash_slice(self._state) } fn write(&mut self, input: [Field]){ @@ -18,7 +18,7 @@ impl Hasher for PedersenHasher { impl Default for PedersenHasher{ fn default() -> Self{ PedersenHasher{ - _state: [] + _state: &[] } } } diff --git a/noir_stdlib/src/hash/poseidon.nr b/noir_stdlib/src/hash/poseidon.nr index 7f99ad36316..85a0802f630 100644 --- a/noir_stdlib/src/hash/poseidon.nr +++ b/noir_stdlib/src/hash/poseidon.nr @@ -171,7 +171,7 @@ impl Hasher for PoseidonHasher { impl Default for PoseidonHasher{ fn default() -> Self{ PoseidonHasher{ - _state: [], + _state: &[], _len: 0, } } diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 52229f18dbd..5b97d809896 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -139,7 +139,7 @@ impl Hasher for Poseidon2Hasher { impl Default for Poseidon2Hasher{ fn default() -> Self{ Poseidon2Hasher{ - _state: [], + _state: &[], _len: 0, } } diff --git a/noir_stdlib/src/schnorr.nr b/noir_stdlib/src/schnorr.nr index 757963d40d7..c63915061cb 100644 --- a/noir_stdlib/src/schnorr.nr +++ b/noir_stdlib/src/schnorr.nr @@ -8,3 +8,15 @@ pub fn verify_signature( ) -> bool // docs:end:schnorr_verify {} + +#[foreign(schnorr_verify)] +// docs:start:schnorr_verify_slice +pub fn verify_signature_slice( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8] +) -> bool +// docs:end:schnorr_verify_slice +{} + diff --git a/noir_stdlib/src/slice.nr b/noir_stdlib/src/slice.nr index ea8d09d14ce..164b4f96cf6 100644 --- a/noir_stdlib/src/slice.nr +++ b/noir_stdlib/src/slice.nr @@ -1,4 +1,7 @@ impl [T] { + #[builtin(array_len)] + pub fn len(self) -> u64 {} + /// Push a new element to the end of the slice, returning a /// new slice with a length one greater than the /// original unmodified slice. diff --git a/test_programs/compile_success_empty/vectors/src/main.nr b/test_programs/compile_success_empty/vectors/src/main.nr index 28187a4f619..d105ceed180 100644 --- a/test_programs/compile_success_empty/vectors/src/main.nr +++ b/test_programs/compile_success_empty/vectors/src/main.nr @@ -26,7 +26,7 @@ fn main(x: Field, y: pub Field) { assert(vector.get(3) == 3); assert(vector.len() == 4); - let mut inputs_vector = Vec::from_slice([x, y]); + let mut inputs_vector = Vec::from_slice(&[x, y]); assert(inputs_vector.get(0) == x); assert(inputs_vector.get(1) == y); } diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index 4f5594c6d11..b97f68fc280 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -1,6 +1,6 @@ // Converts an array into a slice. fn as_slice_push(xs: [T; N]) -> [T] { - let mut slice = []; + let mut slice = &[]; for elem in xs { slice = slice.push_back(elem); } diff --git a/test_programs/execution_success/bigint/src/main.nr b/test_programs/execution_success/bigint/src/main.nr index b6c23276c15..db269d63ac0 100644 --- a/test_programs/execution_success/bigint/src/main.nr +++ b/test_programs/execution_success/bigint/src/main.nr @@ -2,8 +2,8 @@ use dep::std::bigint; use dep::std::{bigint::Secpk1Fq, println}; fn main(mut x: [u8; 5], y: [u8; 5]) { - let a = bigint::Secpk1Fq::from_le_bytes([x[0], x[1], x[2], x[3], x[4]]); - let b = bigint::Secpk1Fq::from_le_bytes([y[0], y[1], y[2], y[3], y[4]]); + let a = bigint::Secpk1Fq::from_le_bytes(&[x[0], x[1], x[2], x[3], x[4]]); + let b = bigint::Secpk1Fq::from_le_bytes(&[y[0], y[1], y[2], y[3], y[4]]); let a_bytes = a.to_le_bytes(); let b_bytes = b.to_le_bytes(); for i in 0..5 { @@ -19,8 +19,8 @@ fn main(mut x: [u8; 5], y: [u8; 5]) { // docs:start:big_int_example fn big_int_example(x: u8, y: u8) { - let a = Secpk1Fq::from_le_bytes([x, y, 0, 45, 2]); - let b = Secpk1Fq::from_le_bytes([y, x, 9]); + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); let c = (a + b) * b / a; let d = c.to_le_bytes(); println(d[0]); diff --git a/test_programs/execution_success/brillig_hash_to_field/src/main.nr b/test_programs/execution_success/brillig_hash_to_field/src/main.nr index 4b4177a521e..53ed85b3ddd 100644 --- a/test_programs/execution_success/brillig_hash_to_field/src/main.nr +++ b/test_programs/execution_success/brillig_hash_to_field/src/main.nr @@ -7,5 +7,5 @@ fn main(input: Field) -> pub Field { } unconstrained fn hash_to_field(input: Field) -> Field { - std::hash::hash_to_field([input]) + std::hash::hash_to_field(&[input]) } diff --git a/test_programs/execution_success/brillig_keccak/src/main.nr b/test_programs/execution_success/brillig_keccak/src/main.nr index 1e9b65a6eb4..a300bc18279 100644 --- a/test_programs/execution_success/brillig_keccak/src/main.nr +++ b/test_programs/execution_success/brillig_keccak/src/main.nr @@ -7,7 +7,7 @@ fn main(x: Field, result: [u8; 32]) { // The padding is taken care of by the program let digest = keccak256([x as u8], 1); assert(digest == result); - //#1399: variable meesage size + //#1399: variable message size let message_size = 4; let hash_a = keccak256([1, 2, 3, 4], message_size); let hash_b = keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); diff --git a/test_programs/execution_success/brillig_schnorr/src/main.nr b/test_programs/execution_success/brillig_schnorr/src/main.nr index 4cc79ae7e07..03c635b4f6f 100644 --- a/test_programs/execution_success/brillig_schnorr/src/main.nr +++ b/test_programs/execution_success/brillig_schnorr/src/main.nr @@ -17,7 +17,7 @@ unconstrained fn main( // Is there ever a situation where someone would want // to ensure that a signature was invalid? // Check that passing a slice as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message_field_bytes); + let valid_signature = std::schnorr::verify_signature_slice(pub_key_x, pub_key_y, signature, message_field_bytes); assert(valid_signature); // Check that passing an array as the message is valid let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); diff --git a/test_programs/execution_success/brillig_slices/src/main.nr b/test_programs/execution_success/brillig_slices/src/main.nr index 847c41de25c..2cf1850f151 100644 --- a/test_programs/execution_success/brillig_slices/src/main.nr +++ b/test_programs/execution_success/brillig_slices/src/main.nr @@ -1,6 +1,6 @@ use dep::std::slice; unconstrained fn main(x: Field, y: Field) { - let mut slice: [Field] = [y, x]; + let mut slice: [Field] = &[y, x]; assert(slice.len() == 2); slice = slice.push_back(7); @@ -108,7 +108,7 @@ unconstrained fn merge_slices_else(x: Field) { } // Test returning a merged slice without a mutation unconstrained fn merge_slices_return(x: Field, y: Field) -> [Field] { - let slice = [0; 2]; + let slice = &[0; 2]; if x != y { if x != 20 { slice.push_back(y) } else { slice } } else { @@ -117,7 +117,7 @@ unconstrained fn merge_slices_return(x: Field, y: Field) -> [Field] { } // Test mutating a slice inside of an if statement unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -128,7 +128,7 @@ unconstrained fn merge_slices_mutate(x: Field, y: Field) -> [Field] { } // Test mutating a slice inside of a loop in an if statement unconstrained fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { for i in 0..5 { slice = slice.push_back(i as Field); diff --git a/test_programs/execution_success/hash_to_field/src/main.nr b/test_programs/execution_success/hash_to_field/src/main.nr index 5af1c5af55e..242b5ecbc18 100644 --- a/test_programs/execution_success/hash_to_field/src/main.nr +++ b/test_programs/execution_success/hash_to_field/src/main.nr @@ -1,5 +1,5 @@ use dep::std; fn main(input: Field) -> pub Field { - std::hash::hash_to_field([input]) + std::hash::hash_to_field(&[input]) } diff --git a/test_programs/execution_success/nested_array_in_slice/src/main.nr b/test_programs/execution_success/nested_array_in_slice/src/main.nr index a3007d5d0dc..0890115e95a 100644 --- a/test_programs/execution_success/nested_array_in_slice/src/main.nr +++ b/test_programs/execution_success/nested_array_in_slice/src/main.nr @@ -13,7 +13,7 @@ fn main(y: Field) { let foo_two = Foo { a: 4, b: [5, 6, 21], bar: Bar { inner: [103, 104, 105] } }; let foo_three = Foo { a: 7, b: [8, 9, 22], bar: Bar { inner: [106, 107, 108] } }; let foo_four = Foo { a: 10, b: [11, 12, 23], bar: Bar { inner: [109, 110, 111] } }; - let mut x = [foo_one]; + let mut x = &[foo_one]; x = x.push_back(foo_two); x = x.push_back(foo_three); x = x.push_back(foo_four); diff --git a/test_programs/execution_success/regression_4202/src/main.nr b/test_programs/execution_success/regression_4202/src/main.nr index 37d2ee4578d..faa14f03912 100644 --- a/test_programs/execution_success/regression_4202/src/main.nr +++ b/test_programs/execution_success/regression_4202/src/main.nr @@ -1,5 +1,5 @@ fn main(input: [u32; 4]) { - let mut slice1: [u32] = [1, 2, 3, 4]; + let mut slice1: [u32] = &[1, 2, 3, 4]; if slice1[0] == 3 { slice1[1] = 4; } diff --git a/test_programs/execution_success/schnorr/src/main.nr b/test_programs/execution_success/schnorr/src/main.nr index 107af152625..9e2838c34d9 100644 --- a/test_programs/execution_success/schnorr/src/main.nr +++ b/test_programs/execution_success/schnorr/src/main.nr @@ -17,7 +17,7 @@ fn main( // Is there ever a situation where someone would want // to ensure that a signature was invalid? // Check that passing a slice as the message is valid - let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message_field_bytes); + let valid_signature = std::schnorr::verify_signature_slice(pub_key_x, pub_key_y, signature, message_field_bytes); assert(valid_signature); // Check that passing an array as the message is valid let valid_signature = std::schnorr::verify_signature(pub_key_x, pub_key_y, signature, message); diff --git a/test_programs/execution_success/slice_dynamic_index/src/main.nr b/test_programs/execution_success/slice_dynamic_index/src/main.nr index 41fc9a645c1..9b97c0721bb 100644 --- a/test_programs/execution_success/slice_dynamic_index/src/main.nr +++ b/test_programs/execution_success/slice_dynamic_index/src/main.nr @@ -4,7 +4,7 @@ fn main(x: Field) { } fn regression_dynamic_slice_index(x: Field, y: Field) { - let mut slice = []; + let mut slice = &[]; for i in 0..5 { slice = slice.push_back(i as Field); } diff --git a/test_programs/execution_success/slices/src/main.nr b/test_programs/execution_success/slices/src/main.nr index e44edf872a4..b20e3478898 100644 --- a/test_programs/execution_success/slices/src/main.nr +++ b/test_programs/execution_success/slices/src/main.nr @@ -2,7 +2,7 @@ use dep::std::slice; use dep::std; fn main(x: Field, y: pub Field) { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; assert(slice[0] == 0); assert(slice[0] != 1); slice[0] = x; @@ -13,7 +13,7 @@ fn main(x: Field, y: pub Field) { assert(slice_plus_10[2] != 8); assert(slice_plus_10.len() == 3); - let mut new_slice = []; + let mut new_slice = &[]; for i in 0..5 { new_slice = new_slice.push_back(i); } @@ -41,7 +41,7 @@ fn main(x: Field, y: pub Field) { assert(remove_slice[3] == 3); assert(remove_slice.len() == 4); - let append = [1, 2].append([3, 4, 5]); + let append = &[1, 2].append(&[3, 4, 5]); assert(append.len() == 5); assert(append[0] == 1); assert(append[4] == 5); @@ -55,9 +55,10 @@ fn main(x: Field, y: pub Field) { regression_slice_call_result(x, y); regression_4506(); } + // Ensure that slices of struct/tuple values work. fn regression_2083() { - let y = [(1, 2)]; + let y = &[(1, 2)]; let y = y.push_back((3, 4)); // [(1, 2), (3, 4)] let y = y.push_back((5, 6)); // [(1, 2), (3, 4), (5, 6)] assert(y[2].1 == 6); @@ -86,6 +87,7 @@ fn regression_2083() { assert(x.0 == 5); assert(x.1 == 6); } + // The parameters to this function must come from witness values (inputs to main) fn regression_merge_slices(x: Field, y: Field) { merge_slices_if(x, y); @@ -146,18 +148,20 @@ fn merge_slices_else(x: Field) { assert(slice[2] == 5); assert(slice.len() == 3); } + // Test returning a merged slice without a mutation fn merge_slices_return(x: Field, y: Field) -> [Field] { - let slice = [0; 2]; + let slice = &[0; 2]; if x != y { if x != 20 { slice.push_back(y) } else { slice } } else { slice } } + // Test mutating a slice inside of an if statement fn merge_slices_mutate(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -166,9 +170,10 @@ fn merge_slices_mutate(x: Field, y: Field) -> [Field] { } slice } + // Test mutating a slice inside of a loop in an if statement fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { for i in 0..5 { slice = slice.push_back(i as Field); @@ -180,7 +185,7 @@ fn merge_slices_mutate_in_loop(x: Field, y: Field) -> [Field] { } fn merge_slices_mutate_two_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -199,7 +204,7 @@ fn merge_slices_mutate_two_ifs(x: Field, y: Field) -> [Field] { } fn merge_slices_mutate_between_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -225,7 +230,7 @@ fn merge_slices_mutate_between_ifs(x: Field, y: Field) -> [Field] { } fn merge_slices_push_then_pop(x: Field, y: Field) { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -249,7 +254,7 @@ fn merge_slices_push_then_pop(x: Field, y: Field) { } fn merge_slices_push_then_insert(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -272,7 +277,7 @@ fn merge_slices_push_then_insert(x: Field, y: Field) -> [Field] { } fn merge_slices_remove_between_ifs(x: Field, y: Field) -> [Field] { - let mut slice = [0; 2]; + let mut slice = &[0; 2]; if x != y { slice = slice.push_back(y); slice = slice.push_back(x); @@ -298,8 +303,8 @@ fn merge_slices_remove_between_ifs(x: Field, y: Field) -> [Field] { // Previously, we'd get a type error when trying to assign an array of a different size to // an existing array variable. Now, we infer the variable must be a slice. fn regression_2370() { - let mut slice = []; - slice = [1, 2, 3]; + let mut slice = &[]; + slice = &[1, 2, 3]; } fn regression_4418(x: Field) { @@ -327,6 +332,6 @@ fn regression_slice_call_result(x: Field, y: Field) { } fn regression_4506() { - let slice: [Field] = [1, 2, 3]; + let slice: [Field] = &[1, 2, 3]; assert(slice == slice); } diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 143ee7987f8..4de1b25f48a 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -12,7 +12,9 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); - let mut dbg_session = spawn_bash(Some(15000)).expect("Could not start bash session"); + let timeout_seconds = 120; + let mut dbg_session = + spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); // Set backend to `/dev/null` to force an error if nargo tries to speak to a backend. dbg_session diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index 66568bec833..0e9e177e023 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -93,16 +93,27 @@ impl DebugVars { .unwrap_or_else(|| panic!("type unavailable for type id {cursor_type_id:?}")); for index in indexes.iter() { (cursor, cursor_type) = match (cursor, cursor_type) { - (PrintableValue::Vec(array), PrintableType::Array { length, typ }) => { + ( + PrintableValue::Vec { array_elements, is_slice }, + PrintableType::Array { length, typ }, + ) => { + assert!(!*is_slice, "slice has array type"); if let Some(len) = length { if *index as u64 >= *len { panic!("unexpected field index past array length") } - if *len != array.len() as u64 { + if *len != array_elements.len() as u64 { panic!("type/array length mismatch") } } - (array.get_mut(*index as usize).unwrap(), &*Box::leak(typ.clone())) + (array_elements.get_mut(*index as usize).unwrap(), &*Box::leak(typ.clone())) + } + ( + PrintableValue::Vec { array_elements, is_slice }, + PrintableType::Slice { typ }, + ) => { + assert!(*is_slice, "array has slice type"); + (array_elements.get_mut(*index as usize).unwrap(), &*Box::leak(typ.clone())) } ( PrintableValue::Struct(field_map), @@ -114,18 +125,22 @@ impl DebugVars { let (key, typ) = fields.get(*index as usize).unwrap(); (field_map.get_mut(key).unwrap(), typ) } - (PrintableValue::Vec(array), PrintableType::Tuple { types }) => { + ( + PrintableValue::Vec { array_elements, is_slice }, + PrintableType::Tuple { types }, + ) => { + assert!(!*is_slice, "slice has tuple type"); if *index >= types.len() as u32 { panic!( "unexpected field index ({index}) past tuple length ({})", types.len() ); } - if types.len() != array.len() { + if types.len() != array_elements.len() { panic!("type/array length mismatch") } let typ = types.get(*index as usize).unwrap(); - (array.get_mut(*index as usize).unwrap(), typ) + (array_elements.get_mut(*index as usize).unwrap(), typ) } _ => { panic!("unexpected assign field of {cursor_type:?} type"); diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index f68ccbfd50e..a796eeac326 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -242,8 +242,8 @@ fn compile_success_empty_{test_name}() {{ }} // `compile_success_empty` tests should be able to compile down to an empty circuit. - let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|_| {{ - panic!("JSON was not well-formatted {{:?}}",output.stdout) + let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ + panic!("JSON was not well-formatted {{:?}}\n\n{{:?}}", e, std::str::from_utf8(&output.stdout)) }}); let num_opcodes = &json["programs"][0]["acir_opcodes"]; assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); diff --git a/tooling/nargo_fmt/src/rewrite/array.rs b/tooling/nargo_fmt/src/rewrite/array.rs index 77e5e756f19..db7dc4701b7 100644 --- a/tooling/nargo_fmt/src/rewrite/array.rs +++ b/tooling/nargo_fmt/src/rewrite/array.rs @@ -6,7 +6,12 @@ use crate::{ visitor::{expr::NewlineMode, FmtVisitor}, }; -pub(crate) fn rewrite(mut visitor: FmtVisitor, array: Vec, array_span: Span) -> String { +pub(crate) fn rewrite( + mut visitor: FmtVisitor, + array: Vec, + array_span: Span, + is_slice: bool, +) -> String { let pattern: &[_] = &[' ', '\t']; visitor.indent.block_indent(visitor.config); @@ -75,8 +80,9 @@ pub(crate) fn rewrite(mut visitor: FmtVisitor, array: Vec, array_spa } } + let open_bracket = if is_slice { "&[" } else { "[" }; crate::visitor::expr::wrap_exprs( - "[", + open_bracket, "]", items_str.trim().into(), nested_indent, diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index 32d104f559b..ffb8dea5a9d 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -122,7 +122,16 @@ pub(crate) fn rewrite( format!("[{repeated}; {length}]") } Literal::Array(ArrayLiteral::Standard(exprs)) => { - super::array(visitor.fork(), exprs, span) + super::array(visitor.fork(), exprs, span, false) + } + Literal::Slice(ArrayLiteral::Repeated { repeated_element, length }) => { + let repeated = rewrite_sub_expr(visitor, shape, *repeated_element); + let length = rewrite_sub_expr(visitor, shape, *length); + + format!("&[{repeated}; {length}]") + } + Literal::Slice(ArrayLiteral::Standard(exprs)) => { + super::array(visitor.fork(), exprs, span, true) } Literal::Unit => "()".to_string(), }, diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index aaa77b0bea5..922337cdb74 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -9,12 +9,12 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) match typ.typ { UnresolvedTypeData::Array(length, element) => { let typ = rewrite(visitor, _shape, *element); - if let Some(length) = length { - let length = visitor.slice(length.span()); - format!("[{typ}; {length}]") - } else { - format!("[{typ}]") - } + let length = visitor.slice(length.span()); + format!("[{typ}; {length}]") + } + UnresolvedTypeData::Slice(element) => { + let typ = rewrite(visitor, _shape, *element); + format!("[{typ}]") } UnresolvedTypeData::Parenthesized(typ) => { let typ = rewrite(visitor, _shape, *typ); diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 26feab65d83..26edb7a2af6 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -178,7 +178,7 @@ impl AbiType { | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) - | Type::NotConstant + | Type::Slice(_) | Type::Function(_, _, _) => unreachable!("Type cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"), From cba1bff95911a7a1d3ea714735786db595a435cf Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 19 Mar 2024 23:36:53 +0000 Subject: [PATCH 099/416] chore: fix assignees for opening issues based on CI (#4596) --- .github/ACVM_NOT_PUBLISHABLE.md | 3 +-- .github/CRATES_IO_PUBLISH_FAILED.md | 2 +- .github/DEAD_LINKS_IN_DOCS.md | 2 +- .github/JS_PUBLISH_FAILED.md | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/ACVM_NOT_PUBLISHABLE.md b/.github/ACVM_NOT_PUBLISHABLE.md index e7eacb3b523..33230f8e8d8 100644 --- a/.github/ACVM_NOT_PUBLISHABLE.md +++ b/.github/ACVM_NOT_PUBLISHABLE.md @@ -1,9 +1,8 @@ --- title: "ACVM crates are not publishable" -assignees: TomAFrench kevaundray savio-sou +assignees: TomAFrench, Savio-Sou --- - The ACVM crates are currently unpublishable, making a release will NOT push our crates to crates.io. This is likely due to a crate we depend on bumping its MSRV above our own. Our lockfile is not taken into account when publishing to crates.io (as people downloading our crate don't use it) so we need to be able to use the most up to date versions of our dependencies (including transient dependencies) specified. diff --git a/.github/CRATES_IO_PUBLISH_FAILED.md b/.github/CRATES_IO_PUBLISH_FAILED.md index ec4de319772..5e2d4b7b77f 100644 --- a/.github/CRATES_IO_PUBLISH_FAILED.md +++ b/.github/CRATES_IO_PUBLISH_FAILED.md @@ -1,6 +1,6 @@ --- title: "ACVM crates failed to publish" -assignees: TomAFrench kevaundray savio-sou +assignees: TomAFrench, Savio-Sou --- The {{env.CRATE_VERSION}} release of the ACVM crates failed. diff --git a/.github/DEAD_LINKS_IN_DOCS.md b/.github/DEAD_LINKS_IN_DOCS.md index b1671276dcf..dcd4f44cdf2 100644 --- a/.github/DEAD_LINKS_IN_DOCS.md +++ b/.github/DEAD_LINKS_IN_DOCS.md @@ -1,6 +1,6 @@ --- title: "Docs contains dead links" -assignees: signorecello catmcgee critesjosh jzaki Savio-Sou +assignees: signorecello, catmcgee, critesjosh, jzaki, Savio-Sou labels: documentation --- diff --git a/.github/JS_PUBLISH_FAILED.md b/.github/JS_PUBLISH_FAILED.md index 9adba2776c8..f91af361d7d 100644 --- a/.github/JS_PUBLISH_FAILED.md +++ b/.github/JS_PUBLISH_FAILED.md @@ -1,6 +1,6 @@ --- title: "JS packages failed to publish" -assignees: TomAFrench kevaundray Savio-Sou +assignees: TomAFrench, Savio-Sou labels: js --- From 859906ddf03ab33f7a14e8551f17cd1c1b59f25e Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:01:15 +0000 Subject: [PATCH 100/416] chore(ci): fix long debugger test times (#4599) # Description ## Problem\* Resolves ## Summary\* We're currently not closing the spawned bash process so we have to wait for the full timeout duration before the test registers as successful. This PR then adds an explicit call to `exit` to finish the test early. I've also shortened the timeout as 2 minutes seems excessive. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/debugger/tests/debug.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tooling/debugger/tests/debug.rs b/tooling/debugger/tests/debug.rs index 4de1b25f48a..b104a2c84ac 100644 --- a/tooling/debugger/tests/debug.rs +++ b/tooling/debugger/tests/debug.rs @@ -12,7 +12,7 @@ mod tests { let nargo_bin = cargo_bin("nargo").into_os_string().into_string().expect("Cannot parse nargo path"); - let timeout_seconds = 120; + let timeout_seconds = 20; let mut dbg_session = spawn_bash(Some(timeout_seconds * 1000)).expect("Could not start bash session"); @@ -51,5 +51,8 @@ mod tests { dbg_session .exp_regex(".*Circuit witness successfully solved.*") .expect("Expected circuit witness to be successfully solved."); + + // Exit the bash session. + dbg_session.send_line("exit").expect("Failed to quit bash session"); } } From 4c3a30b4991a329d3c52e1dfa59d854d7e6910db Mon Sep 17 00:00:00 2001 From: jfecher Date: Wed, 20 Mar 2024 10:48:06 -0500 Subject: [PATCH 101/416] feat: Add experimental `quote` expression to parser (#4595) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4586 Part of https://github.com/noir-lang/noir/issues/4594 ## Summary\* Adds a `quote { ... }` expression to the parser along with a new builtin `Code` type which it is the only method of creating. ## Additional Context The quote expression can only currently quote expressions and statements. It cannot yet quote top-level statements - we'd need more parser changes for this. In particular the top level statement parser would now need to be recursive and passed down all the way to the expression level... Trying to use `quote` in a program gives you an `experimental feature` warning. Indeed, the only thing you can do with it currently is panic once it gets to monomorphization without being removed from the runtime program yet. ## Documentation\* Check one: - [x] No documentation needed. - I'm following the pattern with other experimental features and waiting to document them until they're stable. For this, it means quote won't be documented until the base for metaprogramming is completed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/ast/expression.rs | 2 ++ .../src/hir/resolution/resolver.rs | 4 ++++ .../noirc_frontend/src/hir/type_check/expr.rs | 1 + compiler/noirc_frontend/src/hir_def/expr.rs | 1 + compiler/noirc_frontend/src/hir_def/types.rs | 22 ++++++++++++++++--- compiler/noirc_frontend/src/lexer/token.rs | 3 +++ .../src/monomorphization/mod.rs | 2 ++ compiler/noirc_frontend/src/node_interner.rs | 2 ++ compiler/noirc_frontend/src/parser/parser.rs | 16 +++++++++++++- tooling/nargo_fmt/src/rewrite/expr.rs | 18 ++++++++++----- tooling/noirc_abi/src/lib.rs | 3 ++- 11 files changed, 63 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index 09c09daf9b9..d646a6ca98a 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -27,6 +27,7 @@ pub enum ExpressionKind { Tuple(Vec), Lambda(Box), Parenthesized(Box), + Quote(BlockExpression), Error, } @@ -495,6 +496,7 @@ impl Display for ExpressionKind { } Lambda(lambda) => lambda.fmt(f), Parenthesized(sub_expr) => write!(f, "({sub_expr})"), + Quote(block) => write!(f, "quote {block}"), Error => write!(f, "Error"), } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index c6606386f7b..3fbde8a890b 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1085,6 +1085,7 @@ impl<'a> Resolver<'a> { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::TraitAsType(..) + | Type::Code | Type::Forall(_, _) => (), Type::Array(length, element_type) => { @@ -1620,6 +1621,9 @@ impl<'a> Resolver<'a> { }) }), ExpressionKind::Parenthesized(sub_expr) => return self.resolve_expression(*sub_expr), + + // The quoted expression isn't resolved since we don't want errors if variables aren't defined + ExpressionKind::Quote(block) => HirExpression::Quote(block), }; // If these lines are ever changed, make sure to change the early return diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index eb998b59755..6ee0460784f 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -332,6 +332,7 @@ impl<'interner> TypeChecker<'interner> { Type::Function(params, Box::new(lambda.return_type), Box::new(env_type)) } + HirExpression::Quote(_) => Type::Code, }; self.interner.push_expr_type(*expr_id, typ.clone()); diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 6a1b4385f0d..61743d2cdc7 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -31,6 +31,7 @@ pub enum HirExpression { Tuple(Vec), Lambda(HirLambda), Error, + Quote(crate::BlockExpression), } impl HirExpression { diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 7fb26aa3879..3c5627f739b 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -101,6 +101,9 @@ pub enum Type { /// bind to an integer without special checks to bind it to a non-type. Constant(u64), + /// The type of quoted code in macros. This is always a comptime-only type + Code, + /// The result of some type error. Remembering type errors as their own type variant lets /// us avoid issuing repeat type errors for the same item. For example, a lambda with /// an invalid type would otherwise issue a new error each time it is called @@ -144,6 +147,7 @@ impl Type { | Type::MutableReference(_) | Type::Forall(_, _) | Type::Constant(_) + | Type::Code | Type::Slice(_) | Type::Error => unreachable!("This type cannot exist as a parameter to main"), } @@ -626,6 +630,7 @@ impl Type { | Type::Constant(_) | Type::NamedGeneric(_, _) | Type::Forall(_, _) + | Type::Code | Type::TraitAsType(..) => false, Type::Array(length, elem) => { @@ -689,6 +694,7 @@ impl Type { | Type::Function(_, _, _) | Type::MutableReference(_) | Type::Forall(_, _) + | Type::Code | Type::Slice(_) | Type::TraitAsType(..) => false, @@ -852,6 +858,7 @@ impl std::fmt::Display for Type { Type::MutableReference(element) => { write!(f, "&mut {element}") } + Type::Code => write!(f, "Code"), } } } @@ -1529,6 +1536,7 @@ impl Type { | Type::Constant(_) | Type::TraitAsType(..) | Type::Error + | Type::Code | Type::Unit => self.clone(), } } @@ -1570,6 +1578,7 @@ impl Type { | Type::Constant(_) | Type::TraitAsType(..) | Type::Error + | Type::Code | Type::Unit => false, } } @@ -1621,9 +1630,14 @@ impl Type { // Expect that this function should only be called on instantiated types Forall(..) => unreachable!(), - TraitAsType(..) | FieldElement | Integer(_, _) | Bool | Constant(_) | Unit | Error => { - self.clone() - } + TraitAsType(..) + | FieldElement + | Integer(_, _) + | Bool + | Constant(_) + | Unit + | Code + | Error => self.clone(), } } @@ -1752,6 +1766,7 @@ impl From<&Type> for PrintableType { Type::MutableReference(typ) => { PrintableType::MutableReference { typ: Box::new(typ.as_ref().into()) } } + Type::Code => unreachable!(), } } } @@ -1836,6 +1851,7 @@ impl std::fmt::Debug for Type { Type::MutableReference(element) => { write!(f, "&mut {element:?}") } + Type::Code => write!(f, "Code"), } } } diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 1e341d34510..4432a3f9e07 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -667,6 +667,7 @@ pub enum Keyword { Mod, Mut, Pub, + Quote, Return, ReturnData, String, @@ -710,6 +711,7 @@ impl fmt::Display for Keyword { Keyword::Mod => write!(f, "mod"), Keyword::Mut => write!(f, "mut"), Keyword::Pub => write!(f, "pub"), + Keyword::Quote => write!(f, "quote"), Keyword::Return => write!(f, "return"), Keyword::ReturnData => write!(f, "return_data"), Keyword::String => write!(f, "str"), @@ -756,6 +758,7 @@ impl Keyword { "mod" => Keyword::Mod, "mut" => Keyword::Mut, "pub" => Keyword::Pub, + "quote" => Keyword::Quote, "return" => Keyword::Return, "return_data" => Keyword::ReturnData, "str" => Keyword::String, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index d4fe2e0a2aa..9b0f57a7d39 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -516,6 +516,7 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("Encountered HirExpression::MethodCall during monomorphization {hir_method_call:?}") } HirExpression::Error => unreachable!("Encountered Error node during monomorphization"), + HirExpression::Quote(_) => unreachable!("quote expression remaining in runtime code"), }; Ok(expr) @@ -954,6 +955,7 @@ impl<'interner> Monomorphizer<'interner> { HirType::Forall(_, _) | HirType::Constant(_) | HirType::Error => { unreachable!("Unexpected type {} found", typ) } + HirType::Code => unreachable!("Tried to translate Code type into runtime code"), } } diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 4e184e1391b..09e0cb04d26 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1672,6 +1672,7 @@ enum TypeMethodKey { Tuple, Function, Generic, + Code, } fn get_type_method_key(typ: &Type) -> Option { @@ -1691,6 +1692,7 @@ fn get_type_method_key(typ: &Type) -> Option { Type::Tuple(_) => Some(Tuple), Type::Function(_, _, _) => Some(Function), Type::NamedGeneric(_, _) => Some(Generic), + Type::Code => Some(Code), Type::MutableReference(element) => get_type_method_key(element), Type::Alias(alias, _) => get_type_method_key(&alias.borrow().typ), diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index f2e22da3c2c..dec1c7aa9ce 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1150,7 +1150,8 @@ where nothing().boxed() }, lambdas::lambda(expr_parser.clone()), - block(statement).map(ExpressionKind::Block), + block(statement.clone()).map(ExpressionKind::Block), + quote(statement), variable(), literal(), )) @@ -1175,6 +1176,19 @@ where .labelled(ParsingRuleLabel::Atom) } +fn quote<'a, P>(statement: P) -> impl NoirParser + 'a +where + P: NoirParser + 'a, +{ + keyword(Keyword::Quote).ignore_then(block(statement)).validate(|block, span, emit| { + emit(ParserError::with_reason( + ParserErrorReason::ExperimentalFeature("quoted expressions"), + span, + )); + ExpressionKind::Quote(block) + }) +} + fn tuple

(expr_parser: P) -> impl NoirParser where P: ExprParser, diff --git a/tooling/nargo_fmt/src/rewrite/expr.rs b/tooling/nargo_fmt/src/rewrite/expr.rs index ffb8dea5a9d..6cf69a2309d 100644 --- a/tooling/nargo_fmt/src/rewrite/expr.rs +++ b/tooling/nargo_fmt/src/rewrite/expr.rs @@ -1,4 +1,7 @@ -use noirc_frontend::{token::Token, ArrayLiteral, Expression, ExpressionKind, Literal, UnaryOp}; +use noirc_frontend::{ + macros_api::Span, token::Token, ArrayLiteral, BlockExpression, Expression, ExpressionKind, + Literal, UnaryOp, +}; use crate::visitor::{ expr::{format_brackets, format_parens, NewlineMode}, @@ -20,11 +23,7 @@ pub(crate) fn rewrite( shape: Shape, ) -> String { match kind { - ExpressionKind::Block(block) => { - let mut visitor = visitor.fork(); - visitor.visit_block(block, span); - visitor.finish() - } + ExpressionKind::Block(block) => rewrite_block(visitor, block, span), ExpressionKind::Prefix(prefix) => { let op = match prefix.operator { UnaryOp::Minus => "-", @@ -159,6 +158,13 @@ pub(crate) fn rewrite( visitor.format_if(*if_expr) } ExpressionKind::Lambda(_) | ExpressionKind::Variable(_) => visitor.slice(span).to_string(), + ExpressionKind::Quote(block) => format!("quote {}", rewrite_block(visitor, block, span)), ExpressionKind::Error => unreachable!(), } } + +fn rewrite_block(visitor: &FmtVisitor, block: BlockExpression, span: Span) -> String { + let mut visitor = visitor.fork(); + visitor.visit_block(block, span); + visitor.finish() +} diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 26edb7a2af6..cffb0c20cd5 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -178,8 +178,9 @@ impl AbiType { | Type::TypeVariable(_, _) | Type::NamedGeneric(..) | Type::Forall(..) + | Type::Code | Type::Slice(_) - | Type::Function(_, _, _) => unreachable!("Type cannot be used in the abi"), + | Type::Function(_, _, _) => unreachable!("{typ} cannot be used in the abi"), Type::FmtString(_, _) => unreachable!("format strings cannot be used in the abi"), Type::MutableReference(_) => unreachable!("&mut cannot be used in the abi"), } From 77f03815e5682adceb055e29140840d196feb338 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:00:01 +0000 Subject: [PATCH 102/416] chore: fix up benchmarking scripts (#4601) # Description ## Problem\* Resolves ## Summary\* This PR gets our criterion benchmarks working again after we made various changes to the repo structure. I've also changed it to bench proving times. I'm going to follow up with changes to run this in CI. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/benches/criterion.rs | 24 +++++++++++++++++------- tooling/nargo_cli/benches/utils.rs | 13 +++++++++---- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/tooling/nargo_cli/benches/criterion.rs b/tooling/nargo_cli/benches/criterion.rs index a7b094fd7aa..9f67bcffd6e 100644 --- a/tooling/nargo_cli/benches/criterion.rs +++ b/tooling/nargo_cli/benches/criterion.rs @@ -1,9 +1,10 @@ //! Select representative tests to bench with criterion use assert_cmd::prelude::{CommandCargoExt, OutputAssertExt}; use criterion::{criterion_group, criterion_main, Criterion}; + use paste::paste; use pprof::criterion::{Output, PProfProfiler}; -use std::process::Command; +use std::{process::Command, time::Duration}; include!("./utils.rs"); macro_rules! criterion_command { @@ -15,9 +16,11 @@ macro_rules! criterion_command { let mut cmd = Command::cargo_bin("nargo").unwrap(); cmd.arg("--program-dir").arg(&test_program_dir); cmd.arg($command_string); + cmd.arg("--force"); - c.bench_function(&format!("{}_{}", test_program_dir.file_name().unwrap().to_str().unwrap(), $command_string), |b| { - b.iter(|| cmd.assert()) + let benchmark_name = format!("{}_{}", test_program_dir.file_name().unwrap().to_str().unwrap(), $command_string); + c.bench_function(&benchmark_name, |b| { + b.iter(|| cmd.assert().success()) }); } } @@ -25,9 +28,16 @@ macro_rules! criterion_command { }; } criterion_command!(execution, "execute"); +criterion_command!(prove, "prove"); + +criterion_group! { + name = execution_benches; + config = Criterion::default().sample_size(20).measurement_time(Duration::from_secs(20)).with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = criterion_selected_tests_execution +} criterion_group! { - name = benches; - config = Criterion::default().sample_size(20).with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = criterion_selected_tests_execution + name = prove_benches; + config = Criterion::default().sample_size(10).measurement_time(Duration::from_secs(20)).with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); + targets = criterion_selected_tests_prove } -criterion_main!(benches); +criterion_main!(execution_benches, prove_benches); diff --git a/tooling/nargo_cli/benches/utils.rs b/tooling/nargo_cli/benches/utils.rs index 52a6b718c44..47968f7e898 100644 --- a/tooling/nargo_cli/benches/utils.rs +++ b/tooling/nargo_cli/benches/utils.rs @@ -4,11 +4,16 @@ use std::path::PathBuf; fn get_selected_tests() -> Vec { let manifest_dir = match std::env::var("CARGO_MANIFEST_DIR") { Ok(dir) => PathBuf::from(dir), - Err(_) => std::env::current_dir().unwrap().join("crates").join("nargo_cli"), + Err(_) => std::env::current_dir().unwrap(), }; - let test_dir = manifest_dir.join("tests").join("execution_success"); + let test_dir = manifest_dir + .parent() + .unwrap() + .parent() + .unwrap() + .join("test_programs") + .join("execution_success"); - let selected_tests = - vec!["8_integration", "sha256_blocks", "struct", "eddsa", "regression", "regression_2099"]; + let selected_tests = vec!["struct", "eddsa", "regression"]; selected_tests.into_iter().map(|t| test_dir.join(t)).collect() } From 3d1e45680b07e3c98432b7bfb8ff32c4437dc5f8 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Wed, 20 Mar 2024 16:18:51 -0400 Subject: [PATCH 103/416] chore: regression test for #2540 (#4602) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/2540 ## Summary\* Fixed in https://github.com/noir-lang/noir/pull/4504 ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/tests.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 98dbc42adcd..6f92cb52a88 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1256,6 +1256,22 @@ fn lambda$f1(mut env$l1: (Field)) -> Field { assert_eq!(get_program_errors(src).len(), 2); } + // Regression for #2540 + #[test] + fn for_loop_over_array() { + let src = r#" + fn hello(_array: [u1; N]) { + for _ in 0..N {} + } + + fn main() { + let array: [u1; 2] = [0, 1]; + hello(array); + } + "#; + assert_eq!(get_program_errors(src).len(), 0); + } + // Regression for #4545 #[test] fn type_aliases_in_main() { From 13eb71b8de44eb6aad9c37943ad06fc73db589f5 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Thu, 21 Mar 2024 06:31:07 -0400 Subject: [PATCH 104/416] feat: Sync from aztec-packages (#4581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE feat: Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) feat(acir)!: Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) feat!: automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) END_COMMIT_OVERRIDE --------- Co-authored-by: vezenovm Co-authored-by: Álvaro Rodríguez --- .aztec-sync-commit | 2 +- Cargo.lock | 27 +- acvm-repo/acir/codegen/acir.cpp | 1792 +++++++++-------- acvm-repo/acir/codegen/witness.cpp | 130 +- acvm-repo/acir/src/circuit/mod.rs | 76 +- acvm-repo/acir/src/lib.rs | 10 +- acvm-repo/acir/src/native_types/mod.rs | 4 +- .../acir/src/native_types/witness_stack.rs | 64 + .../acir/tests/test_program_serialization.rs | 119 +- acvm-repo/acvm_js/src/compression.rs | 15 +- acvm-repo/acvm_js/src/execute.rs | 18 +- acvm-repo/acvm_js/src/public_witness.rs | 47 +- acvm-repo/acvm_js/test/shared/addition.ts | 10 +- .../test/shared/complex_foreign_call.ts | 14 +- .../test/shared/fixed_base_scalar_mul.ts | 6 +- acvm-repo/acvm_js/test/shared/foreign_call.ts | 8 +- acvm-repo/acvm_js/test/shared/memory_op.ts | 8 +- acvm-repo/acvm_js/test/shared/pedersen.ts | 4 +- .../acvm_js/test/shared/schnorr_verify.ts | 24 +- .../test/shared/witness_compression.ts | 22 +- acvm-repo/brillig/src/opcodes.rs | 7 + acvm-repo/brillig_vm/src/lib.rs | 55 + aztec_macros/Cargo.toml | 2 + aztec_macros/src/lib.rs | 58 +- aztec_macros/src/transforms/mod.rs | 1 + aztec_macros/src/transforms/note_interface.rs | 583 ++++++ aztec_macros/src/utils/ast_utils.rs | 12 +- aztec_macros/src/utils/errors.rs | 10 +- compiler/noirc_driver/src/contract.rs | 8 +- compiler/noirc_driver/src/lib.rs | 9 +- compiler/noirc_driver/src/program.rs | 8 +- .../src/brillig/brillig_gen/brillig_block.rs | 27 +- .../src/brillig/brillig_ir/debug_show.rs | 18 + .../src/brillig/brillig_ir/instructions.rs | 19 + compiler/noirc_evaluator/src/errors.rs | 4 +- .../src/hir/def_collector/dc_mod.rs | 7 +- .../noirc_frontend/src/hir/def_map/mod.rs | 3 +- .../src/hir/type_check/errors.rs | 4 +- .../noirc_frontend/src/hir/type_check/expr.rs | 2 +- compiler/noirc_frontend/src/lib.rs | 1 + .../unconstrained_oracle/Nargo.toml | 2 + .../unconstrained_ref/Nargo.toml | 2 + .../unconstrained_ref/src/main.nr | 2 +- .../witness_compression/Nargo.toml | 7 + .../witness_compression/Prover.toml | 2 + .../witness_compression/src/main.nr | 7 + tooling/acvm_cli/src/cli/execute_cmd.rs | 6 +- tooling/acvm_cli/src/errors.rs | 2 +- tooling/backend_interface/src/proof_system.rs | 39 +- .../backend_interface/src/smart_contract.rs | 15 +- tooling/bb_abstraction_leaks/build.rs | 2 +- tooling/debugger/src/dap.rs | 9 +- tooling/nargo/src/artifacts/contract.rs | 8 +- tooling/nargo/src/artifacts/program.rs | 24 +- tooling/nargo/src/ops/optimize.rs | 19 +- tooling/nargo/src/ops/test.rs | 15 +- tooling/nargo/src/ops/transform.rs | 25 +- .../nargo_cli/src/cli/codegen_verifier_cmd.rs | 8 +- tooling/nargo_cli/src/cli/compile_cmd.rs | 4 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 2 +- tooling/nargo_cli/src/cli/execute_cmd.rs | 3 +- tooling/nargo_cli/src/cli/fs/program.rs | 9 +- tooling/nargo_cli/src/cli/fs/witness.rs | 6 +- tooling/nargo_cli/src/cli/info_cmd.rs | 8 +- tooling/nargo_cli/src/cli/prove_cmd.rs | 5 +- tooling/nargo_cli/src/cli/verify_cmd.rs | 2 +- tooling/nargo_cli/src/errors.rs | 6 +- .../noir_js_backend_barretenberg/package.json | 2 +- .../noir_js_backend_barretenberg/src/index.ts | 1 - yarn.lock | 10 +- 70 files changed, 2305 insertions(+), 1185 deletions(-) create mode 100644 acvm-repo/acir/src/native_types/witness_stack.rs create mode 100644 aztec_macros/src/transforms/note_interface.rs create mode 100644 test_programs/execution_success/witness_compression/Nargo.toml create mode 100644 test_programs/execution_success/witness_compression/Prover.toml create mode 100644 test_programs/execution_success/witness_compression/src/main.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 973bfd1534b..ead8f3147c0 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -82f8cf5eba9deacdab43ad4ef95dbf27dd1c11c7 +208abbb63af4c9a3f25d723fe1c49e82aa461061 diff --git a/Cargo.lock b/Cargo.lock index 37bf5135468..99c19417267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,6 +439,7 @@ dependencies = [ "iter-extended", "noirc_errors", "noirc_frontend", + "regex", ] [[package]] @@ -2655,9 +2656,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -3822,14 +3823,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.3", - "regex-syntax 0.7.4", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", ] [[package]] @@ -3846,10 +3847,16 @@ name = "regex-automata" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.8.2", ] [[package]] @@ -3864,6 +3871,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "region" version = "3.0.0" diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index 11afd44ed6d..ca281d89637 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -3,7 +3,7 @@ #include "serde.hpp" #include "bincode.hpp" -namespace Circuit { +namespace Program { struct BinaryFieldOp { @@ -152,7 +152,7 @@ namespace Circuit { }; struct HeapArray { - Circuit::MemoryAddress pointer; + Program::MemoryAddress pointer; uint64_t size; friend bool operator==(const HeapArray&, const HeapArray&); @@ -161,8 +161,8 @@ namespace Circuit { }; struct HeapVector { - Circuit::MemoryAddress pointer; - Circuit::MemoryAddress size; + Program::MemoryAddress pointer; + Program::MemoryAddress size; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -172,8 +172,8 @@ namespace Circuit { struct BlackBoxOp { struct Sha256 { - Circuit::HeapVector message; - Circuit::HeapArray output; + Program::HeapVector message; + Program::HeapArray output; friend bool operator==(const Sha256&, const Sha256&); std::vector bincodeSerialize() const; @@ -181,8 +181,8 @@ namespace Circuit { }; struct Blake2s { - Circuit::HeapVector message; - Circuit::HeapArray output; + Program::HeapVector message; + Program::HeapArray output; friend bool operator==(const Blake2s&, const Blake2s&); std::vector bincodeSerialize() const; @@ -190,8 +190,8 @@ namespace Circuit { }; struct Blake3 { - Circuit::HeapVector message; - Circuit::HeapArray output; + Program::HeapVector message; + Program::HeapArray output; friend bool operator==(const Blake3&, const Blake3&); std::vector bincodeSerialize() const; @@ -199,8 +199,8 @@ namespace Circuit { }; struct Keccak256 { - Circuit::HeapVector message; - Circuit::HeapArray output; + Program::HeapVector message; + Program::HeapArray output; friend bool operator==(const Keccak256&, const Keccak256&); std::vector bincodeSerialize() const; @@ -208,8 +208,8 @@ namespace Circuit { }; struct Keccakf1600 { - Circuit::HeapVector message; - Circuit::HeapArray output; + Program::HeapVector message; + Program::HeapArray output; friend bool operator==(const Keccakf1600&, const Keccakf1600&); std::vector bincodeSerialize() const; @@ -217,11 +217,11 @@ namespace Circuit { }; struct EcdsaSecp256k1 { - Circuit::HeapVector hashed_msg; - Circuit::HeapArray public_key_x; - Circuit::HeapArray public_key_y; - Circuit::HeapArray signature; - Circuit::MemoryAddress result; + Program::HeapVector hashed_msg; + Program::HeapArray public_key_x; + Program::HeapArray public_key_y; + Program::HeapArray signature; + Program::MemoryAddress result; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -229,11 +229,11 @@ namespace Circuit { }; struct EcdsaSecp256r1 { - Circuit::HeapVector hashed_msg; - Circuit::HeapArray public_key_x; - Circuit::HeapArray public_key_y; - Circuit::HeapArray signature; - Circuit::MemoryAddress result; + Program::HeapVector hashed_msg; + Program::HeapArray public_key_x; + Program::HeapArray public_key_y; + Program::HeapArray signature; + Program::MemoryAddress result; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -241,11 +241,11 @@ namespace Circuit { }; struct SchnorrVerify { - Circuit::MemoryAddress public_key_x; - Circuit::MemoryAddress public_key_y; - Circuit::HeapVector message; - Circuit::HeapVector signature; - Circuit::MemoryAddress result; + Program::MemoryAddress public_key_x; + Program::MemoryAddress public_key_y; + Program::HeapVector message; + Program::HeapVector signature; + Program::MemoryAddress result; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -253,9 +253,9 @@ namespace Circuit { }; struct PedersenCommitment { - Circuit::HeapVector inputs; - Circuit::MemoryAddress domain_separator; - Circuit::HeapArray output; + Program::HeapVector inputs; + Program::MemoryAddress domain_separator; + Program::HeapArray output; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; @@ -263,9 +263,9 @@ namespace Circuit { }; struct PedersenHash { - Circuit::HeapVector inputs; - Circuit::MemoryAddress domain_separator; - Circuit::MemoryAddress output; + Program::HeapVector inputs; + Program::MemoryAddress domain_separator; + Program::MemoryAddress output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -273,9 +273,9 @@ namespace Circuit { }; struct FixedBaseScalarMul { - Circuit::MemoryAddress low; - Circuit::MemoryAddress high; - Circuit::HeapArray result; + Program::MemoryAddress low; + Program::MemoryAddress high; + Program::HeapArray result; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); std::vector bincodeSerialize() const; @@ -283,11 +283,11 @@ namespace Circuit { }; struct EmbeddedCurveAdd { - Circuit::MemoryAddress input1_x; - Circuit::MemoryAddress input1_y; - Circuit::MemoryAddress input2_x; - Circuit::MemoryAddress input2_y; - Circuit::HeapArray result; + Program::MemoryAddress input1_x; + Program::MemoryAddress input1_y; + Program::MemoryAddress input2_x; + Program::MemoryAddress input2_y; + Program::HeapArray result; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); std::vector bincodeSerialize() const; @@ -295,9 +295,9 @@ namespace Circuit { }; struct BigIntAdd { - Circuit::MemoryAddress lhs; - Circuit::MemoryAddress rhs; - Circuit::MemoryAddress output; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; @@ -305,9 +305,9 @@ namespace Circuit { }; struct BigIntSub { - Circuit::MemoryAddress lhs; - Circuit::MemoryAddress rhs; - Circuit::MemoryAddress output; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; friend bool operator==(const BigIntSub&, const BigIntSub&); std::vector bincodeSerialize() const; @@ -315,9 +315,9 @@ namespace Circuit { }; struct BigIntMul { - Circuit::MemoryAddress lhs; - Circuit::MemoryAddress rhs; - Circuit::MemoryAddress output; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; @@ -325,9 +325,9 @@ namespace Circuit { }; struct BigIntDiv { - Circuit::MemoryAddress lhs; - Circuit::MemoryAddress rhs; - Circuit::MemoryAddress output; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; @@ -335,9 +335,9 @@ namespace Circuit { }; struct BigIntFromLeBytes { - Circuit::HeapVector inputs; - Circuit::HeapVector modulus; - Circuit::MemoryAddress output; + Program::HeapVector inputs; + Program::HeapVector modulus; + Program::MemoryAddress output; friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); std::vector bincodeSerialize() const; @@ -345,8 +345,8 @@ namespace Circuit { }; struct BigIntToLeBytes { - Circuit::MemoryAddress input; - Circuit::HeapVector output; + Program::MemoryAddress input; + Program::HeapVector output; friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); std::vector bincodeSerialize() const; @@ -354,9 +354,9 @@ namespace Circuit { }; struct Poseidon2Permutation { - Circuit::HeapVector message; - Circuit::HeapArray output; - Circuit::MemoryAddress len; + Program::HeapVector message; + Program::HeapArray output; + Program::MemoryAddress len; friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); std::vector bincodeSerialize() const; @@ -364,9 +364,9 @@ namespace Circuit { }; struct Sha256Compression { - Circuit::HeapVector input; - Circuit::HeapVector hash_values; - Circuit::HeapArray output; + Program::HeapVector input; + Program::HeapVector hash_values; + Program::HeapArray output; friend bool operator==(const Sha256Compression&, const Sha256Compression&); std::vector bincodeSerialize() const; @@ -391,7 +391,7 @@ namespace Circuit { }; struct Array { - std::vector value_types; + std::vector value_types; uint64_t size; friend bool operator==(const Array&, const Array&); @@ -400,7 +400,7 @@ namespace Circuit { }; struct Vector { - std::vector value_types; + std::vector value_types; friend bool operator==(const Vector&, const Vector&); std::vector bincodeSerialize() const; @@ -425,7 +425,7 @@ namespace Circuit { struct ValueOrArray { struct MemoryAddress { - Circuit::MemoryAddress value; + Program::MemoryAddress value; friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; @@ -433,7 +433,7 @@ namespace Circuit { }; struct HeapArray { - Circuit::HeapArray value; + Program::HeapArray value; friend bool operator==(const HeapArray&, const HeapArray&); std::vector bincodeSerialize() const; @@ -441,7 +441,7 @@ namespace Circuit { }; struct HeapVector { - Circuit::HeapVector value; + Program::HeapVector value; friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; @@ -458,10 +458,10 @@ namespace Circuit { struct BrilligOpcode { struct BinaryFieldOp { - Circuit::MemoryAddress destination; - Circuit::BinaryFieldOp op; - Circuit::MemoryAddress lhs; - Circuit::MemoryAddress rhs; + Program::MemoryAddress destination; + Program::BinaryFieldOp op; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; @@ -469,11 +469,11 @@ namespace Circuit { }; struct BinaryIntOp { - Circuit::MemoryAddress destination; - Circuit::BinaryIntOp op; + Program::MemoryAddress destination; + Program::BinaryIntOp op; uint32_t bit_size; - Circuit::MemoryAddress lhs; - Circuit::MemoryAddress rhs; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; @@ -481,8 +481,8 @@ namespace Circuit { }; struct Cast { - Circuit::MemoryAddress destination; - Circuit::MemoryAddress source; + Program::MemoryAddress destination; + Program::MemoryAddress source; uint32_t bit_size; friend bool operator==(const Cast&, const Cast&); @@ -491,7 +491,7 @@ namespace Circuit { }; struct JumpIfNot { - Circuit::MemoryAddress condition; + Program::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIfNot&, const JumpIfNot&); @@ -500,7 +500,7 @@ namespace Circuit { }; struct JumpIf { - Circuit::MemoryAddress condition; + Program::MemoryAddress condition; uint64_t location; friend bool operator==(const JumpIf&, const JumpIf&); @@ -517,7 +517,7 @@ namespace Circuit { }; struct CalldataCopy { - Circuit::MemoryAddress destination_address; + Program::MemoryAddress destination_address; uint64_t size; uint64_t offset; @@ -535,9 +535,9 @@ namespace Circuit { }; struct Const { - Circuit::MemoryAddress destination; + Program::MemoryAddress destination; uint32_t bit_size; - Circuit::Value value; + Program::Value value; friend bool operator==(const Const&, const Const&); std::vector bincodeSerialize() const; @@ -552,10 +552,10 @@ namespace Circuit { struct ForeignCall { std::string function; - std::vector destinations; - std::vector destination_value_types; - std::vector inputs; - std::vector input_value_types; + std::vector destinations; + std::vector destination_value_types; + std::vector inputs; + std::vector input_value_types; friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; @@ -563,17 +563,28 @@ namespace Circuit { }; struct Mov { - Circuit::MemoryAddress destination; - Circuit::MemoryAddress source; + Program::MemoryAddress destination; + Program::MemoryAddress source; friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; static Mov bincodeDeserialize(std::vector); }; + struct ConditionalMov { + Program::MemoryAddress destination; + Program::MemoryAddress source_a; + Program::MemoryAddress source_b; + Program::MemoryAddress condition; + + friend bool operator==(const ConditionalMov&, const ConditionalMov&); + std::vector bincodeSerialize() const; + static ConditionalMov bincodeDeserialize(std::vector); + }; + struct Load { - Circuit::MemoryAddress destination; - Circuit::MemoryAddress source_pointer; + Program::MemoryAddress destination; + Program::MemoryAddress source_pointer; friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; @@ -581,8 +592,8 @@ namespace Circuit { }; struct Store { - Circuit::MemoryAddress destination_pointer; - Circuit::MemoryAddress source; + Program::MemoryAddress destination_pointer; + Program::MemoryAddress source; friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; @@ -590,7 +601,7 @@ namespace Circuit { }; struct BlackBox { - Circuit::BlackBoxOp value; + Program::BlackBoxOp value; friend bool operator==(const BlackBox&, const BlackBox&); std::vector bincodeSerialize() const; @@ -612,7 +623,7 @@ namespace Circuit { static Stop bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); std::vector bincodeSerialize() const; @@ -628,7 +639,7 @@ namespace Circuit { }; struct FunctionInput { - Circuit::Witness witness; + Program::Witness witness; uint32_t num_bits; friend bool operator==(const FunctionInput&, const FunctionInput&); @@ -639,9 +650,9 @@ namespace Circuit { struct BlackBoxFuncCall { struct AND { - Circuit::FunctionInput lhs; - Circuit::FunctionInput rhs; - Circuit::Witness output; + Program::FunctionInput lhs; + Program::FunctionInput rhs; + Program::Witness output; friend bool operator==(const AND&, const AND&); std::vector bincodeSerialize() const; @@ -649,9 +660,9 @@ namespace Circuit { }; struct XOR { - Circuit::FunctionInput lhs; - Circuit::FunctionInput rhs; - Circuit::Witness output; + Program::FunctionInput lhs; + Program::FunctionInput rhs; + Program::Witness output; friend bool operator==(const XOR&, const XOR&); std::vector bincodeSerialize() const; @@ -659,7 +670,7 @@ namespace Circuit { }; struct RANGE { - Circuit::FunctionInput input; + Program::FunctionInput input; friend bool operator==(const RANGE&, const RANGE&); std::vector bincodeSerialize() const; @@ -667,8 +678,8 @@ namespace Circuit { }; struct SHA256 { - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; friend bool operator==(const SHA256&, const SHA256&); std::vector bincodeSerialize() const; @@ -676,8 +687,8 @@ namespace Circuit { }; struct Blake2s { - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; friend bool operator==(const Blake2s&, const Blake2s&); std::vector bincodeSerialize() const; @@ -685,8 +696,8 @@ namespace Circuit { }; struct Blake3 { - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; friend bool operator==(const Blake3&, const Blake3&); std::vector bincodeSerialize() const; @@ -694,11 +705,11 @@ namespace Circuit { }; struct SchnorrVerify { - Circuit::FunctionInput public_key_x; - Circuit::FunctionInput public_key_y; - std::vector signature; - std::vector message; - Circuit::Witness output; + Program::FunctionInput public_key_x; + Program::FunctionInput public_key_y; + std::vector signature; + std::vector message; + Program::Witness output; friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; @@ -706,9 +717,9 @@ namespace Circuit { }; struct PedersenCommitment { - std::vector inputs; + std::vector inputs; uint32_t domain_separator; - std::array outputs; + std::array outputs; friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; @@ -716,9 +727,9 @@ namespace Circuit { }; struct PedersenHash { - std::vector inputs; + std::vector inputs; uint32_t domain_separator; - Circuit::Witness output; + Program::Witness output; friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; @@ -726,11 +737,11 @@ namespace Circuit { }; struct EcdsaSecp256k1 { - std::vector public_key_x; - std::vector public_key_y; - std::vector signature; - std::vector hashed_message; - Circuit::Witness output; + std::vector public_key_x; + std::vector public_key_y; + std::vector signature; + std::vector hashed_message; + Program::Witness output; friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; @@ -738,11 +749,11 @@ namespace Circuit { }; struct EcdsaSecp256r1 { - std::vector public_key_x; - std::vector public_key_y; - std::vector signature; - std::vector hashed_message; - Circuit::Witness output; + std::vector public_key_x; + std::vector public_key_y; + std::vector signature; + std::vector hashed_message; + Program::Witness output; friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; @@ -750,9 +761,9 @@ namespace Circuit { }; struct FixedBaseScalarMul { - Circuit::FunctionInput low; - Circuit::FunctionInput high; - std::array outputs; + Program::FunctionInput low; + Program::FunctionInput high; + std::array outputs; friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); std::vector bincodeSerialize() const; @@ -760,11 +771,11 @@ namespace Circuit { }; struct EmbeddedCurveAdd { - Circuit::FunctionInput input1_x; - Circuit::FunctionInput input1_y; - Circuit::FunctionInput input2_x; - Circuit::FunctionInput input2_y; - std::array outputs; + Program::FunctionInput input1_x; + Program::FunctionInput input1_y; + Program::FunctionInput input2_x; + Program::FunctionInput input2_y; + std::array outputs; friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); std::vector bincodeSerialize() const; @@ -772,8 +783,8 @@ namespace Circuit { }; struct Keccak256 { - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; friend bool operator==(const Keccak256&, const Keccak256&); std::vector bincodeSerialize() const; @@ -781,9 +792,9 @@ namespace Circuit { }; struct Keccak256VariableLength { - std::vector inputs; - Circuit::FunctionInput var_message_size; - std::vector outputs; + std::vector inputs; + Program::FunctionInput var_message_size; + std::vector outputs; friend bool operator==(const Keccak256VariableLength&, const Keccak256VariableLength&); std::vector bincodeSerialize() const; @@ -791,8 +802,8 @@ namespace Circuit { }; struct Keccakf1600 { - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; friend bool operator==(const Keccakf1600&, const Keccakf1600&); std::vector bincodeSerialize() const; @@ -800,10 +811,10 @@ namespace Circuit { }; struct RecursiveAggregation { - std::vector verification_key; - std::vector proof; - std::vector public_inputs; - Circuit::FunctionInput key_hash; + std::vector verification_key; + std::vector proof; + std::vector public_inputs; + Program::FunctionInput key_hash; friend bool operator==(const RecursiveAggregation&, const RecursiveAggregation&); std::vector bincodeSerialize() const; @@ -851,7 +862,7 @@ namespace Circuit { }; struct BigIntFromLeBytes { - std::vector inputs; + std::vector inputs; std::vector modulus; uint32_t output; @@ -862,7 +873,7 @@ namespace Circuit { struct BigIntToLeBytes { uint32_t input; - std::vector outputs; + std::vector outputs; friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); std::vector bincodeSerialize() const; @@ -870,8 +881,8 @@ namespace Circuit { }; struct Poseidon2Permutation { - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; uint32_t len; friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); @@ -880,9 +891,9 @@ namespace Circuit { }; struct Sha256Compression { - std::vector inputs; - std::vector hash_values; - std::vector outputs; + std::vector inputs; + std::vector hash_values; + std::vector outputs; friend bool operator==(const Sha256Compression&, const Sha256Compression&); std::vector bincodeSerialize() const; @@ -905,8 +916,8 @@ namespace Circuit { }; struct Expression { - std::vector> mul_terms; - std::vector> linear_combinations; + std::vector> mul_terms; + std::vector> linear_combinations; std::string q_c; friend bool operator==(const Expression&, const Expression&); @@ -917,7 +928,7 @@ namespace Circuit { struct BrilligInputs { struct Single { - Circuit::Expression value; + Program::Expression value; friend bool operator==(const Single&, const Single&); std::vector bincodeSerialize() const; @@ -925,7 +936,7 @@ namespace Circuit { }; struct Array { - std::vector value; + std::vector value; friend bool operator==(const Array&, const Array&); std::vector bincodeSerialize() const; @@ -933,7 +944,7 @@ namespace Circuit { }; struct MemoryArray { - Circuit::BlockId value; + Program::BlockId value; friend bool operator==(const MemoryArray&, const MemoryArray&); std::vector bincodeSerialize() const; @@ -950,7 +961,7 @@ namespace Circuit { struct BrilligOutputs { struct Simple { - Circuit::Witness value; + Program::Witness value; friend bool operator==(const Simple&, const Simple&); std::vector bincodeSerialize() const; @@ -958,7 +969,7 @@ namespace Circuit { }; struct Array { - std::vector value; + std::vector value; friend bool operator==(const Array&, const Array&); std::vector bincodeSerialize() const; @@ -973,10 +984,10 @@ namespace Circuit { }; struct Brillig { - std::vector inputs; - std::vector outputs; - std::vector bytecode; - std::optional predicate; + std::vector inputs; + std::vector outputs; + std::vector bytecode; + std::optional predicate; friend bool operator==(const Brillig&, const Brillig&); std::vector bincodeSerialize() const; @@ -986,8 +997,8 @@ namespace Circuit { struct Directive { struct ToLeRadix { - Circuit::Expression a; - std::vector b; + Program::Expression a; + std::vector b; uint32_t radix; friend bool operator==(const ToLeRadix&, const ToLeRadix&); @@ -1003,9 +1014,9 @@ namespace Circuit { }; struct MemOp { - Circuit::Expression operation; - Circuit::Expression index; - Circuit::Expression value; + Program::Expression operation; + Program::Expression index; + Program::Expression value; friend bool operator==(const MemOp&, const MemOp&); std::vector bincodeSerialize() const; @@ -1015,7 +1026,7 @@ namespace Circuit { struct Opcode { struct AssertZero { - Circuit::Expression value; + Program::Expression value; friend bool operator==(const AssertZero&, const AssertZero&); std::vector bincodeSerialize() const; @@ -1023,7 +1034,7 @@ namespace Circuit { }; struct BlackBoxFuncCall { - Circuit::BlackBoxFuncCall value; + Program::BlackBoxFuncCall value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -1031,7 +1042,7 @@ namespace Circuit { }; struct Directive { - Circuit::Directive value; + Program::Directive value; friend bool operator==(const Directive&, const Directive&); std::vector bincodeSerialize() const; @@ -1039,7 +1050,7 @@ namespace Circuit { }; struct Brillig { - Circuit::Brillig value; + Program::Brillig value; friend bool operator==(const Brillig&, const Brillig&); std::vector bincodeSerialize() const; @@ -1047,9 +1058,9 @@ namespace Circuit { }; struct MemoryOp { - Circuit::BlockId block_id; - Circuit::MemOp op; - std::optional predicate; + Program::BlockId block_id; + Program::MemOp op; + std::optional predicate; friend bool operator==(const MemoryOp&, const MemoryOp&); std::vector bincodeSerialize() const; @@ -1057,8 +1068,8 @@ namespace Circuit { }; struct MemoryInit { - Circuit::BlockId block_id; - std::vector init; + Program::BlockId block_id; + std::vector init; friend bool operator==(const MemoryInit&, const MemoryInit&); std::vector bincodeSerialize() const; @@ -1067,8 +1078,8 @@ namespace Circuit { struct Call { uint32_t id; - std::vector inputs; - std::vector outputs; + std::vector inputs; + std::vector outputs; friend bool operator==(const Call&, const Call&); std::vector bincodeSerialize() const; @@ -1132,7 +1143,7 @@ namespace Circuit { }; struct PublicInputs { - std::vector value; + std::vector value; friend bool operator==(const PublicInputs&, const PublicInputs&); std::vector bincodeSerialize() const; @@ -1141,12 +1152,12 @@ namespace Circuit { struct Circuit { uint32_t current_witness_index; - std::vector opcodes; - Circuit::ExpressionWidth expression_width; - std::vector private_parameters; - Circuit::PublicInputs public_parameters; - Circuit::PublicInputs return_values; - std::vector> assert_messages; + std::vector opcodes; + Program::ExpressionWidth expression_width; + std::vector private_parameters; + Program::PublicInputs public_parameters; + Program::PublicInputs return_values; + std::vector> assert_messages; bool recursive; friend bool operator==(const Circuit&, const Circuit&); @@ -1154,10 +1165,18 @@ namespace Circuit { static Circuit bincodeDeserialize(std::vector); }; -} // end of namespace Circuit + struct Program { + std::vector functions; + friend bool operator==(const Program&, const Program&); + std::vector bincodeSerialize() const; + static Program bincodeDeserialize(std::vector); + }; + +} // end of namespace Program -namespace Circuit { + +namespace Program { inline bool operator==(const BinaryFieldOp &lhs, const BinaryFieldOp &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -1179,11 +1198,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -1191,15 +1210,15 @@ void serde::Serializable::serialize(const Circuit::Binar template <> template -Circuit::BinaryFieldOp serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BinaryFieldOp serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BinaryFieldOp obj; + Program::BinaryFieldOp obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::Add &lhs, const BinaryFieldOp::Add &rhs) { return true; @@ -1220,21 +1239,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::Add &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::Add &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::Add serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::Add obj; +Program::BinaryFieldOp::Add serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::Add obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::Sub &lhs, const BinaryFieldOp::Sub &rhs) { return true; @@ -1255,21 +1274,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::Sub &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::Sub &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::Sub serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::Sub obj; +Program::BinaryFieldOp::Sub serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::Sub obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::Mul &lhs, const BinaryFieldOp::Mul &rhs) { return true; @@ -1290,21 +1309,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::Mul &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::Mul &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::Mul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::Mul obj; +Program::BinaryFieldOp::Mul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::Mul obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::Div &lhs, const BinaryFieldOp::Div &rhs) { return true; @@ -1325,21 +1344,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::Div &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::Div &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::Div serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::Div obj; +Program::BinaryFieldOp::Div serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::Div obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::IntegerDiv &lhs, const BinaryFieldOp::IntegerDiv &rhs) { return true; @@ -1360,21 +1379,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::IntegerDiv &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::IntegerDiv &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::IntegerDiv serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::IntegerDiv obj; +Program::BinaryFieldOp::IntegerDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::IntegerDiv obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::Equals &lhs, const BinaryFieldOp::Equals &rhs) { return true; @@ -1395,21 +1414,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::Equals &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::Equals &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::Equals serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::Equals obj; +Program::BinaryFieldOp::Equals serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::Equals obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::LessThan &lhs, const BinaryFieldOp::LessThan &rhs) { return true; @@ -1430,21 +1449,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::LessThan &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::LessThan &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::LessThan serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::LessThan obj; +Program::BinaryFieldOp::LessThan serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::LessThan obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryFieldOp::LessThanEquals &lhs, const BinaryFieldOp::LessThanEquals &rhs) { return true; @@ -1465,21 +1484,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryFieldOp::LessThanEquals &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryFieldOp::LessThanEquals &obj, Serializer &serializer) { } template <> template -Circuit::BinaryFieldOp::LessThanEquals serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryFieldOp::LessThanEquals obj; +Program::BinaryFieldOp::LessThanEquals serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryFieldOp::LessThanEquals obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp &lhs, const BinaryIntOp &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -1501,11 +1520,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -1513,15 +1532,15 @@ void serde::Serializable::serialize(const Circuit::BinaryI template <> template -Circuit::BinaryIntOp serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BinaryIntOp serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BinaryIntOp obj; + Program::BinaryIntOp obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Add &lhs, const BinaryIntOp::Add &rhs) { return true; @@ -1542,21 +1561,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Add &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Add &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Add serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Add obj; +Program::BinaryIntOp::Add serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Add obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Sub &lhs, const BinaryIntOp::Sub &rhs) { return true; @@ -1577,21 +1596,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Sub &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Sub &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Sub serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Sub obj; +Program::BinaryIntOp::Sub serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Sub obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Mul &lhs, const BinaryIntOp::Mul &rhs) { return true; @@ -1612,21 +1631,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Mul &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Mul &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Mul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Mul obj; +Program::BinaryIntOp::Mul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Mul obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Div &lhs, const BinaryIntOp::Div &rhs) { return true; @@ -1647,21 +1666,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Div &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Div &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Div serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Div obj; +Program::BinaryIntOp::Div serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Div obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Equals &lhs, const BinaryIntOp::Equals &rhs) { return true; @@ -1682,21 +1701,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Equals &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Equals &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Equals serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Equals obj; +Program::BinaryIntOp::Equals serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Equals obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::LessThan &lhs, const BinaryIntOp::LessThan &rhs) { return true; @@ -1717,21 +1736,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::LessThan &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::LessThan &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::LessThan serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::LessThan obj; +Program::BinaryIntOp::LessThan serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::LessThan obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::LessThanEquals &lhs, const BinaryIntOp::LessThanEquals &rhs) { return true; @@ -1752,21 +1771,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::LessThanEquals &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::LessThanEquals &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::LessThanEquals serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::LessThanEquals obj; +Program::BinaryIntOp::LessThanEquals serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::LessThanEquals obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::And &lhs, const BinaryIntOp::And &rhs) { return true; @@ -1787,21 +1806,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::And &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::And &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::And serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::And obj; +Program::BinaryIntOp::And serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::And obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Or &lhs, const BinaryIntOp::Or &rhs) { return true; @@ -1822,21 +1841,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Or &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Or &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Or serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Or obj; +Program::BinaryIntOp::Or serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Or obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Xor &lhs, const BinaryIntOp::Xor &rhs) { return true; @@ -1857,21 +1876,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Xor &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Xor &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Xor serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Xor obj; +Program::BinaryIntOp::Xor serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Xor obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Shl &lhs, const BinaryIntOp::Shl &rhs) { return true; @@ -1892,21 +1911,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Shl &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Shl &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Shl serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Shl obj; +Program::BinaryIntOp::Shl serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Shl obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BinaryIntOp::Shr &lhs, const BinaryIntOp::Shr &rhs) { return true; @@ -1927,21 +1946,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BinaryIntOp::Shr &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BinaryIntOp::Shr &obj, Serializer &serializer) { } template <> template -Circuit::BinaryIntOp::Shr serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BinaryIntOp::Shr obj; +Program::BinaryIntOp::Shr serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BinaryIntOp::Shr obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall &lhs, const BlackBoxFuncCall &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -1963,11 +1982,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -1975,15 +1994,15 @@ void serde::Serializable::serialize(const Circuit::Bl template <> template -Circuit::BlackBoxFuncCall serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BlackBoxFuncCall serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BlackBoxFuncCall obj; + Program::BlackBoxFuncCall obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::AND &lhs, const BlackBoxFuncCall::AND &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -2007,11 +2026,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::AND &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::AND &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2019,15 +2038,15 @@ void serde::Serializable::serialize(const Circui template <> template -Circuit::BlackBoxFuncCall::AND serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::AND obj; +Program::BlackBoxFuncCall::AND serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::AND obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::XOR &lhs, const BlackBoxFuncCall::XOR &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -2051,11 +2070,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::XOR &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::XOR &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2063,15 +2082,15 @@ void serde::Serializable::serialize(const Circui template <> template -Circuit::BlackBoxFuncCall::XOR serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::XOR obj; +Program::BlackBoxFuncCall::XOR serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::XOR obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::RANGE &lhs, const BlackBoxFuncCall::RANGE &rhs) { if (!(lhs.input == rhs.input)) { return false; } @@ -2093,23 +2112,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::RANGE &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::RANGE &obj, Serializer &serializer) { serde::Serializable::serialize(obj.input, serializer); } template <> template -Circuit::BlackBoxFuncCall::RANGE serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::RANGE obj; +Program::BlackBoxFuncCall::RANGE serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::RANGE obj; obj.input = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::SHA256 &lhs, const BlackBoxFuncCall::SHA256 &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2132,25 +2151,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::SHA256 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::SHA256 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxFuncCall::SHA256 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::SHA256 obj; +Program::BlackBoxFuncCall::SHA256 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::SHA256 obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::Blake2s &lhs, const BlackBoxFuncCall::Blake2s &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2173,25 +2192,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Blake2s &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Blake2s &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxFuncCall::Blake2s serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Blake2s obj; +Program::BlackBoxFuncCall::Blake2s serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Blake2s obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::Blake3 &lhs, const BlackBoxFuncCall::Blake3 &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2214,25 +2233,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Blake3 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Blake3 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxFuncCall::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Blake3 obj; +Program::BlackBoxFuncCall::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Blake3 obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::SchnorrVerify &lhs, const BlackBoxFuncCall::SchnorrVerify &rhs) { if (!(lhs.public_key_x == rhs.public_key_x)) { return false; } @@ -2258,11 +2277,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::SchnorrVerify &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::SchnorrVerify &obj, Serializer &serializer) { serde::Serializable::serialize(obj.public_key_x, serializer); serde::Serializable::serialize(obj.public_key_y, serializer); serde::Serializable::serialize(obj.signature, serializer); @@ -2272,8 +2291,8 @@ void serde::Serializable::serialize(co template <> template -Circuit::BlackBoxFuncCall::SchnorrVerify serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::SchnorrVerify obj; +Program::BlackBoxFuncCall::SchnorrVerify serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::SchnorrVerify obj; obj.public_key_x = serde::Deserializable::deserialize(deserializer); obj.public_key_y = serde::Deserializable::deserialize(deserializer); obj.signature = serde::Deserializable::deserialize(deserializer); @@ -2282,7 +2301,7 @@ Circuit::BlackBoxFuncCall::SchnorrVerify serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::PedersenCommitment &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::PedersenCommitment &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -2318,15 +2337,15 @@ void serde::Serializable::seriali template <> template -Circuit::BlackBoxFuncCall::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::PedersenCommitment obj; +Program::BlackBoxFuncCall::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::PedersenCommitment obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::PedersenHash &lhs, const BlackBoxFuncCall::PedersenHash &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2350,11 +2369,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::PedersenHash &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::PedersenHash &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2362,15 +2381,15 @@ void serde::Serializable::serialize(con template <> template -Circuit::BlackBoxFuncCall::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::PedersenHash obj; +Program::BlackBoxFuncCall::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::PedersenHash obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::EcdsaSecp256k1 &lhs, const BlackBoxFuncCall::EcdsaSecp256k1 &rhs) { if (!(lhs.public_key_x == rhs.public_key_x)) { return false; } @@ -2396,11 +2415,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::EcdsaSecp256k1 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::EcdsaSecp256k1 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.public_key_x, serializer); serde::Serializable::serialize(obj.public_key_y, serializer); serde::Serializable::serialize(obj.signature, serializer); @@ -2410,8 +2429,8 @@ void serde::Serializable::serialize(c template <> template -Circuit::BlackBoxFuncCall::EcdsaSecp256k1 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::EcdsaSecp256k1 obj; +Program::BlackBoxFuncCall::EcdsaSecp256k1 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::EcdsaSecp256k1 obj; obj.public_key_x = serde::Deserializable::deserialize(deserializer); obj.public_key_y = serde::Deserializable::deserialize(deserializer); obj.signature = serde::Deserializable::deserialize(deserializer); @@ -2420,7 +2439,7 @@ Circuit::BlackBoxFuncCall::EcdsaSecp256k1 serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::EcdsaSecp256r1 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::EcdsaSecp256r1 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.public_key_x, serializer); serde::Serializable::serialize(obj.public_key_y, serializer); serde::Serializable::serialize(obj.signature, serializer); @@ -2460,8 +2479,8 @@ void serde::Serializable::serialize(c template <> template -Circuit::BlackBoxFuncCall::EcdsaSecp256r1 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::EcdsaSecp256r1 obj; +Program::BlackBoxFuncCall::EcdsaSecp256r1 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::EcdsaSecp256r1 obj; obj.public_key_x = serde::Deserializable::deserialize(deserializer); obj.public_key_y = serde::Deserializable::deserialize(deserializer); obj.signature = serde::Deserializable::deserialize(deserializer); @@ -2470,7 +2489,7 @@ Circuit::BlackBoxFuncCall::EcdsaSecp256r1 serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::FixedBaseScalarMul &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::FixedBaseScalarMul &obj, Serializer &serializer) { serde::Serializable::serialize(obj.low, serializer); serde::Serializable::serialize(obj.high, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -2506,15 +2525,15 @@ void serde::Serializable::seriali template <> template -Circuit::BlackBoxFuncCall::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::FixedBaseScalarMul obj; +Program::BlackBoxFuncCall::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::FixedBaseScalarMul obj; obj.low = serde::Deserializable::deserialize(deserializer); obj.high = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::EmbeddedCurveAdd &lhs, const BlackBoxFuncCall::EmbeddedCurveAdd &rhs) { if (!(lhs.input1_x == rhs.input1_x)) { return false; } @@ -2540,11 +2559,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::EmbeddedCurveAdd &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::EmbeddedCurveAdd &obj, Serializer &serializer) { serde::Serializable::serialize(obj.input1_x, serializer); serde::Serializable::serialize(obj.input1_y, serializer); serde::Serializable::serialize(obj.input2_x, serializer); @@ -2554,8 +2573,8 @@ void serde::Serializable::serialize template <> template -Circuit::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::EmbeddedCurveAdd obj; +Program::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::EmbeddedCurveAdd obj; obj.input1_x = serde::Deserializable::deserialize(deserializer); obj.input1_y = serde::Deserializable::deserialize(deserializer); obj.input2_x = serde::Deserializable::deserialize(deserializer); @@ -2564,7 +2583,7 @@ Circuit::BlackBoxFuncCall::EmbeddedCurveAdd serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Keccak256 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccak256 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxFuncCall::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Keccak256 obj; +Program::BlackBoxFuncCall::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Keccak256 obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::Keccak256VariableLength &lhs, const BlackBoxFuncCall::Keccak256VariableLength &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2629,11 +2648,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Keccak256VariableLength &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccak256VariableLength &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.var_message_size, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -2641,15 +2660,15 @@ void serde::Serializable::se template <> template -Circuit::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Keccak256VariableLength obj; +Program::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Keccak256VariableLength obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.var_message_size = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::Keccakf1600 &lhs, const BlackBoxFuncCall::Keccakf1600 &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2672,25 +2691,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Keccakf1600 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccakf1600 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxFuncCall::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Keccakf1600 obj; +Program::BlackBoxFuncCall::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Keccakf1600 obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::RecursiveAggregation &lhs, const BlackBoxFuncCall::RecursiveAggregation &rhs) { if (!(lhs.verification_key == rhs.verification_key)) { return false; } @@ -2715,11 +2734,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::RecursiveAggregation &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::RecursiveAggregation &obj, Serializer &serializer) { serde::Serializable::serialize(obj.verification_key, serializer); serde::Serializable::serialize(obj.proof, serializer); serde::Serializable::serialize(obj.public_inputs, serializer); @@ -2728,8 +2747,8 @@ void serde::Serializable::seria template <> template -Circuit::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::RecursiveAggregation obj; +Program::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::RecursiveAggregation obj; obj.verification_key = serde::Deserializable::deserialize(deserializer); obj.proof = serde::Deserializable::deserialize(deserializer); obj.public_inputs = serde::Deserializable::deserialize(deserializer); @@ -2737,7 +2756,7 @@ Circuit::BlackBoxFuncCall::RecursiveAggregation serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntAdd &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::BigIntAdd &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2773,15 +2792,15 @@ void serde::Serializable::serialize(const template <> template -Circuit::BlackBoxFuncCall::BigIntAdd serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::BigIntAdd obj; +Program::BlackBoxFuncCall::BigIntAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::BigIntAdd obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::BigIntSub &lhs, const BlackBoxFuncCall::BigIntSub &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -2805,11 +2824,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntSub &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::BigIntSub &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2817,15 +2836,15 @@ void serde::Serializable::serialize(const template <> template -Circuit::BlackBoxFuncCall::BigIntSub serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::BigIntSub obj; +Program::BlackBoxFuncCall::BigIntSub serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::BigIntSub obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::BigIntMul &lhs, const BlackBoxFuncCall::BigIntMul &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -2849,11 +2868,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntMul &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::BigIntMul &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2861,15 +2880,15 @@ void serde::Serializable::serialize(const template <> template -Circuit::BlackBoxFuncCall::BigIntMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::BigIntMul obj; +Program::BlackBoxFuncCall::BigIntMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::BigIntMul obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::BigIntDiv &lhs, const BlackBoxFuncCall::BigIntDiv &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -2893,11 +2912,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntDiv &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::BigIntDiv &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2905,15 +2924,15 @@ void serde::Serializable::serialize(const template <> template -Circuit::BlackBoxFuncCall::BigIntDiv serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::BigIntDiv obj; +Program::BlackBoxFuncCall::BigIntDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::BigIntDiv obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::BigIntFromLeBytes &lhs, const BlackBoxFuncCall::BigIntFromLeBytes &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -2937,11 +2956,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntFromLeBytes &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::BigIntFromLeBytes &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.modulus, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -2949,15 +2968,15 @@ void serde::Serializable::serializ template <> template -Circuit::BlackBoxFuncCall::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::BigIntFromLeBytes obj; +Program::BlackBoxFuncCall::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::BigIntFromLeBytes obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.modulus = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::BigIntToLeBytes &lhs, const BlackBoxFuncCall::BigIntToLeBytes &rhs) { if (!(lhs.input == rhs.input)) { return false; } @@ -2980,25 +2999,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::BigIntToLeBytes &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::BigIntToLeBytes &obj, Serializer &serializer) { serde::Serializable::serialize(obj.input, serializer); serde::Serializable::serialize(obj.outputs, serializer); } template <> template -Circuit::BlackBoxFuncCall::BigIntToLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::BigIntToLeBytes obj; +Program::BlackBoxFuncCall::BigIntToLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::BigIntToLeBytes obj; obj.input = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::Poseidon2Permutation &lhs, const BlackBoxFuncCall::Poseidon2Permutation &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -3022,11 +3041,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Poseidon2Permutation &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Poseidon2Permutation &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); serde::Serializable::serialize(obj.len, serializer); @@ -3034,15 +3053,15 @@ void serde::Serializable::seria template <> template -Circuit::BlackBoxFuncCall::Poseidon2Permutation serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Poseidon2Permutation obj; +Program::BlackBoxFuncCall::Poseidon2Permutation serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Poseidon2Permutation obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); obj.len = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxFuncCall::Sha256Compression &lhs, const BlackBoxFuncCall::Sha256Compression &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -3066,11 +3085,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxFuncCall::Sha256Compression &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Sha256Compression &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.hash_values, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -3078,15 +3097,15 @@ void serde::Serializable::serializ template <> template -Circuit::BlackBoxFuncCall::Sha256Compression serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxFuncCall::Sha256Compression obj; +Program::BlackBoxFuncCall::Sha256Compression serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxFuncCall::Sha256Compression obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.hash_values = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp &lhs, const BlackBoxOp &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -3108,11 +3127,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -3120,15 +3139,15 @@ void serde::Serializable::serialize(const Circuit::BlackBox template <> template -Circuit::BlackBoxOp serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BlackBoxOp serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BlackBoxOp obj; + Program::BlackBoxOp obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Sha256 &lhs, const BlackBoxOp::Sha256 &rhs) { if (!(lhs.message == rhs.message)) { return false; } @@ -3151,25 +3170,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Sha256 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.message, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Sha256 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Sha256 obj; +Program::BlackBoxOp::Sha256 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Sha256 obj; obj.message = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Blake2s &lhs, const BlackBoxOp::Blake2s &rhs) { if (!(lhs.message == rhs.message)) { return false; } @@ -3192,25 +3211,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake2s &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Blake2s &obj, Serializer &serializer) { serde::Serializable::serialize(obj.message, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Blake2s serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Blake2s obj; +Program::BlackBoxOp::Blake2s serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Blake2s obj; obj.message = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Blake3 &lhs, const BlackBoxOp::Blake3 &rhs) { if (!(lhs.message == rhs.message)) { return false; } @@ -3233,25 +3252,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Blake3 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Blake3 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.message, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Blake3 obj; +Program::BlackBoxOp::Blake3 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Blake3 obj; obj.message = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Keccak256 &lhs, const BlackBoxOp::Keccak256 &rhs) { if (!(lhs.message == rhs.message)) { return false; } @@ -3274,25 +3293,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccak256 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Keccak256 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.message, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Keccak256 obj; +Program::BlackBoxOp::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Keccak256 obj; obj.message = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Keccakf1600 &lhs, const BlackBoxOp::Keccakf1600 &rhs) { if (!(lhs.message == rhs.message)) { return false; } @@ -3315,25 +3334,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Keccakf1600 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Keccakf1600 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.message, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Keccakf1600 obj; +Program::BlackBoxOp::Keccakf1600 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Keccakf1600 obj; obj.message = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::EcdsaSecp256k1 &lhs, const BlackBoxOp::EcdsaSecp256k1 &rhs) { if (!(lhs.hashed_msg == rhs.hashed_msg)) { return false; } @@ -3359,11 +3378,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256k1 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::EcdsaSecp256k1 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.hashed_msg, serializer); serde::Serializable::serialize(obj.public_key_x, serializer); serde::Serializable::serialize(obj.public_key_y, serializer); @@ -3373,8 +3392,8 @@ void serde::Serializable::serialize(const C template <> template -Circuit::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::EcdsaSecp256k1 obj; +Program::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::EcdsaSecp256k1 obj; obj.hashed_msg = serde::Deserializable::deserialize(deserializer); obj.public_key_x = serde::Deserializable::deserialize(deserializer); obj.public_key_y = serde::Deserializable::deserialize(deserializer); @@ -3383,7 +3402,7 @@ Circuit::BlackBoxOp::EcdsaSecp256k1 serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EcdsaSecp256r1 &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::EcdsaSecp256r1 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.hashed_msg, serializer); serde::Serializable::serialize(obj.public_key_x, serializer); serde::Serializable::serialize(obj.public_key_y, serializer); @@ -3423,8 +3442,8 @@ void serde::Serializable::serialize(const C template <> template -Circuit::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::EcdsaSecp256r1 obj; +Program::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::EcdsaSecp256r1 obj; obj.hashed_msg = serde::Deserializable::deserialize(deserializer); obj.public_key_x = serde::Deserializable::deserialize(deserializer); obj.public_key_y = serde::Deserializable::deserialize(deserializer); @@ -3433,7 +3452,7 @@ Circuit::BlackBoxOp::EcdsaSecp256r1 serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::SchnorrVerify &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::SchnorrVerify &obj, Serializer &serializer) { serde::Serializable::serialize(obj.public_key_x, serializer); serde::Serializable::serialize(obj.public_key_y, serializer); serde::Serializable::serialize(obj.message, serializer); @@ -3473,8 +3492,8 @@ void serde::Serializable::serialize(const Ci template <> template -Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::SchnorrVerify obj; +Program::BlackBoxOp::SchnorrVerify serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::SchnorrVerify obj; obj.public_key_x = serde::Deserializable::deserialize(deserializer); obj.public_key_y = serde::Deserializable::deserialize(deserializer); obj.message = serde::Deserializable::deserialize(deserializer); @@ -3483,7 +3502,7 @@ Circuit::BlackBoxOp::SchnorrVerify serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenCommitment &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::PedersenCommitment &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3519,15 +3538,15 @@ void serde::Serializable::serialize(con template <> template -Circuit::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::PedersenCommitment obj; +Program::BlackBoxOp::PedersenCommitment serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::PedersenCommitment obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::PedersenHash &lhs, const BlackBoxOp::PedersenHash &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -3551,11 +3570,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::PedersenHash &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::PedersenHash &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.domain_separator, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3563,15 +3582,15 @@ void serde::Serializable::serialize(const Cir template <> template -Circuit::BlackBoxOp::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::PedersenHash obj; +Program::BlackBoxOp::PedersenHash serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::PedersenHash obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.domain_separator = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::FixedBaseScalarMul &lhs, const BlackBoxOp::FixedBaseScalarMul &rhs) { if (!(lhs.low == rhs.low)) { return false; } @@ -3595,11 +3614,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::FixedBaseScalarMul &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::FixedBaseScalarMul &obj, Serializer &serializer) { serde::Serializable::serialize(obj.low, serializer); serde::Serializable::serialize(obj.high, serializer); serde::Serializable::serialize(obj.result, serializer); @@ -3607,15 +3626,15 @@ void serde::Serializable::serialize(con template <> template -Circuit::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::FixedBaseScalarMul obj; +Program::BlackBoxOp::FixedBaseScalarMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::FixedBaseScalarMul obj; obj.low = serde::Deserializable::deserialize(deserializer); obj.high = serde::Deserializable::deserialize(deserializer); obj.result = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::EmbeddedCurveAdd &lhs, const BlackBoxOp::EmbeddedCurveAdd &rhs) { if (!(lhs.input1_x == rhs.input1_x)) { return false; } @@ -3641,11 +3660,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::EmbeddedCurveAdd &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::EmbeddedCurveAdd &obj, Serializer &serializer) { serde::Serializable::serialize(obj.input1_x, serializer); serde::Serializable::serialize(obj.input1_y, serializer); serde::Serializable::serialize(obj.input2_x, serializer); @@ -3655,8 +3674,8 @@ void serde::Serializable::serialize(const template <> template -Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::EmbeddedCurveAdd obj; +Program::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::EmbeddedCurveAdd obj; obj.input1_x = serde::Deserializable::deserialize(deserializer); obj.input1_y = serde::Deserializable::deserialize(deserializer); obj.input2_x = serde::Deserializable::deserialize(deserializer); @@ -3665,7 +3684,7 @@ Circuit::BlackBoxOp::EmbeddedCurveAdd serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntAdd &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::BigIntAdd &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3701,15 +3720,15 @@ void serde::Serializable::serialize(const Circui template <> template -Circuit::BlackBoxOp::BigIntAdd serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::BigIntAdd obj; +Program::BlackBoxOp::BigIntAdd serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::BigIntAdd obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::BigIntSub &lhs, const BlackBoxOp::BigIntSub &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -3733,11 +3752,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntSub &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::BigIntSub &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3745,15 +3764,15 @@ void serde::Serializable::serialize(const Circui template <> template -Circuit::BlackBoxOp::BigIntSub serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::BigIntSub obj; +Program::BlackBoxOp::BigIntSub serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::BigIntSub obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::BigIntMul &lhs, const BlackBoxOp::BigIntMul &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -3777,11 +3796,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntMul &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::BigIntMul &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3789,15 +3808,15 @@ void serde::Serializable::serialize(const Circui template <> template -Circuit::BlackBoxOp::BigIntMul serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::BigIntMul obj; +Program::BlackBoxOp::BigIntMul serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::BigIntMul obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::BigIntDiv &lhs, const BlackBoxOp::BigIntDiv &rhs) { if (!(lhs.lhs == rhs.lhs)) { return false; } @@ -3821,11 +3840,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntDiv &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::BigIntDiv &obj, Serializer &serializer) { serde::Serializable::serialize(obj.lhs, serializer); serde::Serializable::serialize(obj.rhs, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3833,15 +3852,15 @@ void serde::Serializable::serialize(const Circui template <> template -Circuit::BlackBoxOp::BigIntDiv serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::BigIntDiv obj; +Program::BlackBoxOp::BigIntDiv serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::BigIntDiv obj; obj.lhs = serde::Deserializable::deserialize(deserializer); obj.rhs = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::BigIntFromLeBytes &lhs, const BlackBoxOp::BigIntFromLeBytes &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -3865,11 +3884,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntFromLeBytes &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::BigIntFromLeBytes &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.modulus, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -3877,15 +3896,15 @@ void serde::Serializable::serialize(cons template <> template -Circuit::BlackBoxOp::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::BigIntFromLeBytes obj; +Program::BlackBoxOp::BigIntFromLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::BigIntFromLeBytes obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.modulus = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::BigIntToLeBytes &lhs, const BlackBoxOp::BigIntToLeBytes &rhs) { if (!(lhs.input == rhs.input)) { return false; } @@ -3908,25 +3927,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::BigIntToLeBytes &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::BigIntToLeBytes &obj, Serializer &serializer) { serde::Serializable::serialize(obj.input, serializer); serde::Serializable::serialize(obj.output, serializer); } template <> template -Circuit::BlackBoxOp::BigIntToLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::BigIntToLeBytes obj; +Program::BlackBoxOp::BigIntToLeBytes serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::BigIntToLeBytes obj; obj.input = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Poseidon2Permutation &lhs, const BlackBoxOp::Poseidon2Permutation &rhs) { if (!(lhs.message == rhs.message)) { return false; } @@ -3950,11 +3969,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Poseidon2Permutation &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Poseidon2Permutation &obj, Serializer &serializer) { serde::Serializable::serialize(obj.message, serializer); serde::Serializable::serialize(obj.output, serializer); serde::Serializable::serialize(obj.len, serializer); @@ -3962,15 +3981,15 @@ void serde::Serializable::serialize(c template <> template -Circuit::BlackBoxOp::Poseidon2Permutation serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Poseidon2Permutation obj; +Program::BlackBoxOp::Poseidon2Permutation serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Poseidon2Permutation obj; obj.message = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); obj.len = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlackBoxOp::Sha256Compression &lhs, const BlackBoxOp::Sha256Compression &rhs) { if (!(lhs.input == rhs.input)) { return false; } @@ -3994,11 +4013,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlackBoxOp::Sha256Compression &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlackBoxOp::Sha256Compression &obj, Serializer &serializer) { serde::Serializable::serialize(obj.input, serializer); serde::Serializable::serialize(obj.hash_values, serializer); serde::Serializable::serialize(obj.output, serializer); @@ -4006,15 +4025,15 @@ void serde::Serializable::serialize(cons template <> template -Circuit::BlackBoxOp::Sha256Compression serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BlackBoxOp::Sha256Compression obj; +Program::BlackBoxOp::Sha256Compression serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BlackBoxOp::Sha256Compression obj; obj.input = serde::Deserializable::deserialize(deserializer); obj.hash_values = serde::Deserializable::deserialize(deserializer); obj.output = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BlockId &lhs, const BlockId &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4036,11 +4055,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BlockId &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BlockId &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -4048,15 +4067,15 @@ void serde::Serializable::serialize(const Circuit::BlockId &ob template <> template -Circuit::BlockId serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BlockId serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BlockId obj; + Program::BlockId obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Brillig &lhs, const Brillig &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } @@ -4081,11 +4100,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Brillig &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Brillig &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -4096,9 +4115,9 @@ void serde::Serializable::serialize(const Circuit::Brillig &ob template <> template -Circuit::Brillig serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Brillig serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Brillig obj; + Program::Brillig obj; obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); obj.bytecode = serde::Deserializable::deserialize(deserializer); @@ -4107,7 +4126,7 @@ Circuit::Brillig serde::Deserializable::deserialize(Deserializ return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligInputs &lhs, const BrilligInputs &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4129,11 +4148,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligInputs &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligInputs &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -4141,15 +4160,15 @@ void serde::Serializable::serialize(const Circuit::Brill template <> template -Circuit::BrilligInputs serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BrilligInputs serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BrilligInputs obj; + Program::BrilligInputs obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligInputs::Single &lhs, const BrilligInputs::Single &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4171,23 +4190,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligInputs::Single &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligInputs::Single &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::BrilligInputs::Single serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligInputs::Single obj; +Program::BrilligInputs::Single serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligInputs::Single obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligInputs::Array &lhs, const BrilligInputs::Array &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4209,23 +4228,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligInputs::Array &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligInputs::Array &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::BrilligInputs::Array serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligInputs::Array obj; +Program::BrilligInputs::Array serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligInputs::Array obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligInputs::MemoryArray &lhs, const BrilligInputs::MemoryArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4247,23 +4266,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligInputs::MemoryArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligInputs::MemoryArray &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::BrilligInputs::MemoryArray serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligInputs::MemoryArray obj; +Program::BrilligInputs::MemoryArray serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligInputs::MemoryArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode &lhs, const BrilligOpcode &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4285,11 +4304,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -4297,15 +4316,15 @@ void serde::Serializable::serialize(const Circuit::Brill template <> template -Circuit::BrilligOpcode serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BrilligOpcode serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BrilligOpcode obj; + Program::BrilligOpcode obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::BinaryFieldOp &lhs, const BrilligOpcode::BinaryFieldOp &rhs) { if (!(lhs.destination == rhs.destination)) { return false; } @@ -4330,11 +4349,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::BinaryFieldOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::BinaryFieldOp &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination, serializer); serde::Serializable::serialize(obj.op, serializer); serde::Serializable::serialize(obj.lhs, serializer); @@ -4343,8 +4362,8 @@ void serde::Serializable::serialize(const template <> template -Circuit::BrilligOpcode::BinaryFieldOp serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::BinaryFieldOp obj; +Program::BrilligOpcode::BinaryFieldOp serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::BinaryFieldOp obj; obj.destination = serde::Deserializable::deserialize(deserializer); obj.op = serde::Deserializable::deserialize(deserializer); obj.lhs = serde::Deserializable::deserialize(deserializer); @@ -4352,7 +4371,7 @@ Circuit::BrilligOpcode::BinaryFieldOp serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::BinaryIntOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::BinaryIntOp &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination, serializer); serde::Serializable::serialize(obj.op, serializer); serde::Serializable::serialize(obj.bit_size, serializer); @@ -4392,8 +4411,8 @@ void serde::Serializable::serialize(const C template <> template -Circuit::BrilligOpcode::BinaryIntOp serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::BinaryIntOp obj; +Program::BrilligOpcode::BinaryIntOp serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::BinaryIntOp obj; obj.destination = serde::Deserializable::deserialize(deserializer); obj.op = serde::Deserializable::deserialize(deserializer); obj.bit_size = serde::Deserializable::deserialize(deserializer); @@ -4402,7 +4421,7 @@ Circuit::BrilligOpcode::BinaryIntOp serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Cast &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Cast &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination, serializer); serde::Serializable::serialize(obj.source, serializer); serde::Serializable::serialize(obj.bit_size, serializer); @@ -4438,15 +4457,15 @@ void serde::Serializable::serialize(const Circuit: template <> template -Circuit::BrilligOpcode::Cast serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Cast obj; +Program::BrilligOpcode::Cast serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Cast obj; obj.destination = serde::Deserializable::deserialize(deserializer); obj.source = serde::Deserializable::deserialize(deserializer); obj.bit_size = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::JumpIfNot &lhs, const BrilligOpcode::JumpIfNot &rhs) { if (!(lhs.condition == rhs.condition)) { return false; } @@ -4469,25 +4488,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::JumpIfNot &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::JumpIfNot &obj, Serializer &serializer) { serde::Serializable::serialize(obj.condition, serializer); serde::Serializable::serialize(obj.location, serializer); } template <> template -Circuit::BrilligOpcode::JumpIfNot serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::JumpIfNot obj; +Program::BrilligOpcode::JumpIfNot serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::JumpIfNot obj; obj.condition = serde::Deserializable::deserialize(deserializer); obj.location = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::JumpIf &lhs, const BrilligOpcode::JumpIf &rhs) { if (!(lhs.condition == rhs.condition)) { return false; } @@ -4510,25 +4529,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::JumpIf &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::JumpIf &obj, Serializer &serializer) { serde::Serializable::serialize(obj.condition, serializer); serde::Serializable::serialize(obj.location, serializer); } template <> template -Circuit::BrilligOpcode::JumpIf serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::JumpIf obj; +Program::BrilligOpcode::JumpIf serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::JumpIf obj; obj.condition = serde::Deserializable::deserialize(deserializer); obj.location = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Jump &lhs, const BrilligOpcode::Jump &rhs) { if (!(lhs.location == rhs.location)) { return false; } @@ -4550,23 +4569,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Jump &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Jump &obj, Serializer &serializer) { serde::Serializable::serialize(obj.location, serializer); } template <> template -Circuit::BrilligOpcode::Jump serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Jump obj; +Program::BrilligOpcode::Jump serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Jump obj; obj.location = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::CalldataCopy &lhs, const BrilligOpcode::CalldataCopy &rhs) { if (!(lhs.destination_address == rhs.destination_address)) { return false; } @@ -4590,11 +4609,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::CalldataCopy &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::CalldataCopy &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination_address, serializer); serde::Serializable::serialize(obj.size, serializer); serde::Serializable::serialize(obj.offset, serializer); @@ -4602,15 +4621,15 @@ void serde::Serializable::serialize(const template <> template -Circuit::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::CalldataCopy obj; +Program::BrilligOpcode::CalldataCopy serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::CalldataCopy obj; obj.destination_address = serde::Deserializable::deserialize(deserializer); obj.size = serde::Deserializable::deserialize(deserializer); obj.offset = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Call &lhs, const BrilligOpcode::Call &rhs) { if (!(lhs.location == rhs.location)) { return false; } @@ -4632,23 +4651,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Call &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Call &obj, Serializer &serializer) { serde::Serializable::serialize(obj.location, serializer); } template <> template -Circuit::BrilligOpcode::Call serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Call obj; +Program::BrilligOpcode::Call serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Call obj; obj.location = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Const &lhs, const BrilligOpcode::Const &rhs) { if (!(lhs.destination == rhs.destination)) { return false; } @@ -4672,11 +4691,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Const &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Const &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination, serializer); serde::Serializable::serialize(obj.bit_size, serializer); serde::Serializable::serialize(obj.value, serializer); @@ -4684,15 +4703,15 @@ void serde::Serializable::serialize(const Circuit template <> template -Circuit::BrilligOpcode::Const serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Const obj; +Program::BrilligOpcode::Const serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Const obj; obj.destination = serde::Deserializable::deserialize(deserializer); obj.bit_size = serde::Deserializable::deserialize(deserializer); obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Return &lhs, const BrilligOpcode::Return &rhs) { return true; @@ -4713,21 +4732,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Return &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Return &obj, Serializer &serializer) { } template <> template -Circuit::BrilligOpcode::Return serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Return obj; +Program::BrilligOpcode::Return serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Return obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::ForeignCall &lhs, const BrilligOpcode::ForeignCall &rhs) { if (!(lhs.function == rhs.function)) { return false; } @@ -4753,11 +4772,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::ForeignCall &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::ForeignCall &obj, Serializer &serializer) { serde::Serializable::serialize(obj.function, serializer); serde::Serializable::serialize(obj.destinations, serializer); serde::Serializable::serialize(obj.destination_value_types, serializer); @@ -4767,8 +4786,8 @@ void serde::Serializable::serialize(const C template <> template -Circuit::BrilligOpcode::ForeignCall serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::ForeignCall obj; +Program::BrilligOpcode::ForeignCall serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::ForeignCall obj; obj.function = serde::Deserializable::deserialize(deserializer); obj.destinations = serde::Deserializable::deserialize(deserializer); obj.destination_value_types = serde::Deserializable::deserialize(deserializer); @@ -4777,7 +4796,7 @@ Circuit::BrilligOpcode::ForeignCall serde::Deserializable template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Mov &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Mov &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination, serializer); serde::Serializable::serialize(obj.source, serializer); } template <> template -Circuit::BrilligOpcode::Mov serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Mov obj; +Program::BrilligOpcode::Mov serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Mov obj; obj.destination = serde::Deserializable::deserialize(deserializer); obj.source = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { + + inline bool operator==(const BrilligOpcode::ConditionalMov &lhs, const BrilligOpcode::ConditionalMov &rhs) { + if (!(lhs.destination == rhs.destination)) { return false; } + if (!(lhs.source_a == rhs.source_a)) { return false; } + if (!(lhs.source_b == rhs.source_b)) { return false; } + if (!(lhs.condition == rhs.condition)) { return false; } + return true; + } + + inline std::vector BrilligOpcode::ConditionalMov::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline BrilligOpcode::ConditionalMov BrilligOpcode::ConditionalMov::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Program + +template <> +template +void serde::Serializable::serialize(const Program::BrilligOpcode::ConditionalMov &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.destination, serializer); + serde::Serializable::serialize(obj.source_a, serializer); + serde::Serializable::serialize(obj.source_b, serializer); + serde::Serializable::serialize(obj.condition, serializer); +} + +template <> +template +Program::BrilligOpcode::ConditionalMov serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::ConditionalMov obj; + obj.destination = serde::Deserializable::deserialize(deserializer); + obj.source_a = serde::Deserializable::deserialize(deserializer); + obj.source_b = serde::Deserializable::deserialize(deserializer); + obj.condition = serde::Deserializable::deserialize(deserializer); + return obj; +} + +namespace Program { inline bool operator==(const BrilligOpcode::Load &lhs, const BrilligOpcode::Load &rhs) { if (!(lhs.destination == rhs.destination)) { return false; } @@ -4841,25 +4907,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Load &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Load &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination, serializer); serde::Serializable::serialize(obj.source_pointer, serializer); } template <> template -Circuit::BrilligOpcode::Load serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Load obj; +Program::BrilligOpcode::Load serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Load obj; obj.destination = serde::Deserializable::deserialize(deserializer); obj.source_pointer = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Store &lhs, const BrilligOpcode::Store &rhs) { if (!(lhs.destination_pointer == rhs.destination_pointer)) { return false; } @@ -4882,25 +4948,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Store &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Store &obj, Serializer &serializer) { serde::Serializable::serialize(obj.destination_pointer, serializer); serde::Serializable::serialize(obj.source, serializer); } template <> template -Circuit::BrilligOpcode::Store serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Store obj; +Program::BrilligOpcode::Store serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Store obj; obj.destination_pointer = serde::Deserializable::deserialize(deserializer); obj.source = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::BlackBox &lhs, const BrilligOpcode::BlackBox &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -4922,23 +4988,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::BlackBox &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::BlackBox &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::BrilligOpcode::BlackBox serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::BlackBox obj; +Program::BrilligOpcode::BlackBox serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::BlackBox obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Trap &lhs, const BrilligOpcode::Trap &rhs) { return true; @@ -4959,21 +5025,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Trap &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Trap &obj, Serializer &serializer) { } template <> template -Circuit::BrilligOpcode::Trap serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Trap obj; +Program::BrilligOpcode::Trap serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Trap obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOpcode::Stop &lhs, const BrilligOpcode::Stop &rhs) { if (!(lhs.return_data_offset == rhs.return_data_offset)) { return false; } @@ -4996,25 +5062,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOpcode::Stop &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOpcode::Stop &obj, Serializer &serializer) { serde::Serializable::serialize(obj.return_data_offset, serializer); serde::Serializable::serialize(obj.return_data_size, serializer); } template <> template -Circuit::BrilligOpcode::Stop serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOpcode::Stop obj; +Program::BrilligOpcode::Stop serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOpcode::Stop obj; obj.return_data_offset = serde::Deserializable::deserialize(deserializer); obj.return_data_size = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOutputs &lhs, const BrilligOutputs &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5036,11 +5102,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOutputs &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOutputs &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5048,15 +5114,15 @@ void serde::Serializable::serialize(const Circuit::Bril template <> template -Circuit::BrilligOutputs serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::BrilligOutputs serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::BrilligOutputs obj; + Program::BrilligOutputs obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOutputs::Simple &lhs, const BrilligOutputs::Simple &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5078,23 +5144,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOutputs::Simple &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOutputs::Simple &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::BrilligOutputs::Simple serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOutputs::Simple obj; +Program::BrilligOutputs::Simple serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOutputs::Simple obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const BrilligOutputs::Array &lhs, const BrilligOutputs::Array &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5116,23 +5182,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::BrilligOutputs::Array &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::BrilligOutputs::Array &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::BrilligOutputs::Array serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::BrilligOutputs::Array obj; +Program::BrilligOutputs::Array serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::BrilligOutputs::Array obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Circuit &lhs, const Circuit &rhs) { if (!(lhs.current_witness_index == rhs.current_witness_index)) { return false; } @@ -5161,11 +5227,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Circuit &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Circuit &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.current_witness_index, serializer); serde::Serializable::serialize(obj.opcodes, serializer); @@ -5180,9 +5246,9 @@ void serde::Serializable::serialize(const Circuit::Circuit &ob template <> template -Circuit::Circuit serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Circuit serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Circuit obj; + Program::Circuit obj; obj.current_witness_index = serde::Deserializable::deserialize(deserializer); obj.opcodes = serde::Deserializable::deserialize(deserializer); obj.expression_width = serde::Deserializable::deserialize(deserializer); @@ -5195,7 +5261,7 @@ Circuit::Circuit serde::Deserializable::deserialize(Deserializ return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Directive &lhs, const Directive &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5217,11 +5283,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Directive &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Directive &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5229,15 +5295,15 @@ void serde::Serializable::serialize(const Circuit::Directive template <> template -Circuit::Directive serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Directive serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Directive obj; + Program::Directive obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Directive::ToLeRadix &lhs, const Directive::ToLeRadix &rhs) { if (!(lhs.a == rhs.a)) { return false; } @@ -5261,11 +5327,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Directive::ToLeRadix &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Directive::ToLeRadix &obj, Serializer &serializer) { serde::Serializable::serialize(obj.a, serializer); serde::Serializable::serialize(obj.b, serializer); serde::Serializable::serialize(obj.radix, serializer); @@ -5273,15 +5339,15 @@ void serde::Serializable::serialize(const Circuit template <> template -Circuit::Directive::ToLeRadix serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Directive::ToLeRadix obj; +Program::Directive::ToLeRadix serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Directive::ToLeRadix obj; obj.a = serde::Deserializable::deserialize(deserializer); obj.b = serde::Deserializable::deserialize(deserializer); obj.radix = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Expression &lhs, const Expression &rhs) { if (!(lhs.mul_terms == rhs.mul_terms)) { return false; } @@ -5305,11 +5371,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Expression &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Expression &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.mul_terms, serializer); serde::Serializable::serialize(obj.linear_combinations, serializer); @@ -5319,9 +5385,9 @@ void serde::Serializable::serialize(const Circuit::Expressi template <> template -Circuit::Expression serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Expression serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Expression obj; + Program::Expression obj; obj.mul_terms = serde::Deserializable::deserialize(deserializer); obj.linear_combinations = serde::Deserializable::deserialize(deserializer); obj.q_c = serde::Deserializable::deserialize(deserializer); @@ -5329,7 +5395,7 @@ Circuit::Expression serde::Deserializable::deserialize(Dese return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ExpressionWidth &lhs, const ExpressionWidth &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5351,11 +5417,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ExpressionWidth &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ExpressionWidth &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5363,15 +5429,15 @@ void serde::Serializable::serialize(const Circuit::Exp template <> template -Circuit::ExpressionWidth serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::ExpressionWidth serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::ExpressionWidth obj; + Program::ExpressionWidth obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ExpressionWidth::Unbounded &lhs, const ExpressionWidth::Unbounded &rhs) { return true; @@ -5392,21 +5458,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ExpressionWidth::Unbounded &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ExpressionWidth::Unbounded &obj, Serializer &serializer) { } template <> template -Circuit::ExpressionWidth::Unbounded serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::ExpressionWidth::Unbounded obj; +Program::ExpressionWidth::Unbounded serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ExpressionWidth::Unbounded obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ExpressionWidth::Bounded &lhs, const ExpressionWidth::Bounded &rhs) { if (!(lhs.width == rhs.width)) { return false; } @@ -5428,23 +5494,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ExpressionWidth::Bounded &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ExpressionWidth::Bounded &obj, Serializer &serializer) { serde::Serializable::serialize(obj.width, serializer); } template <> template -Circuit::ExpressionWidth::Bounded serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::ExpressionWidth::Bounded obj; +Program::ExpressionWidth::Bounded serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ExpressionWidth::Bounded obj; obj.width = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const FunctionInput &lhs, const FunctionInput &rhs) { if (!(lhs.witness == rhs.witness)) { return false; } @@ -5467,11 +5533,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::FunctionInput &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::FunctionInput &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.witness, serializer); serde::Serializable::serialize(obj.num_bits, serializer); @@ -5480,16 +5546,16 @@ void serde::Serializable::serialize(const Circuit::Funct template <> template -Circuit::FunctionInput serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::FunctionInput serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::FunctionInput obj; + Program::FunctionInput obj; obj.witness = serde::Deserializable::deserialize(deserializer); obj.num_bits = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const HeapArray &lhs, const HeapArray &rhs) { if (!(lhs.pointer == rhs.pointer)) { return false; } @@ -5512,11 +5578,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::HeapArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::HeapArray &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.pointer, serializer); serde::Serializable::serialize(obj.size, serializer); @@ -5525,16 +5591,16 @@ void serde::Serializable::serialize(const Circuit::HeapArray template <> template -Circuit::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::HeapArray obj; + Program::HeapArray obj; obj.pointer = serde::Deserializable::deserialize(deserializer); obj.size = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const HeapValueType &lhs, const HeapValueType &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5556,11 +5622,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::HeapValueType &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::HeapValueType &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5568,15 +5634,15 @@ void serde::Serializable::serialize(const Circuit::HeapV template <> template -Circuit::HeapValueType serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::HeapValueType serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::HeapValueType obj; + Program::HeapValueType obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const HeapValueType::Simple &lhs, const HeapValueType::Simple &rhs) { return true; @@ -5597,21 +5663,21 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::HeapValueType::Simple &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::HeapValueType::Simple &obj, Serializer &serializer) { } template <> template -Circuit::HeapValueType::Simple serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::HeapValueType::Simple obj; +Program::HeapValueType::Simple serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::HeapValueType::Simple obj; return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const HeapValueType::Array &lhs, const HeapValueType::Array &rhs) { if (!(lhs.value_types == rhs.value_types)) { return false; } @@ -5634,25 +5700,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::HeapValueType::Array &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::HeapValueType::Array &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value_types, serializer); serde::Serializable::serialize(obj.size, serializer); } template <> template -Circuit::HeapValueType::Array serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::HeapValueType::Array obj; +Program::HeapValueType::Array serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::HeapValueType::Array obj; obj.value_types = serde::Deserializable::deserialize(deserializer); obj.size = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const HeapValueType::Vector &lhs, const HeapValueType::Vector &rhs) { if (!(lhs.value_types == rhs.value_types)) { return false; } @@ -5674,23 +5740,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::HeapValueType::Vector &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::HeapValueType::Vector &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value_types, serializer); } template <> template -Circuit::HeapValueType::Vector serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::HeapValueType::Vector obj; +Program::HeapValueType::Vector serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::HeapValueType::Vector obj; obj.value_types = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const HeapVector &lhs, const HeapVector &rhs) { if (!(lhs.pointer == rhs.pointer)) { return false; } @@ -5713,11 +5779,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::HeapVector &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::HeapVector &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.pointer, serializer); serde::Serializable::serialize(obj.size, serializer); @@ -5726,16 +5792,16 @@ void serde::Serializable::serialize(const Circuit::HeapVect template <> template -Circuit::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::HeapVector obj; + Program::HeapVector obj; obj.pointer = serde::Deserializable::deserialize(deserializer); obj.size = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const MemOp &lhs, const MemOp &rhs) { if (!(lhs.operation == rhs.operation)) { return false; } @@ -5759,11 +5825,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::MemOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::MemOp &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.operation, serializer); serde::Serializable::serialize(obj.index, serializer); @@ -5773,9 +5839,9 @@ void serde::Serializable::serialize(const Circuit::MemOp &obj, S template <> template -Circuit::MemOp serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::MemOp serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::MemOp obj; + Program::MemOp obj; obj.operation = serde::Deserializable::deserialize(deserializer); obj.index = serde::Deserializable::deserialize(deserializer); obj.value = serde::Deserializable::deserialize(deserializer); @@ -5783,7 +5849,7 @@ Circuit::MemOp serde::Deserializable::deserialize(Deserializer & return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const MemoryAddress &lhs, const MemoryAddress &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5805,11 +5871,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::MemoryAddress &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::MemoryAddress &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5817,15 +5883,15 @@ void serde::Serializable::serialize(const Circuit::Memor template <> template -Circuit::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::MemoryAddress obj; + Program::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode &lhs, const Opcode &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5847,11 +5913,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -5859,15 +5925,15 @@ void serde::Serializable::serialize(const Circuit::Opcode &obj, template <> template -Circuit::Opcode serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Opcode serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Opcode obj; + Program::Opcode obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::AssertZero &lhs, const Opcode::AssertZero &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5889,23 +5955,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::AssertZero &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::AssertZero &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::Opcode::AssertZero serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::AssertZero obj; +Program::Opcode::AssertZero serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::AssertZero obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::BlackBoxFuncCall &lhs, const Opcode::BlackBoxFuncCall &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5927,23 +5993,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::BlackBoxFuncCall &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::BlackBoxFuncCall &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::Opcode::BlackBoxFuncCall serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::BlackBoxFuncCall obj; +Program::Opcode::BlackBoxFuncCall serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::BlackBoxFuncCall obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::Directive &lhs, const Opcode::Directive &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -5965,23 +6031,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::Directive &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::Directive &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::Opcode::Directive serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::Directive obj; +Program::Opcode::Directive serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::Directive obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::Brillig &lhs, const Opcode::Brillig &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6003,23 +6069,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::Brillig &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::Brillig &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::Opcode::Brillig serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::Brillig obj; +Program::Opcode::Brillig serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::Brillig obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::MemoryOp &lhs, const Opcode::MemoryOp &rhs) { if (!(lhs.block_id == rhs.block_id)) { return false; } @@ -6043,11 +6109,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::MemoryOp &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::MemoryOp &obj, Serializer &serializer) { serde::Serializable::serialize(obj.block_id, serializer); serde::Serializable::serialize(obj.op, serializer); serde::Serializable::serialize(obj.predicate, serializer); @@ -6055,15 +6121,15 @@ void serde::Serializable::serialize(const Circuit::Op template <> template -Circuit::Opcode::MemoryOp serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::MemoryOp obj; +Program::Opcode::MemoryOp serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::MemoryOp obj; obj.block_id = serde::Deserializable::deserialize(deserializer); obj.op = serde::Deserializable::deserialize(deserializer); obj.predicate = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::MemoryInit &lhs, const Opcode::MemoryInit &rhs) { if (!(lhs.block_id == rhs.block_id)) { return false; } @@ -6086,25 +6152,25 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::MemoryInit &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::MemoryInit &obj, Serializer &serializer) { serde::Serializable::serialize(obj.block_id, serializer); serde::Serializable::serialize(obj.init, serializer); } template <> template -Circuit::Opcode::MemoryInit serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::MemoryInit obj; +Program::Opcode::MemoryInit serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::MemoryInit obj; obj.block_id = serde::Deserializable::deserialize(deserializer); obj.init = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Opcode::Call &lhs, const Opcode::Call &rhs) { if (!(lhs.id == rhs.id)) { return false; } @@ -6128,11 +6194,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Opcode::Call &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Opcode::Call &obj, Serializer &serializer) { serde::Serializable::serialize(obj.id, serializer); serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); @@ -6140,15 +6206,15 @@ void serde::Serializable::serialize(const Circuit::Opcode template <> template -Circuit::Opcode::Call serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::Opcode::Call obj; +Program::Opcode::Call serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::Opcode::Call obj; obj.id = serde::Deserializable::deserialize(deserializer); obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const OpcodeLocation &lhs, const OpcodeLocation &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6170,11 +6236,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::OpcodeLocation &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::OpcodeLocation &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -6182,15 +6248,15 @@ void serde::Serializable::serialize(const Circuit::Opco template <> template -Circuit::OpcodeLocation serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::OpcodeLocation serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::OpcodeLocation obj; + Program::OpcodeLocation obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const OpcodeLocation::Acir &lhs, const OpcodeLocation::Acir &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6212,23 +6278,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::OpcodeLocation::Acir &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::OpcodeLocation::Acir &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::OpcodeLocation::Acir serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::OpcodeLocation::Acir obj; +Program::OpcodeLocation::Acir serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::OpcodeLocation::Acir obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const OpcodeLocation::Brillig &lhs, const OpcodeLocation::Brillig &rhs) { if (!(lhs.acir_index == rhs.acir_index)) { return false; } @@ -6251,25 +6317,67 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::OpcodeLocation::Brillig &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::OpcodeLocation::Brillig &obj, Serializer &serializer) { serde::Serializable::serialize(obj.acir_index, serializer); serde::Serializable::serialize(obj.brillig_index, serializer); } template <> template -Circuit::OpcodeLocation::Brillig serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::OpcodeLocation::Brillig obj; +Program::OpcodeLocation::Brillig serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::OpcodeLocation::Brillig obj; obj.acir_index = serde::Deserializable::deserialize(deserializer); obj.brillig_index = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { + + inline bool operator==(const Program &lhs, const Program &rhs) { + if (!(lhs.functions == rhs.functions)) { return false; } + return true; + } + + inline std::vector Program::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline Program Program::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace Program + +template <> +template +void serde::Serializable::serialize(const Program::Program &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.functions, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +Program::Program serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + Program::Program obj; + obj.functions = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} + +namespace Program { inline bool operator==(const PublicInputs &lhs, const PublicInputs &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6291,11 +6399,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::PublicInputs &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::PublicInputs &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -6303,15 +6411,15 @@ void serde::Serializable::serialize(const Circuit::Public template <> template -Circuit::PublicInputs serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::PublicInputs serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::PublicInputs obj; + Program::PublicInputs obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Value &lhs, const Value &rhs) { if (!(lhs.inner == rhs.inner)) { return false; } @@ -6333,11 +6441,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Value &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Value &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.inner, serializer); serializer.decrease_container_depth(); @@ -6345,15 +6453,15 @@ void serde::Serializable::serialize(const Circuit::Value &obj, S template <> template -Circuit::Value serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Value serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Value obj; + Program::Value obj; obj.inner = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ValueOrArray &lhs, const ValueOrArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6375,11 +6483,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ValueOrArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ValueOrArray &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -6387,15 +6495,15 @@ void serde::Serializable::serialize(const Circuit::ValueO template <> template -Circuit::ValueOrArray serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::ValueOrArray serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::ValueOrArray obj; + Program::ValueOrArray obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ValueOrArray::MemoryAddress &lhs, const ValueOrArray::MemoryAddress &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6417,23 +6525,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ValueOrArray::MemoryAddress &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ValueOrArray::MemoryAddress &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::ValueOrArray::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::ValueOrArray::MemoryAddress obj; +Program::ValueOrArray::MemoryAddress serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ValueOrArray::MemoryAddress obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ValueOrArray::HeapArray &lhs, const ValueOrArray::HeapArray &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6455,23 +6563,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapArray &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ValueOrArray::HeapArray &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::ValueOrArray::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::ValueOrArray::HeapArray obj; +Program::ValueOrArray::HeapArray serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ValueOrArray::HeapArray obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const ValueOrArray::HeapVector &lhs, const ValueOrArray::HeapVector &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6493,23 +6601,23 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::ValueOrArray::HeapVector &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::ValueOrArray::HeapVector &obj, Serializer &serializer) { serde::Serializable::serialize(obj.value, serializer); } template <> template -Circuit::ValueOrArray::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { - Circuit::ValueOrArray::HeapVector obj; +Program::ValueOrArray::HeapVector serde::Deserializable::deserialize(Deserializer &deserializer) { + Program::ValueOrArray::HeapVector obj; obj.value = serde::Deserializable::deserialize(deserializer); return obj; } -namespace Circuit { +namespace Program { inline bool operator==(const Witness &lhs, const Witness &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -6531,11 +6639,11 @@ namespace Circuit { return value; } -} // end of namespace Circuit +} // end of namespace Program template <> template -void serde::Serializable::serialize(const Circuit::Witness &obj, Serializer &serializer) { +void serde::Serializable::serialize(const Program::Witness &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -6543,9 +6651,9 @@ void serde::Serializable::serialize(const Circuit::Witness &ob template <> template -Circuit::Witness serde::Deserializable::deserialize(Deserializer &deserializer) { +Program::Witness serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - Circuit::Witness obj; + Program::Witness obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; diff --git a/acvm-repo/acir/codegen/witness.cpp b/acvm-repo/acir/codegen/witness.cpp index 118d4ca7ac5..ad2b0550db2 100644 --- a/acvm-repo/acir/codegen/witness.cpp +++ b/acvm-repo/acir/codegen/witness.cpp @@ -3,7 +3,7 @@ #include "serde.hpp" #include "bincode.hpp" -namespace WitnessMap { +namespace WitnessStack { struct Witness { uint32_t value; @@ -14,17 +14,79 @@ namespace WitnessMap { }; struct WitnessMap { - std::map value; + std::map value; friend bool operator==(const WitnessMap&, const WitnessMap&); std::vector bincodeSerialize() const; static WitnessMap bincodeDeserialize(std::vector); }; -} // end of namespace WitnessMap + struct StackItem { + uint32_t index; + WitnessStack::WitnessMap witness; + friend bool operator==(const StackItem&, const StackItem&); + std::vector bincodeSerialize() const; + static StackItem bincodeDeserialize(std::vector); + }; + + struct WitnessStack { + std::vector stack; + + friend bool operator==(const WitnessStack&, const WitnessStack&); + std::vector bincodeSerialize() const; + static WitnessStack bincodeDeserialize(std::vector); + }; + +} // end of namespace WitnessStack + + +namespace WitnessStack { + + inline bool operator==(const StackItem &lhs, const StackItem &rhs) { + if (!(lhs.index == rhs.index)) { return false; } + if (!(lhs.witness == rhs.witness)) { return false; } + return true; + } + + inline std::vector StackItem::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline StackItem StackItem::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace WitnessStack + +template <> +template +void serde::Serializable::serialize(const WitnessStack::StackItem &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.index, serializer); + serde::Serializable::serialize(obj.witness, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +WitnessStack::StackItem serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + WitnessStack::StackItem obj; + obj.index = serde::Deserializable::deserialize(deserializer); + obj.witness = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} -namespace WitnessMap { +namespace WitnessStack { inline bool operator==(const Witness &lhs, const Witness &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -46,11 +108,11 @@ namespace WitnessMap { return value; } -} // end of namespace WitnessMap +} // end of namespace WitnessStack template <> template -void serde::Serializable::serialize(const WitnessMap::Witness &obj, Serializer &serializer) { +void serde::Serializable::serialize(const WitnessStack::Witness &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -58,15 +120,15 @@ void serde::Serializable::serialize(const WitnessMap::Witne template <> template -WitnessMap::Witness serde::Deserializable::deserialize(Deserializer &deserializer) { +WitnessStack::Witness serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - WitnessMap::Witness obj; + WitnessStack::Witness obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } -namespace WitnessMap { +namespace WitnessStack { inline bool operator==(const WitnessMap &lhs, const WitnessMap &rhs) { if (!(lhs.value == rhs.value)) { return false; } @@ -88,11 +150,11 @@ namespace WitnessMap { return value; } -} // end of namespace WitnessMap +} // end of namespace WitnessStack template <> template -void serde::Serializable::serialize(const WitnessMap::WitnessMap &obj, Serializer &serializer) { +void serde::Serializable::serialize(const WitnessStack::WitnessMap &obj, Serializer &serializer) { serializer.increase_container_depth(); serde::Serializable::serialize(obj.value, serializer); serializer.decrease_container_depth(); @@ -100,10 +162,52 @@ void serde::Serializable::serialize(const WitnessMap::Wi template <> template -WitnessMap::WitnessMap serde::Deserializable::deserialize(Deserializer &deserializer) { +WitnessStack::WitnessMap serde::Deserializable::deserialize(Deserializer &deserializer) { deserializer.increase_container_depth(); - WitnessMap::WitnessMap obj; + WitnessStack::WitnessMap obj; obj.value = serde::Deserializable::deserialize(deserializer); deserializer.decrease_container_depth(); return obj; } + +namespace WitnessStack { + + inline bool operator==(const WitnessStack &lhs, const WitnessStack &rhs) { + if (!(lhs.stack == rhs.stack)) { return false; } + return true; + } + + inline std::vector WitnessStack::bincodeSerialize() const { + auto serializer = serde::BincodeSerializer(); + serde::Serializable::serialize(*this, serializer); + return std::move(serializer).bytes(); + } + + inline WitnessStack WitnessStack::bincodeDeserialize(std::vector input) { + auto deserializer = serde::BincodeDeserializer(input); + auto value = serde::Deserializable::deserialize(deserializer); + if (deserializer.get_buffer_offset() < input.size()) { + throw serde::deserialization_error("Some input bytes were not read"); + } + return value; + } + +} // end of namespace WitnessStack + +template <> +template +void serde::Serializable::serialize(const WitnessStack::WitnessStack &obj, Serializer &serializer) { + serializer.increase_container_depth(); + serde::Serializable::serialize(obj.stack, serializer); + serializer.decrease_container_depth(); +} + +template <> +template +WitnessStack::WitnessStack serde::Deserializable::deserialize(Deserializer &deserializer) { + deserializer.increase_container_depth(); + WitnessStack::WitnessStack obj; + obj.stack = serde::Deserializable::deserialize(deserializer); + deserializer.decrease_container_depth(); + return obj; +} diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index 7e6cbf23803..b5d6348d34f 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -32,6 +32,13 @@ pub enum ExpressionWidth { }, } +/// A program represented by multiple ACIR circuits. The execution trace of these +/// circuits is dictated by construction of the [crate::native_types::WitnessStack]. +#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct Program { + pub functions: Vec, +} + #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct Circuit { // current_witness_index is the highest witness index in the circuit. The next witness to be added to this circuit @@ -152,7 +159,9 @@ impl Circuit { self.public_parameters.0.union(&self.return_values.0).cloned().collect(); PublicInputs(public_inputs) } +} +impl Program { fn write(&self, writer: W) -> std::io::Result<()> { let buf = bincode::serialize(self).unwrap(); let mut encoder = flate2::write::GzEncoder::new(writer, Compression::default()); @@ -169,36 +178,36 @@ impl Circuit { .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err)) } - pub fn serialize_circuit(circuit: &Circuit) -> Vec { - let mut circuit_bytes: Vec = Vec::new(); - circuit.write(&mut circuit_bytes).expect("expected circuit to be serializable"); - circuit_bytes + pub fn serialize_program(program: &Program) -> Vec { + let mut program_bytes: Vec = Vec::new(); + program.write(&mut program_bytes).expect("expected circuit to be serializable"); + program_bytes } - pub fn deserialize_circuit(serialized_circuit: &[u8]) -> std::io::Result { - Circuit::read(serialized_circuit) + pub fn deserialize_program(serialized_circuit: &[u8]) -> std::io::Result { + Program::read(serialized_circuit) } - // Serialize and base64 encode circuit - pub fn serialize_circuit_base64(circuit: &Circuit, s: S) -> Result + // Serialize and base64 encode program + pub fn serialize_program_base64(program: &Program, s: S) -> Result where S: Serializer, { - let circuit_bytes = Circuit::serialize_circuit(circuit); - let encoded_b64 = base64::engine::general_purpose::STANDARD.encode(circuit_bytes); + let program_bytes = Program::serialize_program(program); + let encoded_b64 = base64::engine::general_purpose::STANDARD.encode(program_bytes); s.serialize_str(&encoded_b64) } - // Deserialize and base64 decode circuit - pub fn deserialize_circuit_base64<'de, D>(deserializer: D) -> Result + // Deserialize and base64 decode program + pub fn deserialize_program_base64<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { let bytecode_b64: String = serde::Deserialize::deserialize(deserializer)?; - let circuit_bytes = base64::engine::general_purpose::STANDARD + let program_bytes = base64::engine::general_purpose::STANDARD .decode(bytecode_b64) .map_err(D::Error::custom)?; - let circuit = Self::deserialize_circuit(&circuit_bytes).map_err(D::Error::custom)?; + let circuit = Self::deserialize_program(&program_bytes).map_err(D::Error::custom)?; Ok(circuit) } } @@ -240,6 +249,22 @@ impl std::fmt::Debug for Circuit { } } +impl std::fmt::Display for Program { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (func_index, function) in self.functions.iter().enumerate() { + writeln!(f, "func {}", func_index)?; + writeln!(f, "{}", function)?; + } + Ok(()) + } +} + +impl std::fmt::Debug for Program { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self, f) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] pub struct PublicInputs(pub BTreeSet); @@ -262,7 +287,10 @@ mod tests { opcodes::{BlackBoxFuncCall, FunctionInput}, Circuit, Compression, Opcode, PublicInputs, }; - use crate::{circuit::ExpressionWidth, native_types::Witness}; + use crate::{ + circuit::{ExpressionWidth, Program}, + native_types::Witness, + }; use acir_field::FieldElement; fn and_opcode() -> Opcode { @@ -348,14 +376,15 @@ mod tests { assert_messages: Default::default(), recursive: false, }; + let program = Program { functions: vec![circuit] }; - fn read_write(circuit: Circuit) -> (Circuit, Circuit) { - let bytes = Circuit::serialize_circuit(&circuit); - let got_circuit = Circuit::deserialize_circuit(&bytes).unwrap(); - (circuit, got_circuit) + fn read_write(program: Program) -> (Program, Program) { + let bytes = Program::serialize_program(&program); + let got_program = Program::deserialize_program(&bytes).unwrap(); + (program, got_program) } - let (circ, got_circ) = read_write(circuit); + let (circ, got_circ) = read_write(program); assert_eq!(circ, got_circ); } @@ -380,11 +409,12 @@ mod tests { assert_messages: Default::default(), recursive: false, }; + let program = Program { functions: vec![circuit] }; - let json = serde_json::to_string_pretty(&circuit).unwrap(); + let json = serde_json::to_string_pretty(&program).unwrap(); let deserialized = serde_json::from_str(&json).unwrap(); - assert_eq!(circuit, deserialized); + assert_eq!(program, deserialized); } #[test] @@ -400,7 +430,7 @@ mod tests { encoder.write_all(bad_circuit).unwrap(); encoder.finish().unwrap(); - let deserialization_result = Circuit::deserialize_circuit(&zipped_bad_circuit); + let deserialization_result = Program::deserialize_program(&zipped_bad_circuit); assert!(deserialization_result.is_err()); } } diff --git a/acvm-repo/acir/src/lib.rs b/acvm-repo/acir/src/lib.rs index c7be5026850..d14159f34a1 100644 --- a/acvm-repo/acir/src/lib.rs +++ b/acvm-repo/acir/src/lib.rs @@ -42,9 +42,9 @@ mod reflection { brillig::{BrilligInputs, BrilligOutputs}, directives::Directive, opcodes::BlackBoxFuncCall, - Circuit, ExpressionWidth, Opcode, OpcodeLocation, + Circuit, ExpressionWidth, Opcode, OpcodeLocation, Program, }, - native_types::{Witness, WitnessMap}, + native_types::{Witness, WitnessMap, WitnessStack}, }; #[test] @@ -59,6 +59,7 @@ mod reflection { }; let mut tracer = Tracer::new(TracerConfig::default()); + tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); @@ -78,7 +79,7 @@ mod reflection { // Create C++ class definitions. let mut source = Vec::new(); - let config = serde_generate::CodeGeneratorConfig::new("Circuit".to_string()) + let config = serde_generate::CodeGeneratorConfig::new("Program".to_string()) .with_encodings(vec![serde_generate::Encoding::Bincode]); let generator = serde_generate::cpp::CodeGenerator::new(&config); generator.output(&mut source, ®istry).unwrap(); @@ -106,12 +107,13 @@ mod reflection { let mut tracer = Tracer::new(TracerConfig::default()); tracer.trace_simple_type::().unwrap(); tracer.trace_simple_type::().unwrap(); + tracer.trace_simple_type::().unwrap(); let registry = tracer.registry().unwrap(); // Create C++ class definitions. let mut source = Vec::new(); - let config = serde_generate::CodeGeneratorConfig::new("WitnessMap".to_string()) + let config = serde_generate::CodeGeneratorConfig::new("WitnessStack".to_string()) .with_encodings(vec![serde_generate::Encoding::Bincode]); let generator = serde_generate::cpp::CodeGenerator::new(&config); generator.output(&mut source, ®istry).unwrap(); diff --git a/acvm-repo/acir/src/native_types/mod.rs b/acvm-repo/acir/src/native_types/mod.rs index 66c822bfff8..eb9d1f6fd03 100644 --- a/acvm-repo/acir/src/native_types/mod.rs +++ b/acvm-repo/acir/src/native_types/mod.rs @@ -1,8 +1,10 @@ mod expression; mod witness; mod witness_map; +mod witness_stack; pub use expression::Expression; pub use witness::Witness; pub use witness_map::WitnessMap; -pub use witness_map::WitnessMapError; +pub use witness_stack::WitnessStack; +pub use witness_stack::WitnessStackError; diff --git a/acvm-repo/acir/src/native_types/witness_stack.rs b/acvm-repo/acir/src/native_types/witness_stack.rs new file mode 100644 index 00000000000..9592d90b014 --- /dev/null +++ b/acvm-repo/acir/src/native_types/witness_stack.rs @@ -0,0 +1,64 @@ +use std::io::Read; + +use flate2::bufread::GzDecoder; +use flate2::bufread::GzEncoder; +use flate2::Compression; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use super::WitnessMap; + +#[derive(Debug, Error)] +enum SerializationError { + #[error(transparent)] + Deflate(#[from] std::io::Error), +} + +#[derive(Debug, Error)] +#[error(transparent)] +pub struct WitnessStackError(#[from] SerializationError); + +/// An ordered set of witness maps for separate circuits +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)] +pub struct WitnessStack { + pub stack: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)] +pub struct StackItem { + /// Index into a [crate::circuit::Program] function list for which we have an associated witness + pub index: u32, + /// A full witness for the respective constraint system specified by the index + pub witness: WitnessMap, +} + +impl From for WitnessStack { + fn from(witness: WitnessMap) -> Self { + let stack = vec![StackItem { index: 0, witness }]; + Self { stack } + } +} + +impl TryFrom for Vec { + type Error = WitnessStackError; + + fn try_from(val: WitnessStack) -> Result { + let buf = bincode::serialize(&val).unwrap(); + let mut deflater = GzEncoder::new(buf.as_slice(), Compression::best()); + let mut buf_c = Vec::new(); + deflater.read_to_end(&mut buf_c).map_err(|err| WitnessStackError(err.into()))?; + Ok(buf_c) + } +} + +impl TryFrom<&[u8]> for WitnessStack { + type Error = WitnessStackError; + + fn try_from(bytes: &[u8]) -> Result { + let mut deflater = GzDecoder::new(bytes); + let mut buf_d = Vec::new(); + deflater.read_to_end(&mut buf_d).map_err(|err| WitnessStackError(err.into()))?; + let witness_stack = bincode::deserialize(&buf_d).unwrap(); + Ok(witness_stack) + } +} diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 2c8ad2b9986..64385a37582 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -1,13 +1,13 @@ //! This integration test defines a set of circuits which are used in order to test the acvm_js package. //! -//! The acvm_js test suite contains serialized [circuits][`Circuit`] which must be kept in sync with the format +//! The acvm_js test suite contains serialized program [circuits][`Program`] which must be kept in sync with the format //! outputted from the [ACIR crate][acir]. //! Breaking changes to the serialization format then require refreshing acvm_js's test suite. //! This file contains Rust definitions of these circuits and outputs the updated serialized format. //! //! These tests also check this circuit serialization against an expected value, erroring if the serialization changes. //! Generally in this situation we just need to refresh the `expected_serialization` variables to match the -//! actual output, **HOWEVER** note that this results in a breaking change to the ACIR format. +//! actual output, **HOWEVER** note that this results in a breaking change to the backend ACIR format. use std::collections::BTreeSet; @@ -15,7 +15,7 @@ use acir::{ circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::{BlackBoxFuncCall, BlockId, FunctionInput, MemOp}, - Circuit, Opcode, PublicInputs, + Circuit, Opcode, Program, PublicInputs, }, native_types::{Expression, Witness}, }; @@ -41,16 +41,17 @@ fn addition_circuit() { return_values: PublicInputs([Witness(3)].into()), ..Circuit::default() }; + let program = Program { functions: vec![circuit] }; - let bytes = Circuit::serialize_circuit(&circuit); + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 208, 49, 14, 192, 32, 8, 5, 80, 212, 30, 8, 4, 20, - 182, 94, 165, 166, 122, 255, 35, 52, 77, 28, 76, 58, 214, 191, 124, 166, 23, 242, 15, 0, 8, - 240, 77, 154, 125, 206, 198, 127, 161, 176, 209, 138, 139, 197, 88, 68, 122, 205, 157, 152, - 46, 204, 222, 76, 81, 180, 21, 35, 35, 53, 189, 179, 49, 119, 19, 171, 222, 188, 162, 147, - 112, 167, 161, 206, 99, 98, 105, 223, 95, 248, 26, 113, 90, 97, 185, 97, 217, 56, 173, 35, - 63, 243, 81, 87, 163, 125, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 75, 14, 128, 32, 12, 68, 249, 120, 160, 150, + 182, 208, 238, 188, 138, 68, 184, 255, 17, 212, 200, 130, 196, 165, 188, 164, 153, 174, 94, + 38, 227, 221, 203, 118, 159, 119, 95, 226, 200, 125, 36, 252, 3, 253, 66, 87, 152, 92, 4, + 153, 185, 149, 212, 144, 240, 128, 100, 85, 5, 88, 106, 86, 84, 20, 149, 51, 41, 81, 83, + 214, 98, 213, 10, 24, 50, 53, 236, 98, 212, 135, 44, 174, 235, 5, 143, 35, 12, 151, 159, + 126, 55, 109, 28, 231, 145, 47, 245, 105, 191, 143, 133, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -71,13 +72,14 @@ fn fixed_base_scalar_mul_circuit() { return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(3), Witness(4)])), ..Circuit::default() }; + let program = Program { functions: vec![circuit] }; - let bytes = Circuit::serialize_circuit(&circuit); + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 32, 16, 2, 109, 171, 175, 46, 221, - 209, 247, 229, 130, 130, 140, 200, 92, 0, 11, 157, 228, 35, 127, 212, 200, 29, 61, 116, 76, - 220, 217, 250, 171, 91, 113, 160, 66, 104, 242, 97, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 81, 10, 0, 48, 8, 66, 87, 219, 190, 118, 233, + 29, 61, 43, 3, 5, 121, 34, 207, 86, 231, 162, 198, 157, 124, 228, 71, 157, 220, 232, 161, + 227, 226, 206, 214, 95, 221, 74, 0, 116, 58, 13, 182, 105, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -98,13 +100,14 @@ fn pedersen_circuit() { return_values: PublicInputs(BTreeSet::from_iter(vec![Witness(2), Witness(3)])), ..Circuit::default() }; + let program = Program { functions: vec![circuit] }; - let bytes = Circuit::serialize_circuit(&circuit); + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 74, 135, 9, 0, 48, 8, 75, 171, 224, 255, 15, 139, - 27, 196, 64, 200, 100, 0, 15, 133, 80, 57, 89, 219, 127, 39, 173, 126, 235, 236, 247, 151, - 48, 224, 71, 90, 33, 97, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 74, 7, 6, 0, 0, 8, 108, 209, 255, 63, 156, 54, 233, + 56, 55, 17, 26, 18, 196, 241, 169, 250, 178, 141, 167, 32, 159, 254, 234, 238, 255, 87, + 112, 52, 63, 63, 101, 105, 0, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -139,26 +142,27 @@ fn schnorr_verify_circuit() { return_values: PublicInputs(BTreeSet::from([output])), ..Circuit::default() }; + let program = Program { functions: vec![circuit] }; - let bytes = Circuit::serialize_circuit(&circuit); + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 7, 74, 3, 1, 20, 69, 209, 177, 247, 222, 123, - 239, 189, 119, 141, 93, 99, 220, 133, 251, 95, 130, 152, 103, 78, 32, 3, 195, 33, 4, 66, - 248, 239, 254, 20, 69, 209, 84, 212, 158, 216, 206, 223, 234, 219, 204, 146, 239, 91, 170, - 111, 103, 245, 109, 101, 27, 219, 217, 193, 250, 219, 197, 110, 246, 176, 151, 125, 236, - 231, 0, 7, 57, 196, 97, 142, 112, 148, 99, 28, 231, 4, 39, 57, 197, 105, 206, 112, 150, - 115, 156, 231, 2, 23, 185, 196, 101, 174, 112, 149, 107, 92, 231, 6, 55, 185, 197, 109, - 238, 112, 151, 123, 220, 231, 1, 15, 121, 196, 99, 158, 240, 148, 103, 60, 231, 5, 47, 121, - 197, 107, 222, 240, 150, 119, 188, 231, 3, 75, 124, 228, 83, 195, 142, 121, 158, 125, 126, - 225, 43, 223, 248, 206, 15, 126, 178, 204, 47, 86, 248, 237, 119, 43, 76, 127, 105, 47, - 189, 165, 181, 116, 150, 198, 234, 125, 117, 249, 47, 233, 41, 45, 165, 163, 52, 148, 126, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 7, 78, 2, 1, 20, 69, 81, 236, 189, 247, 222, + 123, 239, 93, 177, 33, 34, 238, 194, 253, 47, 193, 200, 147, 67, 194, 36, 147, 163, 33, 33, + 228, 191, 219, 82, 168, 63, 63, 181, 183, 197, 223, 177, 147, 191, 181, 183, 149, 69, 159, + 183, 213, 222, 238, 218, 219, 206, 14, 118, 178, 139, 141, 183, 135, 189, 236, 99, 63, 7, + 56, 200, 33, 14, 115, 132, 163, 28, 227, 56, 39, 56, 201, 41, 78, 115, 134, 179, 156, 227, + 60, 23, 184, 200, 37, 46, 115, 133, 171, 92, 227, 58, 55, 184, 201, 45, 110, 115, 135, 187, + 220, 227, 62, 15, 120, 200, 35, 30, 243, 132, 167, 60, 227, 57, 47, 120, 201, 43, 94, 243, + 134, 183, 188, 227, 61, 31, 248, 200, 39, 22, 249, 204, 151, 166, 29, 243, 188, 250, 255, + 141, 239, 44, 241, 131, 101, 126, 178, 194, 47, 86, 249, 237, 123, 171, 76, 127, 105, 47, + 189, 165, 181, 116, 150, 198, 26, 125, 245, 248, 45, 233, 41, 45, 165, 163, 52, 148, 126, 210, 78, 186, 73, 51, 233, 37, 173, 164, 147, 52, 146, 62, 210, 70, 186, 72, 19, 233, 33, - 45, 164, 131, 52, 144, 253, 23, 139, 218, 238, 217, 60, 123, 103, 235, 236, 156, 141, 179, - 239, 166, 93, 183, 237, 185, 107, 199, 125, 251, 29, 218, 237, 216, 94, 167, 118, 58, 183, - 207, 165, 93, 174, 237, 113, 107, 135, 123, 247, 47, 185, 251, 147, 59, 191, 184, 239, 155, - 187, 126, 184, 103, 217, 29, 235, 55, 171, 223, 173, 104, 184, 231, 255, 243, 7, 236, 52, - 239, 128, 225, 3, 0, 0, + 45, 164, 131, 52, 144, 253, 151, 11, 245, 221, 179, 121, 246, 206, 214, 217, 57, 27, 103, + 223, 109, 187, 238, 218, 115, 223, 142, 135, 246, 59, 182, 219, 169, 189, 206, 237, 116, + 105, 159, 107, 187, 220, 218, 227, 222, 14, 143, 238, 95, 116, 247, 23, 119, 126, 115, 223, + 146, 187, 150, 221, 179, 226, 142, 141, 155, 53, 238, 86, 104, 186, 231, 255, 243, 7, 100, + 141, 232, 192, 233, 3, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -202,15 +206,16 @@ fn simple_brillig_foreign_call() { private_parameters: BTreeSet::from([Witness(1), Witness(2)]), ..Circuit::default() }; + let program = Program { functions: vec![circuit] }; - let bytes = Circuit::serialize_circuit(&circuit); + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 16, 67, 227, 21, 74, 233, - 212, 79, 177, 127, 208, 159, 233, 224, 226, 32, 226, 247, 139, 168, 16, 68, 93, 244, 45, - 119, 228, 142, 144, 92, 0, 20, 50, 7, 237, 76, 213, 190, 50, 245, 26, 175, 218, 231, 165, - 57, 175, 148, 14, 137, 179, 147, 191, 114, 211, 221, 216, 240, 59, 63, 107, 221, 115, 104, - 181, 103, 244, 43, 36, 10, 38, 68, 108, 25, 253, 238, 136, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 65, 10, 192, 32, 12, 4, 77, 10, 165, 244, 214, + 159, 216, 31, 244, 51, 61, 120, 241, 32, 226, 251, 85, 140, 176, 136, 122, 209, 129, 144, + 176, 9, 97, 151, 84, 225, 74, 69, 50, 31, 48, 35, 85, 251, 164, 235, 53, 94, 218, 247, 75, + 163, 95, 150, 12, 153, 179, 227, 191, 114, 195, 222, 216, 240, 59, 63, 75, 221, 251, 208, + 106, 207, 232, 150, 65, 100, 53, 33, 2, 22, 232, 178, 27, 144, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -301,19 +306,20 @@ fn complex_brillig_foreign_call() { private_parameters: BTreeSet::from([Witness(1), Witness(2), Witness(3)]), ..Circuit::default() }; + let program = Program { functions: vec![circuit] }; - let bytes = Circuit::serialize_circuit(&circuit); + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 75, 10, 132, 48, 12, 125, 177, 163, 35, 179, - 154, 35, 8, 51, 7, 232, 204, 9, 188, 139, 184, 83, 116, 233, 241, 173, 152, 98, 12, 213, - 141, 21, 244, 65, 232, 39, 175, 233, 35, 73, 155, 3, 32, 204, 48, 206, 18, 158, 19, 175, - 37, 60, 175, 228, 209, 30, 195, 143, 226, 197, 178, 103, 105, 76, 110, 160, 209, 156, 160, - 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 241, - 250, 201, 99, 206, 251, 96, 95, 161, 242, 14, 193, 243, 40, 162, 105, 253, 219, 12, 75, 47, - 146, 186, 251, 37, 116, 86, 93, 219, 55, 245, 96, 20, 85, 75, 253, 136, 249, 87, 249, 105, - 231, 220, 4, 249, 237, 132, 56, 20, 224, 109, 113, 223, 88, 82, 153, 34, 64, 34, 14, 164, - 69, 172, 48, 2, 23, 243, 6, 31, 25, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 65, 14, 132, 32, 12, 108, 101, 117, 205, 222, + 246, 7, 38, 187, 15, 96, 247, 5, 254, 197, 120, 211, 232, 209, 231, 139, 113, 136, 181, 65, + 47, 98, 162, 147, 52, 20, 24, 202, 164, 45, 48, 205, 200, 157, 49, 124, 227, 44, 129, 207, + 152, 75, 120, 94, 137, 209, 30, 195, 143, 227, 197, 178, 103, 105, 76, 110, 160, 209, 156, + 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 97, + 254, 196, 152, 99, 157, 176, 87, 168, 188, 147, 224, 121, 20, 209, 180, 254, 109, 70, 75, + 47, 178, 186, 251, 37, 116, 86, 93, 219, 55, 245, 96, 20, 85, 75, 253, 8, 255, 171, 246, + 121, 231, 220, 4, 249, 237, 132, 56, 28, 224, 109, 113, 223, 180, 164, 50, 165, 0, 137, 17, + 72, 139, 88, 97, 4, 173, 98, 132, 157, 33, 5, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -342,13 +348,16 @@ fn memory_op_circuit() { return_values: PublicInputs([Witness(4)].into()), ..Circuit::default() }; - let bytes = Circuit::serialize_circuit(&circuit); + let program = Program { functions: vec![circuit] }; + + let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 145, 187, 17, 0, 32, 8, 67, 195, 111, 31, 220, 192, - 253, 167, 178, 144, 2, 239, 236, 132, 194, 52, 129, 230, 93, 8, 6, 64, 176, 101, 225, 28, - 78, 49, 43, 238, 154, 225, 254, 166, 209, 205, 165, 98, 174, 212, 177, 188, 187, 92, 255, - 173, 92, 173, 190, 93, 82, 80, 78, 123, 14, 127, 60, 97, 1, 210, 144, 46, 242, 19, 3, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 81, 201, 13, 0, 32, 8, 147, 195, 125, 112, 3, 247, + 159, 74, 141, 60, 106, 226, 79, 120, 216, 132, 180, 124, 154, 82, 168, 108, 212, 57, 2, + 122, 129, 157, 201, 181, 150, 59, 186, 179, 189, 161, 101, 251, 82, 176, 175, 196, 121, 89, + 118, 185, 246, 91, 185, 26, 125, 187, 64, 80, 134, 29, 195, 31, 79, 24, 2, 250, 167, 252, + 27, 3, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/acvm-repo/acvm_js/src/compression.rs b/acvm-repo/acvm_js/src/compression.rs index fedaa514bf0..18e9216297e 100644 --- a/acvm-repo/acvm_js/src/compression.rs +++ b/acvm-repo/acvm_js/src/compression.rs @@ -1,4 +1,4 @@ -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use js_sys::JsString; use wasm_bindgen::prelude::wasm_bindgen; @@ -13,10 +13,11 @@ pub fn compress_witness(witness_map: JsWitnessMap) -> Result, JsString> console_error_panic_hook::set_once(); let witness_map = WitnessMap::from(witness_map); - let compressed_witness_map: Vec = - Vec::::try_from(witness_map).map_err(|err| err.to_string())?; + let witness_stack = WitnessStack::from(witness_map); + let compressed_witness_stack: Vec = + Vec::::try_from(witness_stack).map_err(|err| err.to_string())?; - Ok(compressed_witness_map) + Ok(compressed_witness_stack) } /// Decompresses a compressed witness as outputted by Nargo into a `WitnessMap`. @@ -27,8 +28,8 @@ pub fn compress_witness(witness_map: JsWitnessMap) -> Result, JsString> pub fn decompress_witness(compressed_witness: Vec) -> Result { console_error_panic_hook::set_once(); - let witness_map = - WitnessMap::try_from(compressed_witness.as_slice()).map_err(|err| err.to_string())?; + let witness_stack = + WitnessStack::try_from(compressed_witness.as_slice()).map_err(|err| err.to_string())?; - Ok(witness_map.into()) + Ok(witness_stack.stack[0].witness.clone().into()) } diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 3f691e1abf2..ac71a573e64 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -1,5 +1,5 @@ use acvm::{ - acir::circuit::Circuit, + acir::circuit::Program, pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; @@ -34,7 +34,7 @@ pub async fn create_black_box_solver() -> WasmBlackBoxFunctionSolver { /// @returns {WitnessMap} The solved witness calculated by executing the circuit on the provided inputs. #[wasm_bindgen(js_name = executeCircuit, skip_jsdoc)] pub async fn execute_circuit( - circuit: Vec, + program: Vec, initial_witness: JsWitnessMap, foreign_call_handler: ForeignCallHandler, ) -> Result { @@ -42,7 +42,7 @@ pub async fn execute_circuit( let solver = WasmBlackBoxFunctionSolver::initialize().await; - execute_circuit_with_black_box_solver(&solver, circuit, initial_witness, foreign_call_handler) + execute_circuit_with_black_box_solver(&solver, program, initial_witness, foreign_call_handler) .await } @@ -56,13 +56,21 @@ pub async fn execute_circuit( #[wasm_bindgen(js_name = executeCircuitWithBlackBoxSolver, skip_jsdoc)] pub async fn execute_circuit_with_black_box_solver( solver: &WasmBlackBoxFunctionSolver, - circuit: Vec, + // TODO(https://github.com/noir-lang/noir/issues/4428): These need to be updated to match the same interfaces + // as the native ACVM executor. Right now native execution still only handles one circuit so I do not feel the need + // to break the JS interface just yet. + program: Vec, initial_witness: JsWitnessMap, foreign_call_handler: ForeignCallHandler, ) -> Result { console_error_panic_hook::set_once(); - let circuit: Circuit = Circuit::deserialize_circuit(&circuit) + let program: Program = Program::deserialize_program(&program) .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; + let circuit = match program.functions.len() { + 0 => return Ok(initial_witness), + 1 => &program.functions[0], + _ => return Err(JsExecutionError::new("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit".to_string(), None).into()) + }; let mut acvm = ACVM::new(&solver.0, &circuit.opcodes, initial_witness.into()); diff --git a/acvm-repo/acvm_js/src/public_witness.rs b/acvm-repo/acvm_js/src/public_witness.rs index 8dc66c435b3..a0d5b5f8be2 100644 --- a/acvm-repo/acvm_js/src/public_witness.rs +++ b/acvm-repo/acvm_js/src/public_witness.rs @@ -1,5 +1,5 @@ use acvm::acir::{ - circuit::Circuit, + circuit::Program, native_types::{Witness, WitnessMap}, }; use js_sys::JsString; @@ -26,16 +26,25 @@ fn extract_indices(witness_map: &WitnessMap, indices: Vec) -> Result, + // TODO(https://github.com/noir-lang/noir/issues/4428): These need to be updated to match the same interfaces + // as the native ACVM executor. Right now native execution still only handles one circuit so I do not feel the need + // to break the JS interface just yet. + program: Vec, witness_map: JsWitnessMap, ) -> Result { console_error_panic_hook::set_once(); - let circuit: Circuit = - Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit"); + let program: Program = + Program::deserialize_program(&program).expect("Failed to deserialize circuit"); + let circuit = match program.functions.len() { + 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), + 1 => &program.functions[0], + _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + }; + let witness_map = WitnessMap::from(witness_map); let return_witness = - extract_indices(&witness_map, circuit.return_values.0.into_iter().collect())?; + extract_indices(&witness_map, circuit.return_values.0.clone().into_iter().collect())?; Ok(JsWitnessMap::from(return_witness)) } @@ -47,16 +56,22 @@ pub fn get_return_witness( /// @returns {WitnessMap} A witness map containing the circuit's public parameters. #[wasm_bindgen(js_name = getPublicParametersWitness)] pub fn get_public_parameters_witness( - circuit: Vec, + program: Vec, solved_witness: JsWitnessMap, ) -> Result { console_error_panic_hook::set_once(); - let circuit: Circuit = - Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit"); + let program: Program = + Program::deserialize_program(&program).expect("Failed to deserialize circuit"); + let circuit = match program.functions.len() { + 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), + 1 => &program.functions[0], + _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + }; + let witness_map = WitnessMap::from(solved_witness); let public_params_witness = - extract_indices(&witness_map, circuit.public_parameters.0.into_iter().collect())?; + extract_indices(&witness_map, circuit.public_parameters.0.clone().into_iter().collect())?; Ok(JsWitnessMap::from(public_params_witness)) } @@ -68,16 +83,22 @@ pub fn get_public_parameters_witness( /// @returns {WitnessMap} A witness map containing the circuit's public inputs. #[wasm_bindgen(js_name = getPublicWitness)] pub fn get_public_witness( - circuit: Vec, + program: Vec, solved_witness: JsWitnessMap, ) -> Result { console_error_panic_hook::set_once(); - let circuit: Circuit = - Circuit::deserialize_circuit(&circuit).expect("Failed to deserialize circuit"); + let program: Program = + Program::deserialize_program(&program).expect("Failed to deserialize circuit"); + let circuit = match program.functions.len() { + 0 => return Ok(JsWitnessMap::from(WitnessMap::new())), + 1 => &program.functions[0], + _ => return Err(JsString::from("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit")) + }; + let witness_map = WitnessMap::from(solved_witness); let public_witness = - extract_indices(&witness_map, circuit.public_inputs().0.into_iter().collect())?; + extract_indices(&witness_map, circuit.public_inputs().0.clone().into_iter().collect())?; Ok(JsWitnessMap::from(public_witness)) } diff --git a/acvm-repo/acvm_js/test/shared/addition.ts b/acvm-repo/acvm_js/test/shared/addition.ts index 217902bdea6..b56a4286878 100644 --- a/acvm-repo/acvm_js/test/shared/addition.ts +++ b/acvm-repo/acvm_js/test/shared/addition.ts @@ -2,11 +2,11 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `addition_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 208, 49, 14, 192, 32, 8, 5, 80, 212, 30, 8, 4, 20, 182, 94, 165, 166, 122, - 255, 35, 52, 77, 28, 76, 58, 214, 191, 124, 166, 23, 242, 15, 0, 8, 240, 77, 154, 125, 206, 198, 127, 161, 176, 209, - 138, 139, 197, 88, 68, 122, 205, 157, 152, 46, 204, 222, 76, 81, 180, 21, 35, 35, 53, 189, 179, 49, 119, 19, 171, 222, - 188, 162, 147, 112, 167, 161, 206, 99, 98, 105, 223, 95, 248, 26, 113, 90, 97, 185, 97, 217, 56, 173, 35, 63, 243, 81, - 87, 163, 125, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 75, 14, 128, 32, 12, 68, 249, 120, 160, 150, 182, 208, 238, 188, 138, 68, + 184, 255, 17, 212, 200, 130, 196, 165, 188, 164, 153, 174, 94, 38, 227, 221, 203, 118, 159, 119, 95, 226, 200, 125, + 36, 252, 3, 253, 66, 87, 152, 92, 4, 153, 185, 149, 212, 144, 240, 128, 100, 85, 5, 88, 106, 86, 84, 20, 149, 51, 41, + 81, 83, 214, 98, 213, 10, 24, 50, 53, 236, 98, 212, 135, 44, 174, 235, 5, 143, 35, 12, 151, 159, 126, 55, 109, 28, + 231, 145, 47, 245, 105, 191, 143, 133, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ diff --git a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index 27abd72305f..fba8470585f 100644 --- a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,13 +2,13 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 75, 10, 132, 48, 12, 125, 177, 163, 35, 179, 154, 35, 8, 51, 7, 232, 204, - 9, 188, 139, 184, 83, 116, 233, 241, 173, 152, 98, 12, 213, 141, 21, 244, 65, 232, 39, 175, 233, 35, 73, 155, 3, 32, - 204, 48, 206, 18, 158, 19, 175, 37, 60, 175, 228, 209, 30, 195, 143, 226, 197, 178, 103, 105, 76, 110, 160, 209, 156, - 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 241, 250, 201, 99, 206, 251, - 96, 95, 161, 242, 14, 193, 243, 40, 162, 105, 253, 219, 12, 75, 47, 146, 186, 251, 37, 116, 86, 93, 219, 55, 245, 96, - 20, 85, 75, 253, 136, 249, 87, 249, 105, 231, 220, 4, 249, 237, 132, 56, 20, 224, 109, 113, 223, 88, 82, 153, 34, 64, - 34, 14, 164, 69, 172, 48, 2, 23, 243, 6, 31, 25, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 65, 14, 132, 32, 12, 108, 101, 117, 205, 222, 246, 7, 38, 187, 15, 96, + 247, 5, 254, 197, 120, 211, 232, 209, 231, 139, 113, 136, 181, 65, 47, 98, 162, 147, 52, 20, 24, 202, 164, 45, 48, + 205, 200, 157, 49, 124, 227, 44, 129, 207, 152, 75, 120, 94, 137, 209, 30, 195, 143, 227, 197, 178, 103, 105, 76, 110, + 160, 209, 156, 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 97, 254, 196, + 152, 99, 157, 176, 87, 168, 188, 147, 224, 121, 20, 209, 180, 254, 109, 70, 75, 47, 178, 186, 251, 37, 116, 86, 93, + 219, 55, 245, 96, 20, 85, 75, 253, 8, 255, 171, 246, 121, 231, 220, 4, 249, 237, 132, 56, 28, 224, 109, 113, 223, 180, + 164, 50, 165, 0, 137, 17, 72, 139, 88, 97, 4, 173, 98, 132, 157, 33, 5, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts b/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts index c0859f50135..5aef521f231 100644 --- a/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts +++ b/acvm-repo/acvm_js/test/shared/fixed_base_scalar_mul.ts @@ -1,8 +1,8 @@ // See `fixed_base_scalar_mul_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 91, 10, 0, 32, 16, 2, 109, 171, 175, 46, 221, 209, 247, 229, 130, 130, - 140, 200, 92, 0, 11, 157, 228, 35, 127, 212, 200, 29, 61, 116, 76, 220, 217, 250, 171, 91, 113, 160, 66, 104, 242, 97, - 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 138, 81, 10, 0, 48, 8, 66, 87, 219, 190, 118, 233, 29, 61, 43, 3, 5, 121, 34, + 207, 86, 231, 162, 198, 157, 124, 228, 71, 157, 220, 232, 161, 227, 226, 206, 214, 95, 221, 74, 0, 116, 58, 13, 182, + 105, 0, 0, 0, ]); export const initialWitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/acvm-repo/acvm_js/test/shared/foreign_call.ts b/acvm-repo/acvm_js/test/shared/foreign_call.ts index 0be8937b57d..dd010f0c5e5 100644 --- a/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,10 +2,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 177, 10, 192, 32, 16, 67, 227, 21, 74, 233, 212, 79, 177, 127, 208, 159, - 233, 224, 226, 32, 226, 247, 139, 168, 16, 68, 93, 244, 45, 119, 228, 142, 144, 92, 0, 20, 50, 7, 237, 76, 213, 190, - 50, 245, 26, 175, 218, 231, 165, 57, 175, 148, 14, 137, 179, 147, 191, 114, 211, 221, 216, 240, 59, 63, 107, 221, 115, - 104, 181, 103, 244, 43, 36, 10, 38, 68, 108, 25, 253, 238, 136, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 65, 10, 192, 32, 12, 4, 77, 10, 165, 244, 214, 159, 216, 31, 244, 51, 61, + 120, 241, 32, 226, 251, 85, 140, 176, 136, 122, 209, 129, 144, 176, 9, 97, 151, 84, 225, 74, 69, 50, 31, 48, 35, 85, + 251, 164, 235, 53, 94, 218, 247, 75, 163, 95, 150, 12, 153, 179, 227, 191, 114, 195, 222, 216, 240, 59, 63, 75, 221, + 251, 208, 106, 207, 232, 150, 65, 100, 53, 33, 2, 22, 232, 178, 27, 144, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/acvm-repo/acvm_js/test/shared/memory_op.ts b/acvm-repo/acvm_js/test/shared/memory_op.ts index b5ab64b3447..1d0e06b3c8a 100644 --- a/acvm-repo/acvm_js/test/shared/memory_op.ts +++ b/acvm-repo/acvm_js/test/shared/memory_op.ts @@ -1,9 +1,9 @@ // See `memory_op_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 145, 187, 17, 0, 32, 8, 67, 195, 111, 31, 220, 192, 253, 167, 178, 144, 2, - 239, 236, 132, 194, 52, 129, 230, 93, 8, 6, 64, 176, 101, 225, 28, 78, 49, 43, 238, 154, 225, 254, 166, 209, 205, 165, - 98, 174, 212, 177, 188, 187, 92, 255, 173, 92, 173, 190, 93, 82, 80, 78, 123, 14, 127, 60, 97, 1, 210, 144, 46, 242, - 19, 3, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 81, 201, 13, 0, 32, 8, 147, 195, 125, 112, 3, 247, 159, 74, 141, 60, 106, 226, + 79, 120, 216, 132, 180, 124, 154, 82, 168, 108, 212, 57, 2, 122, 129, 157, 201, 181, 150, 59, 186, 179, 189, 161, 101, + 251, 82, 176, 175, 196, 121, 89, 118, 185, 246, 91, 185, 26, 125, 187, 64, 80, 134, 29, 195, 31, 79, 24, 2, 250, 167, + 252, 27, 3, 0, 0, ]); export const initialWitnessMap = new Map([ diff --git a/acvm-repo/acvm_js/test/shared/pedersen.ts b/acvm-repo/acvm_js/test/shared/pedersen.ts index 5150d24131c..00d207053d8 100644 --- a/acvm-repo/acvm_js/test/shared/pedersen.ts +++ b/acvm-repo/acvm_js/test/shared/pedersen.ts @@ -1,7 +1,7 @@ // See `pedersen_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 74, 135, 9, 0, 48, 8, 75, 171, 224, 255, 15, 139, 27, 196, 64, 200, 100, 0, 15, - 133, 80, 57, 89, 219, 127, 39, 173, 126, 235, 236, 247, 151, 48, 224, 71, 90, 33, 97, 0, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 93, 74, 7, 6, 0, 0, 8, 108, 209, 255, 63, 156, 54, 233, 56, 55, 17, 26, 18, 196, + 241, 169, 250, 178, 141, 167, 32, 159, 254, 234, 238, 255, 87, 112, 52, 63, 63, 101, 105, 0, 0, 0, ]); export const initialWitnessMap = new Map([[1, '0x0000000000000000000000000000000000000000000000000000000000000001']]); diff --git a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts index 2127de66f69..14c32c615c8 100644 --- a/acvm-repo/acvm_js/test/shared/schnorr_verify.ts +++ b/acvm-repo/acvm_js/test/shared/schnorr_verify.ts @@ -1,17 +1,17 @@ // See `schnorr_verify_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 7, 74, 3, 1, 20, 69, 209, 177, 247, 222, 123, 239, 189, 119, 141, 93, 99, - 220, 133, 251, 95, 130, 152, 103, 78, 32, 3, 195, 33, 4, 66, 248, 239, 254, 20, 69, 209, 84, 212, 158, 216, 206, 223, - 234, 219, 204, 146, 239, 91, 170, 111, 103, 245, 109, 101, 27, 219, 217, 193, 250, 219, 197, 110, 246, 176, 151, 125, - 236, 231, 0, 7, 57, 196, 97, 142, 112, 148, 99, 28, 231, 4, 39, 57, 197, 105, 206, 112, 150, 115, 156, 231, 2, 23, - 185, 196, 101, 174, 112, 149, 107, 92, 231, 6, 55, 185, 197, 109, 238, 112, 151, 123, 220, 231, 1, 15, 121, 196, 99, - 158, 240, 148, 103, 60, 231, 5, 47, 121, 197, 107, 222, 240, 150, 119, 188, 231, 3, 75, 124, 228, 83, 195, 142, 121, - 158, 125, 126, 225, 43, 223, 248, 206, 15, 126, 178, 204, 47, 86, 248, 237, 119, 43, 76, 127, 105, 47, 189, 165, 181, - 116, 150, 198, 234, 125, 117, 249, 47, 233, 41, 45, 165, 163, 52, 148, 126, 210, 78, 186, 73, 51, 233, 37, 173, 164, - 147, 52, 146, 62, 210, 70, 186, 72, 19, 233, 33, 45, 164, 131, 52, 144, 253, 23, 139, 218, 238, 217, 60, 123, 103, - 235, 236, 156, 141, 179, 239, 166, 93, 183, 237, 185, 107, 199, 125, 251, 29, 218, 237, 216, 94, 167, 118, 58, 183, - 207, 165, 93, 174, 237, 113, 107, 135, 123, 247, 47, 185, 251, 147, 59, 191, 184, 239, 155, 187, 126, 184, 103, 217, - 29, 235, 55, 171, 223, 173, 104, 184, 231, 255, 243, 7, 236, 52, 239, 128, 225, 3, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 77, 210, 7, 78, 2, 1, 20, 69, 81, 236, 189, 247, 222, 123, 239, 93, 177, 33, 34, + 238, 194, 253, 47, 193, 200, 147, 67, 194, 36, 147, 163, 33, 33, 228, 191, 219, 82, 168, 63, 63, 181, 183, 197, 223, + 177, 147, 191, 181, 183, 149, 69, 159, 183, 213, 222, 238, 218, 219, 206, 14, 118, 178, 139, 141, 183, 135, 189, 236, + 99, 63, 7, 56, 200, 33, 14, 115, 132, 163, 28, 227, 56, 39, 56, 201, 41, 78, 115, 134, 179, 156, 227, 60, 23, 184, + 200, 37, 46, 115, 133, 171, 92, 227, 58, 55, 184, 201, 45, 110, 115, 135, 187, 220, 227, 62, 15, 120, 200, 35, 30, + 243, 132, 167, 60, 227, 57, 47, 120, 201, 43, 94, 243, 134, 183, 188, 227, 61, 31, 248, 200, 39, 22, 249, 204, 151, + 166, 29, 243, 188, 250, 255, 141, 239, 44, 241, 131, 101, 126, 178, 194, 47, 86, 249, 237, 123, 171, 76, 127, 105, 47, + 189, 165, 181, 116, 150, 198, 26, 125, 245, 248, 45, 233, 41, 45, 165, 163, 52, 148, 126, 210, 78, 186, 73, 51, 233, + 37, 173, 164, 147, 52, 146, 62, 210, 70, 186, 72, 19, 233, 33, 45, 164, 131, 52, 144, 253, 151, 11, 245, 221, 179, + 121, 246, 206, 214, 217, 57, 27, 103, 223, 109, 187, 238, 218, 115, 223, 142, 135, 246, 59, 182, 219, 169, 189, 206, + 237, 116, 105, 159, 107, 187, 220, 218, 227, 222, 14, 143, 238, 95, 116, 247, 23, 119, 126, 115, 223, 146, 187, 150, + 221, 179, 226, 142, 141, 155, 53, 238, 86, 104, 186, 231, 255, 243, 7, 100, 141, 232, 192, 233, 3, 0, 0, ]); export const initialWitnessMap = new Map([ diff --git a/acvm-repo/acvm_js/test/shared/witness_compression.ts b/acvm-repo/acvm_js/test/shared/witness_compression.ts index 36b9a0284af..d2326c1b11b 100644 --- a/acvm-repo/acvm_js/test/shared/witness_compression.ts +++ b/acvm-repo/acvm_js/test/shared/witness_compression.ts @@ -4,19 +4,21 @@ // assert(x != y); // x + y // } +// +// Regenerate this byte array by going to the Noir integration tests and running `/test_programs/execution_success/witness_compression` +// after recompiling Noir to print the witness byte array to be written to file after execution export const expectedCompressedWitnessMap = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 173, 208, 187, 13, 128, 48, 12, 4, 80, 190, 153, 199, 142, 237, 196, 238, 88, 133, - 8, 103, 255, 17, 64, 34, 5, 61, 62, 233, 164, 171, 94, 113, 105, 122, 51, 63, 61, 198, 134, 127, 193, 37, 206, 202, - 235, 199, 34, 40, 204, 94, 179, 35, 225, 9, 217, 154, 10, 176, 180, 162, 168, 40, 42, 87, 86, 34, 87, 214, 106, 205, - 42, 24, 50, 57, 118, 49, 234, 3, 219, 2, 173, 61, 240, 175, 20, 103, 209, 13, 151, 252, 77, 33, 208, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 173, 206, 185, 13, 0, 48, 8, 67, 209, 144, 107, 30, 146, 44, 144, 253, 167, 162, + 65, 130, 158, 239, 198, 174, 158, 44, 45, 178, 211, 254, 222, 90, 203, 17, 206, 186, 29, 252, 53, 64, 107, 114, 150, + 46, 206, 122, 6, 24, 73, 44, 193, 220, 1, 0, 0, ]); export const expectedWitnessMap = new Map([ - [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [2, '0x0000000000000000000000000000000000000000000000000000000000000002'], - [3, '0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000'], - [4, '0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000'], - [5, '0x0000000000000000000000000000000000000000000000000000000000000001'], - [6, '0x0000000000000000000000000000000000000000000000000000000000000003'], + [0, '0x0000000000000000000000000000000000000000000000000000000000000001'], + [1, '0x0000000000000000000000000000000000000000000000000000000000000002'], + [2, '0x0000000000000000000000000000000000000000000000000000000000000001'], + [3, '0x0000000000000000000000000000000000000000000000000000000000000001'], + [4, '0x0000000000000000000000000000000000000000000000000000000000000000'], + [5, '0x0000000000000000000000000000000000000000000000000000000000000003'], ]); diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index 03a8e53e510..22a0ebe1170 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -156,6 +156,13 @@ pub enum BrilligOpcode { destination: MemoryAddress, source: MemoryAddress, }, + /// destination = condition > 0 ? source_a : source_b + ConditionalMov { + destination: MemoryAddress, + source_a: MemoryAddress, + source_b: MemoryAddress, + condition: MemoryAddress, + }, Load { destination: MemoryAddress, source_pointer: MemoryAddress, diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index c7bf014f068..e2a037618a4 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -339,6 +339,15 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.memory.write(*destination_address, source_value); self.increment_program_counter() } + Opcode::ConditionalMov { destination, source_a, source_b, condition } => { + let condition_value = self.memory.read(*condition); + if condition_value.is_zero() { + self.memory.write(*destination, self.memory.read(*source_b)); + } else { + self.memory.write(*destination, self.memory.read(*source_a)); + } + self.increment_program_counter() + } Opcode::Trap => self.fail("explicit trap hit in brillig".to_string()), Opcode::Stop { return_data_offset, return_data_size } => { self.finish(*return_data_offset, *return_data_size) @@ -793,6 +802,52 @@ mod tests { assert_eq!(source_value, Value::from(1u128)); } + #[test] + fn cmov_opcode() { + let calldata = + vec![Value::from(0u128), Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + + let calldata_copy = Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 4, + offset: 0, + }; + + let opcodes = &[ + calldata_copy, + Opcode::ConditionalMov { + destination: MemoryAddress(4), // Sets 3_u128 to memory address 4 + source_a: MemoryAddress(2), + source_b: MemoryAddress(3), + condition: MemoryAddress(0), + }, + Opcode::ConditionalMov { + destination: MemoryAddress(5), // Sets 2_u128 to memory address 5 + source_a: MemoryAddress(2), + source_b: MemoryAddress(3), + condition: MemoryAddress(1), + }, + ]; + let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); + + let VM { memory, .. } = vm; + + let destination_value = memory.read(MemoryAddress::from(4)); + assert_eq!(destination_value, Value::from(3_u128)); + + let source_value = memory.read(MemoryAddress::from(5)); + assert_eq!(source_value, Value::from(2_u128)); + } + #[test] fn cmp_binary_ops() { let bit_size = 32; diff --git a/aztec_macros/Cargo.toml b/aztec_macros/Cargo.toml index ed9821fabcf..355036d28a7 100644 --- a/aztec_macros/Cargo.toml +++ b/aztec_macros/Cargo.toml @@ -14,3 +14,5 @@ noirc_frontend.workspace = true noirc_errors.workspace = true iter-extended.workspace = true convert_case = "0.6.0" +regex = "1.10" + diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index e0100977eee..0fe450e6cb1 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -5,22 +5,27 @@ use transforms::{ compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, events::{generate_selector_impl, transform_events}, functions::{transform_function, transform_unconstrained, transform_vm_function}, + note_interface::generate_note_interface_impl, storage::{ assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, generate_storage_implementation, }, }; -use noirc_frontend::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; - -use noirc_frontend::macros_api::SortedModule; -use noirc_frontend::macros_api::{CrateId, MacroError}; -use noirc_frontend::macros_api::{FileId, MacroProcessor}; -use noirc_frontend::macros_api::{HirContext, SecondaryAttribute, Span}; +use noirc_frontend::{ + hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, + macros_api::{ + CrateId, FileId, HirContext, MacroError, MacroProcessor, SecondaryAttribute, SortedModule, + Span, + }, +}; -use utils::ast_utils::is_custom_attribute; -use utils::checks::{check_for_aztec_dependency, has_aztec_dependency}; -use utils::{constants::MAX_CONTRACT_PRIVATE_FUNCTIONS, errors::AztecMacroError}; +use utils::{ + ast_utils::is_custom_attribute, + checks::{check_for_aztec_dependency, has_aztec_dependency}, + constants::MAX_CONTRACT_PRIVATE_FUNCTIONS, + errors::AztecMacroError, +}; pub struct AztecMacro; impl MacroProcessor for AztecMacro { @@ -28,9 +33,10 @@ impl MacroProcessor for AztecMacro { &self, ast: SortedModule, crate_id: &CrateId, + file_id: FileId, context: &HirContext, ) -> Result { - transform(ast, crate_id, context) + transform(ast, crate_id, file_id, context) } fn process_collected_defs( @@ -61,38 +67,34 @@ impl MacroProcessor for AztecMacro { fn transform( mut ast: SortedModule, crate_id: &CrateId, + file_id: FileId, context: &HirContext, ) -> Result { // Usage -> mut ast -> aztec_library::transform(&mut ast) // Covers all functions in the ast for submodule in ast.submodules.iter_mut().filter(|submodule| submodule.is_contract) { - if transform_module(&mut submodule.contents, crate_id, context) - .map_err(|(err, file_id)| (err.into(), file_id))? - { + if transform_module(&mut submodule.contents).map_err(|err| (err.into(), file_id))? { check_for_aztec_dependency(crate_id, context)?; } } + + generate_note_interface_impl(&mut ast).map_err(|err| (err.into(), file_id))?; + Ok(ast) } /// Determines if ast nodes are annotated with aztec attributes. /// For annotated functions it calls the `transform` function which will perform the required transformations. /// Returns true if an annotated node is found, false otherwise -fn transform_module( - module: &mut SortedModule, - crate_id: &CrateId, - context: &HirContext, -) -> Result { +fn transform_module(module: &mut SortedModule) -> Result { let mut has_transformed_module = false; // Check for a user defined storage struct let storage_defined = check_for_storage_definition(module); let storage_implemented = check_for_storage_implementation(module); - let crate_graph = &context.crate_graph[crate_id]; - if storage_defined && !storage_implemented { - generate_storage_implementation(module).map_err(|err| (err, crate_graph.root_file_id))?; + generate_storage_implementation(module)?; } for structure in module.types.iter() { @@ -144,12 +146,10 @@ fn transform_module( is_initializer, insert_init_check, is_internal, - ) - .map_err(|err| (err, crate_graph.root_file_id))?; + )?; has_transformed_module = true; } else if is_public_vm { - transform_vm_function(func, storage_defined) - .map_err(|err| (err, crate_graph.root_file_id))?; + transform_vm_function(func, storage_defined)?; has_transformed_module = true; } else if storage_defined && func.def.is_unconstrained { transform_unconstrained(func); @@ -173,11 +173,9 @@ fn transform_module( .count(); if private_functions_count > MAX_CONTRACT_PRIVATE_FUNCTIONS { - let crate_graph = &context.crate_graph[crate_id]; - return Err(( - AztecMacroError::ContractHasTooManyPrivateFunctions { span: Span::default() }, - crate_graph.root_file_id, - )); + return Err(AztecMacroError::ContractHasTooManyPrivateFunctions { + span: Span::default(), + }); } } diff --git a/aztec_macros/src/transforms/mod.rs b/aztec_macros/src/transforms/mod.rs index 144ffc3efc3..5a454c75148 100644 --- a/aztec_macros/src/transforms/mod.rs +++ b/aztec_macros/src/transforms/mod.rs @@ -1,4 +1,5 @@ pub mod compute_note_hash_and_nullifier; pub mod events; pub mod functions; +pub mod note_interface; pub mod storage; diff --git a/aztec_macros/src/transforms/note_interface.rs b/aztec_macros/src/transforms/note_interface.rs new file mode 100644 index 00000000000..01d0272088b --- /dev/null +++ b/aztec_macros/src/transforms/note_interface.rs @@ -0,0 +1,583 @@ +use noirc_errors::Span; +use noirc_frontend::{ + parse_program, parser::SortedModule, ItemVisibility, NoirFunction, NoirStruct, PathKind, + TraitImplItem, TypeImpl, UnresolvedTypeData, UnresolvedTypeExpression, +}; +use regex::Regex; + +use crate::{ + chained_dep, + utils::{ + ast_utils::{ + check_trait_method_implemented, ident, ident_path, is_custom_attribute, make_type, + }, + errors::AztecMacroError, + }, +}; + +// Automatic implementation of most of the methods in the NoteInterface trait, guiding the user with meaningful error messages in case some +// methods must be implemented manually. +pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), AztecMacroError> { + // Find structs annotated with #[aztec(note)] + let annotated_note_structs = module + .types + .iter_mut() + .filter(|typ| typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(note)"))); + + let mut note_properties_structs = vec![]; + + for note_struct in annotated_note_structs { + // Look for the NoteInterface trait implementation for the note + let trait_impl = module + .trait_impls + .iter_mut() + .find(|trait_impl| { + if let UnresolvedTypeData::Named(struct_path, _, _) = &trait_impl.object_type.typ { + struct_path.last_segment() == note_struct.name + && trait_impl.trait_name.last_segment().0.contents == "NoteInterface" + } else { + false + } + }) + .ok_or(AztecMacroError::CouldNotImplementNoteInterface { + span: Some(note_struct.name.span()), + secondary_message: Some(format!( + "Could not find NoteInterface trait implementation for note: {}", + note_struct.name.0.contents + )), + })?; + let note_interface_impl_span: Option = trait_impl.object_type.span; + // Look for the note struct implementation, generate a default one if it doesn't exist (in order to append methods to it) + let existing_impl = module.impls.iter_mut().find(|r#impl| match &r#impl.object_type.typ { + UnresolvedTypeData::Named(path, _, _) => path.last_segment().eq(¬e_struct.name), + _ => false, + }); + let note_impl = if let Some(note_impl) = existing_impl { + note_impl + } else { + let default_impl = TypeImpl { + object_type: trait_impl.object_type.clone(), + type_span: note_struct.name.span(), + generics: vec![], + methods: vec![], + }; + module.impls.push(default_impl.clone()); + module.impls.last_mut().unwrap() + }; + // Identify the note type (struct name), its fields and its serialized length (generic param of NoteInterface trait impl) + let note_type = note_struct.name.0.contents.to_string(); + let mut note_fields = vec![]; + let note_serialized_len = match &trait_impl.trait_generics[0].typ { + UnresolvedTypeData::Named(path, _, _) => Ok(path.last_segment().0.contents.to_string()), + UnresolvedTypeData::Expression(UnresolvedTypeExpression::Constant(val, _)) => { + Ok(val.to_string()) + } + _ => Err(AztecMacroError::CouldNotImplementNoteInterface { + span: trait_impl.object_type.span, + secondary_message: Some(format!( + "Cannot find note serialization length for: {}", + note_type + )), + }), + }?; + + // Automatically inject the header field if it's not present + let (header_field_name, _) = if let Some(existing_header) = + note_struct.fields.iter().find(|(_, field_type)| match &field_type.typ { + UnresolvedTypeData::Named(path, _, _) => { + path.last_segment().0.contents == "NoteHeader" + } + _ => false, + }) { + existing_header.clone() + } else { + let generated_header = ( + ident("header"), + make_type(UnresolvedTypeData::Named( + chained_dep!("aztec", "note", "note_header", "NoteHeader"), + vec![], + false, + )), + ); + note_struct.fields.push(generated_header.clone()); + generated_header + }; + + for (field_ident, field_type) in note_struct.fields.iter() { + note_fields.push(( + field_ident.0.contents.to_string(), + field_type.typ.to_string().replace("plain::", ""), + )); + } + + if !check_trait_method_implemented(trait_impl, "serialize_content") + && !check_trait_method_implemented(trait_impl, "deserialize_content") + && !note_impl.methods.iter().any(|(func, _)| func.def.name.0.contents == "properties") + { + let note_serialize_content_fn = generate_note_serialize_content( + ¬e_type, + ¬e_fields, + ¬e_serialized_len, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(note_serialize_content_fn)); + + let note_deserialize_content_fn = generate_note_deserialize_content( + ¬e_type, + ¬e_fields, + ¬e_serialized_len, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(note_deserialize_content_fn)); + + let note_properties_struct = generate_note_properties_struct( + ¬e_type, + ¬e_fields, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + note_properties_structs.push(note_properties_struct); + let note_properties_fn = generate_note_properties_fn( + ¬e_type, + ¬e_fields, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + note_impl.methods.push((note_properties_fn, note_impl.type_span)); + } + + if !check_trait_method_implemented(trait_impl, "get_header") { + let get_header_fn = generate_note_get_header( + ¬e_type, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + } + if !check_trait_method_implemented(trait_impl, "set_header") { + let set_header_fn = generate_note_set_header( + ¬e_type, + &header_field_name.0.contents, + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(set_header_fn)); + } + + if !check_trait_method_implemented(trait_impl, "get_note_type_id") { + let get_note_type_id_fn = + generate_note_get_type_id(¬e_type, note_interface_impl_span)?; + trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); + } + + if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { + let get_header_fn = + generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + } + } + + module.types.extend(note_properties_structs); + Ok(()) +} + +fn generate_note_get_header( + note_type: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn get_header(note: {}) -> dep::aztec::note::note_header::NoteHeader {{ + note.{} + }} + ", + note_type, note_header_field_name + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn get_header). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +fn generate_note_set_header( + note_type: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn set_header(self: &mut {}, header: dep::aztec::note::note_header::NoteHeader) {{ + self.{} = header; + }} + ", + note_type, note_header_field_name + ); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn set_header). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate the note type id getter method. The id itself its calculated as the concatenation +// of the conversion of the characters in the note's struct name to unsigned integers. +fn generate_note_get_type_id( + note_type: &str, + impl_span: Option, +) -> Result { + // TODO(#4519) Improve automatic note id generation and assignment + let note_id = + note_type.chars().map(|c| (c as u32).to_string()).collect::>().join(""); + let function_source = format!( + " + fn get_note_type_id() -> Field {{ + {} + }} + ", + note_id + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn get_note_type_id). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate a struct that represents the note's serialization metadata, as +// +// NoteTypeFields { +// field1: PropertySelector { index: 0, offset: 0, length: 32 }, +// field2: PropertySelector { index: 1, offset: 0, length: 32 }, +// ... +// } +// +// It assumes each field occupies an entire field and its serialized in definition order +fn generate_note_properties_struct( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let struct_source = + generate_note_properties_struct_source(note_type, note_fields, note_header_field_name); + + let (struct_ast, errors) = parse_program(&struct_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (struct {}Properties). This is either a bug in the compiler or the Noir macro code", note_type)), + span: impl_span + }); + } + + let mut struct_ast = struct_ast.into_sorted(); + Ok(struct_ast.types.remove(0)) +} + +// Generate the deserialize_content method as +// +// fn deserialize_content(serialized_note: [Field; NOTE_SERILIZED_LEN]) -> Self { +// NoteType { +// note_field1: serialized_note[0] as Field, +// note_field2: NoteFieldType2::from_field(serialized_note[1])... +// } +// } +// It assumes every note field is stored in an individual serialized field, +// and can be converted to the original type via the from_field() trait (structs) or cast as Field (integers) +fn generate_note_deserialize_content( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = generate_note_deserialize_content_source( + note_type, + note_fields, + note_serialize_len, + note_header_field_name, + ); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn deserialize_content). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Generate the serialize_content method as +// +// fn serialize_content(self: {}) -> [Field; NOTE_SERIALIZED_LEN] { +// [self.note_field1 as Field, self.note_field2.to_field()...] +// } +// +// It assumes every struct field can be converted either via the to_field() trait (structs) or cast as Field (integers) +fn generate_note_serialize_content( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = generate_note_serialize_content_source( + note_type, + note_fields, + note_serialize_len, + note_header_field_name, + ); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn serialize_content). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate a function in the Note's impl that returns the note's fields metadata +fn generate_note_properties_fn( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, + impl_span: Option, +) -> Result { + let function_source = + generate_note_properties_fn_source(note_type, note_fields, note_header_field_name); + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn properties). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Automatically generate the method to compute the note's content hash as: +// fn compute_note_content_hash(self: NoteType) -> Field { +// // TODO(#1205) Should use a non-zero generator index. +// dep::aztec::hash::pedersen_hash(self.serialize_content(), 0) +// } +// +fn generate_compute_note_content_hash( + note_type: &String, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn compute_note_content_hash(self: {}) -> Field {{ + // TODO(#1205) Should use a non-zero generator index. + dep::aztec::hash::pedersen_hash(self.serialize_content(), 0) + }} + ", + note_type + ); + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn compute_note_content_hash). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + +// Source code generator functions. These utility methods produce Noir code as strings, that are then parsed and added to the AST. + +fn generate_note_properties_struct_source( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, +) -> String { + let note_property_selectors = note_fields + .iter() + .filter_map(|(field_name, _)| { + if field_name != note_header_field_name { + Some(format!( + "{}: dep::aztec::note::note_getter_options::PropertySelector", + field_name + )) + } else { + None + } + }) + .collect::>() + .join(",\n"); + format!( + " + struct {}Properties {{ + {} + }}", + note_type, note_property_selectors + ) + .to_string() +} + +fn generate_note_properties_fn_source( + note_type: &str, + note_fields: &[(String, String)], + note_header_field_name: &String, +) -> String { + let note_property_selectors = note_fields + .iter() + .enumerate() + .filter_map(|(index, (field_name, _))| { + if field_name != note_header_field_name { + Some(format!( + "{}: dep::aztec::note::note_getter_options::PropertySelector {{ index: {}, offset: 0, length: 32 }}", + field_name, + index + )) + } else { + None + } + }) + .collect::>() + .join(", "); + format!( + " + pub fn properties() -> {}Properties {{ + {}Properties {{ + {} + }} + }}", + note_type, note_type, note_property_selectors + ) + .to_string() +} + +fn generate_note_serialize_content_source( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, +) -> String { + let note_fields = note_fields + .iter() + .filter_map(|(field_name, field_type)| { + if field_name != note_header_field_name { + if field_type == "Field" { + Some(format!("self.{}", field_name)) + } else { + Some(format!("self.{}.to_field()", field_name)) + } + } else { + None + } + }) + .collect::>() + .join(", "); + format!( + " + fn serialize_content(self: {}) -> [Field; {}] {{ + [{}] + }}", + note_type, note_serialize_len, note_fields + ) + .to_string() +} + +fn generate_note_deserialize_content_source( + note_type: &str, + note_fields: &[(String, String)], + note_serialize_len: &String, + note_header_field_name: &String, +) -> String { + let note_fields = note_fields + .iter() + .enumerate() + .map(|(index, (field_name, field_type))| { + if field_name != note_header_field_name { + // TODO: Simplify this when https://github.com/noir-lang/noir/issues/4463 is fixed + if field_type.eq("Field") + || Regex::new(r"u[0-9]+").unwrap().is_match(field_type) + || field_type.eq("bool") + { + format!("{}: serialized_note[{}] as {},", field_name, index, field_type) + } else { + format!( + "{}: {}::from_field(serialized_note[{}]),", + field_name, field_type, index + ) + } + } else { + format!( + "{}: dep::aztec::note::note_header::NoteHeader::empty()", + note_header_field_name + ) + } + }) + .collect::>() + .join("\n"); + format!( + " + fn deserialize_content(serialized_note: [Field; {}]) -> Self {{ + {} {{ + {} + }} + }}", + note_serialize_len, note_type, note_fields + ) + .to_string() +} diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index 71c6a93f388..bdcbad646c2 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -2,8 +2,9 @@ use noirc_errors::{Span, Spanned}; use noirc_frontend::{ token::SecondaryAttribute, BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, - LetStatement, MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, - Statement, StatementKind, UnaryOp, UnresolvedType, UnresolvedTypeData, + LetStatement, MemberAccessExpression, MethodCallExpression, NoirTraitImpl, Path, Pattern, + PrefixExpression, Statement, StatementKind, TraitImplItem, UnaryOp, UnresolvedType, + UnresolvedTypeData, }; // @@ -173,6 +174,13 @@ pub fn index_array_variable(array: Expression, index: &str) -> Expression { }))) } +pub fn check_trait_method_implemented(trait_impl: &NoirTraitImpl, method_name: &str) -> bool { + trait_impl.items.iter().any(|item| match item { + TraitImplItem::Function(func) => func.def.name.0.contents == method_name, + _ => false, + }) +} + /// Checks if an attribute is a custom attribute with a specific name pub fn is_custom_attribute(attr: &SecondaryAttribute, attribute_name: &str) -> bool { if let SecondaryAttribute::Custom(custom_attr) = attr { diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 199473baec6..48186555eff 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -10,7 +10,7 @@ pub enum AztecMacroError { UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, - CouldNotImplementNoteSerialization { span: Option, typ: UnresolvedTypeData }, + CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, EventError { span: Span, message: String }, UnsupportedAttributes { span: Span, secondary_message: Option }, } @@ -43,9 +43,9 @@ impl From for MacroError { secondary_message, span: None, }, - AztecMacroError::CouldNotImplementNoteSerialization { span, typ } => MacroError { - primary_message: format!("Could not implement serialization methods for note `{typ:?}`, please provide a serialize_content and deserialize_content methods"), - secondary_message: None, + AztecMacroError::CouldNotImplementNoteInterface { span, secondary_message } => MacroError { + primary_message: "Could not implement automatic methods for note, please provide an implementation of the NoteInterface trait".to_string(), + secondary_message, span, }, AztecMacroError::EventError { span, message } => MacroError { @@ -53,7 +53,7 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, -AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { + AztecMacroError::UnsupportedAttributes { span, secondary_message } => MacroError { primary_message: "Unsupported attributes in contract function".to_string(), secondary_message, span: Some(span), diff --git a/compiler/noirc_driver/src/contract.rs b/compiler/noirc_driver/src/contract.rs index 66e8dc0e730..9a0e25a321b 100644 --- a/compiler/noirc_driver/src/contract.rs +++ b/compiler/noirc_driver/src/contract.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::Program; use fm::FileId; use noirc_abi::{Abi, ContractEvent}; use noirc_errors::debug_info::DebugInfo; @@ -45,10 +45,10 @@ pub struct ContractFunction { pub abi: Abi, #[serde( - serialize_with = "Circuit::serialize_circuit_base64", - deserialize_with = "Circuit::deserialize_circuit_base64" + serialize_with = "Program::serialize_program_base64", + deserialize_with = "Program::deserialize_program_base64" )] - pub bytecode: Circuit, + pub bytecode: Program, pub debug: DebugInfo, } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index c9494a64b41..bc3062e5807 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -3,7 +3,7 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] -use acvm::acir::circuit::ExpressionWidth; +use acvm::acir::circuit::{ExpressionWidth, Program}; use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; @@ -298,7 +298,7 @@ pub fn compile_main( if options.print_acir { println!("Compiled ACIR for main (unoptimized):"); - println!("{}", compiled_program.circuit); + println!("{}", compiled_program.program); } Ok((compiled_program, warnings)) @@ -414,7 +414,7 @@ fn compile_contract_inner( name, custom_attributes, abi: function.abi, - bytecode: function.circuit, + bytecode: function.program, debug: function.debug, is_unconstrained: modifiers.is_unconstrained, }); @@ -487,7 +487,8 @@ pub fn compile_no_check( Ok(CompiledProgram { hash, - circuit, + // TODO(https://github.com/noir-lang/noir/issues/4428) + program: Program { functions: vec![circuit] }, debug, abi, file_map, diff --git a/compiler/noirc_driver/src/program.rs b/compiler/noirc_driver/src/program.rs index 8d509d3bf68..6f527297dcb 100644 --- a/compiler/noirc_driver/src/program.rs +++ b/compiler/noirc_driver/src/program.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::Program; use fm::FileId; use noirc_errors::debug_info::DebugInfo; @@ -19,10 +19,10 @@ pub struct CompiledProgram { pub hash: u64, #[serde( - serialize_with = "Circuit::serialize_circuit_base64", - deserialize_with = "Circuit::deserialize_circuit_base64" + serialize_with = "Program::serialize_program_base64", + deserialize_with = "Program::deserialize_program_base64" )] - pub circuit: Circuit, + pub program: Program, pub abi: noirc_abi::Abi, pub debug: DebugInfo, pub file_map: BTreeMap, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index a9eea74ddd9..94e81d02053 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -1346,18 +1346,23 @@ impl<'block> BrilligBlock<'block> { BrilligBinaryOp::LessThan, ); - self.brillig_context.codegen_branch(result_is_negative.address, |ctx, is_negative| { - if is_negative { - // Two's complement of num - let zero = ctx.make_constant_instruction(0_usize.into(), num.bit_size); - ctx.binary_instruction(zero, num, absolute_value, BrilligBinaryOp::Sub); - ctx.deallocate_single_addr(zero); - } else { - // Simply move the original num - ctx.mov_instruction(absolute_value.address, num.address); - } - }); + // Two's complement of num + let zero = self.brillig_context.make_constant_instruction(0_usize.into(), num.bit_size); + let twos_complement = + SingleAddrVariable::new(self.brillig_context.allocate_register(), num.bit_size); + self.brillig_context.binary_instruction(zero, num, twos_complement, BrilligBinaryOp::Sub); + + // absolute_value = result_is_negative ? twos_complement : num + self.brillig_context.conditional_mov_instruction( + absolute_value.address, + result_is_negative.address, + twos_complement.address, + num.address, + ); + + self.brillig_context.deallocate_single_addr(zero); self.brillig_context.deallocate_single_addr(max_positive); + self.brillig_context.deallocate_single_addr(twos_complement); } fn convert_signed_division( diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index fa99e968a31..5611905697c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -121,6 +121,24 @@ impl DebugShow { debug_println!(self.enable_debug_trace, " MOV {}, {}", destination, source); } + /// Emits a conditional `mov` instruction. + pub(crate) fn conditional_mov_instruction( + &self, + destination: MemoryAddress, + condition: MemoryAddress, + source_a: MemoryAddress, + source_b: MemoryAddress, + ) { + debug_println!( + self.enable_debug_trace, + " CMOV {} = {}? {} : {}", + destination, + condition, + source_a, + source_b + ); + } + /// Emits a `cast` instruction. pub(crate) fn cast_instruction( &self, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 5c2a6bfacca..99fb4c89f64 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -359,6 +359,25 @@ impl BrilligContext { self.push_opcode(BrilligOpcode::Mov { destination, source }); } + /// Emits a conditional `mov` instruction. + /// + /// Copies the value at `source` into `destination` + pub(crate) fn conditional_mov_instruction( + &mut self, + destination: MemoryAddress, + condition: MemoryAddress, + source_a: MemoryAddress, + source_b: MemoryAddress, + ) { + self.debug_show.conditional_mov_instruction(destination, condition, source_a, source_b); + self.push_opcode(BrilligOpcode::ConditionalMov { + destination, + source_a, + source_b, + condition, + }); + } + /// Cast truncates the value to the given bit size and converts the type of the value in memory to that bit size. pub(crate) fn cast_instruction( &mut self, diff --git a/compiler/noirc_evaluator/src/errors.rs b/compiler/noirc_evaluator/src/errors.rs index 06259e06248..b3e838e708e 100644 --- a/compiler/noirc_evaluator/src/errors.rs +++ b/compiler/noirc_evaluator/src/errors.rs @@ -140,8 +140,8 @@ impl RuntimeError { | RuntimeError::UnsupportedIntegerSize { call_stack, .. } | RuntimeError::NestedSlice { call_stack, .. } | RuntimeError::BigIntModulus { call_stack, .. } - | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } => call_stack, - RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } => call_stack, + | RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack } + | RuntimeError::UnconstrainedOracleReturnToConstrained { call_stack } => call_stack, } } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index 5adb9eb5b7e..fcb20c740c7 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -577,7 +577,12 @@ impl<'a> ModCollector<'a> { let mut ast = ast.into_sorted(); for macro_processor in macro_processors { - match macro_processor.process_untyped_ast(ast.clone(), &crate_id, context) { + match macro_processor.process_untyped_ast( + ast.clone(), + &crate_id, + child_file_id, + context, + ) { Ok(processed_ast) => { ast = processed_ast; } diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 1326ffca9f7..157227f763e 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -91,7 +91,8 @@ impl CrateDefMap { let mut ast = ast.into_sorted(); for macro_processor in macro_processors { - match macro_processor.process_untyped_ast(ast.clone(), &crate_id, context) { + match macro_processor.process_untyped_ast(ast.clone(), &crate_id, root_file_id, context) + { Ok(processed_ast) => { ast = processed_ast; } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 4e2a3a8b4a2..642cebc83b0 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -235,9 +235,9 @@ impl From for Diagnostic { | TypeCheckError::OverflowingAssignment { span, .. } | TypeCheckError::FieldModulo { span } | TypeCheckError::ConstrainedReferenceToUnconstrained { span } - | TypeCheckError::NonConstantSliceLength { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } - | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } => { + | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } + | TypeCheckError::NonConstantSliceLength { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 6ee0460784f..10476b6caef 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -217,7 +217,7 @@ impl<'interner> TypeChecker<'interner> { }); return Type::Error; } - } + }; return_type } diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 1871b594ae7..6ce6f4325e4 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -72,6 +72,7 @@ pub mod macros_api { &self, ast: SortedModule, crate_id: &CrateId, + file_id: FileId, context: &HirContext, ) -> Result; diff --git a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml index 1081b5ab8e2..7c3fb8766bf 100644 --- a/test_programs/compile_failure/unconstrained_oracle/Nargo.toml +++ b/test_programs/compile_failure/unconstrained_oracle/Nargo.toml @@ -2,4 +2,6 @@ name = "unconstrained_oracle" type = "bin" authors = [""] +compiler_version = ">=0.25.0" + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_ref/Nargo.toml b/test_programs/compile_failure/unconstrained_ref/Nargo.toml index b120a3b5532..c03aaea5fca 100644 --- a/test_programs/compile_failure/unconstrained_ref/Nargo.toml +++ b/test_programs/compile_failure/unconstrained_ref/Nargo.toml @@ -2,4 +2,6 @@ name = "unconstrained_ref" type = "bin" authors = [""] +compiler_version = ">=0.25.0" + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/unconstrained_ref/src/main.nr b/test_programs/compile_failure/unconstrained_ref/src/main.nr index bb4b2090ddb..1491badaa49 100644 --- a/test_programs/compile_failure/unconstrained_ref/src/main.nr +++ b/test_programs/compile_failure/unconstrained_ref/src/main.nr @@ -5,4 +5,4 @@ unconstrained fn uncon_ref() -> &mut Field { fn main() { let e = uncon_ref(); -} \ No newline at end of file +} diff --git a/test_programs/execution_success/witness_compression/Nargo.toml b/test_programs/execution_success/witness_compression/Nargo.toml new file mode 100644 index 00000000000..7d6ba0c1938 --- /dev/null +++ b/test_programs/execution_success/witness_compression/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "witness_compression" +type = "bin" +authors = [""] +compiler_version = ">=0.24.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/witness_compression/Prover.toml b/test_programs/execution_success/witness_compression/Prover.toml new file mode 100644 index 00000000000..8c12ebba6cf --- /dev/null +++ b/test_programs/execution_success/witness_compression/Prover.toml @@ -0,0 +1,2 @@ +x = "1" +y = "2" diff --git a/test_programs/execution_success/witness_compression/src/main.nr b/test_programs/execution_success/witness_compression/src/main.nr new file mode 100644 index 00000000000..3027d35b13a --- /dev/null +++ b/test_programs/execution_success/witness_compression/src/main.nr @@ -0,0 +1,7 @@ +// This test should be used to regenerate the serialized witness used in the `acvm_js` integration tests. +// The `acvm_js` test file containing the serialized witness should be also called `witness_compression`. +// After recompiling Noir, you can manually print the witness byte array to be written to file after execution. +fn main(x: Field, y: pub Field) -> pub Field { + assert(x != y); + x + y +} diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs index 255b6131fd6..b76d0eccc29 100644 --- a/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -1,6 +1,6 @@ use std::io::{self, Write}; -use acir::circuit::Circuit; +use acir::circuit::Program; use acir::native_types::WitnessMap; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -66,10 +66,10 @@ pub(crate) fn execute_program_from_witness( foreign_call_resolver_url: Option<&str>, ) -> Result { let blackbox_solver = Bn254BlackBoxSolver::new(); - let circuit: Circuit = Circuit::deserialize_circuit(bytecode) + let program: Program = Program::deserialize_program(bytecode) .map_err(|_| CliError::CircuitDeserializationError())?; execute_circuit( - &circuit, + &program.functions[0], inputs_map.clone(), &blackbox_solver, &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), diff --git a/tooling/acvm_cli/src/errors.rs b/tooling/acvm_cli/src/errors.rs index 035388d05f7..923046410ea 100644 --- a/tooling/acvm_cli/src/errors.rs +++ b/tooling/acvm_cli/src/errors.rs @@ -32,7 +32,7 @@ pub(crate) enum CliError { FilesystemError(#[from] FilesystemError), /// Error related to circuit deserialization - #[error("Error: failed to deserialize circuit")] + #[error("Error: failed to deserialize circuit in ACVM CLI")] CircuitDeserializationError(), /// Error related to circuit execution diff --git a/tooling/backend_interface/src/proof_system.rs b/tooling/backend_interface/src/proof_system.rs index 485381006df..105ae337793 100644 --- a/tooling/backend_interface/src/proof_system.rs +++ b/tooling/backend_interface/src/proof_system.rs @@ -3,8 +3,8 @@ use std::io::Write; use std::path::Path; use acvm::acir::{ - circuit::{Circuit, ExpressionWidth}, - native_types::WitnessMap, + circuit::{ExpressionWidth, Program}, + native_types::{WitnessMap, WitnessStack}, }; use acvm::FieldElement; use tempfile::tempdir; @@ -17,7 +17,7 @@ use crate::cli::{ use crate::{Backend, BackendError}; impl Backend { - pub fn get_exact_circuit_size(&self, circuit: &Circuit) -> Result { + pub fn get_exact_circuit_size(&self, program: &Program) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -26,8 +26,8 @@ impl Backend { // Create a temporary file for the circuit let circuit_path = temp_directory.join("circuit").with_extension("bytecode"); - let serialized_circuit = Circuit::serialize_circuit(circuit); - write_to_file(&serialized_circuit, &circuit_path); + let serialized_program = Program::serialize_program(program); + write_to_file(&serialized_program, &circuit_path); GatesCommand { crs_path: self.crs_directory(), bytecode_path: circuit_path } .run(binary_path) @@ -55,8 +55,8 @@ impl Backend { #[tracing::instrument(level = "trace", skip_all)] pub fn prove( &self, - circuit: &Circuit, - witness_values: WitnessMap, + program: &Program, + witness_values: WitnessStack, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -72,9 +72,9 @@ impl Backend { // Create a temporary file for the circuit // - let bytecode_path = temp_directory.join("circuit").with_extension("bytecode"); - let serialized_circuit = Circuit::serialize_circuit(circuit); - write_to_file(&serialized_circuit, &bytecode_path); + let bytecode_path = temp_directory.join("program").with_extension("bytecode"); + let serialized_program = Program::serialize_program(program); + write_to_file(&serialized_program, &bytecode_path); // Create proof and store it in the specified path let proof_with_public_inputs = @@ -82,7 +82,8 @@ impl Backend { .run(binary_path)?; let proof = bb_abstraction_leaks::remove_public_inputs( - circuit.public_inputs().0.len(), + // TODO(https://github.com/noir-lang/noir/issues/4428) + program.functions[0].public_inputs().0.len(), &proof_with_public_inputs, ); Ok(proof) @@ -93,7 +94,7 @@ impl Backend { &self, proof: &[u8], public_inputs: WitnessMap, - circuit: &Circuit, + program: &Program, ) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -108,9 +109,9 @@ impl Backend { write_to_file(&proof_with_public_inputs, &proof_path); // Create a temporary file for the circuit - let bytecode_path = temp_directory.join("circuit").with_extension("bytecode"); - let serialized_circuit = Circuit::serialize_circuit(circuit); - write_to_file(&serialized_circuit, &bytecode_path); + let bytecode_path = temp_directory.join("program").with_extension("bytecode"); + let serialized_program = Program::serialize_program(program); + write_to_file(&serialized_program, &bytecode_path); // Create the verification key and write it to the specified path let vk_path = temp_directory.join("vk"); @@ -128,7 +129,7 @@ impl Backend { pub fn get_intermediate_proof_artifacts( &self, - circuit: &Circuit, + program: &Program, proof: &[u8], public_inputs: WitnessMap, ) -> Result<(Vec, FieldElement, Vec), BackendError> { @@ -140,9 +141,9 @@ impl Backend { // Create a temporary file for the circuit // - let bytecode_path = temp_directory.join("circuit").with_extension("bytecode"); - let serialized_circuit = Circuit::serialize_circuit(circuit); - write_to_file(&serialized_circuit, &bytecode_path); + let bytecode_path = temp_directory.join("program").with_extension("bytecode"); + let serialized_program = Program::serialize_program(program); + write_to_file(&serialized_program, &bytecode_path); // Create the verification key and write it to the specified path let vk_path = temp_directory.join("vk"); diff --git a/tooling/backend_interface/src/smart_contract.rs b/tooling/backend_interface/src/smart_contract.rs index 5af75e48389..f6beeeb09d9 100644 --- a/tooling/backend_interface/src/smart_contract.rs +++ b/tooling/backend_interface/src/smart_contract.rs @@ -3,11 +3,11 @@ use crate::{ cli::{ContractCommand, WriteVkCommand}, Backend, BackendError, }; -use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::Program; use tempfile::tempdir; impl Backend { - pub fn eth_contract(&self, circuit: &Circuit) -> Result { + pub fn eth_contract(&self, program: &Program) -> Result { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -15,9 +15,9 @@ impl Backend { let temp_directory_path = temp_directory.path().to_path_buf(); // Create a temporary file for the circuit - let bytecode_path = temp_directory_path.join("circuit").with_extension("bytecode"); - let serialized_circuit = Circuit::serialize_circuit(circuit); - write_to_file(&serialized_circuit, &bytecode_path); + let bytecode_path = temp_directory_path.join("program").with_extension("bytecode"); + let serialized_program = Program::serialize_program(program); + write_to_file(&serialized_program, &bytecode_path); // Create the verification key and write it to the specified path let vk_path = temp_directory_path.join("vk"); @@ -38,7 +38,7 @@ mod tests { use std::collections::BTreeSet; use acvm::acir::{ - circuit::{Circuit, ExpressionWidth, Opcode, PublicInputs}, + circuit::{Circuit, ExpressionWidth, Opcode, Program, PublicInputs}, native_types::{Expression, Witness}, }; @@ -59,8 +59,9 @@ mod tests { assert_messages: Default::default(), recursive: false, }; + let program = Program { functions: vec![circuit] }; - let contract = get_mock_backend()?.eth_contract(&circuit)?; + let contract = get_mock_backend()?.eth_contract(&program)?; assert!(contract.contains("contract VerifierContract")); diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index 2eae9e1f07e..362d2132952 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.29.0"; +const VERSION: &str = "0.30.1"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/debugger/src/dap.rs b/tooling/debugger/src/dap.rs index 7c722ed0a61..ea3204ebbbc 100644 --- a/tooling/debugger/src/dap.rs +++ b/tooling/debugger/src/dap.rs @@ -607,8 +607,13 @@ pub fn run_session( file_map: program.file_map, warnings: program.warnings, }; - let mut session = - DapSession::new(server, solver, &program.circuit, &debug_artifact, initial_witness); + let mut session = DapSession::new( + server, + solver, + &program.program.functions[0], + &debug_artifact, + initial_witness, + ); session.run_loop() } diff --git a/tooling/nargo/src/artifacts/contract.rs b/tooling/nargo/src/artifacts/contract.rs index 020ce49662f..c0316a6d1a2 100644 --- a/tooling/nargo/src/artifacts/contract.rs +++ b/tooling/nargo/src/artifacts/contract.rs @@ -1,4 +1,4 @@ -use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::Program; use noirc_abi::{Abi, ContractEvent}; use noirc_driver::{CompiledContract, ContractFunction}; use serde::{Deserialize, Serialize}; @@ -50,10 +50,10 @@ pub struct ContractFunctionArtifact { pub abi: Abi, #[serde( - serialize_with = "Circuit::serialize_circuit_base64", - deserialize_with = "Circuit::deserialize_circuit_base64" + serialize_with = "Program::serialize_program_base64", + deserialize_with = "Program::deserialize_program_base64" )] - pub bytecode: Circuit, + pub bytecode: Program, #[serde( serialize_with = "DebugInfo::serialize_compressed_base64_json", diff --git a/tooling/nargo/src/artifacts/program.rs b/tooling/nargo/src/artifacts/program.rs index d8dc42ec214..046db1cd8fa 100644 --- a/tooling/nargo/src/artifacts/program.rs +++ b/tooling/nargo/src/artifacts/program.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::Program; use fm::FileId; use noirc_abi::Abi; use noirc_driver::CompiledProgram; @@ -21,10 +21,10 @@ pub struct ProgramArtifact { pub abi: Abi, #[serde( - serialize_with = "Circuit::serialize_circuit_base64", - deserialize_with = "Circuit::deserialize_circuit_base64" + serialize_with = "Program::serialize_program_base64", + deserialize_with = "Program::deserialize_program_base64" )] - pub bytecode: Circuit, + pub bytecode: Program, #[serde( serialize_with = "DebugInfo::serialize_compressed_base64_json", @@ -37,14 +37,14 @@ pub struct ProgramArtifact { } impl From for ProgramArtifact { - fn from(program: CompiledProgram) -> Self { + fn from(compiled_program: CompiledProgram) -> Self { ProgramArtifact { - hash: program.hash, - abi: program.abi, - noir_version: program.noir_version, - bytecode: program.circuit, - debug_symbols: program.debug, - file_map: program.file_map, + hash: compiled_program.hash, + abi: compiled_program.abi, + noir_version: compiled_program.noir_version, + bytecode: compiled_program.program, + debug_symbols: compiled_program.debug, + file_map: compiled_program.file_map, } } } @@ -55,7 +55,7 @@ impl From for CompiledProgram { hash: program.hash, abi: program.abi, noir_version: program.noir_version, - circuit: program.bytecode, + program: program.bytecode, debug: program.debug_symbols, file_map: program.file_map, warnings: vec![], diff --git a/tooling/nargo/src/ops/optimize.rs b/tooling/nargo/src/ops/optimize.rs index 2d0c4c43d25..cfaaf27ea98 100644 --- a/tooling/nargo/src/ops/optimize.rs +++ b/tooling/nargo/src/ops/optimize.rs @@ -1,17 +1,22 @@ use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; -pub fn optimize_program(mut program: CompiledProgram) -> CompiledProgram { - let (optimized_circuit, location_map) = acvm::compiler::optimize(program.circuit); - program.circuit = optimized_circuit; - program.debug.update_acir(location_map); - program +/// TODO(https://github.com/noir-lang/noir/issues/4428): Need to update how these passes are run to account for +/// multiple ACIR functions + +pub fn optimize_program(mut compiled_program: CompiledProgram) -> CompiledProgram { + let (optimized_circuit, location_map) = + acvm::compiler::optimize(std::mem::take(&mut compiled_program.program.functions[0])); + compiled_program.program.functions[0] = optimized_circuit; + compiled_program.debug.update_acir(location_map); + compiled_program } pub fn optimize_contract(contract: CompiledContract) -> CompiledContract { let functions = vecmap(contract.functions, |mut func| { - let (optimized_bytecode, location_map) = acvm::compiler::optimize(func.bytecode); - func.bytecode = optimized_bytecode; + let (optimized_bytecode, location_map) = + acvm::compiler::optimize(std::mem::take(&mut func.bytecode.functions[0])); + func.bytecode.functions[0] = optimized_bytecode; func.debug.update_acir(location_map); func }); diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 8ddcb5cf8d2..8cf2934da4d 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -28,18 +28,23 @@ pub fn run_test( foreign_call_resolver_url: Option<&str>, config: &CompileOptions, ) -> TestStatus { - let program = compile_no_check(context, config, test_function.get_id(), None, false); - match program { - Ok(program) => { + let compiled_program = compile_no_check(context, config, test_function.get_id(), None, false); + match compiled_program { + Ok(compiled_program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. let circuit_execution = execute_circuit( - &program.circuit, + // TODO(https://github.com/noir-lang/noir/issues/4428) + &compiled_program.program.functions[0], WitnessMap::new(), blackbox_solver, &mut DefaultForeignCallExecutor::new(show_output, foreign_call_resolver_url), ); - test_status_program_compile_pass(test_function, program.debug, circuit_execution) + test_status_program_compile_pass( + test_function, + compiled_program.debug, + circuit_execution, + ) } Err(err) => test_status_program_compile_fail(err, test_function), } diff --git a/tooling/nargo/src/ops/transform.rs b/tooling/nargo/src/ops/transform.rs index 9267ed7e045..274286a46e4 100644 --- a/tooling/nargo/src/ops/transform.rs +++ b/tooling/nargo/src/ops/transform.rs @@ -2,16 +2,21 @@ use acvm::acir::circuit::ExpressionWidth; use iter_extended::vecmap; use noirc_driver::{CompiledContract, CompiledProgram}; +/// TODO(https://github.com/noir-lang/noir/issues/4428): Need to update how these passes are run to account for +/// multiple ACIR functions + pub fn transform_program( - mut program: CompiledProgram, + mut compiled_program: CompiledProgram, expression_width: ExpressionWidth, ) -> CompiledProgram { - let (optimized_circuit, location_map) = - acvm::compiler::compile(program.circuit, expression_width); + let (optimized_circuit, location_map) = acvm::compiler::compile( + std::mem::take(&mut compiled_program.program.functions[0]), + expression_width, + ); - program.circuit = optimized_circuit; - program.debug.update_acir(location_map); - program + compiled_program.program.functions[0] = optimized_circuit; + compiled_program.debug.update_acir(location_map); + compiled_program } pub fn transform_contract( @@ -19,9 +24,11 @@ pub fn transform_contract( expression_width: ExpressionWidth, ) -> CompiledContract { let functions = vecmap(contract.functions, |mut func| { - let (optimized_bytecode, location_map) = - acvm::compiler::compile(func.bytecode, expression_width); - func.bytecode = optimized_bytecode; + let (optimized_bytecode, location_map) = acvm::compiler::compile( + std::mem::take(&mut func.bytecode.functions[0]), + expression_width, + ); + func.bytecode.functions[0] = optimized_bytecode; func.debug.update_acir(location_map); func }); diff --git a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs index f0fe2e0ea78..259e209b65a 100644 --- a/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs +++ b/tooling/nargo_cli/src/cli/codegen_verifier_cmd.rs @@ -64,7 +64,13 @@ pub(crate) fn run( let program = nargo::ops::transform_program(program, expression_width); - let smart_contract_string = backend.eth_contract(&program.circuit)?; + // TODO(https://github.com/noir-lang/noir/issues/4428): + // We do not expect to have a smart contract verifier for a foldable program with multiple circuits. + // However, in the future we can expect to possibly have non-inlined ACIR functions during compilation + // that will be inlined at a later step such as by the ACVM compiler or by the backend. + // Add appropriate handling here once the compiler enables multiple ACIR functions. + assert_eq!(program.program.functions.len(), 1); + let smart_contract_string = backend.eth_contract(&program.program)?; let contract_dir = workspace.contracts_directory_path(package); create_named_dir(&contract_dir, "contract"); diff --git a/tooling/nargo_cli/src/cli/compile_cmd.rs b/tooling/nargo_cli/src/cli/compile_cmd.rs index 9b7bf9cdb0c..54e8535f094 100644 --- a/tooling/nargo_cli/src/cli/compile_cmd.rs +++ b/tooling/nargo_cli/src/cli/compile_cmd.rs @@ -214,10 +214,10 @@ pub(super) fn save_program( circuit_dir: &Path, only_acir_opt: bool, ) { - let program_artifact = ProgramArtifact::from(program.clone()); if only_acir_opt { - only_acir(&program_artifact, circuit_dir); + only_acir(program.program, circuit_dir); } else { + let program_artifact = ProgramArtifact::from(program.clone()); save_program_to_file(&program_artifact, &package.name, circuit_dir); } } diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 2c4937b6f16..1f448105ee2 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -235,7 +235,7 @@ pub(crate) fn debug_program( noir_debugger::debug_circuit( &blackbox_solver, - &compiled_program.circuit, + &compiled_program.program.functions[0], debug_artifact, initial_witness, ) diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 1be2fbf61d9..022bf7b761e 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -136,8 +136,9 @@ pub(crate) fn execute_program( let initial_witness = compiled_program.abi.encode(inputs_map, None)?; + // TODO(https://github.com/noir-lang/noir/issues/4428) let solved_witness_err = nargo::ops::execute_circuit( - &compiled_program.circuit, + &compiled_program.program.functions[0], initial_witness, &blackbox_solver, &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), diff --git a/tooling/nargo_cli/src/cli/fs/program.rs b/tooling/nargo_cli/src/cli/fs/program.rs index 1fb57ae6685..77005e8d5af 100644 --- a/tooling/nargo_cli/src/cli/fs/program.rs +++ b/tooling/nargo_cli/src/cli/fs/program.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use acvm::acir::circuit::Circuit; +use acvm::acir::circuit::Program; use nargo::artifacts::{contract::ContractArtifact, program::ProgramArtifact}; use noirc_frontend::graph::CrateName; @@ -18,13 +18,10 @@ pub(crate) fn save_program_to_file>( } /// Writes the bytecode as acir.gz -pub(crate) fn only_acir>( - program_artifact: &ProgramArtifact, - circuit_dir: P, -) -> PathBuf { +pub(crate) fn only_acir>(program: Program, circuit_dir: P) -> PathBuf { create_named_dir(circuit_dir.as_ref(), "target"); let circuit_path = circuit_dir.as_ref().join("acir").with_extension("gz"); - let bytes = Circuit::serialize_circuit(&program_artifact.bytecode); + let bytes = Program::serialize_program(&program); write_to_file(&bytes, &circuit_path); circuit_path diff --git a/tooling/nargo_cli/src/cli/fs/witness.rs b/tooling/nargo_cli/src/cli/fs/witness.rs index 1a2cf88f4a1..52b5c385e5d 100644 --- a/tooling/nargo_cli/src/cli/fs/witness.rs +++ b/tooling/nargo_cli/src/cli/fs/witness.rs @@ -1,6 +1,6 @@ use std::path::{Path, PathBuf}; -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use nargo::constants::WITNESS_EXT; use super::{create_named_dir, write_to_file}; @@ -14,7 +14,9 @@ pub(crate) fn save_witness_to_dir>( create_named_dir(witness_dir.as_ref(), "witness"); let witness_path = witness_dir.as_ref().join(witness_name).with_extension(WITNESS_EXT); - let buf: Vec = witnesses.try_into()?; + // TODO(https://github.com/noir-lang/noir/issues/4428) + let witness_stack: WitnessStack = witnesses.into(); + let buf: Vec = witness_stack.try_into()?; write_to_file(buf.as_slice(), &witness_path); diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index 391e8061a07..49924622392 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -276,8 +276,9 @@ fn count_opcodes_and_gates_in_program( Ok(ProgramInfo { name: package.name.to_string(), expression_width, - acir_opcodes: compiled_program.circuit.opcodes.len(), - circuit_size: backend.get_exact_circuit_size(&compiled_program.circuit)?, + // TODO(https://github.com/noir-lang/noir/issues/4428) + acir_opcodes: compiled_program.program.functions[0].opcodes.len(), + circuit_size: backend.get_exact_circuit_size(&compiled_program.program)?, }) } @@ -292,7 +293,8 @@ fn count_opcodes_and_gates_in_contract( .map(|function| -> Result<_, BackendError> { Ok(FunctionInfo { name: function.name, - acir_opcodes: function.bytecode.opcodes.len(), + // TODO(https://github.com/noir-lang/noir/issues/4428) + acir_opcodes: function.bytecode.functions[0].opcodes.len(), circuit_size: backend.get_exact_circuit_size(&function.bytecode)?, }) }) diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index e413db0e5f3..272f2fa8e5d 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,3 +1,4 @@ +use acvm::acir::native_types::WitnessStack; use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::ops::{compile_program, report_errors}; @@ -138,11 +139,11 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.circuit, solved_witness)?; + let proof = backend.prove(&compiled_program.program, WitnessStack::from(solved_witness))?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.program)?; if !valid_proof { return Err(CliError::InvalidProof("".into())); diff --git a/tooling/nargo_cli/src/cli/verify_cmd.rs b/tooling/nargo_cli/src/cli/verify_cmd.rs index 3e23c9a3e9f..7202a179aae 100644 --- a/tooling/nargo_cli/src/cli/verify_cmd.rs +++ b/tooling/nargo_cli/src/cli/verify_cmd.rs @@ -102,7 +102,7 @@ fn verify_package( let proof = load_hex_data(&proof_path)?; - let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.circuit)?; + let valid_proof = backend.verify(&proof, public_inputs, &compiled_program.program)?; if valid_proof { Ok(()) diff --git a/tooling/nargo_cli/src/errors.rs b/tooling/nargo_cli/src/errors.rs index c2996f53420..40fb7886405 100644 --- a/tooling/nargo_cli/src/errors.rs +++ b/tooling/nargo_cli/src/errors.rs @@ -1,4 +1,4 @@ -use acvm::acir::native_types::WitnessMapError; +use acvm::acir::native_types::WitnessStackError; use hex::FromHexError; use nargo::{errors::CompileError, NargoError}; use nargo_toml::ManifestError; @@ -22,9 +22,9 @@ pub(crate) enum FilesystemError { #[error(transparent)] InputParserError(#[from] InputParserError), - /// WitnessMap serialization error + /// WitnessStack serialization error #[error(transparent)] - WitnessMapSerialization(#[from] WitnessMapError), + WitnessStackSerialization(#[from] WitnessStackError), #[error("Error: could not deserialize build program: {0}")] ProgramSerializationError(String), diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 6223f8d2969..92b8460d1db 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.29.0", + "@aztec/bb.js": "0.30.1", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index af03743eb2f..bfdf1005a93 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -45,7 +45,6 @@ export class BarretenbergBackend implements Backend { } const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); const api = await Barretenberg.new(this.options); - const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); const crs = await Crs.new(subgroupSize + 1); await api.commonInitSlabAllocator(subgroupSize); diff --git a/yarn.lock b/yarn.lock index ee120fbb18e..3932935167d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.29.0": - version: 0.29.0 - resolution: "@aztec/bb.js@npm:0.29.0" +"@aztec/bb.js@npm:0.30.1": + version: 0.30.1 + resolution: "@aztec/bb.js@npm:0.30.1" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: ae2bae0eed1a64f79f4694d0b02a51b87bb00ef1d9a331193dc9ace69a47819d93b923114e95e77b9b4d3de28c94bf5bdd1e839b0236979aaa0cfcda7a04fa97 + checksum: 52d0acaaf0966aa969b863adeb688df5c1abe7c8a0595bc2dca8603a8649f11eb39071aacbdc05b19f320f022b98691a41cb18a601ad84e6d82955ae6f885106 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.29.0 + "@aztec/bb.js": 0.30.1 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 04734976e92475b1ab94257e30bc3438c7358681 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 21 Mar 2024 15:18:51 +0000 Subject: [PATCH 105/416] fix(ssa): Use accurate type during SSA AsSlice simplficiation (#4610) # Description ## Problem\* Resolves #4609 ## Summary\* We were not accurately converting the type from an array to a slice during `AsSlice` SSA simplification. This led to the slice capacity tracker not accurately tracking the slice size. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/ir/instruction/call.rs | 9 ++++---- compiler/noirc_evaluator/src/ssa/ir/types.rs | 7 ++++++ .../array_to_slice/src/main.nr | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 8b800e0db54..5b268de239d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -85,10 +85,11 @@ pub(super) fn simplify_call( } } Intrinsic::AsSlice => { - let slice = dfg.get_array_constant(arguments[0]); - if let Some((slice, element_type)) = slice { - let slice_length = dfg.make_constant(slice.len().into(), Type::length_type()); - let new_slice = dfg.make_array(slice, element_type); + let array = dfg.get_array_constant(arguments[0]); + if let Some((array, array_type)) = array { + let slice_length = dfg.make_constant(array.len().into(), Type::length_type()); + let inner_element_types = array_type.element_types(); + let new_slice = dfg.make_array(array, Type::Slice(inner_element_types)); SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) } else { SimplifyResult::None diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index ea3f5393245..48036580d29 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -159,6 +159,13 @@ impl Type { Type::Reference(element) => element.contains_an_array(), } } + + pub(crate) fn element_types(self) -> Rc> { + match self { + Type::Array(element_types, _) | Type::Slice(element_types) => element_types, + other => panic!("element_types: Expected array or slice, found {other}"), + } + } } /// Composite Types are essentially flattened struct or tuple types. diff --git a/test_programs/execution_success/array_to_slice/src/main.nr b/test_programs/execution_success/array_to_slice/src/main.nr index b97f68fc280..0d0f9562d7b 100644 --- a/test_programs/execution_success/array_to_slice/src/main.nr +++ b/test_programs/execution_success/array_to_slice/src/main.nr @@ -7,6 +7,7 @@ fn as_slice_push(xs: [T; N]) -> [T] { slice } +// Expected that x == 0 and y == 1 fn main(x: Field, y: pub Field) { let xs: [Field; 0] = []; let ys: [Field; 1] = [1]; @@ -30,4 +31,26 @@ fn main(x: Field, y: pub Field) { assert(dynamic.as_slice()[2] == dynamic_expected[2]); assert(dynamic.as_slice()[3] == dynamic_expected[3]); assert(dynamic.as_slice().len() == 4); + + regression_4609_append_slices(x, y); + regression_4609_append_dynamic_slices(x, y); +} + +fn regression_4609_append_slices(x: Field, y: Field) { + let sl = [x, 1, 2, 3].as_slice(); + let sl2 = [y, 5, 6].as_slice(); + let sl3 = sl.append(sl2); + assert(sl3[0] == x); + assert(sl3[4] == y); +} + +fn regression_4609_append_dynamic_slices(x: Field, y: Field) { + let mut sl = [x, 1, 2, 3].as_slice(); + sl[x] = x + 10; + let mut sl2 = [y, 5, 6].as_slice(); + sl2[y] = y + 5; + let sl3 = sl.append(sl2); + assert(sl3[0] == 10); + assert(sl3[4] == y); + assert(sl3[5] == 6); } From ff95fd93451b2053360a16b7d3204ca251199296 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Thu, 21 Mar 2024 11:47:00 -0400 Subject: [PATCH 106/416] feat: add specific error for attempting `string[x] = ".."` (#4611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4267 ## Summary\* New error: ```bash error: Strings do not support indexed assignment ┌─ /Users/michaelklein/Coding/rust/noir/test_programs/compile_failure/mutable_str_index/src/main.nr:3:5 │ 3 │ example[0] = "?"; │ ------- │ Aborting due to 1 previous error ``` ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/hir/type_check/errors.rs | 5 ++++- compiler/noirc_frontend/src/hir/type_check/stmt.rs | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 642cebc83b0..6beb6929ce1 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -140,6 +140,8 @@ pub enum TypeCheckError { method_name: String, span: Span, }, + #[error("Strings do not support indexed assignment")] + StringIndexAssign { span: Span }, } impl TypeCheckError { @@ -237,7 +239,8 @@ impl From for Diagnostic { | TypeCheckError::ConstrainedReferenceToUnconstrained { span } | TypeCheckError::UnconstrainedReferenceToConstrained { span } | TypeCheckError::UnconstrainedSliceReturnToConstrained { span } - | TypeCheckError::NonConstantSliceLength { span } => { + | TypeCheckError::NonConstantSliceLength { span } + | TypeCheckError::StringIndexAssign { span } => { Diagnostic::simple_error(error.to_string(), String::new(), span) } TypeCheckError::PublicReturnType { typ, span } => Diagnostic::simple_error( diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 49ba3244dc9..69363d5f00a 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -258,6 +258,11 @@ impl<'interner> TypeChecker<'interner> { Type::Array(_, elem_type) => *elem_type, Type::Slice(elem_type) => *elem_type, Type::Error => Type::Error, + Type::String(_) => { + let (_lvalue_name, lvalue_span) = self.get_lvalue_name_and_span(&lvalue); + self.errors.push(TypeCheckError::StringIndexAssign { span: lvalue_span }); + Type::Error + } other => { // TODO: Need a better span here self.errors.push(TypeCheckError::TypeMismatch { From 6271b8b82342fbfa147fcd57abf1032eb63306d8 Mon Sep 17 00:00:00 2001 From: CodeDragonVN <150457827+CodeDragonVN@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:14:45 +0000 Subject: [PATCH 107/416] chore: update docs with function names to match version 0.25.0 specifications (#4466) # Description ## Problem Other how-to guides not mentioned in #4410 need to be updated to the v0.24.0 function naming. ## Summary This pull request updates the documentation to match the function naming conventions introduced in version 0.24.0. Specifically, it changes references from `generateFinalProof` and `verifyFinalProof` to `generateProof` and `verifyProof` respectively. ## Additional Context The changes were made across multiple documentation files, including tutorials and how-to guides. ## Documentation - [x] Documentation included in this PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: josh crites --- docs/docs/how_to/how-to-oracles.md | 2 +- docs/docs/tutorials/noirjs_app.md | 4 ++-- docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md | 2 +- docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md | 4 ++-- docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md | 2 +- docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/docs/tutorials/noirjs_app.md b/docs/docs/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/docs/tutorials/noirjs_app.md +++ b/docs/docs/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.24.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.24.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` diff --git a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md index 0d84d992320..ab225b9421f 100644 --- a/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md +++ b/docs/versioned_docs/version-v0.25.0/how_to/how-to-oracles.md @@ -198,7 +198,7 @@ For example, if your Noir program expects the host machine to provide CPU pseudo ```js const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc -await noir.generateFinalProof(inputs, foreignCallHandler) +await noir.generateProof(inputs, foreignCallHandler) ``` As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. diff --git a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md index ad76dd255cc..12beb476994 100644 --- a/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md +++ b/docs/versioned_docs/version-v0.25.0/tutorials/noirjs_app.md @@ -243,7 +243,7 @@ Now we're ready to prove stuff! Let's feed some inputs to our circuit and calcul await setup(); // let's squeeze our wasm inits here display('logs', 'Generating proof... ⌛'); -const proof = await noir.generateFinalProof(input); +const proof = await noir.generateProof(input); display('logs', 'Generating proof... ✅'); display('results', proof.proof); ``` @@ -264,7 +264,7 @@ Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add th ```js display('logs', 'Verifying proof... ⌛'); -const verification = await noir.verifyFinalProof(proof); +const verification = await noir.verifyProof(proof); if (verification) display('logs', 'Verifying proof... ✅'); ``` From ea3bb7f0673b9aa9b290ce263e6ce36475e0bc1a Mon Sep 17 00:00:00 2001 From: George Kushnir Date: Fri, 22 Mar 2024 08:20:39 -0400 Subject: [PATCH 108/416] chore: Update integers.md to note support for Fields using `from_integer` (#4536) Resolves #4524 --- docs/docs/noir/concepts/data_types/integers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/noir/concepts/data_types/integers.md b/docs/docs/noir/concepts/data_types/integers.md index 4d58d96fed5..1c6b375db49 100644 --- a/docs/docs/noir/concepts/data_types/integers.md +++ b/docs/docs/noir/concepts/data_types/integers.md @@ -51,7 +51,7 @@ The built-in structure `U128` allows you to use 128-bit unsigned integers almost - You cannot cast between a native integer and `U128` - There is a higher performance cost when using `U128`, compared to a native type. -Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. ```rust fn main() { From b62ecf53553049d5940cf5f818f8affa51e512ad Mon Sep 17 00:00:00 2001 From: Savio <72797635+Savio-Sou@users.noreply.github.com> Date: Mon, 25 Mar 2024 06:48:48 -0400 Subject: [PATCH 109/416] chore(github): Improve PR template "document later" checkbox description (#4625) # Description ## Problem\* Describing the option as "Exceptional Case" wasn't very intuitive. Some PRs that were meant to check that box checked the "No documentation needed." box instead. ## Summary\* Describe the option as "For Experimental Features" instead, as most if not all such exceptional cases that docs were preferred to be delayed came from features being still experimental (i.e. not intended for public use yet). ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index e81ede7199d..dfb141e29f7 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -17,7 +17,7 @@ Resolves Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. -- [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. +- [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* From 59ea775445ebb548d3ec457721421de0b0399728 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:04:57 +0000 Subject: [PATCH 110/416] chore: convert `BlockExpression` into a standard struct (#4623) # Description ## Problem\* Resolves ## Summary\* This PR splits out some more mundane changes from #4429. `BlockExpression` is now a standard rather than tuple struct to make it easier for us to add an `unsafe` property (and potentially `unchecked` in future) ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- aztec_macros/src/transforms/events.rs | 12 ++++-- aztec_macros/src/transforms/functions.rs | 29 +++++++------- aztec_macros/src/transforms/storage.rs | 2 +- compiler/noirc_frontend/src/ast/expression.rs | 32 +++++++++------- compiler/noirc_frontend/src/ast/function.rs | 2 +- compiler/noirc_frontend/src/ast/statement.rs | 14 ++++--- compiler/noirc_frontend/src/debug/mod.rs | 38 +++++++++++-------- .../src/hir/resolution/resolver.rs | 6 +-- .../noirc_frontend/src/hir/type_check/mod.rs | 3 +- compiler/noirc_frontend/src/hir_def/expr.rs | 8 ++-- .../src/monomorphization/mod.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 20 +++++----- tooling/nargo_fmt/src/visitor/expr.rs | 4 +- 13 files changed, 98 insertions(+), 74 deletions(-) diff --git a/aztec_macros/src/transforms/events.rs b/aztec_macros/src/transforms/events.rs index b02709efacb..e7e39ed29ba 100644 --- a/aztec_macros/src/transforms/events.rs +++ b/aztec_macros/src/transforms/events.rs @@ -47,10 +47,14 @@ pub fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { let mut from_signature_path = selector_path.clone(); from_signature_path.segments.push(ident("from_signature")); - let selector_fun_body = BlockExpression(vec![make_statement(StatementKind::Expression(call( - variable_path(from_signature_path), - vec![expression(ExpressionKind::Literal(Literal::Str(SIGNATURE_PLACEHOLDER.to_string())))], - )))]); + let selector_fun_body = BlockExpression { + statements: vec![make_statement(StatementKind::Expression(call( + variable_path(from_signature_path), + vec![expression(ExpressionKind::Literal(Literal::Str( + SIGNATURE_PLACEHOLDER.to_string(), + )))], + )))], + }; // Define `FunctionSelector` return type let return_type = diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index c719651e10e..855969732d0 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -39,29 +39,29 @@ pub fn transform_function( // Add check that msg sender equals this address and flag function as internal if is_internal { let is_internal_check = create_internal_check(func.name()); - func.def.body.0.insert(0, is_internal_check); + func.def.body.statements.insert(0, is_internal_check); } // Add initialization check if insert_init_check { let init_check = create_init_check(); - func.def.body.0.insert(0, init_check); + func.def.body.statements.insert(0, init_check); } // Add assertion for initialization arguments and sender if is_initializer { - func.def.body.0.insert(0, create_assert_initializer()); + func.def.body.statements.insert(0, create_assert_initializer()); } // Add access to the storage struct if storage_defined { let storage_def = abstract_storage(&ty.to_lowercase(), false); - func.def.body.0.insert(0, storage_def); + func.def.body.statements.insert(0, storage_def); } // Insert the context creation as the first action let create_context = create_context(&context_name, &func.def.parameters)?; - func.def.body.0.splice(0..0, (create_context).iter().cloned()); + func.def.body.statements.splice(0..0, (create_context).iter().cloned()); // Add the inputs to the params let input = create_inputs(&inputs_name); @@ -71,20 +71,20 @@ pub fn transform_function( if let Some(return_values) = abstract_return_values(func) { // In case we are pushing return values to the context, we remove the statement that originated it // This avoids running duplicate code, since blocks like if/else can be value returning statements - func.def.body.0.pop(); + func.def.body.statements.pop(); // Add the new return statement - func.def.body.0.push(return_values); + func.def.body.statements.push(return_values); } // Before returning mark the contract as initialized if is_initializer { let mark_initialized = create_mark_as_initialized(); - func.def.body.0.push(mark_initialized); + func.def.body.statements.push(mark_initialized); } // Push the finish method call to the end of the function let finish_def = create_context_finish(); - func.def.body.0.push(finish_def); + func.def.body.statements.push(finish_def); let return_type = create_return_type(&return_type_name); func.def.return_type = return_type; @@ -109,12 +109,12 @@ pub fn transform_vm_function( // Create access to storage if storage_defined { let storage = abstract_storage("public_vm", true); - func.def.body.0.insert(0, storage); + func.def.body.statements.insert(0, storage); } // Push Avm context creation to the beginning of the function let create_context = create_avm_context()?; - func.def.body.0.insert(0, create_context); + func.def.body.statements.insert(0, create_context); // We want the function to be seen as a public function func.def.is_unconstrained = true; @@ -131,7 +131,7 @@ pub fn transform_vm_function( /// /// This will allow developers to access their contract' storage struct in unconstrained functions pub fn transform_unconstrained(func: &mut NoirFunction) { - func.def.body.0.insert(0, abstract_storage("Unconstrained", true)); + func.def.body.statements.insert(0, abstract_storage("Unconstrained", true)); } /// Helper function that returns what the private context would look like in the ast @@ -393,7 +393,7 @@ fn create_avm_context() -> Result { /// Any primitive type that can be cast will be casted to a field and pushed to the context. fn abstract_return_values(func: &NoirFunction) -> Option { let current_return_type = func.return_type().typ; - let last_statement = func.def.body.0.last()?; + let last_statement = func.def.body.statements.last()?; // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size // Doesn't need done until we have settled on a kernel size @@ -645,7 +645,8 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { // What will be looped over // - `hasher.add({ident}[i] as Field)` - let for_loop_block = expression(ExpressionKind::Block(BlockExpression(loop_body))); + let for_loop_block = + expression(ExpressionKind::Block(BlockExpression { statements: loop_body })); // `for i in 0..{ident}.len()` make_statement(StatementKind::For(ForLoopStatement { diff --git a/aztec_macros/src/transforms/storage.rs b/aztec_macros/src/transforms/storage.rs index 40a094f78e3..10f44d01bb4 100644 --- a/aztec_macros/src/transforms/storage.rs +++ b/aztec_macros/src/transforms/storage.rs @@ -150,7 +150,7 @@ pub fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), true, )), )], - &BlockExpression(vec![storage_constructor_statement]), + &BlockExpression { statements: vec![storage_constructor_statement] }, &[], &return_type(chained_path!("Self")), )); diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index d646a6ca98a..0e5919bf7db 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -192,16 +192,18 @@ impl Expression { // with tuples without calling them. E.g. `if c { t } else { e }(a, b)` is interpreted // as a sequence of { if, tuple } rather than a function call. This behavior matches rust. let kind = if matches!(&lhs.kind, ExpressionKind::If(..)) { - ExpressionKind::Block(BlockExpression(vec![ - Statement { kind: StatementKind::Expression(lhs), span }, - Statement { - kind: StatementKind::Expression(Expression::new( - ExpressionKind::Tuple(arguments), + ExpressionKind::Block(BlockExpression { + statements: vec![ + Statement { kind: StatementKind::Expression(lhs), span }, + Statement { + kind: StatementKind::Expression(Expression::new( + ExpressionKind::Tuple(arguments), + span, + )), span, - )), - span, - }, - ])) + }, + ], + }) } else { ExpressionKind::Call(Box::new(CallExpression { func: Box::new(lhs), arguments })) }; @@ -452,19 +454,21 @@ pub struct IndexExpression { } #[derive(Debug, PartialEq, Eq, Clone)] -pub struct BlockExpression(pub Vec); +pub struct BlockExpression { + pub statements: Vec, +} impl BlockExpression { pub fn pop(&mut self) -> Option { - self.0.pop().map(|stmt| stmt.kind) + self.statements.pop().map(|stmt| stmt.kind) } pub fn len(&self) -> usize { - self.0.len() + self.statements.len() } pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.statements.is_empty() } } @@ -542,7 +546,7 @@ impl Display for Literal { impl Display for BlockExpression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "{{")?; - for statement in &self.0 { + for statement in &self.statements { let statement = statement.kind.to_string(); for line in statement.lines() { writeln!(f, " {line}")?; diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index 46f0ac0fa0f..3e8b78c1312 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -83,7 +83,7 @@ impl NoirFunction { &mut self.def } pub fn number_of_statements(&self) -> usize { - self.def.body.0.len() + self.def.body.statements.len() } pub fn span(&self) -> Span { self.def.span diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index fb7f520ee71..b14ead8ad42 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -603,10 +603,12 @@ impl ForRange { }; let block_span = block.span; - let new_block = BlockExpression(vec![ - let_elem, - Statement { kind: StatementKind::Expression(block), span: block_span }, - ]); + let new_block = BlockExpression { + statements: vec![ + let_elem, + Statement { kind: StatementKind::Expression(block), span: block_span }, + ], + }; let new_block = Expression::new(ExpressionKind::Block(new_block), block_span); let for_loop = Statement { kind: StatementKind::For(ForLoopStatement { @@ -618,7 +620,9 @@ impl ForRange { span: for_loop_span, }; - let block = ExpressionKind::Block(BlockExpression(vec![let_array, for_loop])); + let block = ExpressionKind::Block(BlockExpression { + statements: vec![let_array, for_loop], + }); StatementKind::Expression(Expression::new(block, for_loop_span)) } } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 05916502d73..8e5c174d270 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -113,7 +113,7 @@ impl DebugInstrumenter { }) .collect(); - let func_body = &mut func.body.0; + let func_body = &mut func.body.statements; let mut statements = take(func_body); self.walk_scope(&mut statements, func.span); @@ -243,7 +243,9 @@ impl DebugInstrumenter { pattern: ast::Pattern::Tuple(vars_pattern, let_stmt.pattern.span()), r#type: ast::UnresolvedType::unspecified(), expression: ast::Expression { - kind: ast::ExpressionKind::Block(ast::BlockExpression(block_stmts)), + kind: ast::ExpressionKind::Block(ast::BlockExpression { + statements: block_stmts, + }), span: let_stmt.expression.span, }, }), @@ -330,11 +332,13 @@ impl DebugInstrumenter { kind: ast::StatementKind::Assign(ast::AssignStatement { lvalue: assign_stmt.lvalue.clone(), expression: ast::Expression { - kind: ast::ExpressionKind::Block(ast::BlockExpression(vec![ - ast::Statement { kind: let_kind, span: expression_span }, - new_assign_stmt, - ast::Statement { kind: ret_kind, span: expression_span }, - ])), + kind: ast::ExpressionKind::Block(ast::BlockExpression { + statements: vec![ + ast::Statement { kind: let_kind, span: expression_span }, + new_assign_stmt, + ast::Statement { kind: ret_kind, span: expression_span }, + ], + }), span: expression_span, }, }), @@ -344,7 +348,7 @@ impl DebugInstrumenter { fn walk_expr(&mut self, expr: &mut ast::Expression) { match &mut expr.kind { - ast::ExpressionKind::Block(ast::BlockExpression(ref mut statements)) => { + ast::ExpressionKind::Block(ast::BlockExpression { ref mut statements, .. }) => { self.scope.push(HashMap::default()); self.walk_scope(statements, expr.span); } @@ -415,14 +419,16 @@ impl DebugInstrumenter { self.walk_expr(&mut for_stmt.block); for_stmt.block = ast::Expression { - kind: ast::ExpressionKind::Block(ast::BlockExpression(vec![ - set_stmt, - ast::Statement { - kind: ast::StatementKind::Semi(for_stmt.block.clone()), - span: for_stmt.block.span, - }, - drop_stmt, - ])), + kind: ast::ExpressionKind::Block(ast::BlockExpression { + statements: vec![ + set_stmt, + ast::Statement { + kind: ast::StatementKind::Semi(for_stmt.block.clone()), + span: for_stmt.block.span, + }, + drop_stmt, + ], + }), span: for_stmt.span, }; } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 3fbde8a890b..f8e3c4cab60 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -252,7 +252,7 @@ impl<'a> Resolver<'a> { typ: typ.clone(), span: name.span(), }), - body: BlockExpression(Vec::new()), + body: BlockExpression { statements: Vec::new() }, span: name.span(), where_clause: where_clause.to_vec(), return_type: return_type.clone(), @@ -1952,8 +1952,8 @@ impl<'a> Resolver<'a> { fn resolve_block(&mut self, block_expr: BlockExpression) -> HirExpression { let statements = - self.in_new_scope(|this| vecmap(block_expr.0, |stmt| this.intern_stmt(stmt))); - HirExpression::Block(HirBlockExpression(statements)) + self.in_new_scope(|this| vecmap(block_expr.statements, |stmt| this.intern_stmt(stmt))); + HirExpression::Block(HirBlockExpression { statements }) } pub fn intern_block(&mut self, block: BlockExpression) -> ExprId { diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index ab759f454e5..137608f8037 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -468,7 +468,8 @@ mod test { expression: expr_id, }; let stmt_id = interner.push_stmt(HirStatement::Let(let_stmt)); - let expr_id = interner.push_expr(HirExpression::Block(HirBlockExpression(vec![stmt_id]))); + let expr_id = interner + .push_expr(HirExpression::Block(HirBlockExpression { statements: vec![stmt_id] })); interner.push_expr_location(expr_id, Span::single_char(0), file); // Create function to enclose the let statement diff --git a/compiler/noirc_frontend/src/hir_def/expr.rs b/compiler/noirc_frontend/src/hir_def/expr.rs index 61743d2cdc7..c2f6031bf6d 100644 --- a/compiler/noirc_frontend/src/hir_def/expr.rs +++ b/compiler/noirc_frontend/src/hir_def/expr.rs @@ -37,7 +37,7 @@ pub enum HirExpression { impl HirExpression { /// Returns an empty block expression pub const fn empty_block() -> HirExpression { - HirExpression::Block(HirBlockExpression(vec![])) + HirExpression::Block(HirBlockExpression { statements: vec![] }) } } @@ -249,11 +249,13 @@ pub struct HirIndexExpression { } #[derive(Debug, Clone)] -pub struct HirBlockExpression(pub Vec); +pub struct HirBlockExpression { + pub statements: Vec, +} impl HirBlockExpression { pub fn statements(&self) -> &[StmtId] { - &self.0 + &self.statements } } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 9b0f57a7d39..618eba8f190 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -430,7 +430,7 @@ impl<'interner> Monomorphizer<'interner> { } }, HirExpression::Literal(HirLiteral::Unit) => ast::Expression::Block(vec![]), - HirExpression::Block(block) => self.block(block.0)?, + HirExpression::Block(block) => self.block(block.statements)?, HirExpression::Prefix(prefix) => { let location = self.interner.expr_location(&expr); diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index dec1c7aa9ce..a40355be8aa 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -347,7 +347,7 @@ fn block<'a>( [(LeftParen, RightParen), (LeftBracket, RightBracket)], |span| vec![Statement { kind: StatementKind::Error, span }], )) - .map(BlockExpression) + .map(|statements| BlockExpression { statements }) } fn check_statements_require_semicolon( @@ -1015,10 +1015,12 @@ where // Wrap the inner `if` expression in a block expression. // i.e. rewrite the sugared form `if cond1 {} else if cond2 {}` as `if cond1 {} else { if cond2 {} }`. let if_expression = Expression::new(kind, span); - let desugared_else = BlockExpression(vec![Statement { - kind: StatementKind::Expression(if_expression), - span, - }]); + let desugared_else = BlockExpression { + statements: vec![Statement { + kind: StatementKind::Expression(if_expression), + span, + }], + }; Expression::new(ExpressionKind::Block(desugared_else), span) })); @@ -1399,13 +1401,13 @@ mod test { // Regression for #1310: this should be parsed as a block and not a function call let res = parse_with(block(fresh_statement()), "{ if true { 1 } else { 2 } (3, 4) }").unwrap(); - match unwrap_expr(&res.0.last().unwrap().kind) { + match unwrap_expr(&res.statements.last().unwrap().kind) { // The `if` followed by a tuple is currently creates a block around both in case // there was none to start with, so there is an extra block here. ExpressionKind::Block(block) => { - assert_eq!(block.0.len(), 2); - assert!(matches!(unwrap_expr(&block.0[0].kind), ExpressionKind::If(_))); - assert!(matches!(unwrap_expr(&block.0[1].kind), ExpressionKind::Tuple(_))); + assert_eq!(block.statements.len(), 2); + assert!(matches!(unwrap_expr(&block.statements[0].kind), ExpressionKind::If(_))); + assert!(matches!(unwrap_expr(&block.statements[1].kind), ExpressionKind::Tuple(_))); } _ => unreachable!(), } diff --git a/tooling/nargo_fmt/src/visitor/expr.rs b/tooling/nargo_fmt/src/visitor/expr.rs index 2cd0e881e84..f9836adda18 100644 --- a/tooling/nargo_fmt/src/visitor/expr.rs +++ b/tooling/nargo_fmt/src/visitor/expr.rs @@ -119,11 +119,11 @@ impl FmtVisitor<'_> { self.last_position = block_span.start() + 1; // `{` self.push_str("{"); - self.trim_spaces_after_opening_brace(&block.0); + self.trim_spaces_after_opening_brace(&block.statements); self.indent.block_indent(self.config); - self.visit_stmts(block.0); + self.visit_stmts(block.statements); let span = (self.last_position..block_span.end() - 1).into(); self.close_block(span); From c46b164ce56e6a8f81255fb17eb6539bd040f336 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Mon, 25 Mar 2024 21:27:58 +0000 Subject: [PATCH 111/416] chore: Release Noir(0.26.0) (#4526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* ---

0.26.0 ## [0.26.0](https://github.com/noir-lang/noir/compare/v0.25.0...v0.26.0) (2024-03-25) ### ⚠ BREAKING CHANGES * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) * automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) * separating out array and slice types in the AST ([#4504](https://github.com/noir-lang/noir/issues/4504)) * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) * Remove open keyword from Noir (https://github.com/AztecProtocol/aztec-packages/pull/4967) ### Features * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Add `break` and `continue` in unconstrained code ([#4569](https://github.com/noir-lang/noir/issues/4569)) ([f2f827d](https://github.com/noir-lang/noir/commit/f2f827d51e6fe99fa3d17f125b22743da25e25be)) * Add `nargo compile --watch` command ([#4464](https://github.com/noir-lang/noir/issues/4464)) ([44e60b6](https://github.com/noir-lang/noir/commit/44e60b67469de88f20842c4eead64d736f7bd4a0)) * Add as_slice builtin function, add execution test ([#4523](https://github.com/noir-lang/noir/issues/4523)) ([6a9ea35](https://github.com/noir-lang/noir/commit/6a9ea35c4f1578058179aa08eedf44eb18bad4a1)) * Add checks for bit size consistency on brillig gen ([#4542](https://github.com/noir-lang/noir/issues/4542)) ([f3243b7](https://github.com/noir-lang/noir/commit/f3243b763c0b15ae90beb8e35630df27f3d314c0)) * Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Add experimental `quote` expression to parser ([#4595](https://github.com/noir-lang/noir/issues/4595)) ([4c3a30b](https://github.com/noir-lang/noir/commit/4c3a30b4991a329d3c52e1dfa59d854d7e6910db)) * Add more impls on Option ([#4549](https://github.com/noir-lang/noir/issues/4549)) ([4cf700b](https://github.com/noir-lang/noir/commit/4cf700bcfe157ebc82cdf7321a16959b7a4add57)) * Add specific error for attempting `string[x] = ".."` ([#4611](https://github.com/noir-lang/noir/issues/4611)) ([ff95fd9](https://github.com/noir-lang/noir/commit/ff95fd93451b2053360a16b7d3204ca251199296)) * Allow usage of noir `#[test]` syntax in stdlib ([#4553](https://github.com/noir-lang/noir/issues/4553)) ([a8b7cdb](https://github.com/noir-lang/noir/commit/a8b7cdb8a3698bc8923b6fa8714deebb8bf3923f)) * Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Check initialization arguments in constructors (https://github.com/AztecProtocol/aztec-packages/pull/5144) ([d4213a0](https://github.com/noir-lang/noir/commit/d4213a03c9f77ee8e7663fc965a825258d90a368)) * Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Integrated native ACVM (https://github.com/AztecProtocol/aztec-packages/pull/4903) ([a6016b4](https://github.com/noir-lang/noir/commit/a6016b46abf6da6de4566cf6d35a675d805dd9b5)) * Make brillig-gen more AVM-friendly (https://github.com/AztecProtocol/aztec-packages/pull/5091) ([a6016b4](https://github.com/noir-lang/noir/commit/a6016b46abf6da6de4566cf6d35a675d805dd9b5)) * New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Optimize sha2 implementation ([#4441](https://github.com/noir-lang/noir/issues/4441)) ([80373d6](https://github.com/noir-lang/noir/commit/80373d612c023e3e165b49b6d1729486b0ba3b4b)) * RC optimization pass ([#4560](https://github.com/noir-lang/noir/issues/4560)) ([dfa5126](https://github.com/noir-lang/noir/commit/dfa5126f2c65843c34701cacddf2cbcfb0d7ff11)) * Remove curly braces with fmt ([#4529](https://github.com/noir-lang/noir/issues/4529)) ([fe9a437](https://github.com/noir-lang/noir/commit/fe9a437b6d7ddc3f78665df1a576236555880c51)) * Separating out array and slice types in the AST ([#4504](https://github.com/noir-lang/noir/issues/4504)) ([9a241f9](https://github.com/noir-lang/noir/commit/9a241f9622b342cd9d56bf8481219cfc374c0510)) * Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Visible aliases for nargo commands ([#4453](https://github.com/noir-lang/noir/issues/4453)) ([773cf19](https://github.com/noir-lang/noir/commit/773cf190ee21381d826ba80391a5d7d5efae9174)) ### Bug Fixes * **acir_gen:** More granular element sizes array check ([#4528](https://github.com/noir-lang/noir/issues/4528)) ([f93d16e](https://github.com/noir-lang/noir/commit/f93d16e3e89c5df358c982deae4f3c2d4c82b77f)) * Added error messages for passing oracles and references from unconstrained to constrained functions ([#4570](https://github.com/noir-lang/noir/issues/4570)) ([265bd8b](https://github.com/noir-lang/noir/commit/265bd8b284e5acd572a3812a94a99fc102227ff2)) * Allow non-integer globals to reference struct methods ([#4490](https://github.com/noir-lang/noir/issues/4490)) ([00d6494](https://github.com/noir-lang/noir/commit/00d6494ae70b10e1872d96fb4e57ecb0b5f01787)) * Dynamic assert messages in brillig ([#4531](https://github.com/noir-lang/noir/issues/4531)) ([e24d3fc](https://github.com/noir-lang/noir/commit/e24d3fc5a084610d9511e3c5421275cb9c84a548)) * Evaluate operators in globals in types ([#4537](https://github.com/noir-lang/noir/issues/4537)) ([c8aa16b](https://github.com/noir-lang/noir/commit/c8aa16bc7e78456cce1736fac82496996a8761f4)) * Make `nargo` the default binary for cargo run ([#4554](https://github.com/noir-lang/noir/issues/4554)) ([de4986e](https://github.com/noir-lang/noir/commit/de4986eb74b28b2e1065fa6b413d02457ddf61b0)) * Signed integer comparisons in brillig ([#4579](https://github.com/noir-lang/noir/issues/4579)) ([938d5e8](https://github.com/noir-lang/noir/commit/938d5e85eda00a05de5014e64d3dc9fc7c24936d)) * **ssa:** Use accurate type during SSA AsSlice simplficiation ([#4610](https://github.com/noir-lang/noir/issues/4610)) ([0473497](https://github.com/noir-lang/noir/commit/04734976e92475b1ab94257e30bc3438c7358681)) * Substitute generics when checking the field count of a type ([#4547](https://github.com/noir-lang/noir/issues/4547)) ([eeeebac](https://github.com/noir-lang/noir/commit/eeeebacd10698e847f773e26dac8a4a5eb8e84ed)) ### Miscellaneous Chores * Remove open keyword from Noir (https://github.com/AztecProtocol/aztec-packages/pull/4967) ([a6016b4](https://github.com/noir-lang/noir/commit/a6016b46abf6da6de4566cf6d35a675d805dd9b5))
0.42.0 ## [0.42.0](https://github.com/noir-lang/noir/compare/v0.41.0...v0.42.0) (2024-03-25) ### ⚠ BREAKING CHANGES * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) * automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) * move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) * note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) * rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) * init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) * Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) * Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) * Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) * Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) * Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ### Features * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) * Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) * Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de)) * Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) * Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Aztec-packages ([#3754](https://github.com/noir-lang/noir/issues/3754)) ([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2)) * Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) * Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c)) * Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) * Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671)) * Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3)) * Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Speed up transformation of debug messages ([#3815](https://github.com/noir-lang/noir/issues/3815)) ([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48)) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c)) * Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc)) * Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138)) * Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) ### Bug Fixes * Deserialize odd length hex literals ([#3747](https://github.com/noir-lang/noir/issues/3747)) ([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2)) * Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b)) * Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925)) ### Miscellaneous Chores * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805)) * Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579)) * Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e)) * Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .release-please-manifest.json | 4 +- CHANGELOG.md | 62 ++++++++++++++ Cargo.lock | 50 ++++++------ Cargo.toml | 14 ++-- acvm-repo/CHANGELOG.md | 81 +++++++++++++++++++ acvm-repo/acir/Cargo.toml | 2 +- acvm-repo/acir_field/Cargo.toml | 2 +- acvm-repo/acvm/Cargo.toml | 2 +- acvm-repo/acvm_js/Cargo.toml | 2 +- acvm-repo/acvm_js/package.json | 2 +- acvm-repo/blackbox_solver/Cargo.toml | 2 +- acvm-repo/brillig/Cargo.toml | 2 +- acvm-repo/brillig_vm/Cargo.toml | 2 +- compiler/wasm/package.json | 2 +- flake.nix | 2 +- tooling/noir_codegen/package.json | 2 +- tooling/noir_js/package.json | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- tooling/noir_js_types/package.json | 2 +- tooling/noirc_abi_wasm/package.json | 2 +- 20 files changed, 192 insertions(+), 49 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b38234ca0b9..d4cc095c484 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.25.0", - "acvm-repo": "0.41.0" + ".": "0.26.0", + "acvm-repo": "0.42.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cdbccf768ca..7c4bcad5840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,67 @@ # Changelog +## [0.26.0](https://github.com/noir-lang/noir/compare/v0.25.0...v0.26.0) (2024-03-25) + + +### ⚠ BREAKING CHANGES + +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) +* automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) +* separating out array and slice types in the AST ([#4504](https://github.com/noir-lang/noir/issues/4504)) +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) +* Remove open keyword from Noir (https://github.com/AztecProtocol/aztec-packages/pull/4967) + +### Features + +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Add `break` and `continue` in unconstrained code ([#4569](https://github.com/noir-lang/noir/issues/4569)) ([f2f827d](https://github.com/noir-lang/noir/commit/f2f827d51e6fe99fa3d17f125b22743da25e25be)) +* Add `nargo compile --watch` command ([#4464](https://github.com/noir-lang/noir/issues/4464)) ([44e60b6](https://github.com/noir-lang/noir/commit/44e60b67469de88f20842c4eead64d736f7bd4a0)) +* Add as_slice builtin function, add execution test ([#4523](https://github.com/noir-lang/noir/issues/4523)) ([6a9ea35](https://github.com/noir-lang/noir/commit/6a9ea35c4f1578058179aa08eedf44eb18bad4a1)) +* Add checks for bit size consistency on brillig gen ([#4542](https://github.com/noir-lang/noir/issues/4542)) ([f3243b7](https://github.com/noir-lang/noir/commit/f3243b763c0b15ae90beb8e35630df27f3d314c0)) +* Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Add experimental `quote` expression to parser ([#4595](https://github.com/noir-lang/noir/issues/4595)) ([4c3a30b](https://github.com/noir-lang/noir/commit/4c3a30b4991a329d3c52e1dfa59d854d7e6910db)) +* Add more impls on Option ([#4549](https://github.com/noir-lang/noir/issues/4549)) ([4cf700b](https://github.com/noir-lang/noir/commit/4cf700bcfe157ebc82cdf7321a16959b7a4add57)) +* Add specific error for attempting `string[x] = ".."` ([#4611](https://github.com/noir-lang/noir/issues/4611)) ([ff95fd9](https://github.com/noir-lang/noir/commit/ff95fd93451b2053360a16b7d3204ca251199296)) +* Allow usage of noir `#[test]` syntax in stdlib ([#4553](https://github.com/noir-lang/noir/issues/4553)) ([a8b7cdb](https://github.com/noir-lang/noir/commit/a8b7cdb8a3698bc8923b6fa8714deebb8bf3923f)) +* Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Check initialization arguments in constructors (https://github.com/AztecProtocol/aztec-packages/pull/5144) ([d4213a0](https://github.com/noir-lang/noir/commit/d4213a03c9f77ee8e7663fc965a825258d90a368)) +* Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Integrated native ACVM (https://github.com/AztecProtocol/aztec-packages/pull/4903) ([a6016b4](https://github.com/noir-lang/noir/commit/a6016b46abf6da6de4566cf6d35a675d805dd9b5)) +* Make brillig-gen more AVM-friendly (https://github.com/AztecProtocol/aztec-packages/pull/5091) ([a6016b4](https://github.com/noir-lang/noir/commit/a6016b46abf6da6de4566cf6d35a675d805dd9b5)) +* New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Optimize sha2 implementation ([#4441](https://github.com/noir-lang/noir/issues/4441)) ([80373d6](https://github.com/noir-lang/noir/commit/80373d612c023e3e165b49b6d1729486b0ba3b4b)) +* RC optimization pass ([#4560](https://github.com/noir-lang/noir/issues/4560)) ([dfa5126](https://github.com/noir-lang/noir/commit/dfa5126f2c65843c34701cacddf2cbcfb0d7ff11)) +* Remove curly braces with fmt ([#4529](https://github.com/noir-lang/noir/issues/4529)) ([fe9a437](https://github.com/noir-lang/noir/commit/fe9a437b6d7ddc3f78665df1a576236555880c51)) +* Separating out array and slice types in the AST ([#4504](https://github.com/noir-lang/noir/issues/4504)) ([9a241f9](https://github.com/noir-lang/noir/commit/9a241f9622b342cd9d56bf8481219cfc374c0510)) +* Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Visible aliases for nargo commands ([#4453](https://github.com/noir-lang/noir/issues/4453)) ([773cf19](https://github.com/noir-lang/noir/commit/773cf190ee21381d826ba80391a5d7d5efae9174)) + + +### Bug Fixes + +* **acir_gen:** More granular element sizes array check ([#4528](https://github.com/noir-lang/noir/issues/4528)) ([f93d16e](https://github.com/noir-lang/noir/commit/f93d16e3e89c5df358c982deae4f3c2d4c82b77f)) +* Added error messages for passing oracles and references from unconstrained to constrained functions ([#4570](https://github.com/noir-lang/noir/issues/4570)) ([265bd8b](https://github.com/noir-lang/noir/commit/265bd8b284e5acd572a3812a94a99fc102227ff2)) +* Allow non-integer globals to reference struct methods ([#4490](https://github.com/noir-lang/noir/issues/4490)) ([00d6494](https://github.com/noir-lang/noir/commit/00d6494ae70b10e1872d96fb4e57ecb0b5f01787)) +* Dynamic assert messages in brillig ([#4531](https://github.com/noir-lang/noir/issues/4531)) ([e24d3fc](https://github.com/noir-lang/noir/commit/e24d3fc5a084610d9511e3c5421275cb9c84a548)) +* Evaluate operators in globals in types ([#4537](https://github.com/noir-lang/noir/issues/4537)) ([c8aa16b](https://github.com/noir-lang/noir/commit/c8aa16bc7e78456cce1736fac82496996a8761f4)) +* Make `nargo` the default binary for cargo run ([#4554](https://github.com/noir-lang/noir/issues/4554)) ([de4986e](https://github.com/noir-lang/noir/commit/de4986eb74b28b2e1065fa6b413d02457ddf61b0)) +* Signed integer comparisons in brillig ([#4579](https://github.com/noir-lang/noir/issues/4579)) ([938d5e8](https://github.com/noir-lang/noir/commit/938d5e85eda00a05de5014e64d3dc9fc7c24936d)) +* **ssa:** Use accurate type during SSA AsSlice simplficiation ([#4610](https://github.com/noir-lang/noir/issues/4610)) ([0473497](https://github.com/noir-lang/noir/commit/04734976e92475b1ab94257e30bc3438c7358681)) +* Substitute generics when checking the field count of a type ([#4547](https://github.com/noir-lang/noir/issues/4547)) ([eeeebac](https://github.com/noir-lang/noir/commit/eeeebacd10698e847f773e26dac8a4a5eb8e84ed)) + + +### Miscellaneous Chores + +* Remove open keyword from Noir (https://github.com/AztecProtocol/aztec-packages/pull/4967) ([a6016b4](https://github.com/noir-lang/noir/commit/a6016b46abf6da6de4566cf6d35a675d805dd9b5)) + ## [0.25.0](https://github.com/noir-lang/noir/compare/v0.24.0...v0.25.0) (2024-03-11) diff --git a/Cargo.lock b/Cargo.lock index 99c19417267..d5a610ab57f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.41.0" +version = "0.42.0" dependencies = [ "acir_field", "base64 0.21.2", @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.41.0" +version = "0.42.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -37,7 +37,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.41.0" +version = "0.42.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -53,7 +53,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.41.0" +version = "0.42.0" dependencies = [ "acir", "blake2", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.41.0" +version = "0.42.0" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -232,7 +232,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arena" -version = "0.25.0" +version = "0.26.0" [[package]] name = "ark-bls12-381" @@ -433,7 +433,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aztec_macros" -version = "0.25.0" +version = "0.26.0" dependencies = [ "convert_case 0.6.0", "iter-extended", @@ -623,7 +623,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.41.0" +version = "0.42.0" dependencies = [ "acir_field", "serde", @@ -631,7 +631,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.41.0" +version = "0.42.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1736,7 +1736,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.25.0" +version = "0.26.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -2357,7 +2357,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.25.0" +version = "0.26.0" [[package]] name = "itertools" @@ -2742,7 +2742,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nargo" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "codespan-reporting", @@ -2769,7 +2769,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "assert_cmd", @@ -2824,7 +2824,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.25.0" +version = "0.26.0" dependencies = [ "bytecount", "noirc_frontend", @@ -2836,7 +2836,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.25.0" +version = "0.26.0" dependencies = [ "dirs", "fm", @@ -2909,7 +2909,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "assert_cmd", @@ -2944,7 +2944,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "async-lsp", @@ -2970,7 +2970,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "build-data", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "iter-extended", @@ -3010,7 +3010,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "build-data", @@ -3027,7 +3027,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "aztec_macros", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "base64 0.21.2", @@ -3066,7 +3066,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "fxhash", @@ -3082,7 +3082,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "arena", @@ -3107,7 +3107,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.25.0" +version = "0.26.0" dependencies = [ "acvm", "iter-extended", diff --git a/Cargo.toml b/Cargo.toml index b8f9b9ceacc..74d2e6e05c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.25.0" +version = "0.26.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -52,12 +52,12 @@ repository = "https://github.com/noir-lang/noir/" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "0.41.0", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "0.41.0", path = "acvm-repo/acir", default-features = false } -acvm = { version = "0.41.0", path = "acvm-repo/acvm" } -brillig = { version = "0.41.0", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "0.41.0", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "0.41.0", path = "acvm-repo/blackbox_solver", default-features = false } +acir_field = { version = "0.42.0", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "0.42.0", path = "acvm-repo/acir", default-features = false } +acvm = { version = "0.42.0", path = "acvm-repo/acvm" } +brillig = { version = "0.42.0", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "0.42.0", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "0.42.0", path = "acvm-repo/blackbox_solver", default-features = false } bn254_blackbox_solver = { version = "0.39.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md index 4f220d6eeba..33cc83d7dd9 100644 --- a/acvm-repo/CHANGELOG.md +++ b/acvm-repo/CHANGELOG.md @@ -5,6 +5,87 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.42.0](https://github.com/noir-lang/noir/compare/v0.41.0...v0.42.0) (2024-03-25) + + +### ⚠ BREAKING CHANGES + +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) +* automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) +* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) +* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) +* rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) +* init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) +* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) +* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) +* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) +* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) +* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) + +### Features + +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) +* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) +* Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de)) +* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) +* Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Aztec-packages ([#3754](https://github.com/noir-lang/noir/issues/3754)) ([c043265](https://github.com/noir-lang/noir/commit/c043265e550b59bd4296504826fe15d3ce3e9ad2)) +* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) +* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c)) +* Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) +* Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671)) +* Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3)) +* Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Speed up transformation of debug messages ([#3815](https://github.com/noir-lang/noir/issues/3815)) ([2a8af1e](https://github.com/noir-lang/noir/commit/2a8af1e4141ffff61547ee1c2837a6392bd5db48)) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c)) +* Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc)) +* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138)) +* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) + + +### Bug Fixes + +* Deserialize odd length hex literals ([#3747](https://github.com/noir-lang/noir/issues/3747)) ([4000fb2](https://github.com/noir-lang/noir/commit/4000fb279221eb07187d657bfaa7f1c7b311abf2)) +* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b)) +* Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925)) + + +### Miscellaneous Chores + +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove partial backend feature ([#3805](https://github.com/noir-lang/noir/issues/3805)) ([0383100](https://github.com/noir-lang/noir/commit/0383100853a80a5b28b797cdfeae0d271f1b7805)) +* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579)) +* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e)) +* Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) + ## [0.41.0](https://github.com/noir-lang/noir/compare/v0.40.0...v0.41.0) (2024-03-11) diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml index be859d7d054..368f49258f9 100644 --- a/acvm-repo/acir/Cargo.toml +++ b/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index c2056b73277..d63a885bfd8 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index d585850170a..d0ea52e859d 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 63fca2bd32a..65c072b1d96 100644 --- a/acvm-repo/acvm_js/Cargo.toml +++ b/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index 2f444ba565a..55345a2ddf6 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "0.41.0", + "version": "0.42.0", "publishConfig": { "access": "public" }, diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index a783193edba..8f5ff862360 100644 --- a/acvm-repo/blackbox_solver/Cargo.toml +++ b/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml index 57f89e091b4..d3f082fda86 100644 --- a/acvm-repo/brillig/Cargo.toml +++ b/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 1c7add5cb40..32dabe6ecb0 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "0.41.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index 8db3876a753..6d11a5ba5c8 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.25.0", + "version": "0.26.0", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/flake.nix b/flake.nix index 785d5aead56..1cb421a49ef 100644 --- a/flake.nix +++ b/flake.nix @@ -73,7 +73,7 @@ # Configuration shared between builds config = { # x-release-please-start-version - version = "0.25.0"; + version = "0.26.0"; # x-release-please-end src = pkgs.lib.cleanSourceWith { diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json index 485f4252e29..1eabc6a1398 100644 --- a/tooling/noir_codegen/package.json +++ b/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.25.0", + "version": "0.26.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index 0da6e695202..c8d4873e095 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.25.0", + "version": "0.26.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 92b8460d1db..aec8204f065 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.25.0", + "version": "0.26.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index d7a08debb54..eadb6f49665 100644 --- a/tooling/noir_js_types/package.json +++ b/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "0.25.0", + "version": "0.26.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index a6cc5503242..e93a8e6a5e3 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.25.0", + "version": "0.26.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { From d6f10438487fea39b099f6bd313f89c6197b2af7 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:10:08 +0000 Subject: [PATCH 112/416] chore: fix acvm crates reporting errors as JS packages (#4637) # Description ## Problem\* Addresses incorrect content of #4634 ## Summary\* We were raising an alert for a bad publish of the ACVM crates as if they were the JS packages so this PR updates the alert to be consistent. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/ACVM_PUBLISH_FAILED.md | 10 ++++++++++ .github/workflows/publish-acvm.yml | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .github/ACVM_PUBLISH_FAILED.md diff --git a/.github/ACVM_PUBLISH_FAILED.md b/.github/ACVM_PUBLISH_FAILED.md new file mode 100644 index 00000000000..00e692a64d8 --- /dev/null +++ b/.github/ACVM_PUBLISH_FAILED.md @@ -0,0 +1,10 @@ +--- +title: "ACVM crates failed to publish" +assignees: TomAFrench, Savio-Sou +--- + +The {{env.CRATE_VERSION}} release of the ACVM crates failed. + +Check the [Publish ACVM crates]({{env.WORKFLOW_URL}}) workflow for details. + +This issue was raised by the workflow `{{env.WORKFLOW_NAME}}` diff --git a/.github/workflows/publish-acvm.yml b/.github/workflows/publish-acvm.yml index 959cd8e4bca..d17d8f294fb 100644 --- a/.github/workflows/publish-acvm.yml +++ b/.github/workflows/publish-acvm.yml @@ -74,4 +74,4 @@ jobs: WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} with: update_existing: true - filename: .github/JS_PUBLISH_FAILED.md \ No newline at end of file + filename: .github/ACVM_PUBLISH_FAILED.md \ No newline at end of file From 10f182a77a1662e45c200a701fdd66c5db035a66 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:10:27 +0000 Subject: [PATCH 113/416] chore: fix versioning of `bn254_blackbox_solver` crate (#4638) # Description ## Problem\* Resolves #4634 ## Summary\* The ACVM crate publishing steps are failing because when `barretenberg_blackbox_solver` was renamed to `bn254_blackbox_solver` the release please config wasn't updated to match, resulting in the version not being incremented on releases. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 2 +- Cargo.toml | 2 +- acvm-repo/bn254_blackbox_solver/Cargo.toml | 2 +- release-please-config.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5a610ab57f..caa6eb4d250 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -600,7 +600,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "0.39.0" +version = "0.42.0" dependencies = [ "acir", "acvm_blackbox_solver", diff --git a/Cargo.toml b/Cargo.toml index 74d2e6e05c7..46ccb401fbd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,7 +58,7 @@ acvm = { version = "0.42.0", path = "acvm-repo/acvm" } brillig = { version = "0.42.0", path = "acvm-repo/brillig", default-features = false } brillig_vm = { version = "0.42.0", path = "acvm-repo/brillig_vm", default-features = false } acvm_blackbox_solver = { version = "0.42.0", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "0.39.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "0.42.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies arena = { path = "compiler/utils/arena" } diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index a0a15409604..396e4aa0146 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "0.39.0" +version = "0.42.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/release-please-config.json b/release-please-config.json index e73993ca974..217a86303a1 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -61,8 +61,8 @@ "acir_field/Cargo.toml", "acvm/Cargo.toml", "acvm_js/Cargo.toml", - "barretenberg_blackbox_solver/Cargo.toml", "blackbox_solver/Cargo.toml", + "bn254_blackbox_solver/Cargo.toml", "brillig/Cargo.toml", "brillig_vm/Cargo.toml", { From 1b50ce155cf95193937729c2a23f34b0ade42ea0 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Tue, 26 Mar 2024 17:23:04 +0000 Subject: [PATCH 114/416] fix(ssa): Fix slice intrinsic handling in the capacity tracker (#4643) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4395#issuecomment-2018948631 ## Summary\* We were not accurately accounting for situations where the slice capacity tracker was expecting a capacity from slice intrinsic results. I have added a case for handling the slice capacity of `AsSlice`. I also tested this against the `aztec-packages` work and we can successfully compile now. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../ssa/opt/flatten_cfg/capacity_tracker.rs | 14 +++++++++++--- .../regression_capacity_tracker/Nargo.toml | 7 +++++++ .../regression_capacity_tracker/Prover.toml | 3 +++ .../regression_capacity_tracker/src/main.nr | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 test_programs/execution_success/regression_capacity_tracker/Nargo.toml create mode 100644 test_programs/execution_success/regression_capacity_tracker/Prover.toml create mode 100644 test_programs/execution_success/regression_capacity_tracker/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs index bdfc04f0bbe..93e52542278 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/capacity_tracker.rs @@ -73,6 +73,7 @@ impl<'a> SliceCapacityTracker<'a> { Intrinsic::SlicePopFront => (Some(1), results.len() - 1), // The slice capacity of these intrinsics is not determined by the arguments of the function. Intrinsic::ToBits(_) | Intrinsic::ToRadix(_) => (None, 1), + Intrinsic::AsSlice => (Some(0), 1), _ => return, }; let result_slice = results[result_index]; @@ -90,6 +91,7 @@ impl<'a> SliceCapacityTracker<'a> { self.compute_slice_capacity(*arg, slice_sizes); } } + if let Some(contents_capacity) = slice_sizes.get(&slice_contents) { let new_capacity = *contents_capacity + 1; slice_sizes.insert(result_slice, new_capacity); @@ -102,9 +104,6 @@ impl<'a> SliceCapacityTracker<'a> { .expect("ICE: Should have an argument index for slice intrinsics"); let slice_contents = arguments[argument_index]; - // We do not decrement the size on intrinsics that could remove values from a slice. - // This is because we could potentially go back to the smaller slice and not fill in dummies. - // This pass should be tracking the potential max that a slice ***could be*** if let Some(contents_capacity) = slice_sizes.get(&slice_contents) { let new_capacity = *contents_capacity - 1; slice_sizes.insert(result_slice, new_capacity); @@ -121,6 +120,15 @@ impl<'a> SliceCapacityTracker<'a> { slice_sizes .insert(result_slice, FieldElement::max_num_bytes() as usize); } + Intrinsic::AsSlice => { + let argument_index = argument_index + .expect("ICE: Should have an argument index for AsSlice builtin"); + let array_size = self + .dfg + .try_get_array_length(arguments[argument_index]) + .expect("ICE: Should be have an array length for AsSlice input"); + slice_sizes.insert(result_slice, array_size); + } _ => {} } } diff --git a/test_programs/execution_success/regression_capacity_tracker/Nargo.toml b/test_programs/execution_success/regression_capacity_tracker/Nargo.toml new file mode 100644 index 00000000000..d5a3839626f --- /dev/null +++ b/test_programs/execution_success/regression_capacity_tracker/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_capacity_tracker" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/regression_capacity_tracker/Prover.toml b/test_programs/execution_success/regression_capacity_tracker/Prover.toml new file mode 100644 index 00000000000..bbf35b23a0f --- /dev/null +++ b/test_programs/execution_success/regression_capacity_tracker/Prover.toml @@ -0,0 +1,3 @@ +expected = "10" +first = "10" +input = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] \ No newline at end of file diff --git a/test_programs/execution_success/regression_capacity_tracker/src/main.nr b/test_programs/execution_success/regression_capacity_tracker/src/main.nr new file mode 100644 index 00000000000..be645c811d2 --- /dev/null +++ b/test_programs/execution_success/regression_capacity_tracker/src/main.nr @@ -0,0 +1,19 @@ +// Reference https://github.com/noir-lang/noir/issues/4395#issuecomment-2018948631 +// for context. +// We were not accurately accounting for situations where the slice capacity tracker +// was expecting a capacity from slice intrinsic results. +fn main(expected: pub Field, first: Field, input: [Field; 20]) { + let mut hasher_slice = input.as_slice(); + hasher_slice = hasher_slice.push_front(first); + assert(hasher_slice[0] == expected); + // We need a conditional based upon witnesses + // to force a store of the slice. + // If this successfully compiles it means we have stored + // the results of the slice intrinsics used above. + if expected as u32 > 10 { + hasher_slice[expected - 10] = 100; + } else { + hasher_slice[expected] = 100; + } + assert(hasher_slice[0] == expected); +} From 4deda65f239b59a51e0ab4523f48794b27921707 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 26 Mar 2024 19:23:07 +0000 Subject: [PATCH 115/416] chore(ci): add warning sticky comment (#4647) # Description ## Problem\* Resolves ## Summary\* We currently don't have any feedback for when the release workflow doesn't fully complete to signal that releases shouldn't be made. I've updated the workflow so that we post a sticky comment in this situation. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/release.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83e8e479181..249d83afecc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -100,6 +100,36 @@ jobs: git commit -m "chore(docs): cut new docs version for tag ${{ steps.noir-version.outputs.semver }}" git push + release-end: + name: Release End + runs-on: ubuntu-latest + # We want this job to always run (even if the dependant jobs fail) as we need apply changes to the sticky comment. + if: ${{ always() }} + + needs: + - update-acvm-workspace-package-versions + - update-docs + + env: + # We treat any skipped or failing jobs as a failure for the workflow as a whole. + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') } + + steps: + - name: Add warning to sticky comment + uses: marocchino/sticky-pull-request-comment@v2 + with: + # delete the comment in case failures have been fixed + delete: ${{ !env.FAIL }} + message: "The release workflow has not completed successfully. Releasing now will result in a broken release" + + - name: Report overall success + run: | + if [[ $FAIL == true ]]; then + exit 1 + else + exit 0 + fi + build-binaries: name: Build binaries needs: [release-please] From c0bae17e70f55ebf4b1639e0dfb075d8c5c97892 Mon Sep 17 00:00:00 2001 From: jfecher Date: Tue, 26 Mar 2024 14:40:00 -0500 Subject: [PATCH 116/416] fix: Slice coercions (#4640) # Description ## Problem\* Resolves #4639 ## Summary\* The array to slice coercion was broken - and it seemingly has always been so. The coercion never actually called `replace_expr` to replace the original expression with the `as_slice(original_expr)` coercion. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/hir_def/types.rs | 15 ++++++++++----- .../slice_coercion/Nargo.toml | 7 +++++++ .../slice_coercion/Prover.toml | 2 ++ .../slice_coercion/src/main.nr | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 test_programs/execution_success/slice_coercion/Nargo.toml create mode 100644 test_programs/execution_success/slice_coercion/Prover.toml create mode 100644 test_programs/execution_success/slice_coercion/src/main.nr diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index 3c5627f739b..65592407d7d 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -1662,14 +1662,19 @@ fn convert_array_expression_to_slice( let as_slice = HirExpression::Ident(HirIdent::non_trait_method(as_slice_id, location)); let func = interner.push_expr(as_slice); - let arguments = vec![expression]; + // Copy the expression and give it a new ExprId. The old one + // will be mutated in place into a Call expression. + let argument = interner.expression(&expression); + let argument = interner.push_expr(argument); + interner.push_expr_type(argument, array_type.clone()); + interner.push_expr_location(argument, location.span, location.file); + + let arguments = vec![argument]; let call = HirExpression::Call(HirCallExpression { func, arguments, location }); - let call = interner.push_expr(call); + interner.replace_expr(&expression, call); - interner.push_expr_location(call, location.span, location.file); interner.push_expr_location(func, location.span, location.file); - - interner.push_expr_type(call, target_type.clone()); + interner.push_expr_type(expression, target_type.clone()); let func_type = Type::Function(vec![array_type], Box::new(target_type), Box::new(Type::Unit)); interner.push_expr_type(func, func_type); diff --git a/test_programs/execution_success/slice_coercion/Nargo.toml b/test_programs/execution_success/slice_coercion/Nargo.toml new file mode 100644 index 00000000000..659677cc560 --- /dev/null +++ b/test_programs/execution_success/slice_coercion/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "slice_coercion" +type = "bin" +authors = [""] +compiler_version = ">=0.25.0" + +[dependencies] diff --git a/test_programs/execution_success/slice_coercion/Prover.toml b/test_programs/execution_success/slice_coercion/Prover.toml new file mode 100644 index 00000000000..09c44a855b0 --- /dev/null +++ b/test_programs/execution_success/slice_coercion/Prover.toml @@ -0,0 +1,2 @@ +first = 3 +expected = 3 diff --git a/test_programs/execution_success/slice_coercion/src/main.nr b/test_programs/execution_success/slice_coercion/src/main.nr new file mode 100644 index 00000000000..a3785e79afa --- /dev/null +++ b/test_programs/execution_success/slice_coercion/src/main.nr @@ -0,0 +1,19 @@ +struct Hasher { + fields: [Field], +} + +impl Hasher { + pub fn new() -> Self { + Self { fields: [] } + } + + pub fn add(&mut self, field: Field) { + self.fields = self.fields.push_back(field); + } +} + +fn main(expected: pub Field, first: Field) { + let mut hasher = Hasher::new(); + hasher.add(first); + assert(hasher.fields[0] == expected); +} From 166e569aaaffef4c7e3b23bf5b39f0a331f79287 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 26 Mar 2024 19:41:31 +0000 Subject: [PATCH 117/416] chore: delete `R1CSTransformer` (#4649) # Description ## Problem\* Resolves ## Summary\* The R1CS transformer is currently a no-op as the expression width is unbounded and we guarantee that expressions will be at most degree 2 by default. It also looks increasingly likely that the CSAT transformer code will be moved into the acir_gen pass so this is redundant. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/acvm/src/compiler/transformers/mod.rs | 5 +---- acvm-repo/acvm/src/compiler/transformers/r1cs.rs | 16 ---------------- 2 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 acvm-repo/acvm/src/compiler/transformers/r1cs.rs diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index 2e549854521..18f49c154f1 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -6,10 +6,8 @@ use acir::{ use indexmap::IndexMap; mod csat; -mod r1cs; pub(crate) use csat::CSatTransformer; -pub(crate) use r1cs::R1CSTransformer; use super::{transform_assert_messages, AcirTransformationMap}; @@ -43,8 +41,7 @@ pub(super) fn transform_internal( ) -> (Circuit, Vec) { let mut transformer = match &expression_width { ExpressionWidth::Unbounded => { - let transformer = R1CSTransformer::new(acir); - return (transformer.transform(), acir_opcode_positions); + return (acir, acir_opcode_positions); } ExpressionWidth::Bounded { width } => { let mut csat = CSatTransformer::new(*width); diff --git a/acvm-repo/acvm/src/compiler/transformers/r1cs.rs b/acvm-repo/acvm/src/compiler/transformers/r1cs.rs deleted file mode 100644 index 3bdd29c9c53..00000000000 --- a/acvm-repo/acvm/src/compiler/transformers/r1cs.rs +++ /dev/null @@ -1,16 +0,0 @@ -use acir::circuit::Circuit; - -/// Currently a "noop" transformer. -pub(crate) struct R1CSTransformer { - acir: Circuit, -} - -impl R1CSTransformer { - pub(crate) fn new(acir: Circuit) -> Self { - Self { acir } - } - // TODO: We could possibly make sure that all polynomials are at most degree-2 - pub(crate) fn transform(self) -> Circuit { - self.acir - } -} From 117eb89fa0256088644ba39000716e7390c7383b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:29:55 +0000 Subject: [PATCH 118/416] chore: add missing brace to workflow (#4654) # Description ## Problem\* Resolves ## Summary\* Followup to #4647 ## Additional Context The previous PR has a missing brace which invalidates the workflow. This PR adds this. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 249d83afecc..b6b88561f4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -112,7 +112,7 @@ jobs: env: # We treat any skipped or failing jobs as a failure for the workflow as a whole. - FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') } + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} steps: - name: Add warning to sticky comment From d202a086790f6dae33f9df385ab39b64ec8b54db Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:29:08 +0000 Subject: [PATCH 119/416] chore: install dependencies for building docs dependencies (#4646) # Description ## Problem\* Resolves ## Summary\* See docs builds failing on master https://github.com/noir-lang/noir/actions/runs/8436552091/job/23104382133 This PR updates the workflow to install the necessary dependencies ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Maxim Vezenov --- .github/workflows/release.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6b88561f4e..8ab5c182ef2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,6 +79,15 @@ jobs: - name: Install Yarn dependencies uses: ./.github/actions/setup + - name: Install wasm-bindgen-cli + uses: taiki-e/install-action@v2 + with: + tool: wasm-bindgen-cli@0.2.86 + + - name: Install wasm-opt + run: | + npm i wasm-opt -g + - name: Query new noir version id: noir-version run: | From b87654e2b4761dfacc916dac70d43c1b572ec636 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 27 Mar 2024 17:46:29 +0000 Subject: [PATCH 120/416] fix(ssa): Accurate constant type for slice dummy data in flattening (#4661) # Description ## Problem\* Resolves ## Summary\* We were not accurately constructing the type for slice dummy data. We were always making slice dummy data a `Field` but this could lead to errors when eventually merging this value with a different numeric type. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/opt/flatten_cfg/value_merger.rs | 4 ++-- .../regression_sha256_slice/Nargo.toml | 7 +++++++ .../regression_sha256_slice/Prover.toml | 1 + .../regression_sha256_slice/src/main.nr | 12 ++++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 test_programs/execution_success/regression_sha256_slice/Nargo.toml create mode 100644 test_programs/execution_success/regression_sha256_slice/Prover.toml create mode 100644 test_programs/execution_success/regression_sha256_slice/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index 6b923a2e42d..0a351148fa3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -234,9 +234,9 @@ impl<'a> ValueMerger<'a> { /// such as with dynamic indexing of non-homogenous slices. fn make_slice_dummy_data(&mut self, typ: &Type) -> ValueId { match typ { - Type::Numeric(_) => { + Type::Numeric(numeric_type) => { let zero = FieldElement::zero(); - self.dfg.make_constant(zero, Type::field()) + self.dfg.make_constant(zero, Type::Numeric(*numeric_type)) } Type::Array(element_types, len) => { let mut array = im::Vector::new(); diff --git a/test_programs/execution_success/regression_sha256_slice/Nargo.toml b/test_programs/execution_success/regression_sha256_slice/Nargo.toml new file mode 100644 index 00000000000..759c3b20ba8 --- /dev/null +++ b/test_programs/execution_success/regression_sha256_slice/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_sha256_slice" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/regression_sha256_slice/Prover.toml b/test_programs/execution_success/regression_sha256_slice/Prover.toml new file mode 100644 index 00000000000..8a027e9eca9 --- /dev/null +++ b/test_programs/execution_success/regression_sha256_slice/Prover.toml @@ -0,0 +1 @@ +x = ["5", "10"] diff --git a/test_programs/execution_success/regression_sha256_slice/src/main.nr b/test_programs/execution_success/regression_sha256_slice/src/main.nr new file mode 100644 index 00000000000..60b0911cf09 --- /dev/null +++ b/test_programs/execution_success/regression_sha256_slice/src/main.nr @@ -0,0 +1,12 @@ +use dep::std; + +fn main(x: [u8; 2]) { + let mut y = x.as_slice(); + let digest1 = std::hash::sha256_slice(y); + let mut v = y; + if x[0] != 0 { + v = y.push_back(x[0]); + } + let digest2 = std::hash::sha256_slice(v); + assert(digest1 != digest2); +} From 0bad1bb5ef5dc8cd4ff82e6621d0e6ef72b181fc Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 27 Mar 2024 23:05:57 +0000 Subject: [PATCH 121/416] chore: pass release PR number to sticky comment action (#4665) # Description ## Problem\* Resolves ## Summary\* See comment ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ab5c182ef2..badcd3af2dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -116,6 +116,7 @@ jobs: if: ${{ always() }} needs: + - release-please - update-acvm-workspace-package-versions - update-docs @@ -127,6 +128,8 @@ jobs: - name: Add warning to sticky comment uses: marocchino/sticky-pull-request-comment@v2 with: + # We need to specify the PR on which to make the comment as workflow is triggered by push. + number: ${{ fromJSON(needs.release-please.outputs.release-pr).number }} # delete the comment in case failures have been fixed delete: ${{ !env.FAIL }} message: "The release workflow has not completed successfully. Releasing now will result in a broken release" From 0bc18c4f78171590dd58bded959f68f53a44cc8c Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:59:41 -0400 Subject: [PATCH 122/416] feat: Sync from aztec-packages (#4657) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE chore: Dont double check num bits in brillig vm (https://github.com/AztecProtocol/aztec-packages/pull/5489) chore: unify noir macros flow (https://github.com/AztecProtocol/aztec-packages/pull/5461) feat!: Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) chore: add AvmContextInputs (https://github.com/AztecProtocol/aztec-packages/pull/5396) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant --- .aztec-sync-commit | 2 +- Cargo.lock | 1 + acvm-repo/acir/codegen/acir.cpp | 1245 ++++++++--------- .../acir/tests/test_program_serialization.rs | 46 +- acvm-repo/acvm/src/pwg/brillig.rs | 20 +- acvm-repo/acvm/tests/solver.rs | 41 +- acvm-repo/acvm_js/src/foreign_call/inputs.rs | 4 +- acvm-repo/acvm_js/src/foreign_call/outputs.rs | 8 +- .../test/shared/complex_foreign_call.ts | 14 +- acvm-repo/acvm_js/test/shared/foreign_call.ts | 8 +- acvm-repo/brillig/src/foreign_call.rs | 26 +- acvm-repo/brillig/src/lib.rs | 3 - acvm-repo/brillig/src/opcodes.rs | 15 +- acvm-repo/brillig/src/value.rs | 103 -- acvm-repo/brillig_vm/Cargo.toml | 1 + acvm-repo/brillig_vm/src/arithmetic.rs | 136 +- acvm-repo/brillig_vm/src/black_box.rs | 67 +- acvm-repo/brillig_vm/src/lib.rs | 754 +++++----- acvm-repo/brillig_vm/src/memory.rs | 170 ++- aztec_macros/src/lib.rs | 15 +- aztec_macros/src/transforms/functions.rs | 214 ++- .../brillig/brillig_gen/brillig_black_box.rs | 12 +- .../src/brillig/brillig_gen/brillig_block.rs | 10 +- .../brillig_gen/brillig_block_variables.rs | 7 +- .../brillig/brillig_gen/brillig_directive.rs | 37 +- .../src/brillig/brillig_gen/brillig_fn.rs | 16 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 238 ++-- .../noirc_evaluator/src/brillig/brillig_ir.rs | 17 +- .../brillig/brillig_ir/brillig_variable.rs | 15 +- .../src/brillig/brillig_ir/codegen_binary.rs | 4 +- .../brillig/brillig_ir/codegen_intrinsic.rs | 14 +- .../src/brillig/brillig_ir/debug_show.rs | 11 +- .../src/brillig/brillig_ir/entry_point.rs | 34 +- .../src/brillig/brillig_ir/instructions.rs | 42 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 16 +- compiler/noirc_printable_type/src/lib.rs | 16 +- noir_stdlib/src/collections/bounded_vec.nr | 9 + noir_stdlib/src/field.nr | 2 +- tooling/bb_abstraction_leaks/build.rs | 2 +- tooling/debugger/src/context.rs | 12 +- tooling/debugger/src/foreign_calls.rs | 17 +- tooling/debugger/src/repl.rs | 12 +- tooling/nargo/src/artifacts/debug_vars.rs | 12 +- tooling/nargo/src/ops/foreign_calls.rs | 43 +- .../noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 10 +- 46 files changed, 1886 insertions(+), 1617 deletions(-) delete mode 100644 acvm-repo/brillig/src/value.rs diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ead8f3147c0..ad4ef635e17 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -208abbb63af4c9a3f25d723fe1c49e82aa461061 +a18288d9b8f3057b9e79362d922da656dacf22a9 diff --git a/Cargo.lock b/Cargo.lock index caa6eb4d250..8c47f03b267 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -637,6 +637,7 @@ dependencies = [ "acvm_blackbox_solver", "num-bigint", "num-traits", + "thiserror", ] [[package]] diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index ca281d89637..d7ef849ab75 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -5,957 +5,951 @@ namespace Program { - struct BinaryFieldOp { + struct Witness { + uint32_t value; - struct Add { - friend bool operator==(const Add&, const Add&); + friend bool operator==(const Witness&, const Witness&); + std::vector bincodeSerialize() const; + static Witness bincodeDeserialize(std::vector); + }; + + struct FunctionInput { + Program::Witness witness; + uint32_t num_bits; + + friend bool operator==(const FunctionInput&, const FunctionInput&); + std::vector bincodeSerialize() const; + static FunctionInput bincodeDeserialize(std::vector); + }; + + struct BlackBoxFuncCall { + + struct AND { + Program::FunctionInput lhs; + Program::FunctionInput rhs; + Program::Witness output; + + friend bool operator==(const AND&, const AND&); std::vector bincodeSerialize() const; - static Add bincodeDeserialize(std::vector); + static AND bincodeDeserialize(std::vector); }; - struct Sub { - friend bool operator==(const Sub&, const Sub&); + struct XOR { + Program::FunctionInput lhs; + Program::FunctionInput rhs; + Program::Witness output; + + friend bool operator==(const XOR&, const XOR&); std::vector bincodeSerialize() const; - static Sub bincodeDeserialize(std::vector); + static XOR bincodeDeserialize(std::vector); }; - struct Mul { - friend bool operator==(const Mul&, const Mul&); + struct RANGE { + Program::FunctionInput input; + + friend bool operator==(const RANGE&, const RANGE&); std::vector bincodeSerialize() const; - static Mul bincodeDeserialize(std::vector); + static RANGE bincodeDeserialize(std::vector); }; - struct Div { - friend bool operator==(const Div&, const Div&); + struct SHA256 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const SHA256&, const SHA256&); std::vector bincodeSerialize() const; - static Div bincodeDeserialize(std::vector); + static SHA256 bincodeDeserialize(std::vector); }; - struct IntegerDiv { - friend bool operator==(const IntegerDiv&, const IntegerDiv&); + struct Blake2s { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Blake2s&, const Blake2s&); std::vector bincodeSerialize() const; - static IntegerDiv bincodeDeserialize(std::vector); + static Blake2s bincodeDeserialize(std::vector); }; - struct Equals { - friend bool operator==(const Equals&, const Equals&); + struct Blake3 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Blake3&, const Blake3&); std::vector bincodeSerialize() const; - static Equals bincodeDeserialize(std::vector); + static Blake3 bincodeDeserialize(std::vector); }; - struct LessThan { - friend bool operator==(const LessThan&, const LessThan&); + struct SchnorrVerify { + Program::FunctionInput public_key_x; + Program::FunctionInput public_key_y; + std::vector signature; + std::vector message; + Program::Witness output; + + friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; - static LessThan bincodeDeserialize(std::vector); + static SchnorrVerify bincodeDeserialize(std::vector); }; - struct LessThanEquals { - friend bool operator==(const LessThanEquals&, const LessThanEquals&); + struct PedersenCommitment { + std::vector inputs; + uint32_t domain_separator; + std::array outputs; + + friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; - static LessThanEquals bincodeDeserialize(std::vector); + static PedersenCommitment bincodeDeserialize(std::vector); }; - std::variant value; + struct PedersenHash { + std::vector inputs; + uint32_t domain_separator; + Program::Witness output; - friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); - std::vector bincodeSerialize() const; - static BinaryFieldOp bincodeDeserialize(std::vector); - }; + friend bool operator==(const PedersenHash&, const PedersenHash&); + std::vector bincodeSerialize() const; + static PedersenHash bincodeDeserialize(std::vector); + }; - struct BinaryIntOp { + struct EcdsaSecp256k1 { + std::vector public_key_x; + std::vector public_key_y; + std::vector signature; + std::vector hashed_message; + Program::Witness output; - struct Add { - friend bool operator==(const Add&, const Add&); + friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; - static Add bincodeDeserialize(std::vector); + static EcdsaSecp256k1 bincodeDeserialize(std::vector); }; - struct Sub { - friend bool operator==(const Sub&, const Sub&); + struct EcdsaSecp256r1 { + std::vector public_key_x; + std::vector public_key_y; + std::vector signature; + std::vector hashed_message; + Program::Witness output; + + friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; - static Sub bincodeDeserialize(std::vector); + static EcdsaSecp256r1 bincodeDeserialize(std::vector); }; - struct Mul { - friend bool operator==(const Mul&, const Mul&); + struct FixedBaseScalarMul { + Program::FunctionInput low; + Program::FunctionInput high; + std::array outputs; + + friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); std::vector bincodeSerialize() const; - static Mul bincodeDeserialize(std::vector); + static FixedBaseScalarMul bincodeDeserialize(std::vector); }; - struct Div { - friend bool operator==(const Div&, const Div&); + struct EmbeddedCurveAdd { + Program::FunctionInput input1_x; + Program::FunctionInput input1_y; + Program::FunctionInput input2_x; + Program::FunctionInput input2_y; + std::array outputs; + + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); std::vector bincodeSerialize() const; - static Div bincodeDeserialize(std::vector); + static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct Equals { - friend bool operator==(const Equals&, const Equals&); + struct Keccak256 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Keccak256&, const Keccak256&); std::vector bincodeSerialize() const; - static Equals bincodeDeserialize(std::vector); + static Keccak256 bincodeDeserialize(std::vector); }; - struct LessThan { - friend bool operator==(const LessThan&, const LessThan&); + struct Keccak256VariableLength { + std::vector inputs; + Program::FunctionInput var_message_size; + std::vector outputs; + + friend bool operator==(const Keccak256VariableLength&, const Keccak256VariableLength&); std::vector bincodeSerialize() const; - static LessThan bincodeDeserialize(std::vector); + static Keccak256VariableLength bincodeDeserialize(std::vector); }; - struct LessThanEquals { - friend bool operator==(const LessThanEquals&, const LessThanEquals&); + struct Keccakf1600 { + std::vector inputs; + std::vector outputs; + + friend bool operator==(const Keccakf1600&, const Keccakf1600&); std::vector bincodeSerialize() const; - static LessThanEquals bincodeDeserialize(std::vector); + static Keccakf1600 bincodeDeserialize(std::vector); }; - struct And { - friend bool operator==(const And&, const And&); + struct RecursiveAggregation { + std::vector verification_key; + std::vector proof; + std::vector public_inputs; + Program::FunctionInput key_hash; + + friend bool operator==(const RecursiveAggregation&, const RecursiveAggregation&); std::vector bincodeSerialize() const; - static And bincodeDeserialize(std::vector); + static RecursiveAggregation bincodeDeserialize(std::vector); }; - struct Or { - friend bool operator==(const Or&, const Or&); + struct BigIntAdd { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; - static Or bincodeDeserialize(std::vector); + static BigIntAdd bincodeDeserialize(std::vector); }; - struct Xor { - friend bool operator==(const Xor&, const Xor&); + struct BigIntSub { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntSub&, const BigIntSub&); std::vector bincodeSerialize() const; - static Xor bincodeDeserialize(std::vector); + static BigIntSub bincodeDeserialize(std::vector); }; - struct Shl { - friend bool operator==(const Shl&, const Shl&); + struct BigIntMul { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; - static Shl bincodeDeserialize(std::vector); + static BigIntMul bincodeDeserialize(std::vector); }; - struct Shr { - friend bool operator==(const Shr&, const Shr&); + struct BigIntDiv { + uint32_t lhs; + uint32_t rhs; + uint32_t output; + + friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; - static Shr bincodeDeserialize(std::vector); + static BigIntDiv bincodeDeserialize(std::vector); }; - std::variant value; + struct BigIntFromLeBytes { + std::vector inputs; + std::vector modulus; + uint32_t output; - friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); - std::vector bincodeSerialize() const; - static BinaryIntOp bincodeDeserialize(std::vector); - }; + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + std::vector bincodeSerialize() const; + static BigIntFromLeBytes bincodeDeserialize(std::vector); + }; - struct MemoryAddress { - uint64_t value; + struct BigIntToLeBytes { + uint32_t input; + std::vector outputs; - friend bool operator==(const MemoryAddress&, const MemoryAddress&); - std::vector bincodeSerialize() const; - static MemoryAddress bincodeDeserialize(std::vector); - }; + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + std::vector bincodeSerialize() const; + static BigIntToLeBytes bincodeDeserialize(std::vector); + }; - struct HeapArray { - Program::MemoryAddress pointer; - uint64_t size; + struct Poseidon2Permutation { + std::vector inputs; + std::vector outputs; + uint32_t len; - friend bool operator==(const HeapArray&, const HeapArray&); + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + std::vector bincodeSerialize() const; + static Poseidon2Permutation bincodeDeserialize(std::vector); + }; + + struct Sha256Compression { + std::vector inputs; + std::vector hash_values; + std::vector outputs; + + friend bool operator==(const Sha256Compression&, const Sha256Compression&); + std::vector bincodeSerialize() const; + static Sha256Compression bincodeDeserialize(std::vector); + }; + + std::variant value; + + friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; - static HeapArray bincodeDeserialize(std::vector); + static BlackBoxFuncCall bincodeDeserialize(std::vector); }; - struct HeapVector { - Program::MemoryAddress pointer; - Program::MemoryAddress size; + struct BlockId { + uint32_t value; - friend bool operator==(const HeapVector&, const HeapVector&); + friend bool operator==(const BlockId&, const BlockId&); std::vector bincodeSerialize() const; - static HeapVector bincodeDeserialize(std::vector); + static BlockId bincodeDeserialize(std::vector); }; - struct BlackBoxOp { + struct Expression { + std::vector> mul_terms; + std::vector> linear_combinations; + std::string q_c; - struct Sha256 { - Program::HeapVector message; - Program::HeapArray output; + friend bool operator==(const Expression&, const Expression&); + std::vector bincodeSerialize() const; + static Expression bincodeDeserialize(std::vector); + }; - friend bool operator==(const Sha256&, const Sha256&); - std::vector bincodeSerialize() const; - static Sha256 bincodeDeserialize(std::vector); - }; + struct BrilligInputs { - struct Blake2s { - Program::HeapVector message; - Program::HeapArray output; + struct Single { + Program::Expression value; - friend bool operator==(const Blake2s&, const Blake2s&); + friend bool operator==(const Single&, const Single&); std::vector bincodeSerialize() const; - static Blake2s bincodeDeserialize(std::vector); + static Single bincodeDeserialize(std::vector); }; - struct Blake3 { - Program::HeapVector message; - Program::HeapArray output; + struct Array { + std::vector value; - friend bool operator==(const Blake3&, const Blake3&); + friend bool operator==(const Array&, const Array&); std::vector bincodeSerialize() const; - static Blake3 bincodeDeserialize(std::vector); + static Array bincodeDeserialize(std::vector); }; - struct Keccak256 { - Program::HeapVector message; - Program::HeapArray output; + struct MemoryArray { + Program::BlockId value; - friend bool operator==(const Keccak256&, const Keccak256&); + friend bool operator==(const MemoryArray&, const MemoryArray&); std::vector bincodeSerialize() const; - static Keccak256 bincodeDeserialize(std::vector); + static MemoryArray bincodeDeserialize(std::vector); }; - struct Keccakf1600 { - Program::HeapVector message; - Program::HeapArray output; + std::variant value; - friend bool operator==(const Keccakf1600&, const Keccakf1600&); - std::vector bincodeSerialize() const; - static Keccakf1600 bincodeDeserialize(std::vector); - }; + friend bool operator==(const BrilligInputs&, const BrilligInputs&); + std::vector bincodeSerialize() const; + static BrilligInputs bincodeDeserialize(std::vector); + }; - struct EcdsaSecp256k1 { - Program::HeapVector hashed_msg; - Program::HeapArray public_key_x; - Program::HeapArray public_key_y; - Program::HeapArray signature; - Program::MemoryAddress result; + struct BinaryFieldOp { - friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); + struct Add { + friend bool operator==(const Add&, const Add&); std::vector bincodeSerialize() const; - static EcdsaSecp256k1 bincodeDeserialize(std::vector); + static Add bincodeDeserialize(std::vector); }; - struct EcdsaSecp256r1 { - Program::HeapVector hashed_msg; - Program::HeapArray public_key_x; - Program::HeapArray public_key_y; - Program::HeapArray signature; - Program::MemoryAddress result; - - friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); + struct Sub { + friend bool operator==(const Sub&, const Sub&); std::vector bincodeSerialize() const; - static EcdsaSecp256r1 bincodeDeserialize(std::vector); + static Sub bincodeDeserialize(std::vector); }; - struct SchnorrVerify { - Program::MemoryAddress public_key_x; - Program::MemoryAddress public_key_y; - Program::HeapVector message; - Program::HeapVector signature; - Program::MemoryAddress result; - - friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); + struct Mul { + friend bool operator==(const Mul&, const Mul&); std::vector bincodeSerialize() const; - static SchnorrVerify bincodeDeserialize(std::vector); + static Mul bincodeDeserialize(std::vector); }; - struct PedersenCommitment { - Program::HeapVector inputs; - Program::MemoryAddress domain_separator; - Program::HeapArray output; - - friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); + struct Div { + friend bool operator==(const Div&, const Div&); std::vector bincodeSerialize() const; - static PedersenCommitment bincodeDeserialize(std::vector); + static Div bincodeDeserialize(std::vector); }; - struct PedersenHash { - Program::HeapVector inputs; - Program::MemoryAddress domain_separator; - Program::MemoryAddress output; - - friend bool operator==(const PedersenHash&, const PedersenHash&); + struct IntegerDiv { + friend bool operator==(const IntegerDiv&, const IntegerDiv&); std::vector bincodeSerialize() const; - static PedersenHash bincodeDeserialize(std::vector); + static IntegerDiv bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::MemoryAddress low; - Program::MemoryAddress high; - Program::HeapArray result; - - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); + struct Equals { + friend bool operator==(const Equals&, const Equals&); std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); + static Equals bincodeDeserialize(std::vector); }; - struct EmbeddedCurveAdd { - Program::MemoryAddress input1_x; - Program::MemoryAddress input1_y; - Program::MemoryAddress input2_x; - Program::MemoryAddress input2_y; - Program::HeapArray result; - - friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + struct LessThan { + friend bool operator==(const LessThan&, const LessThan&); std::vector bincodeSerialize() const; - static EmbeddedCurveAdd bincodeDeserialize(std::vector); + static LessThan bincodeDeserialize(std::vector); }; - struct BigIntAdd { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; - - friend bool operator==(const BigIntAdd&, const BigIntAdd&); + struct LessThanEquals { + friend bool operator==(const LessThanEquals&, const LessThanEquals&); std::vector bincodeSerialize() const; - static BigIntAdd bincodeDeserialize(std::vector); + static LessThanEquals bincodeDeserialize(std::vector); }; - struct BigIntSub { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; + std::variant value; - friend bool operator==(const BigIntSub&, const BigIntSub&); - std::vector bincodeSerialize() const; - static BigIntSub bincodeDeserialize(std::vector); - }; + friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); + std::vector bincodeSerialize() const; + static BinaryFieldOp bincodeDeserialize(std::vector); + }; - struct BigIntMul { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; + struct BinaryIntOp { - friend bool operator==(const BigIntMul&, const BigIntMul&); + struct Add { + friend bool operator==(const Add&, const Add&); std::vector bincodeSerialize() const; - static BigIntMul bincodeDeserialize(std::vector); + static Add bincodeDeserialize(std::vector); }; - struct BigIntDiv { - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; - Program::MemoryAddress output; - - friend bool operator==(const BigIntDiv&, const BigIntDiv&); + struct Sub { + friend bool operator==(const Sub&, const Sub&); std::vector bincodeSerialize() const; - static BigIntDiv bincodeDeserialize(std::vector); + static Sub bincodeDeserialize(std::vector); }; - struct BigIntFromLeBytes { - Program::HeapVector inputs; - Program::HeapVector modulus; - Program::MemoryAddress output; - - friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + struct Mul { + friend bool operator==(const Mul&, const Mul&); std::vector bincodeSerialize() const; - static BigIntFromLeBytes bincodeDeserialize(std::vector); + static Mul bincodeDeserialize(std::vector); }; - struct BigIntToLeBytes { - Program::MemoryAddress input; - Program::HeapVector output; - - friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + struct Div { + friend bool operator==(const Div&, const Div&); std::vector bincodeSerialize() const; - static BigIntToLeBytes bincodeDeserialize(std::vector); + static Div bincodeDeserialize(std::vector); }; - struct Poseidon2Permutation { - Program::HeapVector message; - Program::HeapArray output; - Program::MemoryAddress len; - - friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + struct Equals { + friend bool operator==(const Equals&, const Equals&); std::vector bincodeSerialize() const; - static Poseidon2Permutation bincodeDeserialize(std::vector); + static Equals bincodeDeserialize(std::vector); }; - struct Sha256Compression { - Program::HeapVector input; - Program::HeapVector hash_values; - Program::HeapArray output; - - friend bool operator==(const Sha256Compression&, const Sha256Compression&); + struct LessThan { + friend bool operator==(const LessThan&, const LessThan&); std::vector bincodeSerialize() const; - static Sha256Compression bincodeDeserialize(std::vector); + static LessThan bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); - std::vector bincodeSerialize() const; - static BlackBoxOp bincodeDeserialize(std::vector); - }; - - struct HeapValueType; - - struct HeapValueType { + struct LessThanEquals { + friend bool operator==(const LessThanEquals&, const LessThanEquals&); + std::vector bincodeSerialize() const; + static LessThanEquals bincodeDeserialize(std::vector); + }; - struct Simple { - friend bool operator==(const Simple&, const Simple&); + struct And { + friend bool operator==(const And&, const And&); std::vector bincodeSerialize() const; - static Simple bincodeDeserialize(std::vector); + static And bincodeDeserialize(std::vector); }; - struct Array { - std::vector value_types; - uint64_t size; + struct Or { + friend bool operator==(const Or&, const Or&); + std::vector bincodeSerialize() const; + static Or bincodeDeserialize(std::vector); + }; - friend bool operator==(const Array&, const Array&); + struct Xor { + friend bool operator==(const Xor&, const Xor&); std::vector bincodeSerialize() const; - static Array bincodeDeserialize(std::vector); + static Xor bincodeDeserialize(std::vector); }; - struct Vector { - std::vector value_types; + struct Shl { + friend bool operator==(const Shl&, const Shl&); + std::vector bincodeSerialize() const; + static Shl bincodeDeserialize(std::vector); + }; - friend bool operator==(const Vector&, const Vector&); + struct Shr { + friend bool operator==(const Shr&, const Shr&); std::vector bincodeSerialize() const; - static Vector bincodeDeserialize(std::vector); + static Shr bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; - friend bool operator==(const HeapValueType&, const HeapValueType&); + friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; - static HeapValueType bincodeDeserialize(std::vector); + static BinaryIntOp bincodeDeserialize(std::vector); }; - struct Value { - std::string inner; + struct MemoryAddress { + uint64_t value; - friend bool operator==(const Value&, const Value&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static Value bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; - struct ValueOrArray { + struct HeapArray { + Program::MemoryAddress pointer; + uint64_t size; - struct MemoryAddress { - Program::MemoryAddress value; + friend bool operator==(const HeapArray&, const HeapArray&); + std::vector bincodeSerialize() const; + static HeapArray bincodeDeserialize(std::vector); + }; - friend bool operator==(const MemoryAddress&, const MemoryAddress&); - std::vector bincodeSerialize() const; - static MemoryAddress bincodeDeserialize(std::vector); - }; + struct HeapVector { + Program::MemoryAddress pointer; + Program::MemoryAddress size; - struct HeapArray { - Program::HeapArray value; + friend bool operator==(const HeapVector&, const HeapVector&); + std::vector bincodeSerialize() const; + static HeapVector bincodeDeserialize(std::vector); + }; - friend bool operator==(const HeapArray&, const HeapArray&); - std::vector bincodeSerialize() const; - static HeapArray bincodeDeserialize(std::vector); - }; + struct BlackBoxOp { - struct HeapVector { - Program::HeapVector value; + struct Sha256 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const HeapVector&, const HeapVector&); + friend bool operator==(const Sha256&, const Sha256&); std::vector bincodeSerialize() const; - static HeapVector bincodeDeserialize(std::vector); + static Sha256 bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const ValueOrArray&, const ValueOrArray&); - std::vector bincodeSerialize() const; - static ValueOrArray bincodeDeserialize(std::vector); - }; - - struct BrilligOpcode { - - struct BinaryFieldOp { - Program::MemoryAddress destination; - Program::BinaryFieldOp op; - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; + struct Blake2s { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); + friend bool operator==(const Blake2s&, const Blake2s&); std::vector bincodeSerialize() const; - static BinaryFieldOp bincodeDeserialize(std::vector); + static Blake2s bincodeDeserialize(std::vector); }; - struct BinaryIntOp { - Program::MemoryAddress destination; - Program::BinaryIntOp op; - uint32_t bit_size; - Program::MemoryAddress lhs; - Program::MemoryAddress rhs; + struct Blake3 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); + friend bool operator==(const Blake3&, const Blake3&); std::vector bincodeSerialize() const; - static BinaryIntOp bincodeDeserialize(std::vector); + static Blake3 bincodeDeserialize(std::vector); }; - struct Cast { - Program::MemoryAddress destination; - Program::MemoryAddress source; - uint32_t bit_size; + struct Keccak256 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const Cast&, const Cast&); + friend bool operator==(const Keccak256&, const Keccak256&); std::vector bincodeSerialize() const; - static Cast bincodeDeserialize(std::vector); + static Keccak256 bincodeDeserialize(std::vector); }; - struct JumpIfNot { - Program::MemoryAddress condition; - uint64_t location; + struct Keccakf1600 { + Program::HeapVector message; + Program::HeapArray output; - friend bool operator==(const JumpIfNot&, const JumpIfNot&); + friend bool operator==(const Keccakf1600&, const Keccakf1600&); std::vector bincodeSerialize() const; - static JumpIfNot bincodeDeserialize(std::vector); + static Keccakf1600 bincodeDeserialize(std::vector); }; - struct JumpIf { - Program::MemoryAddress condition; - uint64_t location; + struct EcdsaSecp256k1 { + Program::HeapVector hashed_msg; + Program::HeapArray public_key_x; + Program::HeapArray public_key_y; + Program::HeapArray signature; + Program::MemoryAddress result; - friend bool operator==(const JumpIf&, const JumpIf&); + friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); std::vector bincodeSerialize() const; - static JumpIf bincodeDeserialize(std::vector); + static EcdsaSecp256k1 bincodeDeserialize(std::vector); }; - struct Jump { - uint64_t location; + struct EcdsaSecp256r1 { + Program::HeapVector hashed_msg; + Program::HeapArray public_key_x; + Program::HeapArray public_key_y; + Program::HeapArray signature; + Program::MemoryAddress result; - friend bool operator==(const Jump&, const Jump&); + friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); std::vector bincodeSerialize() const; - static Jump bincodeDeserialize(std::vector); + static EcdsaSecp256r1 bincodeDeserialize(std::vector); }; - struct CalldataCopy { - Program::MemoryAddress destination_address; - uint64_t size; - uint64_t offset; + struct SchnorrVerify { + Program::MemoryAddress public_key_x; + Program::MemoryAddress public_key_y; + Program::HeapVector message; + Program::HeapVector signature; + Program::MemoryAddress result; - friend bool operator==(const CalldataCopy&, const CalldataCopy&); + friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); std::vector bincodeSerialize() const; - static CalldataCopy bincodeDeserialize(std::vector); + static SchnorrVerify bincodeDeserialize(std::vector); }; - struct Call { - uint64_t location; + struct PedersenCommitment { + Program::HeapVector inputs; + Program::MemoryAddress domain_separator; + Program::HeapArray output; - friend bool operator==(const Call&, const Call&); + friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); std::vector bincodeSerialize() const; - static Call bincodeDeserialize(std::vector); + static PedersenCommitment bincodeDeserialize(std::vector); }; - struct Const { - Program::MemoryAddress destination; - uint32_t bit_size; - Program::Value value; + struct PedersenHash { + Program::HeapVector inputs; + Program::MemoryAddress domain_separator; + Program::MemoryAddress output; - friend bool operator==(const Const&, const Const&); + friend bool operator==(const PedersenHash&, const PedersenHash&); std::vector bincodeSerialize() const; - static Const bincodeDeserialize(std::vector); + static PedersenHash bincodeDeserialize(std::vector); }; - struct Return { - friend bool operator==(const Return&, const Return&); + struct FixedBaseScalarMul { + Program::MemoryAddress low; + Program::MemoryAddress high; + Program::HeapArray result; + + friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); std::vector bincodeSerialize() const; - static Return bincodeDeserialize(std::vector); + static FixedBaseScalarMul bincodeDeserialize(std::vector); }; - struct ForeignCall { - std::string function; - std::vector destinations; - std::vector destination_value_types; - std::vector inputs; - std::vector input_value_types; + struct EmbeddedCurveAdd { + Program::MemoryAddress input1_x; + Program::MemoryAddress input1_y; + Program::MemoryAddress input2_x; + Program::MemoryAddress input2_y; + Program::HeapArray result; - friend bool operator==(const ForeignCall&, const ForeignCall&); + friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); std::vector bincodeSerialize() const; - static ForeignCall bincodeDeserialize(std::vector); + static EmbeddedCurveAdd bincodeDeserialize(std::vector); }; - struct Mov { - Program::MemoryAddress destination; - Program::MemoryAddress source; + struct BigIntAdd { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const Mov&, const Mov&); + friend bool operator==(const BigIntAdd&, const BigIntAdd&); std::vector bincodeSerialize() const; - static Mov bincodeDeserialize(std::vector); + static BigIntAdd bincodeDeserialize(std::vector); }; - struct ConditionalMov { - Program::MemoryAddress destination; - Program::MemoryAddress source_a; - Program::MemoryAddress source_b; - Program::MemoryAddress condition; + struct BigIntSub { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const ConditionalMov&, const ConditionalMov&); + friend bool operator==(const BigIntSub&, const BigIntSub&); std::vector bincodeSerialize() const; - static ConditionalMov bincodeDeserialize(std::vector); + static BigIntSub bincodeDeserialize(std::vector); }; - struct Load { - Program::MemoryAddress destination; - Program::MemoryAddress source_pointer; + struct BigIntMul { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const Load&, const Load&); + friend bool operator==(const BigIntMul&, const BigIntMul&); std::vector bincodeSerialize() const; - static Load bincodeDeserialize(std::vector); + static BigIntMul bincodeDeserialize(std::vector); }; - struct Store { - Program::MemoryAddress destination_pointer; - Program::MemoryAddress source; + struct BigIntDiv { + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; + Program::MemoryAddress output; - friend bool operator==(const Store&, const Store&); + friend bool operator==(const BigIntDiv&, const BigIntDiv&); std::vector bincodeSerialize() const; - static Store bincodeDeserialize(std::vector); + static BigIntDiv bincodeDeserialize(std::vector); }; - struct BlackBox { - Program::BlackBoxOp value; + struct BigIntFromLeBytes { + Program::HeapVector inputs; + Program::HeapVector modulus; + Program::MemoryAddress output; - friend bool operator==(const BlackBox&, const BlackBox&); + friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); std::vector bincodeSerialize() const; - static BlackBox bincodeDeserialize(std::vector); + static BigIntFromLeBytes bincodeDeserialize(std::vector); }; - struct Trap { - friend bool operator==(const Trap&, const Trap&); + struct BigIntToLeBytes { + Program::MemoryAddress input; + Program::HeapVector output; + + friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); std::vector bincodeSerialize() const; - static Trap bincodeDeserialize(std::vector); + static BigIntToLeBytes bincodeDeserialize(std::vector); }; - struct Stop { - uint64_t return_data_offset; - uint64_t return_data_size; + struct Poseidon2Permutation { + Program::HeapVector message; + Program::HeapArray output; + Program::MemoryAddress len; - friend bool operator==(const Stop&, const Stop&); + friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); std::vector bincodeSerialize() const; - static Stop bincodeDeserialize(std::vector); + static Poseidon2Permutation bincodeDeserialize(std::vector); }; - std::variant value; + struct Sha256Compression { + Program::HeapVector input; + Program::HeapVector hash_values; + Program::HeapArray output; - friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); - std::vector bincodeSerialize() const; - static BrilligOpcode bincodeDeserialize(std::vector); - }; + friend bool operator==(const Sha256Compression&, const Sha256Compression&); + std::vector bincodeSerialize() const; + static Sha256Compression bincodeDeserialize(std::vector); + }; - struct Witness { - uint32_t value; + std::variant value; - friend bool operator==(const Witness&, const Witness&); + friend bool operator==(const BlackBoxOp&, const BlackBoxOp&); std::vector bincodeSerialize() const; - static Witness bincodeDeserialize(std::vector); + static BlackBoxOp bincodeDeserialize(std::vector); }; - struct FunctionInput { - Program::Witness witness; - uint32_t num_bits; - - friend bool operator==(const FunctionInput&, const FunctionInput&); - std::vector bincodeSerialize() const; - static FunctionInput bincodeDeserialize(std::vector); - }; + struct HeapValueType; - struct BlackBoxFuncCall { + struct HeapValueType { - struct AND { - Program::FunctionInput lhs; - Program::FunctionInput rhs; - Program::Witness output; + struct Simple { + uint32_t value; - friend bool operator==(const AND&, const AND&); + friend bool operator==(const Simple&, const Simple&); std::vector bincodeSerialize() const; - static AND bincodeDeserialize(std::vector); + static Simple bincodeDeserialize(std::vector); }; - struct XOR { - Program::FunctionInput lhs; - Program::FunctionInput rhs; - Program::Witness output; + struct Array { + std::vector value_types; + uint64_t size; - friend bool operator==(const XOR&, const XOR&); + friend bool operator==(const Array&, const Array&); std::vector bincodeSerialize() const; - static XOR bincodeDeserialize(std::vector); + static Array bincodeDeserialize(std::vector); }; - struct RANGE { - Program::FunctionInput input; + struct Vector { + std::vector value_types; - friend bool operator==(const RANGE&, const RANGE&); + friend bool operator==(const Vector&, const Vector&); std::vector bincodeSerialize() const; - static RANGE bincodeDeserialize(std::vector); + static Vector bincodeDeserialize(std::vector); }; - struct SHA256 { - std::vector inputs; - std::vector outputs; - - friend bool operator==(const SHA256&, const SHA256&); - std::vector bincodeSerialize() const; - static SHA256 bincodeDeserialize(std::vector); - }; + std::variant value; - struct Blake2s { - std::vector inputs; - std::vector outputs; + friend bool operator==(const HeapValueType&, const HeapValueType&); + std::vector bincodeSerialize() const; + static HeapValueType bincodeDeserialize(std::vector); + }; - friend bool operator==(const Blake2s&, const Blake2s&); - std::vector bincodeSerialize() const; - static Blake2s bincodeDeserialize(std::vector); - }; + struct ValueOrArray { - struct Blake3 { - std::vector inputs; - std::vector outputs; + struct MemoryAddress { + Program::MemoryAddress value; - friend bool operator==(const Blake3&, const Blake3&); + friend bool operator==(const MemoryAddress&, const MemoryAddress&); std::vector bincodeSerialize() const; - static Blake3 bincodeDeserialize(std::vector); + static MemoryAddress bincodeDeserialize(std::vector); }; - struct SchnorrVerify { - Program::FunctionInput public_key_x; - Program::FunctionInput public_key_y; - std::vector signature; - std::vector message; - Program::Witness output; + struct HeapArray { + Program::HeapArray value; - friend bool operator==(const SchnorrVerify&, const SchnorrVerify&); + friend bool operator==(const HeapArray&, const HeapArray&); std::vector bincodeSerialize() const; - static SchnorrVerify bincodeDeserialize(std::vector); + static HeapArray bincodeDeserialize(std::vector); }; - struct PedersenCommitment { - std::vector inputs; - uint32_t domain_separator; - std::array outputs; + struct HeapVector { + Program::HeapVector value; - friend bool operator==(const PedersenCommitment&, const PedersenCommitment&); + friend bool operator==(const HeapVector&, const HeapVector&); std::vector bincodeSerialize() const; - static PedersenCommitment bincodeDeserialize(std::vector); + static HeapVector bincodeDeserialize(std::vector); }; - struct PedersenHash { - std::vector inputs; - uint32_t domain_separator; - Program::Witness output; - - friend bool operator==(const PedersenHash&, const PedersenHash&); - std::vector bincodeSerialize() const; - static PedersenHash bincodeDeserialize(std::vector); - }; + std::variant value; - struct EcdsaSecp256k1 { - std::vector public_key_x; - std::vector public_key_y; - std::vector signature; - std::vector hashed_message; - Program::Witness output; + friend bool operator==(const ValueOrArray&, const ValueOrArray&); + std::vector bincodeSerialize() const; + static ValueOrArray bincodeDeserialize(std::vector); + }; - friend bool operator==(const EcdsaSecp256k1&, const EcdsaSecp256k1&); - std::vector bincodeSerialize() const; - static EcdsaSecp256k1 bincodeDeserialize(std::vector); - }; + struct BrilligOpcode { - struct EcdsaSecp256r1 { - std::vector public_key_x; - std::vector public_key_y; - std::vector signature; - std::vector hashed_message; - Program::Witness output; + struct BinaryFieldOp { + Program::MemoryAddress destination; + Program::BinaryFieldOp op; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; - friend bool operator==(const EcdsaSecp256r1&, const EcdsaSecp256r1&); + friend bool operator==(const BinaryFieldOp&, const BinaryFieldOp&); std::vector bincodeSerialize() const; - static EcdsaSecp256r1 bincodeDeserialize(std::vector); + static BinaryFieldOp bincodeDeserialize(std::vector); }; - struct FixedBaseScalarMul { - Program::FunctionInput low; - Program::FunctionInput high; - std::array outputs; + struct BinaryIntOp { + Program::MemoryAddress destination; + Program::BinaryIntOp op; + uint32_t bit_size; + Program::MemoryAddress lhs; + Program::MemoryAddress rhs; - friend bool operator==(const FixedBaseScalarMul&, const FixedBaseScalarMul&); + friend bool operator==(const BinaryIntOp&, const BinaryIntOp&); std::vector bincodeSerialize() const; - static FixedBaseScalarMul bincodeDeserialize(std::vector); + static BinaryIntOp bincodeDeserialize(std::vector); }; - struct EmbeddedCurveAdd { - Program::FunctionInput input1_x; - Program::FunctionInput input1_y; - Program::FunctionInput input2_x; - Program::FunctionInput input2_y; - std::array outputs; + struct Cast { + Program::MemoryAddress destination; + Program::MemoryAddress source; + uint32_t bit_size; - friend bool operator==(const EmbeddedCurveAdd&, const EmbeddedCurveAdd&); + friend bool operator==(const Cast&, const Cast&); std::vector bincodeSerialize() const; - static EmbeddedCurveAdd bincodeDeserialize(std::vector); + static Cast bincodeDeserialize(std::vector); }; - struct Keccak256 { - std::vector inputs; - std::vector outputs; + struct JumpIfNot { + Program::MemoryAddress condition; + uint64_t location; - friend bool operator==(const Keccak256&, const Keccak256&); + friend bool operator==(const JumpIfNot&, const JumpIfNot&); std::vector bincodeSerialize() const; - static Keccak256 bincodeDeserialize(std::vector); + static JumpIfNot bincodeDeserialize(std::vector); }; - struct Keccak256VariableLength { - std::vector inputs; - Program::FunctionInput var_message_size; - std::vector outputs; + struct JumpIf { + Program::MemoryAddress condition; + uint64_t location; - friend bool operator==(const Keccak256VariableLength&, const Keccak256VariableLength&); + friend bool operator==(const JumpIf&, const JumpIf&); std::vector bincodeSerialize() const; - static Keccak256VariableLength bincodeDeserialize(std::vector); + static JumpIf bincodeDeserialize(std::vector); }; - struct Keccakf1600 { - std::vector inputs; - std::vector outputs; + struct Jump { + uint64_t location; - friend bool operator==(const Keccakf1600&, const Keccakf1600&); + friend bool operator==(const Jump&, const Jump&); std::vector bincodeSerialize() const; - static Keccakf1600 bincodeDeserialize(std::vector); + static Jump bincodeDeserialize(std::vector); }; - struct RecursiveAggregation { - std::vector verification_key; - std::vector proof; - std::vector public_inputs; - Program::FunctionInput key_hash; + struct CalldataCopy { + Program::MemoryAddress destination_address; + uint64_t size; + uint64_t offset; - friend bool operator==(const RecursiveAggregation&, const RecursiveAggregation&); + friend bool operator==(const CalldataCopy&, const CalldataCopy&); std::vector bincodeSerialize() const; - static RecursiveAggregation bincodeDeserialize(std::vector); + static CalldataCopy bincodeDeserialize(std::vector); }; - struct BigIntAdd { - uint32_t lhs; - uint32_t rhs; - uint32_t output; + struct Call { + uint64_t location; - friend bool operator==(const BigIntAdd&, const BigIntAdd&); + friend bool operator==(const Call&, const Call&); std::vector bincodeSerialize() const; - static BigIntAdd bincodeDeserialize(std::vector); + static Call bincodeDeserialize(std::vector); }; - struct BigIntSub { - uint32_t lhs; - uint32_t rhs; - uint32_t output; + struct Const { + Program::MemoryAddress destination; + uint32_t bit_size; + std::string value; - friend bool operator==(const BigIntSub&, const BigIntSub&); + friend bool operator==(const Const&, const Const&); std::vector bincodeSerialize() const; - static BigIntSub bincodeDeserialize(std::vector); + static Const bincodeDeserialize(std::vector); }; - struct BigIntMul { - uint32_t lhs; - uint32_t rhs; - uint32_t output; - - friend bool operator==(const BigIntMul&, const BigIntMul&); + struct Return { + friend bool operator==(const Return&, const Return&); std::vector bincodeSerialize() const; - static BigIntMul bincodeDeserialize(std::vector); + static Return bincodeDeserialize(std::vector); }; - struct BigIntDiv { - uint32_t lhs; - uint32_t rhs; - uint32_t output; + struct ForeignCall { + std::string function; + std::vector destinations; + std::vector destination_value_types; + std::vector inputs; + std::vector input_value_types; - friend bool operator==(const BigIntDiv&, const BigIntDiv&); + friend bool operator==(const ForeignCall&, const ForeignCall&); std::vector bincodeSerialize() const; - static BigIntDiv bincodeDeserialize(std::vector); + static ForeignCall bincodeDeserialize(std::vector); }; - struct BigIntFromLeBytes { - std::vector inputs; - std::vector modulus; - uint32_t output; + struct Mov { + Program::MemoryAddress destination; + Program::MemoryAddress source; - friend bool operator==(const BigIntFromLeBytes&, const BigIntFromLeBytes&); + friend bool operator==(const Mov&, const Mov&); std::vector bincodeSerialize() const; - static BigIntFromLeBytes bincodeDeserialize(std::vector); + static Mov bincodeDeserialize(std::vector); }; - struct BigIntToLeBytes { - uint32_t input; - std::vector outputs; + struct ConditionalMov { + Program::MemoryAddress destination; + Program::MemoryAddress source_a; + Program::MemoryAddress source_b; + Program::MemoryAddress condition; - friend bool operator==(const BigIntToLeBytes&, const BigIntToLeBytes&); + friend bool operator==(const ConditionalMov&, const ConditionalMov&); std::vector bincodeSerialize() const; - static BigIntToLeBytes bincodeDeserialize(std::vector); + static ConditionalMov bincodeDeserialize(std::vector); }; - struct Poseidon2Permutation { - std::vector inputs; - std::vector outputs; - uint32_t len; + struct Load { + Program::MemoryAddress destination; + Program::MemoryAddress source_pointer; - friend bool operator==(const Poseidon2Permutation&, const Poseidon2Permutation&); + friend bool operator==(const Load&, const Load&); std::vector bincodeSerialize() const; - static Poseidon2Permutation bincodeDeserialize(std::vector); + static Load bincodeDeserialize(std::vector); }; - struct Sha256Compression { - std::vector inputs; - std::vector hash_values; - std::vector outputs; + struct Store { + Program::MemoryAddress destination_pointer; + Program::MemoryAddress source; - friend bool operator==(const Sha256Compression&, const Sha256Compression&); + friend bool operator==(const Store&, const Store&); std::vector bincodeSerialize() const; - static Sha256Compression bincodeDeserialize(std::vector); + static Store bincodeDeserialize(std::vector); }; - std::variant value; - - friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); - std::vector bincodeSerialize() const; - static BlackBoxFuncCall bincodeDeserialize(std::vector); - }; - - struct BlockId { - uint32_t value; - - friend bool operator==(const BlockId&, const BlockId&); - std::vector bincodeSerialize() const; - static BlockId bincodeDeserialize(std::vector); - }; - - struct Expression { - std::vector> mul_terms; - std::vector> linear_combinations; - std::string q_c; - - friend bool operator==(const Expression&, const Expression&); - std::vector bincodeSerialize() const; - static Expression bincodeDeserialize(std::vector); - }; - - struct BrilligInputs { - - struct Single { - Program::Expression value; + struct BlackBox { + Program::BlackBoxOp value; - friend bool operator==(const Single&, const Single&); + friend bool operator==(const BlackBox&, const BlackBox&); std::vector bincodeSerialize() const; - static Single bincodeDeserialize(std::vector); + static BlackBox bincodeDeserialize(std::vector); }; - struct Array { - std::vector value; - - friend bool operator==(const Array&, const Array&); + struct Trap { + friend bool operator==(const Trap&, const Trap&); std::vector bincodeSerialize() const; - static Array bincodeDeserialize(std::vector); + static Trap bincodeDeserialize(std::vector); }; - struct MemoryArray { - Program::BlockId value; + struct Stop { + uint64_t return_data_offset; + uint64_t return_data_size; - friend bool operator==(const MemoryArray&, const MemoryArray&); + friend bool operator==(const Stop&, const Stop&); std::vector bincodeSerialize() const; - static MemoryArray bincodeDeserialize(std::vector); + static Stop bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; - friend bool operator==(const BrilligInputs&, const BrilligInputs&); + friend bool operator==(const BrilligOpcode&, const BrilligOpcode&); std::vector bincodeSerialize() const; - static BrilligInputs bincodeDeserialize(std::vector); + static BrilligOpcode bincodeDeserialize(std::vector); }; struct BrilligOutputs { @@ -5645,6 +5639,7 @@ Program::HeapValueType serde::Deserializable::deserializ namespace Program { inline bool operator==(const HeapValueType::Simple &lhs, const HeapValueType::Simple &rhs) { + if (!(lhs.value == rhs.value)) { return false; } return true; } @@ -5668,12 +5663,14 @@ namespace Program { template <> template void serde::Serializable::serialize(const Program::HeapValueType::Simple &obj, Serializer &serializer) { + serde::Serializable::serialize(obj.value, serializer); } template <> template Program::HeapValueType::Simple serde::Deserializable::deserialize(Deserializer &deserializer) { Program::HeapValueType::Simple obj; + obj.value = serde::Deserializable::deserialize(deserializer); return obj; } @@ -6419,48 +6416,6 @@ Program::PublicInputs serde::Deserializable::deserialize( return obj; } -namespace Program { - - inline bool operator==(const Value &lhs, const Value &rhs) { - if (!(lhs.inner == rhs.inner)) { return false; } - return true; - } - - inline std::vector Value::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline Value Value::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::Value &obj, Serializer &serializer) { - serializer.increase_container_depth(); - serde::Serializable::serialize(obj.inner, serializer); - serializer.decrease_container_depth(); -} - -template <> -template -Program::Value serde::Deserializable::deserialize(Deserializer &deserializer) { - deserializer.increase_container_depth(); - Program::Value obj; - obj.inner = serde::Deserializable::deserialize(deserializer); - deserializer.decrease_container_depth(); - return obj; -} - namespace Program { inline bool operator==(const ValueOrArray &lhs, const ValueOrArray &rhs) { diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 64385a37582..8b04292dfaa 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -190,9 +190,9 @@ fn simple_brillig_foreign_call() { brillig::Opcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, brillig::Opcode::Stop { return_data_offset: 0, return_data_size: 1 }, ], @@ -211,11 +211,11 @@ fn simple_brillig_foreign_call() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 65, 10, 192, 32, 12, 4, 77, 10, 165, 244, 214, - 159, 216, 31, 244, 51, 61, 120, 241, 32, 226, 251, 85, 140, 176, 136, 122, 209, 129, 144, - 176, 9, 97, 151, 84, 225, 74, 69, 50, 31, 48, 35, 85, 251, 164, 235, 53, 94, 218, 247, 75, - 163, 95, 150, 12, 153, 179, 227, 191, 114, 195, 222, 216, 240, 59, 63, 75, 221, 251, 208, - 106, 207, 232, 150, 65, 100, 53, 33, 2, 22, 232, 178, 27, 144, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 61, 10, 192, 48, 8, 133, 53, 133, 82, 186, + 245, 38, 233, 13, 122, 153, 14, 93, 58, 132, 144, 227, 135, 252, 41, 56, 36, 46, 201, 7, + 162, 168, 200, 123, 34, 52, 142, 28, 72, 245, 38, 106, 9, 247, 30, 202, 118, 142, 27, 215, + 221, 178, 82, 175, 33, 15, 133, 189, 163, 159, 57, 197, 252, 251, 195, 235, 188, 230, 186, + 16, 65, 255, 12, 239, 92, 131, 89, 149, 198, 77, 3, 10, 9, 119, 8, 198, 242, 152, 1, 0, 0, ]; assert_eq!(bytes, expected_serialization) @@ -264,8 +264,8 @@ fn complex_brillig_foreign_call() { }, brillig::Opcode::Const { destination: MemoryAddress(0), - value: brillig::Value::from(32_usize), - bit_size: 32, + value: FieldElement::from(32_usize), + bit_size: 64, }, brillig::Opcode::CalldataCopy { destination_address: MemoryAddress(1), @@ -280,8 +280,8 @@ fn complex_brillig_foreign_call() { ValueOrArray::MemoryAddress(MemoryAddress::from(1)), ], input_value_types: vec![ - HeapValueType::Array { size: 3, value_types: vec![HeapValueType::Simple] }, - HeapValueType::Simple, + HeapValueType::Array { size: 3, value_types: vec![HeapValueType::field()] }, + HeapValueType::field(), ], destinations: vec![ ValueOrArray::HeapArray(HeapArray { pointer: 0.into(), size: 3 }), @@ -289,9 +289,9 @@ fn complex_brillig_foreign_call() { ValueOrArray::MemoryAddress(MemoryAddress::from(36)), ], destination_value_types: vec![ - HeapValueType::Array { size: 3, value_types: vec![HeapValueType::Simple] }, - HeapValueType::Simple, - HeapValueType::Simple, + HeapValueType::Array { size: 3, value_types: vec![HeapValueType::field()] }, + HeapValueType::field(), + HeapValueType::field(), ], }, brillig::Opcode::Stop { return_data_offset: 32, return_data_size: 5 }, @@ -311,15 +311,15 @@ fn complex_brillig_foreign_call() { let bytes = Program::serialize_program(&program); let expected_serialization: Vec = vec![ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 65, 14, 132, 32, 12, 108, 101, 117, 205, 222, - 246, 7, 38, 187, 15, 96, 247, 5, 254, 197, 120, 211, 232, 209, 231, 139, 113, 136, 181, 65, - 47, 98, 162, 147, 52, 20, 24, 202, 164, 45, 48, 205, 200, 157, 49, 124, 227, 44, 129, 207, - 152, 75, 120, 94, 137, 209, 30, 195, 143, 227, 197, 178, 103, 105, 76, 110, 160, 209, 156, - 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 97, - 254, 196, 152, 99, 157, 176, 87, 168, 188, 147, 224, 121, 20, 209, 180, 254, 109, 70, 75, - 47, 178, 186, 251, 37, 116, 86, 93, 219, 55, 245, 96, 20, 85, 75, 253, 8, 255, 171, 246, - 121, 231, 220, 4, 249, 237, 132, 56, 28, 224, 109, 113, 223, 180, 164, 50, 165, 0, 137, 17, - 72, 139, 88, 97, 4, 173, 98, 132, 157, 33, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 93, 10, 131, 48, 12, 78, 218, 233, 100, 111, + 187, 193, 96, 59, 64, 231, 9, 188, 139, 248, 166, 232, 163, 167, 23, 11, 126, 197, 24, 250, + 34, 86, 208, 64, 72, 218, 252, 125, 36, 105, 153, 22, 42, 60, 51, 116, 235, 217, 64, 103, + 156, 37, 5, 191, 10, 210, 29, 163, 63, 167, 203, 229, 206, 194, 104, 110, 128, 209, 158, + 128, 49, 236, 195, 69, 231, 157, 114, 46, 73, 251, 103, 35, 239, 231, 225, 57, 243, 156, + 227, 252, 132, 44, 112, 79, 176, 125, 84, 223, 73, 248, 145, 152, 69, 149, 4, 107, 233, + 114, 90, 119, 145, 85, 237, 151, 192, 89, 247, 221, 208, 54, 163, 85, 174, 26, 234, 87, + 232, 63, 101, 103, 21, 55, 169, 216, 73, 72, 249, 5, 197, 234, 132, 123, 179, 35, 247, 155, + 214, 246, 102, 20, 73, 204, 72, 168, 123, 191, 161, 25, 66, 136, 159, 187, 53, 5, 0, 0, ]; assert_eq!(bytes, expected_serialization) diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index 51c7f4c6203..bcf736cd926 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use acir::{ - brillig::{ForeignCallParam, ForeignCallResult, Value}, + brillig::{ForeignCallParam, ForeignCallResult}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::BlockId, @@ -11,7 +11,7 @@ use acir::{ FieldElement, }; use acvm_blackbox_solver::BlackBoxFunctionSolver; -use brillig_vm::{VMStatus, VM}; +use brillig_vm::{MemoryValue, VMStatus, VM}; use crate::{pwg::OpcodeNotSolvable, OpcodeResolutionError}; @@ -73,7 +73,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { acir_index: usize, ) -> Result { // Set input values - let mut calldata: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents an expression or array of expressions to evaluate. // Iterate over each input and evaluate the expression(s) associated with it. // Push the results into memory. @@ -81,7 +81,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { for input in &brillig.inputs { match input { BrilligInputs::Single(expr) => match get_value(expr, initial_witness) { - Ok(value) => calldata.push(value.into()), + Ok(value) => calldata.push(value), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -92,7 +92,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { // Attempt to fetch all array input values for expr in expr_arr.iter() { match get_value(expr, initial_witness) { - Ok(value) => calldata.push(value.into()), + Ok(value) => calldata.push(value), Err(_) => { return Err(OpcodeResolutionError::OpcodeNotSolvable( OpcodeNotSolvable::ExpressionHasTooManyUnknowns(expr.clone()), @@ -110,7 +110,7 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { .block_value .get(&memory_index) .expect("All memory is initialized on creation"); - calldata.push((*memory_value).into()); + calldata.push(*memory_value); } } } @@ -122,11 +122,11 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { Ok(Self { vm, acir_index }) } - pub fn get_memory(&self) -> &[Value] { + pub fn get_memory(&self) -> &[MemoryValue] { self.vm.get_memory() } - pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + pub fn write_memory_at(&mut self, ptr: usize, value: MemoryValue) { self.vm.write_memory_at(ptr, value); } @@ -206,13 +206,13 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { for output in brillig.outputs.iter() { match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?; + insert_value(witness, memory[current_ret_data_idx].value, witness_map)?; current_ret_data_idx += 1; } BrilligOutputs::Array(witness_arr) => { for witness in witness_arr.iter() { let value = memory[current_ret_data_idx]; - insert_value(witness, value.to_field(), witness_map)?; + insert_value(witness, value.value, witness_map)?; current_ret_data_idx += 1; } } diff --git a/acvm-repo/acvm/tests/solver.rs b/acvm-repo/acvm/tests/solver.rs index b267c3005a8..a708db5b030 100644 --- a/acvm-repo/acvm/tests/solver.rs +++ b/acvm-repo/acvm/tests/solver.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use acir::{ - brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, Value, ValueOrArray}, + brillig::{BinaryFieldOp, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray}, circuit::{ brillig::{Brillig, BrilligInputs, BrilligOutputs}, opcodes::{BlockId, MemOp}, @@ -70,9 +70,9 @@ fn inversion_brillig_oracle_equivalence() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 3 }, ], @@ -120,8 +120,7 @@ fn inversion_brillig_oracle_equivalence() { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // As caller of VM, need to resolve foreign calls - let foreign_call_result = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let foreign_call_result = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); // Alter Brillig oracle opcode with foreign call resolution acvm.resolve_pending_foreign_call(foreign_call_result.into()); @@ -199,16 +198,16 @@ fn double_inversion_brillig_oracle() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 5 }, ], @@ -257,8 +256,7 @@ fn double_inversion_brillig_oracle() { acvm.get_pending_foreign_call().expect("should have a brillig foreign call request"); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); - let x_plus_y_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let x_plus_y_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); // Resolve Brillig foreign call acvm.resolve_pending_foreign_call(x_plus_y_inverse.into()); @@ -275,8 +273,7 @@ fn double_inversion_brillig_oracle() { acvm.get_pending_foreign_call().expect("should have a brillig foreign call request"); assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); - let i_plus_j_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let i_plus_j_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); assert_ne!(x_plus_y_inverse, i_plus_j_inverse); // Alter Brillig oracle opcode @@ -334,16 +331,16 @@ fn oracle_dependent_execution() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(3))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(2))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 4 }, ], @@ -389,8 +386,7 @@ fn oracle_dependent_execution() { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Resolve Brillig foreign call - let x_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let x_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); acvm.resolve_pending_foreign_call(x_inverse.into()); // After filling data request, continue solving @@ -406,8 +402,7 @@ fn oracle_dependent_execution() { assert_eq!(foreign_call_wait_info.inputs.len(), 1, "Should be waiting for a single input"); // Resolve Brillig foreign call - let y_inverse = - Value::from(foreign_call_wait_info.inputs[0].unwrap_value().to_field().inverse()); + let y_inverse = foreign_call_wait_info.inputs[0].unwrap_field().inverse(); acvm.resolve_pending_foreign_call(y_inverse.into()); // We've resolved all the brillig foreign calls so we should be able to complete execution now. @@ -464,9 +459,9 @@ fn brillig_oracle_predicate() { BrilligOpcode::ForeignCall { function: "invert".into(), destinations: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(1))], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, ], predicate: Some(Expression::default()), diff --git a/acvm-repo/acvm_js/src/foreign_call/inputs.rs b/acvm-repo/acvm_js/src/foreign_call/inputs.rs index 728fc2d3d2f..ebd29fb7d58 100644 --- a/acvm-repo/acvm_js/src/foreign_call/inputs.rs +++ b/acvm-repo/acvm_js/src/foreign_call/inputs.rs @@ -8,8 +8,8 @@ pub(super) fn encode_foreign_call_inputs( let inputs = js_sys::Array::default(); for input in foreign_call_inputs { let input_array = js_sys::Array::default(); - for value in input.values() { - let hex_js_string = field_element_to_js_string(&value.to_field()); + for value in input.fields() { + let hex_js_string = field_element_to_js_string(&value); input_array.push(&hex_js_string); } inputs.push(&input_array); diff --git a/acvm-repo/acvm_js/src/foreign_call/outputs.rs b/acvm-repo/acvm_js/src/foreign_call/outputs.rs index 630b1afb6fd..78fa520aa15 100644 --- a/acvm-repo/acvm_js/src/foreign_call/outputs.rs +++ b/acvm-repo/acvm_js/src/foreign_call/outputs.rs @@ -1,18 +1,18 @@ -use acvm::brillig_vm::brillig::{ForeignCallParam, ForeignCallResult, Value}; +use acvm::brillig_vm::brillig::{ForeignCallParam, ForeignCallResult}; use wasm_bindgen::JsValue; use crate::js_witness_map::js_value_to_field_element; fn decode_foreign_call_output(output: JsValue) -> Result { if output.is_string() { - let value = Value::from(js_value_to_field_element(output)?); + let value = js_value_to_field_element(output)?; Ok(ForeignCallParam::Single(value)) } else if output.is_array() { let output = js_sys::Array::from(&output); - let mut values: Vec = Vec::with_capacity(output.length() as usize); + let mut values: Vec<_> = Vec::with_capacity(output.length() as usize); for elem in output.iter() { - values.push(Value::from(js_value_to_field_element(elem)?)); + values.push(js_value_to_field_element(elem)?); } Ok(ForeignCallParam::Array(values)) } else { diff --git a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts index fba8470585f..e074cf1ad38 100644 --- a/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/complex_foreign_call.ts @@ -2,13 +2,13 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `complex_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 65, 14, 132, 32, 12, 108, 101, 117, 205, 222, 246, 7, 38, 187, 15, 96, - 247, 5, 254, 197, 120, 211, 232, 209, 231, 139, 113, 136, 181, 65, 47, 98, 162, 147, 52, 20, 24, 202, 164, 45, 48, - 205, 200, 157, 49, 124, 227, 44, 129, 207, 152, 75, 120, 94, 137, 209, 30, 195, 143, 227, 197, 178, 103, 105, 76, 110, - 160, 209, 156, 160, 209, 247, 195, 69, 235, 29, 179, 46, 81, 243, 103, 2, 239, 231, 225, 44, 117, 150, 97, 254, 196, - 152, 99, 157, 176, 87, 168, 188, 147, 224, 121, 20, 209, 180, 254, 109, 70, 75, 47, 178, 186, 251, 37, 116, 86, 93, - 219, 55, 245, 96, 20, 85, 75, 253, 8, 255, 171, 246, 121, 231, 220, 4, 249, 237, 132, 56, 28, 224, 109, 113, 223, 180, - 164, 50, 165, 0, 137, 17, 72, 139, 88, 97, 4, 173, 98, 132, 157, 33, 5, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 213, 84, 93, 10, 131, 48, 12, 78, 218, 233, 100, 111, 187, 193, 96, 59, 64, 231, 9, + 188, 139, 248, 166, 232, 163, 167, 23, 11, 126, 197, 24, 250, 34, 86, 208, 64, 72, 218, 252, 125, 36, 105, 153, 22, + 42, 60, 51, 116, 235, 217, 64, 103, 156, 37, 5, 191, 10, 210, 29, 163, 63, 167, 203, 229, 206, 194, 104, 110, 128, + 209, 158, 128, 49, 236, 195, 69, 231, 157, 114, 46, 73, 251, 103, 35, 239, 231, 225, 57, 243, 156, 227, 252, 132, 44, + 112, 79, 176, 125, 84, 223, 73, 248, 145, 152, 69, 149, 4, 107, 233, 114, 90, 119, 145, 85, 237, 151, 192, 89, 247, + 221, 208, 54, 163, 85, 174, 26, 234, 87, 232, 63, 101, 103, 21, 55, 169, 216, 73, 72, 249, 5, 197, 234, 132, 123, 179, + 35, 247, 155, 214, 246, 102, 20, 73, 204, 72, 168, 123, 191, 161, 25, 66, 136, 159, 187, 53, 5, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000001'], diff --git a/acvm-repo/acvm_js/test/shared/foreign_call.ts b/acvm-repo/acvm_js/test/shared/foreign_call.ts index dd010f0c5e5..eb14cb2e9f1 100644 --- a/acvm-repo/acvm_js/test/shared/foreign_call.ts +++ b/acvm-repo/acvm_js/test/shared/foreign_call.ts @@ -2,10 +2,10 @@ import { WitnessMap } from '@noir-lang/acvm_js'; // See `simple_brillig_foreign_call` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 143, 65, 10, 192, 32, 12, 4, 77, 10, 165, 244, 214, 159, 216, 31, 244, 51, 61, - 120, 241, 32, 226, 251, 85, 140, 176, 136, 122, 209, 129, 144, 176, 9, 97, 151, 84, 225, 74, 69, 50, 31, 48, 35, 85, - 251, 164, 235, 53, 94, 218, 247, 75, 163, 95, 150, 12, 153, 179, 227, 191, 114, 195, 222, 216, 240, 59, 63, 75, 221, - 251, 208, 106, 207, 232, 150, 65, 100, 53, 33, 2, 22, 232, 178, 27, 144, 1, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 173, 144, 61, 10, 192, 48, 8, 133, 53, 133, 82, 186, 245, 38, 233, 13, 122, 153, + 14, 93, 58, 132, 144, 227, 135, 252, 41, 56, 36, 46, 201, 7, 162, 168, 200, 123, 34, 52, 142, 28, 72, 245, 38, 106, 9, + 247, 30, 202, 118, 142, 27, 215, 221, 178, 82, 175, 33, 15, 133, 189, 163, 159, 57, 197, 252, 251, 195, 235, 188, 230, + 186, 16, 65, 255, 12, 239, 92, 131, 89, 149, 198, 77, 3, 10, 9, 119, 8, 198, 242, 152, 1, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ [1, '0x0000000000000000000000000000000000000000000000000000000000000005'], diff --git a/acvm-repo/brillig/src/foreign_call.rs b/acvm-repo/brillig/src/foreign_call.rs index 3f124a9a0a7..e547b99f0eb 100644 --- a/acvm-repo/brillig/src/foreign_call.rs +++ b/acvm-repo/brillig/src/foreign_call.rs @@ -1,34 +1,34 @@ -use crate::value::Value; +use acir_field::FieldElement; use serde::{Deserialize, Serialize}; /// Single output of a [foreign call][crate::Opcode::ForeignCall]. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub enum ForeignCallParam { - Single(Value), - Array(Vec), + Single(FieldElement), + Array(Vec), } -impl From for ForeignCallParam { - fn from(value: Value) -> Self { +impl From for ForeignCallParam { + fn from(value: FieldElement) -> Self { ForeignCallParam::Single(value) } } -impl From> for ForeignCallParam { - fn from(values: Vec) -> Self { +impl From> for ForeignCallParam { + fn from(values: Vec) -> Self { ForeignCallParam::Array(values) } } impl ForeignCallParam { - pub fn values(&self) -> Vec { + pub fn fields(&self) -> Vec { match self { ForeignCallParam::Single(value) => vec![*value], ForeignCallParam::Array(values) => values.clone(), } } - pub fn unwrap_value(&self) -> Value { + pub fn unwrap_field(&self) -> FieldElement { match self { ForeignCallParam::Single(value) => *value, ForeignCallParam::Array(_) => panic!("Expected single value, found array"), @@ -43,14 +43,14 @@ pub struct ForeignCallResult { pub values: Vec, } -impl From for ForeignCallResult { - fn from(value: Value) -> Self { +impl From for ForeignCallResult { + fn from(value: FieldElement) -> Self { ForeignCallResult { values: vec![value.into()] } } } -impl From> for ForeignCallResult { - fn from(values: Vec) -> Self { +impl From> for ForeignCallResult { + fn from(values: Vec) -> Self { ForeignCallResult { values: vec![values.into()] } } } diff --git a/acvm-repo/brillig/src/lib.rs b/acvm-repo/brillig/src/lib.rs index 0661e794360..40f2e15acfe 100644 --- a/acvm-repo/brillig/src/lib.rs +++ b/acvm-repo/brillig/src/lib.rs @@ -13,7 +13,6 @@ mod black_box; mod foreign_call; mod opcodes; -mod value; pub use black_box::BlackBoxOp; pub use foreign_call::{ForeignCallParam, ForeignCallResult}; @@ -21,5 +20,3 @@ pub use opcodes::{ BinaryFieldOp, BinaryIntOp, HeapArray, HeapValueType, HeapVector, MemoryAddress, ValueOrArray, }; pub use opcodes::{BrilligOpcode as Opcode, Label}; -pub use value::Typ; -pub use value::Value; diff --git a/acvm-repo/brillig/src/opcodes.rs b/acvm-repo/brillig/src/opcodes.rs index 22a0ebe1170..d1345351986 100644 --- a/acvm-repo/brillig/src/opcodes.rs +++ b/acvm-repo/brillig/src/opcodes.rs @@ -1,4 +1,5 @@ -use crate::{black_box::BlackBoxOp, Value}; +use crate::black_box::BlackBoxOp; +use acir_field::FieldElement; use serde::{Deserialize, Serialize}; pub type Label = usize; @@ -22,8 +23,8 @@ impl From for MemoryAddress { /// Describes the memory layout for an array/vector element #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum HeapValueType { - // A single field element is enough to represent the value - Simple, + // A single field element is enough to represent the value with a given bit size + Simple(u32), // The value read should be interpreted as a pointer to a heap array, which // consists of a pointer to a slice of memory of size elements, and a // reference count @@ -36,7 +37,11 @@ pub enum HeapValueType { impl HeapValueType { pub fn all_simple(types: &[HeapValueType]) -> bool { - types.iter().all(|typ| matches!(typ, HeapValueType::Simple)) + types.iter().all(|typ| matches!(typ, HeapValueType::Simple(_))) + } + + pub fn field() -> HeapValueType { + HeapValueType::Simple(FieldElement::max_num_bits()) } } @@ -131,7 +136,7 @@ pub enum BrilligOpcode { Const { destination: MemoryAddress, bit_size: u32, - value: Value, + value: FieldElement, }, Return, /// Used to get data from an outside source. diff --git a/acvm-repo/brillig/src/value.rs b/acvm-repo/brillig/src/value.rs deleted file mode 100644 index 5a532cbc1a7..00000000000 --- a/acvm-repo/brillig/src/value.rs +++ /dev/null @@ -1,103 +0,0 @@ -use acir_field::FieldElement; -use serde::{Deserialize, Serialize}; -use std::ops::{Add, Div, Mul, Neg, Sub}; - -/// Types of values allowed in the VM -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord)] -pub enum Typ { - Field, - Unsigned { bit_size: u32 }, - Signed { bit_size: u32 }, -} - -/// `Value` represents the base descriptor for a value in the VM. -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Value { - inner: FieldElement, -} - -impl Value { - /// Returns `true` if the `Value` represents `zero` - pub fn is_zero(&self) -> bool { - self.inner.is_zero() - } - - /// Converts `Value` into a `FieldElement`. - pub fn to_field(&self) -> FieldElement { - self.inner - } - - /// Converts `Value` into a `u128`. - // TODO: Check what happens if `Value` cannot fit into a u128 - pub fn to_u128(&self) -> u128 { - self.to_field().to_u128() - } - - /// Converts `Value` into a u64 and then casts it into a usize. - /// Panics: If `Value` cannot fit into a u64 or `Value` does - //// not fit into a usize. - pub fn to_usize(&self) -> usize { - usize::try_from(self.inner.try_to_u64().expect("value does not fit into u64")) - .expect("value does not fit into usize") - } -} - -impl From for Value { - fn from(value: usize) -> Self { - Value { inner: FieldElement::from(value as u128) } - } -} - -impl From for Value { - fn from(value: u128) -> Self { - Value { inner: FieldElement::from(value) } - } -} - -impl From for Value { - fn from(value: FieldElement) -> Self { - Value { inner: value } - } -} - -impl From for Value { - fn from(value: bool) -> Self { - Value { inner: FieldElement::from(value) } - } -} - -impl Add for Value { - type Output = Value; - - fn add(self, rhs: Self) -> Self::Output { - Value { inner: self.inner + rhs.inner } - } -} -impl Sub for Value { - type Output = Value; - - fn sub(self, rhs: Self) -> Self::Output { - Value { inner: self.inner - rhs.inner } - } -} -impl Mul for Value { - type Output = Value; - - fn mul(self, rhs: Self) -> Self::Output { - Value { inner: self.inner * rhs.inner } - } -} -impl Div for Value { - type Output = Value; - - fn div(self, rhs: Self) -> Self::Output { - Value { inner: self.inner / rhs.inner } - } -} -impl Neg for Value { - type Output = Value; - - fn neg(self) -> Self::Output { - Value { inner: -self.inner } - } -} diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 32dabe6ecb0..95675469479 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -17,6 +17,7 @@ acir.workspace = true acvm_blackbox_solver.workspace = true num-bigint.workspace = true num-traits.workspace = true +thiserror.workspace = true [features] default = ["bn254"] diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 81103be582d..3d77982ffb1 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -3,56 +3,98 @@ use acir::FieldElement; use num_bigint::BigUint; use num_traits::{One, ToPrimitive, Zero}; -/// Evaluate a binary operation on two FieldElements and return the result as a FieldElement. +use crate::memory::MemoryValue; + +#[derive(Debug, thiserror::Error)] +pub(crate) enum BrilligArithmeticError { + #[error("Bit size for lhs {lhs_bit_size} does not match op bit size {op_bit_size}")] + MismatchedLhsBitSize { lhs_bit_size: u32, op_bit_size: u32 }, + #[error("Bit size for rhs {rhs_bit_size} does not match op bit size {op_bit_size}")] + MismatchedRhsBitSize { rhs_bit_size: u32, op_bit_size: u32 }, + #[error("Shift with bit size {op_bit_size} is invalid")] + InvalidShift { op_bit_size: u32 }, +} + +/// Evaluate a binary operation on two FieldElement memory values. pub(crate) fn evaluate_binary_field_op( op: &BinaryFieldOp, - a: FieldElement, - b: FieldElement, -) -> FieldElement { - match op { + lhs: MemoryValue, + rhs: MemoryValue, +) -> Result { + if lhs.bit_size != FieldElement::max_num_bits() { + return Err(BrilligArithmeticError::MismatchedLhsBitSize { + lhs_bit_size: lhs.bit_size, + op_bit_size: FieldElement::max_num_bits(), + }); + } + if rhs.bit_size != FieldElement::max_num_bits() { + return Err(BrilligArithmeticError::MismatchedRhsBitSize { + rhs_bit_size: rhs.bit_size, + op_bit_size: FieldElement::max_num_bits(), + }); + } + + let a = lhs.value; + let b = rhs.value; + Ok(match op { // Perform addition, subtraction, multiplication, and division based on the BinaryOp variant. - BinaryFieldOp::Add => a + b, - BinaryFieldOp::Sub => a - b, - BinaryFieldOp::Mul => a * b, - BinaryFieldOp::Div => a / b, + BinaryFieldOp::Add => (a + b).into(), + BinaryFieldOp::Sub => (a - b).into(), + BinaryFieldOp::Mul => (a * b).into(), + BinaryFieldOp::Div => (a / b).into(), BinaryFieldOp::IntegerDiv => { let a_big = BigUint::from_bytes_be(&a.to_be_bytes()); let b_big = BigUint::from_bytes_be(&b.to_be_bytes()); let result = a_big / b_big; - FieldElement::from_be_bytes_reduce(&result.to_bytes_be()) + FieldElement::from_be_bytes_reduce(&result.to_bytes_be()).into() } BinaryFieldOp::Equals => (a == b).into(), BinaryFieldOp::LessThan => (a < b).into(), BinaryFieldOp::LessThanEquals => (a <= b).into(), - } + }) } -/// Evaluate a binary operation on two unsigned big integers with a given bit size and return the result as a big integer. -pub(crate) fn evaluate_binary_bigint_op( +/// Evaluate a binary operation on two unsigned big integers with a given bit size. +pub(crate) fn evaluate_binary_int_op( op: &BinaryIntOp, - a: BigUint, - b: BigUint, + lhs: MemoryValue, + rhs: MemoryValue, bit_size: u32, -) -> Result { +) -> Result { + if lhs.bit_size != bit_size { + return Err(BrilligArithmeticError::MismatchedLhsBitSize { + lhs_bit_size: lhs.bit_size, + op_bit_size: bit_size, + }); + } + if rhs.bit_size != bit_size { + return Err(BrilligArithmeticError::MismatchedRhsBitSize { + rhs_bit_size: rhs.bit_size, + op_bit_size: bit_size, + }); + } + + let lhs = BigUint::from_bytes_be(&lhs.value.to_be_bytes()); + let rhs = BigUint::from_bytes_be(&rhs.value.to_be_bytes()); + let bit_modulo = &(BigUint::one() << bit_size); let result = match op { // Perform addition, subtraction, and multiplication, applying a modulo operation to keep the result within the bit size. - BinaryIntOp::Add => (a + b) % bit_modulo, - BinaryIntOp::Sub => (bit_modulo + a - b) % bit_modulo, - BinaryIntOp::Mul => (a * b) % bit_modulo, + BinaryIntOp::Add => (lhs + rhs) % bit_modulo, + BinaryIntOp::Sub => (bit_modulo + lhs - rhs) % bit_modulo, + BinaryIntOp::Mul => (lhs * rhs) % bit_modulo, // Perform unsigned division using the modulo operation on a and b. BinaryIntOp::Div => { - let b_mod = b % bit_modulo; - if b_mod.is_zero() { + if rhs.is_zero() { BigUint::zero() } else { - (a % bit_modulo) / b_mod + lhs / rhs } } // Perform a == operation, returning 0 or 1 BinaryIntOp::Equals => { - if (a % bit_modulo) == (b % bit_modulo) { + if lhs == rhs { BigUint::one() } else { BigUint::zero() @@ -60,7 +102,7 @@ pub(crate) fn evaluate_binary_bigint_op( } // Perform a < operation, returning 0 or 1 BinaryIntOp::LessThan => { - if (a % bit_modulo) < (b % bit_modulo) { + if lhs < rhs { BigUint::one() } else { BigUint::zero() @@ -68,29 +110,40 @@ pub(crate) fn evaluate_binary_bigint_op( } // Perform a <= operation, returning 0 or 1 BinaryIntOp::LessThanEquals => { - if (a % bit_modulo) <= (b % bit_modulo) { + if lhs <= rhs { BigUint::one() } else { BigUint::zero() } } // Perform bitwise AND, OR, XOR, left shift, and right shift operations, applying a modulo operation to keep the result within the bit size. - BinaryIntOp::And => (a & b) % bit_modulo, - BinaryIntOp::Or => (a | b) % bit_modulo, - BinaryIntOp::Xor => (a ^ b) % bit_modulo, + BinaryIntOp::And => lhs & rhs, + BinaryIntOp::Or => lhs | rhs, + BinaryIntOp::Xor => lhs ^ rhs, BinaryIntOp::Shl => { - assert!(bit_size <= 128, "unsupported bit size for right shift"); - let b = b.to_u128().unwrap(); - (a << b) % bit_modulo + if bit_size > 128 { + return Err(BrilligArithmeticError::InvalidShift { op_bit_size: bit_size }); + } + let rhs = rhs.to_u128().unwrap(); + (lhs << rhs) % bit_modulo } BinaryIntOp::Shr => { - assert!(bit_size <= 128, "unsupported bit size for right shift"); - let b = b.to_u128().unwrap(); - (a >> b) % bit_modulo + if bit_size > 128 { + return Err(BrilligArithmeticError::InvalidShift { op_bit_size: bit_size }); + } + let rhs = rhs.to_u128().unwrap(); + lhs >> rhs } }; - Ok(result) + let result_as_field = FieldElement::from_be_bytes_reduce(&result.to_bytes_be()); + + Ok(match op { + BinaryIntOp::Equals | BinaryIntOp::LessThan | BinaryIntOp::LessThanEquals => { + MemoryValue::new(result_as_field, 1) + } + _ => MemoryValue::new(result_as_field, bit_size), + }) } #[cfg(test)] @@ -104,12 +157,15 @@ mod tests { } fn evaluate_u128(op: &BinaryIntOp, a: u128, b: u128, bit_size: u32) -> u128 { - // Convert to big integers - let lhs_big = BigUint::from(a); - let rhs_big = BigUint::from(b); - let result_value = evaluate_binary_bigint_op(op, lhs_big, rhs_big, bit_size).unwrap(); + let result_value = evaluate_binary_int_op( + op, + MemoryValue::new(a.into(), bit_size), + MemoryValue::new(b.into(), bit_size), + bit_size, + ) + .unwrap(); // Convert back to u128 - result_value.to_u128().unwrap() + result_value.value.to_u128() } fn to_negative(a: u128, bit_size: u32) -> u128 { diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index 73b57b907f3..ab4358739e9 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -1,33 +1,33 @@ -use acir::brillig::{BlackBoxOp, HeapArray, HeapVector, Value}; +use acir::brillig::{BlackBoxOp, HeapArray, HeapVector}; use acir::{BlackBoxFunc, FieldElement}; use acvm_blackbox_solver::{ blake2s, blake3, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, keccakf1600, sha256, sha256compression, BlackBoxFunctionSolver, BlackBoxResolutionError, }; +use crate::memory::MemoryValue; use crate::Memory; -fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [Value] { - memory.read_slice(memory.read_ref(vector.pointer), memory.read(vector.size).to_usize()) +fn read_heap_vector<'a>(memory: &'a Memory, vector: &HeapVector) -> &'a [MemoryValue] { + let size = memory.read(vector.size); + memory.read_slice(memory.read_ref(vector.pointer), size.to_usize()) } -fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [Value] { +fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [MemoryValue] { memory.read_slice(memory.read_ref(array.pointer), array.size) } /// Extracts the last byte of every value -fn to_u8_vec(inputs: &[Value]) -> Vec { +fn to_u8_vec(inputs: &[MemoryValue]) -> Vec { let mut result = Vec::with_capacity(inputs.len()); - for input in inputs { - let field_bytes = input.to_field().to_be_bytes(); - let byte = field_bytes.last().unwrap(); - result.push(*byte); + for &input in inputs { + result.push(input.try_into().unwrap()); } result } -fn to_value_vec(input: &[u8]) -> Vec { - input.iter().map(|x| Value::from(*x as usize)).collect() +fn to_value_vec(input: &[u8]) -> Vec { + input.iter().map(|&x| x.into()).collect() } pub(crate) fn evaluate_black_box( @@ -63,14 +63,13 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::Keccakf1600 { message, output } => { let state_vec: Vec = read_heap_vector(memory, message) .iter() - .map(|value| value.to_field().try_to_u64().unwrap()) + .map(|&memory_value| memory_value.try_into().unwrap()) .collect(); let state: [u64; 25] = state_vec.try_into().unwrap(); let new_state = keccakf1600(state)?; - let new_state: Vec = - new_state.into_iter().map(|x| Value::from(x as usize)).collect(); + let new_state: Vec = new_state.into_iter().map(|x| x.into()).collect(); memory.write_slice(memory.read_ref(output.pointer), &new_state); Ok(()) } @@ -125,8 +124,8 @@ pub(crate) fn evaluate_black_box( Ok(()) } BlackBoxOp::SchnorrVerify { public_key_x, public_key_y, message, signature, result } => { - let public_key_x = memory.read(*public_key_x).to_field(); - let public_key_y = memory.read(*public_key_y).to_field(); + let public_key_x = memory.read(*public_key_x).try_into().unwrap(); + let public_key_y = memory.read(*public_key_y).try_into().unwrap(); let message: Vec = to_u8_vec(read_heap_vector(memory, message)); let signature: Vec = to_u8_vec(read_heap_vector(memory, signature)); let verified = @@ -135,26 +134,26 @@ pub(crate) fn evaluate_black_box( Ok(()) } BlackBoxOp::FixedBaseScalarMul { low, high, result } => { - let low = memory.read(*low).to_field(); - let high = memory.read(*high).to_field(); + let low = memory.read(*low).try_into().unwrap(); + let high = memory.read(*high).try_into().unwrap(); let (x, y) = solver.fixed_base_scalar_mul(&low, &high)?; memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::EmbeddedCurveAdd { input1_x, input1_y, input2_x, input2_y, result } => { - let input1_x = memory.read(*input1_x).to_field(); - let input1_y = memory.read(*input1_y).to_field(); - let input2_x = memory.read(*input2_x).to_field(); - let input2_y = memory.read(*input2_y).to_field(); + let input1_x = memory.read(*input1_x).try_into().unwrap(); + let input1_y = memory.read(*input1_y).try_into().unwrap(); + let input2_x = memory.read(*input2_x).try_into().unwrap(); + let input2_y = memory.read(*input2_y).try_into().unwrap(); let (x, y) = solver.ec_add(&input1_x, &input1_y, &input2_x, &input2_y)?; memory.write_slice(memory.read_ref(result.pointer), &[x.into(), y.into()]); Ok(()) } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); let domain_separator: u32 = - memory.read(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), @@ -166,9 +165,9 @@ pub(crate) fn evaluate_black_box( } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|x| x.to_field()).collect(); + read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); let domain_separator: u32 = - memory.read(*domain_separator).to_u128().try_into().map_err(|_| { + memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( BlackBoxFunc::PedersenCommitment, "Invalid signature length".to_string(), @@ -186,12 +185,12 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::BigIntToLeBytes { .. } => todo!(), BlackBoxOp::Poseidon2Permutation { message, output, len } => { let input = read_heap_vector(memory, message); - let input: Vec = input.iter().map(|x| x.to_field()).collect(); - let len = memory.read(*len).to_u128() as u32; + let input: Vec = input.iter().map(|&x| x.try_into().unwrap()).collect(); + let len = memory.read(*len).try_into().unwrap(); let result = solver.poseidon2_permutation(&input, len)?; let mut values = Vec::new(); for i in result { - values.push(Value::from(i)); + values.push(i.into()); } memory.write_slice(memory.read_ref(output.pointer), &values); Ok(()) @@ -205,8 +204,8 @@ pub(crate) fn evaluate_black_box( format!("Expected 16 inputs but encountered {}", &inputs.len()), )); } - for (i, input) in inputs.iter().enumerate() { - message[i] = input.to_u128() as u32; + for (i, &input) in inputs.iter().enumerate() { + message[i] = input.try_into().unwrap(); } let mut state = [0; 8]; let values = read_heap_vector(memory, hash_values); @@ -216,12 +215,12 @@ pub(crate) fn evaluate_black_box( format!("Expected 8 values but encountered {}", &values.len()), )); } - for (i, value) in values.iter().enumerate() { - state[i] = value.to_u128() as u32; + for (i, &value) in values.iter().enumerate() { + state[i] = value.try_into().unwrap(); } sha256compression(&mut state, &message); - let state = state.map(|x| Value::from(x as u128)); + let state = state.map(|x| x.into()); memory.write_slice(memory.read_ref(output.pointer), &state); Ok(()) diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index e2a037618a4..0f430b0d5b2 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -13,23 +13,22 @@ use acir::brillig::{ BinaryFieldOp, BinaryIntOp, ForeignCallParam, ForeignCallResult, HeapArray, HeapValueType, - HeapVector, MemoryAddress, Opcode, Value, ValueOrArray, + HeapVector, MemoryAddress, Opcode, ValueOrArray, }; use acir::FieldElement; +use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; +use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; +use black_box::evaluate_black_box; +use num_bigint::BigUint; + // Re-export `brillig`. pub use acir::brillig; +pub use memory::{Memory, MemoryValue, MEMORY_ADDRESSING_BIT_SIZE}; mod arithmetic; mod black_box; mod memory; -use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; -use arithmetic::{evaluate_binary_bigint_op, evaluate_binary_field_op}; -use black_box::evaluate_black_box; - -pub use memory::Memory; -use num_bigint::BigUint; - /// The error call stack contains the opcode indexes of the call stack at the time of failure, plus the index of the opcode that failed. pub type ErrorCallStack = Vec; @@ -63,7 +62,7 @@ pub enum VMStatus { /// VM encapsulates the state of the Brillig VM during execution. pub struct VM<'a, B: BlackBoxFunctionSolver> { /// Calldata to the brillig function - calldata: Vec, + calldata: Vec, /// Instruction pointer program_counter: usize, /// A counter maintained throughout a Brillig process that determines @@ -79,7 +78,7 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { /// Memory of the VM memory: Memory, /// Call stack - call_stack: Vec, + call_stack: Vec, /// The solver for blackbox functions black_box_solver: &'a B, } @@ -87,7 +86,7 @@ pub struct VM<'a, B: BlackBoxFunctionSolver> { impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Constructs a new VM instance pub fn new( - calldata: Vec, + calldata: Vec, bytecode: &'a [Opcode], foreign_call_results: Vec, black_box_solver: &'a B, @@ -143,8 +142,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Indicating that the VM encountered a `Trap` Opcode /// or an invalid state. fn fail(&mut self, message: String) -> VMStatus { - let mut error_stack: Vec<_> = - self.call_stack.iter().map(|value| value.to_usize()).collect(); + let mut error_stack: Vec<_> = self.call_stack.clone(); error_stack.push(self.program_counter); self.status(VMStatus::Failure { call_stack: error_stack, message }); self.status.clone() @@ -159,22 +157,18 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { self.status.clone() } - pub fn get_memory(&self) -> &[Value] { + pub fn get_memory(&self) -> &[MemoryValue] { self.memory.values() } - pub fn write_memory_at(&mut self, ptr: usize, value: Value) { + pub fn write_memory_at(&mut self, ptr: usize, value: MemoryValue) { self.memory.write(MemoryAddress(ptr), value); } /// Returns the VM's current call stack, including the actual program /// counter in the last position of the returned vector. pub fn get_call_stack(&self) -> Vec { - self.call_stack - .iter() - .map(|program_counter| program_counter.to_usize()) - .chain(std::iter::once(self.program_counter)) - .collect() + self.call_stack.iter().copied().chain(std::iter::once(self.program_counter)).collect() } /// Process a single opcode and modify the program counter. @@ -182,13 +176,16 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let opcode = &self.bytecode[self.program_counter]; match opcode { Opcode::BinaryFieldOp { op, lhs, rhs, destination: result } => { - self.process_binary_field_op(*op, *lhs, *rhs, *result); - self.increment_program_counter() + if let Err(error) = self.process_binary_field_op(*op, *lhs, *rhs, *result) { + self.fail(error.to_string()) + } else { + self.increment_program_counter() + } } Opcode::BinaryIntOp { op, bit_size, lhs, rhs, destination: result } => { if let Err(error) = self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result) { - self.fail(error) + self.fail(error.to_string()) } else { self.increment_program_counter() } @@ -204,26 +201,29 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // Check if condition is true // We use 0 to mean false and any other value to mean true let condition_value = self.memory.read(*condition); - if !condition_value.is_zero() { + if condition_value.try_into().expect("condition value is not a boolean") { return self.set_program_counter(*destination); } self.increment_program_counter() } Opcode::JumpIfNot { condition, location: destination } => { let condition_value = self.memory.read(*condition); - if condition_value.is_zero() { - return self.set_program_counter(*destination); + if condition_value.try_into().expect("condition value is not a boolean") { + return self.increment_program_counter(); } - self.increment_program_counter() + self.set_program_counter(*destination) } Opcode::CalldataCopy { destination_address, size, offset } => { - let values = &self.calldata[*offset..(*offset + size)]; - self.memory.write_slice(*destination_address, values); + let values: Vec<_> = self.calldata[*offset..(*offset + size)] + .iter() + .map(|value| MemoryValue::new_field(*value)) + .collect(); + self.memory.write_slice(*destination_address, &values); self.increment_program_counter() } Opcode::Return => { if let Some(return_location) = self.call_stack.pop() { - self.set_program_counter(return_location.to_usize() + 1) + self.set_program_counter(return_location + 1) } else { self.fail("return opcode hit, but callstack already empty".to_string()) } @@ -254,81 +254,14 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { return self.wait_for_foreign_call(function.clone(), resolved_inputs); } - let values = &self.foreign_call_results[self.foreign_call_counter].values; + let write_result = self.write_foreign_call_result( + destinations, + destination_value_types, + self.foreign_call_counter, + ); - let mut invalid_foreign_call_result = false; - for ((destination, value_type), output) in - destinations.iter().zip(destination_value_types).zip(values) - { - match (destination, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple) => { - match output { - ForeignCallParam::Single(value) => { - self.memory.write(*value_index, *value); - } - _ => unreachable!( - "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}" - ), - } - } - ( - ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), - HeapValueType::Array { value_types, size: type_size }, - ) if size == type_size => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - if values.len() != *size { - invalid_foreign_call_result = true; - break; - } - // Convert the destination pointer to a usize - let destination = self.memory.read_ref(*pointer_index); - // Write to our destination memory - self.memory.write_slice(destination, values); - } - _ => { - unreachable!("Function result size does not match brillig bytecode size") - } - } - } else { - unimplemented!("deflattening heap arrays from foreign calls"); - } - } - ( - ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), - HeapValueType::Vector { value_types }, - ) => { - if HeapValueType::all_simple(value_types) { - match output { - ForeignCallParam::Array(values) => { - // Set our size in the size address - self.memory.write(*size_index, Value::from(values.len())); - // Convert the destination pointer to a usize - let destination = self.memory.read_ref(*pointer_index); - // Write to our destination memory - self.memory.write_slice(destination, values); - } - _ => { - unreachable!("Function result size does not match brillig bytecode size") - } - } - } else { - unimplemented!("deflattening heap vectors from foreign calls"); - } - } - _ => { - unreachable!("Unexpected value type {value_type:?} for destination {destination:?}"); - } - } - } - - // These checks must come after resolving the foreign call outputs as `fail` uses a mutable reference - if destinations.len() != values.len() { - self.fail(format!("{} output values were provided as a foreign call result for {} destination slots", values.len(), destinations.len())); - } - if invalid_foreign_call_result { - self.fail("Function result size does not match brillig bytecode".to_owned()); + if let Err(e) = write_result { + return self.fail(e); } self.foreign_call_counter += 1; @@ -341,10 +274,10 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } Opcode::ConditionalMov { destination, source_a, source_b, condition } => { let condition_value = self.memory.read(*condition); - if condition_value.is_zero() { - self.memory.write(*destination, self.memory.read(*source_b)); - } else { + if condition_value.try_into().expect("condition value is not a boolean") { self.memory.write(*destination, self.memory.read(*source_a)); + } else { + self.memory.write(*destination, self.memory.read(*source_b)); } self.increment_program_counter() } @@ -369,11 +302,12 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } Opcode::Call { location } => { // Push a return location - self.call_stack.push(Value::from(self.program_counter)); + self.call_stack.push(self.program_counter); self.set_program_counter(*location) } - Opcode::Const { destination, value, bit_size: _ } => { - self.memory.write(*destination, *value); + Opcode::Const { destination, value, bit_size } => { + // Consts are not checked in runtime to fit in the bit size, since they can safely be checked statically. + self.memory.write(*destination, MemoryValue::new(*value, *bit_size)); self.increment_program_counter() } Opcode::BlackBox(black_box_op) => { @@ -413,15 +347,19 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { value_type: &HeapValueType, ) -> ForeignCallParam { match (input, value_type) { - (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple) => { - self.memory.read(value_index).into() + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(_)) => { + self.memory.read(value_index).value.into() } ( ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), HeapValueType::Array { value_types, size: type_size }, ) if *type_size == size => { let start = self.memory.read_ref(pointer_index); - self.read_slice_of_values_from_memory(start, size, value_types).into() + self.read_slice_of_values_from_memory(start, size, value_types) + .into_iter() + .map(|mem_value| mem_value.value) + .collect::>() + .into() } ( ValueOrArray::HeapVector(HeapVector { pointer: pointer_index, size: size_index }), @@ -429,7 +367,11 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { ) => { let start = self.memory.read_ref(pointer_index); let size = self.memory.read(size_index).to_usize(); - self.read_slice_of_values_from_memory(start, size, value_types).into() + self.read_slice_of_values_from_memory(start, size, value_types) + .into_iter() + .map(|mem_value| mem_value.value) + .collect::>() + .into() } _ => { unreachable!("Unexpected value type {value_type:?} for input {input:?}"); @@ -444,7 +386,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { start: MemoryAddress, size: usize, value_types: &[HeapValueType], - ) -> Vec { + ) -> Vec { if HeapValueType::all_simple(value_types) { self.memory.read_slice(start, size).to_vec() } else { @@ -459,9 +401,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { .flat_map(|(i, value_type)| { let value_address: MemoryAddress = (start.to_usize() + i).into(); match value_type { - HeapValueType::Simple => { - let value = self.memory.read(value_address); - vec![value] + HeapValueType::Simple(_) => { + vec![self.memory.read(value_address)] } HeapValueType::Array { value_types, size } => { let array_address = self.memory.read_ref(value_address); @@ -486,6 +427,124 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } } + fn write_foreign_call_result( + &mut self, + destinations: &[ValueOrArray], + destination_value_types: &[HeapValueType], + foreign_call_index: usize, + ) -> Result<(), String> { + let values = &self.foreign_call_results[foreign_call_index].values; + + if destinations.len() != values.len() { + return Err(format!( + "{} output values were provided as a foreign call result for {} destination slots", + values.len(), + destinations.len() + )); + } + + for ((destination, value_type), output) in + destinations.iter().zip(destination_value_types).zip(values) + { + match (destination, value_type) { + (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(bit_size)) => { + match output { + ForeignCallParam::Single(value) => { + let memory_value = MemoryValue::new_checked(*value, *bit_size); + if let Some(memory_value) = memory_value { + self.memory.write(*value_index, memory_value); + } else { + return Err(format!( + "Foreign call result value {} does not fit in bit size {}", + value, + bit_size + )); + } + } + _ => return Err(format!( + "Function result size does not match brillig bytecode. Expected 1 result but got {output:?}") + ), + } + } + ( + ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), + HeapValueType::Array { value_types, size: type_size }, + ) if size == type_size => { + if HeapValueType::all_simple(value_types) { + let bit_sizes_iterator = value_types.iter().map(|typ| match typ { + HeapValueType::Simple(bit_size) => *bit_size, + _ => unreachable!("Expected simple value type"), + }).cycle(); + match output { + ForeignCallParam::Array(values) => { + if values.len() != *size { + return Err("Foreign call result array doesn't match expected size".to_string()); + } + // Convert the destination pointer to a usize + let destination = self.memory.read_ref(*pointer_index); + // Write to our destination memory + let memory_values: Option> = values.iter().zip(bit_sizes_iterator).map( + |(value, bit_size)| MemoryValue::new_checked(*value, bit_size)).collect(); + if let Some(memory_values) = memory_values { + self.memory.write_slice(destination, &memory_values); + } else { + return Err(format!( + "Foreign call result values {:?} do not match expected bit sizes", + values, + )); + } + } + _ => { + return Err("Function result size does not match brillig bytecode size".to_string()); + } + } + } else { + unimplemented!("deflattening heap arrays from foreign calls"); + } + } + ( + ValueOrArray::HeapVector(HeapVector {pointer: pointer_index, size: size_index }), + HeapValueType::Vector { value_types }, + ) => { + if HeapValueType::all_simple(value_types) { + let bit_sizes_iterator = value_types.iter().map(|typ| match typ { + HeapValueType::Simple(bit_size) => *bit_size, + _ => unreachable!("Expected simple value type"), + }).cycle(); + match output { + ForeignCallParam::Array(values) => { + // Set our size in the size address + self.memory.write(*size_index, values.len().into()); + // Convert the destination pointer to a usize + let destination = self.memory.read_ref(*pointer_index); + // Write to our destination memory + let memory_values: Option> = values.iter().zip(bit_sizes_iterator).map(|(value, bit_size)| MemoryValue::new_checked(*value, bit_size)).collect(); + if let Some(memory_values) = memory_values { + self.memory.write_slice(destination, &memory_values); + }else{ + return Err(format!( + "Foreign call result values {:?} do not match expected bit sizes", + values, + )); + } + } + _ => { + return Err("Function result size does not match brillig bytecode size".to_string()); + } + } + } else { + unimplemented!("deflattening heap vectors from foreign calls"); + } + } + _ => { + return Err(format!("Unexpected value type {value_type:?} for destination {destination:?}")); + } + } + } + + Ok(()) + } + /// Process a binary operation. /// This method will not modify the program counter. fn process_binary_field_op( @@ -494,14 +553,15 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { lhs: MemoryAddress, rhs: MemoryAddress, result: MemoryAddress, - ) { + ) -> Result<(), BrilligArithmeticError> { let lhs_value = self.memory.read(lhs); let rhs_value = self.memory.read(rhs); - let result_value = - evaluate_binary_field_op(&op, lhs_value.to_field(), rhs_value.to_field()); + let result_value = evaluate_binary_field_op(&op, lhs_value, rhs_value)?; + + self.memory.write(result, result_value); - self.memory.write(result, result_value.into()); + Ok(()) } /// Process a binary operation. @@ -513,25 +573,23 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { lhs: MemoryAddress, rhs: MemoryAddress, result: MemoryAddress, - ) -> Result<(), String> { + ) -> Result<(), BrilligArithmeticError> { let lhs_value = self.memory.read(lhs); let rhs_value = self.memory.read(rhs); - // Convert to big integers - let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes()); - let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes()); - let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?; - // Convert back to field element - self.memory - .write(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into()); + let result_value = evaluate_binary_int_op(&op, lhs_value, rhs_value, bit_size)?; + self.memory.write(result, result_value); Ok(()) } /// Casts a value to a different bit size. - fn cast(&self, bit_size: u32, value: Value) -> Value { - let lhs_big = BigUint::from_bytes_be(&value.to_field().to_be_bytes()); + fn cast(&self, bit_size: u32, source_value: MemoryValue) -> MemoryValue { + let lhs_big = BigUint::from_bytes_be(&source_value.value.to_be_bytes()); let mask = BigUint::from(2_u32).pow(bit_size) - 1_u32; - FieldElement::from_be_bytes_reduce(&(lhs_big & mask).to_bytes_be()).into() + MemoryValue { + value: FieldElement::from_be_bytes_reduce(&(lhs_big & mask).to_bytes_be()), + bit_size, + } } } @@ -592,7 +650,7 @@ mod tests { #[test] fn add_single_step_smoke() { - let calldata = vec![Value::from(27u128)]; + let calldata = vec![FieldElement::from(27u128)]; // Add opcode to add the value in address `0` and `1` // and place the output in address `2` @@ -618,7 +676,7 @@ mod tests { let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value, Value::from(27u128)); + assert_eq!(output_value.value, FieldElement::from(27u128)); } #[test] @@ -627,12 +685,12 @@ mod tests { let mut opcodes = vec![]; let lhs = { - calldata.push(Value::from(2u128)); + calldata.push(2u128.into()); MemoryAddress::from(calldata.len() - 1) }; let rhs = { - calldata.push(Value::from(2u128)); + calldata.push(2u128.into()); MemoryAddress::from(calldata.len() - 1) }; @@ -643,11 +701,10 @@ mod tests { size: 2, offset: 0, }); - let equal_cmp_opcode = - Opcode::BinaryIntOp { op: BinaryIntOp::Equals, bit_size: 1, lhs, rhs, destination }; - opcodes.push(equal_cmp_opcode); + + opcodes.push(Opcode::BinaryFieldOp { destination, op: BinaryFieldOp::Equals, lhs, rhs }); opcodes.push(Opcode::Jump { location: 3 }); - opcodes.push(Opcode::JumpIf { condition: MemoryAddress::from(2), location: 4 }); + opcodes.push(Opcode::JumpIf { condition: destination, location: 4 }); let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); @@ -657,8 +714,8 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); - let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_cmp_value, Value::from(true)); + let output_cmp_value = vm.memory.read(destination); + assert_eq!(output_cmp_value.value, true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -669,7 +726,7 @@ mod tests { #[test] fn jmpifnot_opcode() { - let calldata = vec![Value::from(1u128), Value::from(2u128)]; + let calldata = vec![1u128.into(), 2u128.into()]; let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -717,7 +774,7 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_cmp_value, Value::from(false)); + assert_eq!(output_cmp_value.value, false.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -734,12 +791,12 @@ mod tests { // The address at index `2` should have not changed as we jumped over the add opcode let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(2)); - assert_eq!(output_value, Value::from(false)); + assert_eq!(output_value.value, false.into()); } #[test] fn cast_opcode() { - let calldata = vec![Value::from((2_u128.pow(32)) - 1)]; + let calldata = vec![((2_u128.pow(32)) - 1).into()]; let opcodes = &[ Opcode::CalldataCopy { @@ -768,12 +825,12 @@ mod tests { let VM { memory, .. } = vm; let casted_value = memory.read(MemoryAddress::from(1)); - assert_eq!(casted_value, Value::from(2_u128.pow(8) - 1)); + assert_eq!(casted_value.value, (2_u128.pow(8) - 1).into()); } #[test] fn mov_opcode() { - let calldata = vec![Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + let calldata = vec![(1u128).into(), (2u128).into(), (3u128).into()]; let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -796,16 +853,15 @@ mod tests { let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(2)); - assert_eq!(destination_value, Value::from(1u128)); + assert_eq!(destination_value.value, (1u128).into()); let source_value = memory.read(MemoryAddress::from(0)); - assert_eq!(source_value, Value::from(1u128)); + assert_eq!(source_value.value, (1u128).into()); } #[test] fn cmov_opcode() { - let calldata = - vec![Value::from(0u128), Value::from(1u128), Value::from(2u128), Value::from(3u128)]; + let calldata = vec![(0u128).into(), (1u128).into(), (2u128).into(), (3u128).into()]; let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -813,8 +869,22 @@ mod tests { offset: 0, }; + let cast_zero = Opcode::Cast { + destination: MemoryAddress::from(0), + source: MemoryAddress::from(0), + bit_size: 1, + }; + + let cast_one = Opcode::Cast { + destination: MemoryAddress::from(1), + source: MemoryAddress::from(1), + bit_size: 1, + }; + let opcodes = &[ calldata_copy, + cast_zero, + cast_one, Opcode::ConditionalMov { destination: MemoryAddress(4), // Sets 3_u128 to memory address 4 source_a: MemoryAddress(2), @@ -836,28 +906,30 @@ mod tests { let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(4)); - assert_eq!(destination_value, Value::from(3_u128)); + assert_eq!(destination_value.value, (3_u128).into()); let source_value = memory.read(MemoryAddress::from(5)); - assert_eq!(source_value, Value::from(2_u128)); + assert_eq!(source_value.value, (2_u128).into()); } #[test] fn cmp_binary_ops() { let bit_size = 32; - let calldata = vec![ - Value::from(2u128), - Value::from(2u128), - Value::from(0u128), - Value::from(5u128), - Value::from(6u128), - ]; + let calldata = + vec![(2u128).into(), (2u128).into(), (0u128).into(), (5u128).into(), (6u128).into()]; + let calldata_size = calldata.len(); let calldata_copy = Opcode::CalldataCopy { destination_address: MemoryAddress::from(0), @@ -865,6 +937,14 @@ mod tests { offset: 0, }; + let cast_opcodes: Vec<_> = (0..calldata_size) + .map(|index| Opcode::Cast { + destination: MemoryAddress::from(index), + source: MemoryAddress::from(index), + bit_size, + }) + .collect(); + let equal_opcode = Opcode::BinaryIntOp { bit_size, op: BinaryIntOp::Equals, @@ -897,42 +977,47 @@ mod tests { destination: MemoryAddress::from(2), }; - let opcodes = [ - calldata_copy, - equal_opcode, - not_equal_opcode, - less_than_opcode, - less_than_equal_opcode, - ]; + let opcodes: Vec<_> = std::iter::once(calldata_copy) + .chain(cast_opcodes) + .chain([equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode]) + .collect(); let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + // Calldata copy let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); + for _ in 0..calldata_size { + let status = vm.process_opcode(); + assert_eq!(status, VMStatus::InProgress); + } + + // Equals let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let output_eq_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_eq_value, Value::from(true)); + assert_eq!(output_eq_value, true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let output_neq_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_neq_value, Value::from(false)); + assert_eq!(output_neq_value, false.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); let lt_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(lt_value, Value::from(true)); + assert_eq!(lt_value, true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); let lte_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(lte_value, Value::from(true)); + assert_eq!(lte_value, true.into()); } + #[test] fn store_opcode() { /// Brillig code for the following: @@ -942,8 +1027,8 @@ mod tests { /// memory[i] = i as Value; /// i += 1; /// } - fn brillig_write_memory(item_count: usize) -> Vec { - let bit_size = 32; + fn brillig_write_memory(item_count: usize) -> Vec { + let bit_size = 64; let r_i = MemoryAddress::from(0); let r_len = MemoryAddress::from(1); let r_tmp = MemoryAddress::from(2); @@ -951,21 +1036,17 @@ mod tests { let start = [ // i = 0 - Opcode::Const { destination: r_i, value: 0u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_i, value: 0u128.into(), bit_size }, // len = memory.len() (approximation) - Opcode::Const { - destination: r_len, - value: Value::from(item_count as u128), - bit_size: 32, - }, + Opcode::Const { destination: r_len, value: item_count.into(), bit_size }, // pointer = free_memory_ptr - Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size }, ]; let loop_body = [ // *i = i Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 - Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -1000,17 +1081,12 @@ mod tests { } let memory = brillig_write_memory(5); - let expected = vec![ - Value::from(0u128), - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - ]; + let expected = + vec![(0u64).into(), (1u64).into(), (2u64).into(), (3u64).into(), (4u64).into()]; assert_eq!(memory, expected); let memory = brillig_write_memory(1024); - let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); + let expected: Vec<_> = (0..1024).map(|i: u64| i.into()).collect(); assert_eq!(memory, expected); } @@ -1024,8 +1100,8 @@ mod tests { /// sum += memory[i]; /// i += 1; /// } - fn brillig_sum_memory(memory: Vec) -> Value { - let bit_size = 32; + fn brillig_sum_memory(memory: Vec) -> FieldElement { + let bit_size = 64; let r_i = MemoryAddress::from(0); let r_len = MemoryAddress::from(1); let r_sum = MemoryAddress::from(2); @@ -1034,17 +1110,17 @@ mod tests { let start = [ // sum = 0 - Opcode::Const { destination: r_sum, value: 0u128.into(), bit_size: 32 }, - // i = 0 - Opcode::Const { destination: r_i, value: 0u128.into(), bit_size: 32 }, - // len = array.len() (approximation) Opcode::Const { - destination: r_len, - value: Value::from(memory.len() as u128), - bit_size: 32, + destination: r_sum, + value: 0u128.into(), + bit_size: FieldElement::max_num_bits(), }, + // i = 0 + Opcode::Const { destination: r_i, value: 0u128.into(), bit_size }, + // len = array.len() (approximation) + Opcode::Const { destination: r_len, value: memory.len().into(), bit_size }, // pointer = array_ptr - Opcode::Const { destination: r_pointer, value: 5u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_pointer, value: 5u128.into(), bit_size }, Opcode::CalldataCopy { destination_address: MemoryAddress(5), size: memory.len(), @@ -1055,15 +1131,14 @@ mod tests { // tmp = *i Opcode::Load { destination: r_tmp, source_pointer: r_pointer }, // sum = sum + tmp - Opcode::BinaryIntOp { + Opcode::BinaryFieldOp { destination: r_sum, lhs: r_sum, - op: BinaryIntOp::Add, + op: BinaryFieldOp::Add, rhs: r_tmp, - bit_size, }, // tmp = 1 - Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -1094,20 +1169,20 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.memory.read(r_sum) + vm.memory.read(r_sum).value } assert_eq!( brillig_sum_memory(vec![ - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - Value::from(5u128), + (1u128).into(), + (2u128).into(), + (3u128).into(), + (4u128).into(), + (5u128).into(), ]), - Value::from(15u128) + (15u128).into() ); - assert_eq!(brillig_sum_memory(vec![Value::from(1u128); 1024]), Value::from(1024u128)); + assert_eq!(brillig_sum_memory(vec![(1u128).into(); 1024]), (1024u128).into()); } #[test] @@ -1121,8 +1196,8 @@ mod tests { /// recursive_write(memory, i + 1, len); /// } /// Note we represent a 100% in-stack optimized form in brillig - fn brillig_recursive_write_memory(size: usize) -> Vec { - let bit_size = 32; + fn brillig_recursive_write_memory(size: usize) -> Vec { + let bit_size = 64; let r_i = MemoryAddress::from(0); let r_len = MemoryAddress::from(1); let r_tmp = MemoryAddress::from(2); @@ -1130,15 +1205,15 @@ mod tests { let start = [ // i = 0 - Opcode::Const { destination: r_i, value: 0u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_i, value: 0u128.into(), bit_size }, // len = size - Opcode::Const { destination: r_len, value: size.into(), bit_size: 32 }, + Opcode::Const { destination: r_len, value: size.into(), bit_size }, // pointer = free_memory_ptr - Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_pointer, value: 4u128.into(), bit_size }, // call recursive_fn Opcode::Call { - location: 5, // Call after 'start' - }, + location: 5, // Call after 'start' + }, // end program by jumping to end Opcode::Jump { location: 100 }, ]; @@ -1160,7 +1235,7 @@ mod tests { // *i = i Opcode::Store { destination_pointer: r_pointer, source: r_i }, // tmp = 1 - Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size: 32 }, + Opcode::Const { destination: r_tmp, value: 1u128.into(), bit_size }, // i = i + 1 (tmp) Opcode::BinaryIntOp { destination: r_i, @@ -1188,23 +1263,18 @@ mod tests { } let memory = brillig_recursive_write_memory(5); - let expected = vec![ - Value::from(0u128), - Value::from(1u128), - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - ]; + let expected = + vec![(0u64).into(), (1u64).into(), (2u64).into(), (3u64).into(), (4u64).into()]; assert_eq!(memory, expected); let memory = brillig_recursive_write_memory(1024); - let expected: Vec = (0..1024).map(|i| Value::from(i as u128)).collect(); + let expected: Vec<_> = (0..1024).map(|i: u64| i.into()).collect(); assert_eq!(memory, expected); } /// Helper to execute brillig code fn brillig_execute_and_get_vm( - calldata: Vec, + calldata: Vec, opcodes: &[Opcode], ) -> VM<'_, DummyBlackBoxSolver> { let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); @@ -1230,14 +1300,14 @@ mod tests { let double_program = vec![ // Load input address with value 5 - Opcode::Const { destination: r_input, value: Value::from(5u128), bit_size: 32 }, + Opcode::Const { destination: r_input, value: (5u128).into(), bit_size: 32 }, // Call foreign function "double" with the input address Opcode::ForeignCall { function: "double".into(), destinations: vec![ValueOrArray::MemoryAddress(r_result)], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::Simple(32)], inputs: vec![ValueOrArray::MemoryAddress(r_input)], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::Simple(32)], }, ]; @@ -1248,13 +1318,13 @@ mod tests { vm.status, VMStatus::ForeignCallWait { function: "double".into(), - inputs: vec![Value::from(5u128).into()] + inputs: vec![FieldElement::from(5usize).into()] } ); // Push result we're waiting for vm.resolve_foreign_call( - Value::from(10u128).into(), // Result of doubling 5u128 + FieldElement::from(10u128).into(), // Result of doubling 5u128 ); // Resume VM @@ -1265,7 +1335,7 @@ mod tests { // Check result address let result_value = vm.memory.read(r_result); - assert_eq!(result_value, Value::from(10u128)); + assert_eq!(result_value, (10u32).into()); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); @@ -1277,12 +1347,11 @@ mod tests { let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory - let initial_matrix = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; + let initial_matrix = vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = - vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; + let expected_result: Vec = + vec![(1u128).into(), (3u128).into(), (2u128).into(), (4u128).into()]; let invert_program = vec![ Opcode::CalldataCopy { @@ -1291,9 +1360,9 @@ mod tests { offset: 0, }, // input = 0 - Opcode::Const { destination: r_input, value: 2_usize.into(), bit_size: 32 }, + Opcode::Const { destination: r_input, value: 2_usize.into(), bit_size: 64 }, // output = 0 - Opcode::Const { destination: r_output, value: 2_usize.into(), bit_size: 32 }, + Opcode::Const { destination: r_output, value: 2_usize.into(), bit_size: 64 }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), @@ -1303,14 +1372,14 @@ mod tests { })], destination_value_types: vec![HeapValueType::Array { size: initial_matrix.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: initial_matrix.len(), })], input_value_types: vec![HeapValueType::Array { - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], size: initial_matrix.len(), }], }, @@ -1338,7 +1407,10 @@ mod tests { // Check result in memory let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); - assert_eq!(result_values, expected_result); + assert_eq!( + result_values.into_iter().map(|mem_value| mem_value.value).collect::>(), + expected_result + ); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); @@ -1354,10 +1426,10 @@ mod tests { let r_output_size = MemoryAddress::from(3); // Our first string to use the identity function with - let input_string = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; + let input_string: Vec = + vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; // Double the string (concatenate it with itself) - let mut output_string: Vec = + let mut output_string: Vec<_> = input_string.iter().cloned().chain(input_string.clone()).collect(); // Reverse the concatenated string output_string.reverse(); @@ -1370,24 +1442,24 @@ mod tests { offset: 0, }, // input_pointer = 4 - Opcode::Const { destination: r_input_pointer, value: Value::from(4u128), bit_size: 32 }, + Opcode::Const { destination: r_input_pointer, value: (4u128).into(), bit_size: 64 }, // input_size = input_string.len() (constant here) Opcode::Const { destination: r_input_size, - value: Value::from(input_string.len()), - bit_size: 32, + value: input_string.len().into(), + bit_size: 64, }, // output_pointer = 4 + input_size Opcode::Const { destination: r_output_pointer, - value: Value::from(4 + input_string.len()), - bit_size: 32, + value: (4 + input_string.len()).into(), + bit_size: 64, }, // output_size = input_size * 2 Opcode::Const { destination: r_output_size, - value: Value::from(input_string.len() * 2), - bit_size: 32, + value: (input_string.len() * 2).into(), + bit_size: 64, }, // output_pointer[0..output_size] = string_double(input_pointer[0...input_size]) Opcode::ForeignCall { @@ -1397,14 +1469,14 @@ mod tests { size: r_output_size, })], destination_value_types: vec![HeapValueType::Vector { - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ValueOrArray::HeapVector(HeapVector { pointer: r_input_pointer, size: r_input_size, })], input_value_types: vec![HeapValueType::Vector { - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], }, ]; @@ -1432,10 +1504,12 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm + let result_values: Vec<_> = vm .memory .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) - .to_vec(); + .iter() + .map(|mem_val| mem_val.value) + .collect(); assert_eq!(result_values, output_string); // Ensure the foreign call counter has been incremented @@ -1448,12 +1522,11 @@ mod tests { let r_output = MemoryAddress::from(1); // Define a simple 2x2 matrix in memory - let initial_matrix = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; + let initial_matrix = vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = - vec![Value::from(1u128), Value::from(3u128), Value::from(2u128), Value::from(4u128)]; + let expected_result: Vec = + vec![(1u128).into(), (3u128).into(), (2u128).into(), (4u128).into()]; let invert_program = vec![ Opcode::CalldataCopy { @@ -1462,9 +1535,9 @@ mod tests { offset: 0, }, // input = 0 - Opcode::Const { destination: r_input, value: Value::from(2u128), bit_size: 32 }, + Opcode::Const { destination: r_input, value: (2u128).into(), bit_size: 64 }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(6u128), bit_size: 32 }, + Opcode::Const { destination: r_output, value: (6u128).into(), bit_size: 64 }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), @@ -1474,7 +1547,7 @@ mod tests { })], destination_value_types: vec![HeapValueType::Array { size: initial_matrix.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, @@ -1482,7 +1555,7 @@ mod tests { })], input_value_types: vec![HeapValueType::Array { size: initial_matrix.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], }, ]; @@ -1508,11 +1581,13 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check initial memory still in place - let initial_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); + let initial_values: Vec<_> = + vm.memory.read_slice(MemoryAddress(2), 4).iter().map(|mem_val| mem_val.value).collect(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values = vm.memory.read_slice(MemoryAddress(6), 4).to_vec(); + let result_values: Vec<_> = + vm.memory.read_slice(MemoryAddress(6), 4).iter().map(|mem_val| mem_val.value).collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1526,23 +1601,13 @@ mod tests { let r_output = MemoryAddress::from(2); // Define a simple 2x2 matrix in memory - let matrix_a = - vec![Value::from(1u128), Value::from(2u128), Value::from(3u128), Value::from(4u128)]; - - let matrix_b = vec![ - Value::from(10u128), - Value::from(11u128), - Value::from(12u128), - Value::from(13u128), - ]; + let matrix_a = vec![(1u128).into(), (2u128).into(), (3u128).into(), (4u128).into()]; + + let matrix_b = vec![(10u128).into(), (11u128).into(), (12u128).into(), (13u128).into()]; // Transpose of the matrix (but arbitrary for this test, the 'correct value') - let expected_result = vec![ - Value::from(34u128), - Value::from(37u128), - Value::from(78u128), - Value::from(85u128), - ]; + let expected_result: Vec = + vec![(34u128).into(), (37u128).into(), (78u128).into(), (85u128).into()]; let matrix_mul_program = vec![ Opcode::CalldataCopy { @@ -1551,11 +1616,11 @@ mod tests { offset: 0, }, // input = 3 - Opcode::Const { destination: r_input_a, value: Value::from(3u128), bit_size: 32 }, + Opcode::Const { destination: r_input_a, value: (3u128).into(), bit_size: 64 }, // input = 7 - Opcode::Const { destination: r_input_b, value: Value::from(7u128), bit_size: 32 }, + Opcode::Const { destination: r_input_b, value: (7u128).into(), bit_size: 64 }, // output = 0 - Opcode::Const { destination: r_output, value: Value::from(0u128), bit_size: 32 }, + Opcode::Const { destination: r_output, value: (0u128).into(), bit_size: 64 }, // *output = matrix_2x2_transpose(*input) Opcode::ForeignCall { function: "matrix_2x2_transpose".into(), @@ -1565,7 +1630,7 @@ mod tests { })], destination_value_types: vec![HeapValueType::Array { size: matrix_a.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }], inputs: vec![ ValueOrArray::HeapArray(HeapArray { pointer: r_input_a, size: matrix_a.len() }), @@ -1574,11 +1639,11 @@ mod tests { input_value_types: vec![ HeapValueType::Array { size: matrix_a.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }, HeapValueType::Array { size: matrix_b.len(), - value_types: vec![HeapValueType::Simple], + value_types: vec![HeapValueType::field()], }, ], }, @@ -1606,7 +1671,8 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values = vm.memory.read_slice(MemoryAddress(0), 4).to_vec(); + let result_values: Vec<_> = + vm.memory.read_slice(MemoryAddress(0), 4).iter().map(|mem_val| mem_val.value).collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1617,47 +1683,54 @@ mod tests { fn foreign_call_opcode_nested_arrays_and_slices_input() { // [(1, <2,3>, [4]), (5, <6,7,8>, [9])] - let v2 = vec![Value::from(2u128), Value::from(3u128)]; - let a4 = vec![Value::from(4u128)]; - let v6 = vec![Value::from(6u128), Value::from(7u128), Value::from(8u128)]; - let a9 = vec![Value::from(9u128)]; + let v2: Vec = vec![ + MemoryValue::from(FieldElement::from(2u128)), + MemoryValue::from(FieldElement::from(3u128)), + ]; + let a4: Vec = vec![FieldElement::from(4u128).into()]; + let v6: Vec = vec![ + MemoryValue::from(FieldElement::from(6u128)), + MemoryValue::from(FieldElement::from(7u128)), + MemoryValue::from(FieldElement::from(8u128)), + ]; + let a9: Vec = vec![FieldElement::from(9u128).into()]; // construct memory by declaring all inner arrays/vectors first - let v2_ptr = 0u128; + let v2_ptr: usize = 0usize; let mut memory = v2.clone(); let v2_start = memory.len(); - memory.extend(vec![Value::from(v2_ptr), Value::from(v2.len()), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(v2_ptr), v2.len().into(), MemoryValue::from(1_usize)]); let a4_ptr = memory.len(); memory.extend(a4.clone()); let a4_start = memory.len(); - memory.extend(vec![Value::from(a4_ptr), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(a4_ptr), MemoryValue::from(1_usize)]); let v6_ptr = memory.len(); memory.extend(v6.clone()); let v6_start = memory.len(); - memory.extend(vec![Value::from(v6_ptr), Value::from(v6.len()), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(v6_ptr), v6.len().into(), MemoryValue::from(1_usize)]); let a9_ptr = memory.len(); memory.extend(a9.clone()); let a9_start = memory.len(); - memory.extend(vec![Value::from(a9_ptr), Value::from(1u128)]); + memory.extend(vec![MemoryValue::from(a9_ptr), MemoryValue::from(1_usize)]); // finally we add the contents of the outer array let outer_ptr = memory.len(); let outer_array = vec![ - Value::from(1u128), - Value::from(v2.len()), - Value::from(v2_start), - Value::from(a4_start), - Value::from(5u128), - Value::from(v6.len()), - Value::from(v6_start), - Value::from(a9_start), + MemoryValue::from(FieldElement::from(1u128)), + MemoryValue::from(v2.len()), + MemoryValue::from(v2_start), + MemoryValue::from(a4_start), + MemoryValue::from(FieldElement::from(5u128)), + MemoryValue::from(v6.len()), + MemoryValue::from(v6_start), + MemoryValue::from(a9_start), ]; memory.extend(outer_array.clone()); - let input_array_value_types = vec![ - HeapValueType::Simple, - HeapValueType::Simple, // size of following vector - HeapValueType::Vector { value_types: vec![HeapValueType::Simple] }, - HeapValueType::Array { value_types: vec![HeapValueType::Simple], size: 1 }, + let input_array_value_types: Vec = vec![ + HeapValueType::field(), + HeapValueType::Simple(64), // size of following vector + HeapValueType::Vector { value_types: vec![HeapValueType::field()] }, + HeapValueType::Array { value_types: vec![HeapValueType::field()], size: 1 }, ]; // memory address of the end of the above data structures @@ -1666,19 +1739,24 @@ mod tests { let r_input = MemoryAddress::from(r_ptr); let r_output = MemoryAddress::from(r_ptr + 1); - let program = vec![ - Opcode::CalldataCopy { - destination_address: MemoryAddress::from(0), - size: memory.len(), - offset: 0, - }, + let program: Vec<_> = std::iter::once(Opcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: memory.len(), + offset: 0, + }) + .chain(memory.iter().enumerate().map(|(index, mem_value)| Opcode::Cast { + destination: MemoryAddress(index), + source: MemoryAddress(index), + bit_size: mem_value.bit_size, + })) + .chain(vec![ // input = 0 - Opcode::Const { destination: r_input, value: Value::from(outer_ptr), bit_size: 32 }, + Opcode::Const { destination: r_input, value: (outer_ptr).into(), bit_size: 64 }, // some_function(input) Opcode::ForeignCall { function: "flat_sum".into(), destinations: vec![ValueOrArray::MemoryAddress(r_output)], - destination_value_types: vec![HeapValueType::Simple], + destination_value_types: vec![HeapValueType::field()], inputs: vec![ValueOrArray::HeapArray(HeapArray { pointer: r_input, size: outer_array.len(), @@ -1688,9 +1766,13 @@ mod tests { size: outer_array.len(), }], }, - ]; + ]) + .collect(); - let mut vm = brillig_execute_and_get_vm(memory, &program); + let mut vm = brillig_execute_and_get_vm( + memory.into_iter().map(|mem_value| mem_value.value).collect(), + &program, + ); // Check that VM is waiting assert_eq!( @@ -1698,23 +1780,23 @@ mod tests { VMStatus::ForeignCallWait { function: "flat_sum".into(), inputs: vec![ForeignCallParam::Array(vec![ - Value::from(1u128), - Value::from(2u128), // size of following vector - Value::from(2u128), - Value::from(3u128), - Value::from(4u128), - Value::from(5u128), - Value::from(3u128), // size of following vector - Value::from(6u128), - Value::from(7u128), - Value::from(8u128), - Value::from(9u128), + (1u128).into(), + (2u128).into(), // size of following vector + (2u128).into(), + (3u128).into(), + (4u128).into(), + (5u128).into(), + (3u128).into(), // size of following vector + (6u128).into(), + (7u128).into(), + (8u128).into(), + (9u128).into(), ])], } ); // Push result we're waiting for - vm.resolve_foreign_call(Value::from(45u128).into()); + vm.resolve_foreign_call(FieldElement::from(45u128).into()); // Resume VM brillig_execute(&mut vm); @@ -1724,7 +1806,7 @@ mod tests { // Check result let result_value = vm.memory.read(r_output); - assert_eq!(result_value, Value::from(45u128)); + assert_eq!(result_value, MemoryValue::from(FieldElement::from(45u128))); // Ensure the foreign call counter has been incremented assert_eq!(vm.foreign_call_counter, 1); diff --git a/acvm-repo/brillig_vm/src/memory.rs b/acvm-repo/brillig_vm/src/memory.rs index d1c81447170..d563e13be2e 100644 --- a/acvm-repo/brillig_vm/src/memory.rs +++ b/acvm-repo/brillig_vm/src/memory.rs @@ -1,30 +1,182 @@ use acir::{brillig::MemoryAddress, FieldElement}; -use crate::Value; +pub const MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct MemoryValue { + pub value: FieldElement, + pub bit_size: u32, +} + +#[derive(Debug, thiserror::Error)] +pub enum MemoryTypeError { + #[error("Bit size for value {value_bit_size} does not match the expected bit size {expected_bit_size}")] + MismatchedBitSize { value_bit_size: u32, expected_bit_size: u32 }, +} + +impl MemoryValue { + pub fn new(value: FieldElement, bit_size: u32) -> Self { + MemoryValue { value, bit_size } + } + + pub fn new_checked(value: FieldElement, bit_size: u32) -> Option { + if value.num_bits() > bit_size { + return None; + } + + Some(MemoryValue::new(value, bit_size)) + } + + pub fn new_field(value: FieldElement) -> Self { + MemoryValue { value, bit_size: FieldElement::max_num_bits() } + } + + pub fn to_usize(&self) -> usize { + assert!(self.bit_size == MEMORY_ADDRESSING_BIT_SIZE, "value is not typed as brillig usize"); + self.value.to_u128() as usize + } + + pub fn expect_bit_size(&self, expected_bit_size: u32) -> Result<(), MemoryTypeError> { + if self.bit_size != expected_bit_size { + return Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: self.bit_size, + expected_bit_size, + }); + } + Ok(()) + } +} + +impl std::fmt::Display for MemoryValue { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { + let typ = match self.bit_size { + 0 => "null".to_string(), + 1 => "bool".to_string(), + _ if self.bit_size == FieldElement::max_num_bits() => "field".to_string(), + _ => format!("u{}", self.bit_size), + }; + f.write_str(format!("{}: {}", self.value, typ).as_str()) + } +} + +impl Default for MemoryValue { + fn default() -> Self { + MemoryValue::new(FieldElement::zero(), 0) + } +} + +impl From for MemoryValue { + fn from(field: FieldElement) -> Self { + MemoryValue::new_field(field) + } +} + +impl From for MemoryValue { + fn from(value: usize) -> Self { + MemoryValue::new(value.into(), MEMORY_ADDRESSING_BIT_SIZE) + } +} + +impl From for MemoryValue { + fn from(value: u64) -> Self { + MemoryValue::new((value as u128).into(), 64) + } +} + +impl From for MemoryValue { + fn from(value: u32) -> Self { + MemoryValue::new((value as u128).into(), 32) + } +} + +impl From for MemoryValue { + fn from(value: u8) -> Self { + MemoryValue::new((value as u128).into(), 8) + } +} + +impl From for MemoryValue { + fn from(value: bool) -> Self { + MemoryValue::new(value.into(), 1) + } +} + +impl TryFrom for FieldElement { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(FieldElement::max_num_bits())?; + Ok(memory_value.value) + } +} + +impl TryFrom for u64 { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(64)?; + Ok(memory_value.value.to_u128() as u64) + } +} + +impl TryFrom for u32 { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(32)?; + Ok(memory_value.value.to_u128() as u32) + } +} + +impl TryFrom for u8 { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(8)?; + + Ok(memory_value.value.to_u128() as u8) + } +} + +impl TryFrom for bool { + type Error = MemoryTypeError; + + fn try_from(memory_value: MemoryValue) -> Result { + memory_value.expect_bit_size(1)?; + + if memory_value.value == FieldElement::zero() { + Ok(false) + } else if memory_value.value == FieldElement::one() { + Ok(true) + } else { + unreachable!("value typed as bool is greater than one") + } + } +} #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Memory { // Memory is a vector of values. // We grow the memory when values past the end are set, extending with 0s. - inner: Vec, + inner: Vec, } impl Memory { /// Gets the value at pointer - pub fn read(&self, ptr: MemoryAddress) -> Value { - self.inner.get(ptr.to_usize()).copied().unwrap_or(0_u128.into()) + pub fn read(&self, ptr: MemoryAddress) -> MemoryValue { + self.inner.get(ptr.to_usize()).copied().unwrap_or_default() } pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { MemoryAddress(self.read(ptr).to_usize()) } - pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[Value] { + pub fn read_slice(&self, addr: MemoryAddress, len: usize) -> &[MemoryValue] { &self.inner[addr.to_usize()..(addr.to_usize() + len)] } /// Sets the value at pointer `ptr` to `value` - pub fn write(&mut self, ptr: MemoryAddress, value: Value) { + pub fn write(&mut self, ptr: MemoryAddress, value: MemoryValue) { self.resize_to_fit(ptr.to_usize() + 1); self.inner[ptr.to_usize()] = value; } @@ -33,17 +185,17 @@ impl Memory { // Calculate new memory size let new_size = std::cmp::max(self.inner.len(), size); // Expand memory to new size with default values if needed - self.inner.resize(new_size, Value::from(FieldElement::zero())); + self.inner.resize(new_size, MemoryValue::default()); } /// Sets the values after pointer `ptr` to `values` - pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[Value]) { + pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[MemoryValue]) { self.resize_to_fit(ptr.to_usize() + values.len()); self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].copy_from_slice(values); } /// Returns the values of the memory - pub fn values(&self) -> &[Value] { + pub fn values(&self) -> &[MemoryValue] { &self.inner } } diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 0fe450e6cb1..3ee6f9c21b9 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -4,7 +4,7 @@ mod utils; use transforms::{ compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, events::{generate_selector_impl, transform_events}, - functions::{transform_function, transform_unconstrained, transform_vm_function}, + functions::{transform_function, transform_unconstrained}, note_interface::generate_note_interface_impl, storage::{ assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, @@ -138,9 +138,15 @@ fn transform_module(module: &mut SortedModule) -> Result } // Apply transformations to the function based on collected attributes - if is_private || is_public { + if is_private || is_public || is_public_vm { transform_function( - if is_private { "Private" } else { "Public" }, + if is_private { + "Private" + } else if is_public_vm { + "Avm" + } else { + "Public" + }, func, storage_defined, is_initializer, @@ -148,9 +154,6 @@ fn transform_module(module: &mut SortedModule) -> Result is_internal, )?; has_transformed_module = true; - } else if is_public_vm { - transform_vm_function(func, storage_defined)?; - has_transformed_module = true; } else if storage_defined && func.def.is_unconstrained { transform_unconstrained(func); has_transformed_module = true; diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index 855969732d0..9844abc30fe 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -35,6 +35,7 @@ pub fn transform_function( let context_name = format!("{}Context", ty); let inputs_name = format!("{}ContextInputs", ty); let return_type_name = format!("{}CircuitPublicInputs", ty); + let is_avm = ty == "Avm"; // Add check that msg sender equals this address and flag function as internal if is_internal { @@ -60,7 +61,11 @@ pub fn transform_function( } // Insert the context creation as the first action - let create_context = create_context(&context_name, &func.def.parameters)?; + let create_context = if !is_avm { + create_context(&context_name, &func.def.parameters)? + } else { + create_context_avm()? + }; func.def.body.statements.splice(0..0, (create_context).iter().cloned()); // Add the inputs to the params @@ -68,12 +73,14 @@ pub fn transform_function( func.def.parameters.insert(0, input); // Abstract return types such that they get added to the kernel's return_values - if let Some(return_values) = abstract_return_values(func) { - // In case we are pushing return values to the context, we remove the statement that originated it - // This avoids running duplicate code, since blocks like if/else can be value returning statements - func.def.body.statements.pop(); - // Add the new return statement - func.def.body.statements.push(return_values); + if !is_avm { + if let Some(return_values) = abstract_return_values(func) { + // In case we are pushing return values to the context, we remove the statement that originated it + // This avoids running duplicate code, since blocks like if/else can be value returning statements + func.def.body.statements.pop(); + // Add the new return statement + func.def.body.statements.push(return_values); + } } // Before returning mark the contract as initialized @@ -83,45 +90,29 @@ pub fn transform_function( } // Push the finish method call to the end of the function - let finish_def = create_context_finish(); - func.def.body.statements.push(finish_def); + if !is_avm { + let finish_def = create_context_finish(); + func.def.body.statements.push(finish_def); + } - let return_type = create_return_type(&return_type_name); - func.def.return_type = return_type; - func.def.return_visibility = Visibility::Public; + // The AVM doesn't need a return type yet. + if !is_avm { + let return_type = create_return_type(&return_type_name); + func.def.return_type = return_type; + func.def.return_visibility = Visibility::Public; + } // Distinct return types are only required for private functions // Public functions should have unconstrained auto-inferred match ty { "Private" => func.def.return_distinctness = Distinctness::Distinct, - "Public" => func.def.is_unconstrained = true, + "Public" | "Avm" => func.def.is_unconstrained = true, _ => (), } Ok(()) } -/// Transform a function to work with AVM bytecode -pub fn transform_vm_function( - func: &mut NoirFunction, - storage_defined: bool, -) -> Result<(), AztecMacroError> { - // Create access to storage - if storage_defined { - let storage = abstract_storage("public_vm", true); - func.def.body.statements.insert(0, storage); - } - - // Push Avm context creation to the beginning of the function - let create_context = create_avm_context()?; - func.def.body.statements.insert(0, create_context); - - // We want the function to be seen as a public function - func.def.is_unconstrained = true; - - Ok(()) -} - /// Transform Unconstrained /// /// Inserts the following code at the beginning of an unconstrained function @@ -232,62 +223,62 @@ fn create_assert_initializer() -> Statement { /// ```noir /// #[aztec(private)] /// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the hasher object -/// let mut hasher = Hasher::new(); +/// // Create the bounded vec object +/// let mut serialized_args = BoundedVec::new(); /// /// // struct inputs call serialize on them to add an array of fields -/// hasher.add_multiple(structInput.serialize()); +/// serialized_args.extend_from_array(structInput.serialize()); /// -/// // Array inputs are iterated over and each element is added to the hasher (as a field) +/// // Array inputs are iterated over and each element is added to the bounded vec (as a field) /// for i in 0..arrayInput.len() { -/// hasher.add(arrayInput[i] as Field); +/// serialized_args.push(arrayInput[i] as Field); /// } -/// // Field inputs are added to the hasher -/// hasher.add({ident}); +/// // Field inputs are added to the bounded vec +/// serialized_args.push({ident}); /// /// // Create the context /// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hasher.hash()); +/// let mut context = PrivateContext::new(inputs, hash_args(serialized_args)); /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { let mut injected_expressions: Vec = vec![]; - // `let mut hasher = Hasher::new();` - let let_hasher = mutable_assignment( - "hasher", // Assigned to + // `let mut serialized_args = BoundedVec::new();` + let let_serialized_args = mutable_assignment( + "serialized_args", // Assigned to call( - variable_path(chained_dep!("aztec", "hasher", "Hasher", "new")), // Path - vec![], // args + variable_path(chained_dep!("std", "collections", "bounded_vec", "BoundedVec", "new")), // Path + vec![], // args ), ); - // Completes: `let mut hasher = Hasher::new();` - injected_expressions.push(let_hasher); + // Completes: `let mut serialized_args = BoundedVec::new();` + injected_expressions.push(let_serialized_args); - // Iterate over each of the function parameters, adding to them to the hasher + // Iterate over each of the function parameters, adding to them to the bounded vec for Param { pattern, typ, span, .. } in params { match pattern { Pattern::Identifier(identifier) => { // Match the type to determine the padding to do let unresolved_type = &typ.typ; let expression = match unresolved_type { - // `hasher.add_multiple({ident}.serialize())` - UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier), + // `serialized_args.extend_from_array({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_serialized_args(identifier), UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type) + add_array_to_serialized_args(identifier, arr_type) } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => add_field_to_hasher(identifier), - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident})` + UnresolvedTypeData::FieldElement => add_field_to_serialized_args(identifier), + // Add the integer to the serialized args, casted to a field + // `serialized_args.push({ident} as Field)` UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier) + add_cast_to_serialized_args(identifier) } UnresolvedTypeData::String(..) => { let (var_bytes, id) = str_to_bytes(identifier); injected_expressions.push(var_bytes); - add_array_to_hasher( + add_array_to_serialized_args( &id, &UnresolvedType { typ: UnresolvedTypeData::Integer( @@ -313,11 +304,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac // Create the inputs to the context let inputs_expression = variable("inputs"); - // `hasher.hash()` - let hash_call = method_call( - variable("hasher"), // variable - "hash", // method name - vec![], // args + // `hash_args(serialized_args)` + let hash_call = call( + variable_path(chained_dep!("aztec", "hash", "hash_args")), // variable + vec![variable("serialized_args")], // args ); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context @@ -336,33 +326,36 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac Ok(injected_expressions) } -/// Creates an mutable avm context +/// Creates the private context object to be accessed within the function, the parameters need to be extracted to be +/// appended into the args hash object. /// +/// The replaced code: /// ```noir -/// /// Before /// #[aztec(public-vm)] -/// fn foo() -> Field { -/// let mut context = aztec::context::AVMContext::new(); -/// let timestamp = context.timestamp(); -/// // ... -/// } -/// -/// /// After -/// #[aztec(private)] -/// fn foo() -> Field { -/// let mut timestamp = context.timestamp(); -/// // ... +/// fn foo(inputs: AvmContextInputs, ...) -> Field { +/// let mut context = AvmContext::new(inputs); /// } -fn create_avm_context() -> Result { +/// ``` +fn create_context_avm() -> Result, AztecMacroError> { + let mut injected_expressions: Vec = vec![]; + + // Create the inputs to the context + let ty = "AvmContext"; + let inputs_expression = variable("inputs"); + let path_snippet = ty.to_case(Case::Snake); // e.g. private_context + + // let mut context = {ty}::new(inputs, hash); let let_context = mutable_assignment( "context", // Assigned to call( - variable_path(chained_dep!("aztec", "context", "AVMContext", "new")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "context", &path_snippet, ty, "new")), // Path + vec![inputs_expression], // args ), ); + injected_expressions.push(let_context); - Ok(let_context) + // Return all expressions that will be injected by the hasher + Ok(injected_expressions) } /// Abstract Return Type @@ -598,11 +591,11 @@ fn create_context_finish() -> Statement { } // -// Methods to create hasher inputs +// Methods to create hash_args inputs // -fn add_struct_to_hasher(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the hasher +fn add_struct_to_serialized_args(identifier: &Ident) -> Statement { + // If this is a struct, we call serialize and add the array to the serialized args let serialized_call = method_call( variable_path(path(identifier.clone())), // variable "serialize", // method name @@ -610,9 +603,9 @@ fn add_struct_to_hasher(identifier: &Ident) -> Statement { ); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add_multiple", // method name - vec![serialized_call], // args + variable("serialized_args"), // variable + "extend_from_array", // method name + vec![serialized_call], // args ))) } @@ -632,7 +625,7 @@ fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { } fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher + // If this is an array of primitive types (integers / fields) we can add them each to the serialized args // casted to a field let span = var.span; @@ -644,7 +637,8 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { ); // What will be looped over - // - `hasher.add({ident}[i] as Field)` + + // - `serialized_args.push({ident}[i] as Field)` let for_loop_block = expression(ExpressionKind::Block(BlockExpression { statements: loop_body })); @@ -663,66 +657,66 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { })) } -fn add_array_to_hasher(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the hasher +fn add_array_to_serialized_args(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the serialized_args // casted to a field // Wrap in the semi thing - does that mean ended with semi colon? - // `hasher.add({ident}[i] as Field)` + // `serialized_args.push({ident}[i] as Field)` let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, hasher_method_name) = match arr_type.typ { + let (add_expression, vec_method_name) = match arr_type.typ { UnresolvedTypeData::Named(..) => { - let hasher_method_name = "add_multiple".to_owned(); + let vec_method_name = "extend_from_array".to_owned(); let call = method_call( // All serialize on each element arr_index, // variable "serialize", // method name vec![], // args ); - (call, hasher_method_name) + (call, vec_method_name) } _ => { - let hasher_method_name = "add".to_owned(); + let vec_method_name = "push".to_owned(); let call = cast( arr_index, // lhs - `ident[i]` UnresolvedTypeData::FieldElement, // cast to - `as Field` ); - (call, hasher_method_name) + (call, vec_method_name) } }; let block_statement = make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - &hasher_method_name, // method name + variable("serialized_args"), // variable + &vec_method_name, // method name vec![add_expression], ))); create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) } -fn add_field_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident})` +fn add_field_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident})` let ident = variable_path(path(identifier.clone())); make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![ident], // args + variable("serialized_args"), // variable + "push", // method name + vec![ident], // args ))) } -fn add_cast_to_hasher(identifier: &Ident) -> Statement { - // `hasher.add({ident} as Field)` +fn add_cast_to_serialized_args(identifier: &Ident) -> Statement { + // `serialized_args.push({ident} as Field)` // `{ident} as Field` let cast_operation = cast( variable_path(path(identifier.clone())), // lhs UnresolvedTypeData::FieldElement, // rhs ); - // `hasher.add({ident} as Field)` + // `serialized_args.push({ident} as Field)` make_statement(StatementKind::Semi(method_call( - variable("hasher"), // variable - "add", // method name - vec![cast_operation], // args + variable("serialized_args"), // variable + "push", // method name + vec![cast_operation], // args ))) } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 36e5c99a2ca..874be06b86c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -1,7 +1,7 @@ use acvm::acir::{brillig::BlackBoxOp, BlackBoxFunc}; use crate::brillig::brillig_ir::{ - brillig_variable::{BrilligVariable, BrilligVector}, + brillig_variable::{BrilligVariable, BrilligVector, SingleAddrVariable}, BrilligContext, }; @@ -56,17 +56,23 @@ pub(crate) fn convert_black_box_call( } BlackBoxFunc::Keccak256 => { if let ( - [message, BrilligVariable::SingleAddr(array_size)], + [message, BrilligVariable::SingleAddr(message_size)], [BrilligVariable::BrilligArray(result_array)], ) = (function_arguments, function_results) { let mut message_vector = convert_array_or_vector(brillig_context, message, bb_func); - message_vector.size = array_size.address; + let message_size_as_usize = + SingleAddrVariable::new_usize(brillig_context.allocate_register()); + // Message_size is not usize + brillig_context.cast_instruction(message_size_as_usize, *message_size); + + message_vector.size = message_size_as_usize.address; brillig_context.black_box_op_instruction(BlackBoxOp::Keccak256 { message: message_vector.to_heap_vector(), output: result_array.to_heap_array(), }); + brillig_context.deallocate_single_addr(message_size_as_usize); } else { unreachable!("ICE: Keccak256 expects message, message size and result array") } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 94e81d02053..cf2501ab1c0 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -514,7 +514,7 @@ impl<'block> BrilligBlock<'block> { .extract_vector(); // Update the user-facing slice length - self.brillig_context.mov_instruction(target_len.address, limb_count.address); + self.brillig_context.cast_instruction(target_len, limb_count); self.brillig_context.codegen_to_radix( source, @@ -522,6 +522,7 @@ impl<'block> BrilligBlock<'block> { radix, limb_count, matches!(endianness, Endian::Big), + 8, ); } Value::Intrinsic(Intrinsic::ToBits(endianness)) => { @@ -554,7 +555,7 @@ impl<'block> BrilligBlock<'block> { let radix = self.brillig_context.make_constant_instruction(2_usize.into(), 32); // Update the user-facing slice length - self.brillig_context.mov_instruction(target_len.address, limb_count.address); + self.brillig_context.cast_instruction(target_len, limb_count); self.brillig_context.codegen_to_radix( source, @@ -562,6 +563,7 @@ impl<'block> BrilligBlock<'block> { radix, limb_count, matches!(endianness, Endian::Big), + 1, ); self.brillig_context.deallocate_single_addr(radix); @@ -654,7 +656,7 @@ impl<'block> BrilligBlock<'block> { // Create a field constant with the max let max = BigUint::from(2_u128).pow(*max_bit_size) - BigUint::from(1_u128); let right = self.brillig_context.make_constant_instruction( - FieldElement::from_be_bytes_reduce(&max.to_bytes_be()).into(), + FieldElement::from_be_bytes_reduce(&max.to_bytes_be()), FieldElement::max_num_bits(), ); @@ -1584,7 +1586,7 @@ impl<'block> BrilligBlock<'block> { self.variables.allocate_constant(self.brillig_context, value_id, dfg); self.brillig_context - .const_instruction(new_variable.extract_single_addr(), (*constant).into()); + .const_instruction(new_variable.extract_single_addr(), *constant); new_variable } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs index dc9900daee3..892e82d771a 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block_variables.rs @@ -2,7 +2,10 @@ use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ brillig::brillig_ir::{ - brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, + brillig_variable::{ + get_bit_size_from_ssa_type, BrilligArray, BrilligVariable, BrilligVector, + SingleAddrVariable, + }, BrilligContext, }, ssa::ir::{ @@ -13,7 +16,7 @@ use crate::{ }, }; -use super::brillig_fn::{get_bit_size_from_ssa_type, FunctionContext}; +use super::brillig_fn::FunctionContext; #[derive(Debug, Default)] pub(crate) struct BlockVariables { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index 26b21e918ff..15a2a531e78 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -1,5 +1,5 @@ use acvm::{ - acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode, Value}, + acir::brillig::{BinaryFieldOp, BinaryIntOp, MemoryAddress, Opcode as BrilligOpcode}, FieldElement, }; @@ -16,18 +16,32 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { // We store the result in this register too. let input = MemoryAddress::from(0); let one_const = MemoryAddress::from(1); + let zero_const = MemoryAddress::from(2); + let input_is_zero = MemoryAddress::from(3); // Location of the stop opcode - let stop_location = 3; + let stop_location = 6; GeneratedBrillig { byte_code: vec![ BrilligOpcode::CalldataCopy { destination_address: input, size: 1, offset: 0 }, + // Put value zero in register (2) + BrilligOpcode::Const { + destination: zero_const, + value: FieldElement::from(0_usize), + bit_size: FieldElement::max_num_bits(), + }, + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::Equals, + lhs: input, + rhs: zero_const, + destination: input_is_zero, + }, // If the input is zero, then we jump to the stop opcode - BrilligOpcode::JumpIfNot { condition: input, location: stop_location }, + BrilligOpcode::JumpIf { condition: input_is_zero, location: stop_location }, // Put value one in register (1) BrilligOpcode::Const { destination: one_const, - value: Value::from(1_usize), + value: FieldElement::from(1_usize), bit_size: FieldElement::max_num_bits(), }, // Divide 1 by the input, and set the result of the division into register (0) @@ -53,9 +67,12 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { /// (a/b, a-a/b*b) /// } /// ``` -pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { +pub(crate) fn directive_quotient(mut bit_size: u32) -> GeneratedBrillig { // `a` is (0) (i.e register index 0) // `b` is (1) + if bit_size > FieldElement::max_num_bits() { + bit_size = FieldElement::max_num_bits(); + } GeneratedBrillig { byte_code: vec![ BrilligOpcode::CalldataCopy { @@ -63,6 +80,16 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { size: 2, offset: 0, }, + BrilligOpcode::Cast { + destination: MemoryAddress(0), + source: MemoryAddress(0), + bit_size, + }, + BrilligOpcode::Cast { + destination: MemoryAddress(1), + source: MemoryAddress(1), + bit_size, + }, //q = a/b is set into register (2) BrilligOpcode::BinaryIntOp { op: BinaryIntOp::Div, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index 42765d10ce2..92027026ce8 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -3,8 +3,8 @@ use iter_extended::vecmap; use crate::{ brillig::brillig_ir::{ artifact::{BrilligParameter, Label}, - brillig_variable::BrilligVariable, - BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + brillig_variable::{get_bit_size_from_ssa_type, BrilligVariable}, + BrilligContext, }, ssa::ir::{ basic_block::BasicBlockId, @@ -112,15 +112,3 @@ impl FunctionContext { .collect() } } - -pub(crate) fn get_bit_size_from_ssa_type(typ: &Type) -> u32 { - match typ { - Type::Numeric(num_type) => num_type.bit_size(), - Type::Reference(_) => BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, - // NB. function references are converted to a constant when - // translating from SSA to Brillig (to allow for debugger - // instrumentation to work properly) - Type::Function => 32, - _ => unreachable!("ICE bit size not on a non numeric type"), - } -} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index 98dd17ce080..e42b2787f73 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -354,7 +354,7 @@ impl<'block> BrilligBlock<'block> { mod tests { use std::vec; - use acvm::acir::brillig::Value; + use acvm::FieldElement; use crate::brillig::brillig_gen::brillig_block::BrilligBlock; use crate::brillig::brillig_gen::brillig_block_variables::BlockVariables; @@ -400,9 +400,9 @@ mod tests { fn test_slice_push_operation() { fn test_case_push( push_back: bool, - array: Vec, - item_to_push: Value, - expected_return: Vec, + array: Vec, + item_to_push: FieldElement, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array( @@ -462,44 +462,65 @@ mod tests { create_and_run_vm(array.into_iter().chain(vec![item_to_push]).collect(), &bytecode); assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_push( true, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], + FieldElement::from(27_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(27_usize), + ], + ); + test_case_push( + true, + vec![], + FieldElement::from(27_usize), + vec![FieldElement::from(27_usize)], ); - test_case_push(true, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); test_case_push( false, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), vec![ - Value::from(27_usize), - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + vec![ + FieldElement::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], ); - test_case_push(false, vec![], Value::from(27_usize), vec![Value::from(27_usize)]); + test_case_push( + false, + vec![], + FieldElement::from(27_usize), + vec![FieldElement::from(27_usize)], + ); } #[test] fn test_slice_pop_back_operation() { fn test_case_pop( pop_back: bool, - array: Vec, - expected_return_array: Vec, - expected_return_item: Value, + array: Vec, + expected_return_array: Vec, + expected_return_item: FieldElement, ) { let arguments = vec![BrilligParameter::Array( vec![BrilligParameter::SingleAddr(BRILLIG_MEMORY_ADDRESSING_BIT_SIZE)], @@ -566,33 +587,44 @@ mod tests { assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_pop( true, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![Value::from(1_usize), Value::from(2_usize)], - Value::from(3_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + vec![FieldElement::from(1_usize), FieldElement::from(2_usize)], + FieldElement::from(3_usize), ); - test_case_pop(true, vec![Value::from(1_usize)], vec![], Value::from(1_usize)); + test_case_pop(true, vec![FieldElement::from(1_usize)], vec![], FieldElement::from(1_usize)); test_case_pop( false, - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - vec![Value::from(2_usize), Value::from(3_usize)], - Value::from(1_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + vec![FieldElement::from(2_usize), FieldElement::from(3_usize)], + FieldElement::from(1_usize), ); } #[test] fn test_slice_insert_operation() { fn test_case_insert( - array: Vec, - item: Value, - index: Value, - expected_return: Vec, + array: Vec, + item: FieldElement, + index: FieldElement, + expected_return: Vec, ) { let arguments = vec![ BrilligParameter::Array( @@ -651,71 +683,90 @@ mod tests { assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(1_usize), vec![ - Value::from(1_usize), - Value::from(27_usize), - Value::from(2_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(1_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(27_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], ); test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(0_usize), vec![ - Value::from(27_usize), - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(0_usize), + vec![ + FieldElement::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), ], ); test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(2_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(27_usize), - Value::from(3_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(2_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(27_usize), + FieldElement::from(3_usize), ], ); test_case_insert( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(27_usize), - Value::from(3_usize), vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(27_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(27_usize), + FieldElement::from(3_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(27_usize), ], ); test_case_insert( vec![], - Value::from(27_usize), - Value::from(0_usize), - vec![Value::from(27_usize)], + FieldElement::from(27_usize), + FieldElement::from(0_usize), + vec![FieldElement::from(27_usize)], ); } #[test] fn test_slice_remove_operation() { fn test_case_remove( - array: Vec, - index: Value, - expected_array: Vec, - expected_removed_item: Value, + array: Vec, + index: FieldElement, + expected_array: Vec, + expected_removed_item: FieldElement, ) { let arguments = vec![ BrilligParameter::Array( @@ -784,36 +835,51 @@ mod tests { assert_eq!(return_data_size, expected_return.len()); assert_eq!( - vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())], + vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), expected_return ); } test_case_remove( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(0_usize), - vec![Value::from(2_usize), Value::from(3_usize)], - Value::from(1_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(0_usize), + vec![FieldElement::from(2_usize), FieldElement::from(3_usize)], + FieldElement::from(1_usize), ); test_case_remove( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(1_usize), - vec![Value::from(1_usize), Value::from(3_usize)], - Value::from(2_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(1_usize), + vec![FieldElement::from(1_usize), FieldElement::from(3_usize)], + FieldElement::from(2_usize), ); test_case_remove( - vec![Value::from(1_usize), Value::from(2_usize), Value::from(3_usize)], - Value::from(2_usize), - vec![Value::from(1_usize), Value::from(2_usize)], - Value::from(3_usize), + vec![ + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + ], + FieldElement::from(2_usize), + vec![FieldElement::from(1_usize), FieldElement::from(2_usize)], + FieldElement::from(3_usize), ); test_case_remove( - vec![Value::from(1_usize)], - Value::from(0_usize), + vec![FieldElement::from(1_usize)], + FieldElement::from(0_usize), vec![], - Value::from(1_usize), + FieldElement::from(1_usize), ); } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs index 9138f57083a..e5c731be679 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir.rs @@ -122,7 +122,7 @@ pub(crate) mod tests { use std::vec; use acvm::acir::brillig::{ - ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, Value, ValueOrArray, + ForeignCallParam, ForeignCallResult, HeapVector, MemoryAddress, ValueOrArray, }; use acvm::brillig_vm::brillig::HeapValueType; use acvm::brillig_vm::{VMStatus, VM}; @@ -205,7 +205,7 @@ pub(crate) mod tests { } pub(crate) fn create_and_run_vm( - calldata: Vec, + calldata: Vec, bytecode: &[BrilligOpcode], ) -> (VM<'_, DummyBlackBoxSolver>, usize, usize) { let mut vm = VM::new(calldata, bytecode, vec![], &DummyBlackBoxSolver); @@ -234,20 +234,20 @@ pub(crate) mod tests { let mut context = BrilligContext::new(true); let r_stack = ReservedRegisters::free_memory_pointer(); // Start stack pointer at 0 - context.usize_const_instruction(r_stack, Value::from(ReservedRegisters::len() + 3)); + context.usize_const_instruction(r_stack, FieldElement::from(ReservedRegisters::len() + 3)); let r_input_size = MemoryAddress::from(ReservedRegisters::len()); let r_array_ptr = MemoryAddress::from(ReservedRegisters::len() + 1); let r_output_size = MemoryAddress::from(ReservedRegisters::len() + 2); let r_equality = MemoryAddress::from(ReservedRegisters::len() + 3); - context.usize_const_instruction(r_input_size, Value::from(12_usize)); + context.usize_const_instruction(r_input_size, FieldElement::from(12_usize)); // copy our stack frame to r_array_ptr context.mov_instruction(r_array_ptr, r_stack); context.foreign_call_instruction( "make_number_sequence".into(), &[ValueOrArray::MemoryAddress(r_input_size)], - &[HeapValueType::Simple], + &[HeapValueType::Simple(32)], &[ValueOrArray::HeapVector(HeapVector { pointer: r_stack, size: r_output_size })], - &[HeapValueType::Vector { value_types: vec![HeapValueType::Simple] }], + &[HeapValueType::Vector { value_types: vec![HeapValueType::Simple(32)] }], ); // push stack frame by r_returned_size context.memory_op_instruction(r_stack, r_output_size, r_stack, BrilligBinaryOp::Add); @@ -266,8 +266,9 @@ pub(crate) mod tests { context.stop_instruction(); - let bytecode = context.artifact().finish().byte_code; - let number_sequence: Vec = (0_usize..12_usize).map(Value::from).collect(); + let bytecode: Vec = context.artifact().finish().byte_code; + let number_sequence: Vec = + (0_usize..12_usize).map(FieldElement::from).collect(); let mut vm = VM::new( vec![], &bytecode, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs index b415421dd92..bbfdbb69f7c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/brillig_variable.rs @@ -125,7 +125,9 @@ impl BrilligVariable { pub(crate) fn type_to_heap_value_type(typ: &Type) -> HeapValueType { match typ { - Type::Numeric(_) | Type::Reference(_) | Type::Function => HeapValueType::Simple, + Type::Numeric(_) | Type::Reference(_) | Type::Function => { + HeapValueType::Simple(get_bit_size_from_ssa_type(typ)) + } Type::Array(elem_type, size) => HeapValueType::Array { value_types: elem_type.as_ref().iter().map(type_to_heap_value_type).collect(), size: typ.element_size() * size, @@ -135,3 +137,14 @@ pub(crate) fn type_to_heap_value_type(typ: &Type) -> HeapValueType { }, } } + +pub(crate) fn get_bit_size_from_ssa_type(typ: &Type) -> u32 { + match typ { + Type::Reference(_) => BRILLIG_MEMORY_ADDRESSING_BIT_SIZE, + // NB. function references are converted to a constant when + // translating from SSA to Brillig (to allow for debugger + // instrumentation to work properly) + Type::Function => 32, + typ => typ.bit_size(), + } +} diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs index 248a304d820..4ef279bd532 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_binary.rs @@ -1,4 +1,4 @@ -use acvm::acir::brillig::{MemoryAddress, Value}; +use acvm::{acir::brillig::MemoryAddress, FieldElement}; use super::{instructions::BrilligBinaryOp, BrilligContext}; @@ -21,7 +21,7 @@ impl BrilligContext { op: BrilligBinaryOp, constant: usize, ) { - let const_register = self.make_usize_constant_instruction(Value::from(constant)); + let const_register = self.make_usize_constant_instruction(FieldElement::from(constant)); self.memory_op_instruction(operand, const_register.address, destination, op); // Mark as no longer used for this purpose, frees for reuse self.deallocate_single_addr(const_register); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs index be262d9dee7..ab756217bcd 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/codegen_intrinsic.rs @@ -39,6 +39,7 @@ impl BrilligContext { radix: SingleAddrVariable, limb_count: SingleAddrVariable, big_endian: bool, + limb_bit_size: u32, ) { assert!(source_field.bit_size == FieldElement::max_num_bits()); assert!(radix.bit_size == 32); @@ -55,19 +56,23 @@ impl BrilligContext { SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); self.mov_instruction(shifted_field.address, source_field.address); - let modulus_field = + let limb_field = SingleAddrVariable::new(self.allocate_register(), FieldElement::max_num_bits()); + let limb_casted = SingleAddrVariable::new(self.allocate_register(), limb_bit_size); + self.codegen_loop(target_vector.size, |ctx, iterator_register| { // Compute the modulus ctx.binary_instruction( shifted_field, radix_as_field, - modulus_field, + limb_field, BrilligBinaryOp::Modulo, ); + // Cast it + ctx.cast_instruction(limb_casted, limb_field); // Write it - ctx.codegen_array_set(target_vector.pointer, iterator_register, modulus_field.address); + ctx.codegen_array_set(target_vector.pointer, iterator_register, limb_casted.address); // Integer div the field ctx.binary_instruction( shifted_field, @@ -79,7 +84,8 @@ impl BrilligContext { // Deallocate our temporary registers self.deallocate_single_addr(shifted_field); - self.deallocate_single_addr(modulus_field); + self.deallocate_single_addr(limb_field); + self.deallocate_single_addr(limb_casted); self.deallocate_single_addr(radix_as_field); if big_endian { diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs index 5611905697c..4ca1144b6a4 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs @@ -2,7 +2,10 @@ use super::BrilligBinaryOp; use crate::brillig::brillig_ir::ReservedRegisters; -use acvm::acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, Value, ValueOrArray}; +use acvm::{ + acir::brillig::{BlackBoxOp, HeapArray, HeapVector, MemoryAddress, ValueOrArray}, + FieldElement, +}; /// Trait for converting values into debug-friendly strings. trait DebugToString { @@ -66,9 +69,9 @@ impl DebugToString for BrilligBinaryOp { } } -impl DebugToString for Value { +impl DebugToString for FieldElement { fn debug_to_string(&self) -> String { - self.to_usize().to_string() + self.to_string() } } @@ -167,7 +170,7 @@ impl DebugShow { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: Value) { + pub(crate) fn const_instruction(&self, result: MemoryAddress, constant: FieldElement) { debug_println!(self.enable_debug_trace, " CONST {} = {}", result, constant); } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 14c4ada8606..1d823ded718 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -453,7 +453,8 @@ impl BrilligContext { #[cfg(test)] mod tests { - use acvm::brillig_vm::brillig::Value; + + use acvm::FieldElement; use crate::brillig::brillig_ir::{ artifact::BrilligParameter, @@ -464,12 +465,12 @@ mod tests { #[test] fn entry_point_with_nested_array_parameter() { let calldata = vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(4_usize), + FieldElement::from(5_usize), + FieldElement::from(6_usize), ]; let arguments = vec![BrilligParameter::Array( vec![ @@ -496,18 +497,18 @@ mod tests { let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata.clone(), &bytecode); assert_eq!(return_data_size, 1, "Return data size is incorrect"); - assert_eq!(vm.get_memory()[return_data_offset], Value::from(1_usize)); + assert_eq!(vm.get_memory()[return_data_offset].value, FieldElement::from(1_usize)); } #[test] fn entry_point_with_nested_array_return() { let flattened_array = vec![ - Value::from(1_usize), - Value::from(2_usize), - Value::from(3_usize), - Value::from(4_usize), - Value::from(5_usize), - Value::from(6_usize), + FieldElement::from(1_usize), + FieldElement::from(2_usize), + FieldElement::from(3_usize), + FieldElement::from(4_usize), + FieldElement::from(5_usize), + FieldElement::from(6_usize), ]; let array_param = BrilligParameter::Array( vec![ @@ -536,7 +537,10 @@ mod tests { let memory = vm.get_memory(); assert_eq!( - memory[return_data_pointer..(return_data_pointer + flattened_array.len())], + memory[return_data_pointer..(return_data_pointer + flattened_array.len())] + .iter() + .map(|mem_val| mem_val.value) + .collect::>(), flattened_array ); assert_eq!(return_data_size, flattened_array.len()); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs index 99fb4c89f64..f305eb81b01 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/instructions.rs @@ -1,7 +1,7 @@ use acvm::{ acir::brillig::{ BinaryFieldOp, BinaryIntOp, BlackBoxOp, HeapValueType, MemoryAddress, - Opcode as BrilligOpcode, Value, ValueOrArray, + Opcode as BrilligOpcode, ValueOrArray, }, FieldElement, }; @@ -43,7 +43,7 @@ impl BrilligContext { // Compile !x as ((-1) - x) let u_max = FieldElement::from(2_i128).pow(&FieldElement::from(input.bit_size as i128)) - FieldElement::one(); - let max = self.make_constant(Value::from(u_max), input.bit_size); + let max = self.make_constant(u_max, input.bit_size); self.binary(max, input, result, BrilligBinaryOp::Sub); self.deallocate_single_addr(max); @@ -397,21 +397,23 @@ impl BrilligContext { } /// Stores the value of `constant` in the `result` register - pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: Value) { + pub(crate) fn const_instruction(&mut self, result: SingleAddrVariable, constant: FieldElement) { self.debug_show.const_instruction(result.address, constant); self.constant(result, constant); } - fn constant(&mut self, result: SingleAddrVariable, constant: Value) { - if result.bit_size > 128 && !constant.to_field().fits_in_u128() { - let high = Value::from(FieldElement::from_be_bytes_reduce( - constant - .to_field() - .to_be_bytes() - .get(0..16) - .expect("FieldElement::to_be_bytes() too short!"), - )); - let low = Value::from(constant.to_u128()); + fn constant(&mut self, result: SingleAddrVariable, constant: FieldElement) { + assert!( + result.bit_size >= constant.num_bits(), + "Constant {} does not fit in bit size {}", + constant, + result.bit_size + ); + if result.bit_size > 128 && !constant.fits_in_u128() { + let high = FieldElement::from_be_bytes_reduce( + constant.to_be_bytes().get(0..16).expect("FieldElement::to_be_bytes() too short!"), + ); + let low = FieldElement::from(constant.to_u128()); let high_register = SingleAddrVariable::new(self.allocate_register(), 254); let low_register = SingleAddrVariable::new(self.allocate_register(), 254); let intermediate_register = SingleAddrVariable::new(self.allocate_register(), 254); @@ -419,7 +421,7 @@ impl BrilligContext { self.constant(low_register, low); // I want to multiply high by 2^128, but I can't get that big constant in. // So I'll multiply by 2^64 twice. - self.constant(intermediate_register, Value::from(1_u128 << 64)); + self.constant(intermediate_register, FieldElement::from(1_u128 << 64)); self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); self.binary(high_register, intermediate_register, high_register, BrilligBinaryOp::Mul); // Now we can add. @@ -437,14 +439,18 @@ impl BrilligContext { } } - pub(crate) fn usize_const_instruction(&mut self, result: MemoryAddress, constant: Value) { + pub(crate) fn usize_const_instruction( + &mut self, + result: MemoryAddress, + constant: FieldElement, + ) { self.const_instruction(SingleAddrVariable::new_usize(result), constant); } /// Returns a register which holds the value of a constant pub(crate) fn make_constant_instruction( &mut self, - constant: Value, + constant: FieldElement, bit_size: u32, ) -> SingleAddrVariable { let var = SingleAddrVariable::new(self.allocate_register(), bit_size); @@ -452,7 +458,7 @@ impl BrilligContext { var } - fn make_constant(&mut self, constant: Value, bit_size: u32) -> SingleAddrVariable { + fn make_constant(&mut self, constant: FieldElement, bit_size: u32) -> SingleAddrVariable { let var = SingleAddrVariable::new(self.allocate_register(), bit_size); self.constant(var, constant); var @@ -461,7 +467,7 @@ impl BrilligContext { /// Returns a register which holds the value of an usize constant pub(crate) fn make_usize_constant_instruction( &mut self, - constant: Value, + constant: FieldElement, ) -> SingleAddrVariable { let register = self.allocate_register(); self.usize_const_instruction(register, constant); diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index fb11bae556c..7e951cf4e00 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -11,7 +11,7 @@ use acvm::acir::circuit::brillig::{BrilligInputs, BrilligOutputs}; use acvm::acir::circuit::opcodes::{BlockId, MemOp}; use acvm::acir::circuit::Opcode; use acvm::blackbox_solver; -use acvm::brillig_vm::{brillig::Value, VMStatus, VM}; +use acvm::brillig_vm::{MemoryValue, VMStatus, VM}; use acvm::{ acir::{ brillig::Opcode as BrilligOpcode, @@ -1623,7 +1623,7 @@ impl AcirContext { let outputs_var = vecmap(outputs_types.iter(), |output| match output { AcirType::NumericType(_) => { let var = self.add_data(AcirVarData::Const( - memory.next().expect("Missing return data").to_field(), + memory.next().expect("Missing return data").value, )); AcirValue::Var(var, output.clone()) } @@ -1640,7 +1640,7 @@ impl AcirContext { &mut self, element_types: &[AcirType], size: usize, - memory_iter: &mut impl Iterator, + memory_iter: &mut impl Iterator, ) -> AcirValue { let mut array_values = im::Vector::new(); for _ in 0..size { @@ -1657,7 +1657,7 @@ impl AcirContext { AcirType::NumericType(_) => { let memory_value = memory_iter.next().expect("ICE: Unexpected end of memory"); - let var = self.add_data(AcirVarData::Const(memory_value.to_field())); + let var = self.add_data(AcirVarData::Const(memory_value.value)); array_values.push_back(AcirValue::Var(var, element_type.clone())); } } @@ -1839,21 +1839,21 @@ pub(crate) struct AcirVar(usize); /// Returns the finished state of the Brillig VM if execution can complete. /// /// Returns `None` if complete execution of the Brillig bytecode is not possible. -fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { +fn execute_brillig(code: &[BrilligOpcode], inputs: &[BrilligInputs]) -> Option> { // Set input values - let mut calldata: Vec = Vec::new(); + let mut calldata: Vec = Vec::new(); // Each input represents a constant or array of constants. // Iterate over each input and push it into registers and/or memory. for input in inputs { match input { BrilligInputs::Single(expr) => { - calldata.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?); } BrilligInputs::Array(expr_arr) => { // Attempt to fetch all array input values for expr in expr_arr.iter() { - calldata.push(expr.to_const()?.into()); + calldata.push(expr.to_const()?); } } BrilligInputs::MemoryArray(_) => { diff --git a/compiler/noirc_printable_type/src/lib.rs b/compiler/noirc_printable_type/src/lib.rs index d3d20654adc..b9240203a5e 100644 --- a/compiler/noirc_printable_type/src/lib.rs +++ b/compiler/noirc_printable_type/src/lib.rs @@ -88,7 +88,7 @@ impl TryFrom<&[ForeignCallParam]> for PrintableValueDisplay { let (is_fmt_str, foreign_call_inputs) = foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?; - if is_fmt_str.unwrap_value().to_field().is_one() { + if is_fmt_str.unwrap_field().is_one() { convert_fmt_string_inputs(foreign_call_inputs) } else { convert_string_inputs(foreign_call_inputs) @@ -106,8 +106,7 @@ fn convert_string_inputs( let printable_type = fetch_printable_type(printable_type_as_values)?; // We must use a flat map here as each value in a struct will be in a separate input value - let mut input_values_as_fields = - input_values.iter().flat_map(|param| vecmap(param.values(), |value| value.to_field())); + let mut input_values_as_fields = input_values.iter().flat_map(|param| param.fields()); let value = decode_value(&mut input_values_as_fields, &printable_type); @@ -120,7 +119,7 @@ fn convert_fmt_string_inputs( let (message, input_and_printable_types) = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; - let message_as_fields = vecmap(message.values(), |value| value.to_field()); + let message_as_fields = message.fields(); let message_as_string = decode_string_value(&message_as_fields); let (num_values, input_and_printable_types) = input_and_printable_types @@ -128,12 +127,11 @@ fn convert_fmt_string_inputs( .ok_or(ForeignCallError::MissingForeignCallInputs)?; let mut output = Vec::new(); - let num_values = num_values.unwrap_value().to_field().to_u128() as usize; + let num_values = num_values.unwrap_field().to_u128() as usize; let types_start_at = input_and_printable_types.len() - num_values; - let mut input_iter = input_and_printable_types[0..types_start_at] - .iter() - .flat_map(|param| vecmap(param.values(), |value| value.to_field())); + let mut input_iter = + input_and_printable_types[0..types_start_at].iter().flat_map(|param| param.fields()); for printable_type in input_and_printable_types.iter().skip(types_start_at) { let printable_type = fetch_printable_type(printable_type)?; let value = decode_value(&mut input_iter, &printable_type); @@ -147,7 +145,7 @@ fn convert_fmt_string_inputs( fn fetch_printable_type( printable_type: &ForeignCallParam, ) -> Result { - let printable_type_as_fields = vecmap(printable_type.values(), |value| value.to_field()); + let printable_type_as_fields = printable_type.fields(); let printable_type_as_string = decode_string_value(&printable_type_as_fields); let printable_type: PrintableType = serde_json::from_str(&printable_type_as_string)?; diff --git a/noir_stdlib/src/collections/bounded_vec.nr b/noir_stdlib/src/collections/bounded_vec.nr index 752b96d6591..6d5fbd44247 100644 --- a/noir_stdlib/src/collections/bounded_vec.nr +++ b/noir_stdlib/src/collections/bounded_vec.nr @@ -48,6 +48,15 @@ impl BoundedVec { self.len = new_len; } + pub fn extend_from_slice(&mut self, slice: [T]) { + let new_len = self.len + slice.len(); + assert(new_len as u64 <= MaxLen as u64, "extend_from_slice out of bounds"); + for i in 0..slice.len() { + self.storage[self.len + i] = slice[i]; + } + self.len = new_len; + } + pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) { let append_len = vec.len(); let new_len = self.len + append_len; diff --git a/noir_stdlib/src/field.nr b/noir_stdlib/src/field.nr index 0f4c2caffdf..b876bcc967b 100644 --- a/noir_stdlib/src/field.nr +++ b/noir_stdlib/src/field.nr @@ -97,7 +97,7 @@ pub fn modulus_be_bytes() -> [u8] {} #[builtin(modulus_le_bytes)] pub fn modulus_le_bytes() -> [u8] {} -// Convert a 32 byte array to a field element +// Convert a 32 byte array to a field element by modding pub fn bytes32_to_field(bytes32: [u8; 32]) -> Field { // Convert it to a field element let mut v = 1; diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index 362d2132952..52f7783851a 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.30.1"; +const VERSION: &str = "0.32.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index a3ee89263a4..f0de8d5d1c8 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -2,7 +2,7 @@ use crate::foreign_calls::DebugForeignCallExecutor; use acvm::acir::circuit::{Circuit, Opcode, OpcodeLocation}; use acvm::acir::native_types::{Witness, WitnessMap}; use acvm::brillig_vm::brillig::ForeignCallResult; -use acvm::brillig_vm::brillig::Value; +use acvm::brillig_vm::MemoryValue; use acvm::pwg::{ ACVMStatus, BrilligSolver, BrilligSolverStatus, ForeignCallWaitInfo, StepResult, ACVM, }; @@ -506,13 +506,13 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { acir_index < opcodes.len() && matches!(opcodes[acir_index], Opcode::Brillig(..)) } - pub(super) fn get_brillig_memory(&self) -> Option<&[Value]> { + pub(super) fn get_brillig_memory(&self) -> Option<&[MemoryValue]> { self.brillig_solver.as_ref().map(|solver| solver.get_memory()) } - pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement) { + pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement, bit_size: u32) { if let Some(solver) = self.brillig_solver.as_mut() { - solver.write_memory_at(ptr, value.into()); + solver.write_memory_at(ptr, MemoryValue::new(value, bit_size)); } } @@ -667,7 +667,7 @@ mod tests { }, BrilligOpcode::Const { destination: MemoryAddress::from(1), - value: Value::from(fe_0), + value: fe_0, bit_size: 32, }, BrilligOpcode::ForeignCall { @@ -675,7 +675,7 @@ mod tests { destinations: vec![], destination_value_types: vec![], inputs: vec![ValueOrArray::MemoryAddress(MemoryAddress::from(0))], - input_value_types: vec![HeapValueType::Simple], + input_value_types: vec![HeapValueType::field()], }, BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 0 }, ], diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index 25f126ff490..aae2212fd54 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -1,6 +1,7 @@ use acvm::{ - acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, + acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, + FieldElement, }; use nargo::{ artifacts::debug::{DebugArtifact, DebugVars, StackFrame}, @@ -81,11 +82,11 @@ impl DebugForeignCallExecutor for DefaultDebugForeignCallExecutor { } } -fn debug_var_id(value: &Value) -> DebugVarId { +fn debug_var_id(value: &FieldElement) -> DebugVarId { DebugVarId(value.to_u128() as u32) } -fn debug_fn_id(value: &Value) -> DebugFnId { +fn debug_fn_id(value: &FieldElement) -> DebugFnId { DebugFnId(value.to_u128() as u32) } @@ -100,8 +101,8 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { let fcp_var_id = &foreign_call.inputs[0]; if let ForeignCallParam::Single(var_id_value) = fcp_var_id { let var_id = debug_var_id(var_id_value); - let values: Vec = - foreign_call.inputs[1..].iter().flat_map(|x| x.values()).collect(); + let values: Vec = + foreign_call.inputs[1..].iter().flat_map(|x| x.fields()).collect(); self.debug_vars.assign_var(var_id, &values); } Ok(ForeignCallResult::default().into()) @@ -129,12 +130,12 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { } }) .collect(); - let values: Vec = (0..n - 1 - arity) + let values: Vec = (0..n - 1 - arity) .flat_map(|i| { foreign_call .inputs .get(1 + i) - .map(|fci| fci.values()) + .map(|fci| fci.fields()) .unwrap_or_default() }) .collect(); @@ -147,7 +148,7 @@ impl ForeignCallExecutor for DefaultDebugForeignCallExecutor { let fcp_value = &foreign_call.inputs[1]; if let ForeignCallParam::Single(var_id_value) = fcp_var_id { let var_id = debug_var_id(var_id_value); - self.debug_vars.assign_deref(var_id, &fcp_value.values()); + self.debug_vars.assign_deref(var_id, &fcp_value.fields()); } Ok(ForeignCallResult::default().into()) } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 41dbf604f99..1c077c6ee9b 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -319,12 +319,12 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { return; }; - for (index, value) in memory.iter().enumerate() { - println!("{index} = {}", value.to_field()); + for (index, value) in memory.iter().enumerate().filter(|(_, value)| value.bit_size > 0) { + println!("{index} = {}", value); } } - pub fn write_brillig_memory(&mut self, index: usize, value: String) { + pub fn write_brillig_memory(&mut self, index: usize, value: String, bit_size: u32) { let Some(field_value) = FieldElement::try_from_str(&value) else { println!("Invalid value: {value}"); return; @@ -333,7 +333,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { println!("Not executing a Brillig block"); return; } - self.context.write_brillig_memory(index, field_value); + self.context.write_brillig_memory(index, field_value, bit_size); } pub fn show_vars(&self) { @@ -513,8 +513,8 @@ pub fn run( "memset", command! { "update a Brillig memory cell with the given value", - (index: usize, value: String) => |index, value| { - ref_context.borrow_mut().write_brillig_memory(index, value); + (index: usize, value: String, bit_size: u32) => |index, value, bit_size| { + ref_context.borrow_mut().write_brillig_memory(index, value, bit_size); Ok(CommandStatus::Done) } }, diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index 0e9e177e023..8e5c2bc46a4 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -1,4 +1,4 @@ -use acvm::brillig_vm::brillig::Value; +use acvm::FieldElement; use noirc_errors::debug_info::{ DebugFnId, DebugFunction, DebugInfo, DebugTypeId, DebugVarId, DebugVariable, }; @@ -66,7 +66,7 @@ impl DebugVars { } } - pub fn assign_var(&mut self, var_id: DebugVarId, values: &[Value]) { + pub fn assign_var(&mut self, var_id: DebugVarId, values: &[FieldElement]) { let type_id = &self.variables.get(&var_id).unwrap().debug_type_id; let ptype = self.types.get(type_id).unwrap(); @@ -74,10 +74,10 @@ impl DebugVars { .last_mut() .expect("unexpected empty stack frames") .1 - .insert(var_id, decode_value(&mut values.iter().map(|v| v.to_field()), ptype)); + .insert(var_id, decode_value(&mut values.iter().copied(), ptype)); } - pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[Value]) { + pub fn assign_field(&mut self, var_id: DebugVarId, indexes: Vec, values: &[FieldElement]) { let current_frame = &mut self.frames.last_mut().expect("unexpected empty stack frames").1; let mut cursor: &mut PrintableValue = current_frame .get_mut(&var_id) @@ -147,10 +147,10 @@ impl DebugVars { } }; } - *cursor = decode_value(&mut values.iter().map(|v| v.to_field()), cursor_type); + *cursor = decode_value(&mut values.iter().copied(), cursor_type); } - pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[Value]) { + pub fn assign_deref(&mut self, _var_id: DebugVarId, _values: &[FieldElement]) { unimplemented![] } diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index f7f36c65c90..ea67f17af2a 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -1,6 +1,7 @@ use acvm::{ - acir::brillig::{ForeignCallParam, ForeignCallResult, Value}, + acir::brillig::{ForeignCallParam, ForeignCallResult}, pwg::ForeignCallWaitInfo, + FieldElement, }; use jsonrpc::{arg as build_json_rpc_arg, minreq_http::Builder, Client}; use noirc_printable_type::{decode_string_value, ForeignCallError, PrintableValueDisplay}; @@ -46,15 +47,15 @@ impl From for NargoForeignCallResult { } } -impl From for NargoForeignCallResult { - fn from(value: Value) -> Self { +impl From for NargoForeignCallResult { + fn from(value: FieldElement) -> Self { let foreign_call_result: ForeignCallResult = value.into(); foreign_call_result.into() } } -impl From> for NargoForeignCallResult { - fn from(values: Vec) -> Self { +impl From> for NargoForeignCallResult { + fn from(values: Vec) -> Self { let foreign_call_result: ForeignCallResult = values.into(); foreign_call_result.into() } @@ -178,7 +179,10 @@ impl DefaultForeignCallExecutor { ) -> Result<(usize, &[ForeignCallParam]), ForeignCallError> { let (id, params) = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?; - Ok((id.unwrap_value().to_usize(), params)) + let id = + usize::try_from(id.unwrap_field().try_to_u64().expect("value does not fit into u64")) + .expect("value does not fit into usize"); + Ok((id, params)) } fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { @@ -186,12 +190,12 @@ impl DefaultForeignCallExecutor { } fn parse_string(param: &ForeignCallParam) -> String { - let fields: Vec<_> = param.values().into_iter().map(|value| value.to_field()).collect(); + let fields: Vec<_> = param.fields().to_vec(); decode_string_value(&fields) } fn execute_print(foreign_call_inputs: &[ForeignCallParam]) -> Result<(), ForeignCallError> { - let skip_newline = foreign_call_inputs[0].unwrap_value().is_zero(); + let skip_newline = foreign_call_inputs[0].unwrap_field().is_zero(); let foreign_call_inputs = foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?.1; @@ -242,7 +246,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { self.mocked_responses.push(MockedCall::new(id, mock_oracle_name)); self.last_mock_id += 1; - Ok(Value::from(id).into()) + Ok(FieldElement::from(id).into()) } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; @@ -262,11 +266,8 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } Some(ForeignCall::SetMockTimes) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; - let times = params[0] - .unwrap_value() - .to_field() - .try_to_u64() - .expect("Invalid bit size of times"); + let times = + params[0].unwrap_field().try_to_u64().expect("Invalid bit size of times"); self.find_mock_by_id(id) .unwrap_or_else(|| panic!("Unknown mock id {}", id)) @@ -325,10 +326,8 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { #[cfg(test)] mod tests { use acvm::{ - acir::brillig::ForeignCallParam, - brillig_vm::brillig::{ForeignCallResult, Value}, - pwg::ForeignCallWaitInfo, - FieldElement, + acir::brillig::ForeignCallParam, brillig_vm::brillig::ForeignCallResult, + pwg::ForeignCallWaitInfo, FieldElement, }; use jsonrpc_core::Result as RpcResult; use jsonrpc_derive::rpc; @@ -356,11 +355,11 @@ mod tests { fn sum(&self, array: ForeignCallParam) -> RpcResult { let mut res: FieldElement = 0_usize.into(); - for value in array.values() { - res += value.to_field(); + for value in array.fields() { + res += value; } - Ok(Value::from(res).into()) + Ok(res.into()) } } @@ -406,7 +405,7 @@ mod tests { }; let result = executor.execute(&foreign_call); - assert_eq!(result.unwrap(), Value::from(3_usize).into()); + assert_eq!(result.unwrap(), FieldElement::from(3_usize).into()); server.close(); } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index aec8204f065..251dd80c2f4 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.30.1", + "@aztec/bb.js": "0.32.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index 3932935167d..a39ae9921da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.30.1": - version: 0.30.1 - resolution: "@aztec/bb.js@npm:0.30.1" +"@aztec/bb.js@npm:0.32.0": + version: 0.32.0 + resolution: "@aztec/bb.js@npm:0.32.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 52d0acaaf0966aa969b863adeb688df5c1abe7c8a0595bc2dca8603a8649f11eb39071aacbdc05b19f320f022b98691a41cb18a601ad84e6d82955ae6f885106 + checksum: 0919957e141ae0a65cfab961dce122fa06de628a10b7cb661d31d8ed4793ce80980fcf315620ceffffa45581db941bad43c392f4b2aa9becaaf7d2faaba01ffc languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.30.1 + "@aztec/bb.js": 0.32.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 55f558e79933256c0ac0876e0d4c7354af1b4a5b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 28 Mar 2024 12:48:35 +0000 Subject: [PATCH 123/416] chore: include durations of tracing spans in log output (#4660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Resolves ## Summary\* This PR updates the filter on span events so that we get the duration of the span being printed. Example output: ``` 2024-03-27T17:18:17.207634Z TRACE check_crate{crate_id=Root(1) deny_warnings=false disable_macros=false}: noirc_driver: new 2024-03-27T17:18:17.238046Z TRACE check_crate{crate_id=Root(1) deny_warnings=false disable_macros=false}: noirc_driver: close time.busy=30.4ms time.idle=39.3µs 2024-03-27T17:18:17.238075Z TRACE compile_no_check{function_name="main"}: noirc_driver: new 2024-03-27T17:18:17.238081Z TRACE compile_no_check{function_name="main"}:monomorphize: noirc_frontend::monomorphization: new 2024-03-27T17:18:17.238180Z TRACE compile_no_check{function_name="main"}:monomorphize: noirc_frontend::monomorphization: close time.busy=95.5µs time.idle=3.56µs 2024-03-27T17:18:17.238217Z TRACE compile_no_check{function_name="main"}:create_circuit: noirc_evaluator::ssa: new 2024-03-27T17:18:17.238234Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation: noirc_evaluator::ssa: new 2024-03-27T17:18:17.238458Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:defunctionalize: noirc_evaluator::ssa::opt::defunctionalize: new 2024-03-27T17:18:17.238476Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:defunctionalize: noirc_evaluator::ssa::opt::defunctionalize: close time.busy=12.5µs time.idle=6.24µs 2024-03-27T17:18:17.238482Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:remove_paired_rc: noirc_evaluator::ssa::opt::rc: new 2024-03-27T17:18:17.238499Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:remove_paired_rc: noirc_evaluator::ssa::opt::rc: close time.busy=7.51µs time.idle=9.60µs 2024-03-27T17:18:17.238504Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:inline_functions: noirc_evaluator::ssa::opt::inlining: new 2024-03-27T17:18:17.238572Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:inline_functions: noirc_evaluator::ssa::opt::inlining: close time.busy=64.0µs time.idle=3.42µs 2024-03-27T17:18:17.238578Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:mem2reg: noirc_evaluator::ssa::opt::mem2reg: new 2024-03-27T17:18:17.238668Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:mem2reg: noirc_evaluator::ssa::opt::mem2reg: close time.busy=83.6µs time.idle=5.48µs 2024-03-27T17:18:17.238675Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:evaluate_assert_constant: noirc_evaluator::ssa::opt::assert_constant: new 2024-03-27T17:18:17.238682Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:evaluate_assert_constant: noirc_evaluator::ssa::opt::assert_constant: close time.busy=3.01µs time.idle=4.07µs 2024-03-27T17:18:17.238688Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:unroll_loops: noirc_evaluator::ssa::opt::unrolling: new 2024-03-27T17:18:17.387314Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:unroll_loops: noirc_evaluator::ssa::opt::unrolling: close time.busy=149ms time.idle=5.25µs 2024-03-27T17:18:17.387343Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:simplify_cfg: noirc_evaluator::ssa::opt::simplify_cfg: new 2024-03-27T17:18:17.393664Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:simplify_cfg: noirc_evaluator::ssa::opt::simplify_cfg: close time.busy=6.31ms time.idle=6.11µs 2024-03-27T17:18:17.393681Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:flatten_cfg: noirc_evaluator::ssa::opt::flatten_cfg: new 2024-03-27T17:18:22.881528Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:flatten_cfg: noirc_evaluator::ssa::opt::flatten_cfg: close time.busy=5.49s time.idle=9.78µs 2024-03-27T17:18:22.881558Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:remove_bit_shifts: noirc_evaluator::ssa::opt::remove_bit_shifts: new 2024-03-27T17:18:22.939961Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:remove_bit_shifts: noirc_evaluator::ssa::opt::remove_bit_shifts: close time.busy=58.4ms time.idle=7.86µs 2024-03-27T17:18:22.939994Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:mem2reg: noirc_evaluator::ssa::opt::mem2reg: new 2024-03-27T17:18:39.127772Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:mem2reg: noirc_evaluator::ssa::opt::mem2reg: close time.busy=16.2s time.idle=7.82µs 2024-03-27T17:18:39.127802Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:fold_constants: noirc_evaluator::ssa::opt::constant_folding: new 2024-03-27T17:18:42.247447Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:fold_constants: noirc_evaluator::ssa::opt::constant_folding: close time.busy=3.12s time.idle=6.51µs 2024-03-27T17:18:42.247480Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:fold_constants_using_constraints: noirc_evaluator::ssa::opt::constant_folding: new 2024-03-27T17:18:46.333707Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:fold_constants_using_constraints: noirc_evaluator::ssa::opt::constant_folding: close time.busy=4.09s time.idle=10.1µs 2024-03-27T17:18:46.333739Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:dead_instruction_elimination: noirc_evaluator::ssa::opt::die: new 2024-03-27T17:18:47.352043Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation:dead_instruction_elimination: noirc_evaluator::ssa::opt::die: close time.busy=1.02s time.idle=8.20µs 2024-03-27T17:18:47.366309Z TRACE compile_no_check{function_name="main"}:create_circuit:find_last_array_uses: noirc_evaluator::ssa::opt::array_use: new 2024-03-27T17:18:47.371971Z TRACE compile_no_check{function_name="main"}:create_circuit:find_last_array_uses: noirc_evaluator::ssa::opt::array_use: close time.busy=5.64ms time.idle=18.0µs 2024-03-27T17:18:47.371988Z TRACE compile_no_check{function_name="main"}:create_circuit:into_acir: noirc_evaluator::ssa::acir_gen: new 2024-03-27T17:18:54.310129Z TRACE compile_no_check{function_name="main"}:create_circuit:into_acir: noirc_evaluator::ssa::acir_gen: close time.busy=6.94s time.idle=8.21µs 2024-03-27T17:18:54.310156Z TRACE compile_no_check{function_name="main"}:create_circuit:ssa_generation: noirc_evaluator::ssa: close time.busy=30.1s time.idle=6.94s 2024-03-27T17:18:54.311567Z TRACE compile_no_check{function_name="main"}:create_circuit:optimize_acir: acvm::compiler::optimizers: new 2024-03-27T17:18:54.311580Z INFO compile_no_check{function_name="main"}:create_circuit:optimize_acir: acvm::compiler::optimizers: Number of opcodes before: 8323 2024-03-27T17:18:54.312564Z INFO compile_no_check{function_name="main"}:create_circuit:optimize_acir: acvm::compiler::optimizers: Number of opcodes after: 8323 2024-03-27T17:18:54.312571Z TRACE compile_no_check{function_name="main"}:create_circuit:optimize_acir: acvm::compiler::optimizers: close time.busy=995µs time.idle=9.56µs 2024-03-27T17:18:54.313322Z TRACE compile_no_check{function_name="main"}:create_circuit:update_acir: noirc_errors::debug_info: new 2024-03-27T17:18:54.314192Z TRACE compile_no_check{function_name="main"}:create_circuit:update_acir: noirc_errors::debug_info: close time.busy=863µs time.idle=6.04µs 2024-03-27T17:18:54.314446Z TRACE compile_no_check{function_name="main"}:create_circuit: noirc_evaluator::ssa: close time.busy=37.1s time.idle=4.23µs 2024-03-27T17:18:54.315015Z TRACE compile_no_check{function_name="main"}: noirc_driver: close time.busy=37.1s time.idle=4.54µs 2024-03-27T17:18:54.320236Z TRACE optimize_acir: acvm::compiler::optimizers: new 2024-03-27T17:18:54.320252Z INFO optimize_acir: acvm::compiler::optimizers: Number of opcodes before: 8323 2024-03-27T17:18:54.321141Z INFO optimize_acir: acvm::compiler::optimizers: Number of opcodes after: 8323 2024-03-27T17:18:54.321147Z TRACE optimize_acir: acvm::compiler::optimizers: close time.busy=902µs time.idle=9.38µs 2024-03-27T17:18:54.321156Z TRACE transform_acir{expression_width=Bounded { width: 3 }}: acvm::compiler::transformers: new 2024-03-27T17:18:54.322544Z TRACE transform_acir{expression_width=Bounded { width: 3 }}: acvm::compiler::transformers: close time.busy=1.38ms time.idle=4.58µs 2024-03-27T17:18:54.323134Z TRACE update_acir: noirc_errors::debug_info: new 2024-03-27T17:18:54.323928Z TRACE update_acir: noirc_errors::debug_info: close time.busy=789µs time.idle=5.42µs ``` ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/nargo_cli/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tooling/nargo_cli/src/main.rs b/tooling/nargo_cli/src/main.rs index 3f797b0bf0c..6e2b7069bc4 100644 --- a/tooling/nargo_cli/src/main.rs +++ b/tooling/nargo_cli/src/main.rs @@ -25,14 +25,14 @@ fn main() { if let Ok(log_dir) = env::var("NARGO_LOG_DIR") { let debug_file = rolling::daily(log_dir, "nargo-log"); tracing_subscriber::fmt() - .with_span_events(FmtSpan::ACTIVE) + .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE) .with_writer(debug_file) .with_ansi(false) .with_env_filter(EnvFilter::from_default_env()) .init(); } else { tracing_subscriber::fmt() - .with_span_events(FmtSpan::ACTIVE) + .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE) .with_ansi(true) .with_env_filter(EnvFilter::from_env("NOIR_LOG")) .init(); From 05b32fc9595d0a281e49b99b153030416115f436 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 28 Mar 2024 17:48:24 +0000 Subject: [PATCH 124/416] chore: Pretty print timings for codegen (#4659) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4658 ## Summary\* I was already timing things for a separate issue so I decided to push this PR. I felt this provided a nice temporary solution. If we deem that the `tracing` crate can still be used for pretty printing the timings of each codegen pass we can switch to that in a follow-up, and for now this provides an easy way to check the timings of each codegen pass. Example output: ``` After Defunctionalization:: 0 ms After Removing Paired rc_inc & rc_decs:: 0 ms After Inlining:: 0 ms After Mem2Reg:: 0 ms After Assert Constant:: 0 ms After Unrolling:: 13 ms After Simplifying:: 0 ms After Flattening:: 9 ms After Removing Bit Shifts:: 0 ms After Mem2Reg:: 19 ms After Constant Folding:: 3 ms After Constraint Folding:: 3 ms After Dead Instruction Elimination:: 0 ms SSA to Brillig: 0 ms SSA to ACIR: 14 ms ``` ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- Cargo.lock | 7 +++--- compiler/noirc_driver/src/lib.rs | 13 +++++++++-- compiler/noirc_evaluator/Cargo.toml | 3 ++- compiler/noirc_evaluator/src/ssa.rs | 34 +++++++++++++++++++++++------ cspell.json | 1 + 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c47f03b267..c42c2b5d74e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -789,9 +789,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.30" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", @@ -799,7 +799,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.1", + "windows-targets 0.52.4", ] [[package]] @@ -3070,6 +3070,7 @@ name = "noirc_evaluator" version = "0.26.0" dependencies = [ "acvm", + "chrono", "fxhash", "im", "iter-extended", diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index bc3062e5807..8b3c126dedb 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -68,6 +68,10 @@ pub struct CompileOptions { #[arg(long)] pub print_acir: bool, + /// Pretty print benchmark times of each code generation pass + #[arg(long, hide = true)] + pub benchmark_codegen: bool, + /// Treat all warnings as errors #[arg(long, conflicts_with = "silence_warnings")] pub deny_warnings: bool, @@ -478,8 +482,13 @@ pub fn compile_no_check( return Ok(cached_program.expect("cache must exist for hashes to match")); } let visibility = program.return_visibility; - let (circuit, debug, input_witnesses, return_witnesses, warnings) = - create_circuit(program, options.show_ssa, options.show_brillig, options.force_brillig)?; + let (circuit, debug, input_witnesses, return_witnesses, warnings) = create_circuit( + program, + options.show_ssa, + options.show_brillig, + options.force_brillig, + options.benchmark_codegen, + )?; let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses, visibility); diff --git a/compiler/noirc_evaluator/Cargo.toml b/compiler/noirc_evaluator/Cargo.toml index a8f0e8d83a9..fad7c3c309e 100644 --- a/compiler/noirc_evaluator/Cargo.toml +++ b/compiler/noirc_evaluator/Cargo.toml @@ -17,4 +17,5 @@ thiserror.workspace = true num-bigint = "0.4" im = { version = "15.1", features = ["serde"] } serde.workspace = true -tracing.workspace = true \ No newline at end of file +tracing.workspace = true +chrono = "0.4.37" \ No newline at end of file diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 808cf7533c9..01dc50a7119 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -38,15 +38,16 @@ pub mod ssa_gen; /// convert the final SSA into ACIR and return it. pub(crate) fn optimize_into_acir( program: Program, - print_ssa_passes: bool, + print_passes: bool, print_brillig_trace: bool, force_brillig_output: bool, + print_timings: bool, ) -> Result { let abi_distinctness = program.return_distinctness; let ssa_gen_span = span!(Level::TRACE, "ssa_generation"); let ssa_gen_span_guard = ssa_gen_span.enter(); - let ssa = SsaBuilder::new(program, print_ssa_passes, force_brillig_output)? + let ssa = SsaBuilder::new(program, print_passes, force_brillig_output, print_timings)? .run_pass(Ssa::defunctionalize, "After Defunctionalization:") .run_pass(Ssa::remove_paired_rc, "After Removing Paired rc_inc & rc_decs:") .run_pass(Ssa::inline_functions, "After Inlining:") @@ -64,13 +65,28 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") .finish(); - let brillig = ssa.to_brillig(print_brillig_trace); + let brillig = time("SSA to Brillig", print_timings, || ssa.to_brillig(print_brillig_trace)); drop(ssa_gen_span_guard); let last_array_uses = ssa.find_last_array_uses(); - ssa.into_acir(brillig, abi_distinctness, &last_array_uses) + time("SSA to ACIR", print_timings, || { + ssa.into_acir(brillig, abi_distinctness, &last_array_uses) + }) +} + +// Helper to time SSA passes +fn time(name: &str, print_timings: bool, f: impl FnOnce() -> T) -> T { + let start_time = chrono::Utc::now().time(); + let result = f(); + + if print_timings { + let end_time = chrono::Utc::now().time(); + println!("{name}: {} ms", (end_time - start_time).num_milliseconds()); + } + + result } /// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Circuit]. @@ -83,6 +99,7 @@ pub fn create_circuit( enable_ssa_logging: bool, enable_brillig_logging: bool, force_brillig_output: bool, + print_codegen_timings: bool, ) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { let debug_variables = program.debug_variables.clone(); let debug_types = program.debug_types.clone(); @@ -94,6 +111,7 @@ pub fn create_circuit( enable_ssa_logging, enable_brillig_logging, force_brillig_output, + print_codegen_timings, )?; let opcodes = generated_acir.take_opcodes(); let current_witness_index = generated_acir.current_witness_index().0; @@ -176,6 +194,7 @@ fn split_public_and_private_inputs( struct SsaBuilder { ssa: Ssa, print_ssa_passes: bool, + print_codegen_timings: bool, } impl SsaBuilder { @@ -183,9 +202,10 @@ impl SsaBuilder { program: Program, print_ssa_passes: bool, force_brillig_runtime: bool, + print_codegen_timings: bool, ) -> Result { let ssa = ssa_gen::generate_ssa(program, force_brillig_runtime)?; - Ok(SsaBuilder { print_ssa_passes, ssa }.print("Initial SSA:")) + Ok(SsaBuilder { print_ssa_passes, print_codegen_timings, ssa }.print("Initial SSA:")) } fn finish(self) -> Ssa { @@ -194,7 +214,7 @@ impl SsaBuilder { /// Runs the given SSA pass and prints the SSA afterward if `print_ssa_passes` is true. fn run_pass(mut self, pass: fn(Ssa) -> Ssa, msg: &str) -> Self { - self.ssa = pass(self.ssa); + self.ssa = time(msg, self.print_codegen_timings, || pass(self.ssa)); self.print(msg) } @@ -204,7 +224,7 @@ impl SsaBuilder { pass: fn(Ssa) -> Result, msg: &str, ) -> Result { - self.ssa = pass(self.ssa)?; + self.ssa = time(msg, self.print_codegen_timings, || pass(self.ssa))?; Ok(self.print(msg)) } diff --git a/cspell.json b/cspell.json index 8cfc5e695a2..e50940fbd10 100644 --- a/cspell.json +++ b/cspell.json @@ -36,6 +36,7 @@ "castable", "catmcgee", "Celo", + "chrono", "chumsky", "codegen", "codegenned", From 30c9f3151d447de8c7467ccbee82e32b8c46a396 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 29 Mar 2024 09:55:13 -0500 Subject: [PATCH 125/416] fix: "Types in a binary operation should match, but found T and T" (#4648) # Description ## Problem\* Resolves #4635 Resolves https://github.com/noir-lang/noir/issues/4502 ## Summary\* This was more difficult to fix than it originally seemed. The main issue was between interactions with unbound type variables, type aliases, type rules for operators, and operator traits. Removing the "infer unbound type variables to be numeric" rule on operators causes a lot of stdlib code to break where it'd be unreasonable to have type annotations. This caused unbound type variables to be bound to the first object type whose impl it was checked against when calling verify trait impl. I eventually settled on just delaying the verify trait impl check for operators until the end of a function when more types are known. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/type_check/errors.rs | 7 +- .../noirc_frontend/src/hir/type_check/expr.rs | 85 ++++++++++--------- .../noirc_frontend/src/hir/type_check/mod.rs | 50 ++++++----- compiler/noirc_frontend/src/hir_def/types.rs | 10 +++ compiler/noirc_frontend/src/tests.rs | 12 +-- noir_stdlib/src/cmp.nr | 14 +-- noir_stdlib/src/ops.nr | 54 ++++++------ .../regression_4635/Nargo.toml | 7 ++ .../regression_4635/src/main.nr | 59 +++++++++++++ .../hashmap_load_factor/Nargo.toml | 0 .../hashmap_load_factor/Prover.toml | 23 +++++ .../hashmap_load_factor/src/main.nr | 0 .../bit_shifts_runtime/src/main.nr | 2 +- 13 files changed, 212 insertions(+), 111 deletions(-) create mode 100644 test_programs/compile_success_empty/regression_4635/Nargo.toml create mode 100644 test_programs/compile_success_empty/regression_4635/src/main.nr rename test_programs/{compile_failure => execution_failure}/hashmap_load_factor/Nargo.toml (100%) create mode 100644 test_programs/execution_failure/hashmap_load_factor/Prover.toml rename test_programs/{compile_failure => execution_failure}/hashmap_load_factor/src/main.nr (100%) diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index 6beb6929ce1..fe9cc9ea2d6 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -65,8 +65,6 @@ pub enum TypeCheckError { VariableMustBeMutable { name: String, span: Span }, #[error("No method named '{method_name}' found for type '{object_type}'")] UnresolvedMethodCall { method_name: String, object_type: Type, span: Span }, - #[error("Comparisons are invalid on Field types. Try casting the operands to a sized integer type first")] - InvalidComparisonOnField { span: Span }, #[error("Integers must have the same signedness LHS is {sign_x:?}, RHS is {sign_y:?}")] IntegerSignedness { sign_x: Signedness, sign_y: Signedness, span: Span }, #[error("Integers must have the same bit width LHS is {bit_width_x}, RHS is {bit_width_y}")] @@ -76,7 +74,7 @@ pub enum TypeCheckError { #[error("{kind} cannot be used in a unary operation")] InvalidUnaryOp { kind: String, span: Span }, #[error("Bitwise operations are invalid on Field types. Try casting the operands to a sized integer type first.")] - InvalidBitwiseOperationOnField { span: Span }, + FieldBitwiseOp { span: Span }, #[error("Integer cannot be used with type {typ}")] IntegerTypeMismatch { typ: Type, span: Span }, #[error("Cannot use an integer and a Field in a binary operation, try converting the Field into an integer first")] @@ -224,12 +222,11 @@ impl From for Diagnostic { | TypeCheckError::TupleIndexOutOfBounds { span, .. } | TypeCheckError::VariableMustBeMutable { span, .. } | TypeCheckError::UnresolvedMethodCall { span, .. } - | TypeCheckError::InvalidComparisonOnField { span } | TypeCheckError::IntegerSignedness { span, .. } | TypeCheckError::IntegerBitWidth { span, .. } | TypeCheckError::InvalidInfixOp { span, .. } | TypeCheckError::InvalidUnaryOp { span, .. } - | TypeCheckError::InvalidBitwiseOperationOnField { span, .. } + | TypeCheckError::FieldBitwiseOp { span, .. } | TypeCheckError::IntegerTypeMismatch { span, .. } | TypeCheckError::FieldComparison { span, .. } | TypeCheckError::AmbiguousBitWidth { span, .. } diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index 10476b6caef..fb66bdeae6e 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -152,14 +152,16 @@ impl<'interner> TypeChecker<'interner> { Ok((typ, use_impl)) => { if use_impl { let id = infix_expr.trait_method_id; - // Assume operators have no trait generics - self.verify_trait_constraint( - &lhs_type, - id.trait_id, - &[], - *expr_id, - span, - ); + + // Delay checking the trait constraint until the end of the function. + // Checking it now could bind an unbound type variable to any type + // that implements the trait. + let constraint = crate::hir_def::traits::TraitConstraint { + typ: lhs_type.clone(), + trait_id: id.trait_id, + trait_generics: Vec::new(), + }; + self.trait_constraints.push((constraint, *expr_id)); self.typecheck_operator_method(*expr_id, id, &lhs_type, span); } typ @@ -836,6 +838,10 @@ impl<'interner> TypeChecker<'interner> { match (lhs_type, rhs_type) { // Avoid reporting errors multiple times (Error, _) | (_, Error) => Ok((Bool, false)), + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + self.comparator_operand_type_rules(&alias, other, op, span) + } // Matches on TypeVariable must be first to follow any type // bindings. @@ -844,12 +850,8 @@ impl<'interner> TypeChecker<'interner> { return self.comparator_operand_type_rules(other, binding, op, span); } - self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); - Ok((Bool, false)) - } - (Alias(alias, args), other) | (other, Alias(alias, args)) => { - let alias = alias.borrow().get_type(args); - self.comparator_operand_type_rules(&alias, other, op, span) + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + Ok((Bool, use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if sign_x != sign_y { @@ -1079,13 +1081,16 @@ impl<'interner> TypeChecker<'interner> { } } + /// Handles the TypeVariable case for checking binary operators. + /// Returns true if we should use the impl for the operator instead of the primitive + /// version of it. fn bind_type_variables_for_infix( &mut self, lhs_type: &Type, op: &HirBinaryOp, rhs_type: &Type, span: Span, - ) { + ) -> bool { self.unify(lhs_type, rhs_type, || TypeCheckError::TypeMismatchWithSource { expected: lhs_type.clone(), actual: rhs_type.clone(), @@ -1093,22 +1098,26 @@ impl<'interner> TypeChecker<'interner> { span, }); - // In addition to unifying both types, we also have to bind either - // the lhs or rhs to an integer type variable. This ensures if both lhs - // and rhs are type variables, that they will have the correct integer - // type variable kind instead of TypeVariableKind::Normal. - let target = if op.kind.is_valid_for_field_type() { - Type::polymorphic_integer_or_field(self.interner) - } else { - Type::polymorphic_integer(self.interner) - }; + let use_impl = !lhs_type.is_numeric(); + + // If this operator isn't valid for fields we have to possibly narrow + // TypeVariableKind::IntegerOrField to TypeVariableKind::Integer. + // Doing so also ensures a type error if Field is used. + // The is_numeric check is to allow impls for custom types to bypass this. + if !op.kind.is_valid_for_field_type() && lhs_type.is_numeric() { + let target = Type::polymorphic_integer(self.interner); + + use BinaryOpKind::*; + use TypeCheckError::*; + self.unify(lhs_type, &target, || match op.kind { + Less | LessEqual | Greater | GreaterEqual => FieldComparison { span }, + And | Or | Xor | ShiftRight | ShiftLeft => FieldBitwiseOp { span }, + Modulo => FieldModulo { span }, + other => unreachable!("Operator {other:?} should be valid for Field"), + }); + } - self.unify(lhs_type, &target, || TypeCheckError::TypeMismatchWithSource { - expected: lhs_type.clone(), - actual: rhs_type.clone(), - source: Source::Binary, - span, - }); + use_impl } // Given a binary operator and another type. This method will produce the output type @@ -1130,6 +1139,10 @@ impl<'interner> TypeChecker<'interner> { match (lhs_type, rhs_type) { // An error type on either side will always return an error (Error, _) | (_, Error) => Ok((Error, false)), + (Alias(alias, args), other) | (other, Alias(alias, args)) => { + let alias = alias.borrow().get_type(args); + self.infix_operand_type_rules(&alias, op, other, span) + } // Matches on TypeVariable must be first so that we follow any type // bindings. @@ -1138,14 +1151,8 @@ impl<'interner> TypeChecker<'interner> { return self.infix_operand_type_rules(binding, op, other, span); } - self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); - - // Both types are unified so the choice of which to return is arbitrary - Ok((other.clone(), false)) - } - (Alias(alias, args), other) | (other, Alias(alias, args)) => { - let alias = alias.borrow().get_type(args); - self.infix_operand_type_rules(&alias, op, other, span) + let use_impl = self.bind_type_variables_for_infix(lhs_type, op, rhs_type, span); + Ok((other.clone(), use_impl)) } (Integer(sign_x, bit_width_x), Integer(sign_y, bit_width_y)) => { if sign_x != sign_y { @@ -1170,7 +1177,7 @@ impl<'interner> TypeChecker<'interner> { if op.kind == BinaryOpKind::Modulo { return Err(TypeCheckError::FieldModulo { span }); } else { - return Err(TypeCheckError::InvalidBitwiseOperationOnField { span }); + return Err(TypeCheckError::FieldBitwiseOp { span }); } } Ok((FieldElement, false)) diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 137608f8037..bb42ebf68fb 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -86,31 +86,13 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec Vec Vec Vec binding.is_bindable(), TypeBinding::Unbound(_) => true, }, + Type::Alias(alias, args) => alias.borrow().get_type(args).is_bindable(), _ => false, } } @@ -605,6 +606,15 @@ impl Type { matches!(self.follow_bindings(), Type::Integer(Signedness::Unsigned, _)) } + pub fn is_numeric(&self) -> bool { + use Type::*; + use TypeVariableKind as K; + matches!( + self.follow_bindings(), + FieldElement | Integer(..) | Bool | TypeVariable(_, K::Integer | K::IntegerOrField) + ) + } + fn contains_numeric_typevar(&self, target_id: TypeVariableId) -> bool { // True if the given type is a NamedGeneric with the target_id let named_generic_id_matches_target = |typ: &Type| { diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 6f92cb52a88..c4f0a8d67ba 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1033,19 +1033,19 @@ mod test { fn resolve_complex_closures() { let src = r#" fn main(x: Field) -> pub Field { - let closure_without_captures = |x| x + x; + let closure_without_captures = |x: Field| -> Field { x + x }; let a = closure_without_captures(1); - let closure_capturing_a_param = |y| y + x; + let closure_capturing_a_param = |y: Field| -> Field { y + x }; let b = closure_capturing_a_param(2); - let closure_capturing_a_local_var = |y| y + b; + let closure_capturing_a_local_var = |y: Field| -> Field { y + b }; let c = closure_capturing_a_local_var(3); - let closure_with_transitive_captures = |y| { + let closure_with_transitive_captures = |y: Field| -> Field { let d = 5; - let nested_closure = |z| { - let doubly_nested_closure = |w| w + x + b; + let nested_closure = |z: Field| -> Field { + let doubly_nested_closure = |w: Field| -> Field { w + x + b }; a + z + y + d + x + doubly_nested_closure(4) + x + y }; let res = nested_closure(5); diff --git a/noir_stdlib/src/cmp.nr b/noir_stdlib/src/cmp.nr index 0eed50eb42b..dde29d7ee87 100644 --- a/noir_stdlib/src/cmp.nr +++ b/noir_stdlib/src/cmp.nr @@ -6,10 +6,10 @@ trait Eq { impl Eq for Field { fn eq(self, other: Field) -> bool { self == other } } -impl Eq for u1 { fn eq(self, other: u1) -> bool { self == other } } -impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } } -impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } } impl Eq for u64 { fn eq(self, other: u64) -> bool { self == other } } +impl Eq for u32 { fn eq(self, other: u32) -> bool { self == other } } +impl Eq for u8 { fn eq(self, other: u8) -> bool { self == other } } +impl Eq for u1 { fn eq(self, other: u1) -> bool { self == other } } impl Eq for i8 { fn eq(self, other: i8) -> bool { self == other } } impl Eq for i32 { fn eq(self, other: i32) -> bool { self == other } } @@ -107,8 +107,8 @@ trait Ord { // Note: Field deliberately does not implement Ord -impl Ord for u8 { - fn cmp(self, other: u8) -> Ordering { +impl Ord for u64 { + fn cmp(self, other: u64) -> Ordering { if self < other { Ordering::less() } else if self > other { @@ -131,8 +131,8 @@ impl Ord for u32 { } } -impl Ord for u64 { - fn cmp(self, other: u64) -> Ordering { +impl Ord for u8 { + fn cmp(self, other: u8) -> Ordering { if self < other { Ordering::less() } else if self > other { diff --git a/noir_stdlib/src/ops.nr b/noir_stdlib/src/ops.nr index e561265629e..d855e794fb4 100644 --- a/noir_stdlib/src/ops.nr +++ b/noir_stdlib/src/ops.nr @@ -6,9 +6,9 @@ trait Add { impl Add for Field { fn add(self, other: Field) -> Field { self + other } } -impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } } -impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } } impl Add for u64 { fn add(self, other: u64) -> u64 { self + other } } +impl Add for u32 { fn add(self, other: u32) -> u32 { self + other } } +impl Add for u8 { fn add(self, other: u8) -> u8 { self + other } } impl Add for i8 { fn add(self, other: i8) -> i8 { self + other } } impl Add for i32 { fn add(self, other: i32) -> i32 { self + other } } @@ -22,9 +22,9 @@ trait Sub { impl Sub for Field { fn sub(self, other: Field) -> Field { self - other } } -impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } } -impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } } impl Sub for u64 { fn sub(self, other: u64) -> u64 { self - other } } +impl Sub for u32 { fn sub(self, other: u32) -> u32 { self - other } } +impl Sub for u8 { fn sub(self, other: u8) -> u8 { self - other } } impl Sub for i8 { fn sub(self, other: i8) -> i8 { self - other } } impl Sub for i32 { fn sub(self, other: i32) -> i32 { self - other } } @@ -38,9 +38,9 @@ trait Mul { impl Mul for Field { fn mul(self, other: Field) -> Field { self * other } } -impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } } -impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } } impl Mul for u64 { fn mul(self, other: u64) -> u64 { self * other } } +impl Mul for u32 { fn mul(self, other: u32) -> u32 { self * other } } +impl Mul for u8 { fn mul(self, other: u8) -> u8 { self * other } } impl Mul for i8 { fn mul(self, other: i8) -> i8 { self * other } } impl Mul for i32 { fn mul(self, other: i32) -> i32 { self * other } } @@ -54,9 +54,9 @@ trait Div { impl Div for Field { fn div(self, other: Field) -> Field { self / other } } -impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } } -impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } } impl Div for u64 { fn div(self, other: u64) -> u64 { self / other } } +impl Div for u32 { fn div(self, other: u32) -> u32 { self / other } } +impl Div for u8 { fn div(self, other: u8) -> u8 { self / other } } impl Div for i8 { fn div(self, other: i8) -> i8 { self / other } } impl Div for i32 { fn div(self, other: i32) -> i32 { self / other } } @@ -68,9 +68,9 @@ trait Rem{ } // docs:end:rem-trait -impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } -impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } @@ -84,9 +84,9 @@ trait BitOr { impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } -impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } -impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } @@ -100,9 +100,9 @@ trait BitAnd { impl BitAnd for bool { fn bitand(self, other: bool) -> bool { self & other } } -impl BitAnd for u8 { fn bitand(self, other: u8) -> u8 { self & other } } -impl BitAnd for u32 { fn bitand(self, other: u32) -> u32 { self & other } } impl BitAnd for u64 { fn bitand(self, other: u64) -> u64 { self & other } } +impl BitAnd for u32 { fn bitand(self, other: u32) -> u32 { self & other } } +impl BitAnd for u8 { fn bitand(self, other: u8) -> u8 { self & other } } impl BitAnd for i8 { fn bitand(self, other: i8) -> i8 { self & other } } impl BitAnd for i32 { fn bitand(self, other: i32) -> i32 { self & other } } @@ -116,9 +116,9 @@ trait BitXor { impl BitXor for bool { fn bitxor(self, other: bool) -> bool { self ^ other } } -impl BitXor for u8 { fn bitxor(self, other: u8) -> u8 { self ^ other } } -impl BitXor for u32 { fn bitxor(self, other: u32) -> u32 { self ^ other } } impl BitXor for u64 { fn bitxor(self, other: u64) -> u64 { self ^ other } } +impl BitXor for u32 { fn bitxor(self, other: u32) -> u32 { self ^ other } } +impl BitXor for u8 { fn bitxor(self, other: u8) -> u8 { self ^ other } } impl BitXor for i8 { fn bitxor(self, other: i8) -> i8 { self ^ other } } impl BitXor for i32 { fn bitxor(self, other: i32) -> i32 { self ^ other } } @@ -130,14 +130,14 @@ trait Shl { } // docs:end:shl-trait -impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u1 { fn shl(self, other: u1) -> u1 { self << other } } -// Bit shifting is not currently supported for signed integer types -// impl Shl for i8 { fn shl(self, other: i8) -> i8 { self << other } } -// impl Shl for i32 { fn shl(self, other: i32) -> i32 { self << other } } -// impl Shl for i64 { fn shl(self, other: i64) -> i64 { self << other } } +impl Shl for i8 { fn shl(self, other: i8) -> i8 { self << other } } +impl Shl for i32 { fn shl(self, other: i32) -> i32 { self << other } } +impl Shl for i64 { fn shl(self, other: i64) -> i64 { self << other } } // docs:start:shr-trait trait Shr { @@ -145,11 +145,11 @@ trait Shr { } // docs:end:shr-trait -impl Shr for u8 { fn shr(self, other: u8) -> u8 { self >> other } } -impl Shr for u32 { fn shr(self, other: u32) -> u32 { self >> other } } impl Shr for u64 { fn shr(self, other: u64) -> u64 { self >> other } } +impl Shr for u32 { fn shr(self, other: u32) -> u32 { self >> other } } +impl Shr for u8 { fn shr(self, other: u8) -> u8 { self >> other } } +impl Shr for u1 { fn shr(self, other: u1) -> u1 { self >> other } } -// Bit shifting is not currently supported for signed integer types -// impl Shr for i8 { fn shr(self, other: i8) -> i8 { self >> other } } -// impl Shr for i32 { fn shr(self, other: i32) -> i32 { self >> other } } -// impl Shr for i64 { fn shr(self, other: i64) -> i64 { self >> other } } +impl Shr for i8 { fn shr(self, other: i8) -> i8 { self >> other } } +impl Shr for i32 { fn shr(self, other: i32) -> i32 { self >> other } } +impl Shr for i64 { fn shr(self, other: i64) -> i64 { self >> other } } diff --git a/test_programs/compile_success_empty/regression_4635/Nargo.toml b/test_programs/compile_success_empty/regression_4635/Nargo.toml new file mode 100644 index 00000000000..563e262410f --- /dev/null +++ b/test_programs/compile_success_empty/regression_4635/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "regression_4635" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] diff --git a/test_programs/compile_success_empty/regression_4635/src/main.nr b/test_programs/compile_success_empty/regression_4635/src/main.nr new file mode 100644 index 00000000000..23918e30785 --- /dev/null +++ b/test_programs/compile_success_empty/regression_4635/src/main.nr @@ -0,0 +1,59 @@ +trait FromField { + fn from_field(field: Field) -> Self; +} + +impl FromField for Field { + fn from_field(value: Field) -> Self { + value + } +} + +trait Deserialize { + fn deserialize(fields: [Field; N]) -> Self; +} + +global AZTEC_ADDRESS_LENGTH = 1; + +struct AztecAddress { + inner : Field +} + +impl FromField for AztecAddress { + fn from_field(value: Field) -> Self { + Self { inner: value } + } +} + +impl Deserialize for AztecAddress { + fn deserialize(fields: [Field; AZTEC_ADDRESS_LENGTH]) -> Self { + AztecAddress::from_field(fields[0]) + } +} + +impl Eq for AztecAddress { + fn eq(self, other: Self) -> bool { + self.inner == other.inner + } +} + +// Custom code + +struct MyStruct { + a: T +} + +impl Deserialize<1> for MyStruct { + fn deserialize(fields: [Field; 1]) -> Self where T: FromField { + Self{ a: FromField::from_field(fields[0]) } + } +} + +fn main() { + let fields = [5; 1]; + let foo = MyStruct::deserialize(fields); // Note I don't specify T here (the type of `foo.a`) + + let bar = AztecAddress { inner: 5 }; + + // Here `T` is apparently inferred to be `AztecAddress`, presumably because of the comparison. + assert(foo.a == bar); +} diff --git a/test_programs/compile_failure/hashmap_load_factor/Nargo.toml b/test_programs/execution_failure/hashmap_load_factor/Nargo.toml similarity index 100% rename from test_programs/compile_failure/hashmap_load_factor/Nargo.toml rename to test_programs/execution_failure/hashmap_load_factor/Nargo.toml diff --git a/test_programs/execution_failure/hashmap_load_factor/Prover.toml b/test_programs/execution_failure/hashmap_load_factor/Prover.toml new file mode 100644 index 00000000000..6d72cab47fa --- /dev/null +++ b/test_programs/execution_failure/hashmap_load_factor/Prover.toml @@ -0,0 +1,23 @@ +[[input]] +key = 1 +value = 0 + +[[input]] +key = 2 +value = 0 + +[[input]] +key = 3 +value = 0 + +[[input]] +key = 4 +value = 0 + +[[input]] +key = 5 +value = 0 + +[[input]] +key = 6 +value = 0 diff --git a/test_programs/compile_failure/hashmap_load_factor/src/main.nr b/test_programs/execution_failure/hashmap_load_factor/src/main.nr similarity index 100% rename from test_programs/compile_failure/hashmap_load_factor/src/main.nr rename to test_programs/execution_failure/hashmap_load_factor/src/main.nr diff --git a/test_programs/execution_success/bit_shifts_runtime/src/main.nr b/test_programs/execution_success/bit_shifts_runtime/src/main.nr index 28b3ef656c1..ff424c9fbf6 100644 --- a/test_programs/execution_success/bit_shifts_runtime/src/main.nr +++ b/test_programs/execution_success/bit_shifts_runtime/src/main.nr @@ -7,7 +7,7 @@ fn main(x: u64, y: u64) { assert(x >> y == 32); // Bit-shift with signed integers - let mut a :i8 = y as i8; + let mut a: i8 = y as i8; let mut b: i8 = x as i8; assert(b << 1 == -128); assert(b >> 2 == 16); From 03cdba45ac073fd6fdd91549736f36f1abaef15a Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 29 Mar 2024 10:05:23 -0500 Subject: [PATCH 126/416] fix: Error when a type variable is unbound during monomorphization instead of defaulting to Field (#4674) # Description ## Problem\* Resolves ## Summary\* Adds an error when encountering an unbound `TypeVariableKind::Normal` instead of defaulting it to a Field. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_driver/src/lib.rs | 4 +- compiler/noirc_frontend/src/ast/statement.rs | 46 ++- compiler/noirc_frontend/src/debug/mod.rs | 10 +- .../src/hir/resolution/resolver.rs | 21 +- .../noirc_frontend/src/hir/type_check/stmt.rs | 32 +- compiler/noirc_frontend/src/hir_def/stmt.rs | 12 + compiler/noirc_frontend/src/hir_def/types.rs | 19 +- .../src/monomorphization/errors.rs | 39 ++ .../src/monomorphization/mod.rs | 338 +++++++++--------- compiler/noirc_frontend/src/parser/parser.rs | 21 +- .../array_length_defaulting/Nargo.toml | 7 + .../array_length_defaulting/src/main.nr | 10 + .../typevar_default/Nargo.toml | 7 + .../typevar_default/src/main.nr | 12 + .../compile_success_empty/option/src/main.nr | 4 +- .../execution_success/prelude/src/main.nr | 4 +- tooling/noirc_abi/src/lib.rs | 4 +- 17 files changed, 356 insertions(+), 234 deletions(-) create mode 100644 compiler/noirc_frontend/src/monomorphization/errors.rs create mode 100644 test_programs/compile_failure/array_length_defaulting/Nargo.toml create mode 100644 test_programs/compile_failure/array_length_defaulting/src/main.nr create mode 100644 test_programs/compile_failure/typevar_default/Nargo.toml create mode 100644 test_programs/compile_failure/typevar_default/src/main.nr diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 8b3c126dedb..151633f9e33 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -16,7 +16,9 @@ use noirc_frontend::graph::{CrateId, CrateName}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; use noirc_frontend::macros_api::MacroProcessor; -use noirc_frontend::monomorphization::{monomorphize, monomorphize_debug, MonomorphizationError}; +use noirc_frontend::monomorphization::{ + errors::MonomorphizationError, monomorphize, monomorphize_debug, +}; use noirc_frontend::node_interner::FuncId; use noirc_frontend::token::SecondaryAttribute; use std::path::Path; diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index b14ead8ad42..dea9fc0f3d3 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -120,7 +120,7 @@ impl StatementKind { // Desugar `a = b` to `a = a b`. This relies on the evaluation of `a` having no side effects, // which is currently enforced by the restricted syntax of LValues. if operator != Token::Assign { - let lvalue_expr = lvalue.as_expression(span); + let lvalue_expr = lvalue.as_expression(); let error_msg = "Token passed to Statement::assign is not a binary operator"; let infix = crate::InfixExpression { @@ -425,9 +425,9 @@ pub struct AssignStatement { #[derive(Debug, PartialEq, Eq, Clone)] pub enum LValue { Ident(Ident), - MemberAccess { object: Box, field_name: Ident }, - Index { array: Box, index: Expression }, - Dereference(Box), + MemberAccess { object: Box, field_name: Ident, span: Span }, + Index { array: Box, index: Expression, span: Span }, + Dereference(Box, Span), } #[derive(Debug, PartialEq, Eq, Clone)] @@ -484,28 +484,40 @@ impl Recoverable for Pattern { } impl LValue { - fn as_expression(&self, span: Span) -> Expression { + fn as_expression(&self) -> Expression { let kind = match self { LValue::Ident(ident) => ExpressionKind::Variable(Path::from_ident(ident.clone())), - LValue::MemberAccess { object, field_name } => { + LValue::MemberAccess { object, field_name, span: _ } => { ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: object.as_expression(span), + lhs: object.as_expression(), rhs: field_name.clone(), })) } - LValue::Index { array, index } => ExpressionKind::Index(Box::new(IndexExpression { - collection: array.as_expression(span), - index: index.clone(), - })), - LValue::Dereference(lvalue) => { + LValue::Index { array, index, span: _ } => { + ExpressionKind::Index(Box::new(IndexExpression { + collection: array.as_expression(), + index: index.clone(), + })) + } + LValue::Dereference(lvalue, _span) => { ExpressionKind::Prefix(Box::new(crate::PrefixExpression { operator: crate::UnaryOp::Dereference { implicitly_added: false }, - rhs: lvalue.as_expression(span), + rhs: lvalue.as_expression(), })) } }; + let span = self.span(); Expression::new(kind, span) } + + pub fn span(&self) -> Span { + match self { + LValue::Ident(ident) => ident.span(), + LValue::MemberAccess { span, .. } + | LValue::Index { span, .. } + | LValue::Dereference(_, span) => *span, + } + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -675,9 +687,11 @@ impl Display for LValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { LValue::Ident(ident) => ident.fmt(f), - LValue::MemberAccess { object, field_name } => write!(f, "{object}.{field_name}"), - LValue::Index { array, index } => write!(f, "{array}[{index}]"), - LValue::Dereference(lvalue) => write!(f, "*{lvalue}"), + LValue::MemberAccess { object, field_name, span: _ } => { + write!(f, "{object}.{field_name}") + } + LValue::Index { array, index, span: _ } => write!(f, "{array}[{index}]"), + LValue::Dereference(lvalue, _span) => write!(f, "*{lvalue}"), } } } diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 8e5c174d270..71e0d44b478 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -282,7 +282,7 @@ impl DebugInstrumenter { .unwrap_or_else(|| panic!("var lookup failed for var_name={}", &id.0.contents)); build_assign_var_stmt(var_id, id_expr(&ident("__debug_expr", id.span()))) } - ast::LValue::Dereference(_lv) => { + ast::LValue::Dereference(_lv, span) => { // TODO: this is a dummy statement for now, but we should // somehow track the derefence and update the pointed to // variable @@ -303,16 +303,16 @@ impl DebugInstrumenter { }); break; } - ast::LValue::MemberAccess { object, field_name } => { + ast::LValue::MemberAccess { object, field_name, span } => { cursor = object; let field_name_id = self.insert_field_name(&field_name.0.contents); - indexes.push(sint_expr(-(field_name_id.0 as i128), expression_span)); + indexes.push(sint_expr(-(field_name_id.0 as i128), *span)); } - ast::LValue::Index { index, array } => { + ast::LValue::Index { index, array, span: _ } => { cursor = array; indexes.push(index.clone()); } - ast::LValue::Dereference(_ref) => { + ast::LValue::Dereference(_ref, _span) => { unimplemented![] } } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index f8e3c4cab60..a6511a6b0fb 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -1303,18 +1303,23 @@ impl<'a> Resolver<'a> { HirLValue::Ident(ident.0, Type::Error) } - LValue::MemberAccess { object, field_name } => { - let object = Box::new(self.resolve_lvalue(*object)); - HirLValue::MemberAccess { object, field_name, field_index: None, typ: Type::Error } - } - LValue::Index { array, index } => { + LValue::MemberAccess { object, field_name, span } => HirLValue::MemberAccess { + object: Box::new(self.resolve_lvalue(*object)), + field_name, + location: Location::new(span, self.file), + field_index: None, + typ: Type::Error, + }, + LValue::Index { array, index, span } => { let array = Box::new(self.resolve_lvalue(*array)); let index = self.resolve_expression(index); - HirLValue::Index { array, index, typ: Type::Error } + let location = Location::new(span, self.file); + HirLValue::Index { array, index, location, typ: Type::Error } } - LValue::Dereference(lvalue) => { + LValue::Dereference(lvalue, span) => { let lvalue = Box::new(self.resolve_lvalue(*lvalue)); - HirLValue::Dereference { lvalue, element_type: Type::Error } + let location = Location::new(span, self.file); + HirLValue::Dereference { lvalue, location, element_type: Type::Error } } } } diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index 69363d5f00a..b97a4530999 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -1,5 +1,5 @@ use iter_extended::vecmap; -use noirc_errors::{Location, Span}; +use noirc_errors::Span; use crate::hir_def::expr::{HirExpression, HirIdent, HirLiteral}; use crate::hir_def::stmt::{ @@ -195,41 +195,43 @@ impl<'interner> TypeChecker<'interner> { (typ.clone(), HirLValue::Ident(ident.clone(), typ), mutable) } - HirLValue::MemberAccess { object, field_name, .. } => { + HirLValue::MemberAccess { object, field_name, location, .. } => { let (lhs_type, object, mut mutable) = self.check_lvalue(object, assign_span); let mut object = Box::new(object); - let span = field_name.span(); let field_name = field_name.clone(); let object_ref = &mut object; let mutable_ref = &mut mutable; + let location = *location; let dereference_lhs = move |_: &mut Self, _, element_type| { // We must create a temporary value first to move out of object_ref before // we eventually reassign to it. let id = DefinitionId::dummy_id(); - let location = Location::new(span, fm::FileId::dummy()); let ident = HirIdent::non_trait_method(id, location); let tmp_value = HirLValue::Ident(ident, Type::Error); let lvalue = std::mem::replace(object_ref, Box::new(tmp_value)); - *object_ref = Box::new(HirLValue::Dereference { lvalue, element_type }); + *object_ref = + Box::new(HirLValue::Dereference { lvalue, element_type, location }); *mutable_ref = true; }; let name = &field_name.0.contents; let (object_type, field_index) = self - .check_field_access(&lhs_type, name, span, Some(dereference_lhs)) + .check_field_access(&lhs_type, name, field_name.span(), Some(dereference_lhs)) .unwrap_or((Type::Error, 0)); let field_index = Some(field_index); let typ = object_type.clone(); - let lvalue = HirLValue::MemberAccess { object, field_name, field_index, typ }; + let lvalue = + HirLValue::MemberAccess { object, field_name, field_index, typ, location }; (object_type, lvalue, mutable) } - HirLValue::Index { array, index, .. } => { + HirLValue::Index { array, index, location, .. } => { let index_type = self.check_expression(index); let expr_span = self.interner.expr_span(index); + let location = *location; index_type.unify( &Type::polymorphic_integer_or_field(self.interner), @@ -248,7 +250,8 @@ impl<'interner> TypeChecker<'interner> { // as needed to unwrap any &mut wrappers. while let Type::MutableReference(element) = lvalue_type.follow_bindings() { let element_type = element.as_ref().clone(); - lvalue = HirLValue::Dereference { lvalue: Box::new(lvalue), element_type }; + lvalue = + HirLValue::Dereference { lvalue: Box::new(lvalue), element_type, location }; lvalue_type = *element; // We know this value to be mutable now since we found an `&mut` mutable = true; @@ -275,11 +278,12 @@ impl<'interner> TypeChecker<'interner> { }; let array = Box::new(lvalue); - (typ.clone(), HirLValue::Index { array, index: *index, typ }, mutable) + (typ.clone(), HirLValue::Index { array, index: *index, typ, location }, mutable) } - HirLValue::Dereference { lvalue, element_type: _ } => { + HirLValue::Dereference { lvalue, element_type: _, location } => { let (reference_type, lvalue, _) = self.check_lvalue(lvalue, assign_span); let lvalue = Box::new(lvalue); + let location = *location; let element_type = Type::type_variable(self.interner.next_type_variable_id()); let expected_type = Type::MutableReference(Box::new(element_type.clone())); @@ -291,7 +295,11 @@ impl<'interner> TypeChecker<'interner> { }); // Dereferences are always mutable since we already type checked against a &mut T - (element_type.clone(), HirLValue::Dereference { lvalue, element_type }, true) + ( + element_type.clone(), + HirLValue::Dereference { lvalue, element_type, location }, + true, + ) } } } diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index 4e5f718cf47..c5e287b393c 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -99,6 +99,15 @@ impl HirPattern { | HirPattern::Struct(_, _, location) => location.span, } } + + pub(crate) fn location(&self) -> Location { + match self { + HirPattern::Identifier(ident) => ident.location, + HirPattern::Mutable(_, location) + | HirPattern::Tuple(_, location) + | HirPattern::Struct(_, _, location) => *location, + } + } } /// Represents an Ast form that can be assigned to. These @@ -111,14 +120,17 @@ pub enum HirLValue { field_name: Ident, field_index: Option, typ: Type, + location: Location, }, Index { array: Box, index: ExprId, typ: Type, + location: Location, }, Dereference { lvalue: Box, element_type: Type, + location: Location, }, } diff --git a/compiler/noirc_frontend/src/hir_def/types.rs b/compiler/noirc_frontend/src/hir_def/types.rs index a2aee5e4716..ec8b54c33b8 100644 --- a/compiler/noirc_frontend/src/hir_def/types.rs +++ b/compiler/noirc_frontend/src/hir_def/types.rs @@ -543,11 +543,11 @@ impl TypeBinding { pub struct TypeVariableId(pub usize); impl Type { - pub fn default_int_type() -> Type { + pub fn default_int_or_field_type() -> Type { Type::FieldElement } - pub fn default_range_loop_type() -> Type { + pub fn default_int_type() -> Type { Type::Integer(Signedness::Unsigned, IntegerBitSize::SixtyFour) } @@ -787,7 +787,7 @@ impl std::fmt::Display for Type { Type::TypeVariable(var, TypeVariableKind::Normal) => write!(f, "{}", var.borrow()), Type::TypeVariable(binding, TypeVariableKind::Integer) => { if let TypeBinding::Unbound(_) = &*binding.borrow() { - write!(f, "{}", TypeVariableKind::Integer.default_type()) + write!(f, "{}", Type::default_int_type()) } else { write!(f, "{}", binding.borrow()) } @@ -1706,11 +1706,12 @@ impl BinaryTypeOperator { impl TypeVariableKind { /// Returns the default type this type variable should be bound to if it is still unbound /// during monomorphization. - pub(crate) fn default_type(&self) -> Type { + pub(crate) fn default_type(&self) -> Option { match self { - TypeVariableKind::IntegerOrField | TypeVariableKind::Normal => Type::default_int_type(), - TypeVariableKind::Integer => Type::default_range_loop_type(), - TypeVariableKind::Constant(length) => Type::Constant(*length), + TypeVariableKind::IntegerOrField => Some(Type::default_int_or_field_type()), + TypeVariableKind::Integer => Some(Type::default_int_type()), + TypeVariableKind::Constant(length) => Some(Type::Constant(*length)), + TypeVariableKind::Normal => None, } } } @@ -1744,12 +1745,12 @@ impl From<&Type> for PrintableType { }, Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_range_loop_type().into(), + TypeBinding::Unbound(_) => Type::default_int_type().into(), }, Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) => { match &*binding.borrow() { TypeBinding::Bound(typ) => typ.into(), - TypeBinding::Unbound(_) => Type::default_int_type().into(), + TypeBinding::Unbound(_) => Type::default_int_or_field_type().into(), } } Type::Bool => PrintableType::Boolean, diff --git a/compiler/noirc_frontend/src/monomorphization/errors.rs b/compiler/noirc_frontend/src/monomorphization/errors.rs new file mode 100644 index 00000000000..3011c26cffe --- /dev/null +++ b/compiler/noirc_frontend/src/monomorphization/errors.rs @@ -0,0 +1,39 @@ +use thiserror::Error; + +use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; + +#[derive(Debug, Error)] +pub enum MonomorphizationError { + #[error("Length of generic array could not be determined.")] + UnknownArrayLength { location: Location }, + + #[error("Type annotations needed")] + TypeAnnotationsNeeded { location: Location }, +} + +impl MonomorphizationError { + fn location(&self) -> Location { + match self { + MonomorphizationError::UnknownArrayLength { location } + | MonomorphizationError::TypeAnnotationsNeeded { location } => *location, + } + } +} + +impl From for FileDiagnostic { + fn from(error: MonomorphizationError) -> FileDiagnostic { + let location = error.location(); + let call_stack = vec![location]; + let diagnostic = error.into_diagnostic(); + diagnostic.in_file(location.file).with_call_stack(call_stack) + } +} + +impl MonomorphizationError { + fn into_diagnostic(self) -> CustomDiagnostic { + let message = self.to_string(); + let location = self.location(); + + CustomDiagnostic::simple_error(message, String::new(), location.span) + } +} diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 618eba8f190..2223773948f 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -8,16 +8,6 @@ //! //! The entry point to this pass is the `monomorphize` function which, starting from a given //! function, will monomorphize the entire reachable program. -use acvm::FieldElement; -use iter_extended::{btree_map, try_vecmap, vecmap}; -use noirc_errors::{CustomDiagnostic, FileDiagnostic, Location}; -use noirc_printable_type::PrintableType; -use std::{ - collections::{BTreeMap, HashMap, VecDeque}, - unreachable, -}; -use thiserror::Error; - use crate::{ debug::DebugInstrumenter, hir_def::{ @@ -31,13 +21,25 @@ use crate::{ FunctionKind, IntegerBitSize, Signedness, Type, TypeBinding, TypeBindings, TypeVariable, TypeVariableKind, UnaryOp, Visibility, }; +use acvm::FieldElement; +use iter_extended::{btree_map, try_vecmap, vecmap}; +use noirc_errors::Location; +use noirc_printable_type::PrintableType; +use std::{ + collections::{BTreeMap, HashMap, VecDeque}, + unreachable, +}; -use self::ast::{Definition, FuncId, Function, LocalId, Program}; use self::debug_types::DebugTypeTracker; +use self::{ + ast::{Definition, FuncId, Function, LocalId, Program}, + errors::MonomorphizationError, +}; pub mod ast; mod debug; pub mod debug_types; +pub mod errors; pub mod printer; struct LambdaContext { @@ -88,40 +90,6 @@ struct Monomorphizer<'interner> { type HirType = crate::Type; -#[derive(Debug, Error)] -pub enum MonomorphizationError { - #[error("Length of generic array could not be determined.")] - UnknownArrayLength { location: Location }, -} - -impl MonomorphizationError { - fn call_stack(&self) -> Vec { - match self { - MonomorphizationError::UnknownArrayLength { location } => vec![*location], - } - } -} - -impl From for FileDiagnostic { - fn from(error: MonomorphizationError) -> FileDiagnostic { - let call_stack = error.call_stack(); - let file_id = call_stack.last().map(|location| location.file).unwrap_or_default(); - let diagnostic = error.into_diagnostic(); - diagnostic.in_file(file_id).with_call_stack(call_stack) - } -} - -impl MonomorphizationError { - fn into_diagnostic(self) -> CustomDiagnostic { - CustomDiagnostic::simple_error( - "Internal Consistency Evaluators Errors: \n - This is likely a bug. Consider opening an issue at https://github.com/noir-lang/noir/issues".to_owned(), - self.to_string(), - noirc_errors::Span::inclusive(0, 0) - ) - } -} - /// Starting from the given `main` function, monomorphize the entire program, /// replacing all references to type variables and NamedGenerics with concrete /// types, duplicating definitions as necessary to do so. @@ -308,13 +276,15 @@ impl<'interner> Monomorphizer<'interner> { let body_expr_id = *self.interner.function(&f).as_expr(); let body_return_type = self.interner.id_type(body_expr_id); - let return_type = self.convert_type(match meta.return_type() { + let return_type = match meta.return_type() { Type::TraitAsType(..) => &body_return_type, - _ => meta.return_type(), - }); + other => other, + }; + + let return_type = Self::convert_type(return_type, meta.location)?; let unconstrained = modifiers.is_unconstrained; - let parameters = self.parameters(&meta.parameters); + let parameters = self.parameters(&meta.parameters)?; let body = self.expr(body_expr_id)?; let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; @@ -329,12 +299,15 @@ impl<'interner> Monomorphizer<'interner> { /// Monomorphize each parameter, expanding tuple/struct patterns into multiple parameters /// and binding any generic types found. - fn parameters(&mut self, params: &Parameters) -> Vec<(ast::LocalId, bool, String, ast::Type)> { + fn parameters( + &mut self, + params: &Parameters, + ) -> Result, MonomorphizationError> { let mut new_params = Vec::with_capacity(params.len()); for (parameter, typ, _) in ¶ms.0 { - self.parameter(parameter, typ, &mut new_params); + self.parameter(parameter, typ, &mut new_params)?; } - new_params + Ok(new_params) } fn parameter( @@ -342,21 +315,22 @@ impl<'interner> Monomorphizer<'interner> { param: &HirPattern, typ: &HirType, new_params: &mut Vec<(ast::LocalId, bool, String, ast::Type)>, - ) { + ) -> Result<(), MonomorphizationError> { match param { HirPattern::Identifier(ident) => { let new_id = self.next_local_id(); let definition = self.interner.definition(ident.id); let name = definition.name.clone(); - new_params.push((new_id, definition.mutable, name, self.convert_type(typ))); + let typ = Self::convert_type(typ, ident.location)?; + new_params.push((new_id, definition.mutable, name, typ)); self.define_local(ident.id, new_id); } - HirPattern::Mutable(pattern, _) => self.parameter(pattern, typ, new_params), + HirPattern::Mutable(pattern, _) => self.parameter(pattern, typ, new_params)?, HirPattern::Tuple(fields, _) => { let tuple_field_types = unwrap_tuple_type(typ); for (field, typ) in fields.iter().zip(tuple_field_types) { - self.parameter(field, &typ, new_params); + self.parameter(field, &typ, new_params)?; } } HirPattern::Struct(_, fields, _) => { @@ -373,10 +347,11 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("Expected a field named '{field_name}' in the struct pattern") }); - self.parameter(field, &field_type, new_params); + self.parameter(field, &field_type, new_params)?; } } } + Ok(()) } fn expr( @@ -399,9 +374,10 @@ impl<'interner> Monomorphizer<'interner> { } HirExpression::Literal(HirLiteral::Bool(value)) => Literal(Bool(value)), HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + let location = self.interner.id_location(expr); + let typ = Self::convert_type(&self.interner.id_type(expr), location)?; + if sign { - let typ = self.convert_type(&self.interner.id_type(expr)); - let location = self.interner.id_location(expr); match typ { ast::Type::Field => Literal(Integer(-value, typ, location)), ast::Type::Integer(_, bit_size) => { @@ -412,8 +388,6 @@ impl<'interner> Monomorphizer<'interner> { _ => unreachable!("Integer literal must be numeric"), } } else { - let typ = self.convert_type(&self.interner.id_type(expr)); - let location = self.interner.id_location(expr); Literal(Integer(value, typ, location)) } } @@ -437,7 +411,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Unary(ast::Unary { operator: prefix.operator, rhs: Box::new(self.expr(prefix.rhs)?), - result_type: self.convert_type(&self.interner.id_type(expr)), + result_type: Self::convert_type(&self.interner.id_type(expr), location)?, location, }) } @@ -466,8 +440,8 @@ impl<'interner> Monomorphizer<'interner> { let function_type = Type::Function(args, Box::new(ret.clone()), env); let method = infix.trait_method_id; - let func = self.resolve_trait_method_reference(expr, function_type, method); - self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location) + let func = self.resolve_trait_method_reference(expr, function_type, method)?; + self.create_operator_impl_call(func, lhs, infix.operator, rhs, ret, location)? } else { let lhs = Box::new(lhs); let rhs = Box::new(rhs); @@ -485,23 +459,22 @@ impl<'interner> Monomorphizer<'interner> { HirExpression::Call(call) => self.function_call(call, expr)?, - HirExpression::Cast(cast) => ast::Expression::Cast(ast::Cast { - lhs: Box::new(self.expr(cast.lhs)?), - r#type: self.convert_type(&cast.r#type), - location: self.interner.expr_location(&expr), - }), + HirExpression::Cast(cast) => { + let location = self.interner.expr_location(&expr); + let typ = Self::convert_type(&cast.r#type, location)?; + let lhs = Box::new(self.expr(cast.lhs)?); + ast::Expression::Cast(ast::Cast { lhs, r#type: typ, location }) + } HirExpression::If(if_expr) => { - let cond = self.expr(if_expr.condition)?; - let then = self.expr(if_expr.consequence)?; + let condition = Box::new(self.expr(if_expr.condition)?); + let consequence = Box::new(self.expr(if_expr.consequence)?); let else_ = if_expr.alternative.map(|alt| self.expr(alt)).transpose()?.map(Box::new); - ast::Expression::If(ast::If { - condition: Box::new(cond), - consequence: Box::new(then), - alternative: else_, - typ: self.convert_type(&self.interner.id_type(expr)), - }) + + let location = self.interner.expr_location(&expr); + let typ = Self::convert_type(&self.interner.id_type(expr), location)?; + ast::Expression::If(ast::If { condition, consequence, alternative: else_, typ }) } HirExpression::Tuple(fields) => { @@ -528,7 +501,8 @@ impl<'interner> Monomorphizer<'interner> { array_elements: Vec, is_slice: bool, ) -> Result { - let typ = self.convert_type(&self.interner.id_type(array)); + let location = self.interner.expr_location(&array); + let typ = Self::convert_type(&self.interner.id_type(array), location)?; let contents = try_vecmap(array_elements, |id| self.expr(id))?; if is_slice { Ok(ast::Expression::Literal(ast::Literal::Slice(ast::ArrayLiteral { contents, typ }))) @@ -544,7 +518,8 @@ impl<'interner> Monomorphizer<'interner> { length: HirType, is_slice: bool, ) -> Result { - let typ = self.convert_type(&self.interner.id_type(array)); + let location = self.interner.expr_location(&array); + let typ = Self::convert_type(&self.interner.id_type(array), location)?; let length = length.evaluate_to_u64().ok_or_else(|| { let location = self.interner.expr_location(&array); @@ -564,7 +539,8 @@ impl<'interner> Monomorphizer<'interner> { id: node_interner::ExprId, index: HirIndexExpression, ) -> Result { - let element_type = self.convert_type(&self.interner.id_type(id)); + let location = self.interner.expr_location(&id); + let element_type = Self::convert_type(&self.interner.id_type(id), location)?; let collection = Box::new(self.expr(index.collection)?); let index = Box::new(self.expr(index.index)?); @@ -595,11 +571,14 @@ impl<'interner> Monomorphizer<'interner> { self.define_local(for_loop.identifier.id, index_variable); let block = Box::new(self.expr(for_loop.block)?); + let index_location = for_loop.identifier.location; + let index_type = self.interner.id_type(for_loop.start_range); + let index_type = Self::convert_type(&index_type, index_location)?; Ok(ast::Expression::For(ast::For { index_variable, index_name: self.interner.definition_name(for_loop.identifier.id).to_owned(), - index_type: self.convert_type(&self.interner.id_type(for_loop.start_range)), + index_type, start_range: Box::new(start), end_range: Box::new(end), start_range_location: self.interner.expr_location(&for_loop.start_range), @@ -623,7 +602,7 @@ impl<'interner> Monomorphizer<'interner> { ) -> Result { let expr = self.expr(let_statement.expression)?; let expected_type = self.interner.id_type(let_statement.expression); - Ok(self.unpack_pattern(let_statement.pattern, expr, &expected_type)) + self.unpack_pattern(let_statement.pattern, expr, &expected_type) } fn constructor( @@ -644,7 +623,8 @@ impl<'interner> Monomorphizer<'interner> { for (field_name, expr_id) in constructor.fields { let new_id = self.next_local_id(); let field_type = field_type_map.get(&field_name.0.contents).unwrap(); - let typ = self.convert_type(field_type); + let location = self.interner.expr_location(&expr_id); + let typ = Self::convert_type(field_type, location)?; field_vars.insert(field_name.0.contents.clone(), (new_id, typ)); let expression = Box::new(self.expr(expr_id)?); @@ -688,19 +668,19 @@ impl<'interner> Monomorphizer<'interner> { pattern: HirPattern, value: ast::Expression, typ: &HirType, - ) -> ast::Expression { + ) -> Result { match pattern { HirPattern::Identifier(ident) => { let new_id = self.next_local_id(); self.define_local(ident.id, new_id); let definition = self.interner.definition(ident.id); - ast::Expression::Let(ast::Let { + Ok(ast::Expression::Let(ast::Let { id: new_id, mutable: definition.mutable, name: definition.name.clone(), expression: Box::new(value), - }) + })) } HirPattern::Mutable(pattern, _) => self.unpack_pattern(*pattern, value, typ), HirPattern::Tuple(patterns, _) => { @@ -729,7 +709,7 @@ impl<'interner> Monomorphizer<'interner> { &mut self, value: ast::Expression, fields: impl Iterator, - ) -> ast::Expression { + ) -> Result { let fresh_id = self.next_local_id(); let mut definitions = vec![ast::Expression::Let(ast::Let { @@ -740,21 +720,22 @@ impl<'interner> Monomorphizer<'interner> { })]; for (i, (field_pattern, field_type)) in fields.into_iter().enumerate() { - let location = None; + let location = field_pattern.location(); let mutable = false; let definition = Definition::Local(fresh_id); let name = i.to_string(); - let typ = self.convert_type(&field_type); + let typ = Self::convert_type(&field_type, location)?; + let location = Some(location); let new_rhs = ast::Expression::Ident(ast::Ident { location, mutable, definition, name, typ }); let new_rhs = ast::Expression::ExtractTupleField(Box::new(new_rhs), i); - let new_expr = self.unpack_pattern(field_pattern, new_rhs, &field_type); + let new_expr = self.unpack_pattern(field_pattern, new_rhs, &field_type)?; definitions.push(new_expr); } - ast::Expression::Block(definitions) + Ok(ast::Expression::Block(definitions)) } /// Find a captured variable in the innermost closure, and construct an expression @@ -780,15 +761,20 @@ impl<'interner> Monomorphizer<'interner> { } /// A local (ie non-global) ident only - fn local_ident(&mut self, ident: &HirIdent) -> Option { + fn local_ident( + &mut self, + ident: &HirIdent, + ) -> Result, MonomorphizationError> { let definition = self.interner.definition(ident.id); let name = definition.name.clone(); let mutable = definition.mutable; - let definition = self.lookup_local(ident.id)?; - let typ = self.convert_type(&self.interner.definition_type(ident.id)); + let Some(definition) = self.lookup_local(ident.id) else { + return Ok(None); + }; - Some(ast::Ident { location: Some(ident.location), mutable, definition, name, typ }) + let typ = Self::convert_type(&self.interner.definition_type(ident.id), ident.location)?; + Ok(Some(ast::Ident { location: Some(ident.location), mutable, definition, name, typ })) } fn ident( @@ -799,7 +785,7 @@ impl<'interner> Monomorphizer<'interner> { let typ = self.interner.id_type(expr_id); if let ImplKind::TraitMethod(method, _, _) = ident.impl_kind { - return Ok(self.resolve_trait_method_reference(expr_id, typ, method)); + return self.resolve_trait_method_reference(expr_id, typ, method); } let definition = self.interner.definition(ident.id); @@ -809,7 +795,7 @@ impl<'interner> Monomorphizer<'interner> { let location = Some(ident.location); let name = definition.name.clone(); let definition = self.lookup_function(*func_id, expr_id, &typ, None); - let typ = self.convert_type(&typ); + let typ = Self::convert_type(&typ, ident.location)?; let ident = ast::Ident { location, mutable, definition, name, typ: typ.clone() }; let ident_expression = ast::Expression::Ident(ident); if self.is_function_closure_type(&typ) { @@ -832,10 +818,13 @@ impl<'interner> Monomorphizer<'interner> { }; self.expr(let_.expression)? } - DefinitionKind::Local(_) => self.lookup_captured_expr(ident.id).unwrap_or_else(|| { - let ident = self.local_ident(&ident).unwrap(); - ast::Expression::Ident(ident) - }), + DefinitionKind::Local(_) => match self.lookup_captured_expr(ident.id) { + Some(expr) => expr, + None => { + let ident = self.local_ident(&ident)?.unwrap(); + ast::Expression::Ident(ident) + } + }, DefinitionKind::GenericType(type_variable) => { let value = match &*type_variable.borrow() { TypeBinding::Unbound(_) => { @@ -848,7 +837,7 @@ impl<'interner> Monomorphizer<'interner> { let value = FieldElement::from(value as u128); let location = self.interner.id_location(expr_id); - let typ = self.convert_type(&typ); + let typ = Self::convert_type(&typ, ident.location)?; ast::Expression::Literal(ast::Literal::Integer(value, typ, location)) } }; @@ -857,26 +846,28 @@ impl<'interner> Monomorphizer<'interner> { } /// Convert a non-tuple/struct type to a monomorphized type - fn convert_type(&self, typ: &HirType) -> ast::Type { - match typ { + fn convert_type(typ: &HirType, location: Location) -> Result { + Ok(match typ { HirType::FieldElement => ast::Type::Field, HirType::Integer(sign, bits) => ast::Type::Integer(*sign, *bits), HirType::Bool => ast::Type::Bool, HirType::String(size) => ast::Type::String(size.evaluate_to_u64().unwrap_or(0)), HirType::FmtString(size, fields) => { let size = size.evaluate_to_u64().unwrap_or(0); - let fields = Box::new(self.convert_type(fields.as_ref())); + let fields = Box::new(Self::convert_type(fields.as_ref(), location)?); ast::Type::FmtString(size, fields) } HirType::Unit => ast::Type::Unit, HirType::Array(length, element) => { - let element = Box::new(self.convert_type(element.as_ref())); - // TODO: convert to MonomorphizationError - let length = length.evaluate_to_u64().unwrap_or(0); + let element = Box::new(Self::convert_type(element.as_ref(), location)?); + let length = match length.evaluate_to_u64() { + Some(length) => length, + None => return Err(MonomorphizationError::TypeAnnotationsNeeded { location }), + }; ast::Type::Array(length, element) } HirType::Slice(element) => { - let element = Box::new(self.convert_type(element.as_ref())); + let element = Box::new(Self::convert_type(element.as_ref(), location)?); ast::Type::Slice(element) } HirType::TraitAsType(..) => { @@ -884,55 +875,53 @@ impl<'interner> Monomorphizer<'interner> { } HirType::NamedGeneric(binding, _) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return self.convert_type(binding); + return Self::convert_type(binding, location); } // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - binding.bind(HirType::default_int_type()); + binding.bind(HirType::default_int_or_field_type()); ast::Type::Field } HirType::TypeVariable(binding, kind) => { if let TypeBinding::Bound(binding) = &*binding.borrow() { - return self.convert_type(binding); + return Self::convert_type(binding, location); } // Default any remaining unbound type variables. // This should only happen if the variable in question is unused // and within a larger generic type. - let default = if self.is_range_loop - && (matches!(kind, TypeVariableKind::IntegerOrField) - || matches!(kind, TypeVariableKind::Integer)) - { - Type::default_range_loop_type() - } else { - kind.default_type() + let default = match kind.default_type() { + Some(typ) => typ, + None => return Err(MonomorphizationError::TypeAnnotationsNeeded { location }), }; - let monomorphized_default = self.convert_type(&default); + let monomorphized_default = Self::convert_type(&default, location)?; binding.bind(default); monomorphized_default } HirType::Struct(def, args) => { let fields = def.borrow().get_fields(args); - let fields = vecmap(fields, |(_, field)| self.convert_type(&field)); + let fields = try_vecmap(fields, |(_, field)| Self::convert_type(&field, location))?; ast::Type::Tuple(fields) } - HirType::Alias(def, args) => self.convert_type(&def.borrow().get_type(args)), + HirType::Alias(def, args) => { + Self::convert_type(&def.borrow().get_type(args), location)? + } HirType::Tuple(fields) => { - let fields = vecmap(fields, |x| self.convert_type(x)); + let fields = try_vecmap(fields, |x| Self::convert_type(x, location))?; ast::Type::Tuple(fields) } HirType::Function(args, ret, env) => { - let args = vecmap(args, |x| self.convert_type(x)); - let ret = Box::new(self.convert_type(ret)); - let env = self.convert_type(env); + let args = try_vecmap(args, |x| Self::convert_type(x, location))?; + let ret = Box::new(Self::convert_type(ret, location)?); + let env = Self::convert_type(env, location)?; match &env { ast::Type::Unit => ast::Type::Function(args, ret, Box::new(env)), ast::Type::Tuple(_elements) => ast::Type::Tuple(vec![ @@ -948,7 +937,7 @@ impl<'interner> Monomorphizer<'interner> { } HirType::MutableReference(element) => { - let element = self.convert_type(element); + let element = Self::convert_type(element, location)?; ast::Type::MutableReference(Box::new(element)) } @@ -956,7 +945,7 @@ impl<'interner> Monomorphizer<'interner> { unreachable!("Unexpected type {} found", typ) } HirType::Code => unreachable!("Tried to translate Code type into runtime code"), - } + }) } fn is_function_closure(&self, t: ast::Type) -> bool { @@ -987,7 +976,7 @@ impl<'interner> Monomorphizer<'interner> { expr_id: node_interner::ExprId, function_type: HirType, method: TraitMethodId, - ) -> ast::Expression { + ) -> Result { let trait_impl = self .interner .get_selected_impl_for_expression(expr_id) @@ -1031,13 +1020,15 @@ impl<'interner> Monomorphizer<'interner> { }; let the_trait = self.interner.get_trait(method.trait_id); - ast::Expression::Ident(ast::Ident { + let location = self.interner.expr_location(&expr_id); + + Ok(ast::Expression::Ident(ast::Ident { definition: Definition::Function(func_id), mutable: false, location: None, name: the_trait.methods[method.method_index].name.0.contents.clone(), - typ: self.convert_type(&function_type), - }) + typ: Self::convert_type(&function_type, location)?, + })) } fn function_call( @@ -1052,7 +1043,8 @@ impl<'interner> Monomorphizer<'interner> { self.patch_debug_instrumentation_call(&call, &mut arguments)?; let return_type = self.interner.id_type(id); - let return_type = self.convert_type(&return_type); + let location = self.interner.expr_location(&id); + let return_type = Self::convert_type(&return_type, location)?; let location = call.location; @@ -1072,7 +1064,7 @@ impl<'interner> Monomorphizer<'interner> { let mut block_expressions = vec![]; let func_type = self.interner.id_type(call.func); - let func_type = self.convert_type(&func_type); + let func_type = Self::convert_type(&func_type, location)?; let is_closure = self.is_function_closure(func_type); let func = if is_closure { @@ -1094,7 +1086,7 @@ impl<'interner> Monomorphizer<'interner> { definition: Definition::Local(local_id), mutable: false, name: "tmp".to_string(), - typ: self.convert_type(&self.interner.id_type(call.func)), + typ: Self::convert_type(&self.interner.id_type(call.func), location)?, }); let env_argument = @@ -1293,24 +1285,24 @@ impl<'interner> Monomorphizer<'interner> { fn lvalue(&mut self, lvalue: HirLValue) -> Result { let value = match lvalue { - HirLValue::Ident(ident, _) => self - .lookup_captured_lvalue(ident.id) - .unwrap_or_else(|| ast::LValue::Ident(self.local_ident(&ident).unwrap())), + HirLValue::Ident(ident, _) => match self.lookup_captured_lvalue(ident.id) { + Some(value) => value, + None => ast::LValue::Ident(self.local_ident(&ident)?.unwrap()), + }, HirLValue::MemberAccess { object, field_index, .. } => { let field_index = field_index.unwrap(); let object = Box::new(self.lvalue(*object)?); ast::LValue::MemberAccess { object, field_index } } - HirLValue::Index { array, index, typ } => { - let location = self.interner.expr_location(&index); + HirLValue::Index { array, index, typ, location } => { let array = Box::new(self.lvalue(*array)?); let index = Box::new(self.expr(index)?); - let element_type = self.convert_type(&typ); + let element_type = Self::convert_type(&typ, location)?; ast::LValue::Index { array, index, element_type, location } } - HirLValue::Dereference { lvalue, element_type } => { + HirLValue::Dereference { lvalue, element_type, location } => { let reference = Box::new(self.lvalue(*lvalue)?); - let element_type = self.convert_type(&element_type); + let element_type = Self::convert_type(&element_type, location)?; ast::LValue::Dereference { reference, element_type } } }; @@ -1324,7 +1316,7 @@ impl<'interner> Monomorphizer<'interner> { expr: node_interner::ExprId, ) -> Result { if lambda.captures.is_empty() { - self.lambda_no_capture(lambda) + self.lambda_no_capture(lambda, expr) } else { let (setup, closure_variable) = self.lambda_with_setup(lambda, expr)?; Ok(ast::Expression::Block(vec![setup, closure_variable])) @@ -1334,16 +1326,19 @@ impl<'interner> Monomorphizer<'interner> { fn lambda_no_capture( &mut self, lambda: HirLambda, + expr: node_interner::ExprId, ) -> Result { - let ret_type = self.convert_type(&lambda.return_type); + let location = self.interner.expr_location(&expr); + let ret_type = Self::convert_type(&lambda.return_type, location)?; let lambda_name = "lambda"; - let parameter_types = vecmap(&lambda.parameters, |(_, typ)| self.convert_type(typ)); + let parameter_types = + try_vecmap(&lambda.parameters, |(_, typ)| Self::convert_type(typ, location))?; // Manually convert to Parameters type so we can reuse the self.parameters method let parameters = vecmap(lambda.parameters, |(pattern, typ)| (pattern, typ, Visibility::Private)).into(); - let parameters = self.parameters(¶meters); + let parameters = self.parameters(¶meters)?; let body = self.expr(lambda.body)?; let id = self.next_function_id(); @@ -1386,15 +1381,17 @@ impl<'interner> Monomorphizer<'interner> { // patterns in the resulting tree, // which seems more fragile, we directly reuse the return parameters // of this function in those cases - let ret_type = self.convert_type(&lambda.return_type); + let location = self.interner.expr_location(&expr); + let ret_type = Self::convert_type(&lambda.return_type, location)?; let lambda_name = "lambda"; - let parameter_types = vecmap(&lambda.parameters, |(_, typ)| self.convert_type(typ)); + let parameter_types = + try_vecmap(&lambda.parameters, |(_, typ)| Self::convert_type(typ, location))?; // Manually convert to Parameters type so we can reuse the self.parameters method let parameters = vecmap(lambda.parameters, |(pattern, typ)| (pattern, typ, Visibility::Private)).into(); - let mut converted_parameters = self.parameters(¶meters); + let mut converted_parameters = self.parameters(¶meters)?; let id = self.next_function_id(); let name = lambda_name.to_owned(); @@ -1402,26 +1399,27 @@ impl<'interner> Monomorphizer<'interner> { let env_local_id = self.next_local_id(); let env_name = "env"; - let env_tuple = ast::Expression::Tuple(vecmap(&lambda.captures, |capture| { - match capture.transitive_capture_index { - Some(field_index) => match self.lambda_envs_stack.last() { - Some(lambda_ctx) => ast::Expression::ExtractTupleField( - Box::new(ast::Expression::Ident(lambda_ctx.env_ident.clone())), - field_index, - ), - None => unreachable!( - "Expected to find a parent closure environment, but found none" - ), - }, - None => { - let ident = self.local_ident(&capture.ident).unwrap(); - ast::Expression::Ident(ident) + let env_tuple = + ast::Expression::Tuple(try_vecmap(&lambda.captures, |capture| { + match capture.transitive_capture_index { + Some(field_index) => { + let lambda_ctx = self.lambda_envs_stack.last().expect( + "Expected to find a parent closure environment, but found none", + ); + + let ident = Box::new(ast::Expression::Ident(lambda_ctx.env_ident.clone())); + Ok(ast::Expression::ExtractTupleField(ident, field_index)) + } + None => { + let ident = self.local_ident(&capture.ident)?.unwrap(); + Ok(ast::Expression::Ident(ident)) + } } - } - })); + })?); + let expr_type = self.interner.id_type(expr); let env_typ = if let types::Type::Function(_, _, function_env_type) = expr_type { - self.convert_type(&function_env_type) + Self::convert_type(&function_env_type, location)? } else { unreachable!("expected a Function type for a Lambda node") }; @@ -1612,10 +1610,10 @@ impl<'interner> Monomorphizer<'interner> { rhs: ast::Expression, ret: Type, location: Location, - ) -> ast::Expression { + ) -> Result { let arguments = vec![lhs, rhs]; let func = Box::new(func); - let return_type = self.convert_type(&ret); + let return_type = Self::convert_type(&ret, location)?; let mut result = ast::Expression::Call(ast::Call { func, arguments, return_type, location }); @@ -1660,7 +1658,7 @@ impl<'interner> Monomorphizer<'interner> { _ => (), } - result + Ok(result) } /// Call sites are instantiated against the trait method, but when an impl is later selected, diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index a40355be8aa..7ecff12163a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -526,8 +526,8 @@ fn assign_operator() -> impl NoirParser { } enum LValueRhs { - MemberAccess(Ident), - Index(Expression), + MemberAccess(Ident, Span), + Index(Expression, Span), } fn lvalue<'a, P>(expr_parser: P) -> impl NoirParser + 'a @@ -539,23 +539,28 @@ where let dereferences = just(Token::Star) .ignore_then(lvalue.clone()) - .map(|lvalue| LValue::Dereference(Box::new(lvalue))); + .map_with_span(|lvalue, span| LValue::Dereference(Box::new(lvalue), span)); let parenthesized = lvalue.delimited_by(just(Token::LeftParen), just(Token::RightParen)); let term = choice((parenthesized, dereferences, l_ident)); - let l_member_rhs = just(Token::Dot).ignore_then(field_name()).map(LValueRhs::MemberAccess); + let l_member_rhs = + just(Token::Dot).ignore_then(field_name()).map_with_span(LValueRhs::MemberAccess); let l_index = expr_parser .delimited_by(just(Token::LeftBracket), just(Token::RightBracket)) - .map(LValueRhs::Index); + .map_with_span(LValueRhs::Index); term.then(l_member_rhs.or(l_index).repeated()).foldl(|lvalue, rhs| match rhs { - LValueRhs::MemberAccess(field_name) => { - LValue::MemberAccess { object: Box::new(lvalue), field_name } + LValueRhs::MemberAccess(field_name, span) => { + let span = lvalue.span().merge(span); + LValue::MemberAccess { object: Box::new(lvalue), field_name, span } + } + LValueRhs::Index(index, span) => { + let span = lvalue.span().merge(span); + LValue::Index { array: Box::new(lvalue), index, span } } - LValueRhs::Index(index) => LValue::Index { array: Box::new(lvalue), index }, }) }) } diff --git a/test_programs/compile_failure/array_length_defaulting/Nargo.toml b/test_programs/compile_failure/array_length_defaulting/Nargo.toml new file mode 100644 index 00000000000..fa376596ee2 --- /dev/null +++ b/test_programs/compile_failure/array_length_defaulting/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "array_length_defaulting" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] diff --git a/test_programs/compile_failure/array_length_defaulting/src/main.nr b/test_programs/compile_failure/array_length_defaulting/src/main.nr new file mode 100644 index 00000000000..216a9ae3f0c --- /dev/null +++ b/test_programs/compile_failure/array_length_defaulting/src/main.nr @@ -0,0 +1,10 @@ +fn main() { + let x = dep::std::unsafe::zeroed(); + foo(x); +} + +fn foo(array: [Field; N]) { + for elem in array { + println(elem); + } +} diff --git a/test_programs/compile_failure/typevar_default/Nargo.toml b/test_programs/compile_failure/typevar_default/Nargo.toml new file mode 100644 index 00000000000..b3cd08bb8fe --- /dev/null +++ b/test_programs/compile_failure/typevar_default/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "typevar_default" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/typevar_default/src/main.nr b/test_programs/compile_failure/typevar_default/src/main.nr new file mode 100644 index 00000000000..1eb9cf63de3 --- /dev/null +++ b/test_programs/compile_failure/typevar_default/src/main.nr @@ -0,0 +1,12 @@ +fn main() { + // Expecting error: type annotations needed (N not known) + let _ = slice_to_array(&[1, 2, 3]); +} + +fn slice_to_array(slice: [Field]) -> [Field; N] { + let mut array = [0; N]; + for i in 0 .. N { + array[i] = slice[i]; + } + array +} diff --git a/test_programs/compile_success_empty/option/src/main.nr b/test_programs/compile_success_empty/option/src/main.nr index 989c8f65bf4..c5f321256b1 100644 --- a/test_programs/compile_success_empty/option/src/main.nr +++ b/test_programs/compile_success_empty/option/src/main.nr @@ -39,9 +39,9 @@ fn main() { let add1_u64 = |value: Field| Option::some(value as u64 + 1); - assert(none.and_then(|_value| Option::none()).is_none()); + assert(none.and_then(|_value| none).is_none()); assert(none.and_then(add1_u64).is_none()); - assert(some.and_then(|_value| Option::none()).is_none()); + assert(some.and_then(|_value| none).is_none()); assert(some.and_then(add1_u64).unwrap() == 4); assert(some.and_then(|x| Option::some(x + ten)).unwrap() == 13); diff --git a/test_programs/execution_success/prelude/src/main.nr b/test_programs/execution_success/prelude/src/main.nr index c9ae448c486..226341f1e7b 100644 --- a/test_programs/execution_success/prelude/src/main.nr +++ b/test_programs/execution_success/prelude/src/main.nr @@ -1,6 +1,6 @@ fn main() { - let _xs = Vec::new(); - let _option = Option::none(); + let _xs: Vec = Vec::new(); + let _option: Option = Option::none(); print("42\n"); println("42"); diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index cffb0c20cd5..d0dcb373963 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -147,7 +147,9 @@ impl AbiType { Type::TypeVariable(binding, TypeVariableKind::IntegerOrField) | Type::TypeVariable(binding, TypeVariableKind::Integer) => match &*binding.borrow() { TypeBinding::Bound(typ) => Self::from_type(context, typ), - TypeBinding::Unbound(_) => Self::from_type(context, &Type::default_int_type()), + TypeBinding::Unbound(_) => { + Self::from_type(context, &Type::default_int_or_field_type()) + } }, Type::Bool => Self::Boolean, Type::String(size) => { From c08cc4fcf1cd949bed65ba422759d70b1e154929 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Fri, 29 Mar 2024 12:20:27 -0400 Subject: [PATCH 127/416] chore: add big list of naughty strings test for lexer (#4617) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4613 ## Summary\* Converts BLNS items into simple test programs and runs them through the lexer. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- .github/Cross.toml | 3 +- Cargo.lock | 9 +- compiler/noirc_frontend/Cargo.toml | 1 + compiler/noirc_frontend/build.rs | 15 + compiler/noirc_frontend/src/blns/LICENSE | 21 + compiler/noirc_frontend/src/blns/README.md | 9 + .../noirc_frontend/src/blns/blns.base64.json | 679 ++++++++++++++++++ compiler/noirc_frontend/src/lexer/lexer.rs | 110 +++ 8 files changed, 842 insertions(+), 5 deletions(-) create mode 100644 compiler/noirc_frontend/build.rs create mode 100644 compiler/noirc_frontend/src/blns/LICENSE create mode 100644 compiler/noirc_frontend/src/blns/README.md create mode 100644 compiler/noirc_frontend/src/blns/blns.base64.json diff --git a/.github/Cross.toml b/.github/Cross.toml index 09e6316a59c..6520a288d5d 100644 --- a/.github/Cross.toml +++ b/.github/Cross.toml @@ -2,7 +2,8 @@ passthrough = [ "HOME", "RUST_BACKTRACE", - "BARRETENBERG_BIN_DIR" + "BARRETENBERG_BIN_DIR", + "BLNS_JSON_PATH" ] volumes = [ "HOME", diff --git a/Cargo.lock b/Cargo.lock index c42c2b5d74e..2f85b26f974 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1044,9 +1044,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "corosensei" @@ -3088,6 +3088,7 @@ version = "0.26.0" dependencies = [ "acvm", "arena", + "base64 0.21.2", "chumsky", "fm", "iter-extended", @@ -4555,9 +4556,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smawk" diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index a3a8d460572..03b92e15032 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -26,6 +26,7 @@ tracing.workspace = true petgraph = "0.6" [dev-dependencies] +base64.workspace = true strum = "0.24" strum_macros = "0.24" tempfile.workspace = true diff --git a/compiler/noirc_frontend/build.rs b/compiler/noirc_frontend/build.rs new file mode 100644 index 00000000000..53ae9489168 --- /dev/null +++ b/compiler/noirc_frontend/build.rs @@ -0,0 +1,15 @@ +use std::path::PathBuf; + +const BLNS_JSON_PATH: &str = "BLNS_JSON_PATH"; + +fn main() -> Result<(), String> { + let out_dir = std::env::var("OUT_DIR").unwrap(); + + let dest_path = PathBuf::from(out_dir.clone()).join("blns.base64.json"); + let dest_path_str = dest_path.to_str().unwrap(); + + println!("cargo:rustc-env={BLNS_JSON_PATH}={dest_path_str}"); + std::fs::copy("./src/blns/blns.base64.json", dest_path).unwrap(); + + Ok(()) +} diff --git a/compiler/noirc_frontend/src/blns/LICENSE b/compiler/noirc_frontend/src/blns/LICENSE new file mode 100644 index 00000000000..4ab5bbd793b --- /dev/null +++ b/compiler/noirc_frontend/src/blns/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2020 Max Woolf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/compiler/noirc_frontend/src/blns/README.md b/compiler/noirc_frontend/src/blns/README.md new file mode 100644 index 00000000000..201ed68aef0 --- /dev/null +++ b/compiler/noirc_frontend/src/blns/README.md @@ -0,0 +1,9 @@ +# BLNS + +[Big List of Naughty Strings](https://github.com/minimaxir/big-list-of-naughty-strings) + +## Updating + +```bash +wget -O blns.base64.json "https://raw.githubusercontent.com/minimaxir/big-list-of-naughty-strings/master/blns.base64.json" +``` diff --git a/compiler/noirc_frontend/src/blns/blns.base64.json b/compiler/noirc_frontend/src/blns/blns.base64.json new file mode 100644 index 00000000000..18e61c0d2c4 --- /dev/null +++ b/compiler/noirc_frontend/src/blns/blns.base64.json @@ -0,0 +1,679 @@ +[ + "", + "dW5kZWZpbmVk", + "dW5kZWY=", + "bnVsbA==", + "TlVMTA==", + "KG51bGwp", + "bmls", + "TklM", + "dHJ1ZQ==", + "ZmFsc2U=", + "VHJ1ZQ==", + "RmFsc2U=", + "VFJVRQ==", + "RkFMU0U=", + "Tm9uZQ==", + "aGFzT3duUHJvcGVydHk=", + "XA==", + "MA==", + "MQ==", + "MS4wMA==", + "JDEuMDA=", + "MS8y", + "MUUy", + "MUUwMg==", + "MUUrMDI=", + "LTE=", + "LTEuMDA=", + "LSQxLjAw", + "LTEvMg==", + "LTFFMg==", + "LTFFMDI=", + "LTFFKzAy", + "MS8w", + "MC8w", + "LTIxNDc0ODM2NDgvLTE=", + "LTkyMjMzNzIwMzY4NTQ3NzU4MDgvLTE=", + "LTA=", + "LTAuMA==", + "KzA=", + "KzAuMA==", + "MC4wMA==", + "MC4uMA==", + "Lg==", + "MC4wLjA=", + "MCwwMA==", + "MCwsMA==", + "LA==", + "MCwwLDA=", + "MC4wLzA=", + "MS4wLzAuMA==", + "MC4wLzAuMA==", + "MSwwLzAsMA==", + "MCwwLzAsMA==", + "LS0x", + "LQ==", + "LS4=", + "LSw=", + "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5", + "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5", + "TmFO", + "SW5maW5pdHk=", + "LUluZmluaXR5", + "SU5G", + "MSNJTkY=", + "LTEjSU5E", + "MSNRTkFO", + "MSNTTkFO", + "MSNJTkQ=", + "MHgw", + "MHhmZmZmZmZmZg==", + "MHhmZmZmZmZmZmZmZmZmZmZm", + "MHhhYmFkMWRlYQ==", + "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5", + "MSwwMDAuMDA=", + "MSAwMDAuMDA=", + "MScwMDAuMDA=", + "MSwwMDAsMDAwLjAw", + "MSAwMDAgMDAwLjAw", + "MScwMDAnMDAwLjAw", + "MS4wMDAsMDA=", + "MSAwMDAsMDA=", + "MScwMDAsMDA=", + "MS4wMDAuMDAwLDAw", + "MSAwMDAgMDAwLDAw", + "MScwMDAnMDAwLDAw", + "MDEwMDA=", + "MDg=", + "MDk=", + "Mi4yMjUwNzM4NTg1MDcyMDExZS0zMDg=", + "LC4vOydbXS09", + "PD4/OiJ7fXxfKw==", + "IUAjJCVeJiooKWB+", + "AQIDBAUGBwgODxAREhMUFRYXGBkaGxwdHh9/", + "woDCgcKCwoPChMKGwofCiMKJworCi8KMwo3CjsKPwpDCkcKSwpPClMKVwpbCl8KYwpnCmsKbwpzC", + "ncKewp8=", + "CwwgwoXCoOGagOKAgOKAgeKAguKAg+KAhOKAheKAhuKAh+KAiOKAieKAiuKAi+KAqOKAqeKAr+KB", + "n+OAgA==", + "wq3YgNiB2ILYg9iE2IXYnNud3I/hoI7igIvigIzigI3igI7igI/igKrigKvigKzigK3igK7igaDi", + "gaHigaLigaPigaTigabigafigajiganigarigavigaziga3iga7iga/vu7/vv7nvv7rvv7vwkYK9", + "8JuyoPCbsqHwm7Ki8Juyo/CdhbPwnYW08J2FtfCdhbbwnYW38J2FuPCdhbnwnYW686CAgfOggKDz", + "oICh86CAovOggKPzoICk86CApfOggKbzoICn86CAqPOggKnzoICq86CAq/OggKzzoICt86CArvOg", + "gK/zoICw86CAsfOggLLzoICz86CAtPOggLXzoIC286CAt/OggLjzoIC586CAuvOggLvzoIC886CA", + "vfOggL7zoIC/86CBgPOggYHzoIGC86CBg/OggYTzoIGF86CBhvOggYfzoIGI86CBifOggYrzoIGL", + "86CBjPOggY3zoIGO86CBj/OggZDzoIGR86CBkvOggZPzoIGU86CBlfOggZbzoIGX86CBmPOggZnz", + "oIGa86CBm/OggZzzoIGd86CBnvOggZ/zoIGg86CBofOggaLzoIGj86CBpPOggaXzoIGm86CBp/Og", + "gajzoIGp86CBqvOggavzoIGs86CBrfOgga7zoIGv86CBsPOggbHzoIGy86CBs/OggbTzoIG186CB", + "tvOggbfzoIG486CBufOggbrzoIG786CBvPOggb3zoIG+86CBvw==", + "77u/", + "77++", + "zqniiYjDp+KImuKIq8ucwrXiiaTiiaXDtw==", + "w6XDn+KIgsaSwqnLmeKIhsuawqzigKbDpg==", + "xZPiiJHCtMKu4oCgwqXCqMuGw7jPgOKAnOKAmA==", + "wqHihKLCo8Ki4oiewqfCtuKAosKqwrrigJPiiaA=", + "wrjLm8OH4peKxLHLnMOCwq/LmMK/", + "w4XDjcOOw4/LncOTw5Tvo7/DksOaw4bimIM=", + "xZLigJ7CtOKAsMuHw4HCqMuGw5jiiI/igJ3igJk=", + "YOKBhOKCrOKAueKAuu+sge+sguKAocKwwrfigJrigJTCsQ==", + "4oWb4oWc4oWd4oWe", + "0IHQgtCD0ITQhdCG0IfQiNCJ0IrQi9CM0I3QjtCP0JDQkdCS0JPQlNCV0JbQl9CY0JnQmtCb0JzQ", + "ndCe0J/QoNCh0KLQo9Ck0KXQptCn0KjQqdCq0KvQrNCt0K7Qr9Cw0LHQstCz0LTQtdC20LfQuNC5", + "0LrQu9C80L3QvtC/0YDRgdGC0YPRhNGF0YbRh9GI0YnRitGL0YzRjdGO0Y8=", + "2aDZodmi2aPZpNml2abZp9mo2ak=", + "4oGw4oG04oG1", + "4oKA4oKB4oKC", + "4oGw4oG04oG14oKA4oKB4oKC", + "4LiU4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH", + "4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ", + "4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH", + "4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmJ4LmH", + "4LmH4LmH4LmH4LmH4LmJ4LmJ4LmJ4LmJ4LmJ4LmH4LmH4LmH4LmHIOC4lOC5ieC5ieC5ieC5ieC5", + "ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5", + "ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5", + "h+C5ieC5ieC5ieC5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5", + "h+C5h+C5h+C5h+C5h+C5ieC5ieC5ieC5ieC5ieC5ieC5ieC5ieC5h+C5h+C5h+C5h+C5h+C5ieC5", + "ieC5ieC5ieC5ieC5h+C5h+C5h+C5hyDguJTguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfg", + "uYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYnguYnguYnguYnguYfg", + "uYfguYfguYfguYfguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYng", + "uYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYng", + "uYnguYnguYnguYnguYnguYnguYnguYfguYfguYfguYfguYfguYnguYnguYnguYnguYnguYfguYfg", + "uYfguYc=", + "Jw==", + "Ig==", + "Jyc=", + "IiI=", + "JyIn", + "IicnJyciJyI=", + "IiciJyInJycnIg==", + "PGZvbyB2YWw94oCcYmFy4oCdIC8+", + "PGZvbyB2YWw94oCcYmFy4oCdIC8+", + "PGZvbyB2YWw94oCdYmFy4oCcIC8+", + "PGZvbyB2YWw9YGJhcicgLz4=", + "55Sw5Lit44GV44KT44Gr44GC44GS44Gm5LiL44GV44GE", + "44OR44O844OG44Kj44O844G46KGM44GL44Gq44GE44GL", + "5ZKM6KO95ryi6Kqe", + "6YOo6JC95qC8", + "7IKs7ZqM6rO87ZWZ7JuQIOyWtO2VmeyXsOq1rOyGjA==", + "7LCm7LCo66W8IO2DgOqzoCDsmKgg7Y6y7Iuc66eo6rO8IOyRm+uLpOumrCDrmKDrsKnqsIHtlZg=", + "56S+5pyD56eR5a246Zmi6Kqe5a2456CU56m25omA", + "7Jq4656A67CU7Yag66W0", + "8KCcjvCgnLHwoJ258KCxk/CgsbjwoLKW8KCzjw==", + "6KGo44Od44GCQem3l8WSw6nvvKLpgI3DnMOfwqrEhcOx5LiC45CA8KCAgA==", + "44O94Ly84LqI2YTNnOC6iOC8ve++iSDjg73gvLzguojZhM2c4LqI4Ly9776J", + "KO+9oeKXlSDiiIAg4peV772hKQ==", + "772A772oKMK04oiA772A4oip", + "X1/vvpsoLF8sKik=", + "44O7KO+/o+KIgO+/oynjg7s6Kjo=", + "776f772l4py/44O+4pWyKO+9oeKXleKAv+KXle+9oSnilbHinL/vvaXvvp8=", + "LOOAguODuzoqOuODu+OCnOKAmSgg4pi7IM+JIOKYuyAp44CC44O7Oio644O744Kc4oCZ", + "KOKVr8Kw4pahwrDvvInila/vuLUg4pS74pSB4pS7KQ==", + "KO++ieCypeebiuCype+8ie++ie+7vyDilLvilIHilLs=", + "4pSs4pSA4pSs44OOKCDCuiBfIMK644OOKQ==", + "KCDNocKwIM2cypYgzaHCsCk=", + "8J+YjQ==", + "8J+RqfCfj70=", + "8J+RqOKAjfCfprAg8J+RqPCfj7/igI3wn6awIPCfkajigI3wn6axIPCfkajwn4+/4oCN8J+msSDwn6a58J+Pv+KAjeKZgu+4jw==", + "8J+RviDwn5mHIPCfkoEg8J+ZhSDwn5mGIPCfmYsg8J+ZjiDwn5mN", + "8J+QtSDwn5mIIPCfmYkg8J+Zig==", + "4p2k77iPIPCfkpQg8J+SjCDwn5KVIPCfkp4g8J+SkyDwn5KXIPCfkpYg8J+SmCDwn5KdIPCfkp8g", + "8J+SnCDwn5KbIPCfkpog8J+SmQ==", + "4pyL8J+PvyDwn5Kq8J+PvyDwn5GQ8J+PvyDwn5mM8J+PvyDwn5GP8J+PvyDwn5mP8J+Pvw==", + "8J+aviDwn4aSIPCfhpMg8J+GlSDwn4aWIPCfhpcg8J+GmSDwn4+n", + "MO+4j+KDoyAx77iP4oOjIDLvuI/ig6MgM++4j+KDoyA077iP4oOjIDXvuI/ig6MgNu+4j+KDoyA3", + "77iP4oOjIDjvuI/ig6MgOe+4j+KDoyDwn5Sf", + "8J+HuvCfh7jwn4e38J+HuvCfh7gg8J+HpvCfh6vwn4em8J+HsvCfh7g=", + "8J+HuvCfh7jwn4e38J+HuvCfh7jwn4em8J+Hq/Cfh6bwn4ey", + "8J+HuvCfh7jwn4e38J+HuvCfh7jwn4em", + "77yR77yS77yT", + "2aHZotmj", + "2KvZhSDZhtmB2LMg2LPZgti32Kog2YjYqNin2YTYqtit2K/Zitiv2IwsINis2LLZitix2KrZiiDY", + "qNin2LPYqtiu2K/Yp9mFINij2YYg2K/ZhtmILiDYpdiwINmH2YbYp9ifINin2YTYs9iq2KfYsSDZ", + "iNiq2YbYtdmK2Kgg2YPYp9mGLiDYo9mH2ZHZhCDYp9mK2LfYp9mE2YrYp9iMINio2LHZiti32KfZ", + "htmK2Kct2YHYsdmG2LPYpyDZgtivINij2K7YsC4g2LPZhNmK2YXYp9mG2Iwg2KXYqtmB2KfZgtmK", + "2Kkg2KjZitmGINmF2KcsINmK2LDZg9ixINin2YTYrdiv2YjYryDYo9mKINio2LnYrywg2YXYudin", + "2YXZhNipINio2YjZhNmG2K/Yp9iMINin2YTYpdi32YTYp9mCINi52YQg2KXZitmILg==", + "15HWsNa816jWtdeQ16nWtNeB15nXqiwg15HWuNa816jWuNeQINeQ1rHXnNa515TWtNeZ150sINeQ", + "1rXXqiDXlNa316nWuNa814HXnta315nWtNedLCDXldaw15DWtdeqINeU1rjXkNa416jWttel", + "15TWuNeZ1rDXqta415R0ZXN02KfZhNi12YHYrdin2Kog2KfZhNiq2ZHYrdmI2YQ=", + "77e9", + "77e6", + "2YXZj9mG2Y7Yp9mC2Y7YtNmO2KnZjyDYs9mP2KjZj9mE2ZAg2KfZkNiz2ZLYqtmQ2K7Zktiv2Y7Y", + "p9mF2ZAg2KfZhNmE2ZHZj9i62Y7YqdmQINmB2ZDZiiDYp9mE2YbZkdmP2LjZj9mF2ZAg2KfZhNmS", + "2YLZjtin2KbZkNmF2Y7YqdmQINmI2Y7ZgdmQ2YrZhSDZitmO2K7Zj9i12ZHZjiDYp9mE2KrZkdmO", + "2LfZktio2ZDZitmC2Y7Yp9iq2Y8g2KfZhNmS2K3Yp9iz2Y/ZiNio2ZDZitmR2Y7YqdmP2Iw=", + "4Zqb4ZqE4ZqT4ZqQ4ZqL4ZqS4ZqE4ZqA4ZqR4ZqE4ZqC4ZqR4ZqP4ZqF4Zqc", + "4Zqb4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqA4ZqcCg==", + "4oCq4oCqdGVzdOKAqg==", + "4oCrdGVzdOKAqw==", + "4oCpdGVzdOKAqQ==", + "dGVzdOKBoHRlc3TigKs=", + "4oGmdGVzdOKBpw==", + "4bmwzLrMusyVb82eIMy3acyyzKzNh8yqzZluzJ3Ml82VdsyfzJzMmMymzZ9vzLbMmcywzKBrw6jN", + "msyuzLrMqsy5zLHMpCDMlnTMnc2VzLPMo8y7zKrNnmjMvM2TzLLMpsyzzJjMsmXNh8yjzLDMpsys", + "zY4gzKLMvMy7zLHMmGjNms2OzZnMnMyjzLLNhWnMpsyyzKPMsMykdsy7zY1lzLrMrcyzzKrMsC1t", + "zKJpzYVuzJbMusyezLLMr8ywZMy1zLzMn82ZzKnMvMyYzLMgzJ7MpcyxzLPMrXLMm8yXzJhlzZlw", + "zaByzLzMnsy7zK3Ml2XMusygzKPNn3PMmM2HzLPNjcydzYllzYnMpcyvzJ7Mss2azKzNnMe5zKzN", + "js2OzJ/Mls2HzKR0zY3MrMykzZPMvMytzZjNhWnMqsyxbs2gZ8y0zYkgzY/Nic2FY8yszJ9ozaFh", + "zKvMu8yvzZhvzKvMn8yWzY3MmcydzYlzzJfMpsyyLsyozLnNiMyj", + "zKHNk8yezYVJzJfMmMymzZ1uzYfNh82ZdsyuzKtva8yyzKvMmc2IacyWzZnMrcy5zKDMnm7Mocy7", + "zK7Mo8y6Z8yyzYjNmcytzZnMrM2OIMywdM2UzKZozJ7MsmXMosykIM2NzKzMss2WZsy0zJjNlcyj", + "w6jNluG6ucylzKlszZbNlM2aac2TzZrMps2gbs2WzY3Ml82TzLPMrmfNjSDMqG/NmsyqzaFmzJjM", + "o8ysIMyWzJjNlsyfzZnMrmPSic2UzKvNls2TzYfNls2FaMy1zKTMo82azZTDocyXzLzNlc2Fb8y8", + "zKPMpXPMsc2IzLrMlsymzLvNoi7Mm8yWzJ7MoMyrzLA=", + "zJfMus2WzLnMr82T4bmuzKTNjcylzYfNiGjMssyBZc2PzZPMvMyXzJnMvMyjzZQgzYfMnMyxzKDN", + "k82NzYVOzZXNoGXMl8yxesyYzJ3MnMy6zZlwzKTMusy5zY3Mr82aZcygzLvMoM2ccsyozKTNjcy6", + "zJbNlMyWzJZkzKDMn8ytzKzMnc2facymzZbMqc2TzZTMpGHMoMyXzKzNicyZbs2azZwgzLvMnsyw", + "zZrNhWjMtc2JacyzzJ52zKLNh+G4mc2OzZ8t0onMrcypzLzNlG3MpMytzKtpzZXNh8ydzKZuzJfN", + "meG4jcyfIMyvzLLNlc2ex6vMn8yvzLDMss2ZzLvMnWYgzKrMsMywzJfMlsytzJjNmGPMps2NzLLM", + "ns2NzKnMmeG4pc2aYcyuzY7Mn8yZzZzGocypzLnNjnPMpC7MncydINKJWsyhzJbMnM2WzLDMo82J", + "zJxhzZbMsM2ZzKzNoWzMssyrzLPNjcypZ8yhzJ/MvMyxzZrMnsyszYVvzJfNnC7Mnw==", + "zKZIzKzMpMyXzKTNnWXNnCDMnMylzJ3Mu82NzJ/MgXfMlWjMlsyvzZNvzJ3NmcyWzY7MscyuINKJ", + "zLrMmcyezJ/NiFfMt8y8zK1hzLrMqs2NxK/NiM2VzK3NmcyvzJx0zLbMvMyuc8yYzZnNlsyVIMyg", + "zKvMoELMu82NzZnNicyzzYVlzLVozLXMrM2HzKvNmWnMuc2TzLPMs8yuzY7Mq8yVbs2fZMy0zKrM", + "nMyWIMywzYnMqc2HzZnMss2ezYVUzZbMvM2TzKrNomjNj82TzK7Mu2XMrMydzJ/NhSDMpMy5zJ1X", + "zZnMnsydzZTNh82dzYVhzY/Nk82UzLnMvMyjbMy0zZTMsMykzJ/NlOG4vcyrLs2V", + "WsyuzJ7MoM2ZzZTNheG4gMyXzJ7NiMy7zJfhuLbNmc2OzK/MucyezZNHzLtPzK3Ml8yu", + "y5nJkG5i4bSJbMmQIMmQdcaDyZDJryDHncm5b2xvcCDKh8edIMedyblvccmQbCDKh24gyod1bnDh", + "tIlw4bSJyZR14bSJIMm5b2TJr8edyocgcG/Jr3Nu4bSJx50gb3AgcMedcyAnyofhtIlsx50gxoN1", + "4bSJyZRz4bSJZOG0iXDJkCDJuW7Kh8edyofJlMedc3VvyZQgJ8qHx53Jr8mQIMqH4bSJcyDJuW9s", + "b3Agya9uc2ThtIkgya/Hncm5b8ul", + "MDDLmcaWJC0=", + "77y0772I772FIO+9ke+9le+9ie+9g++9iyDvvYLvvZLvvY/vvZfvvY4g772G772P772YIO+9iu+9", + "le+9je+9kO+9kyDvvY/vvZbvvYXvvZIg772U772I772FIO+9jO+9ge+9mu+9mSDvvYTvvY/vvYc=", + "8J2Qk/CdkKHwnZCeIPCdkKrwnZCu8J2QovCdkJzwnZCkIPCdkJvwnZCr8J2QqPCdkLDwnZCnIPCd", + "kJ/wnZCo8J2QsSDwnZCj8J2QrvCdkKbwnZCp8J2QrCDwnZCo8J2Qr/CdkJ7wnZCrIPCdkK3wnZCh", + "8J2QniDwnZCl8J2QmvCdkLPwnZCyIPCdkJ3wnZCo8J2QoA==", + "8J2Vv/Cdlo3wnZaKIPCdlpbwnZaa8J2WjvCdlojwnZaQIPCdlofwnZaX8J2WlPCdlpzwnZaTIPCd", + "lovwnZaU8J2WnSDwnZaP8J2WmvCdlpLwnZaV8J2WmCDwnZaU8J2Wm/CdlorwnZaXIPCdlpnwnZaN", + "8J2WiiDwnZaR8J2WhvCdlp/wnZaeIPCdlonwnZaU8J2WjA==", + "8J2Ru/CdkonwnZKGIPCdkpLwnZKW8J2SivCdkoTwnZKMIPCdkoPwnZKT8J2SkPCdkpjwnZKPIPCd", + "kofwnZKQ8J2SmSDwnZKL8J2SlvCdko7wnZKR8J2SlCDwnZKQ8J2Sl/CdkobwnZKTIPCdkpXwnZKJ", + "8J2ShiDwnZKN8J2SgvCdkpvwnZKaIPCdkoXwnZKQ8J2SiA==", + "8J2To/Cdk7HwnZOuIPCdk7rwnZO+8J2TsvCdk6zwnZO0IPCdk6vwnZO78J2TuPCdlIDwnZO3IPCd", + "k6/wnZO48J2UgSDwnZOz8J2TvvCdk7bwnZO58J2TvCDwnZO48J2Tv/Cdk67wnZO7IPCdk73wnZOx", + "8J2TriDwnZO18J2TqvCdlIPwnZSCIPCdk63wnZO48J2TsA==", + "8J2Vi/CdlZnwnZWWIPCdlaLwnZWm8J2VmvCdlZTwnZWcIPCdlZPwnZWj8J2VoPCdlajwnZWfIPCd", + "lZfwnZWg8J2VqSDwnZWb8J2VpvCdlZ7wnZWh8J2VpCDwnZWg8J2Vp/CdlZbwnZWjIPCdlaXwnZWZ", + "8J2VliDwnZWd8J2VkvCdlavwnZWqIPCdlZXwnZWg8J2VmA==", + "8J2ag/CdmpHwnZqOIPCdmprwnZqe8J2akvCdmozwnZqUIPCdmovwnZqb8J2amPCdmqDwnZqXIPCd", + "mo/wnZqY8J2aoSDwnZqT8J2anvCdmpbwnZqZ8J2anCDwnZqY8J2an/Cdmo7wnZqbIPCdmp3wnZqR", + "8J2ajiDwnZqV8J2aivCdmqPwnZqiIPCdmo3wnZqY8J2akA==", + "4pKv4pKj4pKgIOKSrOKSsOKSpOKSnuKSpiDikp3ikq3ikqrikrLikqkg4pKh4pKq4pKzIOKSpeKS", + "sOKSqOKSq+KSriDikqrikrHikqDikq0g4pKv4pKj4pKgIOKSp+KSnOKSteKStCDikp/ikqrikqI=", + "PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+", + "Jmx0O3NjcmlwdCZndDthbGVydCgmIzM5OzEyMyYjMzk7KTsmbHQ7L3NjcmlwdCZndDs=", + "PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEyMykgLz4=", + "PHN2Zz48c2NyaXB0PjEyMzwxPmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "Ij48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "Jz48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "PjxzY3JpcHQ+YWxlcnQoMTIzKTwvc2NyaXB0Pg==", + "PC9zY3JpcHQ+PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+", + "PCAvIHNjcmlwdCA+PCBzY3JpcHQgPmFsZXJ0KDEyMyk8IC8gc2NyaXB0ID4=", + "b25mb2N1cz1KYVZhU0NyaXB0OmFsZXJ0KDEyMykgYXV0b2ZvY3Vz", + "IiBvbmZvY3VzPUphVmFTQ3JpcHQ6YWxlcnQoMTIzKSBhdXRvZm9jdXM=", + "JyBvbmZvY3VzPUphVmFTQ3JpcHQ6YWxlcnQoMTIzKSBhdXRvZm9jdXM=", + "77ycc2NyaXB077yeYWxlcnQoMTIzKe+8nC9zY3JpcHTvvJ4=", + "PHNjPHNjcmlwdD5yaXB0PmFsZXJ0KDEyMyk8L3NjPC9zY3JpcHQ+cmlwdD4=", + "LS0+PHNjcmlwdD5hbGVydCgxMjMpPC9zY3JpcHQ+", + "IjthbGVydCgxMjMpO3Q9Ig==", + "JzthbGVydCgxMjMpO3Q9Jw==", + "SmF2YVNDcmlwdDphbGVydCgxMjMp", + "O2FsZXJ0KDEyMyk7", + "c3JjPUphVmFTQ3JpcHQ6cHJvbXB0KDEzMik=", + "Ij48c2NyaXB0PmFsZXJ0KDEyMyk7PC9zY3JpcHQgeD0i", + "Jz48c2NyaXB0PmFsZXJ0KDEyMyk7PC9zY3JpcHQgeD0n", + "PjxzY3JpcHQ+YWxlcnQoMTIzKTs8L3NjcmlwdCB4PQ==", + "IiBhdXRvZm9jdXMgb25rZXl1cD0iamF2YXNjcmlwdDphbGVydCgxMjMp", + "JyBhdXRvZm9jdXMgb25rZXl1cD0namF2YXNjcmlwdDphbGVydCgxMjMp", + "PHNjcmlwdHgyMHR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgzRXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwRHR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwOXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwQ3R5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgyRnR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "PHNjcmlwdHgwQXR5cGU9InRleHQvamF2YXNjcmlwdCI+amF2YXNjcmlwdDphbGVydCgxKTs8L3Nj", + "cmlwdD4=", + "J2AiPjx4M0NzY3JpcHQ+amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "J2AiPjx4MDBzY3JpcHQ+amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "QUJDPGRpdiBzdHlsZT0ieHgzQWV4cHJlc3Npb24oamF2YXNjcmlwdDphbGVydCgxKSI+REVG", + "QUJDPGRpdiBzdHlsZT0ieDpleHByZXNzaW9ueDVDKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHByZXNzaW9ueDAwKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHB4MDByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDpleHB4NUNyZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MEFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MDlleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTN4ODB4ODBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODRleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4QzJ4QTBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRF", + "Rg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4OEFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MERleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MENleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODdleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RUZ4QkJ4QkZleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MjBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODhleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MDBleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4OEJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODZleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODVleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4MEJleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSkiPkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODFleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODNleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "QUJDPGRpdiBzdHlsZT0ieDp4RTJ4ODB4ODlleHByZXNzaW9uKGphdmFzY3JpcHQ6YWxlcnQoMSki", + "PkRFRg==", + "PGEgaHJlZj0ieDBCamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEMyeEEwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVs", + "ZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUxeEEweDhFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDExamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDEwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDIwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDEzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDhBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweEFGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgxamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFEamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA3amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUxeDlBeDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDgzamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAxamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg0amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUzeDgweDgwamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDEyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBEamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBBamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDBDamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweEE4amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDE2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDAyamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFCamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDA2amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweEE5amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgweDg1amF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFFamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieEUyeDgxeDlGamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6", + "emVsZW1lbnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0ieDFDamF2YXNjcmlwdDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwMDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgzQTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwOTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwRDpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "PGEgaHJlZj0iamF2YXNjcmlwdHgwQTpqYXZhc2NyaXB0OmFsZXJ0KDEpIiBpZD0iZnV6emVsZW1l", + "bnQxIj50ZXN0PC9hPg==", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQW9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyMm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwRG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyRm9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwOW9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwQ29uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgwMG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyN29uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "YCInPjxpbWcgc3JjPXh4eDp4IHgyMG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT4=", + "ImAnPjxzY3JpcHQ+eDNCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDBEamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEVGeEJCeEJGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgxamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg0amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUzeDgweDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDA5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg1amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg4amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDAwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweEE4amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDhBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUxeDlBeDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDBDamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDJCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEYweDkweDk2eDlBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+LWphdmFzY3JpcHQ6YWxlcnQoMSk8L3NjcmlwdD4=", + "ImAnPjxzY3JpcHQ+eDBBamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweEFGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDdFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg3amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgxeDlGamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweEE5amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEMyeDg1amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEVGeEJGeEFFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgzamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDhCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEVGeEJGeEJFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDIxamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDgyamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUyeDgweDg2amF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEUxeEEweDhFamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDBCamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eDIwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "ImAnPjxzY3JpcHQ+eEMyeEEwamF2YXNjcmlwdDphbGVydCgxKTwvc2NyaXB0Pg==", + "PGltZyB4MDBzcmM9eCBvbmVycm9yPSJhbGVydCgxKSI+", + "PGltZyB4NDdzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MTFzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MTJzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZ3g0N3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gxMHNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gxM3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gzMnNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3g0N3NyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ3gxMXNyYz14IG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyB4NDdzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MzRzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MzlzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyB4MDBzcmM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MDk9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTA9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTM9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MzI9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTI9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MTE9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4MDA9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmN4NDc9eCBvbmVycm9yPSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eHgwOW9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxMG9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxMW9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxMm9uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZyBzcmM9eHgxM29uZXJyb3I9ImphdmFzY3JpcHQ6YWxlcnQoMSkiPg==", + "PGltZ1thXVtiXVtjXXNyY1tkXT14W2Vdb25lcnJvcj1bZl0iYWxlcnQoMSkiPg==", + "PGltZyBzcmM9eCBvbmVycm9yPXgwOSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMCJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMSJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgxMiJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgzMiJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGltZyBzcmM9eCBvbmVycm9yPXgwMCJqYXZhc2NyaXB0OmFsZXJ0KDEpIj4=", + "PGEgaHJlZj1qYXZhJiMxJiMyJiMzJiM0JiM1JiM2JiM3JiM4JiMxMSYjMTJzY3JpcHQ6amF2YXNj", + "cmlwdDphbGVydCgxKT5YWFg8L2E+", + "PGltZyBzcmM9InhgIGA8c2NyaXB0PmphdmFzY3JpcHQ6YWxlcnQoMSk8L3NjcmlwdD4iYCBgPg==", + "PGltZyBzcmMgb25lcnJvciAvIiAnIj0gYWx0PWphdmFzY3JpcHQ6YWxlcnQoMSkvLyI+", + "PHRpdGxlIG9ucHJvcGVydHljaGFuZ2U9amF2YXNjcmlwdDphbGVydCgxKT48L3RpdGxlPjx0aXRs", + "ZSB0aXRsZT0+", + "PGEgaHJlZj1odHRwOi8vZm9vLmJhci8jeD1geT48L2E+PGltZyBhbHQ9ImA+PGltZyBzcmM9eDp4", + "IG9uZXJyb3I9amF2YXNjcmlwdDphbGVydCgxKT48L2E+Ij4=", + "PCEtLVtpZl0+PHNjcmlwdD5qYXZhc2NyaXB0OmFsZXJ0KDEpPC9zY3JpcHQgLS0+", + "PCEtLVtpZjxpbWcgc3JjPXggb25lcnJvcj1qYXZhc2NyaXB0OmFsZXJ0KDEpLy9dPiAtLT4=", + "PHNjcmlwdCBzcmM9Ii8lKGpzY3JpcHQpcyI+PC9zY3JpcHQ+", + "PHNjcmlwdCBzcmM9IlwlKGpzY3JpcHQpcyI+PC9zY3JpcHQ+", + "PElNRyAiIiI+PFNDUklQVD5hbGVydCgiWFNTIik8L1NDUklQVD4iPg==", + "PElNRyBTUkM9amF2YXNjcmlwdDphbGVydChTdHJpbmcuZnJvbUNoYXJDb2RlKDg4LDgzLDgzKSk+", + "PElNRyBTUkM9IyBvbm1vdXNlb3Zlcj0iYWxlcnQoJ3h4cycpIj4=", + "PElNRyBTUkM9IG9ubW91c2VvdmVyPSJhbGVydCgneHhzJykiPg==", + "PElNRyBvbm1vdXNlb3Zlcj0iYWxlcnQoJ3h4cycpIj4=", + "PElNRyBTUkM9JiMxMDY7JiM5NzsmIzExODsmIzk3OyYjMTE1OyYjOTk7JiMxMTQ7JiMxMDU7JiMx", + "MTI7JiMxMTY7JiM1ODsmIzk3OyYjMTA4OyYjMTAxOyYjMTE0OyYjMTE2OyYjNDA7JiMzOTsmIzg4", + "OyYjODM7JiM4MzsmIzM5OyYjNDE7Pg==", + "PElNRyBTUkM9JiMwMDAwMTA2JiMwMDAwMDk3JiMwMDAwMTE4JiMwMDAwMDk3JiMwMDAwMTE1JiMw", + "MDAwMDk5JiMwMDAwMTE0JiMwMDAwMTA1JiMwMDAwMTEyJiMwMDAwMTE2JiMwMDAwMDU4JiMwMDAw", + "MDk3JiMwMDAwMTA4JiMwMDAwMTAxJiMwMDAwMTE0JiMwMDAwMTE2JiMwMDAwMDQwJiMwMDAwMDM5", + "JiMwMDAwMDg4JiMwMDAwMDgzJiMwMDAwMDgzJiMwMDAwMDM5JiMwMDAwMDQxPg==", + "PElNRyBTUkM9JiN4NkEmI3g2MSYjeDc2JiN4NjEmI3g3MyYjeDYzJiN4NzImI3g2OSYjeDcwJiN4", + "NzQmI3gzQSYjeDYxJiN4NkMmI3g2NSYjeDcyJiN4NzQmI3gyOCYjeDI3JiN4NTgmI3g1MyYjeDUz", + "JiN4MjcmI3gyOT4=", + "PElNRyBTUkM9ImphdiBhc2NyaXB0OmFsZXJ0KCdYU1MnKTsiPg==", + "PElNRyBTUkM9ImphdiYjeDA5O2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+", + "PElNRyBTUkM9ImphdiYjeDBBO2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+", + "PElNRyBTUkM9ImphdiYjeDBEO2FzY3JpcHQ6YWxlcnQoJ1hTUycpOyI+", + "cGVybCAtZSAncHJpbnQgIjxJTUcgU1JDPWphdmEwc2NyaXB0OmFsZXJ0KCJYU1MiKT4iOycgPiBv", + "dXQ=", + "PElNRyBTUkM9IiAmIzE0OyBqYXZhc2NyaXB0OmFsZXJ0KCdYU1MnKTsiPg==", + "PFNDUklQVC9YU1MgU1JDPSJodHRwOi8vaGEuY2tlcnMub3JnL3hzcy5qcyI+PC9TQ1JJUFQ+", + "PEJPRFkgb25sb2FkISMkJSYoKSp+Ky1fLiw6Oz9AWy98XV5gPWFsZXJ0KCJYU1MiKT4=", + "PFNDUklQVC9TUkM9Imh0dHA6Ly9oYS5ja2Vycy5vcmcveHNzLmpzIj48L1NDUklQVD4=", + "PDxTQ1JJUFQ+YWxlcnQoIlhTUyIpOy8vPDwvU0NSSVBUPg==", + "PFNDUklQVCBTUkM9aHR0cDovL2hhLmNrZXJzLm9yZy94c3MuanM/PCBCID4=", + "PFNDUklQVCBTUkM9Ly9oYS5ja2Vycy5vcmcvLmo+", + "PElNRyBTUkM9ImphdmFzY3JpcHQ6YWxlcnQoJ1hTUycpIg==", + "PGlmcmFtZSBzcmM9aHR0cDovL2hhLmNrZXJzLm9yZy9zY3JpcHRsZXQuaHRtbCA8", + "IjthbGVydCgnWFNTJyk7Ly8=", + "PHUgb25jb3B5PWFsZXJ0KCk+IENvcHkgbWU8L3U+", + "PGkgb253aGVlbD1hbGVydCgxKT4gU2Nyb2xsIG92ZXIgbWUgPC9pPg==", + "PHBsYWludGV4dD4=", + "aHR0cDovL2EvJSUzMCUzMA==", + "PC90ZXh0YXJlYT48c2NyaXB0PmFsZXJ0KDEyMyk8L3NjcmlwdD4=", + "MTtEUk9QIFRBQkxFIHVzZXJz", + "MSc7IERST1AgVEFCTEUgdXNlcnMtLSAx", + "JyBPUiAxPTEgLS0gMQ==", + "JyBPUiAnMSc9JzE=", + "JQ==", + "Xw==", + "LQ==", + "LS0=", + "LS12ZXJzaW9u", + "LS1oZWxw", + "JFVTRVI=", + "L2Rldi9udWxsOyB0b3VjaCAvdG1wL2JsbnMuZmFpbCA7IGVjaG8=", + "YHRvdWNoIC90bXAvYmxucy5mYWlsYA==", + "JCh0b3VjaCAvdG1wL2JsbnMuZmFpbCk=", + "QHtbc3lzdGVtICJ0b3VjaCAvdG1wL2JsbnMuZmFpbCJdfQ==", + "ZXZhbCgicHV0cyAnaGVsbG8gd29ybGQnIik=", + "U3lzdGVtKCJscyAtYWwgLyIp", + "YGxzIC1hbCAvYA==", + "S2VybmVsLmV4ZWMoImxzIC1hbCAvIik=", + "S2VybmVsLmV4aXQoMSk=", + "JXgoJ2xzIC1hbCAvJyk=", + "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/PjwhRE9DVFlQRSBmb28g", + "WyA8IUVMRU1FTlQgZm9vIEFOWSA+PCFFTlRJVFkgeHhlIFNZU1RFTSAiZmlsZTovLy9ldGMvcGFz", + "c3dkIiA+XT48Zm9vPiZ4eGU7PC9mb28+", + "JEhPTUU=", + "JEVOVnsnSE9NRSd9", + "JWQ=", + "JXM=", + "ezB9", + "JSouKnM=", + "Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZCUwMA==", + "Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL2hvc3Rz", + "KCkgeyAwOyB9OyB0b3VjaCAvdG1wL2JsbnMuc2hlbGxzaG9jazEuZmFpbDs=", + "KCkgeyBfOyB9ID5fWyQoJCgpKV0geyB0b3VjaCAvdG1wL2JsbnMuc2hlbGxzaG9jazIuZmFpbDsg", + "fQ==", + "PDw8ICVzKHVuPSclcycpID0gJXU=", + "KysrQVRIMA==", + "Q09O", + "UFJO", + "QVVY", + "Q0xPQ0sk", + "TlVM", + "QTo=", + "Wlo6", + "Q09NMQ==", + "TFBUMQ==", + "TFBUMg==", + "TFBUMw==", + "Q09NMg==", + "Q09NMw==", + "Q09NNA==", + "RENDIFNFTkQgU1RBUlRLRVlMT0dHRVIgMCAwIDA=", + "U2N1bnRob3JwZSBHZW5lcmFsIEhvc3BpdGFs", + "UGVuaXN0b25lIENvbW11bml0eSBDaHVyY2g=", + "TGlnaHR3YXRlciBDb3VudHJ5IFBhcms=", + "SmltbXkgQ2xpdGhlcm9l", + "SG9ybmltYW4gTXVzZXVt", + "c2hpdGFrZSBtdXNocm9vbXM=", + "Um9tYW5zSW5TdXNzZXguY28udWs=", + "aHR0cDovL3d3dy5jdW0ucWMuY2Ev", + "Q3JhaWcgQ29ja2J1cm4sIFNvZnR3YXJlIFNwZWNpYWxpc3Q=", + "TGluZGEgQ2FsbGFoYW4=", + "RHIuIEhlcm1hbiBJLiBMaWJzaGl0eg==", + "bWFnbmEgY3VtIGxhdWRl", + "U3VwZXIgQm93bCBYWFg=", + "bWVkaWV2YWwgZXJlY3Rpb24gb2YgcGFyYXBldHM=", + "ZXZhbHVhdGU=", + "bW9jaGE=", + "ZXhwcmVzc2lvbg==", + "QXJzZW5hbCBjYW5hbA==", + "Y2xhc3NpYw==", + "VHlzb24gR2F5", + "RGljayBWYW4gRHlrZQ==", + "YmFzZW1lbnQ=", + "SWYgeW91J3JlIHJlYWRpbmcgdGhpcywgeW91J3ZlIGJlZW4gaW4gYSBjb21hIGZvciBhbG1vc3Qg", + "MjAgeWVhcnMgbm93LiBXZSdyZSB0cnlpbmcgYSBuZXcgdGVjaG5pcXVlLiBXZSBkb24ndCBrbm93", + "IHdoZXJlIHRoaXMgbWVzc2FnZSB3aWxsIGVuZCB1cCBpbiB5b3VyIGRyZWFtLCBidXQgd2UgaG9w", + "ZSBpdCB3b3Jrcy4gUGxlYXNlIHdha2UgdXAsIHdlIG1pc3MgeW91Lg==", + "Um9zZXMgYXJlIBtbMDszMW1yZWQbWzBtLCB2aW9sZXRzIGFyZSAbWzA7MzRtYmx1ZS4gSG9wZSB5", + "b3UgZW5qb3kgdGVybWluYWwgaHVl", + "QnV0IG5vdy4uLhtbMjBDZm9yIG15IGdyZWF0ZXN0IHRyaWNrLi4uG1s4bQ==", + "VGhlIHF1aWMICAgICAhrIGJyb3duIGZvBwcHBwcHBwcHBwd4Li4uIFtCZWVlZXBd", + "UG93ZXLZhNmP2YTZj9i12ZHYqNmP2YTZj9mE2LXZkdio2Y/Ysdix2Ysg4KWjIOClo2gg4KWjIOCl", + "o+WGlw==", + "2q/ahtm+2pg=", + "eyUgcHJpbnQgJ3gnICogNjQgKiAxMDI0KiozICV9", + "e3sgIiIuX19jbGFzc19fLl9fbXJvX19bMl0uX19zdWJjbGFzc2VzX18oKVs0MF0oIi9ldGMvcGFz", + "c3dkIikucmVhZCgpIH19" +] + diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index cf66ece0c30..d0892f5ae38 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -1087,4 +1087,114 @@ mod tests { assert_eq!(got, token); } } + + // returns a vector of: + // (expected_token_discriminator, strings_to_lex) + // expected_token_discriminator matches a given token when + // std::mem::discriminant returns the same discriminant for both. + fn blns_base64_to_statements(base64_str: String) -> Vec<(Option, Vec)> { + use base64::engine::general_purpose; + use std::borrow::Cow; + use std::io::Cursor; + use std::io::Read; + + let mut wrapped_reader = Cursor::new(base64_str); + let mut decoder = + base64::read::DecoderReader::new(&mut wrapped_reader, &general_purpose::STANDARD); + let mut base64_decoded = Vec::new(); + decoder.read_to_end(&mut base64_decoded).unwrap(); + + // NOTE: when successful, this is the same conversion method as used in + // noirc_driver::stdlib::stdlib_paths_with_source, viz. + // + // let source = std::str::from_utf8(..).unwrap().to_string(); + let s: Cow<'_, str> = match std::str::from_utf8(&base64_decoded) { + Ok(s) => std::borrow::Cow::Borrowed(s), + Err(_err) => { + // recover as much of the string as possible + // when str::from_utf8 fails + String::from_utf8_lossy(&base64_decoded) + } + }; + + vec![ + // Token::Ident(_) + (None, vec![format!("let \"{s}\" = ();")]), + (Some(Token::Str("".to_string())), vec![format!("let s = \"{s}\";")]), + ( + Some(Token::RawStr("".to_string(), 0)), + vec![ + // let s = r"Hello world"; + format!("let s = r\"{s}\";"), + // let s = r#"Simon says "hello world""#; + format!("let s = r#\"{s}\"#;"), + // // Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes + // let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; + format!("let s = r##\"{s}\"##;"), + format!("let s = r###\"{s}\"###;"), + format!("let s = r####\"{s}\"####; "), + format!("let s = r#####\"{s}\"#####;"), + ], + ), + (Some(Token::FmtStr("".to_string())), vec![format!("assert(x == y, f\"{s}\");")]), + // expected token not found + // (Some(Token::LineComment("".to_string(), None)), vec![ + (None, vec![format!("//{s}"), format!("// {s}")]), + // expected token not found + // (Some(Token::BlockComment("".to_string(), None)), vec![ + (None, vec![format!("/*{s}*/"), format!("/* {s} */"), format!("/*\n{s}\n*/")]), + ] + } + + #[test] + fn test_big_list_of_naughty_strings() { + use std::mem::discriminant; + + let blns_contents = include_str!(env!("BLNS_JSON_PATH")); + let blns_base64: Vec = + serde_json::from_str(blns_contents).expect("BLNS json invalid"); + for blns_base64_str in blns_base64 { + let statements = blns_base64_to_statements(blns_base64_str); + for (token_discriminator_opt, blns_program_strs) in statements { + for blns_program_str in blns_program_strs { + let mut expected_token_found = false; + let mut lexer = Lexer::new(&blns_program_str); + let mut result_tokens = Vec::new(); + loop { + match lexer.next_token() { + Ok(next_token) => { + result_tokens.push(next_token.clone()); + expected_token_found |= token_discriminator_opt + .as_ref() + .map(|token_discriminator| { + discriminant(token_discriminator) + == discriminant(&next_token.token()) + }) + .unwrap_or(true); + + if next_token == Token::EOF { + assert!(lexer.done, "lexer not done when EOF emitted!"); + break; + } + } + + Err(LexerErrorKind::InvalidIntegerLiteral { .. }) + | Err(LexerErrorKind::UnexpectedCharacter { .. }) + | Err(LexerErrorKind::UnterminatedBlockComment { .. }) => { + expected_token_found = true; + } + Err(err) => { + panic!("Unexpected lexer error found: {:?}", err) + } + } + } + + assert!( + expected_token_found, + "expected token not found: {token_discriminator_opt:?}\noutput:\n{result_tokens:?}", + ); + } + } + } + } } From 0150600922ee8b3e67c9b592338e8832f446685b Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 29 Mar 2024 11:28:33 -0500 Subject: [PATCH 128/416] fix: Impl search no longer selects an impl if multiple are applicable (#4662) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4653 ## Summary\* If the object type is an unbound type variable, impl search currently just chooses the first available matching impl instead of erroring that type annotations are needed or similar. This can lead to confusing situations where the type chosen is chosen just because it was the first impl in the stdlib to be defined. This PR prevents that. ## Additional Context This PR depends on https://github.com/noir-lang/noir/pull/4648 and should be merged after. ~~The `hashmap` test is currently failing because the `H: Hasher` constraint on its `Eq` implementation ~~is actually unsolvable since `H` isn't mentioned in the hashmap type at all~~. It is solvable since it is meantioned in `B`, although solving it so far has lead to errors during monomorphization. Previously it was fine since H was unbound and the first/only impl was just always chosen. Now I'll need to change or remove H to fix it.~~ This PR is now ready to review. I've been debugging a bug with monomorphizing some HashMap code for a little while but it turns out the bug is also present in master. So I'm considering it a separate issue: https://github.com/noir-lang/noir/issues/4663 ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_frontend/src/hir/type_check/expr.rs | 51 ++++++++++--------- .../noirc_frontend/src/hir/type_check/mod.rs | 40 ++++++++++++++- .../noirc_frontend/src/hir/type_check/stmt.rs | 12 ++--- .../src/monomorphization/mod.rs | 7 ++- compiler/noirc_frontend/src/node_interner.rs | 47 ++++++++++++----- noir_stdlib/src/hash/poseidon2.nr | 2 +- 6 files changed, 113 insertions(+), 46 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/type_check/expr.rs b/compiler/noirc_frontend/src/hir/type_check/expr.rs index fb66bdeae6e..b56e2dce2a9 100644 --- a/compiler/noirc_frontend/src/hir/type_check/expr.rs +++ b/compiler/noirc_frontend/src/hir/type_check/expr.rs @@ -126,7 +126,7 @@ impl<'interner> TypeChecker<'interner> { } } HirLiteral::Bool(_) => Type::Bool, - HirLiteral::Integer(_, _) => Type::polymorphic_integer_or_field(self.interner), + HirLiteral::Integer(_, _) => self.polymorphic_integer_or_field(), HirLiteral::Str(string) => { let len = Type::Constant(string.len() as u64); Type::String(Box::new(len)) @@ -418,23 +418,28 @@ impl<'interner> TypeChecker<'interner> { self.interner.select_impl_for_expression(function_ident_id, impl_kind); } Err(erroring_constraints) => { - // Don't show any errors where try_get_trait returns None. - // This can happen if a trait is used that was never declared. - let constraints = erroring_constraints - .into_iter() - .map(|constraint| { - let r#trait = self.interner.try_get_trait(constraint.trait_id)?; - let mut name = r#trait.name.to_string(); - if !constraint.trait_generics.is_empty() { - let generics = vecmap(&constraint.trait_generics, ToString::to_string); - name += &format!("<{}>", generics.join(", ")); - } - Some((constraint.typ, name)) - }) - .collect::>>(); + if erroring_constraints.is_empty() { + self.errors.push(TypeCheckError::TypeAnnotationsNeeded { span }); + } else { + // Don't show any errors where try_get_trait returns None. + // This can happen if a trait is used that was never declared. + let constraints = erroring_constraints + .into_iter() + .map(|constraint| { + let r#trait = self.interner.try_get_trait(constraint.trait_id)?; + let mut name = r#trait.name.to_string(); + if !constraint.trait_generics.is_empty() { + let generics = + vecmap(&constraint.trait_generics, ToString::to_string); + name += &format!("<{}>", generics.join(", ")); + } + Some((constraint.typ, name)) + }) + .collect::>>(); - if let Some(constraints) = constraints { - self.errors.push(TypeCheckError::NoMatchingImplFound { constraints, span }); + if let Some(constraints) = constraints { + self.errors.push(TypeCheckError::NoMatchingImplFound { constraints, span }); + } } } } @@ -560,15 +565,13 @@ impl<'interner> TypeChecker<'interner> { let index_type = self.check_expression(&index_expr.index); let span = self.interner.expr_span(&index_expr.index); - index_type.unify( - &Type::polymorphic_integer_or_field(self.interner), - &mut self.errors, - || TypeCheckError::TypeMismatch { + index_type.unify(&self.polymorphic_integer_or_field(), &mut self.errors, || { + TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), expr_span: span, - }, - ); + } + }); // When writing `a[i]`, if `a : &mut ...` then automatically dereference `a` as many // times as needed to get the underlying array. @@ -1218,7 +1221,7 @@ impl<'interner> TypeChecker<'interner> { self.errors .push(TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span }); } - let expected = Type::polymorphic_integer_or_field(self.interner); + let expected = self.polymorphic_integer_or_field(); rhs_type.unify(&expected, &mut self.errors, || TypeCheckError::InvalidUnaryOp { kind: rhs_type.to_string(), span, diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index bb42ebf68fb..5bda4b9345e 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -37,6 +37,11 @@ pub struct TypeChecker<'interner> { /// on each variable, but it is only until function calls when the types /// needed for the trait constraint may become known. trait_constraints: Vec<(TraitConstraint, ExprId)>, + + /// All type variables created in the current function. + /// This map is used to default any integer type variables at the end of + /// a function (before checking trait constraints) if a type wasn't already chosen. + type_variables: Vec, } /// Type checks a function and assigns the @@ -127,6 +132,16 @@ pub fn type_check_func(interner: &mut NodeInterner, func_id: FuncId) -> Vec TypeChecker<'interner> { fn new(interner: &'interner mut NodeInterner) -> Self { - Self { interner, errors: Vec::new(), trait_constraints: Vec::new(), current_function: None } + Self { + interner, + errors: Vec::new(), + trait_constraints: Vec::new(), + type_variables: Vec::new(), + current_function: None, + } } fn check_function_body(&mut self, body: &ExprId) -> Type { @@ -348,6 +369,7 @@ impl<'interner> TypeChecker<'interner> { interner, errors: Vec::new(), trait_constraints: Vec::new(), + type_variables: Vec::new(), current_function: None, }; let statement = this.interner.get_global(id).let_statement; @@ -381,6 +403,22 @@ impl<'interner> TypeChecker<'interner> { make_error, ); } + + /// Return a fresh integer or field type variable and log it + /// in self.type_variables to default it later. + fn polymorphic_integer_or_field(&mut self) -> Type { + let typ = Type::polymorphic_integer_or_field(self.interner); + self.type_variables.push(typ.clone()); + typ + } + + /// Return a fresh integer type variable and log it + /// in self.type_variables to default it later. + fn polymorphic_integer(&mut self) -> Type { + let typ = Type::polymorphic_integer(self.interner); + self.type_variables.push(typ.clone()); + typ + } } // XXX: These tests are all manual currently. diff --git a/compiler/noirc_frontend/src/hir/type_check/stmt.rs b/compiler/noirc_frontend/src/hir/type_check/stmt.rs index b97a4530999..fb57aa75f89 100644 --- a/compiler/noirc_frontend/src/hir/type_check/stmt.rs +++ b/compiler/noirc_frontend/src/hir/type_check/stmt.rs @@ -71,7 +71,7 @@ impl<'interner> TypeChecker<'interner> { expr_span: range_span, }); - let expected_type = Type::polymorphic_integer(self.interner); + let expected_type = self.polymorphic_integer(); self.unify(&start_range_type, &expected_type, || TypeCheckError::TypeCannotBeUsed { typ: start_range_type.clone(), @@ -233,15 +233,13 @@ impl<'interner> TypeChecker<'interner> { let expr_span = self.interner.expr_span(index); let location = *location; - index_type.unify( - &Type::polymorphic_integer_or_field(self.interner), - &mut self.errors, - || TypeCheckError::TypeMismatch { + index_type.unify(&self.polymorphic_integer_or_field(), &mut self.errors, || { + TypeCheckError::TypeMismatch { expected_typ: "an integer".to_owned(), expr_typ: index_type.to_string(), expr_span, - }, - ); + } + }); let (mut lvalue_type, mut lvalue, mut mutable) = self.check_lvalue(array, assign_span); diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 2223773948f..e0d86592d9f 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1003,7 +1003,12 @@ impl<'interner> Monomorphizer<'interner> { Err(constraints) => { let failed_constraints = vecmap(constraints, |constraint| { let id = constraint.trait_id; - let name = self.interner.get_trait(id).name.to_string(); + let mut name = self.interner.get_trait(id).name.to_string(); + if !constraint.trait_generics.is_empty() { + let types = + vecmap(&constraint.trait_generics, |t| format!("{t:?}")); + name += &format!("<{}>", types.join(", ")); + } format!(" {}: {name}", constraint.typ) }) .join("\n"); diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 09e0cb04d26..dcfceccdb57 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -1079,6 +1079,7 @@ impl NodeInterner { /// constraint, but when where clauses are involved, the failing constraint may be several /// constraints deep. In this case, all of the constraints are returned, starting with the /// failing one. + /// If this list of failing constraints is empty, this means type annotations are required. pub fn lookup_trait_implementation( &self, object_type: &Type, @@ -1121,6 +1122,10 @@ impl NodeInterner { } /// Similar to `lookup_trait_implementation` but does not apply any type bindings on success. + /// On error returns either: + /// - 1+ failing trait constraints, including the original. + /// Each constraint after the first represents a `where` clause that was followed. + /// - 0 trait constraints indicating type annotations are needed to choose an impl. pub fn try_lookup_trait_implementation( &self, object_type: &Type, @@ -1138,6 +1143,11 @@ impl NodeInterner { Ok((impl_kind, bindings)) } + /// Returns the trait implementation if found. + /// On error returns either: + /// - 1+ failing trait constraints, including the original. + /// Each constraint after the first represents a `where` clause that was followed. + /// - 0 trait constraints indicating type annotations are needed to choose an impl. fn lookup_trait_implementation_helper( &self, object_type: &Type, @@ -1156,15 +1166,22 @@ impl NodeInterner { let object_type = object_type.substitute(type_bindings); + // If the object type isn't known, just return an error saying type annotations are needed. + if object_type.is_bindable() { + return Err(Vec::new()); + } + let impls = self.trait_implementation_map.get(&trait_id).ok_or_else(|| vec![make_constraint()])?; + let mut matching_impls = Vec::new(); + for (existing_object_type2, impl_kind) in impls { // Bug: We're instantiating only the object type's generics here, not all of the trait's generics like we need to let (existing_object_type, instantiation_bindings) = existing_object_type2.instantiate(self); - let mut fresh_bindings = TypeBindings::new(); + let mut fresh_bindings = type_bindings.clone(); let mut check_trait_generics = |impl_generics: &[Type]| { trait_generics.iter().zip(impl_generics).all(|(trait_generic, impl_generic2)| { @@ -1189,16 +1206,13 @@ impl NodeInterner { } if object_type.try_unify(&existing_object_type, &mut fresh_bindings).is_ok() { - // The unification was successful so we can append fresh_bindings to our bindings list - type_bindings.extend(fresh_bindings); - if let TraitImplKind::Normal(impl_id) = impl_kind { let trait_impl = self.get_trait_implementation(*impl_id); let trait_impl = trait_impl.borrow(); if let Err(mut errors) = self.validate_where_clause( &trait_impl.where_clause, - type_bindings, + &mut fresh_bindings, &instantiation_bindings, recursion_limit, ) { @@ -1207,11 +1221,20 @@ impl NodeInterner { } } - return Ok(impl_kind.clone()); + matching_impls.push((impl_kind.clone(), fresh_bindings)); } } - Err(vec![make_constraint()]) + if matching_impls.len() == 1 { + let (impl_, fresh_bindings) = matching_impls.pop().unwrap(); + *type_bindings = fresh_bindings; + Ok(impl_) + } else if matching_impls.is_empty() { + Err(vec![make_constraint()]) + } else { + // multiple matching impls, type annotations needed + Err(vec![]) + } } /// Verifies that each constraint in the given where clause is valid. @@ -1227,12 +1250,11 @@ impl NodeInterner { // Instantiation bindings are generally safe to force substitute into the same type. // This is needed here to undo any bindings done to trait methods by monomorphization. // Otherwise, an impl for (A, B) could get narrowed to only an impl for e.g. (u8, u16). - let constraint_type = constraint.typ.force_substitute(instantiation_bindings); - let constraint_type = constraint_type.substitute(type_bindings); + let constraint_type = + constraint.typ.force_substitute(instantiation_bindings).substitute(type_bindings); let trait_generics = vecmap(&constraint.trait_generics, |generic| { - let generic = generic.force_substitute(instantiation_bindings); - generic.substitute(type_bindings) + generic.force_substitute(instantiation_bindings).substitute(type_bindings) }); self.lookup_trait_implementation_helper( @@ -1241,10 +1263,11 @@ impl NodeInterner { &trait_generics, // Use a fresh set of type bindings here since the constraint_type originates from // our impl list, which we don't want to bind to. - &mut TypeBindings::new(), + type_bindings, recursion_limit - 1, )?; } + Ok(()) } diff --git a/noir_stdlib/src/hash/poseidon2.nr b/noir_stdlib/src/hash/poseidon2.nr index 5b97d809896..12bf373e671 100644 --- a/noir_stdlib/src/hash/poseidon2.nr +++ b/noir_stdlib/src/hash/poseidon2.nr @@ -1,7 +1,7 @@ use crate::hash::Hasher; use crate::default::Default; -global RATE = 3; +global RATE: u32 = 3; struct Poseidon2 { cache: [Field;3], From a0f7474ae6bd74132efdb945d2eb2383f3913cce Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Fri, 29 Mar 2024 15:19:10 -0400 Subject: [PATCH 129/416] feat: Sync from aztec-packages (#4670) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE feat(acvm): Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) feat(acir_gen): Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) refactor: inject fetcher instead of using global (https://github.com/AztecProtocol/aztec-packages/pull/5502) END_COMMIT_OVERRIDE --------- Co-authored-by: vezenovm Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: Tom French Co-authored-by: jfecher --- .aztec-sync-commit | 2 +- acvm-repo/acir/src/circuit/opcodes.rs | 4 +- .../acir/src/native_types/witness_stack.rs | 14 + .../acvm/src/compiler/transformers/mod.rs | 7 +- acvm-repo/acvm/src/pwg/mod.rs | 96 +++- acvm-repo/acvm_js/src/execute.rs | 3 + compiler/noirc_driver/src/lib.rs | 17 +- .../brillig/brillig_gen/brillig_slice_ops.rs | 5 +- .../brillig/brillig_gen/variable_liveness.rs | 6 +- compiler/noirc_evaluator/src/lib.rs | 2 +- compiler/noirc_evaluator/src/ssa.rs | 74 ++- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 24 + .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 520 +++++++++++++++--- .../src/ssa/function_builder/mod.rs | 33 +- compiler/noirc_evaluator/src/ssa/ir/dom.rs | 6 +- .../noirc_evaluator/src/ssa/ir/function.rs | 43 +- .../noirc_evaluator/src/ssa/ir/post_order.rs | 4 +- .../src/ssa/opt/bubble_up_constrains.rs | 4 +- .../src/ssa/opt/constant_folding.rs | 14 +- .../src/ssa/opt/defunctionalize.rs | 4 +- compiler/noirc_evaluator/src/ssa/opt/die.rs | 4 +- .../src/ssa/opt/flatten_cfg.rs | 24 +- .../ssa/opt/flatten_cfg/branch_analysis.rs | 11 +- .../noirc_evaluator/src/ssa/opt/inlining.rs | 44 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 12 +- compiler/noirc_evaluator/src/ssa/opt/rc.rs | 15 +- .../src/ssa/opt/remove_bit_shifts.rs | 4 +- .../src/ssa/opt/simplify_cfg.rs | 6 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 13 +- .../src/ssa/ssa_gen/context.rs | 8 +- .../noirc_evaluator/src/ssa/ssa_gen/mod.rs | 10 +- .../src/ssa/ssa_gen/program.rs | 5 +- compiler/noirc_frontend/src/ast/function.rs | 1 + .../src/hir/resolution/resolver.rs | 5 +- .../src/hir/type_check/errors.rs | 2 +- .../noirc_frontend/src/hir/type_check/mod.rs | 41 +- .../noirc_frontend/src/hir_def/function.rs | 4 + compiler/noirc_frontend/src/lexer/lexer.rs | 10 + compiler/noirc_frontend/src/lexer/token.rs | 12 + .../src/monomorphization/ast.rs | 5 + .../src/monomorphization/mod.rs | 61 +- .../github-dependency-resolver.ts | 6 +- compiler/wasm/src/noir/noir-wasm-compiler.ts | 3 +- .../github-dependency-resolver.test.ts | 8 +- cspell.json | 1 + .../execution_success/fold_basic/Nargo.toml | 7 + .../execution_success/fold_basic/Prover.toml | 2 + .../execution_success/fold_basic/src/main.nr | 11 + .../fold_basic_nested_call/Nargo.toml | 7 + .../fold_basic_nested_call/Prover.toml | 2 + .../fold_basic_nested_call/src/main.nr | 16 + tooling/acvm_cli/src/cli/execute_cmd.rs | 21 +- tooling/backend_interface/src/proof_system.rs | 4 +- tooling/debugger/ignored-tests.txt | 2 + tooling/debugger/src/context.rs | 3 + tooling/nargo/src/errors.rs | 4 +- tooling/nargo/src/ops/execute.rs | 196 +++++-- tooling/nargo/src/ops/mod.rs | 2 +- tooling/nargo/src/ops/test.rs | 14 +- tooling/nargo_cli/src/cli/debug_cmd.rs | 8 +- tooling/nargo_cli/src/cli/execute_cmd.rs | 28 +- tooling/nargo_cli/src/cli/fs/witness.rs | 6 +- tooling/nargo_cli/src/cli/prove_cmd.rs | 11 +- 63 files changed, 1228 insertions(+), 313 deletions(-) create mode 100644 test_programs/execution_success/fold_basic/Nargo.toml create mode 100644 test_programs/execution_success/fold_basic/Prover.toml create mode 100644 test_programs/execution_success/fold_basic/src/main.nr create mode 100644 test_programs/execution_success/fold_basic_nested_call/Nargo.toml create mode 100644 test_programs/execution_success/fold_basic_nested_call/Prover.toml create mode 100644 test_programs/execution_success/fold_basic_nested_call/src/main.nr diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ad4ef635e17..540b447693c 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -a18288d9b8f3057b9e79362d922da656dacf22a9 +bb719200034e3bc6db09fb56538dadca4203abf4 diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index 064a9d1244a..68d28b287e6 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -99,8 +99,8 @@ impl std::fmt::Display for Opcode { } Opcode::Call { id, inputs, outputs } => { write!(f, "CALL func {}: ", id)?; - writeln!(f, "inputs: {:?}", inputs)?; - writeln!(f, "outputs: {:?}", outputs) + write!(f, "inputs: {:?}, ", inputs)?; + write!(f, "outputs: {:?}", outputs) } } } diff --git a/acvm-repo/acir/src/native_types/witness_stack.rs b/acvm-repo/acir/src/native_types/witness_stack.rs index 9592d90b014..a9e8f219b3e 100644 --- a/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/acvm-repo/acir/src/native_types/witness_stack.rs @@ -32,6 +32,20 @@ pub struct StackItem { pub witness: WitnessMap, } +impl WitnessStack { + pub fn push(&mut self, index: u32, witness: WitnessMap) { + self.stack.push(StackItem { index, witness }); + } + + pub fn peek(&self) -> Option<&StackItem> { + self.stack.last() + } + + pub fn length(&self) -> usize { + self.stack.len() + } +} + impl From for WitnessStack { fn from(witness: WitnessMap) -> Self { let stack = vec![StackItem { index: 0, witness }]; diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index 18f49c154f1..1ba261b09a3 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -142,7 +142,12 @@ pub(super) fn transform_internal( new_acir_opcode_positions.push(acir_opcode_positions[index]); transformed_opcodes.push(opcode); } - Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"), + Opcode::Call { .. } => { + // `Call` does not write values to the `WitnessMap` + // A separate ACIR function should have its own respective `WitnessMap` + new_acir_opcode_positions.push(acir_opcode_positions[index]); + transformed_opcodes.push(opcode); + } } } diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 0fd733a6336..3cedcfc0399 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -49,6 +49,12 @@ pub enum ACVMStatus { /// /// Once this is done, the ACVM can be restarted to solve the remaining opcodes. RequiresForeignCall(ForeignCallWaitInfo), + + /// The ACVM has encountered a request for an ACIR [call][acir::circuit::Opcode] + /// to execute a separate ACVM instance. The result of the ACIR call must be passd back to the ACVM. + /// + /// Once this is done, the ACVM can be restarted to solve the remaining opcodes. + RequiresAcirCall(AcirCallWaitInfo), } impl std::fmt::Display for ACVMStatus { @@ -58,6 +64,7 @@ impl std::fmt::Display for ACVMStatus { ACVMStatus::InProgress => write!(f, "In progress"), ACVMStatus::Failure(_) => write!(f, "Execution failure"), ACVMStatus::RequiresForeignCall(_) => write!(f, "Waiting on foreign call"), + ACVMStatus::RequiresAcirCall(_) => write!(f, "Waiting on acir call"), } } } @@ -117,6 +124,10 @@ pub enum OpcodeResolutionError { BlackBoxFunctionFailed(BlackBoxFunc, String), #[error("Failed to solve brillig function, reason: {message}")] BrilligFunctionFailed { message: String, call_stack: Vec }, + #[error("Attempted to call `main` with a `Call` opcode")] + AcirMainCallAttempted { opcode_location: ErrorLocation }, + #[error("{results_size:?} result values were provided for {outputs_size:?} call output witnesses, most likely due to bad ACIR codegen")] + AcirCallOutputsMismatch { opcode_location: ErrorLocation, results_size: u32, outputs_size: u32 }, } impl From for OpcodeResolutionError { @@ -147,6 +158,13 @@ pub struct ACVM<'a, B: BlackBoxFunctionSolver> { witness_map: WitnessMap, brillig_solver: Option>, + + /// A counter maintained throughout an ACVM process that determines + /// whether the caller has resolved the results of an ACIR [call][Opcode::Call]. + acir_call_counter: usize, + /// Represents the outputs of all ACIR calls during an ACVM process + /// List is appended onto by the caller upon reaching a [ACVMStatus::RequiresAcirCall] + acir_call_results: Vec>, } impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { @@ -161,6 +179,8 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { instruction_pointer: 0, witness_map: initial_witness, brillig_solver: None, + acir_call_counter: 0, + acir_call_results: Vec::default(), } } @@ -244,6 +264,29 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { self.status(ACVMStatus::InProgress); } + /// Sets the status of the VM to `RequiresAcirCall` + /// Indicating that the VM is now waiting for an ACIR call to be resolved + fn wait_for_acir_call(&mut self, acir_call: AcirCallWaitInfo) -> ACVMStatus { + self.status(ACVMStatus::RequiresAcirCall(acir_call)) + } + + /// Resolves an ACIR call's result (simply a list of fields) using a result calculated by a separate ACVM instance. + /// + /// The current ACVM instance can then be restarted to solve the remaining ACIR opcodes. + pub fn resolve_pending_acir_call(&mut self, call_result: Vec) { + if !matches!(self.status, ACVMStatus::RequiresAcirCall(_)) { + panic!("ACVM is not expecting an ACIR call response as no call was made"); + } + + if self.acir_call_counter < self.acir_call_results.len() { + panic!("No unresolved ACIR calls"); + } + self.acir_call_results.push(call_result); + + // Now that the ACIR call has been resolved then we can resume execution. + self.status(ACVMStatus::InProgress); + } + /// Executes the ACVM's circuit until execution halts. /// /// Execution can halt due to three reasons: @@ -281,7 +324,10 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { Ok(Some(foreign_call)) => return self.wait_for_foreign_call(foreign_call), res => res.map(|_| ()), }, - Opcode::Call { .. } => todo!("Handle Call opcodes in the ACVM"), + Opcode::Call { .. } => match self.solve_call_opcode() { + Ok(Some(input_values)) => return self.wait_for_acir_call(input_values), + res => res.map(|_| ()), + }, }; self.handle_opcode_resolution(resolution) } @@ -400,6 +446,46 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { self.brillig_solver = Some(solver); self.solve_opcode() } + + pub fn solve_call_opcode(&mut self) -> Result, OpcodeResolutionError> { + let Opcode::Call { id, inputs, outputs } = &self.opcodes[self.instruction_pointer] else { + unreachable!("Not executing a Call opcode"); + }; + if *id == 0 { + return Err(OpcodeResolutionError::AcirMainCallAttempted { + opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir( + self.instruction_pointer(), + )), + }); + } + + if self.acir_call_counter >= self.acir_call_results.len() { + let mut initial_witness = WitnessMap::default(); + for (i, input_witness) in inputs.iter().enumerate() { + let input_value = *witness_to_value(&self.witness_map, *input_witness)?; + initial_witness.insert(Witness(i as u32), input_value); + } + return Ok(Some(AcirCallWaitInfo { id: *id, initial_witness })); + } + + let result_values = &self.acir_call_results[self.acir_call_counter]; + if outputs.len() != result_values.len() { + return Err(OpcodeResolutionError::AcirCallOutputsMismatch { + opcode_location: ErrorLocation::Resolved(OpcodeLocation::Acir( + self.instruction_pointer(), + )), + results_size: result_values.len() as u32, + outputs_size: outputs.len() as u32, + }); + } + + for (output_witness, result_value) in outputs.iter().zip(result_values) { + insert_value(output_witness, *result_value, &mut self.witness_map)?; + } + + self.acir_call_counter += 1; + Ok(None) + } } // Returns the concrete value for a particular witness @@ -469,3 +555,11 @@ fn any_witness_from_expression(expr: &Expression) -> Option { Some(expr.linear_combinations[0].1) } } + +#[derive(Debug, Clone, PartialEq)] +pub struct AcirCallWaitInfo { + /// Index in the list of ACIR function's that should be called + pub id: u32, + /// Initial witness for the given circuit to be called + pub initial_witness: WitnessMap, +} diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index ac71a573e64..60d27a489e2 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -113,6 +113,9 @@ pub async fn execute_circuit_with_black_box_solver( acvm.resolve_pending_foreign_call(result); } + ACVMStatus::RequiresAcirCall(_) => { + todo!("Handle acir calls in acvm JS"); + } } } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 151633f9e33..86d4cd1510d 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -3,13 +3,13 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] -use acvm::acir::circuit::{ExpressionWidth, Program}; +use acvm::acir::circuit::ExpressionWidth; use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; use noirc_abi::{AbiParameter, AbiType, ContractEvent}; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; -use noirc_evaluator::create_circuit; +use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; use noirc_frontend::debug::build_debug_crate_file; use noirc_frontend::graph::{CrateId, CrateName}; @@ -484,7 +484,8 @@ pub fn compile_no_check( return Ok(cached_program.expect("cache must exist for hashes to match")); } let visibility = program.return_visibility; - let (circuit, debug, input_witnesses, return_witnesses, warnings) = create_circuit( + + let (program, debug, warnings, input_witnesses, return_witnesses) = create_program( program, options.show_ssa, options.show_brillig, @@ -494,13 +495,17 @@ pub fn compile_no_check( let abi = abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses, visibility); - let file_map = filter_relevant_files(&[debug.clone()], &context.file_manager); + let file_map = filter_relevant_files(&debug, &context.file_manager); Ok(CompiledProgram { hash, // TODO(https://github.com/noir-lang/noir/issues/4428) - program: Program { functions: vec![circuit] }, - debug, + program, + // TODO(https://github.com/noir-lang/noir/issues/4428) + // Debug info is only relevant for errors at execution time which is not yet supported + // The CompileProgram `debug` field is used in multiple places and is better + // left to be updated once execution of multiple ACIR functions is enabled + debug: debug[0].clone(), abi, file_map, noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index e42b2787f73..b93693d9c79 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -373,8 +373,9 @@ mod tests { use crate::ssa::ssa_gen::Ssa; fn create_test_environment() -> (Ssa, FunctionContext, BrilligContext) { - let builder = - FunctionBuilder::new("main".to_string(), Id::test_new(0), RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("main".to_string(), Id::test_new(0)); + builder.set_runtime(RuntimeType::Brillig); + let ssa = builder.finish(); let mut brillig_context = create_context(); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs index 05978c2c6ab..c9f1cd1e247 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -323,7 +323,8 @@ mod test { // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("main".into(), main_id); + builder.set_runtime(RuntimeType::Brillig); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -425,7 +426,8 @@ mod test { // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("main".into(), main_id); + builder.set_runtime(RuntimeType::Brillig); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/lib.rs b/compiler/noirc_evaluator/src/lib.rs index 70751d3e541..54376963338 100644 --- a/compiler/noirc_evaluator/src/lib.rs +++ b/compiler/noirc_evaluator/src/lib.rs @@ -11,4 +11,4 @@ pub mod ssa; pub mod brillig; -pub use ssa::create_circuit; +pub use ssa::create_program; diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 01dc50a7119..59f2bdc6f84 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -14,11 +14,11 @@ use crate::{ errors::{RuntimeError, SsaReport}, }; use acvm::acir::{ - circuit::{Circuit, ExpressionWidth, PublicInputs}, + circuit::{Circuit, ExpressionWidth, Program as AcirProgram, PublicInputs}, native_types::Witness, }; -use noirc_errors::debug_info::DebugInfo; +use noirc_errors::debug_info::{DebugFunctions, DebugInfo, DebugTypes, DebugVariables}; use noirc_frontend::{ hir_def::function::FunctionSignature, monomorphization::ast::Program, Visibility, @@ -42,7 +42,7 @@ pub(crate) fn optimize_into_acir( print_brillig_trace: bool, force_brillig_output: bool, print_timings: bool, -) -> Result { +) -> Result, RuntimeError> { let abi_distinctness = program.return_distinctness; let ssa_gen_span = span!(Level::TRACE, "ssa_generation"); @@ -72,7 +72,7 @@ pub(crate) fn optimize_into_acir( let last_array_uses = ssa.find_last_array_uses(); time("SSA to ACIR", print_timings, || { - ssa.into_acir(brillig, abi_distinctness, &last_array_uses) + ssa.into_acir(&brillig, abi_distinctness, &last_array_uses) }) } @@ -89,30 +89,80 @@ fn time(name: &str, print_timings: bool, f: impl FnOnce() -> T) -> T { result } -/// Compiles the [`Program`] into [`ACIR`][acvm::acir::circuit::Circuit]. +/// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program]. /// /// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation. #[allow(clippy::type_complexity)] #[tracing::instrument(level = "trace", skip_all)] -pub fn create_circuit( +pub fn create_program( program: Program, enable_ssa_logging: bool, enable_brillig_logging: bool, force_brillig_output: bool, print_codegen_timings: bool, -) -> Result<(Circuit, DebugInfo, Vec, Vec, Vec), RuntimeError> { +) -> Result<(AcirProgram, Vec, Vec, Vec, Vec), RuntimeError> +{ let debug_variables = program.debug_variables.clone(); let debug_types = program.debug_types.clone(); let debug_functions = program.debug_functions.clone(); - let func_sig = program.main_function_signature.clone(); + + let func_sigs = program.function_signatures.clone(); + let recursive = program.recursive; - let mut generated_acir = optimize_into_acir( + let generated_acirs = optimize_into_acir( program, enable_ssa_logging, enable_brillig_logging, force_brillig_output, print_codegen_timings, )?; + assert_eq!( + generated_acirs.len(), + func_sigs.len(), + "The generated ACIRs should match the supplied function signatures" + ); + + let mut functions = vec![]; + let mut debug_infos = vec![]; + let mut warning_infos = vec![]; + let mut main_input_witnesses = Vec::new(); + let mut main_return_witnesses = Vec::new(); + // For setting up the ABI we need separately specify main's input and return witnesses + let mut is_main = true; + for (acir, func_sig) in generated_acirs.into_iter().zip(func_sigs) { + let (circuit, debug_info, warnings, input_witnesses, return_witnesses) = + convert_generated_acir_into_circuit( + acir, + func_sig, + recursive, + // TODO: get rid of these clones + debug_variables.clone(), + debug_functions.clone(), + debug_types.clone(), + ); + functions.push(circuit); + debug_infos.push(debug_info); + warning_infos.extend(warnings); + if is_main { + main_input_witnesses = input_witnesses; + main_return_witnesses = return_witnesses; + } + is_main = false; + } + + let program = AcirProgram { functions }; + + Ok((program, debug_infos, warning_infos, main_input_witnesses, main_return_witnesses)) +} + +fn convert_generated_acir_into_circuit( + mut generated_acir: GeneratedAcir, + func_sig: FunctionSignature, + recursive: bool, + debug_variables: DebugVariables, + debug_functions: DebugFunctions, + debug_types: DebugTypes, +) -> (Circuit, DebugInfo, Vec, Vec, Vec) { let opcodes = generated_acir.take_opcodes(); let current_witness_index = generated_acir.current_witness_index().0; let GeneratedAcir { @@ -124,6 +174,8 @@ pub fn create_circuit( .. } = generated_acir; + let locations = locations.clone(); + let (public_parameter_witnesses, private_parameters) = split_public_and_private_inputs(&func_sig, &input_witnesses); @@ -137,7 +189,7 @@ pub fn create_circuit( private_parameters, public_parameters, return_values, - assert_messages: assert_messages.into_iter().collect(), + assert_messages: assert_messages.clone().into_iter().collect(), recursive, }; @@ -153,7 +205,7 @@ pub fn create_circuit( let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit); debug_info.update_acir(transformation_map); - Ok((optimized_circuit, debug_info, input_witnesses, return_witnesses, warnings)) + (optimized_circuit, debug_info, warnings, input_witnesses, return_witnesses) } // Takes each function argument and partitions the circuit's inputs witnesses according to its visibility. diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 7e951cf4e00..bcd62e3b062 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1757,6 +1757,30 @@ impl AcirContext { } Ok(()) } + + pub(crate) fn call_acir_function( + &mut self, + id: u32, + inputs: Vec, + output_count: usize, + ) -> Result, RuntimeError> { + let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?; + let inputs = inputs + .iter() + .flat_map(|input| vecmap(input, |input| input.witness)) + .collect::>(); + let outputs = vecmap(0..output_count, |_| self.acir_ir.next_witness_index()); + + // Convert `Witness` values which are now constrained to be the output of the + // ACIR function call into `AcirVar`s. + // Similar to black box functions, we do not apply range information on the output of the function. + // See issue https://github.com/noir-lang/noir/issues/1439 + let results = + vecmap(&outputs, |witness_index| self.add_data(AcirVarData::Witness(*witness_index))); + + self.acir_ir.push_opcode(Opcode::Call { id, inputs, outputs }); + Ok(results) + } } /// Enum representing the possible values that a diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 8390f480e3a..96f959612af 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -25,6 +25,7 @@ use crate::brillig::brillig_ir::artifact::GeneratedBrillig; use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; +use crate::ssa::ir::function::InlineType; pub(crate) use acir_ir::generated_acir::GeneratedAcir; use acvm::acir::native_types::Witness; @@ -178,31 +179,45 @@ impl Ssa { #[tracing::instrument(level = "trace", skip_all)] pub(crate) fn into_acir( self, - brillig: Brillig, + brillig: &Brillig, abi_distinctness: Distinctness, last_array_uses: &HashMap, - ) -> Result { - let context = Context::new(); - let mut generated_acir = context.convert_ssa(self, brillig, last_array_uses)?; + ) -> Result, RuntimeError> { + let mut acirs = Vec::new(); + // TODO: can we parallelise this? + for function in self.functions.values() { + let context = Context::new(); + if let Some(generated_acir) = + context.convert_ssa_function(&self, function, brillig, last_array_uses)? + { + acirs.push(generated_acir); + } + } + // TODO: check whether doing this for a single circuit's return witnesses is correct. + // We probably need it for all foldable circuits, as any circuit being folded is essentially an entry point. However, I do not know how that + // plays a part when we potentially want not inlined functions normally as part of the compiler. + // Also at the moment we specify Distinctness as part of the ABI exclusively rather than the function itself + // so this will need to be updated. + let main_func_acir = &mut acirs[0]; match abi_distinctness { Distinctness::Distinct => { // Create a witness for each return witness we have // to guarantee that the return witnesses are distinct - let distinct_return_witness: Vec<_> = generated_acir + let distinct_return_witness: Vec<_> = main_func_acir .return_witnesses .clone() .into_iter() .map(|return_witness| { - generated_acir + main_func_acir .create_witness_for_expression(&Expression::from(return_witness)) }) .collect(); - generated_acir.return_witnesses = distinct_return_witness; - Ok(generated_acir) + main_func_acir.return_witnesses = distinct_return_witness; + Ok(acirs) } - Distinctness::DuplicationAllowed => Ok(generated_acir), + Distinctness::DuplicationAllowed => Ok(acirs), } } } @@ -225,17 +240,33 @@ impl Context { } } - /// Converts SSA into ACIR - fn convert_ssa( + fn convert_ssa_function( self, - ssa: Ssa, - brillig: Brillig, + ssa: &Ssa, + function: &Function, + brillig: &Brillig, last_array_uses: &HashMap, - ) -> Result { - let main_func = ssa.main(); - match main_func.runtime() { - RuntimeType::Acir => self.convert_acir_main(main_func, &ssa, brillig, last_array_uses), - RuntimeType::Brillig => self.convert_brillig_main(main_func, brillig), + ) -> Result, RuntimeError> { + match function.runtime() { + RuntimeType::Acir(inline_type) => { + match inline_type { + InlineType::Fold => {} + InlineType::Inline => { + if function.id() != ssa.main_id { + panic!("ACIR function should have been inlined earlier if not marked otherwise"); + } + } + } + // We only want to convert entry point functions. This being `main` and those marked with `#[fold]` + Ok(Some(self.convert_acir_main(function, ssa, brillig, last_array_uses)?)) + } + RuntimeType::Brillig => { + if function.id() == ssa.main_id { + Ok(Some(self.convert_brillig_main(function, brillig)?)) + } else { + Ok(None) + } + } } } @@ -243,7 +274,7 @@ impl Context { mut self, main_func: &Function, ssa: &Ssa, - brillig: Brillig, + brillig: &Brillig, last_array_uses: &HashMap, ) -> Result { let dfg = &main_func.dfg; @@ -257,7 +288,7 @@ impl Context { *instruction_id, dfg, ssa, - &brillig, + brillig, last_array_uses, )?); } @@ -269,7 +300,7 @@ impl Context { fn convert_brillig_main( mut self, main_func: &Function, - brillig: Brillig, + brillig: &Brillig, ) -> Result { let dfg = &main_func.dfg; @@ -282,7 +313,7 @@ impl Context { let outputs: Vec = vecmap(main_func.returns(), |result_id| dfg.type_of_value(*result_id).into()); - let code = self.gen_brillig_for(main_func, &brillig)?; + let code = self.gen_brillig_for(main_func, brillig)?; // We specifically do not attempt execution of the brillig code being generated as this can result in it being // replaced with constraints on witnesses to the program outputs. @@ -537,15 +568,40 @@ impl Context { Value::Function(id) => { let func = &ssa.functions[id]; match func.runtime() { - RuntimeType::Acir => unimplemented!( - "expected an intrinsic/brillig call, but found {func:?}. All ACIR methods should be inlined" - ), + RuntimeType::Acir(inline_type) => { + assert!(!matches!(inline_type, InlineType::Inline), "ICE: Got an ACIR function named {} that should have already been inlined", func.name()); + + let inputs = vecmap(arguments, |arg| self.convert_value(*arg, dfg)); + // TODO(https://github.com/noir-lang/noir/issues/4608): handle complex return types from ACIR functions + let output_count = + result_ids.iter().fold(0usize, |sum, result_id| { + sum + dfg.try_get_array_length(*result_id).unwrap_or(1) + }); + + let acir_program_id = ssa + .id_to_index + .get(id) + .expect("ICE: should have an associated final index"); + let output_vars = self.acir_context.call_acir_function( + *acir_program_id, + inputs, + output_count, + )?; + let output_values = + self.convert_vars_to_values(output_vars, dfg, result_ids); + + self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; + } RuntimeType::Brillig => { - // Check that we are not attempting to return a slice from + // Check that we are not attempting to return a slice from // an unconstrained runtime to a constrained runtime for result_id in result_ids { if dfg.type_of_value(*result_id).contains_slice_element() { - return Err(RuntimeError::UnconstrainedSliceReturnToConstrained { call_stack: self.acir_context.get_call_stack() }) + return Err( + RuntimeError::UnconstrainedSliceReturnToConstrained { + call_stack: self.acir_context.get_call_stack(), + }, + ); } } @@ -553,22 +609,23 @@ impl Context { let code = self.gen_brillig_for(func, brillig)?; - let outputs: Vec = vecmap(result_ids, |result_id| dfg.type_of_value(*result_id).into()); + let outputs: Vec = vecmap(result_ids, |result_id| { + dfg.type_of_value(*result_id).into() + }); - let output_values = self.acir_context.brillig(self.current_side_effects_enabled_var, code, inputs, outputs, true, false)?; + let output_values = self.acir_context.brillig( + self.current_side_effects_enabled_var, + code, + inputs, + outputs, + true, + false, + )?; // Compiler sanity check assert_eq!(result_ids.len(), output_values.len(), "ICE: The number of Brillig output values should match the result ids in SSA"); - for result in result_ids.iter().zip(output_values) { - if let AcirValue::Array(_) = &result.1 { - let array_id = dfg.resolve(*result.0); - let block_id = self.block_id(&array_id); - let array_typ = dfg.type_of_value(array_id); - self.initialize_array(block_id, array_typ.flattened_size(), Some(result.1.clone()))?; - } - self.ssa_values.insert(*result.0, result.1); - } + self.handle_ssa_call_outputs(result_ids, output_values, dfg)?; } } } @@ -587,30 +644,7 @@ impl Context { // Issue #1438 causes this check to fail with intrinsics that return 0 // results but the ssa form instead creates 1 unit result value. // assert_eq!(result_ids.len(), outputs.len()); - - for (result, output) in result_ids.iter().zip(outputs) { - match &output { - // We need to make sure we initialize arrays returned from intrinsic calls - // or else they will fail if accessed with a dynamic index - AcirValue::Array(_) => { - let block_id = self.block_id(result); - let array_typ = dfg.type_of_value(*result); - let len = if matches!(array_typ, Type::Array(_, _)) { - array_typ.flattened_size() - } else { - Self::flattened_value_size(&output) - }; - self.initialize_array(block_id, len, Some(output.clone()))?; - } - AcirValue::DynamicArray(_) => { - // Do nothing as a dynamic array returned from a slice intrinsic should already be initialized - } - AcirValue::Var(_, _) => { - // Do nothing - } - } - self.ssa_values.insert(*result, output); - } + self.handle_ssa_call_outputs(result_ids, outputs, dfg)?; } Value::ForeignFunction(_) => { return Err(RuntimeError::UnconstrainedOracleReturnToConstrained { @@ -625,6 +659,32 @@ impl Context { Ok(warnings) } + fn handle_ssa_call_outputs( + &mut self, + result_ids: &[ValueId], + output_values: Vec, + dfg: &DataFlowGraph, + ) -> Result<(), RuntimeError> { + for (result_id, output) in result_ids.iter().zip(output_values) { + if let AcirValue::Array(_) = &output { + let array_id = dfg.resolve(*result_id); + let block_id = self.block_id(&array_id); + let array_typ = dfg.type_of_value(array_id); + let len = if matches!(array_typ, Type::Array(_, _)) { + array_typ.flattened_size() + } else { + Self::flattened_value_size(&output) + }; + self.initialize_array(block_id, len, Some(output.clone()))?; + } + // Do nothing for AcirValue::DynamicArray and AcirValue::Var + // A dynamic array returned from a function call should already be initialized + // and a single variable does not require any extra initialization. + self.ssa_values.insert(*result_id, output); + } + Ok(()) + } + fn gen_brillig_for( &self, func: &Function, @@ -1373,6 +1433,7 @@ impl Context { TerminatorInstruction::Return { return_values, call_stack } => { (return_values, call_stack) } + // TODO(https://github.com/noir-lang/noir/issues/4616): Enable recursion on foldable/non-inlined ACIR functions _ => unreachable!("ICE: Program must have a singular return"), }; @@ -2327,3 +2388,338 @@ fn can_omit_element_sizes_array(array_typ: &Type) -> bool { !types.iter().any(|typ| typ.contains_an_array()) } + +#[cfg(test)] +mod test { + use acvm::{ + acir::{circuit::Opcode, native_types::Witness}, + FieldElement, + }; + + use crate::{ + brillig::Brillig, + ssa::{ + function_builder::FunctionBuilder, + ir::{ + function::{FunctionId, InlineType, RuntimeType}, + instruction::BinaryOp, + map::Id, + types::Type, + }, + }, + }; + use fxhash::FxHashMap as HashMap; + + fn build_basic_foo_with_return(builder: &mut FunctionBuilder, foo_id: FunctionId) { + // acir(fold) fn foo f1 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == u1 0 + // return v0 + // } + builder.new_function("foo".into(), foo_id, InlineType::Fold); + let foo_v0 = builder.add_parameter(Type::field()); + let foo_v1 = builder.add_parameter(Type::field()); + + let foo_equality_check = builder.insert_binary(foo_v0, BinaryOp::Eq, foo_v1); + let zero = builder.field_constant(0u128); + builder.insert_constrain(foo_equality_check, zero, None); + builder.terminate_with_return(vec![foo_v0]); + } + + #[test] + fn basic_call_with_outputs_assert() { + // acir(inline) fn main f0 { + // b0(v0: Field, v1: Field): + // v2 = call f1(v0, v1) + // v3 = call f1(v0, v1) + // constrain v2 == v3 + // return + // } + // acir(fold) fn foo f1 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == u1 0 + // return v0 + // } + let foo_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), foo_id); + let main_v0 = builder.add_parameter(Type::field()); + let main_v1 = builder.add_parameter(Type::field()); + + let foo_id = Id::test_new(1); + let foo = builder.import_function(foo_id); + let main_call1_results = + builder.insert_call(foo, vec![main_v0, main_v1], vec![Type::field()]).to_vec(); + let main_call2_results = + builder.insert_call(foo, vec![main_v0, main_v1], vec![Type::field()]).to_vec(); + builder.insert_constrain(main_call1_results[0], main_call2_results[0], None); + builder.terminate_with_return(vec![]); + + build_basic_foo_with_return(&mut builder, foo_id); + + let ssa = builder.finish(); + + let acir_functions = ssa + .into_acir( + &Brillig::default(), + noirc_frontend::Distinctness::Distinct, + &HashMap::default(), + ) + .expect("Should compile manually written SSA into ACIR"); + // Expected result: + // main f0 + // GeneratedAcir { + // ... + // opcodes: [ + // CALL func 1: inputs: [Witness(0), Witness(1)], outputs: [Witness(2)], + // CALL func 1: inputs: [Witness(0), Witness(1)], outputs: [Witness(3)], + // EXPR [ (1, _2) (-1, _3) 0 ], + // ], + // return_witnesses: [], + // input_witnesses: [ + // Witness( + // 0, + // ), + // Witness( + // 1, + // ), + // ], + // ... + // } + // foo f1 + // GeneratedAcir { + // ... + // opcodes: [ + // Same as opcodes as the expected result of `basic_call_codegen` + // ], + // return_witnesses: [ + // Witness( + // 0, + // ), + // ], + // input_witnesses: [ + // Witness( + // 0, + // ), + // Witness( + // 1, + // ), + // ], + // ... + // }, + + let main_acir = &acir_functions[0]; + let main_opcodes = main_acir.opcodes(); + assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo`"); + + check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + + match &main_opcodes[2] { + Opcode::AssertZero(expr) => { + assert_eq!(expr.linear_combinations[0].0, FieldElement::from(1u128)); + assert_eq!(expr.linear_combinations[0].1, Witness(2)); + + assert_eq!(expr.linear_combinations[1].0, FieldElement::from(-1i128)); + assert_eq!(expr.linear_combinations[1].1, Witness(3)); + assert_eq!(expr.q_c, FieldElement::from(0u128)); + } + _ => {} + } + } + + #[test] + fn call_output_as_next_call_input() { + // acir(inline) fn main f0 { + // b0(v0: Field, v1: Field): + // v3 = call f1(v0, v1) + // v4 = call f1(v3, v1) + // constrain v3 == v4 + // return + // } + // acir(fold) fn foo f1 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == u1 0 + // return v0 + // } + let foo_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), foo_id); + let main_v0 = builder.add_parameter(Type::field()); + let main_v1 = builder.add_parameter(Type::field()); + + let foo_id = Id::test_new(1); + let foo = builder.import_function(foo_id); + let main_call1_results = + builder.insert_call(foo, vec![main_v0, main_v1], vec![Type::field()]).to_vec(); + let main_call2_results = builder + .insert_call(foo, vec![main_call1_results[0], main_v1], vec![Type::field()]) + .to_vec(); + builder.insert_constrain(main_call1_results[0], main_call2_results[0], None); + builder.terminate_with_return(vec![]); + + build_basic_foo_with_return(&mut builder, foo_id); + + let ssa = builder.finish(); + + let acir_functions = ssa + .into_acir( + &Brillig::default(), + noirc_frontend::Distinctness::Distinct, + &HashMap::default(), + ) + .expect("Should compile manually written SSA into ACIR"); + // The expected result should look very similar to the abvoe test expect that the input witnesses of the `Call` + // opcodes will be different. The changes can discerned from the checks below. + + let main_acir = &acir_functions[0]; + let main_opcodes = main_acir.opcodes(); + assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); + + check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + // The output of the first call should be the input of the second call + check_call_opcode(&main_opcodes[1], 1, vec![Witness(2), Witness(1)], vec![Witness(3)]); + } + + #[test] + fn basic_nested_call() { + // SSA for the following Noir program: + // fn main(x: Field, y: pub Field) { + // let z = func_with_nested_foo_call(x, y); + // let z2 = func_with_nested_foo_call(x, y); + // assert(z == z2); + // } + // #[fold] + // fn func_with_nested_foo_call(x: Field, y: Field) -> Field { + // nested_call(x + 2, y) + // } + // #[fold] + // fn foo(x: Field, y: Field) -> Field { + // assert(x != y); + // x + // } + // + // SSA: + // acir(inline) fn main f0 { + // b0(v0: Field, v1: Field): + // v3 = call f1(v0, v1) + // v4 = call f1(v0, v1) + // constrain v3 == v4 + // return + // } + // acir(fold) fn func_with_nested_foo_call f1 { + // b0(v0: Field, v1: Field): + // v3 = add v0, Field 2 + // v5 = call f2(v3, v1) + // return v5 + // } + // acir(fold) fn foo f2 { + // b0(v0: Field, v1: Field): + // v2 = eq v0, v1 + // constrain v2 == Field 0 + // return v0 + // } + let foo_id = Id::test_new(0); + let mut builder = FunctionBuilder::new("main".into(), foo_id); + let main_v0 = builder.add_parameter(Type::field()); + let main_v1 = builder.add_parameter(Type::field()); + + let func_with_nested_foo_call_id = Id::test_new(1); + let func_with_nested_foo_call = builder.import_function(func_with_nested_foo_call_id); + let main_call1_results = builder + .insert_call(func_with_nested_foo_call, vec![main_v0, main_v1], vec![Type::field()]) + .to_vec(); + let main_call2_results = builder + .insert_call(func_with_nested_foo_call, vec![main_v0, main_v1], vec![Type::field()]) + .to_vec(); + builder.insert_constrain(main_call1_results[0], main_call2_results[0], None); + builder.terminate_with_return(vec![]); + + builder.new_function( + "func_with_nested_foo_call".into(), + func_with_nested_foo_call_id, + InlineType::Fold, + ); + let func_with_nested_call_v0 = builder.add_parameter(Type::field()); + let func_with_nested_call_v1 = builder.add_parameter(Type::field()); + + let two = builder.field_constant(2u128); + let v0_plus_two = builder.insert_binary(func_with_nested_call_v0, BinaryOp::Add, two); + + let foo_id = Id::test_new(2); + let foo_call = builder.import_function(foo_id); + let foo_call = builder + .insert_call(foo_call, vec![v0_plus_two, func_with_nested_call_v1], vec![Type::field()]) + .to_vec(); + builder.terminate_with_return(vec![foo_call[0]]); + + build_basic_foo_with_return(&mut builder, foo_id); + + let ssa = builder.finish(); + + let acir_functions = ssa + .into_acir( + &Brillig::default(), + noirc_frontend::Distinctness::Distinct, + &HashMap::default(), + ) + .expect("Should compile manually written SSA into ACIR"); + + assert_eq!(acir_functions.len(), 3, "Should have three ACIR functions"); + + let main_acir = &acir_functions[0]; + let main_opcodes = main_acir.opcodes(); + assert_eq!(main_opcodes.len(), 3, "Should have two calls to `foo` and an assert"); + + // Both of these should call func_with_nested_foo_call f1 + check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); + // The output of the first call should be the input of the second call + check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); + + let func_with_nested_call_acir = &acir_functions[1]; + let func_with_nested_call_opcodes = func_with_nested_call_acir.opcodes(); + assert_eq!( + func_with_nested_call_opcodes.len(), + 2, + "Should have an expression and a call to a nested `foo`" + ); + // Should call foo f2 + check_call_opcode( + &func_with_nested_call_opcodes[1], + 2, + vec![Witness(2), Witness(1)], + vec![Witness(3)], + ); + } + + fn check_call_opcode( + opcode: &Opcode, + expected_id: u32, + expected_inputs: Vec, + expected_outputs: Vec, + ) { + match opcode { + Opcode::Call { id, inputs, outputs } => { + assert_eq!( + *id, expected_id, + "Main was expected to call {expected_id} but got {}", + *id + ); + for (expected_input, input) in expected_inputs.iter().zip(inputs) { + assert_eq!( + expected_input, input, + "Expected witness {expected_input:?} but got {input:?}" + ); + } + for (expected_output, output) in expected_outputs.iter().zip(outputs) { + assert_eq!( + expected_output, output, + "Expected witness {expected_output:?} but got {output:?}" + ); + } + } + _ => panic!("Expected only Call opcode"), + } + } +} diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 0726b557616..e0e60b737ad 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -17,7 +17,7 @@ use super::{ ir::{ basic_block::BasicBlock, dfg::{CallStack, InsertInstructionResult}, - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{ConstrainError, InstructionId, Intrinsic}, }, ssa_gen::Ssa, @@ -42,13 +42,8 @@ impl FunctionBuilder { /// /// This creates the new function internally so there is no need to call .new_function() /// right after constructing a new FunctionBuilder. - pub(crate) fn new( - function_name: String, - function_id: FunctionId, - runtime: RuntimeType, - ) -> Self { - let mut new_function = Function::new(function_name, function_id); - new_function.set_runtime(runtime); + pub(crate) fn new(function_name: String, function_id: FunctionId) -> Self { + let new_function = Function::new(function_name, function_id); Self { current_block: new_function.entry_block(), @@ -58,6 +53,15 @@ impl FunctionBuilder { } } + /// Set the runtime of the initial function that is created internally after constructing + /// the FunctionBuilder. A function's default runtime type is `RuntimeType::Acir(InlineType::Inline)`. + /// This should only be used immediately following construction of a FunctionBuilder + /// and will panic if there are any already finished functions. + pub(crate) fn set_runtime(&mut self, runtime: RuntimeType) { + assert_eq!(self.finished_functions.len(), 0, "Attempted to set runtime on a FunctionBuilder with finished functions. A FunctionBuilder's runtime should only be set on its initial function"); + self.current_function.set_runtime(runtime); + } + /// Finish the current function and create a new function. /// /// A FunctionBuilder can always only work on one function at a time, so care @@ -78,8 +82,13 @@ impl FunctionBuilder { } /// Finish the current function and create a new ACIR function. - pub(crate) fn new_function(&mut self, name: String, function_id: FunctionId) { - self.new_function_with_type(name, function_id, RuntimeType::Acir); + pub(crate) fn new_function( + &mut self, + name: String, + function_id: FunctionId, + inline_type: InlineType, + ) { + self.new_function_with_type(name, function_id, RuntimeType::Acir(inline_type)); } /// Finish the current function and create a new unconstrained function. @@ -491,7 +500,7 @@ mod tests { use acvm::FieldElement; use crate::ssa::ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{Endian, Intrinsic}, map::Id, types::Type, @@ -506,7 +515,7 @@ mod tests { // let x = 7; // let bits = x.to_le_bits(8); let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let one = builder.numeric_constant(FieldElement::one(), Type::bool()); let zero = builder.numeric_constant(FieldElement::zero(), Type::bool()); diff --git a/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/compiler/noirc_evaluator/src/ssa/ir/dom.rs index c1df0428c64..bd1481a7474 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -252,7 +252,7 @@ mod tests { basic_block::BasicBlockId, dfg::CallStack, dom::DominatorTree, - function::{Function, RuntimeType}, + function::{Function, InlineType, RuntimeType}, instruction::TerminatorInstruction, map::Id, types::Type, @@ -286,7 +286,7 @@ mod tests { // return () // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let cond = builder.add_parameter(Type::unsigned(1)); let block1_id = builder.insert_block(); @@ -395,7 +395,7 @@ mod tests { // jump block1() // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let block1_id = builder.insert_block(); let block2_id = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/function.rs b/compiler/noirc_evaluator/src/ssa/ir/function.rs index 360f5710113..011bee36661 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/function.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/function.rs @@ -12,11 +12,39 @@ use super::value::ValueId; #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) enum RuntimeType { // A noir function, to be compiled in ACIR and executed by ACVM - Acir, + Acir(InlineType), // Unconstrained function, to be compiled to brillig and executed by the Brillig VM Brillig, } +/// Represents how a RuntimeType::Acir function should be inlined. +/// This type is only relevant for ACIR functions as we do not inline any Brillig functions +#[derive(Default, Clone, Copy, PartialEq, Eq, Debug, Hash)] +pub(crate) enum InlineType { + /// The most basic entry point can expect all its functions to be inlined. + /// All function calls are expected to be inlined into a single ACIR. + #[default] + Inline, + /// Functions marked as foldable will not be inlined and compiled separately into ACIR + Fold, +} + +impl RuntimeType { + /// Returns whether the runtime type represents an entry point. + /// We return `false` for InlineType::Inline on default, which is true + /// in all cases except for main. `main` should be supported with special + /// handling in any places where this function determines logic. + pub(crate) fn is_entry_point(&self) -> bool { + match self { + RuntimeType::Acir(inline_type) => match inline_type { + InlineType::Inline => false, + InlineType::Fold => true, + }, + RuntimeType::Brillig => true, + } + } +} + /// A function holds a list of instructions. /// These instructions are further grouped into Basic blocks /// @@ -47,7 +75,7 @@ impl Function { pub(crate) fn new(name: String, id: FunctionId) -> Self { let mut dfg = DataFlowGraph::default(); let entry_block = dfg.make_block(); - Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir } + Self { name, id, entry_block, dfg, runtime: RuntimeType::Acir(InlineType::default()) } } /// The name of the function. @@ -129,12 +157,21 @@ impl Function { impl std::fmt::Display for RuntimeType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - RuntimeType::Acir => write!(f, "acir"), + RuntimeType::Acir(inline_type) => write!(f, "acir({inline_type})"), RuntimeType::Brillig => write!(f, "brillig"), } } } +impl std::fmt::Display for InlineType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InlineType::Inline => write!(f, "inline"), + InlineType::Fold => write!(f, "fold"), + } + } +} + /// FunctionId is a reference for a function /// /// This Id is how each function refers to other functions diff --git a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs index 3cae86829ed..5d743e953a5 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs @@ -74,7 +74,7 @@ mod tests { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::{Function, RuntimeType}, + function::{Function, InlineType, RuntimeType}, map::Id, post_order::PostOrder, types::Type, @@ -112,7 +112,7 @@ mod tests { // D, F, E, B, A, (C dropped as unreachable) let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let block_b_id = builder.insert_block(); let block_c_id = builder.insert_block(); let block_d_id = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs index b737c51d145..6d556e86cb5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs @@ -77,7 +77,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{Binary, BinaryOp, Instruction}, map::Id, types::Type, @@ -100,7 +100,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let one = builder.field_constant(1u128); diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 06ae4bf5202..88948331960 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -283,7 +283,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction}, map::Id, types::Type, @@ -305,7 +305,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let one = builder.field_constant(1u128); @@ -361,7 +361,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(16)); let v1 = builder.add_parameter(Type::unsigned(16)); @@ -415,7 +415,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(16)); let v1 = builder.add_parameter(Type::unsigned(16)); @@ -471,7 +471,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let one = builder.field_constant(1u128); let v1 = builder.insert_binary(v0, BinaryOp::Add, one); @@ -518,7 +518,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(16)); let v1 = builder.insert_cast(v0, Type::unsigned(32)); @@ -562,7 +562,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::bool()); let v1 = builder.add_parameter(Type::bool()); let v2 = builder.add_parameter(Type::bool()); diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index 1f09a132132..ca6527eb0ec 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -13,7 +13,7 @@ use crate::ssa::{ function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - function::{Function, FunctionId, RuntimeType, Signature}, + function::{Function, FunctionId, Signature}, instruction::{BinaryOp, ConstrainError, Instruction}, types::{NumericType, Type}, value::{Value, ValueId}, @@ -304,7 +304,7 @@ fn create_apply_function( ) -> FunctionId { assert!(!function_ids.is_empty()); ssa.add_fn(|id| { - let mut function_builder = FunctionBuilder::new("apply".to_string(), id, RuntimeType::Acir); + let mut function_builder = FunctionBuilder::new("apply".to_string(), id); let target_id = function_builder.add_parameter(Type::field()); let params_ids = vecmap(signature.params, |typ| function_builder.add_parameter(typ)); diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index 4c7beff0fbe..df89dcb716d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -169,7 +169,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, Intrinsic}, map::Id, types::Type, @@ -199,7 +199,7 @@ mod test { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); let b1 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 46f1e7a2765..e731a7952a6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -167,7 +167,9 @@ impl Ssa { /// For more information, see the module-level comment at the top of this file. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn flatten_cfg(mut self) -> Ssa { - flatten_function_cfg(self.main_mut()); + for function in self.functions.values_mut() { + flatten_function_cfg(function); + } self } } @@ -839,7 +841,7 @@ mod test { function_builder::FunctionBuilder, ir::{ dfg::DataFlowGraph, - function::{Function, RuntimeType}, + function::{Function, InlineType, RuntimeType}, instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, @@ -860,7 +862,7 @@ mod test { // return v1 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -914,7 +916,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -963,7 +965,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1024,7 +1026,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1114,7 +1116,7 @@ mod test { // ↘ ↙ // b9 let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1271,7 +1273,7 @@ mod test { // before the first store to allocate, which loaded an uninitialized value. // In this test we assert the ordering is strictly Allocate then Store then Load. let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -1370,7 +1372,7 @@ mod test { // return // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); builder.insert_block(); // entry @@ -1423,7 +1425,7 @@ mod test { // jmp b3() // } let main_id = Id::test_new(1); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); builder.insert_block(); // b0 let b1 = builder.insert_block(); @@ -1533,7 +1535,7 @@ mod test { // jmp b5() // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs index 59bee00936a..adb6d2871e5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs @@ -114,7 +114,12 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{cfg::ControlFlowGraph, function::RuntimeType, map::Id, types::Type}, + ir::{ + cfg::ControlFlowGraph, + function::{InlineType, RuntimeType}, + map::Id, + types::Type, + }, opt::flatten_cfg::branch_analysis::find_branch_ends, }; @@ -134,7 +139,7 @@ mod test { // ↘ ↙ // b9 let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -195,7 +200,7 @@ mod test { // ↘ ↙ // b15 let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index aff06af9921..7c7698d236d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -11,7 +11,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::{CallStack, InsertInstructionResult}, - function::{Function, FunctionId, RuntimeType}, + function::{Function, FunctionId, InlineType, RuntimeType}, instruction::{Instruction, InstructionId, TerminatorInstruction}, value::{Value, ValueId}, }, @@ -94,12 +94,13 @@ struct PerFunctionContext<'function> { } /// The entry point functions are each function we should inline into - and each function that -/// should be left in the final program. This is usually just `main` but also includes any -/// brillig functions used. +/// should be left in the final program. +/// This is the `main` function, any Acir functions with a [fold inline type][InlineType::Fold], +/// and any brillig functions used. fn get_entry_point_functions(ssa: &Ssa) -> BTreeSet { let functions = ssa.functions.iter(); let mut entry_points = functions - .filter(|(_, function)| function.runtime() == RuntimeType::Brillig) + .filter(|(_, function)| function.runtime().is_entry_point()) .map(|(id, _)| *id) .collect::>(); @@ -115,7 +116,8 @@ impl InlineContext { /// that could not be inlined calling it. fn new(ssa: &Ssa, entry_point: FunctionId) -> InlineContext { let source = &ssa.functions[&entry_point]; - let builder = FunctionBuilder::new(source.name().to_owned(), entry_point, source.runtime()); + let mut builder = FunctionBuilder::new(source.name().to_owned(), entry_point); + builder.set_runtime(source.runtime()); Self { builder, recursion_level: 0, entry_point, call_stack: CallStack::new() } } @@ -350,8 +352,12 @@ impl<'function> PerFunctionContext<'function> { match &self.source_function.dfg[*id] { Instruction::Call { func, arguments } => match self.get_function(*func) { Some(function) => match ssa.functions[&function].runtime() { - RuntimeType::Acir => self.inline_function(ssa, *id, function, arguments), - RuntimeType::Brillig => self.push_instruction(*id), + RuntimeType::Acir(InlineType::Inline) => { + self.inline_function(ssa, *id, function, arguments); + } + RuntimeType::Acir(InlineType::Fold) | RuntimeType::Brillig => { + self.push_instruction(*id); + } }, None => self.push_instruction(*id), }, @@ -517,7 +523,7 @@ mod test { function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, Intrinsic, TerminatorInstruction}, map::Id, types::Type, @@ -536,14 +542,14 @@ mod test { // return 72 // } let foo_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("foo".into(), foo_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("foo".into(), foo_id); let bar_id = Id::test_new(1); let bar = builder.import_function(bar_id); let results = builder.insert_call(bar, Vec::new(), vec![Type::field()]).to_vec(); builder.terminate_with_return(results); - builder.new_function("bar".into(), bar_id); + builder.new_function("bar".into(), bar_id, InlineType::default()); let expected_return = 72u128; let seventy_two = builder.field_constant(expected_return); builder.terminate_with_return(vec![seventy_two]); @@ -585,7 +591,7 @@ mod test { let id2_id = Id::test_new(3); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let main_v0 = builder.add_parameter(Type::field()); let main_f1 = builder.import_function(square_id); @@ -598,18 +604,18 @@ mod test { builder.terminate_with_return(vec![main_v16]); // Compiling square f1 - builder.new_function("square".into(), square_id); + builder.new_function("square".into(), square_id, InlineType::default()); let square_v0 = builder.add_parameter(Type::field()); let square_v2 = builder.insert_binary(square_v0, BinaryOp::Mul, square_v0); builder.terminate_with_return(vec![square_v2]); // Compiling id1 f2 - builder.new_function("id1".into(), id1_id); + builder.new_function("id1".into(), id1_id, InlineType::default()); let id1_v0 = builder.add_parameter(Type::Function); builder.terminate_with_return(vec![id1_v0]); // Compiling id2 f3 - builder.new_function("id2".into(), id2_id); + builder.new_function("id2".into(), id2_id, InlineType::default()); let id2_v0 = builder.add_parameter(Type::Function); builder.terminate_with_return(vec![id2_v0]); @@ -641,7 +647,7 @@ mod test { // return v4 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let factorial_id = Id::test_new(1); let factorial = builder.import_function(factorial_id); @@ -650,7 +656,7 @@ mod test { let results = builder.insert_call(factorial, vec![five], vec![Type::field()]).to_vec(); builder.terminate_with_return(results); - builder.new_function("factorial".into(), factorial_id); + builder.new_function("factorial".into(), factorial_id, InlineType::default()); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -741,7 +747,7 @@ mod test { // jmp b3(Field 2) // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let main_cond = builder.add_parameter(Type::bool()); let inner1_id = Id::test_new(1); @@ -751,14 +757,14 @@ mod test { builder.insert_call(assert_constant, vec![main_v2], vec![]); builder.terminate_with_return(vec![]); - builder.new_function("inner1".into(), inner1_id); + builder.new_function("inner1".into(), inner1_id, InlineType::default()); let inner1_cond = builder.add_parameter(Type::bool()); let inner2_id = Id::test_new(2); let inner2 = builder.import_function(inner2_id); let inner1_v2 = builder.insert_call(inner2, vec![inner1_cond], vec![Type::field()])[0]; builder.terminate_with_return(vec![inner1_v2]); - builder.new_function("inner2".into(), inner2_id); + builder.new_function("inner2".into(), inner2_id, InlineType::default()); let inner2_cond = builder.add_parameter(Type::bool()); let then_block = builder.insert_block(); let else_block = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index 0a49ca4ecca..f1a38585bd6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -414,7 +414,7 @@ mod tests { ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, @@ -433,7 +433,7 @@ mod tests { // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let v0 = builder.insert_allocate(Type::Array(Rc::new(vec![Type::field()]), 2)); let one = builder.field_constant(FieldElement::one()); let two = builder.field_constant(FieldElement::one()); @@ -474,7 +474,7 @@ mod tests { // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let v0 = builder.insert_allocate(Type::field()); let one = builder.field_constant(FieldElement::one()); builder.insert_store(v0, one); @@ -508,7 +508,7 @@ mod tests { // } let func_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("func".into(), func_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("func".into(), func_id); let v0 = builder.insert_allocate(Type::field()); let const_one = builder.field_constant(FieldElement::one()); builder.insert_store(v0, const_one); @@ -567,7 +567,7 @@ mod tests { // return v2, v3, v4 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.insert_allocate(Type::field()); @@ -647,7 +647,7 @@ mod tests { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.insert_allocate(Type::field()); diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index 4766bc3e8d2..7b5196f2004 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -166,8 +166,12 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::Instruction, map::Id, types::Type, + basic_block::BasicBlockId, + dfg::DataFlowGraph, + function::{InlineType, RuntimeType}, + instruction::Instruction, + map::Id, + types::Type, }, }; @@ -206,7 +210,8 @@ mod test { // return [v0] // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("foo".into(), main_id, RuntimeType::Brillig); + let mut builder = FunctionBuilder::new("foo".into(), main_id); + builder.set_runtime(RuntimeType::Brillig); let inner_array_type = Type::Array(Rc::new(vec![Type::field()]), 2); let v0 = builder.add_parameter(inner_array_type.clone()); @@ -245,7 +250,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("mutator".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("mutator".into(), main_id); let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); let v0 = builder.add_parameter(array_type.clone()); @@ -294,7 +299,7 @@ mod test { // return // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("mutator2".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("mutator2".into(), main_id); let array_type = Type::Array(Rc::new(vec![Type::field()]), 2); let reference_type = Type::Reference(Rc::new(array_type.clone())); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index a71a42d5757..1eb1e20d41d 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -20,7 +20,9 @@ impl Ssa { /// See [`constant_folding`][self] module for more information. #[tracing::instrument(level = "trace", skip(self))] pub(crate) fn remove_bit_shifts(mut self) -> Ssa { - remove_bit_shifts(self.main_mut()); + for function in self.functions.values_mut() { + remove_bit_shifts(function); + } self } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index a31def8fd98..b9675d99c90 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -154,7 +154,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::RuntimeType, + function::{InlineType, RuntimeType}, instruction::{BinaryOp, TerminatorInstruction}, map::Id, types::Type, @@ -172,7 +172,7 @@ mod test { // return v1 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -228,7 +228,7 @@ mod test { // return Field 2 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::bool()); let b1 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 645adb6b350..92ec1e8f1bb 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -48,7 +48,7 @@ impl Ssa { // This check is always true with the addition of the above guard, but I'm // keeping it in case the guard on brillig functions is ever removed. - let abort_on_error = function.runtime() == RuntimeType::Acir; + let abort_on_error = matches!(function.runtime(), RuntimeType::Acir(_)); find_all_loops(function).unroll_each_loop(function, abort_on_error)?; } Ok(self) @@ -464,7 +464,12 @@ impl<'f> LoopIteration<'f> { mod tests { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{function::RuntimeType, instruction::BinaryOp, map::Id, types::Type}, + ir::{ + function::{InlineType, RuntimeType}, + instruction::BinaryOp, + map::Id, + types::Type, + }, }; #[test] @@ -503,7 +508,7 @@ mod tests { let main_id = Id::test_new(0); // Compiling main - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); @@ -605,7 +610,7 @@ mod tests { // return Field 0 // } let main_id = Id::test_new(0); - let mut builder = FunctionBuilder::new("main".into(), main_id, RuntimeType::Acir); + let mut builder = FunctionBuilder::new("main".into(), main_id); let b1 = builder.insert_block(); let b2 = builder.insert_block(); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index a9a707ca8ed..569e543ce40 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -12,8 +12,8 @@ use crate::errors::RuntimeError; use crate::ssa::function_builder::FunctionBuilder; use crate::ssa::ir::basic_block::BasicBlockId; use crate::ssa::ir::dfg::DataFlowGraph; -use crate::ssa::ir::function::FunctionId as IrFunctionId; use crate::ssa::ir::function::{Function, RuntimeType}; +use crate::ssa::ir::function::{FunctionId as IrFunctionId, InlineType}; use crate::ssa::ir::instruction::BinaryOp; use crate::ssa::ir::instruction::Instruction; use crate::ssa::ir::map::AtomicCounter; @@ -108,7 +108,8 @@ impl<'a> FunctionContext<'a> { .expect("No function in queue for the FunctionContext to compile") .1; - let builder = FunctionBuilder::new(function_name, function_id, runtime); + let mut builder = FunctionBuilder::new(function_name, function_id); + builder.set_runtime(runtime); let definitions = HashMap::default(); let mut this = Self { definitions, builder, shared_context, loops: Vec::new() }; this.add_parameters_to_scope(parameters); @@ -125,7 +126,8 @@ impl<'a> FunctionContext<'a> { if func.unconstrained { self.builder.new_brillig_function(func.name.clone(), id); } else { - self.builder.new_function(func.name.clone(), id); + let inline_type = if func.should_fold { InlineType::Fold } else { InlineType::Inline }; + self.builder.new_function(func.name.clone(), id, inline_type); } self.add_parameters_to_scope(&func.parameters); } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index a39df14d60a..3fe52f7f0e5 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -14,7 +14,10 @@ use noirc_frontend::{ use crate::{ errors::{InternalError, RuntimeError}, - ssa::{function_builder::data_bus::DataBusBuilder, ir::instruction::Intrinsic}, + ssa::{ + function_builder::data_bus::DataBusBuilder, + ir::{function::InlineType, instruction::Intrinsic}, + }, }; use self::{ @@ -52,14 +55,15 @@ pub(crate) fn generate_ssa( // Queue the main function for compilation context.get_or_queue_function(main_id); - let mut function_context = FunctionContext::new( main.name.clone(), &main.parameters, if force_brillig_runtime || main.unconstrained { RuntimeType::Brillig } else { - RuntimeType::Acir + let main_inline_type = + if main.should_fold { InlineType::Fold } else { InlineType::Inline }; + RuntimeType::Acir(main_inline_type) }, &context, ); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs index 509f778f3b0..9995c031145 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/program.rs @@ -12,6 +12,7 @@ pub(crate) struct Ssa { pub(crate) functions: BTreeMap, pub(crate) main_id: FunctionId, pub(crate) next_id: AtomicCounter, + pub(crate) id_to_index: BTreeMap, } impl Ssa { @@ -26,7 +27,9 @@ impl Ssa { (f.id(), f) }); - Self { functions, main_id, next_id: AtomicCounter::starting_after(max_id) } + let id_to_index = btree_map(functions.iter().enumerate(), |(i, (id, _))| (*id, i as u32)); + + Self { functions, main_id, next_id: AtomicCounter::starting_after(max_id), id_to_index } } /// Returns the entry-point function of the program diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index 3e8b78c1312..10d962a23f7 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -108,6 +108,7 @@ impl From for NoirFunction { Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal, Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, Some(FunctionAttribute::Recursive) => FunctionKind::Recursive, + Some(FunctionAttribute::Fold) => FunctionKind::Normal, None => FunctionKind::Normal, }; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index a6511a6b0fb..ec149dee96e 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -942,7 +942,7 @@ impl<'a> Resolver<'a> { }); } let is_low_level_function = - func.attributes().function.as_ref().map_or(false, |func| func.is_low_level()); + attributes.function.as_ref().map_or(false, |func| func.is_low_level()); if !self.path_resolver.module_id().krate.is_stdlib() && is_low_level_function { let error = ResolverError::LowLevelFunctionOutsideOfStdlib { ident: func.name_ident().clone() }; @@ -991,6 +991,8 @@ impl<'a> Resolver<'a> { .map(|(name, typevar, _span)| (name.clone(), typevar.clone())) .collect(); + let should_fold = attributes.is_foldable(); + FuncMeta { name: name_ident, kind: func.kind, @@ -1005,6 +1007,7 @@ impl<'a> Resolver<'a> { has_body: !func.def.body.is_empty(), trait_constraints: self.resolve_trait_constraints(&func.def.where_clause), is_entry_point: self.is_entry_point_function(func), + should_fold, } } diff --git a/compiler/noirc_frontend/src/hir/type_check/errors.rs b/compiler/noirc_frontend/src/hir/type_check/errors.rs index fe9cc9ea2d6..6c28aabe0fb 100644 --- a/compiler/noirc_frontend/src/hir/type_check/errors.rs +++ b/compiler/noirc_frontend/src/hir/type_check/errors.rs @@ -312,7 +312,7 @@ impl From for Diagnostic { } TypeCheckError::InvalidTypeForEntryPoint { span } => Diagnostic::simple_error( "Only sized types may be used in the entry point to a program".to_string(), - "Slices, references, or any type containing them may not be used in main or a contract function".to_string(), span), + "Slices, references, or any type containing them may not be used in main, contract functions, or foldable functions".to_string(), span), TypeCheckError::MismatchTraitImplNumParameters { expected_num_parameters, actual_num_parameters, diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 5bda4b9345e..c5a04c33883 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -173,7 +173,7 @@ fn check_if_type_is_valid_for_program_input( errors: &mut Vec, ) { let meta = type_checker.interner.function_meta(&func_id); - if meta.is_entry_point && !param.1.is_valid_for_program_input() { + if (meta.is_entry_point || meta.should_fold) && !param.1.is_valid_for_program_input() { let span = param.0.span(); errors.push(TypeCheckError::InvalidTypeForEntryPoint { span }); } @@ -539,6 +539,7 @@ mod test { trait_constraints: Vec::new(), direct_generics: Vec::new(), is_entry_point: true, + should_fold: false, }; interner.push_fn_meta(func_meta, func_id); @@ -597,12 +598,7 @@ mod test { "#; - let expected_num_errors = 0; - type_check_src_code_errors_expected( - src, - expected_num_errors, - vec![String::from("main"), String::from("foo")], - ); + type_check_src_code(src, vec![String::from("main"), String::from("foo")]); } #[test] fn basic_closure() { @@ -627,6 +623,19 @@ mod test { type_check_src_code(src, vec![String::from("main")]); } + + #[test] + fn fold_entry_point() { + let src = r#" + #[fold] + fn fold(x: &mut Field) -> Field { + *x + } + "#; + + type_check_src_code_errors_expected(src, vec![String::from("fold")], 1); + } + // This is the same Stub that is in the resolver, maybe we can pull this out into a test module and re-use? struct TestPathResolver(HashMap); @@ -660,15 +669,15 @@ mod test { } fn type_check_src_code(src: &str, func_namespace: Vec) { - type_check_src_code_errors_expected(src, 0, func_namespace); + type_check_src_code_errors_expected(src, func_namespace, 0); } // This function assumes that there is only one function and this is the // func id that is returned fn type_check_src_code_errors_expected( src: &str, - expected_number_errors: usize, func_namespace: Vec, + expected_num_type_check_errs: usize, ) { let (program, errors) = parse_program(src); let mut interner = NodeInterner::default(); @@ -676,9 +685,8 @@ mod test { assert_eq!( errors.len(), - expected_number_errors, - "expected {} errors, but got {}, errors: {:?}", - expected_number_errors, + 0, + "expected 0 parser errors, but got {}, errors: {:?}", errors.len(), errors ); @@ -724,6 +732,13 @@ mod test { // Type check section let errors = super::type_check_func(&mut interner, func_ids.first().cloned().unwrap()); - assert_eq!(errors, vec![]); + assert_eq!( + errors.len(), + expected_num_type_check_errs, + "expected {} type check errors, but got {}, errors: {:?}", + expected_num_type_check_errs, + errors.len(), + errors + ); } } diff --git a/compiler/noirc_frontend/src/hir_def/function.rs b/compiler/noirc_frontend/src/hir_def/function.rs index 56543e8185c..a3bbc9445a8 100644 --- a/compiler/noirc_frontend/src/hir_def/function.rs +++ b/compiler/noirc_frontend/src/hir_def/function.rs @@ -124,6 +124,10 @@ pub struct FuncMeta { /// True if this function is an entry point to the program. /// For non-contracts, this means the function is `main`. pub is_entry_point: bool, + + /// True if this function is marked with an attribute + /// that indicates it should not be inlined, such as for folding. + pub should_fold: bool, } impl FuncMeta { diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index d0892f5ae38..c4b6bb288dc 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -726,6 +726,16 @@ mod tests { ); } + #[test] + fn fold_attribute() { + let input = r#"#[fold]"#; + + let mut lexer = Lexer::new(input); + let token = lexer.next_token().unwrap(); + + assert_eq!(token.token(), &Token::Attribute(Attribute::Function(FunctionAttribute::Fold))); + } + #[test] fn contract_library_method_attribute() { let input = r#"#[contract_library_method]"#; diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 4432a3f9e07..f8378cdd84b 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -427,6 +427,10 @@ impl Attributes { } None } + + pub fn is_foldable(&self) -> bool { + self.function.as_ref().map_or(false, |func_attribute| func_attribute.is_foldable()) + } } /// An Attribute can be either a Primary Attribute or a Secondary Attribute @@ -486,6 +490,7 @@ impl Attribute { } ["test"] => Attribute::Function(FunctionAttribute::Test(TestScope::None)), ["recursive"] => Attribute::Function(FunctionAttribute::Recursive), + ["fold"] => Attribute::Function(FunctionAttribute::Fold), ["test", name] => { validate(name)?; let malformed_scope = @@ -537,6 +542,7 @@ pub enum FunctionAttribute { Oracle(String), Test(TestScope), Recursive, + Fold, } impl FunctionAttribute { @@ -565,6 +571,10 @@ impl FunctionAttribute { pub fn is_low_level(&self) -> bool { matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) } + + pub fn is_foldable(&self) -> bool { + matches!(self, FunctionAttribute::Fold) + } } impl fmt::Display for FunctionAttribute { @@ -575,6 +585,7 @@ impl fmt::Display for FunctionAttribute { FunctionAttribute::Builtin(ref k) => write!(f, "#[builtin({k})]"), FunctionAttribute::Oracle(ref k) => write!(f, "#[oracle({k})]"), FunctionAttribute::Recursive => write!(f, "#[recursive]"), + FunctionAttribute::Fold => write!(f, "#[fold]"), } } } @@ -619,6 +630,7 @@ impl AsRef for FunctionAttribute { FunctionAttribute::Oracle(string) => string, FunctionAttribute::Test { .. } => "", FunctionAttribute::Recursive => "", + FunctionAttribute::Fold => "", } } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 4f633cbb350..7d20c2bcfee 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -211,6 +211,8 @@ pub struct Function { pub return_type: Type, pub unconstrained: bool, + pub should_fold: bool, + pub func_sig: FunctionSignature, } /// Compared to hir_def::types::Type, this monomorphized Type has: @@ -245,6 +247,7 @@ impl Type { #[derive(Debug, Clone, Hash)] pub struct Program { pub functions: Vec, + pub function_signatures: Vec, pub main_function_signature: FunctionSignature, /// Indicates whether witness indices are allowed to reoccur in the ABI of the resulting ACIR. /// @@ -264,6 +267,7 @@ impl Program { #[allow(clippy::too_many_arguments)] pub fn new( functions: Vec, + function_signatures: Vec, main_function_signature: FunctionSignature, return_distinctness: Distinctness, return_location: Option, @@ -275,6 +279,7 @@ impl Program { ) -> Program { Program { functions, + function_signatures, main_function_signature, return_distinctness, return_location, diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index e0d86592d9f..6aa0abce152 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -129,6 +129,18 @@ pub fn monomorphize_debug( undo_instantiation_bindings(bindings); } + let func_sigs = monomorphizer + .finished_functions + .iter() + .flat_map(|(_, f)| { + if f.should_fold || f.id == Program::main_id() { + Some(f.func_sig.clone()) + } else { + None + } + }) + .collect(); + let functions = vecmap(monomorphizer.finished_functions, |(_, f)| f); let FuncMeta { return_distinctness, return_visibility, kind, .. } = monomorphizer.interner.function_meta(&main); @@ -137,6 +149,7 @@ pub fn monomorphize_debug( monomorphizer.debug_type_tracker.extract_vars_and_types(); let program = Program::new( functions, + func_sigs, function_sig, *return_distinctness, monomorphizer.return_location, @@ -271,6 +284,8 @@ impl<'interner> Monomorphizer<'interner> { } let meta = self.interner.function_meta(&f).clone(); + let func_sig = meta.function_signature(); + let modifiers = self.interner.function_modifiers(&f); let name = self.interner.function_name(&f).to_owned(); @@ -284,9 +299,20 @@ impl<'interner> Monomorphizer<'interner> { let return_type = Self::convert_type(return_type, meta.location)?; let unconstrained = modifiers.is_unconstrained; + let should_fold = meta.should_fold; + let parameters = self.parameters(&meta.parameters)?; let body = self.expr(body_expr_id)?; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold, + func_sig, + }; self.push_function(id, function); Ok(()) @@ -1351,7 +1377,16 @@ impl<'interner> Monomorphizer<'interner> { let name = lambda_name.to_owned(); let unconstrained = false; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold: false, + func_sig: FunctionSignature::default(), + }; self.push_function(id, function); let typ = @@ -1468,7 +1503,16 @@ impl<'interner> Monomorphizer<'interner> { parameters.append(&mut converted_parameters); let unconstrained = false; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold: false, + func_sig: FunctionSignature::default(), + }; self.push_function(id, function); let lambda_value = @@ -1586,7 +1630,16 @@ impl<'interner> Monomorphizer<'interner> { let name = lambda_name.to_owned(); let unconstrained = false; - let function = ast::Function { id, name, parameters, body, return_type, unconstrained }; + let function = ast::Function { + id, + name, + parameters, + body, + return_type, + unconstrained, + should_fold: false, + func_sig: FunctionSignature::default(), + }; self.push_function(id, function); ast::Expression::Ident(ast::Ident { diff --git a/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts b/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts index 8b08b6f0dd8..39ad0d802fb 100644 --- a/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts +++ b/compiler/wasm/src/noir/dependencies/github-dependency-resolver.ts @@ -12,10 +12,12 @@ import { LogData } from '../../utils'; */ export class GithubDependencyResolver implements DependencyResolver { #fm: FileManager; + #fetch: typeof fetch; #log; - constructor(fm: FileManager) { + constructor(fm: FileManager, fetcher: typeof fetch) { this.#fm = fm; + this.#fetch = fetcher; this.#log = (msg: string, _data?: LogData) => { console.log(msg); }; @@ -56,7 +58,7 @@ export class GithubDependencyResolver implements DependencyResolver { return localArchivePath; } - const response = await fetch(url, { + const response = await this.#fetch(url, { method: 'GET', }); diff --git a/compiler/wasm/src/noir/noir-wasm-compiler.ts b/compiler/wasm/src/noir/noir-wasm-compiler.ts index 1ec3af1fd65..1fe3c79eae7 100644 --- a/compiler/wasm/src/noir/noir-wasm-compiler.ts +++ b/compiler/wasm/src/noir/noir-wasm-compiler.ts @@ -72,7 +72,8 @@ export class NoirWasmCompiler { const dependencyManager = new DependencyManager( [ new LocalDependencyResolver(fileManager), - new GithubCodeArchiveDependencyResolver(fileManager), + // use node's global fetch + new GithubCodeArchiveDependencyResolver(fileManager, fetch), // TODO support actual Git repositories ], noirPackage, diff --git a/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts b/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts index e7fae8afe8e..505b2269cd2 100644 --- a/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts +++ b/compiler/wasm/test/dependencies/github-dependency-resolver.test.ts @@ -31,7 +31,7 @@ describe('GithubDependencyResolver', () => { let fetchStub: SinonStub | undefined; beforeEach(() => { - fetchStub = Sinon.stub(globalThis, 'fetch'); + fetchStub = Sinon.stub(); fm = createMemFSFileManager(createFsFromVolume(new Volume()), '/'); libDependency = { @@ -50,16 +50,12 @@ describe('GithubDependencyResolver', () => { }, }); - resolver = new GithubDependencyResolver(fm); + resolver = new GithubDependencyResolver(fm, fetchStub); // cut off outside access fetchStub.onCall(0).throws(new Error()); }); - afterEach(() => { - fetchStub?.restore(); - }); - it("returns null if it can't resolve a dependency", async () => { const dep = await resolver.resolveDependency(pkg, { path: '/lib-c', diff --git a/cspell.json b/cspell.json index e50940fbd10..16de9757fb8 100644 --- a/cspell.json +++ b/cspell.json @@ -6,6 +6,7 @@ "words": [ "aarch", "acir", + "acirs", "acvm", "aeiou", "appender", diff --git a/test_programs/execution_success/fold_basic/Nargo.toml b/test_programs/execution_success/fold_basic/Nargo.toml new file mode 100644 index 00000000000..575ba1f3ad1 --- /dev/null +++ b/test_programs/execution_success/fold_basic/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_basic" +type = "bin" +authors = [""] +compiler_version = ">=0.25.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/fold_basic/Prover.toml b/test_programs/execution_success/fold_basic/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/test_programs/execution_success/fold_basic/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/test_programs/execution_success/fold_basic/src/main.nr b/test_programs/execution_success/fold_basic/src/main.nr new file mode 100644 index 00000000000..6c17120660b --- /dev/null +++ b/test_programs/execution_success/fold_basic/src/main.nr @@ -0,0 +1,11 @@ +fn main(x: Field, y: pub Field) { + let z = foo(x, y); + let z2 = foo(x, y); + assert(z == z2); +} + +#[fold] +fn foo(x: Field, y: Field) -> Field { + assert(x != y); + x +} diff --git a/test_programs/execution_success/fold_basic_nested_call/Nargo.toml b/test_programs/execution_success/fold_basic_nested_call/Nargo.toml new file mode 100644 index 00000000000..1b3c32999ae --- /dev/null +++ b/test_programs/execution_success/fold_basic_nested_call/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_basic_nested_call" +type = "bin" +authors = [""] +compiler_version = ">=0.25.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/fold_basic_nested_call/Prover.toml b/test_programs/execution_success/fold_basic_nested_call/Prover.toml new file mode 100644 index 00000000000..f28f2f8cc48 --- /dev/null +++ b/test_programs/execution_success/fold_basic_nested_call/Prover.toml @@ -0,0 +1,2 @@ +x = "5" +y = "10" diff --git a/test_programs/execution_success/fold_basic_nested_call/src/main.nr b/test_programs/execution_success/fold_basic_nested_call/src/main.nr new file mode 100644 index 00000000000..6d02b734727 --- /dev/null +++ b/test_programs/execution_success/fold_basic_nested_call/src/main.nr @@ -0,0 +1,16 @@ +fn main(x: Field, y: pub Field) { + let z = func_with_nested_foo_call(x, y); + let z2 = func_with_nested_foo_call(x, y); + assert(z == z2); +} + +#[fold] +fn func_with_nested_foo_call(x: Field, y: Field) -> Field { + foo(x + 2, y) +} + +#[fold] +fn foo(x: Field, y: Field) -> Field { + assert(x != y); + x +} diff --git a/tooling/acvm_cli/src/cli/execute_cmd.rs b/tooling/acvm_cli/src/cli/execute_cmd.rs index b76d0eccc29..86e7277451f 100644 --- a/tooling/acvm_cli/src/cli/execute_cmd.rs +++ b/tooling/acvm_cli/src/cli/execute_cmd.rs @@ -1,14 +1,14 @@ use std::io::{self, Write}; use acir::circuit::Program; -use acir::native_types::WitnessMap; +use acir::native_types::{WitnessMap, WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; use crate::cli::fs::inputs::{read_bytecode_from_file, read_inputs_from_file}; use crate::cli::fs::witness::save_witness_to_dir; use crate::errors::CliError; -use nargo::ops::{execute_circuit, DefaultForeignCallExecutor}; +use nargo::ops::{execute_program, DefaultForeignCallExecutor}; use super::fs::witness::create_output_witness_string; @@ -39,8 +39,11 @@ pub(crate) struct ExecuteCommand { fn run_command(args: ExecuteCommand) -> Result { let bytecode = read_bytecode_from_file(&args.working_directory, &args.bytecode)?; let circuit_inputs = read_inputs_from_file(&args.working_directory, &args.input_witness)?; - let output_witness = execute_program_from_witness(&circuit_inputs, &bytecode, None)?; - let output_witness_string = create_output_witness_string(&output_witness)?; + let output_witness = execute_program_from_witness(circuit_inputs, &bytecode, None)?; + assert_eq!(output_witness.length(), 1, "ACVM CLI only supports a witness stack of size 1"); + let output_witness_string = create_output_witness_string( + &output_witness.peek().expect("Should have a witness stack item").witness, + )?; if args.output_witness.is_some() { save_witness_to_dir( &output_witness_string, @@ -61,16 +64,16 @@ pub(crate) fn run(args: ExecuteCommand) -> Result { } pub(crate) fn execute_program_from_witness( - inputs_map: &WitnessMap, + inputs_map: WitnessMap, bytecode: &[u8], foreign_call_resolver_url: Option<&str>, -) -> Result { +) -> Result { let blackbox_solver = Bn254BlackBoxSolver::new(); let program: Program = Program::deserialize_program(bytecode) .map_err(|_| CliError::CircuitDeserializationError())?; - execute_circuit( - &program.functions[0], - inputs_map.clone(), + execute_program( + &program, + inputs_map, &blackbox_solver, &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), ) diff --git a/tooling/backend_interface/src/proof_system.rs b/tooling/backend_interface/src/proof_system.rs index 105ae337793..3b47a7ced3a 100644 --- a/tooling/backend_interface/src/proof_system.rs +++ b/tooling/backend_interface/src/proof_system.rs @@ -56,7 +56,7 @@ impl Backend { pub fn prove( &self, program: &Program, - witness_values: WitnessStack, + witness_stack: WitnessStack, ) -> Result, BackendError> { let binary_path = self.assert_binary_exists()?; self.assert_correct_version()?; @@ -66,7 +66,7 @@ impl Backend { // Create a temporary file for the witness let serialized_witnesses: Vec = - witness_values.try_into().expect("could not serialize witness map"); + witness_stack.try_into().expect("could not serialize witness map"); let witness_path = temp_directory.join("witness").with_extension("tr"); write_to_file(&serialized_witnesses, &witness_path); diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index 854e284dd43..5a42ef36e7e 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -12,3 +12,5 @@ references scalar_mul signed_comparison to_bytes_integration +fold_basic +fold_basic_nested_call diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index f0de8d5d1c8..ba12a20460d 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -381,6 +381,9 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { ACVMStatus::RequiresForeignCall(_) => { unreachable!("Unexpected pending foreign call resolution"); } + ACVMStatus::RequiresAcirCall(_) => { + todo!("Multiple ACIR calls are not supported"); + } } } diff --git a/tooling/nargo/src/errors.rs b/tooling/nargo/src/errors.rs index c743768bee2..ff238d79a46 100644 --- a/tooling/nargo/src/errors.rs +++ b/tooling/nargo/src/errors.rs @@ -64,7 +64,9 @@ impl NargoError { ExecutionError::SolvingError(error) => match error { OpcodeResolutionError::IndexOutOfBounds { .. } | OpcodeResolutionError::OpcodeNotSolvable(_) - | OpcodeResolutionError::UnsatisfiedConstrain { .. } => None, + | OpcodeResolutionError::UnsatisfiedConstrain { .. } + | OpcodeResolutionError::AcirMainCallAttempted { .. } + | OpcodeResolutionError::AcirCallOutputsMismatch { .. } => None, OpcodeResolutionError::BrilligFunctionFailed { message, .. } => Some(message), OpcodeResolutionError::BlackBoxFunctionFailed(_, reason) => Some(reason), }, diff --git a/tooling/nargo/src/ops/execute.rs b/tooling/nargo/src/ops/execute.rs index 370393fea09..6d328d65119 100644 --- a/tooling/nargo/src/ops/execute.rs +++ b/tooling/nargo/src/ops/execute.rs @@ -1,5 +1,7 @@ +use acvm::acir::circuit::Program; +use acvm::acir::native_types::WitnessStack; use acvm::brillig_vm::brillig::ForeignCallResult; -use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}; +use acvm::pwg::{ACVMStatus, ErrorLocation, OpcodeNotSolvable, OpcodeResolutionError, ACVM}; use acvm::BlackBoxFunctionSolver; use acvm::{acir::circuit::Circuit, acir::native_types::WitnessMap}; @@ -8,73 +10,145 @@ use crate::NargoError; use super::foreign_calls::{ForeignCallExecutor, NargoForeignCallResult}; -#[tracing::instrument(level = "trace", skip_all)] -pub fn execute_circuit( - circuit: &Circuit, - initial_witness: WitnessMap, - blackbox_solver: &B, - foreign_call_executor: &mut F, -) -> Result { - let mut acvm = ACVM::new(blackbox_solver, &circuit.opcodes, initial_witness); - - // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. - let mut assert_message: Option = None; - loop { - let solver_status = acvm.solve(); - - match solver_status { - ACVMStatus::Solved => break, - ACVMStatus::InProgress => { - unreachable!("Execution should not stop while in `InProgress` state.") - } - ACVMStatus::Failure(error) => { - let call_stack = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } => Some(vec![*opcode_location]), - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - Some(call_stack.clone()) - } - _ => None, - }; - - return Err(NargoError::ExecutionError(match call_stack { - Some(call_stack) => { - // First check whether we have a runtime assertion message that should be resolved on an ACVM failure - // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded - // messages associated with a specific `OpcodeLocation`. - // Otherwise return the provided opcode resolution error. - if let Some(assert_message) = assert_message { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else if let Some(assert_message) = circuit.get_assert_message( - *call_stack.last().expect("Call stacks should not be empty"), - ) { - ExecutionError::AssertionFailed(assert_message.to_owned(), call_stack) - } else { - ExecutionError::SolvingError(error) +struct ProgramExecutor<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> { + functions: &'a [Circuit], + // This gets built as we run through the program looking at each function call + witness_stack: WitnessStack, + + blackbox_solver: &'a B, + + foreign_call_executor: &'a mut F, +} + +impl<'a, B: BlackBoxFunctionSolver, F: ForeignCallExecutor> ProgramExecutor<'a, B, F> { + fn new( + functions: &'a [Circuit], + blackbox_solver: &'a B, + foreign_call_executor: &'a mut F, + ) -> Self { + ProgramExecutor { + functions, + witness_stack: WitnessStack::default(), + blackbox_solver, + foreign_call_executor, + } + } + + fn finalize(self) -> WitnessStack { + self.witness_stack + } + + #[tracing::instrument(level = "trace", skip_all)] + fn execute_circuit( + &mut self, + circuit: &Circuit, + initial_witness: WitnessMap, + ) -> Result { + let mut acvm = ACVM::new(self.blackbox_solver, &circuit.opcodes, initial_witness); + + // This message should be resolved by a nargo foreign call only when we have an unsatisfied assertion. + let mut assert_message: Option = None; + loop { + let solver_status = acvm.solve(); + + match solver_status { + ACVMStatus::Solved => break, + ACVMStatus::InProgress => { + unreachable!("Execution should not stop while in `InProgress` state.") + } + ACVMStatus::Failure(error) => { + let call_stack = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { + opcode_location: ErrorLocation::Resolved(opcode_location), + } => Some(vec![*opcode_location]), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + Some(call_stack.clone()) } - } - None => ExecutionError::SolvingError(error), - })); - } - ACVMStatus::RequiresForeignCall(foreign_call) => { - let foreign_call_result = foreign_call_executor.execute(&foreign_call)?; - match foreign_call_result { - NargoForeignCallResult::BrilligOutput(foreign_call_result) => { - acvm.resolve_pending_foreign_call(foreign_call_result); - } - NargoForeignCallResult::ResolvedAssertMessage(message) => { - if assert_message.is_some() { - unreachable!("Resolving an assert message should happen only once as the VM should have failed"); + _ => None, + }; + + return Err(NargoError::ExecutionError(match call_stack { + Some(call_stack) => { + // First check whether we have a runtime assertion message that should be resolved on an ACVM failure + // If we do not have a runtime assertion message, we should check whether the circuit has any hardcoded + // messages associated with a specific `OpcodeLocation`. + // Otherwise return the provided opcode resolution error. + if let Some(assert_message) = assert_message { + ExecutionError::AssertionFailed( + assert_message.to_owned(), + call_stack, + ) + } else if let Some(assert_message) = circuit.get_assert_message( + *call_stack.last().expect("Call stacks should not be empty"), + ) { + ExecutionError::AssertionFailed( + assert_message.to_owned(), + call_stack, + ) + } else { + ExecutionError::SolvingError(error) + } + } + None => ExecutionError::SolvingError(error), + })); + } + ACVMStatus::RequiresForeignCall(foreign_call) => { + let foreign_call_result = self.foreign_call_executor.execute(&foreign_call)?; + match foreign_call_result { + NargoForeignCallResult::BrilligOutput(foreign_call_result) => { + acvm.resolve_pending_foreign_call(foreign_call_result); } - assert_message = Some(message); + NargoForeignCallResult::ResolvedAssertMessage(message) => { + if assert_message.is_some() { + unreachable!("Resolving an assert message should happen only once as the VM should have failed"); + } + assert_message = Some(message); - acvm.resolve_pending_foreign_call(ForeignCallResult::default()); + acvm.resolve_pending_foreign_call(ForeignCallResult::default()); + } } } + ACVMStatus::RequiresAcirCall(call_info) => { + let acir_to_call = &self.functions[call_info.id as usize]; + let initial_witness = call_info.initial_witness; + let call_solved_witness = + self.execute_circuit(acir_to_call, initial_witness)?; + let mut call_resolved_outputs = Vec::new(); + for return_witness_index in acir_to_call.return_values.indices() { + if let Some(return_value) = + call_solved_witness.get_index(return_witness_index) + { + call_resolved_outputs.push(*return_value); + } else { + return Err(ExecutionError::SolvingError( + OpcodeNotSolvable::MissingAssignment(return_witness_index).into(), + ) + .into()); + } + } + acvm.resolve_pending_acir_call(call_resolved_outputs); + self.witness_stack.push(call_info.id, call_solved_witness); + } } } + + Ok(acvm.finalize()) } +} + +#[tracing::instrument(level = "trace", skip_all)] +pub fn execute_program( + program: &Program, + initial_witness: WitnessMap, + blackbox_solver: &B, + foreign_call_executor: &mut F, +) -> Result { + let main = &program.functions[0]; + + let mut executor = + ProgramExecutor::new(&program.functions, blackbox_solver, foreign_call_executor); + let main_witness = executor.execute_circuit(main, initial_witness)?; + executor.witness_stack.push(0, main_witness); - Ok(acvm.finalize()) + Ok(executor.finalize()) } diff --git a/tooling/nargo/src/ops/mod.rs b/tooling/nargo/src/ops/mod.rs index 55e9e927800..2f5e6ebb7d4 100644 --- a/tooling/nargo/src/ops/mod.rs +++ b/tooling/nargo/src/ops/mod.rs @@ -2,7 +2,7 @@ pub use self::compile::{ collect_errors, compile_contract, compile_program, compile_program_with_debug_instrumenter, compile_workspace, report_errors, }; -pub use self::execute::execute_circuit; +pub use self::execute::execute_program; pub use self::foreign_calls::{ DefaultForeignCallExecutor, ForeignCall, ForeignCallExecutor, NargoForeignCallResult, }; diff --git a/tooling/nargo/src/ops/test.rs b/tooling/nargo/src/ops/test.rs index 8cf2934da4d..45b1a88f99c 100644 --- a/tooling/nargo/src/ops/test.rs +++ b/tooling/nargo/src/ops/test.rs @@ -1,4 +1,7 @@ -use acvm::{acir::native_types::WitnessMap, BlackBoxFunctionSolver}; +use acvm::{ + acir::native_types::{WitnessMap, WitnessStack}, + BlackBoxFunctionSolver, +}; use noirc_driver::{compile_no_check, CompileError, CompileOptions}; use noirc_errors::{debug_info::DebugInfo, FileDiagnostic}; use noirc_evaluator::errors::RuntimeError; @@ -6,7 +9,7 @@ use noirc_frontend::hir::{def_map::TestFunction, Context}; use crate::{errors::try_to_diagnose_runtime_error, NargoError}; -use super::{execute_circuit, DefaultForeignCallExecutor}; +use super::{execute_program, DefaultForeignCallExecutor}; pub enum TestStatus { Pass, @@ -33,9 +36,8 @@ pub fn run_test( Ok(compiled_program) => { // Run the backend to ensure the PWG evaluates functions like std::hash::pedersen, // otherwise constraints involving these expressions will not error. - let circuit_execution = execute_circuit( - // TODO(https://github.com/noir-lang/noir/issues/4428) - &compiled_program.program.functions[0], + let circuit_execution = execute_program( + &compiled_program.program, WitnessMap::new(), blackbox_solver, &mut DefaultForeignCallExecutor::new(show_output, foreign_call_resolver_url), @@ -83,7 +85,7 @@ fn test_status_program_compile_fail(err: CompileError, test_function: &TestFunct fn test_status_program_compile_pass( test_function: &TestFunction, debug: DebugInfo, - circuit_execution: Result, + circuit_execution: Result, ) -> TestStatus { let circuit_execution_err = match circuit_execution { // Circuit execution was successful; ie no errors or unsatisfied constraints diff --git a/tooling/nargo_cli/src/cli/debug_cmd.rs b/tooling/nargo_cli/src/cli/debug_cmd.rs index 1f448105ee2..4f3e2886b2e 100644 --- a/tooling/nargo_cli/src/cli/debug_cmd.rs +++ b/tooling/nargo_cli/src/cli/debug_cmd.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::{WitnessMap, WitnessStack}; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -187,7 +187,11 @@ fn run_async( } if let Some(witness_name) = witness_name { - let witness_path = save_witness_to_dir(solved_witness, witness_name, target_dir)?; + let witness_path = save_witness_to_dir( + WitnessStack::from(solved_witness), + witness_name, + target_dir, + )?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } diff --git a/tooling/nargo_cli/src/cli/execute_cmd.rs b/tooling/nargo_cli/src/cli/execute_cmd.rs index 022bf7b761e..697f6d7c1ea 100644 --- a/tooling/nargo_cli/src/cli/execute_cmd.rs +++ b/tooling/nargo_cli/src/cli/execute_cmd.rs @@ -1,4 +1,4 @@ -use acvm::acir::native_types::WitnessMap; +use acvm::acir::native_types::WitnessStack; use bn254_blackbox_solver::Bn254BlackBoxSolver; use clap::Args; @@ -91,7 +91,7 @@ pub(crate) fn run( let compiled_program = nargo::ops::transform_program(compiled_program, expression_width); - let (return_value, solved_witness) = execute_program_and_decode( + let (return_value, witness_stack) = execute_program_and_decode( compiled_program, package, &args.prover_name, @@ -103,7 +103,7 @@ pub(crate) fn run( println!("[{}] Circuit output: {return_value:?}", package.name); } if let Some(witness_name) = &args.witness_name { - let witness_path = save_witness_to_dir(solved_witness, witness_name, target_dir)?; + let witness_path = save_witness_to_dir(witness_stack, witness_name, target_dir)?; println!("[{}] Witness saved to {}", package.name, witness_path.display()); } @@ -116,35 +116,37 @@ fn execute_program_and_decode( package: &Package, prover_name: &str, foreign_call_resolver_url: Option<&str>, -) -> Result<(Option, WitnessMap), CliError> { +) -> Result<(Option, WitnessStack), CliError> { // Parse the initial witness values from Prover.toml let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &program.abi)?; - let solved_witness = execute_program(&program, &inputs_map, foreign_call_resolver_url)?; + let witness_stack = execute_program(&program, &inputs_map, foreign_call_resolver_url)?; let public_abi = program.abi.public_abi(); - let (_, return_value) = public_abi.decode(&solved_witness)?; + // Get the entry point witness for the ABI + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + let (_, return_value) = public_abi.decode(main_witness)?; - Ok((return_value, solved_witness)) + Ok((return_value, witness_stack)) } pub(crate) fn execute_program( compiled_program: &CompiledProgram, inputs_map: &InputMap, foreign_call_resolver_url: Option<&str>, -) -> Result { +) -> Result { let blackbox_solver = Bn254BlackBoxSolver::new(); let initial_witness = compiled_program.abi.encode(inputs_map, None)?; - // TODO(https://github.com/noir-lang/noir/issues/4428) - let solved_witness_err = nargo::ops::execute_circuit( - &compiled_program.program.functions[0], + let solved_witness_stack_err = nargo::ops::execute_program( + &compiled_program.program, initial_witness, &blackbox_solver, &mut DefaultForeignCallExecutor::new(true, foreign_call_resolver_url), ); - match solved_witness_err { - Ok(solved_witness) => Ok(solved_witness), + match solved_witness_stack_err { + Ok(solved_witness_stack) => Ok(solved_witness_stack), Err(err) => { let debug_artifact = DebugArtifact { debug_symbols: vec![compiled_program.debug.clone()], diff --git a/tooling/nargo_cli/src/cli/fs/witness.rs b/tooling/nargo_cli/src/cli/fs/witness.rs index 52b5c385e5d..613cdec28da 100644 --- a/tooling/nargo_cli/src/cli/fs/witness.rs +++ b/tooling/nargo_cli/src/cli/fs/witness.rs @@ -1,21 +1,19 @@ use std::path::{Path, PathBuf}; -use acvm::acir::native_types::{WitnessMap, WitnessStack}; +use acvm::acir::native_types::WitnessStack; use nargo::constants::WITNESS_EXT; use super::{create_named_dir, write_to_file}; use crate::errors::FilesystemError; pub(crate) fn save_witness_to_dir>( - witnesses: WitnessMap, + witness_stack: WitnessStack, witness_name: &str, witness_dir: P, ) -> Result { create_named_dir(witness_dir.as_ref(), "witness"); let witness_path = witness_dir.as_ref().join(witness_name).with_extension(WITNESS_EXT); - // TODO(https://github.com/noir-lang/noir/issues/4428) - let witness_stack: WitnessStack = witnesses.into(); let buf: Vec = witness_stack.try_into()?; write_to_file(buf.as_slice(), &witness_path); diff --git a/tooling/nargo_cli/src/cli/prove_cmd.rs b/tooling/nargo_cli/src/cli/prove_cmd.rs index 272f2fa8e5d..b9e4bca9e69 100644 --- a/tooling/nargo_cli/src/cli/prove_cmd.rs +++ b/tooling/nargo_cli/src/cli/prove_cmd.rs @@ -1,4 +1,3 @@ -use acvm::acir::native_types::WitnessStack; use clap::Args; use nargo::constants::{PROVER_INPUT_FILE, VERIFIER_INPUT_FILE}; use nargo::ops::{compile_program, report_errors}; @@ -123,12 +122,14 @@ pub(crate) fn prove_package( let (inputs_map, _) = read_inputs_from_file(&package.root_dir, prover_name, Format::Toml, &compiled_program.abi)?; - let solved_witness = - execute_program(&compiled_program, &inputs_map, foreign_call_resolver_url)?; + let witness_stack = execute_program(&compiled_program, &inputs_map, foreign_call_resolver_url)?; // Write public inputs into Verifier.toml let public_abi = compiled_program.abi.public_abi(); - let (public_inputs, return_value) = public_abi.decode(&solved_witness)?; + // Get the entry point witness for the ABI + let main_witness = + &witness_stack.peek().expect("Should have at least one witness on the stack").witness; + let (public_inputs, return_value) = public_abi.decode(main_witness)?; write_inputs_to_file( &public_inputs, @@ -139,7 +140,7 @@ pub(crate) fn prove_package( Format::Toml, )?; - let proof = backend.prove(&compiled_program.program, WitnessStack::from(solved_witness))?; + let proof = backend.prove(&compiled_program.program, witness_stack)?; if check_proof { let public_inputs = public_abi.encode(&public_inputs, return_value)?; From 301d4beb6a5de2b5fe56bbf95d0eef5bbc8cbfa6 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 1 Apr 2024 15:13:24 +0100 Subject: [PATCH 130/416] chore: Use is_entry_point helper on RuntimeType (#4678) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …y clones # Description ## Problem\* Resolves ## Summary\* I just noticed a place that a spot was missed in inlining where `RuntimeType::is_entry_point` could be used rather than matching on `RuntimeType`. This is also better cause it stays in line with the filter for `get_entry_point_functions`. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa.rs | 4 +--- compiler/noirc_evaluator/src/ssa/opt/inlining.rs | 13 ++++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 59f2bdc6f84..52377d36b36 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -174,8 +174,6 @@ fn convert_generated_acir_into_circuit( .. } = generated_acir; - let locations = locations.clone(); - let (public_parameter_witnesses, private_parameters) = split_public_and_private_inputs(&func_sig, &input_witnesses); @@ -189,7 +187,7 @@ fn convert_generated_acir_into_circuit( private_parameters, public_parameters, return_values, - assert_messages: assert_messages.clone().into_iter().collect(), + assert_messages: assert_messages.into_iter().collect(), recursive, }; diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 7c7698d236d..63c78b99cdf 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -11,7 +11,7 @@ use crate::ssa::{ ir::{ basic_block::BasicBlockId, dfg::{CallStack, InsertInstructionResult}, - function::{Function, FunctionId, InlineType, RuntimeType}, + function::{Function, FunctionId}, instruction::{Instruction, InstructionId, TerminatorInstruction}, value::{Value, ValueId}, }, @@ -351,14 +351,13 @@ impl<'function> PerFunctionContext<'function> { for id in block.instructions() { match &self.source_function.dfg[*id] { Instruction::Call { func, arguments } => match self.get_function(*func) { - Some(function) => match ssa.functions[&function].runtime() { - RuntimeType::Acir(InlineType::Inline) => { - self.inline_function(ssa, *id, function, arguments); - } - RuntimeType::Acir(InlineType::Fold) | RuntimeType::Brillig => { + Some(function) => { + if ssa.functions[&function].runtime().is_entry_point() { self.push_instruction(*id); + } else { + self.inline_function(ssa, *id, function, arguments); } - }, + } None => self.push_instruction(*id), }, _ => self.push_instruction(*id), From 656665e86f685ab664ffb9f21f27e6365bcfd336 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:57:53 +0100 Subject: [PATCH 131/416] chore: remove last traces of nix (#4679) # Description ## Problem\* Resolves ## Summary\* We have a lot of tech debt from a partial removal of nix. As it's no longer in CI we cannot guarantee that the nix installation method works so we should just remove it so we can clean up the code. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .envrc | 20 -- .github/scripts/acvm_js-build.sh | 1 + .github/scripts/noir-wasm-build.sh | 1 + .github/scripts/noirc-abi-build.sh | 1 + .github/scripts/wasm-opt-install.sh | 2 +- .github/workflows/test-js-packages.yml | 17 +- .gitignore | 6 - .vscode/extensions.json | 1 - .vscode/settings.json | 11 - README.md | 20 -- acvm-repo/acvm_js/build.sh | 39 ++- acvm-repo/acvm_js/buildPhaseCargoCommand.sh | 42 --- acvm-repo/acvm_js/installPhase.sh | 10 - acvm-repo/acvm_js/package.json | 4 +- compiler/noirc_driver/build.rs | 3 +- compiler/wasm/README.md | 14 - default.nix | 13 - .../installation/other_install_methods.md | 160 +---------- flake.nix | 260 ------------------ package.json | 12 +- release-please-config.json | 3 +- shell.nix | 13 - tooling/debugger/build.rs | 3 +- tooling/nargo_cli/build.rs | 3 +- tooling/noirc_abi_wasm/README.md | 14 - tooling/noirc_abi_wasm/build.rs | 3 +- tooling/noirc_abi_wasm/build.sh | 39 ++- .../noirc_abi_wasm/buildPhaseCargoCommand.sh | 39 --- tooling/noirc_abi_wasm/installPhase.sh | 10 - tooling/noirc_abi_wasm/package.json | 4 +- wasm-bindgen-cli.nix | 43 --- 31 files changed, 61 insertions(+), 750 deletions(-) delete mode 100644 .envrc delete mode 100755 acvm-repo/acvm_js/buildPhaseCargoCommand.sh delete mode 100755 acvm-repo/acvm_js/installPhase.sh delete mode 100644 default.nix delete mode 100644 flake.nix delete mode 100644 shell.nix delete mode 100755 tooling/noirc_abi_wasm/buildPhaseCargoCommand.sh delete mode 100755 tooling/noirc_abi_wasm/installPhase.sh delete mode 100644 wasm-bindgen-cli.nix diff --git a/.envrc b/.envrc deleted file mode 100644 index b2f868b1898..00000000000 --- a/.envrc +++ /dev/null @@ -1,20 +0,0 @@ -# Based on https://github.com/direnv/direnv-vscode/blob/158e8302c2594cc0eaa5f8b4f0cafedd4e1c0315/.envrc - -# You can define your system-specific logic (like Git settings or GH tokens) in .envrc.local -# If that logic is usable by other people and might improve development environment, consider -# contributing it to this file! - -source_env_if_exists .envrc.local - -if [[ -z "${SKIP_NIX:-}" ]] && has nix; then - - if nix flake metadata &>/dev/null && has use_flake; then - # use flakes if possible - use flake - - else - # Otherwise fall back to pure nix - use nix - fi - -fi diff --git a/.github/scripts/acvm_js-build.sh b/.github/scripts/acvm_js-build.sh index 95bd1efc8b9..e79967e3a8f 100755 --- a/.github/scripts/acvm_js-build.sh +++ b/.github/scripts/acvm_js-build.sh @@ -2,4 +2,5 @@ set -eu .github/scripts/wasm-bindgen-install.sh +.github/scripts/wasm-opt-install.sh yarn workspace @noir-lang/acvm_js build diff --git a/.github/scripts/noir-wasm-build.sh b/.github/scripts/noir-wasm-build.sh index 48e3ad73769..6c0336ee0c5 100755 --- a/.github/scripts/noir-wasm-build.sh +++ b/.github/scripts/noir-wasm-build.sh @@ -2,5 +2,6 @@ set -eu .github/scripts/wasm-pack-install.sh +.github/scripts/wasm-opt-install.sh yarn workspace @noir-lang/types build yarn workspace @noir-lang/noir_wasm build diff --git a/.github/scripts/noirc-abi-build.sh b/.github/scripts/noirc-abi-build.sh index 23b8393088e..99de474eb75 100755 --- a/.github/scripts/noirc-abi-build.sh +++ b/.github/scripts/noirc-abi-build.sh @@ -2,4 +2,5 @@ set -eu .github/scripts/wasm-bindgen-install.sh +.github/scripts/wasm-opt-install.sh yarn workspace @noir-lang/noirc_abi build diff --git a/.github/scripts/wasm-opt-install.sh b/.github/scripts/wasm-opt-install.sh index cbdeb8f2bfe..218778edac6 100755 --- a/.github/scripts/wasm-opt-install.sh +++ b/.github/scripts/wasm-opt-install.sh @@ -5,4 +5,4 @@ cd $(dirname "$0") ./cargo-binstall-install.sh -cargo-binstall wasm-opt --version 0.116.0 -y +cargo-binstall wasm-opt --version 0.116.0 -y --force diff --git a/.github/workflows/test-js-packages.yml b/.github/workflows/test-js-packages.yml index b3908ee5d3e..06a96ee8932 100644 --- a/.github/workflows/test-js-packages.yml +++ b/.github/workflows/test-js-packages.yml @@ -66,9 +66,6 @@ jobs: - name: Install Yarn dependencies uses: ./.github/actions/setup - - name: Install wasm-opt - run: ./.github/scripts/wasm-opt-install.sh - - name: Build noirc_abi run: ./.github/scripts/noirc-abi-build.sh @@ -76,7 +73,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: noirc_abi_wasm - path: ./tooling/noirc_abi_wasm/outputs/out/noirc_abi_wasm + path: | + ./tooling/noirc_abi_wasm/nodejs + ./tooling/noirc_abi_wasm/web retention-days: 10 @@ -100,9 +99,6 @@ jobs: - name: Install Yarn dependencies uses: ./.github/actions/setup - - name: Install wasm-opt - run: ./.github/scripts/wasm-opt-install.sh - - name: Build noir_js_types run: yarn workspace @noir-lang/types build @@ -138,9 +134,6 @@ jobs: - name: Install Yarn dependencies uses: ./.github/actions/setup - - name: Install wasm-opt - run: ./.github/scripts/wasm-opt-install.sh - - name: Build acvm_js run: ./.github/scripts/acvm_js-build.sh @@ -148,7 +141,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: acvm-js - path: ./acvm-repo/acvm_js/outputs/out/acvm_js + path: | + ./acvm-repo/acvm_js/nodejs + ./acvm-repo/acvm_js/web retention-days: 3 test-acvm_js-node: diff --git a/.gitignore b/.gitignore index 5f41566c94b..9a829afab8b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,12 +17,6 @@ pkg/ # Noir.js tooling/noir_js/lib -# Nix stuff -**/outputs -result -.envrc.local -.direnv/ - # Nargo output *.proof *.acir diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1fb1def7ce1..efb17cb0085 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,7 +4,6 @@ // List of extensions which should be recommended for users of this workspace. "recommendations": [ "mkhl.direnv", - "jnoortheen.nix-ide", "rust-lang.rust-analyzer", "redhat.vscode-yaml", "esbenp.prettier-vscode", diff --git a/.vscode/settings.json b/.vscode/settings.json index 171d36f4e04..fb8ea527881 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,17 +2,6 @@ "direnv.restart.automatic": true, "redhat.telemetry.enabled": false, "yaml.recommendations.show": false, - "nix.serverPath": "nil", - "nix.enableLanguageServer": true, - "nix.serverSettings": { - "nil": { - "formatting": { - "command": [ - "nixpkgs-fmt" - ] - } - } - }, "yaml.schemas": { "https://json.schemastore.org/github-workflow.json": "${workspaceRoot}/.github/workflows/*.yml" }, diff --git a/README.md b/README.md index 5c93512ae26..adf68b290ef 100644 --- a/README.md +++ b/README.md @@ -56,26 +56,6 @@ Concretely the following items are on the road map: This crate's minimum supported rustc version is 1.73.0. -## Working on this project - -This project uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. Please follow [our guidelines](https://noir-lang.org/docs/getting_started/installation/other_install_methods#option-3-compile-from-source) to setup your environment for working on the project. - -### Building against a different local/remote version of Barretenberg - -If you are working on this project and want a different version of Barretenberg (instead of the version this project is pinned against), you'll want to replace the lockfile version with your version. This can be done by running: - -```sh -nix flake lock --override-input barretenberg /absolute/path/to/your/barretenberg -``` - -You can also point at a fork and/or branch on GitHub using: - -```sh -nix flake lock --override-input barretenberg github:username/barretenberg/branch_name -``` - -__Note:__ You don't want to commit the updated lockfile, as it will fail in CI! - ## License Noir is free and open source. It is distributed under a dual license. (MIT/APACHE) diff --git a/acvm-repo/acvm_js/build.sh b/acvm-repo/acvm_js/build.sh index 24af149bcea..fe0b4dcbfff 100755 --- a/acvm-repo/acvm_js/build.sh +++ b/acvm-repo/acvm_js/build.sh @@ -6,13 +6,6 @@ function require_command { exit 1 fi } -function check_installed { - if ! command -v "$1" >/dev/null 2>&1; then - echo "$1 is not installed. Please install it." >&2 - return 1 - fi - return 0 -} function run_or_fail { "$@" local status=$? @@ -25,23 +18,29 @@ function run_or_fail { require_command jq require_command cargo require_command wasm-bindgen -check_installed wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") -export pname=$(cargo read-manifest | jq -r '.name') -export CARGO_TARGET_DIR=$self_path/target +pname=$(cargo read-manifest | jq -r '.name') -rm -rf $self_path/outputs >/dev/null 2>&1 -rm -rf $self_path/result >/dev/null 2>&1 +NODE_DIR=$self_path/nodejs +BROWSER_DIR=$self_path/web -if [ -n "$out" ]; then - echo "Will install package to $out (defined outside installPhase.sh script)" -else - export out="$self_path/outputs/out" - echo "Will install package to $out" +# Clear out the existing build artifacts as these aren't automatically removed by wasm-bindgen. +if [ -d ./pkg/ ]; then + rm -r $NODE_DIR + rm -r $BROWSER_DIR fi -run_or_fail $self_path/buildPhaseCargoCommand.sh -run_or_fail $self_path/installPhase.sh +TARGET=wasm32-unknown-unknown +WASM_BINARY=${self_path}/../../target/$TARGET/release/${pname}.wasm + +NODE_WASM=${NODE_DIR}/${pname}_bg.wasm +BROWSER_WASM=${BROWSER_DIR}/${pname}_bg.wasm -ln -s $out $self_path/result +# Build the new wasm package +run_or_fail cargo build --lib --release --target $TARGET --package ${pname} +run_or_fail wasm-bindgen $WASM_BINARY --out-dir $NODE_DIR --typescript --target nodejs +run_or_fail wasm-bindgen $WASM_BINARY --out-dir $BROWSER_DIR --typescript --target web +run_or_fail wasm-opt $NODE_WASM -o $NODE_WASM -O +run_or_fail wasm-opt $BROWSER_WASM -o $BROWSER_WASM -O diff --git a/acvm-repo/acvm_js/buildPhaseCargoCommand.sh b/acvm-repo/acvm_js/buildPhaseCargoCommand.sh deleted file mode 100755 index 6c710bc938f..00000000000 --- a/acvm-repo/acvm_js/buildPhaseCargoCommand.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -function run_or_fail { - "$@" - local status=$? - if [ $status -ne 0 ]; then - echo "Command '$*' failed with exit code $status" >&2 - exit $status - fi -} -function run_if_available { - if command -v "$1" >/dev/null 2>&1; then - "$@" - else - echo "$1 is not installed. Please install it to use this feature." >&2 - fi -} - -export self_path=$(dirname "$(readlink -f "$0")") - - -NODE_DIR=$self_path/nodejs/ -BROWSER_DIR=$self_path/web/ - -# Clear out the existing build artifacts as these aren't automatically removed by wasm-pack. -if [ -d ./pkg/ ]; then - rm -r $NODE_DIR - rm -r $BROWSER_DIR -fi - -TARGET=wasm32-unknown-unknown -WASM_BINARY=$CARGO_TARGET_DIR/$TARGET/release/${pname}.wasm - -NODE_WASM=${NODE_DIR}/${pname}_bg.wasm -BROWSER_WASM=${BROWSER_DIR}/${pname}_bg.wasm - -# Build the new wasm package -run_or_fail cargo build --lib --release --target $TARGET --package ${pname} -run_or_fail wasm-bindgen $WASM_BINARY --out-dir $NODE_DIR --typescript --target nodejs -run_or_fail wasm-bindgen $WASM_BINARY --out-dir $BROWSER_DIR --typescript --target web -run_if_available wasm-opt $NODE_WASM -o $NODE_WASM -O -run_if_available wasm-opt $BROWSER_WASM -o $BROWSER_WASM -O diff --git a/acvm-repo/acvm_js/installPhase.sh b/acvm-repo/acvm_js/installPhase.sh deleted file mode 100755 index 34ddb8155e1..00000000000 --- a/acvm-repo/acvm_js/installPhase.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -export self_path=$(dirname "$(readlink -f "$0")") - -export out_path=$out/acvm_js - -mkdir -p $out_path -cp $self_path/README.md $out_path/ -cp $self_path/package.json $out_path/ -cp -r $self_path/nodejs $out_path/ -cp -r $self_path/web $out_path/ diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index 55345a2ddf6..44d99f13c31 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -34,9 +34,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0", "publish": "echo 📡 publishing `$npm_package_name` && yarn npm publish", "nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json", - "clean": "chmod u+w web nodejs || true && rm -rf web nodejs", - "build:nix": "nix build -L .#acvm_js", - "install:from:nix": "yarn clean && yarn build:nix && cp -rL ./result/acvm_js/nodejs ./ && cp -rL ./result/acvm_js/web ./" + "clean": "chmod u+w web nodejs || true && rm -rf web nodejs" }, "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", diff --git a/compiler/noirc_driver/build.rs b/compiler/noirc_driver/build.rs index 73a56142075..2ed109398a4 100644 --- a/compiler/noirc_driver/build.rs +++ b/compiler/noirc_driver/build.rs @@ -2,8 +2,7 @@ const GIT_COMMIT: &&str = &"GIT_COMMIT"; use std::path::Path; fn main() { - // Only use build_data if the environment variable isn't set - // The environment variable is always set when working via Nix + // Only use build_data if the environment variable isn't set. if std::env::var(GIT_COMMIT).is_err() { build_data::set_GIT_COMMIT(); build_data::set_GIT_DIRTY(); diff --git a/compiler/wasm/README.md b/compiler/wasm/README.md index 52f7e83e19e..fd534722622 100644 --- a/compiler/wasm/README.md +++ b/compiler/wasm/README.md @@ -26,17 +26,3 @@ for (const path of files) { } const myCompiledCode = await compile(fm); ``` - -## Building from source - -Outside of the [noir repo](https://github.com/noir-lang/noir), this package can be built using the command below: - -```bash -nix build -L github:noir-lang/noir/master#noir_wasm -``` - -If you are within the noir repo and would like to build local changes, you can use: - -```bash -nix build -L #noir_wasm -``` diff --git a/default.nix b/default.nix deleted file mode 100644 index 9e230590a61..00000000000 --- a/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -let - lock = builtins.fromJSON (builtins.readFile ./flake.lock); - flakeCompatRev = lock.nodes.flake-compat.locked.rev; - flakeCompatHash = lock.nodes.flake-compat.locked.narHash; - flakeCompat = fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/${flakeCompatRev}.tar.gz"; - sha256 = flakeCompatHash; - }; - compat = import flakeCompat { - src = ./.; - }; -in -compat.defaultNix diff --git a/docs/docs/getting_started/installation/other_install_methods.md b/docs/docs/getting_started/installation/other_install_methods.md index a35e34aaf9c..3634723562b 100644 --- a/docs/docs/getting_started/installation/other_install_methods.md +++ b/docs/docs/getting_started/installation/other_install_methods.md @@ -1,6 +1,6 @@ --- -title: Alternative Install Methods -description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains other methods that don't rely on noirup, such as compiling from source, installing from binaries, and using WSL for windows +title: Alternative Installations +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. keywords: [ Installation Nargo @@ -12,10 +12,7 @@ keywords: [ Linux Nix Direnv - Shell & editor experience - Building and testing Uninstalling Nargo - Noir vs code extension, ] sidebar_position: 1 --- @@ -86,146 +83,7 @@ With `noirup`, you can easily switch between different Nargo versions, including noirup --path ./path/to/local/source ``` -## Alternate Installation Methods (No Longer Recommended) - -While the following methods are available, they are no longer recommended. We advise using noirup for a more efficient and flexible installation experience. - -However, there are other methods for installing Nargo: - -- [Binaries](#option-1-installing-from-binaries) -- [Compiling from Source](#option-2-compile-from-source) -- [WSL for Windows](#option-3-wsl-for-windows) - -### Option 1: Installing from Binaries - -See [GitHub Releases](https://github.com/noir-lang/noir/releases) for the latest and previous -platform specific binaries. - -#### Step 1 - -Paste and run the following in the terminal to extract and install the binary: - -> **macOS / Linux:** If you are prompted with `Permission denied` when running commands, prepend -> `sudo` and re-run it. - -##### macOS (Apple Silicon) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-aarch64-apple-darwin.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-aarch64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ -echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ -source ~/.zshrc -``` - -##### macOS (Intel) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-x86_64-apple-darwin.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-x86_64-apple-darwin.tar.gz -C $HOME/.nargo/bin/ && \ -echo '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.zshrc && \ -source ~/.zshrc -``` - -##### Linux (Bash) - -```bash -mkdir -p $HOME/.nargo/bin && \ -curl -o $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -L https://github.com/noir-lang/noir/releases/download/v0.24.0/nargo-x86_64-unknown-linux-gnu.tar.gz && \ -tar -xvf $HOME/.nargo/bin/nargo-x86_64-unknown-linux-gnu.tar.gz -C $HOME/.nargo/bin/ && \ -echo -e '\nexport PATH=$PATH:$HOME/.nargo/bin' >> ~/.bashrc && \ -source ~/.bashrc -``` - -#### Step 2 - -Check if the installation was successful by running `nargo --version`. You should get a version number. - -> **macOS:** If you are prompted with an OS alert, right-click and open the _nargo_ executable from -> Finder. Close the new terminal popped up and `nargo` should now be accessible. - -### Option 2: Compile from Source - -Due to the large number of native dependencies, Noir projects uses [Nix](https://nixos.org/) and [direnv](https://direnv.net/) to streamline the development experience. It helps mitigating issues commonly associated with dependency management, such as conflicts between required package versions for different projects (often referred to as "dependency hell"). - -Combined with direnv, which automatically sets or clears environment variables based on the directory, it further simplifies the development process by seamlessly integrating with the developer's shell, facilitating an efficient and reliable workflow for managing and deploying Noir projects with multiple dependencies. - -#### Setting up your environment - -For the best experience, please follow these instructions to setup your environment: - -1. Install Nix following [their guide](https://nixos.org/download.html) for your operating system. -2. Create the file `~/.config/nix/nix.conf` with the contents: - -```ini -experimental-features = nix-command -extra-experimental-features = flakes -``` - -3. Install direnv into your Nix profile by running: - -```sh -nix profile install nixpkgs#direnv -``` - -4. Add direnv to your shell following [their guide](https://direnv.net/docs/hook.html). - 1. For bash or zshell, add `eval "$(direnv hook bash)"` or `eval "$(direnv hook zsh)"` to your ~/.bashrc or ~/.zshrc file, respectively. -5. Restart your shell. - -#### Shell & editor experience - -Now that your environment is set up, you can get to work on the project. - -1. Clone the repository, such as: - -```sh -git clone git@github.com:noir-lang/noir -``` - -> Replacing `noir` with whichever repository you want to work on. - -2. Navigate to the directory: - -```sh -cd noir -``` - -> Replacing `noir` with whichever repository you cloned. - -3. You should see a **direnv error** because projects aren't allowed by default. Make sure you've reviewed and trust our `.envrc` file, then you need to run: - -```sh -direnv allow -``` - -4. Now, wait awhile for all the native dependencies to be built. This will take some time and direnv will warn you that it is taking a long time, but we just need to let it run. - -5. Once you are presented with your prompt again, you can start your editor within the project directory (we recommend [VSCode](https://code.visualstudio.com/)): - -```sh -code . -``` - -6. (Recommended) When launching VSCode for the first time, you should be prompted to install our recommended plugins. We highly recommend installing these for the best development experience. - -#### Building and testing - -Assuming you are using `direnv` to populate your environment, building and testing the project can be done -with the typical `cargo build`, `cargo test`, and `cargo clippy` commands. You'll notice that the `cargo` version matches the version we specify in `rust-toolchain.toml`, which is 1.73.0 at the time of this writing. - -If you want to build the entire project in an isolated sandbox, you can use Nix commands: - -1. `nix build .` (or `nix build . -L` for verbose output) to build the project in a Nix sandbox. -2. `nix flake check` (or `nix flake check -L` for verbose output) to run clippy and tests in a Nix sandbox. - -#### Without `direnv` - -If you have hesitations with using direnv, you can launch a subshell with `nix develop` and then launch your editor from within the subshell. However, if VSCode was already launched in the project directory, the environment won't be updated. - -Advanced: If you aren't using direnv nor launching your editor within the subshell, you can try to install Barretenberg and other global dependencies the package needs. This is an advanced workflow and likely won't receive support! - -### Option 3: WSL (for Windows) +## Installation on Windows The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). @@ -235,20 +93,10 @@ step 2: Follow the [Noirup instructions](#encouraged-installation-method-noirup) ## Uninstalling Nargo -### Noirup - -If you installed Nargo with `noirup` or through directly downloading binaries, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. ```bash rm -r ~/.nargo rm -r ~/nargo rm -r ~/noir_cache ``` - -### Nix - -If you installed Nargo with Nix or compiled it from source, you can remove the binary located at `~/.nix-profile/bin/nargo`. - -```bash -rm ~/.nix-profile/bin/nargo -``` diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 1cb421a49ef..00000000000 --- a/flake.nix +++ /dev/null @@ -1,260 +0,0 @@ -{ - description = "Build the Noir programming language"; - - # All of these inputs (a.k.a. dependencies) need to align with inputs we - # use so they use the `inputs.*.follows` syntax to reference our inputs - inputs = { - nixpkgs = { - url = "github:NixOS/nixpkgs/nixos-23.05"; - }; - - flake-utils = { - url = "github:numtide/flake-utils"; - }; - - flake-compat = { - url = "github:edolstra/flake-compat"; - flake = false; - }; - - fenix = { - url = "github:nix-community/fenix"; - inputs = { - nixpkgs.follows = "nixpkgs"; - }; - }; - - crane = { - url = "github:ipetkov/crane"; - inputs = { - nixpkgs.follows = "nixpkgs"; - flake-utils.follows = "flake-utils"; - flake-compat.follows = "flake-compat"; - }; - }; - }; - - outputs = - { self, nixpkgs, crane, flake-utils, fenix, ... }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { - inherit system; - }; - - rustToolchain = fenix.packages.${system}.fromToolchainFile { - file = ./rust-toolchain.toml; - sha256 = "sha256-rLP8+fTxnPHoR96ZJiCa/5Ans1OojI7MLsmSqR2ip8o="; - }; - - craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; - - # The `self.rev` property is only available when the working tree is not dirty - GIT_COMMIT = if (self ? rev) then self.rev else "unknown"; - GIT_DIRTY = if (self ? rev) then "false" else "true"; - - extraBuildInputs = [ ] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [ - # Need libiconv and apple Security on Darwin. See https://github.com/ipetkov/crane/issues/156 - pkgs.libiconv - pkgs.darwin.apple_sdk.frameworks.Security - ]; - - environment = { - # We enable backtraces on any failure for help with debugging - RUST_BACKTRACE = "1"; - - # We download the Wasm version of `acvm_backend` in the barretenberg releases for the ACVM `blackbox_solver` - BARRETENBERG_ARCHIVE = pkgs.fetchurl { - url = "https://github.com/AztecProtocol/barretenberg/releases/download/barretenberg-v0.4.5/acvm_backend.wasm.tar.gz"; - sha256 = "sha256-xONt5pTKWf/YbVnX/NXl/VNBbtKd+CP7CLkB1jf0RHw="; - }; - }; - - # Configuration shared between builds - config = { - # x-release-please-start-version - version = "0.26.0"; - # x-release-please-end - - src = pkgs.lib.cleanSourceWith { - src = craneLib.path ./.; - # Custom filter with various file extensions that we rely upon to build packages - # Currently: `.nr`, `.sol`, `.sh`, `.json`, `.md` and `.wasm` - filter = path: type: - (builtins.match ".*\.(nr|sol|sh|json|md|wasm|txt)$" path != null) || (craneLib.filterCargoSources path type); - }; - - # TODO(#1198): It'd be nice to include these flags when running `cargo clippy` in a devShell. - cargoClippyExtraArgs = "--all-targets -- -D warnings"; - - # TODO(#1198): It'd be nice to include this flag when running `cargo test` in a devShell. - cargoTestExtraArgs = "--workspace"; - }; - - # Combine the environment and other configuration needed for Crane to build our Rust packages - nativeConfig = environment // config // { - nativeBuildInputs = [ ]; - - buildInputs = [ ] ++ extraBuildInputs; - }; - - # Combine the environmnet and other configuration needed for Crane to build our Wasm packages - wasmConfig = environment // config // { - CARGO_TARGET_DIR = "./target"; - - nativeBuildInputs = with pkgs; [ - which - git - jq - rustToolchain - wasm-bindgen-cli - binaryen - ]; - - buildInputs = [ ] ++ extraBuildInputs; - }; - - # Build *just* the cargo dependencies, so we can reuse all of that work between runs - native-cargo-artifacts = craneLib.buildDepsOnly (nativeConfig // { - pname = "nargo"; - }); - noirc-abi-wasm-cargo-artifacts = craneLib.buildDepsOnly (wasmConfig // { - pname = "noirc_abi_wasm"; - }); - acvm-js-cargo-artifacts = craneLib.buildDepsOnly (wasmConfig // { - pname = "acvm_js"; - }); - - nargo = craneLib.buildPackage (nativeConfig // { - pname = "nargo"; - - inherit GIT_COMMIT GIT_DIRTY; - - cargoArtifacts = native-cargo-artifacts; - - # We don't want to run tests because they don't work in the Nix sandbox - doCheck = false; - }); - - noirc_abi_wasm = craneLib.buildPackage (wasmConfig // rec { - pname = "noirc_abi_wasm"; - - inherit GIT_COMMIT GIT_DIRTY; - - cargoArtifacts = noirc-abi-wasm-cargo-artifacts; - - cargoExtraArgs = "--package ${pname} --target wasm32-unknown-unknown"; - - buildPhaseCargoCommand = '' - bash tooling/noirc_abi_wasm/buildPhaseCargoCommand.sh release - ''; - - installPhase = '' - bash tooling/noirc_abi_wasm/installPhase.sh - ''; - - # We don't want to run tests because they don't work in the Nix sandbox - doCheck = false; - }); - - acvm_js = craneLib.buildPackage (wasmConfig // rec { - pname = "acvm_js"; - - inherit GIT_COMMIT GIT_DIRTY; - - cargoArtifacts = acvm-js-cargo-artifacts; - - cargoExtraArgs = "--package ${pname} --target wasm32-unknown-unknown"; - - buildPhaseCargoCommand = '' - bash acvm-repo/acvm_js/buildPhaseCargoCommand.sh release - ''; - - installPhase = '' - bash acvm-repo/acvm_js/installPhase.sh - ''; - - # We don't want to run tests because they don't work in the Nix sandbox - doCheck = false; - }); - - wasm-bindgen-cli = pkgs.callPackage ./wasm-bindgen-cli.nix { - rustPlatform = pkgs.makeRustPlatform { - rustc = rustToolchain; - cargo = rustToolchain; - }; - }; - in - { - # We use `checks` to run `cargo clippy` and `cargo fmt` since we disable checks in the primary derivations - checks = { - cargo-clippy = craneLib.cargoClippy (nativeConfig // { - pname = "noir"; - - inherit GIT_COMMIT GIT_DIRTY; - - cargoArtifacts = native-cargo-artifacts; - }); - - cargo-fmt = craneLib.cargoFmt (nativeConfig // { - pname = "noir"; - - inherit GIT_COMMIT GIT_DIRTY; - - cargoArtifacts = native-cargo-artifacts; - }); - }; - - packages = { - default = nargo; - - # Nix flakes cannot build more than one derivation in one command (see https://github.com/NixOS/nix/issues/5591) - # so we use `symlinkJoin` to build everything as the "all" package. - all = pkgs.symlinkJoin { name = "all"; paths = [ nargo noirc_abi_wasm acvm_js ]; }; - all_wasm = pkgs.symlinkJoin { name = "all_wasm"; paths = [ noirc_abi_wasm acvm_js ]; }; - - # We also export individual packages to enable `nix build .#nargo -L`, etc. - inherit nargo; - inherit noirc_abi_wasm; - inherit acvm_js; - - # We expose the `*-cargo-artifacts` derivations so we can cache our cargo dependencies in CI - inherit native-cargo-artifacts; - inherit noirc-abi-wasm-cargo-artifacts; - inherit acvm-js-cargo-artifacts; - }; - - # Setup the environment to match the environment settings, the inputs from our checks derivations, - # and extra tooling via `nativeBuildInputs` - devShells.default = pkgs.mkShell (environment // { - inputsFrom = [ - nargo - noirc_abi_wasm - acvm_js - ]; - - # Additional tools that weren't included as `nativeBuildInputs` of any of the derivations in `inputsFrom` - nativeBuildInputs = with pkgs; [ - # Rust toolchain - rustToolchain - # Other tools - starship - yarn - nodejs-18_x - # Used by the `bb` binary - curl - gzip - # This ensures the right lldb is in the environment for running rust-lldb - llvmPackages.lldb - # Nix tools - nil - nixpkgs-fmt - ]; - - shellHook = '' - eval "$(starship init bash)" - ''; - }); - }); -} - diff --git a/package.json b/package.json index 049c1634dd2..30cac607010 100644 --- a/package.json +++ b/package.json @@ -17,19 +17,11 @@ "test": "yarn workspaces foreach --parallel --verbose run test", "test:integration": "yarn workspace integration-tests test", "clean:workspaces": "yarn workspaces foreach --exclude @noir-lang/root run clean", - "clean:root": "rm -rf ./result ./target ./packages", + "clean:root": "rm -rf ./target ./packages", "clean": "yarn clean:workspaces && yarn clean:root", "lint": "yarn workspaces foreach --verbose run lint", "spellcheck": "cspell '**/*.{md,rs}' -c ./cspell.json", - "install:acvm_js": "yarn workspace @noir-lang/acvm_js run install:from:nix", - "install:noir_wasm": "yarn workspace @noir-lang/noir_wasm run install:from:nix", - "install:noirc_abi_wasm": "yarn workspace @noir-lang/noirc_abi run install:from:nix", - "install:from:nix": "yarn install:acvm_js && yarn install:noir_wasm && yarn install:noirc_abi_wasm", - "build:types": "yarn workspace @noir-lang/types run build", - "build:backend_barretenberg": "yarn workspace @noir-lang/backend_barretenberg run build", - "build:noir_js": "yarn workspace @noir-lang/noir_js run build", - "build:js:only": "yarn workspaces foreach -vtp --from \"{@noir-lang/types,@noir-lang/backend_barretenberg,@noir-lang/noir_js,@noir-lang/noir_codegen}\" run build", - "prepare:publish": "yarn clean && yarn install:from:nix && yarn build:js:only", + "prepare:publish": "yarn clean && yarn build", "nightly:version": "yarn workspaces foreach run nightly:version", "publish:all": "yarn install && yarn workspaces foreach run publish" }, diff --git a/release-please-config.json b/release-please-config.json index 217a86303a1..0ba192754a0 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -13,7 +13,6 @@ "include-component-in-tag": false, "extra-files": [ "Cargo.toml", - "flake.nix", { "type": "json", "path": "compiler/wasm/package.json", @@ -82,4 +81,4 @@ "sentence-case" ], "bootstrap-sha": "690cfc0468de0b9aee53ccfe832c71c16e61e5fc" -} \ No newline at end of file +} diff --git a/shell.nix b/shell.nix deleted file mode 100644 index b72d4a4697b..00000000000 --- a/shell.nix +++ /dev/null @@ -1,13 +0,0 @@ -let - lock = builtins.fromJSON (builtins.readFile ./flake.lock); - flakeCompatRev = lock.nodes.flake-compat.locked.rev; - flakeCompatHash = lock.nodes.flake-compat.locked.narHash; - flakeCompat = fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/${flakeCompatRev}.tar.gz"; - sha256 = flakeCompatHash; - }; - compat = import flakeCompat { - src = ./.; - }; -in -compat.shellNix diff --git a/tooling/debugger/build.rs b/tooling/debugger/build.rs index 26a8bc64b0e..ebdf2036894 100644 --- a/tooling/debugger/build.rs +++ b/tooling/debugger/build.rs @@ -7,8 +7,7 @@ use std::{env, fs}; const GIT_COMMIT: &&str = &"GIT_COMMIT"; fn main() { - // Only use build_data if the environment variable isn't set - // The environment variable is always set when working via Nix + // Only use build_data if the environment variable isn't set. if std::env::var(GIT_COMMIT).is_err() { build_data::set_GIT_COMMIT(); build_data::set_GIT_DIRTY(); diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index a796eeac326..16df29e6985 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -16,8 +16,7 @@ const GIT_COMMIT: &&str = &"GIT_COMMIT"; fn main() { check_rustc_version(); - // Only use build_data if the environment variable isn't set - // The environment variable is always set when working via Nix + // Only use build_data if the environment variable isn't set. if std::env::var(GIT_COMMIT).is_err() { build_data::set_GIT_COMMIT(); build_data::set_GIT_DIRTY(); diff --git a/tooling/noirc_abi_wasm/README.md b/tooling/noirc_abi_wasm/README.md index 77bc1f5fae2..2b0cf9b74d4 100644 --- a/tooling/noirc_abi_wasm/README.md +++ b/tooling/noirc_abi_wasm/README.md @@ -1,17 +1,3 @@ # Noir Lang ABI JavaScript Package This JavaScript package enables users to ABI encode inputs to a Noir program, i.e. generating an initial witness. - -## Building from source - -Outside of the [noir repo](https://github.com/noir-lang/noir), this package can be built using the command below: - -```bash -nix build -L github:noir-lang/noir/master#abi_wasm -``` - -If you are within the noir repo and would like to build local changes, you can use: - -```bash -nix build -L #abi_wasm -``` diff --git a/tooling/noirc_abi_wasm/build.rs b/tooling/noirc_abi_wasm/build.rs index 3b96be74ef3..7a6eb861de2 100644 --- a/tooling/noirc_abi_wasm/build.rs +++ b/tooling/noirc_abi_wasm/build.rs @@ -1,8 +1,7 @@ const GIT_COMMIT: &&str = &"GIT_COMMIT"; fn main() { - // Only use build_data if the environment variable isn't set - // The environment variable is always set when working via Nix + // Only use build_data if the environment variable isn't set. if std::env::var(GIT_COMMIT).is_err() { build_data::set_GIT_COMMIT(); build_data::set_GIT_DIRTY(); diff --git a/tooling/noirc_abi_wasm/build.sh b/tooling/noirc_abi_wasm/build.sh index 24af149bcea..fe0b4dcbfff 100755 --- a/tooling/noirc_abi_wasm/build.sh +++ b/tooling/noirc_abi_wasm/build.sh @@ -6,13 +6,6 @@ function require_command { exit 1 fi } -function check_installed { - if ! command -v "$1" >/dev/null 2>&1; then - echo "$1 is not installed. Please install it." >&2 - return 1 - fi - return 0 -} function run_or_fail { "$@" local status=$? @@ -25,23 +18,29 @@ function run_or_fail { require_command jq require_command cargo require_command wasm-bindgen -check_installed wasm-opt +require_command wasm-opt self_path=$(dirname "$(readlink -f "$0")") -export pname=$(cargo read-manifest | jq -r '.name') -export CARGO_TARGET_DIR=$self_path/target +pname=$(cargo read-manifest | jq -r '.name') -rm -rf $self_path/outputs >/dev/null 2>&1 -rm -rf $self_path/result >/dev/null 2>&1 +NODE_DIR=$self_path/nodejs +BROWSER_DIR=$self_path/web -if [ -n "$out" ]; then - echo "Will install package to $out (defined outside installPhase.sh script)" -else - export out="$self_path/outputs/out" - echo "Will install package to $out" +# Clear out the existing build artifacts as these aren't automatically removed by wasm-bindgen. +if [ -d ./pkg/ ]; then + rm -r $NODE_DIR + rm -r $BROWSER_DIR fi -run_or_fail $self_path/buildPhaseCargoCommand.sh -run_or_fail $self_path/installPhase.sh +TARGET=wasm32-unknown-unknown +WASM_BINARY=${self_path}/../../target/$TARGET/release/${pname}.wasm + +NODE_WASM=${NODE_DIR}/${pname}_bg.wasm +BROWSER_WASM=${BROWSER_DIR}/${pname}_bg.wasm -ln -s $out $self_path/result +# Build the new wasm package +run_or_fail cargo build --lib --release --target $TARGET --package ${pname} +run_or_fail wasm-bindgen $WASM_BINARY --out-dir $NODE_DIR --typescript --target nodejs +run_or_fail wasm-bindgen $WASM_BINARY --out-dir $BROWSER_DIR --typescript --target web +run_or_fail wasm-opt $NODE_WASM -o $NODE_WASM -O +run_or_fail wasm-opt $BROWSER_WASM -o $BROWSER_WASM -O diff --git a/tooling/noirc_abi_wasm/buildPhaseCargoCommand.sh b/tooling/noirc_abi_wasm/buildPhaseCargoCommand.sh deleted file mode 100755 index 1188d00953e..00000000000 --- a/tooling/noirc_abi_wasm/buildPhaseCargoCommand.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -function run_or_fail { - "$@" - local status=$? - if [ $status -ne 0 ]; then - echo "Command '$*' failed with exit code $status" >&2 - exit $status - fi -} -function run_if_available { - if command -v "$1" >/dev/null 2>&1; then - "$@" - else - echo "$1 is not installed. Please install it to use this feature." >&2 - fi -} - -export self_path=$(dirname "$(readlink -f "$0")") - -# Clear out the existing build artifacts as these aren't automatically removed by wasm-pack. -if [ -d ./pkg/ ]; then - rm -rf $self_path/pkg/ -fi - -TARGET=wasm32-unknown-unknown -WASM_BINARY=$CARGO_TARGET_DIR/$TARGET/release/${pname}.wasm - -NODE_DIR=$self_path/nodejs/ -BROWSER_DIR=$self_path/web/ -NODE_WASM=${NODE_DIR}/${pname}_bg.wasm -BROWSER_WASM=${BROWSER_DIR}/${pname}_bg.wasm - -# Build the new wasm package -run_or_fail cargo build --lib --release --target $TARGET --package ${pname} -run_or_fail wasm-bindgen $WASM_BINARY --out-dir $NODE_DIR --typescript --target nodejs -run_or_fail wasm-bindgen $WASM_BINARY --out-dir $BROWSER_DIR --typescript --target web -run_if_available wasm-opt $NODE_WASM -o $NODE_WASM -O -run_if_available wasm-opt $BROWSER_WASM -o $BROWSER_WASM -O \ No newline at end of file diff --git a/tooling/noirc_abi_wasm/installPhase.sh b/tooling/noirc_abi_wasm/installPhase.sh deleted file mode 100755 index d9b94f2d171..00000000000 --- a/tooling/noirc_abi_wasm/installPhase.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -export self_path=$(dirname "$(readlink -f "$0")") - -export out_path=$out/noirc_abi_wasm - -mkdir -p $out_path -cp $self_path/README.md $out_path/ -cp $self_path/package.json $out_path/ -cp -r $self_path/nodejs $out_path/ -cp -r $self_path/web $out_path/ diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index e93a8e6a5e3..14e528c3b15 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -33,9 +33,7 @@ "clean": "chmod u+w web nodejs || true && rm -rf ./nodejs ./web ./target ./result", "nightly:version": "jq --arg new_version \"-$(git rev-parse --short HEAD)$1\" '.version = .version + $new_version' package.json > package-tmp.json && mv package-tmp.json package.json", "publish": "echo 📡 publishing `$npm_package_name` && yarn npm publish", - "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0", - "build:nix": "nix build -L .#noirc_abi_wasm", - "install:from:nix": "yarn clean && yarn build:nix && cp -rL ./result/noirc_abi_wasm/nodejs ./ && cp -rL ./result/noirc_abi_wasm/web ./" + "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { "@noir-lang/types": "workspace:*" diff --git a/wasm-bindgen-cli.nix b/wasm-bindgen-cli.nix deleted file mode 100644 index 7c3910f032e..00000000000 --- a/wasm-bindgen-cli.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ lib -, rustPlatform -, fetchCrate -, nodejs -, pkg-config -, openssl -, stdenv -, curl -, darwin -, libiconv -, runCommand -}: - -rustPlatform.buildRustPackage rec { - pname = "wasm-bindgen-cli"; - version = "0.2.86"; - - src = fetchCrate { - inherit pname version; - sha256 = "sha256-56EOiLbdgAcoTrkyvB3t9TjtLaRvGxFUXx4haLwE2QY="; - }; - - cargoSha256 = "sha256-4CPBmz92PuPN6KeGDTdYPAf5+vTFk9EN5Cmx4QJy6yI="; - - nativeBuildInputs = [ pkg-config ]; - - buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin [ - curl - # Need libiconv and apple Security on Darwin. See https://github.com/ipetkov/crane/issues/156 - libiconv - darwin.apple_sdk.frameworks.Security - ]; - - doCheck = false; - - meta = with lib; { - homepage = "https://rustwasm.github.io/docs/wasm-bindgen/"; - license = with licenses; [ asl20 /* or */ mit ]; - description = "Facilitating high-level interactions between wasm modules and JavaScript"; - maintainers = with maintainers; [ nitsky rizary ]; - mainProgram = "wasm-bindgen"; - }; -} From 96b811079b0e7c0345210cfc705c00345b0b3334 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 2 Apr 2024 14:49:25 +0100 Subject: [PATCH 132/416] feat: improve optimisations on range constraints (#4690) # Description ## Problem\* Resolves ## Summary\* We're currently not making full use of type information when optimizing out range constraints in SSA. I don't think makes a huge difference as they should be removed in ACIR gen but it's good to catch these early. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../noirc_evaluator/src/ssa/ir/instruction.rs | 10 +++++----- .../src/ssa/ir/instruction/call.rs | 15 ++++++++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index dd190c112f3..a887b279970 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -573,12 +573,12 @@ impl Instruction { Instruction::IncrementRc { .. } => None, Instruction::DecrementRc { .. } => None, Instruction::RangeCheck { value, max_bit_size, .. } => { - if let Some(numeric_constant) = dfg.get_numeric_constant(*value) { - if numeric_constant.num_bits() < *max_bit_size { - return Remove; - } + let max_potential_bits = dfg.get_value_max_num_bits(*value); + if max_potential_bits < *max_bit_size { + Remove + } else { + None } - None } } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 5b268de239d..a2847146087 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -240,11 +240,16 @@ pub(super) fn simplify_call( let max_bit_size = dfg.get_numeric_constant(arguments[1]); if let Some(max_bit_size) = max_bit_size { let max_bit_size = max_bit_size.to_u128() as u32; - SimplifyResult::SimplifiedToInstruction(Instruction::RangeCheck { - value, - max_bit_size, - assert_message: Some("call to assert_max_bit_size".to_owned()), - }) + let max_potential_bits = dfg.get_value_max_num_bits(value); + if max_potential_bits < max_bit_size { + SimplifyResult::Remove + } else { + SimplifyResult::SimplifiedToInstruction(Instruction::RangeCheck { + value, + max_bit_size, + assert_message: Some("call to assert_max_bit_size".to_owned()), + }) + } } else { SimplifyResult::None } From 669f1a0fa47ad9093888a8ce8e525cb02bcf19b5 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 2 Apr 2024 15:20:34 +0100 Subject: [PATCH 133/416] feat: improve SSA type-awareness in EQ and MUL instructions (#4691) # Description ## Problem\* Resolves ## Summary\* This PR adds some improvements for SSA generation for programs such as in #4688 where we now make more use of information on casted values. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- .../src/ssa/ir/instruction/binary.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 36f3ae8620b..e491807995b 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -130,6 +130,12 @@ impl Binary { let zero = dfg.make_constant(FieldElement::zero(), operand_type); return SimplifyResult::SimplifiedTo(zero); } + if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) + && dfg.get_value_max_num_bits(self.lhs) == 1 + { + // Squaring a boolean value is a noop. + return SimplifyResult::SimplifiedTo(self.lhs); + } } BinaryOp::Div => { if rhs_is_one { @@ -164,6 +170,22 @@ impl Binary { let one = dfg.make_constant(FieldElement::one(), Type::bool()); return SimplifyResult::SimplifiedTo(one); } + + if operand_type.is_unsigned() { + // If we're comparing a variable against a constant value which lies outside of the range of + // values which the variable's type can take, we can assume that the equality will be false. + let constant = lhs.or(rhs); + let non_constant = if lhs.is_some() { self.rhs } else { self.lhs }; + if let Some(constant) = constant { + let max_possible_value = + 2u128.pow(dfg.get_value_max_num_bits(non_constant)) - 1; + if constant > max_possible_value.into() { + let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + return SimplifyResult::SimplifiedTo(zero); + } + } + } + if operand_type == Type::bool() { // Simplify forms of `(boolean == true)` into `boolean` if lhs_is_one { From 0d3d5fda9659a563ba9c2014b7c1af9e1d332ab0 Mon Sep 17 00:00:00 2001 From: jfecher Date: Tue, 2 Apr 2024 09:44:05 -0500 Subject: [PATCH 134/416] fix: Last use analysis & make it an SSA pass (#4686) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4685 ## Summary\* Fixes some issues with the last_use analysis and makes it into a dedicated SSA pass (after all other passes, only on 1 block acir functions). ## Additional Context One case I noticed where we don't make array sets mutable currently is in parallel if branches: ```rs if array[0] == 1 { array[1] = 10; } else { array[1] = 20; } ``` Will produce the IR: ```rs v137 = array_get v0, index u64 0 v138 = eq v137, Field 1 enable_side_effects v138 v139 = array_set v0, index u64 1, value Field 10 v140 = not v138 enable_side_effects v140 v141 = array_set mut v0, index u64 1, value Field 20 enable_side_effects u1 1 ``` Note that with this change we can see the second array_set is mutable and not the first. Fixing this isn't in the scope of this PR but this could be a good future optimization. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa.rs | 7 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 66 ++++------- .../src/ssa/function_builder/mod.rs | 4 +- .../noirc_evaluator/src/ssa/ir/instruction.rs | 16 ++- .../src/ssa/ir/instruction/call.rs | 9 +- .../noirc_evaluator/src/ssa/ir/printer.rs | 14 +-- .../noirc_evaluator/src/ssa/opt/array_set.rs | 104 ++++++++++++++++++ .../noirc_evaluator/src/ssa/opt/array_use.rs | 57 ---------- compiler/noirc_evaluator/src/ssa/opt/mod.rs | 2 +- 9 files changed, 154 insertions(+), 125 deletions(-) create mode 100644 compiler/noirc_evaluator/src/ssa/opt/array_set.rs delete mode 100644 compiler/noirc_evaluator/src/ssa/opt/array_use.rs diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 52377d36b36..a45cf3af608 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -63,17 +63,14 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::fold_constants, "After Constant Folding:") .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") + .run_pass(Ssa::array_set_optimization, "After Array Set Optimizations:") .finish(); let brillig = time("SSA to Brillig", print_timings, || ssa.to_brillig(print_brillig_trace)); drop(ssa_gen_span_guard); - let last_array_uses = ssa.find_last_array_uses(); - - time("SSA to ACIR", print_timings, || { - ssa.into_acir(&brillig, abi_distinctness, &last_array_uses) - }) + time("SSA to ACIR", print_timings, || ssa.into_acir(&brillig, abi_distinctness)) } // Helper to time SSA passes diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 96f959612af..46b0318d5de 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -181,15 +181,12 @@ impl Ssa { self, brillig: &Brillig, abi_distinctness: Distinctness, - last_array_uses: &HashMap, ) -> Result, RuntimeError> { let mut acirs = Vec::new(); // TODO: can we parallelise this? for function in self.functions.values() { let context = Context::new(); - if let Some(generated_acir) = - context.convert_ssa_function(&self, function, brillig, last_array_uses)? - { + if let Some(generated_acir) = context.convert_ssa_function(&self, function, brillig)? { acirs.push(generated_acir); } } @@ -245,7 +242,6 @@ impl Context { ssa: &Ssa, function: &Function, brillig: &Brillig, - last_array_uses: &HashMap, ) -> Result, RuntimeError> { match function.runtime() { RuntimeType::Acir(inline_type) => { @@ -258,7 +254,7 @@ impl Context { } } // We only want to convert entry point functions. This being `main` and those marked with `#[fold]` - Ok(Some(self.convert_acir_main(function, ssa, brillig, last_array_uses)?)) + Ok(Some(self.convert_acir_main(function, ssa, brillig)?)) } RuntimeType::Brillig => { if function.id() == ssa.main_id { @@ -275,7 +271,6 @@ impl Context { main_func: &Function, ssa: &Ssa, brillig: &Brillig, - last_array_uses: &HashMap, ) -> Result { let dfg = &main_func.dfg; let entry_block = &dfg[main_func.entry_block()]; @@ -284,13 +279,7 @@ impl Context { self.data_bus = dfg.data_bus.to_owned(); let mut warnings = Vec::new(); for instruction_id in entry_block.instructions() { - warnings.extend(self.convert_ssa_instruction( - *instruction_id, - dfg, - ssa, - brillig, - last_array_uses, - )?); + warnings.extend(self.convert_ssa_instruction(*instruction_id, dfg, ssa, brillig)?); } warnings.extend(self.convert_ssa_return(entry_block.unwrap_terminator(), dfg)?); @@ -463,7 +452,6 @@ impl Context { dfg: &DataFlowGraph, ssa: &Ssa, brillig: &Brillig, - last_array_uses: &HashMap, ) -> Result, RuntimeError> { let instruction = &dfg[instruction_id]; self.acir_context.set_call_stack(dfg.get_call_stack(instruction_id)); @@ -523,7 +511,7 @@ impl Context { self.current_side_effects_enabled_var = acir_var; } Instruction::ArrayGet { .. } | Instruction::ArraySet { .. } => { - self.handle_array_operation(instruction_id, dfg, last_array_uses)?; + self.handle_array_operation(instruction_id, dfg)?; } Instruction::Allocate => { unreachable!("Expected all allocate instructions to be removed before acir_gen") @@ -721,12 +709,16 @@ impl Context { &mut self, instruction: InstructionId, dfg: &DataFlowGraph, - last_array_uses: &HashMap, ) -> Result<(), RuntimeError> { + let mut mutable_array_set = false; + // Pass the instruction between array methods rather than the internal fields themselves let (array, index, store_value) = match dfg[instruction] { Instruction::ArrayGet { array, index } => (array, index, None), - Instruction::ArraySet { array, index, value, .. } => (array, index, Some(value)), + Instruction::ArraySet { array, index, value, mutable } => { + mutable_array_set = mutable; + (array, index, Some(value)) + } _ => { return Err(InternalError::Unexpected { expected: "Instruction should be an ArrayGet or ArraySet".to_owned(), @@ -744,11 +736,8 @@ impl Context { let (new_index, new_value) = self.convert_array_operation_inputs(array, dfg, index, store_value)?; - let resolved_array = dfg.resolve(array); - let map_array = last_array_uses.get(&resolved_array) == Some(&instruction); - if let Some(new_value) = new_value { - self.array_set(instruction, new_index, new_value, dfg, map_array)?; + self.array_set(instruction, new_index, new_value, dfg, mutable_array_set)?; } else { self.array_get(instruction, array, new_index, dfg)?; } @@ -1028,16 +1017,18 @@ impl Context { } } - /// Copy the array and generates a write opcode on the new array - /// - /// Note: Copying the array is inefficient and is not the way we want to do it in the end. + /// If `mutate_array` is: + /// - true: Mutate the array directly + /// - false: Copy the array and generates a write opcode on the new array. This is + /// generally very inefficient and should be avoided if possible. Currently + /// this is controlled by SSA's array set optimization pass. fn array_set( &mut self, instruction: InstructionId, mut var_index: AcirVar, store_value: AcirValue, dfg: &DataFlowGraph, - map_array: bool, + mutate_array: bool, ) -> Result<(), RuntimeError> { // Pass the instruction between array methods rather than the internal fields themselves let array = match dfg[instruction] { @@ -1075,7 +1066,7 @@ impl Context { .first() .expect("Array set does not have one result"); let result_block_id; - if map_array { + if mutate_array { self.memory_blocks.insert(*result_id, block_id); result_block_id = block_id; } else { @@ -2401,14 +2392,13 @@ mod test { ssa::{ function_builder::FunctionBuilder, ir::{ - function::{FunctionId, InlineType, RuntimeType}, + function::{FunctionId, InlineType}, instruction::BinaryOp, map::Id, types::Type, }, }, }; - use fxhash::FxHashMap as HashMap; fn build_basic_foo_with_return(builder: &mut FunctionBuilder, foo_id: FunctionId) { // acir(fold) fn foo f1 { @@ -2461,11 +2451,7 @@ mod test { let ssa = builder.finish(); let acir_functions = ssa - .into_acir( - &Brillig::default(), - noirc_frontend::Distinctness::Distinct, - &HashMap::default(), - ) + .into_acir(&Brillig::default(), noirc_frontend::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); // Expected result: // main f0 @@ -2564,11 +2550,7 @@ mod test { let ssa = builder.finish(); let acir_functions = ssa - .into_acir( - &Brillig::default(), - noirc_frontend::Distinctness::Distinct, - &HashMap::default(), - ) + .into_acir(&Brillig::default(), noirc_frontend::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); // The expected result should look very similar to the abvoe test expect that the input witnesses of the `Call` // opcodes will be different. The changes can discerned from the checks below. @@ -2659,11 +2641,7 @@ mod test { let ssa = builder.finish(); let acir_functions = ssa - .into_acir( - &Brillig::default(), - noirc_frontend::Distinctness::Distinct, - &HashMap::default(), - ) + .into_acir(&Brillig::default(), noirc_frontend::Distinctness::Distinct) .expect("Should compile manually written SSA into ACIR"); assert_eq!(acir_functions.len(), 3, "Should have three ACIR functions"); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index e0e60b737ad..d3e5e506111 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -310,7 +310,8 @@ impl FunctionBuilder { index: ValueId, value: ValueId, ) -> ValueId { - self.insert_instruction(Instruction::ArraySet { array, index, value }, None).first() + self.insert_instruction(Instruction::ArraySet { array, index, value, mutable: false }, None) + .first() } /// Insert an instruction to increment an array's reference count. This only has an effect @@ -500,7 +501,6 @@ mod tests { use acvm::FieldElement; use crate::ssa::ir::{ - function::{InlineType, RuntimeType}, instruction::{Endian, Intrinsic}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index a887b279970..2b23cc1c1e8 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -189,8 +189,9 @@ pub(crate) enum Instruction { ArrayGet { array: ValueId, index: ValueId }, /// Creates a new array with the new value at the given index. All other elements are identical - /// to those in the given array. This will not modify the original array. - ArraySet { array: ValueId, index: ValueId, value: ValueId }, + /// to those in the given array. This will not modify the original array unless `mutable` is + /// set. This flag is off by default and only enabled when optimizations determine it is safe. + ArraySet { array: ValueId, index: ValueId, value: ValueId, mutable: bool }, /// An instruction to increment the reference count of a value. /// @@ -363,9 +364,12 @@ impl Instruction { Instruction::ArrayGet { array, index } => { Instruction::ArrayGet { array: f(*array), index: f(*index) } } - Instruction::ArraySet { array, index, value } => { - Instruction::ArraySet { array: f(*array), index: f(*index), value: f(*value) } - } + Instruction::ArraySet { array, index, value, mutable } => Instruction::ArraySet { + array: f(*array), + index: f(*index), + value: f(*value), + mutable: *mutable, + }, Instruction::IncrementRc { value } => Instruction::IncrementRc { value: f(*value) }, Instruction::DecrementRc { value } => Instruction::DecrementRc { value: f(*value) }, Instruction::RangeCheck { value, max_bit_size, assert_message } => { @@ -416,7 +420,7 @@ impl Instruction { f(*array); f(*index); } - Instruction::ArraySet { array, index, value } => { + Instruction::ArraySet { array, index, value, mutable: _ } => { f(*array); f(*index); f(*value); diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index a2847146087..1187ea8cb07 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -339,8 +339,13 @@ fn simplify_slice_push_back( let element_size = element_type.element_size(); let new_slice = dfg.make_array(slice, element_type); - let set_last_slice_value_instr = - Instruction::ArraySet { array: new_slice, index: arguments[0], value: arguments[2] }; + let set_last_slice_value_instr = Instruction::ArraySet { + array: new_slice, + index: arguments[0], + value: arguments[2], + mutable: false, + }; + let set_last_slice_value = dfg .insert_instruction_and_results(set_last_slice_value_instr, block, None, call_stack) .first(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/printer.rs b/compiler/noirc_evaluator/src/ssa/ir/printer.rs index 6ef618fba6f..fc13ab7307a 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/printer.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/printer.rs @@ -176,14 +176,12 @@ fn display_instruction_inner( Instruction::ArrayGet { array, index } => { writeln!(f, "array_get {}, index {}", show(*array), show(*index)) } - Instruction::ArraySet { array, index, value } => { - writeln!( - f, - "array_set {}, index {}, value {}", - show(*array), - show(*index), - show(*value) - ) + Instruction::ArraySet { array, index, value, mutable } => { + let array = show(*array); + let index = show(*index); + let value = show(*value); + let mutable = if *mutable { " mut" } else { "" }; + writeln!(f, "array_set{mutable} {array}, index {index}, value {value}",) } Instruction::IncrementRc { value } => { writeln!(f, "inc_rc {}", show(*value)) diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_set.rs b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs new file mode 100644 index 00000000000..cf61d7fd73f --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/array_set.rs @@ -0,0 +1,104 @@ +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + dfg::DataFlowGraph, + instruction::{Instruction, InstructionId}, + types::Type::{Array, Slice}, + }, + ssa_gen::Ssa, +}; +use fxhash::{FxHashMap as HashMap, FxHashSet}; + +impl Ssa { + /// Map arrays with the last instruction that uses it + /// For this we simply process all the instructions in execution order + /// and update the map whenever there is a match + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn array_set_optimization(mut self) -> Self { + for func in self.functions.values_mut() { + if !func.runtime().is_entry_point() { + let mut reachable_blocks = func.reachable_blocks(); + assert_eq!(reachable_blocks.len(), 1, "Expected there to be 1 block remaining in Acir function for array_set optimization"); + + let block = reachable_blocks.pop_first().unwrap(); + let instructions_to_update = analyze_last_uses(&func.dfg, block); + make_mutable(&mut func.dfg, block, instructions_to_update); + } + } + self + } +} + +/// Returns the set of ArraySet instructions that can be made mutable +/// because their input value is unused elsewhere afterward. +fn analyze_last_uses(dfg: &DataFlowGraph, block_id: BasicBlockId) -> FxHashSet { + let block = &dfg[block_id]; + let mut array_to_last_use = HashMap::default(); + let mut instructions_that_can_be_made_mutable = FxHashSet::default(); + + for instruction_id in block.instructions() { + match &dfg[*instruction_id] { + Instruction::ArrayGet { array, .. } => { + let array = dfg.resolve(*array); + + if let Some(existing) = array_to_last_use.insert(array, *instruction_id) { + instructions_that_can_be_made_mutable.remove(&existing); + } + } + Instruction::ArraySet { array, .. } => { + let array = dfg.resolve(*array); + + if let Some(existing) = array_to_last_use.insert(array, *instruction_id) { + instructions_that_can_be_made_mutable.remove(&existing); + } + instructions_that_can_be_made_mutable.insert(*instruction_id); + } + Instruction::Call { arguments, .. } => { + for argument in arguments { + if matches!(dfg.type_of_value(*argument), Array { .. } | Slice { .. }) { + let argument = dfg.resolve(*argument); + + if let Some(existing) = array_to_last_use.insert(argument, *instruction_id) + { + instructions_that_can_be_made_mutable.remove(&existing); + } + } + } + } + _ => (), + } + } + + instructions_that_can_be_made_mutable +} + +/// Make each ArraySet instruction in `instructions_to_update` mutable. +fn make_mutable( + dfg: &mut DataFlowGraph, + block_id: BasicBlockId, + instructions_to_update: FxHashSet, +) { + if instructions_to_update.is_empty() { + return; + } + + // Take the instructions temporarily so we can mutate the DFG while we iterate through them + let block = &mut dfg[block_id]; + let instructions = block.take_instructions(); + + for instruction in &instructions { + if instructions_to_update.contains(instruction) { + let instruction = &mut dfg[*instruction]; + + if let Instruction::ArraySet { mutable, .. } = instruction { + *mutable = true; + } else { + unreachable!( + "Non-ArraySet instruction in instructions_to_update!\n{instruction:?}" + ); + } + } + } + + *dfg[block_id].instructions_mut() = instructions; +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/array_use.rs b/compiler/noirc_evaluator/src/ssa/opt/array_use.rs deleted file mode 100644 index 0bb8b0112b6..00000000000 --- a/compiler/noirc_evaluator/src/ssa/opt/array_use.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::ssa::{ - ir::{ - basic_block::BasicBlockId, - dfg::DataFlowGraph, - instruction::{Instruction, InstructionId}, - post_order::PostOrder, - value::{Value, ValueId}, - }, - ssa_gen::Ssa, -}; -use fxhash::FxHashMap as HashMap; - -impl Ssa { - /// Map arrays with the last instruction that uses it - /// For this we simply process all the instructions in execution order - /// and update the map whenever there is a match - #[tracing::instrument(level = "trace", skip(self))] - pub(crate) fn find_last_array_uses(&self) -> HashMap { - let mut array_use = HashMap::default(); - for func in self.functions.values() { - let mut reverse_post_order = PostOrder::with_function(func).into_vec(); - reverse_post_order.reverse(); - for block in reverse_post_order { - last_use(block, &func.dfg, &mut array_use); - } - } - array_use - } -} - -/// Updates the array_def map when an instructions is using an array -fn last_use( - block_id: BasicBlockId, - dfg: &DataFlowGraph, - array_def: &mut HashMap, -) { - let block = &dfg[block_id]; - for instruction_id in block.instructions() { - match &dfg[*instruction_id] { - Instruction::ArrayGet { array, .. } | Instruction::ArraySet { array, .. } => { - let array = dfg.resolve(*array); - array_def.insert(array, *instruction_id); - } - Instruction::Call { arguments, .. } => { - for argument in arguments { - let resolved_arg = dfg.resolve(*argument); - if matches!(dfg[resolved_arg], Value::Array { .. }) { - array_def.insert(resolved_arg, *instruction_id); - } - } - } - _ => { - // Nothing to do - } - } - } -} diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 8f98b3fb17f..479010b1ed8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -3,7 +3,7 @@ //! Each pass is generally expected to mutate the SSA IR into a gradually //! simpler form until the IR only has a single function remaining with 1 block within it. //! Generally, these passes are also expected to minimize the final amount of instructions. -mod array_use; +mod array_set; mod assert_constant; mod bubble_up_constrains; mod constant_folding; From 6ee645ad0d0d4f5806c137d736cd787d453a02fc Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:37:45 +0100 Subject: [PATCH 135/416] chore: fix clippy errors (#4684) # Description ## Problem\* Resolves ## Summary\* This PR fixes a bunch of clippy errors some new, some old. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../bn254_blackbox_solver/src/wasm/mod.rs | 2 +- acvm-repo/brillig_vm/src/black_box.rs | 5 +- acvm-repo/brillig_vm/src/lib.rs | 75 ++++--------------- .../compute_note_hash_and_nullifier.rs | 4 +- .../src/brillig/brillig_ir/artifact.rs | 2 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 15 ++-- compiler/noirc_evaluator/src/ssa/ir/dom.rs | 9 +-- .../noirc_evaluator/src/ssa/ir/post_order.rs | 7 +- .../src/ssa/opt/bubble_up_constrains.rs | 1 - .../src/ssa/opt/constant_folding.rs | 1 - compiler/noirc_evaluator/src/ssa/opt/die.rs | 1 - .../src/ssa/opt/flatten_cfg.rs | 2 +- .../ssa/opt/flatten_cfg/branch_analysis.rs | 7 +- .../noirc_evaluator/src/ssa/opt/inlining.rs | 2 +- .../noirc_evaluator/src/ssa/opt/mem2reg.rs | 1 - compiler/noirc_evaluator/src/ssa/opt/rc.rs | 8 +- .../src/ssa/opt/simplify_cfg.rs | 1 - .../noirc_evaluator/src/ssa/opt/unrolling.rs | 7 +- compiler/noirc_frontend/src/lexer/lexer.rs | 2 +- compiler/noirc_frontend/src/parser/parser.rs | 2 +- .../src/parser/parser/literals.rs | 1 + tooling/debugger/src/context.rs | 7 +- tooling/debugger/src/foreign_calls.rs | 2 +- tooling/nargo/src/artifacts/debug_vars.rs | 4 +- tooling/nargo_fmt/build.rs | 4 +- 25 files changed, 43 insertions(+), 129 deletions(-) diff --git a/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs b/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs index 10b1ab22a8d..3c867ae48dd 100644 --- a/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs +++ b/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs @@ -45,7 +45,7 @@ pub(crate) struct BackendError(#[from] Error); impl From for BackendError { fn from(value: FeatureError) -> Self { - value.into() + BackendError(Error::FromFeature(value)) } } diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index ab4358739e9..bd33b5ee8fc 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -256,10 +256,11 @@ fn black_box_function_from_op(op: &BlackBoxOp) -> BlackBoxFunc { #[cfg(test)] mod test { use acir::brillig::{BlackBoxOp, MemoryAddress}; + use acvm_blackbox_solver::StubbedBlackBoxSolver; use crate::{ black_box::{evaluate_black_box, to_u8_vec, to_value_vec}, - DummyBlackBoxSolver, HeapArray, HeapVector, Memory, + HeapArray, HeapVector, Memory, }; #[test] @@ -280,7 +281,7 @@ mod test { output: HeapArray { pointer: 2.into(), size: 32 }, }; - evaluate_black_box(&op, &DummyBlackBoxSolver, &mut memory).unwrap(); + evaluate_black_box(&op, &StubbedBlackBoxSolver, &mut memory).unwrap(); let result = memory.read_slice(MemoryAddress(result_pointer), 32); diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 0f430b0d5b2..65654e24720 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -16,7 +16,7 @@ use acir::brillig::{ HeapVector, MemoryAddress, Opcode, ValueOrArray, }; use acir::FieldElement; -use acvm_blackbox_solver::{BlackBoxFunctionSolver, BlackBoxResolutionError}; +use acvm_blackbox_solver::BlackBoxFunctionSolver; use arithmetic::{evaluate_binary_field_op, evaluate_binary_int_op, BrilligArithmeticError}; use black_box::evaluate_black_box; use num_bigint::BigUint; @@ -593,59 +593,10 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } } -pub(crate) struct DummyBlackBoxSolver; - -impl BlackBoxFunctionSolver for DummyBlackBoxSolver { - fn schnorr_verify( - &self, - _public_key_x: &FieldElement, - _public_key_y: &FieldElement, - _signature: &[u8], - _message: &[u8], - ) -> Result { - Ok(true) - } - fn pedersen_commitment( - &self, - _inputs: &[FieldElement], - _domain_separator: u32, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Ok((2_u128.into(), 3_u128.into())) - } - fn pedersen_hash( - &self, - _inputs: &[FieldElement], - _domain_separator: u32, - ) -> Result { - Ok(6_u128.into()) - } - fn fixed_base_scalar_mul( - &self, - _low: &FieldElement, - _high: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Ok((4_u128.into(), 5_u128.into())) - } - fn ec_add( - &self, - _input1_x: &FieldElement, - _input1_y: &FieldElement, - _input2_x: &FieldElement, - _input2_y: &FieldElement, - ) -> Result<(FieldElement, FieldElement), BlackBoxResolutionError> { - Ok((5_u128.into(), 6_u128.into())) - } - fn poseidon2_permutation( - &self, - _input: &[FieldElement], - len: u32, - ) -> Result, BlackBoxResolutionError> { - Ok(vec![0_u128.into(); len as usize]) - } -} - #[cfg(test)] mod tests { + use acvm_blackbox_solver::StubbedBlackBoxSolver; + use super::*; #[test] @@ -662,7 +613,7 @@ mod tests { // Start VM let opcodes = [calldata_copy]; - let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, &opcodes, vec![], &StubbedBlackBoxSolver); // Process a single VM opcode // @@ -706,7 +657,7 @@ mod tests { opcodes.push(Opcode::Jump { location: 3 }); opcodes.push(Opcode::JumpIf { condition: destination, location: 4 }); - let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, &opcodes, vec![], &StubbedBlackBoxSolver); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -763,7 +714,7 @@ mod tests { jump_if_not_opcode, add_opcode, ]; - let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, &opcodes, vec![], &StubbedBlackBoxSolver); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -811,7 +762,7 @@ mod tests { }, Opcode::Stop { return_data_offset: 1, return_data_size: 1 }, ]; - let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, opcodes, vec![], &StubbedBlackBoxSolver); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -842,7 +793,7 @@ mod tests { Opcode::Mov { destination: MemoryAddress::from(2), source: MemoryAddress::from(0) }; let opcodes = &[calldata_copy, mov_opcode]; - let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, opcodes, vec![], &StubbedBlackBoxSolver); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -898,7 +849,7 @@ mod tests { condition: MemoryAddress(1), }, ]; - let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, opcodes, vec![], &StubbedBlackBoxSolver); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -981,7 +932,7 @@ mod tests { .chain(cast_opcodes) .chain([equal_opcode, not_equal_opcode, less_than_opcode, less_than_equal_opcode]) .collect(); - let mut vm = VM::new(calldata, &opcodes, vec![], &DummyBlackBoxSolver); + let mut vm = VM::new(calldata, &opcodes, vec![], &StubbedBlackBoxSolver); // Calldata copy let status = vm.process_opcode(); @@ -1276,14 +1227,14 @@ mod tests { fn brillig_execute_and_get_vm( calldata: Vec, opcodes: &[Opcode], - ) -> VM<'_, DummyBlackBoxSolver> { - let mut vm = VM::new(calldata, opcodes, vec![], &DummyBlackBoxSolver); + ) -> VM<'_, StubbedBlackBoxSolver> { + let mut vm = VM::new(calldata, opcodes, vec![], &StubbedBlackBoxSolver); brillig_execute(&mut vm); assert_eq!(vm.call_stack, vec![]); vm } - fn brillig_execute(vm: &mut VM) { + fn brillig_execute(vm: &mut VM) { loop { let status = vm.process_opcode(); if matches!(status, VMStatus::Finished { .. } | VMStatus::ForeignCallWait { .. }) { diff --git a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs index fd538dc578b..1f5681ed470 100644 --- a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs +++ b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs @@ -127,7 +127,7 @@ pub fn inject_compute_note_hash_and_nullifier( Ok(()) } -fn generate_compute_note_hash_and_nullifier(note_types: &Vec) -> NoirFunction { +fn generate_compute_note_hash_and_nullifier(note_types: &[String]) -> NoirFunction { let function_source = generate_compute_note_hash_and_nullifier_source(note_types); let (function_ast, errors) = parse_program(&function_source); @@ -140,7 +140,7 @@ fn generate_compute_note_hash_and_nullifier(note_types: &Vec) -> NoirFun function_ast.functions.remove(0) } -fn generate_compute_note_hash_and_nullifier_source(note_types: &Vec) -> String { +fn generate_compute_note_hash_and_nullifier_source(note_types: &[String]) -> String { // TODO(#4649): The serialized_note parameter is a fixed-size array, but we don't know what length it should have. // For now we hardcode it to 20, which is the same as MAX_NOTE_FIELDS_LENGTH. diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index d10dcf13d9f..90a5d54ae59 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -108,7 +108,7 @@ impl BrilligArtifact { self.byte_code.append(&mut byte_code); // Remove all resolved external calls and transform them to jumps - let is_resolved = |label: &Label| self.labels.get(label).is_some(); + let is_resolved = |label: &Label| self.labels.contains_key(label); let resolved_external_calls = self .unresolved_external_call_labels diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 46b0318d5de..e3dd4c33986 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -2502,16 +2502,13 @@ mod test { check_call_opcode(&main_opcodes[0], 1, vec![Witness(0), Witness(1)], vec![Witness(2)]); check_call_opcode(&main_opcodes[1], 1, vec![Witness(0), Witness(1)], vec![Witness(3)]); - match &main_opcodes[2] { - Opcode::AssertZero(expr) => { - assert_eq!(expr.linear_combinations[0].0, FieldElement::from(1u128)); - assert_eq!(expr.linear_combinations[0].1, Witness(2)); + if let Opcode::AssertZero(expr) = &main_opcodes[2] { + assert_eq!(expr.linear_combinations[0].0, FieldElement::from(1u128)); + assert_eq!(expr.linear_combinations[0].1, Witness(2)); - assert_eq!(expr.linear_combinations[1].0, FieldElement::from(-1i128)); - assert_eq!(expr.linear_combinations[1].1, Witness(3)); - assert_eq!(expr.q_c, FieldElement::from(0u128)); - } - _ => {} + assert_eq!(expr.linear_combinations[1].0, FieldElement::from(-1i128)); + assert_eq!(expr.linear_combinations[1].1, Witness(3)); + assert_eq!(expr.q_c, FieldElement::from(0u128)); } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/dom.rs b/compiler/noirc_evaluator/src/ssa/ir/dom.rs index bd1481a7474..15fa3bad38d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dom.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dom.rs @@ -249,13 +249,8 @@ mod tests { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, - dfg::CallStack, - dom::DominatorTree, - function::{Function, InlineType, RuntimeType}, - instruction::TerminatorInstruction, - map::Id, - types::Type, + basic_block::BasicBlockId, dfg::CallStack, dom::DominatorTree, function::Function, + instruction::TerminatorInstruction, map::Id, types::Type, }, }; diff --git a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs index 5d743e953a5..d95ec451779 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/post_order.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/post_order.rs @@ -73,12 +73,7 @@ impl PostOrder { mod tests { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{ - function::{Function, InlineType, RuntimeType}, - map::Id, - post_order::PostOrder, - types::Type, - }, + ir::{function::Function, map::Id, post_order::PostOrder, types::Type}, }; #[test] diff --git a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs index 6d556e86cb5..0409f0e6a49 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/bubble_up_constrains.rs @@ -77,7 +77,6 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::{InlineType, RuntimeType}, instruction::{Binary, BinaryOp, Instruction}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 88948331960..6cac8c91bc3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -283,7 +283,6 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::{InlineType, RuntimeType}, instruction::{Binary, BinaryOp, Instruction, TerminatorInstruction}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index df89dcb716d..d1b3e1e83f5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -169,7 +169,6 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::{InlineType, RuntimeType}, instruction::{BinaryOp, Intrinsic}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index e731a7952a6..07771397ce8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -841,7 +841,7 @@ mod test { function_builder::FunctionBuilder, ir::{ dfg::DataFlowGraph, - function::{Function, InlineType, RuntimeType}, + function::Function, instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs index adb6d2871e5..ce54bb533f7 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/branch_analysis.rs @@ -114,12 +114,7 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{ - cfg::ControlFlowGraph, - function::{InlineType, RuntimeType}, - map::Id, - types::Type, - }, + ir::{cfg::ControlFlowGraph, map::Id, types::Type}, opt::flatten_cfg::branch_analysis::find_branch_ends, }; diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index 63c78b99cdf..ead3cac071c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -522,7 +522,7 @@ mod test { function_builder::FunctionBuilder, ir::{ basic_block::BasicBlockId, - function::{InlineType, RuntimeType}, + function::InlineType, instruction::{BinaryOp, Intrinsic, TerminatorInstruction}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs index f1a38585bd6..7b87142d824 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mem2reg.rs @@ -414,7 +414,6 @@ mod tests { ir::{ basic_block::BasicBlockId, dfg::DataFlowGraph, - function::{InlineType, RuntimeType}, instruction::{BinaryOp, Instruction, Intrinsic, TerminatorInstruction}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index 7b5196f2004..1561547e32e 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -166,12 +166,8 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, - dfg::DataFlowGraph, - function::{InlineType, RuntimeType}, - instruction::Instruction, - map::Id, - types::Type, + basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, + instruction::Instruction, map::Id, types::Type, }, }; diff --git a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs index b9675d99c90..f524b10f1f2 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/simplify_cfg.rs @@ -154,7 +154,6 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - function::{InlineType, RuntimeType}, instruction::{BinaryOp, TerminatorInstruction}, map::Id, types::Type, diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 92ec1e8f1bb..8110e3469f1 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -464,12 +464,7 @@ impl<'f> LoopIteration<'f> { mod tests { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{ - function::{InlineType, RuntimeType}, - instruction::BinaryOp, - map::Id, - types::Type, - }, + ir::{instruction::BinaryOp, map::Id, types::Type}, }; #[test] diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index c4b6bb288dc..cb21f58e58b 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -1178,7 +1178,7 @@ mod tests { .as_ref() .map(|token_discriminator| { discriminant(token_discriminator) - == discriminant(&next_token.token()) + == discriminant(next_token.token()) }) .unwrap_or(true); diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index 7ecff12163a..cdfa16400ae 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -1242,7 +1242,7 @@ where mod test { use super::test_helpers::*; use super::*; - use crate::{ArrayLiteral, Literal}; + use crate::ArrayLiteral; #[test] fn parse_infix() { diff --git a/compiler/noirc_frontend/src/parser/parser/literals.rs b/compiler/noirc_frontend/src/parser/parser/literals.rs index 32f4f03de2e..83d7b832d27 100644 --- a/compiler/noirc_frontend/src/parser/parser/literals.rs +++ b/compiler/noirc_frontend/src/parser/parser/literals.rs @@ -105,6 +105,7 @@ mod test { Case { source: r#" r#"foo" "#, expect: "(none)", errors: 2 }, // empty string Case { source: r#"r"""#, expect: r#"r"""#, errors: 0 }, + #[allow(clippy::needless_raw_string_hashes)] Case { source: r####"r###""###"####, expect: r####"r###""###"####, errors: 0 }, // miscellaneous Case { source: r##" r#\"foo\"# "##, expect: "plain::r", errors: 2 }, diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index ba12a20460d..1acd581b2be 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -128,9 +128,7 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { line: i64, ) -> Option { let line = line as usize; - let Some(line_to_opcodes) = self.source_to_opcodes.get(file_id) else { - return None; - }; + let line_to_opcodes = self.source_to_opcodes.get(file_id)?; let found_index = match line_to_opcodes.binary_search_by(|x| x.0.cmp(&line)) { Ok(index) => { // move backwards to find the first opcode which matches the line @@ -631,7 +629,6 @@ fn build_source_to_opcode_debug_mappings( #[cfg(test)] mod tests { use super::*; - use crate::context::{DebugCommandResult, DebugContext}; use crate::foreign_calls::DefaultDebugForeignCallExecutor; use acvm::{ @@ -647,8 +644,6 @@ mod tests { BinaryFieldOp, HeapValueType, MemoryAddress, Opcode as BrilligOpcode, ValueOrArray, }, }; - use nargo::artifacts::debug::DebugArtifact; - use std::collections::BTreeMap; #[test] fn test_resolve_foreign_calls_stepping_into_brillig() { diff --git a/tooling/debugger/src/foreign_calls.rs b/tooling/debugger/src/foreign_calls.rs index aae2212fd54..f11ac22cd75 100644 --- a/tooling/debugger/src/foreign_calls.rs +++ b/tooling/debugger/src/foreign_calls.rs @@ -65,7 +65,7 @@ impl DefaultDebugForeignCallExecutor { pub fn load_artifact(&mut self, artifact: &DebugArtifact) { // TODO: handle loading from the correct DebugInfo when we support // debugging contracts - let Some(info) = artifact.debug_symbols.get(0) else { + let Some(info) = artifact.debug_symbols.first() else { return; }; self.debug_vars.insert_debug_info(info); diff --git a/tooling/nargo/src/artifacts/debug_vars.rs b/tooling/nargo/src/artifacts/debug_vars.rs index 8e5c2bc46a4..6a42a4c3311 100644 --- a/tooling/nargo/src/artifacts/debug_vars.rs +++ b/tooling/nargo/src/artifacts/debug_vars.rs @@ -36,9 +36,7 @@ impl DebugVars { fn lookup_var(&self, var_id: DebugVarId) -> Option<(&str, &PrintableType)> { self.variables.get(&var_id).and_then(|debug_var| { - let Some(ptype) = self.types.get(&debug_var.debug_type_id) else { - return None; - }; + let ptype = self.types.get(&debug_var.debug_type_id)?; Some((debug_var.name.as_str(), ptype)) }) } diff --git a/tooling/nargo_fmt/build.rs b/tooling/nargo_fmt/build.rs index c356b403ae5..6f41768c1dc 100644 --- a/tooling/nargo_fmt/build.rs +++ b/tooling/nargo_fmt/build.rs @@ -58,10 +58,10 @@ fn format_{test_name}() {{ let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(&input); + let (parsed_module, _errors) = noirc_frontend::parse_program(input); let config = nargo_fmt::Config::of("{config}").unwrap(); - let fmt_text = nargo_fmt::format(&input, parsed_module, &config); + let fmt_text = nargo_fmt::format(input, parsed_module, &config); if std::env::var("UPDATE_EXPECT").is_ok() {{ std::fs::write("{output_source_path}", fmt_text.clone()).unwrap(); From 420e56d6ea3478301c2ff2f1adb3bcc9df6d1892 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 2 Apr 2024 18:49:01 +0100 Subject: [PATCH 136/416] chore: check for references to private functions during path resolution (#4622) # Description ## Problem\* Resolves ## Summary\* This PR pulls out the actual changes to path resolution from #4491, omitting the changes which add visibility modifiers to modules. We now determine whether a function is private (and so a warning or error should be emitted) during def collection. This is necessary as to properly determine whether a function is public or private we need information on whether all the modules in the function's path relative to where it's being used allow its contents to be used from this module - information which doesn't exist in the typechecking pass. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/hir/def_collector/dc_crate.rs | 23 +- .../src/hir/resolution/errors.rs | 13 +- .../src/hir/resolution/import.rs | 231 ++++++++++++++---- .../src/hir/resolution/path_resolver.rs | 24 +- .../src/hir/resolution/resolver.rs | 81 ++---- .../src/hir/resolution/traits.rs | 17 +- .../noirc_frontend/src/hir/type_check/mod.rs | 7 +- 7 files changed, 243 insertions(+), 153 deletions(-) diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 0c53bff4a54..90aa4baee7c 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -4,7 +4,7 @@ use crate::graph::CrateId; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; use crate::hir::resolution::errors::ResolverError; -use crate::hir::resolution::import::{resolve_import, ImportDirective}; +use crate::hir::resolution::import::{resolve_import, ImportDirective, PathResolution}; use crate::hir::resolution::{ collect_impls, collect_trait_impls, path_resolver, resolve_free_functions, resolve_globals, resolve_impls, resolve_structs, resolve_trait_by_path, resolve_trait_impls, resolve_traits, @@ -56,8 +56,11 @@ impl UnresolvedFunctions { for bound in &mut func.def.where_clause { match resolve_trait_by_path(def_maps, module, bound.trait_bound.trait_path.clone()) { - Ok(trait_id) => { + Ok((trait_id, warning)) => { bound.trait_bound.trait_id = Some(trait_id); + if let Some(warning) = warning { + errors.push(DefCollectorErrorKind::PathResolutionError(warning)); + } } Err(err) => { errors.push(err); @@ -281,6 +284,13 @@ impl DefCollector { for collected_import in def_collector.collected_imports { match resolve_import(crate_id, &collected_import, &context.def_maps) { Ok(resolved_import) => { + if let Some(error) = resolved_import.error { + errors.push(( + DefCollectorErrorKind::PathResolutionError(error).into(), + root_file_id, + )); + } + // Populate module namespaces according to the imports used let current_def_map = context.def_maps.get_mut(&crate_id).unwrap(); @@ -299,9 +309,9 @@ impl DefCollector { } } } - Err((error, module_id)) => { + Err(error) => { let current_def_map = context.def_maps.get(&crate_id).unwrap(); - let file_id = current_def_map.file_id(module_id); + let file_id = current_def_map.file_id(collected_import.module_id); let error = DefCollectorErrorKind::PathResolutionError(error); errors.push((error.into(), file_id)); } @@ -409,12 +419,13 @@ fn inject_prelude( Path { segments: segments.clone(), kind: crate::PathKind::Dep, span: Span::default() }; if !crate_id.is_stdlib() { - if let Ok(module_def) = path_resolver::resolve_path( + if let Ok(PathResolution { module_def_id, error }) = path_resolver::resolve_path( &context.def_maps, ModuleId { krate: crate_id, local_id: crate_root }, path, ) { - let module_id = module_def.as_module().expect("std::prelude should be a module"); + assert!(error.is_none(), "Tried to add private item to prelude"); + let module_id = module_def_id.as_module().expect("std::prelude should be a module"); let prelude = context.module(module_id).scope().names(); for path in prelude { diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index 3c6c0582292..d5b0c612f90 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -25,7 +25,7 @@ pub enum ResolverError { #[error("path is not an identifier")] PathIsNotIdent { span: Span }, #[error("could not resolve path")] - PathResolutionError(PathResolutionError), + PathResolutionError(#[from] PathResolutionError), #[error("Expected")] Expected { span: Span, expected: String, got: String }, #[error("Duplicate field in constructor")] @@ -72,10 +72,6 @@ pub enum ResolverError { NumericConstantInFormatString { name: String, span: Span }, #[error("Closure environment must be a tuple or unit type")] InvalidClosureEnvironment { typ: Type, span: Span }, - #[error("{name} is private and not visible from the current module")] - PrivateFunctionCalled { name: String, span: Span }, - #[error("{name} is not visible from the current crate")] - NonCrateFunctionCalled { name: String, span: Span }, #[error("Nested slices are not supported")] NestedSlices { span: Span }, #[error("#[recursive] attribute is only allowed on entry points to a program")] @@ -290,13 +286,6 @@ impl From for Diagnostic { ResolverError::InvalidClosureEnvironment { span, typ } => Diagnostic::simple_error( format!("{typ} is not a valid closure environment type"), "Closure environment must be a tuple or unit type".to_string(), span), - // This will be upgraded to an error in future versions - ResolverError::PrivateFunctionCalled { span, name } => Diagnostic::simple_warning( - format!("{name} is private and not visible from the current module"), - format!("{name} is private"), span), - ResolverError::NonCrateFunctionCalled { span, name } => Diagnostic::simple_warning( - format!("{name} is not visible from the current crate"), - format!("{name} is only visible within its crate"), span), ResolverError::NestedSlices { span } => Diagnostic::simple_error( "Nested slices are not supported".into(), "Try to use a constant sized array instead".into(), diff --git a/compiler/noirc_frontend/src/hir/resolution/import.rs b/compiler/noirc_frontend/src/hir/resolution/import.rs index 9c8418daf80..ade97e2cf42 100644 --- a/compiler/noirc_frontend/src/hir/resolution/import.rs +++ b/compiler/noirc_frontend/src/hir/resolution/import.rs @@ -1,10 +1,11 @@ use noirc_errors::{CustomDiagnostic, Span}; +use thiserror::Error; use crate::graph::CrateId; use std::collections::BTreeMap; use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId, PerNs}; -use crate::{Ident, Path, PathKind}; +use crate::{Ident, ItemVisibility, Path, PathKind}; #[derive(Debug, Clone)] pub struct ImportDirective { @@ -14,12 +15,30 @@ pub struct ImportDirective { pub is_prelude: bool, } -pub type PathResolution = Result; +struct NamespaceResolution { + module_id: ModuleId, + namespace: PerNs, + error: Option, +} + +type NamespaceResolutionResult = Result; + +pub struct PathResolution { + pub module_def_id: ModuleDefId, + + pub error: Option, +} + +pub(crate) type PathResolutionResult = Result; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum PathResolutionError { + #[error("Could not resolve '{0}' in path")] Unresolved(Ident), + #[error("Contract variable '{0}' referenced from outside the contract")] ExternalContractUsed(Ident), + #[error("{0} is private and not visible from the current module")] + Private(Ident), } #[derive(Debug)] @@ -31,21 +50,26 @@ pub struct ResolvedImport { // The module which we must add the resolved namespace to pub module_scope: LocalModuleId, pub is_prelude: bool, + pub error: Option, } impl From for CustomDiagnostic { fn from(error: PathResolutionError) -> Self { - match error { - PathResolutionError::Unresolved(ident) => CustomDiagnostic::simple_error( - format!("Could not resolve '{ident}' in path"), - String::new(), - ident.span(), - ), + match &error { + PathResolutionError::Unresolved(ident) => { + CustomDiagnostic::simple_error(error.to_string(), String::new(), ident.span()) + } PathResolutionError::ExternalContractUsed(ident) => CustomDiagnostic::simple_error( - format!("Contract variable '{ident}' referenced from outside the contract"), + error.to_string(), "Contracts may only be referenced from within a contract".to_string(), ident.span(), ), + // This will be upgraded to an error in future versions + PathResolutionError::Private(ident) => CustomDiagnostic::simple_warning( + error.to_string(), + format!("{ident} is private"), + ident.span(), + ), } } } @@ -54,27 +78,49 @@ pub fn resolve_import( crate_id: CrateId, import_directive: &ImportDirective, def_maps: &BTreeMap, -) -> Result { - let def_map = &def_maps[&crate_id]; - +) -> Result { let allow_contracts = allow_referencing_contracts(def_maps, crate_id, import_directive.module_id); let module_scope = import_directive.module_id; - let resolved_namespace = - resolve_path_to_ns(import_directive, def_map, def_maps, allow_contracts) - .map_err(|error| (error, module_scope))?; + let NamespaceResolution { + module_id: resolved_module, + namespace: resolved_namespace, + mut error, + } = resolve_path_to_ns(import_directive, crate_id, crate_id, def_maps, allow_contracts)?; let name = resolve_path_name(import_directive); + + let visibility = resolved_namespace + .values + .or(resolved_namespace.types) + .map(|(_, visibility, _)| visibility) + .expect("Found empty namespace"); + + error = error.or_else(|| { + if can_reference_module_id( + def_maps, + crate_id, + import_directive.module_id, + resolved_module, + visibility, + ) { + None + } else { + Some(PathResolutionError::Private(name.clone())) + } + }); + Ok(ResolvedImport { name, resolved_namespace, module_scope, is_prelude: import_directive.is_prelude, + error, }) } -pub(super) fn allow_referencing_contracts( +fn allow_referencing_contracts( def_maps: &BTreeMap, krate: CrateId, local_id: LocalModuleId, @@ -82,27 +128,40 @@ pub(super) fn allow_referencing_contracts( ModuleId { krate, local_id }.module(def_maps).is_contract } -pub fn resolve_path_to_ns( +fn resolve_path_to_ns( import_directive: &ImportDirective, - def_map: &CrateDefMap, + crate_id: CrateId, + importing_crate: CrateId, def_maps: &BTreeMap, allow_contracts: bool, -) -> PathResolution { +) -> NamespaceResolutionResult { let import_path = &import_directive.path.segments; + let def_map = &def_maps[&crate_id]; match import_directive.path.kind { crate::ast::PathKind::Crate => { // Resolve from the root of the crate - resolve_path_from_crate_root(def_map, import_path, def_maps, allow_contracts) - } - crate::ast::PathKind::Dep => { - resolve_external_dep(def_map, import_directive, def_maps, allow_contracts) + resolve_path_from_crate_root( + crate_id, + importing_crate, + import_path, + def_maps, + allow_contracts, + ) } + crate::ast::PathKind::Dep => resolve_external_dep( + def_map, + import_directive, + def_maps, + allow_contracts, + importing_crate, + ), crate::ast::PathKind::Plain => { // Plain paths are only used to import children modules. It's possible to allow import of external deps, but maybe this distinction is better? // In Rust they can also point to external Dependencies, if no children can be found with the specified name resolve_name_in_module( - def_map, + crate_id, + importing_crate, import_path, import_directive.module_id, def_maps, @@ -113,45 +172,60 @@ pub fn resolve_path_to_ns( } fn resolve_path_from_crate_root( - def_map: &CrateDefMap, + crate_id: CrateId, + importing_crate: CrateId, + import_path: &[Ident], def_maps: &BTreeMap, allow_contracts: bool, -) -> PathResolution { - resolve_name_in_module(def_map, import_path, def_map.root, def_maps, allow_contracts) +) -> NamespaceResolutionResult { + resolve_name_in_module( + crate_id, + importing_crate, + import_path, + def_maps[&crate_id].root, + def_maps, + allow_contracts, + ) } fn resolve_name_in_module( - def_map: &CrateDefMap, + krate: CrateId, + importing_crate: CrateId, import_path: &[Ident], starting_mod: LocalModuleId, def_maps: &BTreeMap, allow_contracts: bool, -) -> PathResolution { - let mut current_mod = &def_map.modules[starting_mod.0]; +) -> NamespaceResolutionResult { + let def_map = &def_maps[&krate]; + let mut current_mod_id = ModuleId { krate, local_id: starting_mod }; + let mut current_mod = &def_map.modules[current_mod_id.local_id.0]; // There is a possibility that the import path is empty // In that case, early return if import_path.is_empty() { - let mod_id = ModuleId { krate: def_map.krate, local_id: starting_mod }; - return Ok(PerNs::types(mod_id.into())); + return Ok(NamespaceResolution { + module_id: current_mod_id, + namespace: PerNs::types(current_mod_id.into()), + error: None, + }); } - let mut import_path = import_path.iter(); - let first_segment = import_path.next().expect("ice: could not fetch first segment"); + let first_segment = import_path.first().expect("ice: could not fetch first segment"); let mut current_ns = current_mod.find_name(first_segment); if current_ns.is_none() { return Err(PathResolutionError::Unresolved(first_segment.clone())); } - for segment in import_path { - let typ = match current_ns.take_types() { - None => return Err(PathResolutionError::Unresolved(segment.clone())), - Some(typ) => typ, + let mut warning: Option = None; + for (last_segment, current_segment) in import_path.iter().zip(import_path.iter().skip(1)) { + let (typ, visibility) = match current_ns.types { + None => return Err(PathResolutionError::Unresolved(last_segment.clone())), + Some((typ, visibility, _)) => (typ, visibility), }; // In the type namespace, only Mod can be used in a path. - let new_module_id = match typ { + current_mod_id = match typ { ModuleDefId::ModuleId(id) => id, ModuleDefId::FunctionId(_) => panic!("functions cannot be in the type namespace"), // TODO: If impls are ever implemented, types can be used in a path @@ -161,22 +235,37 @@ fn resolve_name_in_module( ModuleDefId::GlobalId(_) => panic!("globals cannot be in the type namespace"), }; - current_mod = &def_maps[&new_module_id.krate].modules[new_module_id.local_id.0]; + warning = warning.or_else(|| { + if can_reference_module_id( + def_maps, + importing_crate, + starting_mod, + current_mod_id, + visibility, + ) { + None + } else { + Some(PathResolutionError::Private(last_segment.clone())) + } + }); + + current_mod = &def_maps[¤t_mod_id.krate].modules[current_mod_id.local_id.0]; // Check if namespace - let found_ns = current_mod.find_name(segment); + let found_ns = current_mod.find_name(current_segment); if found_ns.is_none() { - return Err(PathResolutionError::Unresolved(segment.clone())); + return Err(PathResolutionError::Unresolved(current_segment.clone())); } + // Check if it is a contract and we're calling from a non-contract context if current_mod.is_contract && !allow_contracts { - return Err(PathResolutionError::ExternalContractUsed(segment.clone())); + return Err(PathResolutionError::ExternalContractUsed(current_segment.clone())); } current_ns = found_ns; } - Ok(current_ns) + Ok(NamespaceResolution { module_id: current_mod_id, namespace: current_ns, error: warning }) } fn resolve_path_name(import_directive: &ImportDirective) -> Ident { @@ -191,11 +280,11 @@ fn resolve_external_dep( directive: &ImportDirective, def_maps: &BTreeMap, allow_contracts: bool, -) -> PathResolution { + importing_crate: CrateId, +) -> NamespaceResolutionResult { // Use extern_prelude to get the dep - // let path = &directive.path.segments; - // + // Fetch the root module from the prelude let crate_name = path.first().unwrap(); let dep_module = current_def_map @@ -218,7 +307,49 @@ fn resolve_external_dep( is_prelude: false, }; - let dep_def_map = def_maps.get(&dep_module.krate).unwrap(); + resolve_path_to_ns(&dep_directive, dep_module.krate, importing_crate, def_maps, allow_contracts) +} + +// Issue an error if the given private function is being called from a non-child module, or +// if the given pub(crate) function is being called from another crate +fn can_reference_module_id( + def_maps: &BTreeMap, + importing_crate: CrateId, + current_module: LocalModuleId, + target_module: ModuleId, + visibility: ItemVisibility, +) -> bool { + // Note that if the target module is in a different crate from the current module then we will either + // return true as the target module is public or return false as it is private without looking at the `CrateDefMap` in either case. + let same_crate = target_module.krate == importing_crate; + let target_crate_def_map = &def_maps[&target_module.krate]; + + match visibility { + ItemVisibility::Public => true, + ItemVisibility::PublicCrate => same_crate, + ItemVisibility::Private => { + same_crate + && module_descendent_of_target( + target_crate_def_map, + target_module.local_id, + current_module, + ) + } + } +} + +// Returns true if `current` is a (potentially nested) child module of `target`. +// This is also true if `current == target`. +fn module_descendent_of_target( + def_map: &CrateDefMap, + target: LocalModuleId, + current: LocalModuleId, +) -> bool { + if current == target { + return true; + } - resolve_path_to_ns(&dep_directive, dep_def_map, def_maps, allow_contracts) + def_map.modules[current.0] + .parent + .map_or(false, |parent| module_descendent_of_target(def_map, target, parent)) } diff --git a/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs b/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs index 4c5fa3bceef..e19af3c732f 100644 --- a/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/path_resolver.rs @@ -1,11 +1,9 @@ -use super::import::{ - allow_referencing_contracts, resolve_path_to_ns, ImportDirective, PathResolutionError, -}; +use super::import::{resolve_import, ImportDirective, PathResolution, PathResolutionResult}; use crate::Path; use std::collections::BTreeMap; use crate::graph::CrateId; -use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleDefId, ModuleId}; +use crate::hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}; pub trait PathResolver { /// Resolve the given path returning the resolved ModuleDefId. @@ -13,7 +11,7 @@ pub trait PathResolver { &self, def_maps: &BTreeMap, path: Path, - ) -> Result; + ) -> PathResolutionResult; fn local_module_id(&self) -> LocalModuleId; @@ -36,7 +34,7 @@ impl PathResolver for StandardPathResolver { &self, def_maps: &BTreeMap, path: Path, - ) -> Result { + ) -> PathResolutionResult { resolve_path(def_maps, self.module_id, path) } @@ -55,17 +53,15 @@ pub fn resolve_path( def_maps: &BTreeMap, module_id: ModuleId, path: Path, -) -> Result { +) -> PathResolutionResult { // lets package up the path into an ImportDirective and resolve it using that let import = ImportDirective { module_id: module_id.local_id, path, alias: None, is_prelude: false }; - let allow_referencing_contracts = - allow_referencing_contracts(def_maps, module_id.krate, module_id.local_id); + let resolved_import = resolve_import(module_id.krate, &import, def_maps)?; - let def_map = &def_maps[&module_id.krate]; - let ns = resolve_path_to_ns(&import, def_map, def_maps, allow_referencing_contracts)?; + let namespace = resolved_import.resolved_namespace; + let id = + namespace.values.or(namespace.types).map(|(id, _, _)| id).expect("Found empty namespace"); - let function = ns.values.map(|(id, _, _)| id); - let id = function.or_else(|| ns.types.map(|(id, _, _)| id)); - Ok(id.expect("Found empty namespace")) + Ok(PathResolution { module_def_id: id, error: resolved_import.error }) } diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index ec149dee96e..f2b8212db7a 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -25,7 +25,7 @@ use std::collections::{BTreeMap, HashSet}; use std::rc::Rc; use crate::graph::CrateId; -use crate::hir::def_map::{LocalModuleId, ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION}; +use crate::hir::def_map::{ModuleDefId, TryFromModuleDefId, MAIN_FUNCTION}; use crate::hir_def::stmt::{HirAssignStatement, HirForStatement, HirLValue, HirPattern}; use crate::node_interner::{ DefinitionId, DefinitionKind, DependencyId, ExprId, FuncId, GlobalId, NodeInterner, StmtId, @@ -56,6 +56,7 @@ use crate::hir_def::{ }; use super::errors::{PubPosition, ResolverError}; +use super::import::PathResolution; const SELF_TYPE_NAME: &str = "Self"; @@ -677,10 +678,14 @@ impl<'a> Resolver<'a> { // If we cannot find a local generic of the same name, try to look up a global match self.path_resolver.resolve(self.def_maps, path.clone()) { - Ok(ModuleDefId::GlobalId(id)) => { + Ok(PathResolution { module_def_id: ModuleDefId::GlobalId(id), error }) => { if let Some(current_item) = self.current_item { self.interner.add_global_dependency(current_item, id); } + + if let Some(error) = error { + self.push_err(error.into()); + } Some(Type::Constant(self.eval_global_as_array_length(id, path))) } _ => None, @@ -1327,59 +1332,6 @@ impl<'a> Resolver<'a> { } } - // Issue an error if the given private function is being called from a non-child module, or - // if the given pub(crate) function is being called from another crate - fn check_can_reference_function( - &mut self, - func: FuncId, - span: Span, - visibility: ItemVisibility, - ) { - let function_module = self.interner.function_module(func); - let current_module = self.path_resolver.module_id(); - - let same_crate = function_module.krate == current_module.krate; - let krate = function_module.krate; - let current_module = current_module.local_id; - let name = self.interner.function_name(&func).to_string(); - match visibility { - ItemVisibility::Public => (), - ItemVisibility::Private => { - if !same_crate - || !self.module_descendent_of_target( - krate, - function_module.local_id, - current_module, - ) - { - self.errors.push(ResolverError::PrivateFunctionCalled { span, name }); - } - } - ItemVisibility::PublicCrate => { - if !same_crate { - self.errors.push(ResolverError::NonCrateFunctionCalled { span, name }); - } - } - } - } - - // Returns true if `current` is a (potentially nested) child module of `target`. - // This is also true if `current == target`. - fn module_descendent_of_target( - &self, - krate: CrateId, - target: LocalModuleId, - current: LocalModuleId, - ) -> bool { - if current == target { - return true; - } - - self.def_maps[&krate].modules[current.0] - .parent - .map_or(false, |parent| self.module_descendent_of_target(krate, target, parent)) - } - fn resolve_local_variable(&mut self, hir_ident: HirIdent, var_scope_index: usize) { let mut transitive_capture_index: Option = None; @@ -1473,15 +1425,6 @@ impl<'a> Resolver<'a> { if let Some(current_item) = self.current_item { self.interner.add_function_dependency(current_item, id); } - - if self.interner.function_visibility(id) != ItemVisibility::Public { - let span = hir_ident.location.span; - self.check_can_reference_function( - id, - span, - self.interner.function_visibility(id), - ); - } } DefinitionKind::Global(global_id) => { if let Some(current_item) = self.current_item { @@ -1920,7 +1863,7 @@ impl<'a> Resolver<'a> { } if let Ok(ModuleDefId::TraitId(trait_id)) = - self.path_resolver.resolve(self.def_maps, trait_bound.trait_path.clone()) + self.resolve_path(trait_bound.trait_path.clone()) { let the_trait = self.interner.get_trait(trait_id); if let Some(method) = @@ -1955,7 +1898,13 @@ impl<'a> Resolver<'a> { } fn resolve_path(&mut self, path: Path) -> Result { - self.path_resolver.resolve(self.def_maps, path).map_err(ResolverError::PathResolutionError) + let path_resolution = self.path_resolver.resolve(self.def_maps, path)?; + + if let Some(error) = path_resolution.error { + self.push_err(error.into()); + } + + Ok(path_resolution.module_def_id) } fn resolve_block(&mut self, block_expr: BlockExpression) -> HirExpression { diff --git a/compiler/noirc_frontend/src/hir/resolution/traits.rs b/compiler/noirc_frontend/src/hir/resolution/traits.rs index 04da558a642..a7669f57e33 100644 --- a/compiler/noirc_frontend/src/hir/resolution/traits.rs +++ b/compiler/noirc_frontend/src/hir/resolution/traits.rs @@ -21,6 +21,7 @@ use crate::{ use super::{ functions, get_module_mut, get_struct_type, + import::{PathResolution, PathResolutionError}, path_resolver::{PathResolver, StandardPathResolver}, resolver::Resolver, take_errors, @@ -274,7 +275,15 @@ fn collect_trait_impl( let module = ModuleId { local_id: trait_impl.module_id, krate: crate_id }; trait_impl.trait_id = match resolve_trait_by_path(def_maps, module, trait_impl.trait_path.clone()) { - Ok(trait_id) => Some(trait_id), + Ok((trait_id, warning)) => { + if let Some(warning) = warning { + errors.push(( + DefCollectorErrorKind::PathResolutionError(warning).into(), + trait_impl.file_id, + )); + } + Some(trait_id) + } Err(error) => { errors.push((error.into(), trait_impl.file_id)); None @@ -362,11 +371,13 @@ pub(crate) fn resolve_trait_by_path( def_maps: &BTreeMap, module: ModuleId, path: Path, -) -> Result { +) -> Result<(TraitId, Option), DefCollectorErrorKind> { let path_resolver = StandardPathResolver::new(module); match path_resolver.resolve(def_maps, path.clone()) { - Ok(ModuleDefId::TraitId(trait_id)) => Ok(trait_id), + Ok(PathResolution { module_def_id: ModuleDefId::TraitId(trait_id), error }) => { + Ok((trait_id, error)) + } Ok(_) => Err(DefCollectorErrorKind::NotATrait { not_a_trait_name: path }), Err(_) => Err(DefCollectorErrorKind::TraitNotFound { trait_path: path }), } diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index c5a04c33883..926dac30bcd 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -434,7 +434,9 @@ mod test { use crate::graph::CrateId; use crate::hir::def_map::{ModuleData, ModuleId}; - use crate::hir::resolution::import::PathResolutionError; + use crate::hir::resolution::import::{ + PathResolution, PathResolutionError, PathResolutionResult, + }; use crate::hir_def::expr::HirIdent; use crate::hir_def::stmt::HirLetStatement; use crate::hir_def::stmt::HirPattern::Identifier; @@ -644,12 +646,13 @@ mod test { &self, _def_maps: &BTreeMap, path: Path, - ) -> Result { + ) -> PathResolutionResult { // Not here that foo::bar and hello::foo::bar would fetch the same thing let name = path.segments.last().unwrap(); self.0 .get(&name.0.contents) .cloned() + .map(|module_def_id| PathResolution { module_def_id, error: None }) .ok_or_else(move || PathResolutionError::Unresolved(name.clone())) } From 24f6d85f2467a109399d21729f8bb0f97c5ba6db Mon Sep 17 00:00:00 2001 From: Cat McGee Date: Wed, 3 Apr 2024 07:15:11 +0900 Subject: [PATCH 137/416] feat(docs): Documenting noir codegen (#4454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WIP, waiting on https://github.com/noir-lang/noir/pull/4448 # Description ## Problem\* Resolves ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: James Zaki Co-authored-by: José Pedro Sousa Co-authored-by: josh crites --- .../getting_started/tooling/noir_codegen.md | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 docs/docs/getting_started/tooling/noir_codegen.md diff --git a/docs/docs/getting_started/tooling/noir_codegen.md b/docs/docs/getting_started/tooling/noir_codegen.md new file mode 100644 index 00000000000..d65151da0ab --- /dev/null +++ b/docs/docs/getting_started/tooling/noir_codegen.md @@ -0,0 +1,113 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 2 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +yarn add @noir-lang/noir_codegen -D +``` + +### Nargo library +Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = {"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. From c5a7242f6d43eb3a7e74995a6334e38cc3c45ba5 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 3 Apr 2024 11:29:56 +0100 Subject: [PATCH 138/416] chore: remove conditional compilation around `acvm_js` package (#4702) # Description ## Problem\* Resolves ## Summary\* We previously had acvm_js wrapped in a big conditional compilation statement which shortcircuited it if it wasn't building for a wasm architecture. This isn't ideal as it prevents a lot of IDE LSP features from working and it shouldn't be necessary (as shown by the other wasm packages). I've then removed this and fixed any issues caused as a result. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 2 +- acvm-repo/acvm_js/Cargo.toml | 7 +--- acvm-repo/acvm_js/src/black_box_solvers.rs | 2 - acvm-repo/acvm_js/src/execute.rs | 2 +- acvm-repo/acvm_js/src/lib.rs | 47 ++++++++++------------ acvm-repo/bn254_blackbox_solver/Cargo.toml | 1 + acvm-repo/bn254_blackbox_solver/src/lib.rs | 13 ++++-- 7 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f85b26f974..2371ef6df05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,7 +93,6 @@ dependencies = [ "acvm", "bn254_blackbox_solver", "build-data", - "cfg-if 1.0.0", "console_error_panic_hook", "const-str", "gloo-utils", @@ -606,6 +605,7 @@ dependencies = [ "acvm_blackbox_solver", "ark-ec", "ark-ff", + "cfg-if 1.0.0", "flate2", "getrandom 0.2.10", "js-sys", diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 65c072b1d96..8319c38aee2 100644 --- a/acvm-repo/acvm_js/Cargo.toml +++ b/acvm-repo/acvm_js/Cargo.toml @@ -16,20 +16,17 @@ repository.workspace = true crate-type = ["cdylib"] [dependencies] -cfg-if = "1.0.0" - -[target.'cfg(target_arch = "wasm32")'.dependencies] acvm.workspace = true bn254_blackbox_solver = { workspace = true, optional = true } wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true console_error_panic_hook.workspace = true gloo-utils.workspace = true -js-sys.workspace = true +js-sys.workspace = true +serde.workspace = true tracing-subscriber.workspace = true tracing-web.workspace = true -serde = { version = "1.0.136", features = ["derive"] } const-str = "0.5.5" [build-dependencies] diff --git a/acvm-repo/acvm_js/src/black_box_solvers.rs b/acvm-repo/acvm_js/src/black_box_solvers.rs index fc0e3b28ebf..188e5334ed5 100644 --- a/acvm-repo/acvm_js/src/black_box_solvers.rs +++ b/acvm-repo/acvm_js/src/black_box_solvers.rs @@ -59,7 +59,6 @@ pub fn ecdsa_secp256k1_verify( signature, ) .unwrap() - .into() } /// Verifies a ECDSA signature over the secp256r1 curve. @@ -81,5 +80,4 @@ pub fn ecdsa_secp256r1_verify( signature, ) .unwrap() - .into() } diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 60d27a489e2..4c23acea5db 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -106,7 +106,7 @@ pub async fn execute_circuit_with_black_box_solver( None => error.to_string(), }; - return Err(JsExecutionError::new(error_string.into(), call_stack).into()); + return Err(JsExecutionError::new(error_string, call_stack).into()); } ACVMStatus::RequiresForeignCall(foreign_call) => { let result = resolve_brillig(&foreign_call_handler, &foreign_call).await?; diff --git a/acvm-repo/acvm_js/src/lib.rs b/acvm-repo/acvm_js/src/lib.rs index 88afd1767c9..8ded77a4e22 100644 --- a/acvm-repo/acvm_js/src/lib.rs +++ b/acvm-repo/acvm_js/src/lib.rs @@ -1,31 +1,26 @@ -#![forbid(unsafe_code)] #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] -// TODO: Absence of per package targets -// https://doc.rust-lang.org/cargo/reference/unstable.html#per-package-target -// otherwise could be reorganized to make this file more pretty. +mod black_box_solvers; +mod build_info; +mod compression; +mod execute; +mod foreign_call; +mod js_execution_error; +mod js_witness_map; +mod logging; +mod public_witness; -cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - mod build_info; - mod compression; - mod execute; - mod foreign_call; - mod js_witness_map; - mod logging; - mod public_witness; - mod js_execution_error; - mod black_box_solvers; - - pub use black_box_solvers::{and, xor, sha256, blake2s256, keccak256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify}; - pub use build_info::build_info; - pub use compression::{compress_witness, decompress_witness}; - pub use execute::{execute_circuit, execute_circuit_with_black_box_solver, create_black_box_solver}; - pub use js_witness_map::JsWitnessMap; - pub use logging::init_log_level; - pub use public_witness::{get_public_parameters_witness, get_public_witness, get_return_witness}; - pub use js_execution_error::JsExecutionError; - } -} +pub use black_box_solvers::{ + and, blake2s256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, sha256, xor, +}; +pub use build_info::build_info; +pub use compression::{compress_witness, decompress_witness}; +pub use execute::{ + create_black_box_solver, execute_circuit, execute_circuit_with_black_box_solver, +}; +pub use js_execution_error::JsExecutionError; +pub use js_witness_map::JsWitnessMap; +pub use logging::init_log_level; +pub use public_witness::{get_public_parameters_witness, get_public_witness, get_return_witness}; diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index 396e4aa0146..8658c37420b 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -17,6 +17,7 @@ acir.workspace = true acvm_blackbox_solver.workspace = true thiserror.workspace = true num-traits.workspace = true +cfg-if = "1.0.0" rust-embed = { version = "6.6.0", features = [ "debug-embed", diff --git a/acvm-repo/bn254_blackbox_solver/src/lib.rs b/acvm-repo/bn254_blackbox_solver/src/lib.rs index be0e60ada96..231594170e3 100644 --- a/acvm-repo/bn254_blackbox_solver/src/lib.rs +++ b/acvm-repo/bn254_blackbox_solver/src/lib.rs @@ -20,10 +20,17 @@ pub struct Bn254BlackBoxSolver { } impl Bn254BlackBoxSolver { - #[cfg(target_arch = "wasm32")] pub async fn initialize() -> Bn254BlackBoxSolver { - let blackbox_vendor = Barretenberg::initialize().await; - Bn254BlackBoxSolver { blackbox_vendor } + // We fallback to the sync initialization of barretenberg on non-wasm targets. + // This ensures that wasm packages consuming this still build on the default target (useful for linting, etc.) + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + let blackbox_vendor = Barretenberg::initialize().await; + Bn254BlackBoxSolver { blackbox_vendor } + } else { + Bn254BlackBoxSolver::new() + } + } } #[cfg(not(target_arch = "wasm32"))] From b55a580388abc95bab6c6ef8c50eae3c5497eb3f Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 3 Apr 2024 16:10:04 +0100 Subject: [PATCH 139/416] fix(ssa): Do not use get_value_max_num_bits when we want pure type information (#4700) # Description ## Problem\* No issue as this was found by @jfecher while testing in debug mode where we get overflow errors after https://github.com/noir-lang/noir/pull/4691 Resolves https://github.com/noir-lang/noir/pull/4699#discussion_r1548634192 ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French --- .../src/ssa/ir/instruction/binary.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index e491807995b..9099268ace9 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -171,21 +171,6 @@ impl Binary { return SimplifyResult::SimplifiedTo(one); } - if operand_type.is_unsigned() { - // If we're comparing a variable against a constant value which lies outside of the range of - // values which the variable's type can take, we can assume that the equality will be false. - let constant = lhs.or(rhs); - let non_constant = if lhs.is_some() { self.rhs } else { self.lhs }; - if let Some(constant) = constant { - let max_possible_value = - 2u128.pow(dfg.get_value_max_num_bits(non_constant)) - 1; - if constant > max_possible_value.into() { - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); - return SimplifyResult::SimplifiedTo(zero); - } - } - } - if operand_type == Type::bool() { // Simplify forms of `(boolean == true)` into `boolean` if lhs_is_one { From 463fb7712b74c3afaeb87597a700121619850386 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 3 Apr 2024 16:13:16 +0100 Subject: [PATCH 140/416] chore: simplify how blns is loaded into tests (#4705) # Description ## Problem\* Resolves ## Summary\* Similarly to #4703, this PR simplifies how we load in the blns file for tests. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/build.rs | 15 --------------- .../noirc_frontend/src/{ => lexer}/blns/LICENSE | 0 .../noirc_frontend/src/{ => lexer}/blns/README.md | 0 .../src/{ => lexer}/blns/blns.base64.json | 0 compiler/noirc_frontend/src/lexer/lexer.rs | 2 +- 5 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 compiler/noirc_frontend/build.rs rename compiler/noirc_frontend/src/{ => lexer}/blns/LICENSE (100%) rename compiler/noirc_frontend/src/{ => lexer}/blns/README.md (100%) rename compiler/noirc_frontend/src/{ => lexer}/blns/blns.base64.json (100%) diff --git a/compiler/noirc_frontend/build.rs b/compiler/noirc_frontend/build.rs deleted file mode 100644 index 53ae9489168..00000000000 --- a/compiler/noirc_frontend/build.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::path::PathBuf; - -const BLNS_JSON_PATH: &str = "BLNS_JSON_PATH"; - -fn main() -> Result<(), String> { - let out_dir = std::env::var("OUT_DIR").unwrap(); - - let dest_path = PathBuf::from(out_dir.clone()).join("blns.base64.json"); - let dest_path_str = dest_path.to_str().unwrap(); - - println!("cargo:rustc-env={BLNS_JSON_PATH}={dest_path_str}"); - std::fs::copy("./src/blns/blns.base64.json", dest_path).unwrap(); - - Ok(()) -} diff --git a/compiler/noirc_frontend/src/blns/LICENSE b/compiler/noirc_frontend/src/lexer/blns/LICENSE similarity index 100% rename from compiler/noirc_frontend/src/blns/LICENSE rename to compiler/noirc_frontend/src/lexer/blns/LICENSE diff --git a/compiler/noirc_frontend/src/blns/README.md b/compiler/noirc_frontend/src/lexer/blns/README.md similarity index 100% rename from compiler/noirc_frontend/src/blns/README.md rename to compiler/noirc_frontend/src/lexer/blns/README.md diff --git a/compiler/noirc_frontend/src/blns/blns.base64.json b/compiler/noirc_frontend/src/lexer/blns/blns.base64.json similarity index 100% rename from compiler/noirc_frontend/src/blns/blns.base64.json rename to compiler/noirc_frontend/src/lexer/blns/blns.base64.json diff --git a/compiler/noirc_frontend/src/lexer/lexer.rs b/compiler/noirc_frontend/src/lexer/lexer.rs index cb21f58e58b..265b9e4b5a3 100644 --- a/compiler/noirc_frontend/src/lexer/lexer.rs +++ b/compiler/noirc_frontend/src/lexer/lexer.rs @@ -1160,7 +1160,7 @@ mod tests { fn test_big_list_of_naughty_strings() { use std::mem::discriminant; - let blns_contents = include_str!(env!("BLNS_JSON_PATH")); + let blns_contents = include_str!("./blns/blns.base64.json"); let blns_base64: Vec = serde_json::from_str(blns_contents).expect("BLNS json invalid"); for blns_base64_str in blns_base64 { From 386f6d0a5822912db878285cb001032a7c0ff622 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 3 Apr 2024 17:06:49 +0100 Subject: [PATCH 141/416] feat(acvm_js): Execute program (#4694) # Description ## Problem\* Resolves #4645 ## Summary\* In order to have a recursive `execute_circuit` function we now have a recursive async call. This requires boxing a future and having to making `execute_circuit` immutable in order to attach the final main witness to the witness stack. In our normal execution flow we could move the `ProgramExecutor` after our recursive async call to execute_circuit, we now need a lifetime on `self`. I also switched all `execute_circuit` methods in ACVM JS to use `execute_program` under the hood so that we do not have a breaking change yet but can still test `execute_program` using all of our already existing tests. I then added a couple extra acvm js tests for multiple acir calls and full witness stack (de)/compression. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [X] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French --- acvm-repo/acir/src/circuit/mod.rs | 36 +-- .../acir/src/native_types/witness_stack.rs | 6 +- .../acir/tests/test_program_serialization.rs | 93 +++++++ acvm-repo/acvm_js/src/compression.rs | 40 ++- acvm-repo/acvm_js/src/execute.rs | 232 +++++++++++++----- acvm-repo/acvm_js/src/js_witness_stack.rs | 71 ++++++ acvm-repo/acvm_js/src/lib.rs | 7 +- .../acvm_js/test/node/execute_circuit.test.ts | 37 +++ .../test/node/witness_conversion.test.ts | 15 +- .../acvm_js/test/shared/nested_acir_call.ts | 59 +++++ 10 files changed, 518 insertions(+), 78 deletions(-) create mode 100644 acvm-repo/acvm_js/src/js_witness_stack.rs create mode 100644 acvm-repo/acvm_js/test/shared/nested_acir_call.ts diff --git a/acvm-repo/acir/src/circuit/mod.rs b/acvm-repo/acir/src/circuit/mod.rs index b5d6348d34f..cb846bdaffa 100644 --- a/acvm-repo/acir/src/circuit/mod.rs +++ b/acvm-repo/acir/src/circuit/mod.rs @@ -216,25 +216,33 @@ impl std::fmt::Display for Circuit { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "current witness index : {}", self.current_witness_index)?; - let write_public_inputs = |f: &mut std::fmt::Formatter<'_>, - public_inputs: &PublicInputs| - -> Result<(), std::fmt::Error> { - write!(f, "[")?; - let public_input_indices = public_inputs.indices(); - for (index, public_input) in public_input_indices.iter().enumerate() { - write!(f, "{public_input}")?; - if index != public_input_indices.len() - 1 { - write!(f, ", ")?; + let write_witness_indices = + |f: &mut std::fmt::Formatter<'_>, indices: &[u32]| -> Result<(), std::fmt::Error> { + write!(f, "[")?; + for (index, witness_index) in indices.iter().enumerate() { + write!(f, "{witness_index}")?; + if index != indices.len() - 1 { + write!(f, ", ")?; + } } - } - writeln!(f, "]") - }; + writeln!(f, "]") + }; + + write!(f, "private parameters indices : ")?; + write_witness_indices( + f, + &self + .private_parameters + .iter() + .map(|witness| witness.witness_index()) + .collect::>(), + )?; write!(f, "public parameters indices : ")?; - write_public_inputs(f, &self.public_parameters)?; + write_witness_indices(f, &self.public_parameters.indices())?; write!(f, "return value indices : ")?; - write_public_inputs(f, &self.return_values)?; + write_witness_indices(f, &self.return_values.indices())?; for opcode in &self.opcodes { writeln!(f, "{opcode}")?; diff --git a/acvm-repo/acir/src/native_types/witness_stack.rs b/acvm-repo/acir/src/native_types/witness_stack.rs index a9e8f219b3e..7c79e3db431 100644 --- a/acvm-repo/acir/src/native_types/witness_stack.rs +++ b/acvm-repo/acir/src/native_types/witness_stack.rs @@ -21,7 +21,7 @@ pub struct WitnessStackError(#[from] SerializationError); /// An ordered set of witness maps for separate circuits #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)] pub struct WitnessStack { - pub stack: Vec, + stack: Vec, } #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default, Serialize, Deserialize)] @@ -37,6 +37,10 @@ impl WitnessStack { self.stack.push(StackItem { index, witness }); } + pub fn pop(&mut self) -> Option { + self.stack.pop() + } + pub fn peek(&self) -> Option<&StackItem> { self.stack.last() } diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index 8b04292dfaa..a5b683c15e1 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -362,3 +362,96 @@ fn memory_op_circuit() { assert_eq!(bytes, expected_serialization) } + +#[test] +fn nested_acir_call_circuit() { + // Circuit for the following program: + // fn main(x: Field, y: pub Field) { + // let z = nested_call(x, y); + // let z2 = nested_call(x, y); + // assert(z == z2); + // } + // #[fold] + // fn nested_call(x: Field, y: Field) -> Field { + // inner_call(x + 2, y) + // } + // #[fold] + // fn inner_call(x: Field, y: Field) -> Field { + // assert(x == y); + // x + // } + let nested_call = + Opcode::Call { id: 1, inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(2)] }; + let nested_call_two = + Opcode::Call { id: 1, inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(3)] }; + + let assert_nested_call_results = Opcode::AssertZero(Expression { + mul_terms: Vec::new(), + linear_combinations: vec![ + (FieldElement::one(), Witness(2)), + (-FieldElement::one(), Witness(3)), + ], + q_c: FieldElement::zero(), + }); + + let main = Circuit { + current_witness_index: 3, + private_parameters: BTreeSet::from([Witness(0)]), + public_parameters: PublicInputs([Witness(1)].into()), + opcodes: vec![nested_call, nested_call_two, assert_nested_call_results], + ..Circuit::default() + }; + + let call_parameter_addition = Opcode::AssertZero(Expression { + mul_terms: Vec::new(), + linear_combinations: vec![ + (FieldElement::one(), Witness(0)), + (-FieldElement::one(), Witness(2)), + ], + q_c: FieldElement::one() + FieldElement::one(), + }); + let call = + Opcode::Call { id: 2, inputs: vec![Witness(2), Witness(1)], outputs: vec![Witness(3)] }; + + let nested_call = Circuit { + current_witness_index: 3, + private_parameters: BTreeSet::from([Witness(0), Witness(1)]), + return_values: PublicInputs([Witness(3)].into()), + opcodes: vec![call_parameter_addition, call], + ..Circuit::default() + }; + + let assert_param_equality = Opcode::AssertZero(Expression { + mul_terms: Vec::new(), + linear_combinations: vec![ + (FieldElement::one(), Witness(0)), + (-FieldElement::one(), Witness(1)), + ], + q_c: FieldElement::zero(), + }); + + let inner_call = Circuit { + current_witness_index: 1, + private_parameters: BTreeSet::from([Witness(0), Witness(1)]), + return_values: PublicInputs([Witness(0)].into()), + opcodes: vec![assert_param_equality], + ..Circuit::default() + }; + + let program = Program { functions: vec![main, nested_call, inner_call] }; + + let bytes = Program::serialize_program(&program); + + let expected_serialization: Vec = vec![ + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, + 24, 109, 227, 191, 93, 101, 50, 123, 255, 35, 172, 99, 25, 83, 17, 250, 99, 14, 250, 224, + 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, 57, 108, 14, 91, 248, 202, 168, 65, + 255, 207, 122, 28, 180, 250, 244, 221, 244, 197, 223, 68, 182, 154, 197, 184, 134, 80, 54, + 95, 136, 233, 142, 62, 101, 137, 24, 98, 94, 133, 132, 162, 196, 135, 23, 230, 34, 65, 182, + 148, 211, 134, 137, 2, 23, 218, 99, 226, 93, 135, 185, 121, 123, 33, 84, 12, 234, 218, 192, + 64, 174, 3, 248, 47, 88, 48, 17, 150, 157, 183, 151, 95, 244, 86, 91, 221, 61, 10, 81, 31, + 178, 190, 110, 194, 102, 96, 76, 251, 202, 80, 13, 204, 77, 224, 25, 176, 70, 79, 197, 128, + 18, 64, 3, 4, 0, 0, + ]; + assert_eq!(bytes, expected_serialization); +} diff --git a/acvm-repo/acvm_js/src/compression.rs b/acvm-repo/acvm_js/src/compression.rs index 18e9216297e..8114e0d57d2 100644 --- a/acvm-repo/acvm_js/src/compression.rs +++ b/acvm-repo/acvm_js/src/compression.rs @@ -2,12 +2,12 @@ use acvm::acir::native_types::{WitnessMap, WitnessStack}; use js_sys::JsString; use wasm_bindgen::prelude::wasm_bindgen; -use crate::JsWitnessMap; +use crate::{JsWitnessMap, JsWitnessStack}; /// Compresses a `WitnessMap` into the binary format outputted by Nargo. /// -/// @param {Uint8Array} compressed_witness - A witness map. -/// @returns {WitnessMap} A compressed witness map +/// @param {WitnessMap} witness_map - A witness map. +/// @returns {Uint8Array} A compressed witness map #[wasm_bindgen(js_name = compressWitness, skip_jsdoc)] pub fn compress_witness(witness_map: JsWitnessMap) -> Result, JsString> { console_error_panic_hook::set_once(); @@ -21,6 +21,7 @@ pub fn compress_witness(witness_map: JsWitnessMap) -> Result, JsString> } /// Decompresses a compressed witness as outputted by Nargo into a `WitnessMap`. +/// This should be used to only fetch the witness map for the main function. /// /// @param {Uint8Array} compressed_witness - A compressed witness. /// @returns {WitnessMap} The decompressed witness map. @@ -28,8 +29,39 @@ pub fn compress_witness(witness_map: JsWitnessMap) -> Result, JsString> pub fn decompress_witness(compressed_witness: Vec) -> Result { console_error_panic_hook::set_once(); + let mut witness_stack = + WitnessStack::try_from(compressed_witness.as_slice()).map_err(|err| err.to_string())?; + + let witness = + witness_stack.pop().expect("Should have at least one witness on the stack").witness; + Ok(witness.into()) +} + +/// Compresses a `WitnessStack` into the binary format outputted by Nargo. +/// +/// @param {WitnessStack} witness_stack - A witness stack. +/// @returns {Uint8Array} A compressed witness stack +#[wasm_bindgen(js_name = compressWitnessStack, skip_jsdoc)] +pub fn compress_witness_stack(witness_stack: JsWitnessStack) -> Result, JsString> { + console_error_panic_hook::set_once(); + + let witness_stack = WitnessStack::from(witness_stack); + let compressed_witness_stack: Vec = + Vec::::try_from(witness_stack).map_err(|err| err.to_string())?; + + Ok(compressed_witness_stack) +} + +/// Decompresses a compressed witness stack as outputted by Nargo into a `WitnessStack`. +/// +/// @param {Uint8Array} compressed_witness - A compressed witness. +/// @returns {WitnessStack} The decompressed witness stack. +#[wasm_bindgen(js_name = decompressWitnessStack, skip_jsdoc)] +pub fn decompress_witness_stack(compressed_witness: Vec) -> Result { + console_error_panic_hook::set_once(); + let witness_stack = WitnessStack::try_from(compressed_witness.as_slice()).map_err(|err| err.to_string())?; - Ok(witness_stack.stack[0].witness.clone().into()) + Ok(witness_stack.into()) } diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 4c23acea5db..0e58ccf039c 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -1,5 +1,9 @@ +use std::{future::Future, pin::Pin}; + +use acvm::BlackBoxFunctionSolver; use acvm::{ - acir::circuit::Program, + acir::circuit::{Circuit, Program}, + acir::native_types::{WitnessMap, WitnessStack}, pwg::{ACVMStatus, ErrorLocation, OpcodeResolutionError, ACVM}, }; use bn254_blackbox_solver::Bn254BlackBoxSolver; @@ -9,7 +13,7 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::{ foreign_call::{resolve_brillig, ForeignCallHandler}, - JsExecutionError, JsWitnessMap, + JsExecutionError, JsWitnessMap, JsWitnessStack, }; #[wasm_bindgen] @@ -42,8 +46,16 @@ pub async fn execute_circuit( let solver = WasmBlackBoxFunctionSolver::initialize().await; - execute_circuit_with_black_box_solver(&solver, program, initial_witness, foreign_call_handler) - .await + let mut witness_stack = execute_program_with_native_type_return( + &solver, + program, + initial_witness, + &foreign_call_handler, + ) + .await?; + let witness_map = + witness_stack.pop().expect("Should have at least one witness on the stack").witness; + Ok(witness_map.into()) } /// Executes an ACIR circuit to generate the solved witness from the initial witness. @@ -56,69 +68,175 @@ pub async fn execute_circuit( #[wasm_bindgen(js_name = executeCircuitWithBlackBoxSolver, skip_jsdoc)] pub async fn execute_circuit_with_black_box_solver( solver: &WasmBlackBoxFunctionSolver, - // TODO(https://github.com/noir-lang/noir/issues/4428): These need to be updated to match the same interfaces - // as the native ACVM executor. Right now native execution still only handles one circuit so I do not feel the need - // to break the JS interface just yet. program: Vec, initial_witness: JsWitnessMap, foreign_call_handler: ForeignCallHandler, ) -> Result { console_error_panic_hook::set_once(); + + let mut witness_stack = execute_program_with_native_type_return( + solver, + program, + initial_witness, + &foreign_call_handler, + ) + .await?; + let witness_map = + witness_stack.pop().expect("Should have at least one witness on the stack").witness; + Ok(witness_map.into()) +} + +#[wasm_bindgen(js_name = executeProgram, skip_jsdoc)] +pub async fn execute_program( + program: Vec, + initial_witness: JsWitnessMap, + foreign_call_handler: ForeignCallHandler, +) -> Result { + console_error_panic_hook::set_once(); + + let solver = WasmBlackBoxFunctionSolver::initialize().await; + + execute_program_with_black_box_solver(&solver, program, initial_witness, &foreign_call_handler) + .await +} + +#[wasm_bindgen(js_name = executeProgramWithBlackBoxSolver, skip_jsdoc)] +pub async fn execute_program_with_black_box_solver( + solver: &WasmBlackBoxFunctionSolver, + program: Vec, + initial_witness: JsWitnessMap, + foreign_call_executor: &ForeignCallHandler, +) -> Result { + let witness_stack = execute_program_with_native_type_return( + solver, + program, + initial_witness, + foreign_call_executor, + ) + .await?; + + Ok(witness_stack.into()) +} + +async fn execute_program_with_native_type_return( + solver: &WasmBlackBoxFunctionSolver, + program: Vec, + initial_witness: JsWitnessMap, + foreign_call_executor: &ForeignCallHandler, +) -> Result { let program: Program = Program::deserialize_program(&program) - .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; - let circuit = match program.functions.len() { - 0 => return Ok(initial_witness), - 1 => &program.functions[0], - _ => return Err(JsExecutionError::new("Program contains multiple circuits however ACVM currently only supports programs containing a single circuit".to_string(), None).into()) - }; - - let mut acvm = ACVM::new(&solver.0, &circuit.opcodes, initial_witness.into()); - - loop { - let solver_status = acvm.solve(); - - match solver_status { - ACVMStatus::Solved => break, - ACVMStatus::InProgress => { - unreachable!("Execution should not stop while in `InProgress` state.") - } - ACVMStatus::Failure(error) => { - let (assert_message, call_stack) = match &error { - OpcodeResolutionError::UnsatisfiedConstrain { - opcode_location: ErrorLocation::Resolved(opcode_location), - } - | OpcodeResolutionError::IndexOutOfBounds { - opcode_location: ErrorLocation::Resolved(opcode_location), - .. - } => { - (circuit.get_assert_message(*opcode_location), Some(vec![*opcode_location])) + .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; + + let executor = ProgramExecutor::new(&program.functions, &solver.0, foreign_call_executor); + let witness_stack = executor.execute(initial_witness.into()).await?; + + Ok(witness_stack) +} + +struct ProgramExecutor<'a, B: BlackBoxFunctionSolver> { + functions: &'a [Circuit], + + blackbox_solver: &'a B, + + foreign_call_handler: &'a ForeignCallHandler, +} + +impl<'a, B: BlackBoxFunctionSolver> ProgramExecutor<'a, B> { + fn new( + functions: &'a [Circuit], + blackbox_solver: &'a B, + foreign_call_handler: &'a ForeignCallHandler, + ) -> Self { + ProgramExecutor { functions, blackbox_solver, foreign_call_handler } + } + + async fn execute(&self, initial_witness: WitnessMap) -> Result { + let main = &self.functions[0]; + + let mut witness_stack = WitnessStack::default(); + let main_witness = self.execute_circuit(main, initial_witness, &mut witness_stack).await?; + witness_stack.push(0, main_witness); + Ok(witness_stack) + } + + fn execute_circuit( + &'a self, + circuit: &'a Circuit, + initial_witness: WitnessMap, + witness_stack: &'a mut WitnessStack, + ) -> Pin> + 'a>> { + Box::pin(async { + let mut acvm = ACVM::new(self.blackbox_solver, &circuit.opcodes, initial_witness); + + loop { + let solver_status = acvm.solve(); + + match solver_status { + ACVMStatus::Solved => break, + ACVMStatus::InProgress => { + unreachable!("Execution should not stop while in `InProgress` state.") } - OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { - let failing_opcode = - call_stack.last().expect("Brillig error call stacks cannot be empty"); - (circuit.get_assert_message(*failing_opcode), Some(call_stack.clone())) + ACVMStatus::Failure(error) => { + let (assert_message, call_stack) = match &error { + OpcodeResolutionError::UnsatisfiedConstrain { + opcode_location: ErrorLocation::Resolved(opcode_location), + } + | OpcodeResolutionError::IndexOutOfBounds { + opcode_location: ErrorLocation::Resolved(opcode_location), + .. + } => ( + circuit.get_assert_message(*opcode_location), + Some(vec![*opcode_location]), + ), + OpcodeResolutionError::BrilligFunctionFailed { call_stack, .. } => { + let failing_opcode = call_stack + .last() + .expect("Brillig error call stacks cannot be empty"); + ( + circuit.get_assert_message(*failing_opcode), + Some(call_stack.clone()), + ) + } + _ => (None, None), + }; + + let error_string = match &assert_message { + Some(assert_message) => format!("Assertion failed: {}", assert_message), + None => error.to_string(), + }; + + return Err(JsExecutionError::new(error_string, call_stack).into()); } - _ => (None, None), - }; - - let error_string = match &assert_message { - Some(assert_message) => format!("Assertion failed: {}", assert_message), - None => error.to_string(), - }; + ACVMStatus::RequiresForeignCall(foreign_call) => { + let result = + resolve_brillig(self.foreign_call_handler, &foreign_call).await?; - return Err(JsExecutionError::new(error_string, call_stack).into()); + acvm.resolve_pending_foreign_call(result); + } + ACVMStatus::RequiresAcirCall(call_info) => { + let acir_to_call = &self.functions[call_info.id as usize]; + let initial_witness = call_info.initial_witness; + let call_solved_witness = self + .execute_circuit(acir_to_call, initial_witness, witness_stack) + .await?; + let mut call_resolved_outputs = Vec::new(); + for return_witness_index in acir_to_call.return_values.indices() { + if let Some(return_value) = + call_solved_witness.get_index(return_witness_index) + { + call_resolved_outputs.push(*return_value); + } else { + // TODO: look at changing this call stack from None + return Err(JsExecutionError::new(format!("Failed to read from solved witness of ACIR call at witness {}", return_witness_index), None).into()); + } + } + acvm.resolve_pending_acir_call(call_resolved_outputs); + witness_stack.push(call_info.id, call_solved_witness.clone()); + } + } } - ACVMStatus::RequiresForeignCall(foreign_call) => { - let result = resolve_brillig(&foreign_call_handler, &foreign_call).await?; - acvm.resolve_pending_foreign_call(result); - } - ACVMStatus::RequiresAcirCall(_) => { - todo!("Handle acir calls in acvm JS"); - } - } + Ok(acvm.finalize()) + }) } - - let witness_map = acvm.finalize(); - Ok(witness_map.into()) } diff --git a/acvm-repo/acvm_js/src/js_witness_stack.rs b/acvm-repo/acvm_js/src/js_witness_stack.rs new file mode 100644 index 00000000000..59f2dbc051e --- /dev/null +++ b/acvm-repo/acvm_js/src/js_witness_stack.rs @@ -0,0 +1,71 @@ +use acvm::acir::native_types::WitnessStack; +use js_sys::{Array, Map, Object}; +use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; + +use crate::JsWitnessMap; + +#[wasm_bindgen(typescript_custom_section)] +const WITNESS_STACK: &'static str = r#" +export type StackItem = { + index: number; + witness: WitnessMap; +} + +export type WitnessStack = Array; +"#; + +// WitnessStack +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(extends = Array, js_name = "WitnessStack", typescript_type = "WitnessStack")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsWitnessStack; + + #[wasm_bindgen(constructor, js_class = "Array")] + pub fn new() -> JsWitnessStack; + + #[wasm_bindgen(extends = Object, js_name = "StackItem", typescript_type = "StackItem")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsStackItem; + + #[wasm_bindgen(constructor, js_class = "Object")] + pub fn new() -> JsStackItem; +} + +impl Default for JsWitnessStack { + fn default() -> Self { + Self::new() + } +} + +impl From for JsWitnessStack { + fn from(mut witness_stack: WitnessStack) -> Self { + let js_witness_stack = JsWitnessStack::new(); + while let Some(stack_item) = witness_stack.pop() { + let js_map = JsWitnessMap::from(stack_item.witness); + let js_index = JsValue::from_f64(stack_item.index.into()); + + let entry_map = Map::new(); + entry_map.set(&JsValue::from_str("index"), &js_index); + entry_map.set(&JsValue::from_str("witness"), &js_map); + let stack_item = Object::from_entries(&entry_map).unwrap(); + + js_witness_stack.push(&stack_item); + } + // `reverse()` returns an `Array` so we have to wrap it + JsWitnessStack { obj: js_witness_stack.reverse() } + } +} + +impl From for WitnessStack { + fn from(js_witness_stack: JsWitnessStack) -> Self { + let mut witness_stack = WitnessStack::default(); + js_witness_stack.for_each(&mut |stack_item, _, _| { + let values_array = Object::values(&Object::from(stack_item)); + let index = values_array.get(0).as_f64().unwrap() as u32; + let js_witness_map: JsWitnessMap = values_array.get(1).into(); + witness_stack.push(index, js_witness_map.into()); + }); + witness_stack + } +} diff --git a/acvm-repo/acvm_js/src/lib.rs b/acvm-repo/acvm_js/src/lib.rs index 8ded77a4e22..d7ecc0ae192 100644 --- a/acvm-repo/acvm_js/src/lib.rs +++ b/acvm-repo/acvm_js/src/lib.rs @@ -9,6 +9,7 @@ mod execute; mod foreign_call; mod js_execution_error; mod js_witness_map; +mod js_witness_stack; mod logging; mod public_witness; @@ -16,11 +17,15 @@ pub use black_box_solvers::{ and, blake2s256, ecdsa_secp256k1_verify, ecdsa_secp256r1_verify, keccak256, sha256, xor, }; pub use build_info::build_info; -pub use compression::{compress_witness, decompress_witness}; +pub use compression::{ + compress_witness, compress_witness_stack, decompress_witness, decompress_witness_stack, +}; pub use execute::{ create_black_box_solver, execute_circuit, execute_circuit_with_black_box_solver, + execute_program, execute_program_with_black_box_solver, }; pub use js_execution_error::JsExecutionError; pub use js_witness_map::JsWitnessMap; +pub use js_witness_stack::JsWitnessStack; pub use logging::init_log_level; pub use public_witness::{get_public_parameters_witness, get_public_witness, get_return_witness}; diff --git a/acvm-repo/acvm_js/test/node/execute_circuit.test.ts b/acvm-repo/acvm_js/test/node/execute_circuit.test.ts index adee3c15312..32487f8bbba 100644 --- a/acvm-repo/acvm_js/test/node/execute_circuit.test.ts +++ b/acvm-repo/acvm_js/test/node/execute_circuit.test.ts @@ -6,6 +6,9 @@ import { WasmBlackBoxFunctionSolver, WitnessMap, ForeignCallHandler, + executeProgram, + WitnessStack, + StackItem, } from '@noir-lang/acvm_js'; it('successfully executes circuit and extracts return value', async () => { @@ -157,3 +160,37 @@ it('successfully executes 500 circuits with same backend', async function () { expect(solvedWitness).to.be.deep.eq(expectedWitnessMap); } }); + +/** + * Below are all the same tests as above but using `executeProgram` + * TODO: also add a couple tests for executing multiple circuits + */ +it('executeProgram: successfully executes program and extracts return value', async () => { + const { bytecode, initialWitnessMap, resultWitness, expectedResult } = await import('../shared/addition'); + + const witnessStack: WitnessStack = await executeProgram(bytecode, initialWitnessMap, () => { + throw Error('unexpected oracle'); + }); + + const solvedStackItem: StackItem = witnessStack[0]; + expect(solvedStackItem.index).to.be.eq(0); + const solvedWitnessMap: WitnessMap = solvedStackItem.witness; + + // Witness stack should be consistent with initial witness + initialWitnessMap.forEach((value, key) => { + expect(solvedWitnessMap.get(key) as string).to.be.eq(value); + }); + + // Solved witness should contain expected return value + expect(solvedWitnessMap.get(resultWitness)).to.be.eq(expectedResult); +}); + +it('executeProgram: successfully process a program of acir functions with a nested call', async () => { + const { bytecode, initialWitnessMap, expectedWitnessStack } = await import('../shared/nested_acir_call'); + + const witnessStack: WitnessStack = await executeProgram(bytecode, initialWitnessMap, () => { + throw Error('unexpected oracle'); + }); + + expect(witnessStack).to.be.deep.eq(expectedWitnessStack); +}); diff --git a/acvm-repo/acvm_js/test/node/witness_conversion.test.ts b/acvm-repo/acvm_js/test/node/witness_conversion.test.ts index 41291c894ea..c6dccb4c83d 100644 --- a/acvm-repo/acvm_js/test/node/witness_conversion.test.ts +++ b/acvm-repo/acvm_js/test/node/witness_conversion.test.ts @@ -1,6 +1,7 @@ import { expect } from 'chai'; -import { compressWitness, decompressWitness } from '@noir-lang/acvm_js'; +import { compressWitness, decompressWitness, compressWitnessStack, decompressWitnessStack } from '@noir-lang/acvm_js'; import { expectedCompressedWitnessMap, expectedWitnessMap } from '../shared/witness_compression'; +import { expectedCompressedWitnessStack, expectedWitnessStack } from '../shared/nested_acir_call'; it('successfully compresses the witness', () => { const compressedWitnessMap = compressWitness(expectedWitnessMap); @@ -13,3 +14,15 @@ it('successfully decompresses the witness', () => { expect(witnessMap).to.be.deep.eq(expectedWitnessMap); }); + +it('successfully compresses the witness stack', () => { + const compressedWitnessStack = compressWitnessStack(expectedWitnessStack); + + expect(compressedWitnessStack).to.be.deep.eq(expectedCompressedWitnessStack); +}); + +it('successfully decompresses the witness stack', () => { + const witnessStack = decompressWitnessStack(expectedCompressedWitnessStack); + + expect(witnessStack).to.be.deep.eq(expectedWitnessStack); +}); diff --git a/acvm-repo/acvm_js/test/shared/nested_acir_call.ts b/acvm-repo/acvm_js/test/shared/nested_acir_call.ts new file mode 100644 index 00000000000..ce91282a681 --- /dev/null +++ b/acvm-repo/acvm_js/test/shared/nested_acir_call.ts @@ -0,0 +1,59 @@ +import { WitnessMap, StackItem, WitnessStack } from '@noir-lang/acvm_js'; + +// See `nested_acir_call_circuit` integration test in `acir/tests/test_program_serialization.rs`. +export const bytecode = Uint8Array.from([ + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, 24, 109, 227, 191, 93, 101, + 50, 123, 255, 35, 172, 99, 25, 83, 17, 250, 99, 14, 250, 224, 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, 57, + 108, 14, 91, 248, 202, 168, 65, 255, 207, 122, 28, 180, 250, 244, 221, 244, 197, 223, 68, 182, 154, 197, 184, 134, 80, + 54, 95, 136, 233, 142, 62, 101, 137, 24, 98, 94, 133, 132, 162, 196, 135, 23, 230, 34, 65, 182, 148, 211, 134, 137, 2, + 23, 218, 99, 226, 93, 135, 185, 121, 123, 33, 84, 12, 234, 218, 192, 64, 174, 3, 248, 47, 88, 48, 17, 150, 157, 183, + 151, 95, 244, 86, 91, 221, 61, 10, 81, 31, 178, 190, 110, 194, 102, 96, 76, 251, 202, 80, 13, 204, 77, 224, 25, 176, + 70, 79, 197, 128, 18, 64, 3, 4, 0, 0, +]); + +export const initialWitnessMap: WitnessMap = new Map([ + [0, '0x0000000000000000000000000000000000000000000000000000000000000008'], + [1, '0x000000000000000000000000000000000000000000000000000000000000000a'], +]); + +const inner_call_witness: StackItem = { + index: 2, + witness: new Map([ + [0, '0x000000000000000000000000000000000000000000000000000000000000000a'], + [1, '0x000000000000000000000000000000000000000000000000000000000000000a'], + ]), +}; + +const nested_call_witness: StackItem = { + index: 1, + witness: new Map([ + [0, '0x0000000000000000000000000000000000000000000000000000000000000008'], + [1, '0x000000000000000000000000000000000000000000000000000000000000000a'], + [2, '0x000000000000000000000000000000000000000000000000000000000000000a'], + [3, '0x000000000000000000000000000000000000000000000000000000000000000a'], + ]), +}; + +const main_witness: StackItem = { + index: 0, + witness: new Map([ + [0, '0x0000000000000000000000000000000000000000000000000000000000000008'], + [1, '0x000000000000000000000000000000000000000000000000000000000000000a'], + [2, '0x000000000000000000000000000000000000000000000000000000000000000a'], + [3, '0x000000000000000000000000000000000000000000000000000000000000000a'], + ]), +}; + +export const expectedWitnessStack: WitnessStack = [ + inner_call_witness, + nested_call_witness, + inner_call_witness, + nested_call_witness, + main_witness, +]; + +export const expectedCompressedWitnessStack = Uint8Array.from([ + 31, 139, 8, 0, 0, 0, 0, 0, 2, 255, 237, 145, 177, 13, 0, 32, 8, 4, 17, 117, 31, 75, 75, 87, 113, 255, 37, 44, 196, 5, + 228, 42, 194, 39, 132, 238, 114, 249, 239, 114, 163, 118, 47, 203, 254, 240, 101, 23, 152, 213, 120, 199, 73, 58, 42, + 200, 170, 176, 87, 238, 27, 119, 95, 201, 238, 190, 89, 7, 37, 195, 196, 176, 4, 5, 0, 0, +]); From 079cb2a99d2d50b50688bfb56fa014acde3e3d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Wed, 3 Apr 2024 19:32:06 +0200 Subject: [PATCH 142/416] fix: Field comparisons (#4704) # Description ## Problem\* Field comparisons could be tricked by a malicious prover by decomposing 0 as either (PLO,PHI) o (0,0). Now we reuse instead the assert_gt code by extracting it to an assert_gt_limbs that is shared among decompose (to check that the decomposition of x is less than the decomposition of the field modulus, that is, that the decomposition of the field modulus is greater than the decomposition of x) and assert_gt (to check that the decomposition of a is greater than the decomposition of b) ## Summary\* ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- noir_stdlib/src/field/bn254.nr | 37 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/noir_stdlib/src/field/bn254.nr b/noir_stdlib/src/field/bn254.nr index 765f8a9d849..d70310be391 100644 --- a/noir_stdlib/src/field/bn254.nr +++ b/noir_stdlib/src/field/bn254.nr @@ -21,11 +21,23 @@ unconstrained fn decompose_unsafe(x: Field) -> (Field, Field) { (low, high) } +// Assert that (alo > blo && ahi >= bhi) || (alo <= blo && ahi > bhi) +fn assert_gt_limbs(a: (Field, Field), b: (Field, Field)) { + let (alo, ahi) = a; + let (blo, bhi) = b; + let borrow = lte_unsafe(alo, blo, 16); + + let rlo = alo - blo - 1 + (borrow as Field) * TWO_POW_128; + let rhi = ahi - bhi - (borrow as Field); + + rlo.assert_max_bit_size(128); + rhi.assert_max_bit_size(128); +} + /// Decompose a single field into two 16 byte fields. pub fn decompose(x: Field) -> (Field, Field) { // Take hints of the decomposition let (xlo, xhi) = decompose_unsafe(x); - let borrow = lt_unsafe(PLO, xlo, 16); // Range check the limbs xlo.assert_max_bit_size(128); @@ -34,13 +46,8 @@ pub fn decompose(x: Field) -> (Field, Field) { // Check that the decomposition is correct assert_eq(x, xlo + TWO_POW_128 * xhi); - // Check that (xlo < plo && xhi <= phi) || (xlo >= plo && xhi < phi) - let rlo = PLO - xlo + (borrow as Field) * TWO_POW_128; - let rhi = PHI - xhi - (borrow as Field); - - rlo.assert_max_bit_size(128); - rhi.assert_max_bit_size(128); - + // Assert that the decomposition of P is greater than the decomposition of x + assert_gt_limbs((PLO, PHI), (xlo, xhi)); (xlo, xhi) } @@ -69,17 +76,11 @@ unconstrained fn lte_unsafe(x: Field, y: Field, num_bytes: u32) -> bool { pub fn assert_gt(a: Field, b: Field) { // Decompose a and b - let (alo, ahi) = decompose(a); - let (blo, bhi) = decompose(b); - - let borrow = lte_unsafe(alo, blo, 16); + let a_limbs = decompose(a); + let b_limbs = decompose(b); - // Assert that (alo > blo && ahi >= bhi) || (alo <= blo && ahi > bhi) - let rlo = alo - blo - 1 + (borrow as Field) * TWO_POW_128; - let rhi = ahi - bhi - (borrow as Field); - - rlo.assert_max_bit_size(128); - rhi.assert_max_bit_size(128); + // Assert that a_limbs is greater than b_limbs + assert_gt_limbs(a_limbs, b_limbs) } pub fn assert_lt(a: Field, b: Field) { From 8fea40576f262bd5bb588923c0660d8967404e56 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 3 Apr 2024 19:32:23 +0100 Subject: [PATCH 143/416] fix(acvm): Mark outputs of Opcode::Call solvable (#4708) # Description ## Problem\* Resolves No issue as bug found while experimenting ## Summary\* We should mark the outputs of a call as solvable. Otherwise we can potentially have more than one linear term and the outputs of call will not be used by the transformers. The new test `fold_call_witness_condition` would previously error out with the following when `nargo info` was ran: ``` The backend encountered an error: "backend_binary: /home/runner/work/aztec-packages/aztec-packages/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp:57: poly_triple acir_format::serialize_arithmetic_gate(const Program::Expression &): Assertion `(arg.mul_terms.size() <= 1)' failed.\n" ``` Now it successfully calls nargo info on main (we still need to update fetching the info for every circuit). ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- acvm-repo/acvm/src/compiler/transformers/mod.rs | 6 +++++- .../fold_call_witness_condition/Nargo.toml | 7 +++++++ .../fold_call_witness_condition/Prover.toml | 5 +++++ .../fold_call_witness_condition/src/main.nr | 16 ++++++++++++++++ tooling/debugger/ignored-tests.txt | 1 + 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test_programs/execution_success/fold_call_witness_condition/Nargo.toml create mode 100644 test_programs/execution_success/fold_call_witness_condition/Prover.toml create mode 100644 test_programs/execution_success/fold_call_witness_condition/src/main.nr diff --git a/acvm-repo/acvm/src/compiler/transformers/mod.rs b/acvm-repo/acvm/src/compiler/transformers/mod.rs index 1ba261b09a3..003cd4279a1 100644 --- a/acvm-repo/acvm/src/compiler/transformers/mod.rs +++ b/acvm-repo/acvm/src/compiler/transformers/mod.rs @@ -142,7 +142,11 @@ pub(super) fn transform_internal( new_acir_opcode_positions.push(acir_opcode_positions[index]); transformed_opcodes.push(opcode); } - Opcode::Call { .. } => { + Opcode::Call { ref outputs, .. } => { + for witness in outputs { + transformer.mark_solvable(*witness); + } + // `Call` does not write values to the `WitnessMap` // A separate ACIR function should have its own respective `WitnessMap` new_acir_opcode_positions.push(acir_opcode_positions[index]); diff --git a/test_programs/execution_success/fold_call_witness_condition/Nargo.toml b/test_programs/execution_success/fold_call_witness_condition/Nargo.toml new file mode 100644 index 00000000000..cedaea348c5 --- /dev/null +++ b/test_programs/execution_success/fold_call_witness_condition/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_call_witness_condition" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/fold_call_witness_condition/Prover.toml b/test_programs/execution_success/fold_call_witness_condition/Prover.toml new file mode 100644 index 00000000000..8481ce25648 --- /dev/null +++ b/test_programs/execution_success/fold_call_witness_condition/Prover.toml @@ -0,0 +1,5 @@ +# TODO(https://github.com/noir-lang/noir/issues/4707): Change these inputs to fail the assertion in `fn return_value` +# and change `enable` to false. For now we need the inputs to pass as we do not handle predicates with ACIR calls +x = "5" +y = "10" +enable = true \ No newline at end of file diff --git a/test_programs/execution_success/fold_call_witness_condition/src/main.nr b/test_programs/execution_success/fold_call_witness_condition/src/main.nr new file mode 100644 index 00000000000..5dc75e4a99f --- /dev/null +++ b/test_programs/execution_success/fold_call_witness_condition/src/main.nr @@ -0,0 +1,16 @@ +global NUM_RESULTS = 2; +fn main(x: Field, y: pub Field, enable: bool) -> pub [Field; NUM_RESULTS] { + let mut result = [0; NUM_RESULTS]; + for i in 0..NUM_RESULTS { + if enable { + result[i] = return_value(x, y); + } + } + result +} + +#[fold] +fn return_value(x: Field, y: Field) -> Field { + assert(x != y); + x +} diff --git a/tooling/debugger/ignored-tests.txt b/tooling/debugger/ignored-tests.txt index 5a42ef36e7e..4507aeb8545 100644 --- a/tooling/debugger/ignored-tests.txt +++ b/tooling/debugger/ignored-tests.txt @@ -14,3 +14,4 @@ signed_comparison to_bytes_integration fold_basic fold_basic_nested_call +fold_call_witness_condition From 68f9eeb54655dc87b6ecaec75c93b20d34ff8bad Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 3 Apr 2024 19:51:53 +0100 Subject: [PATCH 144/416] chore: simplify how `acvm_backend.wasm` is embedded (#4703) # Description ## Problem\* Resolves ## Summary\* We only need a single file to be embedded into `bn254_blackbox_solver` so we don't need the overhead of `rust_embed`. We can instead just assign the bytes to a const variable. I've also removed some build dependencies which are unnecessary. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 16 -------------- acvm-repo/bn254_blackbox_solver/Cargo.toml | 20 ++---------------- acvm-repo/bn254_blackbox_solver/build.rs | 14 ------------ .../src/{ => wasm}/acvm_backend.wasm | Bin .../bn254_blackbox_solver/src/wasm/mod.rs | 13 ++++-------- 5 files changed, 6 insertions(+), 57 deletions(-) delete mode 100644 acvm-repo/bn254_blackbox_solver/build.rs rename acvm-repo/bn254_blackbox_solver/src/{ => wasm}/acvm_backend.wasm (100%) diff --git a/Cargo.lock b/Cargo.lock index 2371ef6df05..906e49df6ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -606,16 +606,11 @@ dependencies = [ "ark-ec", "ark-ff", "cfg-if 1.0.0", - "flate2", "getrandom 0.2.10", "js-sys", "noir_grumpkin", "num-bigint", "num-traits", - "pkg-config", - "reqwest", - "rust-embed", - "tar", "thiserror", "wasm-bindgen-futures", "wasmer", @@ -4037,7 +4032,6 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "shellexpand", "syn 2.0.32", "walkdir", ] @@ -4048,7 +4042,6 @@ version = "7.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d38ff6bf570dc3bb7100fce9f7b60c33fa71d80e88da3f2580df4ff2bdded74" dependencies = [ - "globset", "sha2", "walkdir", ] @@ -4469,15 +4462,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" -[[package]] -name = "shellexpand" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" -dependencies = [ - "dirs", -] - [[package]] name = "signature" version = "1.6.4" diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index 8658c37420b..1ad5103d2cb 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -19,15 +19,8 @@ thiserror.workspace = true num-traits.workspace = true cfg-if = "1.0.0" -rust-embed = { version = "6.6.0", features = [ - "debug-embed", - "interpolate-folder-path", - "include-exclude", -] } - -grumpkin = { version = "0.1.0", package = "noir_grumpkin", features = [ - "std", -] } # BN254 fixed base scalar multiplication solver +# BN254 fixed base scalar multiplication solver +grumpkin = { version = "0.1.0", package = "noir_grumpkin", features = ["std"] } ark-ec = { version = "^0.4.0", default-features = false } ark-ff = { version = "^0.4.0", default-features = false } num-bigint.workspace = true @@ -45,15 +38,6 @@ js-sys.workspace = true getrandom.workspace = true wasmer = "4.2.6" -[build-dependencies] -pkg-config = "0.3" -tar = "~0.4.15" -flate2 = "~1.0.1" -reqwest = { version = "0.11.20", default-features = false, features = [ - "rustls-tls", - "blocking", -] } - [features] default = ["bn254"] bn254 = ["acir/bn254"] diff --git a/acvm-repo/bn254_blackbox_solver/build.rs b/acvm-repo/bn254_blackbox_solver/build.rs deleted file mode 100644 index 4269c86aba0..00000000000 --- a/acvm-repo/bn254_blackbox_solver/build.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::path::PathBuf; - -const BARRETENBERG_BIN_DIR: &str = "BARRETENBERG_BIN_DIR"; - -fn main() -> Result<(), String> { - let out_dir = std::env::var("OUT_DIR").unwrap(); - - let dest_path = PathBuf::from(out_dir.clone()).join("acvm_backend.wasm"); - - println!("cargo:rustc-env={BARRETENBERG_BIN_DIR}={out_dir}"); - std::fs::copy("./src/acvm_backend.wasm", dest_path).unwrap(); - - Ok(()) -} diff --git a/acvm-repo/bn254_blackbox_solver/src/acvm_backend.wasm b/acvm-repo/bn254_blackbox_solver/src/wasm/acvm_backend.wasm similarity index 100% rename from acvm-repo/bn254_blackbox_solver/src/acvm_backend.wasm rename to acvm-repo/bn254_blackbox_solver/src/wasm/acvm_backend.wasm diff --git a/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs b/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs index 3c867ae48dd..f4f6f56aa99 100644 --- a/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs +++ b/acvm-repo/bn254_blackbox_solver/src/wasm/mod.rs @@ -76,10 +76,7 @@ use wasmer::{ pub(super) const WASM_SCRATCH_BYTES: usize = 1024; /// Embed the Barretenberg WASM file -#[derive(rust_embed::RustEmbed)] -#[folder = "$BARRETENBERG_BIN_DIR"] -#[include = "acvm_backend.wasm"] -struct Wasm; +const WASM_BIN: &[u8] = include_bytes!("./acvm_backend.wasm"); impl Barretenberg { #[cfg(not(target_arch = "wasm32"))] @@ -287,7 +284,7 @@ fn instance_load() -> (Instance, Memory, Store) { let (memory, mut store, custom_imports) = init_memory_and_state(); - let module = Module::new(&store, Wasm::get("acvm_backend.wasm").unwrap().data).unwrap(); + let module = Module::new(&store, WASM_BIN).unwrap(); (Instance::new(&mut store, &module, &custom_imports).unwrap(), memory, store) } @@ -299,9 +296,7 @@ async fn instance_load() -> (Instance, Memory, Store) { let (memory, mut store, custom_imports) = init_memory_and_state(); - let wasm_binary = Wasm::get("acvm_backend.wasm").unwrap().data; - - let js_bytes = unsafe { js_sys::Uint8Array::view(&wasm_binary) }; + let js_bytes = unsafe { js_sys::Uint8Array::view(&WASM_BIN) }; let js_module_promise = WebAssembly::compile(&js_bytes); let js_module: js_sys::WebAssembly::Module = wasm_bindgen_futures::JsFuture::from(js_module_promise).await.unwrap().into(); @@ -309,7 +304,7 @@ async fn instance_load() -> (Instance, Memory, Store) { let js_instance_promise = WebAssembly::instantiate_module(&js_module, &custom_imports.as_jsvalue(&store).into()); let js_instance = wasm_bindgen_futures::JsFuture::from(js_instance_promise).await.unwrap(); - let module: wasmer::Module = (js_module, wasm_binary).into(); + let module = wasmer::Module::from((js_module, WASM_BIN)); let instance: wasmer::Instance = Instance::from_jsvalue(&mut store, &module, &js_instance) .map_err(|_| "Error while creating BlackBox Functions vendor instance") .unwrap(); From 62423d552beca749b6f86b1330555aab18db58d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 4 Apr 2024 16:19:56 +0200 Subject: [PATCH 145/416] feat: Allow slices to brillig entry points (#4713) # Description ## Problem\* Allows passing slices to brillig entry points, since ACIR knows the initial length of the slice and is fixed at compile time. ## Summary\* ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_fn.rs | 19 +----- .../src/brillig/brillig_ir/artifact.rs | 5 +- .../src/brillig/brillig_ir/entry_point.rs | 60 ++++++++++++++----- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 46 ++++++++++++-- .../brillig_slice_input/Nargo.toml | 6 ++ .../brillig_slice_input/src/main.nr | 40 +++++++++++++ 6 files changed, 139 insertions(+), 37 deletions(-) create mode 100644 test_programs/execution_success/brillig_slice_input/Nargo.toml create mode 100644 test_programs/execution_success/brillig_slice_input/src/main.nr diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs index 92027026ce8..617e400b92f 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_fn.rs @@ -70,7 +70,7 @@ impl FunctionContext { function_id.to_string() } - fn ssa_type_to_parameter(typ: &Type) -> BrilligParameter { + pub(crate) fn ssa_type_to_parameter(typ: &Type) -> BrilligParameter { match typ { Type::Numeric(_) | Type::Reference(_) => { BrilligParameter::SingleAddr(get_bit_size_from_ssa_type(typ)) @@ -81,26 +81,13 @@ impl FunctionContext { }), *size, ), - Type::Slice(item_type) => { - BrilligParameter::Slice(vecmap(item_type.iter(), |item_typ| { - FunctionContext::ssa_type_to_parameter(item_typ) - })) + Type::Slice(_) => { + panic!("ICE: Slice parameters cannot be derived from type information") } _ => unimplemented!("Unsupported function parameter/return type {typ:?}"), } } - /// Collects the parameters of a given function - pub(crate) fn parameters(func: &Function) -> Vec { - func.parameters() - .iter() - .map(|&value_id| { - let typ = func.dfg.type_of_value(value_id); - FunctionContext::ssa_type_to_parameter(&typ) - }) - .collect() - } - /// Collects the return values of a given function pub(crate) fn return_values(func: &Function) -> Vec { func.returns() diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs index 90a5d54ae59..8ce15ba4e73 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/artifact.rs @@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap}; use crate::ssa::ir::dfg::CallStack; -/// Represents a parameter or a return value of a function. +/// Represents a parameter or a return value of an entry point function. #[derive(Debug, Clone)] pub(crate) enum BrilligParameter { /// A single address parameter or return value. Holds the bit size of the parameter. @@ -11,7 +11,8 @@ pub(crate) enum BrilligParameter { /// An array parameter or return value. Holds the type of an array item and its size. Array(Vec, usize), /// A slice parameter or return value. Holds the type of a slice item. - Slice(Vec), + /// Only known-length slices can be passed to brillig entry points, so the size is available as well. + Slice(Vec, usize), } /// The result of compiling and linking brillig artifacts. diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index 1d823ded718..db872487fcc 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -1,6 +1,6 @@ use super::{ artifact::{BrilligArtifact, BrilligParameter}, - brillig_variable::{BrilligArray, BrilligVariable, SingleAddrVariable}, + brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable}, debug_show::DebugShow, registers::BrilligRegistersContext, BrilligBinaryOp, BrilligContext, ReservedRegisters, @@ -83,24 +83,56 @@ impl BrilligContext { current_calldata_pointer += flattened_size; var } - BrilligParameter::Slice(_) => unimplemented!("Unsupported slices as parameter"), + BrilligParameter::Slice(_, _) => { + let pointer_to_the_array_in_calldata = + self.make_usize_constant_instruction(current_calldata_pointer.into()); + + let flattened_size = BrilligContext::flattened_size(argument); + let size_register = self.make_usize_constant_instruction(flattened_size.into()); + let rc_register = self.make_usize_constant_instruction(1_usize.into()); + + let var = BrilligVariable::BrilligVector(BrilligVector { + pointer: pointer_to_the_array_in_calldata.address, + size: size_register.address, + rc: rc_register.address, + }); + + current_calldata_pointer += flattened_size; + var + } }) .collect(); // Deflatten arrays for (argument_variable, argument) in argument_variables.iter_mut().zip(arguments) { - if let ( - BrilligVariable::BrilligArray(array), - BrilligParameter::Array(item_type, item_count), - ) = (argument_variable, argument) - { - if BrilligContext::has_nested_arrays(item_type) { + match (argument_variable, argument) { + ( + BrilligVariable::BrilligArray(array), + BrilligParameter::Array(item_type, item_count), + ) => { let deflattened_address = self.deflatten_array(item_type, array.size, array.pointer); self.mov_instruction(array.pointer, deflattened_address); array.size = item_type.len() * item_count; self.deallocate_register(deflattened_address); } + ( + BrilligVariable::BrilligVector(vector), + BrilligParameter::Slice(item_type, item_count), + ) => { + let flattened_size = BrilligContext::flattened_size(argument); + + let deflattened_address = + self.deflatten_array(item_type, flattened_size, vector.pointer); + self.mov_instruction(vector.pointer, deflattened_address); + self.usize_const_instruction( + vector.size, + (item_type.len() * item_count).into(), + ); + + self.deallocate_register(deflattened_address); + } + _ => {} } } } @@ -112,10 +144,10 @@ impl BrilligContext { fn flat_bit_sizes(param: &BrilligParameter) -> Box + '_> { match param { BrilligParameter::SingleAddr(bit_size) => Box::new(std::iter::once(*bit_size)), - BrilligParameter::Array(item_types, item_count) => Box::new( + BrilligParameter::Array(item_types, item_count) + | BrilligParameter::Slice(item_types, item_count) => Box::new( (0..*item_count).flat_map(move |_| item_types.iter().flat_map(flat_bit_sizes)), ), - BrilligParameter::Slice(..) => unimplemented!("Unsupported slices as parameter"), } } @@ -134,13 +166,11 @@ impl BrilligContext { fn flattened_size(param: &BrilligParameter) -> usize { match param { BrilligParameter::SingleAddr(_) => 1, - BrilligParameter::Array(item_types, item_count) => { + BrilligParameter::Array(item_types, item_count) + | BrilligParameter::Slice(item_types, item_count) => { let item_size: usize = item_types.iter().map(BrilligContext::flattened_size).sum(); item_count * item_size } - BrilligParameter::Slice(_) => { - unreachable!("ICE: Slices cannot be passed as entry point arguments") - } } } @@ -457,8 +487,8 @@ mod tests { use acvm::FieldElement; use crate::brillig::brillig_ir::{ - artifact::BrilligParameter, brillig_variable::BrilligArray, + entry_point::BrilligParameter, tests::{create_and_run_vm, create_context, create_entry_point_bytecode}, }; diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index e3dd4c33986..d4760c29eda 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -21,7 +21,7 @@ use super::{ }, ssa_gen::Ssa, }; -use crate::brillig::brillig_ir::artifact::GeneratedBrillig; +use crate::brillig::brillig_ir::artifact::{BrilligParameter, GeneratedBrillig}; use crate::brillig::brillig_ir::BrilligContext; use crate::brillig::{brillig_gen::brillig_fn::FunctionContext as BrilligFunctionContext, Brillig}; use crate::errors::{InternalError, InternalWarning, RuntimeError, SsaReport}; @@ -297,12 +297,14 @@ impl Context { let typ = dfg.type_of_value(*param_id); self.create_value_from_type(&typ, &mut |this, _| Ok(this.acir_context.add_variable())) })?; + let arguments = self.gen_brillig_parameters(dfg[main_func.entry_block()].parameters(), dfg); + let witness_inputs = self.acir_context.extract_witness(&inputs); let outputs: Vec = vecmap(main_func.returns(), |result_id| dfg.type_of_value(*result_id).into()); - let code = self.gen_brillig_for(main_func, brillig)?; + let code = self.gen_brillig_for(main_func, arguments, brillig)?; // We specifically do not attempt execution of the brillig code being generated as this can result in it being // replaced with constraints on witnesses to the program outputs. @@ -594,8 +596,9 @@ impl Context { } let inputs = vecmap(arguments, |arg| self.convert_value(*arg, dfg)); + let arguments = self.gen_brillig_parameters(arguments, dfg); - let code = self.gen_brillig_for(func, brillig)?; + let code = self.gen_brillig_for(func, arguments, brillig)?; let outputs: Vec = vecmap(result_ids, |result_id| { dfg.type_of_value(*result_id).into() @@ -673,14 +676,49 @@ impl Context { Ok(()) } + fn gen_brillig_parameters( + &self, + values: &[ValueId], + dfg: &DataFlowGraph, + ) -> Vec { + values + .iter() + .map(|&value_id| { + let typ = dfg.type_of_value(value_id); + if let Type::Slice(item_types) = typ { + let len = match self + .ssa_values + .get(&value_id) + .expect("ICE: Unknown slice input to brillig") + { + AcirValue::DynamicArray(AcirDynamicArray { len, .. }) => *len, + AcirValue::Array(array) => array.len(), + _ => unreachable!("ICE: Slice value is not an array"), + }; + + BrilligParameter::Slice( + item_types + .iter() + .map(BrilligFunctionContext::ssa_type_to_parameter) + .collect(), + len / item_types.len(), + ) + } else { + BrilligFunctionContext::ssa_type_to_parameter(&typ) + } + }) + .collect() + } + fn gen_brillig_for( &self, func: &Function, + arguments: Vec, brillig: &Brillig, ) -> Result { // Create the entry point artifact let mut entry_point = BrilligContext::new_entry_point_artifact( - BrilligFunctionContext::parameters(func), + arguments, BrilligFunctionContext::return_values(func), BrilligFunctionContext::function_id_to_function_label(func.id()), ); diff --git a/test_programs/execution_success/brillig_slice_input/Nargo.toml b/test_programs/execution_success/brillig_slice_input/Nargo.toml new file mode 100644 index 00000000000..a1c8cc3242b --- /dev/null +++ b/test_programs/execution_success/brillig_slice_input/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "brillig_slice_input" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/brillig_slice_input/src/main.nr b/test_programs/execution_success/brillig_slice_input/src/main.nr new file mode 100644 index 00000000000..09a9d9aef9d --- /dev/null +++ b/test_programs/execution_success/brillig_slice_input/src/main.nr @@ -0,0 +1,40 @@ +struct Point { + x: Field, + y: Field, +} + +unconstrained fn sum_slice(slice: [[Point; 2]]) -> Field { + let mut sum = 0; + for i in 0..slice.len() { + for j in 0..slice[i].len() { + sum += slice[i][j].x + slice[i][j].y; + } + } + sum +} + +fn main() { + let mut slice = &[]; + slice = slice.push_back([ + Point { + x: 13, + y: 14, + }, + Point { + x: 20, + y: 8, + } + ]); + slice = slice.push_back([ + Point { + x: 15, + y: 5, + }, + Point { + x: 12, + y: 13, + } + ]); + let brillig_sum = sum_slice(slice); + assert_eq(brillig_sum, 100); +} From 5e7fbd4e706b1691ba2dd960469cfa3b31dfb753 Mon Sep 17 00:00:00 2001 From: SamiAlHassan <150172180+SamiAlHassan@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:47:10 +0100 Subject: [PATCH 146/416] feat: improve nargo check cli with --override flag and feedback for existing files (#4575) Recently, while working with `nargo check`, I noticed when Prover.toml and Verifier.toml files already existed, running `nargo check` would not update these files unless they were manually deleted and the command was rerun. This was a bit frustrating, as there was no indication of why changes weren't being applied and a success message was still output. To address this, I've made an enhancement that improves t he user experience in two ways: 1. If Prover.toml and Verifier.toml exist and `--override` is not used, `nargo check` will no longer show a success message misleadingly. This clarifies that no changes were made due to the presence of these files. 2. It introduces guidance to use the `--override` flag if the intention is to update these existing files, making it clear how to proceed to apply the desired changes. This makes it more intuitive by removing the need to manually delete files Prover.toml and Verifier.toml ### Changes: - **When both files exist without using --override**: The tool will alert you that Prover.toml and Verifier.toml already exist and suggest using `--override` to update them. ```sh sh-5.2# nargo check Warning: Prover.toml already exists. Use --override to force overwrite. Warning: Verifier.toml already exists. Use --override to force overwrite. ``` - **When no file exists**: It proceeds as expected, building the constraint system successfully. ```sh sh-5.2# nargo check [simple_range] Constraint system successfully built! ``` - **When files exist and --override is used**: It overwrites the existing files and rebuilds the constraint system. ```sh sh-5.2# nargo check --override [simple_range] Constraint system successfully built! ``` - **When only one of the files exists**: It creates or overwrites the missing file, as needed, without the need for `--override` for the missing one, but warns about the existing file. ```sh sh-5.2# nargo check Warning: Prover.toml already exists. Use --override to force overwrite. [simple_range] Constraint system successfully built! ``` ```sh sh-5.2# nargo check Warning: Verifier.toml already exists. Use --override to force overwrite. [simple_range] Constraint system successfully built! ``` - **With --override, even if only one file exists**: Both files are re/created. ```sh sh-5.2# nargo check --override [simple_range] Constraint system successfully built! ``` **override flag in --help:** ```sh sh-5.2# nargo check --help Checks the constraint system for errors Usage: nargo check [OPTIONS] Options: --package The name of the package to check --workspace Check all packages in the workspace --override Force overwrite of existing files --expression-width Override the expression width requested by the backend --force Force a full recompilation --print-acir Display the ACIR for compiled circuit --deny-warnings Treat all warnings as errors --silence-warnings Suppress warnings -h, --help Print help ``` --------- Co-authored-by: jfecher --- tooling/nargo_cli/src/cli/check_cmd.rs | 43 +++++++++++++++++++++----- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/tooling/nargo_cli/src/cli/check_cmd.rs b/tooling/nargo_cli/src/cli/check_cmd.rs index 897073f4e20..2b729e44b8a 100644 --- a/tooling/nargo_cli/src/cli/check_cmd.rs +++ b/tooling/nargo_cli/src/cli/check_cmd.rs @@ -34,6 +34,10 @@ pub(crate) struct CheckCommand { #[clap(long, conflicts_with = "package")] workspace: bool, + /// Force overwrite of existing files + #[clap(long = "overwrite")] + allow_overwrite: bool, + #[clap(flatten)] compile_options: CompileOptions, } @@ -58,18 +62,29 @@ pub(crate) fn run( let parsed_files = parse_all(&workspace_file_manager); for package in &workspace { - check_package(&workspace_file_manager, &parsed_files, package, &args.compile_options)?; - println!("[{}] Constraint system successfully built!", package.name); + let any_file_written = check_package( + &workspace_file_manager, + &parsed_files, + package, + &args.compile_options, + args.allow_overwrite, + )?; + if any_file_written { + println!("[{}] Constraint system successfully built!", package.name); + } } Ok(()) } +/// Evaluates the necessity to create or update Prover.toml and Verifier.toml based on the allow_overwrite flag and files' existence. +/// Returns `true` if any file was generated or updated, `false` otherwise. fn check_package( file_manager: &FileManager, parsed_files: &ParsedFiles, package: &Package, compile_options: &CompileOptions, -) -> Result<(), CompileError> { + allow_overwrite: bool, +) -> Result { let (mut context, crate_id) = prepare_package(file_manager, parsed_files, package); check_crate_and_report_errors( &mut context, @@ -81,27 +96,39 @@ fn check_package( if package.is_library() || package.is_contract() { // Libraries do not have ABIs while contracts have many, so we cannot generate a `Prover.toml` file. - Ok(()) + Ok(false) } else { // XXX: We can have a --overwrite flag to determine if you want to overwrite the Prover/Verifier.toml files if let Some((parameters, return_type)) = compute_function_abi(&context, &crate_id) { let path_to_prover_input = package.prover_input_path(); let path_to_verifier_input = package.verifier_input_path(); - // If they are not available, then create them and populate them based on the ABI - if !path_to_prover_input.exists() { + // Before writing the file, check if it exists and whether overwrite is set + let should_write_prover = !path_to_prover_input.exists() || allow_overwrite; + let should_write_verifier = !path_to_verifier_input.exists() || allow_overwrite; + + if should_write_prover { let prover_toml = create_input_toml_template(parameters.clone(), None); write_to_file(prover_toml.as_bytes(), &path_to_prover_input); + } else { + eprintln!("Note: Prover.toml already exists. Use --overwrite to force overwrite."); } - if !path_to_verifier_input.exists() { + + if should_write_verifier { let public_inputs = parameters.into_iter().filter(|param| param.is_public()).collect(); let verifier_toml = create_input_toml_template(public_inputs, return_type); write_to_file(verifier_toml.as_bytes(), &path_to_verifier_input); + } else { + eprintln!( + "Note: Verifier.toml already exists. Use --overwrite to force overwrite." + ); } - Ok(()) + let any_file_written = should_write_prover || should_write_verifier; + + Ok(any_file_written) } else { Err(CompileError::MissingMainFunction(package.name.clone())) } From 59fb83291063d48496b57f3b1f986376bc8f56a4 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 4 Apr 2024 22:46:39 +0100 Subject: [PATCH 147/416] chore: remove unused env vars from `Cross.toml` (#4717) # Description ## Problem\* Resolves ## Summary\* Followup to #4703 and #4705. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/Cross.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/Cross.toml b/.github/Cross.toml index 6520a288d5d..d8516b9ae09 100644 --- a/.github/Cross.toml +++ b/.github/Cross.toml @@ -2,8 +2,6 @@ passthrough = [ "HOME", "RUST_BACKTRACE", - "BARRETENBERG_BIN_DIR", - "BLNS_JSON_PATH" ] volumes = [ "HOME", From f21129ef05efb76c5df6ee15a134f1ea535d8e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Fri, 5 Apr 2024 14:36:09 +0200 Subject: [PATCH 148/416] fix: unknown slice lengths coming from as_slice (#4725) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4722 ## Summary\* ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French --- compiler/noirc_evaluator/src/ssa.rs | 1 + compiler/noirc_evaluator/src/ssa/ir/dfg.rs | 24 +++++++ .../src/ssa/opt/as_slice_length.rs | 71 +++++++++++++++++++ compiler/noirc_evaluator/src/ssa/opt/mod.rs | 1 + .../array_to_slice_constant_length/Nargo.toml | 7 ++ .../Prover.toml | 1 + .../src/main.nr | 10 +++ 7 files changed, 115 insertions(+) create mode 100644 compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs create mode 100644 test_programs/execution_success/array_to_slice_constant_length/Nargo.toml create mode 100644 test_programs/execution_success/array_to_slice_constant_length/Prover.toml create mode 100644 test_programs/execution_success/array_to_slice_constant_length/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index a45cf3af608..4b2f1060c88 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -53,6 +53,7 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::inline_functions, "After Inlining:") // Run mem2reg with the CFG separated into blocks .run_pass(Ssa::mem2reg, "After Mem2Reg:") + .run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization") .try_run_pass(Ssa::evaluate_assert_constant, "After Assert Constant:")? .try_run_pass(Ssa::unroll_loops, "After Unrolling:")? .run_pass(Ssa::simplify_cfg, "After Simplifying:") diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 870b5e602f1..6b950c327cf 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -378,6 +378,30 @@ impl DataFlowGraph { value_id } + /// Replaces an instruction result with a fresh id. + pub(crate) fn replace_result( + &mut self, + instruction_id: InstructionId, + prev_value_id: ValueId, + ) -> ValueId { + let typ = self.type_of_value(prev_value_id); + let results = self.results.get_mut(&instruction_id).unwrap(); + let res_position = results + .iter() + .position(|&id| id == prev_value_id) + .expect("Result id not found while replacing"); + + let value_id = self.values.insert(Value::Instruction { + typ, + position: res_position, + instruction: instruction_id, + }); + + // Replace the value in list of results for this instruction + results[res_position] = value_id; + value_id + } + /// Returns the number of instructions /// inserted into functions. pub(crate) fn num_instructions(&self) -> usize { diff --git a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs new file mode 100644 index 00000000000..69eab1da0ed --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs @@ -0,0 +1,71 @@ +use crate::ssa::{ + ir::{ + function::Function, + instruction::{Instruction, InstructionId, Intrinsic}, + types::Type, + value::Value, + }, + ssa_gen::Ssa, +}; +use fxhash::FxHashMap as HashMap; + +impl Ssa { + /// A simple SSA pass to find any calls to `Intrinsic::AsSlice` and replacing any references to the length of the + /// resulting slice with the length of the array from which it was generated. + /// + /// This allows the length of a slice generated from an array to be used in locations where a constant value is + /// necessary when the value of the array is unknown. + /// + /// Note that this pass must be placed before loop unrolling to be useful. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn as_slice_optimization(mut self) -> Self { + for func in self.functions.values_mut() { + let known_slice_lengths = known_slice_lengths(func); + replace_known_slice_lengths(func, known_slice_lengths); + } + self + } +} + +fn known_slice_lengths(func: &Function) -> HashMap { + let mut known_slice_lengths = HashMap::default(); + for block_id in func.reachable_blocks() { + let block = &func.dfg[block_id]; + for instruction_id in block.instructions() { + let (target_func, arguments) = match &func.dfg[*instruction_id] { + Instruction::Call { func, arguments } => (func, arguments), + _ => continue, + }; + + match &func.dfg[*target_func] { + Value::Intrinsic(Intrinsic::AsSlice) => { + let array_typ = func.dfg.type_of_value(arguments[0]); + if let Type::Array(_, length) = array_typ { + known_slice_lengths.insert(*instruction_id, length); + } else { + unreachable!("AsSlice called with non-array {}", array_typ); + } + } + _ => continue, + }; + } + } + known_slice_lengths +} + +fn replace_known_slice_lengths( + func: &mut Function, + known_slice_lengths: HashMap, +) { + known_slice_lengths.into_iter().for_each(|(instruction_id, known_length)| { + let call_returns = func.dfg.instruction_results(instruction_id); + let original_slice_length = call_returns[0]; + + // We won't use the new id for the original unknown length. + // This isn't strictly necessary as a new result will be defined the next time for which the instruction + // is reinserted but this avoids leaving the program in an invalid state. + func.dfg.replace_result(instruction_id, original_slice_length); + let known_length = func.dfg.make_constant(known_length.into(), Type::length_type()); + func.dfg.set_value_from_id(original_slice_length, known_length); + }); +} diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index 479010b1ed8..f37f45b30bb 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -4,6 +4,7 @@ //! simpler form until the IR only has a single function remaining with 1 block within it. //! Generally, these passes are also expected to minimize the final amount of instructions. mod array_set; +mod as_slice_length; mod assert_constant; mod bubble_up_constrains; mod constant_folding; diff --git a/test_programs/execution_success/array_to_slice_constant_length/Nargo.toml b/test_programs/execution_success/array_to_slice_constant_length/Nargo.toml new file mode 100644 index 00000000000..b338cf9b6ae --- /dev/null +++ b/test_programs/execution_success/array_to_slice_constant_length/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "array_to_slice_constant_length" +type = "bin" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] diff --git a/test_programs/execution_success/array_to_slice_constant_length/Prover.toml b/test_programs/execution_success/array_to_slice_constant_length/Prover.toml new file mode 100644 index 00000000000..a52e9d3c46a --- /dev/null +++ b/test_programs/execution_success/array_to_slice_constant_length/Prover.toml @@ -0,0 +1 @@ +val = "42" diff --git a/test_programs/execution_success/array_to_slice_constant_length/src/main.nr b/test_programs/execution_success/array_to_slice_constant_length/src/main.nr new file mode 100644 index 00000000000..e81dd4a0c5f --- /dev/null +++ b/test_programs/execution_success/array_to_slice_constant_length/src/main.nr @@ -0,0 +1,10 @@ +// Regression test for https://github.com/noir-lang/noir/issues/4722 + +unconstrained fn return_array(val: Field) -> [Field; 1] { + [val; 1] +} + +fn main(val: Field) { + let array = return_array(val); + assert_constant(array.as_slice().len()); +} From 0dac0934d1cc646f87d266b2c54d8d505756fa68 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:18:37 +0100 Subject: [PATCH 149/416] chore: remove docker CI flow (#4724) # Description ## Problem\* Resolves ## Summary\* This set of workflows aren't really usable currently due to lack of caching. They're not being run on PRs so it's constantly breaking but putting it on PRs would blow up our CI times. We're likely to move instead to earthly (#4696) if we are moving to a docker-based system so we should remove this. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/docker-test-flow.yml | 808 ------------------------- Dockerfile.ci | 30 - 2 files changed, 838 deletions(-) delete mode 100644 .github/workflows/docker-test-flow.yml delete mode 100644 Dockerfile.ci diff --git a/.github/workflows/docker-test-flow.yml b/.github/workflows/docker-test-flow.yml deleted file mode 100644 index c8b4f53fadd..00000000000 --- a/.github/workflows/docker-test-flow.yml +++ /dev/null @@ -1,808 +0,0 @@ -name: Test Nargo and JS packages - -on: - push: - branches: - - 'master' - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} - cancel-in-progress: true - -jobs: - build-base-nargo: - name: Build base nargo docker image - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Get current date - id: date - run: echo "date=$(date +'%Y.%m.%d.%H.%M')" >> $GITHUB_STATE - - name: prepare docker images tags - id: prep - run: | - REGISTRY="ghcr.io" - IMG_RAW="${REGISTRY}/${{ github.repository }}" - IMAGE=$(echo "$IMG_RAW" | tr '[:upper:]' '[:lower:]') - TAGS="${IMAGE}:${{ github.sha }}-nargo" - FULL_TAGS="${TAGS},${IMAGE}:latest-nargo,${IMAGE}:v${{ steps.date.outputs.date }}-nargo" - echo "tags=$FULL_TAGS" >> $GITHUB_OUTPUT - echo "image=$IMAGE" >> $GITHUB_OUTPUT - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build nargo base dockerfile - uses: docker/build-push-action@v5 - with: - context: . - file: Dockerfile.ci - tags: ${{ steps.prep.outputs.tags }} - target: base-nargo - cache-from: type=gha - cache-to: type=gha,mode=max - push: true - - build-base-js: - name: Build base js docker image - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Get current date - id: date - run: echo "date=$(date +'%Y.%m.%d.%H.%M')" >> $GITHUB_STATE - - name: Prepare docker image tags - id: prep - run: | - REGISTRY="ghcr.io" - IMG_RAW="${REGISTRY}/${{ github.repository }}" - IMAGE=$(echo "$IMG_RAW" | tr '[:upper:]' '[:lower:]') - TAGS="${IMAGE}:${{ github.sha }}-js" - FULL_TAGS="${TAGS},${IMAGE}:latest-js,${IMAGE}:v${{ steps.date.outputs.date }}-js" - echo "tags=$FULL_TAGS" >> $GITHUB_OUTPUT - echo "image=$IMAGE" >> $GITHUB_OUTPUT - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Build js base dockerfile - uses: docker/build-push-action@v5 - with: - context: . - file: Dockerfile.ci - tags: ${{ steps.prep.outputs.tags }} - target: base-js - cache-from: type=gha - cache-to: type=gha,mode=max - push: true - - artifact-nargo: - name: Artifact nargo - runs-on: ubuntu-latest - needs: [build-base-nargo] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-nargo - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Artifact nargo - uses: actions/upload-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release/nargo - if-no-files-found: error - compression-level: 0 - - test-nargo: - name: Test nargo - runs-on: ubuntu-latest - needs: [build-base-nargo] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-nargo - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Test - working-directory: /usr/src/noir - run: | - .github/scripts/nargo-test.sh - - build-noir-wasm: - name: Build noir wasm - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-wasm-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: noir_wasm - path: /usr/src/noir/compiler/wasm - retention-days: 10 - - test-noir-wasm: - name: Test noir wasm - runs-on: ubuntu-latest - needs: [build-base-js, artifact-nargo, build-noir-wasm] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: Prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Download noir_wasm artifact - uses: actions/download-artifact@v4 - with: - name: noir_wasm - path: /usr/src/noir/compiler/wasm - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-wasm-test.sh - - test-noir-wasm-browser: - name: Test noir wasm browser - runs-on: ubuntu-latest - needs: [build-base-js, artifact-nargo, build-noir-wasm] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: Prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Download noir_wasm artifact - uses: actions/download-artifact@v4 - with: - name: noir_wasm - path: /usr/src/noir/compiler/wasm - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-wasm-test-browser.sh - - build-acvm_js: - name: Build acvm js - runs-on: ubuntu-latest - needs: [build-base-js] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/acvm_js-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: acvm_js - path: - /usr/src/noir/acvm-repo/acvm_js/outputs/out/acvm_js - if-no-files-found: error - compression-level: 0 - - test-acvm_js: - name: Test acvm js - runs-on: ubuntu-latest - needs: [build-base-js, build-acvm_js] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: | - /usr/src/noir/acvm-repo/acvm_js - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/acvm_js-test.sh - - test-acvm_js-browser: - name: Test acvm js browser - runs-on: ubuntu-latest - needs: [build-base-js, build-acvm_js] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: | - /usr/src/noir/acvm-repo/acvm_js - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/acvm_js-test-browser.sh - - build-noirc-abi: - name: Build noirc abi - runs-on: ubuntu-latest - needs: [build-base-js] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/noirc-abi-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: noirc_abi_wasm - path: - /usr/src/noir/tooling/noirc_abi_wasm/outputs/out/noirc_abi_wasm - if-no-files-found: error - compression-level: 0 - - test-noirc-abi: - name: Test noirc abi - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/noirc-abi-test.sh - - test-noirc-abi-browser: - name: Test noirc abi browser - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/noirc-abi-test-browser.sh - - build-noir-js-types: - name: Build noir js types - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-js-types-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - if-no-files-found: error - compression-level: 0 - - build-barretenberg-backend: - name: Build Barretenberg backend - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi, build-noir-js-types] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: /usr/src/noir/tooling/noir_js_types/lib/ - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/backend-barretenberg-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: barretenberg-backend - path: - /usr/src/noir/tooling/noir_js_backend_barretenberg/lib - if-no-files-found: error - compression-level: 0 - - test-barretenberg-backend: - name: Test Barretenberg backend - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi, build-noir-js-types, build-barretenberg-backend] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: /usr/src/noir/tooling/noir_js_types/lib/ - - name: Download Backend barretenberg - uses: actions/download-artifact@v4 - with: - name: barretenberg-backend - path: - /usr/src/noir/tooling/noir_js_backend_barretenberg/lib - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/backend-barretenberg-test.sh - - build-noir_js: - name: Build noirjs - runs-on: ubuntu-latest - needs: [build-base-js, artifact-nargo, build-noirc-abi, build-acvm_js, build-barretenberg-backend, build-noir-js-types] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: | - /usr/src/noir/acvm-repo/acvm_js - - name: Download Barretenberg backend - uses: actions/download-artifact@v4 - with: - name: barretenberg-backend - path: - /usr/src/noir/tooling/noir_js_backend_barretenberg/lib - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-js-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: noir_js - path: - /usr/src/noir/tooling/noir_js/lib - - test-noir_js: - name: Test noirjs - runs-on: ubuntu-latest - needs: [ - build-base-js, - build-noirc-abi, - artifact-nargo, - build-acvm_js, - build-barretenberg-backend, - build-noir_js, - build-noir-js-types - ] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: Prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: | - /usr/src/noir/tooling/noirc_abi_wasm - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: | - /usr/src/noir/acvm-repo/acvm_js - - name: Download Barretenberg backend - uses: actions/download-artifact@v4 - with: - name: barretenberg-backend - path: - /usr/src/noir/tooling/noir_js_backend_barretenberg/lib - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - - name: Download noir js - uses: actions/download-artifact@v4 - with: - name: noir_js - path: - /usr/src/noir/tooling/noir_js/lib - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-js-test.sh - - build-noir_codegen: - name: Build noir codegen - runs-on: ubuntu-latest - needs: [build-base-js, build-noirc-abi, build-acvm_js, build-noir-js-types, build-noir_js] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: Prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noirc abi package - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: /usr/src/noir/tooling/noirc_abi_wasm - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: /usr/src/noir/acvm-repo/acvm_js - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - - name: Download noir js - uses: actions/download-artifact@v4 - with: - name: noir_js - path: - /usr/src/noir/tooling/noir_js/lib - - name: Build - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-codegen-build.sh - - name: Artifact - uses: actions/upload-artifact@v4 - with: - name: noir_codegen - path: - /usr/src/noir/tooling/noir_codegen/lib - - test-noir_codegen: - name: Test noir codegen - runs-on: ubuntu-latest - needs: [build-base-js, artifact-nargo, build-noirc-abi, build-acvm_js, build-noir-js-types, build-noir_js, build-noir_codegen] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: Prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: /usr/src/noir/tooling/noirc_abi_wasm - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: /usr/src/noir/acvm-repo/acvm_js - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - - name: Download noir js - uses: actions/download-artifact@v4 - with: - name: noir_js - path: - /usr/src/noir/tooling/noir_js/lib - - name: Download noir codegen - uses: actions/download-artifact@v4 - with: - name: noir_codegen - path: - /usr/src/noir/tooling/noir_codegen/lib - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/noir-codegen-test.sh - - test-integration: - name: Integration test - runs-on: ubuntu-latest - needs: [ - build-base-js, - artifact-nargo, - build-noir-wasm, - build-noirc-abi, - build-acvm_js, - build-noir-js-types, - build-noir_js, - build-barretenberg-backend - ] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download nargo - uses: actions/download-artifact@v4 - with: - name: nargo - path: /usr/src/noir/target/release - - name: Prep downloaded artifact - run: | - chmod +x /usr/src/noir/target/release/nargo - - name: Download noir wasm - uses: actions/download-artifact@v4 - with: - name: noir_wasm - path: /usr/src/noir/compiler/wasm - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: /usr/src/noir/tooling/noirc_abi_wasm - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: /usr/src/noir/acvm-repo/acvm_js - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - - name: Download noir js - uses: actions/download-artifact@v4 - with: - name: noir_js - path: - /usr/src/noir/tooling/noir_js/lib - - name: Download Barretenberg backend - uses: actions/download-artifact@v4 - with: - name: barretenberg-backend - path: - /usr/src/noir/tooling/noir_js_backend_barretenberg/lib - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/integration-test-node.sh - - test-integration-browser: - name: Integration test browser - runs-on: ubuntu-latest - needs: [ - build-base-js, - build-noir-wasm, - build-noirc-abi, - build-acvm_js, - build-noir-js-types, - build-noir_js, - build-barretenberg-backend - ] - container: - image: ghcr.io/noir-lang/noir:${{ github.sha }}-js - credentials: - username: ${{ github.actor }} - password: ${{ secrets.github_token }} - steps: - - name: Download noir wasm - uses: actions/download-artifact@v4 - with: - name: noir_wasm - path: /usr/src/noir/compiler/wasm - - name: Download noirc abi - uses: actions/download-artifact@v4 - with: - name: noirc_abi_wasm - path: /usr/src/noir/tooling/noirc_abi_wasm - - name: Download acvm js - uses: actions/download-artifact@v4 - with: - name: acvm_js - path: /usr/src/noir/acvm-repo/acvm_js - - name: Download noir js types - uses: actions/download-artifact@v4 - with: - name: noir-js-types - path: | - /usr/src/noir/tooling/noir_js_types/lib - - name: Download noir js - uses: actions/download-artifact@v4 - with: - name: noir_js - path: - /usr/src/noir/tooling/noir_js/lib - - name: Download Barretenberg backend - uses: actions/download-artifact@v4 - with: - name: barretenberg-backend - path: - /usr/src/noir/tooling/noir_js_backend_barretenberg/lib - - name: Test - working-directory: /usr/src/noir - run: | - ./.github/scripts/integration-test-browser.sh - - tests-end: - name: End - runs-on: ubuntu-latest - if: ${{ always() }} - needs: - - test-nargo - - test-noirc-abi - - test-noirc-abi-browser - - test-noir-wasm - - test-noir-wasm-browser - - test-integration - - test-integration-browser - - test-noir_codegen - - test-acvm_js - - test-acvm_js-browser - - test-barretenberg-backend - - test-noir_js - - steps: - - name: Report overall success - run: | - if [[ $FAIL == true ]]; then - exit 1 - else - exit 0 - fi - env: - FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} diff --git a/Dockerfile.ci b/Dockerfile.ci deleted file mode 100644 index e0dc030980c..00000000000 --- a/Dockerfile.ci +++ /dev/null @@ -1,30 +0,0 @@ -FROM rust:1.73.0-slim-bookworm as base -RUN apt-get update && apt-get upgrade -y && apt-get install build-essential git -y -WORKDIR /usr/src/noir -ENV PATH="${PATH}:/usr/src/noir/target/release" - -FROM base as base-nargo -COPY . . -RUN .github/scripts/nargo-build.sh - -FROM base as base-js -RUN apt-get install -y ca-certificates curl gnupg -RUN mkdir -p /etc/apt/keyrings -RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list -RUN apt-get update && apt-get install nodejs -y -RUN corepack enable -RUN apt-get install -y jq -COPY yarn.lock package.json .yarnrc.yml ./ -COPY .yarn/ ./.yarn/ -COPY ./acvm-repo/acvm_js/package.json ./acvm-repo/acvm_js/ -COPY ./tooling/noirc_abi_wasm/package.json ./tooling/noirc_abi_wasm/ -COPY ./compiler/wasm/package.json ./compiler/wasm/ -COPY ./tooling/noir_js_types/package.json ./tooling/noir_js_types/ -COPY ./tooling/noir_js_backend_barretenberg/package.json ./tooling/noir_js_backend_barretenberg/ -COPY ./tooling/noir_js/package.json ./tooling/noir_js/ -COPY ./tooling/noir_codegen/package.json ./tooling/noir_codegen/ -COPY ./compiler/integration-tests/package.json ./compiler/integration-tests/ -COPY ./docs/package.json ./docs/ -RUN yarn --immutable -COPY . . From ff0b5bc585520d26d9dc2ac63ff31ef435597c1d Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:18:53 +0100 Subject: [PATCH 150/416] chore: remove `FunctionInput::dummy` (#4723) # Description ## Problem\* Resolves ## Summary\* This function is unused. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../acir/src/circuit/opcodes/black_box_function_call.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index 8a0c4692282..c955e435b37 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -10,12 +10,6 @@ pub struct FunctionInput { pub num_bits: u32, } -impl FunctionInput { - pub fn dummy() -> Self { - Self { witness: Witness(0), num_bits: 0 } - } -} - #[derive(Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum BlackBoxFuncCall { AND { From 70f41d81cbd7f1eccfc12c2dc12f2eb8844818fb Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:58:31 +0100 Subject: [PATCH 151/416] chore(ci): stop updating version list before cutting new docs version (#4726) # Description ## Problem\* Resolves ## Summary\* When cutting a new version of the docs in release PRs we're querying the list of released noir versions from github and updating `versions.json` before we cut the new version. This results in docusaurus thinking that it should already have content for that version. We can then no longer build the docs site in order to do all of the necessary preprocessing before creating the new versioned docs folder. This PR stops the automated polling of github on every build to prevent this issue. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/docs-pr.yml | 8 +++++--- .github/workflows/publish-docs.yml | 7 ++++--- docs/package.json | 5 +++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/docs-pr.yml b/.github/workflows/docs-pr.yml index 5d0b72c6ad8..1f9ccdd946b 100644 --- a/.github/workflows/docs-pr.yml +++ b/.github/workflows/docs-pr.yml @@ -75,9 +75,11 @@ jobs: run: | npm i wasm-opt -g + - name: Query active docs versions + run: yarn workspace docs version::stables + - name: Build docs - run: - yarn workspaces foreach -Rpt --from docs run build + run: yarn workspaces foreach -Rpt --from docs run build - name: Upload artifact uses: actions/upload-artifact@v4 @@ -126,4 +128,4 @@ jobs: with: message: | FYI @noir-lang/developerrelations on Noir doc changes. - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index a56583b34eb..8896e613608 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -28,10 +28,11 @@ jobs: run: | npm i wasm-opt -g + - name: Query active docs versions + run: yarn workspace docs version::stables + - name: Build docs for deploying - working-directory: docs - run: - yarn workspaces foreach -Rt run build + run: yarn workspaces foreach -Rpt --from docs run build - name: Deploy to Netlify uses: nwtgck/actions-netlify@v2.1 diff --git a/docs/package.json b/docs/package.json index 78560707795..4bbdf254c50 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,10 +5,11 @@ "scripts": { "preprocess": "yarn workspace @noir-lang/acvm_js build && ./scripts/codegen_nargo_reference.sh && yarn node ./scripts/preprocess/index.js", "start": "yarn preprocess && docusaurus start", - "build": "yarn preprocess && yarn version::stables && docusaurus build", + "build": "yarn preprocess && docusaurus build", + "clean": "rm -rf ./processed-docs ./processed-docs ./build", "version::stables": "ts-node ./scripts/setStable.ts", "serve": "serve build", - "version": "yarn preprocess && docusaurus build && docusaurus docs:version" + "version": "yarn build && docusaurus docs:version" }, "dependencies": { "@docusaurus/core": "^3.0.1", From 66835dae8adcb5e0e375a2ded87a8a891f3bee60 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 5 Apr 2024 15:16:24 +0100 Subject: [PATCH 152/416] chore: update from vulnerable version of h2 (#4714) # Description ## Problem\* Resolves ## Summary\* This fixes a vulnerability notice in cargo-deny. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 906e49df6ce..170b23be189 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1983,9 +1983,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", From 94952db604b70a1ec18115b291de3c52565a641e Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 5 Apr 2024 20:11:21 +0100 Subject: [PATCH 153/416] feat: add `remove_enable_side_effects` SSA pass (#4224) # Description ## Problem\* Resolves ## Summary\* The `EnableSideEffects` instruction can hamper dead instruction elimination in the case where it's not covering any instructions which have side effects (and so are affected by the `EnableSideEffects` instruction). In this case we cannot perform any DIE on instructions used to calculate the predicate as the compiler sees their results as being used. This PR adds a new SSA pass which delays inserting `EnableSideEffects` instructions until the next instruction which would be affected by them. This means that if we hit another `EnableSideEffects` instruction first then we can omit the previous one, unlocking more DIE opportunities. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- compiler/noirc_evaluator/src/ssa.rs | 1 + compiler/noirc_evaluator/src/ssa/opt/mod.rs | 1 + .../src/ssa/opt/remove_enable_side_effects.rs | 167 ++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 4b2f1060c88..9ecf6960333 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -62,6 +62,7 @@ pub(crate) fn optimize_into_acir( // Run mem2reg once more with the flattened CFG to catch any remaining loads/stores .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::fold_constants, "After Constant Folding:") + .run_pass(Ssa::remove_enable_side_effects, "After EnableSideEffects removal:") .run_pass(Ssa::fold_constants_using_constraints, "After Constraint Folding:") .run_pass(Ssa::dead_instruction_elimination, "After Dead Instruction Elimination:") .run_pass(Ssa::array_set_optimization, "After Array Set Optimizations:") diff --git a/compiler/noirc_evaluator/src/ssa/opt/mod.rs b/compiler/noirc_evaluator/src/ssa/opt/mod.rs index f37f45b30bb..4452840a28c 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/mod.rs @@ -15,5 +15,6 @@ mod inlining; mod mem2reg; mod rc; mod remove_bit_shifts; +mod remove_enable_side_effects; mod simplify_cfg; mod unrolling; diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs new file mode 100644 index 00000000000..8535dc2661f --- /dev/null +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -0,0 +1,167 @@ +//! The goal of the "remove enable side effects" optimization pass is to delay any [Instruction::EnableSideEffects] +//! instructions such that they cover the minimum number of instructions possible. +//! +//! The pass works as follows: +//! - Insert instructions until an [Instruction::EnableSideEffects] is encountered, save this [InstructionId]. +//! - Continue inserting instructions until either +//! - Another [Instruction::EnableSideEffects] is encountered, if so then drop the previous [InstructionId] in favour +//! of this one. +//! - An [Instruction] with side-effects is encountered, if so then insert the currently saved [Instruction::EnableSideEffects] +//! before the [Instruction]. Continue inserting instructions until the next [Instruction::EnableSideEffects] is encountered. +use std::collections::HashSet; + +use acvm::FieldElement; + +use crate::ssa::{ + ir::{ + basic_block::BasicBlockId, + dfg::DataFlowGraph, + function::Function, + instruction::{BinaryOp, Instruction, Intrinsic}, + value::Value, + }, + ssa_gen::Ssa, +}; + +impl Ssa { + /// See [`remove_enable_side_effects`][self] module for more information. + #[tracing::instrument(level = "trace", skip(self))] + pub(crate) fn remove_enable_side_effects(mut self) -> Ssa { + for function in self.functions.values_mut() { + remove_enable_side_effects(function); + } + self + } +} + +fn remove_enable_side_effects(function: &mut Function) { + let mut context = Context::default(); + context.block_queue.push(function.entry_block()); + + while let Some(block) = context.block_queue.pop() { + if context.visited_blocks.contains(&block) { + continue; + } + + context.visited_blocks.insert(block); + context.remove_enable_side_effects_in_block(function, block); + } +} + +#[derive(Default)] +struct Context { + visited_blocks: HashSet, + block_queue: Vec, +} + +impl Context { + fn remove_enable_side_effects_in_block( + &mut self, + function: &mut Function, + block: BasicBlockId, + ) { + let instructions = function.dfg[block].take_instructions(); + + let mut last_side_effects_enabled_instruction = None; + + let mut new_instructions = Vec::with_capacity(instructions.len()); + for instruction_id in instructions { + let instruction = &function.dfg[instruction_id]; + + // If we run into another `Instruction::EnableSideEffects` before encountering any + // instructions with side effects then we can drop the instruction we're holding and + // continue with the new `Instruction::EnableSideEffects`. + if let Instruction::EnableSideEffects { condition } = instruction { + // If we're seeing an `enable_side_effects u1 1` then we want to insert it immediately. + // This is because we want to maximize the effect it will have. + if function + .dfg + .get_numeric_constant(*condition) + .map_or(false, |condition| condition.is_one()) + { + new_instructions.push(instruction_id); + last_side_effects_enabled_instruction = None; + continue; + } + + last_side_effects_enabled_instruction = Some(instruction_id); + continue; + } + + // If we hit an instruction which is affected by the side effects var then we must insert the + // `Instruction::EnableSideEffects` before we insert this new instruction. + if Self::responds_to_side_effects_var(&function.dfg, instruction) { + if let Some(enable_side_effects_instruction_id) = + last_side_effects_enabled_instruction.take() + { + new_instructions.push(enable_side_effects_instruction_id); + } + } + new_instructions.push(instruction_id); + } + + *function.dfg[block].instructions_mut() = new_instructions; + + self.block_queue.extend(function.dfg[block].successors()); + } + + fn responds_to_side_effects_var(dfg: &DataFlowGraph, instruction: &Instruction) -> bool { + use Instruction::*; + match instruction { + Binary(binary) => { + if matches!(binary.operator, BinaryOp::Div | BinaryOp::Mod) { + if let Some(rhs) = dfg.get_numeric_constant(binary.rhs) { + rhs == FieldElement::zero() + } else { + true + } + } else { + false + } + } + + Cast(_, _) + | Not(_) + | Truncate { .. } + | Constrain(..) + | RangeCheck { .. } + | IncrementRc { .. } + | DecrementRc { .. } => false, + + EnableSideEffects { .. } + | ArrayGet { .. } + | ArraySet { .. } + | Allocate + | Store { .. } + | Load { .. } => true, + + // Some `Intrinsic`s have side effects so we must check what kind of `Call` this is. + Call { func, .. } => match dfg[*func] { + Value::Intrinsic(intrinsic) => match intrinsic { + Intrinsic::SlicePushBack + | Intrinsic::SlicePushFront + | Intrinsic::SlicePopBack + | Intrinsic::SlicePopFront + | Intrinsic::SliceInsert + | Intrinsic::SliceRemove => true, + + Intrinsic::ArrayLen + | Intrinsic::AssertConstant + | Intrinsic::ApplyRangeConstraint + | Intrinsic::StrAsBytes + | Intrinsic::ToBits(_) + | Intrinsic::ToRadix(_) + | Intrinsic::BlackBox(_) + | Intrinsic::FromField + | Intrinsic::AsField + | Intrinsic::AsSlice => false, + }, + + // We must assume that functions contain a side effect as we cannot inspect more deeply. + Value::Function(_) => true, + + _ => false, + }, + } + } +} From 46fc01dc4cf1a43896862f8cc9ed6e852f4a426b Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:44:02 +0100 Subject: [PATCH 154/416] chore: update JS publish workflow to upload build artifacts correctly. (#4734) # Description ## Problem\* Resolves ## Summary\* This does some fixes to the publishing workflow to account for #4679 ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/publish-es-packages.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-es-packages.yml b/.github/workflows/publish-es-packages.yml index 470db3b78f7..819be308169 100644 --- a/.github/workflows/publish-es-packages.yml +++ b/.github/workflows/publish-es-packages.yml @@ -44,7 +44,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: noirc_abi_wasm - path: ./tooling/noirc_abi_wasm/outputs/out/noirc_abi_wasm + path: | + ./tooling/noirc_abi_wasm/nodejs + ./tooling/noirc_abi_wasm/web retention-days: 10 build-noir_wasm: @@ -113,7 +115,9 @@ jobs: uses: actions/upload-artifact@v4 with: name: acvm-js - path: ./acvm-repo/acvm_js/outputs/out/acvm_js + path: | + ./acvm-repo/acvm_js/nodejs + ./acvm-repo/acvm_js/web retention-days: 3 publish-es-packages: From da27e3469b08e9b77aa9bda383533707489106bf Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:04:56 +0100 Subject: [PATCH 155/416] chore(ci): replace `yarn build:js:only` script (#4735) --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 30cac607010..3cffdf4c802 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "lint": "yarn workspaces foreach --verbose run lint", "spellcheck": "cspell '**/*.{md,rs}' -c ./cspell.json", "prepare:publish": "yarn clean && yarn build", + "build:js:only": "yarn workspaces foreach -vtp --exclude \"{@noir-lang/acvm_js,@noir-lang/noirc_abi,@noir-lang/noir_wasm,docs,@noir-lang/root}\" run build", "nightly:version": "yarn workspaces foreach run nightly:version", "publish:all": "yarn install && yarn workspaces foreach run publish" }, From 753851e1ec74892ca76319255c3277cca9e96a15 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:40:46 +0100 Subject: [PATCH 156/416] chore(ci): fix cutting new versions of the docs (#4737) --- docs/package.json | 2 +- docs/scripts/cut_version.sh | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100755 docs/scripts/cut_version.sh diff --git a/docs/package.json b/docs/package.json index 4bbdf254c50..f9e95fc02f8 100644 --- a/docs/package.json +++ b/docs/package.json @@ -9,7 +9,7 @@ "clean": "rm -rf ./processed-docs ./processed-docs ./build", "version::stables": "ts-node ./scripts/setStable.ts", "serve": "serve build", - "version": "yarn build && docusaurus docs:version" + "version": "yarn version::stables && ./scripts/cut_version.sh" }, "dependencies": { "@docusaurus/core": "^3.0.1", diff --git a/docs/scripts/cut_version.sh b/docs/scripts/cut_version.sh new file mode 100755 index 00000000000..4000707328c --- /dev/null +++ b/docs/scripts/cut_version.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -eu + +cd $(dirname "$0")/.. + +VERSION=$1 + +# We assume that the new release tag has been made on github, so setStable.ts will add this to `versions.json`. +# We don't have a version of the docs for this release however (that's what we're doing right now!) so we need to remove it. +jq 'map(select(. != "'"$VERSION"'"))' versions.json > tmp.json && mv tmp.json versions.json + +# We need to build the docs in order to perform all necessary preprocessing. +yarn build + +# Finally cut the actual new docs version. +yarn docusaurus docs:version $VERSION From dbb86acd43de5d34905c8e7cb47cff7afc8a6071 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:58:42 +0100 Subject: [PATCH 157/416] chore: update condition for clearing warning comment on release PRs (#4739) # Description ## Problem\* Resolves ## Summary\* This is not triggering correctly atm so I'm switching to a more explicit syntax. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index badcd3af2dd..d27fac0e039 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -131,7 +131,7 @@ jobs: # We need to specify the PR on which to make the comment as workflow is triggered by push. number: ${{ fromJSON(needs.release-please.outputs.release-pr).number }} # delete the comment in case failures have been fixed - delete: ${{ !env.FAIL }} + delete: ${{ env.FAIL == false }} message: "The release workflow has not completed successfully. Releasing now will result in a broken release" - name: Report overall success From 50d2735825454a8638a308156d4ea23b3c4420d8 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 8 Apr 2024 17:41:13 +0100 Subject: [PATCH 158/416] feat(nargo): Multiple circuits info for binary programs (#4719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Resolves #4697 ## Summary\* The `nargo info` command previously assumed that we only had a single circuit. In order to display accurate circuits sizes we should display the circuit size of each individual circuit entry part which is part of an entire `Program`. For improved debugging I also had to exposed names in the `GeneratedAcir`. This prompted also refactoring the context that we pass around in `ssa.rs` when creating a circuit and a program. Two new structs,`SsaProgramArtifact` and `SsaCircuitArtifact`, have been created to clean this up. Example output from `nargo info` on `fold_basic_nested_call`: Screenshot 2024-04-04 at 3 54 46 PM ## Additional Context It isn't necessarily obvious how we should display foldable circuits for contracts which already have multiple entry points. I have left this for follow-up work: https://github.com/noir-lang/noir/issues/4720 ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [X] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [X] I have tested the changes locally. - [X] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/gates_report.yml | 2 +- compiler/noirc_driver/src/contract.rs | 3 + compiler/noirc_driver/src/lib.rs | 21 ++++- compiler/noirc_driver/src/program.rs | 2 + compiler/noirc_evaluator/src/ssa.rs | 82 ++++++++++++------- .../ssa/acir_gen/acir_ir/generated_acir.rs | 6 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 7 +- .../fold_non_contract_method/Nargo.toml | 7 ++ .../fold_non_contract_method/src/main.nr | 18 ++++ tooling/nargo/src/artifacts/program.rs | 4 + tooling/nargo_cli/build.rs | 2 +- tooling/nargo_cli/src/cli/info_cmd.rs | 62 ++++++++------ 12 files changed, 157 insertions(+), 59 deletions(-) create mode 100644 test_programs/compile_success_contract/fold_non_contract_method/Nargo.toml create mode 100644 test_programs/compile_success_contract/fold_non_contract_method/src/main.nr diff --git a/.github/workflows/gates_report.yml b/.github/workflows/gates_report.yml index f3f798fc5ea..ebf17f7374c 100644 --- a/.github/workflows/gates_report.yml +++ b/.github/workflows/gates_report.yml @@ -74,7 +74,7 @@ jobs: - name: Compare gates reports id: gates_diff - uses: TomAFrench/noir-gates-diff@e7cf131b7e7f044c01615f93f0b855f65ddc02d4 + uses: TomAFrench/noir-gates-diff@df05f34e2ab275ddc4f2cac065df1c88f8a05e5d with: report: gates_report.json summaryQuantile: 0.9 # only display the 10% most significant circuit size diffs in the summary (defaults to 20%) diff --git a/compiler/noirc_driver/src/contract.rs b/compiler/noirc_driver/src/contract.rs index 9a0e25a321b..bd78cb23cdc 100644 --- a/compiler/noirc_driver/src/contract.rs +++ b/compiler/noirc_driver/src/contract.rs @@ -51,4 +51,7 @@ pub struct ContractFunction { pub bytecode: Program, pub debug: DebugInfo, + + /// Names of the functions in the program. These are used for more informative debugging and benchmarking. + pub names: Vec, } diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 86d4cd1510d..8fa2d9680c8 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -11,6 +11,7 @@ use noirc_abi::{AbiParameter, AbiType, ContractEvent}; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; +use noirc_evaluator::ssa::SsaProgramArtifact; use noirc_frontend::debug::build_debug_crate_file; use noirc_frontend::graph::{CrateId, CrateName}; use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; @@ -423,6 +424,7 @@ fn compile_contract_inner( bytecode: function.program, debug: function.debug, is_unconstrained: modifiers.is_unconstrained, + names: function.names, }); } @@ -485,7 +487,14 @@ pub fn compile_no_check( } let visibility = program.return_visibility; - let (program, debug, warnings, input_witnesses, return_witnesses) = create_program( + let SsaProgramArtifact { + program, + debug, + warnings, + main_input_witnesses, + main_return_witnesses, + names, + } = create_program( program, options.show_ssa, options.show_brillig, @@ -493,8 +502,13 @@ pub fn compile_no_check( options.benchmark_codegen, )?; - let abi = - abi_gen::gen_abi(context, &main_function, input_witnesses, return_witnesses, visibility); + let abi = abi_gen::gen_abi( + context, + &main_function, + main_input_witnesses, + main_return_witnesses, + visibility, + ); let file_map = filter_relevant_files(&debug, &context.file_manager); Ok(CompiledProgram { @@ -510,5 +524,6 @@ pub fn compile_no_check( file_map, noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, + names, }) } diff --git a/compiler/noirc_driver/src/program.rs b/compiler/noirc_driver/src/program.rs index 6f527297dcb..9ffd2d70dda 100644 --- a/compiler/noirc_driver/src/program.rs +++ b/compiler/noirc_driver/src/program.rs @@ -27,4 +27,6 @@ pub struct CompiledProgram { pub debug: DebugInfo, pub file_map: BTreeMap, pub warnings: Vec, + /// Names of the functions in the program. These are used for more informative debugging and benchmarking. + pub names: Vec, } diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index 9ecf6960333..fac7a7c0829 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -88,6 +88,29 @@ fn time(name: &str, print_timings: bool, f: impl FnOnce() -> T) -> T { result } +#[derive(Default)] +pub struct SsaProgramArtifact { + pub program: AcirProgram, + pub debug: Vec, + pub warnings: Vec, + pub main_input_witnesses: Vec, + pub main_return_witnesses: Vec, + pub names: Vec, +} + +impl SsaProgramArtifact { + fn add_circuit(&mut self, mut circuit_artifact: SsaCircuitArtifact, is_main: bool) { + self.program.functions.push(circuit_artifact.circuit); + self.debug.push(circuit_artifact.debug_info); + self.warnings.append(&mut circuit_artifact.warnings); + if is_main { + self.main_input_witnesses = circuit_artifact.input_witnesses; + self.main_return_witnesses = circuit_artifact.return_witnesses; + } + self.names.push(circuit_artifact.name); + } +} + /// Compiles the [`Program`] into [`ACIR``][acvm::acir::circuit::Program]. /// /// The output ACIR is is backend-agnostic and so must go through a transformation pass before usage in proof generation. @@ -99,8 +122,7 @@ pub fn create_program( enable_brillig_logging: bool, force_brillig_output: bool, print_codegen_timings: bool, -) -> Result<(AcirProgram, Vec, Vec, Vec, Vec), RuntimeError> -{ +) -> Result { let debug_variables = program.debug_variables.clone(); let debug_types = program.debug_types.clone(); let debug_functions = program.debug_functions.clone(); @@ -121,37 +143,33 @@ pub fn create_program( "The generated ACIRs should match the supplied function signatures" ); - let mut functions = vec![]; - let mut debug_infos = vec![]; - let mut warning_infos = vec![]; - let mut main_input_witnesses = Vec::new(); - let mut main_return_witnesses = Vec::new(); + let mut program_artifact = SsaProgramArtifact::default(); // For setting up the ABI we need separately specify main's input and return witnesses let mut is_main = true; for (acir, func_sig) in generated_acirs.into_iter().zip(func_sigs) { - let (circuit, debug_info, warnings, input_witnesses, return_witnesses) = - convert_generated_acir_into_circuit( - acir, - func_sig, - recursive, - // TODO: get rid of these clones - debug_variables.clone(), - debug_functions.clone(), - debug_types.clone(), - ); - functions.push(circuit); - debug_infos.push(debug_info); - warning_infos.extend(warnings); - if is_main { - main_input_witnesses = input_witnesses; - main_return_witnesses = return_witnesses; - } + let circuit_artifact = convert_generated_acir_into_circuit( + acir, + func_sig, + recursive, + // TODO: get rid of these clones + debug_variables.clone(), + debug_functions.clone(), + debug_types.clone(), + ); + program_artifact.add_circuit(circuit_artifact, is_main); is_main = false; } - let program = AcirProgram { functions }; + Ok(program_artifact) +} - Ok((program, debug_infos, warning_infos, main_input_witnesses, main_return_witnesses)) +pub struct SsaCircuitArtifact { + name: String, + circuit: Circuit, + debug_info: DebugInfo, + warnings: Vec, + input_witnesses: Vec, + return_witnesses: Vec, } fn convert_generated_acir_into_circuit( @@ -161,7 +179,7 @@ fn convert_generated_acir_into_circuit( debug_variables: DebugVariables, debug_functions: DebugFunctions, debug_types: DebugTypes, -) -> (Circuit, DebugInfo, Vec, Vec, Vec) { +) -> SsaCircuitArtifact { let opcodes = generated_acir.take_opcodes(); let current_witness_index = generated_acir.current_witness_index().0; let GeneratedAcir { @@ -170,6 +188,7 @@ fn convert_generated_acir_into_circuit( input_witnesses, assert_messages, warnings, + name, .. } = generated_acir; @@ -202,7 +221,14 @@ fn convert_generated_acir_into_circuit( let (optimized_circuit, transformation_map) = acvm::compiler::optimize(circuit); debug_info.update_acir(transformation_map); - (optimized_circuit, debug_info, warnings, input_witnesses, return_witnesses) + SsaCircuitArtifact { + name, + circuit: optimized_circuit, + debug_info, + warnings, + input_witnesses, + return_witnesses, + } } // Takes each function argument and partitions the circuit's inputs witnesses according to its visibility. diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 1d05e998b13..b43110b2f5b 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -25,7 +25,7 @@ use iter_extended::vecmap; use num_bigint::BigUint; #[derive(Debug, Default)] -/// The output of the Acir-gen pass +/// The output of the Acir-gen pass, which should only be produced for entry point Acir functions pub(crate) struct GeneratedAcir { /// The next witness index that may be declared. /// If witness index is `None` then we have not yet created a witness @@ -58,6 +58,10 @@ pub(crate) struct GeneratedAcir { pub(crate) assert_messages: BTreeMap, pub(crate) warnings: Vec, + + /// Name for the corresponding entry point represented by this Acir-gen output. + /// Only used for debugging and benchmarking purposes + pub(crate) name: String, } impl GeneratedAcir { diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index d4760c29eda..9e597dc8e9a 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -186,7 +186,10 @@ impl Ssa { // TODO: can we parallelise this? for function in self.functions.values() { let context = Context::new(); - if let Some(generated_acir) = context.convert_ssa_function(&self, function, brillig)? { + if let Some(mut generated_acir) = + context.convert_ssa_function(&self, function, brillig)? + { + generated_acir.name = function.name().to_owned(); acirs.push(generated_acir); } } @@ -253,7 +256,7 @@ impl Context { } } } - // We only want to convert entry point functions. This being `main` and those marked with `#[fold]` + // We only want to convert entry point functions. This being `main` and those marked with `InlineType::Fold` Ok(Some(self.convert_acir_main(function, ssa, brillig)?)) } RuntimeType::Brillig => { diff --git a/test_programs/compile_success_contract/fold_non_contract_method/Nargo.toml b/test_programs/compile_success_contract/fold_non_contract_method/Nargo.toml new file mode 100644 index 00000000000..ff64bbb6f1b --- /dev/null +++ b/test_programs/compile_success_contract/fold_non_contract_method/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "fold_non_contract_method" +type = "contract" +authors = [""] +compiler_version = ">=0.26.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_contract/fold_non_contract_method/src/main.nr b/test_programs/compile_success_contract/fold_non_contract_method/src/main.nr new file mode 100644 index 00000000000..2e00ffa1381 --- /dev/null +++ b/test_programs/compile_success_contract/fold_non_contract_method/src/main.nr @@ -0,0 +1,18 @@ +contract Foo { + use crate::times_10; + + fn double(x: Field) -> pub Field { + x * 2 + } + fn triple(x: Field) -> pub Field { + x * 3 + } + fn times_40(x: Field) -> pub Field { + times_10(x) * 4 + } +} + +#[fold] +fn times_10(x: Field) -> Field { + x * 10 +} diff --git a/tooling/nargo/src/artifacts/program.rs b/tooling/nargo/src/artifacts/program.rs index 046db1cd8fa..9e660cbd359 100644 --- a/tooling/nargo/src/artifacts/program.rs +++ b/tooling/nargo/src/artifacts/program.rs @@ -34,6 +34,8 @@ pub struct ProgramArtifact { /// Map of file Id to the source code so locations in debug info can be mapped to source code they point to. pub file_map: BTreeMap, + + pub names: Vec, } impl From for ProgramArtifact { @@ -45,6 +47,7 @@ impl From for ProgramArtifact { bytecode: compiled_program.program, debug_symbols: compiled_program.debug, file_map: compiled_program.file_map, + names: compiled_program.names, } } } @@ -59,6 +62,7 @@ impl From for CompiledProgram { debug: program.debug_symbols, file_map: program.file_map, warnings: vec![], + names: program.names, } } } diff --git a/tooling/nargo_cli/build.rs b/tooling/nargo_cli/build.rs index 16df29e6985..bf97dfb3e96 100644 --- a/tooling/nargo_cli/build.rs +++ b/tooling/nargo_cli/build.rs @@ -244,7 +244,7 @@ fn compile_success_empty_{test_name}() {{ let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap_or_else(|e| {{ panic!("JSON was not well-formatted {{:?}}\n\n{{:?}}", e, std::str::from_utf8(&output.stdout)) }}); - let num_opcodes = &json["programs"][0]["acir_opcodes"]; + let num_opcodes = &json["programs"][0]["functions"][0]["acir_opcodes"]; assert_eq!(num_opcodes.as_u64().expect("number of opcodes should fit in a u64"), 0); }} "#, diff --git a/tooling/nargo_cli/src/cli/info_cmd.rs b/tooling/nargo_cli/src/cli/info_cmd.rs index 49924622392..72784013e17 100644 --- a/tooling/nargo_cli/src/cli/info_cmd.rs +++ b/tooling/nargo_cli/src/cli/info_cmd.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use acvm::acir::circuit::ExpressionWidth; +use acvm::acir::circuit::{ExpressionWidth, Program}; use backend_interface::BackendError; use clap::Args; use iter_extended::vecmap; @@ -24,9 +24,9 @@ use crate::errors::CliError; use super::{compile_cmd::compile_workspace, NargoConfig}; -/// Provides detailed information on a circuit +/// Provides detailed information on each of a program's function (represented by a single circuit) /// -/// Current information provided: +/// Current information provided per circuit: /// 1. The number of ACIR opcodes /// 2. Counts the final number gates in the circuit used by a backend #[derive(Debug, Clone, Args)] @@ -114,6 +114,7 @@ pub(crate) fn run( let binary_packages = workspace.into_iter().filter(|package| package.is_binary()).zip(compiled_programs); + let program_info = binary_packages .par_bridge() .map(|(package, program)| { @@ -134,10 +135,13 @@ pub(crate) fn run( } else { // Otherwise print human-readable table. if !info_report.programs.is_empty() { - let mut program_table = table!([Fm->"Package", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Backend Circuit Size"]); + let mut program_table = table!([Fm->"Package", Fm->"Function", Fm->"Expression Width", Fm->"ACIR Opcodes", Fm->"Backend Circuit Size"]); - for program in info_report.programs { - program_table.add_row(program.into()); + for program_info in info_report.programs { + let program_rows: Vec = program_info.into(); + for row in program_rows { + program_table.add_row(row); + } } program_table.printstd(); } @@ -223,18 +227,20 @@ struct ProgramInfo { name: String, #[serde(skip)] expression_width: ExpressionWidth, - acir_opcodes: usize, - circuit_size: u32, + functions: Vec, } -impl From for Row { +impl From for Vec { fn from(program_info: ProgramInfo) -> Self { - row![ - Fm->format!("{}", program_info.name), - format!("{:?}", program_info.expression_width), - Fc->format!("{}", program_info.acir_opcodes), - Fc->format!("{}", program_info.circuit_size), - ] + vecmap(program_info.functions, |function| { + row![ + Fm->format!("{}", program_info.name), + Fc->format!("{}", function.name), + format!("{:?}", program_info.expression_width), + Fc->format!("{}", function.acir_opcodes), + Fc->format!("{}", function.circuit_size), + ] + }) } } @@ -243,6 +249,7 @@ struct ContractInfo { name: String, #[serde(skip)] expression_width: ExpressionWidth, + // TODO(https://github.com/noir-lang/noir/issues/4720): Settle on how to display contract functions with non-inlined Acir calls functions: Vec, } @@ -273,13 +280,22 @@ fn count_opcodes_and_gates_in_program( package: &Package, expression_width: ExpressionWidth, ) -> Result { - Ok(ProgramInfo { - name: package.name.to_string(), - expression_width, - // TODO(https://github.com/noir-lang/noir/issues/4428) - acir_opcodes: compiled_program.program.functions[0].opcodes.len(), - circuit_size: backend.get_exact_circuit_size(&compiled_program.program)?, - }) + let functions = compiled_program + .program + .functions + .into_par_iter() + .enumerate() + .map(|(i, function)| -> Result<_, BackendError> { + Ok(FunctionInfo { + name: compiled_program.names[i].clone(), + acir_opcodes: function.opcodes.len(), + circuit_size: backend + .get_exact_circuit_size(&Program { functions: vec![function] })?, + }) + }) + .collect::>()?; + + Ok(ProgramInfo { name: package.name.to_string(), expression_width, functions }) } fn count_opcodes_and_gates_in_contract( @@ -293,7 +309,7 @@ fn count_opcodes_and_gates_in_contract( .map(|function| -> Result<_, BackendError> { Ok(FunctionInfo { name: function.name, - // TODO(https://github.com/noir-lang/noir/issues/4428) + // TODO(https://github.com/noir-lang/noir/issues/4720) acir_opcodes: function.bytecode.functions[0].opcodes.len(), circuit_size: backend.get_exact_circuit_size(&function.bytecode)?, }) From 464591438e9f1adf22e955763ffcbd7f3b2441e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Tue, 9 Apr 2024 10:17:49 +0200 Subject: [PATCH 159/416] chore: Remove the old Value struct from the oracle docs (#4738) # Description ## Problem\* We removed the old Value struct that added the objects with "inner" on the JS side for oracles. ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- docs/docs/how_to/how-to-oracles.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/docs/docs/how_to/how-to-oracles.md b/docs/docs/how_to/how-to-oracles.md index ab225b9421f..8cf8035a5c4 100644 --- a/docs/docs/how_to/how-to-oracles.md +++ b/docs/docs/how_to/how-to-oracles.md @@ -138,8 +138,8 @@ Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` d ```js server.addMethod("getSqrt", async (params) => { - const values = params[0].Array.map(({ inner }) => { - return { inner: `${Math.sqrt(parseInt(inner, 16))}` }; + const values = params[0].Array.map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; }); return { values: [{ Array: values }] }; }); @@ -147,27 +147,23 @@ server.addMethod("getSqrt", async (params) => { :::tip -Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a `inner` property *as a string*. For example: +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a field element *as a string*. For example: ```json -{ "values": [{ "Array": [{ "inner": "1" }, { "inner": "2"}]}]} -{ "values": [{ "Single": { "inner": "1" }}]} -{ "values": [{ "Single": { "inner": "1" }}, { "Array": [{ "inner": "1", { "inner": "2" }}]}]} +{ "values": [{ "Array": ["1", "2"] }]} +{ "values": [{ "Single": "1" }]} +{ "values": [{ "Single": "1" }, { "Array": ["1", "2"] }]} ``` If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: ```js -interface Value { - inner: string, -} - interface SingleForeignCallParam { - Single: Value, + Single: string, } interface ArrayForeignCallParam { - Array: Value[], + Array: string[], } type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; @@ -243,9 +239,9 @@ const foreignCallHandler = async (name, input) => { // notice that the "inputs" parameter contains *all* the inputs // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] const oracleReturn = await client.request(name, [ - { Array: input[0].map((i) => ({ inner: i.toString("hex") })) }, + { Array: input[0].map((i) => i.toString("hex")) }, ]); - return [oracleReturn.values[0].Array.map((x) => x.inner)]; + return [oracleReturn.values[0].Array]; }; // the rest of your NoirJS code From ac1c33e10148df263429dd42a2493bcfecb025af Mon Sep 17 00:00:00 2001 From: josh crites Date: Tue, 9 Apr 2024 04:42:34 -0400 Subject: [PATCH 160/416] chore(doc): Fix broken docs links (#4606) # Description ## Problem\* Resolves #4598 ## Summary\* ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../modules_packages_crates/crates_and_packages.md | 2 +- .../version-v0.19.0/language_concepts/06_generics.md | 4 ---- .../modules_packages_crates/crates_and_packages.md | 2 +- .../modules_packages_crates/crates_and_packages.md | 2 +- .../modules_packages_crates/crates_and_packages.md | 2 +- .../modules_packages_crates/crates_and_packages.md | 2 +- .../modules_packages_crates/crates_and_packages.md | 2 +- .../noir/modules_packages_crates/crates_and_packages.md | 2 +- .../noir/modules_packages_crates/crates_and_packages.md | 2 +- .../noir/modules_packages_crates/crates_and_packages.md | 2 +- 10 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md index 744de72bb2c..23f60855c30 100644 --- a/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.17.0/modules_packages_crates/crates_and_packages.md @@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.19.0/language_concepts/06_generics.md b/docs/versioned_docs/version-v0.19.0/language_concepts/06_generics.md index b700bd5bc5b..a932d7b47c2 100644 --- a/docs/versioned_docs/version-v0.19.0/language_concepts/06_generics.md +++ b/docs/versioned_docs/version-v0.19.0/language_concepts/06_generics.md @@ -107,7 +107,3 @@ fn main() { let array = [MyStruct::new(), MyStruct::new()]; assert(array_eq(array, array, MyStruct::eq)); } -``` - -You can see an example of generics in the tests -[here](https://github.com/noir-lang/noir/blob/master/crates/nargo_cli/tests/test_data/generics/src/main.nr). diff --git a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md index 744de72bb2c..23f60855c30 100644 --- a/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.19.0/modules_packages_crates/crates_and_packages.md @@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md index 744de72bb2c..23f60855c30 100644 --- a/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.19.1/modules_packages_crates/crates_and_packages.md @@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md index 744de72bb2c..23f60855c30 100644 --- a/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.19.2/modules_packages_crates/crates_and_packages.md @@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md index 744de72bb2c..23f60855c30 100644 --- a/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.19.3/modules_packages_crates/crates_and_packages.md @@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md index 744de72bb2c..23f60855c30 100644 --- a/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.19.4/modules_packages_crates/crates_and_packages.md @@ -23,7 +23,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md index 760a463094c..95ee9f52ab2 100644 --- a/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.22.0/noir/modules_packages_crates/crates_and_packages.md @@ -24,7 +24,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md index 760a463094c..95ee9f52ab2 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.23.0/noir/modules_packages_crates/crates_and_packages.md @@ -24,7 +24,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root diff --git a/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/crates_and_packages.md index 760a463094c..95ee9f52ab2 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/crates_and_packages.md +++ b/docs/versioned_docs/version-v0.24.0/noir/modules_packages_crates/crates_and_packages.md @@ -24,7 +24,7 @@ _Library crates_ don't have a `main` function and they don't compile down to ACI #### Contracts -Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/yarn-project/noir-contracts/contracts). +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). ### Crate Root From b8dd77043fddb20f02806ddfd639e72e61e9291b Mon Sep 17 00:00:00 2001 From: Savio <72797635+Savio-Sou@users.noreply.github.com> Date: Tue, 9 Apr 2024 07:01:08 -0400 Subject: [PATCH 161/416] chore(docs): Fix link in the Data Types page (#4527) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description ## Problem\* Existing link in the [Data Types page](https://noir-lang.org/docs/dev/noir/concepts/data_types/) to the generics page opens https://noir-lang.org/assets/files/generics-ff7aa43d69e6cca523a1f2e74064a062.md in a separate tab, instead of the page in the docs. ### 1. Click: image ### 2. Opens: image ## Summary\* Fix link to relatively redirects to the generics page. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: José Pedro Sousa --- docs/docs/noir/concepts/data_types/index.md | 2 +- .../version-v0.23.0/noir/concepts/data_types/index.md | 2 +- .../version-v0.24.0/noir/concepts/data_types/index.md | 2 +- .../version-v0.25.0/noir/concepts/data_types/index.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/noir/concepts/data_types/index.md b/docs/docs/noir/concepts/data_types/index.md index 97b3b2cb094..357813c147a 100644 --- a/docs/docs/noir/concepts/data_types/index.md +++ b/docs/docs/noir/concepts/data_types/index.md @@ -79,7 +79,7 @@ fn main() { } ``` -Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): +Type aliases can also be used with [generics](../generics.md): ```rust type Id = Size; diff --git a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md index 3c9cd4c2437..f09bca0ee04 100644 --- a/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md +++ b/docs/versioned_docs/version-v0.23.0/noir/concepts/data_types/index.md @@ -79,7 +79,7 @@ fn main() { } ``` -Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): +Type aliases can also be used with [generics](../generics.md): ```rust type Id = Size; diff --git a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/index.md index 3c9cd4c2437..f09bca0ee04 100644 --- a/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/index.md +++ b/docs/versioned_docs/version-v0.24.0/noir/concepts/data_types/index.md @@ -79,7 +79,7 @@ fn main() { } ``` -Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): +Type aliases can also be used with [generics](../generics.md): ```rust type Id = Size; diff --git a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md index 97b3b2cb094..357813c147a 100644 --- a/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md +++ b/docs/versioned_docs/version-v0.25.0/noir/concepts/data_types/index.md @@ -79,7 +79,7 @@ fn main() { } ``` -Type aliases can also be used with [generics](@site/docs/noir/concepts/generics.md): +Type aliases can also be used with [generics](../generics.md): ```rust type Id = Size; From f3eee6c00a9b1ea939c5757d91faac693e909301 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Tue, 9 Apr 2024 14:59:19 +0100 Subject: [PATCH 162/416] fix: correct ICE panic messages in brillig `convert_black_box_call` (#4761) # Description ## Problem\* Related to #4687 ## Summary\* The error messages here were clearly copy-pasted without any updates so this PR fixes these to match the actual black box function they're for. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/brillig/brillig_gen/brillig_black_box.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs index 874be06b86c..36b5f8793cb 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_black_box.rs @@ -246,7 +246,7 @@ pub(crate) fn convert_black_box_call( }); } else { unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + "ICE: BigIntAdd expects two register arguments and one result register" ) } } @@ -263,7 +263,7 @@ pub(crate) fn convert_black_box_call( }); } else { unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + "ICE: BigIntSub expects two register arguments and one result register" ) } } @@ -280,7 +280,7 @@ pub(crate) fn convert_black_box_call( }); } else { unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + "ICE: BigIntMul expects two register arguments and one result register" ) } } @@ -297,7 +297,7 @@ pub(crate) fn convert_black_box_call( }); } else { unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + "ICE: BigIntDiv expects two register arguments and one result register" ) } } @@ -314,7 +314,7 @@ pub(crate) fn convert_black_box_call( }); } else { unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + "ICE: BigIntFromLeBytes expects two register arguments and one result register" ) } } @@ -330,7 +330,7 @@ pub(crate) fn convert_black_box_call( }); } else { unreachable!( - "ICE: EmbeddedCurveAdd expects two register arguments and one array result" + "ICE: BigIntToLeBytes expects one register argument and one array result" ) } } From a9766c5e9650160bcafc693f2617e441ed47721a Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Wed, 10 Apr 2024 17:25:32 +0100 Subject: [PATCH 163/416] fix: Update commit for noir-gates-diff (#4773) # Description ## Problem\* Resolves [](https://github.com/noir-lang/noir/pull/4764#issuecomment-2047911499) ## Summary\* This is the noir-gates-diff PR this commit update references: https://github.com/TomAFrench/noir-gates-diff/pull/3. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .github/workflows/gates_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gates_report.yml b/.github/workflows/gates_report.yml index ebf17f7374c..be55236c40f 100644 --- a/.github/workflows/gates_report.yml +++ b/.github/workflows/gates_report.yml @@ -74,7 +74,7 @@ jobs: - name: Compare gates reports id: gates_diff - uses: TomAFrench/noir-gates-diff@df05f34e2ab275ddc4f2cac065df1c88f8a05e5d + uses: vezenovm/noir-gates-diff@f80ea702d579873ff80f0261c62e2bae5203748e with: report: gates_report.json summaryQuantile: 0.9 # only display the 10% most significant circuit size diffs in the summary (defaults to 20%) From 606ff4448571b0cfe4b92420c766c4239a4b23a0 Mon Sep 17 00:00:00 2001 From: kevaundray Date: Wed, 10 Apr 2024 22:38:26 +0100 Subject: [PATCH 164/416] chore: Release Noir(0.27.0) (#4632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* ---
0.27.0 ## [0.27.0](https://github.com/noir-lang/noir/compare/v0.26.0...v0.27.0) (2024-04-10) ### ⚠ BREAKING CHANGES * Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ### Features * **acir_gen:** Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) * **acvm_js:** Execute program ([#4694](https://github.com/noir-lang/noir/issues/4694)) ([386f6d0](https://github.com/noir-lang/noir/commit/386f6d0a5822912db878285cb001032a7c0ff622)) * **acvm:** Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) * Add `remove_enable_side_effects` SSA pass ([#4224](https://github.com/noir-lang/noir/issues/4224)) ([94952db](https://github.com/noir-lang/noir/commit/94952db604b70a1ec18115b291de3c52565a641e)) * Allow slices to brillig entry points ([#4713](https://github.com/noir-lang/noir/issues/4713)) ([62423d5](https://github.com/noir-lang/noir/commit/62423d552beca749b6f86b1330555aab18db58d0)) * Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ([0bc18c4](https://github.com/noir-lang/noir/commit/0bc18c4f78171590dd58bded959f68f53a44cc8c)) * **docs:** Documenting noir codegen ([#4454](https://github.com/noir-lang/noir/issues/4454)) ([24f6d85](https://github.com/noir-lang/noir/commit/24f6d85f2467a109399d21729f8bb0f97c5ba6db)) * Improve nargo check cli with --override flag and feedback for existing files ([#4575](https://github.com/noir-lang/noir/issues/4575)) ([5e7fbd4](https://github.com/noir-lang/noir/commit/5e7fbd4e706b1691ba2dd960469cfa3b31dfb753)) * Improve optimisations on range constraints ([#4690](https://github.com/noir-lang/noir/issues/4690)) ([96b8110](https://github.com/noir-lang/noir/commit/96b811079b0e7c0345210cfc705c00345b0b3334)) * Improve SSA type-awareness in EQ and MUL instructions ([#4691](https://github.com/noir-lang/noir/issues/4691)) ([669f1a0](https://github.com/noir-lang/noir/commit/669f1a0fa47ad9093888a8ce8e525cb02bcf19b5)) * **nargo:** Multiple circuits info for binary programs ([#4719](https://github.com/noir-lang/noir/issues/4719)) ([50d2735](https://github.com/noir-lang/noir/commit/50d2735825454a8638a308156d4ea23b3c4420d8)) ### Bug Fixes * "Types in a binary operation should match, but found T and T" ([#4648](https://github.com/noir-lang/noir/issues/4648)) ([30c9f31](https://github.com/noir-lang/noir/commit/30c9f3151d447de8c7467ccbee82e32b8c46a396)) * **acvm:** Mark outputs of Opcode::Call solvable ([#4708](https://github.com/noir-lang/noir/issues/4708)) ([8fea405](https://github.com/noir-lang/noir/commit/8fea40576f262bd5bb588923c0660d8967404e56)) * Correct ICE panic messages in brillig `convert_black_box_call` ([#4761](https://github.com/noir-lang/noir/issues/4761)) ([f3eee6c](https://github.com/noir-lang/noir/commit/f3eee6c00a9b1ea939c5757d91faac693e909301)) * Error when a type variable is unbound during monomorphization instead of defaulting to Field ([#4674](https://github.com/noir-lang/noir/issues/4674)) ([03cdba4](https://github.com/noir-lang/noir/commit/03cdba45ac073fd6fdd91549736f36f1abaef15a)) * Field comparisons ([#4704](https://github.com/noir-lang/noir/issues/4704)) ([079cb2a](https://github.com/noir-lang/noir/commit/079cb2a99d2d50b50688bfb56fa014acde3e3d71)) * Impl search no longer selects an impl if multiple are applicable ([#4662](https://github.com/noir-lang/noir/issues/4662)) ([0150600](https://github.com/noir-lang/noir/commit/0150600922ee8b3e67c9b592338e8832f446685b)) * Last use analysis & make it an SSA pass ([#4686](https://github.com/noir-lang/noir/issues/4686)) ([0d3d5fd](https://github.com/noir-lang/noir/commit/0d3d5fda9659a563ba9c2014b7c1af9e1d332ab0)) * Slice coercions ([#4640](https://github.com/noir-lang/noir/issues/4640)) ([c0bae17](https://github.com/noir-lang/noir/commit/c0bae17e70f55ebf4b1639e0dfb075d8c5c97892)) * **ssa:** Accurate constant type for slice dummy data in flattening ([#4661](https://github.com/noir-lang/noir/issues/4661)) ([b87654e](https://github.com/noir-lang/noir/commit/b87654e2b4761dfacc916dac70d43c1b572ec636)) * **ssa:** Do not use get_value_max_num_bits when we want pure type information ([#4700](https://github.com/noir-lang/noir/issues/4700)) ([b55a580](https://github.com/noir-lang/noir/commit/b55a580388abc95bab6c6ef8c50eae3c5497eb3f)) * **ssa:** Fix slice intrinsic handling in the capacity tracker ([#4643](https://github.com/noir-lang/noir/issues/4643)) ([1b50ce1](https://github.com/noir-lang/noir/commit/1b50ce155cf95193937729c2a23f34b0ade42ea0)) * Unknown slice lengths coming from as_slice ([#4725](https://github.com/noir-lang/noir/issues/4725)) ([f21129e](https://github.com/noir-lang/noir/commit/f21129ef05efb76c5df6ee15a134f1ea535d8e90)) * Update commit for noir-gates-diff ([#4773](https://github.com/noir-lang/noir/issues/4773)) ([a9766c5](https://github.com/noir-lang/noir/commit/a9766c5e9650160bcafc693f2617e441ed47721a))
0.43.0 ## [0.43.0](https://github.com/noir-lang/noir/compare/v0.42.0...v0.43.0) (2024-04-10) ### ⚠ BREAKING CHANGES * Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) * automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) * move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) * note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) * rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) * init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) * Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) * Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) * Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) * Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ### Features * Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **acir_gen:** Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) * **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * **acvm_js:** Execute program ([#4694](https://github.com/noir-lang/noir/issues/4694)) ([386f6d0](https://github.com/noir-lang/noir/commit/386f6d0a5822912db878285cb001032a7c0ff622)) * **acvm:** Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) * Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) * Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) * Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de)) * Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) * Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) * **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) * Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c)) * Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ([0bc18c4](https://github.com/noir-lang/noir/commit/0bc18c4f78171590dd58bded959f68f53a44cc8c)) * Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) * Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671)) * Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3)) * Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c)) * Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc)) * Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138)) * Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) * Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) ### Bug Fixes * **acvm:** Mark outputs of Opcode::Call solvable ([#4708](https://github.com/noir-lang/noir/issues/4708)) ([8fea405](https://github.com/noir-lang/noir/commit/8fea40576f262bd5bb588923c0660d8967404e56)) * Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) * Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b)) * Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925)) ### Miscellaneous Chores * **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) * Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) * Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579)) * Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e)) * Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc))
--- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --- .release-please-manifest.json | 4 +- CHANGELOG.md | 38 ++ Cargo.lock | 52 +- Cargo.toml | 16 +- acvm-repo/CHANGELOG.md | 82 +++ acvm-repo/acir/Cargo.toml | 2 +- acvm-repo/acir_field/Cargo.toml | 2 +- acvm-repo/acvm/Cargo.toml | 2 +- acvm-repo/acvm_js/Cargo.toml | 2 +- acvm-repo/acvm_js/package.json | 2 +- acvm-repo/blackbox_solver/Cargo.toml | 2 +- acvm-repo/bn254_blackbox_solver/Cargo.toml | 2 +- acvm-repo/brillig/Cargo.toml | 2 +- acvm-repo/brillig_vm/Cargo.toml | 2 +- compiler/wasm/package.json | 2 +- .../explainers/explainer-oracle.md | 57 ++ .../explainers/explainer-recursion.md | 176 ++++++ .../getting_started/_category_.json | 5 + .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 142 +++++ .../hello_noir/project_breakdown.md | 199 ++++++ .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 ++ .../installation/other_install_methods.md | 102 ++++ .../getting_started/tooling/_category_.json | 6 + .../getting_started/tooling/index.mdx | 38 ++ .../tooling/language_server.md | 43 ++ .../getting_started/tooling/noir_codegen.md | 113 ++++ .../getting_started/tooling/testing.md | 62 ++ .../version-v0.27.0/how_to/_category_.json | 5 + .../version-v0.27.0/how_to/how-to-oracles.md | 276 +++++++++ .../how_to/how-to-recursion.md | 179 ++++++ .../how_to/how-to-solidity-verifier.md | 231 +++++++ .../version-v0.27.0/how_to/merkle-proof.mdx | 48 ++ .../how_to/using-devcontainers.mdx | 110 ++++ docs/versioned_docs/version-v0.27.0/index.mdx | 67 +++ .../version-v0.27.0/migration_notes.md | 105 ++++ .../noir/concepts/_category_.json | 6 + .../version-v0.27.0/noir/concepts/assert.md | 45 ++ .../version-v0.27.0/noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 77 +++ .../version-v0.27.0/noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 251 ++++++++ .../noir/concepts/data_types/booleans.md | 31 + .../noir/concepts/data_types/fields.md | 192 ++++++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 110 ++++ .../noir/concepts/data_types/integers.md | 155 +++++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 170 ++++++ .../noir/concepts/data_types/strings.md | 80 +++ .../noir/concepts/data_types/structs.md | 70 +++ .../noir/concepts/data_types/tuples.md | 48 ++ .../version-v0.27.0/noir/concepts/distinct.md | 64 ++ .../noir/concepts/functions.md | 226 +++++++ .../version-v0.27.0/noir/concepts/generics.md | 106 ++++ .../version-v0.27.0/noir/concepts/globals.md | 72 +++ .../version-v0.27.0/noir/concepts/lambdas.md | 81 +++ .../noir/concepts/mutability.md | 121 ++++ .../version-v0.27.0/noir/concepts/ops.md | 98 +++ .../version-v0.27.0/noir/concepts/oracles.md | 23 + .../noir/concepts/shadowing.md | 44 ++ .../version-v0.27.0/noir/concepts/traits.md | 389 ++++++++++++ .../noir/concepts/unconstrained.md | 99 +++ .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 ++ .../modules_packages_crates/dependencies.md | 124 ++++ .../noir/modules_packages_crates/modules.md | 105 ++++ .../modules_packages_crates/workspaces.md | 40 ++ .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/bigint.md | 119 ++++ .../noir/standard_library/black_box_fns.md | 31 + .../noir/standard_library/bn254.md | 46 ++ .../standard_library/containers/boundedvec.md | 326 ++++++++++ .../standard_library/containers/hashmap.md | 569 ++++++++++++++++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 151 +++++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ec_primitives.md | 102 ++++ .../ecdsa_sig_verification.mdx | 98 +++ .../cryptographic_primitives/eddsa.mdx | 37 ++ .../cryptographic_primitives/hashes.mdx | 331 ++++++++++ .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/scalar.mdx | 33 + .../cryptographic_primitives/schnorr.mdx | 64 ++ .../noir/standard_library/logging.md | 78 +++ .../noir/standard_library/merkle_trees.md | 58 ++ .../noir/standard_library/options.md | 101 ++++ .../noir/standard_library/recursion.md | 88 +++ .../noir/standard_library/traits.md | 408 +++++++++++++ .../noir/standard_library/zeroed.md | 26 + .../NoirJS/backend_barretenberg/.nojekyll | 1 + .../classes/BarretenbergBackend.md | 119 ++++ .../NoirJS/backend_barretenberg/index.md | 58 ++ .../type-aliases/BackendOptions.md | 21 + .../backend_barretenberg/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 132 ++++ .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../NoirJS/noir_js/functions/keccak256.md | 21 + .../NoirJS/noir_js/functions/sha256.md | 21 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 54 ++ .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 ++ .../noir_wasm/functions/compile_contract.md | 51 ++ .../noir_wasm/functions/createFileManager.md | 21 + .../functions/inflateDebugSymbols.md | 21 + .../reference/NoirJS/noir_wasm/index.md | 49 ++ .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + .../version-v0.27.0/reference/_category_.json | 5 + .../reference/nargo_commands.md | 381 ++++++++++++ .../version-v0.27.0/tutorials/noirjs_app.md | 279 +++++++++ .../version-v0.27.0-sidebars.json | 83 +++ tooling/noir_codegen/package.json | 2 +- tooling/noir_js/package.json | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- tooling/noir_js_types/package.json | 2 +- tooling/noirc_abi_wasm/package.json | 2 +- 128 files changed, 9428 insertions(+), 51 deletions(-) create mode 100644 docs/versioned_docs/version-v0.27.0/explainers/explainer-oracle.md create mode 100644 docs/versioned_docs/version-v0.27.0/explainers/explainer-recursion.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/project_breakdown.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/installation/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/installation/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/installation/other_install_methods.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/tooling/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/tooling/language_server.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/tooling/noir_codegen.md create mode 100644 docs/versioned_docs/version-v0.27.0/getting_started/tooling/testing.md create mode 100644 docs/versioned_docs/version-v0.27.0/how_to/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/how_to/how-to-oracles.md create mode 100644 docs/versioned_docs/version-v0.27.0/how_to/how-to-recursion.md create mode 100644 docs/versioned_docs/version-v0.27.0/how_to/how-to-solidity-verifier.md create mode 100644 docs/versioned_docs/version-v0.27.0/how_to/merkle-proof.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/how_to/using-devcontainers.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/index.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/migration_notes.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/assert.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/comments.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/control_flow.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_bus.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/fields.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/function_types.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/integers.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/references.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/slices.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/structs.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/tuples.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/distinct.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/functions.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/globals.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/lambdas.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/mutability.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/ops.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/oracles.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/shadowing.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/traits.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/crates_and_packages.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/modules.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/workspaces.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/bigint.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/bn254.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/boundedvec.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/hashmap.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/vec.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/scalar.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/logging.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/merkle_trees.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/options.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/traits.md create mode 100644 docs/versioned_docs/version-v0.27.0/noir/standard_library/zeroed.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/.nojekyll create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/.nojekyll create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/and.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/keccak256.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/sha256.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/xor.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/index.md create mode 100644 docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.27.0/reference/_category_.json create mode 100644 docs/versioned_docs/version-v0.27.0/reference/nargo_commands.md create mode 100644 docs/versioned_docs/version-v0.27.0/tutorials/noirjs_app.md create mode 100644 docs/versioned_sidebars/version-v0.27.0-sidebars.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d4cc095c484..7579928c999 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "0.26.0", - "acvm-repo": "0.42.0" + ".": "0.27.0", + "acvm-repo": "0.43.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c4bcad5840..0ab84df44e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # Changelog +## [0.27.0](https://github.com/noir-lang/noir/compare/v0.26.0...v0.27.0) (2024-04-10) + + +### ⚠ BREAKING CHANGES + +* Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) + +### Features + +* **acir_gen:** Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) +* **acvm_js:** Execute program ([#4694](https://github.com/noir-lang/noir/issues/4694)) ([386f6d0](https://github.com/noir-lang/noir/commit/386f6d0a5822912db878285cb001032a7c0ff622)) +* **acvm:** Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) +* Add `remove_enable_side_effects` SSA pass ([#4224](https://github.com/noir-lang/noir/issues/4224)) ([94952db](https://github.com/noir-lang/noir/commit/94952db604b70a1ec18115b291de3c52565a641e)) +* Allow slices to brillig entry points ([#4713](https://github.com/noir-lang/noir/issues/4713)) ([62423d5](https://github.com/noir-lang/noir/commit/62423d552beca749b6f86b1330555aab18db58d0)) +* Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ([0bc18c4](https://github.com/noir-lang/noir/commit/0bc18c4f78171590dd58bded959f68f53a44cc8c)) +* **docs:** Documenting noir codegen ([#4454](https://github.com/noir-lang/noir/issues/4454)) ([24f6d85](https://github.com/noir-lang/noir/commit/24f6d85f2467a109399d21729f8bb0f97c5ba6db)) +* Improve nargo check cli with --override flag and feedback for existing files ([#4575](https://github.com/noir-lang/noir/issues/4575)) ([5e7fbd4](https://github.com/noir-lang/noir/commit/5e7fbd4e706b1691ba2dd960469cfa3b31dfb753)) +* Improve optimisations on range constraints ([#4690](https://github.com/noir-lang/noir/issues/4690)) ([96b8110](https://github.com/noir-lang/noir/commit/96b811079b0e7c0345210cfc705c00345b0b3334)) +* Improve SSA type-awareness in EQ and MUL instructions ([#4691](https://github.com/noir-lang/noir/issues/4691)) ([669f1a0](https://github.com/noir-lang/noir/commit/669f1a0fa47ad9093888a8ce8e525cb02bcf19b5)) +* **nargo:** Multiple circuits info for binary programs ([#4719](https://github.com/noir-lang/noir/issues/4719)) ([50d2735](https://github.com/noir-lang/noir/commit/50d2735825454a8638a308156d4ea23b3c4420d8)) + + +### Bug Fixes + +* "Types in a binary operation should match, but found T and T" ([#4648](https://github.com/noir-lang/noir/issues/4648)) ([30c9f31](https://github.com/noir-lang/noir/commit/30c9f3151d447de8c7467ccbee82e32b8c46a396)) +* **acvm:** Mark outputs of Opcode::Call solvable ([#4708](https://github.com/noir-lang/noir/issues/4708)) ([8fea405](https://github.com/noir-lang/noir/commit/8fea40576f262bd5bb588923c0660d8967404e56)) +* Correct ICE panic messages in brillig `convert_black_box_call` ([#4761](https://github.com/noir-lang/noir/issues/4761)) ([f3eee6c](https://github.com/noir-lang/noir/commit/f3eee6c00a9b1ea939c5757d91faac693e909301)) +* Error when a type variable is unbound during monomorphization instead of defaulting to Field ([#4674](https://github.com/noir-lang/noir/issues/4674)) ([03cdba4](https://github.com/noir-lang/noir/commit/03cdba45ac073fd6fdd91549736f36f1abaef15a)) +* Field comparisons ([#4704](https://github.com/noir-lang/noir/issues/4704)) ([079cb2a](https://github.com/noir-lang/noir/commit/079cb2a99d2d50b50688bfb56fa014acde3e3d71)) +* Impl search no longer selects an impl if multiple are applicable ([#4662](https://github.com/noir-lang/noir/issues/4662)) ([0150600](https://github.com/noir-lang/noir/commit/0150600922ee8b3e67c9b592338e8832f446685b)) +* Last use analysis & make it an SSA pass ([#4686](https://github.com/noir-lang/noir/issues/4686)) ([0d3d5fd](https://github.com/noir-lang/noir/commit/0d3d5fda9659a563ba9c2014b7c1af9e1d332ab0)) +* Slice coercions ([#4640](https://github.com/noir-lang/noir/issues/4640)) ([c0bae17](https://github.com/noir-lang/noir/commit/c0bae17e70f55ebf4b1639e0dfb075d8c5c97892)) +* **ssa:** Accurate constant type for slice dummy data in flattening ([#4661](https://github.com/noir-lang/noir/issues/4661)) ([b87654e](https://github.com/noir-lang/noir/commit/b87654e2b4761dfacc916dac70d43c1b572ec636)) +* **ssa:** Do not use get_value_max_num_bits when we want pure type information ([#4700](https://github.com/noir-lang/noir/issues/4700)) ([b55a580](https://github.com/noir-lang/noir/commit/b55a580388abc95bab6c6ef8c50eae3c5497eb3f)) +* **ssa:** Fix slice intrinsic handling in the capacity tracker ([#4643](https://github.com/noir-lang/noir/issues/4643)) ([1b50ce1](https://github.com/noir-lang/noir/commit/1b50ce155cf95193937729c2a23f34b0ade42ea0)) +* Unknown slice lengths coming from as_slice ([#4725](https://github.com/noir-lang/noir/issues/4725)) ([f21129e](https://github.com/noir-lang/noir/commit/f21129ef05efb76c5df6ee15a134f1ea535d8e90)) +* Update commit for noir-gates-diff ([#4773](https://github.com/noir-lang/noir/issues/4773)) ([a9766c5](https://github.com/noir-lang/noir/commit/a9766c5e9650160bcafc693f2617e441ed47721a)) + ## [0.26.0](https://github.com/noir-lang/noir/compare/v0.25.0...v0.26.0) (2024-03-25) diff --git a/Cargo.lock b/Cargo.lock index 170b23be189..a31c415c77e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "acir" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acir_field", "base64 0.21.2", @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "acir_field" -version = "0.42.0" +version = "0.43.0" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -37,7 +37,7 @@ dependencies = [ [[package]] name = "acvm" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -53,7 +53,7 @@ dependencies = [ [[package]] name = "acvm_blackbox_solver" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acir", "blake2", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "acvm_js" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acvm", "bn254_blackbox_solver", @@ -231,7 +231,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arena" -version = "0.26.0" +version = "0.27.0" [[package]] name = "ark-bls12-381" @@ -432,7 +432,7 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aztec_macros" -version = "0.26.0" +version = "0.27.0" dependencies = [ "convert_case 0.6.0", "iter-extended", @@ -599,7 +599,7 @@ dependencies = [ [[package]] name = "bn254_blackbox_solver" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -618,7 +618,7 @@ dependencies = [ [[package]] name = "brillig" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acir_field", "serde", @@ -626,7 +626,7 @@ dependencies = [ [[package]] name = "brillig_vm" -version = "0.42.0" +version = "0.43.0" dependencies = [ "acir", "acvm_blackbox_solver", @@ -1732,7 +1732,7 @@ dependencies = [ [[package]] name = "fm" -version = "0.26.0" +version = "0.27.0" dependencies = [ "codespan-reporting", "iter-extended", @@ -2353,7 +2353,7 @@ dependencies = [ [[package]] name = "iter-extended" -version = "0.26.0" +version = "0.27.0" [[package]] name = "itertools" @@ -2738,7 +2738,7 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nargo" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "codespan-reporting", @@ -2765,7 +2765,7 @@ dependencies = [ [[package]] name = "nargo_cli" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "assert_cmd", @@ -2820,7 +2820,7 @@ dependencies = [ [[package]] name = "nargo_fmt" -version = "0.26.0" +version = "0.27.0" dependencies = [ "bytecount", "noirc_frontend", @@ -2832,7 +2832,7 @@ dependencies = [ [[package]] name = "nargo_toml" -version = "0.26.0" +version = "0.27.0" dependencies = [ "dirs", "fm", @@ -2905,7 +2905,7 @@ dependencies = [ [[package]] name = "noir_debugger" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "assert_cmd", @@ -2940,7 +2940,7 @@ dependencies = [ [[package]] name = "noir_lsp" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "async-lsp", @@ -2966,7 +2966,7 @@ dependencies = [ [[package]] name = "noir_wasm" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "build-data", @@ -2989,7 +2989,7 @@ dependencies = [ [[package]] name = "noirc_abi" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "iter-extended", @@ -3006,7 +3006,7 @@ dependencies = [ [[package]] name = "noirc_abi_wasm" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "build-data", @@ -3023,7 +3023,7 @@ dependencies = [ [[package]] name = "noirc_driver" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "aztec_macros", @@ -3044,7 +3044,7 @@ dependencies = [ [[package]] name = "noirc_errors" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "base64 0.21.2", @@ -3062,7 +3062,7 @@ dependencies = [ [[package]] name = "noirc_evaluator" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "chrono", @@ -3079,7 +3079,7 @@ dependencies = [ [[package]] name = "noirc_frontend" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "arena", @@ -3105,7 +3105,7 @@ dependencies = [ [[package]] name = "noirc_printable_type" -version = "0.26.0" +version = "0.27.0" dependencies = [ "acvm", "iter-extended", diff --git a/Cargo.toml b/Cargo.toml index 46ccb401fbd..5dd453415aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ resolver = "2" [workspace.package] # x-release-please-start-version -version = "0.26.0" +version = "0.27.0" # x-release-please-end authors = ["The Noir Team "] edition = "2021" @@ -52,13 +52,13 @@ repository = "https://github.com/noir-lang/noir/" [workspace.dependencies] # ACVM workspace dependencies -acir_field = { version = "0.42.0", path = "acvm-repo/acir_field", default-features = false } -acir = { version = "0.42.0", path = "acvm-repo/acir", default-features = false } -acvm = { version = "0.42.0", path = "acvm-repo/acvm" } -brillig = { version = "0.42.0", path = "acvm-repo/brillig", default-features = false } -brillig_vm = { version = "0.42.0", path = "acvm-repo/brillig_vm", default-features = false } -acvm_blackbox_solver = { version = "0.42.0", path = "acvm-repo/blackbox_solver", default-features = false } -bn254_blackbox_solver = { version = "0.42.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } +acir_field = { version = "0.43.0", path = "acvm-repo/acir_field", default-features = false } +acir = { version = "0.43.0", path = "acvm-repo/acir", default-features = false } +acvm = { version = "0.43.0", path = "acvm-repo/acvm" } +brillig = { version = "0.43.0", path = "acvm-repo/brillig", default-features = false } +brillig_vm = { version = "0.43.0", path = "acvm-repo/brillig_vm", default-features = false } +acvm_blackbox_solver = { version = "0.43.0", path = "acvm-repo/blackbox_solver", default-features = false } +bn254_blackbox_solver = { version = "0.43.0", path = "acvm-repo/bn254_blackbox_solver", default-features = false } # Noir compiler workspace dependencies arena = { path = "compiler/utils/arena" } diff --git a/acvm-repo/CHANGELOG.md b/acvm-repo/CHANGELOG.md index 33cc83d7dd9..9d9ff539559 100644 --- a/acvm-repo/CHANGELOG.md +++ b/acvm-repo/CHANGELOG.md @@ -5,6 +5,88 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.43.0](https://github.com/noir-lang/noir/compare/v0.42.0...v0.43.0) (2024-04-10) + + +### ⚠ BREAKING CHANGES + +* Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) +* automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) +* move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) +* note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) +* rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) +* init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) +* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) +* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) +* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) +* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) + +### Features + +* Acir call opcode (https://github.com/AztecProtocol/aztec-packages/pull/4773) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **acir_gen:** Fold attribute at compile-time and initial non inlined ACIR (https://github.com/AztecProtocol/aztec-packages/pull/5341) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) +* **acir:** Program and witness stack structure (https://github.com/AztecProtocol/aztec-packages/pull/5149) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* **acvm_js:** Execute program ([#4694](https://github.com/noir-lang/noir/issues/4694)) ([386f6d0](https://github.com/noir-lang/noir/commit/386f6d0a5822912db878285cb001032a7c0ff622)) +* **acvm:** Execute multiple circuits (https://github.com/AztecProtocol/aztec-packages/pull/5380) ([a0f7474](https://github.com/noir-lang/noir/commit/a0f7474ae6bd74132efdb945d2eb2383f3913cce)) +* Add bit size to const opcode (https://github.com/AztecProtocol/aztec-packages/pull/4385) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add CMOV instruction to brillig and brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5308) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* Add expression width into acir (https://github.com/AztecProtocol/aztec-packages/pull/4014) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Add instrumentation for tracking variables in debugging ([#4122](https://github.com/noir-lang/noir/issues/4122)) ([c58d691](https://github.com/noir-lang/noir/commit/c58d69141b54a918cd1675400c00bfd48720f896)) +* Add poseidon2 opcode implementation for acvm/brillig, and Noir ([#4398](https://github.com/noir-lang/noir/issues/4398)) ([10e8292](https://github.com/noir-lang/noir/commit/10e82920798380f50046e52db4a20ca205191ab7)) +* Add support for overriding expression width ([#4117](https://github.com/noir-lang/noir/issues/4117)) ([c8026d5](https://github.com/noir-lang/noir/commit/c8026d557d535b10fe455165d6445076df7a03de)) +* Added cast opcode and cast calldata (https://github.com/AztecProtocol/aztec-packages/pull/4423) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Allow brillig to read arrays directly from memory (https://github.com/AztecProtocol/aztec-packages/pull/4460) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow nested arrays and vectors in Brillig foreign calls (https://github.com/AztecProtocol/aztec-packages/pull/4478) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Allow variables and stack trace inspection in the debugger ([#4184](https://github.com/noir-lang/noir/issues/4184)) ([bf263fc](https://github.com/noir-lang/noir/commit/bf263fc8d843940f328a90f6366edd2671fb2682)) +* Automatic NoteInterface and NoteGetterOptions auto select (https://github.com/AztecProtocol/aztec-packages/pull/4508) ([13eb71b](https://github.com/noir-lang/noir/commit/13eb71b8de44eb6aad9c37943ad06fc73db589f5)) +* **avm:** Back in avm context with macro - refactor context (https://github.com/AztecProtocol/aztec-packages/pull/4438) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* **avm:** Brillig CONST of size > u128 (https://github.com/AztecProtocol/aztec-packages/pull/5217) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* **aztec-nr:** Initial work for aztec public vm macro (https://github.com/AztecProtocol/aztec-packages/pull/4400) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Backpropagate constants in ACIR during optimization ([#3926](https://github.com/noir-lang/noir/issues/3926)) ([aad0da0](https://github.com/noir-lang/noir/commit/aad0da024c69663f42e6913e674682d5864b26ae)) +* Breaking changes from aztec-packages ([#3955](https://github.com/noir-lang/noir/issues/3955)) ([5be049e](https://github.com/noir-lang/noir/commit/5be049eee6c342649462282ee04f6411e6ea392c)) +* Brillig IR refactor (https://github.com/AztecProtocol/aztec-packages/pull/5233) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Brillig typed memory (https://github.com/AztecProtocol/aztec-packages/pull/5395) ([0bc18c4](https://github.com/noir-lang/noir/commit/0bc18c4f78171590dd58bded959f68f53a44cc8c)) +* Check initializer msg.sender matches deployer from address preimage (https://github.com/AztecProtocol/aztec-packages/pull/5222) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Evaluation of dynamic assert messages ([#4101](https://github.com/noir-lang/noir/issues/4101)) ([c284e01](https://github.com/noir-lang/noir/commit/c284e01bfe20ceae4414dc123624b5cbb8b66d09)) +* Init storage macro (https://github.com/AztecProtocol/aztec-packages/pull/4200) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Initial Earthly CI (https://github.com/AztecProtocol/aztec-packages/pull/5069) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Internal as a macro (https://github.com/AztecProtocol/aztec-packages/pull/4898) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* New brillig field operations and refactor of binary operations (https://github.com/AztecProtocol/aztec-packages/pull/5208) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Note type ids (https://github.com/AztecProtocol/aztec-packages/pull/4500) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove range constraints from witnesses which are constrained to be constants ([#3928](https://github.com/noir-lang/noir/issues/3928)) ([afe9c7a](https://github.com/noir-lang/noir/commit/afe9c7a38bb9d4245205d3aa46d4ce23d70a5671)) +* Remove replacement of boolean range opcodes with `AssertZero` opcodes ([#4107](https://github.com/noir-lang/noir/issues/4107)) ([dac0e87](https://github.com/noir-lang/noir/commit/dac0e87ee3be3446b92bbb12ef4832fd493fcee3)) +* Signed integer division and modulus in brillig gen (https://github.com/AztecProtocol/aztec-packages/pull/5279) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Support contracts with no constructor (https://github.com/AztecProtocol/aztec-packages/pull/5175) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync `aztec-packages` ([#4011](https://github.com/noir-lang/noir/issues/4011)) ([fee2452](https://github.com/noir-lang/noir/commit/fee24523c427c27f0bdaf98ea09a852a2da3e94c)) +* Sync commits from `aztec-packages` ([#4068](https://github.com/noir-lang/noir/issues/4068)) ([7a8f3a3](https://github.com/noir-lang/noir/commit/7a8f3a33b57875e681e3d81e667e3570a1cdbdcc)) +* Sync commits from `aztec-packages` ([#4144](https://github.com/noir-lang/noir/issues/4144)) ([0205d3b](https://github.com/noir-lang/noir/commit/0205d3b4ad0cf5ffd775a43eb5af273a772cf138)) +* Sync from aztec-packages ([#4483](https://github.com/noir-lang/noir/issues/4483)) ([fe8f277](https://github.com/noir-lang/noir/commit/fe8f2776ccfde29209a2c3fc162311c99e4f59be)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5234) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) +* Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5286) ([c3c9e19](https://github.com/noir-lang/noir/commit/c3c9e19a20d61272a04b95fd6c7d34cc4cb96e45)) + + +### Bug Fixes + +* **acvm:** Mark outputs of Opcode::Call solvable ([#4708](https://github.com/noir-lang/noir/issues/4708)) ([8fea405](https://github.com/noir-lang/noir/commit/8fea40576f262bd5bb588923c0660d8967404e56)) +* Noir test incorrect reporting (https://github.com/AztecProtocol/aztec-packages/pull/4925) ([5f57ebb](https://github.com/noir-lang/noir/commit/5f57ebb7ff4b810802f90699a10f4325ef904f2e)) +* Remove panic from `init_log_level` in `acvm_js` ([#4195](https://github.com/noir-lang/noir/issues/4195)) ([2e26530](https://github.com/noir-lang/noir/commit/2e26530bf53006c1ed4fee310bcaa905c95dd95b)) +* Return error rather instead of panicking on invalid circuit ([#3976](https://github.com/noir-lang/noir/issues/3976)) ([67201bf](https://github.com/noir-lang/noir/commit/67201bfc21a9c8858aa86be9cd47d463fb78d925)) + + +### Miscellaneous Chores + +* **acir:** Move `is_recursive` flag to be part of the circuit definition (https://github.com/AztecProtocol/aztec-packages/pull/4221) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) +* Move noir out of yarn-project (https://github.com/AztecProtocol/aztec-packages/pull/4479) ([78ef013](https://github.com/noir-lang/noir/commit/78ef0134b82e76a73dadb6c7975def22290e3a1a)) +* Remove unused methods on ACIR opcodes ([#3841](https://github.com/noir-lang/noir/issues/3841)) ([9e5d0e8](https://github.com/noir-lang/noir/commit/9e5d0e813d61a0bfb5ee68174ed287c5a20f1579)) +* Rename Arithmetic opcode to AssertZero ([#3840](https://github.com/noir-lang/noir/issues/3840)) ([836f171](https://github.com/noir-lang/noir/commit/836f17145c2901060706294461c2d282dd121b3e)) +* Rename bigint_neg into bigint_sub (https://github.com/AztecProtocol/aztec-packages/pull/4420) ([158c8ce](https://github.com/noir-lang/noir/commit/158c8cec7f0dc698042e9512001dd2c9d6b40bcc)) + ## [0.42.0](https://github.com/noir-lang/noir/compare/v0.41.0...v0.42.0) (2024-03-25) diff --git a/acvm-repo/acir/Cargo.toml b/acvm-repo/acir/Cargo.toml index 368f49258f9..99095ad3f61 100644 --- a/acvm-repo/acir/Cargo.toml +++ b/acvm-repo/acir/Cargo.toml @@ -2,7 +2,7 @@ name = "acir" description = "ACIR is the IR that the VM processes, it is analogous to LLVM IR" # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acir_field/Cargo.toml b/acvm-repo/acir_field/Cargo.toml index d63a885bfd8..7a260ea1fa2 100644 --- a/acvm-repo/acir_field/Cargo.toml +++ b/acvm-repo/acir_field/Cargo.toml @@ -2,7 +2,7 @@ name = "acir_field" description = "The field implementation being used by ACIR." # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm/Cargo.toml b/acvm-repo/acvm/Cargo.toml index d0ea52e859d..e6554d3f773 100644 --- a/acvm-repo/acvm/Cargo.toml +++ b/acvm-repo/acvm/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm" description = "The virtual machine that processes ACIR given a backend/proof system." # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/Cargo.toml b/acvm-repo/acvm_js/Cargo.toml index 8319c38aee2..4635dc8663e 100644 --- a/acvm-repo/acvm_js/Cargo.toml +++ b/acvm-repo/acvm_js/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_js" description = "Typescript wrapper around the ACVM allowing execution of ACIR code" # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/acvm_js/package.json b/acvm-repo/acvm_js/package.json index 44d99f13c31..63f12942018 100644 --- a/acvm-repo/acvm_js/package.json +++ b/acvm-repo/acvm_js/package.json @@ -1,6 +1,6 @@ { "name": "@noir-lang/acvm_js", - "version": "0.42.0", + "version": "0.43.0", "publishConfig": { "access": "public" }, diff --git a/acvm-repo/blackbox_solver/Cargo.toml b/acvm-repo/blackbox_solver/Cargo.toml index 8f5ff862360..d093b24a129 100644 --- a/acvm-repo/blackbox_solver/Cargo.toml +++ b/acvm-repo/blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "acvm_blackbox_solver" description = "A solver for the blackbox functions found in ACIR and Brillig" # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/bn254_blackbox_solver/Cargo.toml b/acvm-repo/bn254_blackbox_solver/Cargo.toml index 1ad5103d2cb..318833180ea 100644 --- a/acvm-repo/bn254_blackbox_solver/Cargo.toml +++ b/acvm-repo/bn254_blackbox_solver/Cargo.toml @@ -2,7 +2,7 @@ name = "bn254_blackbox_solver" description = "Solvers for black box functions which are specific for the bn254 curve" # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig/Cargo.toml b/acvm-repo/brillig/Cargo.toml index d3f082fda86..463f6286d6b 100644 --- a/acvm-repo/brillig/Cargo.toml +++ b/acvm-repo/brillig/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig" description = "Brillig is the bytecode ACIR uses for non-determinism." # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/acvm-repo/brillig_vm/Cargo.toml b/acvm-repo/brillig_vm/Cargo.toml index 95675469479..67e16c21d8b 100644 --- a/acvm-repo/brillig_vm/Cargo.toml +++ b/acvm-repo/brillig_vm/Cargo.toml @@ -2,7 +2,7 @@ name = "brillig_vm" description = "The virtual machine that processes Brillig bytecode, used to introduce non-determinism to the ACVM" # x-release-please-start-version -version = "0.42.0" +version = "0.43.0" # x-release-please-end authors.workspace = true edition.workspace = true diff --git a/compiler/wasm/package.json b/compiler/wasm/package.json index 6d11a5ba5c8..3bcf60afbe8 100644 --- a/compiler/wasm/package.json +++ b/compiler/wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.26.0", + "version": "0.27.0", "license": "(MIT OR Apache-2.0)", "main": "dist/main.js", "types": "./dist/types/src/index.d.cts", diff --git a/docs/versioned_docs/version-v0.27.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.27.0/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.27.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.27.0/explainers/explainer-recursion.md new file mode 100644 index 00000000000..18846176ca7 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.27.0/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..743c4d8d634 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/index.md @@ -0,0 +1,142 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution of our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..6160a102c6c --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,199 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. + +Take a private asset transfer as an example: + +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.27.0/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.27.0/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.27.0/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..3634723562b --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/installation/other_install_methods.md @@ -0,0 +1,102 @@ +--- +title: Alternative Installations +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Uninstalling Nargo + ] +sidebar_position: 1 +--- + +## Encouraged Installation Method: Noirup + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + ```sh + noirup --path ./path/to/local/source + ``` + +## Installation on Windows + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#encouraged-installation-method-noirup). + +## Uninstalling Nargo + +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/tooling/_category_.json b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/_category_.json new file mode 100644 index 00000000000..55804c03a71 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 2, + "label": "Tooling", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx new file mode 100644 index 00000000000..ac480f3c9f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/index.mdx @@ -0,0 +1,38 @@ +--- +title: Tooling +Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] +--- + +Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. + +## Playground + +The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). + +## IDE tools + +When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. + +The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +## Codespaces + +Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. + + + +## GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. + +## Community projects + +As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/tooling/language_server.md b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/tooling/noir_codegen.md b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/noir_codegen.md new file mode 100644 index 00000000000..d65151da0ab --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/noir_codegen.md @@ -0,0 +1,113 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 2 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +yarn add @noir-lang/noir_codegen -D +``` + +### Nargo library +Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = {"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.27.0/getting_started/tooling/testing.md b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/getting_started/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/docs/versioned_docs/version-v0.27.0/how_to/_category_.json b/docs/versioned_docs/version-v0.27.0/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.27.0/how_to/how-to-oracles.md new file mode 100644 index 00000000000..8cf8035a5c4 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/how_to/how-to-oracles.md @@ -0,0 +1,276 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("getSqrt", async (params) => { + const values = params[0].Array.map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; + }); + return { values: [{ Array: values }] }; +}); +``` + +:::tip + +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a field element *as a string*. For example: + +```json +{ "values": [{ "Array": ["1", "2"] }]} +{ "values": [{ "Single": "1" }]} +{ "values": [{ "Single": "1" }, { "Array": ["1", "2"] }]} +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface SingleForeignCallParam { + Single: string, +} + +interface ArrayForeignCallParam { + Array: string[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.generateProof(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => i.toString("hex")) }, + ]); + return [oracleReturn.values[0].Array]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/docs/versioned_docs/version-v0.27.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.27.0/how_to/how-to-recursion.md new file mode 100644 index 00000000000..4c45bb87ae2 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/how_to/how-to-recursion.md @@ -0,0 +1,179 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit, backend) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const recursiveProof = await noir_programs.recursive.generateProof(recursiveInputs) +``` + +::: diff --git a/docs/versioned_docs/version-v0.27.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.27.0/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +
Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.27.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.27.0/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..003c7019a93 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message.as_slice()); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message.as_slice()); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/docs/versioned_docs/version-v0.27.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.27.0/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.27.0/index.mdx b/docs/versioned_docs/version-v0.27.0/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.27.0/migration_notes.md b/docs/versioned_docs/version-v0.27.0/migration_notes.md new file mode 100644 index 00000000000..6bd740024e5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.27.0/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/assert.md new file mode 100644 index 00000000000..bcff613a695 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/assert.md @@ -0,0 +1,45 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/control_flow.md new file mode 100644 index 00000000000..045d3c3a5f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/control_flow.md @@ -0,0 +1,77 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_bus.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..efce3e95d32 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/arrays.md @@ -0,0 +1,251 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = &[]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..69826fcd724 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/booleans.md @@ -0,0 +1,31 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..a10a4810788 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/fields.md @@ -0,0 +1,192 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..357813c147a --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/index.md @@ -0,0 +1,110 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](../generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..1c6b375db49 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/integers.md @@ -0,0 +1,155 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. The Noir frontend supports both unsigned and signed integer types. The allowed sizes are 1, 8, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let x = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..828faf4a8f8 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/slices.mdx @@ -0,0 +1,170 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = &[]; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = &[1, 2].append(&[3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..311dfd64416 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/strings.md @@ -0,0 +1,80 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/distinct.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/distinct.md new file mode 100644 index 00000000000..6c993b8b5e0 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/distinct.md @@ -0,0 +1,64 @@ +--- +title: Distinct Witnesses +sidebar_position: 11 +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/functions.md new file mode 100644 index 00000000000..2c9bc33fdfc --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main(&[1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md new file mode 100644 index 00000000000..ddd42bf1f9b --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/globals.md new file mode 100644 index 00000000000..063a3d89248 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/globals.md @@ -0,0 +1,72 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type can be inferred by the compiler entirely: + +```rust +global N = 5; // Same as `global N: Field = 5` + +global TUPLE = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: Field = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/mutability.md new file mode 100644 index 00000000000..fdeef6a87c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/ops.md new file mode 100644 index 00000000000..60425cb8994 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/oracles.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/oracles.md new file mode 100644 index 00000000000..2e6a6818d48 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/traits.md new file mode 100644 index 00000000000..ef1445a5907 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: dep::some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: dep::some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..b8e71fe65f0 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/concepts/unconstrained.md @@ -0,0 +1,99 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) diff --git a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..95ee9f52ab2 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..04c1703d929 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use dep::ecrecover; +use dep::lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..67a1dafa372 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,40 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.27.0/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/bigint.md new file mode 100644 index 00000000000..da6a7cdfd81 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/bigint.md @@ -0,0 +1,119 @@ +--- +title: Big Integers +description: How to use big integers from Noir standard library +keywords: + [ + Big Integer, + Noir programming language, + Noir libraries, + ] +--- + +The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. + +:::note + +The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. + +::: + +Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: + +- BN254 Fq: Bn254Fq +- BN254 Fr: Bn254Fr +- Secp256k1 Fq: Secpk1Fq +- Secp256k1 Fr: Secpk1Fr +- Secp256r1 Fr: Secpr1Fr +- Secp256r1 Fq: Secpr1Fq + +Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. +For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. + +Feel free to explore the source code for the other primes: + +```rust title="big_int_definition" showLineNumbers +struct BigInt { + pointer: u32, + modulus: u32, +} +``` +> Source code: noir_stdlib/src/bigint.nr#L16-L21 + + +## Example usage + +A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: + +```rust title="big_int_example" showLineNumbers +fn big_int_example(x: u8, y: u8) { + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); + let c = (a + b) * b / a; + let d = c.to_le_bytes(); + println(d[0]); +} +``` +> Source code: test_programs/execution_success/bigint/src/main.nr#L20-L28 + + +## Methods + +The available operations for each big integer are: + +### from_le_bytes + +Construct a big integer from its little-endian bytes representation. Example: + +```rust + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + ``` + +Sure, here's the formatted version of the remaining methods: + +### to_le_bytes + +Return the little-endian bytes representation of a big integer. Example: + +```rust +let bytes = a.to_le_bytes(); +``` + +### add + +Add two big integers. Example: + +```rust +let sum = a + b; +``` + +### sub + +Subtract two big integers. Example: + +```rust +let difference = a - b; +``` + +### mul + +Multiply two big integers. Example: + +```rust +let product = a * b; +``` + +### div + +Divide two big integers. Note that division is field division and not euclidean division. Example: + +```rust +let quotient = a / b; +``` + +### eq + +Compare two big integers. Example: + +```rust +let are_equal = a == b; +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..be8c65679c3 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/bn254.md new file mode 100644 index 00000000000..3294f005dbb --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/boundedvec.md new file mode 100644 index 00000000000..ce4529f6e57 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,326 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn foo() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of foo's return value + v2 +} + +fn bad() { + let mut v3 = BoundedVec::new(); + + // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L68-L76 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L81-L93 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L98-L113 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L118-L124 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L129-L136 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L141-L148 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L153-L162 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L229-L235 + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/hashmap.md new file mode 100644 index 00000000000..91604af765d --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/hashmap.md @@ -0,0 +1,569 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already +known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which +will likely change the result of the program. This behavior is set to become an error in future +versions instead. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a pedersen hash +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L196-L198 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L82-L86 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L211 + + +### get + +```rust title="get" showLineNumbers +pub fn get( + self, + key: K + ) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L278-L287 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L299-L307 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert( + &mut self, + key: K, + value: V + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L313-L323 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L213-L217 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove( + &mut self, + key: K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L356-L365 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L221-L228 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L115-L117 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L230-L238 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u64 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L264-L266 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L240-L255 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u64 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L271-L273 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L261 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L93-L95 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key( + self, + key: K + ) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L101-L110 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L310-L321 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L144-L146 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L323-L333 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L164-L166 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L335-L344 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut( + &mut self, + f: fn(K, V) -> (K, V) + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L183-L192 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut( + &mut self, + f: fn(K) -> K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L208-L217 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L353-L356 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L233-L235 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L358-L361 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L247-L249 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L281-L283 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher +{ + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L426-L435 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L285-L296 + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/index.md new file mode 100644 index 00000000000..ea84c6d5c21 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/vec.mdx new file mode 100644 index 00000000000..fcfd7e07aa0 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/containers/vec.mdx @@ -0,0 +1,151 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..d2b42d67b7c --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..4394b48f907 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,98 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +```rust title="ecdsa_secp256k1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 + + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +```rust title="ecdsa_secp256r1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 + + + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..c2c0624dfad --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,37 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + +It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify_with_hasher` function with a parameter implementing the Hasher trait. For instance, if you want to use Poseidon2 instead, you can do the following: +```rust +use dep::std::hash::poseidon2::Poseidon2Hasher; + +let mut hasher = Poseidon2Hasher::default(); +eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher); +``` + + + +## eddsa::eddsa_to_pub + +Private to public key conversion. + +Returns `(pub_key_x, pub_key_y)` + +```rust +fn eddsa_to_pub(secret : Field) -> (Field, Field) +``` + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..695c7d9406f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,331 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. +See sha256_slice for a version that works directly on slices. + +```rust title="sha256" showLineNumbers +pub fn sha256(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L10-L12 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## sha256_slice + +A version of sha256 specialized to slices: + +```rust title="sha256_slice" showLineNumbers +pub fn sha256_slice(input: [u8]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L16-L18 + + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash +See blake2s_slice for a version that works directly on slices. + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L22-L24 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake2s_slice + +A version of blake2s specialized to slices: + +```rust title="blake2s_slice" showLineNumbers +pub fn blake2s_slice(input: [u8]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L28-L30 + + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash +See blake3_slice for a version that works directly on slices. + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L34-L36 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## blake3_slice + +A version of blake3 specialized to slices: + +```rust title="blake3_slice" showLineNumbers +pub fn blake3_slice(input: [u8]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L40-L42 + + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. +See pedersen_hash_slice for a version that works directly on slices. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L78-L80 + + +example: + +```rust title="pedersen-hash" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 + + + + +## pedersen_hash_slice + +Given a slice of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash_slice" showLineNumbers +pub fn pedersen_hash_slice(input: [Field]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L85-L87 + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. +See pedersen_commitment_slice for a version that works directly on slices. + +```rust title="pedersen_commitment" showLineNumbers +struct PedersenPoint { + x : Field, + y : Field, +} + +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { +``` +> Source code: noir_stdlib/src/hash.nr#L45-L52 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 + + + + +## pedersen_commitment_slice + +Given a slice of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment_slice" showLineNumbers +pub fn pedersen_commitment_slice(input: [Field]) -> PedersenPoint { + pedersen_commitment_with_separator_slice(input, 0) +} +``` +> Source code: noir_stdlib/src/hash.nr#L56-L60 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. See keccak256_slice for a version that works +directly on slices. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L113-L115 + + +example: + +```rust title="keccak256" showLineNumbers +use dep::std; + +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 + + + + +## keccak256_slice + +Given a slice of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). + +```rust title="keccak256_slice" showLineNumbers +pub fn keccak256_slice(input: [u8], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L119-L121 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use dep::std::hash::poseidon; +use dep::std::hash::poseidon2; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field; 4], y3: Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); + + let hash3 = poseidon2::Poseidon2::hash(x3, x3.len()); + assert(hash3 == y3); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L15 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +The above example for Poseidon also includes Poseidon2. + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/scalar.mdx b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/scalar.mdx new file mode 100644 index 00000000000..df411ca5443 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -0,0 +1,33 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust title="fixed_base_embedded_curve" showLineNumbers +pub fn fixed_base_embedded_curve( + low: Field, + high: Field +) -> [Field; 2] +``` +> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 + + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + println(scal); +} +``` + + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..b59e69c8f07 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,64 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). +See schnorr::verify_signature_slice for a version that works directly on slices. + +```rust title="schnorr_verify" showLineNumbers +pub fn verify_signature( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L2-L9 + + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + + +## schnorr::verify_signature_slice + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) +where the message is a slice. + +```rust title="schnorr_verify_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L13-L20 + + + diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..6a9ebf72ada --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/options.md new file mode 100644 index 00000000000..a1bd4e1de5f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md new file mode 100644 index 00000000000..a93894043dc --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/recursion.md @@ -0,0 +1,88 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## The `#[recursive]` Attribute + +In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. + +### Example usage with `#[recursive]` + +```rust +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); +} + +// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit +// are intended for recursive verification. +``` + +By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. + +## Verifying Recursive Proofs + +```rust +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Example usage + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/traits.md new file mode 100644 index 00000000000..68a9dc3d54b --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/traits.md @@ -0,0 +1,408 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L1-L5 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for [T] { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. + + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +``` +> Source code: noir_stdlib/src/convert.nr#L25-L52 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +trait Into { + fn into(input: Self) -> T; +} + +impl Into for U where T: From { + fn into(input: U) -> T { + T::from(input) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L23 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L1-L5 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for [T] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L102-L106 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for [T] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L17-L21 + +```rust title="mul-trait" showLineNumbers +trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L33-L37 + +```rust title="div-trait" showLineNumbers +trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L49-L53 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +trait Rem{ + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L65-L69 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L79-L83 + +```rust title="bitand-trait" showLineNumbers +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L95-L99 + +```rust title="bitxor-trait" showLineNumbers +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L111-L115 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +trait Shl { + fn shl(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L127-L131 + +```rust title="shr-trait" showLineNumbers +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L142-L146 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/docs/versioned_docs/version-v0.27.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.27.0/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..f450fecdd36 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/noir/standard_library/zeroed.md @@ -0,0 +1,26 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..b18c1926b93 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,119 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../index.md#backend) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | `CompiledCircuit` | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +*** + +### generateProof() + +```ts +generateProof(compressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `compressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<`ProofData`\> + +#### Description + +Generates a proof + +*** + +### generateRecursiveProofArtifacts() + +```ts +generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +Generates artifacts that will be passed to a circuit that will verify this proof. + +Instead of passing the proof and verification key as a byte array, we pass them +as fields which makes it cheaper to verify in a circuit. + +The proof that is passed here will have been created using a circuit +that has the #[recursive] attribute on its `main` method. + +The number of public inputs denotes how many public inputs are in the inner proof. + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | `ProofData` | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Example + +```typescript +const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | `ProofData` | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/index.md new file mode 100644 index 00000000000..c146316a915 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/index.md @@ -0,0 +1,58 @@ +# backend_barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | + +## References + +### CompiledCircuit + +Renames and re-exports [Backend](index.md#backend) + +*** + +### ProofData + +Renames and re-exports [Backend](index.md#backend) + +## Variables + +### Backend + +```ts +Backend: any; +``` + +## Functions + +### publicInputsToWitnessMap() + +```ts +publicInputsToWitnessMap(publicInputs, abi): Backend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `publicInputs` | `string`[] | +| `abi` | `Abi` | + +#### Returns + +[`Backend`](index.md#backend) + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..b49a479f4f4 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,21 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `memory` | `object` | - | +| `memory.maximum` | `number` | - | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..339353b9862 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 00000000000..45dd62ee57e --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,132 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | `CompiledCircuit` | +| `backend`? | `any` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateProof() + +```ts +generateProof(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`ProofData`\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateProof(input) +``` + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | `ProofData` | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..5e3cd53e9d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/index.md new file mode 100644 index 00000000000..cca6b3ace41 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/index.md @@ -0,0 +1,54 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +## References + +### CompiledCircuit + +Renames and re-exports [InputMap](index.md#inputmap) + +*** + +### ProofData + +Renames and re-exports [InputMap](index.md#inputmap) + +## Variables + +### InputMap + +```ts +InputMap: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..c6d8125eaad --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 00000000000..6faf763b37f --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 00000000000..7d0b39a43ef --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 00000000000..7e65c1d69c7 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 00000000000..fcea9275341 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 00000000000..b6e0f9d1bc0 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 00000000000..e0870710349 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.27.0/reference/_category_.json b/docs/versioned_docs/version-v0.27.0/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.27.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.27.0/reference/nargo_commands.md new file mode 100644 index 00000000000..218fcfb0c8c --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/reference/nargo_commands.md @@ -0,0 +1,381 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo backend`↴](#nargo-backend) +* [`nargo backend current`↴](#nargo-backend-current) +* [`nargo backend ls`↴](#nargo-backend-ls) +* [`nargo backend use`↴](#nargo-backend-use) +* [`nargo backend install`↴](#nargo-backend-install) +* [`nargo backend uninstall`↴](#nargo-backend-uninstall) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo codegen-verifier`↴](#nargo-codegen-verifier) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo prove`↴](#nargo-prove) +* [`nargo verify`↴](#nargo-verify) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `backend` — Install and select custom backends used to generate and verify proofs +* `check` — Checks the constraint system for errors +* `fmt` — Format the Noir files in a workspace +* `codegen-verifier` — Generates a Solidity verifier smart contract for the program +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `prove` — Create proof for this program. The proof is returned as a hex encoded string +* `verify` — Given a proof and a program, verify whether the proof is valid +* `test` — Run the tests for this program +* `info` — Provides detailed information on each of a program's function (represented by a single circuit) +* `lsp` — Starts the Noir LSP server + +###### **Options:** + + + + +## `nargo backend` + +Install and select custom backends used to generate and verify proofs + +**Usage:** `nargo backend ` + +###### **Subcommands:** + +* `current` — Prints the name of the currently active backend +* `ls` — Prints the list of currently installed backends +* `use` — Select the backend to use +* `install` — Install a new backend from a URL +* `uninstall` — Uninstalls a backend + + + +## `nargo backend current` + +Prints the name of the currently active backend + +**Usage:** `nargo backend current` + + + +## `nargo backend ls` + +Prints the list of currently installed backends + +**Usage:** `nargo backend ls` + + + +## `nargo backend use` + +Select the backend to use + +**Usage:** `nargo backend use ` + +###### **Arguments:** + +* `` + + + +## `nargo backend install` + +Install a new backend from a URL + +**Usage:** `nargo backend install ` + +###### **Arguments:** + +* `` — The name of the backend to install +* `` — The URL from which to download the backend + + + +## `nargo backend uninstall` + +Uninstalls a backend + +**Usage:** `nargo backend uninstall ` + +###### **Arguments:** + +* `` — The name of the backend to uninstall + + + +## `nargo check` + +Checks the constraint system for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to check +* `--workspace` — Check all packages in the workspace +* `--overwrite` — Force overwrite of existing files +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + + +## `nargo codegen-verifier` + +Generates a Solidity verifier smart contract for the program + +**Usage:** `nargo codegen-verifier [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to codegen +* `--workspace` — Codegen all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to compile +* `--workspace` — Compile all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--workspace` — Execute all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo prove` + +Create proof for this program. The proof is returned as a hex encoded string + +**Usage:** `nargo prove [OPTIONS]` + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `-v`, `--verifier-name ` — The name of the toml file which contains the inputs for the verifier + + Default value: `Verifier` +* `--verify` — Verify proof after proving +* `--package ` — The name of the package to prove +* `--workspace` — Prove all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid + +**Usage:** `nargo verify [OPTIONS]` + +###### **Options:** + +* `-v`, `--verifier-name ` — The name of the toml file which contains the inputs for the verifier + + Default value: `Verifier` +* `--package ` — The name of the package verify +* `--workspace` — Verify all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAME]` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements +* `--exact` — Only run tests that match exactly +* `--package ` — The name of the package to test +* `--workspace` — Test all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo info` + +Provides detailed information on each of a program's function (represented by a single circuit) + +Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to detail +* `--workspace` — Detail all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/docs/versioned_docs/version-v0.27.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.27.0/tutorials/noirjs_app.md new file mode 100644 index 00000000000..12beb476994 --- /dev/null +++ b/docs/versioned_docs/version-v0.27.0/tutorials/noirjs_app.md @@ -0,0 +1,279 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. + +In this guide, we will be pinned to 0.19.4. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +```nargo new circuit``` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +```nargo compile``` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: + +```bash +npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It *could* be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import("@noir-lang/noirc_abi").then(module => + module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + ), + import("@noir-lang/acvm_js").then(module => + module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) + ) + ]); +} + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch(err) { + display("logs", "Oh 💔 Wrong guess") + } +}); + +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/docs/versioned_sidebars/version-v0.27.0-sidebars.json b/docs/versioned_sidebars/version-v0.27.0-sidebars.json new file mode 100644 index 00000000000..b16f79cc176 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.27.0-sidebars.json @@ -0,0 +1,83 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} diff --git a/tooling/noir_codegen/package.json b/tooling/noir_codegen/package.json index 1eabc6a1398..569841b2c6a 100644 --- a/tooling/noir_codegen/package.json +++ b/tooling/noir_codegen/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.26.0", + "version": "0.27.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js/package.json b/tooling/noir_js/package.json index c8d4873e095..838f317c622 100644 --- a/tooling/noir_js/package.json +++ b/tooling/noir_js/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.26.0", + "version": "0.27.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 251dd80c2f4..11ef9248853 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.26.0", + "version": "0.27.0", "packageManager": "yarn@3.5.1", "license": "(MIT OR Apache-2.0)", "type": "module", diff --git a/tooling/noir_js_types/package.json b/tooling/noir_js_types/package.json index eadb6f49665..316612a7c51 100644 --- a/tooling/noir_js_types/package.json +++ b/tooling/noir_js_types/package.json @@ -4,7 +4,7 @@ "The Noir Team " ], "packageManager": "yarn@3.5.1", - "version": "0.26.0", + "version": "0.27.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { diff --git a/tooling/noirc_abi_wasm/package.json b/tooling/noirc_abi_wasm/package.json index 14e528c3b15..0e4aaceeae3 100644 --- a/tooling/noirc_abi_wasm/package.json +++ b/tooling/noirc_abi_wasm/package.json @@ -3,7 +3,7 @@ "contributors": [ "The Noir Team " ], - "version": "0.26.0", + "version": "0.27.0", "license": "(MIT OR Apache-2.0)", "homepage": "https://noir-lang.org/", "repository": { From f831b0bdbf99cab1bcd24d494c4546a36309465e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Rodr=C3=ADguez?= Date: Thu, 11 Apr 2024 17:08:49 +0200 Subject: [PATCH 165/416] feat: Unroll loops iteratively (#4779) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4736 ## Summary\* Instead of trying to unroll loops once, adds a meta pass that tries to unroll as many times as necessary, simplifying and doing mem2reg between unrolls. - For the cases where the program unrolls succesfully at the first try, no compile time overhead is created - For the cases where the program does contain an unknown at compile time loop bound, it'll try one more time before failing, since the stop condition is that "this unroll retry generated the same amount of errors as the previous one" - For the cases where the program doesn't contain an unknown at compile time loop bound, instead of failing it'll do as many unrolls as necessary to unroll the loop. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: jfecher --- compiler/noirc_evaluator/src/ssa.rs | 2 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 65 +++++++++++++------ .../execution_success/slice_loop/Nargo.toml | 6 ++ .../execution_success/slice_loop/Prover.toml | 11 ++++ .../execution_success/slice_loop/src/main.nr | 32 +++++++++ 5 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 test_programs/execution_success/slice_loop/Nargo.toml create mode 100644 test_programs/execution_success/slice_loop/Prover.toml create mode 100644 test_programs/execution_success/slice_loop/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa.rs b/compiler/noirc_evaluator/src/ssa.rs index fac7a7c0829..ce4a9bbe9f1 100644 --- a/compiler/noirc_evaluator/src/ssa.rs +++ b/compiler/noirc_evaluator/src/ssa.rs @@ -55,7 +55,7 @@ pub(crate) fn optimize_into_acir( .run_pass(Ssa::mem2reg, "After Mem2Reg:") .run_pass(Ssa::as_slice_optimization, "After `as_slice` optimization") .try_run_pass(Ssa::evaluate_assert_constant, "After Assert Constant:")? - .try_run_pass(Ssa::unroll_loops, "After Unrolling:")? + .try_run_pass(Ssa::unroll_loops_iteratively, "After Unrolling:")? .run_pass(Ssa::simplify_cfg, "After Simplifying:") .run_pass(Ssa::flatten_cfg, "After Flattening:") .run_pass(Ssa::remove_bit_shifts, "After Removing Bit Shifts:") diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index 8110e3469f1..c6bf7923fa8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -34,10 +34,41 @@ use crate::{ use fxhash::FxHashMap as HashMap; impl Ssa { - /// Unroll all loops in each SSA function. + /// Loop unrolling can return errors, since ACIR functions need to be fully unrolled. + /// This meta-pass will keep trying to unroll loops and simplifying the SSA until no more errors are found. + pub(crate) fn unroll_loops_iteratively(mut ssa: Ssa) -> Result { + // Try to unroll loops first: + let mut unroll_errors; + (ssa, unroll_errors) = ssa.try_to_unroll_loops(); + + // Keep unrolling until no more errors are found + while !unroll_errors.is_empty() { + let prev_unroll_err_count = unroll_errors.len(); + + // Simplify the SSA before retrying + + // Do a mem2reg after the last unroll to aid simplify_cfg + ssa = ssa.mem2reg(); + ssa = ssa.simplify_cfg(); + // Do another mem2reg after simplify_cfg to aid the next unroll + ssa = ssa.mem2reg(); + + // Unroll again + (ssa, unroll_errors) = ssa.try_to_unroll_loops(); + // If we didn't manage to unroll any more loops, exit + if unroll_errors.len() >= prev_unroll_err_count { + return Err(unroll_errors.swap_remove(0)); + } + } + Ok(ssa) + } + + /// Tries to unroll all loops in each SSA function. /// If any loop cannot be unrolled, it is left as-is or in a partially unrolled state. + /// Returns the ssa along with all unrolling errors encountered #[tracing::instrument(level = "trace", skip(self))] - pub(crate) fn unroll_loops(mut self) -> Result { + pub(crate) fn try_to_unroll_loops(mut self) -> (Ssa, Vec) { + let mut errors = vec![]; for function in self.functions.values_mut() { // Loop unrolling in brillig can lead to a code explosion currently. This can // also be true for ACIR, but we have no alternative to unrolling in ACIR. @@ -46,12 +77,9 @@ impl Ssa { continue; } - // This check is always true with the addition of the above guard, but I'm - // keeping it in case the guard on brillig functions is ever removed. - let abort_on_error = matches!(function.runtime(), RuntimeType::Acir(_)); - find_all_loops(function).unroll_each_loop(function, abort_on_error)?; + errors.extend(find_all_loops(function).unroll_each_loop(function)); } - Ok(self) + (self, errors) } } @@ -115,34 +143,29 @@ fn find_all_loops(function: &Function) -> Loops { impl Loops { /// Unroll all loops within a given function. /// Any loops which fail to be unrolled (due to using non-constant indices) will be unmodified. - fn unroll_each_loop( - mut self, - function: &mut Function, - abort_on_error: bool, - ) -> Result<(), RuntimeError> { + fn unroll_each_loop(mut self, function: &mut Function) -> Vec { + let mut unroll_errors = vec![]; while let Some(next_loop) = self.yet_to_unroll.pop() { // If we've previously modified a block in this loop we need to refresh the context. // This happens any time we have nested loops. if next_loop.blocks.iter().any(|block| self.modified_blocks.contains(block)) { let mut new_context = find_all_loops(function); new_context.failed_to_unroll = self.failed_to_unroll; - return new_context.unroll_each_loop(function, abort_on_error); + return new_context.unroll_each_loop(function); } // Don't try to unroll the loop again if it is known to fail if !self.failed_to_unroll.contains(&next_loop.header) { match unroll_loop(function, &self.cfg, &next_loop) { Ok(_) => self.modified_blocks.extend(next_loop.blocks), - Err(call_stack) if abort_on_error => { - return Err(RuntimeError::UnknownLoopBound { call_stack }); - } - Err(_) => { + Err(call_stack) => { self.failed_to_unroll.insert(next_loop.header); + unroll_errors.push(RuntimeError::UnknownLoopBound { call_stack }); } } } } - Ok(()) + unroll_errors } } @@ -585,7 +608,8 @@ mod tests { // } // The final block count is not 1 because unrolling creates some unnecessary jmps. // If a simplify cfg pass is ran afterward, the expected block count will be 1. - let ssa = ssa.unroll_loops().expect("All loops should be unrolled"); + let (ssa, errors) = ssa.try_to_unroll_loops(); + assert_eq!(errors.len(), 0, "All loops should be unrolled"); assert_eq!(ssa.main().reachable_blocks().len(), 5); } @@ -634,6 +658,7 @@ mod tests { assert_eq!(ssa.main().reachable_blocks().len(), 4); // Expected that we failed to unroll the loop - assert!(ssa.unroll_loops().is_err()); + let (_, errors) = ssa.try_to_unroll_loops(); + assert_eq!(errors.len(), 1, "Expected to fail to unroll loop"); } } diff --git a/test_programs/execution_success/slice_loop/Nargo.toml b/test_programs/execution_success/slice_loop/Nargo.toml new file mode 100644 index 00000000000..09ad90c4187 --- /dev/null +++ b/test_programs/execution_success/slice_loop/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "slice_loop" +type = "bin" +authors = [""] + +[dependencies] diff --git a/test_programs/execution_success/slice_loop/Prover.toml b/test_programs/execution_success/slice_loop/Prover.toml new file mode 100644 index 00000000000..089a1764b54 --- /dev/null +++ b/test_programs/execution_success/slice_loop/Prover.toml @@ -0,0 +1,11 @@ +[[points]] +x = "1" +y = "2" + +[[points]] +x = "3" +y = "4" + +[[points]] +x = "5" +y = "6" diff --git a/test_programs/execution_success/slice_loop/src/main.nr b/test_programs/execution_success/slice_loop/src/main.nr new file mode 100644 index 00000000000..4ff3e865b1f --- /dev/null +++ b/test_programs/execution_success/slice_loop/src/main.nr @@ -0,0 +1,32 @@ +struct Point { + x: Field, + y: Field, +} + +impl Point { + fn serialize(self) -> [Field; 2] { + [self.x, self.y] + } +} + +fn sum(values: [Field]) -> Field { + let mut sum = 0; + for value in values { + sum = sum + value; + } + sum +} + +fn main(points: [Point; 3]) { + let mut serialized_points = &[]; + for point in points { + serialized_points = serialized_points.append(point.serialize().as_slice()); + } + // Do a compile-time check that needs the previous loop to be unrolled + if serialized_points.len() > 5 { + let empty_point = Point { x: 0, y: 0 }; + serialized_points = serialized_points.append(empty_point.serialize().as_slice()); + } + // Do a sum that needs both the previous loop and the previous if to have been simplified + assert_eq(sum(serialized_points), 21); +} From 2bd006ae07499e8702b0fa9565855f0a5ef1a589 Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:40:07 -0400 Subject: [PATCH 166/416] feat: Sync from aztec-packages (#4764) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5619) feat: add return values to aztec fns (https://github.com/AztecProtocol/aztec-packages/pull/5389) feat!: storage_layout and `#[aztec(storage)]` (https://github.com/AztecProtocol/aztec-packages/pull/5387) feat(acir)!: Add predicate to call opcode (https://github.com/AztecProtocol/aztec-packages/pull/5616) feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5572) feat!: contract_abi-exports (https://github.com/AztecProtocol/aztec-packages/pull/5386) feat(avm): integrate AVM with initializers (https://github.com/AztecProtocol/aztec-packages/pull/5469) feat: Restore hashing args via slice for performance (https://github.com/AztecProtocol/aztec-packages/pull/5539) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> Co-authored-by: vezenovm Co-authored-by: TomAFrench --- .aztec-sync-commit | 2 +- Cargo.lock | 30 +-- acvm-repo/acir/codegen/acir.cpp | 4 + acvm-repo/acir/src/circuit/opcodes.rs | 7 +- .../acir/tests/test_program_serialization.rs | 40 ++- acvm-repo/acvm/src/pwg/memory_op.rs | 15 +- acvm-repo/acvm/src/pwg/mod.rs | 28 +- acvm-repo/acvm_js/build.sh | 7 + .../acvm_js/test/shared/nested_acir_call.ts | 14 +- aztec_macros/src/lib.rs | 69 ++--- .../compute_note_hash_and_nullifier.rs | 155 ++++------- aztec_macros/src/transforms/events.rs | 10 +- aztec_macros/src/transforms/functions.rs | 246 +++++++++++------ aztec_macros/src/transforms/note_interface.rs | 135 +++++++++- aztec_macros/src/transforms/storage.rs | 254 +++++++++++++++--- aztec_macros/src/utils/ast_utils.rs | 2 + aztec_macros/src/utils/errors.rs | 18 ++ aztec_macros/src/utils/hir_utils.rs | 206 ++++++++++---- compiler/noirc_driver/src/abi_gen.rs | 59 +++- compiler/noirc_driver/src/contract.rs | 15 +- compiler/noirc_driver/src/lib.rs | 56 +++- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 4 +- .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 3 +- compiler/noirc_frontend/src/ast/statement.rs | 13 +- compiler/noirc_frontend/src/debug/mod.rs | 3 + .../src/hir/def_collector/dc_crate.rs | 14 - .../src/hir/def_collector/dc_mod.rs | 9 +- .../noirc_frontend/src/hir/def_map/mod.rs | 52 ++-- compiler/noirc_frontend/src/hir/mod.rs | 2 +- .../src/hir/resolution/errors.rs | 9 + .../src/hir/resolution/resolver.rs | 23 +- .../noirc_frontend/src/hir/type_check/mod.rs | 1 + compiler/noirc_frontend/src/hir_def/stmt.rs | 2 + compiler/noirc_frontend/src/lexer/token.rs | 7 +- compiler/noirc_frontend/src/lib.rs | 10 - compiler/noirc_frontend/src/node_interner.rs | 12 +- compiler/noirc_frontend/src/parser/parser.rs | 13 +- .../src/parser/parser/attributes.rs | 23 ++ .../src/parser/parser/structs.rs | 32 +-- compiler/wasm/src/compile.rs | 9 +- compiler/wasm/src/compile_new.rs | 2 +- compiler/wasm/src/types/noir_artifact.ts | 61 +++-- package.json | 2 +- .../fold_call_witness_condition/Prover.toml | 6 +- tooling/bb_abstraction_leaks/build.rs | 2 +- tooling/nargo/src/artifacts/contract.rs | 24 +- .../noir_js_backend_barretenberg/package.json | 2 +- tooling/noirc_abi/src/lib.rs | 58 ++-- tooling/noirc_abi/src/serialization.rs | 34 ++- tooling/noirc_abi_wasm/build.sh | 7 + yarn.lock | 10 +- 51 files changed, 1272 insertions(+), 549 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index 540b447693c..ec4c854ef2b 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -bb719200034e3bc6db09fb56538dadca4203abf4 +ff28080bcfb946177010960722925973ee19646b diff --git a/Cargo.lock b/Cargo.lock index a31c415c77e..e62f966b8fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,9 +541,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitmaps" @@ -1602,12 +1602,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -2564,16 +2564,16 @@ version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "libc", "redox_syscall 0.4.1", ] [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" @@ -3128,7 +3128,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -3582,7 +3582,7 @@ checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.4.2", + "bitflags 2.5.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -4069,15 +4069,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.48.0", ] [[package]] @@ -5534,7 +5534,7 @@ version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.5.0", "indexmap 2.0.0", "semver", ] diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index d7ef849ab75..e4203b579b0 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -1074,6 +1074,7 @@ namespace Program { uint32_t id; std::vector inputs; std::vector outputs; + std::optional predicate; friend bool operator==(const Call&, const Call&); std::vector bincodeSerialize() const; @@ -6173,6 +6174,7 @@ namespace Program { if (!(lhs.id == rhs.id)) { return false; } if (!(lhs.inputs == rhs.inputs)) { return false; } if (!(lhs.outputs == rhs.outputs)) { return false; } + if (!(lhs.predicate == rhs.predicate)) { return false; } return true; } @@ -6199,6 +6201,7 @@ void serde::Serializable::serialize(const Program::Opcode serde::Serializable::serialize(obj.id, serializer); serde::Serializable::serialize(obj.inputs, serializer); serde::Serializable::serialize(obj.outputs, serializer); + serde::Serializable::serialize(obj.predicate, serializer); } template <> @@ -6208,6 +6211,7 @@ Program::Opcode::Call serde::Deserializable::deserialize( obj.id = serde::Deserializable::deserialize(deserializer); obj.inputs = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); + obj.predicate = serde::Deserializable::deserialize(deserializer); return obj; } diff --git a/acvm-repo/acir/src/circuit/opcodes.rs b/acvm-repo/acir/src/circuit/opcodes.rs index 68d28b287e6..d8204132b3e 100644 --- a/acvm-repo/acir/src/circuit/opcodes.rs +++ b/acvm-repo/acir/src/circuit/opcodes.rs @@ -39,6 +39,8 @@ pub enum Opcode { inputs: Vec, /// Outputs of the function call outputs: Vec, + /// Predicate of the circuit execution - indicates if it should be skipped + predicate: Option, }, } @@ -97,8 +99,11 @@ impl std::fmt::Display for Opcode { write!(f, "INIT ")?; write!(f, "(id: {}, len: {}) ", block_id.0, init.len()) } - Opcode::Call { id, inputs, outputs } => { + Opcode::Call { id, inputs, outputs, predicate } => { write!(f, "CALL func {}: ", id)?; + if let Some(pred) = predicate { + writeln!(f, "PREDICATE = {pred}")?; + } write!(f, "inputs: {:?}, ", inputs)?; write!(f, "outputs: {:?}", outputs) } diff --git a/acvm-repo/acir/tests/test_program_serialization.rs b/acvm-repo/acir/tests/test_program_serialization.rs index a5b683c15e1..8b9160ccf6a 100644 --- a/acvm-repo/acir/tests/test_program_serialization.rs +++ b/acvm-repo/acir/tests/test_program_serialization.rs @@ -380,10 +380,18 @@ fn nested_acir_call_circuit() { // assert(x == y); // x // } - let nested_call = - Opcode::Call { id: 1, inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(2)] }; - let nested_call_two = - Opcode::Call { id: 1, inputs: vec![Witness(0), Witness(1)], outputs: vec![Witness(3)] }; + let nested_call = Opcode::Call { + id: 1, + inputs: vec![Witness(0), Witness(1)], + outputs: vec![Witness(2)], + predicate: None, + }; + let nested_call_two = Opcode::Call { + id: 1, + inputs: vec![Witness(0), Witness(1)], + outputs: vec![Witness(3)], + predicate: None, + }; let assert_nested_call_results = Opcode::AssertZero(Expression { mul_terms: Vec::new(), @@ -410,8 +418,12 @@ fn nested_acir_call_circuit() { ], q_c: FieldElement::one() + FieldElement::one(), }); - let call = - Opcode::Call { id: 2, inputs: vec![Witness(2), Witness(1)], outputs: vec![Witness(3)] }; + let call = Opcode::Call { + id: 2, + inputs: vec![Witness(2), Witness(1)], + outputs: vec![Witness(3)], + predicate: None, + }; let nested_call = Circuit { current_witness_index: 3, @@ -444,14 +456,14 @@ fn nested_acir_call_circuit() { let expected_serialization: Vec = vec![ 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, - 24, 109, 227, 191, 93, 101, 50, 123, 255, 35, 172, 99, 25, 83, 17, 250, 99, 14, 250, 224, - 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, 57, 108, 14, 91, 248, 202, 168, 65, - 255, 207, 122, 28, 180, 250, 244, 221, 244, 197, 223, 68, 182, 154, 197, 184, 134, 80, 54, - 95, 136, 233, 142, 62, 101, 137, 24, 98, 94, 133, 132, 162, 196, 135, 23, 230, 34, 65, 182, - 148, 211, 134, 137, 2, 23, 218, 99, 226, 93, 135, 185, 121, 123, 33, 84, 12, 234, 218, 192, - 64, 174, 3, 248, 47, 88, 48, 17, 150, 157, 183, 151, 95, 244, 86, 91, 221, 61, 10, 81, 31, - 178, 190, 110, 194, 102, 96, 76, 251, 202, 80, 13, 204, 77, 224, 25, 176, 70, 79, 197, 128, - 18, 64, 3, 4, 0, 0, + 24, 173, 241, 223, 174, 50, 153, 189, 255, 17, 214, 177, 148, 89, 17, 250, 99, 14, 246, + 224, 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, 217, 109, 118, 91, 248, 200, 168, + 225, 248, 191, 106, 114, 208, 233, 104, 188, 233, 139, 223, 137, 108, 51, 139, 113, 13, + 161, 38, 95, 137, 233, 142, 62, 23, 137, 24, 98, 89, 133, 132, 162, 196, 135, 23, 230, 42, + 65, 82, 46, 57, 97, 166, 192, 149, 182, 152, 121, 211, 97, 110, 222, 94, 8, 13, 132, 182, + 54, 48, 144, 235, 8, 254, 10, 22, 76, 132, 101, 231, 237, 229, 23, 189, 213, 54, 119, 15, + 83, 212, 199, 172, 175, 79, 113, 51, 48, 198, 253, 207, 84, 13, 204, 141, 224, 21, 176, + 147, 158, 66, 231, 43, 145, 6, 4, 0, 0, ]; assert_eq!(bytes, expected_serialization); } diff --git a/acvm-repo/acvm/src/pwg/memory_op.rs b/acvm-repo/acvm/src/pwg/memory_op.rs index e51797707a7..672c13e11c2 100644 --- a/acvm-repo/acvm/src/pwg/memory_op.rs +++ b/acvm-repo/acvm/src/pwg/memory_op.rs @@ -6,7 +6,9 @@ use acir::{ FieldElement, }; -use super::{arithmetic::ExpressionSolver, get_value, insert_value, witness_to_value}; +use super::{ + arithmetic::ExpressionSolver, get_value, insert_value, is_predicate_false, witness_to_value, +}; use super::{ErrorLocation, OpcodeResolutionError}; type MemoryIndex = u32; @@ -80,11 +82,8 @@ impl MemoryOpSolver { // `operation == 0` implies a read operation. (`operation == 1` implies write operation). let is_read_operation = operation.is_zero(); - // If the predicate is `None`, then we simply return the value 1 - let pred_value = match predicate { - Some(pred) => get_value(pred, initial_witness), - None => Ok(FieldElement::one()), - }?; + // Fetch whether or not the predicate is false (e.g. equal to zero) + let skip_operation = is_predicate_false(initial_witness, predicate)?; if is_read_operation { // `value_read = arr[memory_index]` @@ -97,7 +96,7 @@ impl MemoryOpSolver { // A zero predicate indicates that we should skip the read operation // and zero out the operation's output. - let value_in_array = if pred_value.is_zero() { + let value_in_array = if skip_operation { FieldElement::zero() } else { self.read_memory_index(memory_index)? @@ -111,7 +110,7 @@ impl MemoryOpSolver { let value_write = value; // A zero predicate indicates that we should skip the write operation. - if pred_value.is_zero() { + if skip_operation { // We only want to write to already initialized memory. // Do nothing if the predicate is zero. Ok(()) diff --git a/acvm-repo/acvm/src/pwg/mod.rs b/acvm-repo/acvm/src/pwg/mod.rs index 3cedcfc0399..bb98eda2689 100644 --- a/acvm-repo/acvm/src/pwg/mod.rs +++ b/acvm-repo/acvm/src/pwg/mod.rs @@ -377,7 +377,7 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { }; let witness = &mut self.witness_map; - if BrilligSolver::::should_skip(witness, brillig)? { + if is_predicate_false(witness, &brillig.predicate)? { return BrilligSolver::::zero_out_brillig_outputs(witness, brillig).map(|_| None); } @@ -448,7 +448,9 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { } pub fn solve_call_opcode(&mut self) -> Result, OpcodeResolutionError> { - let Opcode::Call { id, inputs, outputs } = &self.opcodes[self.instruction_pointer] else { + let Opcode::Call { id, inputs, outputs, predicate } = + &self.opcodes[self.instruction_pointer] + else { unreachable!("Not executing a Call opcode"); }; if *id == 0 { @@ -459,6 +461,14 @@ impl<'a, B: BlackBoxFunctionSolver> ACVM<'a, B> { }); } + if is_predicate_false(&self.witness_map, predicate)? { + // Zero out the outputs if we have a false predicate + for output in outputs { + insert_value(output, FieldElement::zero(), &mut self.witness_map)?; + } + return Ok(None); + } + if self.acir_call_counter >= self.acir_call_results.len() { let mut initial_witness = WitnessMap::default(); for (i, input_witness) in inputs.iter().enumerate() { @@ -556,6 +566,20 @@ fn any_witness_from_expression(expr: &Expression) -> Option { } } +/// Returns `true` if the predicate is zero +/// A predicate is used to indicate whether we should skip a certain operation. +/// If we have a zero predicate it means the operation should be skipped. +pub(crate) fn is_predicate_false( + witness: &WitnessMap, + predicate: &Option, +) -> Result { + match predicate { + Some(pred) => get_value(pred, witness).map(|pred_value| pred_value.is_zero()), + // If the predicate is `None`, then we treat it as an unconditional `true` + None => Ok(false), + } +} + #[derive(Debug, Clone, PartialEq)] pub struct AcirCallWaitInfo { /// Index in the list of ACIR function's that should be called diff --git a/acvm-repo/acvm_js/build.sh b/acvm-repo/acvm_js/build.sh index fe0b4dcbfff..4486a214c9c 100755 --- a/acvm-repo/acvm_js/build.sh +++ b/acvm-repo/acvm_js/build.sh @@ -14,6 +14,13 @@ function run_or_fail { exit $status fi } +function run_if_available { + if command -v "$1" >/dev/null 2>&1; then + "$@" + else + echo "$1 is not installed. Please install it to use this feature." >&2 + fi +} require_command jq require_command cargo diff --git a/acvm-repo/acvm_js/test/shared/nested_acir_call.ts b/acvm-repo/acvm_js/test/shared/nested_acir_call.ts index ce91282a681..1b745ab6a79 100644 --- a/acvm-repo/acvm_js/test/shared/nested_acir_call.ts +++ b/acvm-repo/acvm_js/test/shared/nested_acir_call.ts @@ -2,13 +2,13 @@ import { WitnessMap, StackItem, WitnessStack } from '@noir-lang/acvm_js'; // See `nested_acir_call_circuit` integration test in `acir/tests/test_program_serialization.rs`. export const bytecode = Uint8Array.from([ - 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, 24, 109, 227, 191, 93, 101, - 50, 123, 255, 35, 172, 99, 25, 83, 17, 250, 99, 14, 250, 224, 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, 57, - 108, 14, 91, 248, 202, 168, 65, 255, 207, 122, 28, 180, 250, 244, 221, 244, 197, 223, 68, 182, 154, 197, 184, 134, 80, - 54, 95, 136, 233, 142, 62, 101, 137, 24, 98, 94, 133, 132, 162, 196, 135, 23, 230, 34, 65, 182, 148, 211, 134, 137, 2, - 23, 218, 99, 226, 93, 135, 185, 121, 123, 33, 84, 12, 234, 218, 192, 64, 174, 3, 248, 47, 88, 48, 17, 150, 157, 183, - 151, 95, 244, 86, 91, 221, 61, 10, 81, 31, 178, 190, 110, 194, 102, 96, 76, 251, 202, 80, 13, 204, 77, 224, 25, 176, - 70, 79, 197, 128, 18, 64, 3, 4, 0, 0, + 31, 139, 8, 0, 0, 0, 0, 0, 0, 255, 205, 146, 97, 10, 195, 32, 12, 133, 163, 66, 207, 147, 24, 173, 241, 223, 174, 50, + 153, 189, 255, 17, 214, 177, 148, 89, 17, 250, 99, 14, 246, 224, 97, 144, 16, 146, 143, 231, 224, 45, 167, 126, 105, + 217, 109, 118, 91, 248, 200, 168, 225, 248, 191, 106, 114, 208, 233, 104, 188, 233, 139, 223, 137, 108, 51, 139, 113, + 13, 161, 38, 95, 137, 233, 142, 62, 23, 137, 24, 98, 89, 133, 132, 162, 196, 135, 23, 230, 42, 65, 82, 46, 57, 97, + 166, 192, 149, 182, 152, 121, 211, 97, 110, 222, 94, 8, 13, 132, 182, 54, 48, 144, 235, 8, 254, 10, 22, 76, 132, 101, + 231, 237, 229, 23, 189, 213, 54, 119, 15, 83, 212, 199, 172, 175, 79, 113, 51, 48, 198, 253, 207, 84, 13, 204, 141, + 224, 21, 176, 147, 158, 66, 231, 43, 145, 6, 4, 0, 0, ]); export const initialWitnessMap: WitnessMap = new Map([ diff --git a/aztec_macros/src/lib.rs b/aztec_macros/src/lib.rs index 3ee6f9c21b9..48c30ab6ffa 100644 --- a/aztec_macros/src/lib.rs +++ b/aztec_macros/src/lib.rs @@ -4,20 +4,16 @@ mod utils; use transforms::{ compute_note_hash_and_nullifier::inject_compute_note_hash_and_nullifier, events::{generate_selector_impl, transform_events}, - functions::{transform_function, transform_unconstrained}, - note_interface::generate_note_interface_impl, + functions::{export_fn_abi, transform_function, transform_unconstrained}, + note_interface::{generate_note_interface_impl, inject_note_exports}, storage::{ assign_storage_slots, check_for_storage_definition, check_for_storage_implementation, - generate_storage_implementation, + generate_storage_implementation, generate_storage_layout, }, }; -use noirc_frontend::{ - hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, - macros_api::{ - CrateId, FileId, HirContext, MacroError, MacroProcessor, SecondaryAttribute, SortedModule, - Span, - }, +use noirc_frontend::macros_api::{ + CrateId, FileId, HirContext, MacroError, MacroProcessor, SortedModule, Span, }; use utils::{ @@ -39,16 +35,6 @@ impl MacroProcessor for AztecMacro { transform(ast, crate_id, file_id, context) } - fn process_collected_defs( - &self, - crate_id: &CrateId, - context: &mut HirContext, - collected_trait_impls: &[UnresolvedTraitImpl], - collected_functions: &mut [UnresolvedFunctions], - ) -> Result<(), (MacroError, FileId)> { - transform_collected_defs(crate_id, context, collected_trait_impls, collected_functions) - } - fn process_typed_ast( &self, crate_id: &CrateId, @@ -90,15 +76,19 @@ fn transform_module(module: &mut SortedModule) -> Result let mut has_transformed_module = false; // Check for a user defined storage struct - let storage_defined = check_for_storage_definition(module); - let storage_implemented = check_for_storage_implementation(module); - if storage_defined && !storage_implemented { - generate_storage_implementation(module)?; + let maybe_storage_struct_name = check_for_storage_definition(module)?; + let storage_defined = maybe_storage_struct_name.is_some(); + + if let Some(storage_struct_name) = maybe_storage_struct_name { + if !check_for_storage_implementation(module, &storage_struct_name) { + generate_storage_implementation(module, &storage_struct_name)?; + } + generate_storage_layout(module, storage_struct_name)?; } - for structure in module.types.iter() { - if structure.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { + for structure in module.types.iter_mut() { + if structure.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { module.impls.push(generate_selector_impl(structure)); has_transformed_module = true; } @@ -139,6 +129,7 @@ fn transform_module(module: &mut SortedModule) -> Result // Apply transformations to the function based on collected attributes if is_private || is_public || is_public_vm { + export_fn_abi(&mut module.types, func)?; transform_function( if is_private { "Private" @@ -185,24 +176,6 @@ fn transform_module(module: &mut SortedModule) -> Result Ok(has_transformed_module) } -fn transform_collected_defs( - crate_id: &CrateId, - context: &mut HirContext, - collected_trait_impls: &[UnresolvedTraitImpl], - collected_functions: &mut [UnresolvedFunctions], -) -> Result<(), (MacroError, FileId)> { - if has_aztec_dependency(crate_id, context) { - inject_compute_note_hash_and_nullifier( - crate_id, - context, - collected_trait_impls, - collected_functions, - ) - } else { - Ok(()) - } -} - // // Transform Hir Nodes for Aztec // @@ -212,6 +185,12 @@ fn transform_hir( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { - transform_events(crate_id, context)?; - assign_storage_slots(crate_id, context) + if has_aztec_dependency(crate_id, context) { + transform_events(crate_id, context)?; + inject_compute_note_hash_and_nullifier(crate_id, context)?; + assign_storage_slots(crate_id, context)?; + inject_note_exports(crate_id, context) + } else { + Ok(()) + } } diff --git a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs index 1f5681ed470..1b6630935d9 100644 --- a/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs +++ b/aztec_macros/src/transforms/compute_note_hash_and_nullifier.rs @@ -1,48 +1,43 @@ use noirc_errors::{Location, Span}; use noirc_frontend::{ graph::CrateId, - hir::{ - def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}, - def_map::{LocalModuleId, ModuleId}, - }, - macros_api::{FileId, HirContext, MacroError}, - node_interner::FuncId, - parse_program, FunctionReturnType, ItemVisibility, NoirFunction, UnresolvedTypeData, + macros_api::{FileId, HirContext}, + parse_program, FunctionReturnType, NoirFunction, Type, UnresolvedTypeData, }; -use crate::utils::hir_utils::fetch_struct_trait_impls; +use crate::utils::{ + errors::AztecMacroError, + hir_utils::{collect_crate_functions, fetch_notes, get_contract_module_data, inject_fn}, +}; // Check if "compute_note_hash_and_nullifier(AztecAddress,Field,Field,Field,[Field; N]) -> [Field; 4]" is defined fn check_for_compute_note_hash_and_nullifier_definition( - functions_data: &[(LocalModuleId, FuncId, NoirFunction)], - module_id: LocalModuleId, + crate_id: &CrateId, + context: &HirContext, ) -> bool { - functions_data.iter().filter(|func_data| func_data.0 == module_id).any(|func_data| { - func_data.2.def.name.0.contents == "compute_note_hash_and_nullifier" - && func_data.2.def.parameters.len() == 5 - && match &func_data.2.def.parameters[0].typ.typ { - UnresolvedTypeData::Named(path, _, _) => path.segments.last().unwrap().0.contents == "AztecAddress", - _ => false, - } - && func_data.2.def.parameters[1].typ.typ == UnresolvedTypeData::FieldElement - && func_data.2.def.parameters[2].typ.typ == UnresolvedTypeData::FieldElement - && func_data.2.def.parameters[3].typ.typ == UnresolvedTypeData::FieldElement - // checks if the 5th parameter is an array and the Box in - // Array(Option, Box) contains only fields - && match &func_data.2.def.parameters[4].typ.typ { - UnresolvedTypeData::Array(_, inner_type) => { - matches!(inner_type.typ, UnresolvedTypeData::FieldElement) - }, - _ => false, - } + collect_crate_functions(crate_id, context).iter().any(|funct_id| { + let func_data = context.def_interner.function_meta(funct_id); + let func_name = context.def_interner.function_name(funct_id); + func_name == "compute_note_hash_and_nullifier" + && func_data.parameters.len() == 5 + && func_data.parameters.0.first().is_some_and(| (_, typ, _) | match typ { + Type::Struct(struct_typ, _) => struct_typ.borrow().name.0.contents == "AztecAddress", + _ => false + }) + && func_data.parameters.0.get(1).is_some_and(|(_, typ, _)| typ.is_field()) + && func_data.parameters.0.get(2).is_some_and(|(_, typ, _)| typ.is_field()) + && func_data.parameters.0.get(3).is_some_and(|(_, typ, _)| typ.is_field()) + // checks if the 5th parameter is an array and contains only fields + && func_data.parameters.0.get(4).is_some_and(|(_, typ, _)| match typ { + Type::Array(_, inner_type) => inner_type.to_owned().is_field(), + _ => false + }) // We check the return type the same way as we did the 5th parameter - && match &func_data.2.def.return_type { + && match &func_data.return_type { FunctionReturnType::Default(_) => false, FunctionReturnType::Ty(unresolved_type) => { match &unresolved_type.typ { - UnresolvedTypeData::Array(_, inner_type) => { - matches!(inner_type.typ, UnresolvedTypeData::FieldElement) - }, + UnresolvedTypeData::Array(_, inner_type) => matches!(inner_type.typ, UnresolvedTypeData::FieldElement), _ => false, } } @@ -53,77 +48,33 @@ fn check_for_compute_note_hash_and_nullifier_definition( pub fn inject_compute_note_hash_and_nullifier( crate_id: &CrateId, context: &mut HirContext, - unresolved_traits_impls: &[UnresolvedTraitImpl], - collected_functions: &mut [UnresolvedFunctions], -) -> Result<(), (MacroError, FileId)> { - // We first fetch modules in this crate which correspond to contracts, along with their file id. - let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context - .def_map(crate_id) - .expect("ICE: Missing crate in def_map") - .modules() - .iter() - .filter(|(_, module)| module.is_contract) - .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) - .collect(); - - // If the current crate does not contain a contract module we simply skip it. - if contract_module_file_ids.is_empty() { - return Ok(()); - } else if contract_module_file_ids.len() != 1 { - panic!("Found multiple contracts in the same crate"); +) -> Result<(), (AztecMacroError, FileId)> { + if let Some((module_id, file_id)) = get_contract_module_data(context, crate_id) { + // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an + // escape hatch for this mechanism. + // TODO(#4647): improve this diagnosis and error messaging. + if check_for_compute_note_hash_and_nullifier_definition(crate_id, context) { + return Ok(()); + } + + // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the + // contract might use. These are the types that are marked as #[aztec(note)]. + let note_types = fetch_notes(context) + .iter() + .map(|(_, note)| note.borrow().name.0.contents.clone()) + .collect::>(); + + // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. + let func = generate_compute_note_hash_and_nullifier(¬e_types); + + // And inject the newly created function into the contract. + + // TODO(#4373): We don't have a reasonable location for the source code of this autogenerated function, so we simply + // pass an empty span. This function should not produce errors anyway so this should not matter. + let location = Location::new(Span::empty(0), file_id); + + inject_fn(crate_id, context, func, location, module_id, file_id); } - - let (module_id, file_id) = contract_module_file_ids[0]; - - // If compute_note_hash_and_nullifier is already defined by the user, we skip auto-generation in order to provide an - // escape hatch for this mechanism. - // TODO(#4647): improve this diagnosis and error messaging. - if collected_functions.iter().any(|coll_funcs_data| { - check_for_compute_note_hash_and_nullifier_definition(&coll_funcs_data.functions, module_id) - }) { - return Ok(()); - } - - // In order to implement compute_note_hash_and_nullifier, we need to know all of the different note types the - // contract might use. These are the types that implement the NoteInterface trait, which provides the - // get_note_type_id function. - let note_types = fetch_struct_trait_impls(context, unresolved_traits_impls, "NoteInterface"); - - // We can now generate a version of compute_note_hash_and_nullifier tailored for the contract in this crate. - let func = generate_compute_note_hash_and_nullifier(¬e_types); - - // And inject the newly created function into the contract. - - // TODO(#4373): We don't have a reasonable location for the source code of this autogenerated function, so we simply - // pass an empty span. This function should not produce errors anyway so this should not matter. - let location = Location::new(Span::empty(0), file_id); - - // These are the same things the ModCollector does when collecting functions: we push the function to the - // NodeInterner, declare it in the module (which checks for duplicate definitions), and finally add it to the list - // on collected but unresolved functions. - - let func_id = context.def_interner.push_empty_fn(); - context.def_interner.push_function( - func_id, - &func.def, - ModuleId { krate: *crate_id, local_id: module_id }, - location, - ); - - context.def_map_mut(crate_id).unwrap() - .modules_mut()[module_id.0] - .declare_function( - func.name_ident().clone(), ItemVisibility::Public, func_id - ).expect( - "Failed to declare the autogenerated compute_note_hash_and_nullifier function, likely due to a duplicate definition. See https://github.com/AztecProtocol/aztec-packages/issues/4647." - ); - - collected_functions - .iter_mut() - .find(|fns| fns.file_id == file_id) - .expect("ICE: no functions found in contract file") - .push_fn(module_id, func_id, func.clone()); - Ok(()) } diff --git a/aztec_macros/src/transforms/events.rs b/aztec_macros/src/transforms/events.rs index e7e39ed29ba..4f2b70453df 100644 --- a/aztec_macros/src/transforms/events.rs +++ b/aztec_macros/src/transforms/events.rs @@ -16,7 +16,8 @@ use crate::{ chained_dep, utils::{ ast_utils::{ - call, expression, ident, ident_path, make_statement, make_type, path, variable_path, + call, expression, ident, ident_path, is_custom_attribute, make_statement, make_type, + path, variable_path, }, constants::SIGNATURE_PLACEHOLDER, errors::AztecMacroError, @@ -38,7 +39,8 @@ use crate::{ /// This allows developers to emit events without having to write the signature of the event every time they emit it. /// The signature cannot be known at this point since types are not resolved yet, so we use a signature placeholder. /// It'll get resolved after by transforming the HIR. -pub fn generate_selector_impl(structure: &NoirStruct) -> TypeImpl { +pub fn generate_selector_impl(structure: &mut NoirStruct) -> TypeImpl { + structure.attributes.push(SecondaryAttribute::Abi("events".to_string())); let struct_type = make_type(UnresolvedTypeData::Named(path(structure.name.clone()), vec![], true)); @@ -172,9 +174,9 @@ pub fn transform_events( crate_id: &CrateId, context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { - for struct_id in collect_crate_structs(crate_id, context) { + for (_, struct_id) in collect_crate_structs(crate_id, context) { let attributes = context.def_interner.struct_attributes(&struct_id); - if attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Event)) { + if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(event)")) { transform_event(struct_id, &mut context.def_interner)?; } } diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index 9844abc30fe..a3064ecdd01 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -1,10 +1,10 @@ use convert_case::{Case, Casing}; use noirc_errors::Span; use noirc_frontend::{ - macros_api::FieldElement, BlockExpression, ConstrainKind, ConstrainStatement, Distinctness, - Expression, ExpressionKind, ForLoopStatement, ForRange, FunctionReturnType, Ident, Literal, - NoirFunction, Param, PathKind, Pattern, Signedness, Statement, StatementKind, UnresolvedType, - UnresolvedTypeData, Visibility, + macros_api::FieldElement, parse_program, BlockExpression, ConstrainKind, ConstrainStatement, + Distinctness, Expression, ExpressionKind, ForLoopStatement, ForRange, FunctionReturnType, + Ident, Literal, NoirFunction, NoirStruct, Param, PathKind, Pattern, Signedness, Statement, + StatementKind, UnresolvedType, UnresolvedTypeData, Visibility, }; use crate::{ @@ -45,13 +45,13 @@ pub fn transform_function( // Add initialization check if insert_init_check { - let init_check = create_init_check(); + let init_check = create_init_check(ty); func.def.body.statements.insert(0, init_check); } // Add assertion for initialization arguments and sender if is_initializer { - func.def.body.statements.insert(0, create_assert_initializer()); + func.def.body.statements.insert(0, create_assert_initializer(ty)); } // Add access to the storage struct @@ -85,7 +85,7 @@ pub fn transform_function( // Before returning mark the contract as initialized if is_initializer { - let mark_initialized = create_mark_as_initialized(); + let mark_initialized = create_mark_as_initialized(ty); func.def.body.statements.push(mark_initialized); } @@ -113,6 +113,92 @@ pub fn transform_function( Ok(()) } +// Generates a global struct containing the original (before transform_function gets executed) function abi that gets exported +// in the contract artifact after compilation. The abi will be later used to decode the function return values in the simulator. +pub fn export_fn_abi( + types: &mut Vec, + func: &NoirFunction, +) -> Result<(), AztecMacroError> { + let mut parameters_struct_source: Option<&str> = None; + + let struct_source = format!( + " + struct {}_parameters {{ + {} + }} + ", + func.name(), + func.parameters() + .iter() + .map(|param| { + let param_name = match param.pattern.clone() { + Pattern::Identifier(ident) => Ok(ident.0.contents), + _ => Err(AztecMacroError::CouldNotExportFunctionAbi { + span: Some(param.span), + secondary_message: Some( + "Only identifier patterns are supported".to_owned(), + ), + }), + }; + + format!( + "{}: {}", + param_name.unwrap(), + param.typ.typ.to_string().replace("plain::", "") + ) + }) + .collect::>() + .join(",\n"), + ); + + if !func.parameters().is_empty() { + parameters_struct_source = Some(&struct_source); + } + + let mut program = String::new(); + + let parameters = if let Some(parameters_struct_source) = parameters_struct_source { + program.push_str(parameters_struct_source); + format!("parameters: {}_parameters,\n", func.name()) + } else { + "".to_string() + }; + + let return_type_str = func.return_type().typ.to_string().replace("plain::", ""); + let return_type = if return_type_str != "()" { + format!("return_type: {},\n", return_type_str) + } else { + "".to_string() + }; + + let export_struct_source = format!( + " + #[abi(functions)] + struct {}_abi {{ + {}{} + }}", + func.name(), + parameters, + return_type + ); + + program.push_str(&export_struct_source); + + let (ast, errors) = parse_program(&program); + if !errors.is_empty() { + return Err(AztecMacroError::CouldNotExportFunctionAbi { + span: None, + secondary_message: Some( + format!("Failed to parse Noir macro code (struct {}_abi). This is either a bug in the compiler or the Noir macro code", func.name()) + ) + }); + } + + let sorted_ast = ast.into_sorted(); + types.extend(sorted_ast.types); + Ok(()) +} + /// Transform Unconstrained /// /// Inserts the following code at the beginning of an unconstrained function @@ -159,9 +245,10 @@ fn create_inputs(ty: &str) -> Param { /// ```noir /// assert_is_initialized(&mut context); /// ``` -fn create_init_check() -> Statement { +fn create_init_check(ty: &str) -> Statement { + let fname = format!("assert_is_initialized_{}", ty.to_case(Case::Snake)); make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", "assert_is_initialized")), + variable_path(chained_dep!("aztec", "initializer", &fname)), vec![mutable_reference("context")], ))) } @@ -172,9 +259,10 @@ fn create_init_check() -> Statement { /// ```noir /// mark_as_initialized(&mut context); /// ``` -fn create_mark_as_initialized() -> Statement { +fn create_mark_as_initialized(ty: &str) -> Statement { + let fname = format!("mark_as_initialized_{}", ty.to_case(Case::Snake)); make_statement(StatementKind::Expression(call( - variable_path(chained_dep!("aztec", "initializer", "mark_as_initialized")), + variable_path(chained_dep!("aztec", "initializer", &fname)), vec![mutable_reference("context")], ))) } @@ -205,13 +293,11 @@ fn create_internal_check(fname: &str) -> Statement { /// ```noir /// assert_initialization_matches_address_preimage(context); /// ``` -fn create_assert_initializer() -> Statement { +fn create_assert_initializer(ty: &str) -> Statement { + let fname = + format!("assert_initialization_matches_address_preimage_{}", ty.to_case(Case::Snake)); make_statement(StatementKind::Expression(call( - variable_path(chained_dep!( - "aztec", - "initializer", - "assert_initialization_matches_address_preimage" - )), + variable_path(chained_dep!("aztec", "initializer", &fname)), vec![variable("context")], ))) } @@ -223,62 +309,66 @@ fn create_assert_initializer() -> Statement { /// ```noir /// #[aztec(private)] /// fn foo(structInput: SomeStruct, arrayInput: [u8; 10], fieldInput: Field) -> Field { -/// // Create the bounded vec object -/// let mut serialized_args = BoundedVec::new(); +/// // Create the hasher object +/// let mut hasher = Hasher::new(); /// /// // struct inputs call serialize on them to add an array of fields -/// serialized_args.extend_from_array(structInput.serialize()); +/// hasher.add_multiple(structInput.serialize()); /// -/// // Array inputs are iterated over and each element is added to the bounded vec (as a field) +/// // Array inputs are iterated over and each element is added to the hasher (as a field) /// for i in 0..arrayInput.len() { -/// serialized_args.push(arrayInput[i] as Field); +/// hasher.add(arrayInput[i] as Field); /// } -/// // Field inputs are added to the bounded vec -/// serialized_args.push({ident}); +/// // Field inputs are added to the hasher +/// hasher.add({ident}); /// /// // Create the context /// // The inputs (injected by this `create_inputs`) and completed hash object are passed to the context -/// let mut context = PrivateContext::new(inputs, hash_args(serialized_args)); +/// let mut context = PrivateContext::new(inputs, hasher.hash()); /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { let mut injected_expressions: Vec = vec![]; - // `let mut serialized_args = BoundedVec::new();` - let let_serialized_args = mutable_assignment( - "serialized_args", // Assigned to + let hasher_name = "args_hasher"; + + // `let mut args_hasher = Hasher::new();` + let let_hasher = mutable_assignment( + hasher_name, // Assigned to call( - variable_path(chained_dep!("std", "collections", "bounded_vec", "BoundedVec", "new")), // Path - vec![], // args + variable_path(chained_dep!("aztec", "hash", "ArgsHasher", "new")), // Path + vec![], // args ), ); - // Completes: `let mut serialized_args = BoundedVec::new();` - injected_expressions.push(let_serialized_args); + // Completes: `let mut args_hasher = Hasher::new();` + injected_expressions.push(let_hasher); - // Iterate over each of the function parameters, adding to them to the bounded vec + // Iterate over each of the function parameters, adding to them to the hasher for Param { pattern, typ, span, .. } in params { match pattern { Pattern::Identifier(identifier) => { // Match the type to determine the padding to do let unresolved_type = &typ.typ; let expression = match unresolved_type { - // `serialized_args.extend_from_array({ident}.serialize())` - UnresolvedTypeData::Named(..) => add_struct_to_serialized_args(identifier), + // `hasher.add_multiple({ident}.serialize())` + UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier, hasher_name), UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_serialized_args(identifier, arr_type) + add_array_to_hasher(identifier, arr_type, hasher_name) + } + // `hasher.add({ident})` + UnresolvedTypeData::FieldElement => { + add_field_to_hasher(identifier, hasher_name) } - // `serialized_args.push({ident})` - UnresolvedTypeData::FieldElement => add_field_to_serialized_args(identifier), - // Add the integer to the serialized args, casted to a field - // `serialized_args.push({ident} as Field)` + // Add the integer to the hasher, casted to a field + // `hasher.add({ident} as Field)` UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_serialized_args(identifier) + add_cast_to_hasher(identifier, hasher_name) } UnresolvedTypeData::String(..) => { let (var_bytes, id) = str_to_bytes(identifier); injected_expressions.push(var_bytes); - add_array_to_serialized_args( + add_array_to_hasher( &id, &UnresolvedType { typ: UnresolvedTypeData::Integer( @@ -287,6 +377,7 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac ), span: None, }, + hasher_name, ) } _ => { @@ -304,10 +395,11 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac // Create the inputs to the context let inputs_expression = variable("inputs"); - // `hash_args(serialized_args)` - let hash_call = call( - variable_path(chained_dep!("aztec", "hash", "hash_args")), // variable - vec![variable("serialized_args")], // args + // `args_hasher.hash()` + let hash_call = method_call( + variable(hasher_name), // variable + "hash", // method name + vec![], // args ); let path_snippet = ty.to_case(Case::Snake); // e.g. private_context @@ -591,11 +683,11 @@ fn create_context_finish() -> Statement { } // -// Methods to create hash_args inputs +// Methods to create hasher inputs // -fn add_struct_to_serialized_args(identifier: &Ident) -> Statement { - // If this is a struct, we call serialize and add the array to the serialized args +fn add_struct_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement { + // If this is a struct, we call serialize and add the array to the hasher let serialized_call = method_call( variable_path(path(identifier.clone())), // variable "serialize", // method name @@ -603,9 +695,9 @@ fn add_struct_to_serialized_args(identifier: &Ident) -> Statement { ); make_statement(StatementKind::Semi(method_call( - variable("serialized_args"), // variable - "extend_from_array", // method name - vec![serialized_call], // args + variable(hasher_name), // variable + "add_multiple", // method name + vec![serialized_call], // args ))) } @@ -625,7 +717,7 @@ fn str_to_bytes(identifier: &Ident) -> (Statement, Ident) { } fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the serialized args + // If this is an array of primitive types (integers / fields) we can add them each to the hasher // casted to a field let span = var.span; @@ -638,7 +730,7 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { // What will be looped over - // - `serialized_args.push({ident}[i] as Field)` + // - `hasher.add({ident}[i] as Field)` let for_loop_block = expression(ExpressionKind::Block(BlockExpression { statements: loop_body })); @@ -657,66 +749,70 @@ fn create_loop_over(var: Expression, loop_body: Vec) -> Statement { })) } -fn add_array_to_serialized_args(identifier: &Ident, arr_type: &UnresolvedType) -> Statement { - // If this is an array of primitive types (integers / fields) we can add them each to the serialized_args +fn add_array_to_hasher( + identifier: &Ident, + arr_type: &UnresolvedType, + hasher_name: &str, +) -> Statement { + // If this is an array of primitive types (integers / fields) we can add them each to the hasher // casted to a field // Wrap in the semi thing - does that mean ended with semi colon? - // `serialized_args.push({ident}[i] as Field)` + // `hasher.add({ident}[i] as Field)` let arr_index = index_array(identifier.clone(), "i"); - let (add_expression, vec_method_name) = match arr_type.typ { + let (add_expression, hasher_method_name) = match arr_type.typ { UnresolvedTypeData::Named(..) => { - let vec_method_name = "extend_from_array".to_owned(); + let hasher_method_name = "add_multiple".to_owned(); let call = method_call( // All serialize on each element arr_index, // variable "serialize", // method name vec![], // args ); - (call, vec_method_name) + (call, hasher_method_name) } _ => { - let vec_method_name = "push".to_owned(); + let hasher_method_name = "add".to_owned(); let call = cast( arr_index, // lhs - `ident[i]` UnresolvedTypeData::FieldElement, // cast to - `as Field` ); - (call, vec_method_name) + (call, hasher_method_name) } }; let block_statement = make_statement(StatementKind::Semi(method_call( - variable("serialized_args"), // variable - &vec_method_name, // method name + variable(hasher_name), // variable + &hasher_method_name, // method name vec![add_expression], ))); create_loop_over(variable_ident(identifier.clone()), vec![block_statement]) } -fn add_field_to_serialized_args(identifier: &Ident) -> Statement { - // `serialized_args.push({ident})` +fn add_field_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement { + // `hasher.add({ident})` let ident = variable_path(path(identifier.clone())); make_statement(StatementKind::Semi(method_call( - variable("serialized_args"), // variable - "push", // method name - vec![ident], // args + variable(hasher_name), // variable + "add", // method name + vec![ident], // args ))) } -fn add_cast_to_serialized_args(identifier: &Ident) -> Statement { - // `serialized_args.push({ident} as Field)` +fn add_cast_to_hasher(identifier: &Ident, hasher_name: &str) -> Statement { + // `hasher.add({ident} as Field)` // `{ident} as Field` let cast_operation = cast( variable_path(path(identifier.clone())), // lhs UnresolvedTypeData::FieldElement, // rhs ); - // `serialized_args.push({ident} as Field)` + // `hasher.add({ident} as Field)` make_statement(StatementKind::Semi(method_call( - variable("serialized_args"), // variable - "push", // method name - vec![cast_operation], // args + variable(hasher_name), // variable + "add", // method name + vec![cast_operation], // args ))) } diff --git a/aztec_macros/src/transforms/note_interface.rs b/aztec_macros/src/transforms/note_interface.rs index 01d0272088b..0514155824e 100644 --- a/aztec_macros/src/transforms/note_interface.rs +++ b/aztec_macros/src/transforms/note_interface.rs @@ -1,7 +1,11 @@ use noirc_errors::Span; use noirc_frontend::{ - parse_program, parser::SortedModule, ItemVisibility, NoirFunction, NoirStruct, PathKind, - TraitImplItem, TypeImpl, UnresolvedTypeData, UnresolvedTypeExpression, + graph::CrateId, + macros_api::{FileId, HirContext, HirExpression, HirLiteral, HirStatement}, + parse_program, + parser::SortedModule, + ItemVisibility, LetStatement, NoirFunction, NoirStruct, PathKind, TraitImplItem, Type, + TypeImpl, UnresolvedTypeData, UnresolvedTypeExpression, }; use regex::Regex; @@ -12,6 +16,7 @@ use crate::{ check_trait_method_implemented, ident, ident_path, is_custom_attribute, make_type, }, errors::AztecMacroError, + hir_utils::{fetch_notes, get_contract_module_data, inject_global}, }, }; @@ -24,7 +29,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt .iter_mut() .filter(|typ| typ.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(note)"))); - let mut note_properties_structs = vec![]; + let mut structs_to_inject = vec![]; for note_struct in annotated_note_structs { // Look for the NoteInterface trait implementation for the note @@ -80,6 +85,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt )), }), }?; + let note_type_id = note_type_id(¬e_type); // Automatically inject the header field if it's not present let (header_field_name, _) = if let Some(existing_header) = @@ -138,7 +144,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt &header_field_name.0.contents, note_interface_impl_span, )?; - note_properties_structs.push(note_properties_struct); + structs_to_inject.push(note_properties_struct); let note_properties_fn = generate_note_properties_fn( ¬e_type, ¬e_fields, @@ -167,7 +173,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt if !check_trait_method_implemented(trait_impl, "get_note_type_id") { let get_note_type_id_fn = - generate_note_get_type_id(¬e_type, note_interface_impl_span)?; + generate_note_get_type_id(¬e_type_id, note_interface_impl_span)?; trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); } @@ -178,7 +184,7 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt } } - module.types.extend(note_properties_structs); + module.types.extend(structs_to_inject); Ok(()) } @@ -245,19 +251,16 @@ fn generate_note_set_header( // Automatically generate the note type id getter method. The id itself its calculated as the concatenation // of the conversion of the characters in the note's struct name to unsigned integers. fn generate_note_get_type_id( - note_type: &str, + note_type_id: &str, impl_span: Option, ) -> Result { - // TODO(#4519) Improve automatic note id generation and assignment - let note_id = - note_type.chars().map(|c| (c as u32).to_string()).collect::>().join(""); let function_source = format!( " fn get_note_type_id() -> Field {{ {} }} ", - note_id + note_type_id ) .to_string(); @@ -443,6 +446,34 @@ fn generate_compute_note_content_hash( Ok(noir_fn) } +fn generate_note_exports_global( + note_type: &str, + note_type_id: &str, +) -> Result { + let struct_source = format!( + " + #[abi(notes)] + global {0}_EXPORTS: (Field, str<{1}>) = ({2},\"{0}\"); + ", + note_type, + note_type_id.len(), + note_type_id + ) + .to_string(); + + let (global_ast, errors) = parse_program(&struct_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some(format!("Failed to parse Noir macro code (struct {}Exports). This is either a bug in the compiler or the Noir macro code", note_type)), + span: None + }); + } + + let mut global_ast = global_ast.into_sorted(); + Ok(global_ast.globals.pop().unwrap()) +} + // Source code generator functions. These utility methods produce Noir code as strings, that are then parsed and added to the AST. fn generate_note_properties_struct_source( @@ -581,3 +612,85 @@ fn generate_note_deserialize_content_source( ) .to_string() } + +// Utility function to generate the note type id as a Field +fn note_type_id(note_type: &str) -> String { + // TODO(#4519) Improve automatic note id generation and assignment + note_type.chars().map(|c| (c as u32).to_string()).collect::>().join("") +} + +pub fn inject_note_exports( + crate_id: &CrateId, + context: &mut HirContext, +) -> Result<(), (AztecMacroError, FileId)> { + if let Some((module_id, file_id)) = get_contract_module_data(context, crate_id) { + let notes = fetch_notes(context); + + for (_, note) in notes { + let func_id = context + .def_interner + .lookup_method( + &Type::Struct(context.def_interner.get_struct(note.borrow().id), vec![]), + note.borrow().id, + "get_note_type_id", + false, + ) + .ok_or(( + AztecMacroError::CouldNotExportStorageLayout { + span: None, + secondary_message: Some(format!( + "Could not retrieve get_note_type_id function for note {}", + note.borrow().name.0.contents + )), + }, + file_id, + ))?; + let init_function = + context.def_interner.function(&func_id).block(&context.def_interner); + let init_function_statement_id = init_function.statements().first().ok_or(( + AztecMacroError::CouldNotExportStorageLayout { + span: None, + secondary_message: Some(format!( + "Could not retrieve note id statement from function for note {}", + note.borrow().name.0.contents + )), + }, + file_id, + ))?; + let note_id_statement = context.def_interner.statement(init_function_statement_id); + + let note_id_value = match note_id_statement { + HirStatement::Expression(expression_id) => { + match context.def_interner.expression(&expression_id) { + HirExpression::Literal(HirLiteral::Integer(value, _)) => Ok(value), + _ => Err(( + AztecMacroError::CouldNotExportStorageLayout { + span: None, + secondary_message: Some( + "note_id statement must be a literal expression".to_string(), + ), + }, + file_id, + )), + } + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some( + "note_id statement must be an expression".to_string(), + ), + }, + file_id, + )), + }?; + let global = generate_note_exports_global( + ¬e.borrow().name.0.contents, + ¬e_id_value.to_string(), + ) + .map_err(|err| (err, file_id))?; + + inject_global(crate_id, context, global, module_id, file_id); + } + } + Ok(()) +} diff --git a/aztec_macros/src/transforms/storage.rs b/aztec_macros/src/transforms/storage.rs index 10f44d01bb4..0bfb39cbc71 100644 --- a/aztec_macros/src/transforms/storage.rs +++ b/aztec_macros/src/transforms/storage.rs @@ -1,4 +1,4 @@ -use std::borrow::{Borrow, BorrowMut}; +use std::borrow::Borrow; use noirc_errors::Span; use noirc_frontend::{ @@ -7,33 +7,53 @@ use noirc_frontend::{ FieldElement, FileId, HirContext, HirExpression, HirLiteral, HirStatement, NodeInterner, }, node_interner::{TraitId, TraitImplKind}, + parse_program, parser::SortedModule, + token::SecondaryAttribute, BlockExpression, Expression, ExpressionKind, FunctionDefinition, Ident, Literal, NoirFunction, - PathKind, Pattern, StatementKind, Type, TypeImpl, UnresolvedType, UnresolvedTypeData, + NoirStruct, PathKind, Pattern, StatementKind, Type, TypeImpl, UnresolvedType, + UnresolvedTypeData, }; use crate::{ chained_dep, chained_path, utils::{ ast_utils::{ - call, expression, ident, ident_path, lambda, make_statement, make_type, pattern, - return_type, variable, variable_path, + call, expression, ident, ident_path, is_custom_attribute, lambda, make_statement, + make_type, pattern, return_type, variable, variable_path, }, errors::AztecMacroError, - hir_utils::{collect_crate_structs, collect_traits}, + hir_utils::{collect_crate_structs, collect_traits, get_contract_module_data}, }, }; // Check to see if the user has defined a storage struct -pub fn check_for_storage_definition(module: &SortedModule) -> bool { - module.types.iter().any(|r#struct| r#struct.name.0.contents == "Storage") +pub fn check_for_storage_definition( + module: &SortedModule, +) -> Result, AztecMacroError> { + let result: Vec<&NoirStruct> = module + .types + .iter() + .filter(|r#struct| { + r#struct.attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(storage)")) + }) + .collect(); + if result.len() > 1 { + return Err(AztecMacroError::MultipleStorageDefinitions { + span: result.first().map(|res| res.name.span()), + }); + } + Ok(result.iter().map(|&r#struct| r#struct.name.0.contents.clone()).next()) } // Check to see if the user has defined a storage struct -pub fn check_for_storage_implementation(module: &SortedModule) -> bool { +pub fn check_for_storage_implementation( + module: &SortedModule, + storage_struct_name: &String, +) -> bool { module.impls.iter().any(|r#impl| match &r#impl.object_type.typ { UnresolvedTypeData::Named(path, _, _) => { - path.segments.last().is_some_and(|segment| segment.0.contents == "Storage") + path.segments.last().is_some_and(|segment| segment.0.contents == *storage_struct_name) } _ => false, }) @@ -117,9 +137,15 @@ pub fn generate_storage_field_constructor( /// /// Storage slots are generated as 0 and will be populated using the information from the HIR /// at a later stage. -pub fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), AztecMacroError> { - let definition = - module.types.iter().find(|r#struct| r#struct.name.0.contents == "Storage").unwrap(); +pub fn generate_storage_implementation( + module: &mut SortedModule, + storage_struct_name: &String, +) -> Result<(), AztecMacroError> { + let definition = module + .types + .iter() + .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) + .unwrap(); let slot_zero = expression(ExpressionKind::Literal(Literal::Integer( FieldElement::from(i128::from(0)), @@ -136,7 +162,7 @@ pub fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), .collect(); let storage_constructor_statement = make_statement(StatementKind::Expression(expression( - ExpressionKind::constructor((chained_path!("Storage"), field_constructors)), + ExpressionKind::constructor((chained_path!(storage_struct_name), field_constructors)), ))); let init = NoirFunction::normal(FunctionDefinition::normal( @@ -157,7 +183,7 @@ pub fn generate_storage_implementation(module: &mut SortedModule) -> Result<(), let storage_impl = TypeImpl { object_type: UnresolvedType { - typ: UnresolvedTypeData::Named(chained_path!("Storage"), vec![], true), + typ: UnresolvedTypeData::Named(chained_path!(storage_struct_name), vec![], true), span: Some(Span::default()), }, type_span: Span::default(), @@ -239,16 +265,51 @@ pub fn assign_storage_slots( context: &mut HirContext, ) -> Result<(), (AztecMacroError, FileId)> { let traits: Vec<_> = collect_traits(context); - for struct_id in collect_crate_structs(crate_id, context) { - let interner: &mut NodeInterner = context.def_interner.borrow_mut(); - let r#struct = interner.get_struct(struct_id); - let file_id = r#struct.borrow().location.file; - if r#struct.borrow().name.0.contents == "Storage" && r#struct.borrow().id.krate().is_root() + if let Some((_, file_id)) = get_contract_module_data(context, crate_id) { + let maybe_storage_struct = + collect_crate_structs(crate_id, context).iter().find_map(|&(_, struct_id)| { + let r#struct = context.def_interner.get_struct(struct_id); + let attributes = context.def_interner.struct_attributes(&struct_id); + if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(storage)")) + && r#struct.borrow().id.krate().is_root() + { + Some(r#struct) + } else { + None + } + }); + + let maybe_storage_layout = + context.def_interner.get_all_globals().iter().find_map(|global_info| { + let statement = context.def_interner.get_global_let_statement(global_info.id); + if statement.clone().is_some_and(|stmt| { + stmt.attributes + .iter() + .any(|attr| *attr == SecondaryAttribute::Abi("storage".to_string())) + }) { + let expr = context.def_interner.expression(&statement.unwrap().expression); + match expr { + HirExpression::Constructor(hir_constructor_expression) => { + Some(hir_constructor_expression) + } + _ => None, + } + } else { + None + } + }); + + if let (Some(storage_struct), Some(storage_layout)) = + (maybe_storage_struct, maybe_storage_layout) { - let init_id = interner + let init_id = context + .def_interner .lookup_method( - &Type::Struct(interner.get_struct(struct_id), vec![]), - struct_id, + &Type::Struct( + context.def_interner.get_struct(storage_struct.borrow().id), + vec![], + ), + storage_struct.borrow().id, "init", false, ) @@ -260,28 +321,33 @@ pub fn assign_storage_slots( }, file_id, ))?; - let init_function = interner.function(&init_id).block(interner); + let init_function = + context.def_interner.function(&init_id).block(&context.def_interner); let init_function_statement_id = init_function.statements().first().ok_or(( AztecMacroError::CouldNotAssignStorageSlots { secondary_message: Some("Init storage statement not found".to_string()), }, file_id, ))?; - let storage_constructor_statement = interner.statement(init_function_statement_id); + let storage_constructor_statement = + context.def_interner.statement(init_function_statement_id); let storage_constructor_expression = match storage_constructor_statement { HirStatement::Expression(expression_id) => { - match interner.expression(&expression_id) { - HirExpression::Constructor(hir_constructor_expression) => { - Ok(hir_constructor_expression) - } - _ => Err((AztecMacroError::CouldNotAssignStorageSlots { + match context.def_interner.expression(&expression_id) { + HirExpression::Constructor(hir_constructor_expression) => { + Ok(hir_constructor_expression) + } + _ => Err(( + AztecMacroError::CouldNotAssignStorageSlots { secondary_message: Some( "Storage constructor statement must be a constructor expression" .to_string(), ), - }, file_id)) - } + }, + file_id, + )), + } } _ => Err(( AztecMacroError::CouldNotAssignStorageSlots { @@ -295,9 +361,9 @@ pub fn assign_storage_slots( let mut storage_slot: u64 = 1; for (index, (_, expr_id)) in storage_constructor_expression.fields.iter().enumerate() { - let fields = r#struct.borrow().get_fields(&[]); - let (_, field_type) = fields.get(index).unwrap(); - let new_call_expression = match interner.expression(expr_id) { + let fields = storage_struct.borrow().get_fields(&[]); + let (field_name, field_type) = fields.get(index).unwrap(); + let new_call_expression = match context.def_interner.expression(expr_id) { HirExpression::Call(hir_call_expression) => Ok(hir_call_expression), _ => Err(( AztecMacroError::CouldNotAssignStorageSlots { @@ -310,7 +376,8 @@ pub fn assign_storage_slots( )), }?; - let slot_arg_expression = interner.expression(&new_call_expression.arguments[1]); + let slot_arg_expression = + context.def_interner.expression(&new_call_expression.arguments[1]); let current_storage_slot = match slot_arg_expression { HirExpression::Literal(HirLiteral::Integer(slot, _)) => Ok(slot.to_u128()), @@ -325,22 +392,123 @@ pub fn assign_storage_slots( )), }?; - if current_storage_slot != 0 { - continue; - } + let storage_layout_field = + storage_layout.fields.iter().find(|field| field.0 .0.contents == *field_name); - let type_serialized_len = get_serialized_length(&traits, field_type, interner) - .map_err(|err| (err, file_id))?; - interner.update_expression(new_call_expression.arguments[1], |expr| { + let storage_layout_slot_expr_id = + if let Some((_, expr_id)) = storage_layout_field { + let expr = context.def_interner.expression(expr_id); + if let HirExpression::Constructor(storage_layout_field_storable_expr) = expr + { + storage_layout_field_storable_expr.fields.iter().find_map( + |(field, expr_id)| { + if field.0.contents == "slot" { + Some(*expr_id) + } else { + None + } + }, + ) + } else { + None + } + } else { + None + } + .ok_or(( + AztecMacroError::CouldNotAssignStorageSlots { + secondary_message: Some(format!( + "Storage layout field ({}) not found or has an incorrect type", + field_name + )), + }, + file_id, + ))?; + + let new_storage_slot = if current_storage_slot == 0 { + u128::from(storage_slot) + } else { + current_storage_slot + }; + + let type_serialized_len = + get_serialized_length(&traits, field_type, &context.def_interner) + .map_err(|err| (err, file_id))?; + + context.def_interner.update_expression(new_call_expression.arguments[1], |expr| { *expr = HirExpression::Literal(HirLiteral::Integer( - FieldElement::from(u128::from(storage_slot)), + FieldElement::from(new_storage_slot), false, - )); + )) + }); + + context.def_interner.update_expression(storage_layout_slot_expr_id, |expr| { + *expr = HirExpression::Literal(HirLiteral::Integer( + FieldElement::from(new_storage_slot), + false, + )) }); storage_slot += type_serialized_len; } } } + + Ok(()) +} + +pub fn generate_storage_layout( + module: &mut SortedModule, + storage_struct_name: String, +) -> Result<(), AztecMacroError> { + let definition = module + .types + .iter() + .find(|r#struct| r#struct.name.0.contents == *storage_struct_name) + .unwrap(); + + let mut generic_args = vec![]; + let mut storable_fields = vec![]; + let mut storable_fields_impl = vec![]; + + definition.fields.iter().enumerate().for_each(|(index, (field_ident, field_type))| { + storable_fields.push(format!("{}: dep::aztec::prelude::Storable", field_ident, index)); + generic_args.push(format!("N{}", index)); + storable_fields_impl.push(format!( + "{}: dep::aztec::prelude::Storable {{ slot: 0, typ: \"{}\" }}", + field_ident, + field_type.to_string().replace("plain::", "") + )); + }); + + let storage_fields_source = format!( + " + struct StorageLayout<{}> {{ + {} + }} + + #[abi(storage)] + global STORAGE_LAYOUT = StorageLayout {{ + {} + }}; + ", + generic_args.join(", "), + storable_fields.join(",\n"), + storable_fields_impl.join(",\n") + ); + + let (struct_ast, errors) = parse_program(&storage_fields_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (struct StorageLayout). This is either a bug in the compiler or the Noir macro code".to_string()), + span: None + }); + } + + let mut struct_ast = struct_ast.into_sorted(); + module.types.push(struct_ast.types.pop().unwrap()); + module.globals.push(struct_ast.globals.pop().unwrap()); + Ok(()) } diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index bdcbad646c2..eb8f5a4156d 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -67,6 +67,7 @@ pub fn mutable_assignment(name: &str, assigned_to: Expression) -> Statement { pattern: mutable(name), r#type: make_type(UnresolvedTypeData::Unspecified), expression: assigned_to, + attributes: vec![], })) } @@ -82,6 +83,7 @@ pub fn assignment(name: &str, assigned_to: Expression) -> Statement { pattern: pattern(name), r#type: make_type(UnresolvedTypeData::Unspecified), expression: assigned_to, + attributes: vec![], })) } diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 48186555eff..9aead1756f9 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -11,6 +11,9 @@ pub enum AztecMacroError { UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, + MultipleStorageDefinitions { span: Option }, + CouldNotExportStorageLayout { span: Option, secondary_message: Option }, + CouldNotExportFunctionAbi { span: Option, secondary_message: Option }, EventError { span: Span, message: String }, UnsupportedAttributes { span: Span, secondary_message: Option }, } @@ -46,6 +49,21 @@ impl From for MacroError { AztecMacroError::CouldNotImplementNoteInterface { span, secondary_message } => MacroError { primary_message: "Could not implement automatic methods for note, please provide an implementation of the NoteInterface trait".to_string(), secondary_message, + span + }, + AztecMacroError::MultipleStorageDefinitions { span } => MacroError { + primary_message: "Only one struct can be tagged as #[aztec(storage)]".to_string(), + secondary_message: None, + span, + }, + AztecMacroError::CouldNotExportStorageLayout { secondary_message, span } => MacroError { + primary_message: "Could not generate and export storage layout".to_string(), + secondary_message, + span, + }, + AztecMacroError::CouldNotExportFunctionAbi { secondary_message, span } => MacroError { + primary_message: "Could not generate and export function abi".to_string(), + secondary_message, span, }, AztecMacroError::EventError { span, message } => MacroError { diff --git a/aztec_macros/src/utils/hir_utils.rs b/aztec_macros/src/utils/hir_utils.rs index f31a0584261..c4414e6419b 100644 --- a/aztec_macros/src/utils/hir_utils.rs +++ b/aztec_macros/src/utils/hir_utils.rs @@ -1,22 +1,43 @@ use iter_extended::vecmap; +use noirc_errors::Location; use noirc_frontend::{ graph::CrateId, - hir::def_collector::dc_crate::UnresolvedTraitImpl, - macros_api::{HirContext, ModuleDefId, StructId}, - node_interner::{TraitId, TraitImplId}, - Signedness, Type, UnresolvedTypeData, + hir::{ + def_map::{LocalModuleId, ModuleId}, + resolution::{path_resolver::StandardPathResolver, resolver::Resolver}, + type_check::type_check_func, + }, + macros_api::{FileId, HirContext, ModuleDefId, StructId}, + node_interner::{FuncId, TraitId}, + ItemVisibility, LetStatement, NoirFunction, Shared, Signedness, StructType, Type, }; -pub fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec { +use super::ast_utils::is_custom_attribute; + +pub fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec<(String, StructId)> { context .def_map(crate_id) .expect("ICE: Missing crate in def_map") .modules() .iter() .flat_map(|(_, module)| { - module.type_definitions().filter_map(|typ| { + module.type_definitions().filter_map(move |typ| { if let ModuleDefId::TypeId(struct_id) = typ { - Some(struct_id) + let module_id = struct_id.module_id(); + let path = + context.fully_qualified_struct_path(context.root_crate_id(), struct_id); + let path = if path.contains("::") { + let prefix = if &module_id.krate == context.root_crate_id() { + "crate" + } else { + "dep" + }; + format!("{}::{}", prefix, path) + } else { + path + }; + + Some((path, struct_id)) } else { None } @@ -25,6 +46,16 @@ pub fn collect_crate_structs(crate_id: &CrateId, context: &HirContext) -> Vec Vec { + context + .def_map(crate_id) + .expect("ICE: Missing crate in def_map") + .modules() + .iter() + .flat_map(|(_, module)| module.value_definitions().filter_map(|id| id.as_function())) + .collect() +} + pub fn collect_traits(context: &HirContext) -> Vec { let crates = context.crates(); crates @@ -32,8 +63,8 @@ pub fn collect_traits(context: &HirContext) -> Vec { .flatten() .flat_map(|module| { module.type_definitions().filter_map(|typ| { - if let ModuleDefId::TraitId(struct_id) = typ { - Some(struct_id) + if let ModuleDefId::TraitId(trait_id) = typ { + Some(trait_id) } else { None } @@ -69,50 +100,127 @@ pub fn signature_of_type(typ: &Type) -> String { } } -// Fetches the name of all structs that implement trait_name, both in the current crate and all of its dependencies. -pub fn fetch_struct_trait_impls( - context: &mut HirContext, - unresolved_traits_impls: &[UnresolvedTraitImpl], - trait_name: &str, -) -> Vec { - let mut struct_typenames: Vec = Vec::new(); - - // These structs can be declared in either external crates or the current one. External crates that contain - // dependencies have already been processed and resolved, but are available here via the NodeInterner. Note that - // crates on which the current crate does not depend on may not have been processed, and will be ignored. - for trait_impl_id in 0..context.def_interner.next_trait_impl_id().0 { - let trait_impl = &context.def_interner.get_trait_implementation(TraitImplId(trait_impl_id)); - - if trait_impl.borrow().ident.0.contents == *trait_name { - if let Type::Struct(s, _) = &trait_impl.borrow().typ { - struct_typenames.push(s.borrow().name.0.contents.clone()); +// Fetches the name of all structs tagged as #[aztec(note)] in a given crate +pub fn fetch_crate_notes( + context: &HirContext, + crate_id: &CrateId, +) -> Vec<(String, Shared)> { + collect_crate_structs(crate_id, context) + .iter() + .filter_map(|(path, struct_id)| { + let r#struct = context.def_interner.get_struct(*struct_id); + let attributes = context.def_interner.struct_attributes(struct_id); + if attributes.iter().any(|attr| is_custom_attribute(attr, "aztec(note)")) { + Some((path.clone(), r#struct)) } else { - panic!("Found impl for {} on non-Struct", trait_name); + None } - } + }) + .collect() +} + +// Fetches the name of all structs tagged as #[aztec(note)], both in the current crate and all of its dependencies. +pub fn fetch_notes(context: &HirContext) -> Vec<(String, Shared)> { + context.crates().flat_map(|crate_id| fetch_crate_notes(context, &crate_id)).collect() +} + +pub fn get_contract_module_data( + context: &mut HirContext, + crate_id: &CrateId, +) -> Option<(LocalModuleId, FileId)> { + // We first fetch modules in this crate which correspond to contracts, along with their file id. + let contract_module_file_ids: Vec<(LocalModuleId, FileId)> = context + .def_map(crate_id) + .expect("ICE: Missing crate in def_map") + .modules() + .iter() + .filter(|(_, module)| module.is_contract) + .map(|(idx, module)| (LocalModuleId(idx), module.location.file)) + .collect(); + + // If the current crate does not contain a contract module we simply skip it. More than 1 contract in a crate is forbidden by the compiler + if contract_module_file_ids.is_empty() { + return None; } - // This crate's traits and impls have not yet been resolved, so we look for impls in unresolved_trait_impls. - struct_typenames.extend( - unresolved_traits_impls - .iter() - .filter(|trait_impl| { - trait_impl - .trait_path - .segments - .last() - .expect("ICE: empty trait_impl path") - .0 - .contents - == *trait_name - }) - .filter_map(|trait_impl| match &trait_impl.object_type.typ { - UnresolvedTypeData::Named(path, _, _) => { - Some(path.segments.last().unwrap().0.contents.clone()) - } - _ => None, - }), + Some(contract_module_file_ids[0]) +} + +pub fn inject_fn( + crate_id: &CrateId, + context: &mut HirContext, + func: NoirFunction, + location: Location, + module_id: LocalModuleId, + file_id: FileId, +) { + let func_id = context.def_interner.push_empty_fn(); + context.def_interner.push_function( + func_id, + &func.def, + ModuleId { krate: *crate_id, local_id: module_id }, + location, + ); + + context.def_map_mut(crate_id).unwrap().modules_mut()[module_id.0] + .declare_function(func.name_ident().clone(), ItemVisibility::Public, func_id) + .unwrap_or_else(|_| { + panic!( + "Failed to declare autogenerated {} function, likely due to a duplicate definition", + func.name() + ) + }); + + let def_maps = &mut context.def_maps; + + let path_resolver = + StandardPathResolver::new(ModuleId { local_id: module_id, krate: *crate_id }); + + let resolver = Resolver::new(&mut context.def_interner, &path_resolver, def_maps, file_id); + + let (hir_func, meta, _) = resolver.resolve_function(func, func_id); + + context.def_interner.push_fn_meta(meta, func_id); + context.def_interner.update_fn(func_id, hir_func); + + type_check_func(&mut context.def_interner, func_id); +} + +pub fn inject_global( + crate_id: &CrateId, + context: &mut HirContext, + global: LetStatement, + module_id: LocalModuleId, + file_id: FileId, +) { + let name = global.pattern.name_ident().clone(); + + let global_id = context.def_interner.push_empty_global( + name.clone(), + module_id, + file_id, + global.attributes.clone(), ); - struct_typenames + // Add the statement to the scope so its path can be looked up later + context.def_map_mut(crate_id).unwrap().modules_mut()[module_id.0] + .declare_global(name, global_id) + .unwrap_or_else(|(name, _)| { + panic!( + "Failed to declare autogenerated {} global, likely due to a duplicate definition", + name + ) + }); + + let def_maps = &mut context.def_maps; + + let path_resolver = + StandardPathResolver::new(ModuleId { local_id: module_id, krate: *crate_id }); + + let mut resolver = Resolver::new(&mut context.def_interner, &path_resolver, def_maps, file_id); + + let hir_stmt = resolver.resolve_global_let(global, global_id); + + let statement_id = context.def_interner.get_global(global_id).let_statement; + context.def_interner.replace_statement(statement_id, hir_stmt); } diff --git a/compiler/noirc_driver/src/abi_gen.rs b/compiler/noirc_driver/src/abi_gen.rs index 7fafa719186..86f10818dbc 100644 --- a/compiler/noirc_driver/src/abi_gen.rs +++ b/compiler/noirc_driver/src/abi_gen.rs @@ -2,10 +2,11 @@ use std::collections::BTreeMap; use acvm::acir::native_types::Witness; use iter_extended::{btree_map, vecmap}; -use noirc_abi::{Abi, AbiParameter, AbiReturnType, AbiType}; +use noirc_abi::{Abi, AbiParameter, AbiReturnType, AbiType, AbiValue}; use noirc_frontend::{ hir::Context, - hir_def::{function::Param, stmt::HirPattern}, + hir_def::{expr::HirArrayLiteral, function::Param, stmt::HirPattern}, + macros_api::{HirExpression, HirLiteral}, node_interner::{FuncId, NodeInterner}, Visibility, }; @@ -109,6 +110,60 @@ fn collapse_ranges(witnesses: &[Witness]) -> Vec> { wit } +pub(super) fn value_from_hir_expression(context: &Context, expression: HirExpression) -> AbiValue { + match expression { + HirExpression::Tuple(expr_ids) => { + let fields = expr_ids + .iter() + .map(|expr_id| { + value_from_hir_expression(context, context.def_interner.expression(expr_id)) + }) + .collect(); + AbiValue::Tuple { fields } + } + HirExpression::Constructor(constructor) => { + let fields = constructor + .fields + .iter() + .map(|(ident, expr_id)| { + ( + ident.0.contents.to_string(), + value_from_hir_expression( + context, + context.def_interner.expression(expr_id), + ), + ) + }) + .collect(); + AbiValue::Struct { fields } + } + HirExpression::Literal(literal) => match literal { + HirLiteral::Array(hir_array) => match hir_array { + HirArrayLiteral::Standard(expr_ids) => { + let value = expr_ids + .iter() + .map(|expr_id| { + value_from_hir_expression( + context, + context.def_interner.expression(expr_id), + ) + }) + .collect(); + AbiValue::Array { value } + } + _ => unreachable!("Repeated arrays cannot be used in the abi"), + }, + HirLiteral::Bool(value) => AbiValue::Boolean { value }, + HirLiteral::Str(value) => AbiValue::String { value }, + HirLiteral::Integer(field, sign) => { + AbiValue::Integer { value: field.to_string(), sign } + } + _ => unreachable!("Literal cannot be used in the abi"), + }, + _ => unreachable!("Type cannot be used in the abi {:?}", expression), + } +} + #[cfg(test)] mod test { use std::ops::Range; diff --git a/compiler/noirc_driver/src/contract.rs b/compiler/noirc_driver/src/contract.rs index bd78cb23cdc..a33a9b809d3 100644 --- a/compiler/noirc_driver/src/contract.rs +++ b/compiler/noirc_driver/src/contract.rs @@ -1,14 +1,20 @@ use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use acvm::acir::circuit::Program; use fm::FileId; -use noirc_abi::{Abi, ContractEvent}; +use noirc_abi::{Abi, AbiType, AbiValue}; use noirc_errors::debug_info::DebugInfo; use noirc_evaluator::errors::SsaReport; use super::debug::DebugFile; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CompiledContractOutputs { + pub structs: HashMap>, + pub globals: HashMap>, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CompiledContract { pub noir_version: String, @@ -19,10 +25,7 @@ pub struct CompiledContract { /// stored in this `Vector`. pub functions: Vec, - /// All the events defined inside the contract scope. - /// An event is a struct value that can be emitted via oracles - /// by any contract function during execution. - pub events: Vec, + pub outputs: CompiledContractOutputs, pub file_map: BTreeMap, pub warnings: Vec, diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 8fa2d9680c8..6fe44780484 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -3,11 +3,12 @@ #![warn(unreachable_pub)] #![warn(clippy::semicolon_if_nothing_returned)] +use abi_gen::value_from_hir_expression; use acvm::acir::circuit::ExpressionWidth; use clap::Args; use fm::{FileId, FileManager}; use iter_extended::vecmap; -use noirc_abi::{AbiParameter, AbiType, ContractEvent}; +use noirc_abi::{AbiParameter, AbiType, AbiValue}; use noirc_errors::{CustomDiagnostic, FileDiagnostic}; use noirc_evaluator::create_program; use noirc_evaluator::errors::RuntimeError; @@ -34,7 +35,7 @@ mod stdlib; use debug::filter_relevant_files; -pub use contract::{CompiledContract, ContractFunction}; +pub use contract::{CompiledContract, CompiledContractOutputs, ContractFunction}; pub use debug::DebugFile; pub use program::CompiledProgram; @@ -432,18 +433,51 @@ fn compile_contract_inner( let debug_infos: Vec<_> = functions.iter().map(|function| function.debug.clone()).collect(); let file_map = filter_relevant_files(&debug_infos, &context.file_manager); + let out_structs = contract + .outputs + .structs + .into_iter() + .map(|(tag, structs)| { + let structs = structs + .into_iter() + .map(|struct_id| { + let typ = context.def_interner.get_struct(struct_id); + let typ = typ.borrow(); + let fields = vecmap(typ.get_fields(&[]), |(name, typ)| { + (name, AbiType::from_type(context, &typ)) + }); + let path = + context.fully_qualified_struct_path(context.root_crate_id(), typ.id); + AbiType::Struct { path, fields } + }) + .collect(); + (tag.to_string(), structs) + }) + .collect(); + + let out_globals = contract + .outputs + .globals + .iter() + .map(|(tag, globals)| { + let globals: Vec = globals + .iter() + .map(|global_id| { + let let_statement = + context.def_interner.get_global_let_statement(*global_id).unwrap(); + let hir_expression = + context.def_interner.expression(&let_statement.expression); + value_from_hir_expression(context, hir_expression) + }) + .collect(); + (tag.to_string(), globals) + }) + .collect(); + Ok(CompiledContract { name: contract.name, - events: contract - .events - .iter() - .map(|event_id| { - let typ = context.def_interner.get_struct(*event_id); - let typ = typ.borrow(); - ContractEvent::from_struct_type(context, &typ) - }) - .collect(), functions, + outputs: CompiledContractOutputs { structs: out_structs, globals: out_globals }, file_map, noir_version: NOIR_ARTIFACT_VERSION_STRING.to_string(), warnings, diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index bcd62e3b062..53d9e2530cc 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1763,6 +1763,7 @@ impl AcirContext { id: u32, inputs: Vec, output_count: usize, + predicate: AcirVar, ) -> Result, RuntimeError> { let inputs = self.prepare_inputs_for_black_box_func_call(inputs)?; let inputs = inputs @@ -1778,7 +1779,8 @@ impl AcirContext { let results = vecmap(&outputs, |witness_index| self.add_data(AcirVarData::Witness(*witness_index))); - self.acir_ir.push_opcode(Opcode::Call { id, inputs, outputs }); + let predicate = Some(self.var_to_expression(predicate)?); + self.acir_ir.push_opcode(Opcode::Call { id, inputs, outputs, predicate }); Ok(results) } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs index 9e597dc8e9a..9f2cec5c949 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -579,6 +579,7 @@ impl Context { *acir_program_id, inputs, output_count, + self.current_side_effects_enabled_var, )?; let output_values = self.convert_vars_to_values(output_vars, dfg, result_ids); @@ -2716,7 +2717,7 @@ mod test { expected_outputs: Vec, ) { match opcode { - Opcode::Call { id, inputs, outputs } => { + Opcode::Call { id, inputs, outputs, .. } => { assert_eq!( *id, expected_id, "Main was expected to call {expected_id} but got {}", diff --git a/compiler/noirc_frontend/src/ast/statement.rs b/compiler/noirc_frontend/src/ast/statement.rs index dea9fc0f3d3..753b5a31d32 100644 --- a/compiler/noirc_frontend/src/ast/statement.rs +++ b/compiler/noirc_frontend/src/ast/statement.rs @@ -2,6 +2,7 @@ use std::fmt::Display; use std::sync::atomic::{AtomicU32, Ordering}; use crate::lexer::token::SpannedToken; +use crate::macros_api::SecondaryAttribute; use crate::parser::{ParserError, ParserErrorReason}; use crate::token::Token; use crate::{ @@ -107,7 +108,7 @@ impl StatementKind { pub fn new_let( ((pattern, r#type), expression): ((Pattern, UnresolvedType), Expression), ) -> StatementKind { - StatementKind::Let(LetStatement { pattern, r#type, expression }) + StatementKind::Let(LetStatement { pattern, r#type, expression, attributes: vec![] }) } /// Create a Statement::Assign value, desugaring any combined operators like += if needed. @@ -405,13 +406,17 @@ pub struct LetStatement { pub pattern: Pattern, pub r#type: UnresolvedType, pub expression: Expression, + pub attributes: Vec, } impl LetStatement { pub fn new_let( - ((pattern, r#type), expression): ((Pattern, UnresolvedType), Expression), + (((pattern, r#type), expression), attributes): ( + ((Pattern, UnresolvedType), Expression), + Vec, + ), ) -> LetStatement { - LetStatement { pattern, r#type, expression } + LetStatement { pattern, r#type, expression, attributes } } } @@ -568,6 +573,7 @@ impl ForRange { pattern: Pattern::Identifier(array_ident.clone()), r#type: UnresolvedType::unspecified(), expression: array, + attributes: vec![], }), span: array_span, }; @@ -610,6 +616,7 @@ impl ForRange { pattern: Pattern::Identifier(identifier), r#type: UnresolvedType::unspecified(), expression: Expression::new(loop_element, array_span), + attributes: vec![], }), span: array_span, }; diff --git a/compiler/noirc_frontend/src/debug/mod.rs b/compiler/noirc_frontend/src/debug/mod.rs index 71e0d44b478..67b52071d7b 100644 --- a/compiler/noirc_frontend/src/debug/mod.rs +++ b/compiler/noirc_frontend/src/debug/mod.rs @@ -145,6 +145,7 @@ impl DebugInstrumenter { pattern: ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)), r#type: ast::UnresolvedType::unspecified(), expression: ret_expr.clone(), + attributes: vec![], }), span: ret_expr.span, }; @@ -248,6 +249,7 @@ impl DebugInstrumenter { }), span: let_stmt.expression.span, }, + attributes: vec![], }), span: *span, } @@ -273,6 +275,7 @@ impl DebugInstrumenter { pattern: ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)), r#type: ast::UnresolvedType::unspecified(), expression: assign_stmt.expression.clone(), + attributes: vec![], }); let expression_span = assign_stmt.expression.span; let new_assign_stmt = match &assign_stmt.lvalue { diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index 90aa4baee7c..463b8a4b329 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -256,20 +256,6 @@ impl DefCollector { // Add the current crate to the collection of DefMaps context.def_maps.insert(crate_id, def_collector.def_map); - // TODO(#4653): generalize this function - for macro_processor in macro_processors { - macro_processor - .process_collected_defs( - &crate_id, - context, - &def_collector.collected_traits_impls, - &mut def_collector.collected_functions, - ) - .unwrap_or_else(|(macro_err, file_id)| { - errors.push((macro_err.into(), file_id)); - }); - } - inject_prelude(crate_id, context, crate_root, &mut def_collector.collected_imports); for submodule in submodules { inject_prelude( diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index fcb20c740c7..6fbb3b67546 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -102,8 +102,12 @@ impl<'a> ModCollector<'a> { for global in globals { let name = global.pattern.name_ident().clone(); - let global_id = - context.def_interner.push_empty_global(name.clone(), self.module_id, self.file_id); + let global_id = context.def_interner.push_empty_global( + name.clone(), + self.module_id, + self.file_id, + global.attributes.clone(), + ); // Add the statement to the scope so its path can be looked up later let result = self.def_collector.def_map.modules[self.module_id.0] @@ -455,6 +459,7 @@ impl<'a> ModCollector<'a> { name.clone(), trait_id.0.local_id, self.file_id, + vec![], ); if let Err((first_def, second_def)) = self.def_collector.def_map.modules diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 157227f763e..7c0090ff95b 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -2,13 +2,13 @@ use crate::graph::CrateId; use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::hir::Context; use crate::macros_api::MacroProcessor; -use crate::node_interner::{FuncId, NodeInterner, StructId}; +use crate::node_interner::{FuncId, GlobalId, NodeInterner, StructId}; use crate::parser::{parse_program, ParsedModule, ParserError}; use crate::token::{FunctionAttribute, SecondaryAttribute, TestScope}; use arena::{Arena, Index}; use fm::{FileId, FileManager}; use noirc_errors::Location; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; mod module_def; pub use module_def::*; mod item_scope; @@ -217,20 +217,37 @@ impl CrateDefMap { }) .collect(); - let events = module - .type_definitions() - .filter_map(|id| { - id.as_type().filter(|struct_id| { - interner - .struct_attributes(struct_id) - .iter() - .any(|attr| attr == &SecondaryAttribute::Event) - }) - }) - .collect(); + let mut outputs = + ContractOutputs { structs: HashMap::new(), globals: HashMap::new() }; + + interner.get_all_globals().iter().for_each(|global_info| { + interner.global_attributes(&global_info.id).iter().for_each(|attr| { + if let SecondaryAttribute::Abi(tag) = attr { + if let Some(tagged) = outputs.globals.get_mut(tag) { + tagged.push(global_info.id); + } else { + outputs.globals.insert(tag.to_string(), vec![global_info.id]); + } + } + }); + }); + + module.type_definitions().for_each(|id| { + if let ModuleDefId::TypeId(struct_id) = id { + interner.struct_attributes(&struct_id).iter().for_each(|attr| { + if let SecondaryAttribute::Abi(tag) = attr { + if let Some(tagged) = outputs.structs.get_mut(tag) { + tagged.push(struct_id); + } else { + outputs.structs.insert(tag.to_string(), vec![struct_id]); + } + } + }); + } + }); let name = self.get_module_path(id, module.parent); - Some(Contract { name, location: module.location, functions, events }) + Some(Contract { name, location: module.location, functions, outputs }) } else { None } @@ -283,6 +300,11 @@ pub struct ContractFunctionMeta { pub is_entry_point: bool, } +pub struct ContractOutputs { + pub structs: HashMap>, + pub globals: HashMap>, +} + /// A 'contract' in Noir source code with a given name, functions and events. /// This is not an AST node, it is just a convenient form to return for CrateDefMap::get_all_contracts. pub struct Contract { @@ -290,7 +312,7 @@ pub struct Contract { pub name: String, pub location: Location, pub functions: Vec, - pub events: Vec, + pub outputs: ContractOutputs, } /// Given a FileId, fetch the File, from the FileManager and parse it's content diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 00bcb0cdebf..727a6596df1 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -26,7 +26,7 @@ pub type ParsedFiles = HashMap)>; pub struct Context<'file_manager, 'parsed_files> { pub def_interner: NodeInterner, pub crate_graph: CrateGraph, - pub(crate) def_maps: BTreeMap, + pub def_maps: BTreeMap, // In the WASM context, we take ownership of the file manager, // which is why this needs to be a Cow. In all use-cases, the file manager // is read-only however, once it has been passed to the Context. diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index d5b0c612f90..71e3f3482fc 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -76,6 +76,8 @@ pub enum ResolverError { NestedSlices { span: Span }, #[error("#[recursive] attribute is only allowed on entry points to a program")] MisplacedRecursiveAttribute { ident: Ident }, + #[error("#[abi(tag)] attribute is only allowed in contracts")] + AbiAttributeOusideContract { span: Span }, #[error("Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library")] LowLevelFunctionOutsideOfStdlib { ident: Ident }, #[error("Dependency cycle found, '{item}' recursively depends on itself: {cycle} ")] @@ -303,6 +305,13 @@ impl From for Diagnostic { diag.add_note("The `#[recursive]` attribute specifies to the backend whether it should use a prover which generates proofs that are friendly for recursive verification in another circuit".to_owned()); diag } + ResolverError::AbiAttributeOusideContract { span } => { + Diagnostic::simple_error( + "#[abi(tag)] attributes can only be used in contracts".to_string(), + "misplaced #[abi(tag)] attribute".to_string(), + span, + ) + }, ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index f2b8212db7a..08b12069d76 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -19,6 +19,7 @@ use crate::hir_def::expr::{ }; use crate::hir_def::traits::{Trait, TraitConstraint}; +use crate::macros_api::SecondaryAttribute; use crate::token::{Attributes, FunctionAttribute}; use regex::Regex; use std::collections::{BTreeMap, HashSet}; @@ -617,7 +618,17 @@ impl<'a> Resolver<'a> { match self.lookup_struct_or_error(path) { Some(struct_type) => { let expected_generic_count = struct_type.borrow().generics.len(); - + if !self.in_contract + && self + .interner + .struct_attributes(&struct_type.borrow().id) + .iter() + .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) + { + self.push_err(ResolverError::AbiAttributeOusideContract { + span: struct_type.borrow().name.span(), + }); + } self.verify_generics_count(expected_generic_count, &mut args, span, || { struct_type.borrow().to_string() }); @@ -1167,10 +1178,19 @@ impl<'a> Resolver<'a> { let global_id = self.interner.next_global_id(); let definition = DefinitionKind::Global(global_id); + if !self.in_contract + && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) + { + self.push_err(ResolverError::AbiAttributeOusideContract { + span: let_stmt.pattern.span(), + }); + } + HirStatement::Let(HirLetStatement { pattern: self.resolve_pattern(let_stmt.pattern, definition), r#type: self.resolve_type(let_stmt.r#type), expression, + attributes: let_stmt.attributes, }) } @@ -1183,6 +1203,7 @@ impl<'a> Resolver<'a> { pattern: self.resolve_pattern(let_stmt.pattern, definition), r#type: self.resolve_type(let_stmt.r#type), expression, + attributes: let_stmt.attributes, }) } StatementKind::Constrain(constrain_stmt) => { diff --git a/compiler/noirc_frontend/src/hir/type_check/mod.rs b/compiler/noirc_frontend/src/hir/type_check/mod.rs index 926dac30bcd..cdfc19b3a33 100644 --- a/compiler/noirc_frontend/src/hir/type_check/mod.rs +++ b/compiler/noirc_frontend/src/hir/type_check/mod.rs @@ -504,6 +504,7 @@ mod test { pattern: Identifier(z), r#type: Type::FieldElement, expression: expr_id, + attributes: vec![], }; let stmt_id = interner.push_stmt(HirStatement::Let(let_stmt)); let expr_id = interner diff --git a/compiler/noirc_frontend/src/hir_def/stmt.rs b/compiler/noirc_frontend/src/hir_def/stmt.rs index c5e287b393c..4c9a33d3dc0 100644 --- a/compiler/noirc_frontend/src/hir_def/stmt.rs +++ b/compiler/noirc_frontend/src/hir_def/stmt.rs @@ -1,4 +1,5 @@ use super::expr::HirIdent; +use crate::macros_api::SecondaryAttribute; use crate::node_interner::ExprId; use crate::{Ident, Type}; use fm::FileId; @@ -26,6 +27,7 @@ pub struct HirLetStatement { pub pattern: HirPattern, pub r#type: Type, pub expression: ExprId, + pub attributes: Vec, } impl HirLetStatement { diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index f8378cdd84b..357b1ead593 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -510,6 +510,7 @@ impl Attribute { Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod) } ["event"] => Attribute::Secondary(SecondaryAttribute::Event), + ["abi", tag] => Attribute::Secondary(SecondaryAttribute::Abi(tag.to_string())), ["export"] => Attribute::Secondary(SecondaryAttribute::Export), ["deprecated", name] => { if !name.starts_with('"') && !name.ends_with('"') { @@ -604,6 +605,7 @@ pub enum SecondaryAttribute { Export, Field(String), Custom(String), + Abi(String), } impl fmt::Display for SecondaryAttribute { @@ -618,6 +620,7 @@ impl fmt::Display for SecondaryAttribute { SecondaryAttribute::Event => write!(f, "#[event]"), SecondaryAttribute::Export => write!(f, "#[export]"), SecondaryAttribute::Field(ref k) => write!(f, "#[field({k})]"), + SecondaryAttribute::Abi(ref k) => write!(f, "#[abi({k})]"), } } } @@ -640,7 +643,9 @@ impl AsRef for SecondaryAttribute { match self { SecondaryAttribute::Deprecated(Some(string)) => string, SecondaryAttribute::Deprecated(None) => "", - SecondaryAttribute::Custom(string) | SecondaryAttribute::Field(string) => string, + SecondaryAttribute::Custom(string) + | SecondaryAttribute::Field(string) + | SecondaryAttribute::Abi(string) => string, SecondaryAttribute::ContractLibraryMethod => "", SecondaryAttribute::Event | SecondaryAttribute::Export => "", } diff --git a/compiler/noirc_frontend/src/lib.rs b/compiler/noirc_frontend/src/lib.rs index 6ce6f4325e4..93d7960faf5 100644 --- a/compiler/noirc_frontend/src/lib.rs +++ b/compiler/noirc_frontend/src/lib.rs @@ -45,7 +45,6 @@ pub mod macros_api { pub use noirc_errors::Span; pub use crate::graph::CrateId; - use crate::hir::def_collector::dc_crate::{UnresolvedFunctions, UnresolvedTraitImpl}; pub use crate::hir::def_collector::errors::MacroError; pub use crate::hir_def::expr::{HirExpression, HirLiteral}; pub use crate::hir_def::stmt::HirStatement; @@ -76,15 +75,6 @@ pub mod macros_api { context: &HirContext, ) -> Result; - // TODO(#4653): generalize this function - fn process_collected_defs( - &self, - _crate_id: &CrateId, - _context: &mut HirContext, - _collected_trait_impls: &[UnresolvedTraitImpl], - _collected_functions: &mut [UnresolvedFunctions], - ) -> Result<(), (MacroError, FileId)>; - /// Function to manipulate the AST after type checking has been completed. /// The AST after type checking has been done is called the HIR. fn process_typed_ast( diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index dcfceccdb57..ffd760d6d7f 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -146,6 +146,7 @@ pub struct NodeInterner { // Maps GlobalId -> GlobalInfo // NOTE: currently only used for checking repeat globals and restricting their scope to a module globals: Vec, + global_attributes: HashMap>, next_type_variable_id: std::cell::Cell, @@ -480,6 +481,7 @@ impl Default for NodeInterner { field_indices: HashMap::new(), next_type_variable_id: std::cell::Cell::new(0), globals: Vec::new(), + global_attributes: HashMap::new(), struct_methods: HashMap::new(), primitive_methods: HashMap::new(), type_alias_ref: Vec::new(), @@ -647,11 +649,13 @@ impl NodeInterner { local_id: LocalModuleId, let_statement: StmtId, file: FileId, + attributes: Vec, ) -> GlobalId { let id = GlobalId(self.globals.len()); let location = Location::new(ident.span(), file); let name = ident.to_string(); let definition_id = self.push_definition(name, false, DefinitionKind::Global(id), location); + self.globals.push(GlobalInfo { id, definition_id, @@ -660,6 +664,7 @@ impl NodeInterner { let_statement, location, }); + self.global_attributes.insert(id, attributes); id } @@ -673,9 +678,10 @@ impl NodeInterner { name: Ident, local_id: LocalModuleId, file: FileId, + attributes: Vec, ) -> GlobalId { let statement = self.push_stmt(HirStatement::Error); - self.push_global(name, local_id, statement, file) + self.push_global(name, local_id, statement, file, attributes) } /// Intern an empty function. @@ -838,6 +844,10 @@ impl NodeInterner { &self.struct_attributes[struct_id] } + pub fn global_attributes(&self, global_id: &GlobalId) -> &[SecondaryAttribute] { + &self.global_attributes[global_id] + } + /// Returns the interned statement corresponding to `stmt_id` pub fn statement(&self, stmt_id: &StmtId) -> HirStatement { let def = diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index cdfa16400ae..0a21465fe87 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -158,14 +158,17 @@ fn implementation() -> impl NoirParser { /// global_declaration: 'global' ident global_type_annotation '=' literal fn global_declaration() -> impl NoirParser { - let p = ignore_then_commit( - keyword(Keyword::Global).labelled(ParsingRuleLabel::Global), - ident().map(Pattern::Identifier), - ); + let p = attributes::attributes() + .then_ignore(keyword(Keyword::Global).labelled(ParsingRuleLabel::Global)) + .then(ident().map(Pattern::Identifier)); let p = then_commit(p, optional_type_annotation()); let p = then_commit_ignore(p, just(Token::Assign)); let p = then_commit(p, expression()); - p.map(LetStatement::new_let).map(TopLevelStatement::Global) + p.validate(|(((attributes, pattern), r#type), expression), span, emit| { + let global_attributes = attributes::validate_secondary_attributes(attributes, span, emit); + LetStatement { pattern, r#type, expression, attributes: global_attributes } + }) + .map(TopLevelStatement::Global) } /// submodule: 'mod' ident '{' module '}' diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 4b256a95c8b..47add6f82e0 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -2,6 +2,7 @@ use chumsky::Parser; use noirc_errors::Span; use crate::{ + macros_api::SecondaryAttribute, parser::{NoirParser, ParserError, ParserErrorReason}, token::{Attribute, Attributes, Token, TokenKind}, }; @@ -44,3 +45,25 @@ pub(super) fn validate_attributes( Attributes { function: primary, secondary } } + +pub(super) fn validate_secondary_attributes( + attributes: Vec, + span: Span, + emit: &mut dyn FnMut(ParserError), +) -> Vec { + let mut struct_attributes = vec![]; + + for attribute in attributes { + match attribute { + Attribute::Function(..) => { + emit(ParserError::with_reason( + ParserErrorReason::NoFunctionAttributesAllowedOnStruct, + span, + )); + } + Attribute::Secondary(attr) => struct_attributes.push(attr), + } + } + + struct_attributes +} diff --git a/compiler/noirc_frontend/src/parser/parser/structs.rs b/compiler/noirc_frontend/src/parser/parser/structs.rs index 0212f56783f..87e58f69efb 100644 --- a/compiler/noirc_frontend/src/parser/parser/structs.rs +++ b/compiler/noirc_frontend/src/parser/parser/structs.rs @@ -1,17 +1,15 @@ use chumsky::prelude::*; -use noirc_errors::Span; use crate::{ - macros_api::SecondaryAttribute, parser::{ parser::{ - attributes::attributes, + attributes::{attributes, validate_secondary_attributes}, function, parse_type, primitives::{ident, keyword}, }, - NoirParser, ParserError, ParserErrorReason, TopLevelStatement, + NoirParser, TopLevelStatement, }, - token::{Attribute, Keyword, Token}, + token::{Keyword, Token}, Ident, NoirStruct, UnresolvedType, }; @@ -35,7 +33,7 @@ pub(super) fn struct_definition() -> impl NoirParser { .then(function::generics()) .then(fields) .validate(|(((raw_attributes, name), generics), fields), span, emit| { - let attributes = validate_struct_attributes(raw_attributes, span, emit); + let attributes = validate_secondary_attributes(raw_attributes, span, emit); TopLevelStatement::Struct(NoirStruct { name, attributes, generics, fields, span }) }) } @@ -48,28 +46,6 @@ fn struct_fields() -> impl NoirParser> { .allow_trailing() } -fn validate_struct_attributes( - attributes: Vec, - span: Span, - emit: &mut dyn FnMut(ParserError), -) -> Vec { - let mut struct_attributes = vec![]; - - for attribute in attributes { - match attribute { - Attribute::Function(..) => { - emit(ParserError::with_reason( - ParserErrorReason::NoFunctionAttributesAllowedOnStruct, - span, - )); - } - Attribute::Secondary(attr) => struct_attributes.push(attr), - } - } - - struct_attributes -} - #[cfg(test)] mod test { use super::*; diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index 9e6fca1126e..de157a1fe20 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -30,11 +30,16 @@ export type DependencyGraph = { library_dependencies: Readonly>; } +export type ContractOutputsArtifact = { + structs: Record>; + globals: Record>; +} + export type ContractArtifact = { noir_version: string; name: string; functions: Array; - events: Array; + outputs: ContractOutputsArtifact; file_map: Record; }; @@ -218,7 +223,7 @@ pub fn compile_contract( noir_version: String::from(NOIR_ARTIFACT_VERSION_STRING), name: optimized_contract.name, functions, - events: optimized_contract.events, + outputs: optimized_contract.outputs.into(), file_map: optimized_contract.file_map, }; diff --git a/compiler/wasm/src/compile_new.rs b/compiler/wasm/src/compile_new.rs index d6b382f669f..c187fe7f3de 100644 --- a/compiler/wasm/src/compile_new.rs +++ b/compiler/wasm/src/compile_new.rs @@ -146,7 +146,7 @@ impl CompilerContext { noir_version: String::from(NOIR_ARTIFACT_VERSION_STRING), name: optimized_contract.name, functions, - events: optimized_contract.events, + outputs: optimized_contract.outputs.into(), file_map: optimized_contract.file_map, }; diff --git a/compiler/wasm/src/types/noir_artifact.ts b/compiler/wasm/src/types/noir_artifact.ts index 935c99043da..f241b539dc7 100644 --- a/compiler/wasm/src/types/noir_artifact.ts +++ b/compiler/wasm/src/types/noir_artifact.ts @@ -1,35 +1,55 @@ import { Abi, AbiType } from '@noir-lang/types'; /** - * A named type. + * A basic value. */ -export interface ABIVariable { +export interface BasicValue { /** - * The name of the variable. - */ - name: string; - /** - * The type of the variable. + * The kind of the value. */ - type: AbiType; + kind: T; + value: V; } /** - * A contract event. + * An exported value. */ -export interface EventAbi { +export type AbiValue = + | BasicValue<'boolean', boolean> + | BasicValue<'string', string> + | BasicValue<'array', AbiValue[]> + | TupleValue + | IntegerValue + | StructValue; + +export type TypedStructFieldValue = { name: string; value: T }; + +export interface StructValue { + kind: 'struct'; + fields: TypedStructFieldValue[]; +} + +export interface TupleValue { + kind: 'tuple'; + fields: AbiValue[]; +} + +export interface IntegerValue extends BasicValue<'integer', string> { + sign: boolean; +} + +/** + * A named type. + */ +export interface ABIVariable { /** - * The event name. + * The name of the variable. */ name: string; /** - * Fully qualified name of the event. - */ - path: string; - /** - * The fields of the event. + * The type of the variable. */ - fields: ABIVariable[]; + type: AbiType; } /** @@ -60,8 +80,11 @@ export interface ContractArtifact { noir_version: string; /** The functions of the contract. */ functions: NoirFunctionEntry[]; - /** The events of the contract */ - events: EventAbi[]; + + outputs: { + structs: Record; + globals: Record; + }; /** The map of file ID to the source code and path of the file. */ file_map: DebugFileMap; } diff --git a/package.json b/package.json index 3cffdf4c802..8abaced7bdd 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "docs" ], "scripts": { - "build": "yarn workspaces foreach --parallel --topological-dev --verbose run build", + "build": "yarn workspaces foreach -vp --topological-dev --exclude \"{docs,@noir-lang/root}\" run build", "test": "yarn workspaces foreach --parallel --verbose run test", "test:integration": "yarn workspace integration-tests test", "clean:workspaces": "yarn workspaces foreach --exclude @noir-lang/root run clean", diff --git a/test_programs/execution_success/fold_call_witness_condition/Prover.toml b/test_programs/execution_success/fold_call_witness_condition/Prover.toml index 8481ce25648..a4d6339b661 100644 --- a/test_programs/execution_success/fold_call_witness_condition/Prover.toml +++ b/test_programs/execution_success/fold_call_witness_condition/Prover.toml @@ -1,5 +1,3 @@ -# TODO(https://github.com/noir-lang/noir/issues/4707): Change these inputs to fail the assertion in `fn return_value` -# and change `enable` to false. For now we need the inputs to pass as we do not handle predicates with ACIR calls -x = "5" +x = "10" y = "10" -enable = true \ No newline at end of file +enable = false diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index 52f7783851a..e055d7a3a5f 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.32.0"; +const VERSION: &str = "0.33.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/nargo/src/artifacts/contract.rs b/tooling/nargo/src/artifacts/contract.rs index c0316a6d1a2..868fb4404fd 100644 --- a/tooling/nargo/src/artifacts/contract.rs +++ b/tooling/nargo/src/artifacts/contract.rs @@ -1,14 +1,26 @@ use acvm::acir::circuit::Program; -use noirc_abi::{Abi, ContractEvent}; -use noirc_driver::{CompiledContract, ContractFunction}; +use noirc_abi::{Abi, AbiType, AbiValue}; +use noirc_driver::{CompiledContract, CompiledContractOutputs, ContractFunction}; use serde::{Deserialize, Serialize}; use noirc_driver::DebugFile; use noirc_errors::debug_info::DebugInfo; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use fm::FileId; +#[derive(Serialize, Deserialize)] +pub struct ContractOutputsArtifact { + pub structs: HashMap>, + pub globals: HashMap>, +} + +impl From for ContractOutputsArtifact { + fn from(outputs: CompiledContractOutputs) -> Self { + ContractOutputsArtifact { structs: outputs.structs, globals: outputs.globals } + } +} + #[derive(Serialize, Deserialize)] pub struct ContractArtifact { /// Version of noir used to compile this contract @@ -17,8 +29,8 @@ pub struct ContractArtifact { pub name: String, /// Each of the contract's functions are compiled into a separate program stored in this `Vec`. pub functions: Vec, - /// All the events defined inside the contract scope. - pub events: Vec, + + pub outputs: ContractOutputsArtifact, /// Map of file Id to the source code so locations in debug info can be mapped to source code they point to. pub file_map: BTreeMap, } @@ -29,7 +41,7 @@ impl From for ContractArtifact { noir_version: contract.noir_version, name: contract.name, functions: contract.functions.into_iter().map(ContractFunctionArtifact::from).collect(), - events: contract.events, + outputs: contract.outputs.into(), file_map: contract.file_map, } } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index 11ef9248853..e592ed440ee 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.32.0", + "@aztec/bb.js": "0.33.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index d0dcb373963..89a60b0ed26 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -10,9 +10,7 @@ use acvm::{ use errors::AbiError; use input_parser::InputValue; use iter_extended::{try_btree_map, try_vecmap, vecmap}; -use noirc_frontend::{ - hir::Context, Signedness, StructType, Type, TypeBinding, TypeVariableKind, Visibility, -}; +use noirc_frontend::{hir::Context, Signedness, Type, TypeBinding, TypeVariableKind, Visibility}; use serde::{Deserialize, Serialize}; use std::ops::Range; use std::{collections::BTreeMap, str}; @@ -515,31 +513,35 @@ fn decode_string_value(field_elements: &[FieldElement]) -> String { final_string.to_owned() } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ContractEvent { - /// Event name - name: String, - /// The fully qualified path to the event definition - path: String, - - /// Fields of the event - #[serde( - serialize_with = "serialization::serialize_struct_fields", - deserialize_with = "serialization::deserialize_struct_fields" - )] - fields: Vec<(String, AbiType)>, -} - -impl ContractEvent { - pub fn from_struct_type(context: &Context, struct_type: &StructType) -> Self { - let fields = vecmap(struct_type.get_fields(&[]), |(name, typ)| { - (name, AbiType::from_type(context, &typ)) - }); - // For the ABI, we always want to resolve the struct paths from the root crate - let path = context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id); - - Self { name: struct_type.name.0.contents.clone(), path, fields } - } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(tag = "kind", rename_all = "lowercase")] +pub enum AbiValue { + Field { + value: FieldElement, + }, + Integer { + sign: bool, + value: String, + }, + Boolean { + value: bool, + }, + String { + value: String, + }, + Array { + value: Vec, + }, + Struct { + #[serde( + serialize_with = "serialization::serialize_struct_field_values", + deserialize_with = "serialization::deserialize_struct_field_values" + )] + fields: Vec<(String, AbiValue)>, + }, + Tuple { + fields: Vec, + }, } fn range_to_vec(ranges: &[Range]) -> Vec { diff --git a/tooling/noirc_abi/src/serialization.rs b/tooling/noirc_abi/src/serialization.rs index ed838803fab..4f91d9b7dfd 100644 --- a/tooling/noirc_abi/src/serialization.rs +++ b/tooling/noirc_abi/src/serialization.rs @@ -1,8 +1,7 @@ +use crate::{AbiType, AbiValue}; use iter_extended::vecmap; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::AbiType; - // This module exposes a custom serializer and deserializer for `BTreeMap` // (representing the fields of a struct) to serialize it as a `Vec`. // @@ -41,6 +40,37 @@ where Ok(vecmap(fields_vector, |StructField { name, typ }| (name, typ))) } +#[derive(Serialize, Deserialize)] +struct StructFieldValue { + name: String, + value: AbiValue, +} + +pub(crate) fn serialize_struct_field_values( + fields: &[(String, AbiValue)], + s: S, +) -> Result +where + S: Serializer, +{ + let fields_vector = vecmap(fields, |(name, value)| StructFieldValue { + name: name.to_owned(), + value: value.to_owned(), + }); + + fields_vector.serialize(s) +} + +pub(crate) fn deserialize_struct_field_values<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let fields_vector = Vec::::deserialize(deserializer)?; + Ok(vecmap(fields_vector, |StructFieldValue { name, value }| (name, value))) +} + #[cfg(test)] mod tests { use crate::{AbiParameter, AbiType, AbiVisibility, Sign}; diff --git a/tooling/noirc_abi_wasm/build.sh b/tooling/noirc_abi_wasm/build.sh index fe0b4dcbfff..4486a214c9c 100755 --- a/tooling/noirc_abi_wasm/build.sh +++ b/tooling/noirc_abi_wasm/build.sh @@ -14,6 +14,13 @@ function run_or_fail { exit $status fi } +function run_if_available { + if command -v "$1" >/dev/null 2>&1; then + "$@" + else + echo "$1 is not installed. Please install it to use this feature." >&2 + fi +} require_command jq require_command cargo diff --git a/yarn.lock b/yarn.lock index a39ae9921da..0fdad4ad2ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.32.0": - version: 0.32.0 - resolution: "@aztec/bb.js@npm:0.32.0" +"@aztec/bb.js@npm:0.33.0": + version: 0.33.0 + resolution: "@aztec/bb.js@npm:0.33.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 0919957e141ae0a65cfab961dce122fa06de628a10b7cb661d31d8ed4793ce80980fcf315620ceffffa45581db941bad43c392f4b2aa9becaaf7d2faaba01ffc + checksum: 16244a52ef1cb5efca582e863a3521d04f0fb66b02cd584b904e6e65f684e392eec56679439d1a831127e126d117bf0e116166fc4b24efdd6e1ebe9097efed06 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.32.0 + "@aztec/bb.js": 0.33.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From f14d913768294009c264dfaff364e325f33c6e18 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:50:00 +0100 Subject: [PATCH 167/416] chore: create placeholder version of 0.26.0 docs (#4782) # Description ## Problem\* Resolves ## Summary\* This PR copies the 0.27.0 docs into the 0.26.0 directory so that the build passes again. I went for this route as for some reason the 0.26.0 commit is not building for me. cc @noir-lang/developerrelations as there may be changes between these two versions which won't be reflected in these docs. ## Additional Context ## Documentation\* Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [ ] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../explainers/explainer-oracle.md | 57 ++ .../explainers/explainer-recursion.md | 176 ++++++ .../getting_started/_category_.json | 5 + .../hello_noir/_category_.json | 5 + .../getting_started/hello_noir/index.md | 142 +++++ .../hello_noir/project_breakdown.md | 199 ++++++ .../installation/_category_.json | 6 + .../getting_started/installation/index.md | 48 ++ .../installation/other_install_methods.md | 102 ++++ .../getting_started/tooling/_category_.json | 6 + .../getting_started/tooling/index.mdx | 38 ++ .../tooling/language_server.md | 43 ++ .../getting_started/tooling/noir_codegen.md | 113 ++++ .../getting_started/tooling/testing.md | 62 ++ .../version-v0.26.0/how_to/_category_.json | 5 + .../version-v0.26.0/how_to/how-to-oracles.md | 276 +++++++++ .../how_to/how-to-recursion.md | 179 ++++++ .../how_to/how-to-solidity-verifier.md | 231 +++++++ .../version-v0.26.0/how_to/merkle-proof.mdx | 48 ++ .../how_to/using-devcontainers.mdx | 110 ++++ docs/versioned_docs/version-v0.26.0/index.mdx | 67 +++ .../version-v0.26.0/migration_notes.md | 105 ++++ .../noir/concepts/_category_.json | 6 + .../version-v0.26.0/noir/concepts/assert.md | 45 ++ .../version-v0.26.0/noir/concepts/comments.md | 33 + .../noir/concepts/control_flow.md | 77 +++ .../version-v0.26.0/noir/concepts/data_bus.md | 21 + .../noir/concepts/data_types/_category_.json | 5 + .../noir/concepts/data_types/arrays.md | 251 ++++++++ .../noir/concepts/data_types/booleans.md | 31 + .../noir/concepts/data_types/fields.md | 192 ++++++ .../concepts/data_types/function_types.md | 26 + .../noir/concepts/data_types/index.md | 110 ++++ .../noir/concepts/data_types/integers.md | 155 +++++ .../noir/concepts/data_types/references.md | 23 + .../noir/concepts/data_types/slices.mdx | 170 ++++++ .../noir/concepts/data_types/strings.md | 80 +++ .../noir/concepts/data_types/structs.md | 70 +++ .../noir/concepts/data_types/tuples.md | 48 ++ .../version-v0.26.0/noir/concepts/distinct.md | 64 ++ .../noir/concepts/functions.md | 226 +++++++ .../version-v0.26.0/noir/concepts/generics.md | 106 ++++ .../version-v0.26.0/noir/concepts/globals.md | 72 +++ .../version-v0.26.0/noir/concepts/lambdas.md | 81 +++ .../noir/concepts/mutability.md | 121 ++++ .../version-v0.26.0/noir/concepts/ops.md | 98 +++ .../version-v0.26.0/noir/concepts/oracles.md | 23 + .../noir/concepts/shadowing.md | 44 ++ .../version-v0.26.0/noir/concepts/traits.md | 389 ++++++++++++ .../noir/concepts/unconstrained.md | 99 +++ .../modules_packages_crates/_category_.json | 6 + .../crates_and_packages.md | 43 ++ .../modules_packages_crates/dependencies.md | 124 ++++ .../noir/modules_packages_crates/modules.md | 105 ++++ .../modules_packages_crates/workspaces.md | 40 ++ .../noir/standard_library/_category_.json | 6 + .../noir/standard_library/bigint.md | 119 ++++ .../noir/standard_library/black_box_fns.md | 31 + .../noir/standard_library/bn254.md | 46 ++ .../standard_library/containers/boundedvec.md | 326 ++++++++++ .../standard_library/containers/hashmap.md | 569 ++++++++++++++++++ .../noir/standard_library/containers/index.md | 5 + .../noir/standard_library/containers/vec.mdx | 151 +++++ .../cryptographic_primitives/_category_.json | 5 + .../cryptographic_primitives/ec_primitives.md | 102 ++++ .../ecdsa_sig_verification.mdx | 98 +++ .../cryptographic_primitives/eddsa.mdx | 37 ++ .../cryptographic_primitives/hashes.mdx | 331 ++++++++++ .../cryptographic_primitives/index.md | 14 + .../cryptographic_primitives/scalar.mdx | 33 + .../cryptographic_primitives/schnorr.mdx | 64 ++ .../noir/standard_library/logging.md | 78 +++ .../noir/standard_library/merkle_trees.md | 58 ++ .../noir/standard_library/options.md | 101 ++++ .../noir/standard_library/recursion.md | 88 +++ .../noir/standard_library/traits.md | 408 +++++++++++++ .../noir/standard_library/zeroed.md | 26 + .../NoirJS/backend_barretenberg/.nojekyll | 1 + .../classes/BarretenbergBackend.md | 119 ++++ .../NoirJS/backend_barretenberg/index.md | 58 ++ .../type-aliases/BackendOptions.md | 21 + .../backend_barretenberg/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_js/.nojekyll | 1 + .../reference/NoirJS/noir_js/classes/Noir.md | 132 ++++ .../reference/NoirJS/noir_js/functions/and.md | 22 + .../NoirJS/noir_js/functions/blake2s256.md | 21 + .../functions/ecdsa_secp256k1_verify.md | 28 + .../functions/ecdsa_secp256r1_verify.md | 28 + .../NoirJS/noir_js/functions/keccak256.md | 21 + .../NoirJS/noir_js/functions/sha256.md | 21 + .../reference/NoirJS/noir_js/functions/xor.md | 22 + .../reference/NoirJS/noir_js/index.md | 54 ++ .../type-aliases/ForeignCallHandler.md | 24 + .../noir_js/type-aliases/ForeignCallInput.md | 9 + .../noir_js/type-aliases/ForeignCallOutput.md | 9 + .../NoirJS/noir_js/type-aliases/WitnessMap.md | 9 + .../NoirJS/noir_js/typedoc-sidebar.cjs | 4 + .../reference/NoirJS/noir_wasm/.nojekyll | 1 + .../NoirJS/noir_wasm/functions/compile.md | 51 ++ .../noir_wasm/functions/compile_contract.md | 51 ++ .../noir_wasm/functions/createFileManager.md | 21 + .../functions/inflateDebugSymbols.md | 21 + .../reference/NoirJS/noir_wasm/index.md | 49 ++ .../NoirJS/noir_wasm/typedoc-sidebar.cjs | 4 + .../version-v0.26.0/reference/_category_.json | 5 + .../reference/nargo_commands.md | 381 ++++++++++++ .../version-v0.26.0/tutorials/noirjs_app.md | 279 +++++++++ .../version-v0.26.0-sidebars.json | 83 +++ 108 files changed, 9257 insertions(+) create mode 100644 docs/versioned_docs/version-v0.26.0/explainers/explainer-oracle.md create mode 100644 docs/versioned_docs/version-v0.26.0/explainers/explainer-recursion.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/project_breakdown.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/installation/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/installation/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/installation/other_install_methods.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/tooling/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/tooling/language_server.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/tooling/noir_codegen.md create mode 100644 docs/versioned_docs/version-v0.26.0/getting_started/tooling/testing.md create mode 100644 docs/versioned_docs/version-v0.26.0/how_to/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/how_to/how-to-oracles.md create mode 100644 docs/versioned_docs/version-v0.26.0/how_to/how-to-recursion.md create mode 100644 docs/versioned_docs/version-v0.26.0/how_to/how-to-solidity-verifier.md create mode 100644 docs/versioned_docs/version-v0.26.0/how_to/merkle-proof.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/how_to/using-devcontainers.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/index.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/migration_notes.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/assert.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/comments.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/control_flow.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_bus.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/fields.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/function_types.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/integers.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/references.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/slices.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/structs.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/tuples.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/distinct.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/functions.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/globals.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/lambdas.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/mutability.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/ops.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/oracles.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/shadowing.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/traits.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/crates_and_packages.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/modules.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/workspaces.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/bigint.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/bn254.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/boundedvec.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/hashmap.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/vec.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ec_primitives.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/eddsa.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/hashes.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/scalar.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/schnorr.mdx create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/logging.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/merkle_trees.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/options.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/traits.md create mode 100644 docs/versioned_docs/version-v0.26.0/noir/standard_library/zeroed.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/.nojekyll create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/.nojekyll create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/classes/Noir.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/and.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/blake2s256.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/keccak256.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/sha256.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/xor.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/.nojekyll create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile_contract.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/createFileManager.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/index.md create mode 100644 docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs create mode 100644 docs/versioned_docs/version-v0.26.0/reference/_category_.json create mode 100644 docs/versioned_docs/version-v0.26.0/reference/nargo_commands.md create mode 100644 docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md create mode 100644 docs/versioned_sidebars/version-v0.26.0-sidebars.json diff --git a/docs/versioned_docs/version-v0.26.0/explainers/explainer-oracle.md b/docs/versioned_docs/version-v0.26.0/explainers/explainer-oracle.md new file mode 100644 index 00000000000..b84ca5dd986 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/explainers/explainer-oracle.md @@ -0,0 +1,57 @@ +--- +title: Oracles +description: This guide provides an in-depth understanding of how Oracles work in Noir programming. Learn how to use outside calculations in your programs, constrain oracles, and understand their uses and limitations. +keywords: + - Noir Programming + - Oracles + - JSON-RPC + - Foreign Call Handlers + - Constrained Functions + - Blockchain Programming +sidebar_position: 1 +--- + +If you've seen "The Matrix" you may recall "The Oracle" as Gloria Foster smoking cigarettes and baking cookies. While she appears to "know things", she is actually providing a calculation of a pre-determined future. Noir Oracles are similar, in a way. They don't calculate the future (yet), but they allow you to use outside calculations in your programs. + +![matrix oracle prediction](@site/static/img/memes/matrix_oracle.jpeg) + +A Noir program is usually self-contained. You can pass certain inputs to it, and it will generate a deterministic output for those inputs. But what if you wanted to defer some calculation to an outside process or source? + +Oracles are functions that provide this feature. + +## Use cases + +An example usage for Oracles is proving something on-chain. For example, proving that the ETH-USDC quote was below a certain target at a certain block time. Or even making more complex proofs like proving the ownership of an NFT as an anonymous login method. + +Another interesting use case is to defer expensive calculations to be made outside of the Noir program, and then constraining the result; similar to the use of [unconstrained functions](../noir/concepts//unconstrained.md). + +In short, anything that can be constrained in a Noir program but needs to be fetched from an external source is a great candidate to be used in oracles. + +## Constraining oracles + +Just like in The Matrix, Oracles are powerful. But with great power, comes great responsibility. Just because you're using them in a Noir program doesn't mean they're true. Noir has no superpowers. If you want to prove that Portugal won the Euro Cup 2016, you're still relying on potentially untrusted information. + +To give a concrete example, Alice wants to login to the [NounsDAO](https://nouns.wtf/) forum with her username "noir_nouner" by proving she owns a noun without revealing her ethereum address. Her Noir program could have a oracle call like this: + +```rust +#[oracle(getNoun)] +unconstrained fn get_noun(address: Field) -> Field +``` + +This oracle could naively resolve with the number of Nouns she possesses. However, it is useless as a trusted source, as the oracle could resolve to anything Alice wants. In order to make this oracle call actually useful, Alice would need to constrain the response from the oracle, by proving her address and the noun count belongs to the state tree of the contract. + +In short, **Oracles don't prove anything. Your Noir program does.** + +:::danger + +If you don't constrain the return of your oracle, you could be clearly opening an attack vector on your Noir program. Make double-triple sure that the return of an oracle call is constrained! + +::: + +## How to use Oracles + +On CLI, Nargo resolves oracles by making JSON RPC calls, which means it would require an RPC node to be running. + +In JavaScript, NoirJS accepts and resolves arbitrary call handlers (that is, not limited to JSON) as long as they matches the expected types the developer defines. Refer to [Foreign Call Handler](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) to learn more about NoirJS's call handling. + +If you want to build using oracles, follow through to the [oracle guide](../how_to/how-to-oracles.md) for a simple example on how to do that. diff --git a/docs/versioned_docs/version-v0.26.0/explainers/explainer-recursion.md b/docs/versioned_docs/version-v0.26.0/explainers/explainer-recursion.md new file mode 100644 index 00000000000..18846176ca7 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/explainers/explainer-recursion.md @@ -0,0 +1,176 @@ +--- +title: Recursive proofs +description: Explore the concept of recursive proofs in Zero-Knowledge programming. Understand how recursion works in Noir, a language for writing smart contracts on the EVM blockchain. Learn through practical examples like Alice and Bob's guessing game, Charlie's recursive merkle tree, and Daniel's reusable components. Discover how to use recursive proofs to optimize computational resources and improve efficiency. + +keywords: + [ + "Recursive Proofs", + "Zero-Knowledge Programming", + "Noir", + "EVM Blockchain", + "Smart Contracts", + "Recursion in Noir", + "Alice and Bob Guessing Game", + "Recursive Merkle Tree", + "Reusable Components", + "Optimizing Computational Resources", + "Improving Efficiency", + "Verification Key", + "Aggregation", + "Recursive zkSNARK schemes", + "PLONK", + "Proving and Verification Keys" + ] +sidebar_position: 1 +pagination_next: how_to/how-to-recursion +--- + +In programming, we tend to think of recursion as something calling itself. A classic example would be the calculation of the factorial of a number: + +```js +function factorial(n) { + if (n === 0 || n === 1) { + return 1; + } else { + return n * factorial(n - 1); + } +} +``` + +In this case, while `n` is not `1`, this function will keep calling itself until it hits the base case, bubbling up the result on the call stack: + +```md + Is `n` 1? <--------- + /\ / + / \ n = n -1 + / \ / + Yes No -------- +``` + +In Zero-Knowledge, recursion has some similarities. + +It is not a Noir function calling itself, but a proof being used as an input to another circuit. In short, you verify one proof *inside* another proof, returning the proof that both proofs are valid. + +This means that, given enough computational resources, you can prove the correctness of any arbitrary number of proofs in a single proof. This could be useful to design state channels (for which a common example would be [Bitcoin's Lightning Network](https://en.wikipedia.org/wiki/Lightning_Network)), to save on gas costs by settling one proof on-chain, or simply to make business logic less dependent on a consensus mechanism. + +## Examples + +Let us look at some of these examples + +### Alice and Bob - Guessing game + +Alice and Bob are friends, and they like guessing games. They want to play a guessing game online, but for that, they need a trusted third-party that knows both of their secrets and finishes the game once someone wins. + +So, they use zero-knowledge proofs. Alice tries to guess Bob's number, and Bob will generate a ZK proof stating whether she succeeded or failed. + +This ZK proof can go on a smart contract, revealing the winner and even giving prizes. However, this means every turn needs to be verified on-chain. This incurs some cost and waiting time that may simply make the game too expensive or time-consuming to be worth it. + +As a solution, Alice proposes the following: "what if Bob generates his proof, and instead of sending it on-chain, I verify it *within* my own proof before playing my own turn?". + +She can then generate a proof that she verified his proof, and so on. + +```md + Did you fail? <-------------------------- + / \ / + / \ n = n -1 + / \ / + Yes No / + | | / + | | / + | You win / + | / + | / +Generate proof of that / + + / + my own guess ---------------- +``` + +### Charlie - Recursive merkle tree + +Charlie is a concerned citizen, and wants to be sure his vote in an election is accounted for. He votes with a ZK proof, but he has no way of knowing that his ZK proof was included in the total vote count! + +If the vote collector puts all of the votes into a [Merkle tree](https://en.wikipedia.org/wiki/Merkle_tree), everyone can prove the verification of two proofs within one proof, as such: + +```md + abcd + __________|______________ + | | + ab cd + _____|_____ ______|______ + | | | | + alice bob charlie daniel +``` + +Doing this recursively allows us to arrive on a final proof `abcd` which if true, verifies the correctness of all the votes. + +### Daniel - Reusable components + +Daniel has a big circuit and a big headache. A part of his circuit is a setup phase that finishes with some assertions that need to be made. But that section alone takes most of the proving time, and is largely independent of the rest of the circuit. + +He might find it more efficient to generate a proof for that setup phase separately, and verify that proof recursively in the actual business logic section of his circuit. This will allow for parallelization of both proofs, which results in a considerable speedup. + +## What params do I need + +As you can see in the [recursion reference](noir/standard_library/recursion.md), a simple recursive proof requires: + +- The proof to verify +- The Verification Key of the circuit that generated the proof +- A hash of this verification key, as it's needed for some backends +- The public inputs for the proof + +:::info + +Recursive zkSNARK schemes do not necessarily "verify a proof" in the sense that you expect a true or false to be spit out by the verifier. Rather an aggregation object is built over the public inputs. + +So, taking the example of Alice and Bob and their guessing game: + +- Alice makes her guess. Her proof is *not* recursive: it doesn't verify any proof within it! It's just a standard `assert(x != y)` circuit +- Bob verifies Alice's proof and makes his own guess. In this circuit, he doesn't exactly *prove* the verification of Alice's proof. Instead, he *aggregates* his proof to Alice's proof. The actual verification is done when the full proof is verified, for example when using `nargo verify` or through the verifier smart contract. + +We can imagine recursive proofs a [relay race](https://en.wikipedia.org/wiki/Relay_race). The first runner doesn't have to receive the baton from anyone else, as he/she already starts with it. But when his/her turn is over, the next runner needs to receive it, run a bit more, and pass it along. Even though every runner could theoretically verify the baton mid-run (why not? 🏃🔍), only at the end of the race does the referee verify that the whole race is valid. + +::: + +## Some architecture + +As with everything in computer science, there's no one-size-fits all. But there are some patterns that could help understanding and implementing them. To give three examples: + +### Adding some logic to a proof verification + +This would be an approach for something like our guessing game, where proofs are sent back and forth and are verified by each opponent. This circuit would be divided in two sections: + +- A `recursive verification` section, which would be just the call to `std::verify_proof`, and that would be skipped on the first move (since there's no proof to verify) +- A `guessing` section, which is basically the logic part where the actual guessing happens + +In such a situation, and assuming Alice is first, she would skip the first part and try to guess Bob's number. Bob would then verify her proof on the first section of his run, and try to guess Alice's number on the second part, and so on. + +### Aggregating proofs + +In some one-way interaction situations, recursion would allow for aggregation of simple proofs that don't need to be immediately verified on-chain or elsewhere. + +To give a practical example, a barman wouldn't need to verify a "proof-of-age" on-chain every time he serves alcohol to a customer. Instead, the architecture would comprise two circuits: + +- A `main`, non-recursive circuit with some logic +- A `recursive` circuit meant to verify two proofs in one proof + +The customer's proofs would be intermediate, and made on their phones, and the barman could just verify them locally. He would then aggregate them into a final proof sent on-chain (or elsewhere) at the end of the day. + +### Recursively verifying different circuits + +Nothing prevents you from verifying different circuits in a recursive proof, for example: + +- A `circuit1` circuit +- A `circuit2` circuit +- A `recursive` circuit + +In this example, a regulator could verify that taxes were paid for a specific purchase by aggregating both a `payer` circuit (proving that a purchase was made and taxes were paid), and a `receipt` circuit (proving that the payment was received) + +## How fast is it + +At the time of writing, verifying recursive proofs is surprisingly fast. This is because most of the time is spent on generating the verification key that will be used to generate the next proof. So you are able to cache the verification key and reuse it later. + +Currently, Noir JS packages don't expose the functionality of loading proving and verification keys, but that feature exists in the underlying `bb.js` package. + +## How can I try it + +Learn more about using recursion in Nargo and NoirJS in the [how-to guide](../how_to/how-to-recursion.md) and see a full example in [noir-examples](https://github.com/noir-lang/noir-examples). diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/_category_.json b/docs/versioned_docs/version-v0.26.0/getting_started/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/_category_.json b/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/index.md b/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/index.md new file mode 100644 index 00000000000..743c4d8d634 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/index.md @@ -0,0 +1,142 @@ +--- +title: Creating a Project +description: + Learn how to create and verify your first Noir program using Nargo, a programming language for + zero-knowledge proofs. +keywords: + [ + Nargo, + Noir, + zero-knowledge proofs, + programming language, + create Noir program, + verify Noir program, + step-by-step guide, + ] +sidebar_position: 1 + +--- + +Now that we have installed Nargo, it is time to make our first hello world program! + +## Create a Project Directory + +Noir code can live anywhere on your computer. Let us create a _projects_ folder in the home +directory to house our Noir programs. + +For Linux, macOS, and Windows PowerShell, create the directory and change directory into it by +running: + +```sh +mkdir ~/projects +cd ~/projects +``` + +## Create Our First Nargo Project + +Now that we are in the projects directory, create a new Nargo project by running: + +```sh +nargo new hello_world +``` + +> **Note:** `hello_world` can be any arbitrary project name, we are simply using `hello_world` for +> demonstration. +> +> In production, the common practice is to name the project folder as `circuits` for better +> identifiability when sitting alongside other folders in the codebase (e.g. `contracts`, `scripts`, +> `test`). + +A `hello_world` folder would be created. Similar to Rust, the folder houses _src/main.nr_ and +_Nargo.toml_ which contain the source code and environmental options of your Noir program +respectively. + +### Intro to Noir Syntax + +Let us take a closer look at _main.nr_. The default _main.nr_ generated should look like this: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +The first line of the program specifies the program's inputs: + +```rust +x : Field, y : pub Field +``` + +Program inputs in Noir are private by default (e.g. `x`), but can be labeled public using the +keyword `pub` (e.g. `y`). To learn more about private and public values, check the +[Data Types](../../noir/concepts/data_types/index.md) section. + +The next line of the program specifies its body: + +```rust +assert(x != y); +``` + +The Noir syntax `assert` can be interpreted as something similar to constraints in other zk-contract languages. + +For more Noir syntax, check the [Language Concepts](../../noir/concepts/comments.md) chapter. + +## Build In/Output Files + +Change directory into _hello_world_ and build in/output files for your Noir program by running: + +```sh +cd hello_world +nargo check +``` + +Two additional files would be generated in your project directory: + +_Prover.toml_ houses input values, and _Verifier.toml_ houses public values. + +## Prove Our Noir Program + +Now that the project is set up, we can create a proof of correct execution of our Noir program. + +Fill in input values for execution in the _Prover.toml_ file. For example: + +```toml +x = "1" +y = "2" +``` + +Prove the valid execution of your Noir program: + +```sh +nargo prove +``` + +A new folder _proofs_ would then be generated in your project directory, containing the proof file +`.proof`, where the project name is defined in Nargo.toml. + +The _Verifier.toml_ file would also be updated with the public values computed from program +execution (in this case the value of `y`): + +```toml +y = "0x0000000000000000000000000000000000000000000000000000000000000002" +``` + +> **Note:** Values in _Verifier.toml_ are computed as 32-byte hex values. + +## Verify Our Noir Program + +Once a proof is generated, we can verify correct execution of our Noir program by verifying the +proof file. + +Verify your proof by running: + +```sh +nargo verify +``` + +The verification will complete in silence if it is successful. If it fails, it will log the +corresponding error instead. + +Congratulations, you have now created and verified a proof for your very first Noir program! + +In the [next section](./project_breakdown.md), we will go into more detail on each step performed. diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/project_breakdown.md b/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/project_breakdown.md new file mode 100644 index 00000000000..6160a102c6c --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/hello_noir/project_breakdown.md @@ -0,0 +1,199 @@ +--- +title: Project Breakdown +description: + Learn about the anatomy of a Nargo project, including the purpose of the Prover and Verifier TOML + files, and how to prove and verify your program. +keywords: + [Nargo, Nargo project, Prover.toml, Verifier.toml, proof verification, private asset transfer] +sidebar_position: 2 +--- + +This section breaks down our hello world program from the previous section. We elaborate on the project +structure and what the `prove` and `verify` commands did. + +## Anatomy of a Nargo Project + +Upon creating a new project with `nargo new` and building the in/output files with `nargo check` +commands, you would get a minimal Nargo project of the following structure: + + - src + - Prover.toml + - Verifier.toml + - Nargo.toml + +The source directory _src_ holds the source code for your Noir program. By default only a _main.nr_ +file will be generated within it. + +### Prover.toml + +_Prover.toml_ is used for specifying the input values for executing and proving the program. You can specify `toml` files with different names by using the `--prover-name` or `-p` flags, see the [Prover](#provertoml) section below. Optionally you may specify expected output values for prove-time checking as well. + +### Verifier.toml + +_Verifier.toml_ contains public in/output values computed when executing the Noir program. + +### Nargo.toml + +_Nargo.toml_ contains the environmental options of your project. It contains a "package" section and a "dependencies" section. + +Example Nargo.toml: + +```toml +[package] +name = "noir_starter" +type = "bin" +authors = ["Alice"] +compiler_version = "0.9.0" +description = "Getting started with Noir" +entry = "circuit/main.nr" +license = "MIT" + +[dependencies] +ecrecover = {tag = "v0.9.0", git = "https://github.com/colinnielsen/ecrecover-noir.git"} +``` + +Nargo.toml for a [workspace](../../noir/modules_packages_crates/workspaces.md) will look a bit different. For example: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +#### Package section + +The package section defines a number of fields including: + +- `name` (**required**) - the name of the package +- `type` (**required**) - can be "bin", "lib", or "contract" to specify whether its a binary, library or Aztec contract +- `authors` (optional) - authors of the project +- `compiler_version` - specifies the version of the compiler to use. This is enforced by the compiler and follow's [Rust's versioning](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field), so a `compiler_version = 0.18.0` will enforce Nargo version 0.18.0, `compiler_version = ^0.18.0` will enforce anything above 0.18.0 but below 0.19.0, etc. For more information, see how [Rust handles these operators](https://docs.rs/semver/latest/semver/enum.Op.html) +- `description` (optional) +- `entry` (optional) - a relative filepath to use as the entry point into your package (overrides the default of `src/lib.nr` or `src/main.nr`) +- `backend` (optional) +- `license` (optional) + +#### Dependencies section + +This is where you will specify any dependencies for your project. See the [Dependencies page](../../noir/modules_packages_crates/dependencies.md) for more info. + +`./proofs/` and `./contract/` directories will not be immediately visible until you create a proof or +verifier contract respectively. + +### main.nr + +The _main.nr_ file contains a `main` method, this method is the entry point into your Noir program. + +In our sample program, _main.nr_ looks like this: + +```rust +fn main(x : Field, y : Field) { + assert(x != y); +} +``` + +The parameters `x` and `y` can be seen as the API for the program and must be supplied by the +prover. Since neither `x` nor `y` is marked as public, the verifier does not supply any inputs, when +verifying the proof. + +The prover supplies the values for `x` and `y` in the _Prover.toml_ file. + +As for the program body, `assert` ensures that the condition to be satisfied (e.g. `x != y`) is +constrained by the proof of the execution of said program (i.e. if the condition was not met, the +verifier would reject the proof as an invalid proof). + +### Prover.toml + +The _Prover.toml_ file is a file which the prover uses to supply his witness values(both private and +public). + +In our hello world program the _Prover.toml_ file looks like this: + +```toml +x = "1" +y = "2" +``` + +When the command `nargo prove` is executed, two processes happen: + +1. Noir creates a proof that `x`, which holds the value of `1`, and `y`, which holds the value of `2`, + is not equal. This inequality constraint is due to the line `assert(x != y)`. + +2. Noir creates and stores the proof of this statement in the _proofs_ directory in a file called your-project.proof. So if your project is named "private_voting" (defined in the project Nargo.toml), the proof will be saved at `./proofs/private_voting.proof`. Opening this file will display the proof in hex format. + +#### Arrays of Structs + +The following code shows how to pass an array of structs to a Noir program to generate a proof. + +```rust +// main.nr +struct Foo { + bar: Field, + baz: Field, +} + +fn main(foos: [Foo; 3]) -> pub Field { + foos[2].bar + foos[2].baz +} +``` + +Prover.toml: + +```toml +[[foos]] # foos[0] +bar = 0 +baz = 0 + +[[foos]] # foos[1] +bar = 0 +baz = 0 + +[[foos]] # foos[2] +bar = 1 +baz = 2 +``` + +#### Custom toml files + +You can specify a `toml` file with a different name to use for proving by using the `--prover-name` or `-p` flags. + +This command looks for proof inputs in the default **Prover.toml** and generates the proof and saves it at `./proofs/.proof`: + +```bash +nargo prove +``` + +This command looks for proof inputs in the custom **OtherProver.toml** and generates proof and saves it at `./proofs/.proof`: + +```bash +nargo prove -p OtherProver +``` + +## Verifying a Proof + +When the command `nargo verify` is executed, two processes happen: + +1. Noir checks in the _proofs_ directory for a proof file with the project name (eg. test_project.proof) + +2. If that file is found, the proof's validity is checked + +> **Note:** The validity of the proof is linked to the current Noir program; if the program is +> changed and the verifier verifies the proof, it will fail because the proof is not valid for the +> _modified_ Noir program. + +In production, the prover and the verifier are usually two separate entities. A prover would +retrieve the necessary inputs, execute the Noir program, generate a proof and pass it to the +verifier. The verifier would then retrieve the public inputs, usually from external sources, and +verify the validity of the proof against it. + +Take a private asset transfer as an example: + +A person using a browser as the prover would retrieve private inputs locally (e.g. the user's private key) and +public inputs (e.g. the user's encrypted balance on-chain), compute the transfer, generate a proof +and submit it to the verifier smart contract. + +The verifier contract would then draw the user's encrypted balance directly from the blockchain and +verify the proof submitted against it. If the verification passes, additional functions in the +verifier contract could trigger (e.g. approve the asset transfer). + +Now that you understand the concepts, you'll probably want some editor feedback while you are writing more complex code. diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/installation/_category_.json b/docs/versioned_docs/version-v0.26.0/getting_started/installation/_category_.json new file mode 100644 index 00000000000..0c02fb5d4d7 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/installation/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 0, + "label": "Install Nargo", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/installation/index.md b/docs/versioned_docs/version-v0.26.0/getting_started/installation/index.md new file mode 100644 index 00000000000..4ef86aa5914 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/installation/index.md @@ -0,0 +1,48 @@ +--- +title: Nargo Installation +description: + nargo is a command line tool for interacting with Noir programs. This page is a quick guide on how to install Nargo through the most common and easy method, noirup +keywords: [ + Nargo + Noir + Rust + Cargo + Noirup + Installation + Terminal Commands + Version Check + Nightlies + Specific Versions + Branches + Noirup Repository +] +pagination_next: getting_started/hello_noir/index +--- + +`nargo` is the one-stop-shop for almost everything related with Noir. The name comes from our love for Rust and its package manager `cargo`. + +With `nargo`, you can start new projects, compile, execute, prove, verify, test, generate solidity contracts, and do pretty much all that is available in Noir. + +Similarly to `rustup`, we also maintain an easy installation method that covers most machines: `noirup`. + +## Installing Noirup + +Open a terminal on your machine, and write: + +```bash +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Close the terminal, open another one, and run + +```bash +noirup +``` + +Done. That's it. You should have the latest version working. You can check with `nargo --version`. + +You can also install nightlies, specific versions +or branches. Check out the [noirup repository](https://github.com/noir-lang/noirup) for more +information. + +Now we're ready to start working on [our first Noir program!](../hello_noir/index.md) diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/installation/other_install_methods.md b/docs/versioned_docs/version-v0.26.0/getting_started/installation/other_install_methods.md new file mode 100644 index 00000000000..3634723562b --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/installation/other_install_methods.md @@ -0,0 +1,102 @@ +--- +title: Alternative Installations +description: There are different ways to install Nargo, the one-stop shop and command-line tool for developing Noir programs. This guide explains how to specify which version to install when using noirup, and using WSL for windows. +keywords: [ + Installation + Nargo + Noirup + Binaries + Compiling from Source + WSL for Windows + macOS + Linux + Nix + Direnv + Uninstalling Nargo + ] +sidebar_position: 1 +--- + +## Encouraged Installation Method: Noirup + +Noirup is the endorsed method for installing Nargo, streamlining the process of fetching binaries or compiling from source. It supports a range of options to cater to your specific needs, from nightly builds and specific versions to compiling from various sources. + +### Installing Noirup + +First, ensure you have `noirup` installed: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +### Fetching Binaries + +With `noirup`, you can easily switch between different Nargo versions, including nightly builds: + +- **Nightly Version**: Install the latest nightly build. + + ```sh + noirup --version nightly + ``` + +- **Specific Version**: Install a specific version of Nargo. + ```sh + noirup --version + ``` + +### Compiling from Source + +`noirup` also enables compiling Nargo from various sources: + +- **From a Specific Branch**: Install from the latest commit on a branch. + + ```sh + noirup --branch + ``` + +- **From a Fork**: Install from the main branch of a fork. + + ```sh + noirup --repo + ``` + +- **From a Specific Branch in a Fork**: Install from a specific branch in a fork. + + ```sh + noirup --repo --branch + ``` + +- **From a Specific Pull Request**: Install from a specific PR. + + ```sh + noirup --pr + ``` + +- **From a Specific Commit**: Install from a specific commit. + + ```sh + noirup -C + ``` + +- **From Local Source**: Compile and install from a local directory. + ```sh + noirup --path ./path/to/local/source + ``` + +## Installation on Windows + +The default backend for Noir (Barretenberg) doesn't provide Windows binaries at this time. For that reason, Noir cannot be installed natively. However, it is available by using Windows Subsystem for Linux (WSL). + +Step 1: Follow the instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) to install and run WSL. + +step 2: Follow the [Noirup instructions](#encouraged-installation-method-noirup). + +## Uninstalling Nargo + +If you installed Nargo with `noirup`, you can uninstall Nargo by removing the files in `~/.nargo`, `~/nargo`, and `~/noir_cache`. This ensures that all installed binaries, configurations, and cache related to Nargo are fully removed from your system. + +```bash +rm -r ~/.nargo +rm -r ~/nargo +rm -r ~/noir_cache +``` diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/tooling/_category_.json b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/_category_.json new file mode 100644 index 00000000000..55804c03a71 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/_category_.json @@ -0,0 +1,6 @@ +{ + "position": 2, + "label": "Tooling", + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx new file mode 100644 index 00000000000..ac480f3c9f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/index.mdx @@ -0,0 +1,38 @@ +--- +title: Tooling +Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. +Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] +--- + +Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. + +## Playground + +The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). + +## IDE tools + +When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. + +The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +## Codespaces + +Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. + + + +## GitHub Actions + +You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as +installing `noirup` and running tests in your GitHub Action `yml` file. + +See the +[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. + +## Community projects + +As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/tooling/language_server.md b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/language_server.md new file mode 100644 index 00000000000..81e0356ef8a --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/language_server.md @@ -0,0 +1,43 @@ +--- +title: Language Server +description: Learn about the Noir Language Server, how to install the components, and configuration that may be required. +keywords: [Nargo, Language Server, LSP, VSCode, Visual Studio Code] +sidebar_position: 0 +--- + +This section helps you install and configure the Noir Language Server. + +The Language Server Protocol (LSP) has two components, the [Server](#language-server) and the [Client](#language-client). Below we describe each in the context of Noir. + +## Language Server + +The Server component is provided by the Nargo command line tool that you installed at the beginning of this guide. +As long as Nargo is installed and you've used it to run other commands in this guide, it should be good to go! + +If you'd like to verify that the `nargo lsp` command is available, you can run `nargo --help` and look for `lsp` in the list of commands. If you see it, you're using a version of Noir with LSP support. + +## Language Client + +The Client component is usually an editor plugin that launches the Server. It communicates LSP messages between the editor and the Server. For example, when you save a file, the Client will alert the Server, so it can try to compile the project and report any errors. + +Currently, Noir provides a Language Client for Visual Studio Code via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). + +> **Note:** Noir's Language Server Protocol support currently assumes users' VSCode workspace root to be the same as users' Noir project root (i.e. where Nargo.toml lies). +> +> If LSP features seem to be missing / malfunctioning, make sure you are opening your Noir project directly (instead of as a sub-folder) in your VSCode instance. + +When your language server is running correctly and the VSCode plugin is installed, you should see handy codelens buttons for compilation, measuring circuit size, execution, and tests: + +![Compile and Execute](@site/static/img/codelens_compile_execute.png) +![Run test](@site/static/img/codelens_run_test.png) + +You should also see your tests in the `testing` panel: + +![Testing panel](@site/static/img/codelens_testing_panel.png) + +### Configuration + +- **Noir: Enable LSP** - If checked, the extension will launch the Language Server via `nargo lsp` and communicate with it. +- **Noir: Nargo Flags** - Additional flags may be specified if you require them to be added when the extension calls `nargo lsp`. +- **Noir: Nargo Path** - An absolute path to a Nargo binary with the `lsp` command. This may be useful if Nargo is not within the `PATH` of your editor. +- **Noir > Trace: Server** - Setting this to `"messages"` or `"verbose"` will log LSP messages between the Client and Server. Useful for debugging. diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/tooling/noir_codegen.md b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/noir_codegen.md new file mode 100644 index 00000000000..d65151da0ab --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/noir_codegen.md @@ -0,0 +1,113 @@ +--- +title: Noir Codegen for TypeScript +description: Learn how to use Noir codegen to generate TypeScript bindings +keywords: [Nargo, Noir, compile, TypeScript] +sidebar_position: 2 +--- + +When using TypeScript, it is extra work to interpret Noir program outputs in a type-safe way. Third party libraries may exist for popular Noir programs, but they are either hard to find or unmaintained. + +Now you can generate TypeScript bindings for your Noir programs in two steps: +1. Exporting Noir functions using `nargo export` +2. Using the TypeScript module `noir_codegen` to generate TypeScript binding + +**Note:** you can only export functions from a Noir *library* (not binary or contract program types). + +## Installation + +### Your TypeScript project + +If you don't already have a TypeScript project you can add the module with `yarn` (or `npm`), then initialize it: + +```bash +yarn add typescript -D +npx tsc --init +``` + +### Add TypeScript module - `noir_codegen` + +The following command will add the module to your project's devDependencies: + +```bash +yarn add @noir-lang/noir_codegen -D +``` + +### Nargo library +Make sure you have Nargo, v0.25.0 or greater, installed. If you don't, follow the [installation guide](../installation/index.md). + +If you're in a new project, make a `circuits` folder and create a new Noir library: + +```bash +mkdir circuits && cd circuits +nargo new --lib myNoirLib +``` + +## Usage + +### Export ABI of specified functions + +First go to the `.nr` files in your Noir library, and add the `#[export]` macro to each function that you want to use in TypeScript. + +```rust +#[export] +fn your_function(... +``` + +From your Noir library (where `Nargo.toml` is), run the following command: + +```bash +nargo export +``` + +You will now have an `export` directory with a .json file per exported function. + +You can also specify the directory of Noir programs using `--program-dir`, for example: + +```bash +nargo export --program-dir=./circuits/myNoirLib +``` + +### Generate TypeScript bindings from exported functions + +To use the `noir-codegen` package we added to the TypeScript project: + +```bash +yarn noir-codegen ./export/your_function.json +``` + +This creates an `exports` directory with an `index.ts` file containing all exported functions. + +**Note:** adding `--out-dir` allows you to specify an output dir for your TypeScript bindings to go. Eg: + +```bash +yarn noir-codegen ./export/*.json --out-dir ./path/to/output/dir +``` + +## Example .nr function to .ts output + +Consider a Noir library with this function: + +```rust +#[export] +fn not_equal(x: Field, y: Field) -> bool { + x != y +} +``` + +After the export and codegen steps, you should have an `index.ts` like: + +```typescript +export type Field = string; + + +export const is_equal_circuit: CompiledCircuit = {"abi":{"parameters":[{"name":"x","type":{"kind":"field"},"visibility":"private"},{"name":"y","type":{"kind":"field"},"visibility":"private"}],"param_witnesses":{"x":[{"start":0,"end":1}],"y":[{"start":1,"end":2}]},"return_type":{"abi_type":{"kind":"boolean"},"visibility":"private"},"return_witnesses":[4]},"bytecode":"H4sIAAAAAAAA/7WUMQ7DIAxFQ0Krrr2JjSGYLVcpKrn/CaqqDQN12WK+hPBgmWd/wEyHbF1SS923uhOs3pfoChI+wKXMAXzIKyNj4PB0TFTYc0w5RUjoqeAeEu1wqK0F54RGkWvW44LPzExnlkbMEs4JNZmN8PxS42uHv82T8a3Jeyn2Ks+VLPcO558HmyLMCDOXAXXtpPt4R/Rt9T36ss6dS9HGPx/eG17nGegKBQAA"}; + +export async function is_equal(x: Field, y: Field, foreignCallHandler?: ForeignCallHandler): Promise { + const program = new Noir(is_equal_circuit); + const args: InputMap = { x, y }; + const { returnValue } = await program.execute(args, foreignCallHandler); + return returnValue as boolean; +} +``` + +Now the `is_equal()` function and relevant types are readily available for use in TypeScript. diff --git a/docs/versioned_docs/version-v0.26.0/getting_started/tooling/testing.md b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/testing.md new file mode 100644 index 00000000000..d3e0c522473 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/getting_started/tooling/testing.md @@ -0,0 +1,62 @@ +--- +title: Testing in Noir +description: Learn how to use Nargo to test your Noir program in a quick and easy way +keywords: [Nargo, testing, Noir, compile, test] +sidebar_position: 1 +--- + +You can test your Noir programs using Noir circuits. + +Nargo will automatically compile and run any functions which have the decorator `#[test]` on them if +you run `nargo test`. + +For example if you have a program like: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test] +fn test_add() { + assert(add(2,2) == 4); + assert(add(0,1) == 1); + assert(add(1,0) == 1); +} +``` + +Running `nargo test` will test that the `test_add` function can be executed while satisfying all +the constraints which allows you to test that add returns the expected values. Test functions can't +have any arguments currently. + +### Test fail + +You can write tests that are expected to fail by using the decorator `#[test(should_fail)]`. For example: + +```rust +fn add(x: u64, y: u64) -> u64 { + x + y +} +#[test(should_fail)] +fn test_add() { + assert(add(2,2) == 5); +} +``` + +You can be more specific and make it fail with a specific reason by using `should_fail_with = "`: + +```rust +fn main(african_swallow_avg_speed : Field) { + assert(african_swallow_avg_speed == 65, "What is the airspeed velocity of an unladen swallow"); +} + +#[test] +fn test_king_arthur() { + main(65); +} + +#[test(should_fail_with = "What is the airspeed velocity of an unladen swallow")] +fn test_bridgekeeper() { + main(32); +} + +``` diff --git a/docs/versioned_docs/version-v0.26.0/how_to/_category_.json b/docs/versioned_docs/version-v0.26.0/how_to/_category_.json new file mode 100644 index 00000000000..23b560f610b --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/how_to/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/how_to/how-to-oracles.md b/docs/versioned_docs/version-v0.26.0/how_to/how-to-oracles.md new file mode 100644 index 00000000000..8cf8035a5c4 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/how_to/how-to-oracles.md @@ -0,0 +1,276 @@ +--- +title: How to use Oracles +description: Learn how to use oracles in your Noir program with examples in both Nargo and NoirJS. This guide also covers writing a JSON RPC server and providing custom foreign call handlers for NoirJS. +keywords: + - Noir Programming + - Oracles + - Nargo + - NoirJS + - JSON RPC Server + - Foreign Call Handlers +sidebar_position: 1 +--- + +This guide shows you how to use oracles in your Noir program. For the sake of clarity, it assumes that: + +- You have read the [explainer on Oracles](../explainers/explainer-oracle.md) and are comfortable with the concept. +- You have a Noir program to add oracles to. You can create one using the [vite-hardhat starter](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) as a boilerplate. +- You understand the concept of a JSON-RPC server. Visit the [JSON-RPC website](https://www.jsonrpc.org/) if you need a refresher. +- You are comfortable with server-side JavaScript (e.g. Node.js, managing packages, etc.). + +For reference, you can find the snippets used in this tutorial on the [Aztec DevRel Repository](https://github.com/AztecProtocol/dev-rel/tree/main/code-snippets/how-to-oracles). + +## Rundown + +This guide has 3 major steps: + +1. How to modify our Noir program to make use of oracle calls as unconstrained functions +2. How to write a JSON RPC Server to resolve these oracle calls with Nargo +3. How to use them in Nargo and how to provide a custom resolver in NoirJS + +## Step 1 - Modify your Noir program + +An oracle is defined in a Noir program by defining two methods: + +- An unconstrained method - This tells the compiler that it is executing an [unconstrained functions](../noir/concepts//unconstrained.md). +- A decorated oracle method - This tells the compiler that this method is an RPC call. + +An example of an oracle that returns a `Field` would be: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(number: Field) -> Field { } + +unconstrained fn get_sqrt(number: Field) -> Field { + sqrt(number) +} +``` + +In this example, we're wrapping our oracle function in a unconstrained method, and decorating it with `oracle(getSqrt)`. We can then call the unconstrained function as we would call any other function: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); +} +``` + +In the next section, we will make this `getSqrt` (defined on the `sqrt` decorator) be a method of the RPC server Noir will use. + +:::danger + +As explained in the [Oracle Explainer](../explainers/explainer-oracle.md), this `main` function is unsafe unless you constrain its return value. For example: + +```rust +fn main(input: Field) { + let sqrt = get_sqrt(input); + assert(sqrt.pow_32(2) as u64 == input as u64); // <---- constrain the return of an oracle! +} +``` + +::: + +:::info + +Currently, oracles only work with single params or array params. For example: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt([Field; 2]) -> [Field; 2] { } +``` + +::: + +## Step 2 - Write an RPC server + +Brillig will call *one* RPC server. Most likely you will have to write your own, and you can do it in whatever language you prefer. In this guide, we will do it in Javascript. + +Let's use the above example of an oracle that consumes an array with two `Field` and returns their square roots: + +```rust +#[oracle(getSqrt)] +unconstrained fn sqrt(input: [Field; 2]) -> [Field; 2] { } + +unconstrained fn get_sqrt(input: [Field; 2]) -> [Field; 2] { + sqrt(input) +} + +fn main(input: [Field; 2]) { + let sqrt = get_sqrt(input); + assert(sqrt[0].pow_32(2) as u64 == input[0] as u64); + assert(sqrt[1].pow_32(2) as u64 == input[1] as u64); +} +``` + +:::info + +Why square root? + +In general, computing square roots is computationally more expensive than multiplications, which takes a toll when speaking about ZK applications. In this case, instead of calculating the square root in Noir, we are using our oracle to offload that computation to be made in plain. In our circuit we can simply multiply the two values. + +::: + +Now, we should write the correspondent RPC server, starting with the [default JSON-RPC 2.0 boilerplate](https://www.npmjs.com/package/json-rpc-2.0#example): + +```js +import { JSONRPCServer } from "json-rpc-2.0"; +import express from "express"; +import bodyParser from "body-parser"; + +const app = express(); +app.use(bodyParser.json()); + +const server = new JSONRPCServer(); +app.post("/", (req, res) => { + const jsonRPCRequest = req.body; + server.receive(jsonRPCRequest).then((jsonRPCResponse) => { + if (jsonRPCResponse) { + res.json(jsonRPCResponse); + } else { + res.sendStatus(204); + } + }); +}); + +app.listen(5555); +``` + +Now, we will add our `getSqrt` method, as expected by the `#[oracle(getSqrt)]` decorator in our Noir code. It maps through the params array and returns their square roots: + +```js +server.addMethod("getSqrt", async (params) => { + const values = params[0].Array.map((field) => { + return `${Math.sqrt(parseInt(field, 16))}`; + }); + return { values: [{ Array: values }] }; +}); +``` + +:::tip + +Brillig expects an object with an array of values. Each value is an object declaring to be `Single` or `Array` and returning a field element *as a string*. For example: + +```json +{ "values": [{ "Array": ["1", "2"] }]} +{ "values": [{ "Single": "1" }]} +{ "values": [{ "Single": "1" }, { "Array": ["1", "2"] }]} +``` + +If you're using Typescript, the following types may be helpful in understanding the expected return value and making sure they're easy to follow: + +```js +interface SingleForeignCallParam { + Single: string, +} + +interface ArrayForeignCallParam { + Array: string[], +} + +type ForeignCallParam = SingleForeignCallParam | ArrayForeignCallParam; + +interface ForeignCallResult { + values: ForeignCallParam[], +} +``` + +::: + +## Step 3 - Usage with Nargo + +Using the [`nargo` CLI tool](../getting_started/installation/index.md), you can use oracles in the `nargo test`, `nargo execute` and `nargo prove` commands by passing a value to `--oracle-resolver`. For example: + +```bash +nargo test --oracle-resolver http://localhost:5555 +``` + +This tells `nargo` to use your RPC Server URL whenever it finds an oracle decorator. + +## Step 4 - Usage with NoirJS + +In a JS environment, an RPC server is not strictly necessary, as you may want to resolve your oracles without needing any JSON call at all. NoirJS simply expects that you pass a callback function when you generate proofs, and that callback function can be anything. + +For example, if your Noir program expects the host machine to provide CPU pseudo-randomness, you could simply pass it as the `foreignCallHandler`. You don't strictly need to create an RPC server to serve pseudo-randomness, as you may as well get it directly in your app: + +```js +const foreignCallHandler = (name, inputs) => crypto.randomBytes(16) // etc + +await noir.generateProof(inputs, foreignCallHandler) +``` + +As one can see, in NoirJS, the [`foreignCallHandler`](../reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md) function simply means "a callback function that returns a value of type [`ForeignCallOutput`](../reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md). It doesn't have to be an RPC call like in the case for Nargo. + +:::tip + +Does this mean you don't have to write an RPC server like in [Step #2](#step-2---write-an-rpc-server)? + +You don't technically have to, but then how would you run `nargo test` or `nargo prove`? To use both `Nargo` and `NoirJS` in your development flow, you will have to write a JSON RPC server. + +::: + +In this case, let's make `foreignCallHandler` call the JSON RPC Server we created in [Step #2](#step-2---write-an-rpc-server), by making it a JSON RPC Client. + +For example, using the same `getSqrt` program in [Step #1](#step-1---modify-your-noir-program) (comments in the code): + +```js +import { JSONRPCClient } from "json-rpc-2.0"; + +// declaring the JSONRPCClient +const client = new JSONRPCClient((jsonRPCRequest) => { +// hitting the same JSON RPC Server we coded above + return fetch("http://localhost:5555", { + method: "POST", + headers: { + "content-type": "application/json", + }, + body: JSON.stringify(jsonRPCRequest), + }).then((response) => { + if (response.status === 200) { + return response + .json() + .then((jsonRPCResponse) => client.receive(jsonRPCResponse)); + } else if (jsonRPCRequest.id !== undefined) { + return Promise.reject(new Error(response.statusText)); + } + }); +}); + +// declaring a function that takes the name of the foreign call (getSqrt) and the inputs +const foreignCallHandler = async (name, input) => { + // notice that the "inputs" parameter contains *all* the inputs + // in this case we to make the RPC request with the first parameter "numbers", which would be input[0] + const oracleReturn = await client.request(name, [ + { Array: input[0].map((i) => i.toString("hex")) }, + ]); + return [oracleReturn.values[0].Array]; +}; + +// the rest of your NoirJS code +const input = { input: [4, 16] }; +const { witness } = await noir.execute(numbers, foreignCallHandler); +``` + +:::tip + +If you're in a NoirJS environment running your RPC server together with a frontend app, you'll probably hit a familiar problem in full-stack development: requests being blocked by [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) policy. For development only, you can simply install and use the [`cors` npm package](https://www.npmjs.com/package/cors) to get around the problem: + +```bash +yarn add cors +``` + +and use it as a middleware: + +```js +import cors from "cors"; + +const app = express(); +app.use(cors()) +``` + +::: + +## Conclusion + +Hopefully by the end of this guide, you should be able to: + +- Write your own logic around Oracles and how to write a JSON RPC server to make them work with your Nargo commands. +- Provide custom foreign call handlers for NoirJS. diff --git a/docs/versioned_docs/version-v0.26.0/how_to/how-to-recursion.md b/docs/versioned_docs/version-v0.26.0/how_to/how-to-recursion.md new file mode 100644 index 00000000000..4c45bb87ae2 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/how_to/how-to-recursion.md @@ -0,0 +1,179 @@ +--- +title: How to use recursion on NoirJS +description: Learn how to implement recursion with NoirJS, a powerful tool for creating smart contracts on the EVM blockchain. This guide assumes familiarity with NoirJS, solidity verifiers, and the Barretenberg proving backend. Discover how to generate both final and intermediate proofs using `noir_js` and `backend_barretenberg`. +keywords: + [ + "NoirJS", + "EVM blockchain", + "smart contracts", + "recursion", + "solidity verifiers", + "Barretenberg backend", + "noir_js", + "backend_barretenberg", + "intermediate proofs", + "final proofs", + "nargo compile", + "json import", + "recursive circuit", + "recursive app" + ] +sidebar_position: 1 +--- + +This guide shows you how to use recursive proofs in your NoirJS app. For the sake of clarity, it is assumed that: + +- You already have a NoirJS app. If you don't, please visit the [NoirJS tutorial](../tutorials/noirjs_app.md) and the [reference](../reference/NoirJS/noir_js/index.md). +- You are familiar with what are recursive proofs and you have read the [recursion explainer](../explainers/explainer-recursion.md) +- You already built a recursive circuit following [the reference](../noir/standard_library/recursion.md), and understand how it works. + +It is also assumed that you're not using `noir_wasm` for compilation, and instead you've used [`nargo compile`](../reference/nargo_commands.md) to generate the `json` you're now importing into your project. However, the guide should work just the same if you're using `noir_wasm`. + +:::info + +As you've read in the [explainer](../explainers/explainer-recursion.md), a recursive proof is an intermediate proof. This means that it doesn't necessarily generate the final step that makes it verifiable in a smart contract. However, it is easy to verify within another circuit. + +While "standard" usage of NoirJS packages abstracts final proofs, it currently lacks the necessary interface to abstract away intermediate proofs. This means that these proofs need to be created by using the backend directly. + +In short: + +- `noir_js` generates *only* final proofs +- `backend_barretenberg` generates both types of proofs + +::: + +In a standard recursive app, you're also dealing with at least two circuits. For the purpose of this guide, we will assume the following: + +- `main`: a circuit of type `assert(x != y)`, where `main` is marked with a `#[recursive]` attribute. This attribute states that the backend should generate proofs that are friendly for verification within another circuit. +- `recursive`: a circuit that verifies `main` + +For a full example on how recursive proofs work, please refer to the [noir-examples](https://github.com/noir-lang/noir-examples) repository. We will *not* be using it as a reference for this guide. + +## Step 1: Setup + +In a common NoirJS app, you need to instantiate a backend with something like `const backend = new Backend(circuit)`. Then you feed it to the `noir_js` interface. + +For recursion, this doesn't happen, and the only need for `noir_js` is only to `execute` a circuit and get its witness and return value. Everything else is not interfaced, so it needs to happen on the `backend` object. + +It is also recommended that you instantiate the backend with as many threads as possible, to allow for maximum concurrency: + +```js +const backend = new Backend(circuit, { threads: 8 }) +``` + +:::tip +You can use the [`os.cpus()`](https://nodejs.org/api/os.html#oscpus) object in `nodejs` or [`navigator.hardwareConcurrency`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/hardwareConcurrency) on the browser to make the most out of those glorious cpu cores +::: + +## Step 2: Generating the witness and the proof for `main` + +After instantiating the backend, you should also instantiate `noir_js`. We will use it to execute the circuit and get the witness. + +```js +const noir = new Noir(circuit, backend) +const { witness } = noir.execute(input) +``` + +With this witness, you are now able to generate the intermediate proof for the main circuit: + +```js +const { proof, publicInputs } = await backend.generateProof(witness) +``` + +:::warning + +Always keep in mind what is actually happening on your development process, otherwise you'll quickly become confused about what circuit we are actually running and why! + +In this case, you can imagine that Alice (running the `main` circuit) is proving something to Bob (running the `recursive` circuit), and Bob is verifying her proof within his proof. + +With this in mind, it becomes clear that our intermediate proof is the one *meant to be verified within another circuit*, so it must be Alice's. Actually, the only final proof in this theoretical scenario would be the last one, sent on-chain. + +::: + +## Step 3 - Verification and proof artifacts + +Optionally, you are able to verify the intermediate proof: + +```js +const verified = await backend.verifyProof({ proof, publicInputs }) +``` + +This can be useful to make sure our intermediate proof was correctly generated. But the real goal is to do it within another circuit. For that, we need to generate recursive proof artifacts that will be passed to the circuit that is verifying the proof we just generated. Instead of passing the proof and verification key as a byte array, we pass them as fields which makes it cheaper to verify in a circuit: + +```js +const { proofAsFields, vkAsFields, vkHash } = await backend.generateRecursiveProofArtifacts( { publicInputs, proof }, publicInputsCount) +``` + +This call takes the public inputs and the proof, but also the public inputs count. While this is easily retrievable by simply counting the `publicInputs` length, the backend interface doesn't currently abstract it away. + +:::info + +The `proofAsFields` has a constant size `[Field; 93]` and verification keys in Barretenberg are always `[Field; 114]`. + +::: + +:::warning + +One common mistake is to forget *who* makes this call. + +In a situation where Alice is generating the `main` proof, if she generates the proof artifacts and sends them to Bob, which gladly takes them as true, this would mean Alice could prove anything! + +Instead, Bob needs to make sure *he* extracts the proof artifacts, using his own instance of the `main` circuit backend. This way, Alice has to provide a valid proof for the correct `main` circuit. + +::: + +## Step 4 - Recursive proof generation + +With the artifacts, generating a recursive proof is no different from a normal proof. You simply use the `backend` (with the recursive circuit) to generate it: + +```js +const recursiveInputs = { + verification_key: vkAsFields, // array of length 114 + proof: proofAsFields, // array of length 93 + size of public inputs + publicInputs: [mainInput.y], // using the example above, where `y` is the only public input + key_hash: vkHash, +} + +const { witness, returnValue } = noir.execute(recursiveInputs) // we're executing the recursive circuit now! +const { proof, publicInputs } = backend.generateProof(witness) +const verified = backend.verifyProof({ proof, publicInputs }) +``` + +You can obviously chain this proof into another proof. In fact, if you're using recursive proofs, you're probably interested of using them this way! + +:::tip + +Managing circuits and "who does what" can be confusing. To make sure your naming is consistent, you can keep them in an object. For example: + +```js +const circuits = { + main: mainJSON, + recursive: recursiveJSON +} +const backends = { + main: new BarretenbergBackend(circuits.main), + recursive: new BarretenbergBackend(circuits.recursive) +} +const noir_programs = { + main: new Noir(circuits.main, backends.main), + recursive: new Noir(circuits.recursive, backends.recursive) +} +``` + +This allows you to neatly call exactly the method you want without conflicting names: + +```js +// Alice runs this 👇 +const { witness: mainWitness } = await noir_programs.main.execute(input) +const proof = await backends.main.generateProof(mainWitness) + +// Bob runs this 👇 +const verified = await backends.main.verifyProof(proof) +const { proofAsFields, vkAsFields, vkHash } = await backends.main.generateRecursiveProofArtifacts( + proof, + numPublicInputs, +); +const recursiveProof = await noir_programs.recursive.generateProof(recursiveInputs) +``` + +::: diff --git a/docs/versioned_docs/version-v0.26.0/how_to/how-to-solidity-verifier.md b/docs/versioned_docs/version-v0.26.0/how_to/how-to-solidity-verifier.md new file mode 100644 index 00000000000..e3c7c1065da --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/how_to/how-to-solidity-verifier.md @@ -0,0 +1,231 @@ +--- +title: Generate a Solidity Verifier +description: + Learn how to run the verifier as a smart contract on the blockchain. Compile a Solidity verifier + contract for your Noir program and deploy it on any EVM blockchain acting as a verifier smart + contract. Read more to find out +keywords: + [ + solidity verifier, + smart contract, + blockchain, + compiler, + plonk_vk.sol, + EVM blockchain, + verifying Noir programs, + proving backend, + Barretenberg, + ] +sidebar_position: 0 +pagination_next: tutorials/noirjs_app +--- + +Noir has the ability to generate a verifier contract in Solidity, which can be deployed in many EVM-compatible blockchains such as Ethereum. + +This allows for a powerful feature set, as one can make use of the conciseness and the privacy provided by Noir in an immutable ledger. Applications can range from simple P2P guessing games, to complex private DeFi interactions. + +This guide shows you how to generate a Solidity Verifier and deploy it on the [Remix IDE](https://remix.ethereum.org/). It is assumed that: + +- You are comfortable with the Solidity programming language and understand how contracts are deployed on the Ethereum network +- You have Noir installed and you have a Noir program. If you don't, [get started](../getting_started/installation/index.md) with Nargo and the example Hello Noir circuit +- You are comfortable navigating RemixIDE. If you aren't or you need a refresher, you can find some video tutorials [here](https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA) that could help you. + +## Rundown + +Generating a Solidity Verifier contract is actually a one-command process. However, compiling it and deploying it can have some caveats. Here's the rundown of this guide: + +1. How to generate a solidity smart contract +2. How to compile the smart contract in the RemixIDE +3. How to deploy it to a testnet + +## Step 1 - Generate a contract + +This is by far the most straight-forward step. Just run: + +```sh +nargo codegen-verifier +``` + +A new `contract` folder would then be generated in your project directory, containing the Solidity +file `plonk_vk.sol`. It can be deployed to any EVM blockchain acting as a verifier smart contract. + +:::info + +It is possible to generate verifier contracts of Noir programs for other smart contract platforms as long as the proving backend supplies an implementation. + +Barretenberg, the default proving backend for Nargo, supports generation of verifier contracts, for the time being these are only in Solidity. +::: + +## Step 2 - Compiling + +We will mostly skip the details of RemixIDE, as the UI can change from version to version. For now, we can just open +Remix and create a blank workspace. + +![Create Workspace](@site/static/img/how-tos/solidity_verifier_1.png) + +We will create a new file to contain the contract Nargo generated, and copy-paste its content. + +:::warning + +You'll likely see a warning advising you to not trust pasted code. While it is an important warning, it is irrelevant in the context of this guide and can be ignored. We will not be deploying anywhere near a mainnet. + +::: + +To compile our the verifier, we can navigate to the compilation tab: + +![Compilation Tab](@site/static/img/how-tos/solidity_verifier_2.png) + +Remix should automatically match a suitable compiler version. However, hitting the "Compile" button will most likely generate a "Stack too deep" error: + +![Stack too deep](@site/static/img/how-tos/solidity_verifier_3.png) + +This is due to the verify function needing to put many variables on the stack, but enabling the optimizer resolves the issue. To do this, let's open the "Advanced Configurations" tab and enable optimization. The default 200 runs will suffice. + +:::info + +This time we will see a warning about an unused function parameter. This is expected, as the `verify` function doesn't use the `_proof` parameter inside a solidity block, it is loaded from calldata and used in assembly. + +::: + +![Compilation success](@site/static/img/how-tos/solidity_verifier_4.png) + +## Step 3 - Deploying + +At this point we should have a compiled contract read to deploy. If we navigate to the deploy section in Remix, we will see many different environments we can deploy to. The steps to deploy on each environment would be out-of-scope for this guide, so we will just use the default Remix VM. + +Looking closely, we will notice that our "Solidity Verifier" is actually three contracts working together: + +- An `UltraVerificationKey` library which simply stores the verification key for our circuit. +- An abstract contract `BaseUltraVerifier` containing most of the verifying logic. +- A main `UltraVerifier` contract that inherits from the Base and uses the Key contract. + +Remix will take care of the dependencies for us so we can simply deploy the UltraVerifier contract by selecting it and hitting "deploy": + +![Deploying UltraVerifier](@site/static/img/how-tos/solidity_verifier_5.png) + +A contract will show up in the "Deployed Contracts" section, where we can retrieve the Verification Key Hash. This is particularly useful for double-checking the deployer contract is the correct one. + +:::note + +Why "UltraVerifier"? + +To be precise, the Noir compiler (`nargo`) doesn't generate the verifier contract directly. It compiles the Noir code into an intermediate language (ACIR), which is then executed by the backend. So it is the backend that returns the verifier smart contract, not Noir. + +In this case, the Barretenberg Backend uses the UltraPlonk proving system, hence the "UltraVerifier" name. + +::: + +## Step 4 - Verifying + +To verify a proof using the Solidity verifier contract, we call the `verify` function in this extended contract: + +```solidity +function verify(bytes calldata _proof, bytes32[] calldata _publicInputs) external view returns (bool) +``` + +When using the default example in the [Hello Noir](../getting_started/hello_noir/index.md) guide, the easiest way to confirm that the verifier contract is doing its job is by calling the `verify` function via remix with the required parameters. For `_proof`, run `nargo prove` and use the string in `proof/.proof` (adding the hex `0x` prefix). We can also copy the public input from `Verifier.toml`, as it will be properly formatted as 32-byte strings: + +``` +0x...... , [0x0000.....02] +``` + +A programmatic example of how the `verify` function is called can be seen in the example zk voting application [here](https://github.com/noir-lang/noir-examples/blob/33e598c257e2402ea3a6b68dd4c5ad492bce1b0a/foundry-voting/src/zkVote.sol#L35): + +```solidity +function castVote(bytes calldata proof, uint proposalId, uint vote, bytes32 nullifierHash) public returns (bool) { + // ... + bytes32[] memory publicInputs = new bytes32[](4); + publicInputs[0] = merkleRoot; + publicInputs[1] = bytes32(proposalId); + publicInputs[2] = bytes32(vote); + publicInputs[3] = nullifierHash; + require(verifier.verify(proof, publicInputs), "Invalid proof"); +``` + +:::info[Return Values] + +A circuit doesn't have the concept of a return value. Return values are just syntactic sugar in +Noir. + +Under the hood, the return value is passed as an input to the circuit and is checked at the end of +the circuit program. + +For example, if you have Noir program like this: + +```rust +fn main( + // Public inputs + pubkey_x: pub Field, + pubkey_y: pub Field, + // Private inputs + priv_key: Field, +) -> pub Field +``` + +the `verify` function will expect the public inputs array (second function parameter) to be of length 3, the two inputs and the return value. Like before, these values are populated in Verifier.toml after running `nargo prove`. + +Passing only two inputs will result in an error such as `PUBLIC_INPUT_COUNT_INVALID(3, 2)`. + +In this case, the inputs parameter to `verify` would be an array ordered as `[pubkey_x, pubkey_y, return]`. + +::: + +:::tip[Structs] + +You can pass structs to the verifier contract. They will be flattened so that the array of inputs is 1-dimensional array. + +For example, consider the following program: + +```rust +struct Type1 { + val1: Field, + val2: Field, +} + +struct Nested { + t1: Type1, + is_true: bool, +} + +fn main(x: pub Field, nested: pub Nested, y: pub Field) { + //... +} +``` + +The order of these inputs would be flattened to: `[x, nested.t1.val1, nested.t1.val2, nested.is_true, y]` + +::: + +The other function you can call is our entrypoint `verify` function, as defined above. + +:::tip + +It's worth noticing that the `verify` function is actually a `view` function. A `view` function does not alter the blockchain state, so it doesn't need to be distributed (i.e. it will run only on the executing node), and therefore doesn't cost any gas. + +This can be particularly useful in some situations. If Alice generated a proof and wants Bob to verify its correctness, Bob doesn't need to run Nargo, NoirJS, or any Noir specific infrastructure. He can simply make a call to the blockchain with the proof and verify it is correct without paying any gas. + +It would be incorrect to say that a Noir proof verification costs any gas at all. However, most of the time the result of `verify` is used to modify state (for example, to update a balance, a game state, etc). In that case the whole network needs to execute it, which does incur gas costs (calldata and execution, but not storage). + +::: + +## A Note on EVM chains + +ZK-SNARK verification depends on some precompiled cryptographic primitives such as Elliptic Curve Pairings (if you like complex math, you can read about EC Pairings [here](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)). Not all EVM chains support EC Pairings, notably some of the ZK-EVMs. This means that you won't be able to use the verifier contract in all of them. + +For example, chains like `zkSync ERA` and `Polygon zkEVM` do not currently support these precompiles, so proof verification via Solidity verifier contracts won't work. Here's a quick list of EVM chains that have been tested and are known to work: + +- Optimism +- Arbitrum +- Polygon PoS +- Scroll +- Celo + +If you test any other chains, please open a PR on this page to update the list. See [this doc](https://github.com/noir-lang/noir-starter/tree/main/with-foundry#testing-on-chain) for more info about testing verifier contracts on different EVM chains. + +## What's next + +Now that you know how to call a Noir Solidity Verifier on a smart contract using Remix, you should be comfortable with using it with some programmatic frameworks, such as [hardhat](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat) and [foundry](https://github.com/noir-lang/noir-starter/tree/main/with-foundry). + +You can find other tools, examples, boilerplates and libraries in the [awesome-noir](https://github.com/noir-lang/awesome-noir) repository. + +You should also be ready to write and deploy your first NoirJS app and start generating proofs on websites, phones, and NodeJS environments! Head on to the [NoirJS tutorial](../tutorials/noirjs_app.md) to learn how to do that. diff --git a/docs/versioned_docs/version-v0.26.0/how_to/merkle-proof.mdx b/docs/versioned_docs/version-v0.26.0/how_to/merkle-proof.mdx new file mode 100644 index 00000000000..003c7019a93 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/how_to/merkle-proof.mdx @@ -0,0 +1,48 @@ +--- +title: Prove Merkle Tree Membership +description: + Learn how to use merkle membership proof in Noir to prove that a given leaf is a member of a + merkle tree with a specified root, at a given index. +keywords: + [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +--- + +Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is +in a merkle tree. + +```rust +use dep::std; + +fn main(message : [Field; 62], index : Field, hashpath : [Field; 40], root : Field) { + let leaf = std::hash::hash_to_field(message.as_slice()); + let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); + assert(merkle_root == root); +} + +``` + +The message is hashed using `hash_to_field`. The specific hash function that is being used is chosen +by the backend. The only requirement is that this hash function can heuristically be used as a +random oracle. If only collision resistance is needed, then one can call `std::hash::pedersen_hash` +instead. + +```rust +let leaf = std::hash::hash_to_field(message.as_slice()); +``` + +The leaf is then passed to a compute_merkle_root function with the root, index and hashpath. The returned root can then be asserted to be the same as the provided root. + +```rust +let merkle_root = std::merkle::compute_merkle_root(leaf, index, hashpath); +assert (merkle_root == root); +``` + +> **Note:** It is possible to re-implement the merkle tree implementation without standard library. +> However, for most usecases, it is enough. In general, the standard library will always opt to be +> as conservative as possible, while striking a balance with efficiency. + +An example, the merkle membership proof, only requires a hash function that has collision +resistance, hence a hash function like Pedersen is allowed, which in most cases is more efficient +than the even more conservative sha256. + +[View an example on the starter repo](https://github.com/noir-lang/noir-examples/blob/3ea09545cabfa464124ec2f3ea8e60c608abe6df/stealthdrop/circuits/src/main.nr#L20) diff --git a/docs/versioned_docs/version-v0.26.0/how_to/using-devcontainers.mdx b/docs/versioned_docs/version-v0.26.0/how_to/using-devcontainers.mdx new file mode 100644 index 00000000000..727ec6ca667 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/how_to/using-devcontainers.mdx @@ -0,0 +1,110 @@ +--- +title: Developer Containers and Codespaces +description: "Learn how to set up a devcontainer in your GitHub repository for a seamless coding experience with Codespaces. Follow our easy 8-step guide to create your own Noir environment without installing Nargo locally." +keywords: ["Devcontainer", "Codespaces", "GitHub", "Noir Environment", "Docker Image", "Development Environment", "Remote Coding", "GitHub Codespaces", "Noir Programming", "Nargo", "VSCode Extensions", "Noirup"] +sidebar_position: 1 +--- + +Adding a developer container configuration file to your Noir project is one of the easiest way to unlock coding in browser. + +## What's a devcontainer after all? + +A [Developer Container](https://containers.dev/) (devcontainer for short) is a Docker image that comes preloaded with tools, extensions, and other tools you need to quickly get started or continue a project, without having to install Nargo locally. Think of it as a development environment in a box. + +There are many advantages to this: + +- It's platform and architecture agnostic +- You don't need to have an IDE installed, or Nargo, or use a terminal at all +- It's safer for using on a public machine or public network + +One of the best ways of using devcontainers is... not using your machine at all, for maximum control, performance, and ease of use. +Enter Codespaces. + +## Codespaces + +If a devcontainer is just a Docker image, then what stops you from provisioning a `p3dn.24xlarge` AWS EC2 instance with 92 vCPUs and 768 GiB RAM and using it to prove your 10-gate SNARK proof? + +Nothing! Except perhaps the 30-40$ per hour it will cost you. + +The problem is that provisioning takes time, and I bet you don't want to see the AWS console every time you want to code something real quick. + +Fortunately, there's an easy and free way to get a decent remote machine ready and loaded in less than 2 minutes: Codespaces. [Codespaces is a Github feature](https://github.com/features/codespaces) that allows you to code in a remote machine by using devcontainers, and it's pretty cool: + +- You can start coding Noir in less than a minute +- It uses the resources of a remote machine, so you can code on your grandma's phone if needed be +- It makes it easy to share work with your frens +- It's fully reusable, you can stop and restart whenever you need to + +:::info + +Don't take out your wallet just yet. Free GitHub accounts get about [15-60 hours of coding](https://github.com/features/codespaces) for free per month, depending on the size of your provisioned machine. + +::: + +## Tell me it's _actually_ easy + +It is! + +Github comes with a default codespace and you can use it to code your own devcontainer. That's exactly what we will be doing in this guide. + + + +8 simple steps: + +#### 1. Create a new repository on GitHub. + +#### 2. Click "Start coding with Codespaces". This will use the default image. + +#### 3. Create a folder called `.devcontainer` in the root of your repository. + +#### 4. Create a Dockerfile in that folder, and paste the following code: + +```docker +FROM --platform=linux/amd64 node:lts-bookworm-slim +SHELL ["/bin/bash", "-c"] +RUN apt update && apt install -y curl bash git tar gzip libc++-dev +RUN curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +ENV PATH="/root/.nargo/bin:$PATH" +RUN noirup +ENTRYPOINT ["nargo"] +``` +#### 5. Create a file called `devcontainer.json` in the same folder, and paste the following code: + +```json +{ + "name": "Noir on Codespaces", + "build": { + "context": ".", + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "extensions": ["noir-lang.vscode-noir"] + } + } +} +``` +#### 6. Commit and push your changes + +This will pull the new image and build it, so it could take a minute or so + +#### 8. Done! +Just wait for the build to finish, and there's your easy Noir environment. + + +Refer to [noir-starter](https://github.com/noir-lang/noir-starter/) as an example of how devcontainers can be used together with codespaces. + + + +## How do I use it? + +Using the codespace is obviously much easier than setting it up. +Just navigate to your repository and click "Code" -> "Open with Codespaces". It should take a few seconds to load, and you're ready to go. + +:::info + +If you really like the experience, you can add a badge to your readme, links to existing codespaces, and more. +Check out the [official docs](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces) for more info. diff --git a/docs/versioned_docs/version-v0.26.0/index.mdx b/docs/versioned_docs/version-v0.26.0/index.mdx new file mode 100644 index 00000000000..75086ddcdde --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/index.mdx @@ -0,0 +1,67 @@ +--- +title: Noir Lang +hide_title: true +description: + Learn about the public alpha release of Noir, a domain specific language heavily influenced by Rust that compiles to + an intermediate language which can be compiled to an arithmetic circuit or a rank-1 constraint system. +keywords: + [Noir, + Domain Specific Language, + Rust, + Intermediate Language, + Arithmetic Circuit, + Rank-1 Constraint System, + Ethereum Developers, + Protocol Developers, + Blockchain Developers, + Proving System, + Smart Contract Language] +sidebar_position: 0 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Noir Logo + +Noir is a Domain-Specific Language for SNARK proving systems developed by [Aztec Labs](https://aztec.network/). It allows you to generate complex Zero-Knowledge Programs (ZKP) by using simple and flexible syntax, requiring no previous knowledge on the underlying mathematics or cryptography. + +ZK programs are programs that can generate short proofs of a certain statement without revealing some details about it. You can read more about ZKPs [here](https://dev.to/spalladino/a-beginners-intro-to-coding-zero-knowledge-proofs-c56). + +## What's new about Noir? + +Noir works differently from most ZK languages by taking a two-pronged path. First, it compiles the program to an adaptable intermediate language known as ACIR. From there, depending on a given project's needs, ACIR can be further compiled into an arithmetic circuit for integration with the proving backend. + +:::info + +Noir is backend agnostic, which means it makes no assumptions on which proving backend powers the ZK proof. Being the language that powers [Aztec Contracts](https://docs.aztec.network/developers/contracts/main), it defaults to Aztec's Barretenberg proving backend. + +However, the ACIR output can be transformed to be compatible with other PLONK-based backends, or into a [rank-1 constraint system](https://www.rareskills.io/post/rank-1-constraint-system) suitable for backends such as Arkwork's Marlin. + +::: + +## Who is Noir for? + +Noir can be used both in complex cloud-based backends and in user's smartphones, requiring no knowledge on the underlying math or cryptography. From authorization systems that keep a password in the user's device, to complex on-chain verification of recursive proofs, Noir is designed to abstract away complexity without any significant overhead. Here are some examples of situations where Noir can be used: + + + + Noir Logo + + Aztec Contracts leverage Noir to allow for the storage and execution of private information. Writing an Aztec Contract is as easy as writing Noir, and Aztec developers can easily interact with the network storage and execution through the [Aztec.nr](https://docs.aztec.network/developers/contracts/main) library. + + + Soliditry Verifier Example + Noir can auto-generate Solidity verifier contracts that verify Noir proofs. This allows for non-interactive verification of proofs containing private information in an immutable system. This feature powers a multitude of use-case scenarios, from P2P chess tournaments, to [Aztec Layer-2 Blockchain](https://docs.aztec.network/) + + + Aztec Labs developed NoirJS, an easy interface to generate and verify Noir proofs in a Javascript environment. This allows for Noir to be used in webpages, mobile apps, games, and any other environment supporting JS execution in a standalone manner. + + + + +## Libraries + +Noir is meant to be easy to extend by simply importing Noir libraries just like in Rust. +The [awesome-noir repo](https://github.com/noir-lang/awesome-noir#libraries) is a collection of libraries developed by the Noir community. +Writing a new library is easy and makes code be composable and easy to reuse. See the section on [dependencies](noir/modules_packages_crates/dependencies.md) for more information. diff --git a/docs/versioned_docs/version-v0.26.0/migration_notes.md b/docs/versioned_docs/version-v0.26.0/migration_notes.md new file mode 100644 index 00000000000..6bd740024e5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/migration_notes.md @@ -0,0 +1,105 @@ +--- +title: Migration notes +description: Read about migration notes from previous versions, which could solve problems while updating +keywords: [Noir, notes, migration, updating, upgrading] +--- + +Noir is in full-speed development. Things break fast, wild, and often. This page attempts to leave some notes on errors you might encounter when upgrading and how to resolve them until proper patches are built. + +### `backend encountered an error: libc++.so.1` + +Depending on your OS, you may encounter the following error when running `nargo prove` for the first time: + +```text +The backend encountered an error: "/home/codespace/.nargo/backends/acvm-backend-barretenberg/backend_binary: error while loading shared libraries: libc++.so.1: cannot open shared object file: No such file or directory\n" +``` + +Install the `libc++-dev` library with: + +```bash +sudo apt install libc++-dev +``` + +## ≥0.19 + +### Enforcing `compiler_version` + +From this version on, the compiler will check for the `compiler_version` field in `Nargo.toml`, and will error if it doesn't match the current Nargo version in use. + +To update, please make sure this field in `Nargo.toml` matches the output of `nargo --version`. + +## ≥0.14 + +The index of the [for loops](noir/concepts/control_flow.md#loops) is now of type `u64` instead of `Field`. An example refactor would be: + +```rust +for i in 0..10 { + let i = i as Field; +} +``` + +## ≥v0.11.0 and Nargo backend + +From this version onwards, Nargo starts managing backends through the `nargo backend` command. Upgrading to the versions per usual steps might lead to: + +### `backend encountered an error` + +This is likely due to the existing locally installed version of proving backend (e.g. barretenberg) is incompatible with the version of Nargo in use. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo prove +``` + +with your Noir program. + +This will trigger the download and installation of the latest version of barretenberg compatible with your Nargo in use. + +### `backend encountered an error: illegal instruction` + +On certain Intel-based systems, an `illegal instruction` error may arise due to incompatibility of barretenberg with certain CPU instructions. + +To fix the issue: + +1. Uninstall the existing backend + +```bash +nargo backend uninstall acvm-backend-barretenberg +``` + +You may replace _acvm-backend-barretenberg_ with the name of your backend listed in `nargo backend ls` or in ~/.nargo/backends. + +2. Reinstall a compatible version of the proving backend. + +If you are using the default barretenberg backend, simply run: + +``` +nargo backend install acvm-backend-barretenberg https://github.com/noir-lang/barretenberg-js-binary/raw/master/run-bb.tar.gz +``` + +This downloads and installs a specific bb.js based version of barretenberg binary from GitHub. + +The gzipped file is running [this bash script](https://github.com/noir-lang/barretenberg-js-binary/blob/master/run-bb-js.sh), where we need to gzip it as the Nargo currently expect the backend to be zipped up. + +Then run: + +``` +DESIRED_BINARY_VERSION=0.8.1 nargo info +``` + +This overrides the bb native binary with a bb.js node application instead, which should be compatible with most if not all hardware. This does come with the drawback of being generally slower than native binary. + +0.8.1 indicates bb.js version 0.8.1, so if you change that it will update to a different version or the default version in the script if none was supplied. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/_category_.json b/docs/versioned_docs/version-v0.26.0/noir/concepts/_category_.json new file mode 100644 index 00000000000..7da08f8a8c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Concepts", + "position": 0, + "collapsible": true, + "collapsed": true +} \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/assert.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/assert.md new file mode 100644 index 00000000000..bcff613a695 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/assert.md @@ -0,0 +1,45 @@ +--- +title: Assert Function +description: + Learn about the assert function in Noir, which can be used to explicitly constrain the predicate or + comparison expression that follows to be true, and what happens if the expression is false at + runtime. +keywords: [Noir programming language, assert statement, predicate expression, comparison expression] +sidebar_position: 4 +--- + +Noir includes a special `assert` function which will explicitly constrain the predicate/comparison +expression that follows to be true. If this expression is false at runtime, the program will fail to +be proven. Example: + +```rust +fn main(x : Field, y : Field) { + assert(x == y); +} +``` + +> Assertions only work for predicate operations, such as `==`. If there's any ambiguity on the operation, the program will fail to compile. For example, it is unclear if `assert(x + y)` would check for `x + y == 0` or simply would return `true`. + +You can optionally provide a message to be logged when the assertion fails: + +```rust +assert(x == y, "x and y are not equal"); +``` + +Aside string literals, the optional message can be a format string or any other type supported as input for Noir's [print](../standard_library/logging.md) functions. This feature lets you incorporate runtime variables into your failed assertion logs: + +```rust +assert(x == y, f"Expected x == y, but got {x} == {y}"); +``` + +Using a variable as an assertion message directly: + +```rust +struct myStruct { + myField: Field +} + +let s = myStruct { myField: y }; +assert(s.myField == x, s); +``` + diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/comments.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/comments.md new file mode 100644 index 00000000000..b51a85f5c94 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/comments.md @@ -0,0 +1,33 @@ +--- +title: Comments +description: + Learn how to write comments in Noir programming language. A comment is a line of code that is + ignored by the compiler, but it can be read by programmers. Single-line and multi-line comments + are supported in Noir. +keywords: [Noir programming language, comments, single-line comments, multi-line comments] +sidebar_position: 10 +--- + +A comment is a line in your codebase which the compiler ignores, however it can be read by +programmers. + +Here is a single line comment: + +```rust +// This is a comment and is ignored +``` + +`//` is used to tell the compiler to ignore the rest of the line. + +Noir also supports multi-line block comments. Start a block comment with `/*` and end the block with `*/`. + +Noir does not natively support doc comments. You may be able to use [Rust doc comments](https://doc.rust-lang.org/reference/comments.html) in your code to leverage some Rust documentation build tools with Noir code. + +```rust +/* + This is a block comment describing a complex function. +*/ +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/control_flow.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/control_flow.md new file mode 100644 index 00000000000..045d3c3a5f5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/control_flow.md @@ -0,0 +1,77 @@ +--- +title: Control Flow +description: + Learn how to use loops and if expressions in the Noir programming language. Discover the syntax + and examples for for loops and if-else statements. +keywords: [Noir programming language, loops, for loop, if-else statements, Rust syntax] +sidebar_position: 2 +--- + +## If Expressions + +Noir supports `if-else` statements. The syntax is most similar to Rust's where it is not required +for the statement's conditional to be surrounded by parentheses. + +```rust +let a = 0; +let mut x: u32 = 0; + +if a == 0 { + if a != 0 { + x = 6; + } else { + x = 2; + } +} else { + x = 5; + assert(x == 5); +} +assert(x == 2); +``` + +## Loops + +Noir has one kind of loop: the `for` loop. `for` loops allow you to repeat a block of code multiple +times. + +The following block of code between the braces is run 10 times. + +```rust +for i in 0..10 { + // do something +} +``` + +The index for loops is of type `u64`. + +### Break and Continue + +In unconstrained code, `break` and `continue` are also allowed in `for` loops. These are only allowed +in unconstrained code since normal constrained code requires that Noir knows exactly how many iterations +a loop may have. `break` and `continue` can be used like so: + +```rust +for i in 0 .. 10 { + println("Iteration start") + + if i == 2 { + continue; + } + + if i == 5 { + break; + } + + println(i); +} +println("Loop end") +``` + +When used, `break` will end the current loop early and jump to the statement after the for loop. In the example +above, the `break` will stop the loop and jump to the `println("Loop end")`. + +`continue` will stop the current iteration of the loop, and jump to the start of the next iteration. In the example +above, `continue` will jump to `println("Iteration start")` when used. Note that the loop continues as normal after this. +The iteration variable `i` is still increased by one as normal when `continue` is used. + +`break` and `continue` cannot currently be used to jump out of more than a single loop at a time. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_bus.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_bus.md new file mode 100644 index 00000000000..e54fc861257 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_bus.md @@ -0,0 +1,21 @@ +--- +title: Data Bus +sidebar_position: 13 +--- +**Disclaimer** this feature is experimental, do not use it! + +The data bus is an optimization that the backend can use to make recursion more efficient. +In order to use it, you must define some inputs of the program entry points (usually the `main()` +function) with the `call_data` modifier, and the return values with the `return_data` modifier. +These modifiers are incompatible with `pub` and `mut` modifiers. + +## Example + +```rust +fn main(mut x: u32, y: call_data u32, z: call_data [u32;4] ) -> return_data u32 { + let a = z[x]; + a+y +} +``` + +As a result, both call_data and return_data will be treated as private inputs and encapsulated into a read-only array each, for the backend to process. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/_category_.json b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md new file mode 100644 index 00000000000..efce3e95d32 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/arrays.md @@ -0,0 +1,251 @@ +--- +title: Arrays +description: + Dive into the Array data type in Noir. Grasp its methods, practical examples, and best practices for efficiently using Arrays in your Noir code. +keywords: + [ + noir, + array type, + methods, + examples, + indexing, + ] +sidebar_position: 4 +--- + +An array is one way of grouping together values into one compound type. Array types can be inferred +or explicitly specified via the syntax `[; ]`: + +```rust +fn main(x : Field, y : Field) { + let my_arr = [x, y]; + let your_arr: [Field; 2] = [x, y]; +} +``` + +Here, both `my_arr` and `your_arr` are instantiated as an array containing two `Field` elements. + +Array elements can be accessed using indexing: + +```rust +fn main() { + let a = [1, 2, 3, 4, 5]; + + let first = a[0]; + let second = a[1]; +} +``` + +All elements in an array must be of the same type (i.e. homogeneous). That is, an array cannot group +a `Field` value and a `u8` value together for example. + +You can write mutable arrays, like: + +```rust +fn main() { + let mut arr = [1, 2, 3, 4, 5]; + assert(arr[0] == 1); + + arr[0] = 42; + assert(arr[0] == 42); +} +``` + +You can instantiate a new array of a fixed size with the same value repeated for each element. The following example instantiates an array of length 32 where each element is of type Field and has the value 0. + +```rust +let array: [Field; 32] = [0; 32]; +``` + +Like in Rust, arrays in Noir are a fixed size. However, if you wish to convert an array to a [slice](./slices), you can just call `as_slice` on your array: + +```rust +let array: [Field; 32] = [0; 32]; +let sl = array.as_slice() +``` + +You can define multidimensional arrays: + +```rust +let array : [[Field; 2]; 2]; +let element = array[0][0]; +``` +However, multidimensional slices are not supported. For example, the following code will error at compile time: +```rust +let slice : [[Field]] = &[]; +``` + +## Types + +You can create arrays of primitive types or structs. There is not yet support for nested arrays +(arrays of arrays) or arrays of structs that contain arrays. + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for arrays. +Each of these functions are located within the generic impl `impl [T; N] {`. +So anywhere `self` appears, it refers to the variable `self: [T; N]`. + +### len + +Returns the length of an array + +```rust +fn len(self) -> Field +``` + +example + +```rust +fn main() { + let array = [42, 42]; + assert(array.len() == 2); +} +``` + +### sort + +Returns a new sorted array. The original array remains untouched. Notice that this function will +only work for arrays of fields or integers, not for any arbitrary type. This is because the sorting +logic it uses internally is optimized specifically for these values. If you need a sort function to +sort any type, you should use the function `sort_via` described below. + +```rust +fn sort(self) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32]; + let sorted = arr.sort(); + assert(sorted == [32, 42]); +} +``` + +### sort_via + +Sorts the array with a custom comparison function + +```rust +fn sort_via(self, ordering: fn(T, T) -> bool) -> [T; N] +``` + +example + +```rust +fn main() { + let arr = [42, 32] + let sorted_ascending = arr.sort_via(|a, b| a < b); + assert(sorted_ascending == [32, 42]); // verifies + + let sorted_descending = arr.sort_via(|a, b| a > b); + assert(sorted_descending == [32, 42]); // does not verify +} +``` + +### map + +Applies a function to each element of the array, returning a new array containing the mapped elements. + +```rust +fn map(self, f: fn(T) -> U) -> [U; N] +``` + +example + +```rust +let a = [1, 2, 3]; +let b = a.map(|a| a * 2); // b is now [2, 4, 6] +``` + +### fold + +Applies a function to each element of the array, returning the final accumulated value. The first +parameter is the initial value. + +```rust +fn fold(self, mut accumulator: U, f: fn(U, T) -> U) -> U +``` + +This is a left fold, so the given function will be applied to the accumulator and first element of +the array, then the second, and so on. For a given call the expected result would be equivalent to: + +```rust +let a1 = [1]; +let a2 = [1, 2]; +let a3 = [1, 2, 3]; + +let f = |a, b| a - b; +a1.fold(10, f) //=> f(10, 1) +a2.fold(10, f) //=> f(f(10, 1), 2) +a3.fold(10, f) //=> f(f(f(10, 1), 2), 3) +``` + +example: + +```rust + +fn main() { + let arr = [2, 2, 2, 2, 2]; + let folded = arr.fold(0, |a, b| a + b); + assert(folded == 10); +} + +``` + +### reduce + +Same as fold, but uses the first element as starting element. + +```rust +fn reduce(self, f: fn(T, T) -> T) -> T +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let reduced = arr.reduce(|a, b| a + b); + assert(reduced == 10); +} +``` + +### all + +Returns true if all the elements satisfy the given predicate + +```rust +fn all(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 2]; + let all = arr.all(|a| a == 2); + assert(all); +} +``` + +### any + +Returns true if any of the elements satisfy the given predicate + +```rust +fn any(self, predicate: fn(T) -> bool) -> bool +``` + +example: + +```rust +fn main() { + let arr = [2, 2, 2, 2, 5]; + let any = arr.any(|a| a == 5); + assert(any); +} + +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md new file mode 100644 index 00000000000..69826fcd724 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/booleans.md @@ -0,0 +1,31 @@ +--- +title: Booleans +description: + Delve into the Boolean data type in Noir. Understand its methods, practical examples, and best practices for using Booleans in your Noir programs. +keywords: + [ + noir, + boolean type, + methods, + examples, + logical operations, + ] +sidebar_position: 2 +--- + + +The `bool` type in Noir has two possible values: `true` and `false`: + +```rust +fn main() { + let t = true; + let f: bool = false; +} +``` + +> **Note:** When returning a boolean value, it will show up as a value of 1 for `true` and 0 for +> `false` in _Verifier.toml_. + +The boolean type is most commonly used in conditionals like `if` expressions and `assert` +statements. More about conditionals is covered in the [Control Flow](../control_flow) and +[Assert Function](../assert) sections. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/fields.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/fields.md new file mode 100644 index 00000000000..a10a4810788 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/fields.md @@ -0,0 +1,192 @@ +--- +title: Fields +description: + Dive deep into the Field data type in Noir. Understand its methods, practical examples, and best practices to effectively use Fields in your Noir programs. +keywords: + [ + noir, + field type, + methods, + examples, + best practices, + ] +sidebar_position: 0 +--- + +The field type corresponds to the native field type of the proving backend. + +The size of a Noir field depends on the elliptic curve's finite field for the proving backend +adopted. For example, a field would be a 254-bit integer when paired with the default backend that +spans the Grumpkin curve. + +Fields support integer arithmetic and are often used as the default numeric type in Noir: + +```rust +fn main(x : Field, y : Field) { + let z = x + y; +} +``` + +`x`, `y` and `z` are all private fields in this example. Using the `let` keyword we defined a new +private value `z` constrained to be equal to `x + y`. + +If proving efficiency is of priority, fields should be used as a default for solving problems. +Smaller integer types (e.g. `u64`) incur extra range constraints. + +## Methods + +After declaring a Field, you can use these common methods on it: + +### to_le_bits + +Transforms the field into an array of bits, Little Endian. + +```rust +fn to_le_bits(_x : Field, _bit_size: u32) -> [u1] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_le_bits(32); +} +``` + +### to_be_bits + +Transforms the field into an array of bits, Big Endian. + +```rust +fn to_be_bits(_x : Field, _bit_size: u32) -> [u1] +``` + +example: + +```rust +fn main() { + let field = 2; + let bits = field.to_be_bits(32); +} +``` + +### to_le_bytes + +Transforms into an array of bytes, Little Endian + +```rust +fn to_le_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_le_bytes(4); +} +``` + +### to_be_bytes + +Transforms into an array of bytes, Big Endian + +```rust +fn to_be_bytes(_x : Field, byte_size: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let bytes = field.to_be_bytes(4); +} +``` + +### to_le_radix + +Decomposes into a vector over the specified base, Little Endian + +```rust +fn to_le_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_le_radix(256, 4); +} +``` + +### to_be_radix + +Decomposes into a vector over the specified base, Big Endian + +```rust +fn to_be_radix(_x : Field, _radix: u32, _result_len: u32) -> [u8] +``` + +example: + +```rust +fn main() { + let field = 2; + let radix = field.to_be_radix(256, 4); +} +``` + +### pow_32 + +Returns the value to the power of the specified exponent + +```rust +fn pow_32(self, exponent: Field) -> Field +``` + +example: + +```rust +fn main() { + let field = 2 + let pow = field.pow_32(4); + assert(pow == 16); +} +``` + +### assert_max_bit_size + +Adds a constraint to specify that the field can be represented with `bit_size` number of bits + +```rust +fn assert_max_bit_size(self, bit_size: u32) +``` + +example: + +```rust +fn main() { + let field = 2 + field.assert_max_bit_size(32); +} +``` + +### sgn0 + +Parity of (prime) Field element, i.e. sgn0(x mod p) = 0 if x ∈ \{0, ..., p-1\} is even, otherwise sgn0(x mod p) = 1. + +```rust +fn sgn0(self) -> u1 +``` + + +### lt + +Returns true if the field is less than the other field + +```rust +pub fn lt(self, another: Field) -> bool +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/function_types.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/function_types.md new file mode 100644 index 00000000000..f6121af17e2 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/function_types.md @@ -0,0 +1,26 @@ +--- +title: Function types +sidebar_position: 10 +--- + +Noir supports higher-order functions. The syntax for a function type is as follows: + +```rust +fn(arg1_type, arg2_type, ...) -> return_type +``` + +Example: + +```rust +fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field + assert(f() == 100); +} + +fn main() { + assert_returns_100(|| 100); // ok + assert_returns_100(|| 150); // fails +} +``` + +A function type also has an optional capture environment - this is necessary to support closures. +See [Lambdas](../lambdas.md) for more details. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/index.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/index.md new file mode 100644 index 00000000000..357813c147a --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/index.md @@ -0,0 +1,110 @@ +--- +title: Data Types +description: + Get a clear understanding of the two categories of Noir data types - primitive types and compound + types. Learn about their characteristics, differences, and how to use them in your Noir + programming. +keywords: + [ + noir, + data types, + primitive types, + compound types, + private types, + public types, + ] +--- + +Every value in Noir has a type, which determines which operations are valid for it. + +All values in Noir are fundamentally composed of `Field` elements. For a more approachable +developing experience, abstractions are added on top to introduce different data types in Noir. + +Noir has two category of data types: primitive types (e.g. `Field`, integers, `bool`) and compound +types that group primitive types (e.g. arrays, tuples, structs). Each value can either be private or +public. + +## Private & Public Types + +A **private value** is known only to the Prover, while a **public value** is known by both the +Prover and Verifier. Mark values as `private` when the value should only be known to the prover. All +primitive types (including individual fields of compound types) in Noir are private by default, and +can be marked public when certain values are intended to be revealed to the Verifier. + +> **Note:** For public values defined in Noir programs paired with smart contract verifiers, once +> the proofs are verified on-chain the values can be considered known to everyone that has access to +> that blockchain. + +Public data types are treated no differently to private types apart from the fact that their values +will be revealed in proofs generated. Simply changing the value of a public type will not change the +circuit (where the same goes for changing values of private types as well). + +_Private values_ are also referred to as _witnesses_ sometimes. + +> **Note:** The terms private and public when applied to a type (e.g. `pub Field`) have a different +> meaning than when applied to a function (e.g. `pub fn foo() {}`). +> +> The former is a visibility modifier for the Prover to interpret if a value should be made known to +> the Verifier, while the latter is a visibility modifier for the compiler to interpret if a +> function should be made accessible to external Noir programs like in other languages. + +### pub Modifier + +All data types in Noir are private by default. Types are explicitly declared as public using the +`pub` modifier: + +```rust +fn main(x : Field, y : pub Field) -> pub Field { + x + y +} +``` + +In this example, `x` is **private** while `y` and `x + y` (the return value) are **public**. Note +that visibility is handled **per variable**, so it is perfectly valid to have one input that is +private and another that is public. + +> **Note:** Public types can only be declared through parameters on `main`. + +## Type Aliases + +A type alias is a new name for an existing type. Type aliases are declared with the keyword `type`: + +```rust +type Id = u8; + +fn main() { + let id: Id = 1; + let zero: u8 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can also be used with [generics](../generics.md): + +```rust +type Id = Size; + +fn main() { + let id: Id = 1; + let zero: u32 = 0; + assert(zero + 1 == id); +} +``` + +Type aliases can even refer to other aliases. An error will be issued if they form a cycle: + +```rust +// Ok! +type A = B; +type B = Field; + +type Bad1 = Bad2; + +// error: Dependency cycle found +type Bad2 = Bad1; +// ^^^^^^^^^^^ 'Bad2' recursively depends on itself: Bad2 -> Bad1 -> Bad2 +``` + +### BigInt + +You can achieve BigInt functionality using the [Noir BigInt](https://github.com/shuklaayush/noir-bigint) library. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/integers.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/integers.md new file mode 100644 index 00000000000..1c6b375db49 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/integers.md @@ -0,0 +1,155 @@ +--- +title: Integers +description: Explore the Integer data type in Noir. Learn about its methods, see real-world examples, and grasp how to efficiently use Integers in your Noir code. +keywords: [noir, integer types, methods, examples, arithmetic] +sidebar_position: 1 +--- + +An integer type is a range constrained field type. The Noir frontend supports both unsigned and signed integer types. The allowed sizes are 1, 8, 32 and 64 bits. + +:::info + +When an integer is defined in Noir without a specific type, it will default to `Field`. + +The one exception is for loop indices which default to `u64` since comparisons on `Field`s are not possible. + +::: + +## Unsigned Integers + +An unsigned integer type is specified first with the letter `u` (indicating its unsigned nature) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: u8 = 1; + let y: u8 = 1; + let z = x + y; + assert (z == 2); +} +``` + +The bit size determines the maximum value the integer type can store. For example, a `u8` variable can store a value in the range of 0 to 255 (i.e. $\\2^{8}-1\\$). + +## Signed Integers + +A signed integer type is specified first with the letter `i` (which stands for integer) followed by its bit size (e.g. `8`): + +```rust +fn main() { + let x: i8 = -1; + let y: i8 = -1; + let z = x + y; + assert (z == -2); +} +``` + +The bit size determines the maximum and minimum range of value the integer type can store. For example, an `i8` variable can store a value in the range of -128 to 127 (i.e. $\\-2^{7}\\$ to $\\2^{7}-1\\$). + +## 128 bits Unsigned Integers + +The built-in structure `U128` allows you to use 128-bit unsigned integers almost like a native integer type. However, there are some differences to keep in mind: +- You cannot cast between a native integer and `U128` +- There is a higher performance cost when using `U128`, compared to a native type. + +Conversion between unsigned integer types and U128 are done through the use of `from_integer` and `to_integer` functions. `from_integer` also accepts the `Field` type as input. + +```rust +fn main() { + let x = U128::from_integer(23); + let y = U128::from_hex("0x7"); + let z = x + y; + assert(z.to_integer() == 30); +} +``` + +`U128` is implemented with two 64 bits limbs, representing the low and high bits, which explains the performance cost. You should expect `U128` to be twice more costly for addition and four times more costly for multiplication. +You can construct a U128 from its limbs: +```rust +fn main(x: u64, y: u64) { + let x = U128::from_u64s_be(x,y); + assert(z.hi == x as Field); + assert(z.lo == y as Field); +} +``` + +Note that the limbs are stored as Field elements in order to avoid unnecessary conversions. +Apart from this, most operations will work as usual: + +```rust +fn main(x: U128, y: U128) { + // multiplication + let c = x * y; + // addition and subtraction + let c = c - x + y; + // division + let c = x / y; + // bit operation; + let c = x & y | y; + // bit shift + let c = x << y; + // comparisons; + let c = x < y; + let c = x == y; +} +``` + +## Overflows + +Computations that exceed the type boundaries will result in overflow errors. This happens with both signed and unsigned integers. For example, attempting to prove: + +```rust +fn main(x: u8, y: u8) { + let z = x + y; +} +``` + +With: + +```toml +x = "255" +y = "1" +``` + +Would result in: + +``` +$ nargo prove +error: Assertion failed: 'attempt to add with overflow' +┌─ ~/src/main.nr:9:13 +│ +│ let z = x + y; +│ ----- +│ += Call stack: + ... +``` + +A similar error would happen with signed integers: + +```rust +fn main() { + let x: i8 = -118; + let y: i8 = -11; + let z = x + y; +} +``` + +### Wrapping methods + +Although integer overflow is expected to error, some use-cases rely on wrapping. For these use-cases, the standard library provides `wrapping` variants of certain common operations: + +```rust +fn wrapping_add(x: T, y: T) -> T; +fn wrapping_sub(x: T, y: T) -> T; +fn wrapping_mul(x: T, y: T) -> T; +``` + +Example of how it is used: + +```rust +use dep::std; + +fn main(x: u8, y: u8) -> pub u8 { + std::wrapping_add(x, y) +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/references.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/references.md new file mode 100644 index 00000000000..a5293d11cfb --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/references.md @@ -0,0 +1,23 @@ +--- +title: References +sidebar_position: 9 +--- + +Noir supports first-class references. References are a bit like pointers: they point to a specific address that can be followed to access the data stored at that address. You can use Rust-like syntax to use pointers in Noir: the `&` operator references the variable, the `*` operator dereferences it. + +Example: + +```rust +fn main() { + let mut x = 2; + + // you can reference x as &mut and pass it to multiplyBy2 + multiplyBy2(&mut x); +} + +// you can access &mut here +fn multiplyBy2(x: &mut Field) { + // and dereference it with * + *x = *x * 2; +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/slices.mdx b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/slices.mdx new file mode 100644 index 00000000000..828faf4a8f8 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/slices.mdx @@ -0,0 +1,170 @@ +--- +title: Slices +description: Explore the Slice data type in Noir. Understand its methods, see real-world examples, and learn how to effectively use Slices in your Noir programs. +keywords: [noir, slice type, methods, examples, subarrays] +sidebar_position: 5 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A slice is a dynamically-sized view into a sequence of elements. They can be resized at runtime, but because they don't own the data, they cannot be returned from a circuit. You can treat slices as arrays without a constrained size. + +```rust +use dep::std::slice; + +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +To write a slice literal, use a preceeding ampersand as in: `&[0; 2]` or +`&[1, 2, 3]`. + +It is important to note that slices are not references to arrays. In Noir, +`&[..]` is more similar to an immutable, growable vector. + +View the corresponding test file [here][test-file]. + +[test-file]: https://github.com/noir-lang/noir/blob/f387ec1475129732f72ba294877efdf6857135ac/crates/nargo_cli/tests/test_data_ssa_refactor/slices/src/main.nr + +## Methods + +For convenience, the STD provides some ready-to-use, common methods for slices: + +### push_back + +Pushes a new element to the end of the slice, returning a new slice with a length one greater than the original unmodified slice. + +```rust +fn push_back(_self: [T], _elem: T) -> [T] +``` + +example: + +```rust +fn main() -> pub Field { + let mut slice: [Field] = &[0; 2]; + + let mut new_slice = slice.push_back(6); + new_slice.len() +} +``` + +View the corresponding test file [here][test-file]. + +### push_front + +Returns a new array with the specified element inserted at index 0. The existing elements indexes are incremented by 1. + +```rust +fn push_front(_self: Self, _elem: T) -> Self +``` + +Example: + +```rust +let mut new_slice: [Field] = &[]; +new_slice = new_slice.push_front(20); +assert(new_slice[0] == 20); // returns true +``` + +View the corresponding test file [here][test-file]. + +### pop_front + +Returns a tuple of two items, the first element of the array and the rest of the array. + +```rust +fn pop_front(_self: Self) -> (T, Self) +``` + +Example: + +```rust +let (first_elem, rest_of_slice) = slice.pop_front(); +``` + +View the corresponding test file [here][test-file]. + +### pop_back + +Returns a tuple of two items, the beginning of the array with the last element omitted and the last element. + +```rust +fn pop_back(_self: Self) -> (Self, T) +``` + +Example: + +```rust +let (popped_slice, last_elem) = slice.pop_back(); +``` + +View the corresponding test file [here][test-file]. + +### append + +Loops over a slice and adds it to the end of another. + +```rust +fn append(mut self, other: Self) -> Self +``` + +Example: + +```rust +let append = &[1, 2].append(&[3, 4, 5]); +``` + +### insert + +Inserts an element at a specified index and shifts all following elements by 1. + +```rust +fn insert(_self: Self, _index: Field, _elem: T) -> Self +``` + +Example: + +```rust +new_slice = rest_of_slice.insert(2, 100); +assert(new_slice[2] == 100); +``` + +View the corresponding test file [here][test-file]. + +### remove + +Remove an element at a specified index, shifting all elements after it to the left, returning the altered slice and the removed element. + +```rust +fn remove(_self: Self, _index: Field) -> (Self, T) +``` + +Example: + +```rust +let (remove_slice, removed_elem) = slice.remove(3); +``` + +### len + +Returns the length of a slice + +```rust +fn len(self) -> Field +``` + +Example: + +```rust +fn main() { + let slice = &[42, 42]; + assert(slice.len() == 2); +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md new file mode 100644 index 00000000000..311dfd64416 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/strings.md @@ -0,0 +1,80 @@ +--- +title: Strings +description: + Discover the String data type in Noir. Learn about its methods, see real-world examples, and understand how to effectively manipulate and use Strings in Noir. +keywords: + [ + noir, + string type, + methods, + examples, + concatenation, + ] +sidebar_position: 3 +--- + + +The string type is a fixed length value defined with `str`. + +You can use strings in `assert()` functions or print them with +`println()`. See more about [Logging](../../standard_library/logging). + +```rust +use dep::std; + +fn main(message : pub str<11>, hex_as_string : str<4>) { + println(message); + assert(message == "hello world"); + assert(hex_as_string == "0x41"); +} +``` + +You can convert a `str` to a byte array by calling `as_bytes()` +or a vector by calling `as_bytes_vec()`. + +```rust +fn main() { + let message = "hello world"; + let message_bytes = message.as_bytes(); + let mut message_vec = message.as_bytes_vec(); + assert(message_bytes.len() == 11); + assert(message_bytes[0] == 104); + assert(message_bytes[0] == message_vec.get(0)); +} +``` + +## Escape characters + +You can use escape characters for your strings: + +| Escape Sequence | Description | +|-----------------|-----------------| +| `\r` | Carriage Return | +| `\n` | Newline | +| `\t` | Tab | +| `\0` | Null Character | +| `\"` | Double Quote | +| `\\` | Backslash | + +Example: + +```rust +let s = "Hello \"world" // prints "Hello "world" +let s = "hey \tyou"; // prints "hey you" +``` + +## Raw strings + +A raw string begins with the letter `r` and is optionally delimited by a number of hashes `#`. + +Escape characters are *not* processed within raw strings. All contents are interpreted literally. + +Example: + +```rust +let s = r"Hello world"; +let s = r#"Simon says "hello world""#; + +// Any number of hashes may be used (>= 1) as long as the string also terminates with the same number of hashes +let s = r#####"One "#, Two "##, Three "###, Four "####, Five will end the string."#####; +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/structs.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/structs.md new file mode 100644 index 00000000000..dbf68c99813 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/structs.md @@ -0,0 +1,70 @@ +--- +title: Structs +description: + Explore the Struct data type in Noir. Learn about its methods, see real-world examples, and grasp how to effectively define and use Structs in your Noir programs. +keywords: + [ + noir, + struct type, + methods, + examples, + data structures, + ] +sidebar_position: 8 +--- + +A struct also allows for grouping multiple values of different types. Unlike tuples, we can also +name each field. + +> **Note:** The usage of _field_ here refers to each element of the struct and is unrelated to the +> field type of Noir. + +Defining a struct requires giving it a name and listing each field within as `: ` pairs: + +```rust +struct Animal { + hands: Field, + legs: Field, + eyes: u8, +} +``` + +An instance of a struct can then be created with actual values in `: ` pairs in any +order. Struct fields are accessible using their given names: + +```rust +fn main() { + let legs = 4; + + let dog = Animal { + eyes: 2, + hands: 0, + legs, + }; + + let zero = dog.hands; +} +``` + +Structs can also be destructured in a pattern, binding each field to a new variable: + +```rust +fn main() { + let Animal { hands, legs: feet, eyes } = get_octopus(); + + let ten = hands + feet + eyes as u8; +} + +fn get_octopus() -> Animal { + let octopus = Animal { + hands: 0, + legs: 8, + eyes: 2, + }; + + octopus +} +``` + +The new variables can be bound with names different from the original struct field names, as +showcased in the `legs --> feet` binding in the example above. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/tuples.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/tuples.md new file mode 100644 index 00000000000..2ec5c9c4113 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/data_types/tuples.md @@ -0,0 +1,48 @@ +--- +title: Tuples +description: + Dive into the Tuple data type in Noir. Understand its methods, practical examples, and best practices for efficiently using Tuples in your Noir code. +keywords: + [ + noir, + tuple type, + methods, + examples, + multi-value containers, + ] +sidebar_position: 7 +--- + +A tuple collects multiple values like an array, but with the added ability to collect values of +different types: + +```rust +fn main() { + let tup: (u8, u64, Field) = (255, 500, 1000); +} +``` + +One way to access tuple elements is via destructuring using pattern matching: + +```rust +fn main() { + let tup = (1, 2); + + let (one, two) = tup; + + let three = one + two; +} +``` + +Another way to access tuple elements is via direct member access, using a period (`.`) followed by +the index of the element we want to access. Index `0` corresponds to the first tuple element, `1` to +the second and so on: + +```rust +fn main() { + let tup = (5, 6, 7, 8); + + let five = tup.0; + let eight = tup.3; +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/distinct.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/distinct.md new file mode 100644 index 00000000000..6c993b8b5e0 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/distinct.md @@ -0,0 +1,64 @@ +--- +title: Distinct Witnesses +sidebar_position: 11 +--- + +The `distinct` keyword prevents repetitions of witness indices in the program's ABI. This ensures +that the witnesses being returned as public inputs are all unique. + +The `distinct` keyword is only used for return values on program entry points (usually the `main()` +function). + +When using `distinct` and `pub` simultaneously, `distinct` comes first. See the example below. + +You can read more about the problem this solves +[here](https://github.com/noir-lang/noir/issues/1183). + +## Example + +Without the `distinct` keyword, the following program + +```rust +fn main(x : pub Field, y : pub Field) -> pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + "return_witnesses": [3, 2, 4, 4] + } +} +``` + +Whereas (with the `distinct` keyword) + +```rust +fn main(x : pub Field, y : pub Field) -> distinct pub [Field; 4] { + let a = 1; + let b = 1; + [x + 1, y, a, b] +} +``` + +compiles to + +```json +{ + //... + "abi": { + //... + "param_witnesses": { "x": [1], "y": [2] }, + //... + "return_witnesses": [3, 4, 5, 6] + } +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/functions.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/functions.md new file mode 100644 index 00000000000..2c9bc33fdfc --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/functions.md @@ -0,0 +1,226 @@ +--- +title: Functions +description: + Learn how to declare functions and methods in Noir, a programming language with Rust semantics. + This guide covers parameter declaration, return types, call expressions, and more. +keywords: [Noir, Rust, functions, methods, parameter declaration, return types, call expressions] +sidebar_position: 1 +--- + +Functions in Noir follow the same semantics of Rust, though Noir does not support early returns. + +To declare a function the `fn` keyword is used. + +```rust +fn foo() {} +``` + +By default, functions are visible only within the package they are defined. To make them visible outside of that package (for example, as part of a [library](../modules_packages_crates/crates_and_packages.md#libraries)), you should mark them as `pub`: + +```rust +pub fn foo() {} +``` + +You can also restrict the visibility of the function to only the crate it was defined in, by specifying `pub(crate)`: + +```rust +pub(crate) fn foo() {} //foo can only be called within its crate +``` + +All parameters in a function must have a type and all types are known at compile time. The parameter +is pre-pended with a colon and the parameter type. Multiple parameters are separated using a comma. + +```rust +fn foo(x : Field, y : Field){} +``` + +The return type of a function can be stated by using the `->` arrow notation. The function below +states that the foo function must return a `Field`. If the function returns no value, then the arrow +is omitted. + +```rust +fn foo(x : Field, y : Field) -> Field { + x + y +} +``` + +Note that a `return` keyword is unneeded in this case - the last expression in a function's body is +returned. + +## Main function + +If you're writing a binary, the `main` function is the starting point of your program. You can pass all types of expressions to it, as long as they have a fixed size at compile time: + +```rust +fn main(x : Field) // this is fine: passing a Field +fn main(x : [Field; 2]) // this is also fine: passing a Field with known size at compile-time +fn main(x : (Field, bool)) // 👌: passing a (Field, bool) tuple means size 2 +fn main(x : str<5>) // this is fine, as long as you pass a string of size 5 + +fn main(x : Vec) // can't compile, has variable size +fn main(x : [Field]) // can't compile, has variable size +fn main(....// i think you got it by now +``` + +Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: + +```rust +fn main(x : [Field]) { + assert(x[0] == 1); +} + +#[test] +fn test_one() { + main(&[1, 2]); +} +``` + +```bash +$ nargo test +[testing] Running 1 test functions +[testing] Testing test_one... ok +[testing] All tests passed + +$ nargo check +The application panicked (crashed). +Message: Cannot have variable sized arrays as a parameter to main +``` + +## Call Expressions + +Calling a function in Noir is executed by using the function name and passing in the necessary +arguments. + +Below we show how to call the `foo` function from the `main` function using a call expression: + +```rust +fn main(x : Field, y : Field) { + let z = foo(x); +} + +fn foo(x : Field) -> Field { + x + x +} +``` + +## Methods + +You can define methods in Noir on any struct type in scope. + +```rust +struct MyStruct { + foo: Field, + bar: Field, +} + +impl MyStruct { + fn new(foo: Field) -> MyStruct { + MyStruct { + foo, + bar: 2, + } + } + + fn sum(self) -> Field { + self.foo + self.bar + } +} + +fn main() { + let s = MyStruct::new(40); + assert(s.sum() == 42); +} +``` + +Methods are just syntactic sugar for functions, so if we wanted to we could also call `sum` as +follows: + +```rust +assert(MyStruct::sum(s) == 42); +``` + +It is also possible to specialize which method is chosen depending on the [generic](./generics.md) type that is used. In this example, the `foo` function returns different values depending on its type: + +```rust +struct Foo {} + +impl Foo { + fn foo(self) -> Field { 1 } +} + +impl Foo { + fn foo(self) -> Field { 2 } +} + +fn main() { + let f1: Foo = Foo{}; + let f2: Foo = Foo{}; + assert(f1.foo() + f2.foo() == 3); +} +``` + +Also note that impls with the same method name defined in them cannot overlap. For example, if we already have `foo` defined for `Foo` and `Foo` like we do above, we cannot also define `foo` in an `impl Foo` since it would be ambiguous which version of `foo` to choose. + +```rust +// Including this impl in the same project as the above snippet would +// cause an overlapping impls error +impl Foo { + fn foo(self) -> Field { 3 } +} +``` + +## Lambdas + +Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +See [Lambdas](./lambdas.md) for more details. + +## Attributes + +Attributes are metadata that can be applied to a function, using the following syntax: `#[attribute(value)]`. + +Supported attributes include: + +- **builtin**: the function is implemented by the compiler, for efficiency purposes. +- **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` +- **field**: Used to enable conditional compilation of code depending on the field size. See below for more details +- **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. +- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details + +### Field Attribute + +The field attribute defines which field the function is compatible for. The function is conditionally compiled, under the condition that the field attribute matches the Noir native field. +The field can be defined implicitly, by using the name of the elliptic curve usually associated to it - for instance bn254, bls12_381 - or explicitly by using the field (prime) order, in decimal or hexadecimal form. +As a result, it is possible to define multiple versions of a function with each version specialized for a different field attribute. This can be useful when a function requires different parameters depending on the underlying elliptic curve. + +Example: we define the function `foo()` three times below. Once for the default Noir bn254 curve, once for the field $\mathbb F_{23}$, which will normally never be used by Noir, and once again for the bls12_381 curve. + +```rust +#[field(bn254)] +fn foo() -> u32 { + 1 +} + +#[field(23)] +fn foo() -> u32 { + 2 +} + +// This commented code would not compile as foo would be defined twice because it is the same field as bn254 +// #[field(21888242871839275222246405745257275088548364400416034343698204186575808495617)] +// fn foo() -> u32 { +// 2 +// } + +#[field(bls12_381)] +fn foo() -> u32 { + 3 +} +``` + +If the field name is not known to Noir, it will discard the function. Field names are case insensitive. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md new file mode 100644 index 00000000000..ddd42bf1f9b --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/generics.md @@ -0,0 +1,106 @@ +--- +title: Generics +description: Learn how to use Generics in Noir +keywords: [Noir, Rust, generics, functions, structs] +sidebar_position: 7 +--- + +Generics allow you to use the same functions with multiple different concrete data types. You can +read more about the concept of generics in the Rust documentation +[here](https://doc.rust-lang.org/book/ch10-01-syntax.html). + +Here is a trivial example showing the identity function that supports any type. In Rust, it is +common to refer to the most general type as `T`. We follow the same convention in Noir. + +```rust +fn id(x: T) -> T { + x +} +``` + +## In Structs + +Generics are useful for specifying types in structs. For example, we can specify that a field in a +struct will be of a certain generic type. In this case `value` is of type `T`. + +```rust +struct RepeatedValue { + value: T, + count: Field, +} + +impl RepeatedValue { + fn print(self) { + for _i in 0 .. self.count { + println(self.value); + } + } +} + +fn main() { + let repeated = RepeatedValue { value: "Hello!", count: 2 }; + repeated.print(); +} +``` + +The `print` function will print `Hello!` an arbitrary number of times, twice in this case. + +If we want to be generic over array lengths (which are type-level integers), we can use numeric +generics. Using these looks just like using regular generics, but these generics can resolve to +integers at compile-time, rather than resolving to types. Here's an example of a struct that is +generic over the size of the array it contains internally: + +```rust +struct BigInt { + limbs: [u32; N], +} + +impl BigInt { + // `N` is in scope of all methods in the impl + fn first(first: BigInt, second: BigInt) -> Self { + assert(first.limbs != second.limbs); + first + + fn second(first: BigInt, second: Self) -> Self { + assert(first.limbs != second.limbs); + second + } +} +``` + +## Calling functions on generic parameters + +Since a generic type `T` can represent any type, how can we call functions on the underlying type? +In other words, how can we go from "any type `T`" to "any type `T` that has certain methods available?" + +This is what [traits](../concepts/traits) are for in Noir. Here's an example of a function generic over +any type `T` that implements the `Eq` trait for equality: + +```rust +fn first_element_is_equal(array1: [T; N], array2: [T; N]) -> bool + where T: Eq +{ + if (array1.len() == 0) | (array2.len() == 0) { + true + } else { + array1[0] == array2[0] + } +} + +fn main() { + assert(first_element_is_equal([1, 2, 3], [1, 5, 6])); + + // We can use first_element_is_equal for arrays of any type + // as long as we have an Eq impl for the types we pass in + let array = [MyStruct::new(), MyStruct::new()]; + assert(array_eq(array, array, MyStruct::eq)); +} + +impl Eq for MyStruct { + fn eq(self, other: MyStruct) -> bool { + self.foo == other.foo + } +} +``` + +You can find more details on traits and trait implementations on the [traits page](../concepts/traits). diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/globals.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/globals.md new file mode 100644 index 00000000000..063a3d89248 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/globals.md @@ -0,0 +1,72 @@ +--- +title: Global Variables +description: + Learn about global variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, globals, global variables, constants] +sidebar_position: 8 +--- + +## Globals + + +Noir supports global variables. The global's type can be inferred by the compiler entirely: + +```rust +global N = 5; // Same as `global N: Field = 5` + +global TUPLE = (3, 2); + +fn main() { + assert(N == 5); + assert(N == TUPLE.0 + TUPLE.1); +} +``` + +:::info + +Globals can be defined as any expression, so long as they don't depend on themselves - otherwise there would be a dependency cycle! For example: + +```rust +global T = foo(T); // dependency error +``` + +::: + + +If they are initialized to a literal integer, globals can be used to specify an array's length: + +```rust +global N: Field = 2; + +fn main(y : [Field; N]) { + assert(y[0] == y[1]) +} +``` + +A global from another module can be imported or referenced externally like any other name: + +```rust +global N = 20; + +fn main() { + assert(my_submodule::N != N); +} + +mod my_submodule { + global N: Field = 10; +} +``` + +When a global is used, Noir replaces the name with its definition on each occurrence. +This means globals defined using function calls will repeat the call each time they're used: + +```rust +global RESULT = foo(); + +fn foo() -> [Field; 100] { ... } +``` + +This is usually fine since Noir will generally optimize any function call that does not +refer to a program input into a constant. It should be kept in mind however, if the called +function performs side-effects like `println`, as these will still occur on each use. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/lambdas.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/lambdas.md new file mode 100644 index 00000000000..be3c7e0b5ca --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/lambdas.md @@ -0,0 +1,81 @@ +--- +title: Lambdas +description: Learn how to use anonymous functions in Noir programming language. +keywords: [Noir programming language, lambda, closure, function, anonymous function] +sidebar_position: 9 +--- + +## Introduction + +Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`. + +```rust +let add_50 = |val| val + 50; +assert(add_50(100) == 150); +``` + +A block can be used as the body of a lambda, allowing you to declare local variables inside it: + +```rust +let cool = || { + let x = 100; + let y = 100; + x + y +} + +assert(cool() == 200); +``` + +## Closures + +Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda: + +```rust +fn main() { + let x = 100; + let closure = || x + 150; + assert(closure() == 250); +} +``` + +## Passing closures to higher-order functions + +It may catch you by surprise that the following code fails to compile: + +```rust +fn foo(f: fn () -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // error :( +} +``` + +The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo` +expects a regular function as an argument - those are incompatible. +:::note + +Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters. + +E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure. + +::: +The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure - +in this example that's `(Field, Field)`. + +The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called +with closures with any environment, as well as with regular functions: + +```rust +fn foo(f: fn[Env]() -> Field) -> Field { + f() +} + +fn main() { + let (x, y) = (50, 50); + assert(foo(|| x + y) == 100); // compiles fine + assert(foo(|| 60) == 60); // compiles fine +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/mutability.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/mutability.md new file mode 100644 index 00000000000..fdeef6a87c5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/mutability.md @@ -0,0 +1,121 @@ +--- +title: Mutability +description: + Learn about mutable variables in Noir. Discover how + to declare, modify, and use them in your programs. +keywords: [noir programming language, mutability in noir, mutable variables] +sidebar_position: 8 +--- + +Variables in noir can be declared mutable via the `mut` keyword. Mutable variables can be reassigned +to via an assignment expression. + +```rust +let x = 2; +x = 3; // error: x must be mutable to be assigned to + +let mut y = 3; +let y = 4; // OK +``` + +The `mut` modifier can also apply to patterns: + +```rust +let (a, mut b) = (1, 2); +a = 11; // error: a must be mutable to be assigned to +b = 12; // OK + +let mut (c, d) = (3, 4); +c = 13; // OK +d = 14; // OK + +// etc. +let MyStruct { x: mut y } = MyStruct { x: a }; +// y is now in scope +``` + +Note that mutability in noir is local and everything is passed by value, so if a called function +mutates its parameters then the parent function will keep the old value of the parameters. + +```rust +fn main() -> pub Field { + let x = 3; + helper(x); + x // x is still 3 +} + +fn helper(mut x: i32) { + x = 4; +} +``` + +## Non-local mutability + +Non-local mutability can be achieved through the mutable reference type `&mut T`: + +```rust +fn set_to_zero(x: &mut Field) { + *x = 0; +} + +fn main() { + let mut y = 42; + set_to_zero(&mut y); + assert(*y == 0); +} +``` + +When creating a mutable reference, the original variable being referred to (`y` in this +example) must also be mutable. Since mutable references are a reference type, they must +be explicitly dereferenced via `*` to retrieve the underlying value. Note that this yields +a copy of the value, so mutating this copy will not change the original value behind the +reference: + +```rust +fn main() { + let mut x = 1; + let x_ref = &mut x; + + let mut y = *x_ref; + let y_ref = &mut y; + + x = 2; + *x_ref = 3; + + y = 4; + *y_ref = 5; + + assert(x == 3); + assert(*x_ref == 3); + assert(y == 5); + assert(*y_ref == 5); +} +``` + +Note that types in Noir are actually deeply immutable so the copy that occurs when +dereferencing is only a conceptual copy - no additional constraints will occur. + +Mutable references can also be stored within structs. Note that there is also +no lifetime parameter on these unlike rust. This is because the allocated memory +always lasts the entire program - as if it were an array of one element. + +```rust +struct Foo { + x: &mut Field +} + +impl Foo { + fn incr(mut self) { + *self.x += 1; + } +} + +fn main() { + let foo = Foo { x: &mut 0 }; + foo.incr(); + assert(*foo.x == 1); +} +``` + +In general, you should avoid non-local & shared mutability unless it is needed. Sticking +to only local mutability will improve readability and potentially improve compiler optimizations as well. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/ops.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/ops.md new file mode 100644 index 00000000000..60425cb8994 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/ops.md @@ -0,0 +1,98 @@ +--- +title: Logical Operations +description: + Learn about the supported arithmetic and logical operations in the Noir programming language. + Discover how to perform operations on private input types, integers, and booleans. +keywords: + [ + Noir programming language, + supported operations, + arithmetic operations, + logical operations, + predicate operators, + bitwise operations, + short-circuiting, + backend, + ] +sidebar_position: 3 +--- + +# Operations + +## Table of Supported Operations + +| Operation | Description | Requirements | +| :-------- | :------------------------------------------------------------: | -------------------------------------: | +| + | Adds two private input types together | Types must be private input | +| - | Subtracts two private input types together | Types must be private input | +| \* | Multiplies two private input types together | Types must be private input | +| / | Divides two private input types together | Types must be private input | +| ^ | XOR two private input types together | Types must be integer | +| & | AND two private input types together | Types must be integer | +| \| | OR two private input types together | Types must be integer | +| \<\< | Left shift an integer by another integer amount | Types must be integer | +| >> | Right shift an integer by another integer amount | Types must be integer | +| ! | Bitwise not of a value | Type must be integer or boolean | +| \< | returns a bool if one value is less than the other | Upper bound must have a known bit size | +| \<= | returns a bool if one value is less than or equal to the other | Upper bound must have a known bit size | +| > | returns a bool if one value is more than the other | Upper bound must have a known bit size | +| >= | returns a bool if one value is more than or equal to the other | Upper bound must have a known bit size | +| == | returns a bool if one value is equal to the other | Both types must not be constants | +| != | returns a bool if one value is not equal to the other | Both types must not be constants | + +### Predicate Operators + +`<,<=, !=, == , >, >=` are known as predicate/comparison operations because they compare two values. +This differs from the operations such as `+` where the operands are used in _computation_. + +### Bitwise Operations Example + +```rust +fn main(x : Field) { + let y = x as u32; + let z = y & y; +} +``` + +`z` is implicitly constrained to be the result of `y & y`. The `&` operand is used to denote bitwise +`&`. + +> `x & x` would not compile as `x` is a `Field` and not an integer type. + +### Logical Operators + +Noir has no support for the logical operators `||` and `&&`. This is because encoding the +short-circuiting that these operators require can be inefficient for Noir's backend. Instead you can +use the bitwise operators `|` and `&` which operate identically for booleans, just without the +short-circuiting. + +```rust +let my_val = 5; + +let mut flag = 1; +if (my_val > 6) | (my_val == 0) { + flag = 0; +} +assert(flag == 1); + +if (my_val != 10) & (my_val < 50) { + flag = 0; +} +assert(flag == 0); +``` + +### Shorthand operators + +Noir shorthand operators for most of the above operators, namely `+=, -=, *=, /=, %=, &=, |=, ^=, <<=`, and `>>=`. These allow for more concise syntax. For example: + +```rust +let mut i = 0; +i = i + 1; +``` + +could be written as: + +```rust +let mut i = 0; +i += 1; +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/oracles.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/oracles.md new file mode 100644 index 00000000000..2e6a6818d48 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/oracles.md @@ -0,0 +1,23 @@ +--- +title: Oracles +description: Dive into how Noir supports Oracles via RPC calls, and learn how to declare an Oracle in Noir with our comprehensive guide. +keywords: + - Noir + - Oracles + - RPC Calls + - Unconstrained Functions + - Programming + - Blockchain +sidebar_position: 6 +--- + +Noir has support for Oracles via RPC calls. This means Noir will make an RPC call and use the return value for proof generation. + +Since Oracles are not resolved by Noir, they are [`unconstrained` functions](./unconstrained.md) + +You can declare an Oracle through the `#[oracle()]` flag. Example: + +```rust +#[oracle(get_number_sequence)] +unconstrained fn get_number_sequence(_size: Field) -> [Field] {} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/shadowing.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/shadowing.md new file mode 100644 index 00000000000..5ce6130d201 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/shadowing.md @@ -0,0 +1,44 @@ +--- +title: Shadowing +sidebar_position: 12 +--- + +Noir allows for inheriting variables' values and re-declaring them with the same name similar to Rust, known as shadowing. + +For example, the following function is valid in Noir: + +```rust +fn main() { + let x = 5; + + { + let x = x * 2; + assert (x == 10); + } + + assert (x == 5); +} +``` + +In this example, a variable x is first defined with the value 5. + +The local scope that follows shadows the original x, i.e. creates a local mutable x based on the value of the original x. It is given a value of 2 times the original x. + +When we return to the main scope, x once again refers to just the original x, which stays at the value of 5. + +## Temporal mutability + +One way that shadowing is useful, in addition to ergonomics across scopes, is for temporarily mutating variables. + +```rust +fn main() { + let age = 30; + // age = age + 5; // Would error as `age` is immutable by default. + + let mut age = age + 5; // Temporarily mutates `age` with a new value. + + let age = age; // Locks `age`'s mutability again. + + assert (age == 35); +} +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/traits.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/traits.md new file mode 100644 index 00000000000..ef1445a5907 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/traits.md @@ -0,0 +1,389 @@ +--- +title: Traits +description: + Traits in Noir can be used to abstract out a common interface for functions across + several data types. +keywords: [noir programming language, traits, interfaces, generic, protocol] +sidebar_position: 14 +--- + +## Overview + +Traits in Noir are a useful abstraction similar to interfaces or protocols in other languages. Each trait defines +the interface of several methods contained within the trait. Types can then implement this trait by providing +implementations for these methods. For example in the program: + +```rust +struct Rectangle { + width: Field, + height: Field, +} + +impl Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +fn log_area(r: Rectangle) { + println(r.area()); +} +``` + +We have a function `log_area` to log the area of a `Rectangle`. Now how should we change the program if we want this +function to work on `Triangle`s as well?: + +```rust +struct Triangle { + width: Field, + height: Field, +} + +impl Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Making `log_area` generic over all types `T` would be invalid since not all types have an `area` method. Instead, we can +introduce a new `Area` trait and make `log_area` generic over all types `T` that implement `Area`: + +```rust +trait Area { + fn area(self) -> Field; +} + +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +We also need to explicitly implement `Area` for `Rectangle` and `Triangle`. We can do that by changing their existing +impls slightly. Note that the parameter types and return type of each of our `area` methods must match those defined +by the `Area` trait. + +```rust +impl Area for Rectangle { + fn area(self) -> Field { + self.width * self.height + } +} + +impl Area for Triangle { + fn area(self) -> Field { + self.width * self.height / 2 + } +} +``` + +Now we have a working program that is generic over any type of Shape that is used! Others can even use this program +as a library with their own types - such as `Circle` - as long as they also implement `Area` for these types. + +## Where Clauses + +As seen in `log_area` above, when we want to create a function or method that is generic over any type that implements +a trait, we can add a where clause to the generic function. + +```rust +fn log_area(shape: T) where T: Area { + println(shape.area()); +} +``` + +It is also possible to apply multiple trait constraints on the same variable at once by combining traits with the `+` +operator. Similarly, we can have multiple trait constraints by separating each with a comma: + +```rust +fn foo(elements: [T], thing: U) where + T: Default + Add + Eq, + U: Bar, +{ + let mut sum = T::default(); + + for element in elements { + sum += element; + } + + if sum == T::default() { + thing.bar(); + } +} +``` + +## Generic Implementations + +You can add generics to a trait implementation by adding the generic list after the `impl` keyword: + +```rust +trait Second { + fn second(self) -> Field; +} + +impl Second for (T, Field) { + fn second(self) -> Field { + self.1 + } +} +``` + +You can also implement a trait for every type this way: + +```rust +trait Debug { + fn debug(self); +} + +impl Debug for T { + fn debug(self) { + println(self); + } +} + +fn main() { + 1.debug(); +} +``` + +### Generic Trait Implementations With Where Clauses + +Where clauses can also be placed on trait implementations themselves to restrict generics in a similar way. +For example, while `impl Foo for T` implements the trait `Foo` for every type, `impl Foo for T where T: Bar` +will implement `Foo` only for types that also implement `Bar`. This is often used for implementing generic types. +For example, here is the implementation for array equality: + +```rust +impl Eq for [T; N] where T: Eq { + // Test if two arrays have the same elements. + // Because both arrays must have length N, we know their lengths already match. + fn eq(self, other: Self) -> bool { + let mut result = true; + + for i in 0 .. self.len() { + // The T: Eq constraint is needed to call == on the array elements here + result &= self[i] == other[i]; + } + + result + } +} +``` + +## Generic Traits + +Traits themselves can also be generic by placing the generic arguments after the trait name. These generics are in +scope of every item within the trait. + +```rust +trait Into { + // Convert `self` to type `T` + fn into(self) -> T; +} +``` + +When implementing generic traits the generic arguments of the trait must be specified. This is also true anytime +when referencing a generic trait (e.g. in a `where` clause). + +```rust +struct MyStruct { + array: [Field; 2], +} + +impl Into<[Field; 2]> for MyStruct { + fn into(self) -> [Field; 2] { + self.array + } +} + +fn as_array(x: T) -> [Field; 2] + where T: Into<[Field; 2]> +{ + x.into() +} + +fn main() { + let array = [1, 2]; + let my_struct = MyStruct { array }; + + assert_eq(as_array(my_struct), array); +} +``` + +## Trait Methods With No `self` + +A trait can contain any number of methods, each of which have access to the `Self` type which represents each type +that eventually implements the trait. Similarly, the `self` variable is available as well but is not required to be used. +For example, we can define a trait to create a default value for a type. This trait will need to return the `Self` type +but doesn't need to take any parameters: + +```rust +trait Default { + fn default() -> Self; +} +``` + +Implementing this trait can be done similarly to any other trait: + +```rust +impl Default for Field { + fn default() -> Field { + 0 + } +} + +struct MyType {} + +impl Default for MyType { + fn default() -> Field { + MyType {} + } +} +``` + +However, since there is no `self` parameter, we cannot call it via the method call syntax `object.method()`. +Instead, we'll need to refer to the function directly. This can be done either by referring to the +specific impl `MyType::default()` or referring to the trait itself `Default::default()`. In the later +case, type inference determines the impl that is selected. + +```rust +let my_struct = MyStruct::default(); + +let x: Field = Default::default(); +let result = x + Default::default(); +``` + +:::warning + +```rust +let _ = Default::default(); +``` + +If type inference cannot select which impl to use because of an ambiguous `Self` type, an impl will be +arbitrarily selected. This occurs most often when the result of a trait function call with no parameters +is unused. To avoid this, when calling a trait function with no `self` or `Self` parameters or return type, +always refer to it via the implementation type's namespace - e.g. `MyType::default()`. +This is set to change to an error in future Noir versions. + +::: + +## Default Method Implementations + +A trait can also have default implementations of its methods by giving a body to the desired functions. +Note that this body must be valid for all types that may implement the trait. As a result, the only +valid operations on `self` will be operations valid for any type or other operations on the trait itself. + +```rust +trait Numeric { + fn add(self, other: Self) -> Self; + + // Default implementation of double is (self + self) + fn double(self) -> Self { + self.add(self) + } +} +``` + +When implementing a trait with default functions, a type may choose to implement only the required functions: + +```rust +impl Numeric for Field { + fn add(self, other: Field) -> Field { + self + other + } +} +``` + +Or it may implement the optional methods as well: + +```rust +impl Numeric for u32 { + fn add(self, other: u32) -> u32 { + self + other + } + + fn double(self) -> u32 { + self * 2 + } +} +``` + +## Impl Specialization + +When implementing traits for a generic type it is possible to implement the trait for only a certain combination +of generics. This can be either as an optimization or because those specific generics are required to implement the trait. + +```rust +trait Sub { + fn sub(self, other: Self) -> Self; +} + +struct NonZero { + value: T, +} + +impl Sub for NonZero { + fn sub(self, other: Self) -> Self { + let value = self.value - other.value; + assert(value != 0); + NonZero { value } + } +} +``` + +## Overlapping Implementations + +Overlapping implementations are disallowed by Noir to ensure Noir's decision on which impl to select is never ambiguous. +This means if a trait `Foo` is already implemented +by a type `Bar` for all `T`, then we cannot also have a separate impl for `Bar` (or any other +type argument). Similarly, if there is an impl for all `T` such as `impl Debug for T`, we cannot create +any more impls to `Debug` for other types since it would be ambiguous which impl to choose for any given +method call. + +```rust +trait Trait {} + +// Previous impl defined here +impl Trait for (A, B) {} + +// error: Impl for type `(Field, Field)` overlaps with existing impl +impl Trait for (Field, Field) {} +``` + +## Trait Coherence + +Another restriction on trait implementations is coherence. This restriction ensures other crates cannot create +impls that may overlap with other impls, even if several unrelated crates are used as dependencies in the same +program. + +The coherence restriction is: to implement a trait, either the trait itself or the object type must be declared +in the crate the impl is in. + +In practice this often comes up when using types provided by libraries. If a library provides a type `Foo` that does +not implement a trait in the standard library such as `Default`, you may not `impl Default for Foo` in your own crate. +While restrictive, this prevents later issues or silent changes in the program if the `Foo` library later added its +own impl for `Default`. If you are a user of the `Foo` library in this scenario and need a trait not implemented by the +library your choices are to either submit a patch to the library or use the newtype pattern. + +### The Newtype Pattern + +The newtype pattern gets around the coherence restriction by creating a new wrapper type around the library type +that we cannot create `impl`s for. Since the new wrapper type is defined in our current crate, we can create +impls for any trait we need on it. + +```rust +struct Wrapper { + foo: dep::some_library::Foo, +} + +impl Default for Wrapper { + fn default() -> Wrapper { + Wrapper { + foo: dep::some_library::Foo::new(), + } + } +} +``` + +Since we have an impl for our own type, the behavior of this code will not change even if `some_library` is updated +to provide its own `impl Default for Foo`. The downside of this pattern is that it requires extra wrapping and +unwrapping of values when converting to and from the `Wrapper` and `Foo` types. diff --git a/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md b/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md new file mode 100644 index 00000000000..b8e71fe65f0 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/concepts/unconstrained.md @@ -0,0 +1,99 @@ +--- +title: Unconstrained Functions +description: "Learn about what unconstrained functions in Noir are, how to use them and when you'd want to." + +keywords: [Noir programming language, unconstrained, open] +sidebar_position: 5 +--- + +Unconstrained functions are functions which do not constrain any of the included computation and allow for non-deterministic computation. + +## Why? + +Zero-knowledge (ZK) domain-specific languages (DSL) enable developers to generate ZK proofs from their programs by compiling code down to the constraints of an NP complete language (such as R1CS or PLONKish languages). However, the hard bounds of a constraint system can be very limiting to the functionality of a ZK DSL. + +Enabling a circuit language to perform unconstrained execution is a powerful tool. Said another way, unconstrained execution lets developers generate witnesses from code that does not generate any constraints. Being able to execute logic outside of a circuit is critical for both circuit performance and constructing proofs on information that is external to a circuit. + +Fetching information from somewhere external to a circuit can also be used to enable developers to improve circuit efficiency. + +A ZK DSL does not just prove computation, but proves that some computation was handled correctly. Thus, it is necessary that when we switch from performing some operation directly inside of a circuit to inside of an unconstrained environment that the appropriate constraints are still laid down elsewhere in the circuit. + +## Example + +An in depth example might help drive the point home. This example comes from the excellent [post](https://discord.com/channels/1113924620781883405/1124022445054111926/1128747641853972590) by Tom in the Noir Discord. + +Let's look at how we can optimize a function to turn a `u72` into an array of `u8`s. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u72 & 0xff) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 91 +Backend circuit size: 3619 +``` + +A lot of the operations in this function are optimized away by the compiler (all the bit-shifts turn into divisions by constants). However we can save a bunch of gates by casting to u8 a bit earlier. This automatically truncates the bit-shifted value to fit in a u8 which allows us to remove the AND against 0xff. This saves us ~480 gates in total. + +```rust +fn main(num: u72) -> pub [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8)) as u8; + } + + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 75 +Backend circuit size: 3143 +``` + +Those are some nice savings already but we can do better. This code is all constrained so we're proving every step of calculating out using num, but we don't actually care about how we calculate this, just that it's correct. This is where brillig comes in. + +It turns out that truncating a u72 into a u8 is hard to do inside a snark, each time we do as u8 we lay down 4 ACIR opcodes which get converted into multiple gates. It's actually much easier to calculate num from out than the other way around. All we need to do is multiply each element of out by a constant and add them all together, both relatively easy operations inside a snark. + +We can then run u72_to_u8 as unconstrained brillig code in order to calculate out, then use that result in our constrained function and assert that if we were to do the reverse calculation we'd get back num. This looks a little like the below: + +```rust +fn main(num: u72) -> pub [u8; 8] { + let out = u72_to_u8(num); + + let mut reconstructed_num: u72 = 0; + for i in 0..8 { + reconstructed_num += (out[i] as u72 << (56 - (8 * i))); + } + assert(num == reconstructed_num); + out +} + +unconstrained fn u72_to_u8(num: u72) -> [u8; 8] { + let mut out: [u8; 8] = [0; 8]; + for i in 0..8 { + out[i] = (num >> (56 - (i * 8))) as u8; + } + out +} +``` + +``` +Total ACIR opcodes generated for language PLONKCSat { width: 3 }: 78 +Backend circuit size: 2902 +``` + +This ends up taking off another ~250 gates from our circuit! We've ended up with more ACIR opcodes than before but they're easier for the backend to prove (resulting in fewer gates). + +Generally we want to use brillig whenever there's something that's easy to verify but hard to compute within the circuit. For example, if you wanted to calculate a square root of a number it'll be a much better idea to calculate this in brillig and then assert that if you square the result you get back your number. + +## Break and Continue + +In addition to loops over runtime bounds, `break` and `continue` are also available in unconstrained code. See [break and continue](../concepts/control_flow/#break-and-continue) diff --git a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/_category_.json b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/_category_.json new file mode 100644 index 00000000000..1debcfe7675 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Modules, Packages and Crates", + "position": 2, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/crates_and_packages.md b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/crates_and_packages.md new file mode 100644 index 00000000000..95ee9f52ab2 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/crates_and_packages.md @@ -0,0 +1,43 @@ +--- +title: Crates and Packages +description: Learn how to use Crates and Packages in your Noir project +keywords: [Nargo, dependencies, package management, crates, package] +sidebar_position: 0 +--- + +## Crates + +A crate is the smallest amount of code that the Noir compiler considers at a time. +Crates can contain modules, and the modules may be defined in other files that get compiled with the crate, as we’ll see in the coming sections. + +### Crate Types + +A Noir crate can come in several forms: binaries, libraries or contracts. + +#### Binaries + +_Binary crates_ are programs which you can compile to an ACIR circuit which you can then create proofs against. Each must have a function called `main` that defines the ACIR circuit which is to be proved. + +#### Libraries + +_Library crates_ don't have a `main` function and they don't compile down to ACIR. Instead they define functionality intended to be shared with multiple projects, and eventually included in a binary crate. + +#### Contracts + +Contract crates are similar to binary crates in that they compile to ACIR which you can create proofs against. They are different in that they do not have a single `main` function, but are a collection of functions to be deployed to the [Aztec network](https://aztec.network). You can learn more about the technical details of Aztec in the [monorepo](https://github.com/AztecProtocol/aztec-packages) or contract [examples](https://github.com/AztecProtocol/aztec-packages/tree/master/noir-projects/noir-contracts/contracts). + +### Crate Root + +Every crate has a root, which is the source file that the compiler starts, this is also known as the root module. The Noir compiler does not enforce any conditions on the name of the file which is the crate root, however if you are compiling via Nargo the crate root must be called `lib.nr` or `main.nr` for library or binary crates respectively. + +## Packages + +A Nargo _package_ is a collection of one of more crates that provides a set of functionality. A package must include a Nargo.toml file. + +A package _must_ contain either a library or a binary crate, but not both. + +### Differences from Cargo Packages + +One notable difference between Rust's Cargo and Noir's Nargo is that while Cargo allows a package to contain an unlimited number of binary crates and a single library crate, Nargo currently only allows a package to contain a single crate. + +In future this restriction may be lifted to allow a Nargo package to contain both a binary and library crate or multiple binary crates. diff --git a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md new file mode 100644 index 00000000000..04c1703d929 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/dependencies.md @@ -0,0 +1,124 @@ +--- +title: Dependencies +description: + Learn how to specify and manage dependencies in Nargo, allowing you to upload packages to GitHub + and use them easily in your project. +keywords: [Nargo, dependencies, GitHub, package management, versioning] +sidebar_position: 1 +--- + +Nargo allows you to upload packages to GitHub and use them as dependencies. + +## Specifying a dependency + +Specifying a dependency requires a tag to a specific commit and the git url to the url containing +the package. + +Currently, there are no requirements on the tag contents. If requirements are added, it would follow +semver 2.0 guidelines. + +> Note: Without a `tag` , there would be no versioning and dependencies would change each time you +> compile your project. + +For example, to add the [ecrecover-noir library](https://github.com/colinnielsen/ecrecover-noir) to your project, add it to `Nargo.toml`: + +```toml +# Nargo.toml + +[dependencies] +ecrecover = {tag = "v0.8.0", git = "https://github.com/colinnielsen/ecrecover-noir"} +``` + +If the module is in a subdirectory, you can define a subdirectory in your git repository, for example: + +```toml +# Nargo.toml + +[dependencies] +easy_private_token_contract = {tag ="v0.1.0-alpha62", git = "https://github.com/AztecProtocol/aztec-packages", directory = "noir-contracts/contracts/easy_private_token_contract"} +``` + +## Specifying a local dependency + +You can also specify dependencies that are local to your machine. + +For example, this file structure has a library and binary crate + +```tree +├── binary_crate +│   ├── Nargo.toml +│   └── src +│   └── main.nr +└── lib_a + ├── Nargo.toml + └── src + └── lib.nr +``` + +Inside of the binary crate, you can specify: + +```toml +# Nargo.toml + +[dependencies] +lib_a = { path = "../lib_a" } +``` + +## Importing dependencies + +You can import a dependency to a Noir file using the following syntax. For example, to import the +ecrecover-noir library and local lib_a referenced above: + +```rust +use dep::ecrecover; +use dep::lib_a; +``` + +You can also import only the specific parts of dependency that you want to use, like so: + +```rust +use dep::std::hash::sha256; +use dep::std::scalar_mul::fixed_base_embedded_curve; +``` + +Lastly, as demonstrated in the +[elliptic curve example](../standard_library/cryptographic_primitives/ec_primitives#examples), you +can import multiple items in the same line by enclosing them in curly braces: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; +``` + +We don't have a way to consume libraries from inside a [workspace](./workspaces) as external dependencies right now. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +## Dependencies of Dependencies + +Note that when you import a dependency, you also get access to all of the dependencies of that package. + +For example, the [phy_vector](https://github.com/resurgencelabs/phy_vector) library imports an [fraction](https://github.com/resurgencelabs/fraction) library. If you're importing the phy_vector library, then you can access the functions in fractions library like so: + +```rust +use dep::phy_vector; + +fn main(x : Field, y : pub Field) { + //... + let f = phy_vector::fraction::toFraction(true, 2, 1); + //... +} +``` + +## Available Libraries + +Noir does not currently have an official package manager. You can find a list of available Noir libraries in the [awesome-noir repo here](https://github.com/noir-lang/awesome-noir#libraries). + +Some libraries that are available today include: + +- [Standard Library](https://github.com/noir-lang/noir/tree/master/noir_stdlib) - the Noir Standard Library +- [Ethereum Storage Proof Verification](https://github.com/aragonzkresearch/noir-trie-proofs) - a library that contains the primitives necessary for RLP decoding (in the form of look-up table construction) and Ethereum state and storage proof verification (or verification of any trie proof involving 32-byte long keys) +- [BigInt](https://github.com/shuklaayush/noir-bigint) - a library that provides a custom BigUint56 data type, allowing for computations on large unsigned integers +- [ECrecover](https://github.com/colinnielsen/ecrecover-noir/tree/main) - a library to verify an ECDSA signature and return the source Ethereum address +- [Sparse Merkle Tree Verifier](https://github.com/vocdoni/smtverifier-noir/tree/main) - a library for verification of sparse Merkle trees +- [Signed Int](https://github.com/resurgencelabs/signed_int) - a library for accessing a custom Signed Integer data type, allowing access to negative numbers on Noir +- [Fraction](https://github.com/resurgencelabs/fraction) - a library for accessing fractional number data type in Noir, allowing results that aren't whole numbers diff --git a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/modules.md b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/modules.md new file mode 100644 index 00000000000..ae822a1cff4 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/modules.md @@ -0,0 +1,105 @@ +--- +title: Modules +description: + Learn how to organize your files using modules in Noir, following the same convention as Rust's + module system. Examples included. +keywords: [Noir, Rust, modules, organizing files, sub-modules] +sidebar_position: 2 +--- + +Noir's module system follows the same convention as the _newer_ version of Rust's module system. + +## Purpose of Modules + +Modules are used to organize files. Without modules all of your code would need to live in a single +file. In Noir, the compiler does not automatically scan all of your files to detect modules. This +must be done explicitly by the developer. + +## Examples + +### Importing a module in the crate root + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::hello_world(); +} +``` + +Filename : `src/foo.nr` + +```rust +fn from_foo() {} +``` + +In the above snippet, the crate root is the `src/main.nr` file. The compiler sees the module +declaration `mod foo` which prompts it to look for a foo.nr file. + +Visually this module hierarchy looks like the following : + +``` +crate + ├── main + │ + └── foo + └── from_foo + +``` + +### Importing a module throughout the tree + +All modules are accessible from the `crate::` namespace. + +``` +crate + ├── bar + ├── foo + └── main + +``` + +In the above snippet, if `bar` would like to use functions in `foo`, it can do so by `use crate::foo::function_name`. + +### Sub-modules + +Filename : `src/main.nr` + +```rust +mod foo; + +fn main() { + foo::from_foo(); +} +``` + +Filename : `src/foo.nr` + +```rust +mod bar; +fn from_foo() {} +``` + +Filename : `src/foo/bar.nr` + +```rust +fn from_bar() {} +``` + +In the above snippet, we have added an extra module to the module tree; `bar`. `bar` is a submodule +of `foo` hence we declare bar in `foo.nr` with `mod bar`. Since `foo` is not the crate root, the +compiler looks for the file associated with the `bar` module in `src/foo/bar.nr` + +Visually the module hierarchy looks as follows: + +``` +crate + ├── main + │ + └── foo + ├── from_foo + └── bar + └── from_bar +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/workspaces.md b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/workspaces.md new file mode 100644 index 00000000000..67a1dafa372 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/modules_packages_crates/workspaces.md @@ -0,0 +1,40 @@ +--- +title: Workspaces +sidebar_position: 3 +--- + +Workspaces are a feature of nargo that allow you to manage multiple related Noir packages in a single repository. A workspace is essentially a group of related projects that share common build output directories and configurations. + +Each Noir project (with it's own Nargo.toml file) can be thought of as a package. Each package is expected to contain exactly one "named circuit", being the "name" defined in Nargo.toml with the program logic defined in `./src/main.nr`. + +For a project with the following structure: + +```tree +├── crates +│   ├── a +│   │   ├── Nargo.toml +│   │   └── src +│   │   └── main.nr +│   └── b +│   ├── Nargo.toml +│   └── src +│   └── main.nr +├── Nargo.toml +└── Prover.toml +``` + +You can define a workspace in Nargo.toml like so: + +```toml +[workspace] +members = ["crates/a", "crates/b"] +default-member = "crates/a" +``` + +`members` indicates which packages are included in the workspace. As such, all member packages of a workspace will be processed when the `--workspace` flag is used with various commands or if a `default-member` is not specified. + +`default-member` indicates which package various commands process by default. + +Libraries can be defined in a workspace. Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. + +Inside a workspace, these are consumed as `{ path = "../to_lib" }` dependencies in Nargo.toml. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/_category_.json b/docs/versioned_docs/version-v0.26.0/noir/standard_library/_category_.json new file mode 100644 index 00000000000..af04c0933fd --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Standard Library", + "position": 1, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/bigint.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/bigint.md new file mode 100644 index 00000000000..da6a7cdfd81 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/bigint.md @@ -0,0 +1,119 @@ +--- +title: Big Integers +description: How to use big integers from Noir standard library +keywords: + [ + Big Integer, + Noir programming language, + Noir libraries, + ] +--- + +The BigInt module in the standard library exposes some class of integers which do not fit (well) into a Noir native field. It implements modulo arithmetic, modulo a 'big' prime number. + +:::note + +The module can currently be considered as `Field`s with fixed modulo sizes used by a set of elliptic curves, in addition to just the native curve. [More work](https://github.com/noir-lang/noir/issues/510) is needed to achieve arbitrarily sized big integers. + +::: + +Currently 6 classes of integers (i.e 'big' prime numbers) are available in the module, namely: + +- BN254 Fq: Bn254Fq +- BN254 Fr: Bn254Fr +- Secp256k1 Fq: Secpk1Fq +- Secp256k1 Fr: Secpk1Fr +- Secp256r1 Fr: Secpr1Fr +- Secp256r1 Fq: Secpr1Fq + +Where XXX Fq and XXX Fr denote respectively the order of the base and scalar field of the (usual) elliptic curve XXX. +For instance the big integer 'Secpk1Fq' in the standard library refers to integers modulo $2^{256}-2^{32}-977$. + +Feel free to explore the source code for the other primes: + +```rust title="big_int_definition" showLineNumbers +struct BigInt { + pointer: u32, + modulus: u32, +} +``` +> Source code: noir_stdlib/src/bigint.nr#L16-L21 + + +## Example usage + +A common use-case is when constructing a big integer from its bytes representation, and performing arithmetic operations on it: + +```rust title="big_int_example" showLineNumbers +fn big_int_example(x: u8, y: u8) { + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + let b = Secpk1Fq::from_le_bytes(&[y, x, 9]); + let c = (a + b) * b / a; + let d = c.to_le_bytes(); + println(d[0]); +} +``` +> Source code: test_programs/execution_success/bigint/src/main.nr#L20-L28 + + +## Methods + +The available operations for each big integer are: + +### from_le_bytes + +Construct a big integer from its little-endian bytes representation. Example: + +```rust + let a = Secpk1Fq::from_le_bytes(&[x, y, 0, 45, 2]); + ``` + +Sure, here's the formatted version of the remaining methods: + +### to_le_bytes + +Return the little-endian bytes representation of a big integer. Example: + +```rust +let bytes = a.to_le_bytes(); +``` + +### add + +Add two big integers. Example: + +```rust +let sum = a + b; +``` + +### sub + +Subtract two big integers. Example: + +```rust +let difference = a - b; +``` + +### mul + +Multiply two big integers. Example: + +```rust +let product = a * b; +``` + +### div + +Divide two big integers. Note that division is field division and not euclidean division. Example: + +```rust +let quotient = a / b; +``` + +### eq + +Compare two big integers. Example: + +```rust +let are_equal = a == b; +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md new file mode 100644 index 00000000000..be8c65679c3 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/black_box_fns.md @@ -0,0 +1,31 @@ +--- +title: Black Box Functions +description: Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. +keywords: [noir, black box functions] +--- + +Black box functions are functions in Noir that rely on backends implementing support for specialized constraints. This makes certain zk-snark unfriendly computations cheaper than if they were implemented in Noir. + +The ACVM spec defines a set of blackbox functions which backends will be expected to implement. This allows backends to use optimized implementations of these constraints if they have them, however they may also fallback to less efficient naive implementations if not. + +## Function list + +Here is a list of the current black box functions: + +- [SHA256](./cryptographic_primitives/hashes.mdx#sha256) +- [Schnorr signature verification](./cryptographic_primitives/schnorr.mdx) +- [Blake2s](./cryptographic_primitives/hashes.mdx#blake2s) +- [Blake3](./cryptographic_primitives/hashes.mdx#blake3) +- [Pedersen Hash](./cryptographic_primitives/hashes.mdx#pedersen_hash) +- [Pedersen Commitment](./cryptographic_primitives/hashes.mdx#pedersen_commitment) +- [ECDSA signature verification](./cryptographic_primitives/ecdsa_sig_verification.mdx) +- [Fixed base scalar multiplication](./cryptographic_primitives/scalar.mdx) +- AND +- XOR +- RANGE +- [Keccak256](./cryptographic_primitives/hashes.mdx#keccak256) +- [Recursive proof verification](./recursion) + +Most black box functions are included as part of the Noir standard library, however `AND`, `XOR` and `RANGE` are used as part of the Noir language syntax. For instance, using the bitwise operator `&` will invoke the `AND` black box function. + +You can view the black box functions defined in the ACVM code [here](https://github.com/noir-lang/noir/blob/master/acvm-repo/acir/src/circuit/black_box_functions.rs). diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/bn254.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/bn254.md new file mode 100644 index 00000000000..3294f005dbb --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/bn254.md @@ -0,0 +1,46 @@ +--- +title: Bn254 Field Library +--- + +Noir provides a module in standard library with some optimized functions for bn254 Fr in `std::field::bn254`. + +## decompose + +```rust +fn decompose(x: Field) -> (Field, Field) {} +``` + +Decomposes a single field into two fields, low and high. The low field contains the lower 16 bytes of the input field and the high field contains the upper 16 bytes of the input field. Both field results are range checked to 128 bits. + + +## assert_gt + +```rust +fn assert_gt(a: Field, b: Field) {} +``` + +Asserts that a > b. This will generate less constraints than using `assert(gt(a, b))`. + +## assert_lt + +```rust +fn assert_lt(a: Field, b: Field) {} +``` + +Asserts that a < b. This will generate less constraints than using `assert(lt(a, b))`. + +## gt + +```rust +fn gt(a: Field, b: Field) -> bool {} +``` + +Returns true if a > b. + +## lt + +```rust +fn lt(a: Field, b: Field) -> bool {} +``` + +Returns true if a < b. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/boundedvec.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/boundedvec.md new file mode 100644 index 00000000000..ce4529f6e57 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/boundedvec.md @@ -0,0 +1,326 @@ +--- +title: Bounded Vectors +keywords: [noir, vector, bounded vector, slice] +sidebar_position: 1 +--- + +A `BoundedVec` is a growable storage similar to a `Vec` except that it +is bounded with a maximum possible length. Unlike `Vec`, `BoundedVec` is not implemented +via slices and thus is not subject to the same restrictions slices are (notably, nested +slices - and thus nested vectors as well - are disallowed). + +Since a BoundedVec is backed by a normal array under the hood, growing the BoundedVec by +pushing an additional element is also more efficient - the length only needs to be increased +by one. + +For these reasons `BoundedVec` should generally be preferred over `Vec` when there +is a reasonable maximum bound that can be placed on the vector. + +Example: + +```rust +let mut vector: BoundedVec = BoundedVec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +assert(vector.max_len() == 10); +``` + +## Methods + +### new + +```rust +pub fn new() -> Self +``` + +Creates a new, empty vector of length zero. + +Since this container is backed by an array internally, it still needs an initial value +to give each element. To resolve this, each element is zeroed internally. This value +is guaranteed to be inaccessible unless `get_unchecked` is used. + +Example: + +```rust +let empty_vector: BoundedVec = BoundedVec::new(); +assert(empty_vector.len() == 0); +``` + +Note that whenever calling `new` the maximum length of the vector should always be specified +via a type signature: + +```rust title="new_example" showLineNumbers +fn foo() -> BoundedVec { + // Ok! MaxLen is specified with a type annotation + let v1: BoundedVec = BoundedVec::new(); + let v2 = BoundedVec::new(); + + // Ok! MaxLen is known from the type of foo's return value + v2 +} + +fn bad() { + let mut v3 = BoundedVec::new(); + + // Not Ok! We don't know if v3's MaxLen is at least 1, and the compiler often infers 0 by default. + v3.push(5); +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L11-L27 + + +This defaulting of `MaxLen` (and numeric generics in general) to zero may change in future noir versions +but for now make sure to use type annotations when using bounded vectors. Otherwise, you will receive a constraint failure at runtime when the vec is pushed to. + +### get + +```rust +pub fn get(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero. + +If the given index is equal to or greater than the length of the vector, this +will issue a constraint failure. + +Example: + +```rust +fn foo(v: BoundedVec) { + let first = v.get(0); + let last = v.get(v.len() - 1); + assert(first != last); +} +``` + +### get_unchecked + +```rust +pub fn get_unchecked(mut self: Self, index: u64) -> T { +``` + +Retrieves an element from the vector at the given index, starting from zero, without +performing a bounds check. + +Since this function does not perform a bounds check on length before accessing the element, +it is unsafe! Use at your own risk! + +Example: + +```rust title="get_unchecked_example" showLineNumbers +fn sum_of_first_three(v: BoundedVec) -> u32 { + // Always ensure the length is larger than the largest + // index passed to get_unchecked + assert(v.len() > 2); + let first = v.get_unchecked(0); + let second = v.get_unchecked(1); + let third = v.get_unchecked(2); + first + second + third +} +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L54-L64 + + + +### push + +```rust +pub fn push(&mut self, elem: T) { +``` + +Pushes an element to the end of the vector. This increases the length +of the vector by one. + +Panics if the new length of the vector will be greater than the max length. + +Example: + +```rust title="bounded-vec-push-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + v.push(1); + v.push(2); + + // Panics with failed assertion "push out of bounds" + v.push(3); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L68-L76 + + +### pop + +```rust +pub fn pop(&mut self) -> T +``` + +Pops the element at the end of the vector. This will decrease the length +of the vector by one. + +Panics if the vector is empty. + +Example: + +```rust title="bounded-vec-pop-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.push(1); + v.push(2); + + let two = v.pop(); + let one = v.pop(); + + assert(two == 2); + assert(one == 1); + // error: cannot pop from an empty vector + // let _ = v.pop(); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L81-L93 + + +### len + +```rust +pub fn len(self) -> u64 { +``` + +Returns the current length of this vector + +Example: + +```rust title="bounded-vec-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + assert(v.len() == 0); + + v.push(100); + assert(v.len() == 1); + + v.push(200); + v.push(300); + v.push(400); + assert(v.len() == 4); + + let _ = v.pop(); + let _ = v.pop(); + assert(v.len() == 2); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L98-L113 + + +### max_len + +```rust +pub fn max_len(_self: BoundedVec) -> u64 { +``` + +Returns the maximum length of this vector. This is always +equal to the `MaxLen` parameter this vector was initialized with. + +Example: + +```rust title="bounded-vec-max-len-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.max_len() == 5); + v.push(10); + assert(v.max_len() == 5); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L118-L124 + + +### storage + +```rust +pub fn storage(self) -> [T; MaxLen] { +``` + +Returns the internal array within this vector. +Since arrays in Noir are immutable, mutating the returned storage array will not mutate +the storage held internally by this vector. + +Note that uninitialized elements may be zeroed out! + +Example: + +```rust title="bounded-vec-storage-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + + assert(v.storage() == [0, 0, 0, 0, 0]); + + v.push(57); + assert(v.storage() == [57, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L129-L136 + + +### extend_from_array + +```rust +pub fn extend_from_array(&mut self, array: [T; Len]) +``` + +Pushes each element from the given array to this vector. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-array-example" showLineNumbers +let mut vec: BoundedVec = BoundedVec::new(); + vec.extend_from_array([2, 4]); + + assert(vec.len == 2); + assert(vec.get(0) == 2); + assert(vec.get(1) == 4); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L141-L148 + + +### extend_from_bounded_vec + +```rust +pub fn extend_from_bounded_vec(&mut self, vec: BoundedVec) +``` + +Pushes each element from the other vector to this vector. The length of +the other vector is left unchanged. + +Panics if pushing each element would cause the length of this vector +to exceed the maximum length. + +Example: + +```rust title="bounded-vec-extend-from-bounded-vec-example" showLineNumbers +let mut v1: BoundedVec = BoundedVec::new(); + let mut v2: BoundedVec = BoundedVec::new(); + + v2.extend_from_array([1, 2, 3]); + v1.extend_from_bounded_vec(v2); + + assert(v1.storage() == [1, 2, 3, 0, 0]); + assert(v2.storage() == [1, 2, 3, 0, 0, 0, 0]); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L153-L162 + + +### any + +```rust +pub fn any(self, predicate: fn[Env](T) -> bool) -> bool +``` + +Returns true if the given predicate returns true for any element +in this vector. + +Example: + +```rust title="bounded-vec-any-example" showLineNumbers +let mut v: BoundedVec = BoundedVec::new(); + v.extend_from_array([2, 4, 6]); + + let all_even = !v.any(|elem: u32| elem % 2 != 0); + assert(all_even); +``` +> Source code: test_programs/noir_test_success/bounded_vec/src/main.nr#L229-L235 + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/hashmap.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/hashmap.md new file mode 100644 index 00000000000..91604af765d --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/hashmap.md @@ -0,0 +1,569 @@ +--- +title: HashMap +keywords: [noir, map, hash, hashmap] +sidebar_position: 1 +--- + +`HashMap` is used to efficiently store and look up key-value pairs. + +`HashMap` is a bounded type which can store anywhere from zero to `MaxLen` total elements. +Note that due to hash collisions, the actual maximum number of elements stored by any particular +hashmap is likely lower than `MaxLen`. This is true even with cryptographic hash functions since +every hash value will be performed modulo `MaxLen`. + +When creating `HashMap`s, the `MaxLen` generic should always be specified if it is not already +known. Otherwise, the compiler may infer a different value for `MaxLen` (such as zero), which +will likely change the result of the program. This behavior is set to become an error in future +versions instead. + +Example: + +```rust +// Create a mapping from Fields to u32s with a maximum length of 12 +// using a pedersen hash +let mut map: HashMap> = HashMap::default(); + +map.insert(1, 2); +map.insert(3, 4); + +let two = map.get(1).unwrap(); +``` + +## Methods + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Creates a fresh, empty HashMap. + +When using this function, always make sure to specify the maximum size of the hash map. + +This is the same `default` from the `Default` implementation given further below. It is +repeated here for convenience since it is the recommended way to create a hashmap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +Because `HashMap` has so many generic arguments that are likely to be the same throughout +your program, it may be helpful to create a type alias: + +```rust title="type_alias" showLineNumbers +type MyMap = HashMap>; +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L196-L198 + + +### with_hasher + +```rust title="with_hasher" showLineNumbers +pub fn with_hasher(_build_hasher: B) -> Self + where + B: BuildHasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L82-L86 + + +Creates a hashmap with an existing `BuildHasher`. This can be used to ensure multiple +hashmaps are created with the same hasher instance. + +Example: + +```rust title="with_hasher_example" showLineNumbers +let my_hasher: BuildHasherDefault = Default::default(); + let hashmap: HashMap> = HashMap::with_hasher(my_hasher); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L207-L211 + + +### get + +```rust title="get" showLineNumbers +pub fn get( + self, + key: K + ) -> Option + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L278-L287 + + +Retrieves a value from the hashmap, returning `Option::none()` if it was not found. + +Example: + +```rust title="get_example" showLineNumbers +fn get_example(map: HashMap>) { + let x = map.get(12); + + if x.is_some() { + assert(x.unwrap() == 42); + } +} +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L299-L307 + + +### insert + +```rust title="insert" showLineNumbers +pub fn insert( + &mut self, + key: K, + value: V + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L313-L323 + + +Inserts a new key-value pair into the map. If the key was already in the map, its +previous value will be overridden with the newly provided one. + +Example: + +```rust title="insert_example" showLineNumbers +let mut map: HashMap> = HashMap::default(); + map.insert(12, 42); + assert(map.len() == 1); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L213-L217 + + +### remove + +```rust title="remove" showLineNumbers +pub fn remove( + &mut self, + key: K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L356-L365 + + +Removes the given key-value pair from the map. If the key was not already present +in the map, this does nothing. + +Example: + +```rust title="remove_example" showLineNumbers +map.remove(12); + assert(map.is_empty()); + + // If a key was not present in the map, remove does nothing + map.remove(12); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L221-L228 + + +### is_empty + +```rust title="is_empty" showLineNumbers +pub fn is_empty(self) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L115-L117 + + +True if the length of the hash map is empty. + +Example: + +```rust title="is_empty_example" showLineNumbers +assert(map.is_empty()); + + map.insert(1, 2); + assert(!map.is_empty()); + + map.remove(1); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L230-L238 + + +### len + +```rust title="len" showLineNumbers +pub fn len(self) -> u64 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L264-L266 + + +Returns the current length of this hash map. + +Example: + +```rust title="len_example" showLineNumbers +// This is equivalent to checking map.is_empty() + assert(map.len() == 0); + + map.insert(1, 2); + map.insert(3, 4); + map.insert(5, 6); + assert(map.len() == 3); + + // 3 was already present as a key in the hash map, so the length is unchanged + map.insert(3, 7); + assert(map.len() == 3); + + map.remove(1); + assert(map.len() == 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L240-L255 + + +### capacity + +```rust title="capacity" showLineNumbers +pub fn capacity(_self: Self) -> u64 { +``` +> Source code: noir_stdlib/src/collections/map.nr#L271-L273 + + +Returns the maximum capacity of this hashmap. This is always equal to the capacity +specified in the hashmap's type. + +Unlike hashmaps in general purpose programming languages, hashmaps in Noir have a +static capacity that does not increase as the map grows larger. Thus, this capacity +is also the maximum possible element count that can be inserted into the hashmap. +Due to hash collisions (modulo the hashmap length), it is likely the actual maximum +element count will be lower than the full capacity. + +Example: + +```rust title="capacity_example" showLineNumbers +let empty_map: HashMap> = HashMap::default(); + assert(empty_map.len() == 0); + assert(empty_map.capacity() == 42); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L257-L261 + + +### clear + +```rust title="clear" showLineNumbers +pub fn clear(&mut self) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L93-L95 + + +Clears the hashmap, removing all key-value pairs from it. + +Example: + +```rust title="clear_example" showLineNumbers +assert(!map.is_empty()); + map.clear(); + assert(map.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L263-L267 + + +### contains_key + +```rust title="contains_key" showLineNumbers +pub fn contains_key( + self, + key: K + ) -> bool + where + K: Hash + Eq, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L101-L110 + + +True if the hashmap contains the given key. Unlike `get`, this will not also return +the value associated with the key. + +Example: + +```rust title="contains_key_example" showLineNumbers +if map.contains_key(7) { + let value = map.get(7); + assert(value.is_some()); + } else { + println("No value for key 7!"); + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L269-L276 + + +### entries + +```rust title="entries" showLineNumbers +pub fn entries(self) -> BoundedVec<(K, V), N> { +``` +> Source code: noir_stdlib/src/collections/map.nr#L123-L125 + + +Returns a vector of each key-value pair present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="entries_example" showLineNumbers +let entries = map.entries(); + + // The length of a hashmap may not be compile-time known, so we + // need to loop over its capacity instead + for i in 0..map.capacity() { + if i < entries.len() { + let (key, value) = entries.get(i); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L310-L321 + + +### keys + +```rust title="keys" showLineNumbers +pub fn keys(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L144-L146 + + +Returns a vector of each key present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="keys_example" showLineNumbers +let keys = map.keys(); + + for i in 0..keys.max_len() { + if i < keys.len() { + let key = keys.get_unchecked(i); + let value = map.get(key).unwrap_unchecked(); + println(f"{key} -> {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L323-L333 + + +### values + +```rust title="values" showLineNumbers +pub fn values(self) -> BoundedVec { +``` +> Source code: noir_stdlib/src/collections/map.nr#L164-L166 + + +Returns a vector of each value present in the hashmap. + +The length of the returned vector is always equal to the length of the hashmap. + +Example: + +```rust title="values_example" showLineNumbers +let values = map.values(); + + for i in 0..values.max_len() { + if i < values.len() { + let value = values.get_unchecked(i); + println(f"Found value {value}"); + } + } +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L335-L344 + + +### iter_mut + +```rust title="iter_mut" showLineNumbers +pub fn iter_mut( + &mut self, + f: fn(K, V) -> (K, V) + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L183-L192 + + +Iterates through each key-value pair of the HashMap, setting each key-value pair to the +result returned from the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If this is not desired, use `iter_values_mut` if only values need to be mutated, +or `entries` if neither keys nor values need to be mutated. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_mut_example" showLineNumbers +// Add 1 to each key in the map, and double the value associated with that key. + map.iter_mut(|k, v| (k + 1, v * 2)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L348-L351 + + +### iter_keys_mut + +```rust title="iter_keys_mut" showLineNumbers +pub fn iter_keys_mut( + &mut self, + f: fn(K) -> K + ) + where + K: Eq + Hash, + B: BuildHasher, + H: Hasher { +``` +> Source code: noir_stdlib/src/collections/map.nr#L208-L217 + + +Iterates through the HashMap, mutating each key to the result returned from +the given function. + +Note that since keys can be mutated, the HashMap needs to be rebuilt as it is iterated +through. If only iteration is desired and the keys are not intended to be mutated, +prefer using `entries` instead. + +The iteration order is left unspecified. As a result, if two keys are mutated to become +equal, which of the two values that will be present for the key in the resulting map is also unspecified. + +Example: + +```rust title="iter_keys_mut_example" showLineNumbers +// Double each key, leaving the value associated with that key untouched + map.iter_keys_mut(|k| k * 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L353-L356 + + +### iter_values_mut + +```rust title="iter_values_mut" showLineNumbers +pub fn iter_values_mut(&mut self, f: fn(V) -> V) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L233-L235 + + +Iterates through the HashMap, applying the given function to each value and mutating the +value to equal the result. This function is more efficient than `iter_mut` and `iter_keys_mut` +because the keys are untouched and the underlying hashmap thus does not need to be reordered. + +Example: + +```rust title="iter_values_mut_example" showLineNumbers +// Halve each value + map.iter_values_mut(|v| v / 2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L358-L361 + + +### retain + +```rust title="retain" showLineNumbers +pub fn retain(&mut self, f: fn(K, V) -> bool) { +``` +> Source code: noir_stdlib/src/collections/map.nr#L247-L249 + + +Retains only the key-value pairs for which the given function returns true. +Any key-value pairs for which the function returns false will be removed from the map. + +Example: + +```rust title="retain_example" showLineNumbers +map.retain(|k, v| (k != 0) & (v != 0)); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L281-L283 + + +## Trait Implementations + +### default + +```rust title="default" showLineNumbers +impl Default for HashMap +where + B: BuildHasher + Default, + H: Hasher + Default +{ + fn default() -> Self { +``` +> Source code: noir_stdlib/src/collections/map.nr#L462-L469 + + +Constructs an empty HashMap. + +Example: + +```rust title="default_example" showLineNumbers +let hashmap: HashMap> = HashMap::default(); + assert(hashmap.is_empty()); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L202-L205 + + +### eq + +```rust title="eq" showLineNumbers +impl Eq for HashMap +where + K: Eq + Hash, + V: Eq, + B: BuildHasher, + H: Hasher +{ + fn eq(self, other: HashMap) -> bool { +``` +> Source code: noir_stdlib/src/collections/map.nr#L426-L435 + + +Checks if two HashMaps are equal. + +Example: + +```rust title="eq_example" showLineNumbers +let mut map1: HashMap> = HashMap::default(); + let mut map2: HashMap> = HashMap::default(); + + map1.insert(1, 2); + map1.insert(3, 4); + + map2.insert(3, 4); + map2.insert(1, 2); + + assert(map1 == map2); +``` +> Source code: test_programs/execution_success/hashmap/src/main.nr#L285-L296 + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/index.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/index.md new file mode 100644 index 00000000000..ea84c6d5c21 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/index.md @@ -0,0 +1,5 @@ +--- +title: Containers +description: Container types provided by Noir's standard library for storing and retrieving data +keywords: [containers, data types, vec, hashmap] +--- diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/vec.mdx b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/vec.mdx new file mode 100644 index 00000000000..fcfd7e07aa0 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/containers/vec.mdx @@ -0,0 +1,151 @@ +--- +title: Vectors +description: Delve into the Vec data type in Noir. Learn about its methods, practical examples, and best practices for using Vectors in your Noir code. +keywords: [noir, vector type, methods, examples, dynamic arrays] +sidebar_position: 6 +--- + +import Experimental from '@site/src/components/Notes/_experimental.mdx'; + + + +A vector is a collection type similar to Rust's `Vec` type. In Noir, it is a convenient way to use slices as mutable arrays. + +Example: + +```rust +let mut vector: Vec = Vec::new(); +for i in 0..5 { + vector.push(i); +} +assert(vector.len() == 5); +``` + +## Methods + +### new + +Creates a new, empty vector. + +```rust +pub fn new() -> Self +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### from_slice + +Creates a vector containing each element from a given slice. Mutations to the resulting vector will not affect the original slice. + +```rust +pub fn from_slice(slice: [T]) -> Self +``` + +Example: + +```rust +let slice: [Field] = &[1, 2, 3]; +let vector_from_slice = Vec::from_slice(slice); +assert(vector_from_slice.len() == 3); +``` + +### len + +Returns the number of elements in the vector. + +```rust +pub fn len(self) -> Field +``` + +Example: + +```rust +let empty_vector: Vec = Vec::new(); +assert(empty_vector.len() == 0); +``` + +### get + +Retrieves an element from the vector at a given index. Panics if the index points beyond the vector's end. + +```rust +pub fn get(self, index: Field) -> T +``` + +Example: + +```rust +let vector: Vec = Vec::from_slice(&[10, 20, 30]); +assert(vector.get(1) == 20); +``` + +### push + +Adds a new element to the vector's end, returning a new vector with a length one greater than the original unmodified vector. + +```rust +pub fn push(&mut self, elem: T) +``` + +Example: + +```rust +let mut vector: Vec = Vec::new(); +vector.push(10); +assert(vector.len() == 1); +``` + +### pop + +Removes an element from the vector's end, returning a new vector with a length one less than the original vector, along with the removed element. Panics if the vector's length is zero. + +```rust +pub fn pop(&mut self) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20]); +let popped_elem = vector.pop(); +assert(popped_elem == 20); +assert(vector.len() == 1); +``` + +### insert + +Inserts an element at a specified index, shifting subsequent elements to the right. + +```rust +pub fn insert(&mut self, index: Field, elem: T) +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 30]); +vector.insert(1, 20); +assert(vector.get(1) == 20); +``` + +### remove + +Removes an element at a specified index, shifting subsequent elements to the left, and returns the removed element. + +```rust +pub fn remove(&mut self, index: Field) -> T +``` + +Example: + +```rust +let mut vector = Vec::from_slice(&[10, 20, 30]); +let removed_elem = vector.remove(1); +assert(removed_elem == 20); +assert(vector.len() == 2); +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/_category_.json b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/_category_.json new file mode 100644 index 00000000000..5d694210bbf --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 0, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ec_primitives.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ec_primitives.md new file mode 100644 index 00000000000..d2b42d67b7c --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ec_primitives.md @@ -0,0 +1,102 @@ +--- +title: Elliptic Curve Primitives +keywords: [cryptographic primitives, Noir project] +sidebar_position: 4 +--- + +Data structures and methods on them that allow you to carry out computations involving elliptic +curves over the (mathematical) field corresponding to `Field`. For the field currently at our +disposal, applications would involve a curve embedded in BN254, e.g. the +[Baby Jubjub curve](https://eips.ethereum.org/EIPS/eip-2494). + +## Data structures + +### Elliptic curve configurations + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Curve`), i.e. the specific elliptic +curve you want to use, which would be specified using any one of the methods +`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::new` which take the coefficients in the +defining equation together with a generator point as parameters. You can find more detail in the +comments in +[`noir_stdlib/src/ec.nr`](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr), but +the gist of it is that the elliptic curves of interest are usually expressed in one of the standard +forms implemented here (Twisted Edwards, Montgomery and Short Weierstraß), and in addition to that, +you could choose to use `affine` coordinates (Cartesian coordinates - the usual (x,y) - possibly +together with a point at infinity) or `curvegroup` coordinates (some form of projective coordinates +requiring more coordinates but allowing for more efficient implementations of elliptic curve +operations). Conversions between all of these forms are provided, and under the hood these +conversions are done whenever an operation is more efficient in a different representation (or a +mixed coordinate representation is employed). + +### Points + +(`std::ec::{tecurve,montcurve,swcurve}::{affine,curvegroup}::Point`), i.e. points lying on the +elliptic curve. For a curve configuration `c` and a point `p`, it may be checked that `p` +does indeed lie on `c` by calling `c.contains(p1)`. + +## Methods + +(given a choice of curve representation, e.g. use `std::ec::tecurve::affine::Curve` and use +`std::ec::tecurve::affine::Point`) + +- The **zero element** is given by `Point::zero()`, and we can verify whether a point `p: Point` is + zero by calling `p.is_zero()`. +- **Equality**: Points `p1: Point` and `p2: Point` may be checked for equality by calling + `p1.eq(p2)`. +- **Addition**: For `c: Curve` and points `p1: Point` and `p2: Point` on the curve, adding these two + points is accomplished by calling `c.add(p1,p2)`. +- **Negation**: For a point `p: Point`, `p.negate()` is its negation. +- **Subtraction**: For `c` and `p1`, `p2` as above, subtracting `p2` from `p1` is accomplished by + calling `c.subtract(p1,p2)`. +- **Scalar multiplication**: For `c` as above, `p: Point` a point on the curve and `n: Field`, + scalar multiplication is given by `c.mul(n,p)`. If instead `n :: [u1; N]`, i.e. `n` is a bit + array, the `bit_mul` method may be used instead: `c.bit_mul(n,p)` +- **Multi-scalar multiplication**: For `c` as above and arrays `n: [Field; N]` and `p: [Point; N]`, + multi-scalar multiplication is given by `c.msm(n,p)`. +- **Coordinate representation conversions**: The `into_group` method converts a point or curve + configuration in the affine representation to one in the CurveGroup representation, and + `into_affine` goes in the other direction. +- **Curve representation conversions**: `tecurve` and `montcurve` curves and points are equivalent + and may be converted between one another by calling `into_montcurve` or `into_tecurve` on their + configurations or points. `swcurve` is more general and a curve c of one of the other two types + may be converted to this representation by calling `c.into_swcurve()`, whereas a point `p` lying + on the curve given by `c` may be mapped to its corresponding `swcurve` point by calling + `c.map_into_swcurve(p)`. +- **Map-to-curve methods**: The Elligator 2 method of mapping a field element `n: Field` into a + `tecurve` or `montcurve` with configuration `c` may be called as `c.elligator2_map(n)`. For all of + the curve configurations, the SWU map-to-curve method may be called as `c.swu_map(z,n)`, where + `z: Field` depends on `Field` and `c` and must be chosen by the user (the conditions it needs to + satisfy are specified in the comments + [here](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/ec.nr)). + +## Examples + +The +[ec_baby_jubjub test](https://github.com/noir-lang/noir/blob/master/test_programs/compile_success_empty/ec_baby_jubjub/src/main.nr) +illustrates all of the above primitives on various forms of the Baby Jubjub curve. A couple of more +interesting examples in Noir would be: + +Public-key cryptography: Given an elliptic curve and a 'base point' on it, determine the public key +from the private key. This is a matter of using scalar multiplication. In the case of Baby Jubjub, +for example, this code would do: + +```rust +use dep::std::ec::tecurve::affine::{Curve, Point}; + +fn bjj_pub_key(priv_key: Field) -> Point +{ + + let bjj = Curve::new(168700, 168696, G::new(995203441582195749578291179787384436505546430278305826713579947235728471134,5472060717959818805561601436314318772137091100104008585924551046643952123905)); + + let base_pt = Point::new(5299619240641551281634865583518297030282874472190772894086521144482721001553, 16950150798460657717958625567821834550301663161624707787222815936182638968203); + + bjj.mul(priv_key,base_pt) +} +``` + +This would come in handy in a Merkle proof. + +- EdDSA signature verification: This is a matter of combining these primitives with a suitable hash + function. See + [feat(stdlib): EdDSA sig verification noir#1136](https://github.com/noir-lang/noir/pull/1136) for + the case of Baby Jubjub and the Poseidon hash function. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx new file mode 100644 index 00000000000..4394b48f907 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/ecdsa_sig_verification.mdx @@ -0,0 +1,98 @@ +--- +title: ECDSA Signature Verification +description: Learn about the cryptographic primitives regarding ECDSA over the secp256k1 and secp256r1 curves +keywords: [cryptographic primitives, Noir project, ecdsa, secp256k1, secp256r1, signatures] +sidebar_position: 3 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +Noir supports ECDSA signatures verification over the secp256k1 and secp256r1 curves. + +## ecdsa_secp256k1::verify_signature + +Verifier for ECDSA Secp256k1 signatures. +See ecdsa_secp256k1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256k1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256k1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256k1::verify_signature_slice + +Verifier for ECDSA Secp256k1 signatures where the message is a slice. + +```rust title="ecdsa_secp256k1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256k1.nr#L13-L20 + + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures. +See ecdsa_secp256r1::verify_signature_slice for a version that accepts slices directly. + +```rust title="ecdsa_secp256r1" showLineNumbers +pub fn verify_signature( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L2-L9 + + +example: + +```rust +fn main(hashed_message : [u8;32], pub_key_x : [u8;32], pub_key_y : [u8;32], signature : [u8;64]) { + let valid_signature = std::ecdsa_secp256r1::verify_signature(pub_key_x, pub_key_y, signature, hashed_message); + assert(valid_signature); +} +``` + + + +## ecdsa_secp256r1::verify_signature + +Verifier for ECDSA Secp256r1 signatures where the message is a slice. + +```rust title="ecdsa_secp256r1_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: [u8; 32], + public_key_y: [u8; 32], + signature: [u8; 64], + message_hash: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/ecdsa_secp256r1.nr#L13-L20 + + + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/eddsa.mdx b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/eddsa.mdx new file mode 100644 index 00000000000..c2c0624dfad --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/eddsa.mdx @@ -0,0 +1,37 @@ +--- +title: EdDSA Verification +description: Learn about the cryptographic primitives regarding EdDSA +keywords: [cryptographic primitives, Noir project, eddsa, signatures] +sidebar_position: 5 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## eddsa::eddsa_poseidon_verify + +Verifier for EdDSA signatures + +```rust +fn eddsa_poseidon_verify(public_key_x : Field, public_key_y : Field, signature_s: Field, signature_r8_x: Field, signature_r8_y: Field, message: Field) -> bool +``` + +It is also possible to specify the hash algorithm used for the signature by using the `eddsa_verify_with_hasher` function with a parameter implementing the Hasher trait. For instance, if you want to use Poseidon2 instead, you can do the following: +```rust +use dep::std::hash::poseidon2::Poseidon2Hasher; + +let mut hasher = Poseidon2Hasher::default(); +eddsa_verify_with_hasher(pub_key_a.x, pub_key_a.y, s_a, r8_a.x, r8_a.y, msg, &mut hasher); +``` + + + +## eddsa::eddsa_to_pub + +Private to public key conversion. + +Returns `(pub_key_x, pub_key_y)` + +```rust +fn eddsa_to_pub(secret : Field) -> (Field, Field) +``` + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/hashes.mdx b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/hashes.mdx new file mode 100644 index 00000000000..695c7d9406f --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/hashes.mdx @@ -0,0 +1,331 @@ +--- +title: Hash methods +description: + Learn about the cryptographic primitives ready to use for any Noir project, including sha256, + blake2s, pedersen, mimc_bn254 and mimc +keywords: + [cryptographic primitives, Noir project, sha256, blake2s, pedersen, mimc_bn254, mimc, hash] +sidebar_position: 0 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## sha256 + +Given an array of bytes, returns the resulting sha256 hash. +See sha256_slice for a version that works directly on slices. + +```rust title="sha256" showLineNumbers +pub fn sha256(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L10-L12 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::sha256(x); +} +``` + + + +## sha256_slice + +A version of sha256 specialized to slices: + +```rust title="sha256_slice" showLineNumbers +pub fn sha256_slice(input: [u8]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L16-L18 + + + + +## blake2s + +Given an array of bytes, returns an array with the Blake2 hash +See blake2s_slice for a version that works directly on slices. + +```rust title="blake2s" showLineNumbers +pub fn blake2s(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L22-L24 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake2s(x); +} +``` + + + +## blake2s_slice + +A version of blake2s specialized to slices: + +```rust title="blake2s_slice" showLineNumbers +pub fn blake2s_slice(input: [u8]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L28-L30 + + + + +## blake3 + +Given an array of bytes, returns an array with the Blake3 hash +See blake3_slice for a version that works directly on slices. + +```rust title="blake3" showLineNumbers +pub fn blake3(input: [u8; N]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L34-L36 + + +example: + +```rust +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::blake3(x); +} +``` + + + +## blake3_slice + +A version of blake3 specialized to slices: + +```rust title="blake3_slice" showLineNumbers +pub fn blake3_slice(input: [u8]) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L40-L42 + + + + +## pedersen_hash + +Given an array of Fields, returns the Pedersen hash. +See pedersen_hash_slice for a version that works directly on slices. + +```rust title="pedersen_hash" showLineNumbers +pub fn pedersen_hash(input: [Field; N]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L78-L80 + + +example: + +```rust title="pedersen-hash" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_hash: Field) { + let hash = std::hash::pedersen_hash([x, y]); + assert_eq(hash, expected_hash); +} +``` +> Source code: test_programs/execution_success/pedersen_hash/src/main.nr#L1-L8 + + + + +## pedersen_hash_slice + +Given a slice of Fields, returns the Pedersen hash. + +```rust title="pedersen_hash_slice" showLineNumbers +pub fn pedersen_hash_slice(input: [Field]) -> Field +``` +> Source code: noir_stdlib/src/hash.nr#L85-L87 + + + + +## pedersen_commitment + +Given an array of Fields, returns the Pedersen commitment. +See pedersen_commitment_slice for a version that works directly on slices. + +```rust title="pedersen_commitment" showLineNumbers +struct PedersenPoint { + x : Field, + y : Field, +} + +pub fn pedersen_commitment(input: [Field; N]) -> PedersenPoint { +``` +> Source code: noir_stdlib/src/hash.nr#L45-L52 + + +example: + +```rust title="pedersen-commitment" showLineNumbers +use dep::std; + +fn main(x: Field, y: Field, expected_commitment: std::hash::PedersenPoint) { + let commitment = std::hash::pedersen_commitment([x, y]); + assert_eq(commitment.x, expected_commitment.x); + assert_eq(commitment.y, expected_commitment.y); +} +``` +> Source code: test_programs/execution_success/pedersen_commitment/src/main.nr#L1-L9 + + + + +## pedersen_commitment_slice + +Given a slice of Fields, returns the Pedersen commitment. + +```rust title="pedersen_commitment_slice" showLineNumbers +pub fn pedersen_commitment_slice(input: [Field]) -> PedersenPoint { + pedersen_commitment_with_separator_slice(input, 0) +} +``` +> Source code: noir_stdlib/src/hash.nr#L56-L60 + + + + +## keccak256 + +Given an array of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). Specify a message_size to hash only the first +`message_size` bytes of the input. See keccak256_slice for a version that works +directly on slices. + +```rust title="keccak256" showLineNumbers +pub fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L113-L115 + + +example: + +```rust title="keccak256" showLineNumbers +use dep::std; + +fn main(x: Field, result: [u8; 32]) { + // We use the `as` keyword here to denote the fact that we want to take just the first byte from the x Field + // The padding is taken care of by the program + let digest = std::hash::keccak256([x as u8], 1); + assert(digest == result); + + //#1399: variable message size + let message_size = 4; + let hash_a = std::hash::keccak256([1, 2, 3, 4], message_size); + let hash_b = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size); + + assert(hash_a == hash_b); + + let message_size_big = 8; + let hash_c = std::hash::keccak256([1, 2, 3, 4, 0, 0, 0, 0], message_size_big); + + assert(hash_a != hash_c); +} +``` +> Source code: test_programs/execution_success/keccak256/src/main.nr#L1-L22 + + + + +## keccak256_slice + +Given a slice of bytes (`u8`), returns the resulting keccak hash as an array of +32 bytes (`[u8; 32]`). + +```rust title="keccak256_slice" showLineNumbers +pub fn keccak256_slice(input: [u8], message_size: u32) -> [u8; 32] +``` +> Source code: noir_stdlib/src/hash.nr#L119-L121 + + + + +## poseidon + +Given an array of Fields, returns a new Field with the Poseidon Hash. Mind that you need to specify +how many inputs are there to your Poseidon function. + +```rust +// example for hash_1, hash_2 accepts an array of length 2, etc +fn hash_1(input: [Field; 1]) -> Field +``` + +example: + +```rust title="poseidon" showLineNumbers +use dep::std::hash::poseidon; +use dep::std::hash::poseidon2; + +fn main(x1: [Field; 2], y1: pub Field, x2: [Field; 4], y2: pub Field, x3: [Field; 4], y3: Field) { + let hash1 = poseidon::bn254::hash_2(x1); + assert(hash1 == y1); + + let hash2 = poseidon::bn254::hash_4(x2); + assert(hash2 == y2); + + let hash3 = poseidon2::Poseidon2::hash(x3, x3.len()); + assert(hash3 == y3); +} +``` +> Source code: test_programs/execution_success/poseidon_bn254_hash/src/main.nr#L1-L15 + + +## poseidon 2 + +Given an array of Fields, returns a new Field with the Poseidon2 Hash. Contrary to the Poseidon +function, there is only one hash and you can specify a message_size to hash only the first +`message_size` bytes of the input, + +```rust +// example for hashing the first three elements of the input +Poseidon2::hash(input, 3); +``` + +The above example for Poseidon also includes Poseidon2. + +## mimc_bn254 and mimc + +`mimc_bn254` is `mimc`, but with hardcoded parameters for the BN254 curve. You can use it by +providing an array of Fields, and it returns a Field with the hash. You can use the `mimc` method if +you're willing to input your own constants: + +```rust +fn mimc(x: Field, k: Field, constants: [Field; N], exp : Field) -> Field +``` + +otherwise, use the `mimc_bn254` method: + +```rust +fn mimc_bn254(array: [Field; N]) -> Field +``` + +example: + +```rust + +fn main() { + let x = [163, 117, 178, 149]; // some random bytes + let hash = std::hash::mimc::mimc_bn254(x); +} +``` + +## hash_to_field + +```rust +fn hash_to_field(_input : [Field]) -> Field {} +``` + +Calculates the `blake2s` hash of the inputs and returns the hash modulo the field modulus to return +a value which can be represented as a `Field`. + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/index.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/index.md new file mode 100644 index 00000000000..650f30165d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/index.md @@ -0,0 +1,14 @@ +--- +title: Cryptographic Primitives +description: + Learn about the cryptographic primitives ready to use for any Noir project +keywords: + [ + cryptographic primitives, + Noir project, + ] +--- + +The Noir team is progressively adding new cryptographic primitives to the standard library. Reach out for news or if you would be interested in adding more of these calculations in Noir. + +Some methods are available thanks to the Aztec backend, not being performed using Noir. When using other backends, these methods may or may not be supplied. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/scalar.mdx b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/scalar.mdx new file mode 100644 index 00000000000..df411ca5443 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/scalar.mdx @@ -0,0 +1,33 @@ +--- +title: Scalar multiplication +description: See how you can perform scalar multiplications over a fixed base in Noir +keywords: [cryptographic primitives, Noir project, scalar multiplication] +sidebar_position: 1 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## scalar_mul::fixed_base_embedded_curve + +Performs scalar multiplication over the embedded curve whose coordinates are defined by the +configured noir field. For the BN254 scalar field, this is BabyJubJub or Grumpkin. + +```rust title="fixed_base_embedded_curve" showLineNumbers +pub fn fixed_base_embedded_curve( + low: Field, + high: Field +) -> [Field; 2] +``` +> Source code: noir_stdlib/src/scalar_mul.nr#L27-L32 + + +example + +```rust +fn main(x : Field) { + let scal = std::scalar_mul::fixed_base_embedded_curve(x); + println(scal); +} +``` + + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/schnorr.mdx b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/schnorr.mdx new file mode 100644 index 00000000000..b59e69c8f07 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/cryptographic_primitives/schnorr.mdx @@ -0,0 +1,64 @@ +--- +title: Schnorr Signatures +description: Learn how you can verify Schnorr signatures using Noir +keywords: [cryptographic primitives, Noir project, schnorr, signatures] +sidebar_position: 2 +--- + +import BlackBoxInfo from '@site/src/components/Notes/_blackbox.mdx'; + +## schnorr::verify_signature + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin). +See schnorr::verify_signature_slice for a version that works directly on slices. + +```rust title="schnorr_verify" showLineNumbers +pub fn verify_signature( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8; N] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L2-L9 + + +where `_signature` can be generated like so using the npm package +[@noir-lang/barretenberg](https://www.npmjs.com/package/@noir-lang/barretenberg) + +```js +const { BarretenbergWasm } = require('@noir-lang/barretenberg/dest/wasm'); +const { Schnorr } = require('@noir-lang/barretenberg/dest/crypto/schnorr'); + +... + +const barretenberg = await BarretenbergWasm.new(); +const schnorr = new Schnorr(barretenberg); +const pubKey = schnorr.computePublicKey(privateKey); +const message = ... +const signature = Array.from( + schnorr.constructSignature(hash, privateKey).toBuffer() +); + +... +``` + + + +## schnorr::verify_signature_slice + +Verifier for Schnorr signatures over the embedded curve (for BN254 it is Grumpkin) +where the message is a slice. + +```rust title="schnorr_verify_slice" showLineNumbers +pub fn verify_signature_slice( + public_key_x: Field, + public_key_y: Field, + signature: [u8; 64], + message: [u8] +) -> bool +``` +> Source code: noir_stdlib/src/schnorr.nr#L13-L20 + + + diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/logging.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/logging.md new file mode 100644 index 00000000000..db75ef9f86f --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/logging.md @@ -0,0 +1,78 @@ +--- +title: Logging +description: + Learn how to use the println statement for debugging in Noir with this tutorial. Understand the + basics of logging in Noir and how to implement it in your code. +keywords: + [ + noir logging, + println statement, + print statement, + debugging in noir, + noir std library, + logging tutorial, + basic logging in noir, + noir logging implementation, + noir debugging techniques, + rust, + ] +--- + +The standard library provides two familiar statements you can use: `println` and `print`. Despite being a limited implementation of rust's `println!` and `print!` macros, these constructs can be useful for debugging. + +You can print the output of both statements in your Noir code by using the `nargo execute` command or the `--show-output` flag when using `nargo test` (provided there are print statements in your tests). + +It is recommended to use `nargo execute` if you want to debug failing constraints with `println` or `print` statements. This is due to every input in a test being a constant rather than a witness, so we issue an error during compilation while we only print during execution (which comes after compilation). Neither `println`, nor `print` are callable for failed constraints caught at compile time. + +Both `print` and `println` are generic functions which can work on integers, fields, strings, and even structs or expressions. Note however, that slices are currently unsupported. For example: + +```rust +struct Person { + age: Field, + height: Field, +} + +fn main(age: Field, height: Field) { + let person = Person { + age: age, + height: height, + }; + println(person); + println(age + height); + println("Hello world!"); +} +``` + +You can print different types in the same statement (including strings) with a type called `fmtstr`. It can be specified in the same way as a normal string, just prepended with an "f" character: + +```rust + let fmt_str = f"i: {i}, j: {j}"; + println(fmt_str); + + let s = myStruct { y: x, x: y }; + println(s); + + println(f"i: {i}, s: {s}"); + + println(x); + println([x, y]); + + let foo = fooStruct { my_struct: s, foo: 15 }; + println(f"s: {s}, foo: {foo}"); + + println(15); // prints 0x0f, implicit Field + println(-1 as u8); // prints 255 + println(-1 as i8); // prints -1 +``` + +Examples shown above are interchangeable between the two `print` statements: + +```rust +let person = Person { age : age, height : height }; + +println(person); +print(person); + +println("Hello world!"); // Prints with a newline at the end of the input +print("Hello world!"); // Prints the input and keeps cursor on the same line +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/merkle_trees.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/merkle_trees.md new file mode 100644 index 00000000000..6a9ebf72ada --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/merkle_trees.md @@ -0,0 +1,58 @@ +--- +title: Merkle Trees +description: Learn about Merkle Trees in Noir with this tutorial. Explore the basics of computing a merkle root using a proof, with examples. +keywords: + [ + Merkle trees in Noir, + Noir programming language, + check membership, + computing root from leaf, + Noir Merkle tree implementation, + Merkle tree tutorial, + Merkle tree code examples, + Noir libraries, + pedersen hash., + ] +--- + +## compute_merkle_root + +Returns the root of the tree from the provided leaf and its hash path, using a [Pedersen hash](./cryptographic_primitives/hashes.mdx#pedersen_hash). + +```rust +fn compute_merkle_root(leaf : Field, index : Field, hash_path: [Field]) -> Field +``` + +example: + +```rust +/** + // these values are for this example only + index = "0" + priv_key = "0x000000000000000000000000000000000000000000000000000000616c696365" + secret = "0x1929ea3ab8d9106a899386883d9428f8256cfedb3c4f6b66bf4aa4d28a79988f" + note_hash_path = [ + "0x1e61bdae0f027b1b2159e1f9d3f8d00fa668a952dddd822fda80dc745d6f65cc", + "0x0e4223f3925f98934393c74975142bd73079ab0621f4ee133cee050a3c194f1a", + "0x2fd7bb412155bf8693a3bd2a3e7581a679c95c68a052f835dddca85fa1569a40" + ] + */ +fn main(index: Field, priv_key: Field, secret: Field, note_hash_path: [Field; 3]) { + + let pubkey = std::scalar_mul::fixed_base_embedded_curve(priv_key); + let pubkey_x = pubkey[0]; + let pubkey_y = pubkey[1]; + let note_commitment = std::hash::pedersen(&[pubkey_x, pubkey_y, secret]); + + let root = std::merkle::compute_merkle_root(note_commitment[0], index, note_hash_path.as_slice()); + println(root); +} +``` + +To check merkle tree membership: + +1. Include a merkle root as a program input. +2. Compute the merkle root of a given leaf, index and hash path. +3. Assert the merkle roots are equal. + +For more info about merkle trees, see the Wikipedia [page](https://en.wikipedia.org/wiki/Merkle_tree). diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/options.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/options.md new file mode 100644 index 00000000000..a1bd4e1de5f --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/options.md @@ -0,0 +1,101 @@ +--- +title: Option Type +--- + +The `Option` type is a way to express that a value might be present (`Some(T))` or absent (`None`). It's a safer way to handle potential absence of values, compared to using nulls in many other languages. + +```rust +struct Option { + None, + Some(T), +} +``` + +The `Option` type, already imported into your Noir program, can be used directly: + +```rust +fn main() { + let none = Option::none(); + let some = Option::some(3); +} +``` + +See [this test](https://github.com/noir-lang/noir/blob/5cbfb9c4a06c8865c98ff2b594464b037d821a5c/crates/nargo_cli/tests/test_data/option/src/main.nr) for a more comprehensive set of examples of each of the methods described below. + +## Methods + +### none + +Constructs a none value. + +### some + +Constructs a some wrapper around a given value. + +### is_none + +Returns true if the Option is None. + +### is_some + +Returns true of the Option is Some. + +### unwrap + +Asserts `self.is_some()` and returns the wrapped value. + +### unwrap_unchecked + +Returns the inner value without asserting `self.is_some()`. This method can be useful within an if condition when we already know that `option.is_some()`. If the option is None, there is no guarantee what value will be returned, only that it will be of type T for an `Option`. + +### unwrap_or + +Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. + +### unwrap_or_else + +Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return a default value. + +### expect + +Asserts `self.is_some()` with a provided custom message and returns the contained `Some` value. The custom message is expected to be a format string. + +### map + +If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. + +### map_or + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. + +### map_or_else + +If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. + +### and + +Returns None if self is None. Otherwise, this returns `other`. + +### and_then + +If self is None, this returns None. Otherwise, this calls the given function with the Some value contained within self, and returns the result of that call. In some languages this function is called `flat_map` or `bind`. + +### or + +If self is Some, return self. Otherwise, return `other`. + +### or_else + +If self is Some, return self. Otherwise, return `default()`. + +### xor + +If only one of the two Options is Some, return that option. Otherwise, if both options are Some or both are None, None is returned. + +### filter + +Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. Otherwise, this returns `None`. + +### flatten + +Flattens an `Option>` into a `Option`. This returns `None` if the outer Option is None. Otherwise, this returns the inner Option. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md new file mode 100644 index 00000000000..a93894043dc --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/recursion.md @@ -0,0 +1,88 @@ +--- +title: Recursive Proofs +description: Learn about how to write recursive proofs in Noir. +keywords: [recursion, recursive proofs, verification_key, verify_proof] +--- + +Noir supports recursively verifying proofs, meaning you verify the proof of a Noir program in another Noir program. This enables creating proofs of arbitrary size by doing step-wise verification of smaller components of a large proof. + +Read [the explainer on recursion](../../explainers/explainer-recursion.md) to know more about this function and the [guide on how to use it.](../../how_to/how-to-recursion.md) + +## The `#[recursive]` Attribute + +In Noir, the `#[recursive]` attribute is used to indicate that a circuit is designed for recursive proof generation. When applied, it informs the compiler and the tooling that the circuit should be compiled in a way that makes its proofs suitable for recursive verification. This attribute eliminates the need for manual flagging of recursion at the tooling level, streamlining the proof generation process for recursive circuits. + +### Example usage with `#[recursive]` + +```rust +#[recursive] +fn main(x: Field, y: pub Field) { + assert(x == y, "x and y are not equal"); +} + +// This marks the circuit as recursion-friendly and indicates that proofs generated from this circuit +// are intended for recursive verification. +``` + +By incorporating this attribute directly in the circuit's definition, tooling like Nargo and NoirJS can automatically execute recursive-specific duties for Noir programs (e.g. recursive-friendly proof artifact generation) without additional flags or configurations. + +## Verifying Recursive Proofs + +```rust +#[foreign(recursive_aggregation)] +pub fn verify_proof(verification_key: [Field], proof: [Field], public_inputs: [Field], key_hash: Field) {} +``` + +:::info + +This is a black box function. Read [this section](./black_box_fns) to learn more about black box functions in Noir. + +::: + +## Example usage + +```rust +use dep::std; + +fn main( + verification_key : [Field; 114], + proof : [Field; 93], + public_inputs : [Field; 1], + key_hash : Field, + proof_b : [Field; 93], +) { + std::verify_proof( + verification_key.as_slice(), + proof.as_slice(), + public_inputs.as_slice(), + key_hash + ); + + std::verify_proof( + verification_key.as_slice(), + proof_b.as_slice(), + public_inputs.as_slice(), + key_hash + ); +} +``` + +You can see a full example of recursive proofs in [this example recursion demo repo](https://github.com/noir-lang/noir-examples/tree/master/recursion). + +## Parameters + +### `verification_key` + +The verification key for the zk program that is being verified. + +### `proof` + +The proof for the zk program that is being verified. + +### `public_inputs` + +These represent the public inputs of the proof we are verifying. + +### `key_hash` + +A key hash is used to check the validity of the verification key. The circuit implementing this opcode can use this hash to ensure that the key provided to the circuit matches the key produced by the circuit creator. diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/traits.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/traits.md new file mode 100644 index 00000000000..68a9dc3d54b --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/traits.md @@ -0,0 +1,408 @@ +--- +title: Traits +description: Noir's stdlib provides a few commonly used traits. +keywords: [traits, trait, interface, protocol, default, add, eq] +--- + +## `std::default` + +### `std::default::Default` + +```rust title="default-trait" showLineNumbers +trait Default { + fn default() -> Self; +} +``` +> Source code: noir_stdlib/src/default.nr#L1-L5 + + +Constructs a default value of a type. + +Implementations: +```rust +impl Default for Field { .. } + +impl Default for i8 { .. } +impl Default for i16 { .. } +impl Default for i32 { .. } +impl Default for i64 { .. } + +impl Default for u8 { .. } +impl Default for u16 { .. } +impl Default for u32 { .. } +impl Default for u64 { .. } + +impl Default for () { .. } +impl Default for bool { .. } + +impl Default for [T; N] + where T: Default { .. } + +impl Default for [T] { .. } + +impl Default for (A, B) + where A: Default, B: Default { .. } + +impl Default for (A, B, C) + where A: Default, B: Default, C: Default { .. } + +impl Default for (A, B, C, D) + where A: Default, B: Default, C: Default, D: Default { .. } + +impl Default for (A, B, C, D, E) + where A: Default, B: Default, C: Default, D: Default, E: Default { .. } +``` + +For primitive integer types, the return value of `default` is `0`. Container +types such as arrays are filled with default values of their element type, +except slices whose length is unknown and thus defaulted to zero. + + +## `std::convert` + +### `std::convert::From` + +```rust title="from-trait" showLineNumbers +trait From { + fn from(input: T) -> Self; +} +``` +> Source code: noir_stdlib/src/convert.nr#L1-L5 + + +The `From` trait defines how to convert from a given type `T` to the type on which the trait is implemented. + +The Noir standard library provides a number of implementations of `From` between primitive types. +```rust title="from-impls" showLineNumbers +// Unsigned integers + +impl From for u32 { fn from(value: u8) -> u32 { value as u32 } } + +impl From for u64 { fn from(value: u8) -> u64 { value as u64 } } +impl From for u64 { fn from(value: u32) -> u64 { value as u64 } } + +impl From for Field { fn from(value: u8) -> Field { value as Field } } +impl From for Field { fn from(value: u32) -> Field { value as Field } } +impl From for Field { fn from(value: u64) -> Field { value as Field } } + +// Signed integers + +impl From for i32 { fn from(value: i8) -> i32 { value as i32 } } + +impl From for i64 { fn from(value: i8) -> i64 { value as i64 } } +impl From for i64 { fn from(value: i32) -> i64 { value as i64 } } + +// Booleans +impl From for u8 { fn from(value: bool) -> u8 { value as u8 } } +impl From for u32 { fn from(value: bool) -> u32 { value as u32 } } +impl From for u64 { fn from(value: bool) -> u64 { value as u64 } } +impl From for i8 { fn from(value: bool) -> i8 { value as i8 } } +impl From for i32 { fn from(value: bool) -> i32 { value as i32 } } +impl From for i64 { fn from(value: bool) -> i64 { value as i64 } } +impl From for Field { fn from(value: bool) -> Field { value as Field } } +``` +> Source code: noir_stdlib/src/convert.nr#L25-L52 + + +#### When to implement `From` + +As a general rule of thumb, `From` may be implemented in the [situations where it would be suitable in Rust](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from): + +- The conversion is *infallible*: Noir does not provide an equivalent to Rust's `TryFrom`, if the conversion can fail then provide a named method instead. +- The conversion is *lossless*: semantically, it should not lose or discard information. For example, `u32: From` can losslessly convert any `u16` into a valid `u32` such that the original `u16` can be recovered. On the other hand, `u16: From` should not be implemented as `2**16` is a `u32` which cannot be losslessly converted into a `u16`. +- The conversion is *value-preserving*: the conceptual kind and meaning of the resulting value is the same, even though the Noir type and technical representation might be different. While it's possible to infallibly and losslessly convert a `u8` into a `str<2>` hex representation, `4u8` and `"04"` are too different for `str<2>: From` to be implemented. +- The conversion is *obvious*: it's the only reasonable conversion between the two types. If there's ambiguity on how to convert between them such that the same input could potentially map to two different values then a named method should be used. For instance rather than implementing `U128: From<[u8; 16]>`, the methods `U128::from_le_bytes` and `U128::from_be_bytes` are used as otherwise the endianness of the array would be ambiguous, resulting in two potential values of `U128` from the same byte array. + +One additional recommendation specific to Noir is: +- The conversion is *efficient*: it's relatively cheap to convert between the two types. Due to being a ZK DSL, it's more important to avoid unnecessary computation compared to Rust. If the implementation of `From` would encourage users to perform unnecessary conversion, resulting in additional proving time, then it may be preferable to expose functionality such that this conversion may be avoided. + +### `std::convert::Into` + +The `Into` trait is defined as the reciprocal of `From`. It should be easy to convince yourself that if we can convert to type `A` from type `B`, then it's possible to convert type `B` into type `A`. + +For this reason, implementing `From` on a type will automatically generate a matching `Into` implementation. One should always prefer implementing `From` over `Into` as implementing `Into` will not generate a matching `From` implementation. + +```rust title="into-trait" showLineNumbers +trait Into { + fn into(input: Self) -> T; +} + +impl Into for U where T: From { + fn into(input: U) -> T { + T::from(input) + } +} +``` +> Source code: noir_stdlib/src/convert.nr#L13-L23 + + +`Into` is most useful when passing function arguments where the types don't quite match up with what the function expects. In this case, the compiler has enough type information to perform the necessary conversion by just appending `.into()` onto the arguments in question. + + +## `std::cmp` + +### `std::cmp::Eq` + +```rust title="eq-trait" showLineNumbers +trait Eq { + fn eq(self, other: Self) -> bool; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L1-L5 + + +Returns `true` if `self` is equal to `other`. Implementing this trait on a type +allows the type to be used with `==` and `!=`. + +Implementations: +```rust +impl Eq for Field { .. } + +impl Eq for i8 { .. } +impl Eq for i16 { .. } +impl Eq for i32 { .. } +impl Eq for i64 { .. } + +impl Eq for u8 { .. } +impl Eq for u16 { .. } +impl Eq for u32 { .. } +impl Eq for u64 { .. } + +impl Eq for () { .. } +impl Eq for bool { .. } + +impl Eq for [T; N] + where T: Eq { .. } + +impl Eq for [T] + where T: Eq { .. } + +impl Eq for (A, B) + where A: Eq, B: Eq { .. } + +impl Eq for (A, B, C) + where A: Eq, B: Eq, C: Eq { .. } + +impl Eq for (A, B, C, D) + where A: Eq, B: Eq, C: Eq, D: Eq { .. } + +impl Eq for (A, B, C, D, E) + where A: Eq, B: Eq, C: Eq, D: Eq, E: Eq { .. } +``` + +### `std::cmp::Ord` + +```rust title="ord-trait" showLineNumbers +trait Ord { + fn cmp(self, other: Self) -> Ordering; +} +``` +> Source code: noir_stdlib/src/cmp.nr#L102-L106 + + +`a.cmp(b)` compares two values returning `Ordering::less()` if `a < b`, +`Ordering::equal()` if `a == b`, or `Ordering::greater()` if `a > b`. +Implementing this trait on a type allows `<`, `<=`, `>`, and `>=` to be +used on values of the type. + +Implementations: + +```rust +impl Ord for u8 { .. } +impl Ord for u16 { .. } +impl Ord for u32 { .. } +impl Ord for u64 { .. } + +impl Ord for i8 { .. } +impl Ord for i16 { .. } +impl Ord for i32 { .. } + +impl Ord for i64 { .. } + +impl Ord for () { .. } +impl Ord for bool { .. } + +impl Ord for [T; N] + where T: Ord { .. } + +impl Ord for [T] + where T: Ord { .. } + +impl Ord for (A, B) + where A: Ord, B: Ord { .. } + +impl Ord for (A, B, C) + where A: Ord, B: Ord, C: Ord { .. } + +impl Ord for (A, B, C, D) + where A: Ord, B: Ord, C: Ord, D: Ord { .. } + +impl Ord for (A, B, C, D, E) + where A: Ord, B: Ord, C: Ord, D: Ord, E: Ord { .. } +``` + +## `std::ops` + +### `std::ops::Add`, `std::ops::Sub`, `std::ops::Mul`, and `std::ops::Div` + +These traits abstract over addition, subtraction, multiplication, and division respectively. +Implementing these traits for a given type will also allow that type to be used with the corresponding operator +for that trait (`+` for Add, etc) in addition to the normal method names. + +```rust title="add-trait" showLineNumbers +trait Add { + fn add(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L1-L5 + +```rust title="sub-trait" showLineNumbers +trait Sub { + fn sub(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L17-L21 + +```rust title="mul-trait" showLineNumbers +trait Mul { + fn mul(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L33-L37 + +```rust title="div-trait" showLineNumbers +trait Div { + fn div(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L49-L53 + + +The implementations block below is given for the `Add` trait, but the same types that implement +`Add` also implement `Sub`, `Mul`, and `Div`. + +Implementations: +```rust +impl Add for Field { .. } + +impl Add for i8 { .. } +impl Add for i16 { .. } +impl Add for i32 { .. } +impl Add for i64 { .. } + +impl Add for u8 { .. } +impl Add for u16 { .. } +impl Add for u32 { .. } +impl Add for u64 { .. } +``` + +### `std::ops::Rem` + +```rust title="rem-trait" showLineNumbers +trait Rem{ + fn rem(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L65-L69 + + +`Rem::rem(a, b)` is the remainder function returning the result of what is +left after dividing `a` and `b`. Implementing `Rem` allows the `%` operator +to be used with the implementation type. + +Unlike other numeric traits, `Rem` is not implemented for `Field`. + +Implementations: +```rust +impl Rem for u8 { fn rem(self, other: u8) -> u8 { self % other } } +impl Rem for u16 { fn rem(self, other: u16) -> u16 { self % other } } +impl Rem for u32 { fn rem(self, other: u32) -> u32 { self % other } } +impl Rem for u64 { fn rem(self, other: u64) -> u64 { self % other } } + +impl Rem for i8 { fn rem(self, other: i8) -> i8 { self % other } } +impl Rem for i16 { fn rem(self, other: i16) -> i16 { self % other } } +impl Rem for i32 { fn rem(self, other: i32) -> i32 { self % other } } +impl Rem for i64 { fn rem(self, other: i64) -> i64 { self % other } } +``` + +### `std::ops::{ BitOr, BitAnd, BitXor }` + +```rust title="bitor-trait" showLineNumbers +trait BitOr { + fn bitor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L79-L83 + +```rust title="bitand-trait" showLineNumbers +trait BitAnd { + fn bitand(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L95-L99 + +```rust title="bitxor-trait" showLineNumbers +trait BitXor { + fn bitxor(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L111-L115 + + +Traits for the bitwise operations `|`, `&`, and `^`. + +Implementing `BitOr`, `BitAnd` or `BitXor` for a type allows the `|`, `&`, or `^` operator respectively +to be used with the type. + +The implementations block below is given for the `BitOr` trait, but the same types that implement +`BitOr` also implement `BitAnd` and `BitXor`. + +Implementations: +```rust +impl BitOr for bool { fn bitor(self, other: bool) -> bool { self | other } } + +impl BitOr for u8 { fn bitor(self, other: u8) -> u8 { self | other } } +impl BitOr for u16 { fn bitor(self, other: u16) -> u16 { self | other } } +impl BitOr for u32 { fn bitor(self, other: u32) -> u32 { self | other } } +impl BitOr for u64 { fn bitor(self, other: u64) -> u64 { self | other } } + +impl BitOr for i8 { fn bitor(self, other: i8) -> i8 { self | other } } +impl BitOr for i16 { fn bitor(self, other: i16) -> i16 { self | other } } +impl BitOr for i32 { fn bitor(self, other: i32) -> i32 { self | other } } +impl BitOr for i64 { fn bitor(self, other: i64) -> i64 { self | other } } +``` + +### `std::ops::{ Shl, Shr }` + +```rust title="shl-trait" showLineNumbers +trait Shl { + fn shl(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L127-L131 + +```rust title="shr-trait" showLineNumbers +trait Shr { + fn shr(self, other: Self) -> Self; +} +``` +> Source code: noir_stdlib/src/ops.nr#L142-L146 + + +Traits for a bit shift left and bit shift right. + +Implementing `Shl` for a type allows the left shift operator (`<<`) to be used with the implementation type. +Similarly, implementing `Shr` allows the right shift operator (`>>`) to be used with the type. + +Note that bit shifting is not currently implemented for signed types. + +The implementations block below is given for the `Shl` trait, but the same types that implement +`Shl` also implement `Shr`. + +Implementations: +```rust +impl Shl for u8 { fn shl(self, other: u8) -> u8 { self << other } } +impl Shl for u16 { fn shl(self, other: u16) -> u16 { self << other } } +impl Shl for u32 { fn shl(self, other: u32) -> u32 { self << other } } +impl Shl for u64 { fn shl(self, other: u64) -> u64 { self << other } } +``` diff --git a/docs/versioned_docs/version-v0.26.0/noir/standard_library/zeroed.md b/docs/versioned_docs/version-v0.26.0/noir/standard_library/zeroed.md new file mode 100644 index 00000000000..f450fecdd36 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/noir/standard_library/zeroed.md @@ -0,0 +1,26 @@ +--- +title: Zeroed Function +description: + The zeroed function returns a zeroed value of any type. +keywords: + [ + zeroed + ] +--- + +Implements `fn zeroed() -> T` to return a zeroed value of any type. This function is generally unsafe to use as the zeroed bit pattern is not guaranteed to be valid for all types. It can however, be useful in cases when the value is guaranteed not to be used such as in a BoundedVec library implementing a growable vector, up to a certain length, backed by an array. The array can be initialized with zeroed values which are guaranteed to be inaccessible until the vector is pushed to. Similarly, enumerations in noir can be implemented using this method by providing zeroed values for the unused variants. + +You can access the function at `std::unsafe::zeroed`. + +This function currently supports the following types: + +- Field +- Bool +- Uint +- Array +- Slice +- String +- Tuple +- Function + +Using it on other types could result in unexpected behavior. diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/.nojekyll b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md new file mode 100644 index 00000000000..b18c1926b93 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend.md @@ -0,0 +1,119 @@ +# BarretenbergBackend + +## Implements + +- [`Backend`](../index.md#backend) + +## Constructors + +### new BarretenbergBackend(acirCircuit, options) + +```ts +new BarretenbergBackend(acirCircuit, options): BarretenbergBackend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `acirCircuit` | `CompiledCircuit` | +| `options` | [`BackendOptions`](../type-aliases/BackendOptions.md) | + +#### Returns + +[`BarretenbergBackend`](BarretenbergBackend.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +*** + +### generateProof() + +```ts +generateProof(compressedWitness): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `compressedWitness` | `Uint8Array` | + +#### Returns + +`Promise`\<`ProofData`\> + +#### Description + +Generates a proof + +*** + +### generateRecursiveProofArtifacts() + +```ts +generateRecursiveProofArtifacts(proofData, numOfPublicInputs): Promise +``` + +Generates artifacts that will be passed to a circuit that will verify this proof. + +Instead of passing the proof and verification key as a byte array, we pass them +as fields which makes it cheaper to verify in a circuit. + +The proof that is passed here will have been created using a circuit +that has the #[recursive] attribute on its `main` method. + +The number of public inputs denotes how many public inputs are in the inner proof. + +#### Parameters + +| Parameter | Type | Default value | +| :------ | :------ | :------ | +| `proofData` | `ProofData` | `undefined` | +| `numOfPublicInputs` | `number` | `0` | + +#### Returns + +`Promise`\<`object`\> + +#### Example + +```typescript +const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); +``` + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | `ProofData` | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Verifies a proof + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/index.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/index.md new file mode 100644 index 00000000000..c146316a915 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/index.md @@ -0,0 +1,58 @@ +# backend_barretenberg + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [BarretenbergBackend](classes/BarretenbergBackend.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [BackendOptions](type-aliases/BackendOptions.md) | - | + +## References + +### CompiledCircuit + +Renames and re-exports [Backend](index.md#backend) + +*** + +### ProofData + +Renames and re-exports [Backend](index.md#backend) + +## Variables + +### Backend + +```ts +Backend: any; +``` + +## Functions + +### publicInputsToWitnessMap() + +```ts +publicInputsToWitnessMap(publicInputs, abi): Backend +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `publicInputs` | `string`[] | +| `abi` | `Abi` | + +#### Returns + +[`Backend`](index.md#backend) + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md new file mode 100644 index 00000000000..b49a479f4f4 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions.md @@ -0,0 +1,21 @@ +# BackendOptions + +```ts +type BackendOptions: object; +``` + +## Description + +An options object, currently only used to specify the number of threads to use. + +## Type declaration + +| Member | Type | Description | +| :------ | :------ | :------ | +| `memory` | `object` | - | +| `memory.maximum` | `number` | - | +| `threads` | `number` | **Description**

Number of threads | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs new file mode 100644 index 00000000000..339353b9862 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/backend_barretenberg/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/classes/BarretenbergBackend","label":"BarretenbergBackend"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/backend_barretenberg/type-aliases/BackendOptions","label":"BackendOptions"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/.nojekyll b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/classes/Noir.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/classes/Noir.md new file mode 100644 index 00000000000..45dd62ee57e --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/classes/Noir.md @@ -0,0 +1,132 @@ +# Noir + +## Constructors + +### new Noir(circuit, backend) + +```ts +new Noir(circuit, backend?): Noir +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `circuit` | `CompiledCircuit` | +| `backend`? | `any` | + +#### Returns + +[`Noir`](Noir.md) + +## Methods + +### destroy() + +```ts +destroy(): Promise +``` + +#### Returns + +`Promise`\<`void`\> + +#### Description + +Destroys the underlying backend instance. + +#### Example + +```typescript +await noir.destroy(); +``` + +*** + +### execute() + +```ts +execute(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`object`\> + +#### Description + +Allows to execute a circuit to get its witness and return value. + +#### Example + +```typescript +async execute(inputs) +``` + +*** + +### generateProof() + +```ts +generateProof(inputs, foreignCallHandler?): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `inputs` | `InputMap` | +| `foreignCallHandler`? | [`ForeignCallHandler`](../type-aliases/ForeignCallHandler.md) | + +#### Returns + +`Promise`\<`ProofData`\> + +#### Description + +Generates a witness and a proof given an object as input. + +#### Example + +```typescript +async generateProof(input) +``` + +*** + +### verifyProof() + +```ts +verifyProof(proofData): Promise +``` + +#### Parameters + +| Parameter | Type | +| :------ | :------ | +| `proofData` | `ProofData` | + +#### Returns + +`Promise`\<`boolean`\> + +#### Description + +Instantiates the verification key and verifies a proof. + +#### Example + +```typescript +async verifyProof(proof) +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/and.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/and.md new file mode 100644 index 00000000000..c783283e396 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/and.md @@ -0,0 +1,22 @@ +# and() + +```ts +and(lhs, rhs): string +``` + +Performs a bitwise AND operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/blake2s256.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/blake2s256.md new file mode 100644 index 00000000000..7882d0da8d5 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/blake2s256.md @@ -0,0 +1,21 @@ +# blake2s256() + +```ts +blake2s256(inputs): Uint8Array +``` + +Calculates the Blake2s256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md new file mode 100644 index 00000000000..5e3cd53e9d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256k1\_verify() + +```ts +ecdsa_secp256k1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256k1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md new file mode 100644 index 00000000000..0b20ff68957 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify.md @@ -0,0 +1,28 @@ +# ecdsa\_secp256r1\_verify() + +```ts +ecdsa_secp256r1_verify( + hashed_msg, + public_key_x_bytes, + public_key_y_bytes, + signature): boolean +``` + +Verifies a ECDSA signature over the secp256r1 curve. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `hashed_msg` | `Uint8Array` | | +| `public_key_x_bytes` | `Uint8Array` | | +| `public_key_y_bytes` | `Uint8Array` | | +| `signature` | `Uint8Array` | | + +## Returns + +`boolean` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/keccak256.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/keccak256.md new file mode 100644 index 00000000000..d10f155ce86 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/keccak256.md @@ -0,0 +1,21 @@ +# keccak256() + +```ts +keccak256(inputs): Uint8Array +``` + +Calculates the Keccak256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/sha256.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/sha256.md new file mode 100644 index 00000000000..6ba4ecac022 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/sha256.md @@ -0,0 +1,21 @@ +# sha256() + +```ts +sha256(inputs): Uint8Array +``` + +Calculates the SHA256 hash of the input bytes + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `inputs` | `Uint8Array` | | + +## Returns + +`Uint8Array` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/xor.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/xor.md new file mode 100644 index 00000000000..8d762b895d3 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/functions/xor.md @@ -0,0 +1,22 @@ +# xor() + +```ts +xor(lhs, rhs): string +``` + +Performs a bitwise XOR operation between `lhs` and `rhs` + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `lhs` | `string` | | +| `rhs` | `string` | | + +## Returns + +`string` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/index.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/index.md new file mode 100644 index 00000000000..cca6b3ace41 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/index.md @@ -0,0 +1,54 @@ +# noir_js + +## Exports + +### Classes + +| Class | Description | +| :------ | :------ | +| [Noir](classes/Noir.md) | - | + +### Type Aliases + +| Type alias | Description | +| :------ | :------ | +| [ForeignCallHandler](type-aliases/ForeignCallHandler.md) | A callback which performs an foreign call and returns the response. | +| [ForeignCallInput](type-aliases/ForeignCallInput.md) | - | +| [ForeignCallOutput](type-aliases/ForeignCallOutput.md) | - | +| [WitnessMap](type-aliases/WitnessMap.md) | - | + +### Functions + +| Function | Description | +| :------ | :------ | +| [and](functions/and.md) | Performs a bitwise AND operation between `lhs` and `rhs` | +| [blake2s256](functions/blake2s256.md) | Calculates the Blake2s256 hash of the input bytes | +| [ecdsa\_secp256k1\_verify](functions/ecdsa_secp256k1_verify.md) | Verifies a ECDSA signature over the secp256k1 curve. | +| [ecdsa\_secp256r1\_verify](functions/ecdsa_secp256r1_verify.md) | Verifies a ECDSA signature over the secp256r1 curve. | +| [keccak256](functions/keccak256.md) | Calculates the Keccak256 hash of the input bytes | +| [sha256](functions/sha256.md) | Calculates the SHA256 hash of the input bytes | +| [xor](functions/xor.md) | Performs a bitwise XOR operation between `lhs` and `rhs` | + +## References + +### CompiledCircuit + +Renames and re-exports [InputMap](index.md#inputmap) + +*** + +### ProofData + +Renames and re-exports [InputMap](index.md#inputmap) + +## Variables + +### InputMap + +```ts +InputMap: any; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md new file mode 100644 index 00000000000..812b8b16481 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallHandler.md @@ -0,0 +1,24 @@ +# ForeignCallHandler + +```ts +type ForeignCallHandler: (name, inputs) => Promise; +``` + +A callback which performs an foreign call and returns the response. + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | The identifier for the type of foreign call being performed. | +| `inputs` | [`ForeignCallInput`](ForeignCallInput.md)[] | An array of hex encoded inputs to the foreign call. | + +## Returns + +`Promise`\<[`ForeignCallOutput`](ForeignCallOutput.md)[]\> + +outputs - An array of hex encoded outputs containing the results of the foreign call. + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md new file mode 100644 index 00000000000..dd95809186a --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallInput.md @@ -0,0 +1,9 @@ +# ForeignCallInput + +```ts +type ForeignCallInput: string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md new file mode 100644 index 00000000000..b71fb78a946 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/ForeignCallOutput.md @@ -0,0 +1,9 @@ +# ForeignCallOutput + +```ts +type ForeignCallOutput: string | string[]; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md new file mode 100644 index 00000000000..258c46f9d0c --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/type-aliases/WitnessMap.md @@ -0,0 +1,9 @@ +# WitnessMap + +```ts +type WitnessMap: Map; +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs new file mode 100644 index 00000000000..c6d8125eaad --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_js/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"category","label":"Classes","items":[{"type":"doc","id":"reference/NoirJS/noir_js/classes/Noir","label":"Noir"}]},{"type":"category","label":"Type Aliases","items":[{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallHandler","label":"ForeignCallHandler"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallInput","label":"ForeignCallInput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/ForeignCallOutput","label":"ForeignCallOutput"},{"type":"doc","id":"reference/NoirJS/noir_js/type-aliases/WitnessMap","label":"WitnessMap"}]},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_js/functions/and","label":"and"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/blake2s256","label":"blake2s256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256k1_verify","label":"ecdsa_secp256k1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/ecdsa_secp256r1_verify","label":"ecdsa_secp256r1_verify"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/keccak256","label":"keccak256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/sha256","label":"sha256"},{"type":"doc","id":"reference/NoirJS/noir_js/functions/xor","label":"xor"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/.nojekyll b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/.nojekyll new file mode 100644 index 00000000000..e2ac6616add --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile.md new file mode 100644 index 00000000000..6faf763b37f --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile.md @@ -0,0 +1,51 @@ +# compile() + +```ts +compile( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ProgramCompilationArtifacts`](../index.md#programcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_program(fm); +``` + +```typescript +// Browser + +import { compile_program, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_program(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile_contract.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile_contract.md new file mode 100644 index 00000000000..7d0b39a43ef --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/compile_contract.md @@ -0,0 +1,51 @@ +# compile\_contract() + +```ts +compile_contract( + fileManager, + projectPath?, + logFn?, +debugLogFn?): Promise +``` + +Compiles a Noir project + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `fileManager` | `FileManager` | The file manager to use | +| `projectPath`? | `string` | The path to the project inside the file manager. Defaults to the root of the file manager | +| `logFn`? | `LogFn` | A logging function. If not provided, console.log will be used | +| `debugLogFn`? | `LogFn` | A debug logging function. If not provided, logFn will be used | + +## Returns + +`Promise`\<[`ContractCompilationArtifacts`](../index.md#contractcompilationartifacts)\> + +## Example + +```typescript +// Node.js + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager(myProjectPath); +const myCompiledCode = await compile_contract(fm); +``` + +```typescript +// Browser + +import { compile_contract, createFileManager } from '@noir-lang/noir_wasm'; + +const fm = createFileManager('/'); +for (const path of files) { + await fm.writeFile(path, await getFileAsStream(path)); +} +const myCompiledCode = await compile_contract(fm); +``` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/createFileManager.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/createFileManager.md new file mode 100644 index 00000000000..7e65c1d69c7 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/createFileManager.md @@ -0,0 +1,21 @@ +# createFileManager() + +```ts +createFileManager(dataDir): FileManager +``` + +Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `dataDir` | `string` | root of the file system | + +## Returns + +`FileManager` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md new file mode 100644 index 00000000000..fcea9275341 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/functions/inflateDebugSymbols.md @@ -0,0 +1,21 @@ +# inflateDebugSymbols() + +```ts +inflateDebugSymbols(debugSymbols): any +``` + +Decompresses and decodes the debug symbols + +## Parameters + +| Parameter | Type | Description | +| :------ | :------ | :------ | +| `debugSymbols` | `string` | The base64 encoded debug symbols | + +## Returns + +`any` + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/index.md b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/index.md new file mode 100644 index 00000000000..b6e0f9d1bc0 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/index.md @@ -0,0 +1,49 @@ +# noir_wasm + +## Exports + +### Functions + +| Function | Description | +| :------ | :------ | +| [compile](functions/compile.md) | Compiles a Noir project | +| [compile\_contract](functions/compile_contract.md) | Compiles a Noir project | +| [createFileManager](functions/createFileManager.md) | Creates a new FileManager instance based on fs in node and memfs in the browser (via webpack alias) | +| [inflateDebugSymbols](functions/inflateDebugSymbols.md) | Decompresses and decodes the debug symbols | + +## References + +### compile\_program + +Renames and re-exports [compile](functions/compile.md) + +## Interfaces + +### ContractCompilationArtifacts + +The compilation artifacts of a given contract. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `contract` | `ContractArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +### ProgramCompilationArtifacts + +The compilation artifacts of a given program. + +#### Properties + +| Property | Type | Description | +| :------ | :------ | :------ | +| `name` | `string` | not part of the compilation output, injected later | +| `program` | `ProgramArtifact` | The compiled contract. | +| `warnings` | `unknown`[] | Compilation warnings. | + +*** + +Generated using [typedoc-plugin-markdown](https://www.npmjs.com/package/typedoc-plugin-markdown) and [TypeDoc](https://typedoc.org/) diff --git a/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs new file mode 100644 index 00000000000..e0870710349 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/NoirJS/noir_wasm/typedoc-sidebar.cjs @@ -0,0 +1,4 @@ +// @ts-check +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const typedocSidebar = { items: [{"type":"doc","id":"reference/NoirJS/noir_wasm/index","label":"API"},{"type":"category","label":"Functions","items":[{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile","label":"compile"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/compile_contract","label":"compile_contract"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/createFileManager","label":"createFileManager"},{"type":"doc","id":"reference/NoirJS/noir_wasm/functions/inflateDebugSymbols","label":"inflateDebugSymbols"}]}]}; +module.exports = typedocSidebar.items; \ No newline at end of file diff --git a/docs/versioned_docs/version-v0.26.0/reference/_category_.json b/docs/versioned_docs/version-v0.26.0/reference/_category_.json new file mode 100644 index 00000000000..5b6a20a609a --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/_category_.json @@ -0,0 +1,5 @@ +{ + "position": 4, + "collapsible": true, + "collapsed": true +} diff --git a/docs/versioned_docs/version-v0.26.0/reference/nargo_commands.md b/docs/versioned_docs/version-v0.26.0/reference/nargo_commands.md new file mode 100644 index 00000000000..218fcfb0c8c --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/reference/nargo_commands.md @@ -0,0 +1,381 @@ +--- +title: Nargo +description: + Noir CLI Commands for Noir Prover and Verifier to create, execute, prove and verify programs, + generate Solidity verifier smart contract and compile into JSON file containing ACIR + representation and ABI of circuit. +keywords: + [ + Nargo, + Noir CLI, + Noir Prover, + Noir Verifier, + generate Solidity verifier, + compile JSON file, + ACIR representation, + ABI of circuit, + TypeScript, + ] +sidebar_position: 0 +--- + +# Command-Line Help for `nargo` + +This document contains the help content for the `nargo` command-line program. + +**Command Overview:** + +* [`nargo`↴](#nargo) +* [`nargo backend`↴](#nargo-backend) +* [`nargo backend current`↴](#nargo-backend-current) +* [`nargo backend ls`↴](#nargo-backend-ls) +* [`nargo backend use`↴](#nargo-backend-use) +* [`nargo backend install`↴](#nargo-backend-install) +* [`nargo backend uninstall`↴](#nargo-backend-uninstall) +* [`nargo check`↴](#nargo-check) +* [`nargo fmt`↴](#nargo-fmt) +* [`nargo codegen-verifier`↴](#nargo-codegen-verifier) +* [`nargo compile`↴](#nargo-compile) +* [`nargo new`↴](#nargo-new) +* [`nargo init`↴](#nargo-init) +* [`nargo execute`↴](#nargo-execute) +* [`nargo prove`↴](#nargo-prove) +* [`nargo verify`↴](#nargo-verify) +* [`nargo test`↴](#nargo-test) +* [`nargo info`↴](#nargo-info) +* [`nargo lsp`↴](#nargo-lsp) + +## `nargo` + +Noir's package manager + +**Usage:** `nargo ` + +###### **Subcommands:** + +* `backend` — Install and select custom backends used to generate and verify proofs +* `check` — Checks the constraint system for errors +* `fmt` — Format the Noir files in a workspace +* `codegen-verifier` — Generates a Solidity verifier smart contract for the program +* `compile` — Compile the program and its secret execution trace into ACIR format +* `new` — Create a Noir project in a new directory +* `init` — Create a Noir project in the current directory +* `execute` — Executes a circuit to calculate its return value +* `prove` — Create proof for this program. The proof is returned as a hex encoded string +* `verify` — Given a proof and a program, verify whether the proof is valid +* `test` — Run the tests for this program +* `info` — Provides detailed information on each of a program's function (represented by a single circuit) +* `lsp` — Starts the Noir LSP server + +###### **Options:** + + + + +## `nargo backend` + +Install and select custom backends used to generate and verify proofs + +**Usage:** `nargo backend ` + +###### **Subcommands:** + +* `current` — Prints the name of the currently active backend +* `ls` — Prints the list of currently installed backends +* `use` — Select the backend to use +* `install` — Install a new backend from a URL +* `uninstall` — Uninstalls a backend + + + +## `nargo backend current` + +Prints the name of the currently active backend + +**Usage:** `nargo backend current` + + + +## `nargo backend ls` + +Prints the list of currently installed backends + +**Usage:** `nargo backend ls` + + + +## `nargo backend use` + +Select the backend to use + +**Usage:** `nargo backend use ` + +###### **Arguments:** + +* `` + + + +## `nargo backend install` + +Install a new backend from a URL + +**Usage:** `nargo backend install ` + +###### **Arguments:** + +* `` — The name of the backend to install +* `` — The URL from which to download the backend + + + +## `nargo backend uninstall` + +Uninstalls a backend + +**Usage:** `nargo backend uninstall ` + +###### **Arguments:** + +* `` — The name of the backend to uninstall + + + +## `nargo check` + +Checks the constraint system for errors + +**Usage:** `nargo check [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to check +* `--workspace` — Check all packages in the workspace +* `--overwrite` — Force overwrite of existing files +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo fmt` + +Format the Noir files in a workspace + +**Usage:** `nargo fmt [OPTIONS]` + +###### **Options:** + +* `--check` — Run noirfmt in check mode + + + +## `nargo codegen-verifier` + +Generates a Solidity verifier smart contract for the program + +**Usage:** `nargo codegen-verifier [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to codegen +* `--workspace` — Codegen all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo compile` + +Compile the program and its secret execution trace into ACIR format + +**Usage:** `nargo compile [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to compile +* `--workspace` — Compile all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo new` + +Create a Noir project in a new directory + +**Usage:** `nargo new [OPTIONS] ` + +###### **Arguments:** + +* `` — The path to save the new project + +###### **Options:** + +* `--name ` — Name of the package [default: package directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo init` + +Create a Noir project in the current directory + +**Usage:** `nargo init [OPTIONS]` + +###### **Options:** + +* `--name ` — Name of the package [default: current directory name] +* `--lib` — Use a library template +* `--bin` — Use a binary template [default] +* `--contract` — Use a contract template + + + +## `nargo execute` + +Executes a circuit to calculate its return value + +**Usage:** `nargo execute [OPTIONS] [WITNESS_NAME]` + +###### **Arguments:** + +* `` — Write the execution witness to named file + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `--package ` — The name of the package to execute +* `--workspace` — Execute all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo prove` + +Create proof for this program. The proof is returned as a hex encoded string + +**Usage:** `nargo prove [OPTIONS]` + +###### **Options:** + +* `-p`, `--prover-name ` — The name of the toml file which contains the inputs for the prover + + Default value: `Prover` +* `-v`, `--verifier-name ` — The name of the toml file which contains the inputs for the verifier + + Default value: `Verifier` +* `--verify` — Verify proof after proving +* `--package ` — The name of the package to prove +* `--workspace` — Prove all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo verify` + +Given a proof and a program, verify whether the proof is valid + +**Usage:** `nargo verify [OPTIONS]` + +###### **Options:** + +* `-v`, `--verifier-name ` — The name of the toml file which contains the inputs for the verifier + + Default value: `Verifier` +* `--package ` — The name of the package verify +* `--workspace` — Verify all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo test` + +Run the tests for this program + +**Usage:** `nargo test [OPTIONS] [TEST_NAME]` + +###### **Arguments:** + +* `` — If given, only tests with names containing this string will be run + +###### **Options:** + +* `--show-output` — Display output of `println` statements +* `--exact` — Only run tests that match exactly +* `--package ` — The name of the package to test +* `--workspace` — Test all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings +* `--oracle-resolver ` — JSON RPC url to solve oracle calls + + + +## `nargo info` + +Provides detailed information on each of a program's function (represented by a single circuit) + +Current information provided per circuit: 1. The number of ACIR opcodes 2. Counts the final number gates in the circuit used by a backend + +**Usage:** `nargo info [OPTIONS]` + +###### **Options:** + +* `--package ` — The name of the package to detail +* `--workspace` — Detail all packages in the workspace +* `--expression-width ` — Override the expression width requested by the backend +* `--force` — Force a full recompilation +* `--print-acir` — Display the ACIR for compiled circuit +* `--deny-warnings` — Treat all warnings as errors +* `--silence-warnings` — Suppress warnings + + + +## `nargo lsp` + +Starts the Noir LSP server + +Starts an LSP server which allows IDEs such as VS Code to display diagnostics in Noir source. + +VS Code Noir Language Support: https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir + +**Usage:** `nargo lsp` + + + +
+ + + This document was generated automatically by + clap-markdown. + + diff --git a/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md b/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md new file mode 100644 index 00000000000..12beb476994 --- /dev/null +++ b/docs/versioned_docs/version-v0.26.0/tutorials/noirjs_app.md @@ -0,0 +1,279 @@ +--- +title: Building a web app with NoirJS +description: Learn how to setup a new app that uses Noir to generate and verify zero-knowledge SNARK proofs in a typescript or javascript environment. +keywords: [how to, guide, javascript, typescript, noir, barretenberg, zero-knowledge, proofs, app] +sidebar_position: 0 +pagination_next: noir/concepts/data_types/index +--- + +NoirJS is a set of packages meant to work both in a browser and a server environment. In this tutorial, we will build a simple web app using them. From here, you should get an idea on how to proceed with your own Noir projects! + +You can find the complete app code for this guide [here](https://github.com/noir-lang/tiny-noirjs-app). + +## Setup + +:::note + +Feel free to use whatever versions, just keep in mind that Nargo and the NoirJS packages are meant to be in sync. For example, Nargo 0.19.x matches `noir_js@0.19.x`, etc. + +In this guide, we will be pinned to 0.19.4. + +::: + +Before we start, we want to make sure we have Node and Nargo installed. + +We start by opening a terminal and executing `node --version`. If we don't get an output like `v20.10.0`, that means node is not installed. Let's do that by following the handy [nvm guide](https://github.com/nvm-sh/nvm?tab=readme-ov-file#install--update-script). + +As for `Nargo`, we can follow the the [Nargo guide](../getting_started/installation/index.md) to install it. If you're lazy, just paste this on a terminal and run `noirup`: + +```sh +curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash +``` + +Easy enough. Onwards! + +## Our project + +ZK is a powerful technology. An app that doesn't reveal one of the inputs to *anyone* is almost unbelievable, yet Noir makes it as easy as a single line of code. + +In fact, it's so simple that it comes nicely packaged in `nargo`. Let's do that! + +### Nargo + +Run: + +```nargo new circuit``` + +And... That's about it. Your program is ready to be compiled and run. + +To compile, let's `cd` into the `circuit` folder to enter our project, and call: + +```nargo compile``` + +This compiles our circuit into `json` format and add it to a new `target` folder. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit <---- our working directory + ├── Nargo.toml + ├── src + │ └── main.nr + └── target + └── circuit.json +``` + +::: + +### Node and Vite + +If you want to explore Nargo, feel free to go on a side-quest now and follow the steps in the +[getting started](../getting_started/hello_noir/index.md) guide. However, we want our app to run on the browser, so we need Vite. + +Vite is a powerful tool to generate static websites. While it provides all kinds of features, let's just go barebones with some good old vanilla JS. + +To do this this, go back to the previous folder (`cd ..`) and create a new vite project by running `npm create vite` and choosing "Vanilla" and "Javascript". + +You should see `vite-project` appear in your root folder. This seems like a good time to `cd` into it and install our NoirJS packages: + +```bash +npm i @noir-lang/backend_barretenberg@0.19.4 @noir-lang/noir_js@0.19.4 +``` + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...etc... +└── vite-project <---- our working directory + └── ...etc... +``` + +::: + +#### Some cleanup + +`npx create vite` is amazing but it creates a bunch of files we don't really need for our simple example. Actually, let's just delete everything except for `index.html`, `main.js` and `package.json`. I feel lighter already. + +![my heart is ready for you, noir.js](@site/static/img/memes/titanic.jpeg) + +## HTML + +Our app won't run like this, of course. We need some working HTML, at least. Let's open our broken-hearted `index.html` and replace everything with this code snippet: + +```html + + + + + + +

Noir app

+
+ + +
+
+

Logs

+

Proof

+
+ + +``` + +It *could* be a beautiful UI... Depending on which universe you live in. + +## Some good old vanilla Javascript + +Our love for Noir needs undivided attention, so let's just open `main.js` and delete everything (this is where the romantic scenery becomes a bit creepy). + +Start by pasting in this boilerplate code: + +```js +const setup = async () => { + await Promise.all([ + import("@noir-lang/noirc_abi").then(module => + module.default(new URL("@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm", import.meta.url).toString()) + ), + import("@noir-lang/acvm_js").then(module => + module.default(new URL("@noir-lang/acvm_js/web/acvm_js_bg.wasm", import.meta.url).toString()) + ) + ]); +} + +function display(container, msg) { + const c = document.getElementById(container); + const p = document.createElement('p'); + p.textContent = msg; + c.appendChild(p); +} + +document.getElementById('submitGuess').addEventListener('click', async () => { + try { + // here's where love happens + } catch(err) { + display("logs", "Oh 💔 Wrong guess") + } +}); + +``` + +The display function doesn't do much. We're simply manipulating our website to see stuff happening. For example, if the proof fails, it will simply log a broken heart 😢 + +As for the `setup` function, it's just a sad reminder that dealing with `wasm` on the browser is not as easy as it should. Just copy, paste, and forget. + +:::info + +At this point in the tutorial, your folder structure should look like this: + +```tree +. +└── circuit + └── ...same as above +└── vite-project + ├── main.js + ├── package.json + └── index.html +``` + +You'll see other files and folders showing up (like `package-lock.json`, `node_modules`) but you shouldn't have to care about those. + +::: + +## Some NoirJS + +We're starting with the good stuff now. If you've compiled the circuit as described above, you should have a `json` file we want to import at the very top of our `main.js` file: + +```ts +import circuit from '../circuit/target/circuit.json'; +``` + +[Noir is backend-agnostic](../index.mdx#whats-new-about-noir). We write Noir, but we also need a proving backend. That's why we need to import and instantiate the two dependencies we installed above: `BarretenbergBackend` and `Noir`. Let's import them right below: + +```js +import { BarretenbergBackend } from '@noir-lang/backend_barretenberg'; +import { Noir } from '@noir-lang/noir_js'; +``` + +And instantiate them inside our try-catch block: + +```ts +// try { +const backend = new BarretenbergBackend(circuit); +const noir = new Noir(circuit, backend); +// } +``` + +:::note + +For the remainder of the tutorial, everything will be happening inside the `try` block + +::: + +## Our app + +Now for the app itself. We're capturing whatever is in the input when people press the submit button. Just add this: + +```js +const x = parseInt(document.getElementById('guessInput').value); +const input = { x, y: 2 }; +``` + +Now we're ready to prove stuff! Let's feed some inputs to our circuit and calculate the proof: + +```js +await setup(); // let's squeeze our wasm inits here + +display('logs', 'Generating proof... ⌛'); +const proof = await noir.generateProof(input); +display('logs', 'Generating proof... ✅'); +display('results', proof.proof); +``` + +You're probably eager to see stuff happening, so go and run your app now! + +From your terminal, run `npm run dev`. If it doesn't open a browser for you, just visit `localhost:5173`. You should now see the worst UI ever, with an ugly input. + +![Getting Started 0](@site/static/img/noir_getting_started_1.png) + +Now, our circuit says `fn main(x: Field, y: pub Field)`. This means only the `y` value is public, and it's hardcoded above: `input = { x, y: 2 }`. In other words, you won't need to send your secret`x` to the verifier! + +By inputting any number other than 2 in the input box and clicking "submit", you should get a valid proof. Otherwise the proof won't even generate correctly. By the way, if you're human, you shouldn't be able to understand anything on the "proof" box. That's OK. We like you, human ❤️. + +## Verifying + +Time to celebrate, yes! But we shouldn't trust machines so blindly. Let's add these lines to see our proof being verified: + +```js +display('logs', 'Verifying proof... ⌛'); +const verification = await noir.verifyProof(proof); +if (verification) display('logs', 'Verifying proof... ✅'); +``` + +You have successfully generated a client-side Noir web app! + +![coded app without math knowledge](@site/static/img/memes/flextape.jpeg) + +## Further Reading + +You can see how noirjs is used in a full stack Next.js hardhat application in the [noir-starter repo here](https://github.com/noir-lang/noir-starter/tree/main/vite-hardhat). The example shows how to calculate a proof in the browser and verify it with a deployed Solidity verifier contract from noirjs. + +You should also check out the more advanced examples in the [noir-examples repo](https://github.com/noir-lang/noir-examples), where you'll find reference usage for some cool apps. diff --git a/docs/versioned_sidebars/version-v0.26.0-sidebars.json b/docs/versioned_sidebars/version-v0.26.0-sidebars.json new file mode 100644 index 00000000000..b16f79cc176 --- /dev/null +++ b/docs/versioned_sidebars/version-v0.26.0-sidebars.json @@ -0,0 +1,83 @@ +{ + "sidebar": [ + { + "type": "doc", + "id": "index" + }, + { + "type": "category", + "label": "Getting Started", + "items": [ + { + "type": "autogenerated", + "dirName": "getting_started" + } + ] + }, + { + "type": "category", + "label": "The Noir Language", + "items": [ + { + "type": "autogenerated", + "dirName": "noir" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "category", + "label": "How To Guides", + "items": [ + { + "type": "autogenerated", + "dirName": "how_to" + } + ] + }, + { + "type": "category", + "label": "Explainers", + "items": [ + { + "type": "autogenerated", + "dirName": "explainers" + } + ] + }, + { + "type": "category", + "label": "Tutorials", + "items": [ + { + "type": "autogenerated", + "dirName": "tutorials" + } + ] + }, + { + "type": "category", + "label": "Reference", + "items": [ + { + "type": "autogenerated", + "dirName": "reference" + } + ] + }, + { + "type": "html", + "value": "
", + "defaultStyle": true + }, + { + "type": "doc", + "id": "migration_notes", + "label": "Migration notes" + } + ] +} From 90ee4792c8e7115e55a3b1dadd1e43066ad8ac66 Mon Sep 17 00:00:00 2001 From: jfecher Date: Thu, 11 Apr 2024 14:35:48 -0400 Subject: [PATCH 168/416] fix: ArrayGet and Set are not pure (#4783) # Description ## Problem\* Resolves #4600 ## Summary\* Array sets and gets are not pure. Listing them as such can cause constant folding to fold away enabled array gets for ones in disabled contexts (enable_side_effects_if u1 0). When used in disabled contexts they will only ever retrieve from index zero. ## Additional Context No reproduceable test case unfortunately. The ones in #4600 all require aztec as a dependency and the ones in #4717 only trigger with the remove_if_else pass added there. I'm going to add a SSA unit test instead where two array get instructions should survive constant folding. This PR is a draft until I add that. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/ssa/function_builder/mod.rs | 6 +++ .../noirc_evaluator/src/ssa/ir/instruction.rs | 8 ++- .../src/ssa/opt/constant_folding.rs | 51 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index d3e5e506111..75a427397b6 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -326,6 +326,12 @@ impl FunctionBuilder { self.insert_instruction(Instruction::DecrementRc { value }, None); } + /// Insert an enable_side_effects_if instruction. These are normally only automatically + /// inserted during the flattening pass when branching is removed. + pub(crate) fn insert_enable_side_effects_if(&mut self, condition: ValueId) { + self.insert_instruction(Instruction::EnableSideEffects { condition }, None); + } + /// Terminates the current block with the given terminator instruction /// if the current block does not already have a terminator instruction. fn terminate_block_with(&mut self, terminator: TerminatorInstruction) { diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index 2b23cc1c1e8..641d971af3c 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -254,7 +254,7 @@ impl Instruction { // In ACIR, a division with a false predicate outputs (0,0), so it cannot replace another instruction unless they have the same predicate bin.operator != BinaryOp::Div } - Cast(_, _) | Truncate { .. } | Not(_) | ArrayGet { .. } | ArraySet { .. } => true, + Cast(_, _) | Truncate { .. } | Not(_) => true, // These either have side-effects or interact with memory Constrain(..) @@ -266,6 +266,12 @@ impl Instruction { | DecrementRc { .. } | RangeCheck { .. } => false, + // These can have different behavior depending on the EnableSideEffectsIf context. + // Enabling constant folding for these potentially enables replacing an enabled + // array get with one that was disabled. See + // https://github.com/noir-lang/noir/pull/4716#issuecomment-2047846328. + ArrayGet { .. } | ArraySet { .. } => false, + Call { func, .. } => match dfg[*func] { Value::Intrinsic(intrinsic) => !intrinsic.has_side_effects(), _ => false, diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 6cac8c91bc3..5a7134f3486 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -607,4 +607,55 @@ mod test { assert_eq!(main.dfg[instructions[4]], Instruction::Constrain(v1, v_true, None)); assert_eq!(main.dfg[instructions[5]], Instruction::Constrain(v2, v_false, None)); } + + // Regression for #4600 + #[test] + fn array_get_regression() { + // fn main f0 { + // b0(v0: u1, v1: u64): + // enable_side_effects_if v0 + // v2 = array_get [Field 0, Field 1], index v1 + // v3 = not v0 + // enable_side_effects_if v3 + // v4 = array_get [Field 0, Field 1], index v1 + // } + // + // We want to make sure after constant folding both array_gets remain since they are + // under different enable_side_effects_if contexts and thus one may be disabled while + // the other is not. If one is removed, it is possible e.g. v4 is replaced with v2 which + // is disabled (only gets from index 0) and thus returns the wrong result. + let main_id = Id::test_new(0); + + // Compiling main + let mut builder = FunctionBuilder::new("main".into(), main_id); + let v0 = builder.add_parameter(Type::bool()); + let v1 = builder.add_parameter(Type::unsigned(64)); + + builder.insert_enable_side_effects_if(v0); + + let zero = builder.field_constant(0u128); + let one = builder.field_constant(1u128); + + let typ = Type::Array(Rc::new(vec![Type::field()]), 2); + let array = builder.array_constant(vec![zero, one].into(), typ); + + let _v2 = builder.insert_array_get(array, v1, Type::field()); + let v3 = builder.insert_not(v0); + + builder.insert_enable_side_effects_if(v3); + let _v4 = builder.insert_array_get(array, v1, Type::field()); + + // Expected output is unchanged + let ssa = builder.finish(); + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + let starting_instruction_count = instructions.len(); + assert_eq!(starting_instruction_count, 5); + + let ssa = ssa.fold_constants(); + let main = ssa.main(); + let instructions = main.dfg[main.entry_block()].instructions(); + let ending_instruction_count = instructions.len(); + assert_eq!(starting_instruction_count, ending_instruction_count); + } } From 305bcdcbd01cb84dbaac900f14cb6cf867f83bda Mon Sep 17 00:00:00 2001 From: Aztec Bot <49558828+AztecBot@users.noreply.github.com> Date: Fri, 12 Apr 2024 04:46:40 -0400 Subject: [PATCH 169/416] feat: Sync from aztec-packages (#4787) Automated pull of Noir development from [aztec-packages](https://github.com/AztecProtocol/aztec-packages). BEGIN_COMMIT_OVERRIDE fix: avoid huge unrolling in hash_args (https://github.com/AztecProtocol/aztec-packages/pull/5703) feat: Sync from noir (https://github.com/AztecProtocol/aztec-packages/pull/5697) feat: Variable length returns (https://github.com/AztecProtocol/aztec-packages/pull/5633) feat(simulator): Fetch return values at circuit execution (https://github.com/AztecProtocol/aztec-packages/pull/5642) feat: Brillig heterogeneous memory cells (https://github.com/AztecProtocol/aztec-packages/pull/5608) chore!: remove fixed-length keccak256 (https://github.com/AztecProtocol/aztec-packages/pull/5617) END_COMMIT_OVERRIDE --------- Co-authored-by: sirasistant Co-authored-by: ludamad Co-authored-by: vezenovm Co-authored-by: Alvaro Rodriguez --- .aztec-sync-commit | 2 +- acvm-repo/acir/codegen/acir.cpp | 58 +--- .../opcodes/black_box_function_call.rs | 11 +- acvm-repo/acvm/src/pwg/blackbox/mod.rs | 10 +- acvm-repo/acvm/src/pwg/brillig.rs | 6 +- acvm-repo/acvm_js/src/execute.rs | 56 +++- acvm-repo/acvm_js/src/js_witness_map.rs | 38 ++- acvm-repo/acvm_js/src/lib.rs | 3 +- acvm-repo/acvm_js/src/public_witness.rs | 9 +- acvm-repo/brillig_vm/src/arithmetic.rs | 68 +++-- acvm-repo/brillig_vm/src/black_box.rs | 14 +- acvm-repo/brillig_vm/src/lib.rs | 71 +++-- acvm-repo/brillig_vm/src/memory.rs | 234 ++++++++++++--- aztec_macros/src/transforms/functions.rs | 277 ++++++++---------- aztec_macros/src/utils/ast_utils.rs | 29 +- aztec_macros/src/utils/errors.rs | 6 + .../brillig/brillig_gen/brillig_directive.rs | 152 ++++++---- .../brillig/brillig_gen/brillig_slice_ops.rs | 8 +- .../src/brillig/brillig_ir/entry_point.rs | 4 +- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 4 +- .../ssa/acir_gen/acir_ir/generated_acir.rs | 6 +- .../noirc_evaluator/src/ssa/opt/unrolling.rs | 5 +- tooling/bb_abstraction_leaks/build.rs | 2 +- tooling/debugger/src/context.rs | 6 +- tooling/debugger/src/repl.rs | 2 +- .../noir_js_backend_barretenberg/package.json | 2 +- yarn.lock | 10 +- 27 files changed, 647 insertions(+), 446 deletions(-) diff --git a/.aztec-sync-commit b/.aztec-sync-commit index ec4c854ef2b..9ebd1dcccac 100644 --- a/.aztec-sync-commit +++ b/.aztec-sync-commit @@ -1 +1 @@ -ff28080bcfb946177010960722925973ee19646b +10d9ad99200a5897417ff5669763ead4e38d87fa diff --git a/acvm-repo/acir/codegen/acir.cpp b/acvm-repo/acir/codegen/acir.cpp index e4203b579b0..7cd9fbefba0 100644 --- a/acvm-repo/acir/codegen/acir.cpp +++ b/acvm-repo/acir/codegen/acir.cpp @@ -159,6 +159,7 @@ namespace Program { struct Keccak256 { std::vector inputs; + Program::FunctionInput var_message_size; std::vector outputs; friend bool operator==(const Keccak256&, const Keccak256&); @@ -166,16 +167,6 @@ namespace Program { static Keccak256 bincodeDeserialize(std::vector); }; - struct Keccak256VariableLength { - std::vector inputs; - Program::FunctionInput var_message_size; - std::vector outputs; - - friend bool operator==(const Keccak256VariableLength&, const Keccak256VariableLength&); - std::vector bincodeSerialize() const; - static Keccak256VariableLength bincodeDeserialize(std::vector); - }; - struct Keccakf1600 { std::vector inputs; std::vector outputs; @@ -275,7 +266,7 @@ namespace Program { static Sha256Compression bincodeDeserialize(std::vector); }; - std::variant value; + std::variant value; friend bool operator==(const BlackBoxFuncCall&, const BlackBoxFuncCall&); std::vector bincodeSerialize() const; @@ -2582,6 +2573,7 @@ namespace Program { inline bool operator==(const BlackBoxFuncCall::Keccak256 &lhs, const BlackBoxFuncCall::Keccak256 &rhs) { if (!(lhs.inputs == rhs.inputs)) { return false; } + if (!(lhs.var_message_size == rhs.var_message_size)) { return false; } if (!(lhs.outputs == rhs.outputs)) { return false; } return true; } @@ -2607,6 +2599,7 @@ template <> template void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccak256 &obj, Serializer &serializer) { serde::Serializable::serialize(obj.inputs, serializer); + serde::Serializable::serialize(obj.var_message_size, serializer); serde::Serializable::serialize(obj.outputs, serializer); } @@ -2615,49 +2608,6 @@ template Program::BlackBoxFuncCall::Keccak256 serde::Deserializable::deserialize(Deserializer &deserializer) { Program::BlackBoxFuncCall::Keccak256 obj; obj.inputs = serde::Deserializable::deserialize(deserializer); - obj.outputs = serde::Deserializable::deserialize(deserializer); - return obj; -} - -namespace Program { - - inline bool operator==(const BlackBoxFuncCall::Keccak256VariableLength &lhs, const BlackBoxFuncCall::Keccak256VariableLength &rhs) { - if (!(lhs.inputs == rhs.inputs)) { return false; } - if (!(lhs.var_message_size == rhs.var_message_size)) { return false; } - if (!(lhs.outputs == rhs.outputs)) { return false; } - return true; - } - - inline std::vector BlackBoxFuncCall::Keccak256VariableLength::bincodeSerialize() const { - auto serializer = serde::BincodeSerializer(); - serde::Serializable::serialize(*this, serializer); - return std::move(serializer).bytes(); - } - - inline BlackBoxFuncCall::Keccak256VariableLength BlackBoxFuncCall::Keccak256VariableLength::bincodeDeserialize(std::vector input) { - auto deserializer = serde::BincodeDeserializer(input); - auto value = serde::Deserializable::deserialize(deserializer); - if (deserializer.get_buffer_offset() < input.size()) { - throw serde::deserialization_error("Some input bytes were not read"); - } - return value; - } - -} // end of namespace Program - -template <> -template -void serde::Serializable::serialize(const Program::BlackBoxFuncCall::Keccak256VariableLength &obj, Serializer &serializer) { - serde::Serializable::serialize(obj.inputs, serializer); - serde::Serializable::serialize(obj.var_message_size, serializer); - serde::Serializable::serialize(obj.outputs, serializer); -} - -template <> -template -Program::BlackBoxFuncCall::Keccak256VariableLength serde::Deserializable::deserialize(Deserializer &deserializer) { - Program::BlackBoxFuncCall::Keccak256VariableLength obj; - obj.inputs = serde::Deserializable::deserialize(deserializer); obj.var_message_size = serde::Deserializable::deserialize(deserializer); obj.outputs = serde::Deserializable::deserialize(deserializer); return obj; diff --git a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs index c955e435b37..d9089578739 100644 --- a/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs +++ b/acvm-repo/acir/src/circuit/opcodes/black_box_function_call.rs @@ -81,10 +81,6 @@ pub enum BlackBoxFuncCall { outputs: (Witness, Witness), }, Keccak256 { - inputs: Vec, - outputs: Vec, - }, - Keccak256VariableLength { inputs: Vec, /// This is the number of bytes to take /// from the input. Note: if `var_message_size` @@ -183,7 +179,6 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::FixedBaseScalarMul { .. } => BlackBoxFunc::FixedBaseScalarMul, BlackBoxFuncCall::EmbeddedCurveAdd { .. } => BlackBoxFunc::EmbeddedCurveAdd, BlackBoxFuncCall::Keccak256 { .. } => BlackBoxFunc::Keccak256, - BlackBoxFuncCall::Keccak256VariableLength { .. } => BlackBoxFunc::Keccak256, BlackBoxFuncCall::Keccakf1600 { .. } => BlackBoxFunc::Keccakf1600, BlackBoxFuncCall::RecursiveAggregation { .. } => BlackBoxFunc::RecursiveAggregation, BlackBoxFuncCall::BigIntAdd { .. } => BlackBoxFunc::BigIntAdd, @@ -206,7 +201,6 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { inputs, .. } | BlackBoxFuncCall::Blake2s { inputs, .. } | BlackBoxFuncCall::Blake3 { inputs, .. } - | BlackBoxFuncCall::Keccak256 { inputs, .. } | BlackBoxFuncCall::Keccakf1600 { inputs, .. } | BlackBoxFuncCall::PedersenCommitment { inputs, .. } | BlackBoxFuncCall::PedersenHash { inputs, .. } @@ -280,7 +274,7 @@ impl BlackBoxFuncCall { inputs.extend(hashed_message.iter().copied()); inputs } - BlackBoxFuncCall::Keccak256VariableLength { inputs, var_message_size, .. } => { + BlackBoxFuncCall::Keccak256 { inputs, var_message_size, .. } => { let mut inputs = inputs.clone(); inputs.push(*var_message_size); inputs @@ -306,9 +300,8 @@ impl BlackBoxFuncCall { BlackBoxFuncCall::SHA256 { outputs, .. } | BlackBoxFuncCall::Blake2s { outputs, .. } | BlackBoxFuncCall::Blake3 { outputs, .. } - | BlackBoxFuncCall::Keccak256 { outputs, .. } | BlackBoxFuncCall::Keccakf1600 { outputs, .. } - | BlackBoxFuncCall::Keccak256VariableLength { outputs, .. } + | BlackBoxFuncCall::Keccak256 { outputs, .. } | BlackBoxFuncCall::Poseidon2Permutation { outputs, .. } | BlackBoxFuncCall::Sha256Compression { outputs, .. } => outputs.to_vec(), BlackBoxFuncCall::AND { output, .. } diff --git a/acvm-repo/acvm/src/pwg/blackbox/mod.rs b/acvm-repo/acvm/src/pwg/blackbox/mod.rs index 6ee926043cd..e7ed402a8eb 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/mod.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/mod.rs @@ -95,15 +95,7 @@ pub(crate) fn solve( blake3, bb_func.get_black_box_func(), ), - BlackBoxFuncCall::Keccak256 { inputs, outputs } => solve_generic_256_hash_opcode( - initial_witness, - inputs, - None, - outputs, - keccak256, - bb_func.get_black_box_func(), - ), - BlackBoxFuncCall::Keccak256VariableLength { inputs, var_message_size, outputs } => { + BlackBoxFuncCall::Keccak256 { inputs, var_message_size, outputs } => { solve_generic_256_hash_opcode( initial_witness, inputs, diff --git a/acvm-repo/acvm/src/pwg/brillig.rs b/acvm-repo/acvm/src/pwg/brillig.rs index bcf736cd926..81e752d5656 100644 --- a/acvm-repo/acvm/src/pwg/brillig.rs +++ b/acvm-repo/acvm/src/pwg/brillig.rs @@ -206,13 +206,13 @@ impl<'b, B: BlackBoxFunctionSolver> BrilligSolver<'b, B> { for output in brillig.outputs.iter() { match output { BrilligOutputs::Simple(witness) => { - insert_value(witness, memory[current_ret_data_idx].value, witness_map)?; + insert_value(witness, memory[current_ret_data_idx].to_field(), witness_map)?; current_ret_data_idx += 1; } BrilligOutputs::Array(witness_arr) => { for witness in witness_arr.iter() { - let value = memory[current_ret_data_idx]; - insert_value(witness, value.value, witness_map)?; + let value = &memory[current_ret_data_idx]; + insert_value(witness, value.to_field(), witness_map)?; current_ret_data_idx += 1; } } diff --git a/acvm-repo/acvm_js/src/execute.rs b/acvm-repo/acvm_js/src/execute.rs index 0e58ccf039c..c97b8ea1a66 100644 --- a/acvm-repo/acvm_js/src/execute.rs +++ b/acvm-repo/acvm_js/src/execute.rs @@ -13,7 +13,8 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::{ foreign_call::{resolve_brillig, ForeignCallHandler}, - JsExecutionError, JsWitnessMap, JsWitnessStack, + public_witness::extract_indices, + JsExecutionError, JsSolvedAndReturnWitness, JsWitnessMap, JsWitnessStack, }; #[wasm_bindgen] @@ -58,6 +59,44 @@ pub async fn execute_circuit( Ok(witness_map.into()) } +/// Executes an ACIR circuit to generate the solved witness from the initial witness. +/// This method also extracts the public return values from the solved witness into its own return witness. +/// +/// @param {&WasmBlackBoxFunctionSolver} solver - A black box solver. +/// @param {Uint8Array} circuit - A serialized representation of an ACIR circuit +/// @param {WitnessMap} initial_witness - The initial witness map defining all of the inputs to `circuit`.. +/// @param {ForeignCallHandler} foreign_call_handler - A callback to process any foreign calls from the circuit. +/// @returns {SolvedAndReturnWitness} The solved witness calculated by executing the circuit on the provided inputs, as well as the return witness indices as specified by the circuit. +#[wasm_bindgen(js_name = executeCircuitWithReturnWitness, skip_jsdoc)] +pub async fn execute_circuit_with_return_witness( + solver: &WasmBlackBoxFunctionSolver, + program: Vec, + initial_witness: JsWitnessMap, + foreign_call_handler: ForeignCallHandler, +) -> Result { + console_error_panic_hook::set_once(); + + let program: Program = Program::deserialize_program(&program) + .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; + + let mut witness_stack = execute_program_with_native_program_and_return( + solver, + &program, + initial_witness, + &foreign_call_handler, + ) + .await?; + let solved_witness = + witness_stack.pop().expect("Should have at least one witness on the stack").witness; + + let main_circuit = &program.functions[0]; + let return_witness = + extract_indices(&solved_witness, main_circuit.return_values.0.iter().copied().collect()) + .map_err(|err| JsExecutionError::new(err, None))?; + + Ok((solved_witness, return_witness).into()) +} + /// Executes an ACIR circuit to generate the solved witness from the initial witness. /// /// @param {&WasmBlackBoxFunctionSolver} solver - A black box solver. @@ -127,6 +166,21 @@ async fn execute_program_with_native_type_return( let program: Program = Program::deserialize_program(&program) .map_err(|_| JsExecutionError::new("Failed to deserialize circuit. This is likely due to differing serialization formats between ACVM_JS and your compiler".to_string(), None))?; + execute_program_with_native_program_and_return( + solver, + &program, + initial_witness, + foreign_call_executor, + ) + .await +} + +async fn execute_program_with_native_program_and_return( + solver: &WasmBlackBoxFunctionSolver, + program: &Program, + initial_witness: JsWitnessMap, + foreign_call_executor: &ForeignCallHandler, +) -> Result { let executor = ProgramExecutor::new(&program.functions, &solver.0, foreign_call_executor); let witness_stack = executor.execute(initial_witness.into()).await?; diff --git a/acvm-repo/acvm_js/src/js_witness_map.rs b/acvm-repo/acvm_js/src/js_witness_map.rs index 481b8caaa2d..c4482c4a234 100644 --- a/acvm-repo/acvm_js/src/js_witness_map.rs +++ b/acvm-repo/acvm_js/src/js_witness_map.rs @@ -2,13 +2,23 @@ use acvm::{ acir::native_types::{Witness, WitnessMap}, FieldElement, }; -use js_sys::{JsString, Map}; +use js_sys::{JsString, Map, Object}; use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; #[wasm_bindgen(typescript_custom_section)] const WITNESS_MAP: &'static str = r#" // Map from witness index to hex string value of witness. export type WitnessMap = Map; + +/** + * An execution result containing two witnesses. + * 1. The full solved witness of the execution. + * 2. The return witness which contains the given public return values within the full witness. + */ +export type SolvedAndReturnWitness = { + solvedWitness: WitnessMap; + returnWitness: WitnessMap; +} "#; // WitnessMap @@ -21,6 +31,12 @@ extern "C" { #[wasm_bindgen(constructor, js_class = "Map")] pub fn new() -> JsWitnessMap; + #[wasm_bindgen(extends = Object, js_name = "SolvedAndReturnWitness", typescript_type = "SolvedAndReturnWitness")] + #[derive(Clone, Debug, PartialEq, Eq)] + pub type JsSolvedAndReturnWitness; + + #[wasm_bindgen(constructor, js_class = "Object")] + pub fn new() -> JsSolvedAndReturnWitness; } impl Default for JsWitnessMap { @@ -29,6 +45,12 @@ impl Default for JsWitnessMap { } } +impl Default for JsSolvedAndReturnWitness { + fn default() -> Self { + Self::new() + } +} + impl From for JsWitnessMap { fn from(witness_map: WitnessMap) -> Self { let js_map = JsWitnessMap::new(); @@ -54,6 +76,20 @@ impl From for WitnessMap { } } +impl From<(WitnessMap, WitnessMap)> for JsSolvedAndReturnWitness { + fn from(witness_maps: (WitnessMap, WitnessMap)) -> Self { + let js_solved_witness = JsWitnessMap::from(witness_maps.0); + let js_return_witness = JsWitnessMap::from(witness_maps.1); + + let entry_map = Map::new(); + entry_map.set(&JsValue::from_str("solvedWitness"), &js_solved_witness); + entry_map.set(&JsValue::from_str("returnWitness"), &js_return_witness); + + let solved_and_return_witness = Object::from_entries(&entry_map).unwrap(); + JsSolvedAndReturnWitness { obj: solved_and_return_witness } + } +} + pub(crate) fn js_value_to_field_element(js_value: JsValue) -> Result { let hex_str = js_value.as_string().ok_or("failed to parse field element from non-string")?; diff --git a/acvm-repo/acvm_js/src/lib.rs b/acvm-repo/acvm_js/src/lib.rs index d7ecc0ae192..66a4388b132 100644 --- a/acvm-repo/acvm_js/src/lib.rs +++ b/acvm-repo/acvm_js/src/lib.rs @@ -22,9 +22,10 @@ pub use compression::{ }; pub use execute::{ create_black_box_solver, execute_circuit, execute_circuit_with_black_box_solver, - execute_program, execute_program_with_black_box_solver, + execute_circuit_with_return_witness, execute_program, execute_program_with_black_box_solver, }; pub use js_execution_error::JsExecutionError; +pub use js_witness_map::JsSolvedAndReturnWitness; pub use js_witness_map::JsWitnessMap; pub use js_witness_stack::JsWitnessStack; pub use logging::init_log_level; diff --git a/acvm-repo/acvm_js/src/public_witness.rs b/acvm-repo/acvm_js/src/public_witness.rs index a0d5b5f8be2..4ba054732d4 100644 --- a/acvm-repo/acvm_js/src/public_witness.rs +++ b/acvm-repo/acvm_js/src/public_witness.rs @@ -7,7 +7,10 @@ use wasm_bindgen::prelude::wasm_bindgen; use crate::JsWitnessMap; -fn extract_indices(witness_map: &WitnessMap, indices: Vec) -> Result { +pub(crate) fn extract_indices( + witness_map: &WitnessMap, + indices: Vec, +) -> Result { let mut extracted_witness_map = WitnessMap::new(); for witness in indices { let witness_value = witness_map.get(&witness).ok_or(format!( @@ -44,7 +47,7 @@ pub fn get_return_witness( let witness_map = WitnessMap::from(witness_map); let return_witness = - extract_indices(&witness_map, circuit.return_values.0.clone().into_iter().collect())?; + extract_indices(&witness_map, circuit.return_values.0.iter().copied().collect())?; Ok(JsWitnessMap::from(return_witness)) } @@ -71,7 +74,7 @@ pub fn get_public_parameters_witness( let witness_map = WitnessMap::from(solved_witness); let public_params_witness = - extract_indices(&witness_map, circuit.public_parameters.0.clone().into_iter().collect())?; + extract_indices(&witness_map, circuit.public_parameters.0.iter().copied().collect())?; Ok(JsWitnessMap::from(public_params_witness)) } diff --git a/acvm-repo/brillig_vm/src/arithmetic.rs b/acvm-repo/brillig_vm/src/arithmetic.rs index 3d77982ffb1..2107d10c093 100644 --- a/acvm-repo/brillig_vm/src/arithmetic.rs +++ b/acvm-repo/brillig_vm/src/arithmetic.rs @@ -1,9 +1,10 @@ use acir::brillig::{BinaryFieldOp, BinaryIntOp}; use acir::FieldElement; use num_bigint::BigUint; -use num_traits::{One, ToPrimitive, Zero}; +use num_traits::ToPrimitive; +use num_traits::{One, Zero}; -use crate::memory::MemoryValue; +use crate::memory::{MemoryTypeError, MemoryValue}; #[derive(Debug, thiserror::Error)] pub(crate) enum BrilligArithmeticError { @@ -11,6 +12,8 @@ pub(crate) enum BrilligArithmeticError { MismatchedLhsBitSize { lhs_bit_size: u32, op_bit_size: u32 }, #[error("Bit size for rhs {rhs_bit_size} does not match op bit size {op_bit_size}")] MismatchedRhsBitSize { rhs_bit_size: u32, op_bit_size: u32 }, + #[error("Integer operation BinaryIntOp::{op:?} is not supported on FieldElement")] + IntegerOperationOnField { op: BinaryIntOp }, #[error("Shift with bit size {op_bit_size} is invalid")] InvalidShift { op_bit_size: u32 }, } @@ -21,21 +24,19 @@ pub(crate) fn evaluate_binary_field_op( lhs: MemoryValue, rhs: MemoryValue, ) -> Result { - if lhs.bit_size != FieldElement::max_num_bits() { + let MemoryValue::Field(a) = lhs else { return Err(BrilligArithmeticError::MismatchedLhsBitSize { - lhs_bit_size: lhs.bit_size, + lhs_bit_size: lhs.bit_size(), op_bit_size: FieldElement::max_num_bits(), }); - } - if rhs.bit_size != FieldElement::max_num_bits() { - return Err(BrilligArithmeticError::MismatchedRhsBitSize { - rhs_bit_size: rhs.bit_size, + }; + let MemoryValue::Field(b) = rhs else { + return Err(BrilligArithmeticError::MismatchedLhsBitSize { + lhs_bit_size: rhs.bit_size(), op_bit_size: FieldElement::max_num_bits(), }); - } + }; - let a = lhs.value; - let b = rhs.value; Ok(match op { // Perform addition, subtraction, multiplication, and division based on the BinaryOp variant. BinaryFieldOp::Add => (a + b).into(), @@ -62,21 +63,26 @@ pub(crate) fn evaluate_binary_int_op( rhs: MemoryValue, bit_size: u32, ) -> Result { - if lhs.bit_size != bit_size { - return Err(BrilligArithmeticError::MismatchedLhsBitSize { - lhs_bit_size: lhs.bit_size, - op_bit_size: bit_size, - }); - } - if rhs.bit_size != bit_size { - return Err(BrilligArithmeticError::MismatchedRhsBitSize { - rhs_bit_size: rhs.bit_size, - op_bit_size: bit_size, - }); - } + let lhs = lhs.expect_integer_with_bit_size(bit_size).map_err(|err| match err { + MemoryTypeError::MismatchedBitSize { value_bit_size, expected_bit_size } => { + BrilligArithmeticError::MismatchedLhsBitSize { + lhs_bit_size: value_bit_size, + op_bit_size: expected_bit_size, + } + } + })?; + let rhs = rhs.expect_integer_with_bit_size(bit_size).map_err(|err| match err { + MemoryTypeError::MismatchedBitSize { value_bit_size, expected_bit_size } => { + BrilligArithmeticError::MismatchedRhsBitSize { + rhs_bit_size: value_bit_size, + op_bit_size: expected_bit_size, + } + } + })?; - let lhs = BigUint::from_bytes_be(&lhs.value.to_be_bytes()); - let rhs = BigUint::from_bytes_be(&rhs.value.to_be_bytes()); + if bit_size == FieldElement::max_num_bits() { + return Err(BrilligArithmeticError::IntegerOperationOnField { op: *op }); + } let bit_modulo = &(BigUint::one() << bit_size); let result = match op { @@ -136,13 +142,11 @@ pub(crate) fn evaluate_binary_int_op( } }; - let result_as_field = FieldElement::from_be_bytes_reduce(&result.to_bytes_be()); - Ok(match op { BinaryIntOp::Equals | BinaryIntOp::LessThan | BinaryIntOp::LessThanEquals => { - MemoryValue::new(result_as_field, 1) + MemoryValue::new_integer(result, 1) } - _ => MemoryValue::new(result_as_field, bit_size), + _ => MemoryValue::new_integer(result, bit_size), }) } @@ -159,13 +163,13 @@ mod tests { fn evaluate_u128(op: &BinaryIntOp, a: u128, b: u128, bit_size: u32) -> u128 { let result_value = evaluate_binary_int_op( op, - MemoryValue::new(a.into(), bit_size), - MemoryValue::new(b.into(), bit_size), + MemoryValue::new_integer(a.into(), bit_size), + MemoryValue::new_integer(b.into(), bit_size), bit_size, ) .unwrap(); // Convert back to u128 - result_value.value.to_u128() + result_value.to_field().to_u128() } fn to_negative(a: u128, bit_size: u32) -> u128 { diff --git a/acvm-repo/brillig_vm/src/black_box.rs b/acvm-repo/brillig_vm/src/black_box.rs index bd33b5ee8fc..73981fb0625 100644 --- a/acvm-repo/brillig_vm/src/black_box.rs +++ b/acvm-repo/brillig_vm/src/black_box.rs @@ -20,7 +20,7 @@ fn read_heap_array<'a>(memory: &'a Memory, array: &HeapArray) -> &'a [MemoryValu /// Extracts the last byte of every value fn to_u8_vec(inputs: &[MemoryValue]) -> Vec { let mut result = Vec::with_capacity(inputs.len()); - for &input in inputs { + for input in inputs { result.push(input.try_into().unwrap()); } result @@ -63,7 +63,7 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::Keccakf1600 { message, output } => { let state_vec: Vec = read_heap_vector(memory, message) .iter() - .map(|&memory_value| memory_value.try_into().unwrap()) + .map(|memory_value| memory_value.try_into().unwrap()) .collect(); let state: [u64; 25] = state_vec.try_into().unwrap(); @@ -151,7 +151,7 @@ pub(crate) fn evaluate_black_box( } BlackBoxOp::PedersenCommitment { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.try_into().unwrap()).collect(); let domain_separator: u32 = memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( @@ -165,7 +165,7 @@ pub(crate) fn evaluate_black_box( } BlackBoxOp::PedersenHash { inputs, domain_separator, output } => { let inputs: Vec = - read_heap_vector(memory, inputs).iter().map(|&x| x.try_into().unwrap()).collect(); + read_heap_vector(memory, inputs).iter().map(|x| x.try_into().unwrap()).collect(); let domain_separator: u32 = memory.read(*domain_separator).try_into().map_err(|_| { BlackBoxResolutionError::Failed( @@ -185,7 +185,7 @@ pub(crate) fn evaluate_black_box( BlackBoxOp::BigIntToLeBytes { .. } => todo!(), BlackBoxOp::Poseidon2Permutation { message, output, len } => { let input = read_heap_vector(memory, message); - let input: Vec = input.iter().map(|&x| x.try_into().unwrap()).collect(); + let input: Vec = input.iter().map(|x| x.try_into().unwrap()).collect(); let len = memory.read(*len).try_into().unwrap(); let result = solver.poseidon2_permutation(&input, len)?; let mut values = Vec::new(); @@ -204,7 +204,7 @@ pub(crate) fn evaluate_black_box( format!("Expected 16 inputs but encountered {}", &inputs.len()), )); } - for (i, &input) in inputs.iter().enumerate() { + for (i, input) in inputs.iter().enumerate() { message[i] = input.try_into().unwrap(); } let mut state = [0; 8]; @@ -215,7 +215,7 @@ pub(crate) fn evaluate_black_box( format!("Expected 8 values but encountered {}", &values.len()), )); } - for (i, &value) in values.iter().enumerate() { + for (i, value) in values.iter().enumerate() { state[i] = value.try_into().unwrap(); } diff --git a/acvm-repo/brillig_vm/src/lib.rs b/acvm-repo/brillig_vm/src/lib.rs index 65654e24720..26d5da67576 100644 --- a/acvm-repo/brillig_vm/src/lib.rs +++ b/acvm-repo/brillig_vm/src/lib.rs @@ -289,8 +289,8 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { // Convert our source_pointer to an address let source = self.memory.read_ref(*source_pointer); // Use our usize source index to lookup the value in memory - let value = &self.memory.read(source); - self.memory.write(*destination_address, *value); + let value = self.memory.read(source); + self.memory.write(*destination_address, value); self.increment_program_counter() } Opcode::Store { destination_pointer, source: source_address } => { @@ -307,7 +307,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { } Opcode::Const { destination, value, bit_size } => { // Consts are not checked in runtime to fit in the bit size, since they can safely be checked statically. - self.memory.write(*destination, MemoryValue::new(*value, *bit_size)); + self.memory.write(*destination, MemoryValue::new_from_field(*value, *bit_size)); self.increment_program_counter() } Opcode::BlackBox(black_box_op) => { @@ -348,7 +348,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { ) -> ForeignCallParam { match (input, value_type) { (ValueOrArray::MemoryAddress(value_index), HeapValueType::Simple(_)) => { - self.memory.read(value_index).value.into() + self.memory.read(value_index).to_field().into() } ( ValueOrArray::HeapArray(HeapArray { pointer: pointer_index, size }), @@ -357,7 +357,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let start = self.memory.read_ref(pointer_index); self.read_slice_of_values_from_memory(start, size, value_types) .into_iter() - .map(|mem_value| mem_value.value) + .map(|mem_value| mem_value.to_field()) .collect::>() .into() } @@ -369,7 +369,7 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { let size = self.memory.read(size_index).to_usize(); self.read_slice_of_values_from_memory(start, size, value_types) .into_iter() - .map(|mem_value| mem_value.value) + .map(|mem_value| mem_value.to_field()) .collect::>() .into() } @@ -584,12 +584,9 @@ impl<'a, B: BlackBoxFunctionSolver> VM<'a, B> { /// Casts a value to a different bit size. fn cast(&self, bit_size: u32, source_value: MemoryValue) -> MemoryValue { - let lhs_big = BigUint::from_bytes_be(&source_value.value.to_be_bytes()); + let lhs_big = source_value.to_integer(); let mask = BigUint::from(2_u32).pow(bit_size) - 1_u32; - MemoryValue { - value: FieldElement::from_be_bytes_reduce(&(lhs_big & mask).to_bytes_be()), - bit_size, - } + MemoryValue::new_from_integer(lhs_big & mask, bit_size) } } @@ -627,7 +624,7 @@ mod tests { let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(0)); - assert_eq!(output_value.value, FieldElement::from(27u128)); + assert_eq!(output_value.to_field(), FieldElement::from(27u128)); } #[test] @@ -666,7 +663,7 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let output_cmp_value = vm.memory.read(destination); - assert_eq!(output_cmp_value.value, true.into()); + assert_eq!(output_cmp_value.to_field(), true.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -725,7 +722,7 @@ mod tests { assert_eq!(status, VMStatus::InProgress); let output_cmp_value = vm.memory.read(MemoryAddress::from(2)); - assert_eq!(output_cmp_value.value, false.into()); + assert_eq!(output_cmp_value.to_field(), false.into()); let status = vm.process_opcode(); assert_eq!(status, VMStatus::InProgress); @@ -742,7 +739,7 @@ mod tests { // The address at index `2` should have not changed as we jumped over the add opcode let VM { memory, .. } = vm; let output_value = memory.read(MemoryAddress::from(2)); - assert_eq!(output_value.value, false.into()); + assert_eq!(output_value.to_field(), false.into()); } #[test] @@ -776,7 +773,7 @@ mod tests { let VM { memory, .. } = vm; let casted_value = memory.read(MemoryAddress::from(1)); - assert_eq!(casted_value.value, (2_u128.pow(8) - 1).into()); + assert_eq!(casted_value.to_field(), (2_u128.pow(8) - 1).into()); } #[test] @@ -804,10 +801,10 @@ mod tests { let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(2)); - assert_eq!(destination_value.value, (1u128).into()); + assert_eq!(destination_value.to_field(), (1u128).into()); let source_value = memory.read(MemoryAddress::from(0)); - assert_eq!(source_value.value, (1u128).into()); + assert_eq!(source_value.to_field(), (1u128).into()); } #[test] @@ -869,10 +866,10 @@ mod tests { let VM { memory, .. } = vm; let destination_value = memory.read(MemoryAddress::from(4)); - assert_eq!(destination_value.value, (3_u128).into()); + assert_eq!(destination_value.to_field(), (3_u128).into()); let source_value = memory.read(MemoryAddress::from(5)); - assert_eq!(source_value.value, (2_u128).into()); + assert_eq!(source_value.to_field(), (2_u128).into()); } #[test] @@ -1120,7 +1117,7 @@ mod tests { let opcodes = [&start[..], &loop_body[..]].concat(); let vm = brillig_execute_and_get_vm(memory, &opcodes); - vm.memory.read(r_sum).value + vm.memory.read(r_sum).to_field() } assert_eq!( @@ -1359,7 +1356,7 @@ mod tests { // Check result in memory let result_values = vm.memory.read_slice(MemoryAddress(2), 4).to_vec(); assert_eq!( - result_values.into_iter().map(|mem_value| mem_value.value).collect::>(), + result_values.into_iter().map(|mem_value| mem_value.to_field()).collect::>(), expected_result ); @@ -1459,7 +1456,7 @@ mod tests { .memory .read_slice(MemoryAddress(4 + input_string.len()), output_string.len()) .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.clone().to_field()) .collect(); assert_eq!(result_values, output_string); @@ -1532,13 +1529,21 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check initial memory still in place - let initial_values: Vec<_> = - vm.memory.read_slice(MemoryAddress(2), 4).iter().map(|mem_val| mem_val.value).collect(); + let initial_values: Vec<_> = vm + .memory + .read_slice(MemoryAddress(2), 4) + .iter() + .map(|mem_val| mem_val.clone().to_field()) + .collect(); assert_eq!(initial_values, initial_matrix); // Check result in memory - let result_values: Vec<_> = - vm.memory.read_slice(MemoryAddress(6), 4).iter().map(|mem_val| mem_val.value).collect(); + let result_values: Vec<_> = vm + .memory + .read_slice(MemoryAddress(6), 4) + .iter() + .map(|mem_val| mem_val.clone().to_field()) + .collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1622,8 +1627,12 @@ mod tests { assert_eq!(vm.status, VMStatus::Finished { return_data_offset: 0, return_data_size: 0 }); // Check result in memory - let result_values: Vec<_> = - vm.memory.read_slice(MemoryAddress(0), 4).iter().map(|mem_val| mem_val.value).collect(); + let result_values: Vec<_> = vm + .memory + .read_slice(MemoryAddress(0), 4) + .iter() + .map(|mem_val| mem_val.clone().to_field()) + .collect(); assert_eq!(result_values, expected_result); // Ensure the foreign call counter has been incremented @@ -1698,7 +1707,7 @@ mod tests { .chain(memory.iter().enumerate().map(|(index, mem_value)| Opcode::Cast { destination: MemoryAddress(index), source: MemoryAddress(index), - bit_size: mem_value.bit_size, + bit_size: mem_value.bit_size(), })) .chain(vec![ // input = 0 @@ -1721,7 +1730,7 @@ mod tests { .collect(); let mut vm = brillig_execute_and_get_vm( - memory.into_iter().map(|mem_value| mem_value.value).collect(), + memory.into_iter().map(|mem_value| mem_value.to_field()).collect(), &program, ); diff --git a/acvm-repo/brillig_vm/src/memory.rs b/acvm-repo/brillig_vm/src/memory.rs index d563e13be2e..feeb3706bde 100644 --- a/acvm-repo/brillig_vm/src/memory.rs +++ b/acvm-repo/brillig_vm/src/memory.rs @@ -1,11 +1,13 @@ use acir::{brillig::MemoryAddress, FieldElement}; +use num_bigint::BigUint; +use num_traits::{One, Zero}; pub const MEMORY_ADDRESSING_BIT_SIZE: u32 = 64; -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct MemoryValue { - pub value: FieldElement, - pub bit_size: u32, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum MemoryValue { + Field(FieldElement), + Integer(BigUint, u32), } #[derive(Debug, thiserror::Error)] @@ -15,53 +17,147 @@ pub enum MemoryTypeError { } impl MemoryValue { - pub fn new(value: FieldElement, bit_size: u32) -> Self { - MemoryValue { value, bit_size } + /// Builds a memory value from a field element. + pub fn new_from_field(value: FieldElement, bit_size: u32) -> Self { + if bit_size == FieldElement::max_num_bits() { + MemoryValue::new_field(value) + } else { + MemoryValue::new_integer(BigUint::from_bytes_be(&value.to_be_bytes()), bit_size) + } + } + + /// Builds a memory value from an integer + pub fn new_from_integer(value: BigUint, bit_size: u32) -> Self { + if bit_size == FieldElement::max_num_bits() { + MemoryValue::new_field(FieldElement::from_be_bytes_reduce(&value.to_bytes_be())) + } else { + MemoryValue::new_integer(value, bit_size) + } } + /// Builds a memory value from a field element, checking that the value is within the bit size. pub fn new_checked(value: FieldElement, bit_size: u32) -> Option { - if value.num_bits() > bit_size { + if bit_size < FieldElement::max_num_bits() && value.num_bits() > bit_size { return None; } - Some(MemoryValue::new(value, bit_size)) + Some(MemoryValue::new_from_field(value, bit_size)) } + /// Builds a field-typed memory value. pub fn new_field(value: FieldElement) -> Self { - MemoryValue { value, bit_size: FieldElement::max_num_bits() } + MemoryValue::Field(value) + } + + /// Builds an integer-typed memory value. + pub fn new_integer(value: BigUint, bit_size: u32) -> Self { + assert!( + bit_size != FieldElement::max_num_bits(), + "Tried to build a field memory value via new_integer" + ); + MemoryValue::Integer(value, bit_size) + } + + /// Extracts the field element from the memory value, if it is typed as field element. + pub fn extract_field(&self) -> Option<&FieldElement> { + match self { + MemoryValue::Field(value) => Some(value), + _ => None, + } + } + + /// Extracts the integer from the memory value, if it is typed as integer. + pub fn extract_integer(&self) -> Option<(&BigUint, u32)> { + match self { + MemoryValue::Integer(value, bit_size) => Some((value, *bit_size)), + _ => None, + } + } + + /// Converts the memory value to a field element, independent of its type. + pub fn to_field(&self) -> FieldElement { + match self { + MemoryValue::Field(value) => *value, + MemoryValue::Integer(value, _) => { + FieldElement::from_be_bytes_reduce(&value.to_bytes_be()) + } + } + } + + /// Converts the memory value to an integer, independent of its type. + pub fn to_integer(self) -> BigUint { + match self { + MemoryValue::Field(value) => BigUint::from_bytes_be(&value.to_be_bytes()), + MemoryValue::Integer(value, _) => value, + } + } + + pub fn bit_size(&self) -> u32 { + match self { + MemoryValue::Field(_) => FieldElement::max_num_bits(), + MemoryValue::Integer(_, bit_size) => *bit_size, + } } pub fn to_usize(&self) -> usize { - assert!(self.bit_size == MEMORY_ADDRESSING_BIT_SIZE, "value is not typed as brillig usize"); - self.value.to_u128() as usize + assert!( + self.bit_size() == MEMORY_ADDRESSING_BIT_SIZE, + "value is not typed as brillig usize" + ); + self.extract_integer().unwrap().0.try_into().unwrap() } - pub fn expect_bit_size(&self, expected_bit_size: u32) -> Result<(), MemoryTypeError> { - if self.bit_size != expected_bit_size { - return Err(MemoryTypeError::MismatchedBitSize { - value_bit_size: self.bit_size, + pub fn expect_field(&self) -> Result<&FieldElement, MemoryTypeError> { + match self { + MemoryValue::Integer(_, bit_size) => Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: *bit_size, + expected_bit_size: FieldElement::max_num_bits(), + }), + MemoryValue::Field(field) => Ok(field), + } + } + + pub fn expect_integer_with_bit_size( + &self, + expected_bit_size: u32, + ) -> Result<&BigUint, MemoryTypeError> { + match self { + MemoryValue::Integer(value, bit_size) => { + if *bit_size != expected_bit_size { + return Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: *bit_size, + expected_bit_size, + }); + } + Ok(value) + } + MemoryValue::Field(_) => Err(MemoryTypeError::MismatchedBitSize { + value_bit_size: FieldElement::max_num_bits(), expected_bit_size, - }); + }), } - Ok(()) } } impl std::fmt::Display for MemoryValue { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { - let typ = match self.bit_size { - 0 => "null".to_string(), - 1 => "bool".to_string(), - _ if self.bit_size == FieldElement::max_num_bits() => "field".to_string(), - _ => format!("u{}", self.bit_size), - }; - f.write_str(format!("{}: {}", self.value, typ).as_str()) + match self { + MemoryValue::Field(value) => write!(f, "{}: field", value), + MemoryValue::Integer(value, bit_size) => { + let typ = match bit_size { + 0 => "null".to_string(), + 1 => "bool".to_string(), + _ => format!("u{}", bit_size), + }; + write!(f, "{}: {}", value, typ) + } + } } } impl Default for MemoryValue { fn default() -> Self { - MemoryValue::new(FieldElement::zero(), 0) + MemoryValue::new_integer(BigUint::zero(), 0) } } @@ -73,31 +169,32 @@ impl From for MemoryValue { impl From for MemoryValue { fn from(value: usize) -> Self { - MemoryValue::new(value.into(), MEMORY_ADDRESSING_BIT_SIZE) + MemoryValue::new_integer(value.into(), MEMORY_ADDRESSING_BIT_SIZE) } } impl From for MemoryValue { fn from(value: u64) -> Self { - MemoryValue::new((value as u128).into(), 64) + MemoryValue::new_integer(value.into(), 64) } } impl From for MemoryValue { fn from(value: u32) -> Self { - MemoryValue::new((value as u128).into(), 32) + MemoryValue::new_integer(value.into(), 32) } } impl From for MemoryValue { fn from(value: u8) -> Self { - MemoryValue::new((value as u128).into(), 8) + MemoryValue::new_integer(value.into(), 8) } } impl From for MemoryValue { fn from(value: bool) -> Self { - MemoryValue::new(value.into(), 1) + let value = if value { BigUint::one() } else { BigUint::zero() }; + MemoryValue::new_integer(value, 1) } } @@ -105,8 +202,7 @@ impl TryFrom for FieldElement { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(FieldElement::max_num_bits())?; - Ok(memory_value.value) + memory_value.expect_field().copied() } } @@ -114,8 +210,7 @@ impl TryFrom for u64 { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(64)?; - Ok(memory_value.value.to_u128() as u64) + memory_value.expect_integer_with_bit_size(64).map(|value| value.try_into().unwrap()) } } @@ -123,8 +218,7 @@ impl TryFrom for u32 { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(32)?; - Ok(memory_value.value.to_u128() as u32) + memory_value.expect_integer_with_bit_size(32).map(|value| value.try_into().unwrap()) } } @@ -132,9 +226,7 @@ impl TryFrom for u8 { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(8)?; - - Ok(memory_value.value.to_u128() as u8) + memory_value.expect_integer_with_bit_size(8).map(|value| value.try_into().unwrap()) } } @@ -142,11 +234,65 @@ impl TryFrom for bool { type Error = MemoryTypeError; fn try_from(memory_value: MemoryValue) -> Result { - memory_value.expect_bit_size(1)?; + let as_integer = memory_value.expect_integer_with_bit_size(1)?; + + if as_integer.is_zero() { + Ok(false) + } else if as_integer.is_one() { + Ok(true) + } else { + unreachable!("value typed as bool is greater than one") + } + } +} + +impl TryFrom<&MemoryValue> for FieldElement { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_field().copied() + } +} + +impl TryFrom<&MemoryValue> for u64 { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_integer_with_bit_size(64).map(|value| { + value.try_into().expect("memory_value has been asserted to contain a 64 bit integer") + }) + } +} + +impl TryFrom<&MemoryValue> for u32 { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_integer_with_bit_size(32).map(|value| { + value.try_into().expect("memory_value has been asserted to contain a 32 bit integer") + }) + } +} + +impl TryFrom<&MemoryValue> for u8 { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + memory_value.expect_integer_with_bit_size(8).map(|value| { + value.try_into().expect("memory_value has been asserted to contain an 8 bit integer") + }) + } +} + +impl TryFrom<&MemoryValue> for bool { + type Error = MemoryTypeError; + + fn try_from(memory_value: &MemoryValue) -> Result { + let as_integer = memory_value.expect_integer_with_bit_size(1)?; - if memory_value.value == FieldElement::zero() { + if as_integer.is_zero() { Ok(false) - } else if memory_value.value == FieldElement::one() { + } else if as_integer.is_one() { Ok(true) } else { unreachable!("value typed as bool is greater than one") @@ -164,7 +310,7 @@ pub struct Memory { impl Memory { /// Gets the value at pointer pub fn read(&self, ptr: MemoryAddress) -> MemoryValue { - self.inner.get(ptr.to_usize()).copied().unwrap_or_default() + self.inner.get(ptr.to_usize()).cloned().unwrap_or_default() } pub fn read_ref(&self, ptr: MemoryAddress) -> MemoryAddress { @@ -191,7 +337,7 @@ impl Memory { /// Sets the values after pointer `ptr` to `values` pub fn write_slice(&mut self, ptr: MemoryAddress, values: &[MemoryValue]) { self.resize_to_fit(ptr.to_usize() + values.len()); - self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].copy_from_slice(values); + self.inner[ptr.to_usize()..(ptr.to_usize() + values.len())].clone_from_slice(values); } /// Returns the values of the memory diff --git a/aztec_macros/src/transforms/functions.rs b/aztec_macros/src/transforms/functions.rs index a3064ecdd01..534d24289b7 100644 --- a/aztec_macros/src/transforms/functions.rs +++ b/aztec_macros/src/transforms/functions.rs @@ -11,10 +11,9 @@ use crate::{ chained_dep, chained_path, utils::{ ast_utils::{ - assignment, call, cast, expression, ident, ident_path, index_array, - index_array_variable, make_eq, make_statement, make_type, member_access, method_call, - mutable_assignment, mutable_reference, path, return_type, variable, variable_ident, - variable_path, + assignment, assignment_with_type, call, cast, expression, ident, ident_path, + index_array, make_eq, make_statement, make_type, method_call, mutable_assignment, + mutable_reference, path, return_type, variable, variable_ident, variable_path, }, errors::AztecMacroError, }, @@ -74,12 +73,12 @@ pub fn transform_function( // Abstract return types such that they get added to the kernel's return_values if !is_avm { - if let Some(return_values) = abstract_return_values(func) { + if let Some(return_values_statements) = abstract_return_values(func)? { // In case we are pushing return values to the context, we remove the statement that originated it // This avoids running duplicate code, since blocks like if/else can be value returning statements func.def.body.statements.pop(); // Add the new return statement - func.def.body.statements.push(return_values); + func.def.body.statements.extend(return_values_statements); } } @@ -302,6 +301,51 @@ fn create_assert_initializer(ty: &str) -> Statement { ))) } +fn serialize_to_hasher( + identifier: &Ident, + typ: &UnresolvedTypeData, + hasher_name: &str, +) -> Option> { + let mut statements = Vec::new(); + + // Match the type to determine the padding to do + match typ { + // `{hasher_name}.extend_from_array({ident}.serialize())` + UnresolvedTypeData::Named(..) => { + statements.push(add_struct_to_hasher(identifier, hasher_name)); + } + UnresolvedTypeData::Array(_, arr_type) => { + statements.push(add_array_to_hasher(identifier, arr_type, hasher_name)); + } + // `{hasher_name}.push({ident})` + UnresolvedTypeData::FieldElement => { + statements.push(add_field_to_hasher(identifier, hasher_name)); + } + // Add the integer to the bounded vec, casted to a field + // `{hasher_name}.push({ident} as Field)` + UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { + statements.push(add_cast_to_hasher(identifier, hasher_name)); + } + UnresolvedTypeData::String(..) => { + let (var_bytes, id) = str_to_bytes(identifier); + statements.push(var_bytes); + statements.push(add_array_to_hasher( + &id, + &UnresolvedType { + typ: UnresolvedTypeData::Integer( + Signedness::Unsigned, + noirc_frontend::IntegerBitSize::ThirtyTwo, + ), + span: None, + }, + hasher_name, + )) + } + _ => return None, + }; + Some(statements) +} + /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be /// appended into the args hash object. /// @@ -328,7 +372,7 @@ fn create_assert_initializer(ty: &str) -> Statement { /// } /// ``` fn create_context(ty: &str, params: &[Param]) -> Result, AztecMacroError> { - let mut injected_expressions: Vec = vec![]; + let mut injected_statements: Vec = vec![]; let hasher_name = "args_hasher"; @@ -342,7 +386,7 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac ); // Completes: `let mut args_hasher = Hasher::new();` - injected_expressions.push(let_hasher); + injected_statements.push(let_hasher); // Iterate over each of the function parameters, adding to them to the hasher for Param { pattern, typ, span, .. } in params { @@ -350,44 +394,14 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac Pattern::Identifier(identifier) => { // Match the type to determine the padding to do let unresolved_type = &typ.typ; - let expression = match unresolved_type { - // `hasher.add_multiple({ident}.serialize())` - UnresolvedTypeData::Named(..) => add_struct_to_hasher(identifier, hasher_name), - UnresolvedTypeData::Array(_, arr_type) => { - add_array_to_hasher(identifier, arr_type, hasher_name) - } - // `hasher.add({ident})` - UnresolvedTypeData::FieldElement => { - add_field_to_hasher(identifier, hasher_name) - } - // Add the integer to the hasher, casted to a field - // `hasher.add({ident} as Field)` - UnresolvedTypeData::Integer(..) | UnresolvedTypeData::Bool => { - add_cast_to_hasher(identifier, hasher_name) - } - UnresolvedTypeData::String(..) => { - let (var_bytes, id) = str_to_bytes(identifier); - injected_expressions.push(var_bytes); - add_array_to_hasher( - &id, - &UnresolvedType { - typ: UnresolvedTypeData::Integer( - Signedness::Unsigned, - noirc_frontend::IntegerBitSize::ThirtyTwo, - ), - span: None, - }, - hasher_name, - ) - } - _ => { - return Err(AztecMacroError::UnsupportedFunctionArgumentType { + injected_statements.extend( + serialize_to_hasher(identifier, unresolved_type, hasher_name).ok_or_else( + || AztecMacroError::UnsupportedFunctionArgumentType { typ: unresolved_type.clone(), span: *span, - }) - } - }; - injected_expressions.push(expression); + }, + )?, + ); } _ => todo!(), // Maybe unreachable? } @@ -412,10 +426,10 @@ fn create_context(ty: &str, params: &[Param]) -> Result, AztecMac vec![inputs_expression, hash_call], // args ), ); - injected_expressions.push(let_context); + injected_statements.push(let_context); // Return all expressions that will be injected by the hasher - Ok(injected_expressions) + Ok(injected_statements) } /// Creates the private context object to be accessed within the function, the parameters need to be extracted to be @@ -452,53 +466,86 @@ fn create_context_avm() -> Result, AztecMacroError> { /// Abstract Return Type /// -/// This function intercepts the function's current return type and replaces it with pushes -/// To the kernel +/// This function intercepts the function's current return type and replaces it with pushes to a hasher +/// that will be used to generate the returns hash for the kernel. /// /// The replaced code: /// ```noir /// /// Before /// #[aztec(private)] -/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { -/// // ... -/// let my_return_value: Field = 10; -/// context.return_values.push(my_return_value); -/// } -/// -/// /// After -/// #[aztec(private)] /// fn foo() -> Field { /// // ... /// let my_return_value: Field = 10; /// my_return_value /// } +/// +/// /// After +/// #[aztec(private)] +/// fn foo() -> protocol_types::abis::private_circuit_public_inputs::PrivateCircuitPublicInputs { +/// // ... +/// let my_return_value: Field = 10; +/// let macro__returned__values = my_return_value; +/// let mut returns_hasher = ArgsHasher::new(); +/// returns_hasher.add(macro__returned__values); +/// context.set_return_hash(returns_hasher); +/// } /// ``` -/// Similarly; Structs will be pushed to the context, after serialize() is called on them. -/// Arrays will be iterated over and each element will be pushed to the context. -/// Any primitive type that can be cast will be casted to a field and pushed to the context. -fn abstract_return_values(func: &NoirFunction) -> Option { +/// Similarly; Structs will be pushed to the hasher, after serialize() is called on them. +/// Arrays will be iterated over and each element will be pushed to the hasher. +/// Any primitive type that can be cast will be casted to a field and pushed to the hasher. +fn abstract_return_values(func: &NoirFunction) -> Result>, AztecMacroError> { let current_return_type = func.return_type().typ; - let last_statement = func.def.body.statements.last()?; - // TODO: (length, type) => We can limit the size of the array returned to be limited by kernel size - // Doesn't need done until we have settled on a kernel size + // Short circuit if the function doesn't return anything + match current_return_type { + UnresolvedTypeData::Unit | UnresolvedTypeData::Unspecified => return Ok(None), + _ => (), + } + + let Some(last_statement) = func.def.body.statements.last() else { + return Ok(None); + }; + // TODO: support tuples here and in inputs -> convert into an issue // Check if the return type is an expression, if it is, we can handle it match last_statement { Statement { kind: StatementKind::Expression(expression), .. } => { - match current_return_type { - // Call serialize on structs, push the whole array, calling push_array - UnresolvedTypeData::Named(..) => Some(make_struct_return_type(expression.clone())), - UnresolvedTypeData::Array(..) => Some(make_array_return_type(expression.clone())), - // Cast these types to a field before pushing - UnresolvedTypeData::Bool | UnresolvedTypeData::Integer(..) => { - Some(make_castable_return_type(expression.clone())) - } - UnresolvedTypeData::FieldElement => Some(make_return_push(expression.clone())), - _ => None, - } + let return_value_name = "macro__returned__values"; + let hasher_name = "returns_hasher"; + + let mut replacement_statements = vec![ + assignment_with_type( + return_value_name, // Assigned to + current_return_type.clone(), + expression.clone(), + ), + mutable_assignment( + hasher_name, // Assigned to + call( + variable_path(chained_dep!("aztec", "hash", "ArgsHasher", "new")), // Path + vec![], // args + ), + ), + ]; + + let serialization_statements = + serialize_to_hasher(&ident(return_value_name), ¤t_return_type, hasher_name) + .ok_or_else(|| AztecMacroError::UnsupportedFunctionReturnType { + typ: current_return_type.clone(), + span: func.return_type().span.unwrap_or_default(), + })?; + + replacement_statements.extend(serialization_statements); + + replacement_statements.push(make_statement(StatementKind::Semi(method_call( + variable("context"), + "set_return_hash", + vec![variable(hasher_name)], + )))); + + Ok(Some(replacement_statements)) } - _ => None, + _ => Ok(None), } } @@ -547,86 +594,6 @@ fn abstract_storage(typ: &str, unconstrained: bool) -> Statement { ) } -/// Context Return Values -/// -/// Creates an instance to the context return values -/// ```noir -/// `context.return_values` -/// ``` -fn context_return_values() -> Expression { - member_access("context", "return_values") -} - -/// Make return Push -/// -/// Translates to: -/// `context.return_values.push({push_value})` -fn make_return_push(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "push", - vec![push_value], - ))) -} - -/// Make Return push array -/// -/// Translates to: -/// `context.return_values.extend_from_array({push_value})` -fn make_return_extend_from_array(push_value: Expression) -> Statement { - make_statement(StatementKind::Semi(method_call( - context_return_values(), - "extend_from_array", - vec![push_value], - ))) -} - -/// Make struct return type -/// -/// Translates to: -/// ```noir -/// `context.return_values.extend_from_array({push_value}.serialize())` -fn make_struct_return_type(expression: Expression) -> Statement { - let serialized_call = method_call( - expression, // variable - "serialize", // method name - vec![], // args - ); - make_return_extend_from_array(serialized_call) -} - -/// Make array return type -/// -/// Translates to: -/// ```noir -/// for i in 0..{ident}.len() { -/// context.return_values.push({ident}[i] as Field) -/// } -/// ``` -fn make_array_return_type(expression: Expression) -> Statement { - let inner_cast_expression = - cast(index_array_variable(expression.clone(), "i"), UnresolvedTypeData::FieldElement); - let assignment = make_statement(StatementKind::Semi(method_call( - context_return_values(), // variable - "push", // method name - vec![inner_cast_expression], - ))); - - create_loop_over(expression, vec![assignment]) -} - -/// Castable return type -/// -/// Translates to: -/// ```noir -/// context.return_values.push({ident} as Field) -/// ``` -fn make_castable_return_type(expression: Expression) -> Statement { - // Cast these types to a field before pushing - let cast_expression = cast(expression, UnresolvedTypeData::FieldElement); - make_return_push(cast_expression) -} - /// Create Return Type /// /// Public functions return protocol_types::abis::public_circuit_public_inputs::PublicCircuitPublicInputs while diff --git a/aztec_macros/src/utils/ast_utils.rs b/aztec_macros/src/utils/ast_utils.rs index eb8f5a4156d..1731dfab49c 100644 --- a/aztec_macros/src/utils/ast_utils.rs +++ b/aztec_macros/src/utils/ast_utils.rs @@ -2,9 +2,8 @@ use noirc_errors::{Span, Spanned}; use noirc_frontend::{ token::SecondaryAttribute, BinaryOpKind, CallExpression, CastExpression, Expression, ExpressionKind, FunctionReturnType, Ident, IndexExpression, InfixExpression, Lambda, - LetStatement, MemberAccessExpression, MethodCallExpression, NoirTraitImpl, Path, Pattern, - PrefixExpression, Statement, StatementKind, TraitImplItem, UnaryOp, UnresolvedType, - UnresolvedTypeData, + LetStatement, MethodCallExpression, NoirTraitImpl, Path, Pattern, PrefixExpression, Statement, + StatementKind, TraitImplItem, UnaryOp, UnresolvedType, UnresolvedTypeData, }; // @@ -79,21 +78,22 @@ pub fn mutable_reference(variable_name: &str) -> Expression { } pub fn assignment(name: &str, assigned_to: Expression) -> Statement { + assignment_with_type(name, UnresolvedTypeData::Unspecified, assigned_to) +} + +pub fn assignment_with_type( + name: &str, + typ: UnresolvedTypeData, + assigned_to: Expression, +) -> Statement { make_statement(StatementKind::Let(LetStatement { pattern: pattern(name), - r#type: make_type(UnresolvedTypeData::Unspecified), + r#type: make_type(typ), expression: assigned_to, attributes: vec![], })) } -pub fn member_access(lhs: &str, rhs: &str) -> Expression { - expression(ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { - lhs: variable(lhs), - rhs: ident(rhs), - }))) -} - pub fn return_type(path: Path) -> FunctionReturnType { let ty = make_type(UnresolvedTypeData::Named(path, vec![], true)); FunctionReturnType::Ty(ty) @@ -169,13 +169,6 @@ pub fn index_array(array: Ident, index: &str) -> Expression { }))) } -pub fn index_array_variable(array: Expression, index: &str) -> Expression { - expression(ExpressionKind::Index(Box::new(IndexExpression { - collection: array, - index: variable(index), - }))) -} - pub fn check_trait_method_implemented(trait_impl: &NoirTraitImpl, method_name: &str) -> bool { trait_impl.items.iter().any(|item| match item { TraitImplItem::Function(func) => func.def.name.0.contents == method_name, diff --git a/aztec_macros/src/utils/errors.rs b/aztec_macros/src/utils/errors.rs index 9aead1756f9..5d3a61a51dc 100644 --- a/aztec_macros/src/utils/errors.rs +++ b/aztec_macros/src/utils/errors.rs @@ -8,6 +8,7 @@ pub enum AztecMacroError { AztecDepNotFound, ContractHasTooManyPrivateFunctions { span: Span }, UnsupportedFunctionArgumentType { span: Span, typ: UnresolvedTypeData }, + UnsupportedFunctionReturnType { span: Span, typ: UnresolvedTypeData }, UnsupportedStorageType { span: Option, typ: UnresolvedTypeData }, CouldNotAssignStorageSlots { secondary_message: Option }, CouldNotImplementNoteInterface { span: Option, secondary_message: Option }, @@ -36,6 +37,11 @@ impl From for MacroError { secondary_message: None, span: Some(span), }, + AztecMacroError::UnsupportedFunctionReturnType { span, typ } => MacroError { + primary_message: format!("Provided return type `{typ:?}` is not supported in Aztec contract interface"), + secondary_message: None, + span: Some(span), + }, AztecMacroError::UnsupportedStorageType { span, typ } => MacroError { primary_message: format!("Provided storage type `{typ:?}` is not directly supported in Aztec. Please provide a custom storage implementation"), secondary_message: None, diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index 15a2a531e78..4b97a61491d 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -67,61 +67,105 @@ pub(crate) fn directive_invert() -> GeneratedBrillig { /// (a/b, a-a/b*b) /// } /// ``` -pub(crate) fn directive_quotient(mut bit_size: u32) -> GeneratedBrillig { +pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { // `a` is (0) (i.e register index 0) // `b` is (1) - if bit_size > FieldElement::max_num_bits() { - bit_size = FieldElement::max_num_bits(); - } - GeneratedBrillig { - byte_code: vec![ - BrilligOpcode::CalldataCopy { - destination_address: MemoryAddress::from(0), - size: 2, - offset: 0, - }, - BrilligOpcode::Cast { - destination: MemoryAddress(0), - source: MemoryAddress(0), - bit_size, - }, - BrilligOpcode::Cast { - destination: MemoryAddress(1), - source: MemoryAddress(1), - bit_size, - }, - //q = a/b is set into register (2) - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Div, - lhs: MemoryAddress::from(0), - rhs: MemoryAddress::from(1), - destination: MemoryAddress::from(2), - bit_size, - }, - //(1)= q*b - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Mul, - lhs: MemoryAddress::from(2), - rhs: MemoryAddress::from(1), - destination: MemoryAddress::from(1), - bit_size, - }, - //(1) = a-q*b - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Sub, - lhs: MemoryAddress::from(0), - rhs: MemoryAddress::from(1), - destination: MemoryAddress::from(1), - bit_size, - }, - //(0) = q - BrilligOpcode::Mov { - destination: MemoryAddress::from(0), - source: MemoryAddress::from(2), - }, - BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, - ], - assert_messages: Default::default(), - locations: Default::default(), + + // TODO: The only difference between these implementations is the integer version will truncate the input to the `bit_size` via cast. + // Once we deduplicate brillig functions then we can modify this so that fields and integers share the same quotient function. + if bit_size >= FieldElement::max_num_bits() { + // Field version + GeneratedBrillig { + byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, + // No cast, since calldata is typed as field by default + //q = a/b is set into register (2) + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::IntegerDiv, // We want integer division, not field division! + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), + }, + //(1)= q*b + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::Mul, + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + }, + //(1) = a-q*b + BrilligOpcode::BinaryFieldOp { + op: BinaryFieldOp::Sub, + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + }, + //(0) = q + BrilligOpcode::Mov { + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, + ], + assert_messages: Default::default(), + locations: Default::default(), + } + } else { + // Integer version + GeneratedBrillig { + byte_code: vec![ + BrilligOpcode::CalldataCopy { + destination_address: MemoryAddress::from(0), + size: 2, + offset: 0, + }, + BrilligOpcode::Cast { + destination: MemoryAddress(0), + source: MemoryAddress(0), + bit_size, + }, + BrilligOpcode::Cast { + destination: MemoryAddress(1), + source: MemoryAddress(1), + bit_size, + }, + //q = a/b is set into register (2) + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Div, + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(2), + bit_size, + }, + //(1)= q*b + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Mul, + lhs: MemoryAddress::from(2), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + bit_size, + }, + //(1) = a-q*b + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Sub, + lhs: MemoryAddress::from(0), + rhs: MemoryAddress::from(1), + destination: MemoryAddress::from(1), + bit_size, + }, + //(0) = q + BrilligOpcode::Mov { + destination: MemoryAddress::from(0), + source: MemoryAddress::from(2), + }, + BrilligOpcode::Stop { return_data_offset: 0, return_data_size: 2 }, + ], + assert_messages: Default::default(), + locations: Default::default(), + } } } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs index b93693d9c79..3e1515b1eed 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_slice_ops.rs @@ -465,7 +465,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); @@ -590,7 +590,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); @@ -686,7 +686,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); @@ -838,7 +838,7 @@ mod tests { assert_eq!( vm.get_memory()[return_data_offset..(return_data_offset + expected_return.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), expected_return ); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs index db872487fcc..fe3c5e0bb9c 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_ir/entry_point.rs @@ -527,7 +527,7 @@ mod tests { let (vm, return_data_offset, return_data_size) = create_and_run_vm(calldata.clone(), &bytecode); assert_eq!(return_data_size, 1, "Return data size is incorrect"); - assert_eq!(vm.get_memory()[return_data_offset].value, FieldElement::from(1_usize)); + assert_eq!(vm.get_memory()[return_data_offset].to_field(), FieldElement::from(1_usize)); } #[test] @@ -569,7 +569,7 @@ mod tests { assert_eq!( memory[return_data_pointer..(return_data_pointer + flattened_array.len())] .iter() - .map(|mem_val| mem_val.value) + .map(|mem_val| mem_val.to_field()) .collect::>(), flattened_array ); diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 53d9e2530cc..775571f4a41 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -1623,7 +1623,7 @@ impl AcirContext { let outputs_var = vecmap(outputs_types.iter(), |output| match output { AcirType::NumericType(_) => { let var = self.add_data(AcirVarData::Const( - memory.next().expect("Missing return data").value, + memory.next().expect("Missing return data").to_field(), )); AcirValue::Var(var, output.clone()) } @@ -1657,7 +1657,7 @@ impl AcirContext { AcirType::NumericType(_) => { let memory_value = memory_iter.next().expect("ICE: Unexpected end of memory"); - let var = self.add_data(AcirVarData::Const(memory_value.value)); + let var = self.add_data(AcirVarData::Const(memory_value.to_field())); array_values.push_back(AcirValue::Var(var, element_type.clone())); } } diff --git a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index b43110b2f5b..ba4e03bff95 100644 --- a/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/compiler/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -240,11 +240,7 @@ impl GeneratedAcir { } }; - BlackBoxFuncCall::Keccak256VariableLength { - inputs: inputs[0].clone(), - var_message_size, - outputs, - } + BlackBoxFuncCall::Keccak256 { inputs: inputs[0].clone(), var_message_size, outputs } } BlackBoxFunc::Keccakf1600 => { BlackBoxFuncCall::Keccakf1600 { inputs: inputs[0].clone(), outputs } diff --git a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs index c6bf7923fa8..6cf155f85ab 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/unrolling.rs @@ -151,7 +151,10 @@ impl Loops { if next_loop.blocks.iter().any(|block| self.modified_blocks.contains(block)) { let mut new_context = find_all_loops(function); new_context.failed_to_unroll = self.failed_to_unroll; - return new_context.unroll_each_loop(function); + return unroll_errors + .into_iter() + .chain(new_context.unroll_each_loop(function)) + .collect(); } // Don't try to unroll the loop again if it is known to fail diff --git a/tooling/bb_abstraction_leaks/build.rs b/tooling/bb_abstraction_leaks/build.rs index e055d7a3a5f..0f9770c805d 100644 --- a/tooling/bb_abstraction_leaks/build.rs +++ b/tooling/bb_abstraction_leaks/build.rs @@ -10,7 +10,7 @@ use const_format::formatcp; const USERNAME: &str = "AztecProtocol"; const REPO: &str = "aztec-packages"; -const VERSION: &str = "0.33.0"; +const VERSION: &str = "0.34.0"; const TAG: &str = formatcp!("aztec-packages-v{}", VERSION); const API_URL: &str = diff --git a/tooling/debugger/src/context.rs b/tooling/debugger/src/context.rs index 1acd581b2be..b211832518d 100644 --- a/tooling/debugger/src/context.rs +++ b/tooling/debugger/src/context.rs @@ -513,7 +513,11 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> { pub(super) fn write_brillig_memory(&mut self, ptr: usize, value: FieldElement, bit_size: u32) { if let Some(solver) = self.brillig_solver.as_mut() { - solver.write_memory_at(ptr, MemoryValue::new(value, bit_size)); + solver.write_memory_at( + ptr, + MemoryValue::new_checked(value, bit_size) + .expect("Invalid value for the given bit size"), + ); } } diff --git a/tooling/debugger/src/repl.rs b/tooling/debugger/src/repl.rs index 1c077c6ee9b..e30d519b62e 100644 --- a/tooling/debugger/src/repl.rs +++ b/tooling/debugger/src/repl.rs @@ -319,7 +319,7 @@ impl<'a, B: BlackBoxFunctionSolver> ReplDebugger<'a, B> { return; }; - for (index, value) in memory.iter().enumerate().filter(|(_, value)| value.bit_size > 0) { + for (index, value) in memory.iter().enumerate().filter(|(_, value)| value.bit_size() > 0) { println!("{index} = {}", value); } } diff --git a/tooling/noir_js_backend_barretenberg/package.json b/tooling/noir_js_backend_barretenberg/package.json index e592ed440ee..98bfdf1c3a8 100644 --- a/tooling/noir_js_backend_barretenberg/package.json +++ b/tooling/noir_js_backend_barretenberg/package.json @@ -42,7 +42,7 @@ "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0" }, "dependencies": { - "@aztec/bb.js": "0.33.0", + "@aztec/bb.js": "0.34.0", "@noir-lang/types": "workspace:*", "fflate": "^0.8.0" }, diff --git a/yarn.lock b/yarn.lock index 0fdad4ad2ad..38e13814929 100644 --- a/yarn.lock +++ b/yarn.lock @@ -221,9 +221,9 @@ __metadata: languageName: node linkType: hard -"@aztec/bb.js@npm:0.33.0": - version: 0.33.0 - resolution: "@aztec/bb.js@npm:0.33.0" +"@aztec/bb.js@npm:0.34.0": + version: 0.34.0 + resolution: "@aztec/bb.js@npm:0.34.0" dependencies: comlink: ^4.4.1 commander: ^10.0.1 @@ -231,7 +231,7 @@ __metadata: tslib: ^2.4.0 bin: bb.js: dest/node/main.js - checksum: 16244a52ef1cb5efca582e863a3521d04f0fb66b02cd584b904e6e65f684e392eec56679439d1a831127e126d117bf0e116166fc4b24efdd6e1ebe9097efed06 + checksum: 9d07834d81ed19e4d6fd5c1f3b07c565648df1165c30115f020ece9660b2b8599a5ed894a2090410f14020e73dd290484b30b76c9c71e863b8390fa2b7c1b729 languageName: node linkType: hard @@ -4396,7 +4396,7 @@ __metadata: version: 0.0.0-use.local resolution: "@noir-lang/backend_barretenberg@workspace:tooling/noir_js_backend_barretenberg" dependencies: - "@aztec/bb.js": 0.33.0 + "@aztec/bb.js": 0.34.0 "@noir-lang/types": "workspace:*" "@types/node": ^20.6.2 "@types/prettier": ^3 From 9fd91b771fe726e6a4872d16208b64d8b6517b0a Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Fri, 12 Apr 2024 05:47:29 -0400 Subject: [PATCH 170/416] chore: testing that nargo fmt is idempotent (#4765) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4666 ## Summary\* Ensures that `nargo_fmt(nargo_fmt(source_code)) == nargo_fmt(source_code)`. Because we have expected outputs, we can avoid re-running on the unformatted inputs: ```rust nargo_fmt(expected_output) == expected_output ``` ### Bugs found - https://github.com/noir-lang/noir/issues/4766 - https://github.com/noir-lang/noir/issues/4767 - https://github.com/noir-lang/noir/issues/4768 Currently failing on arrays and tuples: ```bash ---- tests::format_idempotent_array stdout ---- thread 'tests::format_idempotent_array' panicked at /Users/michaelklein/Coding/rust/noir/target/debug/build/nargo_fmt-14fb91f269fc38b6/out/execute.rs:3418:9: assertion failed: `(left == right)`' left: `"fn big_array() {\n [\n 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 1..."` (truncated) right: `"fn big_array() {\n [\n 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 1..."` (truncated) Differences (-left|+right): [ // hello! 1, // asd - 10 + 10 // asdasd ]; [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], [[13, 14, 15], [16, 17, 18]]]; note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- tests::format_idempotent_tuple stdout ---- thread 'tests::format_idempotent_tuple' panicked at /Users/michaelklein/Coding/rust/noir/target/debug/build/nargo_fmt-14fb91f269fc38b6/out/execute.rs:776:9: assertion failed: `(left == right)`' left: `"fn main() {\n (1,);\n (// hello\n 1,);\n (/*hello*/ 1,);\n (1/*hello*/,);\n (1,);\n (/*test*/ 1,);\n (/*a*/ 1/*b*/,);\n (/*a*/ 1/*b*/, /*c*/ 2/*d*/, /*c*/ 2/*d*/);\n (/*a*/ 1/*..."` (truncated) right: `"fn main() {\n (1,);\n (// hello\n 1,);\n (/*hello*/ 1,);\n (1/*hello*/,);\n (1,);\n (/*test*/ 1,);\n (/*a*/ 1/*b*/,);\n (/*a*/ 1/*b*/, /*c*/ 2/*d*/, /*c*/ 2/*d*/);\n (/*a*/ 1/*..."` (truncated) Differences (-left|+right): (// 1,); (// 1 - 1,// 2, + 1, // 2, 2); (/*1*/ 1, /*2*/ 2); // FIXME: (((//2 1,),),); (/*a*/ - 1/*b*/, /*c*/ 2/*d*/, /*c*/ 2/*d*/, /*e*/ 3/*f*/); + 1/*b*/, +/*c*/ 2/*d*/, /*c*/ 2/*d*/, /*e*/ 3/*f*/); } ``` ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- tooling/nargo_fmt/build.rs | 51 +++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/tooling/nargo_fmt/build.rs b/tooling/nargo_fmt/build.rs index 6f41768c1dc..7d5f07c43bf 100644 --- a/tooling/nargo_fmt/build.rs +++ b/tooling/nargo_fmt/build.rs @@ -49,28 +49,55 @@ fn generate_formatter_tests(test_file: &mut File, test_data_dir: &Path) { let output_source_path = outputs_dir.join(file_name).display().to_string(); let output_source = std::fs::read_to_string(output_source_path.clone()).unwrap(); + let skip_idempotent_test = + // TODO(https://github.com/noir-lang/noir/issues/4766): spurious trailing space + test_name == "array" || + // TODO(https://github.com/noir-lang/noir/issues/4767): pre-comment space + // TODO(https://github.com/noir-lang/noir/issues/4768): spurious newline + test_name == "tuple"; + write!( test_file, r##" -#[test] -fn format_{test_name}() {{ - let input = r#"{input_source}"#; - let expected_output = r#"{output_source}"#; + #[test] + fn format_{test_name}() {{ + let input = r#"{input_source}"#; + let expected_output = r#"{output_source}"#; - let (parsed_module, _errors) = noirc_frontend::parse_program(input); + let (parsed_module, _errors) = noirc_frontend::parse_program(input); - let config = nargo_fmt::Config::of("{config}").unwrap(); - let fmt_text = nargo_fmt::format(input, parsed_module, &config); + let config = nargo_fmt::Config::of("{config}").unwrap(); + let fmt_text = nargo_fmt::format(input, parsed_module, &config); - if std::env::var("UPDATE_EXPECT").is_ok() {{ - std::fs::write("{output_source_path}", fmt_text.clone()).unwrap(); - }} + if std::env::var("UPDATE_EXPECT").is_ok() {{ + std::fs::write("{output_source_path}", fmt_text.clone()).unwrap(); + }} - similar_asserts::assert_eq!(fmt_text, expected_output); -}} + similar_asserts::assert_eq!(fmt_text, expected_output); + }} "## ) .expect("Could not write templated test file."); + + if !skip_idempotent_test { + write!( + test_file, + r##" + #[test] + fn format_idempotent_{test_name}() {{ + let expected_output = r#"{output_source}"#; + + let (parsed_module, _errors) = noirc_frontend::parse_program(expected_output); + + let config = nargo_fmt::Config::of("{config}").unwrap(); + let fmt_text = nargo_fmt::format(expected_output, parsed_module, &config); + + similar_asserts::assert_eq!(fmt_text, expected_output); + }} + "## + ) + .expect("Could not write templated test file."); + } } } From ce1e6624ece3c91f06b0273af9ba88e703c1b589 Mon Sep 17 00:00:00 2001 From: Michael J Klein Date: Fri, 12 Apr 2024 06:32:32 -0400 Subject: [PATCH 171/416] feat: split `backend_barretenburg` into prover and verifier classes (#4769) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/3450 ## Summary\* - In order to prove + verify, we have proving and verification keys + The proving key is derived from the circuit representation and the verification key is derived from the proving key + The proving key is generally quite big, so we always generate it from the circuit, which is easier than lugging it around + The verification key is much smaller and can be compressed - Ideally, a user verifying a proof should be able to simply send the verification key and proof to BB ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --------- Co-authored-by: TomAFrench Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com> --- tooling/noir_js/test/node/e2e.test.ts | 24 ++- .../src/backend.ts | 143 +++++++++++++++++ .../noir_js_backend_barretenberg/src/index.ts | 151 +----------------- .../src/verifier.ts | 78 +++++++++ tooling/noir_js_types/src/types.ts | 20 +-- 5 files changed, 259 insertions(+), 157 deletions(-) create mode 100644 tooling/noir_js_backend_barretenberg/src/backend.ts create mode 100644 tooling/noir_js_backend_barretenberg/src/verifier.ts diff --git a/tooling/noir_js/test/node/e2e.test.ts b/tooling/noir_js/test/node/e2e.test.ts index 8921314e8ea..979841c47e6 100644 --- a/tooling/noir_js/test/node/e2e.test.ts +++ b/tooling/noir_js/test/node/e2e.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import assert_lt_json from '../noir_compiled_examples/assert_lt/target/assert_lt.json' assert { type: 'json' }; import { Noir } from '@noir-lang/noir_js'; -import { BarretenbergBackend as Backend } from '@noir-lang/backend_barretenberg'; +import { BarretenbergBackend as Backend, BarretenbergVerifier as Verifier } from '@noir-lang/backend_barretenberg'; import { CompiledCircuit } from '@noir-lang/types'; const assert_lt_program = assert_lt_json as CompiledCircuit; @@ -47,6 +47,28 @@ it('end-to-end proof creation and verification (outer) -- Program API', async () expect(isValid).to.be.true; }); +it('end-to-end proof creation and verification (outer) -- Verifier API', async () => { + // Noir.Js part + const inputs = { + x: '2', + y: '3', + }; + + // Initialize backend + const backend = new Backend(assert_lt_program); + // Initialize program + const program = new Noir(assert_lt_program, backend); + // Generate proof + const proof = await program.generateProof(inputs); + + const verificationKey = await backend.getVerificationKey(); + + // Proof verification + const verifier = new Verifier(); + const isValid = await verifier.verifyProof(proof, verificationKey); + expect(isValid).to.be.true; +}); + // TODO: maybe switch to using assert_statement_recursive here to test both options it('end-to-end proof creation and verification (inner)', async () => { // Noir.Js part diff --git a/tooling/noir_js_backend_barretenberg/src/backend.ts b/tooling/noir_js_backend_barretenberg/src/backend.ts new file mode 100644 index 00000000000..d07681dd8c1 --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/src/backend.ts @@ -0,0 +1,143 @@ +import { decompressSync as gunzip } from 'fflate'; +import { acirToUint8Array } from './serialize.js'; +import { Backend, CompiledCircuit, ProofData, VerifierBackend } from '@noir-lang/types'; +import { BackendOptions } from './types.js'; +import { deflattenPublicInputs } from './public_inputs.js'; +import { reconstructProofWithPublicInputs } from './verifier.js'; +import { type Barretenberg } from '@aztec/bb.js'; + +// This is the number of bytes in a UltraPlonk proof +// minus the public inputs. +const numBytesInProofWithoutPublicInputs: number = 2144; + +export class BarretenbergVerifierBackend implements VerifierBackend { + // These type assertions are used so that we don't + // have to initialize `api` and `acirComposer` in the constructor. + // These are initialized asynchronously in the `init` function, + // constructors cannot be asynchronous which is why we do this. + + protected api!: Barretenberg; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + protected acirComposer: any; + protected acirUncompressedBytecode: Uint8Array; + + constructor( + acirCircuit: CompiledCircuit, + protected options: BackendOptions = { threads: 1 }, + ) { + const acirBytecodeBase64 = acirCircuit.bytecode; + this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); + } + + /** @ignore */ + async instantiate(): Promise { + if (!this.api) { + if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { + this.options.threads = navigator.hardwareConcurrency; + } else { + try { + const os = await import('os'); + this.options.threads = os.cpus().length; + } catch (e) { + console.log('Could not detect environment. Falling back to one thread.', e); + } + } + const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); + const api = await Barretenberg.new(this.options); + + const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); + const crs = await Crs.new(subgroupSize + 1); + await api.commonInitSlabAllocator(subgroupSize); + await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); + + this.acirComposer = await api.acirNewAcirComposer(subgroupSize); + await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); + this.api = api; + } + } + + /** @description Verifies a proof */ + async verifyProof(proofData: ProofData): Promise { + const proof = reconstructProofWithPublicInputs(proofData); + await this.instantiate(); + await this.api.acirInitVerificationKey(this.acirComposer); + return await this.api.acirVerifyProof(this.acirComposer, proof); + } + + async getVerificationKey(): Promise { + await this.instantiate(); + await this.api.acirInitVerificationKey(this.acirComposer); + return await this.api.acirGetVerificationKey(this.acirComposer); + } + + async destroy(): Promise { + if (!this.api) { + return; + } + await this.api.destroy(); + } +} + +export class BarretenbergBackend extends BarretenbergVerifierBackend implements Backend { + /** @description Generates a proof */ + async generateProof(compressedWitness: Uint8Array): Promise { + await this.instantiate(); + const proofWithPublicInputs = await this.api.acirCreateProof( + this.acirComposer, + this.acirUncompressedBytecode, + gunzip(compressedWitness), + ); + + const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; + + const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); + const proof = proofWithPublicInputs.slice(splitIndex); + const publicInputs = deflattenPublicInputs(publicInputsConcatenated); + + return { proof, publicInputs }; + } + + /** + * Generates artifacts that will be passed to a circuit that will verify this proof. + * + * Instead of passing the proof and verification key as a byte array, we pass them + * as fields which makes it cheaper to verify in a circuit. + * + * The proof that is passed here will have been created using a circuit + * that has the #[recursive] attribute on its `main` method. + * + * The number of public inputs denotes how many public inputs are in the inner proof. + * + * @example + * ```typescript + * const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); + * ``` + */ + async generateRecursiveProofArtifacts( + proofData: ProofData, + numOfPublicInputs = 0, + ): Promise<{ + proofAsFields: string[]; + vkAsFields: string[]; + vkHash: string; + }> { + await this.instantiate(); + const proof = reconstructProofWithPublicInputs(proofData); + const proofAsFields = ( + await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs) + ).slice(numOfPublicInputs); + + // TODO: perhaps we should put this in the init function. Need to benchmark + // TODO how long it takes. + await this.api.acirInitVerificationKey(this.acirComposer); + + // Note: If you don't init verification key, `acirSerializeVerificationKeyIntoFields`` will just hang on serialization + const vk = await this.api.acirSerializeVerificationKeyIntoFields(this.acirComposer); + + return { + proofAsFields: proofAsFields.map((p) => p.toString()), + vkAsFields: vk[0].map((vk) => vk.toString()), + vkHash: vk[1].toString(), + }; + } +} diff --git a/tooling/noir_js_backend_barretenberg/src/index.ts b/tooling/noir_js_backend_barretenberg/src/index.ts index bfdf1005a93..f28abb9a658 100644 --- a/tooling/noir_js_backend_barretenberg/src/index.ts +++ b/tooling/noir_js_backend_barretenberg/src/index.ts @@ -1,150 +1,7 @@ -import { decompressSync as gunzip } from 'fflate'; -import { acirToUint8Array } from './serialize.js'; -import { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; -import { BackendOptions } from './types.js'; -import { deflattenPublicInputs, flattenPublicInputsAsArray } from './public_inputs.js'; -import { type Barretenberg } from '@aztec/bb.js'; - +export { BarretenbergBackend } from './backend.js'; +export { BarretenbergVerifier } from './verifier.js'; export { publicInputsToWitnessMap } from './public_inputs.js'; -// This is the number of bytes in a UltraPlonk proof -// minus the public inputs. -const numBytesInProofWithoutPublicInputs: number = 2144; - -export class BarretenbergBackend implements Backend { - // These type assertions are used so that we don't - // have to initialize `api` and `acirComposer` in the constructor. - // These are initialized asynchronously in the `init` function, - // constructors cannot be asynchronous which is why we do this. - - private api!: Barretenberg; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private acirComposer: any; - private acirUncompressedBytecode: Uint8Array; - - constructor( - acirCircuit: CompiledCircuit, - private options: BackendOptions = { threads: 1 }, - ) { - const acirBytecodeBase64 = acirCircuit.bytecode; - this.acirUncompressedBytecode = acirToUint8Array(acirBytecodeBase64); - } - - /** @ignore */ - async instantiate(): Promise { - if (!this.api) { - if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { - this.options.threads = navigator.hardwareConcurrency; - } else { - try { - const os = await import('os'); - this.options.threads = os.cpus().length; - } catch (e) { - console.log('Could not detect environment. Falling back to one thread.', e); - } - } - const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); - const api = await Barretenberg.new(this.options); - const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); - const crs = await Crs.new(subgroupSize + 1); - await api.commonInitSlabAllocator(subgroupSize); - await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); - - this.acirComposer = await api.acirNewAcirComposer(subgroupSize); - await api.acirInitProvingKey(this.acirComposer, this.acirUncompressedBytecode); - this.api = api; - } - } - - /** @description Generates a proof */ - async generateProof(compressedWitness: Uint8Array): Promise { - await this.instantiate(); - // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag - const proofWithPublicInputs = await this.api.acirCreateProof( - this.acirComposer, - this.acirUncompressedBytecode, - gunzip(compressedWitness), - ); - - const splitIndex = proofWithPublicInputs.length - numBytesInProofWithoutPublicInputs; - - const publicInputsConcatenated = proofWithPublicInputs.slice(0, splitIndex); - const proof = proofWithPublicInputs.slice(splitIndex); - const publicInputs = deflattenPublicInputs(publicInputsConcatenated); - - return { proof, publicInputs }; - } - - /** - * Generates artifacts that will be passed to a circuit that will verify this proof. - * - * Instead of passing the proof and verification key as a byte array, we pass them - * as fields which makes it cheaper to verify in a circuit. - * - * The proof that is passed here will have been created using a circuit - * that has the #[recursive] attribute on its `main` method. - * - * The number of public inputs denotes how many public inputs are in the inner proof. - * - * @example - * ```typescript - * const artifacts = await backend.generateRecursiveProofArtifacts(proof, numOfPublicInputs); - * ``` - */ - async generateRecursiveProofArtifacts( - proofData: ProofData, - numOfPublicInputs = 0, - ): Promise<{ - proofAsFields: string[]; - vkAsFields: string[]; - vkHash: string; - }> { - await this.instantiate(); - const proof = reconstructProofWithPublicInputs(proofData); - const proofAsFields = ( - await this.api.acirSerializeProofIntoFields(this.acirComposer, proof, numOfPublicInputs) - ).slice(numOfPublicInputs); - - // TODO: perhaps we should put this in the init function. Need to benchmark - // TODO how long it takes. - await this.api.acirInitVerificationKey(this.acirComposer); - - // Note: If you don't init verification key, `acirSerializeVerificationKeyIntoFields`` will just hang on serialization - const vk = await this.api.acirSerializeVerificationKeyIntoFields(this.acirComposer); - - return { - proofAsFields: proofAsFields.map((p) => p.toString()), - vkAsFields: vk[0].map((vk) => vk.toString()), - vkHash: vk[1].toString(), - }; - } - - /** @description Verifies a proof */ - async verifyProof(proofData: ProofData): Promise { - const proof = reconstructProofWithPublicInputs(proofData); - await this.instantiate(); - await this.api.acirInitVerificationKey(this.acirComposer); - // TODO: Change once `@aztec/bb.js` version is updated to use methods without isRecursive flag - return await this.api.acirVerifyProof(this.acirComposer, proof); - } - - async destroy(): Promise { - if (!this.api) { - return; - } - await this.api.destroy(); - } -} - -function reconstructProofWithPublicInputs(proofData: ProofData): Uint8Array { - // Flatten publicInputs - const publicInputsConcatenated = flattenPublicInputsAsArray(proofData.publicInputs); - - // Concatenate publicInputs and proof - const proofWithPublicInputs = Uint8Array.from([...publicInputsConcatenated, ...proofData.proof]); - - return proofWithPublicInputs; -} - // typedoc exports -export { Backend, BackendOptions, CompiledCircuit, ProofData }; +export { Backend, CompiledCircuit, ProofData } from '@noir-lang/types'; +export { BackendOptions } from './types.js'; diff --git a/tooling/noir_js_backend_barretenberg/src/verifier.ts b/tooling/noir_js_backend_barretenberg/src/verifier.ts new file mode 100644 index 00000000000..fe9fa9cfffd --- /dev/null +++ b/tooling/noir_js_backend_barretenberg/src/verifier.ts @@ -0,0 +1,78 @@ +import { ProofData } from '@noir-lang/types'; +import { BackendOptions } from './types.js'; +import { flattenPublicInputsAsArray } from './public_inputs.js'; +import { type Barretenberg } from '@aztec/bb.js'; + +export class BarretenbergVerifier { + // These type assertions are used so that we don't + // have to initialize `api` and `acirComposer` in the constructor. + // These are initialized asynchronously in the `init` function, + // constructors cannot be asynchronous which is why we do this. + + private api!: Barretenberg; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private acirComposer: any; + + constructor(private options: BackendOptions = { threads: 1 }) {} + + /** @ignore */ + async instantiate(): Promise { + if (!this.api) { + if (typeof navigator !== 'undefined' && navigator.hardwareConcurrency) { + this.options.threads = navigator.hardwareConcurrency; + } else { + try { + const os = await import('os'); + this.options.threads = os.cpus().length; + } catch (e) { + console.log('Could not detect environment. Falling back to one thread.', e); + } + } + const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); + + // This is the number of CRS points necessary to verify a Barretenberg proof. + const NUM_CRS_POINTS_FOR_VERIFICATION: number = 0; + const [api, crs] = await Promise.all([Barretenberg.new(this.options), Crs.new(NUM_CRS_POINTS_FOR_VERIFICATION)]); + + await api.commonInitSlabAllocator(NUM_CRS_POINTS_FOR_VERIFICATION); + await api.srsInitSrs( + new RawBuffer([] /* crs.getG1Data() */), + NUM_CRS_POINTS_FOR_VERIFICATION, + new RawBuffer(crs.getG2Data()), + ); + + this.acirComposer = await api.acirNewAcirComposer(NUM_CRS_POINTS_FOR_VERIFICATION); + this.api = api; + } + } + + /** @description Verifies a proof */ + async verifyProof(proofData: ProofData, verificationKey: Uint8Array): Promise { + const { RawBuffer } = await import('@aztec/bb.js'); + + await this.instantiate(); + // The verifier can be used for a variety of ACIR programs so we should not assume that it + // is preloaded with the correct verification key. + await this.api.acirLoadVerificationKey(this.acirComposer, new RawBuffer(verificationKey)); + + const proof = reconstructProofWithPublicInputs(proofData); + return await this.api.acirVerifyProof(this.acirComposer, proof); + } + + async destroy(): Promise { + if (!this.api) { + return; + } + await this.api.destroy(); + } +} + +export function reconstructProofWithPublicInputs(proofData: ProofData): Uint8Array { + // Flatten publicInputs + const publicInputsConcatenated = flattenPublicInputsAsArray(proofData.publicInputs); + + // Concatenate publicInputs and proof + const proofWithPublicInputs = Uint8Array.from([...publicInputsConcatenated, ...proofData.proof]); + + return proofWithPublicInputs; +} diff --git a/tooling/noir_js_types/src/types.ts b/tooling/noir_js_types/src/types.ts index 3a62d79a807..456e5a57f40 100644 --- a/tooling/noir_js_types/src/types.ts +++ b/tooling/noir_js_types/src/types.ts @@ -29,7 +29,17 @@ export type Abi = { return_witnesses: number[]; }; -export interface Backend { +export interface VerifierBackend { + /** + * @description Verifies a proof */ + verifyProof(proofData: ProofData): Promise; + + /** + * @description Destroys the backend */ + destroy(): Promise; +} + +export interface Backend extends VerifierBackend { /** * @description Generates a proof */ generateProof(decompressedWitness: Uint8Array): Promise; @@ -49,14 +59,6 @@ export interface Backend { /** @description A Field containing the verification key hash */ vkHash: string; }>; - - /** - * @description Verifies a proof */ - verifyProof(proofData: ProofData): Promise; - - /** - * @description Destroys the backend */ - destroy(): Promise; } /** From 1d96937a8e94a91c0c17c97102498d067fca76c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 12 Apr 2024 09:04:08 -0300 Subject: [PATCH 172/416] feat: get last mock oracles params (#4789) Resolves https://github.com/noir-lang/noir/issues/4652 # Description This adds a new foreign call `get_mock_last_params` that can be used to configure mocked oracles. It returns the last parameters the mock was called with, or panics if it wasn't. Note that this doesn't play very nicely with the `times` feature, since e.g. if a mock is setup with `times(1)` it is then impossible to ever retrieve the last params, as the mock will be automatically cleared. There's a bunch of ways this could be handled, including keeping track of cleared mocks and llowing these queries over them. I didn't stress too much about this since we're only now starting to use the mocks, and I think it might be better to experiment more with them and get more feedback before worrying about having a nicer API. I also moved the mock oracle tests outside of `execution_success` into `noir_test_success`, and added a few simpler cases that I think also help illustrate how the mocks are supposed to be used (which I personally found quite useful). I considered adding tests for the scenarios in which the mocked oracle engine panics (e.g. if no mock matches the foreign call, or if requesting last params for a mock that was never called), but run into quite a lot of trouble getting this working and ultimately gave up. I did at least improve a misleading error message. --------- Co-authored-by: Tom French --- noir_stdlib/src/test.nr | 7 + .../execution_success/mock_oracle/Prover.toml | 2 - .../execution_success/mock_oracle/src/main.nr | 27 ---- .../mock_oracle/Nargo.toml | 3 +- .../noir_test_success/mock_oracle/Prover.toml | 0 .../noir_test_success/mock_oracle/src/main.nr | 130 ++++++++++++++++++ tooling/nargo/src/ops/foreign_calls.rs | 38 ++++- 7 files changed, 172 insertions(+), 35 deletions(-) delete mode 100644 test_programs/execution_success/mock_oracle/Prover.toml delete mode 100644 test_programs/execution_success/mock_oracle/src/main.nr rename test_programs/{execution_success => noir_test_success}/mock_oracle/Nargo.toml (57%) create mode 100644 test_programs/noir_test_success/mock_oracle/Prover.toml create mode 100644 test_programs/noir_test_success/mock_oracle/src/main.nr diff --git a/noir_stdlib/src/test.nr b/noir_stdlib/src/test.nr index e1c320215de..e6a7e03fefc 100644 --- a/noir_stdlib/src/test.nr +++ b/noir_stdlib/src/test.nr @@ -4,6 +4,9 @@ unconstrained fn create_mock_oracle(name: str) -> Field {} #[oracle(set_mock_params)] unconstrained fn set_mock_params_oracle

(id: Field, params: P) {} +#[oracle(get_mock_last_params)] +unconstrained fn get_mock_last_params_oracle

(id: Field) -> P {} + #[oracle(set_mock_returns)] unconstrained fn set_mock_returns_oracle(id: Field, returns: R) {} @@ -27,6 +30,10 @@ impl OracleMock { self } + unconstrained pub fn get_last_params

(self) -> P { + get_mock_last_params_oracle(self.id) + } + unconstrained pub fn returns(self, returns: R) -> Self { set_mock_returns_oracle(self.id, returns); self diff --git a/test_programs/execution_success/mock_oracle/Prover.toml b/test_programs/execution_success/mock_oracle/Prover.toml deleted file mode 100644 index 2b26a4ce471..00000000000 --- a/test_programs/execution_success/mock_oracle/Prover.toml +++ /dev/null @@ -1,2 +0,0 @@ -x = "10" - diff --git a/test_programs/execution_success/mock_oracle/src/main.nr b/test_programs/execution_success/mock_oracle/src/main.nr deleted file mode 100644 index 90fca7993cc..00000000000 --- a/test_programs/execution_success/mock_oracle/src/main.nr +++ /dev/null @@ -1,27 +0,0 @@ -use dep::std::test::OracleMock; - -struct Point { - x: Field, - y: Field, -} - -#[oracle(foo)] -unconstrained fn foo_oracle(_point: Point, _array: [Field; 4]) -> Field {} - -unconstrained fn main() { - let array = [1, 2, 3, 4]; - let another_array = [4, 3, 2, 1]; - let point = Point { x: 14, y: 27 }; - - OracleMock::mock("foo").returns(42).times(1); - let mock = OracleMock::mock("foo").returns(0); - assert_eq(42, foo_oracle(point, array)); - assert_eq(0, foo_oracle(point, array)); - mock.clear(); - - OracleMock::mock("foo").with_params((point, array)).returns(10); - OracleMock::mock("foo").with_params((point, another_array)).returns(20); - assert_eq(10, foo_oracle(point, array)); - assert_eq(20, foo_oracle(point, another_array)); -} - diff --git a/test_programs/execution_success/mock_oracle/Nargo.toml b/test_programs/noir_test_success/mock_oracle/Nargo.toml similarity index 57% rename from test_programs/execution_success/mock_oracle/Nargo.toml rename to test_programs/noir_test_success/mock_oracle/Nargo.toml index b2916487e8c..428e965899c 100644 --- a/test_programs/execution_success/mock_oracle/Nargo.toml +++ b/test_programs/noir_test_success/mock_oracle/Nargo.toml @@ -2,5 +2,6 @@ name = "mock_oracle" type = "bin" authors = [""] +compiler_version = ">=0.23.0" -[dependencies] +[dependencies] \ No newline at end of file diff --git a/test_programs/noir_test_success/mock_oracle/Prover.toml b/test_programs/noir_test_success/mock_oracle/Prover.toml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test_programs/noir_test_success/mock_oracle/src/main.nr b/test_programs/noir_test_success/mock_oracle/src/main.nr new file mode 100644 index 00000000000..d840ffaef66 --- /dev/null +++ b/test_programs/noir_test_success/mock_oracle/src/main.nr @@ -0,0 +1,130 @@ +use dep::std::test::OracleMock; + +struct Point { + x: Field, + y: Field, +} + +impl Eq for Point { + fn eq(self, other: Point) -> bool { + (self.x == other.x) & (self.y == other.y) + } +} + +#[oracle(void_field)] +unconstrained fn void_field_oracle() -> Field {} + +unconstrained fn void_field() -> Field { + void_field_oracle() +} + +#[oracle(field_field)] +unconstrained fn field_field_oracle(_x: Field) -> Field {} + +unconstrained fn field_field(x: Field) -> Field { + field_field_oracle(x) +} + +#[oracle(struct_field)] +unconstrained fn struct_field_oracle(_point: Point, _array: [Field; 4]) -> Field {} + +unconstrained fn struct_field(point: Point, array: [Field; 4]) -> Field { + struct_field_oracle(point, array) +} + +#[test(should_fail)] +fn test_mock_no_returns() { + OracleMock::mock("void_field"); + void_field(); // Some return value must be set +} + +#[test] +fn test_mock() { + OracleMock::mock("void_field").returns(10); + assert_eq(void_field(), 10); +} + +#[test] +fn test_multiple_mock() { + let first_mock = OracleMock::mock("void_field").returns(10); + OracleMock::mock("void_field").returns(42); + + // The mocks are searched for in creation order, so the first one prevents the second from being called. + assert_eq(void_field(), 10); + + first_mock.clear(); + assert_eq(void_field(), 42); +} + +#[test] +fn test_multiple_mock_times() { + OracleMock::mock("void_field").returns(10).times(2); + OracleMock::mock("void_field").returns(42); + + assert_eq(void_field(), 10); + assert_eq(void_field(), 10); + assert_eq(void_field(), 42); +} + +#[test] +fn test_mock_with_params() { + OracleMock::mock("field_field").with_params((5,)).returns(10); + assert_eq(field_field(5), 10); +} + +#[test] +fn test_multiple_mock_with_params() { + OracleMock::mock("field_field").with_params((5,)).returns(10); + OracleMock::mock("field_field").with_params((7,)).returns(14); + + assert_eq(field_field(5), 10); + assert_eq(field_field(7), 14); +} + +#[test] +fn test_mock_last_params() { + let mock = OracleMock::mock("field_field").returns(10); + assert_eq(field_field(5), 10); + + assert_eq(mock.get_last_params(), 5); +} + +#[test] +fn test_mock_last_params_many_calls() { + let mock = OracleMock::mock("field_field").returns(10); + assert_eq(field_field(5), 10); + assert_eq(field_field(7), 10); + + assert_eq(mock.get_last_params(), 7); +} + +#[test] +fn test_mock_struct_field() { + // Combination of simpler test cases + + let array = [1, 2, 3, 4]; + let another_array = [4, 3, 2, 1]; + let point = Point { x: 14, y: 27 }; + + OracleMock::mock("struct_field").returns(42).times(2); + let timeless_mock = OracleMock::mock("struct_field").returns(0); + + assert_eq(42, struct_field(point, array)); + assert_eq(42, struct_field(point, array)); + // The times(2) mock is now cleared + + assert_eq(0, struct_field(point, array)); + + let last_params: (Point, [Field; 4]) = timeless_mock.get_last_params(); + assert_eq(last_params.0, point); + assert_eq(last_params.1, array); + + // We clear the mock with no times() to allow other mocks to be callable + timeless_mock.clear(); + + OracleMock::mock("struct_field").with_params((point, array)).returns(10); + OracleMock::mock("struct_field").with_params((point, another_array)).returns(20); + assert_eq(10, struct_field(point, array)); + assert_eq(20, struct_field(point, another_array)); +} + diff --git a/tooling/nargo/src/ops/foreign_calls.rs b/tooling/nargo/src/ops/foreign_calls.rs index ea67f17af2a..bc91929e5e7 100644 --- a/tooling/nargo/src/ops/foreign_calls.rs +++ b/tooling/nargo/src/ops/foreign_calls.rs @@ -75,6 +75,7 @@ pub enum ForeignCall { AssertMessage, CreateMock, SetMockParams, + GetMockLastParams, SetMockReturns, SetMockTimes, ClearMock, @@ -93,6 +94,7 @@ impl ForeignCall { ForeignCall::AssertMessage => "assert_message", ForeignCall::CreateMock => "create_mock", ForeignCall::SetMockParams => "set_mock_params", + ForeignCall::GetMockLastParams => "get_mock_last_params", ForeignCall::SetMockReturns => "set_mock_returns", ForeignCall::SetMockTimes => "set_mock_times", ForeignCall::ClearMock => "clear_mock", @@ -105,6 +107,7 @@ impl ForeignCall { "assert_message" => Some(ForeignCall::AssertMessage), "create_mock" => Some(ForeignCall::CreateMock), "set_mock_params" => Some(ForeignCall::SetMockParams), + "get_mock_last_params" => Some(ForeignCall::GetMockLastParams), "set_mock_returns" => Some(ForeignCall::SetMockReturns), "set_mock_times" => Some(ForeignCall::SetMockTimes), "clear_mock" => Some(ForeignCall::ClearMock), @@ -122,6 +125,8 @@ struct MockedCall { name: String, /// Optionally match the parameters params: Option>, + /// The parameters with which the mock was last called + last_called_params: Option>, /// The result to return when this mock is called result: ForeignCallResult, /// How many times should this mock be called before it is removed @@ -134,6 +139,7 @@ impl MockedCall { id, name, params: None, + last_called_params: None, result: ForeignCallResult { values: vec![] }, times_left: None, } @@ -185,7 +191,11 @@ impl DefaultForeignCallExecutor { Ok((id, params)) } - fn find_mock_by_id(&mut self, id: usize) -> Option<&mut MockedCall> { + fn find_mock_by_id(&self, id: usize) -> Option<&MockedCall> { + self.mocked_responses.iter().find(|response| response.id == id) + } + + fn find_mock_by_id_mut(&mut self, id: usize) -> Option<&mut MockedCall> { self.mocked_responses.iter_mut().find(|response| response.id == id) } @@ -250,15 +260,27 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { } Some(ForeignCall::SetMockParams) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; - self.find_mock_by_id(id) + self.find_mock_by_id_mut(id) .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .params = Some(params.to_vec()); Ok(ForeignCallResult::default().into()) } + Some(ForeignCall::GetMockLastParams) => { + let (id, _) = Self::extract_mock_id(&foreign_call.inputs)?; + let mock = + self.find_mock_by_id(id).unwrap_or_else(|| panic!("Unknown mock id {}", id)); + + let last_called_params = mock + .last_called_params + .clone() + .unwrap_or_else(|| panic!("Mock {} was never called", mock.name)); + + Ok(last_called_params.into()) + } Some(ForeignCall::SetMockReturns) => { let (id, params) = Self::extract_mock_id(&foreign_call.inputs)?; - self.find_mock_by_id(id) + self.find_mock_by_id_mut(id) .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .result = ForeignCallResult { values: params.to_vec() }; @@ -269,7 +291,7 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { let times = params[0].unwrap_field().try_to_u64().expect("Invalid bit size of times"); - self.find_mock_by_id(id) + self.find_mock_by_id_mut(id) .unwrap_or_else(|| panic!("Unknown mock id {}", id)) .times_left = Some(times); @@ -292,6 +314,9 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { .mocked_responses .get_mut(response_position) .expect("Invalid position of mocked response"); + + mock.last_called_params = Some(foreign_call.inputs.clone()); + let result = mock.result.values.clone(); if let Some(times_left) = &mut mock.times_left { @@ -316,7 +341,10 @@ impl ForeignCallExecutor for DefaultForeignCallExecutor { Ok(parsed_response.into()) } - (None, None) => panic!("Unknown foreign call {}", foreign_call_name), + (None, None) => panic!( + "No mock for foreign call {}({:?})", + foreign_call_name, &foreign_call.inputs + ), } } } From 2ea92926956658ea99d8fb97734831eba00d3a4b Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 12 Apr 2024 12:30:26 -0400 Subject: [PATCH 173/416] fix: Fix panic when returning a zeroed unit value (#4797) # Description ## Problem\* Resolves #4791 ## Summary\* Zeroed used boolean values since we didn't have true unit values before. These were meant to be filtered out by the type but as shown in the issue, they'd still be used if they were directly returned. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs | 1 + compiler/noirc_frontend/src/monomorphization/ast.rs | 1 + compiler/noirc_frontend/src/monomorphization/mod.rs | 4 +--- compiler/noirc_frontend/src/monomorphization/printer.rs | 3 +++ test_programs/execution_success/unit_value/Nargo.toml | 7 +++++++ test_programs/execution_success/unit_value/src/main.nr | 7 +++++++ 6 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 test_programs/execution_success/unit_value/Nargo.toml create mode 100644 test_programs/execution_success/unit_value/src/main.nr diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 3fe52f7f0e5..fd1a029af26 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -241,6 +241,7 @@ impl<'a> FunctionContext<'a> { Ok(Tree::Branch(vec![string, field_count.into(), fields])) } + ast::Literal::Unit => Ok(Self::unit_value()), } } diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index 7d20c2bcfee..d9c33d8604e 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -92,6 +92,7 @@ pub enum Literal { Slice(ArrayLiteral), Integer(FieldElement, Type, Location), Bool(bool), + Unit, Str(String), FmtStr(String, u64, Box), } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 6aa0abce152..ed620c2da0d 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -1553,9 +1553,7 @@ impl<'interner> Monomorphizer<'interner> { ast::Expression::Literal(ast::Literal::Integer(0_u128.into(), typ, location)) } ast::Type::Bool => ast::Expression::Literal(ast::Literal::Bool(false)), - // There is no unit literal currently. Replace it with 'false' since it should be ignored - // anyway. - ast::Type::Unit => ast::Expression::Literal(ast::Literal::Bool(false)), + ast::Type::Unit => ast::Expression::Literal(ast::Literal::Unit), ast::Type::Array(length, element_type) => { let element = self.zeroed_value_of_type(element_type.as_ref(), location); ast::Expression::Literal(ast::Literal::Array(ast::ArrayLiteral { diff --git a/compiler/noirc_frontend/src/monomorphization/printer.rs b/compiler/noirc_frontend/src/monomorphization/printer.rs index c253bfe7930..ea8f079cc2f 100644 --- a/compiler/noirc_frontend/src/monomorphization/printer.rs +++ b/compiler/noirc_frontend/src/monomorphization/printer.rs @@ -110,6 +110,9 @@ impl AstPrinter { s.fmt(f)?; write!(f, "\"") } + super::ast::Literal::Unit => { + write!(f, "()") + } } } diff --git a/test_programs/execution_success/unit_value/Nargo.toml b/test_programs/execution_success/unit_value/Nargo.toml new file mode 100644 index 00000000000..f7e3697a7c1 --- /dev/null +++ b/test_programs/execution_success/unit_value/Nargo.toml @@ -0,0 +1,7 @@ +[package] +name = "short" +type = "bin" +authors = [""] +compiler_version = ">=0.23.0" + +[dependencies] \ No newline at end of file diff --git a/test_programs/execution_success/unit_value/src/main.nr b/test_programs/execution_success/unit_value/src/main.nr new file mode 100644 index 00000000000..f3844e03cf2 --- /dev/null +++ b/test_programs/execution_success/unit_value/src/main.nr @@ -0,0 +1,7 @@ +fn get_transaction() { + dep::std::unsafe::zeroed() +} + +fn main() { + get_transaction(); +} From 6bdc324a2506a3c8cceb0995066cfd8f3b67ac5a Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 12 Apr 2024 13:18:28 -0400 Subject: [PATCH 174/416] chore: Add Hir -> Ast conversion (#4788) # Description ## Problem\* Resolves https://github.com/noir-lang/noir/issues/4796 ## Summary\* Adds a way to go from the Hir back to the Ast. This conversion isn't exact and information is lost currently. Notably many spans are lost and some constructs like `Type::TypeVariable` have no equivalent in the Ast. This also isn't expected to be idempotent. Different nodes may be desugared, `let` statements may have explicit types added, etc. ## Additional Context This is unused currently and can't be triggered from the current compiler at all. I'm adding this PR now to split off required changes for metaprogramming into smaller chunks. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- compiler/noirc_frontend/src/ast/mod.rs | 4 + .../src/hir/comptime/hir_to_ast.rs | 350 ++++++++++++++++++ .../noirc_frontend/src/hir/comptime/mod.rs | 1 + compiler/noirc_frontend/src/hir/mod.rs | 1 + .../src/hir/resolution/resolver.rs | 1 + compiler/noirc_frontend/src/node_interner.rs | 4 + tooling/nargo_fmt/src/rewrite/typ.rs | 1 + 7 files changed, 362 insertions(+) create mode 100644 compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs create mode 100644 compiler/noirc_frontend/src/hir/comptime/mod.rs diff --git a/compiler/noirc_frontend/src/ast/mod.rs b/compiler/noirc_frontend/src/ast/mod.rs index 4547dc2a176..254ec4a7590 100644 --- a/compiler/noirc_frontend/src/ast/mod.rs +++ b/compiler/noirc_frontend/src/ast/mod.rs @@ -112,6 +112,9 @@ pub enum UnresolvedTypeData { /*env:*/ Box, ), + // The type of quoted code for metaprogramming + Code, + Unspecified, // This is for when the user declares a variable without specifying it's type Error, } @@ -200,6 +203,7 @@ impl std::fmt::Display for UnresolvedTypeData { } } MutableReference(element) => write!(f, "&mut {element}"), + Code => write!(f, "Code"), Unit => write!(f, "()"), Error => write!(f, "error"), Unspecified => write!(f, "unspecified"), diff --git a/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs new file mode 100644 index 00000000000..8ffcbce7d62 --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/hir_to_ast.rs @@ -0,0 +1,350 @@ +use iter_extended::vecmap; +use noirc_errors::{Span, Spanned}; + +use crate::ast::{ConstrainStatement, Expression, Statement, StatementKind}; +use crate::hir_def::expr::{HirArrayLiteral, HirExpression, HirIdent}; +use crate::hir_def::stmt::{HirLValue, HirPattern, HirStatement}; +use crate::macros_api::HirLiteral; +use crate::node_interner::{ExprId, NodeInterner, StmtId}; +use crate::{ + ArrayLiteral, AssignStatement, BlockExpression, CallExpression, CastExpression, ConstrainKind, + ConstructorExpression, ExpressionKind, ForLoopStatement, ForRange, Ident, IfExpression, + IndexExpression, InfixExpression, LValue, Lambda, LetStatement, Literal, + MemberAccessExpression, MethodCallExpression, Path, Pattern, PrefixExpression, Type, + UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, +}; + +// TODO: +// - Full path for idents & types +// - Assert/AssertEq information lost +// - The type name span is lost in constructor patterns & expressions +// - All type spans are lost +// - Type::TypeVariable has no equivalent in the Ast + +impl StmtId { + #[allow(unused)] + fn to_ast(self, interner: &NodeInterner) -> Statement { + let statement = interner.statement(&self); + let span = interner.statement_span(&self); + + let kind = match statement { + HirStatement::Let(let_stmt) => { + let pattern = let_stmt.pattern.into_ast(interner); + let r#type = interner.id_type(let_stmt.expression).to_ast(); + let expression = let_stmt.expression.to_ast(interner); + StatementKind::Let(LetStatement { + pattern, + r#type, + expression, + attributes: Vec::new(), + }) + } + HirStatement::Constrain(constrain) => { + let expr = constrain.0.to_ast(interner); + let message = constrain.2.map(|message| message.to_ast(interner)); + + // TODO: Find difference in usage between Assert & AssertEq + StatementKind::Constrain(ConstrainStatement(expr, message, ConstrainKind::Assert)) + } + HirStatement::Assign(assign) => StatementKind::Assign(AssignStatement { + lvalue: assign.lvalue.into_ast(interner), + expression: assign.expression.to_ast(interner), + }), + HirStatement::For(for_stmt) => StatementKind::For(ForLoopStatement { + identifier: for_stmt.identifier.to_ast(interner), + range: ForRange::Range( + for_stmt.start_range.to_ast(interner), + for_stmt.end_range.to_ast(interner), + ), + block: for_stmt.block.to_ast(interner), + span, + }), + HirStatement::Break => StatementKind::Break, + HirStatement::Continue => StatementKind::Continue, + HirStatement::Expression(expr) => StatementKind::Expression(expr.to_ast(interner)), + HirStatement::Semi(expr) => StatementKind::Semi(expr.to_ast(interner)), + HirStatement::Error => StatementKind::Error, + }; + + Statement { kind, span } + } +} + +impl ExprId { + #[allow(unused)] + fn to_ast(self, interner: &NodeInterner) -> Expression { + let expression = interner.expression(&self); + let span = interner.expr_span(&self); + + let kind = match expression { + HirExpression::Ident(ident) => { + let path = Path::from_ident(ident.to_ast(interner)); + ExpressionKind::Variable(path) + } + HirExpression::Literal(HirLiteral::Array(array)) => { + let array = array.into_ast(interner, span); + ExpressionKind::Literal(Literal::Array(array)) + } + HirExpression::Literal(HirLiteral::Slice(array)) => { + let array = array.into_ast(interner, span); + ExpressionKind::Literal(Literal::Slice(array)) + } + HirExpression::Literal(HirLiteral::Bool(value)) => { + ExpressionKind::Literal(Literal::Bool(value)) + } + HirExpression::Literal(HirLiteral::Integer(value, sign)) => { + ExpressionKind::Literal(Literal::Integer(value, sign)) + } + HirExpression::Literal(HirLiteral::Str(string)) => { + ExpressionKind::Literal(Literal::Str(string)) + } + HirExpression::Literal(HirLiteral::FmtStr(string, _exprs)) => { + // TODO: Is throwing away the exprs here valid? + ExpressionKind::Literal(Literal::FmtStr(string)) + } + HirExpression::Literal(HirLiteral::Unit) => ExpressionKind::Literal(Literal::Unit), + HirExpression::Block(expr) => { + let statements = vecmap(expr.statements, |statement| statement.to_ast(interner)); + ExpressionKind::Block(BlockExpression { statements }) + } + HirExpression::Prefix(prefix) => ExpressionKind::Prefix(Box::new(PrefixExpression { + operator: prefix.operator, + rhs: prefix.rhs.to_ast(interner), + })), + HirExpression::Infix(infix) => ExpressionKind::Infix(Box::new(InfixExpression { + lhs: infix.lhs.to_ast(interner), + operator: Spanned::from(infix.operator.location.span, infix.operator.kind), + rhs: infix.rhs.to_ast(interner), + })), + HirExpression::Index(index) => ExpressionKind::Index(Box::new(IndexExpression { + collection: index.collection.to_ast(interner), + index: index.index.to_ast(interner), + })), + HirExpression::Constructor(constructor) => { + let type_name = constructor.r#type.borrow().name.to_string(); + let type_name = Path::from_single(type_name, span); + let fields = + vecmap(constructor.fields, |(name, expr)| (name, expr.to_ast(interner))); + + ExpressionKind::Constructor(Box::new(ConstructorExpression { type_name, fields })) + } + HirExpression::MemberAccess(access) => { + ExpressionKind::MemberAccess(Box::new(MemberAccessExpression { + lhs: access.lhs.to_ast(interner), + rhs: access.rhs, + })) + } + HirExpression::Call(call) => { + let func = Box::new(call.func.to_ast(interner)); + let arguments = vecmap(call.arguments, |arg| arg.to_ast(interner)); + ExpressionKind::Call(Box::new(CallExpression { func, arguments })) + } + HirExpression::MethodCall(method_call) => { + ExpressionKind::MethodCall(Box::new(MethodCallExpression { + object: method_call.object.to_ast(interner), + method_name: method_call.method, + arguments: vecmap(method_call.arguments, |arg| arg.to_ast(interner)), + })) + } + HirExpression::Cast(cast) => { + let lhs = cast.lhs.to_ast(interner); + let r#type = cast.r#type.to_ast(); + ExpressionKind::Cast(Box::new(CastExpression { lhs, r#type })) + } + HirExpression::If(if_expr) => ExpressionKind::If(Box::new(IfExpression { + condition: if_expr.condition.to_ast(interner), + consequence: if_expr.consequence.to_ast(interner), + alternative: if_expr.alternative.map(|expr| expr.to_ast(interner)), + })), + HirExpression::Tuple(fields) => { + ExpressionKind::Tuple(vecmap(fields, |field| field.to_ast(interner))) + } + HirExpression::Lambda(lambda) => { + let parameters = vecmap(lambda.parameters, |(pattern, typ)| { + (pattern.into_ast(interner), typ.to_ast()) + }); + let return_type = lambda.return_type.to_ast(); + let body = lambda.body.to_ast(interner); + ExpressionKind::Lambda(Box::new(Lambda { parameters, return_type, body })) + } + HirExpression::Quote(block) => ExpressionKind::Quote(block), + HirExpression::Error => ExpressionKind::Error, + }; + + Expression::new(kind, span) + } +} + +impl HirPattern { + fn into_ast(self, interner: &NodeInterner) -> Pattern { + match self { + HirPattern::Identifier(ident) => Pattern::Identifier(ident.to_ast(interner)), + HirPattern::Mutable(pattern, location) => { + let pattern = Box::new(pattern.into_ast(interner)); + Pattern::Mutable(pattern, location.span, false) + } + HirPattern::Tuple(patterns, location) => { + let patterns = vecmap(patterns, |pattern| pattern.into_ast(interner)); + Pattern::Tuple(patterns, location.span) + } + HirPattern::Struct(typ, patterns, location) => { + let patterns = + vecmap(patterns, |(name, pattern)| (name, pattern.into_ast(interner))); + let name = match typ.follow_bindings() { + Type::Struct(struct_def, _) => { + let struct_def = struct_def.borrow(); + struct_def.name.0.contents.clone() + } + // This pass shouldn't error so if the type isn't a struct we just get a string + // representation of any other type and use that. We're relying on name + // resolution to fail later when this Ast is re-converted to Hir. + other => other.to_string(), + }; + // The name span is lost here + let path = Path::from_single(name, location.span); + Pattern::Struct(path, patterns, location.span) + } + } + } +} + +impl HirIdent { + fn to_ast(&self, interner: &NodeInterner) -> Ident { + let name = interner.definition_name(self.id).to_owned(); + Ident(Spanned::from(self.location.span, name)) + } +} + +impl Type { + fn to_ast(&self) -> UnresolvedType { + let typ = match self { + Type::FieldElement => UnresolvedTypeData::FieldElement, + Type::Array(length, element) => { + let length = length.to_type_expression(); + let element = Box::new(element.to_ast()); + UnresolvedTypeData::Array(length, element) + } + Type::Slice(element) => { + let element = Box::new(element.to_ast()); + UnresolvedTypeData::Slice(element) + } + Type::Integer(sign, bit_size) => UnresolvedTypeData::Integer(*sign, *bit_size), + Type::Bool => UnresolvedTypeData::Bool, + Type::String(length) => { + let length = length.to_type_expression(); + UnresolvedTypeData::String(Some(length)) + } + Type::FmtString(length, element) => { + let length = length.to_type_expression(); + let element = Box::new(element.to_ast()); + UnresolvedTypeData::FormatString(length, element) + } + Type::Unit => UnresolvedTypeData::Unit, + Type::Tuple(fields) => { + let fields = vecmap(fields, |field| field.to_ast()); + UnresolvedTypeData::Tuple(fields) + } + Type::Struct(def, generics) => { + let struct_def = def.borrow(); + let generics = vecmap(generics, |generic| generic.to_ast()); + let name = Path::from_ident(struct_def.name.clone()); + UnresolvedTypeData::Named(name, generics, false) + } + Type::Alias(type_def, generics) => { + // Keep the alias name instead of expanding this in case the + // alias' definition was changed + let type_def = type_def.borrow(); + let generics = vecmap(generics, |generic| generic.to_ast()); + let name = Path::from_ident(type_def.name.clone()); + UnresolvedTypeData::Named(name, generics, false) + } + Type::TypeVariable(_, _) => todo!("Convert Type::TypeVariable Hir -> Ast"), + Type::TraitAsType(_, name, generics) => { + let generics = vecmap(generics, |generic| generic.to_ast()); + let name = Path::from_single(name.as_ref().clone(), Span::default()); + UnresolvedTypeData::TraitAsType(name, generics) + } + Type::NamedGeneric(_, name) => { + let name = Path::from_single(name.as_ref().clone(), Span::default()); + UnresolvedTypeData::TraitAsType(name, Vec::new()) + } + Type::Function(args, ret, env) => { + let args = vecmap(args, |arg| arg.to_ast()); + let ret = Box::new(ret.to_ast()); + let env = Box::new(env.to_ast()); + UnresolvedTypeData::Function(args, ret, env) + } + Type::MutableReference(element) => { + let element = Box::new(element.to_ast()); + UnresolvedTypeData::MutableReference(element) + } + // Type::Forall is only for generic functions which don't store a type + // in their Ast so they don't need to call to_ast for their Forall type. + // Since there is no UnresolvedTypeData equivalent for Type::Forall, we use + // this to ignore this case since it shouldn't be needed anyway. + Type::Forall(_, typ) => return typ.to_ast(), + Type::Constant(_) => panic!("Type::Constant where a type was expected: {self:?}"), + Type::Code => UnresolvedTypeData::Code, + Type::Error => UnresolvedTypeData::Error, + }; + + UnresolvedType { typ, span: None } + } + + fn to_type_expression(&self) -> UnresolvedTypeExpression { + let span = Span::default(); + + match self.follow_bindings() { + Type::Constant(length) => UnresolvedTypeExpression::Constant(length, span), + Type::NamedGeneric(_, name) => { + let path = Path::from_single(name.as_ref().clone(), span); + UnresolvedTypeExpression::Variable(path) + } + // TODO: This should be turned into a proper error. + other => panic!("Cannot represent {other:?} as type expression"), + } + } +} + +impl HirLValue { + fn into_ast(self, interner: &NodeInterner) -> LValue { + match self { + HirLValue::Ident(ident, _) => LValue::Ident(ident.to_ast(interner)), + HirLValue::MemberAccess { object, field_name, field_index: _, typ: _, location } => { + let object = Box::new(object.into_ast(interner)); + LValue::MemberAccess { object, field_name, span: location.span } + } + HirLValue::Index { array, index, typ: _, location } => { + let array = Box::new(array.into_ast(interner)); + let index = index.to_ast(interner); + LValue::Index { array, index, span: location.span } + } + HirLValue::Dereference { lvalue, element_type: _, location } => { + let lvalue = Box::new(lvalue.into_ast(interner)); + LValue::Dereference(lvalue, location.span) + } + } + } +} + +impl HirArrayLiteral { + fn into_ast(self, interner: &NodeInterner, span: Span) -> ArrayLiteral { + match self { + HirArrayLiteral::Standard(elements) => { + ArrayLiteral::Standard(vecmap(elements, |element| element.to_ast(interner))) + } + HirArrayLiteral::Repeated { repeated_element, length } => { + let repeated_element = Box::new(repeated_element.to_ast(interner)); + let length = match length { + Type::Constant(length) => { + let literal = Literal::Integer((length as u128).into(), false); + let kind = ExpressionKind::Literal(literal); + Box::new(Expression::new(kind, span)) + } + other => panic!("Cannot convert non-constant type for repeated array literal from Hir -> Ast: {other:?}"), + }; + ArrayLiteral::Repeated { repeated_element, length } + } + } + } +} diff --git a/compiler/noirc_frontend/src/hir/comptime/mod.rs b/compiler/noirc_frontend/src/hir/comptime/mod.rs new file mode 100644 index 00000000000..91621c857cf --- /dev/null +++ b/compiler/noirc_frontend/src/hir/comptime/mod.rs @@ -0,0 +1 @@ +mod hir_to_ast; diff --git a/compiler/noirc_frontend/src/hir/mod.rs b/compiler/noirc_frontend/src/hir/mod.rs index 727a6596df1..b4804739cac 100644 --- a/compiler/noirc_frontend/src/hir/mod.rs +++ b/compiler/noirc_frontend/src/hir/mod.rs @@ -1,3 +1,4 @@ +pub mod comptime; pub mod def_collector; pub mod def_map; pub mod resolution; diff --git a/compiler/noirc_frontend/src/hir/resolution/resolver.rs b/compiler/noirc_frontend/src/hir/resolution/resolver.rs index 08b12069d76..9180201fe17 100644 --- a/compiler/noirc_frontend/src/hir/resolution/resolver.rs +++ b/compiler/noirc_frontend/src/hir/resolution/resolver.rs @@ -502,6 +502,7 @@ impl<'a> Resolver<'a> { let fields = self.resolve_type_inner(*fields, new_variables); Type::FmtString(Box::new(resolved_size), Box::new(fields)) } + Code => Type::Code, Unit => Type::Unit, Unspecified => Type::Error, Error => Type::Error, diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index ffd760d6d7f..84eb2d77315 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -917,6 +917,10 @@ impl NodeInterner { self.id_location(expr_id) } + pub fn statement_span(&self, stmt_id: &StmtId) -> Span { + self.id_location(stmt_id).span + } + pub fn get_struct(&self, id: StructId) -> Shared { self.structs[&id].clone() } diff --git a/tooling/nargo_fmt/src/rewrite/typ.rs b/tooling/nargo_fmt/src/rewrite/typ.rs index 922337cdb74..980d02ee5dc 100644 --- a/tooling/nargo_fmt/src/rewrite/typ.rs +++ b/tooling/nargo_fmt/src/rewrite/typ.rs @@ -64,6 +64,7 @@ pub(crate) fn rewrite(visitor: &FmtVisitor, _shape: Shape, typ: UnresolvedType) | UnresolvedTypeData::Expression(_) | UnresolvedTypeData::String(_) | UnresolvedTypeData::FormatString(_, _) + | UnresolvedTypeData::Code | UnresolvedTypeData::TraitAsType(_, _) => visitor.slice(typ.span.unwrap()).into(), UnresolvedTypeData::Error => unreachable!(), } From d71d5dea097b4a2e4799240c5b8d27df1581da34 Mon Sep 17 00:00:00 2001 From: jfecher Date: Fri, 12 Apr 2024 15:13:42 -0400 Subject: [PATCH 175/416] chore: Rename 'global' to 'function' in the monomorphization pass (#4774) # Description ## Problem\* Resolves ## Summary\* The original intention of this was to store functions and globals but after globals were implemented, only functions ended up being used in the `globals` hashmap. We also probably don't want to monomorphize globals into different copies going forward so I renamed the map to `functions` and clarified references to it to avoid confusion. ## Additional Context ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- .../src/monomorphization/mod.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index ed620c2da0d..4e779244d30 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -52,14 +52,13 @@ struct LambdaContext { /// This struct holds the FIFO queue of functions to monomorphize, which is added to /// whenever a new (function, type) combination is encountered. struct Monomorphizer<'interner> { - /// Globals are keyed by their unique ID and expected type so that we can monomorphize - /// a new version of the global for each type. Note that 'global' here means 'globally - /// visible' and thus includes both functions and global variables. + /// Functions are keyed by their unique ID and expected type so that we can monomorphize + /// a new version of the function for each type. /// /// Using nested HashMaps here lets us avoid cloning HirTypes when calling .get() - globals: HashMap>, + functions: HashMap>, - /// Unlike globals, locals are only keyed by their unique ID because they are never + /// Unlike functions, locals are only keyed by their unique ID because they are never /// duplicated during monomorphization. Doing so would allow them to be used polymorphically /// but would also cause them to be re-evaluated which is a performance trap that would /// confuse users. @@ -165,7 +164,7 @@ pub fn monomorphize_debug( impl<'interner> Monomorphizer<'interner> { fn new(interner: &'interner mut NodeInterner, debug_type_tracker: DebugTypeTracker) -> Self { Monomorphizer { - globals: HashMap::new(), + functions: HashMap::new(), locals: HashMap::new(), queue: VecDeque::new(), finished_functions: BTreeMap::new(), @@ -203,7 +202,7 @@ impl<'interner> Monomorphizer<'interner> { trait_method: Option, ) -> Definition { let typ = typ.follow_bindings(); - match self.globals.get(&id).and_then(|inner_map| inner_map.get(&typ)) { + match self.functions.get(&id).and_then(|inner_map| inner_map.get(&typ)) { Some(id) => Definition::Function(*id), None => { // Function has not been monomorphized yet @@ -251,8 +250,8 @@ impl<'interner> Monomorphizer<'interner> { } /// Prerequisite: typ = typ.follow_bindings() - fn define_global(&mut self, id: node_interner::FuncId, typ: HirType, new_id: FuncId) { - self.globals.entry(id).or_default().insert(typ, new_id); + fn define_function(&mut self, id: node_interner::FuncId, typ: HirType, new_id: FuncId) { + self.functions.entry(id).or_default().insert(typ, new_id); } fn compile_main( @@ -786,7 +785,7 @@ impl<'interner> Monomorphizer<'interner> { }) } - /// A local (ie non-global) ident only + /// A local (ie non-function) ident only fn local_ident( &mut self, ident: &HirIdent, @@ -1280,7 +1279,7 @@ impl<'interner> Monomorphizer<'interner> { trait_method: Option, ) -> FuncId { let new_id = self.next_function_id(); - self.define_global(id, function_type.clone(), new_id); + self.define_function(id, function_type.clone(), new_id); let bindings = self.interner.get_instantiation_bindings(expr_id); let bindings = self.follow_bindings(bindings); From e412e6e30910472b9d5f9000370ce5138ad39ce7 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Fri, 12 Apr 2024 20:15:40 +0100 Subject: [PATCH 176/416] feat: narrow ABI encoding errors down to target problem argument/field (#4798) # Description ## Problem\* Resolves #3560 Resolves #4778 ## Summary\* This PR updates `InputValue.matches_abi` to `InputValue.find_type_mismatch` which returns an error related to the type which is failing to be abi encoded. ## Additional Context This should help @alexghr with debugging #4778 ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- tooling/noirc_abi/src/errors.rs | 9 +- tooling/noirc_abi/src/input_parser/mod.rs | 157 +++++++++++++++--- tooling/noirc_abi/src/lib.rs | 10 +- .../test/browser/errors.test.ts | 2 +- .../noirc_abi_wasm/test/node/errors.test.ts | 2 +- 5 files changed, 139 insertions(+), 41 deletions(-) diff --git a/tooling/noirc_abi/src/errors.rs b/tooling/noirc_abi/src/errors.rs index 687fecfcc1d..4209a9e218b 100644 --- a/tooling/noirc_abi/src/errors.rs +++ b/tooling/noirc_abi/src/errors.rs @@ -1,4 +1,7 @@ -use crate::{input_parser::InputValue, AbiParameter, AbiType}; +use crate::{ + input_parser::{InputTypecheckingError, InputValue}, + AbiType, +}; use acvm::acir::native_types::Witness; use thiserror::Error; @@ -38,8 +41,8 @@ impl From for InputParserError { pub enum AbiError { #[error("Received parameters not expected by ABI: {0:?}")] UnexpectedParams(Vec), - #[error("The parameter {} is expected to be a {:?} but found incompatible value {value:?}", .param.name, .param.typ)] - TypeMismatch { param: AbiParameter, value: InputValue }, + #[error("The value passed for parameter `{}` does not match the specified type:\n{0}", .0.path())] + TypeMismatch(#[from] InputTypecheckingError), #[error("ABI expects the parameter `{0}`, but this was not found")] MissingParam(String), #[error( diff --git a/tooling/noirc_abi/src/input_parser/mod.rs b/tooling/noirc_abi/src/input_parser/mod.rs index f66e069d487..024cb06007e 100644 --- a/tooling/noirc_abi/src/input_parser/mod.rs +++ b/tooling/noirc_abi/src/input_parser/mod.rs @@ -1,6 +1,7 @@ use num_bigint::{BigInt, BigUint}; use num_traits::{Num, Zero}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; +use thiserror::Error; use acvm::FieldElement; use serde::Serialize; @@ -22,63 +23,165 @@ pub enum InputValue { Struct(BTreeMap), } +#[derive(Debug, Error)] +pub enum InputTypecheckingError { + #[error("Value {value:?} does not fall within range of allowable values for a {typ:?}")] + OutsideOfValidRange { path: String, typ: AbiType, value: InputValue }, + #[error("Type {typ:?} is expected to have length {expected_length} but value {value:?} has length {actual_length}")] + LengthMismatch { + path: String, + typ: AbiType, + value: InputValue, + expected_length: usize, + actual_length: usize, + }, + #[error("Could not find value for required field `{expected_field}`. Found values for fields {found_fields:?}")] + MissingField { path: String, expected_field: String, found_fields: Vec }, + #[error("Additional unexpected field was provided for type {typ:?}. Found field named `{extra_field}`")] + UnexpectedField { path: String, typ: AbiType, extra_field: String }, + #[error("Type {typ:?} and value {value:?} do not match")] + IncompatibleTypes { path: String, typ: AbiType, value: InputValue }, +} + +impl InputTypecheckingError { + pub(crate) fn path(&self) -> &str { + match self { + InputTypecheckingError::OutsideOfValidRange { path, .. } + | InputTypecheckingError::LengthMismatch { path, .. } + | InputTypecheckingError::MissingField { path, .. } + | InputTypecheckingError::UnexpectedField { path, .. } + | InputTypecheckingError::IncompatibleTypes { path, .. } => path, + } + } +} + impl InputValue { /// Checks whether the ABI type matches the InputValue type - /// and also their arity - pub fn matches_abi(&self, abi_param: &AbiType) -> bool { + pub(crate) fn find_type_mismatch( + &self, + abi_param: &AbiType, + path: String, + ) -> Result<(), InputTypecheckingError> { match (self, abi_param) { - (InputValue::Field(_), AbiType::Field) => true, + (InputValue::Field(_), AbiType::Field) => Ok(()), (InputValue::Field(field_element), AbiType::Integer { width, .. }) => { - field_element.num_bits() <= *width + if field_element.num_bits() <= *width { + Ok(()) + } else { + Err(InputTypecheckingError::OutsideOfValidRange { + path, + typ: abi_param.clone(), + value: self.clone(), + }) + } } (InputValue::Field(field_element), AbiType::Boolean) => { - field_element.is_one() || field_element.is_zero() + if field_element.is_one() || field_element.is_zero() { + Ok(()) + } else { + Err(InputTypecheckingError::OutsideOfValidRange { + path, + typ: abi_param.clone(), + value: self.clone(), + }) + } } (InputValue::Vec(array_elements), AbiType::Array { length, typ, .. }) => { if array_elements.len() != *length as usize { - return false; + return Err(InputTypecheckingError::LengthMismatch { + path, + typ: abi_param.clone(), + value: self.clone(), + expected_length: *length as usize, + actual_length: array_elements.len(), + }); } // Check that all of the array's elements' values match the ABI as well. - array_elements.iter().all(|input_value| input_value.matches_abi(typ)) + for (i, element) in array_elements.iter().enumerate() { + let mut path = path.clone(); + path.push_str(&format!("[{i}]")); + + element.find_type_mismatch(typ, path)?; + } + Ok(()) } (InputValue::String(string), AbiType::String { length }) => { - string.len() == *length as usize + if string.len() == *length as usize { + Ok(()) + } else { + Err(InputTypecheckingError::LengthMismatch { + path, + typ: abi_param.clone(), + value: self.clone(), + actual_length: string.len(), + expected_length: *length as usize, + }) + } } (InputValue::Struct(map), AbiType::Struct { fields, .. }) => { - if map.len() != fields.len() { - return false; + for (field_name, field_type) in fields { + if let Some(value) = map.get(field_name) { + let mut path = path.clone(); + path.push_str(&format!(".{field_name}")); + value.find_type_mismatch(field_type, path)?; + } else { + return Err(InputTypecheckingError::MissingField { + path, + expected_field: field_name.to_string(), + found_fields: map.keys().cloned().collect(), + }); + } } - let field_types = BTreeMap::from_iter(fields.iter().cloned()); + if map.len() > fields.len() { + let expected_fields: HashSet = + fields.iter().map(|(field, _)| field.to_string()).collect(); + let extra_field = map.keys().cloned().find(|key| !expected_fields.contains(key)).expect("`map` is larger than the expected type's `fields` so it must contain an unexpected field"); + return Err(InputTypecheckingError::UnexpectedField { + path, + typ: abi_param.clone(), + extra_field: extra_field.to_string(), + }); + } - // Check that all of the struct's fields' values match the ABI as well. - map.iter().all(|(field_name, field_value)| { - if let Some(field_type) = field_types.get(field_name) { - field_value.matches_abi(field_type) - } else { - false - } - }) + Ok(()) } (InputValue::Vec(vec_elements), AbiType::Tuple { fields }) => { if vec_elements.len() != fields.len() { - return false; + return Err(InputTypecheckingError::LengthMismatch { + path, + typ: abi_param.clone(), + value: self.clone(), + actual_length: vec_elements.len(), + expected_length: fields.len(), + }); } - - vec_elements - .iter() - .zip(fields) - .all(|(input_value, abi_param)| input_value.matches_abi(abi_param)) + // Check that all of the array's elements' values match the ABI as well. + for (i, (element, expected_typ)) in vec_elements.iter().zip(fields).enumerate() { + let mut path = path.clone(); + path.push_str(&format!(".{i}")); + element.find_type_mismatch(expected_typ, path)?; + } + Ok(()) } // All other InputValue-AbiType combinations are fundamentally incompatible. - _ => false, + _ => Err(InputTypecheckingError::IncompatibleTypes { + path, + typ: abi_param.clone(), + value: self.clone(), + }), } } + + /// Checks whether the ABI type matches the InputValue type. + pub fn matches_abi(&self, abi_param: &AbiType) -> bool { + self.find_type_mismatch(abi_param, String::new()).is_ok() + } } /// The different formats that are supported when parsing diff --git a/tooling/noirc_abi/src/lib.rs b/tooling/noirc_abi/src/lib.rs index 89a60b0ed26..6ad13500bdd 100644 --- a/tooling/noirc_abi/src/lib.rs +++ b/tooling/noirc_abi/src/lib.rs @@ -307,15 +307,7 @@ impl Abi { .ok_or_else(|| AbiError::MissingParam(param_name.clone()))? .clone(); - if !value.matches_abi(&expected_type) { - let param = self - .parameters - .iter() - .find(|param| param.name == param_name) - .unwrap() - .clone(); - return Err(AbiError::TypeMismatch { param, value }); - } + value.find_type_mismatch(&expected_type, param_name.clone())?; Self::encode_value(value, &expected_type).map(|v| (param_name, v)) }) diff --git a/tooling/noirc_abi_wasm/test/browser/errors.test.ts b/tooling/noirc_abi_wasm/test/browser/errors.test.ts index 429a2d446a3..0f75ff64a3e 100644 --- a/tooling/noirc_abi_wasm/test/browser/errors.test.ts +++ b/tooling/noirc_abi_wasm/test/browser/errors.test.ts @@ -9,7 +9,7 @@ it('errors when an integer input overflows', async () => { const { abi, inputs } = await import('../shared/uint_overflow'); expect(() => abiEncode(abi, inputs)).to.throw( - 'The parameter foo is expected to be a Integer { sign: Unsigned, width: 32 } but found incompatible value Field(2³⁸)', + 'The value passed for parameter `foo` does not match the specified type:\nValue Field(2³⁸) does not fall within range of allowable values for a Integer { sign: Unsigned, width: 32 }', ); }); diff --git a/tooling/noirc_abi_wasm/test/node/errors.test.ts b/tooling/noirc_abi_wasm/test/node/errors.test.ts index 0d007e64803..fba451b4a8c 100644 --- a/tooling/noirc_abi_wasm/test/node/errors.test.ts +++ b/tooling/noirc_abi_wasm/test/node/errors.test.ts @@ -5,7 +5,7 @@ it('errors when an integer input overflows', async () => { const { abi, inputs } = await import('../shared/uint_overflow'); expect(() => abiEncode(abi, inputs)).to.throw( - 'The parameter foo is expected to be a Integer { sign: Unsigned, width: 32 } but found incompatible value Field(2³⁸)', + 'The value passed for parameter `foo` does not match the specified type:\nValue Field(2³⁸) does not fall within range of allowable values for a Integer { sign: Unsigned, width: 32 }', ); }); From 21f9f6fd26bdc9edcfe7ce3ff97d427303b7c87d Mon Sep 17 00:00:00 2001 From: Martin Verzilli Date: Fri, 12 Apr 2024 22:41:03 +0200 Subject: [PATCH 177/416] chore(debugger): Docs (#4145) # Description Adds documentation for the Noir debugger. ## Problem Part of #3015. ## Summary Adds quickstart, how to's and reference pages to Noir's docsite covering both the VS Code and REPL debuggers. ## Documentation Check one: - [ ] No documentation needed. - [x] Documentation included in this PR. - [ ] **[Exceptional Case]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings. --- cspell.json | 1 + docs/docs/getting_started/tooling/index.mdx | 38 -- docs/docs/how_to/debugger/_category_.json | 6 + .../debugger/debugging_with_the_repl.md | 164 ++++++++ .../how_to/debugger/debugging_with_vs_code.md | 68 ++++ docs/docs/how_to/merkle-proof.mdx | 1 + docs/docs/noir/concepts/functions.md | 4 +- .../debugger}/_category_.json | 4 +- .../debugger/debugger_known_limitations.md | 59 +++ docs/docs/reference/debugger/debugger_repl.md | 360 ++++++++++++++++++ .../reference/debugger/debugger_vscode.md | 82 ++++ docs/docs/tooling/debugger.md | 27 ++ .../tooling/language_server.md | 0 .../{getting_started => }/tooling/testing.md | 0 docs/sidebars.js | 5 + docs/static/img/debugger/1-started.png | Bin 0 -> 786947 bytes docs/static/img/debugger/2-icon.png | Bin 0 -> 27014 bytes docs/static/img/debugger/3-debug-pane.png | Bin 0 -> 83927 bytes .../img/debugger/4-debugger-buttons.png | Bin 0 -> 12345 bytes docs/static/img/debugger/5-assert.png | Bin 0 -> 512053 bytes docs/static/img/debugger/6-hover.png | Bin 0 -> 292419 bytes docs/static/img/debugger/7-break.png | Bin 0 -> 627627 bytes docs/static/img/debugger/debugger-intro.gif | Bin 0 -> 1260930 bytes .../img/debugger/ref1-create-launch.png | Bin 0 -> 113387 bytes 24 files changed, 777 insertions(+), 42 deletions(-) delete mode 100644 docs/docs/getting_started/tooling/index.mdx create mode 100644 docs/docs/how_to/debugger/_category_.json create mode 100644 docs/docs/how_to/debugger/debugging_with_the_repl.md create mode 100644 docs/docs/how_to/debugger/debugging_with_vs_code.md rename docs/docs/{getting_started/tooling => reference/debugger}/_category_.json (53%) create mode 100644 docs/docs/reference/debugger/debugger_known_limitations.md create mode 100644 docs/docs/reference/debugger/debugger_repl.md create mode 100644 docs/docs/reference/debugger/debugger_vscode.md create mode 100644 docs/docs/tooling/debugger.md rename docs/docs/{getting_started => }/tooling/language_server.md (100%) rename docs/docs/{getting_started => }/tooling/testing.md (100%) create mode 100644 docs/static/img/debugger/1-started.png create mode 100644 docs/static/img/debugger/2-icon.png create mode 100644 docs/static/img/debugger/3-debug-pane.png create mode 100644 docs/static/img/debugger/4-debugger-buttons.png create mode 100644 docs/static/img/debugger/5-assert.png create mode 100644 docs/static/img/debugger/6-hover.png create mode 100644 docs/static/img/debugger/7-break.png create mode 100644 docs/static/img/debugger/debugger-intro.gif create mode 100644 docs/static/img/debugger/ref1-create-launch.png diff --git a/cspell.json b/cspell.json index 16de9757fb8..bf3040265c2 100644 --- a/cspell.json +++ b/cspell.json @@ -113,6 +113,7 @@ "Maddiaa", "mathbb", "memfs", + "memset", "merkle", "metas", "minreq", diff --git a/docs/docs/getting_started/tooling/index.mdx b/docs/docs/getting_started/tooling/index.mdx deleted file mode 100644 index ac480f3c9f5..00000000000 --- a/docs/docs/getting_started/tooling/index.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Tooling -Description: This section provides information about the various tools and utilities available for Noir development. It covers the Noir playground, IDE tools, Codespaces, and community projects. -Keywords: [Noir, Development, Playground, IDE Tools, Language Service Provider, VS Code Extension, Codespaces, noir-starter, Community Projects, Awesome Noir Repository, Developer Tooling] ---- - -Noir is meant to be easy to develop with. For that reason, a number of utilities have been put together to ease the development process as much as feasible in the zero-knowledge world. - -## Playground - -The Noir playground is an easy way to test small ideas, share snippets, and integrate in other websites. You can access it at [play.noir-lang.org](https://play.noir-lang.org). - -## IDE tools - -When you install Nargo, you're also installing a Language Service Provider (LSP), which can be used by IDEs to provide syntax highlighting, codelens, warnings, and more. - -The easiest way to use these tools is by installing the [Noir VS Code extension](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). - -## Codespaces - -Some Noir repos have leveraged Codespaces in order to ease the development process. You can visit the [noir-starter](https://github.com/noir-lang/noir-starter) for an example. - - - -## GitHub Actions - -You can use `noirup` with GitHub Actions for CI/CD and automated testing. It is as simple as -installing `noirup` and running tests in your GitHub Action `yml` file. - -See the -[config file in the Noir repo](https://github.com/TomAFrench/noir-hashes/blob/master/.github/workflows/noir.yml) for an example usage. - -## Community projects - -As an open-source project, Noir has received many contributions over time. Some of them are related with developer tooling, and you can see some of them in [Awesome Noir repository](https://github.com/noir-lang/awesome-noir#dev-tools) diff --git a/docs/docs/how_to/debugger/_category_.json b/docs/docs/how_to/debugger/_category_.json new file mode 100644 index 00000000000..cc2cbb1c253 --- /dev/null +++ b/docs/docs/how_to/debugger/_category_.json @@ -0,0 +1,6 @@ +{ + "label": "Debugging", + "position": 5, + "collapsible": true, + "collapsed": true +} diff --git a/docs/docs/how_to/debugger/debugging_with_the_repl.md b/docs/docs/how_to/debugger/debugging_with_the_repl.md new file mode 100644 index 00000000000..09e5bae68ad --- /dev/null +++ b/docs/docs/how_to/debugger/debugging_with_the_repl.md @@ -0,0 +1,164 @@ +--- +title: Using the REPL Debugger +description: + Step by step guide on how to debug your Noir circuits with the REPL Debugger. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +#### Pre-requisites + +In order to use the REPL debugger, first you need to install recent enough versions of Nargo and vscode-noir. + +## Debugging a simple circuit + +Let's debug a simple circuit: + +```rust +fn main(x : Field, y : pub Field) { + assert(x != y); +} +``` + +To start the REPL debugger, using a terminal, go to a Noir circuit's home directory. Then: + +`$ nargo debug` + +You should be seeing this in your terminal: + +``` +[main] Starting debugger +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:9 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> +``` + +The debugger displays the current Noir code location, and it is now waiting for us to drive it. + +Let's first take a look at the available commands. For that we'll use the `help` command. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +Some commands operate only for unconstrained functions, such as `memory` and `memset`. If you try to use them while execution is paused at an ACIR opcode, the debugger will simply inform you that you are not executing unconstrained code: + +``` +> memory +Unconstrained VM memory not available +> +``` + +Before continuing, we can take a look at the initial witness map: + +``` +> witness +_0 = 1 +_1 = 2 +> +``` + +Cool, since `x==1`, `y==2`, and we want to check that `x != y`, our circuit should succeed. At this point we could intervene and use the witness setter command to change one of the witnesses. Let's set `y=3`, then back to 2, so we don't affect the expected result: + +``` +> witness +_0 = 1 +_1 = 2 +> witness 1 3 +_1 = 3 +> witness +_0 = 1 +_1 = 3 +> witness 1 2 +_1 = 2 +> witness +_0 = 1 +_1 = 2 +> +``` + +Now we can inspect the current state of local variables. For that we use the `vars` command. + +``` +> vars +> +``` + +We currently have no vars in context, since we are at the entry point of the program. Let's use `next` to execute until the next point in the program. + +``` +> vars +> next +At ~/noir-examples/recursion/circuits/main/src/main.nr:1:20 + 1 -> fn main(x : Field, y : pub Field) { + 2 assert(x != y); + 3 } +> vars +x:Field = 0x01 +``` + +As a result of stepping, the variable `x`, whose initial value comes from the witness map, is now in context and returned by `vars`. + +``` +> next + 1 fn main(x : Field, y : pub Field) { + 2 -> assert(x != y); + 3 } +> vars +y:Field = 0x02 +x:Field = 0x01 +``` + +Stepping again we can finally see both variables and their values. And now we can see that the next assertion should succeed. + +Let's continue to the end: + +``` +> continue +(Continuing execution...) +Finished execution +> q +[main] Circuit witness successfully solved +``` + +Upon quitting the debugger after a solved circuit, the resulting circuit witness gets saved, equivalent to what would happen if we had run the same circuit with `nargo execute`. + +We just went through the basics of debugging using Noir REPL debugger. For a comprehensive reference, check out [the reference page](../../reference/debugger/debugger_repl.md). diff --git a/docs/docs/how_to/debugger/debugging_with_vs_code.md b/docs/docs/how_to/debugger/debugging_with_vs_code.md new file mode 100644 index 00000000000..a5858c1a5eb --- /dev/null +++ b/docs/docs/how_to/debugger/debugging_with_vs_code.md @@ -0,0 +1,68 @@ +--- +title: Using the VS Code Debugger +description: + Step by step guide on how to debug your Noir circuits with the VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +This guide will show you how to use VS Code with the vscode-noir extension to debug a Noir project. + +#### Pre-requisites + +- Nargo +- vscode-noir +- A Noir project with a `Nargo.toml`, `Prover.toml` and at least one Noir (`.nr`) containing an entry point function (typically `main`). + +## Running the debugger + +The easiest way to start debugging is to open the file you want to debug, and press `F5`. This will cause the debugger to launch, using your `Prover.toml` file as input. + +You should see something like this: + +![Debugger launched](@site/static/img/debugger/1-started.png) + +Let's inspect the state of the program. For that, we open VS Code's _Debug pane_. Look for this icon: + +![Debug pane icon](@site/static/img/debugger/2-icon.png) + +You will now see two categories of variables: Locals and Witness Map. + +![Debug pane expanded](@site/static/img/debugger/3-debug-pane.png) + +1. **Locals**: variables of your program. At this point in execution this section is empty, but as we step through the code it will get populated by `x`, `result`, `digest`, etc. + +2. **Witness map**: these are initially populated from your project's `Prover.toml` file. In this example, they will be used to populate `x` and `result` at the beginning of the `main` function. + +Most of the time you will probably be focusing mostly on locals, as they represent the high level state of your program. + +You might be interested in inspecting the witness map in case you are trying to solve a really low level issue in the compiler or runtime itself, so this concerns mostly advanced or niche users. + +Let's step through the program, by using the debugger buttons or their corresponding keyboard shortcuts. + +![Debugger buttons](@site/static/img/debugger/4-debugger-buttons.png) + +Now we can see in the variables pane that there's values for `digest`, `result` and `x`. + +![Inspecting locals](@site/static/img/debugger/5-assert.png) + +We can also inspect the values of variables by directly hovering on them on the code. + +![Hover locals](@site/static/img/debugger/6-hover.png) + +Let's set a break point at the `keccak256` function, so we can continue execution up to the point when it's first invoked without having to go one step at a time. + +We just need to click the to the right of the line number 18. Once the breakpoint appears, we can click the `continue` button or use its corresponding keyboard shortcut (`F5` by default). + +![Breakpoint](@site/static/img/debugger/7-break.png) + +Now we are debugging the `keccak256` function, notice the _Call Stack pane_ at the lower right. This lets us inspect the current call stack of our process. + +That covers most of the current debugger functionalities. Check out [the reference](../../reference/debugger/debugger_vscode.md) for more details on how to configure the debugger. \ No newline at end of file diff --git a/docs/docs/how_to/merkle-proof.mdx b/docs/docs/how_to/merkle-proof.mdx index 003c7019a93..16c425bed76 100644 --- a/docs/docs/how_to/merkle-proof.mdx +++ b/docs/docs/how_to/merkle-proof.mdx @@ -5,6 +5,7 @@ description: merkle tree with a specified root, at a given index. keywords: [merkle proof, merkle membership proof, Noir, rust, hash function, Pedersen, sha256, merkle tree] +sidebar_position: 4 --- Let's walk through an example of a merkle membership proof in Noir that proves that a given leaf is diff --git a/docs/docs/noir/concepts/functions.md b/docs/docs/noir/concepts/functions.md index 2c9bc33fdfc..f656cdfd97a 100644 --- a/docs/docs/noir/concepts/functions.md +++ b/docs/docs/noir/concepts/functions.md @@ -62,7 +62,7 @@ fn main(x : [Field]) // can't compile, has variable size fn main(....// i think you got it by now ``` -Keep in mind [tests](../../getting_started/tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: +Keep in mind [tests](../../tooling/testing.md) don't differentiate between `main` and any other function. The following snippet passes tests, but won't compile or prove: ```rust fn main(x : [Field]) { @@ -190,7 +190,7 @@ Supported attributes include: - **deprecated**: mark the function as _deprecated_. Calling the function will generate a warning: `warning: use of deprecated function` - **field**: Used to enable conditional compilation of code depending on the field size. See below for more details - **oracle**: mark the function as _oracle_; meaning it is an external unconstrained function, implemented in noir_js. See [Unconstrained](./unconstrained.md) and [NoirJS](../../reference/NoirJS/noir_js/index.md) for more details. -- **test**: mark the function as unit tests. See [Tests](../../getting_started/tooling/testing.md) for more details +- **test**: mark the function as unit tests. See [Tests](../../tooling/testing.md) for more details ### Field Attribute diff --git a/docs/docs/getting_started/tooling/_category_.json b/docs/docs/reference/debugger/_category_.json similarity index 53% rename from docs/docs/getting_started/tooling/_category_.json rename to docs/docs/reference/debugger/_category_.json index 55804c03a71..27869205ad3 100644 --- a/docs/docs/getting_started/tooling/_category_.json +++ b/docs/docs/reference/debugger/_category_.json @@ -1,6 +1,6 @@ { - "position": 2, - "label": "Tooling", + "label": "Debugger", + "position": 1, "collapsible": true, "collapsed": true } diff --git a/docs/docs/reference/debugger/debugger_known_limitations.md b/docs/docs/reference/debugger/debugger_known_limitations.md new file mode 100644 index 00000000000..936d416ac4b --- /dev/null +++ b/docs/docs/reference/debugger/debugger_known_limitations.md @@ -0,0 +1,59 @@ +--- +title: Known limitations +description: + An overview of known limitations of the current version of the Noir debugger +keywords: + [ + Nargo, + Noir Debugger, + VS Code, + ] +sidebar_position: 2 +--- + +# Debugger Known Limitations + +There are currently some limits to what the debugger can observe. + +## Mutable references + +The debugger is currently blind to any state mutated via a mutable reference. For example, in: + +``` +let mut x = 1; +let y = &mut x; +*y = 2; +``` + +The update on `x` will not be observed by the debugger. That means, when running `vars` from the debugger REPL, or inspecting the _local variables_ pane in the VS Code debugger, `x` will appear with value 1 despite having executed `*y = 2;`. + +## Variables of type function or mutable references are opaque + +When inspecting variables, any variable of type `Function` or `MutableReference` will render its value as `<>` or `<>`. + +## Debugger instrumentation affects resulting ACIR + +In order to make the state of local variables observable, the debugger compiles Noir circuits interleaving foreign calls that track any mutations to them. While this works (except in the cases described above) and doesn't introduce any behavior changes, it does as a side effect produce bigger bytecode. In particular, when running the command `opcodes` on the REPL debugger, you will notice Unconstrained VM blocks that look like this: + +``` +... +5 BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [], q_c: 2 }), Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(2))], q_c: 0 })] + | outputs=[] + 5.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 5.1 | Mov { destination: RegisterIndex(3), source: RegisterIndex(1) } + 5.2 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 5.3 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 5.4 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 5.5 | Mov { destination: RegisterIndex(3), source: RegisterIndex(3) } + 5.6 | Call { location: 8 } + 5.7 | Stop + 5.8 | ForeignCall { function: "__debug_var_assign", destinations: [], inputs: [RegisterIndex(RegisterIndex(2)), RegisterIndex(RegisterIndex(3))] } +... +``` + +If you are interested in debugging/inspecting compiled ACIR without these synthetic changes, you can invoke the REPL debugger with the `--skip-instrumentation` flag or launch the VS Code debugger with the `skipConfiguration` property set to true in its launch configuration. You can find more details about those in the [Debugger REPL reference](debugger_repl.md) and the [VS Code Debugger reference](debugger_vscode.md). + +:::note +Skipping debugger instrumentation means you won't be able to inspect values of local variables. +::: + diff --git a/docs/docs/reference/debugger/debugger_repl.md b/docs/docs/reference/debugger/debugger_repl.md new file mode 100644 index 00000000000..46e2011304e --- /dev/null +++ b/docs/docs/reference/debugger/debugger_repl.md @@ -0,0 +1,360 @@ +--- +title: REPL Debugger +description: + Noir Debugger REPL options and commands. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + REPL, + ] +sidebar_position: 1 +--- + +## Running the REPL debugger + +`nargo debug [OPTIONS] [WITNESS_NAME]` + +Runs the Noir REPL debugger. If a `WITNESS_NAME` is provided the debugger writes the resulting execution witness to a `WITNESS_NAME` file. + +### Options + +| Option | Description | +| --------------------- | ------------------------------------------------------------ | +| `-p, --prover-name ` | The name of the toml file which contains the inputs for the prover [default: Prover]| +| `--package ` | The name of the package to debug | +| `--print-acir` | Display the ACIR for compiled circuit | +| `--deny-warnings` | Treat all warnings as errors | +| `--silence-warnings` | Suppress warnings | +| `-h, --help` | Print help | + +None of these options are required. + +:::note +Since the debugger starts by compiling the target package, all Noir compiler options are also available. Check out the [compiler reference](../nargo_commands.md#nargo-compile) to learn more about the compiler options. +::: + +## REPL commands + +Once the debugger is running, it accepts the following commands. + +#### `help` (h) + +Displays the menu of available commands. + +``` +> help +Available commands: + + opcodes display ACIR opcodes + into step into to the next opcode + next step until a new source location is reached + out step until a new source location is reached + and the current stack frame is finished + break LOCATION:OpcodeLocation add a breakpoint at an opcode location + over step until a new source location is reached + without diving into function calls + restart restart the debugging session + delete LOCATION:OpcodeLocation delete breakpoint at an opcode location + witness show witness map + witness index:u32 display a single witness from the witness map + witness index:u32 value:String update a witness with the given value + memset index:usize value:String update a memory cell with the given + value + continue continue execution until the end of the + program + vars show variable values available at this point + in execution + stacktrace display the current stack trace + memory show memory (valid when executing unconstrained code) value + step step to the next ACIR opcode + +Other commands: + + help Show this help message + quit Quit repl + +``` + +### Stepping through programs + +#### `next` (n) + +Step until the next Noir source code location. While other commands, such as [`into`](#into-i) and [`step`](#step-s), allow for finer grained control of the program's execution at the opcode level, `next` is source code centric. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `next` here would cause the debugger to jump to the definition of `deep_entry_point` (if available). + +If you want to step over `deep_entry_point` and go straight to line 8, use [the `over` command](#over) instead. + +#### `over` + +Step until the next source code location, without diving into function calls. For example: + +``` +3 ... +4 fn main(x: u32) { +5 assert(entry_point(x) == 2); +6 swap_entry_point(x, x + 1); +7 -> assert(deep_entry_point(x) == 4); +8 multiple_values_entry_point(x); +9 } +``` + + +Using `over` here would cause the debugger to execute until line 8 (`multiple_values_entry_point(x);`). + +If you want to step into `deep_entry_point` instead, use [the `next` command](#next-n). + +#### `out` + +Step until the end of the current function call. For example: + +``` + 3 ... + 4 fn main(x: u32) { + 5 assert(entry_point(x) == 2); + 6 swap_entry_point(x, x + 1); + 7 -> assert(deep_entry_point(x) == 4); + 8 multiple_values_entry_point(x); + 9 } + 10 + 11 unconstrained fn returns_multiple_values(x: u32) -> (u32, u32, u32, u32) { + 12 ... + ... + 55 + 56 unconstrained fn deep_entry_point(x: u32) -> u32 { + 57 -> level_1(x + 1) + 58 } + +``` + +Running `out` here will resume execution until line 8. + +#### `step` (s) + +Skips to the next ACIR code. A compiled Noir program is a sequence of ACIR opcodes. However, an unconstrained VM opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `step` command at this point would result in the debugger stopping at ACIR opcode 2, `EXPR`, skipping unconstrained computation steps. + +Use [the `into` command](#into-i) instead if you want to follow unconstrained computation step by step. + +#### `into` (i) + +Steps into the next opcode. A compiled Noir program is a sequence of ACIR opcodes. However, a BRILLIG opcode denotes the start of an unconstrained code block, to be executed by the unconstrained VM. For example (redacted for brevity): + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +The `->` here shows the debugger paused at an ACIR opcode: `BRILLIG`, at index 1, which denotes an unconstrained code block is about to start. + +Using the `into` command at this point would result in the debugger stopping at opcode 1.0, `Mov ...`, allowing the debugger user to follow unconstrained computation step by step. + +Use [the `step` command](#step-s) instead if you want to skip to the next ACIR code directly. + +#### `continue` (c) + +Continues execution until the next breakpoint, or the end of the program. + +#### `restart` (res) + +Interrupts execution, and restarts a new debugging session from scratch. + +#### `opcodes` (o) + +Display the program's ACIR opcode sequence. For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +### Breakpoints + +#### `break [Opcode]` (or shorthand `b [Opcode]`) + +Sets a breakpoint on the specified opcode index. To get a list of the program opcode numbers, see [the `opcode` command](#opcodes-o). For example: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +In this example, issuing a `break 1.2` command adds break on opcode 1.2, as denoted by the `*` character: + +``` +0 BLACKBOX::RANGE [(_0, num_bits: 32)] [ ] +1 -> BRILLIG inputs=[Single(Expression { mul_terms: [], linear_combinations: [(1, Witness(0))], q_c: 0 })] outputs=[Simple(Witness(1))] + 1.0 | Mov { destination: RegisterIndex(2), source: RegisterIndex(0) } + 1.1 | Const { destination: RegisterIndex(0), value: Value { inner: 0 } } + 1.2 | * Const { destination: RegisterIndex(1), value: Value { inner: 0 } } + 1.3 | Mov { destination: RegisterIndex(2), source: RegisterIndex(2) } + 1.4 | Call { location: 7 } + ... + 1.43 | Return +2 EXPR [ (1, _1) -2 ] +``` + +Running [the `continue` command](#continue-c) at this point would cause the debugger to execute the program until opcode 1.2. + +#### `delete [Opcode]` (or shorthand `d [Opcode]`) + +Deletes a breakpoint at an opcode location. Usage is analogous to [the `break` command](#). + +### Variable inspection + +#### vars + +Show variable values available at this point in execution. + +:::note +The ability to inspect variable values from the debugger depends on compilation to be run in a special debug instrumentation mode. This instrumentation weaves variable tracing code with the original source code. + +So variable value inspection comes at the expense of making the resulting ACIR bytecode bigger and harder to understand and optimize. + +If you find this compromise unacceptable, you can run the debugger with the flag `--skip-debug-instrumentation`. This will compile your circuit without any additional debug information, so the resulting ACIR bytecode will be identical to the one produced by standard Noir compilation. However, if you opt for this, the `vars` command will not be available while debugging. +::: + + +### Stacktrace + +#### `stacktrace` + +Displays the current stack trace. + + +### Witness map + +#### `witness` (w) + +Show witness map. For example: + +``` +_0 = 0 +_1 = 2 +_2 = 1 +``` + +#### `witness [Witness Index]` + +Display a single witness from the witness map. For example: + +``` +> witness 1 +_1 = 2 +``` + +#### `witness [Witness Index] [New value]` + +Overwrite the given index with a new value. For example: + +``` +> witness 1 3 +_1 = 3 +``` + + +### Unconstrained VM memory + +#### `memory` + +Show unconstrained VM memory state. For example: + +``` +> memory +At opcode 1.13: Store { destination_pointer: RegisterIndex(0), source: RegisterIndex(3) } +... +> registers +0 = 0 +1 = 10 +2 = 0 +3 = 1 +4 = 1 +5 = 2³² +6 = 1 +> into +At opcode 1.14: Const { destination: RegisterIndex(5), value: Value { inner: 1 } } +... +> memory +0 = 1 +> +``` + +In the example above: we start with clean memory, then step through a `Store` opcode which stores the value of register 3 (1) into the memory address stored in register 0 (0). Thus now `memory` shows memory address 0 contains value 1. + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: + +#### `memset [Memory address] [New value]` + +Update a memory cell with the given value. For example: + +``` +> memory +0 = 1 +> memset 0 2 +> memory +0 = 2 +> memset 1 4 +> memory +0 = 2 +1 = 4 +> +``` + +:::note +This command is only functional while the debugger is executing unconstrained code. +::: \ No newline at end of file diff --git a/docs/docs/reference/debugger/debugger_vscode.md b/docs/docs/reference/debugger/debugger_vscode.md new file mode 100644 index 00000000000..c027332b3b0 --- /dev/null +++ b/docs/docs/reference/debugger/debugger_vscode.md @@ -0,0 +1,82 @@ +--- +title: VS Code Debugger +description: + VS Code Debugger configuration and features. +keywords: + [ + Nargo, + Noir CLI, + Noir Debugger, + VS Code, + IDE, + ] +sidebar_position: 0 +--- + +# VS Code Noir Debugger Reference + +The Noir debugger enabled by the vscode-noir extension ships with default settings such that the most common scenario should run without any additional configuration steps. + +These defaults can nevertheless be overridden by defining a launch configuration file. This page provides a reference for the properties you can override via a launch configuration file, as well as documenting the Nargo `dap` command, which is a dependency of the VS Code Noir debugger. + + +## Creating and editing launch configuration files + +To create a launch configuration file from VS Code, open the _debug pane_, and click on _create a launch.json file_. + +![Creating a launch configuration file](@site/static/img/debugger/ref1-create-launch.png) + +A `launch.json` file will be created, populated with basic defaults. + +### Noir Debugger launch.json properties + +#### projectFolder + +_String, optional._ + +Absolute path to the Nargo project to debug. By default, it is dynamically determined by looking for the nearest `Nargo.toml` file to the active file at the moment of launching the debugger. + +#### proverName + +_String, optional._ + +Name of the prover input to use. Defaults to `Prover`, which looks for a file named `Prover.toml` at the `projectFolder`. + +#### generateAcir + +_Boolean, optional._ + +If true, generate ACIR opcodes instead of unconstrained opcodes which will be closer to release binaries but less convenient for debugging. Defaults to `false`. + +#### skipInstrumentation + +_Boolean, optional._ + +Skips variables debugging instrumentation of code, making debugging less convenient but the resulting binary smaller and closer to production. Defaults to `false`. + +:::note +Skipping instrumentation causes the debugger to be unable to inspect local variables. +::: + +## `nargo dap [OPTIONS]` + +When run without any option flags, it starts the Nargo Debug Adapter Protocol server, which acts as the debugging backend for the VS Code Noir Debugger. + +All option flags are related to preflight checks. The Debug Adapter Protocol specifies how errors are to be informed from a running DAP server, but it doesn't specify mechanisms to communicate server initialization errors between the DAP server and its client IDE. + +Thus `nargo dap` ships with a _preflight check_ mode. If flag `--preflight-check` and the rest of the `--preflight-*` flags are provided, Nargo will run the same initialization routine except it will not start the DAP server. + +`vscode-noir` will then run `nargo dap` in preflight check mode first before a debugging session starts. If the preflight check ends in error, vscode-noir will present stderr and stdout output from this process through its own Output pane in VS Code. This makes it possible for users to diagnose what pieces of configuration might be wrong or missing in case of initialization errors. + +If the preflight check succeeds, `vscode-noir` proceeds to start the DAP server normally but running `nargo dap` without any additional flags. + +### Options + +| Option | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| `--preflight-check` | If present, dap runs in preflight check mode. | +| `--preflight-project-folder ` | Absolute path to the project to debug for preflight check. | +| `--preflight-prover-name ` | Name of prover file to use for preflight check | +| `--preflight-generate-acir` | Optional. If present, compile in ACIR mode while running preflight check. | +| `--preflight-skip-instrumentation` | Optional. If present, compile without introducing debug instrumentation while running preflight check. | +| `-h, --help` | Print help. | diff --git a/docs/docs/tooling/debugger.md b/docs/docs/tooling/debugger.md new file mode 100644 index 00000000000..184c436068f --- /dev/null +++ b/docs/docs/tooling/debugger.md @@ -0,0 +1,27 @@ +--- +title: Debugger +description: Learn about the Noir Debugger, in its REPL or VS Code versions. +keywords: [Nargo, VSCode, Visual Studio Code, REPL, Debugger] +sidebar_position: 2 +--- + +# Noir Debugger + +There are currently two ways of debugging Noir programs: + +1. From VS Code, via the [vscode-noir](https://github.com/noir-lang/vscode-noir) extension. You can install it via the [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=noir-lang.vscode-noir). +2. Via the REPL debugger, which ships with Nargo. + +In order to use either version of the debugger, you will need to install recent enough versions of Noir, [Nargo](../getting_started/installation) and vscode-noir: + +- Noir 0.xx +- Nargo 0.xx +- vscode-noir 0.xx + +:::info +At the moment, the debugger supports debugging binary projects, but not contracts. +::: + +We cover the VS Code Noir debugger more in depth in [its VS Code debugger how-to guide](../how_to/debugger/debugging_with_vs_code.md) and [the reference](../reference/debugger/debugger_vscode.md). + +The REPL debugger is discussed at length in [the REPL debugger how-to guide](../how_to/debugger/debugging_with_the_repl.md) and [the reference](../reference/debugger/debugger_repl.md). diff --git a/docs/docs/getting_started/tooling/language_server.md b/docs/docs/tooling/language_server.md similarity index 100% rename from docs/docs/getting_started/tooling/language_server.md rename to docs/docs/tooling/language_server.md diff --git a/docs/docs/getting_started/tooling/testing.md b/docs/docs/tooling/testing.md similarity index 100% rename from docs/docs/getting_started/tooling/testing.md rename to docs/docs/tooling/testing.md diff --git a/docs/sidebars.js b/docs/sidebars.js index f1e79ba9ebc..cf7e852fed5 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -65,6 +65,11 @@ export default { label: 'Reference', items: [{ type: 'autogenerated', dirName: 'reference' }], }, + { + type: 'category', + label: 'Tooling', + items: [{ type: 'autogenerated', dirName: 'tooling' }], + }, { type: 'html', value: '

', diff --git a/docs/static/img/debugger/1-started.png b/docs/static/img/debugger/1-started.png new file mode 100644 index 0000000000000000000000000000000000000000..6f764d4e60125cdbc08d2a7fd06de694324ba10f GIT binary patch literal 786947 zcmaHR1z1#D_dei^!hnQ?AV`;VNOwrb&>-C+-3<<)2$IrL14t=IcMC{&*U%j!-S8j0 z*ZY0<-rx71=Naa3&e{8%z4lt~dRK&im82hno`8^$kRHm)NU9@BQp%#n~}LgEr0sH;s8yc=lPq{yHlPaRAqPeoD{f0cnc z+#)4|67)!12D_Lskcurc@4e9r=@)n;v6QA_k|hsO2#S@W#Kedmpl6zRKGp_U!o1*x zRdD`opY5j0ow8G%Q?p$pQB5=Sw^qfJNSi1n^t}Tg2yls|TOT5$=8;FDuvt2n289g{ zlE0bky>j>V$J&?Lsa!QH@b|wpR1E3qSwcd3OPSK#xAd9H=Lr!NNrnst(rb}SVy4IF zbTvdm)Py_3FJ+&l)v_qf8pAE_$^O z@^|r|xseghYSeVeNxXfr=fh?`Uf36V;qK|TG}aMQ);o&d!fpSV$LL^jm^>Ovln}dM<;(pWtkY$9+qs(G9v)4qv^VU;oQzWO#0l%W zoZGn14j+d$n0cd(h@0QkKNm?#i+jqV%P1w>IaQHS%Bf)dAgG@W^}*(whmxWgugD1p zAzVhGo_tGP;)z7x^>9R<=_r_z^Mpy8qVZ}vTUCx%o**f}CN>8aZRbaYg4FlRrteb+ zpA(?YCXNN*BJ+l))TquCScIL?7a&6`(x`CEm95p>(eAzPEJDK1yZ6)xNdZDr42)>T z_dp@TMiLhVya&o7KcJ?X`q1oD{HWj_5Q=JpEFcP;e1I8{wE)O_pt!(Pjr#2kF%*3k zhy}sJ2uLu(tABut4Ok@Skc5m<=y#%)zOkX;U%@;V6$2CEp|?xOMj?F^Wz2X8#!&}p zh(3V6-gvx$;eu0p-&Nu?!|xTL*FE0=6*jb-@6*NXf>=4N7`)gKZxWhi9cWV?thXjF zI#q*&F?qf}UL^jhuMKJn>Hs6r1gpN-%wSS+!NbK52$i78l*}8;6IUnt0@C>KTFez{ zm-RUhXH@z*fuh*RVEiksauNPVePWiGT3H{4@HetHq+A3pxNFITLT^GTOv~8_2p?mh z;|9upXX?N+>ShaOf0)>lSeGc)>&-;)HI%D`-}t(uWD;)&WW(-;Rf#zhy88pusKbFb zg})zV{T+HsiKB@gvfbl7-aV9W4|v{RzDa1^Ub5Y9JmalLY9cj!C-P1t65b-xp^S}( zCIxsbjtY_Qq>07G#CeHYg&G)ymicHXXhSNDJO_IMrzfPMt*1@8t;)&C>B6bs34Qg< z$$GVWRjFG)Zad*pMwK=xCh>DSHJTOYCTf11OZri~0xfZjP?w__yC-MOC!dh3pewO7 zN_(1tkL%q<(M7TLvG8dBY*8?qsu4YtMyDFR8gI6Pys>07^i@Hpx*6j)%>s47tjVvu zIg%=+Dj*d(Ee1{g92Z3^8KPX9f}N~hjeafRJRe!lHvyT1;6yx3^LwkrYWX3l zh~}8aY$A0M_-yu!(rgT@{CZF-9m*GUcS<(WJ|t}mYOz(lwkx9P`e>YFt7AH4pZyx& ztOE9>Yimrf)zk5?vVS}AbZokXzwaXISc2F*87cXgo0;3*rq7nIGOp5nr_-&`&3fn4 zuEC7%hB;YAGe>tCzql*(B-#9;}01a?w%TUKk;#RIWms9l;gj{U zelmR${Wg|k7XO-;wK=t<&QCwtbENa!4qhSi`0`ZFAoJu4^a;Uyo9ad-1_F0gR`*9o znq#&-ClC2r!=k_Q_Is?F@6sH+{`thRmQ9OYw?PHw2YU-k748;}7RDD=_Br;A@v-)i zz9zZSJgc}cy}EZ@b#Yo+u0x{9W&OPQnURL5H~BL*!D7mvR#Bc&^m#Odn3$+Af~!}M zA(WPY9Bl0Gpecd_-cjo3oJSLHC*NkEGd>nZFGJHM8plyWGbHe1y7CRul9X+0l^Kzd zHPek1A6?4Z%G-Yh-xz*n)mxva!OmmCWpZB9=U82xSKaDp`oV`_SNnPOq_JbmlDE7< z{0dV|rMjur^ssXeUVKSHYG_BBbO+go+g?m#t&)yM+GusSQhW;iO8xTR=-dU|6W4z* zt_mr;$TKOD$T`VR$3IWmiPno3{BC)`cvhr#rFN1Xmiz8AsqA$;L8@j#gmR>^2o;#% zGiaLAg~N%X_ASA8l#J&iG|tj%dGufxUi9vlAIF$-D8q*oL-JQ1$of}$yP2N^ud4JY zuhOrgI@zs#TDR-=O(r`a>(@ycj16cMRLLU5&WP`Hfv?o&<88Ti>6!C={xV zsAh4Uv)?Y9NciSoMd2kO-{^KTeFY)A^2>V`#fpu8Walzh4VONOydo3enX;X(*tDjo z`so$zs-iQrZlbh*oNnXCaJxCP*4CFw6G7u$WmJV)IXkmea{MY^)yiPAxpKiae8y@9 zy{c)Z#HOQyb(h>*Y6@(g<-DWFD0D|&37UEW3`zv3!hh3J{T_f*JzPx&%1^CeK{!~+MIhB z`|i6`9($Ez`Li0?X_A>)U)lu)-4E9Ctwowmq37z#jWgIh ztx0rogxc!Nw92z5?3J_H!0Smo&bRRA858&kpFaLpt1sl;w>q7)eYCB}Lt_6N=2AWN zGr86NVQNHbcvXrWmph+(h06e3cz$`vdB5^>m(g9@-ELOjq3Y6LrJ)hl-Y_g|H$Qk? zepy6jCX(n;eS)*5vS`}gVXj=BrhaXA;+;CG;`MF5?sjeDXD-$Q-g~UKofpf8MmL-X zWu@!217Cw}g8MNqG2I)qk4%1M|6Fa8X-Sv!-}5uS*g7&y^PeLLCwl6`>~+1XxUYL$ zB@LTuvL;4<>U|)5eck4<_}~~zT4Y$n%_sEc$j9W|a(4ZI)xz*)<9$EhBj)QF9tz#I zwAVmz=NsJU#>GAyq|Oo~a4ix!%cs5ET#QdQSZpsAMD|?z4!YVwH|Q|NlZ;bRu=KAX6j^a zZs+{U-sL^Ms6OHZnuCmvGZGR3&D|GSR+aiEV*W`hb!``IMFoCSds`M`GkX(r77ts8 zyLpfVJ@^raw&pIzoova*OtnBT`@8&f&v3GS5qNKcg&|jZF-qYN}>OW7i zbN+K#hy}9VU14QsVPpMkZp2W*yR-aYD-UxU9Z4%&gv=1n5a!_EW)J@rzKiyAQws@rVA8U&QhK-Kn65CyGc&qDZomV(K2q zJE;$h%rqKq{nbXQcq+}xnCqB2DFdM7nUoC5z%+Vt`uEomJRp;@k7`>)z;Q^PKiCrr zN6Vy#YKwNO#+NpnrWH<=TiqVkx$n)^aK`MIameQQVA||8u5w{ZN(?$33l=%Qy}9k+LKWPNcjbDUQFW*>Ia9$Nqaq4vSuTBEC}DPoJJ-f2)+* z+ImfcjUl6`7!K$g!^5xZ52^`$mj1dsw2F9`H$Co~r)i1D0nEOl(9v>PpQn@yvyBK#oL{Z3V~O~mUrYFFUie(y znBT(M;OSR4ee+~j*X{G^SJ!=4frp$!SMQ8&e2i{h@J%`^w({MY4UA0rPw+KW;d9*5 zbe;3;#&Fulo?k}J8RCd64_w;AAgUg>$NFUbaFOX?5qSFbp|ro<#+4X_h<~C#@uzbh zeJm$G|B=)28w~e#e@_1!y5aHposuyQiy$@gIruHi?{;vWY6p(9E;MBA_0;Zi`kk8c z8k{U*x1nO-vg0)UI%1k6W!_ZhcIl4=X*G!)9jXOh67{dMHu+=p!O80VeQx*9Ti01d z{LjvN;ACsL8C-_n;f7Z??Qp}LreTr3`~0`~AKE{E{o3<#V0&XDs=uFQs>#3U++Ead z_jQczj9a3=0L`P2q?!ikSX$+#55eZx!xPK$>l8oun!}zC1AQ@_KUWs9%b}>sE8{9Nh&e$wzbr4_oYtxs}LsL%Hf?ko{k`zcAy*MNed;6z*T=Rb-iq4mRBCU zAT{mIKso5Y^r5SyqdyK;*{j0E{gm)UtKC$+XR^&?@mL*ePR8Zn&*17Hx>~Uf)kS z7tkkj*&8Y3Kmi9L+y}q$oNiZ6_p>{*V0@eT%cvlt+G=nHkt#N0?tyb0APt zBmRGC0%8$?V&o*4UHctPauI?~@ia^x3lNd6AK^1$RRK}1iHX}uvq??zhMX=-qoqMq z^n)X^ZS=TdiR9x{bSWOO`w9-_^+&@T=Mzl^Z2Mb};{L8^|D_TbLXe}bh9xu*HZI1s z%rNC2I%!mmilL{zz`|yM4l38OyKp#;cBA;e=KzNTuKdzdoVxd&df5-das$>^_B+z! zL`7c!kpo*j$cl6JIM(4Q$I=3@128n^Jp-)_wbx3YJ)eqH7zvI4^>$3Dv!yPVQ})2` zUH`AQ`?Z-kDE($>)=KcS2hSr@n@$Cle1)C)Wd{pm1%BHRLmI*oxqMyxAOCdYt#L?v zsiiRY+1z=#^aarFKUmKzl8Dl!*oJCC>e%cCDgAn(}jp&#|D%6%?fURrbPZ<8b~ON(w`$TgQ7(b9e|aa zVQw$2$qy{UwpTK?*G5^owElYYWYOk7HI9lt`h)l|? z-fuDH`Jgb+gN-3i^&p}bWsC%!soy;31l_uh4mR0mCH|K}OURSsfx44nE+5M)cB69B z{f6Fy#4Use>~B^-!4ZNkbK`aIpTS+GU4 z{l~L-`fv!VJ%>l;ywh2Y6rY?7lMnq?Cv`T6B&8hySW-@!!%8`Qx>sxB^+~NQ-1*Qv zmyi9gMg0?wAsKYon87Q%_97Z-o@+!1U`qg?Q1qbcszaT9YTY)RKt5K& zj+E|$qbRYe@pPeHo;z%Qn(7*iko|nQ;+u|Y25av>1HZ-pX0vY&!~($`R5eBg$zjn=;6GftrJJyekL6v`I6Lr z$^{PWN2BIrAP4=qFbD%V_J3MJx7LxuqRzV=hmI_ zKOjU6YUuk3`13AN4IHo-#L*aj*;Ujw3-8ctN)L}(LzliIXO~9 z?3Co&bm<^PjC9LYB0qr9rT0s>)9{gP09QZcdg zPT8P8@IxILVj~7|HK;P_ulUG<6Jyn+5 ziOyPY0|0WxCj=G zU%8O}4@=7yYZkDbs525RRQ&A8Cd0Z8Th4$!m%?ZKQ$1T1n+fpp@VCZiOK4q9+jaBv z^AJc^cDWNjpb?4N@jrP5p%O}#bR?T~$Xu!Sso(zPy&rf@Ts#{EB9!!m{X-eH|ECoo zqaMjMyUo|3)v2sH2a#@_R^CCO;a90T`<1g-bkXg~s#Qhs!5|u z)kY}l@Q8wqrtkv|!_2@VCndi>mIsai9NBlhdQLkxC*;(br-emoli+)aDJMwE+OcQE z`FDGVe4z$wX)$K?eY$xUz8xMM5S4VO#tO6(1uv8?VE$WOAb!|+MMW_oxEDi-m66xs6$QP6jKiJt2HOIb_Ja^EFYc+p^yjSB>Q2gk+L@>< zE^4p@hf|x(RH9Q=G`_QMaFSoX1Kh{Ny?f-gpRdQ@pfZ24pEC+(a^pNOF zj<+vM1oQV+$eWZTx;p7I{g8FdW;2)s+X3p;T&IYkBO@bk?@ucWFzd!frLh_-#knYR zu=4O{o7gwAI`!)|kdXQJvkFXOYvDVG^+posf5owaZ}=CU`sGSTq%Gf&46ALpE_=}2 z2LOE|UmKdBF>rw&%1#)hyX!c!I2k&e!W zEdo9FLE#738;_CUr^C$%H{@$4gbKRgB55$=4zBLZoOkt974h=bPSZ2vo<6}*e{ZyA zojqeiO5=eRW+T^`WFOX3a=66dDK1VMsoZ&1VnJ1AJZ=Ss&n}k%h^@KLR-)~KCPtA zFE6N+FIKg%?wjxC?94Txg0S(9q!kwG-Q#E(cz5e|ACWHJC}3Xo&gd!&{(=>=N2tMp9Om8BJ9AqsjPR|O6L@!sPf^bJhWUD1_~Lp`kT=(ClPOhGrLq9k4ayiPs;G8R_(1g1P%d z_gYi_1IA#pb3JLI0d|q3VHt z5NH1)P20q*&ubzjxMn}n5bnRctY|(iAtGb8)}LyeRO5YWFjbOOII8$V7&Z18WXFmh z{-d||=zsu^NqdmNdF{Dn_?QHi5XBadMhJj{%(tGxlP#@tO3Y)I@D}~CWjmIeq(1;i zmyCPn?_{d16qDxJ#hTv4MJ+J>#j-Wqi>CTWNTp`ydtOj4EzIQYoVTyjVPxRJMiYu= z94cs5akGNI;Ta+s$ID4epjn^C>Pl#Q9NQ$gsW8i*W#~DPb3dAFevqo8fBX9R(c^^6)pHuZsm30h$Cw9|vg@E66=SY5L>CKJ-$@2-Q%g&MP zZeq}Ac({DwIgEJfQB1u1kqmDzwgiryHv{#6Pqx9MDH%@GgnrW;=)!oPu%``taMIl; zrZYiz*1hxy2<)cO2i&nmqgODZmykR6y#>foOg8gm*w~BcTxw^e$zf?n{U;cL9|0@| zQs_P;2-^aHu1lu^EV{dV!h(3QZP>jtuXPRio_R{#ff)alX1rHqjkVC!SvovqM=7 zdY(=iJ#`CX4p*CnbJE?$cSs!UOoZi;3v_jOEAoGCj(Hx^@VaGXObW$1%y~9ZT zcQOGO0qx}J+}z#|)2h9UCf#DF##*mc|Le1i;Iya+O!&78xkE5E7g=r#iT8{v=XJ$A zM%PCTKDRE*XohrDd}N>XLma;A<0&{y;ufP*%MUNs_lKHmUk#1BC_|Dl6PEd~iJAF2So?)L!blzofAS zuj?<}c_8(V^i1A!lrqcyIGP>;IdZV6AYR_OO)75XdKiL)T4gEv|1F&V1s5+Up>*Bh zWFk^0dqU*8ReMaTh3fYZA&ydWU6y+0~`^F1-KN~oyx(6QclpSDY$356DqSqDO>EUElP* znIaQ>*J=NyXzv2vc+qQ@x?}{2yXNruJ7Iz!XZ-}T;ayqX`&R@4c0;63n@HSeRo2W< z(hGFTRBBz;=@5BELNk=9x(-GiFq@Z4%yTX-GxoVs^Mp1NSl?o8zTj=L)2)w{ zmA*Rrflt}NVl4q>)-ckNbOhPyYXIcO-FZdIx( zbY@BfEV$VX9i6pr8re?od(s*Df7?P{42y9t8};he06Uz&RXJY)stl2}Cd?D|KV#_B z0z*v#5TJu|U0JstiCl9oEk&e;Y~0-zbk$UJ+=;V1H!x_o>Z(@Rnae5$$=py>|0#k6 zT*)DeCo5`20~wsl78cy-E2_W(p5;odoLLLBrROMau%}6rUdzNBk;egi<$uC75@eW~&7xN(Nyxhv!Bx%1zC6sfqSh3HE}>Q@8>fELD1|m}n(`TZ zpr8jU1D?P<#P#eNIVeq~%p-4GBq$AU{A6yf&YG3>t8|~F70uUk&v-XKZX*6Tf1O?J z`=toK=2T#g!}%q*^sXl#2t>jT;dE*U0UhAreg1%!a={?0&NlWQ5u zsHeL6&oO1`m3gHyAOiy}c>nF`p|*3pAA&v?Z`xCM?h*fJm&R$GJ*+doX*Yl~f4wb| zGN@_w35@@Tv5x~@>gx99Cy41Zcm?iEl{FPy2JHW}e)7i*2E?9UyaK24>8 zTajpaD!jMp8QATo= zqwnJ8y*Y35jtK5*1c{RnC-tw&Mv>WEyE7z;aOyv8$Fxxa~@$y@&F z=BY`mzCeWOwyik$Bs)>1__(h`ym0ZTd!_rn%#X_y2<#@0 ztjzy(kl~+kDjT`Le2l_{#<$$>mj86yFaE`}??EnlGWGbf?Y4vKLv#KBMDkPOym5#C zAs)zUN;bzOULqd5SgxXSa0U6zEc+s{8>nv6s5}fWJe=?q|k}F z>|~4;pnxM}#gCNhbPm(LoS8DCwuB_s}wLfv4Ci-H%CUsiv`F5bDKuXhIAvt3&n7yMf zvS)67V^bQ9$GQd$GySGa>Va^wC_ZIG$C=}5ja7 zMmb)$M$L)^REktb%}TF@4H(l^2!2BVvsc*-nHEoX+UmVS(&2*>QqSYK)HK?(Teno3 zEBOd4Zsl)_-gkb@=nN+O@-E#7o&rdPy^69LiHJHm!PKdBaW!|3hy6|>f;9kgR)O{R z8fU6^>(7?9SMqZS_*7^d-)i`^-i52@tE^4ThiiSkMa|f&;_7UY(BNXC2?+=YbS2Bm zSgRC;jo4FO?B~FLbK}P-MSjOVJe&lJ}li|8rgbsFvNDJksvxmCX9;HQVCax-F+xC8y}`y`#X6o zT7i8waC`N6|Gq=xnEv(C++4bkS`%~%gp8aP3Ka)iA@Dl4-M+7S!bv~EiJ8kRxOy@q zQl0i{5)#4^%fOoFM>T_j*_~f1E2R*Hr*s{72jt%FL5Ijq;ozQm9C{+IHte*Kvux=S zHzPBks`1!ls_sEM%pN7OVj3qiOB=BYBpChhzoQkT42RRG%# zYSdU(WcTHYkq7Xu$)rtG5Q&n6?^6Xa$O#P|Ud`4Lf?T+beroI6nMaquKG0b%G}`VT zo+CHZVl4g1qo0{uFCZqwl$Qq`1iyzuQ`!NTxve1}nB!{&nDe{KvX?@5S1XOsSx2?V z=UluMlM{UcKB^&UX!Q*{*ZI=`pyJ5V!Mma=`M!&OifytMJO`Ig@afvWw6alE+Z ztjL@1R}q}8UkK55?K9t z;a3gj>dB*(su;OxUIAA-mWEspR^J|Q+i#DjI>9`(x_^Z0UNSi^g`lr|t80A5aIwKC zNr_W)G(6BFa(ixLI3-8PJR$+Hjl~iZlMTa^`g?$Xw;;KwW@|(5F!QmGG}WJ3TNYbB zhtfnfES7x)0*k!HY@A;Ymlq8AoGs>lE-UN7Ey~@f-`3CdS~@Zw$$5HkbtvIKylR9F zN{MLPo2;Ha*nF=*7;|k$B1r8t5_!K;Y~IR}?OPEa1B<)w+#I;Tw*Gkd6LJXoT3=X? z*0<0#+i&{^%(v%rU>z^o&K)zu%z$uRpW#nyL*hd0VVuOBIJjo2c=_C7kVjyZ7Hi=- ziW|Z4k+e(AR1$5&>WvNx;q3xazBSy`{w!BSV~v!|K+E&D(R8jJWY~D*^n@^;s9&ic zb#6#j#3x%j?m!e_*|TXf6&06V9pP<82^&y3b!Tt1L?T zw=PNo(FuBE3~w(4h9?u#*ERGfE}9>Fd*;brfdH#{ej{uw;I*%J=Rf)ppyH@OeJq_m zbM~4t37b;L00fgl3a7*-WP|J|F2^y^YZpVus-d?1nJ0r1$}-Z@mC%J0XIN~U`|^+7 zQisK*g0q$gLI#AHn3}1H+g+)vEBQBgqxgE=eygXU_V;vJAbvBl&Ge(eORIX1YH#hf z(pQ-8II~fC|Cv->oSw9OW`NIy-fkKiiPRW9y~BlY>;ziJi`A7P!ZEd%9!FVCVqvcxYcOnyKQ>EWwEApX%};KC1|O!X>Wx zO^+^8@SsR|+wU-0`Qx2?^uDCd0UeLA;}rxjO=^Q@(Mkd9XD` zT+nyfu)1qspEld%bKZK*93R(&2LzT`Dkn7E9JBO{d+&LIg->hj&8Oyc+hUYqbZR*Y zxAaB=S-HM?00aj`5=MSkP+@ZkN+NY!8a>S$|t|pm;9`@<@9|0 zaZiro%!Q7`xtTN=ENuEVJ5I8B&S&=sBnDB;oCX5@IHNB!P9;}EKV+_r60YwS7iBZh z|9sh{#*TI*t^OvlS)No3lF(r!G^QNdGvl|1k9V~jDm7Jw$b!-yE++$W&4J zPro!Kj*$Efl|KE~E8Pu$C4{`wlj7;y6%SBBAmSb!DR8mb=7>dMKFel?=sK-yBNi6$ zElxJx-?aN_ zk=$c^a{A8mC)j8ZG1{o>q@C-;eg$Q16uKFDp1#W|yN;RTyEI0L*q*fC80()IP;p#zLLQr|7%B5l@@eXCPcB5aX<}W34TqacyB~; zP$!9RMi{@CiM2#a_eT+N(&oBP)ZWg_r*ZC?*9q-2Y8-mwo21go@#nU}Dx8D&tudcpY`9T^fi{Ae&?$s!6+~MM`{rj#2)<_vO3Acm2 z2I2Fo!i*GKi*?$ZM`%Yhcf#^+!@*lwQ{9;yKXvO-NvKVa-hbbD&yq;R?j*9tghwCj zdxwB{M@wMTUp9I``36ePs$drId$D1th+XwB{tbb_0XLi@k38tBYm^-fFQ>VjT7dA< zgILP1F3V*=V5K|Cu_{b0 z>eD?mhE4MOmp7gHID8gT>MwI8KYQA!|A-9L%?+y9#p--Fhpbb>MbBD<-c#B~I&nAvWKAoKuwDplxZq7BQ6dR9ZEGlY5s(oc8nmvi%tzdL) zEK~%Kz<-bgXdzDdB>I(dq>H<2SI(U6LC{q5E!LrZ7e!T8L(4O%tDzC9gEiCI)zR}g z4qK;&(eZJaYFg?>qvqc4PbLnBswHcwhNs4uldD`hMdHUhO<5;8mmXGLw-;v=@o(v7 za$HQ>5gFT%IJU{4O-#rP{zShYU~)wG=)(`{C!5h}{VaUduIW4eHJT@lgz*u_5~hZR zl`;4K7QEmoi8egSb4Q^=I1w3)t53S>#iQ>*l^FF0kMfbl#6mCt7V1O@`K)p*1U{qZ zbu-Xw^i!0~QR?<1s}0Kvw4cTK=zVLp_7n|U=8g`{D23#DT=`}gR_;FeRI{(1W$tv? zKVm`}NjAW`-_w0u%&_+JywCH6eZwJiRM}y^%heyF=fgaYT>hhnzuf-D*ASgwZa)Cw z_CpD!_y_0(f-`7$Pf-Bz$n%z9K z@R+c!TbZMC>D-s*k+oH8IJ*K(zDFHRt3Ka4tTFKEYiKhac7>{?>3V_vnu?W3^|S>N z*SaQSYR!&hQ#lql*i|P+sr{Bo4VHdja*a&pRz9J!^;;9R*x44GWD$4_k84n#kV`(7 z{(BQscVzQwf+6*U!R1iw;(Ihz`cOB79sFGS&|`Le(f7jIl$TK)%$9OwRp*6fW>4NB zA!gP1Fpz?N(5g;3g)A;I+21RjD86vN=Pt!u+h<`{B=^0|5iN;>f?~Vs%xvIhYtuar zgLRJNK1zk@GRJZ+>E`Te!m&RlRw&RJHp47!IK7&GnVxPHTbUhLr@3rZoQ{PGDlpHa z<~l9YoT%Da&`b5%D{Ej&=#3*yWlBm*3thVZ%TB(8B6jG@6FdGtYwUTT|3dHkEq_Dr z9!>C1*giX-y|{vfqCHMN+RO#Aykb;JKsonD^21AEq0JvQp*%5hXc|v8KAV?IkAE|y zyDg)lZ<^!c#T{EupQd=+n4xA-5y1mP4Uredm>!7o4THxMT6OEuU*rS+jVUjo%s#$ml zvw+XRwIOizH1c7;p1sDK>=+Gsqodix-t$wtBp;_Xl4-Mg0{8P3Rxd_S zDXly@Qpe-@6(}MyAPAp3<}jl3N1aqu13^-vZrL7pGu#qfq~jt22g@9qpI`^)^o7Qg z3hR{sbeQ?-GX!J%VTlN+)XNg;YtGf(Eha)(Az!^{&whT+>rG_uu^H##oRwu%OFr%k z$ImwGO{v-&Q=Sx9-j!8yPJb_DK6g>!v|hR7&EQeBukbUPbJ|>K8;4TNRJ8X*Zec|6 zl7V?JM6CjmJPA9v)BqThhVi@^W322^CbeR!%|$s@VB74)Od6fG2|}UJRWN#@!RQ)7 zZs(~iW1^uOmQe|7A>2LOzWq`E-bS}Zwo*i$)%&M9H0p1KIC@VMME@@E;4P~Ur8(Ul zK@h9!qG9kr6r~Vd?KxuB&h!jYh%9_8A`71caN+18c46s#nDzkI*1}6wC7&2~9Y$yA z|M7h1Y;P}7m&P54LyssD&e=zFJr*H~en(>IwKKaMGvF^kt=#mP(`yW;(M3nH-&-0E z-^jWJb&eTw9;R*GB#ui+j!Qka@Td|w=+xzNe zJ9{s6ZnmE&>*WiP)3f!6kMl=IC~Vyq6U%SNn?w{;M4<|32-O>bwA%cxcOx_o5jG0q zEo4Be5?e|qDq#OOrXw+=gE}T%6D`T z=D9f-->@(`mg7RW|%yvb7c&!Fg6S;7WML4F&x;9hYL?qf8a^1 z4aI))GSjJzZyS*9 zjY0K`%Gr79r`v%qiX#~QR;GfqQ2rc>Y*cPMYwuAVqoS3`AUsM|P=Rot4a+lz7wM*(ZqzuF{6(DO`s)VGPJ+nz7+FTbaz7mF`jq+< z8e)2$MhNJ9z2SG%6OTwqii%c5)2V^cedQsvZ_rpcm%zgII>eeWZ*zgK_c_lOBq&DW zTk4yv)p@TZD!b&^Dm>;Z?_>49mdtANlDl-q5QFH<$URiK)2%Uo6O_A_R#8U;y`;yK zetlQr{?#?*@{4aW{he=Sdu-hpfk5R0$Hx^1Uk&FNI-a`63X^izA9REGZm+&(H(U=I zA7WbG?~s5n(mNDBlf?oel&JfKbBFBi`{>GSi}n$n2V?mP)|(?fLug8Kgm6^!0zi)j zBqxc;>98s1bZ4H`y>OCQWcDSA`=nO=SA7<~GN~=;=TC;-fB;M5n>T1Z_ufp^+E1-1 z)lQo{Fb#1N=h;b)DxI60^n~jB?SrdkZ;Vb*zQom}8`7rFv2enss2^rY5wYI;k>)}i z38(6KUBGG6E)LH!1vOXd8je!6H`% z=feps%Bu(}FV1epQzHpvZK@YXwBzuaapRRy0D$eEW%aQ;6W(y@F0Z+-we2MKy14iE zrf#ykr-{1}33NBI;v))jz(Nj7rLvi+l{rMmiE--L$23I8-pXCZh17h`ezsblfJH9_ zUt|*J9zLGW(M9pB%L_@`@BtDvPOV9rgE0Z|gC?xUmMqT2I(TI_hW=lr33^AeR4y>R zss}>gUkgLS7ks1H<_Qi$4l@rN50&AmP#?Rd83O&pLo?KO%^Nsq=S4l42jejf#9L$N zoyS#gS3=hNBz@lohZKc$Z`+@+ShR;HF2xCKneD<`-=4w>Xh^OXS&|OZ_6qpZ&U^xQ zyh@JRMebOeSwp?#uOy!6(4Fc$kz@EPRFOo*tCc0i0(#JI?Bf6UgA|YKB4J8iOf z{HL0)Tb7+xM)PJ}7XA3PG-8;B0=y_>_~m3O(!U5=#jyVfRaG0KW+z4V*#b7dM|W9n z)GS_!+jY7wlIkw)yO*q2WFH~dL-WAwK!sWJ+VMCIy30uHY)w`2%xa`o&{8*-hud)V zO8`dC=$mNyL}5GqQYG#ypU*BfPZE7|@c-U;12#iR&^v%TwLa9!dHv!}hgR-%sHnbF zRkH}jwo-{vE(QSJqml7M#S<5`G_4m~cb;AFWE(;SA&Rd?eIwbltFt%;_1&!L*P5OD z=CJU>2mzts)v0+m*~0zVC}rN$?qVigwe#d=Z_|u=KQHIVlxnCu@{c=5q+!V1zj@d& zrGmM?EJe5CZx6?5I3ch*heus)wH&p_z?|aUuN<{pyN{~Oy^*IbBxUYez9cU;SEuX^ zik6EuqYxF^(q=^YMH^9u(bx2OCPhwP93R{}=;6l?Uut(omZn?8UlZ~})hTf@UUzcnRvpol`w=>I$7f|OGIY{5#S;+b>N z=(cMEJi}|fBmBi4`wT-dXx+~c=94@0eY~4uL4m$TNE~cbmiHVBxQ9C9&R=m>Hh+KB zy9xA3cD;9W%Yzir7Vh7~co}pE^YX8lqQ8AV#b#KzxKx(qG$)ODTk5kYK;gMiG}a?K zA4%quFRkQl3;&MZmu2u2=KLbOV8&)3(prz^zJ3DlVp$CkDY){|ylcSPyrXQ*Ij_tf z({*`y*dz#TWS$_p)^W8*5a=6)j8~aPabiQ7TlJNBqZyeFv`cnAwYIfF})WBhXI)L4Igw z)Wfi6P3P8ivo3H{MF!sIhA!ZoCfBghF&^!J2R#fUMzb${D`zIU$L=rFx12;RxR4XU zf2tRMTERNY9{D+QHX&5HxJQdC@%Iov36Ta7iGef{f{65xQgU>c z(jXnu3{V6Ck#0tJNJy80bVy1gA>GpT-l#l}`uP1FuYY(bZ1=9~?D(AL5cQ(h<7v1i zUF}Van-yf4*}DbrdOe;m2ugZuvph&<@TJz$*!XkaEFR%)wIT_iBrOp-0=JM{g3MExvZ}$?baY&3oc!&<+E2J45*#@cSQr#5RHkoBx}Sot>T2Gyn@J zbc>V~a5Q_E;3i6kk;AnRH(Ok{7*aWMOniKlVXEK6(=B4|4q>7m>f3X8r10SjZ?RLA z?8NRHzHtH4+RCE#JC=rHTQb3~zntwE!}9B%>Mo-mlyHnW9e+gM^wZ&^4wdO(e&~18 z=rZqhh{31=rHKYf7)<(smpwZAjrHE7bRQ{hZYq4)mm|Kka|<0nRY=OJSS_)|K|T1E zqlpLre;*tk>sACRJewAeAwZOYkbnZ{Bs{S<0vFAo;PW~)Phjo(Rf7S?yvveJ!a{n- z*~7r!jS^*H&h*a=S`{LY_48L#cPDe2Xz1wo^~_#ZTR6O;exO$zt#w`0o~f24J=*NO zl;P4@<*rG7gy5y(-mozyOP+Le2kp+7wgM+AIVa}7 zHPobl{{iR=i9$x!`)MPVk3>~UeYH&;dIld#R^A4_t5pY~X*F@UZJ~nj0_|GUpO5PA zIEoYMUw{64d^(M1Y>{yL?0CLF;j!2B;6&ZXd5OJ^fA2WS0!(m_kYwd$C~W~k40d$$0tZqW5;=H)b_77nR}6w3vX1qhoW0+ zg)@XadJjsKmOJ&98h!X&jZP;>Wq1mdvB;NVP14d6Y@MYnY8mU|SK9~o=f;>&-?_tC zNRMyga26#z6n<*=yMdJ52Ygk@r%!l6hR> zjy@c_Xt-`|Bs5r7rGLEf>}dWq3u;<{0VO^0@f{@Odkf>dkX{;a_vB)H2^;Z%t92&n z5|H({MFI&B7rR?CFw zZ_FDbH+Tmo?pdyS%O=!XIx6oC6sQ$TOjPOXUIp!CkB*yN>;mx6E9>AlEi&m=)m{2_ zCX0SA7}4mpe+huWt?oZa71m`#TbK>t zQdQu}%IvT+(r#gskfT{lNYdG&|# zYbIJKpDn#c!niysa4KV}`$=P2IDb`=N8GO|d(3?a_j5|JE+WN|7 zDkBwCRjDA>wc-Bt>jx(O?j)e3v50D4G{c>uNdKWnUqHtFGb1}s)Pb#oF)u!6r-xpi zYy=h}D#=Pn=&O<6k-59#JR0{c+u2(`h7TFTfj~ooOzrR|KqFK8d!TXjTc80Z$H16T z_D@rU7douUqYvGUVV-s7tF^!>n-`UogruZnhQ-K&c>MY#L4y4??401KuyL^ z2W8bw_%q5Zhf^x>KRSzAYGe={+T+)C6{^qZfgzG*H-#Eh6_PjD&f1>><%bUzkF@Bb z&SnhfxCd%Y6h=GGeX!zFcy0wro$H6rsky}S^^^Y5B}2VSw^oKJOa)n685 z_YjnpRVp^@6dNuw5MOf2v{olk-q?)oKeOsFq*>3G29?oV#Mh@x3& z%}{t1aX6Q}e8mAOC$Ji(PQIhew#v-kYa{kLAbL_zPe>(JXYhpKc8-m*lc*qt}Mkh;eA`!K$zr!S%OQ_L407uowuzTtxMqYNT&a_J+` ztL!25+Rv-KnwlnXFzW`)ZHO=#PE?mY%BU1wqq%#xzdchGDFIl2rw z#`|ku3lBVpIW2ZWV=Wd+XTcD`xV~EjW>4RUu<3X7bsjp~sPyXd+fRz9`!2>xuMH}d zHOEfee-9eJWZ90c_!3FesqAdaUMJ!_8y&tp7n*bB9n`AXz-2BBKj1o|Fxp_K!v2X) zugEAG=SY#}>b(Z`&$afoBM!qAxt)jZPoCVRI#Vx~6gc!oI9-ae9T12<9AUu2d`?Zy zL9O^rSLl;+c;L<33Y=Z+4-*x)bn^%E7|d3R$S=G5iB{_Oy?jBVU1I~Lu!%ZxR z3H%}kcDN#KGeK+M1r$V_ZP2mW0H>U-Vem}hB>;^~k2VcewOQ)%n^~ek#?}j-ZDuZf zbh?I{!m3)+z$#ct2la5PbmqrTRdC+#vXEQ9?tAr>%E4T94(dHR_wsuZ+*&tI7Bj}b zS|xtaIM_bB+E(VSoItliQR{dxXB>wY`L)kQA1IG5xQ#z6HVppAV$(twUv*M6GmJDp zQF*wV%QpGi*W=o@SvA<^T4W~j!?D!kme*>IFt0#hvDte^^Yf zvi=F#<-`TLw9+ZkIu~79q4O^7jOK4$+Dv6^NGP3bu*-UlbWAMW9=gS^P?ruB3_k!T z%|VV4@lX{48BE$X{M3d)Lr=f|{@cqqe#au8rKQmA9>aShF~NhZU5zPjK&7LERat^P zk(s{dG3`Ay>XA>Bbs7~)wUv6h87f*H6DZt!%-8VlW#bbPPzT^fkSULj#Lf8Cr*hLS z2Xcg(j;HhIxEXTZvdL6%KN@nV*qp!}iTC>O?G;vANni6rH{q1VWJ4nL<=4v?1|iSI zZg?}_Tg)>SC>^S#=4T)V5FE|dHy#ZoISg+^))Z|S-6o)8jzo~x?b3B!C3Lt9Xs49Hr1LRs`Qa@ctEi1OZLlYG;5nS|irPq*-jT;sVn ze1-X+S%8&Q1xly1Oybs5Y!r#+TU%QR=EITvCH-|N-O?+asYUnED?UORuu3Em^ zs3Z2taZ@-Ov2$UE=+=JG-S5^;-2L_fKQSl|4&jO^u-m+}mqhKHAFZV{j0M2^5d|v{ z8kc=ni&VeDI@XoUj&`u56%&k(vD^z4&{@{1PG?`fp{`5(jA8 zi>0scH|)ZL3!>`Z7HNt=iVaKJDtFpx2(qu&ZNiZaJTJ8ifZ@1?6S#!ji8d8z1gDVwuWXFe09=i0^TD%_0TX*Q;W%{UJxD~zJ@a|s4 z(_#06Nv+GV=mP84;vumPUZ^{dl$E5Y_97D;Xvj&BC zfd$;P6RUns=F%rdzu0=3qQJADUx< z-g;Hi*0ZBa1``aFnEq`LB5$A9onGm^e|S+2Km4x7g}rK8cAt#&&rZD$u9K(6S-Q^n z*>rMJyfZzUzHS*_weGGPtln0q4xp`yCAztzyu7a1P8}znxn|5o>jEg9tufnzRIYe? zkCnIEc{L=AjL({R1#TbhlC`ucFyaC_&k5+fM9VXJpfTM=Zd0sL-4BLrFX3;cMZD~Y z3pwIvw6T_x|$OI}0CotgUF9_3?;Y}@HeWQu)>9G~4EslLk!G&SwL*Ly16dNI8cEDCiCJ+^M1 z^H_2(b)jzosey3M3r;>|)pOoO&O0xTuVM;yH4qR;MYGE#xNZsLT;H2K%hYt5>vY>) zFw?0LV=14~QcjT954Sj8iQd-l9HGH`$45|p)L>uW)(SwIH`>!h=8`LB(i|GmjFzjY zZ_8fV)pj&8>5rb~l1$DzHI18zhwZH_yLBTj+YI?@ZGB+2m{)*clvhk^`Ob!Le2^C` zY8@#oxLOh+R$pRRFu?NwEl=~Q!dt}GJ6E#u>rEBbrDM2H>z3<<4i1FJZH~MHmD^x- zQUXW89nXwX?go&Uln}J;&RFNTiCcV8ol||1gJE99Z1It$&Y_Le(%$$?K8+^Xu+nM6BA}FZ1L}@miQ^Re`!xY#R}dmsb%P z4<@S%h{tT^8u?RC&Y{^dkfm+vXx zSf~wC~XZ>M|^Nm3aHFwRKJKXw*?%m}(Ka-ZWb?9C#F$uTdMDsVF&$EeGZ7+diqwaMGOUJf z;jr!Uzb%xuI1)~|eUF^LANL%EF}LE)zSGEHs37@yS6;Uf^Ic|xHcW=tSYRhcSD_TX znAeSlp4&y-jqYws;b+&L{#?Ug^d!*6*v%SzqiNxm{YA;+hx%2Z11qWI065~P!II)m zHm%JRtSa+rYoxY%Le-_WmuZ6;XnX3gb(~G2b2ZAG>g}t;DXSp*nXs4{y~W3mX`y*m1U-WW*bn`XHem zz-W!d8osiV6Md8!mZ+xbyRSpB{40hjy9^yQv zghLN`sVPZ;<*~F=ck6zw_$7ZFx{GwH`8uGfPRLiiN zBi8QNFr~(X7w8%Gz2KoLyf@D@I}7CbYKX9etoIRHSnZ$w{37|zaGBflF&SL^6p9i55C zpgABzL0Y2DQNrm_T(IbB>}=HOZiGi$P>}1poLI`JixRB8OF=H~ACB+xZ3CK9t#F%S#Rd3YU(H&~;ZZeASZMsJN zuK`4x&rMgPH%;?Sw>yMKa1&akY`4y0t}V|^kf9uO=1=>h$8{y{y}+v22qa@S>H@ci zdYO{tK36MNnZ$?)G?W`6&cVXRGezofZN-{VQ{||vh3T9W3!Tny@?^9nYd0qjGT={) zQQ{lQa(L_9+Kd``%*swDT?__C)YlVdTARjnuSX^%SPWM^DtN4cvtCoyjw>EeW^T#l@k-Nm)BZe43e(b&=$smR>_sCcRan|D#r&BZ z41uJiK*9d7`WR7(fzUc6?A2$38gh)4XBoin7)r)|+P&-$m~`M~_0tDuV8~ib(;{t;REF?QnTI=pPeIX(}UAddOzQy1S8LLmSFh6BXxq(TiV z(Y6NX-WrSZ<)r0RjFqzYq!K!9D zB?`~vmaEIjk$hKD+B$ROz4hA7UCpK?zQb;bBw+bBke)=%+XAr%hU|myx>rVCH>GCq zQfZCz7n#`l9^0A(^6lCL-k!iA!L%(}%C+=s%Y0I$-|uB%5_4DL{Z1(!CIr~l8C#9+ zCz_1+N-VW1MH0(g5PGD8(O!GqEcHnZe^Q(hCGL;3<0ncFHjHCz68+c@gI1cvXxMcD zo}p>eY7e0C+YS1BYhDAQUK=}MP4#6w_Pl#6GY-B}G0-ri}+lH*RNp|Z^o`;IORWcR=JWHSG<`*0x>JyRKYc5X*c z?VZ!JdrL!H;%RzfB1;*fG*X1$PAsahKeqCX0R1T$^-*M*RY; z@EyUhgB!(d8-q(YMvG4yP|7o@*VssL*kd%en4yy^Kzw<{n&nOdu(vTnI`-YQ!mqtd z0Oh`Y1i#Am^Q<|TTzosDR`29cjq|yCsiIH%k*qL0xa+ygh4ml>*4tT$UYg+KY;LM3 z4$d^dA-F$V{$uBY2Y||1V7;?-5s3o(0saNoTfDx(Ay347SQB0iOx2ieyIVRP-P8g0 z5w%AOj!iyRJHRDS3_?Q3g%REBT=Jkg{sSYTo9zHrg37NLhKZQiQlRl(VB?N4`iH0}hl;6U>K! z(*ANncPG~{7xK7Rq2fj{421T4LR*;b-xT*Pl!*CreX}JABHw&B^n@; zy1_u`O-x;=c(GZhz7Qy&1PdPiR%1P%$FS=>eIxF!x9Xu!HuA*_ z9<@@_n(C9)NQt{7+hI@5&2ij3=wuV1%R`oLfQh1p(1e4Hks+2~U@FyMD4&08uuS{5 z%U;A9d4L+nJP9^LGK3tX7dvr}lD>shHQ|q`FGWTK0Kd^lzz};)&Y+@copSsI+asqx zA4|RNd{t&gELY#i%Ok*IwS4UyMa|3ml20tuVD-C8Q>mg1-qUXwbea8oo^HtI*T^(lm7;v z`j(0#*HGMDO9dbt)J!Y|XB9)YV+t~nnvLL*Ry%BnK;7nJQzr(_WnV+Qp7XUWyBG#C zcSU|0_FwM>jMd28|Dt3#QBT(%Szs_@U5d$ba+?w~+-I+i4K_7(P8cJ{$kkMX;N!if zOU-Crt}XT9dChQPJTa+&Rf$u3yk02t*=8@+YMdn001*9iuOFKu7U)n2-wr1T;3wa7 z_!lOK#6ugi>l@et`twii=PW&3Mo1qJwdQE_rAvnZvszE)w5p$_ZF*@}d$)fB?>q^L zX429cw>dpfX-yWD+%7UHpi33BWY|y*tKV8K?wgHFU{`)klt&IsBK$HaU z=^JP5p&vov0^r^z(7OS(%1c0RXM2{3v2b~9Er?_tGA9g{ywD@!`zVB{{r%uj!ir>+ zK;WA~tZMszWG336(o(%oeGhKEF>Wct##m)TSSFERdr`O3F z@fc~#&nNzXQK4h#yWYBNy>P(-tNYZBW1EDMioqUP4c%F&-SLhS;pa9BKwlBP@TvdD zeGkMQ)O*Tc(9!_FA>t4eB<_Wr{v#r=3fw3`k9T+P4YmVoKW2N9x@6fs%d=-C$;?|5MVE3 z1Tdwl36#`Ft3IWMks*Mo9zcULBRvI;$#lMU%zjBiuDrnazM%ERnSh19vOsLbz-~RQ zI$Y~k229-FJicNAMEot#U(kQag6aW z=oANeOGnldWvdAEP}!wQDB+k3?}(esR|i`|S~UjlTM7o{-@PPaiIUA?jGm+Hu8hNq zaVQ|2in7g%3>VB)64nJ`Sej2m2??3F*5r)n8qORb<(fB}A5pzgZrn-!bdy_3mW)g* z`7z-G1KdJBf?T!RNme!cZjWTsAF%?-D5F{K;eqV41xf6 zBQ^!)6W5z?OX3I95*Kdk`(upp*q~#SNXg1dOy-p{Hca8p2r#O>xZm98HWt)4Calnj;uw5~af=nm}Kd>gm zXN(V6-mswvKd{8(|K5g1EYZMU8iCT3q8vdv`aAE_=fB@PmjxJ5-w%J`SN~JLZTBB~N1tuSK7ws`-xfIRmlU$y&RWZ!X1nq5XF2*eWe+1q z+~7!W1Nu4oduufjE{DCvIIp1aMPQzAwb~1`>S34wU|YFrxa5Rx6LGyyD(||1ccDR< zmq3hYXje|~(4s_zn4a;YE>Imx$RiE<})g=gPi4~RSt7ODXteS_Vx zO|MiQiHjTU*d-+V;dtovKny}cpV2T}QJ!Q)cXNcg@#FwL*9R4=?}BXq%E||6WSW7$ zR1}*sP!P$(pDU8(qOiDXLIDbwfoiY?*R%?9$g)s+a>=f(&vo!co7XHxXYpMKCR;za zh-<@z>u6r0J78O2bgstQpqJ36HI!)hXvS@ z$O3(IK>hF+{Nj3z731|me^ehM@JE$qG-Z5)s&45x?tHwoeQ1}HtaMG=QN9a zcpn(!*$?yuB~Gfwu#un7{VoLSov+OJP0V3PJG21OVQSm?u0qU_8kd5V&!3ceKI)-^ zv%O%u=_f1Fr^8Nmpbe`78y7|^`z^^JZrfU}j0;5^s$lOybimj+S)3AQC*{D6@wR(K{S> zzi#(jG;V+)&7~)54i3GRPQK?8u+iM!dO%NXBb9sfjoNz6oIRizvi&=}TRr z?Ld50=&-|Fm-y|AOV{8pxXrj5M%VIR*1w8gQe74>8o@PT(F&}j-syhPG4?}*Ut&(FK_grA(QbU9_%^ARpee|Us%cBb+!Og&MFe~k`VzW; zFZ#_d`LkOxnm-k@9ZN4Sur+HVargqSmY!!XD1plJ{Fgue zf^Fg#vJ*5mKGN4O!F{NkN~`jEAB~>geG)aF7#M?xUHbPggyz*ShEQm!nM&m6&%`N# z!oIzLPjO4WjH!wPg9<`cP}k>~Ghur}Dpk942TspR0)Kvt)Ad4Zs-9Itvi-yx&|v#S z$CHlA3AYm7!78lL@8|eJi@c1+s7OzTj=uRZjZDy8{+{_~DxMsgtN)zy`=emS82!?w zPzDC3TujK}Q{Bes`4?aUpRfEWDi`;;xS3BbOi?kcKT}SWQHjxVXLGhy;iKdeGTq>W z1eOe$w482CGeIUaC=MpdkeE5gasX5P~qp8yLxosNH<1iWI;FCQg#Y|GWSJI%0qSewJoMbr`FTGQZUZ zshuJC|~yb)YP@p&ENUJEA62ByG<)|y&3b?d2SWyFc~NAk(4j;{wIKe&c42fJcV z4WSyG?OEtFK-Ai`R`vNKwJJ`yi{C|eioU&i&Rb|Mn4Mil(sW5+xPn8;AdinkdFW%) zPwDu1fiH|xE;veq0jyR1@*>^+5j#7-axA$AW*Liq4(3;yx@{`d7VV;5>^IhuA zEXJ_C&-rn>US5@vaj3vS(Uee5EXCsew)QviLn0g$vB-FCAMH_u1Yoi&yjbE-(fN&l zp7EaIFhm<1`5sM?^JF5}6U58f_M0?c$Td9`D4_wxFoVMzqXX~6L}#exMFG$5suOuD z(Z!jnVS|{L4JlSGqjc2dcjww~4JH^Zn^XRxMgKAywtz;A0_oDXzAqu+6LS}iN)U%m z2Nf1t7^8Le`uAo3{tIRSUbzfe@xHXOJd!w2SGha?*7A+~Pt8M_ppq&sBKMsTF`;Qf2LOvMu@Uu5bP=a*=w+FxQ>EiA% zs0yeS^=uS-e@|<2Z_(B^V14lOcgq3X%?Y<<6?&H2e1JvWbJDIZ$XfGN^>>4C@fdni zM5R5+!?rpm;4`b+;5Y_$4~!UkBt<%7u5Z)!Ag8YX^?m+Dp6ku7yF!pdbvKGoF27?_ zF3Xx};~%f}e*tsHoG!Br)%8l=3`9o$^}e%8NNssxYo5@iFFh!bPh#{4_mt~NqbAB=$UJp9OKJ3=85<<}-%D8xnWc?WwQm1R~kGUJw1?XSalfo=sThWBl#V2tEuP-C%JT)(>?; z27`e*9`!SsSAQcW?BP|wLDMrZwBq687l1I5*^BWrwL_Djsg`8%7tH%j=AnvUGe|~9I&X|>{Jit7&6tiA5VY}r5}m@_Qb!@=zsjoKge?( z69Oh6h_8Am3;6Y4KkTo1_P3Xy!OxZT+z8>~v%Y!ri4i@!%WJ7k686ID(w(fJzme(x z<7Y5A-0$)QP%`B*1{IZ3nGy_v!Puh*!F}|IOfv~0I%m3YZhCO0K^Zdfcb!#fljL9S zg{}QnQU8&5g7o^g_~HS585tC;9Trv=|Dhb(Ekv<|PV}59sUJhgv?lGHpb)^`wUth` z0M<^3=r#sG7eqA%4q#!0{%&C}sOax|2?f$LBVZf;hX|q(qgmN+Ht(H7N@k!8L zek9C*0dSH+6XadbFM*nbJUzXpts*PL{&T!v7yw-dfeB{EgNgpbsd|NaN0?9%2K`F~ zz$-8^3;DDx_APmPf@JyMogW{H23Q=hNX#z}yEyc}&+CA85jSXkE=e1!Ux1NNI4KwiqOlyK&3=Yrf0wK+7J#M`*Gy3U^J_lH z3JBvdzJh+dE#S(hm0oz!6F$N*(Wi&wLh#;%^$Rr-6@IsSU`c>hx{rDRWXRv_;E$i- zOaP?v**lqA`uBc6{zZ?8qJsjFA%x>RxkB*JM@ClH?zN}a`*O)RpsxL19e8$OZH*@B z|M~E{C?E_ku}8xHIe?W|y$THRCaGRdp6Zt33(ynpyvG<+t#B(bZ)||GP-Ea!;J}a< ze;(>rz90&Tph#%0CF}J+tv4(Q?K6bne%pN-8k*r2AUwTc0LFV6r+o4XibTTK@aRM@ z{qHybrh#B-;GRgiUY!42GY+@#D*a1oDclU(=*IigN^FSph>9cfWojxGPl+ewoNv&F z=hr|FG5kNT!FC5Xm90xERjKIcrDP4W~moKS>g($~v$pHC--noQC zP3p4z-J$%{53~?KR)xG$Z~RAJ3c;?Z1k{pJQgKX37zD4yBs>zQb@pZw(f@MuB6 z8yl=s3IFrPUw8l=gP_r)?eTW({ApX@8z2W1NRUXz{rJ{tt4vG?t{kxTh?)wB{$2u1 zn6kt*61jx1I=}kZI@{2n9Q>UL;s6t-?)PH+hY9)^0G}Ia(_47v*{vHhJ-u4!mDDn5 zdzt|w#L#@6Ip9#Aiy-(B$A3idAHxUrghgEi)cz}1RQsQf2#Eu>0gT1V07X6UoBV{V z+uv=h-A547Iav@;bl`o|4`YP>Yb5?<`Fl;^_-u6zn*VX-&!NFjVThDe5wT2N5aB~8 zk3WnK=prI<*6=a_>ikdn{+&Aihlp;F!3z~h@6gatu}d`JW@SE zI_=XxIrj@)E`h!<0YbJvp^=aIhaEz^$1B99-&cw2O8eo@sg&*`+T@YfK^U4?$Ns;y@f#Vbsnj=7Ab<)qm<&|BL?cwR zIxG>Prm^oC1>mc|VTJGj8G0Ce_aZs@E2DiX0BP29_padkQ=Bn`>RFJ`dv`>qM`{~4MkAk6QN zPHpV}yd=SkpN>by$R+|4^56wvs71dUYAt{ZR&^gVDgL88zc2)c^ju439{x9C4rb83 z4#$a&N(Ny>Jd%>C_NVN*fuYHb@lEbL*pM+I@A{sf!?XcuYRQ2%1B;~p_Xjlz1BaQm zPOOml^O6|y02k`{frO?@0(px6Ne|3e0=(9%nm-PxB=~y+)NlI2VjKA9y??obCv4*U z3{{lOzy9$1X@Ee!z|)JssL1Gr7sn$EH2RHiPtFSzEycidooG}4FPg)Usz6Ktziq*B z{g0xJClGc0YJM6eMfAhkB$3xomQC0Rh>4ERlX|cma1H3=UV{Hk6~IV+JlBHF&bi({ zc~4LMT?>*G_&5j_y5iB99!e}(RR$lnnVi>}3JCzwzir8M9f*wnH)X)*B7h9-h5q@P zChc>ghbQS~wZY-?R8&-fXZ@^SjZRT-MNf1dBxD+N;kB%1YQ zeEMsJ^8D8D>hpNW?gc1A1BM`Ce}JJF0Rmz8@9jO$5x<0R#Q`Wp0XM<>Uo1w}LnI<@ zQ@(wB=cChW3PA=FlV*%+?B~|+qoRx%bdv1!;zsVzqZ`yXt|vX<6{KTP1r!BiFt+$V zcO?b?I0hMf3Vs8F3JA+MzJP(q(rbDneu@MahV%r;M6YWl^FLi(un`IwpVjWLeX9G1 zCHWjfgDlOoTnP{I)lX}!p&9z~L2gCXGcsyb4zF&@YEw7}UE%S3TrWJHrd?x7KBOId z^Tv%ETWgJ$`kTr25GVC6rSsr}0FN(2^miiyg9boWE@Q+gzwl~lF%FNfZ;?e3$N-60 zK;2bMK$!{7BZlYT?sEzIJJmo$#$)B_n*FEd6-ZHU&({N}$Hi889r3ZZb5x$T4QwXa z!Rf*HQRHC4D7-Icj{pZU`6M4$7x$ZRBfN;t11L^*0_vPcBGc3l4Ct+%<7eu$!r?t- zzT1?*LKT2_$opdk`M7_)_2R$uBOv>bu8^Dp3ufuqfp;~*B_Epg*K37sRu-(EcxYZlJff)5_fx>4 z;32W%AY`JE`0#>L1eLIQbHpRi90!H#J&LFpAg5HeIScfE%BiT>xgP0(32x(yOI0sm zLU=e3{_T%KC~7ZY}l|6kCrq_qj*tcxVw>-6RFt0f2P! z?D_xJ0RZ%ipcXdQMp(r6y6v@gTKZb#Otb8)cps!U9msRK;Duckia^QwE<3?OkO*KU zp?lt;KxlCXuuDkd2|E^G2i|W?KmRWh23GuPVA7wN-k+l*-4@QoJl94??CpU~#z*Bc zFg4%b?>((qrUWd8q0V^q$xgxVlcE)VuX6Vv(>PjC+ zCk0a_YyI(wm#DaS*1DKi82y)E)ssZw&XTm+`!7DA8(6QT9_EvV;nT?+jOwcn^bJD} zzpbvk35Tw3$Q^v~^_U&vmb`o#)Y!y5J1yDuCWq(UMvG>l>%o<1_5+PalDx{P#{hBU z%b3i0Bw<3I$0S(5zuya*2yxzBq3!11(-lZ;8aM{N(`K%V*Ef4!LmXq)wL!pd-P6ke z2$PI}2)+kRM;^hWrBz~$RV8?|3;`g!n1xG-@d;j}Z*+&@M+ zEO>PF>!VjTaK!f{I*UD&HU|^3$tWz8+9r*YMIM--oNmg`kR^O4CWS6a2Y-Ye0(^}u zO2tgja)9&+XgbMPJ7l~D`2!3jYQALfQC)kZJaM zRK<#N!}Ua|GtmgT0>&hUEe`Wqo!+9032K!O&p@IGe2- zQD6VC#QxQ=44Kurys`wmliP*J$Yc`5A3++`a-1aKc%G@e01?XTvWN;Gp_ECfW5ZRk zPW(b=q+AZppF4Z;%~J<*>J)TX!rAvbiu67xFZO(qu?>|4+Ypq`hD0zz4xESIK5-1P zKgsJ!Yo}qioS@=t@2B*@Mpp-z_}6S6U}TdjQUu+1Y@*m(GxG|46>C*L=8;E-4#Q~K zsS0D?X$^2k@JxwYYgn*h~ADEiM5aUk$eT$g&b_X>z^YU?z~ ze)k%%2{23T{)6cIn~>MmZBAx)Hd0uYP4NvwIV%?JT04ZzoZp{hILEd2;iv?D>QL8fd`lr)nPb_Lk`wIeE10 zC11Ym%Iut;g-TJYO>!?+Pw5lKQupbhEC5GMi9u^&e5Ym7gRHb$9FZX5%r{xpDYZ!n za9Vyvb<&}OsFib*8KRRIiM{&=vJoT7ZeoYPl%ruQ^fXqXeX{KkNXN?T$+~>X^`=7 zxn71=AElbZs{PY6+el@xNjqe#S&CrkHQFA+{KY}BO6~EHdHasUvIhds&QKGNI>~r4 zr#=aBsjt7Tjjn+ix^YjAkrgEZvG+6;txJ zEC7cE#1o}Eg^cY-?)DaWoas3|?avVK##k%gW;7Yi&$3>0R{pkLd!kVr!>M!aWjkd^ z{WVF!TPx1?Oi!#z4yrfyDq6R`c(~i-DA_6RK%bFGAvv%XXtbnXl$=hka$|Y!)dHb1 zeU?U9b_}96?Co+%TtunaJL0{@DvL}DUd4BabZN6!Gg7anpLogvVO+3x(Yn^dm)2&p?+4Z`? z&sqs`yTKTU(;Ug>$>de#A19ES9^v|P;s_n#TM41?19FS1a6mP^YPPy()e+SWg@b) zri#cLO5YNFDY}ShP_fe0eKjXeub2!>Gwv7_m`$~<@oVI{eQj%1pwmA?@E7UznI0G| zvOcb*ySrEAQK_>%aZ)v#(yeVf8Na;K$WAz0^eR6xJy1RC2CtTftd-?Tqu-N|>LuCe z?W1{{4R}|y-`LUOM9`7*$3@JY`8AYB<7En`YFn?&Co4X=IY!t&9Zvo7T&>NPQMF`6 zCHicrjC^1##ePLPaQX!X3v@Jqk;M@4SZDUb7LL&9 zC|-a0@7^^fKp~EywT(kYD;DU4HxDm~uPqg~pb{+jR;%dPzI7__I^~`TrRbLuKdnQe zIivbN^p58#joe+;X!*8SOvCN;6nBwx920_aGVZk9Nk_xOGqNB9C=H<^8$6A%`#>+k z@vO?RPF^gC)cl0D*2ciU4bG-ogT}j{#i>))iTd0CPm?ubYe)a?U%MbMTpTNG+MaP~ zX@2??C0K*@&SH(`#23Dbg9ypM$7C=zyH) z+vV$CV~6`Y2Rkl;hde)qX2|$;m0NvV@Efz2t)a_SsPb3LRvf&qjY{Uqf~?W9b@y1=r`WAvS()9Enwio$+nnu{~Me&F)*xMttz;P5DGc+e5}-aEo^ z2aD0S+O!_~UfrJlFy_v5E^~Dn1pyKZ3zEcxPlB)U3lYDDQj|1sRTM}!KbP_qtd*HJ zWI`VZ!jYgVPp{971Zwk|>9nSPj_q(HIZ;j;AXmgH4Xi+}o(jpYE{YEzI|?IIoU*gH z>xCh&;FiI+S36}f&Jy`4#aF!n4B*zodDlskz0k3oR66&b0wGX*6#jo|I_J3wB?9J! zehy>Wz;&Y$nBY`Beigl~2z)E(mTPHzRY{0hftgu}?f!NR_9jAI~*?aZ@Bh8|wacv*2t9?NaNP!Uv<$FrlPR*O&3} z;W(dH_ps=D#C7_zUWc2&EYC_8=%POe*3e_FZmx z7De6d#l?)Gl|IPYNlD&RidAA;CEIBY-9SaLA!b{iK_O69i7Z}Dd@Y&$8{X>y3<>nGo9%}z1s#SEA;>Uf{Y${^f@_!UfX-DI%opn^02DOJ?Z3-I6WFC# z2!Pf}-6|bw6tz80&}XM+xw)SY#g?!{et}2V{Xd8H* z+e$M|I3^1TivP$b?6w%eV%_G4zk8Yal>}Vq%%!poa{Vrw#k|x`{Mp3obyt0TA3pBf z48M(oLx=i25Xmhwn^YU+v!5` zb@@z}0|flA{8dZrf{u?zGm?w%MoB)%5~-LDi4DAc<*-}M_L25ZhU@O!ecQg7zM4dy zg{ko~zWKL0Mq=~|>D9O$J%?U7jbE%Ffr}DH*eh>_ngyrjbS%p?)*g-1>bUcAOOjQ2 zeK0{48LeUkOvc`>+OI$xL=PT)hbM@iXs3Fci@+<>G3i|8XL!OI+dEMcXtWOzAO_jpOtM*zGJy_mMx*utDlGrw8 zTjn=@Z5r zQeGW?G7LQB~r3j6R-Xo2FjcCT2QYP`w3wTu)_1cEz&-VAf~yo%vs!zm)2?GA@q0WRmiPl?_+8#&;*$fH02b zlKN&G@RW zr3SvwjEz#2Ah~B}KzHAID{SG$^bWE?ml+6Y!?KmMyqoucJZgE+0$sUJ$TH(W6T3WD zR`#d0FC{5Oj}KlT6H;V))#S4tCx807yP8#G(#Mjx>gxZ2W&l}nz4*uZ zsQCKo&o*;-vxDT3lH0Z+9aJ-_@j@c3bOU=jS&%{l`O z|LUWL;x(mQdq7*vl&tFS5>ii@WkvRDI*;>x9&ww}&92_v{V@1W+ZwQ$k!BX~J6d`;zxxx_SKJIHy*8*Q{?nT}WAGB}sC-BHYEuF|KbNGL>y=7R`Th~9X zz(@!pB_a|^4Im;d4JutjcPOQ#v~*cC(%nddk`e@V&h50YT{Vf)C^Zo4s8vptk5|oR4k@*~ zjh^zYC%pQ-BdKlT)=+xgi42`L6GC)BP&mMo#tCjv%Z)lYWXN@xVMO`|)gq4i{m+Y^ ziWpzG8S_9`-z zJ4;*75xS_=wS~$j#%;yAGr7)7YHLburBWiHVKdl(P}kx6Sbp$9z8ehbqf>VcX$HkT>N{aQ5Fxw{hWo8 zfy=1HjO0+Hf`eVOwu}$ywl&ScGvWfnyx^X-qB5_WtgK@JKH(+;`PerKHCQcq>&-jt zz)*M^{ZpJsQOF; znOl#sOQT1t48_I$FHmeRjp^!DTbZTEprz2P4)FGYB*2ckQ?3?&SVr3`d(n9!l;3rSO2!x?#Ez^aT5yWW(dV}1 zqQzpvX~v4EP7H}nyi+ZkL+$4_TVy&c)5EwLV;Ywe=hoq9V~P)VCdWHb?qjw?tvrbF zbtif_`n<vB|Z*dp#dY5_wQ4=@#_ow#Jepqf{D?^-hi&*XpsSCLe zf^@R8x|w^5hD;qdrXM0dvX?$)m}wR@^?pTp+rSQoY%f~#ixp$K7RW-f=p>y@yI_(j zP*MOVW&Td-s@ZbI@t9~e%=!p{E8gBP6_0!DF=jk&V7ET<=`p&Gs;M0z&H*NN1x%Pk zzpY}-)M)5g$&x~%n2PR!ImrXF;-!R5>{@n_UtT$O8(m*PZs)5S z7>JoP_+hR4oj+bpytBhP(r6mEDOCpyid^qx>rj{Dj!@3#*5CnO-LXk_|4RWX(AdX> z#R6mdds_l#JEOjxT^VN3{0#0}go~%a18?2FKd<`I(!Mh^&c48FYq?6p=jwM|s9|b! z5PW%fs71ta3aORxvfCr0g6;#Szg`_35}pQ_Q4IGlRO_#(`DG!jOj1~e6mWJi5w-Ay zD619c*~HC0p(KgpkR*^-KJN7-mKdTs=Uk}m)I)5@z+kw<(8;~E6>of{)jYn6TOdqD zba-NDTQxix`<6*lG5B)sfyiZF8R6q_^ z(B$UjjTwcc&CMTe)hig`2G`?~f9Q_?;gC9N#OZo+dQz`j+JL%k+H}Y{iRP(4l$7wV zH|R#%j;7>sJ2gB$_F7b=aGMl-aEV5q$LCUWgl~6y?|YL^+)G32_&YR$aZX<#FpyNl zl9(s|=k@z5S#X0jatsuEaG}RN!&2OQL}%)x!xL_Uw~8(f=`{K9QQh6M$ewuNTlPk7 z-G^-338&%{-pOL~}iMf~BYE=!E2USG$rR zck?c80cU0h9!M}gHe9nFsd%_yscYwC-yW+F`GetrYM{nul7q8xBl9rxMuoD3Wb1hF zqX7~SX{Ll_G~M<1c#M54trOi&qby9H-6EFXj)*$ctgS6p&}F0Z5o#Z=JnrnG-?Mz> zWs-VWpPtu7=WA*4x~)s%*?PE3V^-d^jj0|s+7mrrL7&g3ZaI^Z#HWX=Jo_#u@mii9 zOajLZ=u%24ONs9taki|eHC)6sr6|HgkhtF+no?Z?`XzLBYJp#s3f9l zLCuJvd_iK6$My%kmxN2}n~>wxnip2hyP5N>#R!C$r-D=)@B8YVcxql7Ri=fj;)t(Va*dVOP%>AQ?Z}nDk^(STP@{%*Gp^@LKG%sxwPYdRQkc zQY}x!gZxQrAaUcO5&1@)r-c9EC!xbHAmSrMtTUtl_oxc(gbWmFx|T76)?X3BR)zf4Es}$@#rd(%Zv`?{W@UYs6S-vS018!d?A=Af4mW?SHFOZ+5u*`C{i)o;686R+X6RVqO{- zM1?-3E3~S$9JvqTfU=(2#SAQ(gzP4P$&UA?EM~)G>r@LnlLEjX*4TYTDjgSg9v!hc zNkck@L%!!r?Hs9sMsh}NX=X$;t_#g{tUE;?TTO$?Otu*}Y6hNmFezEeGIW#WnT#*m zu5MrL$#DKl)l+2INo1~q+L4eYRQqNI{fG0A10!P(RkzxeEM&=8spY1<#;F&pdR>F6 z>ph~6zM5cIvv`F*Fc0X+W+ESAv*;_Gpo=!kZN7O~3kzWXC<#Oq9~n>mP^*1JWNM9= zT;;a-X*#LtI8OWQR9{Ufvsl%BW}c5N*>AyfZ+zQn@e3Ja>2vn6o?eQr*{w2>(-r)F z?)s3{h!b-+vE9F+60_+Na1Xv5OGLL?=DjS?h)Y-OXoFf#Ni=c>e`ZD@-+wH%e{b+$ zIdM`okq;@bKNDdtNSntFhQufyCvTIQj<2HTloR<^g|ms8F1=OD#3R>|khHr!Xcy{Y zH;UiPbpinZ@Mw=Uy;*F60DrAW+hHOJHz5JJ766@ao&mn07WM=W-S;F4qh~*C6NPr1 zocazLj(4qR_ZR5OtKC*d*=IwaE5@3zFBfTDR@W@`MoE@>=j=fD;a4(-{EjRc zzLzFFRd6Z3nm;j_Y>;w9PpA~&4zyIPuy+E03xRsN(|^KR0TlQ1porQ1ZI5&4(}}~k zQ`oiGf)4>wAe1_N|N4Z87V(Ec?6QldtlFiOi=Fs_9G(O82QK@(uZ=9@@{Ktc#ICEJ zhqQH^bQKgyP8gSXKnjey`1%@0HcAZ-!ECi7_Y20gj=DQWIl=CQ2EJ>e5 zEyoPn$}-$_q0m7UV4nO&u!NzN|N>3%tPZS00UoM(6jw22vMkfW~6 z96Qyu?L7J6y>H#4f^jNq`bTbvog5KG95?d!_NkbU#L^viIFe$3@_)K3C_{-bV=c0a z;x{SSEy8=obC@ss9xUMtD#K8%3^i^03j(Oc8TPUU&W?CXI_Jq8IT|XV${N0cAUhL) z6tN<-`Jo6~b2x9a2M`w@S(K`U0kY>L*1W?vesNJ}$LVrGxSMEt57{w@t8rLvxOosu zAX-}^f!I1wbl6--=jpdkNaSg_n@X*@A!(dWqhB%a>5`otdR(`9uv3Q0FRGw2aFEh3 zy2WH3Eq}3LaCFyb^OD-$e6q3m$8!kDomzY-Q0Wi|+(>T;Z^b1SP~C{iY1| zSPSV>8jzlbVvF0sHP?F4>yw`8aR73nW1d+K%4hP&@eh6rLjP$3!M+5&9a)A802r5esU$7$LfbE%hxkvx z5lw0I3h{`J%?wOd=!d6q#iT$o?k!bp77L%_@Y=-HyhHlEs_t*xs})0DgF_?uwA6Q$ z&2>2+_6xeBo;UE;rORyG&%pk%s{EFRe$i=ZF3;GqbzHpsG_k@uduC2F$F}2kb7^)< zui01XwDphJOL?VZn<$V_jkFF8@KF&usyc~Ed$ZnF+Ao9kx<_bqrfm$lFj>|RP4{R;1JVoVQ!EO?tW+EcusCXKy z3YOS{!(3Y1W2bZu)z#4o9dQDBV;RnfTv?zw^UvwqAwN={|xIejV93B=Fq{f5byE8h-`7An`$I|Q4JU(); zh>fDX-onQS!hqkzw*P< zzdiOjj)Ph*-KR9DS}bk1;^+X$J?8O3ckg51M6PbdD6OH+HbRI&!CxkGsM%%6m@!qz za8Z+ud;7iHz;nY_6*0*WP?DX6E!GfE=es9!ayVi?`Ay0y8C03u#W$UY)D8H+)8eiT zEPJL*@4tap5W1rCr>lR6;Z15wNIE-*^4boom97ns5ioyOTJ%5h_}Fwv+A8r3Ju^O( zPJ{C2d%E0V&(fBa>yO)CX+5A>TGzRwtZ#Yl*~vitxXO*S<9@Q`)lv9}Y18EE5CvEw zsl2{Fj>QXm-vii*!5+N!iFcj8-V2}NS#sFr;*%)@AYg z`*Ig#>8&{n|1TsA(ab~@Cq_1Lh{UZ3#k8Z`+9!u2_2_}#%=-!v-UsV3Zd0NL;WWZ) zs@>9yzqs7<;V*c!BlCSp{2iWIpz}%ql+vx)U8vpj+(BoeczcFq*`Cc-mq2< z=v2*mG?-vEsB73s;IYwk3F}V$OCsTdctYL2b4iC4hyA)(w;MKW67T#FEYbsc7SJxZ zp#eSVTjIK^<#X_)u)jckV$`Kua012qY+N`)!jcq7V84OJiD1uWT+ye7=uh68jp-E@ z$11e$W*^vy4gq>?m!TJvo zGH)8Z}a5su6&iW!G`ekHO`E$cyp$jQ_BukIGd#9~yetjOrD zE*o(;?kD8P(M0@W{#AB;KTmg5q4-<{XG<+56%wRmR=Kgw1V6r=UizfY$i_B0yfuO7 zn~i8{swjx{R(%y>9}_ZWWUV3O^E2U%>c?X|v`<~UWldTpH|f#c1+v7`_+tQ96}MuAI*UY%8~GOVX9H*K+t zDE~bAtM&oZSLFM4G#8~~4>fTxFy50Qe-1$41QZ?ZV>9WU0_*zrK8oD^flDSFli1&< zyxm?N+*8^fX#lmlv?8KzYxmN3K;*6*q?9c^*L&wkm&W%W2_CMzR4dd-If(jtm3YIs zGH8Od*e}4k>{wj4QRFZ&#-jT=OINZ$WgQ8~JJFbB268y+0i?K-mX|LUfn!~|U~0s1 z+8>~)aBn?tPQxaLrS%gqy-U+7YhmCb$R zGq153yEg7$afwgjbleG9=q63FF9Z|6+P$N(>xZ%|$IZ#Hi5B%NPv?bxcWQ2FDMT}s zerlHqr;P^~1dFi;&I$0s;8${({~7*aL+?dkTTNJvNEwxrjNFeMY`8jS`p%$ zaZJm?(}*k{e`k1I6-jF)m?*c83UQaF7NpV7dVasoa7^nOf8h;W;+!^t3d+_x7kww8 zTeD~~ke(U)v@UT^1l&uYjfCAS@)&+la5J@y0N`N|K(0f>UqTHjg%=DmLA1c`M_ zu6oRx7WMsEPz1YqirtCvR4e-Qa9E^jDt*WWuHP*@M)9@w^dqgi_YnhaOC7q;^i@Qd zXu;SlNKEGWgnQ$@_@IB#zB`@0f9o4y|644Xf0@?5kj90YMT|A$j=35dGk%y@Gmpj5 z>`+dh$A%kvExvFwcSrL$8?`g!q*?vo^h9a2@`)kHGy)s(Sx21U+!n%D^;GPxF8z>x zii+b*SZcq{l0U8g`P!XUPzgD{ooiUXH5W)M?Q#9u^1o``?))l75Y;kJ8qI1Bl>voH zcqiK&v?BZUss(C-i6V2gUczlvLn7pH=ONw)qy}d?QYQWS-n2! z+LtftydCm6hLvIVXNbOzOI_hB@*-`^Q+hFRY=Z4C%hF+Z(06vSc%{xO&)zib4Bnxb z{z<@`AYlJydtF>HoKE}(haPVf7W0Sb-qd@>hF|C2f6 z5AuZ?(@!6Cynb^$;M7DaZM;*w?BTj&!r@_ZGNUqS!JCJxOH6|*<$5Qmv$~)y92At> zk0ZzzI=@q4EBaVCfoE$7#?ze4^J#U$^G*)#f>yqc^fI$1->p9*FF3LA|BxbdyyL&N z{Vq?F-*1^B>_lA5IcjB$dT(nh1&hGz=kN+t`?loEh(*hlHibc)o^Zy5_vHqccU|%x2dq7T%f7~U^y5kr?A5^t z9f%1Y)<{cv=N7@DF`e`UIy8i8r$$qac#E*r*#aI!uDgC{vGUnQ%;4DkZE658JPZQx z4i6@u&B#S`UUnA*j_%3Qh$U23m5Ly)z!&k23e;1nBhvR{{Q_1Ve&Nb6S?(w4ZS zK3Rc?ph;oViFQoiL&?&TZ1zdNC-Pu`6YLYKApcE5lkd1P?0}5aDoN^?ZR1n`6%VS_ zR+_=v%R31xW0d1m1;u0+-T}YqoQ}qS%0}Q>`%458)VbO+4dsvI?POWL^7R~#x7zTU z402YRJj8X5x9GB>4)fmJUq?;2`7XeJU|C6`>&@hbaM+Xi*P~X))B+tXM3cuxx{$KY zF==7)i7aLD3^I;s?**YO2q^}Sb2EYqjJk<0hKfgunUe%lLgLD4Nhud#QxQVVAzg$! zx%zcq3oP=4W2>^*@OMnGCYFTP8stZ-t*y3w0pxFPy-AB2>3>Ap=@$@Ja!oG1F*U3Y zap+xpv=B&#y(I5Hzyx3v`GRkfF?C8rP$-qL-i682w-2OhnK}AC;edEC)I-8ERZr>G z{u0krjHdM!y)jqzwZQ2idjr-!G3wM@y@LYvCFpu6)xs+n2B(jHyGByK{DR+&nyz~-%=NJImsti9|8lvJxN0b%Y zu2Z;mm|P&=b0nnWgnGKX=2UosNq5r^P`5sp(9&EcCgcol2PAVV=7YZ{1;&UG0 zYr{Sz@gX%Ygh`gW+5flzuzV994+X3;W)8FNj2bdZJUKIy|_H{N-C%e>4=NEKxDv!pRUFE4CI~|afxX+(I{Kw z5}G|Mxj*K)+Gg{K1}IvpwJWlnfUyNVbWQ^DOCcS9gYG`k$q zfukx3@km5Xx|L5OGzDqZuTp>l5JH!3P|NfblRn(iEpXMEKSbfHziiu0r5-r^`i$!6 zqt4DH?&iJTCA524xVMDT3?F52lAWj|g|cLxsUhE!0UX`BM@Gu8 zbPva>mTzhs4t^OO9Km;v!$N;c_bJR|=HC0{pvEos>8^(5lV@{XhqE8rADh(R^l^(H zSPohKRxC?zk0mF^H;XKrde4k2=s272wfCdgk9u`zojGFV0*EQgB$!G_n#CN%r|NCn zh#X|+TSD)YX5k%^E}q*q!6olZ7dK?nE;R`{27npIA;SoLkgAI}8g0M&&Nhw=Ch-z$ zt3Hx0eaL7`(#YH8brRPD8PT9y1|P=ve3r#!y*O4!Q#rw4jI5nk-E}V18n90$BNdkE zW%p6=6u$s3mU++=d)RB)OwdPp<;L5_nJ)YBdLE^E|*5RW8LfXJKrHE8f?Rbf6F zVjecr$HQ$_+pZjm+;toZF`&uCzTw>b60|sL-hA>z#ypnbovaGl-u3Z%^z!a9aGmEm zV)QVr_h#zNBK>K1Z(wqN{0JPy46Q z#e{?`{pU_dtmvC)zo+-9-Q+i-RhK+xv3o&u&v(!IxZ?V}y|~L;oK&t)23I(aH5-py zqiUBnsT4bo7Sm-cNB+K*pl&n+ZxKH`Pl?*Rs>*4B_Q}|;mi5?FET{^JPPnRlMaY{F z$fY&_>HAk@{a1zOR4|k06e%EmBtAGjz_#Ltg{CX+vyxA|KvYV4e6M+k+oDsw7u1Uk zu8E)M*boj@6ZE(L2_`AYYV=Nz5p=quGv;C59z)`|`HiEv93^0yL*=Pj$Pb$w}dbAb6A}?^`qJz5wZ2s@;i-Dwnfs@{W9C`=wYt5@7wwbNCnj zQ^zYP8`hd61oRDy8sl!t$WbD%AUcYPXnkz71r(Y&&D@JpCd*vzLk( zmp1))#GWwyCqy2lDqIkzWQXevf5*F&f^V0p8(wU3fj-TLi`}n@%Uf>p!)7+L+`@{dPe9j z(Ukk%_SOQ)sS0VP?6vSzwvbO34sTtsIDBzDa=KRPC>e4=TEN2Rq~7avVoK0e&eHlB zs$dM@%sSf&Is+>^HYmlTybmuWO8I!ZE*En@cW!FUuYdYOFOk=w+PH;6nh9hT?x#AD z!Y+f0i>ZBhb7-XQJ1;|JT!@XUKDSdtr|R48-gt3+V#c9By<^9X(9mIiC8(Uyy>yk5 z3-Ig?{t*qH{TNlvXLe4w)NL#8D$R0tq>wwnOfAEG>on)#q#)bm>Dfe2(XL(Pdc4u$ zoLEDTTyOK=-pcfpma8p~4ddV03lv}$VFU2O_uUUm|B!gQpgQUAnE7SAG>p2h=u}d` zB=;ZWdAv<2#^N;qF(*Ts4|ZNrnIE4*-x(7KXMlnOy5jR=9v8A-5*aSpt)APqQMyR! zbKAWAgAv=JkQo#N&DZLqhRUh!R~R{)j`DXV49d-737D^v!p;SCzvX)P*-49W6o3$c zSd#!v@c7CmF77?UBEVvv0p-#Eyi>n|C;pFaW&{!(>>_W`DIQCT)^DYnYglr5Z~Qm3r*uY>EN92h~ua);WlRXVBWVbvt?N@hD{Y_S-z*+npfl-zU{^{qEAHeQ$C3F z5<@<{9t4umfq~L_9~zBO?Q{t>=Xr3(=)8^TFVxg^7M{mc1cPd=vRYj;F_+5{GtGP@ z3++N3pDp-2Pu3=I9UEGN*d_M~a&s-Z&=C z45D?9%yIx*&L0o@IY10daWdG3ulFm3x!}?!fkppSdler8*VU1vxeg5wBq8)MDSjYR z&&<{rxFHkaDGMBeRy6^PsGIR8e2(a)stIU)ElaicQ6H0YDdKgRy0pL2QDZ(9zN3{` zt5$DeTKj&?I||kwnMO`V1CNN*wHZwF~(iH^*}C%*2=2Ix!{0 z0SIbE`d=N^{s`A@fYM65G;=$>@{7=?=1`qNW-C@!lI5wm1M!`Z#YC^0!5$KlOTD?X zYn}$g_nojUXnE#KBIfoSPSxN^Ewl3#@30BtIFfT|N1OEx>3A0@5(T$p02r``_*v7z zGEIScUY)L)*ULvQvB0Ro8atYPH=%p;U57FUXgZ7J(F8&WZ4I{>za6|Pq+#zTL-TQ8 zs=TrhJKTdvi$@|CGTw5DX0-K#142gnyJ7!0nWAt+FD1d7^+S~~+{mAsYvyB!1 zh}wxmwlmOa@Y2XDlol4{1FuRA>!o~7IdnlG7=!2Tbr@n-A-N#L|WzL39h9+K(E609wb2YotWzjdsq z>}E`y;_Hbbqa7Ii)2L!Qv^|MOZP}+|;767Mb{qmmo=uv?xIdbJXNSRX&eK1eczY=E z48FR~SEd7(5Ia4f@qS&E0>Gu)O*6Vd!R>^heZnrTexPQksA(%l&dVS`qf|p(p2h3} z>7o#rzYtk7<@p(p?fckdh^Kf&p{sO*Lzx>z#43n>zX8#2UGsIHjTD=a`O4~@;QHJX zVR?@_B*HR6o}W4-+BdqaDSi1pi}=BU0~Y^3s2?23@n8&!4UnG(AQd+LO2^?ZxuIFeoW8BPrU&cJidAx zZ&H|v>hn(d7X;>W@y242d(X6mmMGW<3rqr9Ks4-QFH^no_gL-f==Y}SYMku`#0as| zBU~zxXE~m+Ud94+C$Vx*a~r$SWR6(>K~jRY4>}pUP;vL1(m!BC)wF8SyT7Z1q^DZC_v z57ec#MK{Jao$7&UIAtO^+=0AyY8~~%qm#9!=^|U(8rH(~6vq`~dG?k??d+Vsw~(6C z(ojwSdqYj&Pc;JA+c10}@P{#`Oef#wk(B#lTF>AV_U`HwlfB~A$E)b7-ujjqwLC2{ zYqWs)iOiLwt-z`h?Qz^sWiGelo}|O)--~}ILi=y{Uq>FfB_3He7wKM-yQMCY|3F8c>9 z-`2m^gazCL!5-LEr-|3U%Jru~E2H?=vf5V+O!a8_1c}c>Eb~J^NqaQ&j@e-G<{&l& z+CC`_|D$&^GkksH2Lp($T1W@NE<(S5!V${<=&f5}0e4>Nor1UOuw>9*l#?Z|v~mdt zgVzi&O?g3SFECB!#1cL>WGJVOb|l+hihgl1UZKfHg?vwe3yYvr_`h_-{6qqc){!5r zv1hg&<&QS%Rut~L8iKyM$<>`qU50p%-wTh5InJKG%OK#47S>GS@URx9qh=cNx-MFwR)eD2QE zVOsPpm`GCHcZ46b1+e2@aAmDLj*CHKTJAQVtG=;Qo??QcH}^q$E8dgXio zq_z&QNMhzk60)+iu_eC%9$lX=Hvo)Q)>*Tq+T8+lnJ|bQMKo1%l4rTFN0V#sZ`zsYS#a19qtHskwTwT5MFAt#ncMrg7+V>AMx%tkrZ;5Sv zY$8vK+}3tTRqi zm<}VuLGJ`WX)<>s<-zfr>erfANZ7K!Y?`vneQ3XzA&zDXA!~nomjj!2Ka^PN0K7Ujny4WJi&cH5-e!fl?HP{xvnBZ9ZJ;nXxKr0Qd)PkbVJv>0D9S_f zS*7G&*l)7&T7HFG3-O^p8qjWPB~uBF1KqXTRm7F5T*n~fgmfqQad;|LO%$ z)Ey=u_Ly)M<0%TSi*RZiyrJ6bX*{+qw;j8UX44%69xBs;0J+n9Ab<`nx{~OXk_UMT zZ9xz1R^^Pq8x8SICkKjM2C?<+t6c%;gVAdE!1(r|6Q*#N8MZ!Nn_g^K|7m?@tLRa^>p}-EUFi1X z#(yT#ezhFLAhbZ~uF~h9*M#kGD4qj|%jbYgd0T@;+6sErHl-G~g1NBH$8j24Q$N4l zK4g%2PdD4sb9d$9oLz6tBaBMms_ijg{txkg%$0#X$8pw*IIV9=rqIZ!KLLm>)VEIe z>JcAm-r`bbD>e??OF3=r6x0=PwN#Ryf3^KsUNQ;T1l4$n_b=(1XEvg~GYH56Z-giU zBzL?)VxOKwJ?3j1PUy3rznR2T)d*ad*PD zl;U^N|0^{F6~YoymjvPD+$R;IT6r$Jesqn-(5BcFy6FsGs)_P$T7z@`yd~DnfK(&9yVh!a>wPw?e4*=jRJn{5(SnG8OObEjS}} zjk^T|LdGqjI24x^VY7|=?#p*T@3WsXCcjx{)2AcCWD?Zv{Ndtg$1;xkd)2aPvbis= z#AxMeM3Ga-$>n+vJIjkYweb)KQl)I^?zai?|Bg*4BI zsYCC?M7K%z_$gr?%eBreUs^nJH83NORG8}r1hN&Hkr6|Y`7vBMLlR7|V03MKOoMe< z_8xd3>ZuwPDGvR=Pg?jjpTVDz0ajz=BDgRfnhQQ(tZt z3E2k*k`KiF`8agmca*?AKz84bLOyx&E!Q#D5g&QjO;i7wK!|jZ^;$TH&O;pTD||aM z8-zYm0<%!ODP9^zsBBr}!|Oqv@y=wK^xy8{t4e;NRp)2=RyzkJuz~hf_ zqOAVgqQPJe`Zb(uv|9SgVzoiM!fz$hMw7T^EEBoi8*k|zDWruj(BrKj!GN$8q~*Dp zUlSbv9_A650{(;1)hG~5DPc2f?+vYGz9;#_{yA2kCJrPhl~WHq9~F5nAb>GN<0a1D z-#bG=`ZoN>v;NIW%kqI2_;Qs~3jxeoYRln0K!()29S6(`g$9Mx!R_<;AA^vX@ zZ~mUJaoO*qn5=}<1t$3%U52Umrj$SZy*I9#v~IHaWze65Vx~C#^Wk$i1ZiM{0hVia zT;&-Vp*cBMDuVSL17b9AAa>7+?}IrUuR+^V$omXgnloQKdnWYzVBVnYiJ-;vGY{@c z{n`;yG9X|ZMHTf%aOwdQ2U9`8Hzk{Z;zgXOM}x})))g%84d1$825YrR0m?Xbv1I>q zh~n>aP5wB5O)!a*-pH)r_u)TaccgOQ7j7FHG2*`hNN)YCN-)u|W#3$(O zYnPRmFWg5!@o(`-5(ET%Ri-Qfyxk@X_<_5d&Ggq~sejP^?8o<*quy04AUV4hQk7pE zMK2>MX~o6B1e5e{X2P7>NEYB_dR7e8+n<=j{{MCW-bFtT1ku;)tQ43t{par(WEMk4!G&1W|LLCS0Cv4>oz;)~+q=Q>z3`VP z#(}864UJnKK)b!Fak(wV1+SJK!%`2tO*VvBTU`ZR619_j7Stds&Sy-fz z*nMv91xtJawGiD21+GU}T$%WZ!SzA?M#O)z^ z?R0k3dOg0ZG3kl{{rOsOGe{}8(b7;T11p%HmxH4#mrc=MG}OFEAp7gAF0*5llvI?* ze?NdO3X@KVi^A_$Avn*+1U<=f~N2u>K>`*=J`*IYL8me%^q-9=N;S2zsY_?|f5s zQHU>%tmv*A9Z`N6OE2j!d`}X{5`4u-tCvnV0pdqt+vHq~$zP<)u zzUrTI?d;mmY%_8eW8UoJ@Q9pw@DK{*K~U-*y`*FiL1HDz*iyW5nA3Yeh*)ZYBjyu)awi^Cl)n11Y{0(Mo$07?|cEykuh6mVSHe(o}?-2zbL1Lp`42a73Kd# zIS#Po_j7W#|3_5wpeQM?S%UY%y_^#N_CZ5b#R- zlThR&_#*$$tjt~x$xkA3D(oEBGEE|+nTM(Wj}8bhf&$Mx5P!M>SnqKl1zIxd{0QN2 zJaCzLemN_l5;Ly3(KZhVasLPTCqYk()7w|k$i6xWXP_NWXZg3IrN4m#29M%Vi5}?Y zttut+D@-+V3XTOC2%^Uad&}qED^teZ(q&#qaSE$QC1n2(p68cho(p&q88Bd5F3>dauH&otNMESy+psJEmnJyiO2GG zsIMtx_+DfNJ^bEFGqXSZNhZF5MLaddP%H*!SuW%QDD>kJmfxpLW;*lyGR54O_Z8-)4cRIiU) zRaKj|h(m_il+J!pqWm({NTdWtock`AAWBy;gXICEq@>pKfS-fz*{TG*H=ytCY!IJa zeX`>io-iEKHMnbN`yZ1jb z)Sv1d_7Nx@@;gtH0)JP5k!Co-0U7jdk(5C{Faos0QdWB23m7*TM=&gbo?Xaa)eEeS zv_hT*BS8Pl7XD#G&@W+xN*$}Y#F?`rhyfxM>zkq3wTq@FZ%goF@j$@_ptAb-uue|=DTp8;1B&A3iNZDmY$kgNTSy6RXNGRfos6dkjNwW z-)+TD!Va)kD+gEm8B@>CFZo;mmH^S9E!H2#wE2@}138|6fMPs|-sl6?@23J!`X5Gk z-%WS{ir2g+ak!_heN_&=_~-yg~8feMD;CBHrM z^1tFnTvyfJWtb%1>Plc=dXme;#58b#up*F@Bz&z7`Cm#AdN4_deD?gcS7&MHcmJHE ze_#Lwr9Ut%J@yIPT2s=Qpt2kmvC4mWZ_@!SZ2!yGWMqKr4TbhrV*h?b^nw^Fb-Q=1 z=l>ypEo7i~0s_dl#MwzQ>6<K`=6$qzD0dVzD&0*PRAEgH-8CLgmdaO?Y z0p`-MJ2Jh!@^V&I4#+zS8sMPu{`UvHhdF59=E^fCN1uqVZZ4f8dAERz za_|#MAEsmZ_VI3rVCrCPr$?&qeYDq=EftqWMQzZdvAka zcZPysCQ>ZJ`h37Gy~@>F>^{1ruj2B7YxX+M?^>km z-BUeQqgzVN0OSgiHlm~VP1Q;#C12^Uj>UR!FF|!bnAO1(BhB+d>>@|`h~JND{pg$i zfetaYap(ZZ0Wzrfbqt?w}vndQVC`gs*y#r7E8J6&+`G3vs0Q(5V)zt5^A)|AW&jIL<-E zHx>OXPi~61JA>f)8Mo@3{|5|R1S&Po(&bOagK|`#%F-e)*Jj0#`KO1}XMNp9o1xxqJE7qW546ALPEIE%naTnrG3Vcbl|8YEfhPswH$%lNN zTV#uJek9ZW->3t8pI{(5AH2ykJR>^WFs`j*r)jawo9qnwssvz~tEm;gB_aRnaUmaZ z6PERWwLmkw@xJN6a_D8w$!=vQ`AOF=PT`bxUB*ogsf2VR0`+*-Kq32+$*OSPpiK8o z?Z17LEaFAI8=xZE^8uX{~ohSzK`u7B zjrR-JKnqa)FpYHY%_|Bz(`HwCVjo|n=*gs5vXgN#y3&7ctg=oviDsnT$}S|BKrts^ zhkB4gW29(1|Zir)zAC@QZ8GZKS!mk6vKsc-6Mg)1d7}D%}H*c82K6O&6-Cv0GpKZE!)I zF-BM&D=Rq-(CvSkK=Qx@k^^4DC`fL+c=S1{ip*A}N%0z;PXiAa>rH|RHjcYHb`o2m zT(1%Sxc_sTawUsn9z(H&2^~vhn%2Gky|LYxQ)H4kx83JyEWjwtGZu%ui|zw~^X7U? z*6Kt@xJ`~tM_UMH&zh>xW*xDl_k8(N8l&lb69t}ay(6fTuO!{BszyOCRaI$?RaZTE z7tAHYSNr2A6QHblI!bmU2Q{Iz0uuZ-M{xRj34Kp|MpO-bTr`llalBhvl!znunP%1h z#BKsW<&A84KAm88TyCS3Pt_x0GB11ebp`&2N|;msy=-HWh_Y7cK^vU?DO`HTV_sOB z(|YQ>W_jw}F?n#5>B{u9FGjx@^dvyNxkx^qJD%^(m<$+-C zd`rrWmgG}Md4R5t@vXLrB5Jh!@}kml!#3eS|Eiv5IY(siEzz+OFVZe{tA zmb*u7sDT`Z<{u+XYlgovJ%0*1a&Ew<1S8MMqt^Zib;w#Yi^z?!L%)~msS-Gk7%n*c^;;E%Ac=X*gg=7wdDFR%q_#*ZAkR3nMEq=ZjzR^N}L#jg0!7)KfF{~=X z`?K`}+KAL!jsl@E9GdJ>Tu@NZyZ3G;*Q~9V+xG|WNVT^DUclqfxojH!Zv=-5*)Ln# zld9Ms7)P=w1iw@*53PcRF^q1N)3@{=blY}-z05Mv>u+$$i~kg#r%=S*Gz2XAJ^nB!EQ3e;0JQXl1@yu^TDq| zNVijxy26?8jMsF9fIpH*E5}J;t9$ylmedF~^^J5A400cU+S4zNtyJ(-<#(q@q<~bWN>s zQZj5dSlLa^F0^L;D)zC%I#HD9yzh@Ff*awYg-RbL9&QZ?_EAfZzjxGpCKKVY@#7UO zJIxrH|H1>N&x3Q5nQsU*ir;&gqr8foBO-n!+kV}rNJ1RPEyX0l8FRuO61+d6Xr>^F z#&z%Hk?tcOifw97N*qX!L$1bc{8=ejWCt@8q$0`b{VRViIri*}=DK#>FV^zicPR+< zwST>GAmEb~@cHrhbs`iv&5-5cEGl1K^Q?#DN}@4A9R8m@jH-n;MP`B)c&*r^{>82Ir7y|tl*`xMW)P?um|YldC1lj zvH#?cUK_|eGZGT-E-E$#&HnPYn*A2!>HTX=u1)XdVLEnV&)lPhVq_<2wD10RLU_mq zJa>Owvl`b0R6D@m=A@_hsJKbO;5G!<@u3PDiJnETmg(+>gmk{3IKFbF=1DFEGi^hg z{Pv{eWU2RBX^zbWPG%Je_35sqFRJWxoZt0){!>|?=Sa@W$s!_iXV;!?tySwzpKx$z zfLWu&vWY{Glb)|R4bA0)lQ4U1*U|fiq=j8$6i(8DdZh5b4850=Xs$iwuQU=88*JG_ zpZU|g=)0!eBgu_m#2vqj7O))o0iLt(VBVZ<{N$V*ycYh&C%t$(XU+?x>ag=5B4JKJvBt6+bT;N}hSx*v{@Kj;w}or!%?#vwF>{i|FlRhDW6 zo?L??O7zcER4dfQ%(tgSe;Og69oZL^<)>@xcK%&GUTm_fE_Q7|ezq1Fw3f9O$4E?s z`fNBAl;f+c*4y)LaT<1**bG+tt(e0HS$3ewQ{YrljGPfrD^{K&M4VAN!(+|$kfr)% ztPOW0477$=rna`8u2Og~SKtG6zZ}JudsJ-%PF;2E{|ZM8rX%$}rCsIn5IFLFsULa} z2gO^k-fSbYX_&5#3CMP^5mTR4Ajg+%jKX`$@W>2dqd?O-Z@FJ5kN1jOp{n4YVnHC( z@wxWOc)!ZL+K8m>@xa4o`Ke@&xT`ych!Qhlcm=LIGi|WKZ=ghqfUhYZH1~07 zd{3*A3;>&tZcMq@Gd2EBdwMjClgV#2^O%TFjkBIF#~150q;3itm00TT1dOM(LUN*4 zOS*w1mRaN6JQ`uxrkuj@l$=6zKulkyAnilN95G1=8TgjPHhi8(^YKnc3Ev7q0LuW4 z0Y;1QU%Tg?u|Ty`WEGNeua^IaLBMp;mRR57GUVKo@vrhEJqCnmi`~p{$PU~dQdZw7HBeMY_KxEdz zR2w3du%12-9|%^XZE{6!XNI2RyxuLyPowe(*DGF*(0b4Ypi(eZKUN+bH&O0+OB3$P z&J|g6r@3^hrt#$jR(T_FrMeZ)Amc66=f#yEL6)59Pagan?umx?6Ijg|sG5BGh>_rS zhFwqVv}+w9u0g;f`<4o!4o=$mzdWJ=DXZolb*ATd62Hf~CAxA3e8H^Z#AEc}5qiV! z6UyD#N^1|JFJ-alz(TE+78Gh&?VJ8HTUbfR<-Dssq2yUFb71LN!~}Oix$Q-!sJIg|GD4x zjl7=K{Yh-ci+$CRa~umr8jEevet(_tZypYfO7U0=Xh8uo28r|iw9%$2vX7=!^(vjM zdgF%<3V;q)cJ&`ua2o;@!*PKFX^dI9x!Zp>tfeIL-W1h)xy>}GIFQ1-F@wdfldor) zG=f55+DHV9*v~b5s+`KfXa~x$H2}&3A_0d zXCu?y{t0pri`1!g7|oUpTz?ZcaN_LmyKAul-?-!1hhgf16Jzg!Fq0~gdeOjNcK3vU)51y0;%O{%I_tD|u#FUx!Oco-^hP-i;Mr+-K3en;WrJ)Yv|HkEb~CF~{C~i;V41 z))Vcofz)H0_{^8BiuqmfUZr&Bv#fe&gJLIcJ=?qUZAVQnI+*Mv>Tb|e;RbuGj7Q3D z55s2pU3A=3G|ue?+59#_ztm3C)!Zo5Ha2_M`@k!v4*Y{rJt{&!HsvCTJtEe3l+MHD zrhgPei-Y(LgtOBvvk=m4zvI0W?9lD+{nBkRNAZr3+WO~arF-b-1%LIQI`1hDdJy!` zd2B`031A)ap<%I#3i`vZhY6oD(v?^fhri|3xYV^ISr9t4^%@Om|>r`&L~C|L^i$ z+VJ6c7??scfNOf((z%% z5GtEAzw>F}i;G2tDualf<$+&k5H&OVyvSmW=O*LGYc##w2D_k<`L%pezP+P?wbDV< zkwj$ilFRC5(cWV>!))J+8Fx!`T}3OV$@kcoh9->2G~mj9e74%hs&*7RVOq2()o}{R z{An&Ql(lE1o+R!9S)4Iz5R>VNW#P!;-FAj0`gnh$$wGhcLiyMk?i`BTkYVVTk(b8b zU;P5Mpioj*Pi9k?yxk^c-hHPMEt@ZgQ=R3f2Q-FiDA=e&$`CVncAE!~drLnEZp55RYL| zLtc1$#UX#QGPA#vuZb*)^Xvn@g|-|%E`0(U5H=lYDqgC-KD2riKZ8tPs1rZ^h+9Pn z(gTkbSP+bLwRHHq*}~LZ8&X56uiBKUTVl4#Uvc3XNApRk!t2<&(19DbNZA4ebiR%d z_zM>8`2j@gcwxdL)Ky3=Y&t@R##LGxX$Ogd6plC#ko({p$F@2pdxDiizu88_2lR@RG(_cO_7y4Twwjt$KR!lGqBe$tGz*>l?IOrP;_3%vQ1G}#cs`8P%S zjm)trkvEQDTCdb|XUuo@)&Zzwkp^H0JXNt{aFg2<{={!H(9Z)qj|@ngMz42tq=`9i zDJN7?&=pzFs67%?&-wrp+2u7QSAw-l-G5xKsoD3*rxxxX_CWR@-{h;;m!{Colh;W5 z>fDE2?c$%nvg6McZ@Yf5tK`$2*d}$K7+vJNS1O>;w3w z0iKDGJKB%zdb67JE1L9mY{n4GINW6FjX-?n2QyR^L(-e(KCU3?@9gpcQ~SGISIc-v zL$1&mmIRkT)P^}rr11|{NMIZ{Nsoi7eslVy3;NU^J~(a^A2iZu#lN4!#%yQj!~Iw? z(3~IKV0sKYxKGo5S{^Gl#8YlQTq_f&gVdCWNIT-3+QH^p#IvwyA)5uT!u9(UE$fp0 zpu{Bkrp@U!mxSP2(p~xZecCkpr-b1~k^MKKE#0DejWM_66Z{urw0`rYIlP9kWkM))U&|l!`uysJyMUK}?lFg?lxr_s!O<4M#${GKF#pvn7+EF$-t zYW#oq@V{+>S4UKR038AU0y=%GUSck_gHw`_B`L~uFF)RN3tn@wKvY84`q>rwI>XKQ za4lISE@=zkj+cVBM0fK?w5C^-sV=)B=du`x5{K76S>pLca1< z{>!$-e9mz~<3m)Ekqe>I;pmXo6>>*Yoru70?!4&HaUB9U(;SG&p_KY22qRw-A+k?B z2+T66&kW@&+s>E2agaGmRB&=w21EXW+DuwK>hT6hwc=g}!GMWXyDIdu+IGJ%A0_EF z0_@~x;f=7V0?PtUU`cR*&O?6tP2v@DCVWT;lXzqFa86b|s*Q*ft~OgEbSAdRb@A~`Lis}$S9(24dU0*)A?723 zg+lDjeD?AJ$z)q4L(L6(t)2OjB>$e;vwfLzjIT-lUfi0*vlcaad6SNAXlZL`gxFcAS^*gr&^qi=QJ(@6P_k@QM`zZ zp)WKUJu5&QefxsFcGAnkQ4PG-50?sx>f{BQ($3DA6Rw-#i2I9AiF*XT)pHG2rzT-s zC*7CaAQ;lZ!&zP)!=iXFE0))DVIzuscI@@<6P<4VH& z-oV|IWWMKPzOP}^o1)ENgc>?GmDwGN7#{kBj|&jo%1R6>{U#r-AgxqIj(Nx;;7!B% z^E1)3&w>DUu>_R5k&Lp~;&J?LVbU#~W?(v>RERN1mwJUk@j@kQ;Pi;IWXlLFXo7JF3z_dGq8b~Eb<-B9#dbgS6;kq z8HWU(C?$5Rp4KD@3Bhhs5i3li2^L`RiSZ=F2`4oj`^@K92Ilm?2TD6!V0Jy&nR)+( zZTw{5h_?KIR*qCiHWT2;a3F~X(JVGmxevzyuHoi)g@4>q|GDo0!*>M zFTYBh>m44DrTOnz)b=>iUdmw-$CDpVzBc@`L%JBj5pyhb9%Avg}-JKrdL^Z6+I#kSAV4Q7<3$+*oNo^c5hq-XEex+;qH&O%|nSHQ+N*57kgL>R4%T-dpOI|hsN3n-~EwJehdh!b+p_mC_ zjt_fau=h}1G%n!Fh7WgEj~~O^tN#_ta$ns%&3SpdK$C&<_}ZlpnwtBzvL^JB2Zw@Z zG)ZGT!>$Ovt@0UN&Q{Xo>v*5~Gf}tfCC}9XnOJE{^vZDJ08JTWAdUeYd(r4p$gU1! zG8A)A9Z#blD<~EU7xOh~yh^*5tCn1>;e18g>fn)X`4*FNM#rlI7Rvrtkwh+=g zn8(5xcy~_iMl!7F;~cZr2K4IJH%DdSJ%ueE-zvUwp;7fSNIEz$k>QJa4yNH&QOnOG z{?h9^!1FlzLD1$4Z%VMJ$8gipTGI;1?{Ids@0R0Rdv+RSz`}Yv(o#-IW|TcT{NiZ3 zcKc80CU}vS^6(z@rs%J>UMuFSwt~JH{ClitZtME--yLS~&-xsSOt>L&t=fsfRS@Kf zfB00?!)=QTtdyR3WZAMwMk+1j+9PS^9L!$^=K0S~G-{H>hm1w0j(96q5Ct3649FZW zh+Ts8mH}1aTR?xzlCd&O({{+zo3!dXbFR9Fc~YfR#hBG)KzAP(m$CQAI!m2u2y<4* zdcW9u|IjYSyJS+NI@|Q8QR!GcgE}tRSYv6i2z}tOZerLNkhE4Kv(brXT)N*_EWI*u z)y18~r2;onK?rrFr5SHdhB`?{%!#EA@(!P{@dr-pA7NH+?K@tXoA~*2?RG3J(Ty#n z?|ND6a56)9qvY1btE=cvHV%Cdoe?G1crGgqZOwuQ3E9?#LZXf0C)Dvbv}Ez$71EwcD-r209~H0t8?%*TIT@J8;=&W z>R64HGW2LgF`FO-6e~o%X(EU%?A-(#yU~b=clNM0QMyTdx9$iQ(Z65o_`H4I7i8{| z$GBPZ>}@scH1LF~pNsG1&xVuK?7D`N;`*I^yoPl@mg(akg_;Kl>-YXBt(JNnY>FB% z5Bi{_EtP_tuB^Z2#jp-rFx0=ch{Vug!m7;jkIyv@-MX$T3>vNrPXs2ApBqaj#9{tk zWp0=^of{uw3VbT;n8dGWWz9w^`%{Sy@$irP_x$z9C*tBHGDDq)`{zq&7a$1#x>!hZ z*x!-=<&;9KY0lYt@Zo>)J~=L~Sw7+z)hsPu8V25%BSTSPMJ{$fFC>*GAr)M;w-j~j z9K-k&y>0`Y7~oo%6=YN#;bOTXD$!@`ulOk0t<%kU4OsUhb|L5Ci>Xu${t7PSBbbF< z86Q7*3D}hHjy4KCxaatk5}0EGRzIG5|?tUb&9qqF0 z)Hw^ei&uGrPWY=rLh5EsY#(zp)s?xWQb>+XVV%FttQp>l8K8U;)?bmETS|$Tx0e+n z9Lo~Ha`2CHoF>GEpZdu?pQC1w{qb3IyT7^rA%(@<{S$8tB*mlv0w!3V!yx?yB1R+8 z@d2mfv!jrZ|CrK{9^%Ir2cKTx!0Op9#q7VB_2=1b;vQvYO}^6UzC!RkamWJLk9D;Z zWDgy>kdne^&PF*+kdl(wE!J!D6#T1CfoX*gm+O060oX1D6LP}8R1F6GZhswMH;+GA zonaJ=`*XRF$;F*4e6o72MY>6>r^%?Y_!g-eII!kL6ljJyui*i1|9zgkuCx-!xGWOv z!(h2SE>zus4vkF>G7?i*%s7oP{ZS`Tr^@aCti9~6fTmu7{?;G0qMWmneL~k&s`yPv z@>qBdGd>q*7jLB22zP3~Mgj;`97FL=d-Y{pRKV*^$!s?uN4s(Bz1bGm?rzLxq#icn zW+!*B>pM8njoSMlW{rej*jO3rYv5{zwPnW9!yV(ki_VU^FhH4X36ftm)>#sEX~b1{ z5v?VHZ#-@K4u=uX()Ot_F1u>dAy)*OGllRkeQFZDeDLRtiT2Xc;YYfw>0Bu6u}Am| zH9bmw4bpR-G52ubhx7XT`{A9fmUVd6QOCt|^!waLpJ|&mly9>>s81dqEUp>p0|1U+ zhQqa5-B%0d*03V*l~z{8VoG3}X`F!Vz?j|gNqZe}b+|JiDBIn(dt>&O(x}T!;0slWRi-JTkwRy!!FUYSByDCirl4a{(p*bY`mZmQJULcyXJD z(eYFHh_&nI4O$pBQU%y^x*BN8E`0I#f0RPyYO)_*^$g4&+hBl_-s5rjoXeq`j)F&C zi&n?mSi0r(m*oPa>`-XcoIWx@OCUW}s^rsB1eh6n(RkXO2YE9ZQ2K307cE@Eqj7*3 z68c}X4BYi#gIlC2di@`^Qj2pfxn{a$S z08JN?)D&yP2H~bn+)JvDKVAE;Xr2RawRv@x$DzGruSa#Pmx7Rgp6D;npKBi}1 zK#2=a)ybO|-D3*H0bv+8id>qJbV=i-Qz~&X*(i{l`{06Pvs#6W>a{<-);CqF;)3Sg zZ`y9GHw58MfpI9rx9m?sm%6EqK9ieY1f@znf8H&hag-4j1Dnix+CoF^x&Q9HfTM3_ zQG70BXIj*xa{o8Ja#OU=$?4o5ws!i&qx;HdacIm&J2uUR@gEb>!R3nMyPo$P^MpY< zn1}5-^g<;mPGbrCSCyHS)Y;=-&1US1-G3lVFn{%asw6In$8PlPWuK|2s(v(+7tokZ zX_~NJF*a<<{JRU_PftLnqQ8n~EYyoT_ikJxF(ALV;A9R2L)CnG4fN-<`F?aMfLx|K z84pW@@vL!J6Y&z-K+IRyqC7T^<}Vv7c|E_q)A^nA0*GDgC$@cB58bDmBfft%AM|{(_l&u}1i6+V+e&==)qh&&6o*RRB==x%hP3 zFQAW$BAwPIBTIs)4A{TX3}LhVu5XzVz>}^IvG|0XK0p`Q;|=4Us-7b}LhQ`&ct!&| z4|y-A3@U4HxOJS4gV~{BNl11WoE)@bTVXdn-`|5K%vqEHZyKwo3ti>Z^tP3$7I|4IBA5WgNY_%I7Fg8OFYN=D3Syp_^zn)F%Zb$hU~S%Y{3QLh92xK zZ99f2FAQsK4%dbAlRuN0ix&&{T9zP1@s})ybXPme4a-253A>m@Q2V2FWcG7|)T*C^ z;$=^JRE&Ig%hqhsgZm6?uk%fRDUn}btK4&8oM5Q=nk<()zbO|1XO{SI1UfW>I1Ivl z6z2PcLPy==PvuGUk@TIJdi`53LA10X(=l(tKIX(Ie$p80Rk>#7kLDaJ7Litxb8P?v z{Kt5){G#2UM%Sv36dmAG3ZyA=PH(&|XtZqMW4;_D%r(mfUH_^a@fhjw1^@DIC;B-y zh_WDk+n~QJkh3^ZuFl~5HzAd=y@5T%wnF?wP^wOk*HhvO7w|8-$3Ts3^L5! z$~Qax!?UJ^gOC(H{h5jR{RQH{rK-^nz~8>4VKW+EzJ;orQPv78Y8?E0FWkQ;OeTOj zsFjePy@79Z2`761No%0DUXonS|H2>WT}&Pt7|>h@r&u01L#I`lJn546%U4~EXlv_u zhZeKQqfd@DaVct_c4f)Ymky3$(m7sID}x%d0s6ff z<~1krADuJekdGXf==8r6y6Tr{)|i^JNjT5RCt~3zc=Y@7+;6Coz;B6{Z_RS5`a_y( zNw}K0TUN6k-s$s5j^YEHkNWn#+oy4<_qhq0 zlTrLyM~u~ypj`FbrVB-{Rht~-&GQUSl%ZJ}T1#Kz8VSKqOMM2gEsWzAwDW(Z->~)j zJ)P!`j&s9igNTN`rrvHWB^HleesNCX>R`QX5_y`pH%R6fR)!GHWDE&lfgkUy4n@R& zV|rOhS=vFvi!nB)GYvgHs&S5d5>=7K(#>BJ;J(`He3_|L^7x*iXKa4nOBJHiGrN6y z>p_7@+G-m_uN+}JVgvQ6(yx+l4Znp1rjZe{3LK-G*BA=FsiB}3rQBzJsW5u)Enz)g zvXBtK+9ju?D%Q{-2?gHNRr_&u)`dc>t;rern2)SwZstzYZ{b!ZfnCX7aZibTD}o|7 zq4>%2dC6}M|DY*W?e(MZ<(TzP(15y$f+?j9(KX5E8v~Zemf!I8<0cIf!*g62bMs!o z#;0s>_|vS(yoy_{fvHnj;qQwE6>{PR^by0Nb?^Pog37gf2Xv(d=cuk5UPq|ePI8%b zS;Jf<04puZt9#`^Y-`&YH(dfUB*42wk!4@_6~d05ra4UI**Bd>U$fW(iNVqB=oT1# z?J@E;4wk#IzMtqmW-IG)DO=<&}Hj z&$5y{M%uNE9%Nw55S1$Mz%ne}egf|kn|N<)233h^R)<5hf&XQPwp`+J3xk~0;f0qT zy3yB%yPOLF3w2tB z?u~WqJY^9lD~l}DThHyx&)h$*#xu$cN(yhLB)o_nTnoA57~ zf3#~GeA=XparDZR5KKp)pD%8;H|i^O$+Sb~%bMjz*5p)dS4$}l?^2&`aI$wb+54W6 z=O_Rk1Q>z8H}oag=C@m%qYOh!-iVhOD}15N=aT8`ixoxw3#{2y*RbW@AuW}f_6xeI zCD_44BqRp-mmDw(6I%$ve%(<5?#j9p#ZUE3()ShsAiGBJz^2OXZd2|n^NteALLSR# z9jFBr^(I~w<>Bql*fZxRJs3}W?EZtLRcb6G#7F9ZZD*6WPx~P_NAkk6z{`9B^KR=P zqPMTb&fCjd0bE>l<-4syqj$W=ZDnAa7Y7U#c1Wa<7$C~8hxSCO;SNoeUa-7U9*iEE z8yHhn_fC(I2LPhLc&T*(PQ$!bU)iQ6rveA|E@kVD9m>mI)W-S23gp-S+0ZW;Pwmc9xZLg!VbsJ9~gDo1jM>kM)f zR&8~NoC|3uwo&MfRm8aN6(m(1qhjZaHc>DAD5RPa^F_Mh^HuiG@Hde zW3$cF=NX4|aHC&8iSPhroZ35K*NQUzi)7>aWVXnrgI%rxN>a~<(&+QW9~M0-1lnN{ zDqvol!oMid1kF)qdvkc41^BJSZ9a&cESb`%Z22v1_uyo}YQ)Sca}w|Dn(k@~TK$zj zD>Bz&IEKn*2|P)ElSNSa(WNHQQPFapkCb{WO#B@HR~~f^`ozTiUkR7+%SddeAWL}R zWeLibyhftsHIj54Cs&>ff$c;dtHxSVqSVJ{dLPT$7I(Jn1$y{O*d#*5@tLCPY;>$W z@A^><6TH~NjUMz#-rJG6!%gFl15<@_YctGgQyTkg`YerY$s0DB_o(Yd_Q}w%S5557 z0ltPWCrGqsCcfVNZ$STtEB6{LHQCGT*TQrOUy{bV_o}Ec51fla?ok&beshT-m!tJ8 zu+nKnrmBDhxwEd%zoGRb= z41lKElgFD?>;;&XHZS?C82Wy`g)6yUV%+HBjk;s_Z@vC`m+Pz2>kBJL#t&R>D$V<+ zBqU=HW`sZ9S@_teRQF_?az1N5B*b3M0TKY5rTL&0^zh)3J*&v3$OU#%_BLs~`hEdo zpp3Hp-JDGZi+^jq#Fs3X0}uL0Vu@gy0EqBM@xp__Q+2OZ!0f#+><4b+n!X9tg_FH( z-Z>`SfT%kM|4O5pYjF5mi@TxOBj|J|L0)4?O1>!vLg>le4O?yLzIKi)vJ3$jvm{i< zCW|4%M`;iV$YoKDF{J!0Mfe}dvuJ-Oy(t>WB=0bwBq z3;*%hJWyV3kRg6eH~E5k^Uv<%JKG9bkH>D3x~Hob@dJ^H|L-g*LaqQxDu)QrIN)te z>oC50_r28<&F^l#f**jMVyEa^jjsAHzTY&gwGQ_(DJP1q_i3+&c=$)?lmXu6{neCb z`lW15$E1OFFMkj9KCzmS*f;>_Bs;r1TnznhN4c;71SZ3jMRJRDy4=*FgQPOXpo}3- z{USg|5uk$o;QpxD1{s0Z!Cx(VzO4*rTvz(czXs|pQ3#6T4*;gd>@Y{-?`Bfk*A68M4@}hkGZ@PMU zUkX%vrL4ThjUwv9P7|U9KVb>pN2_bP8r4=CLi9pMkH$P+%<5qNnoWJXwiL>sqT6N8 zgp^YT5U7tYP}Wdvlul~kKQ4U|1t6^Py@pqrNA5mA0*8cfXFycc5cq^>Yy33WB}_7(1kwaOIzjvQ3VH@Po7(z zWeQK0$$A>{fCmLd}{ONh)}`YDuE;IQIEL7x-w!f2f*b9-BNUmw-%@R!Hv z36rtSH@Oo(1p>z@NW@1z+A|bBdDQr{s!L z8hYe~9*`P3=VV~7O+&_~efH>x+dg2d3hZ3bU2wgY#T{}5i}}K5X-@ZnNAmr((!=Fm zmAMH%j~HrTSM=V^qA(NU!_$fck{JP-V|?C*Qc$w|?a|^t(}8QMZG8Y&k=XAol>^;U zTbXglfPGG|iP=Iu&BnRZe+)eB2iI;Eea`+3C_i-`sGjncWL3|irciGh`)vI;2Tif` zQs1KfgeDlW=tTLh+4v{}ki0^8X3LBIHQ!lQ0Y(q1H=IQG!>eX01U3V ztae&ZIV{MyLECJnDPaVVtrkA(E1|7M>dR_U&$E`Gf#QSAXn3a9#+{~59NmkkK1e0# zK$dpo3m^zOD}=Lolk#6Agx5d;H|3M-!q8gS%B(Vg_5T_YRcWa6Jvti&k8pk?{jb3F z55(j$bRH04O=oxE05pe{QdZB6-TKLrd6N_%Jpk^K$5eb_{qu3wdO|&|d53U0cF&iN z3sExlYBIDq3rM6G_H?`)UHpk2cebyB{v`?ETMu@&FNFl88R|w-J0$1tbjA51#3ym> z(f9s?idAb9PoteX?w%8=!e0T))ytroTdu(m!as9(OAkUs$zq2y|jfgO;!}6=dVvd@E8_P|H%x}TlJWxzj6 zd=Gnxe^$DUGr`s$=4y6wW|RZ@S^0wlMs8YpKn!P~6FSlyE0$Oucc|bKf|IjH&M|p) zkXC&qe0s_e1>8p4l~_Ns){f(W(6c)?jDf21t@)Y+9U*K+djTN1)mP+Np~oZJnOE*p z6$=-Qt(f(_sMRo=E7itnh;|qAy-C9y8nN7_KzzqPwu(`r&>Xd1bEj~ z%LBjFFM6o?ypa3^$u}+?XZ)OI5-M*FPFJe>#tXlgoq33sM|G>DJ>$YIGHky>wXsN& z9xtJ)%7GAwQgWBEZ!HG2cQwJBw~aWrw|avIUQ(wj}JL zZr>+zP)YeOTn=X{C_7p5#ogmc^s~&qle>w&`A^&I{EUGr3Zpxc#YqoLnvy0<0TL~R zmsTrjc~_`>O8}sQoX9C2ntK?X&Y>2Y zu7$W9rUx0&ANEikZJZ-B&q*D-dDun|S4Dm%xZp_+&kk9H>1Bntq9%UW0b5w0w zY>v9wQi=vW!s%bs8eDRX$t+Je`r?*biue)okAdMM0I(p1kc_pDRhwH^40%dJLPCEw zO){IZ9K^~Ar~XSD1nu^ZpxW1UH8jc%-265xH6PuHI?jYRe#5LAZ+WXrEihhUk4NXI z;R%d-M}Q2QIwoii*hSyHZD@7JPbJ{Wl5<{e41n`x(1HWqTNQkVDG1jXb5(|oeh=3a zMZTw4B;|R9RB9fHkN-a-daGHN?F4ly>#VFmm&UjnLenZe+}^%V;z!kO1W- z%q*^~uGw_+aEz|tV57ZsOa$7R(m9t+6rHTXZ|0qwB$0w5MzdLgg%iTM+hs4T)Rf}( zTz5a4s*%g*(EHYHROj(>9FX-mU8Yzk4R!!3;&_*VNVwZ?lFjsZl8-}UWI#ogBU!Oz zr?5#y(2ClX9iF?S!73zzjJmU1e^@1Dy$MeEmEmX9nr~@OVucLU)FT{eXK#;(0)mA6 z96b${fWXGlR!4kzTT@b-%r489=(5h?q8QRkzdIBZY%G!^B=+YA8=Ue;NEo~n z@==#ugNb(yp~I#KpiW|Moc}pPU)j*b$jrKtJu}rGtuxJ;(xrT<{~><>9zZ`WG7Ku6 zAwxT~Pu+s=A!KiSz{b6L3zRi2Rrb36V2sOs)%}{GUuop?PAh0P`n<-fSBU;%;Hq28 z$pYTH%KyhTc_;p!s9X0QIgUy?a99XlxB~h4Y`;%UZ%h5oq-Lk~=5GVw!ktzz>g1`A z3xH3Gd~g3WnuhwvhU5yI7Ix?jHFVB<&~hovdq5ZNefb6o{pSznZy7Jq_zDS-nF=Z? z&Q;GD1R8C-Bo^jtZ8Dy+_5HkVR z?w%CYpzb94-G*bWHxLK&+MmI!njs+{dip0#g~|CT*_E3iE2ca#At7B10?ETnE-#Rk zwe41|3e*XHLuI+IaD{m{XrxZH6LY(HsXUoyam-r?(?&+=5OFzHj?ts#YiuJFEvl2Z zK8ly*E`80$&l^rd;K9OCL80Wzs*E{LfHdQs881|R*6X9bzaIpb(Q58Ln}EI#P*D7R zDsg3_1d`yZWx6lpudOQ`)_?E^U>@&7U~dV2y*07pK;`}jN3j;nhqP)xAW+>lOQp5I zusN}lJD(nIxGMMFhRhh|^&E#r;MFH{hjV_93Bzd}1uJ#B`T>31xK@^u^Mm6gE3h`b zWPUF!7I_L{D_izymb#C52zLu|xkdss9XPe$;P#$9sRCv-ok}OUmc}`mVT)OBqO=d4 zo1FEZVaU5+e2*7@fVFuMv#C=1*oPwV7jq()`@;1Z?;}Q0X8&VOdEa-zusQzM@v9iU z+l#(?R9dyNClvAK8X7&aRn3dCHPy+aI@v4%kzTN0fO>YdjOmG&W0`Oq^7XTh0bZgB8x@gz36*N4ac%wc+0IRY7=^*=1Rd?vUEzQT4?vD(& zmK%g~jwUIq$eTXB&4_M&zMe%Z+Z`UQ21M01{Upg~qSpR>WpRtr9A92pW|P}TQoZ-B zfk|=UUz4J!@o|EJLN!(wnZm0(-Cw7F>`%}h!G0hH zAmc;HT2^Sjyj|=>S)a5kd8c*qLWv#?7Y$qLBZPltj4#y$=71wo^=z0MkbEECtM%U- zO0?l7A+cKYTMW1{&oCY#C6guJLh?|0-s_EPy#KVQrxfd*60MR}0q1}`9S%4=8D%zx zVi~z}B$hXhm&R<^nxR=aih+pL@iy~CoWckK%XTLX!-z2I;;_AT?^&k9#P;u3H9tbS zr9LVCoQZ$BSEIniQ4D$I+;W&ciV}cV_CHtqvR_x`k(#h5K8wy$@Y!r?s6mlEPaR4GECGa=&`u@)Z%M?z3vW+FIi7)v%_>TP>KLF=jniLlfm1K@ ziWO`Q$Sqy8w5Z=Dfj1$|gG?R$O5-N8#2OeB0eDJGjtxq*i24&|W&rb-SU>C(cYh;g z*tSo*^^VY>g~~0bugPbjgll#_!1*RLW+|Uk%37ItW>He72zFi(=Q($X z2srIm^nYpX;HR_iBJr#(j2#(-1+bBo+v+nv7h{c07*&f6KZExw5b!Y&6tw<(nvdk-JPxh7xA1 z1=ob9C+#2Q_effqChvDNBmVBI=pVO#%%J;2tp+Zyy;?ubw7bodK(P-Vnprk-W2^^z zH*!2n^=e`GxlxMzO9vJ0Sir*fBG?<@I$!PwfSe!PP_Tls;1bJlJWr^fU}n>VLQcFp zSz-TJZOXP0=p`ke_Gu!QLi~}ZSOUqriHv|k3Yy2KVcX6)LcFL zEvhl~e4O}oC;MI&i+IU%o73WsM|}CZ)gEp*aG^_@Dk7yFg-7S zqEt-3aKuupxi`#JNC z<9?ph10)>rN%2ZmhT@fFEzXr1#qj8TYSpkSM87RsdHX<*b*SBGoR>Zm+AHDHH{UIK z@u1wz8)?1;$}|*-`5Cit7XQ_vLJx@$0cZwfZ(&?q+%eTZyLMoG@ws8bdyMEN%EzpU z>d-h5(ZSZ`MzCwi+-C$}C;M!uk+=+E<0%a>TqVuqm+>)*RUG5LY^yLzUxj-kFme$M z6&k^N0hqAl`+v1H|Kq~C{Fq34g#;M1B)75Q=n+?*OG}MSHT@}<-jkkS6+A?tPd5v7 z>-`>_=*_60O-%12-OBdaC0xCJ2c{Ba+|-$q<=TZ#B*ofZljFFlKb<25q#uFj0Au#< zJdnKj=56PdyxwaK-SE-BuOc1Xwy3xxV+J!1I>Xo%DJFi0*S#E|!QTPoTk;-@Tq%+% zbB&NIdxI@@CJvA3a@J6(b$Arq5SckIZ#p~-<9hZD2xVml$DifVP}}P+KW1ycn7zs- zcB-CjE8YnIe(#+xKh~vqNhV9XR7QcuH*5z7n3*XkXeONw$v=CsZarBF3SxaNeT6;m zra%QHDbPABn1oU`?%XIpy|!+?(%jZZN?NL4!D=eDu>D;MsD(H?eJ>31JBfhI=v4i5 zNmv449HVJ`#{jP-4_-cY`?Egzwm?ZFVW(<;r-Zey%T?lBum9%NN}(%%`WV%Dhqp@# z8F!-=5!*k|tEX0K94iMdOk^3`4}cGt18 z*tc*_unl0P0`kfp^-HOLxe`WAm9)y4@LV=rAk@4?YV;Ef!2EtNS;#W#tZZW#Yt4na zR~U3dwVE`su`|d|ilnHpSpAoio2GGcdj;8894v9qlr`0B#(C9lOZedwZvB^!S-phb zYSCh5Jce;$Zmo+xb_Z|D`ZXfpPQ-EeYm_$j2lmWA$_+ddW4fehK!|LSWLbE}FqL_=7N87GHl>(j2 z!3>c1_uG7=&<_B_?nlRFrIfD6`C(a>P!_5~WO74=aO};u4U|_;lO?(*EYMK1h?%-StzsGj3<9{Q{rr zNPebrSrcMj1ye}lF*d5)ou1D499fZO4jGXMwSJ#?q#>sh`9w?O?!S-lVSo?` zTNu5l_vSuPVj|`>EyE%ArEC;ZtmZ)a{}wg^=lX8MD`0)JX#J45+gRk2M3pcSFOfB; z7I4(P!|yj6863HWIFP8;O8#sw9SlimZ@~VcZf)6C1*C2?DEs?r)RlT_{Z6VS1}}!e zL652J6|{zYPR#VB*rv*uSGV-*8P8Q6H~S(b9+P2ffOO5Sd11tHi_qL*lIcj!4x$f8 z@zn@d*eI?1wOyHUoxbbKz3dBn;bX?E7>Rl0WF9Eh{U77Y;ZvYqrz0S_#2YC8mE(90 zohzVw6fE|PcRb`uR_^UNOd?5B6o?@e@lNAr~awyEHM31OhgE zCL~kIr~=>am{|lflr@RtluNu{@u<&Ou(Aj;KM2CEV)n=Mb-3TM3o7H?$9+|Tk4I96 zv|7gh)Ix?0NzB}&pJzty9RV8i0s>un0~GNsz3B%XHpniFWJmbIWk?$+Q;1>kD!F0e z!ywq>J7L;1o@HaYu@BbvDNT!ZWrMRz+5BuI`#P1lQ_N~K;5IbtD=P(#JH#onG9MKI zFn>_h@$4et5yaNaavQ?r$s@1^J>MbZIkQKvDb%6$$v~)&#mA)yf4u-QLT^O|nW`3E z?1#d=nwn0FJ|AUKmTZb<*|x(Gf|LSkf`~xTv_D%@5x8tK93kaR$Q7nJzn)heUtXjn zux$mRdq0~)WbMKMU3p~`e9qu8;=wqz<2nhLHcMxw)Bf63Wh{b^;;|kPRaPb)?lpd6 z638(7EO!5&FjbV`Py)!*ajF=VGVw9Dk0PA~0A&9UWp5o;b=Tz&D=8t}c_gGkK)R8V z?ha|`ZVo9SNJ&X|BPrb?UDDm%-EhwP<(avE^UUo%*SvFG=MOl!_u8x1XRRH%4M1wV z%D^q#5bkr7B%W;@8IpX2=9g%;mPZkX{MTmSsU4vk&ZrdyO0UZ$b&(Lw1{gsRe64wsd1!KseQ_tD!qEN$SP!ELUqEVCr9PLul;&5S_T77wl0 z!00KK$Cqxh@sA9>!pn$p_NJ7ZZu@uNJdWnbilk((sFf8nVS%*uQ}eI8K+1HX{xp_U z6Ag_NcYNYLo``=6{XYTVHWJ!fnQ0aEhmva(P=*h8Z__idB{?j8)(_2xxO$Te!?ys< zpxgBs59edsMal`#468#%z4*wu7Z9)veVF7v@{r|xZ0QhS9P#wo=4b;9_LneTX6|bu zY>q1&g=NRNnW{@W4~t(=7)q9zo-F|L(Bs^6pwBboJ$_xFWSoK4Ab8QdySUzmA*l*6 zew@Dpo2>i>B5#K1f2)@wlLPa#g6!mxt)i-cDKS)CGmE>+oUZg$03hI*C5CSr>#!3s zcosJNhw8f<4G9nEi%H%IV*6yI#E%64a^tm3#H(x}7ohjQCtAJ*r6keNEzETJz*9vf zTnk|n7&`O8T)bvNNIInTF0@eiXGVY+GKB4_mWcKUGV&>oF~Q7K94N3W4Y@3mBi)Zl zia)~^^6e~}!+J*Ibx|~YMQnG?B{4IQYU3sTQnX3e(Q3x?&=Hac@#nKPF^FyHX)Eb{ zf&63LJk3muT7m>S&e`YkVMrO(L#EL19)J1Exmhw(TftE@Oe-pfri&yUaJk0sP@+ZV zws~Xj{Stb6c4eyY^Dk1_Sqik+nx7d|B3ul7_qCL=VuZTxh+eYc)y_{?Nw#D*9kW~s zwnPR;c@KVy9`vEZ8)!UmJ*&DPjxMe|`F6^pX%yK!kvMM*`0p(=Kq^UpV)7|=^O#+@ z4=B4g!N#GDo1#gxL$aiJK6ZZC8D$WwO4;h@wafAa5WNs0r~#0m8j1GdfNA9F5jgHc z3Cmum!!TPPxop!~+6d$?`n4`NwQ;iLc(t3>dGXr&3Mf_9i6I_g0VxuYdF}4Ta9Dj= zHhY~P<1sL0HB^r1CDr0)hgk?F3L2+TR2rYLvB@DG_Qypazs7o^Pv-F3V_`PaM`bh= zXbI?oPhP+*KXBZan=x(4@H*!4z<+9t`*EL~Vw&xX;y6B?+D}1yf^{Y-zkB_2h-^gp zw0`8tVB#23zhz5c>;IR>>t$OB|3?MhPxuJv&hA}P%TspY`R*cVF>z9lLFvJzbg3_7 z4l!A$Iy?-O*!KKFq*YQ`!)G&F^`6YDtbmVUdf&45*tIz}1-Ui(?eN9o{bKDg*qC2! zhig_c{b;KXWlr%`cM#P4n17{&wpPdo>jm{i`tQ7JecWA804+V97$#qW`E`7oLPj}$ zlMz0OrZBu1oLJma_yw8gaZ4TaVT&!LA*F@7PtGy+0gb%M#?|$zsk++hSfI_x6a8%d zV(8j#U<|kVVJfJ4#r7Td{WrEip}QPpg1T8oPe(USZT6d&mj?~wj-v35^Yx~AvKbr_ zXe4ts@h9nW$1W{7$K#C|vo$O?hZ?s&50Y2K6r4h7j3pV=a?$Pd&my4z}wBPN!G* z0&lIG`a^4m%5b=R+EU0hVnRx0N??cWW^3goB5#HH*b(VBa9=K#9OTk17at%ls!i+| zc{;rjd5-);L+Nn-$H9?We5sPsyzjS@q-rLTZ3>lo+1)eV#=^tikF_3hn2(S1tjG5} zQnQ7iMYm%d||%!T6ic4f?%V*J?5i zs3a^O-qmzZ9MsRM5+ipNTEQJtl+PN_S=PvYE1^oj?Dqo4XG+V_;NY{D=28u;8d z8cg`QHGCoHu~fIqO`B}_WpK1^j7rXN^=`J_Bi<`kA8mRTf21Q%gA?4fo0~kK`*3-=#Gu*a>#bv`+ZpeFku z;yD)RkB1cLb7bTPkJamwz+I!nZ!Rqx5EMn_*a5IQT_3g5-8J|WLYIal` z=S>n$*Z6!b4z9lbXr?X0=7MiT!8+Sq6uSF>#*iBQ^?U!~im9Gn7lEU%yc{VW5^+Ms zV@Q!{+^XiN!gz?3w0=a7hv*QK$yi4`c`1$BqnNeE_RpBZU%FSGp;QJ`DzrUBfrvao zo%oVA+RoVAos?tEi5<^96C9NdntUp!&CJvIWIFlC1;{VvjC=OImU7%VT=v`_)6CyV zVA&%}en2n~y=c9%y`{F)UfdJOT@+ z^_eBYwrN+1G!G^TNj{yIEZ2F13*ON-ILB_}=rOIbSCH)vk_s<0@Uu(hLeti+dYim) zo7(>)Z18rc&M|a!iNxMVxNVy%%bL1{G^XEx^g%VO3S8D?06v}S)q-|=jT99gLI{l( zV)kAo*z?(T8hW)K_9?wPK?MeRW~3Z&=*Vu7?IpDS3d(50D zoNCw4s~#p%#ay)sl-iNLw6SMuv?)WyH&G-l7 zjZK&yc+VSFDIfsDJZjh+m6f7v!d*5WRDiBl||tG;76E{4dHBb zKn<-m#|U|UEABQGBbSwft-8~SPOjbhQ8ig!$iiiiK|;qgp}ANlr|`IB!;MOOT6{3q z(#4qntr32brR_3Tx6zqZyg@3GeYN@Ae52{sM*2oe{PWQV3=wcv)nJ;C@36}-Bp|^4 zNXMJg!|1nfn&(vE2)WP~=!90ctYvw@@-T2c(mzOb?>p?}#ULV3S{ z0946TW3*_^vQ%W*=0O@-MMC5c+Nzn}cE&_g$NR^68AA$9ir$#tZBFX2UGBgfH@|9pFfA03Ez-AlIl zF)$Q;x2D-+4X)Tbl*e8#l8mo(dt8U*Dn!Np^O`BOzW92inq^X8zX&N>j=s_|r9<<_ z5?D0Z&zr?B?(j(_SKX7wSm~S96PZ**s>R~exGR5-BF79-8ro-efwi*52zh)SLajBx zxY=<(h_a&->4RRNn^OC>!T=Mxfx3f9K>?+1eNX6v01;)L8nH~G^n{$BiUBta`tg-S z74tEr`}ZJ93+r@#kswO_d(&>-ren&mTS00HUT-*_(6TV#lFtG)vzHg>FEBuR?@PozNdhyGM(jd=fkH3Di)Q1xS@|XU z?t85R)zzGNW4q*Go*idz{4NF{+HMBm;15% zN*I_0zq45e9-yHrM1_|j6%qNOi}o!|g$-w;Y@n|?!VD&@tW}B6S)wrl2;gHcrE$e5 zpV%rz_|zzGbEyfkQOcYDOPd@FaWkK_%C}!NI{V$8sJexe@e{*zNlnWGbm2c)KCVnO zWtX!}d5cJqo#fObStUIs{%iYpwj}C?5joXLBkDTi$@oGBLvL$D90zRm%I#&Jl#$?4 znS2Hhom~sGM@p^7^Y2Ug`wAs7GBZJvx)SMde?JTpLcpk2p@M7q?2SZu-b-uev*Y~g zrU0?B;pi&IYJRrRY%5RBiFt-3769x&CbO3p_$}pknfhsXj*A?y53UXNs{z(C&B|BD z%XHhr8Mr2Fz|6$2RE1DmVu344tu}{->f`_zM0oT6ZH=8zR9p@9c=mTC@t20+%BIl} zd?jK^tGQ=F`+F9NncP1hAT-;|v?1*og)dE935f%0$rNK;-?*fBv%b0%3{^Dyi;K3+ z_eF}W+QkZHQ_V(KME(~J;0XpOz_X=% zCieHOfMi|(Yp0&6-V`}Rg=g)R&V=lv!qclf2RP>JYt`s~^3jSyrqW~WSj)K$7IAop zxBa`RfQ;8*>XPY4yk=0{*MAn2|2i-eTHvVIa99{<{-#I$NdOlmfW-GRrE7psK)?)t zG-jwW?#>+@Z76}6`d_|`mJCR+HS}%iyfz@|w0Sbr(xchb)C$)64eq-EsRCZwSBJ}V z4C*6^HT(DjK`8iDwUzw5|MlztB*aM3eu|3i@_&j;G#LyIPHu?)(OjLkXh5lgHvR3| z8npxpvONR`5M=%KYU;o3;eY-&lpgplvB}L3#($7~9ur{edA%w|t2b;o)>7K99e_c# zA47+g{VY1~z$~U(1MVOG&Q1Slg#P2n-n=JMl!u>QAoq7xz@z{a(S9$52Sk`4RBzldwtA*SB-!k0>J@XEBb8BEX1!*F`9*0 zKRRorDpgocwJad`@?PIj+A;W#&-_h&{rkTll_yc7`{sf1_to`QK5e!#k#mhE;hUm7 zpeBmhaDc0LG)!rJ-y&T&(9(u-+dgT9KiTzvcqR+bNEpeOGJmo@+-raxK5#CqDF876 z(BZ+T0^BcymBWq$lJv*Iee=#=h?W`9nSXxppPvyndph)LsdMhXFD{z>Nh`9KTS=s< zW#i#6#(O8S2@feLe(+yo{Qtd|XGkV^PkJi+ZPdHJ)0!vtNrRp@n*GvhfQOfm1+iL|89ufq zvaMzQpEe=|dIS6fO!X?>-*!u7_q>PM=;R6C>_6%gd zB$ulcZ~@*2T|=<2Zu&#Rmaa$k->LqO{182l+LBc^m0!2+yKw7Gp$#Rm z#NYQxh0y{hpUS)OmYG~ghnJB%_N%aCaDe;gh@l*K_C%>UzS413hY4&v01ma1i191 z3s;p8`K%Zfo}yP80~>n)6~3Vq7^^m(mkFA7dk*uT2FAbe^>1hbO`hnjWn)B>t6hvC zrGCWTRh7d(Zu{v;jKe2NT`~N+`jg(dA;8J)W+gUV1!aQhERr@N80@tapS|}V`Igll zz4BJ$Nug-ynx#|yC)rG7V1W5nUz_d)3g&%I*Tg;SdQlwLHY%+>n62ObLE66*q!`N6 zv2IFNkp8(!LJ{C(zGNn|@5{>47QjC-H1%uFD8W90v;tvMxZR!!Oe292f9k%!DEINn#J|;hSsV1{Nj!0r6!B=Y$9k?X#GS_tQu^dD zG3)i;9$oY*RtQ=Y{h|{QDaFFMIm0e&W0h71zG{va>$*Q3_+W4Pbj0`n{9XT4`Anl* zOUE82g}w2GMsN!%`K_zGkZC&k|41DZWPmzs@a*WKf2f&gap3D;pa~kZ!YCJs$`wUx zs?k&FzA}~8hz@{9MuLOyX4Y>6709PWulGdtUg;6TwesEgKK#N|p^}sZFsA0Tp`X=w zVWg0OMPlM7(!}sSxowfaX!gI3#stk@eW*$5k(X)nXBP^a(ZmNHr!Cbjl5diIHE?Sv zcnRGv%N>V(ch9sO&n6tqli2?}!KY`U|7EpR%BV&H{~&IAIKX6dMeRzh(s^@Z^*%a9 zdeuq4Hir_c6bY-d3lLj(SjkVF{hf%j0beci zPq;6cnd%X*l}jWj_3j_U+xnNld=%cfE0d<(r(SwaiR$R2g3ShIKu1%v2w??BPSkSk zB!_ab3D4r$sgN%nemtz1nYNj3=O8~h8N(r5RU=D&%jQBXoU($Sv4Q(w7!ETt>6~6U z%1Anw5*pbY_EMw;ZT#rS{M)ymn@pbhOYFhgZc1~w42_#V(n>~=%3dp#wYg>j?XI%Z zPG=je9&WuTd*D5z#(h)xQ5ldgBw+0OI8&p)PHkE2X33U>oiR%tBUgk2u4;aAe#G1^ zdj{t@B9H5X{e%JepUR=?No1-9;*I$Jv>s!ChKKRBoGkxHt2ehyWhxziy&N5xhfS~2 z@z51(x|D4YSFz&FYuP`i2L|Tpeq})m2;lDvfT3;&q}i(cWmJ0K6D`fJ`SL^-)$exJ zU!%pwTj7~l%CN29wE3~$(J>SNtpoRCi zy*t}B+%~=Pre%koA#u%&QWuuj7B1hTt8B*;II5uS%;gkXRY$eNyh= z{@Xv)zJKqN+GHcqLit1Hg`Kf7Guz$tq!Xp%j?!#n8R(~nDo|WjR>*F5r&&zTYikHy zzkDgJ|Ib7;NFSb2gAjSb7jOnls9dK=Bc9YvbII4yVIi%VF}Sc+Wj64T$q3mD zqi@CUym%1_qNv~O4xzS7ulEyMr4_WkDRI2%h4`6Pbs~s~m+9BE+U^ApF#zE2i1eYz z{K}DPf29g6`DJ@Z@K7?B2rz{mqe|^Y*y%XU*RlV*UYl6=7w9i7G|pH+em?XJN6x2v z-0FB#2E_A13(czCcK0o7#j)VQ4f$-GGt+xq<%Yr*|UESOO$ z+WION^hfV5FsIj&!gHj}TGleXlyn%*3d|gYfdrdWk!_4)wx z!zj`&id^u1e5VZZ^=?OTzZ-n-4_X1r1CARrB0P>8ek5utH!(O2+R|b8OiW#2MHZD~ z%2n>OewQCQtKzHUD^?hAU)<~db{wr`t<>dtKUeXTwiGEf)K<0M#bU=1o9{C6VoLy& z9^t@rnLtjOZ`IsqKJ8RX85|Z$*n6nR+S!)V$$E#|&4~|P?k5D`1XW;tzEs=%%q=&$ zEp8ZQGl#*hFMyJ}hk3jC;`fyfT_Uu@8-%uXnJA2){)_PWO6{dyEY8I@qjB2X4l$i*UHf-YSwb zm@+~!$Zrs0;A|Fxb;Yp#Tc8Z`fAe%&uW9lAbfJ0^;2fI(OixSgSjoK}J#q7h$PySc z=9SOs*)CAZq1cRdT56efYH%((8_Rx=XrWmbfklFI!dUHeZFakL?-s8xi#c*QT3-c( zaQ%uE6M&P|Yr3p#MjP5a(8Xy=^<;LxmiaCc9zMiQ$}iNOZ`XhBG}%mshdg7mKtda_ z_YO|uagq)$k;?bFvUQ3?E7*Sin684%Yj7117;@F$^A5Udy)F9zu&Hon*oQh4uV~#> zd4ll6Sc(-~mLX|pm%Vix7rU{;MKrHfV*thFR)zmi>)iOjcFAY%<(iTOC<%>Hhd_qS zWahg4{;AS?IiGJQS_HT~AGeis2!hjm9(J7`MP94I6FxS-ba^bTO@S&NU9I?f z*csAiX~hb$wKZh==MwshVodbF+K5>2=i_3YG@Mi;#KrtaASFEf3Mr+6&{f#_Z~mK{ zZGkk>n6HRvaozg9+gkCK+of6?z@@1`py=0!P%_RyORLh@ymHs+J?fuv&A&g_KYNU@ zd4b61d6C%+-IehaEbBnh)(eoDjayF?Txwl z8*RSEEBvWOl8$~wlao=`5t&9>7leiL&56FW>H)R!+HS8?rBt88ceI?%Ws9JiXS^T= zP|=?29_@usDeei~u*{c0S~B@5J*kH0nCKI-TjJgwEy{Rn z>mGk^|KzLP9_&MVvpVmUt;1_^KO=2Y0sj54;>y4dkO4@HW3Vj!HBmIKGc+m%a>I~{ z_z%}=J?xHXuuBHtbgi7ynMxxCXF~FOvRjNau9eg%1@-1bkkcMY0Bn`@rmj|jD!)Ht z>dw_FiIC!>CV77cMocuH{-UJ#a>V##>2kg9l$~ZmcE2>PrPTTB!#uIms;>rlwmd#- zp$Bu-YCv~>?p9Pjt)05q-c7#X^;;4Vk(7kRJd;y4&uQ3@t3pobTA9SlFQZ;BRo+h3 zWLs|XI_^hA?bI}>sRv{k(1auCif>jTLa9&TQT8Vde6JHg5ek;2K4}TcdCE*J13$Ec zdb`5$^W+cBluNWJ>BorZW=j|;!!g|OeF*qnh!8V<9n>{D(&wjfIV+6<}V zQMJWVU{U>`$^6(gNdQWdHrY}3;xWsrr_~ep%AWwX)?=TD$7dlixffhse{ZC#D_Eq} z5~HfIE}1lj`V7}ekr&-;MYYKcy92CICM(WU9{07E`?+});5b?ybzGs>Fe#+- zj}`jpyq7JcH;o_nPUT$&OOrN$w2FQlSiwx@ZZzwC>gMX>b9E?-E|eSFiEj4A(ZwSzw?BL z*W+8F7$P$GZY3d<#ADvvqa+qZt{}M9bd1sQ04VnpM*Lx5s#AE8;Ii^E%MMrlRnw$q zxK*{!I$PVp3sjvgg-So~B;x5C}n(gxRZaguUhg0!kpwiBC0Ki4p4p>uelai>S+ zugy~|mO8!(2SY^k<9Gn`!n3(JQqHmOs{(Gx3fox{<{Mjw_ZXl zaIJa%n)LEUIoIAn?_lP%v&zQnE2AdAct8a1kr(N;?4hw;8VG5w3vvw%B#ZjJq0CNG=*K7JN3J7dsrHDcVakH-Z#HBKd z>+Vb(Wrktb{@9lMOGR+#Kmje9>Js8-9*~%~Nt8u)A^8Id%%*QT$K!JMX)pX9KoJlY z`S>)<`&3j48P#ddf~BNf>6#4t;BTg=}I zm+M54kd-@$jA;3>4Xd~0UffuVrpy()(dnZ9K)U8y;(Eipmh|zu5~zF)C{c&9nvolq zdD@bz9vkWho15BPHU|x*@`+4h?(h&&zTB@#;N2r*DeG<~$otZB%4wtjJ)L9xg0EiV z(d^)SZmY(2I3T0b6VLOIxagjWIb!x_fW2MU3#X+B(+K=!vynjailt4UzM) z!KJOHkjYG2bMXr+J?%RI0uGg9j0%K3Pa>U4%k(5%H(!S+2WpwP$zvCSeUu_MIriZ7 zN7UIyQ7vu3`4#=0iLxlN<(MkRzB4Ulxy5%i#avSvu%qN5Q6#C^(6vRvW4Zc}6I)Ly z0Ts8yrDC9$a_^DB9S*hGV5J3TeZM3yLw?#ziT3q^{NH2g27kaIYgnZ}CwULrf1z5e ztBtU*_#W&FCrNVIvOmbdb~_!{)?}`S;$iY{9wiXYgSe29Jzbkrk~uFPA=U=Vn|s&W z89tB7D^0POQ-zTDv77dyRNxPE*wujG8Y-tkTim$XBT?!eVi@@U3==;w5Gt@$12-)K}T9 zb8X=2GcIlu4TZU1PKhd*u~jFav8+2;{VW1?Nu8F{@(K&RJqDlQo5w#s1o&l{j>}7P zt1QjhjtqWdm){!9_*j0HVz;m&y}NYF8B((>QR7z&0h~jvX6s-8(^Y%yvM|7P>C;}m zz_^);YHnd{qvL}Af)<9byL@zAP~BK%PEfI(mFW5nYQPr&xlH*xEA^6qVdZTY1VL%d zDllVI_D%3lv%y;>)`wZTV-D;c4=cXTcN;g4^$N|d(~n2Hl3>vnIPL~|cIyOa)Q(YQ zk3ZJyw}&mMk1h>64K5!*33FDenZnJH{d)bB;;B54j(1+03|)A`hZd|(EFTO)1p~l- zpPr#?qjwU7urAr`9`+QXYcU-uj`j}K89fXOA!{&}!@C-0@<#K1wVdm8>#2Z0;Z zZyQ#y?SvngV+k#6{igimWThQH?OLyVy=huxB39c$iRw6ZqP4Dm-VP;hVIGZqw!FNn zoez@91@9x@bS>MEI^~1zkJg?4Ve5$HiXdcL9w@$h9HG71?a6>r_Rd6Kjc~edQJ6&e zS9FI)$Qlbio(V-tJv+|g(!ch@BmH)Gw5v-!oGxeu;4Q1I&70rLv^mqSq5cu!`vJM~ z=X|2_H1_wTdU;Z#H>f;A4#wsK!=(4@xkstWyNlP(U;E?1B1iV}H#WP=P?93w?OwX= z-t&Ktj6!W;8sroe8${(A&Zo>Dyos1~xywJn84P^IWoLgdiU&|AcUL|$NK2Mz^%nb< zgo1nD{rq|48cI{=2$3=PaiQQ=2WjngRCp~93-^P;*BFtRndxC+K0rl3HD50I?t5hD zeyJjR=ogRUH0bvB1Cse`?x-1rkXRs{T%)n;G5^YG;g=z=Q4<}`@TuJF2bK9-vlGb? zKJggO&2)&bGXgtzhp6m|y2NEGkOKQYIXH{XZtpsnLNjcq2ih*bAX`2Lk)hOL*UgKx zn%6<(CscG30S?uZWT`3XL!16>Yv;M8!?{oOg!Rx^bwxv~)7Me-Yc+6uDJnY&$2lil(559! zaVtnAV3C4;5@Lfqh2iuyL6YJu=91<6C_YaCMOZ+q)^uXB&@L{{C%009hQoZxv_Vqa ztoQCNp+7#(De=~|^_6oUF{HMEmsdQ&XI*V^nI`21vlqz72p3-R((#Pj)5{Lly4W-R zuuY~rCmN~7b0aKnzLLa;_b7a?h<~n`(iu#|EEX6y5vO{x20j{MIL~l`uQb~jk<3!S zWUIp zu{-asG(j+=AK$F9r}1Qq90_2rxmOR^O5_@2sHmSLt-M=e0z+E~$DyadecAD2ka%*_uZ8tj=k z0$@y@H8k3==+z`jQDHq2%zBeGZ?pLDI|%b59^4~o5g`{_zkE~fRz6Wtn|9Lz2j7e8 zu&g(7zo=5*vvAEVM(SThE-tO;cRh@F1gXYRnhpMv#dPFF23`wPFc_OMuRN>JdD?3j=1 zqESP`s!eF_=gO$D&{R^^VC4`&h}Tbp6E~5zwi#m~u`zP*I}HKbZ;wN7Q?Ci)E*smd zMf|6~eR>3Ov+Xh*s~qd*O*AF4E-+w>ttA<^IWYk!zGc2Vx+sf~lCs1prp7(B-e;WZ z0_6V?30gc=iRNZa>ja3ois%^<>?Y9`V?Oc;4&)3|%S^aQKn#2(vhPACPp_I3WRj{{ z8QU>SE*!<7H~Z#cyWLV6DLH#yD;>69qj#XBui@ZZGu3$ZR6W?JqAg(vB(0`o61EA$u)#(mWDNq4+Xw(Qx5HkMn0*|Xf9w6_Po6b@S+-ncy>mt*?ju)^)AxPU~ z%*sD9acEai8h0n<;iQ?g`thkwv#}OQx$R{$VjpRknad~a6yM)gq$W<5xN^6`x+WdG zArBLGwOUh9S&(#*-! zBy-OYo5S7t%6T(6XbuzM-c=(;Tuw}z``VlT;<%ByNnn?!RZk()WB&6z9uDa18w%vk z6|tbgK70K-rY7BYO+!T)Q8A9vNYg;79Kc&eERja~b;cQ=kWHGaSk(f)zbGU7T~Acp zyCWrkDR?xmc!sHq61#e2OuO?C+HtOFq)z7biTs#-tOrQS|1@*fykS zI`*~-bM?A>zdLschtn=C>)xpK{xS%8>Q6{B9V94_u6Ash^Bvp3Z2Ybna$lN=^^y?r zhqz3BU-`I1^4c)YPhxBj(wOd5**#NNgI}&Eq&XD+lvr;{m4k@J>PA34hVuib549;| zP9)=)goM@(b{%2#G}14=$x4LoR{N?BPj*ly?2~>Msts)68-IQjA}Da|a%90$DOqBc zmbX3$P9^FfK^y0m{80kbZs_AxghJ?$J$dLk3#YX~H>?PVd$aTb0;%<*QN;$M!exi9 zA?s4;h|O+75T{}x#<^{)DD*7+6e z4`~XwyO20+J5Sa;?uVjyC|G9#GYw9_W z*DN8i=PJpQQLeXV-E{*l5OV4sctwU5Z|}?{ACHK3sC2!3rJ}2*nTcBWR8KTKURvxn~xk9;}|gBWongkgI;DTi;}cZ34XUY~j{ zCBx2)&c<9C57fGt#j@9=WyN5w0SWv|PMv5!+;KBVW*uRV0XM?(#J$LNYu&sZy}}U3 zUQX&4)&zpiQ=kx9aE!>0PgHoIbj2O_!yyQm@s&kKcJ&s}zV~;GT7oh@>LXajF*`l( zXaern;mSWgzSm=#f#jxpx!VFbUwJI-VkvX#7h=JL^UWmbDxJLFGM`=dbzVOX5hj8_ zBI@Z8I_kUIPouEoa-8gFI{hsYo8)4Et7VnuSeHP=eF-*JlTNE&`wtY=p4ti4K~dLp zQG!sN;&j5Ra|$LfJ>^mCkLEhzK{%z@N)7;wcHkkjTZua2(R^ReF6kjqFa+_>kJ-7- zKiVeqRS`mb)7|zkg>B{%Rz$cy7`&V?91R4vMJz*LgZ%lQjB$jI>pfNapLJ}2VPM2S z-|Al1JxTY#qF1e+U{ttg>>|wxbBK9vA54H3qQp#aXH^vs`>ap5w1oPH#@WFZ7g1(}B`@v)1bargWOhr}RDK;}xZ(pBR zI0gJF;d?5RF^!LHd4Jq3(45dW)Ixs7hX?bc6}lT92~Aw?rq!g)hz0>zB6^`>cU)(} z0GOH0wn0Sfk>oafN9bxY00T!iKT5vG_UXOImd z1`7@0{HyU7$Ug;Ic_a#p99jVl9Ex;w1ofiPew~fsdo}5+BE}n{a;y^7yvFS*iuzpX zla2jDD^MLFiBHsU9{NGHEGS>`q$Ws6sz+KihQ^O)2?*z-zE?A?^J+F<$y5u?Mi`W$L0Ltc`rpzAM0QF%vUM`Sx?+bD64r(Voo-AnF8i5g-FxO?J$U0>JVln>CeE4Z-*eu z?vROtV`eY4eXBf|fih2yZsmCp=+^b33Fh>j<#%C!cVO0C$VCGg(4{0!4&m<-fJJoS zF78};1D(}?p7Y77BP0LTSlO}r#hz~%S`Rq^`%Z6fdLd*BQA7KUgkV=e4@)#G`MqCZ zv-&%)U-NO4tB?R{sSCZ zc1c_)bB)>caUC!aDokqk=q?P8jxO*B{JuPR{3_)b+Cev_&tqL@u}Yi+vi{W08r_gl z=gOml`or}R>+p+>U5L~EcvNeC54mp1^fGwLgQZ;nd}->i8Z5_PGW-xHn(u1k2|#1L zcIda6Q>lRV?`{_39g$l-+oI>|!dB4fDH z`j}Is9|G}$P1vW*cApHi3A`^{=(PS{Kf6NXv7XtYgP#467ao~=L^|dSwYEbcUEFL* zpaf^+Ubb}MwZZjAo3(|n=r*U1>u-N74;O3Wy37+-tgt*3y(F|LEc%%=2lTLrj!Jpg zI-nrKL@0gy4HRUY;9&jPP(be##HrH*MJm>Z1wUQ(N}V-@Kq9m{?l&2pG z43%iwnwwHCjAp5F$erghzEhs9ySC3Q8FILcXVlUp;w+}%t0j-i&DG;MdTs8|i_3hbg>Ks2B(1mo{3_|Qfl3!eHyyvVym@jdzWpc=iW=frx_xrvq_K`?coICR&V5Ii&N$ z+!#2yTQ~U-MV0vujNxS1d3t=h*(<_8LGH&Ok`_U+aA2aYcVh{;W;+29s@ zsudJJ;0IzaHRe*Y84uUvV74AQc5K;*Jf~P1PURC6cbFd~$GpINzih*G5!e{4Tur(2H}P+3Lv z#rNL0i;Dw;0{;7}t}}IA1Qq8wO5T> z%>KrlgNKEovM7N^k0ndN4tPJ2uy=LKe?XiMXEe;G+l!fWru#!jk2Q7Pjm&}RumS>@ zNo}#jgk6d6hWA{;O}Y;p=)^!rRl8c8@`)=!=aozh@bIDa{N5Y$u1={Q^9`3y)BzRj zIWuUB%Ml(1(McTx(MX7+=Y|B+qvqDt$Cn@EfAy9IUbX)Y0bm;D$vvX?H%A`#&5-M@ z^`jxWghQWAvh(QORZQxBKMy^(CN}n3_XtxAGk(?`-@9<_a!>2+(;o{~wb8vLLZ7@M zl8d*w1V40u6#YR=(X(KyvkRpv`_lvM_wFiO*~m_wqIE4v^Th7(1cIQJJ-mlQOAolM=I2^!;7e-u?}6 znJ{oJ8jgI5)LLQ$#VQ8rG-?{u#ldariJeWuB=NVe<6e1TEa`l}curCM{F5G`VNrv^ z*xi!Rr_b)Kt+$Y4ShK-O_OJCBX$s* zxK7_fy=yB0;5o(2?mZVKzUE&w(Eu(!WX z_TTD`PXMo`CzK>#nQz^ZT&~=__HZ8n3el5@NR9PW^2fry*qanh<|yBqs8JQCB;_;n zhmg){^$|X0(gD!@8$tE*n8FdCk)uv+DvTFh-|GzWV3xc1QY5_vR3b@b^eSl0&4;6f z(Wn%2Lmjni#bRp59p7>nggBvxSegT%^{O|h1T&+UDuPGEKQSv*swDR za{$;b(%HXPEp7w8!dEqmP!M3V5+hDtprGAZzNQm7kudH>IO$7-_G1WvoW3ZI&wS&D zF<1@T1dD7clC?3kv2S&VL z1kQd9`7k#g51_Bl!D3w}4~^GWpQOGz`r3nM{QPV^lu1E4=&a8`c3m(i&|$25(<02< zj(pmREF1MAF#sOL%3rl3AZVk}3V~bvP-=mo=Wh8fZpn4$L~zr2*U8ZpS%>}QUhSAt zQ)T~Q;Yah#lYT}+HOeA7t7G#mN+w#r93x^}LegNVJ@N1a#fPxuPK%IZJBb!bdwKct zEFZ8mVATFUo)jq^=_?l`!nD7VIOP5X~s+Jt_}u^LA{xHfryoaF^sGvRpQ>I+}eWhaUZZ-40aP z0DdD$Ts~v}Uoq0y_D!IZUV~%G7cw3$HBrCxmSmZ8yuL_KI8eTKFvia>j!Yd++6J)O zfL^(1D5{BLKD_^qGn%~mjI>ueo|)osZZiG>e$8Yu%%;kyYf4zWAsx<<+8^O%-1yn1 zTVi;>#Vz)L1rJf`cxOY%yBc=D7r<}zjDWM4`;n}x+O|28R;})O#Qlxw{v9pe>{)Jh z^C%%TZM%8E8LoGm+VQ-XShu&3Q2+bT4FnO9pkJ136&WngY8yaKc(QSQ>=H>`j4A3x zsyau_UE4BNM66Cn+)14Gh}8rf93{keMAxT%EQ$$jkoH)m!CtYLUYm=fszItGK2%H0;-{ z9SN^!{VLiuwWI8=&^c;I`wy!m)u?`73xWIeH@PwZ={M*cua6e;6%qO2hRs1XS)UUW zqEf>|dvh@W-8sNLdbAc10c|&b2!NSig*+G*Hx%)Ii~@a-;BA3W(|1qjPAb_fy_Ko&Tt?pi}%W{%oa?8l^f$&?hx)qSE3*+ety*M zE5}opFE52X zYP8;kR&*pM4h=yAu27pqNU zd5o`62*A>P1&aUsjFK}n(eT(fpNOJt4Br)&!n$(8mK!5jW@y=6(^WJ7zF5zKMg#D$ z{27uWA8Wpi5YMAoEfc^e`1rx{2=QJIT}vWb@8`EPR+@>M&!tkmk#J6RPcG4fF2guP z_}YIK&2aaTo)Vocv3-l_K|Fs*8j5%`Sg!!nQa5AN`oS1}czW1u(?{Oe>rJ?78&}Vp z?RD%R0YmkIZ^18wE#*KW+`vGval~Gbi?#X;LQXW zoAMuzu09Dei|q^6fWx}&}jE+J?K7I4(>Z7_M+ zHLjs{UjL{F8H!Iw-Wz7>R(ZBX0vmtSZp`5MZcU8QlI8`@PZpeCw!L=yHr3?coR-Tp zegwmUVbTpWq=>pEIoX)Qyy!ML*)=(`aLK3V8}N27LwkI|g=Rx-D}dA?YT@a=dEX9x zOJwm#x4}JpDW33*NAGyD^kkZT;7Y(t*aspgZO-JUEmffGS!48R=nbxto?B zpbPxGq3gylLZP+2>b)a<_sVn^`wjo7SfnAvuYeSe1}v=+TWN%mA4*rbGhFzKiTo~w z$90vqOYqZXg6ZOMnJ}}%=@7~MtSsNF1!^Vb@%EGbN_fE~sRM_56v3Ji|CPcT zKPwxR8p4YJ;{VH5GgM|7ugfJ{Kh>{fc*fLlv{xx`g1{BAwBMnU z6fk_0n-vVaKKm`$2KiVi5@%$(I!es$^V5Q3FEZ~c>mckfl%9_+6A7Yq3 z8s-_gGV%7hDvKm0ncIjEh6<_Qz(0P*z$m0O*VIoS%GtLxO}9TlL#8KkEU`;O>Jn;bjNPR&Pc%hw<$|CF zH4cH1v=wb7ie=7C&Qn8a)?<~aIC`W>#ZNfz?R;j0>I$=pjoeSP3d-~oHtvyGA7j(L zd|^D-jJM*_Z=ie6d|xx?SYDw~RI@l$9|i4jOX@{^q!QRqTr(bi=Gj_h{Fs#IE>XYY z`)kcXp%vWbJ+Sk3I=f-DF7sVWw{TnYvjx86<&lxkGFq8`&(t84e6D+1@on|jy!U2T zN$)>cE-#)t%`VaWbh0#lL{eIRu-J&J@QhDct77>}h0@t=obq`hgDmduohr)jo?mUZ z5V};DGb~NAW=ZuOI@ou#UHDb;v2Xo(L#DJu*6SyShoFR#%dA-*CTfq3^D3i|wI*9R zAPHp)?VMXIWHteFNV^1~2+Zey3yW!hSwY;|@sp8sF+>3YZ-a2&wcq#qiss4>Cp<|C zN~Uzgk1ggaz{NS;>}ONP_nbc&N2_+=Ki&c>isnA_hQ%wTX!O=0C@J1-b+#hCdT)Rs|gX>5ha4cmE76 z15)mUunaLT1z48QE{QAdKhaiQih{4FlTbD;-kq+^#8e*i2y==4*FCGz_{ah1%z-<- z{gV@gJh8C1Mb=7^lC_wx4yJmLtyE{fdaotWUy6iR&r7Im?(e(c4L8b|>E$`u9Z-Zf zp@B1=imL%XXdq&??Cofud0wArlZe(MeV9C7diF{%PB_>I}FxQg`^DNf3q2n+A zv?|jfh>*6Vh+ZCQj=xC(gyPqe}pBy;8t*>xs<=72no=X0n7 zg&bp}j09a%td~U{)oK-Jq^v?;?1}?p;FRrzI@Tll7KJj(-cc1~P|`^O?YZIP6t(%p zl)YuuK+dYbSqq*(9YSfU$A1y}y7y)XaPHg|*H!S3SeNal>64Pn?Dsm8KYS=GeRY>W zNR;?eN{Ti?>PO{@}qxga45{ zO{g@$O_w-&kbeL3y6PZ?cDE6-viI-x1m;Ht`W$@plceFcI4#tgMoF{DFuRjKv)E3a zJNyy{+;<=rtMiy9W*_vwGvfZdA5wf2G&145*5x7LU*e;9Bm50Gy&`?T&H}0$374=3M+C(e`_l1p6@wmdnA6&VrFHg01N{WNDb>+b2NSrf~oNM`&M9xwEByiu4EBIra zKgtCMl+PU^ofwQ-zIu;OD{iXAq%2m>qQh^i!PGdk)i^{*BFU{wS2%VTwi^^1lA9V9G05n^`)0d;^Hk2lQ}QD6ITLQtN)Aq6 zSR7J2^EP^w zje!GPuK*{(0GXOWhD}R%FH$>&SE;1*B@1cuuS`_7VVuv$lRLf(zThT@(ciBjjgtkV zyKO{DUPJekJhE1ar&}LBSB?G%?6r*EtpGs8CRTjEJ(6u%{4b05Nwx!^7Gi+K{cdX7CIWo8`Ca*D*n5*RH zMhhNu(kst0)c*1IDq*sEboS96Jp-biYrdX6CRe*{HDl@nmV3r;9t9T23)z;!5l`pfYmQ|Z6UE=Ce+s5Ya$hM1=RPF2YpDV!bb+_&I!gapI?&Fj}ywFxI z>N4=*gjj6I@DhG2;$tyYLxlGpi0bl^uxpyL9)`8-mR(h6OWNgT(-m`)QpJt`n}pBE z?inVn?&yZoT#kuJ(VZ6gU^Jgf5RE*G7l;x0A~D~FUhWY=P;_XbqsLi)_RamSknJy` zW8gCPbD7?nGJs)65&`qnoaiA%!z`}>Mq}E;DxhotEVBynue4_o4H4v>qixp}9aRH% z^II>P5C=xV>XyKs?tJ&C7Z3ZzT1|R5%(LF9D`cLi_&l8Z-Xb`=16rC6z_pxSXFRX8 ztdrgW1nIuSGMkL*o=zSxE>3!!Dj!pLQtYzWVd6KZg!A1E7Fb_1ZMz9Mwun8L;5waf zoIuT2#nW5m6c-RnB)580Uz~kGs+R_C^~cEr<1aZ&Y70Vj9P0jT8uQ?UqKl=5g+I|q z>TymrIjh=Ra4P=Yi}6wQQ{DsX+57^7`KAz~fBE2T^SYqa<*_p(($0o3&VN~fE33cF ztk5v+6t>lO&(79^pJ4+Du}Q=V)oBi$VIty~o)C6bDG5tUVFF1~?eomR9!1&J(epj# zC82PZhu{Mx;|4PoP%l1wQHj0A-9-7A7?tJ!lOraZ%Rf{D>y!i%pA4+|g@(k8i`oZb9Bn0sxPCqL`!>Enrx0WcO{0Xd|p^KVVjwvfw} zM5QF-eBY!T!%nTaQ3AJMwEZM5070K|{WXLKk8Fnm@FdiYneRp(S>D^no%5*QhsCZ) zoaDn74qBq|iW+HcgAaL@U{Km2;U(X?)yqSHwZ@=GaHZrOCgLRkfn(LGd1a+O<+$<{ znHL{kNZ=fSc|M?V+bPC<43pU5(&WK^J4MFM>)oIU+4eEJP6yV%n69D)rbeUKHwI16 z7TK`X@|lvwQR`-dY>&yOw#uWIQFk8={WCv+p5Q_vahXA(-4H6TXFzxn2nVfg9N3s% z`k8Bl_sN|j-l0b7g(W5!%!#}Azx<;3cJm^5)b4We{r`@&y#oV=f{78%=A-$;-KL2m zXKCac!YfoSE%~+2!AhPDqXgu_!+ExUlsHM~#v&wTpB?m7-JtE(gJ$Tm4Qo5W+I_y7 zW1`a3vfa*MNUJU0cTdqWf~<(0I58KlOMv{<`|`+|>@MFSvAb$5bf@m;dGqF8h1%2m z_Yv8T@V`1t)ZoGFdQK2V(O1zD*Rs$9(H*0VVoTtEF=7JADsXfXgh8QO4v#Ke^40eb zB8gWa)JX?-JXx^;0Ww=8i&|l73W@Cd;-yC7h19i>VVyD><^wbbo%XG|l5|bvH2>vk zg!MxIn6CBk;#fh#^P@}Oq+h$Lj7Sls`g{uCB%7uD{1FABz42(y*O)#Q867(2$!R{V z`L26FBR^aM9=U~58uStmNco*s4AwfgvMJdw<})ow-~)eS_kRW}qNr!nVBhjk z-teX;OXYX|D7hvdl^Pbn&fg=h_4bv8g!On%Q#UJ%ZGC6|B_!eTZ?cySgEF3!D4h@( z-e&k`%X53z!|RiqwdYK)uI2nRAFkheXk$A!pvHdSB7xf�=vhI@sm?DBh=7c$t=B4ORUp5 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

Bv=&i-c|8hFe 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
    ox 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%0D&6S?>Y?rTy12u`PsICnRWXe9EK+%jq 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�)4= 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

    %Xr*uMZU203=6REF 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%0AF;lKL;?{hGb^dCWO7iP? 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�mlja@7 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%0DqUd!#CQChK*` zD-_EJ+A?KI?F>St$<{cDgbFfU!_my5-WywD|DI$d807>}2jHq_rdJE6RaK`Q0i)Rz z{y4%^OU<^}1(E4J6A}ReEfXL4%acpC5KK`zvqv^X7^ z9Mvxjpx#9_iD1dZM5I_M!+Yj9jhR!P6=iz!?ApvY}`u&jo zVL|2|#hmg?k3M(OsL zcyvNSIAkro@HY!wG@^&KUhj>T>0-O=me<=;VB8w6LbaL7J5(E$-#rz(e7?ShS(b!+v;i$Ew;%+!wv`6D<9#TMyYgSxTfq}gC-D=R&^2?e2VccDcI@q5B zC3R_<*&$O97}fs`@=a`QR(~*D#874Y@dWR#_oW3nTr>iQ59I-Vh$-345y!tI8x%HxT zkk86jyT2ca_ksnDNHMl02>pu?Psv3c9#U2vc$r1tuwzcTF`VGFRzyrDqFUX*k)xM# zmwZAOPiJ0S(P~-SM14BArRLx*DJOqjy7fGlrQqgZ;5pR~c48XKcg_p{F#3k$oO$H6 zSaX+lJu53JWi3K#S$jsfYDFq*76=tus%P~JYZa^j2C{xh?2aO(Upa)&&{&HvL*Ki( zrCd6U_3k?Tri>IrypU@bo49R$7%4%Kn^0`VCHRlV&cyNnJ-pAKTS*LY zq`WCa0(ib|BiOpQU=E3Uj1DC!mHkpK1t>yZo+|(vYA=EBOs(=5FV5Ic7{AC9BJ4Bo zK|i51W0DePkzN?cKc1Bsy|GW3bI6?4LYg`n;z5u+D;C0|#&B=)vj?rbD4ImS_EnE6 zVv)w7{jOmq0q^gaKe0?zdy6Gs@g;f3$H~eksN+)arrJFGIe(tfsC{G}oc0aTWrybN z41q_ry!tInZFZxjCe;6p#p}umQNBY$#*>m>uMtUVx143Bt(B+hdjvtqWt1+t&2BGb z`LZEDwDNDoz$vF=MbBV z97@aN3j)U5me2LIIF11kvAyf+oG(x2*bk3dSqW+_-Nb~KyyC+~i)ng_o9N64X;e4@ zed1ZcV`W?au`z}*EZ~8fD+!P7nf71P7Aj7@xzKV33ulIun6pzV$G|T9`Np%zsL255 za~}74Vbd{!wuDk;A8%x%*Ow7R6?@7%fitoQ*X0y*_sZG3&lOslKq*Oq9(COkT9Y4i z&C&NT-w*$6lApQ|nk-!-K5Qn!tK%!CJC(k_2gU2weN6!#vf2{E;3oLvMRrvfo*<{$V z)aM^|;fiV+NWX4Bu-m@P*~8Fg0Au=hyG=s{1DaErV#gooYb(+xupyptCA=0AFjdR>yD+<6 zS|vAEX2C9zTMs^@oB&>A9C65F_f`^&1u$DNRJa(5Tj8f&Ap~T|KP=9tS|Px%ETLjs ziw1mTC0eCT@v_XKRb;S{;Y#IP_*&5(CQx}T|TpsR2FEN)3 zQa9S{vOGFZM`2XR*z>s$lUb;!6u*WS)3>5AS9WJE9+-SRL7!W?-mJL^L&Twm7FYN8EzIg8F* zJq!RHG{gC)R0Og}SinGV;&G9C*1TQ9{D=%K!QY zIMCP3E?%(0ho#8vzSEZW@){_;rqcy|rK-ISzt(o!JG5wYHrSW1x($l(tsGZ(lmX6m z^xCWuQR@vypGA?AM~{?k3{kl`&b0cM+bbukf6|MzQRcbviYwjcYjQdqEk`!2-Lym@ zjk#Sl=fWp^OJxGILPHvXxDum~xMmAENGY{3mS&;ye_@`)k81X&P}w;ec(n-T#>S{_ zWVF6Q;j-q(76%>&F&sMWI$CxCZH*EmX@pVGUD+fxi_3$twZ^yiXP5_p?c3y%j6`5B z&4pSBd#n52$(Fk34xesAEr1gwklnOhFO0yDzEi-{WWZNs&GCX5q@_N&yga6pvUcT) z^~+8oY56CI)cN}&jt+6@q?C%HR^Jr3JRXypFgz+@9_SQdX%}$j`s~YLopFIPl z1z5zXtD&Zsn^aRM(}%X8IUfRldlNeUK}l*7s!YwQUoUkO{^_%jk@(_~YFOoIB(@ovC$KUxI+`|?&(*!e z%^#u47-xSF+9x8gEofC_F4-9~`Et#2;c@Pm-wa83Ev>wotvY;mK8~Um1S{)X+_px?fQ_AJ+)ei`0q_0IQHXpKDXy%EiI7RBJlpX$Og|LJzx2=Cnvv0ocvvhG3)rK%G*Aoyc!V!zn=UUZf< zYL%!pMD|~Bg8&n1lALBA`A%6z7)Yl|Z*pMCLYXYuvFVLS^{$va8#gXp`5Z7W(&YvW zmQJ_PZq{y9!LD`)`;84yaHiXzUgOazZ?#3gj%tEc`5!BJEHR%Daj$@PYPLt7QcLrc z>@Wqpe3M72+zv$32|ql*?e-6b__?glMYMc&JSNcPP{|4orcrVwg$Qd+;j)yx?UpfF zH67pe6YRUZ%C7mu(A&?p?4mFXs6f%#EHK4G3LJWwQ$jMwqomsc;Y z8K~a`#lWei%Z3iQ3zqY&nHhJX+W$=CAAeY*U*ZE{D^fgne~!2Po%U2W{GD3+cl!GVKq+6WeB1S@ebA&pxv4xn`6?>^jQ6a

OS;~uS$Ob zfQ@8Y7b^oN$0#4YPQttt&BbGcpVg01rSst*Isi+j+%wi}6f2L*FO~!@Kdi1TQLn?4 zgymcjdJRu0L#;Cn+zWXx>@GYe_SI+Yr8izTM( zqIaJyCrZ_%K*7x9M4bHIO*q7AS~B>?dX;MRVa(9tFH;J8?lPj2En~6dN5;(WagsP;m19&%8YwVK1cf1-P6ZkI z*n9%PV1w@Wxs;dQdO;zU1BSR`IIlGY2**w<)jZSJ3;ozc{N@qT-$(5~!*&yiA0!O3 zLVhBnevba>#nKj;*y@%LKUSTj>7s9Vs?szifG0tr(;-PEf@i#^rO=%wwW(40V%M?j z?`uMP=gCY2V_Pj_h_)FnoCp>;ua51%o~rldGsg~+3VeLxx}=)F)wi6>e9@LyWG;(( z&cm{^$SKu~&`wvs#n$d<>*i7yZzNq~4M{rmlXRx~$!xZ)631tzfodC?aqs5q&nRUz z1h?}UWwrTjXthJC)4_{IMY*>}*0@DF0{$P(aO4c;EwS8(Lxty1B*D3-X3gGaCLwY+ z|4_`c8*}G5zNLWsYA^ayp#Vp&OuW{i>&jl(JL)ZN1lA%)FRwkypMW)Ro{h!Mf%RBn|@&|qI%Qg;Hw>lna$@DA1 z4*+tGVa42Qbbz%?R5x8bso-j(@F@YCpRlw`^n1F8DCmgyS14-DUwAFdH=VxtK-o%t zKB`xY%@SIUf^x;eR)k6L7W$FimUGaPsdDkrV5}usn%xC2M&kD8PjU0kE~8paCVjF?qIn#j zRYX`y^w8(Tw7sl0eY`o_P^y9;xTZx)EHq8A+4_wP;_}9l9O4pLqs95-r3;+j?a;)8Xo>*c#~O91j}#&3}f`q_bMPm!x- z0@8CHehVk|i=^z$5m3*L4=S0wOLg7NS7Hw=IKJjk(mM4i1~Cz?5Lu}0(W#Sn+q(H9 zaRVR7lO~}oDd#6A<}m+6DadnMxE^hMJ&EEkw%WDpI0dO|k4Lr3?d{fP>yrt|i~|W> zs$BL27}S*dBBgEG87K|z0-H=kW8cCDT+c@bi@h97^Y3TdjhTVmlDSiea2iodcBOcY zLDDQz)gB#r-RQ*dBY-lxJYV~%k|idbYYKdNll5<9Of}XELOm8c{$4g!pSsg{F<$i< zIP-v_Er6wQ$)eseN3Q=c?(@X4NdI)ZyM9=8w-)aSjZELYqi;vH_>YQ0nOb(|Ju+L3 z0K%B)>rD8`-oo?Bf#?8u={5pcMb9(+ZeE&FU3No zfB@Dn*KHsfv6k>EH#1Z2F4>&8y>-HB{>Jno`zf0?Dbw;g z#*e2l<-zsTAM8sPxFjVNwkuxY8>!IdH(anm%Yxpcju#F~e?Jt<2%Spau7#=1GSE}T$-CDY_ZOc7=|tY0GyrqO5?B*=LN@+ zGyUkpO1!()CX;o0rj4$LrK!Xllq`Mgb4~0O*^*QADT>hMYG3DflaxT+qgfmNE()FbeUeFUPR+;f4%uxViRxJ_BQhTlaSmYJ&~`9lBx!F=+#P!#fA~C& zP_bCmk@^--CW}kIFF`zPLQ0llAY&nm@e?>1$7Xa@0aU)qNL)MbB9+M+34K#!+f;?b zrRU4hhG1CFBry-b(;iq(8hr7U-kNZP^ckF~GpRq@mVfrX44>2TJZG^v-951WqYwtD z%uUL?2HeOvYFP~wPSpHQf&F-qM%D~xQ_@CkNBSf*dJ1Dm7-$U;<5tTEj zg;s$+eMq1{rN5F;{&3me8PORPU1I0t-V>y3PgjPBYmRcima)QES;0(KHE%W(X7#kV zrXp=dhn4;mWvgsAQ!G%Zxey}uH!w{L&wBE6G22!2gCKmC>?6BS7R(j zI@|rP7C^$pqd9^ph)(*?9$N9-+{b{Z`EJv(R_;gaGvjB4bKgqmB9H91tOuj3wB|fA zKzexhE^6}iC1*a`qmHZU{gEQYZ2Zu@fRyDa7C=FGiMt^9l&P`FZk1zcLA;5N!|eoZ znQeT8f`xW}Bp4BOViatF5Q@8~!E7cs6iijD%~&Cx@-V+PkN~{A%Kk+--@gwBN^Lhr zU5oMUBDs0AFdQYs_25*7cPb>S#+Y%giSvCeZ#>&fB~tX^A~O6u1)wtdTz*+53Pax* z$&GI8Gx$`ZdFtPtq_c4|UR>A3jLTUl5|E`t$ZKOH8M*l)S4MiNjLH&aXLn9AU@wWP zgMu-QCv4@r4mI;;V%(Pw^yu{$o65;va6| zB^1xoo2lIM<1hzY`ZT%N&{0sZD3ZH5*vHl3SJ?VkYk&?c8vGU9#i){_{T8*sSj@= zt3Dsdz9l{Tsyk7r{61hXUN@jDR#NHK)$Lmn+oi1RTbDwm6pl~p$6NbpQ8ehUXxnbR#;{cqAyVVxou)<0YvBR_)o=I=`-5bQeF({Q?19dgrjP}PP=lvg?uKtMT08Ox7#R?YZ|16M`i^|1?g@NfWs8YZvIQ^ zpbXO{e`qoO0aOV_%)JBm9*4Df=mp?f0dAL1|II*piv*W3za<+6v+tc>yVDwM0sU|7 z0Of=BNEw)_LR&sENVYBRDCIxjv4}MW$iwxMolB0xMT-CHW5G^`w_Sh>Q*>|-^#&xS zz=te4#3_s!{BCB4Jj}k{a^OL79oLdTx4|Tw5JvZIgXvzl;0ZF^gS9k+Oz$0wr{9*fS0Sc@M%_3wF zh`)$v1-rl6V)n1G*oT8x%$CS4Q1o-p%r&Ke2?_WIcK@_rc>`oQhD3B=;LHs zqAp?6a=3f%e|1zBx!H@9;RB(_RW9%PFJS^0Sswur#Iu0~%nayV-rf!1Yx}cjzgs%> z9bI&tuBhH$v*CcSn9}Yb>WkC>QwQL@tBQvK6-K<9|7*?eAxswelo0(#2?ajoj^@*H zb|8^}024?_C$a)Bh7rlXnu3dDS_SnK{)>y^&hV`f!>1m0lFp9O5J{``A@wJ!zn4{JnwuAU|0oc%fQb48{>L!?F;AF< z?&0j4Kz$^Gx&jA#C-MpmE~mTwJ42L}I?klyDCUt`p(%CD44|NM)8G|byQgDi?G&}* zH?)ZLwEzKTP{04d`+2vbqL&hO9b}AU83YI@hc?~rbM$MmN`+h=p!g%{KIX3``Qsbl z+j0_M6?xo+|DV>zAAQGM1T#K)dIwBm6pkJ2aQgEPfeV<<3I82T!tL+=T7P`?*AT)z zhy6STu*FL*a)Q6V?JeXDb{D;cyMS%iZr#P;!sBx@wf}SJoXfi=5RC#|VwKTknqL1RM}W{4mh*lkfj+#jk1gXrMmoN!phC zoH4=%%$ks9*TdQE!6(zdq(}Z3cs&uAW{H}HF$ahWU#nKMlZqpSfb)O#(EI&vMmdglJ;|lMKodC((iw5{-6zsHZPhdslZX^A64|P50JuKjvz$` zZBjBweI&Ka-wHxL-c)y>g5k$_~6Unm;Y|1CwkAk}bmp-jET0KJaM|zK*q>GCHZD$F@O8e_q_Fuxw zC%X%0cqn4!LE48d^xthG8Q}DUfhi zq|ATxusTfF&~?g$6tYRj$popWsqDcGDHbr2s33*TXJ$VLEJhg7vdc9E>S0l^kIgPV=8*v7j zo5IVoflEMb=W$*_wwi4WPUpW>^FUeqz2gEfCk+GRaq=4}&~cy>YX5E~062A$_#l(E(d%D6X`$Q+y9DoeKN6 zCYT=!e2ks2Of;@ZPbZ;OS{CbJud}~tjj|lgQ4g!n^5U-a6qDaPu2BsbcX(a@`jZ@w z%MnJgYj{06(ToPR(T91XRbNGQ)k+Pj!Ngm^MrMC352<%t)HSAD-qt}XnAjdOlK$Ic!CBM{)wGkxQa01vb&baL_Vm|nHRKWJV3XuIVr-k92Eo!a_g zRwR<)OHFHk9;iVv*d9xFAV?QFuk~zF^`CD#j&2N9kR(GaV!Gw*raJUz<4SR_c1y&6 z#$XX@jSHFtAT_WBMeXFg+{)`w>>mzJQTIps@vUU;70}bk%j-W8uCqXeG0sMx;qCcB zl8R9rog$rW2W}j{1)PDsdp7H9JCnD7WYwSB4TOp|GuWZ}z1yBLn%_G^6wVLha)_q{ zvg(($NL|$7SrmruQN95|?tm^2%@zNoCk;?`G28h(Mc9hT$XY&>iBYE0n)$Nzc{YIE zjB1kW<;tZ;_9k2;AFMDI4a(3yzm*0~Yq^x|PZ942slMr%PJ`h*6EU$GpSf5nQx*lZu zOeWfxI+nK~6y~7WNAr$WR#tn)JWVVxFc3K?QaITTrn7VTz;Z0-kvMS@tCvO|emueL zk^bP#@yLazV`G(w-;+xdP{HzdTTe{b%wt%yNkU5V z7PgkUYSOZ@;JbI9R;J4Eyz!#7hI zY)?K`@;lk#l@Ppb?lF##eMbd{PWG3CI0 z^wMp>$oUl15UO~cJO}ZpdL6S&$0-kO41UsAbZ+d%khq5D)H7@@d{O$+%8vg00L4p$Xq;$uQM4`OnzpZ zBtG>^MXvoW0tFMqhREmSPxZE88|%aN%)*A% z?!NuwCM<-Oik=Ilqn$bYF*I`%InivM-`tqHLLW&s1eHAcNIEQLcayI>z5l76<7jzR znYwH^Bs+HYGk``9SR%5EKdPBhhqlMVC>wMKimpY9H8pwGfT@wYYC&T*!Ug&r2Csrr z%D7c!cU)lELs&E-J^jdNJC6BirO+`*AejjBcSnvD=5^&gehe9qhncPoz}l_P)N!wN z#|WDjJ?RX5W_(|%$*Bf62Jj)o9j@wEPrL^zpT4CW%-gypnwL^Gnyvp?=yjTu42hi7 z;D}+CS2h3UUhw1bR8X6V@m47NsjgWm&|^xynXJ-?&5PpV=~=yKAlYoYLBPM_(@GNq zwHv^u0+JO`l0a_9T#R@Zlef_*=>5ea9q0A+X8jXTts`wl5Fz6ei-(;p~ZXO{@)<2pnd(OtNwHUMWZdAfi;F{YyO0p-+kU#Sd?`DCvrFoB%}bjob|OFK2fq_sa8@B z5-%l399>KXkJAUrR2m69oeOoNS3Mu zvBX@zAm*v{J0YEwW6_z^F*htG*7~L6_MhJEqXBD7UkcawD&Dp#a@U#V_1E>WCTF(Z zSlX&FO-aC#=%`fAZw&2beS35z#p_ueFl%~_z&lCA4fS)-#0{mG=V7Uxr0#ay0E%fCbNKP#}4lyi#c#z_Y$^R9rUK;+#g) zzn%U3-2e$5;p{N%oS(-(y6VcVmStqGXPUnvLZp22*5~uOKsF@1GW=j;6x7602Nf$| zFbOg@kol_x01v-5V*uXKuca#g@1!ZSl{*3pnP)39|$ykmGtH}`1FO;ODc`` zTYe^4t&z(Ur}v2pK6BX;DAspY3Z>S^~nf)Yt$8!N| z7m>s8LTjwC=M?Z+tX{5fQ@wwp4>kPZySH&3Mw`sTy<9M~7a-9l&o@uy=uBf1h2r;%YBxfn#R9*TsT(CqfM^KxG?cc9`k=IZ2vKwg@|Y>S4HqGH{qG_=%*lk7L1CP-0Y+11!aU;28a!9mSmI! zzZ*y1)p?by$1`jqKq!`K5~ufb$nUAvuWg~>LwN_768Q6u6@mh zO{ml2DJ=S>8e-}6;gxLw_2(#DS|`4K$CL5>nY}QGnZ&FXJ%md$AumRiVtdW6#PICg zXp1nL6?jq0)Kve?YyQgM{Gim6H!V=iCLS-*99}}I!f1Nk-@#Z4n6}{yP?cC za==P8I4&ZenS9Ou*afwB!tZ%b86hHu^8*G*4oVPwHTxE*7#xj;R^V^g`BIAbHC+H= zPn%VlY6BwPlMmN+A8!|H%kK~0=f|>*<({;6FjhYq%O5c83@&3)=T4t2g8yAEy3HLH z(p$?7QJmuJ1LFCd9y!$QRN|Mwm>MI8AEr|ZMSo!#2(ZbpV1bq@C1ca9yjkZ z^p8B?tZL7@_Pb!JY|w@_GFq&`JmvM3!F1_2Fi8gOWY*S)n+IRyHza@#)k|9XHNd6c z-Od=MnobbE%lQ!;t4Hb$Z)F6}R_2=YEO6pI z6z+w9iu7p4L;n0`ZF+0X(BCtWdIIoZ_pIH#Is4xHfb(pw$r)n!nPEeqM*Oa(zx7<> zLRPau*MTBbq#@@B1t($X=6d(%Uf0C9T`(49qdauXf<12e@bPqT7BPF5$k4lo-BP~faU>N?>|w_ z?Un+Le|i1s<}us5DzPwBigq1nd)l?Vgei=-A8;r}8I)X#drwP!0yylNX9s7F--$1_ zhV~>rQ8D4@Lxo8v$yd7iz8v=i`~8+sO!W2!8VPvv%BbjA@pW(Jm^CUpJy`O|AYu@B zAIG_Dk-OQ(f@s@OK6rc&7@wgLaRilWJ$3S?Gd^eon~1`CafIoV7>L0GvPDYR;jCNg z^$J%zb(xzSDp(5a0wnBIc#!V3LVYMx%tyEk_NeOQM}_0awI`q9Ig%@{9nscy58VUk zQ|FUGEGW5aW%bd^agI0vD<==N9Fm7WZXj~8Z#u0uHc<>rV&5mbUq}_$RA*6+;(z=oz}GN%Cx>wnwE9k?=V zR*=kn6XOFXYP!E^_`0!|pBcgmq?wDvm}ZJ$ zPSsjaTY(^_Jvc=ms-~Vd0I4)K&AAds!317VtvM~9eSv5oF2wEkuvAMHs0@Be=a)3= zjWaH_YQCE*Y-)2y^^Q@%P$r>~W*;qu2FI{?V*BCsrp@Kaj?1}{z$sADR^6Li@8oZ1g<=?v>9kx<>e*QzG{;MoVv$%diVzMU%$0|?lM1lg? zhOmHk5&Fh$Y$;XNf~T&S+wHU-NHmqx*nHK2B$jINmKbp+vtm4+(|T5>_N?%EGUr1f zi2<79)syvoN^0p;B}dxfWb~@_J<6FJh{6$zY+myipkd1seGq?jv}BCveAB>i^*A*f zC;v7M0oUR@qF%kPSiw1Wh}Bh9u(1Dvhf9ad+N|h6-7u7h_T#WIbpÐm%cWWB*}@ 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 z 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%0D~?iWdwxpqtquq-$yH* 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

lsJ7HPq^=;aIb! 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≈yth24x$k!6tt@A# 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 z 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

  • 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

    8cAejVQ`QHzb# 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ⅈ&#RqgiRR|CJNm^a)u^e37n?9D4frJ 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 zฮ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 z z89`m~5;CUJDXoF@0_jgzN;8MHU??|xp2$U*x&-;^%@0S?mxA@!9(`wg_T3wFq(X^e zrwvG#<{3z{}N6fvD&j z(uXqY)pL|V)#nK*KXFG#tAo5F2-C#%N(BMkULmN+LCBM|_cVyNs?&T}Vo3Dyi2B|f5`1xL;USqjUD+aBDwE~OHi)TMr zt;E)Y{C)o+Q1sv%-ihjWG_04cODTkw3o#`Qr%|sj9B%-=%e}}GeH|WLTWr+GVYI5f zMc`h}X>h(9=njHUKb{g6T_^n}^<-J>eJ4C_;xxg%E`0_|0%z`NzggazqcdOltVZd= z@>3vY^Z4^QIgHzAxc<69Io8xt;h2jZ{;v!hl=HwSisf*O6{QRPwl9eqEG z&_g#6eo8-H1x@<9H!71*QFQ#po#{EJ1azp*FXJ_MIV)+)0GKT^PR4y)>hJ2B5my=B zIabnrJs3$pq0`Z0xmoA268gmeb(gPU%T-^0KRqsw3Bo+`SN1vtO~?H~Hye~7uCyDf zrOiwdK!lw;kKs3IaWrv=y8x(}R(r6LM!~BXJZ+;|yAvD*&7rmgRgFctT{+2FU*Rr- zEP?D(=?_ep&e!kj!)DXp6+<1z{Zro6aQd|W8qR|oh9ekO#%J|>%PhH{zW~Y+%rP*x zI22avy1Q&U8|!3s-75ao8&zDT?c2;m)@E?5(p`|)}F%UPRgengiQ9?1g9Y3?q8XRJKMyj_Z% zV+ZEyILng9dRy&t)?q*6-ztGT>rpg{ikE`tbuqT&T{$Z}Wh~L#muARdy|2np+11WD8W?7m#`T3gq)_HeMBkdn+pq1C!);vcW%@epXGSz9+G}DesPvxpbKyi zpd~XSu5Xh(*7G)m0F9q6pHYudGTH&as4+I>*aHN#&f#UcTT7?ISJVe;X>kURQVFX= zxp=PSDziEAjq!aLaM08Z+75)N|8Adjz+&ER)((Sz5-<)h7LBQDB6@m*-jU*00;4Q% zm#z66mv^R*HHTyU3ez)}SrN|(Xxjqr)zKK@u^oY~J4bz{*o_rpSJ~RPEdA&$4a)SU z_B)ZzuJU}^?t|R2-EKPw=AhTheqII+F-@{m6tc|+GS;h+8<1Y^Ih9zjn=i^uF{{aa(y!A=g~*liKfu)SRCzGr`HFa^wBxu za=-95fPFm)CnV>A7`$HIr&qU4`b)SL(jNrs3~uG#{X>wG`@%AqX2`pe+h^v*NdufL zqn~JmY?LAuaaw1)zYND`d>s2Rv0t#BNQ0Vr4vCAfo*z6B70-3@cDVIp?hO5mPuCoV zt%?JyHK4i<&Wh$|PxOSR99BTShM4$GcE}9a4D-~``TJF9W!qxUE>GSg$fXCW*C_Kf z%*7BDtX1E5ySLtzWm6E7(3zZkr((sfTCuXZ<69YF);>-rvNQ{G285rx zT(%QEBfb8nYJ&cA-1*WQq@Ioq)p)pZ=hnE4Df1(`EvCOLVwJ zAD~h7AqiFN<>)yByEI^YcYHa z$gsD?!Ihzz zEJRfAD`M_RNnkh-5^^5l3T&vdYMzb?_hd8dau=&S2#(7L7Zn2)uKMi-=QNC>jm((q zPednL`2C}QOCga_QSh=>z$0M*l1k)Cgit;Qze`@wO_Yc4EthxOxeW~(fT*rZW~H+N zw7N%q5Mrp9>9R%j`Cb>b19Kr4PAF>+rqexus1yup3@J$)StaXxkm1gm&B^hZPM2rs zEPGMw@eO*h&;$j`2MYjSJn#7U+cln-TSCo&=kHgPR&71y!v3bQwC%7;d;$OXvMFe`0+8iNEzh?rb%{~+G zmGq*a=tVGsB}n7GCpTV7raCTR2ABol{+7K|VdUO&AM)tvaA6md$hYxIbl5vBE7r<2HE0uj90*{SO9Yd8NRN_`rOb$xK z4*^h%stK`z#^C#m~DLS?1<`=7Ia7vR-ze|2kj@sDWoGElA{}Y&NXB2rtn-%e|)H#!ogjHizVw_4%okuIPFPYT4c1DwxS5?BdrZZ5LP50WJBd@M$IOR_B z)ih{nK{hwuO=A;HU1s^89IalO$Vmvy+*oCM^1ee;xTiuj3bc{znyFjol9=o42D5w} zuxX$>zSnmKicTj^(9epl=3-(nfV&|iutZNJ8!&24M5@kAtT;5PKTV0GtAAo6 zyJ%un02r^qr^yci*^Ng|`T-(gZVUlez-Y($dX!K<=d%@>(t8T!{8?Pgd;5`G91TvV zBzFnunx__7GCuQ030(#9X^RRk8q%5FMP#WoJX8T<(h$q9gX9Q_-~_4VGRw9gv@**YI?*B~j_;56q(V8EFe7{R>exa9<*AAyLd1%bTCB8cSa( ziGQf|GG8e!F{OI4x^Pa0-F-^;-p9Z*d+AMOrkUqC@yv^-Xm+)2u%Gw~J*v~9fR3AT zS0>H&fAb(DuJS@GjJ=m=K!}(1s0tb_Tztant8VIM**q?X*o=K}D8uJ8*1)ZmCrhHS z%1clhqMY?-z>F}tc69{1suH<(6Pp!BY8`dj=03aCG%O+}VQO3@3$+-24Eg=VG^q{T z!if(UFyVJOK*ZB#n!1NdrLrtxZb=3I#I z;l46zD95z%1@z($&Y&5b=$d-#PJ^?lxvk?wdVOAK%jRB@bmk&VITi+k;EpGkzk)nAf1+9cjVXEz%3X1DJLWL?~!@nFJ z?uM}hUDKw1N5DJKth%SOuY6581Txm5`221I{i}mA?G9y56reUVVbe;V<%O+n33Pwm zU+T%SmD={?+H#*DTx1Bhg^pHuT(Er4lmL*G`|MYtuH{9KN*Q6z(STTT&CRj>YSCc3 zz^jD!?^m)AgThCLZbEqngTePp>nm46MW7l;I9}J!^^~*62e6V*Z?hhM(OMFn2GWIhnhsd3C5$(xPtBvwG+7s&q`YS6{}%8) zCU6(a+F|Q$V+ixWb8eU2<>`w40*l2L zfqf~k{2RU*W6DpC{b_YGIwXK`1nwpsy=mK+gpkuWO;Bb4a;r=0k>I`6Y;Ab@TuMz{ zNqM(?_Yogkw3ZGEaG~7hiC)5#bmY09Mem~6b3W-k{;RiFGyF`^^bec`8e3dj$u~#dB~a=Lk*eUgk2MvoimRl`Lt`TwVbHD zz?IiizsevPE!DusmQid6oEr^BJz!CbO_EVdL8zD3eSWy)sclC4b23cqC8xl2NxVs3 zI63_{5)*IJef7Eo`z`-%_hj5^27-3Oce$xC@vZuj`utm9Pn2h(zrg(h%Gr~HQ7qLz zjihf2&9wW_bE$Avrf>`C#s(+s&9|H6DvSeTKK{`)RWZ6AIEat9*ZF-=V=1AR@RW_bsuc( zyiPhAfgBh~Y&?wyJQuH9l6qkqr>Mf9V-_K<2TD=kE~ylQfvu8r zpvzcMLd9I4c6{JMWYG>Zr?F{)sWjrDBxbf15I6=u1E;;c9Um#J_qUB~m=wCBn}M6` z79p+1kNpKC1KBX+?lQgY1#b6Do@E|NK9tc-Ge#m-sp_eB=t)D^hn4_oRTtKCp0l?duqOf30T^uBOy&DN^4fJ$ZO z{1i_CD?`?{0T$AxR=-J)E1@}28+%TG*Lv5ixxjs#xJXKiwo`@bZY{K>-VavSWLBLM zpCghLxH{;nGW4Juw(o~7EKt2q+YMZdbveGB?+NI;$mq@RdR1d-Y%NO&&Pt+eJ?Fip z@mFK7m|;lD-ncOw3G@r$Lj8wB(FgD=6gToEn%Q@cf>7f?u6BvjH?iv~Fglb?h;aC-PSIjeQK8MzXJc z3Fc$Td&KB*dK|+3R%v+%8r;&o<+*QvfYXTrvIH-llT+tR9(y3CQ3I&4o7Y=Zf7i}~ z%H}P8b~<{shQuaTyMU)^na>UvkX9!7Yv|d@~&jSd&F`;_G;< z1EU?9Iv{kQX>?~(Pwg{e2)%1(-ONekxQMeuQibExaF>JFlC2|3)O+kOlQqOzwaenR z{ZX_$6M}3PWl@v6%x}lgEus?U(KtGj@@61WzoRWLp(+u2R*+_-#r>zw!XQ7fB(UF$ zeb+6Xu66SNF@{DOrl?F;gEj@h(Rep~)yZ@+=m56VWv&G?Li{m3_jQWrtr?lp^du4a z!mA|sGK>h&jbp2Z4`i?a0lF`PmA76M{jh?7ZuRH-_}!qjMepLIbcZQ)L(dfI(O=fX zQ&IAu;G}imnBaFgYx{@f((L9el;ZWboE~G>*hODBy2c?+_A6sRRBZghvKwc2m9M;~ zRZ+gp8%`?{F+0{r*YMt9VZUBr%j$|%J>ivbPfynIdt5~fvvDF0-+r#VeLLZm=GK49 z{B_X)RF^;JW3tn4%XAB&wCEmBA|QxyS}wl8twT?bMS>N=Blu$Jb|yO=3`0p&H$a#8 zCaO*=ETmr9@^ZKNn1KE2?TMe$(>JWtPV~eo6BuBLR<;LCt?=*YI+rR=2&dTlob3tV zwwJ*I%^E!i`Z9bNVNWB9oRt78vW7M+9;yJcP8pqRMR`d3r}{?*j44gowTJPa&yFP> z4oI6>0iE=5t{~M01uTnHp)_#(g!IAZFXeqLqE#az1|Pm!r1

    !DqQtR@JM4ghG_ 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

    g#qgbNJ)DW&TtkBb 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

    IUf3D(rk`Nc(u*^Vp>eSnhlsy>DMdSEl=IG#( zS8!%?H~H=yjnM%Upr8)PSNCnh{duM?p@EqK z!S@AD42AECMOS{6whQvea@KX}H{ar08aoTMD05Kv&J=hG!V;w22hjpHe31lkPLp$d zLq0$QvJn&rM#vCfpgtwvRK0uL`~9urowt6?@8~Uj&5!_L<#+m;C5z6VCzF7N0Ck#! z#vj~C1lsXTia_>}e70cWg^6(Z&DoEagq60C6T)#GXqPx%n>x*@IwhXBr-C^7 zg8q;MtLVPhmXLD64UlYKdv7+lk%)$%tSqB?e&?7%#DKG$;~of2#?LIY`oF#@@_wm9 zSO-OiZ@G}>_^3?P&b%53|1$BbvfW%7oRb6mVHv=7Z!=$_0D*9{Zl=&+{zOlyH}(Ll z0?;qeY~@}{pwTQhgINj^>(_(30GDwa)|>$2DT|812u8M0?6w{LnqtsOP)4(jrnxem zH-+2AG+&_9t^1O-z3@ax>;uKQn1doz^(|jo>|v&1b$y}1xHv;Gb;3Wg8R_EG3{~l3 zc&uSQ%GD6PqfqUUAEI-;S8+J((k>pBgZ%SM`NMn=S>v6Z&lnx|x_O*y{tzSHHLZ8Uj`@IEhIE7B zNZG7+xhgi=iLKj7AIzTQ<9nyZu4aP*Kila$a>Sj>+3A;&{43=pnBE#Y{Gzcvl@0;KHC2qR4RO*C?bj;4GczB8BNr7bOpq#LNHu%p&1#^LV z$W-YAIX<{)w?!L$U^u!y-QZ^G%}!(y*eojcaGBr8rkdhyKF?(2?D5@lEl^bGK8|8^vv}D9WFz%crA^EALKxzmED&r4*hhWaHmb2((p0ZBiqu}t?_NMtE=)~N zpqw;9EbtR?GEqZ^JxvsXO-PuY|9a4fTL5V4a~Md9%@59300pS&;V6DTRh~pjjGM!K zsytNQlU8irkoKl*p$pS>Uo?tr zxWUAb$hpcC(nJCjXFid#Nplns}qC*E5nln#Ph;zyO4;XQMaD zWVevp;p7=tO>j=&T<#_^W6#x*je# zmZJQ2_pe~L_5MnF$NGo#E|IXp{UE`m2rDzCfb zx|e{TU>`6_&|~xl>nJ`p1h#kj4u6`2|K3QVUVjW%{vX4&($pDXKY20wz95C=WbDfu zb5td!4)=W^?!Sp;bO&{-e)_EKNcqHV4R^F$|3fzgXk?|(4G572j^6&-O^-M+TrY%p zVF4OMcJS8O)`bI+Wsb(p0r!+zz4Ra={(_K#aZ`KRB!V31Z$=5w`vd=0KfMZ$eAa*l zjJ`-cJJW0;g8q}v8_TG2a3=?bA&nDdhTa2!>LLS-gXLCv=p@~Sd{qjj6V#d=D^cPL z67Z~iboxmD<5^t-|FC}*ul^p8gWxL8+A$T1_EDm|b#ZpWuRZA__BO3Lb3C)9hF;mk zIiP(Q)^UpkvIv)|mNi`tyYPljpDXFWWKl(MO(wJ3OI>^-t}nJ|@`VVZ7kXa3H^v~9 zlq=>-vWHrAs|A1o1a^WDcSy~5;lGeRppZoa0v_{Swn6G_vf?g9|QTXzoQ_pa#I8bz!z{xFJ%YR zV~Lu9mukD+^Ol7sYj)qjz?l1ZyzFSH)#%ekbT~1I59!8b01oukvk9tOoK0(HFr1gks{SGPhfe+MMa#L} z#_^?Qh*@CO8wpZazhFZkWY3kv6f9>x5ookW%lGl&4RX7-bGevNN~VPX7m0c~kd68; z3i*#o^xYJA%>j|m7ZvhsM$7_v**$4x>2hAm@VX1fcIii?tBJ>l-EB*Q% zAjDae-G)y(B+S}GMW2DNW^7$E`HTR=4+6B&`4P_dXPV-_-dV*Y^2wM#L|}d$F4-Lz zFt0W*phyIZ;Q)qY%gEmTRhYxsIYnr&%kDK2U(tjtGa+7I9DRz>l=s>MMU%@}oQC$j zr>6%6(}!|Wqz11Il%)pO?jzSOBXD#Cf5;t@Yp2^&UDh;q`(Uf3HuRLT3iv-+Fxhri9ajp`iOsp8d z7GT3j6zP230=lgFX#Z;qIQ_8&N?x~s-*4$Tzn`r@jy{k{3JopF$Hn0(Y&vN`2cl7D z#%wuXHzoTGDGBJ}CELr4A50DndT)bMX|q41>#%>Q+d7n@TovRGtWzl{2>TFkz$fbB z(GUOEdi>pq{QY|L4_S01%O3;(XV>|U|DH}9of-fxStO|X#%!iA3{-V9w)gU22pPt# zB!h_V-K$(M4oE)#sabcTEfx0d$&CCWU-CLy78R>Kz8=p_ zp1|(Sf8zcsox!C?ep3ew6A~~?N130w&m>-lX)A2`ca-8UMqzc1_;r}*U#W?|8K(3% zYao-~<+DJi%dV?P8eZ4(7ZL^X$1G#6m-hoYp~=jLf)ol6m^~5U;khd>SQ0>*Jwu$k zJ|r?!%UR)P=`(TQc3ir{M*i3BVE$3)gztaf4lgh%MM~A$n+FF6Xp2|a38q)~t~D>G zPV1r&wqksIrbY6NZi+>ZVVfHp`n&u6v1B!$_s{^RDlKbKgAobA4rGYT`g(cyC2nGq{P2f#}2u5VR4T+F;{vc34Y)Z(c`RgMG;0`>?_?s-oYN2rB9 zkVpvoj#@L)Zi_tENmsSq?uv6b{RLdB73j8le1r)^y4S@1cdn_x$l{5FBK*$@CwpCg zFu??RkL zE*v8Pftd)DO)` zb`Uc}{!7^YpPT*1lSQ*aoGPT?Is-n)ZYIKS0M2h_ms8|57)YKv;RVV8(DR16Tt?@Q z;7IS#;IPCEN``hQUTXH6DP~z{i6=1UP&v6{;(29U<1UJG+?3_m@09l1HBSmIf&e{i zklUumfEXsH>61FEf4|H5CX6RrcF;5_xqU3Kq^!Wgg0_Lf2wahNw`ww#c{8I^CZ&H| ziOl3Ky7r8n5*lj#9WDExUMtM$LbBWRl_(Di_gDmrQhl1Y3SL9)0me zz@0NBuOFxYzlpYJqyUnXx}=w?gEoo1UqFiU)zu6(^_v{FN{~LJW=I zeqhPe)m0QZfR`Ty1HZg(Ta3~PT5r=Wp%0Ax8}n@Ew`;uF_+XO%=RSXbNiYt*TENV4 z`M=B+GQ#FCuCS%{o1-1PSm7sMC`&iZn z<4&af$l;qyJ7_2eG+S*zTs#83u?Xr9ZY#VSj|(_6@j-85e;4xndsY17LKNwBEOq~v z%iw22AhCTom37q|$6)voJkb-XegYr17j;3AxI2=U+#kFWe5a^%sMA-U>}LGyJI3Ap zT3DvSQD@*x;KzR32`Yn3227VdmHq-y@vV|%XK4XQpkC^Qss{h>XzusB3zh-9t(=H_ zMFSoF0+jrhXEhZ7UgS5SP*8Uwckhq{FAF#ZlI>D|!N)F*z3pp?3(1v<^XFPsFv)FI=dC304mrxRg|zpj&;n zvc=w)zk-x;#SdyqOGGpF>1#har=1(Y6pURW$5HIkFCpCI6+1ufJr0AYQ{4ZueQkfv|hig(mJ?xnF2bB1m@I19MMo6Sc|hkv(nBD;;r^f>K zW&19JUQ?Z!nQ@orgN|K`5xvGcBYrhIE5v3B|CNgPUycgk5&-RF7hNY$gzNirDg43v zH+l6~{=)AAfoMtRl}AT_fHpp`Zn;MYfW$r`DTg*b1lVl;8~nv9x94SUKERn=h0cSP z6k9{YXB~p*1cmy5MC`Hu0@UoSGQbxI>?{R z5_|Q=f7#r|cmI9680mvSkR_hT{n+Z=oavo!VgNKPTB{H~3wQbGTo$98&AvLp)iZTH zsdUBnsFSLJr9p-1^mnNU)%M$X!@pACI}P>xteGF3zR4Skij_-QNGDww+sz32rxVUI zc~O?)w$7PiGw+u3G}LLgd-SQlE!L4mCE5)b5Z~Moz27(zqQPt&9Bydu(P<~IzW*8k zf3c#Ky|biddN1YqQM26VC+P(wO}@ZjBwO6VJ2d2{mQ!6Ry?pw-lpU){nTUKS-$y0`6Yn6e7wwjG zNO+tz*~mYK!@uovn+V=1YxuHS&ueu)*3&~=`$PJ_iVJwxkwk&KDj-47Y_5jv-GI&j z$nkNj2Gd@B`CiX3WuTpbo(KWcheVIg&g4}~qr=0& zuft}6Ynjp{fj&pm3X~G7x1%wm&kqR>{M-!dX1JLnilyyU0_==+309;;_GtDYGpRkH z4-m~=y<1`iAMWmj(%zQ|;{b~D$dCVz!0nI2MDLGsN|k>9~_>vJaxCil>#T9Tc_2;z73yRSajc& zh-jk}q#_axRxGsgGGi|B)h&Er!5f>y-P!wbWHTVg?$5B@|G6*$)40?3l5Ik6wq2VA zqlpK34R7SdJ%$Fifhhd>i6U39B3Lk5&MA+&UUFHiSh$2G&bEgNCmQ}2^waqdO7(V3 zyMkFFq{2?fGJFMhqO<3&xE3|mF?Xf+;z`^AZLm@J-1!>^(&HqRCoxeOvVbp3uCNRj zS1W6zSN`JE=^L0aoy9aKf8cp0o-RI)%z?KYfy7CRv2VD2Ib&aA`^cR1iHc1foKbNy zVv?oyf}DWd$1Z+U7n2&a6Eq%U=5L&Pl=N3WYv_MK6S?F#g{ozEt+Lg5p!~TuRGjY1 zhz_^EtOVE?Yyk(IcM(|0Gk>h)TXckQ3(w^AQFoP$#~oOH)A966H*GB#SXW*yGwpoS z6vpxG4Pj!7IU4KjpiA>veFlk6t8`&1_d zI#9lHBvQMnZ)33xmZ|h7&cgM!I9~l|GL0J=XxR#rEl25{RZf=a0n9LkO5i&1#gk2)X&Z$h^hef_G__&^ptF zSemqKmc@AbaS-fy$H_uOHYl4<`w6Q0*bQBt81g$;AIs|l1X_MXd`#=Ys@8&UjTxhS z<XJpY6qY!7XUa<)Lq-vIXLCwE%_xV<}Gp2c2vYePMDMuxN|eE9T;f%HZ2cB`Y{ z%Jt9{uNfCjdwRXP>GL8ymg377?P+&)t9e^?(b3-C0g0Dl5g}~!iRNX-6A!T=#7`^cRcq|IuORg9f zNQSEMMSka%+b1H&4=bP_v&U2bZ#aTaJz-;ru>28+{7RhO?QBE+o<^J7Bc8NAIqRR} zc+-7EVJV8``LWvlNr-GFW8z|8ycATa(Bom}lIYbz|F6ni|97#XfdLA81djjV?kf6& zV=@}#MOrQwWGap``$_7K`VKQ$j|;k;L!2ydIx7p__y>cJQ?*#Z185lA2cQ7V?QyMw zBF!k5=XnrKe(sIes2Jej9vGIDDbm;k1=exqu<%g(gDWr_pcAU$SO>Ss_`NlNc9UE( z*PN-xY-fgjYM}{BkBW+SYKJC%k^kW~%hP_2t;OY}HJYMwUv?nwTa?p{ri={b_B||)xfyu&@mYH1Y%*IC?*#Lat0GJWP9U+Zl(0aSy+7zaJ1eD!dS&1^6 z{wUMt)plRkVs|@6!S3}~@>dk!@19DU&xX7EY`xSoHIUx*+>7B3{Z}~1DlWv=FE#{9 z15;C#M-Q(+E_e-)aWr9V-%bm}Im3AcwR$HuxPYj~Hdj3em=u?yO{sgCoC}0l z9&bh;r?;ao*pYepxCLK!=a&)#AdVGwirW7D>_Hr%F?;L0I4j*}b3{Gj$A;WWTIqa_gyVfJpyy*y0)7q;6xGbZ zFy$Nbog4t(+3$uz$IowH*7?2@J1^dTMMNYJO`nS>qS4^A4w*J@_bsxNJBvC^=yR~C z_v?t(w>uwkM-*LDA4Q*7oQQGDyum7a3uVC}_~#mq|Ayy4pu3WNE}YjZj~Os6fsmhp zJ@MDOzl8S7Nmq|LK>Qg;CyV$!oeH`h`^3&POf=u0A1@Y zazxn#1ELU+TtW76~~S~Hd!m0QZLm&`jhtWSf> zZkx1ku8&D6Rq}J7lzG`co|i&Kek(34LQV$@#fk+gLK1AwAi@p2sFPWc8Vo9NWS0co zhQf`**~@$TW}2}Y1p7-5c?}9xO8if#{J{mFSX2e0U!o|9*gL<+VDEO_D}1v|efK@^ z<+$qYKw_v{>29vG=hG&9@|Z<3V5|1(E3x0?hNG+kR#OPt@Gp;`f4m7J@~Tb&PXE6{ z%Y9g2p*;N0^e%+8id19H1998zZvv6v;j#cJ0~WB@IDN;W|7BX!jj?y|2yucL`3DhD z_*pv`eCo|~vcGMWDfnbOZ8_f6@i^X*&}y}AVEC>+Fc%bB9J}9p9ikw^(u7pa443pT7FF0;NBXzjBXf8b$ZL! zp)rY*1LiSy3igS{QNa57tmW`>^x45}Swq>ae4n3UQUuecy7yk!(7Pf{{b8?MzOI7G z4ZGT=V|FD0(&8vn8uXf*H^1@vichet+ADBv?aM zmy;3-S1r?T>vxV=fi$lFKUQM>TPPq?TMYi7?$e>03>eQ))# zchdj4{D55WRLqk>>3nAn;YJ4TsVcyTY}VQBojmxPy=#>6+}F&eGR5oHvz&8q;0r1Q z$EnPGfqj4Fl%ryh1Gu_-K>F3*qlm`9hwxEDEV(rix5v}UA$F=}S}Pe-fr;HMAahcs zrL~CTt{|AvG3$@Cl-2k&r5N^>`9Wd54v}Y<|tV~ z{3{Am>ik`P`D3G)Ao#@Ooj2`7x%Fe&&XMvu5zeX?l*IvGo0nV+CEg;MUQF_}9M15PWNyMwKDJ=~#`jT9x#b07n6_ zvj_s1(YnpU*9w<|_xRb0zA^KV1!ZsKQ!>sumTph#5#^H6S81AOZNCFCWJ-GFtoj>Y$0HayZD2o z?dNxufF=ro9v94S@}%>u2Pq$JaC63Uj4%fNE&>1norO_tA9V11*jz&C^xgFIsC52I z`@1aAvEUE>W6ng^&HX zKH*d)x6^1z4#aQ_62RG*cQomA;Odw+cM7RkDDBnShr8Bs%oX`1(B7C<&hOrO@9FPC z2M~zGR}u=B_V0O>jud}z3i;gZV_Zmuc_!h7UtA9?6B%J%*Nf6QWC<_;XGro-&XA$X z-yHBS9xIds!1Z-_#7OKHXQB4Fs9BC!adUUJ2As>&vh7^6BLF7IWjxOvlk=Ec7`6JY>ZsF@cHDB zBlEp^oaFXMSDG_19_hz+0Mv%{uuki6V#dTc=RJFOL@rQv35YiVX%^;%LFR~e&Gdr@ z(Xl*g^0PP&Ne9i{{e%2mU7pD({=?pn(%oQ$mWX(uAT$A?a{$ieeuL4yEbE&$> z3Kcz*)m|k7j-S~&IA94B_Y~w2ZQI0Rz^s8GkA$cE%Z2}sH$(Ve*;L*C!9lkG8PM)Z z$BpWjdZja)2iRM2x3@)4Or9g;q2V5zjcLL%G^o(<2mqO2sD3sTO&bX2DiNKLk)i9q z9nln8MP9xl<3D|w+U~_PWxxHa|LO8{4LryWjg>6(EmYHNi{ufd)-?7?%ZbQR{$>{A zEO;#g&kEV)iqTG?(VEFm&$0?s1S(Wyu*awLv2$?Ky`3wy-OJj-woAS)@#-C`vF0n< zU7fD4sBCqbfLtfuW%xN&2oxeGXGBt9d4|$++0-6;ewqll4SY z`=O!8q`4}?B!fBrFe>4fo6;(K_u{42*2%2P{0s+UV~qILD?yVrxQ(6D7cP986(yTu35@xjh{JeR3=GQ$X?~JHc zqGVmn#$8#@9H(sq+}2FWJAK=%FMW<_oz`cbY9K!%mLqnoryUi;^+Ub)yT6H^wh|Vm-a%hLQQy zJj#$<{zo4KUfaEHm?$d&QVuaP0hpisZ$vEUWcCR*G}IBH-5kNSw;{Ig)CL9Nms=6PK=$Xg zi*^=MED04)xc(v-QY{S)S`=8Q7S^0cF6(8;k^D3gruET2dtW;mJ>Ej=BJH#>Q7KlN zQ0HqxvSRvEoS$|W5L1nVcXW&EU)_pZF9Tr+A4tUeoA8R0s?etBADCtxrigGvBcy^Vl-UvQ>~Is%<9{T-XXcwPC9|7W8o&`^l{H*vc+4gj?5)G9{JL&oy1Tn2rE8NC5`usrCEcxTq`N~J>5eTWt#q?Vk#6Y?(kx>%B8?}{L57us>uKP4*z|(|MM^J zkrY9BeV;-Q?OaM?|J}LRWHhOQ!k;eDpKDSh;KNLrs7i38oDC?c$l$pEoh{is6pw&`Mw+;#}__d*apl#GCG^m7|Eqtu&6Tqs^t43_W*A*mf&D3u9hGj6GVpO-d~^#`vpfTj|*6+c%ML%;r_)VkdTwP9oV zG{o&U|F$OT9NgJ69htXntws`?V5jnU48GFj8yP5MpT9Yfu4 zt%x|C9njK1Id!CmTOcYQU~j17aj{QsAvv%ay~s~@;zMv0YGjKg0_*IZ*16t17kw#( z$z>+v`AS5*DHGluXDNKGm7uVas(F!n0+qrgZf&+t7>Wvnu`6$1s2qZaj|ei5!S**F zWb~0_Cj-ad%&s7QquJ~nHf+e}X&Lx^{eI;{M8JWl)cyW^&)sCxndrR~E=KmhNezF$ zun7}<1y@k?etnn#3rq_8-2jCPrAjv>Pa2HmuW&i}dDU-zox;W4g(FrK=t7{>tBVY~ zIq&YAoJ1rb_zW9Jz>I7-hhmy{{V4sKC{T!rff;2woFSn!ZN=&C?jD>k>n#PYej)Cc zg>rkDD`fwuCu%>(gCHY=K$J;BnhGb+((n=k?KqTKrQoXa^b`}eIeg2EZN8twV~aN~ zN63tYx7nN1 zWhZYmG;Dts%#RHySzB9gkL8POnN&}7uZCg@d$!;(w!kj(C}cT$a7rpf{z*64oM=9LdxB_xPS(REDg6;iXcx_* zY{$~50_)#r2F1mZjv`=}RT(r@q$|hKyie~Er?8dqO8R=O=j|}}%~HVSXgNiAj%4&` z93n8+beh(v$o)8>oR}6^X%q@Y7q!x_wMjJMq-bqwB-_fu!5&*%BRIQB$cd(y4kwjD z+vtxq{N_b;j82FmrK=my6UyHI=g&vo);Ye>fCNTw*wWe4-le{Ugt-}o^y5SJudx;1 zhF`4fqt(??WCA6W+$1L1VDh?A09^~$I<8Q?VGSuD75G9-5Q#dLCMqaMI95V>o1k2K zv{9m>yFZ#Wx$xzwLXH#$vtcBYSHjkPJ#@aU2iFrx%uvBP-RJY8`i~? zCUbX)ryJVsd(&Xxpx_?jBqVfcEn5PA1Z!hsJ{0k4d7|{KvJfeeeF2XY*Q*t^8f-tc z)jL{o#m_B0^gk6rtMNar>Ru&+JMBtr>AdNspzz-B(Dz1zLl9@IKd3Rf?Jc8_a9;;6 zCviD(UpsHz8C6BA{+qP>ubtNS6pbnPfsHkuFRG7sb-O=HKemucJD&lCC*ru8Ip6G8 z@yA2(9&_c zVU3t;di!g8P8SB}y{>adZz~JQ1wkM0tiSwff z3=BG`#bQqq1irktSyBG9Jz~4Er^~tlToIV|?I;}QV8qk9D%uJnK$}lBd)OKH9sJnt z<5DsI?FbcX8~TZp0z+4Tw!uMUC@MCIOX`HfFEwIvqEKH@K@{KE+HP4r6tT_QeYJ-w zrVI@Q38xPOm068uNTHFSQiqTiI6#?%_1YL>p%+kI;Rd*ieG&Nd^gjiN`PqzAuRyD4 zWfmq?d*h78 z?jA?{(EG83e+Qrbi6Hs}c`xcp#{sBGLH+eRGQ2)IYP-QPS7TG-pN7_xGwC_+>srna zE0ujzd|$-!V8!uciI+ea4t?7TnOjAD+zgg)0-|l?!9g7Z6BArt9Brp(zuQD6gS@d6 zqxYHD&9UjXul-szuKptknftccsO37Q2Pe4LjGv%#j_Ue#RVt5gzn`uzE` z4+8^uZS=CTi0Q)4-ccZvSc$-#lH2pW0dYD^;VLaqfRVS;!JE9`eFMXDhX`sWP@$@XMenUy#9KYUhK*N`gf(s-&oM;g1|)e#vIs8 zf})z%m$T9Q7@>CcH+hus%14II62`8tc1^PuTVh)3ku9e_?EA5 zpR|r`V4Zn#iv85u+kpZ3*Lyi=uDSI>cKW=FDLHoEQw z=H#;+uEbL7gYoI~b`FeX8JOoUZ+d`;a2hYhFSI7*dU4+wXqaz1$!eq-Y^E$wPr`CH za% zBvrLIO1og9BI7TjWigv1bt%xj=nUjv6blu#7kRn7oWo<@2X?EHp%jD#jLCz{1Tdll z=tz{-yaP2tC}5)AFFu1imvsi}PazSA^&4uCg#b8oR0p^v)i5SlK=yMamEiv|05H5+ zM`NWFm~PY;goOdS2tlB^r{HNjskh3zOZq<`ft^Z;H1levKR?sgl-P&R-+Xx$JO|RL z>gz6jtMjXg(PGTx7)WV*{!(`*Gg43?}C_Wy`<6i@YYja)yUd+tmO^h>? zB|_liBTjzv`7@u9R9J!z*0VFRgTq6VlM`I6OtatkH3}cjtYYN9h7lZi%ZBWCv@;)i z3usoAcof~V)(E@r=hZVsR4UBwFsP}WSp)0bukhcs9*L1j_$oL zbAm!bEE0JoFBic;Q%&W^HeM)vD_JQ2)lt-{iT?~~yMpiIJy#+Ub~z!X74VH@uW)!= zUV4D0K7Arr?ywT=G#e)xmJJ9&+=OjMv?+!n-adRps{9b`+M}N@Lim3tZTc5~IaF$7?lKZLni@3Ku1(F0oUS|x8t)98r%KY254W6ecU+Z@5!bpMbG_E9AC|vhib;U8kLq)gCPu#Q*(MuKGS=ozDTI6m*ZyHiGK%c1 z{qB#25YDo&yusLKn-%+fi;nmE>L8N}-?&kG*mWGo*+B!PAh)KQGQYpzp0}cQ9%g#DGnuzE`1ch-Z(7=qgb~|^S*-=CWv5-@hoa7h5-u>9&6q;7&nVQz*7iH{9NJxNi_bao&^u~fpL5<(#V;D@Yj&sUO zB3ijO>8x>efsegFnQ|yiEvFctD9Xp9xDRP3p!yB_x)=yY6dkZe7G)irpjjqoH;BB zYzGb?J5&>UQl$RR!9)rvLQ3Ts072`LpZ)#BM1kN(I9ixh^eK^py+LLsKqUQ%hscvqfYfr*KPNlw1RM5NM2>hPN2KHaqXoLh7= z!lNG-fuY77XEco6kJYOhoXkZ!25d1vd+s^{cm$m-YBoJ&r-Q=unv((J% z20yVm0+PN#P6uNl91{asDMX zCTCAY^irEfULfhwoD&w!3sv)bvdkJ5SZ4@2Vw1PCR%_^RCa&zOSEXic8lg?I<5@t; zvO8F6kq8Mv0?S*`tgfx~kYB?;wn}%uFdZc(P3`=%@rQxbdH<8wxeO?H?~Z+3xF-w( zw6v^J!0zj9CzCiy4~-Z5V^fBnQEjhud^bYEXA9@IuFTw5Z?c`d8LQB@@3hXXPCb4X zyjDypd`Llj&;|UbFn)4s>K{km!os8v^(Vg~UDvD`B}Y%1xKtntmw_cEJla&0-ne69 zlLW8q1nwr)NLObnQ01}ZZjES354?$R=8xJH67VrNPfjCBOG?rR^$y33Ut(JeTCOPP z!WYR?bw;osEC}l`rN+l&6{{J)E!QdQyo^|G69XR+J~%8Ldm0-UV7z*@13q4T_shCe zBX^SsXVqQ3#cjB0K$hlC?0Ne)9Zm7|-bOP-?`P)s+7%Rt?cV5YVq&RrT#Q+nAA!n= zh!!2as`n^|)^F~|!F5eJM`2Sn3VV7)gbA5&j#%|AE)ZG*=o1feM;N0CL93eBdqBBj!>1(8L~NZlSfn?HUqkkIA{AC%vx zw1!$rPY{V!|3Sj6L~Pp2n*gU3h}_dnW-e$%I%J7MZLy&GrVzwn6jx+{B0{X@=|g3J zkoY7)pA-HJVA(rZ-K4rTUpKA*b*R}l9g7p6ige*ruoM-t+=mC7mtGr#@0-|r`AA8v z(*$hsRv&bfukIGw?^(Gu)$PMwHwkcRKP(}6T{sl8;Jl&K2gDX{o30@AAyEC7FVVt- zFB#uCvz$rAJP#dZlh1zF>{2$~J=Q*}Ab)F=8-6x4b4zn6PEmll1EeJI;UYFn^R%XD z-`g1-RX%zf8Jkj`^^dZuAHqWY{TJOTOa0*yx>DnZuJIjL(C9TbC61K#%v;$dwwcSF$plhlOzc>yCO81CO5M?X6(EIC z$oAKm-=4^r=2QF476f!B5TK({+18c6nEgjR(WMoOu~B{shtGT|GXEdKA(M$8V{yhy zfXo)O6+E~#5?_*7g_L5_kLfRsY50%=UeAF}yepFlaVh$s872~(JTNTV>3f}#Ug1)v z@?)=pLcF%7yV1l$$mxpOJGk>;*PPrMae4{zhkB>dv7Ul{fZCC+GCG(eW2KNf2@tRq zJd=|x21pnlh~~b+@qA=Xb)gx8Y*-t$Xl=mZ(t=`fH|1*xy*R}iNPbm(QV1eM{ygyJ z+%?Pp?gY-l;x}4qx=Z;FAYD~K;2N8O0QKdiTCGh)g&?3BJg;HtaVDVLOXpaFyI85S zx8I&(3&VE3yRuwHl;i*hM9*w}%Gc*@fESd6SPfJ3dj4^rrKE`2%ate_i_#6kuBG^z zAqyI7a^~2cC@frV#mXKaf79SVJ}l?B)59fa8d_@9{Emf>92ZwOO~m=jGQa=h13&m^ znK6APEXrnOk8HeswkH1PFS8YM7a(%>4=9phD<~o(%Vh~)7&1q-*g@MHtYHCX5^%E> z<>gtoRr}UQ56SpV)l?m*DV&qcK3^-raE#=z#gGzWPkuR)OVF4W-xT|7G@iY=j!P!7#Q!}s^LZ%@Qsz5XXoB`APZyB$F zAR_4Ly0iSBvBz8xfXt^#5&?AxXF5HxGqo<#q$JY(C$x3kozT|B~$FB zQ&7OHyecXYxyffjoIKst$tK6ZmNLq{vc_5@p(cyf=6X_|>)G zPO4KgVWuC^d-J$`E{A10&G&b_bAAscM(KsBWAXmWtxjKQ^2b563urqh>jHB%vdaNm z0B{d3w%(vH_dXkJv!8VuS^b*SAuifXgGD|OLoQqDnRju19=4T3*|!-+&BVmi4Un^5 zISS)*q!LaIcGGFXbNcN*lrK`Q9!Z|34YazDlBWKlsqZ~6`hD;U6p+)_Tk3ZY-kzxt zr8b#6)fM}zV5{AgF5w>=IOyS{4pjH`o7>m~~Z4E!8>a-5Y%=EJ$j4ohcl7%wmXM?FETUi9`h}g0|s*6T8 zSuZ)MS|kmp!aT@SkY}Y7?Pm`Kw?mtop@Drd5R>h(LumZn$;{p6?2P8h~M8N%~2mtseN7tpb)-o1L4(;hwTCw%IeM8P!L?b4y95UZn?N z0p!z9BtyMW0|aj$?X|^_5a?vCUo%>RBg?Ve$jC@Q&QUiMA(R=sIpo^tBNCg2b0Bq-L{DULLxqqINC~GESVt?jJ__E&gf|L1nPAl+tI=i;7qj(7K<_e z4>I)Md}#9Si6>YGfcaA< z#{$8{{wu$mUsgKGmH=`>z%VB3)}ze6;$#*_k`T9mJr@V_Fe{k^{&9 z4vi|AIMQBTBw~&$?IL+63GriBgfUWuuXf3Ge*6@rxPrhp#V1937oLiG{=qP4v1;ui zq|nK)sfs^bd=(OmI*L)uOgqd;CDnyZ5nlT2jHJbN55JDthnnIA1iv;OP+QZysY69p zYeb>&(Lp@66Ld4we`I7uh_Drb%PiXRrGvrkx~d?P(x)6+7=j22n>#jQ8w~T{x=0}% zrQHfcV5rjsFlA$S9n;B{#?CK>**F(}m^FTXk{@$hn}p{>P`@Q--&de8%ai7#coPY! z>*;?HA{#!U)fAJKCO_8UZ>`P6LWM_JySYXBXL;-wvelqsFS@PmyDa$ge7j4oW#QVR zbEk>+hQoGRo;-e*3EM9nXa)Rt%U$klL|2-^A~)@+7^uhTkt-{9=*Y!vY+8@h^ler% z1%6AqN{cqUo-#H@!P!M+I zQ7J@A##IzSZy>3&A3hwGg?1q8Y$?T!+9ds1#x<7G_N&~#n+ien%UM1i6>p3@`#l~G?F@OLYC0cMP!M}4c4MDSG`sa#XpZ9eTXyDh1+^JDxjzVK0&@gdrsH^u zc33N|p0Z>hV5AI5D3(o#wUXU0zKlyLZUgV%1D+94WoN7$N$DSnDDCXdWjLr67Q<^* z55)oYSG#JNQ>Rcz8@PZ)WLDi#WAT-12%;GxX#aAmw9MxWeTU5B1He5zZL_Fnp@_2K z7Xf`Sdo?f;Zh3Pl*Ku@v2UPMJ`tp>W)l-Y{0u{ZCpeugMXs{H;dy|#+$yOcbCTW(} zWSyS*IBR9KWS_MaKx5yn#cXCP$us3OG(;U%xM&ml;=yooXk@msiez1uqdAGt(s!}z zedpm3Xog4y<>dx*)utW$?qM~E6YV<80BU;&Ku6z|3Vq7$xth>FL3akHPXzvgyVA_| z!fNjeVepwRw;fKLNW(CpwsT%cN8EK=*)EJy-bRwHa}wk*?yWZ`wEs~ zw|maU7Hs*m{+p)~e^~T+Zvkp_-gmQJ99zZXtRqM?c;k?wJDIs9WFpOW*tB(O|OeixB_3@%r*m3Pq0&deMo z?>-v0=d)0Bmx9pKAFTMJ94?hnY)F;ph+XVX)6CbuAIxC&Ot!=pu<8lRm?6OV{rh*< ze4RJ;$e1Q9plMcJR2{Invht-1J5nu#uSu{|BaUJNd9)6IZ1_h`g`+WzZM>ePbJnMu z-SH_Ywy#Nr-rJXhJIRjztc@1(LIbZ)S$K{nxA7?@_&J`PNX{_WmBfNq|3L`Fs4s}A z?5Dpj1tcK+{!9-G5cmp>p>&5`Ic*%hYvP?6l#e^aR9DY0NB7bT_>Jb6aw}}-AyQ>?bA;ng%?V9@-+kT`1a8W&hJw2N6`D^2v zfGU$O!{|#7b7mJ4(*aG3Ddn!{ZCX9;3j&dkzY$nmeDj29Pd?F``<{p&KO9TOs7#ay zD~)?#4foDDsaHosE$IzA{7_Z18X2PgB-@ES&o(j&t9Q+v2gVS*oM`jQdC-lB@TQT%PFn3E$&QM1xjHNKaYNp=yod}% z42wRCX(I>nPZ_xL$B$2>il6!i0ZBf0cVk?a?2&Nd&&EQ2qpoB#MtuTB4uB?TzkSQW zNadg@EZ`F19$kwn%r%b2VT_#*mLIn1;rNosQU$HEKY`lT#ZE+Evwno$62%!;z&A$O zu5b{3@vVAJRsFj?RX^bgdmXP<28Bh-b!Cg7-MRL0;O*+{)O`O^PGqEi=l^x-Dc$XXSB zgVh1&vG5{<=Y_c5Tad{BM~#QkMSKG=9(d||O-*#Y;|mO%q|zin$26HDj&5gQf;BzQ zQjQA?LDUO1nefBTzC2u_nXes}bCn*Q0c;dJWj1hIbb@(BzE%3j zBS(VeS5uoca88dnEEcX4z@7DXwl0)-Ps^1z8UO#VTmt_r7l14NYq_kK#a4F?yWEpg z5|L>uul4Hmp8*=Qr5vI!eRc;WX4HiviK=BB&_1N=W%Nzh3L;bL^yb3{$#SE%aN&9B zb?VnL>5D{w5Q)U8FaMqWjL%R#|A0K^Acb>bu(CILZkzo@M|@mB-fU+CVJYw3Ooam7 zvuE8AG`}+=u?6iT!JUhHtk0~0TS}oJfp{d^Y#LBWQqFlKo6e#ntiLh(wB`#q&wA8t ztkRHQXt78ooK8X_Dtp1ZZ#e`JAHR#|gMq(<0~xxsy^OhhG)msndb;eBAucu7SuN3k zz$k!Oq-Ug%ToxLbN9Ui$XUWsD-xuQzzPozn_CcxW{X)?gU`}yklc&%H;`8oSqM#6; z?5z=b+SRRM893mgjZe*aO8l?xVP!~uvuuX_j9g*m@`9BL&)*2Ifp-+3{j$V8<-RH1 z6tB7KRsdu&zSZH9Hx}RtnmN+_Sj0@#S!5Q7gwcHb%i*;QFPyy1*AILf5|W@fT4uEw zUt_}-w|=_I-I2+xS-`81C17u4jBol6C0G8Z!@_P^Vv>6LBT!qEY)}S#$o-GJO29Al zvE2Eo_|Jc<>EM7l%EzT7h2uC*4x$!USGPd!&Brh~V$ffL8QOq*c7aSpjJDG`{k{6( zw;v2}?ez%5JqVqqmir8!w1KZ~Jl(j4|4N>kws-CSXy5Oa@Dv)CCr%0aYEmI{KVMK- z0%|RSe9UflQSs&hFbZ@tpl?n_)M*XFpu|Cu(bB5)(VEK_l;$guuFn2@*44nI{QgBjj)@!b{&PSpaLy!f7%lrArX}~c% zhkEghNfsaV;ck(eZt;bp4Gs|Qj5+T(^T66n1Tj0fhM6}heC*?>E zqZ%7?3$_)>^)0L6izwT&Yb zUoV7c&GVP+GH|>HUfy|fv9rUlo;PG-Cr@=c-{0KFC!bH5Jd(YKgW#aO;#`CC66%*= zAv;+Y)PTXIe8Sel#E4wIJ#sgzdG)F_whWNC*IJ6c5-Ag0d!oA@u}J&c z-H-4|ounQNiE~e#w_;5Z^jqasK|Js0-=Lu#Z?wFW=2Qpnt22` zf8ql%ePd2z^#38Iv;5<9`)jQJn}WINr?>--zSg#48QmVz(p{GB6Hy~}kV(FCR0I@c zj-GMM&);)8X=i-PW{p7UQ@(i7+jDP08*%n=N5QhQqdUo;)E@YcsE=JPSEG3N;4!dk znUzPB_o&LJRfoTq&*sEMB0?$;AfOW>I&`)PJ%_Ha;ik*+GtQd?pZ?7sBVH07iTx zWsY#JD*pL1s^equ7B)(#6Oau}y2B8mrLt7L@|iY0jTWyg&&Zu$8{IVt^abcSn0z_~ z+;d_b?nAsRz;@fNplVCO1hhdh@E;2;+^H)E*`l1BJ5vKRbezSeZn{WIul2gPJO(2Y zQA8wWW^&|><56g!mx`jOX)tm;>vlGM&Z+PhxUKqTI#U-?0VmJpV4gzMb;8p~O-U*I zU?FbT^=NMo8z3CvNmG>qBi@t(Y)DTIUSAIcV2_qs8h{~kE@1(EWa0%|t0UR^e^55b z)GcN!3F9*}xpV5Z{s6i83FNOn5D|DO8hy9o&SUYdW4gTiTrUhbrM$iRqG9D?BIAjG zl-e=dGj1A!`5UVQ@NlBp3~(?#;|+RK<&uPfr(L-S9~#=LG+bt=dTT_Te|0-R_5}G{ zD~o27AeVou%z3#oXrrL%$^rx^!Uj6G#3SE2Wlw!>X~|@kPQNnLv+seYa(}o(_d4P! z(>W^rL(RFtHl#D6OS=`7fF9y*E65u(7FVj^jDh-8|6MlaJFtKH#CCU!!GUL))E=BX zgVDjCot-^IVJag)odQ5JS!r_eCr|&AWcU0&gAFk}(vBH8|5HP*g)I#KnWYqas^J9z ze(7(v;Awm>oVKiQJr}5sD!JTaksxrQgYNIj`=L>-uZ_TCWFKHkM20*=@?Mbsz73mw?aTG(d#@ z*{Q)x+M*Ag|``0TS^r}5xHp$KJIfOErCQTb=N1{c@y z$;n!b0W#vV^0$0xGPu8NIv1N=&6hn;+l-VGvz(?57H|Q3_Ni9uMZp-Hgh_Z9O6PK0 z@fSmcDNeX)|>WO%WIK}L6I)tCD=i}K$2ap|9 zW-;&6)@|YdU)$~7DJ1`cp}Sj@T8>3G;Pd&_1ez>Bj^r`xTv#9^;hblx{QCK6<59oD zenl&x6q+p-cWVCfB~te>OadO8Y2!0s=0AE%MO{=|8y#0^qDV)70EiMNtDD+QxKWXj zu-l_D$?n40RS_=(;h8aYY~+rEmE1g{9g&yd5qX+a?Ss1%}{iP~4^Q zQSFY`CMteB*>J^4+3TnRKgpibcQ*Wy^B+BFj!)9N#?|>5qbTt=j0QmPY{(bU5G2?i zO*yljP2b@gy#AC(GU>_5g11A6Fz%NSNi@Q|6fhCV*Xvanxttx9CkN4JznSyIjZgz? z+1aIT1bdOlbZ~{@QE1{kEPAKL=^%>_y9T}MnDI>c{`1r1m;Y1TDv|yNjivqTd z=e8en9AA`_%~Dg$-p-s)@UOL99OD44k5DN`({j#L1?Xn&YL1VIQNz$BYn#E=QK3>9Xe38??30G%}uYA}^6cPZ9_h z4Gdu7v$G89#wl5(s@v!4lVxZOmEw)q#s|OhgpKX9729yv8*L0)Eq;)wyxpGTMQL`m zub3sN8U;EK&dy#-L0tU(X{CJBBgM^O0Rz3KbkynE1a?Uv()+hU=e zW&k?N=gktJmh_w&(e-qxLBaRHX_t0bs>YUlJBBGpQCJY@Cj}?j z(VDK#Q&~|1g=Il^D?+5e+LY7L+bQ1YgsjBrgK%VFtRmO`qp&nVkIdAc@f6hYH zS+5cNtFaNuC_dCxFL91XdrweC?04wtbcW3eCG)cf$pYVwcS0HwA{T9)sbbhu(PIk9L7~za16^YP|{jTc_GR3&iyr4c-jIwm6cg`hsV)aO?lgRa4g+J=%T- zm$?^96X|&7VU(_gRj0y;0rR#O;A_0=inMrp`?R)Y*09`KGzl%}${*WWaqaY-e&%Jgb{dLtT817CG^SPnX9t6$DwnWL~J~8PD(h5c5^vPj*Ia=`rZd~ zR%^?K6Be*Y_2h7HJm$~|x3swubFp#ndsmwMCRRQhaN~#A_bL$a=BK^l8qSfxXl*@1 z_F8jmBppj5hCGXy)dj@t0+MJ+dbAJ6G=RktZoA^0{nuEql&#T6^U3y9h(_J(J73K@ zl7I_|~8BmDb{;wDxOe-ALwia>l0RF`TaRIw#! zlcQlLG=F8Xnba0)ZMW->RFBK0)4GpMTVbaNX!*f!iz}gk@ctyi=xr4Uk@>Lz52>eC zlHa3$^Nwiw{EIpzxb=w+eEAaX@dGQ(3ekAhl~!S29*mZSlJ@#d5H!>*1CZLQMgKy%8yW(;8?PoO*q5ImN3l&GCNJ;{AtEP#EPlav zN}#c3@+~$Pt*wJzZq;${QNvBOv-ePAlVq?gM`CBU;e%LI2;xPfodC+(CRIug-+;%X2nJIaeS=U5>+xzKCwgh?9*82TFv|JqT`A9 zPg;qBG-&CSs^JZh&}3uO1YZWE30aFKHz^iY6Cy|ck`Pd>nqE6EwSw%~Z+6t02v(j099KJ)AU9lLhn(Lue7H;7b}@BJG{U$lfs*3#6;16>+b$*chYvvtTmf0?ZL zwdH7>Qk>$PQfZt1VS7A^*^|G@_ICL*G?1KM{usYJX=eTITK9pxLNjjTDm$X@hE<3t z%BpvE^^}*FH~9!Nw<&BQ2$?XQN4b(yUhwMSHv6NN&%0EvioCbQY3!$6d&ktTB2o&c zb!j2Yt1WF{|ErOZ@ob)nn5NG&O{_8DE6!Kf&?XFY#uuz@lWJ)^a8PJaND+9w&k*Ey z$H(2=c}_&&_X6n03ntAT$$ep@=F@C7Y&5*$DdVpf$3!)z^wq7N5g7SV?wu{FI`+-e zdV8vp8wn*O(Av=Pu%Cg*Ip_}ys9tF2aa>Xx>rVq{MXA~*lDnoa8ZB09g*e8ou(?hbj9?vFk`QGq2rkq6y(cUAbRM< z<2KN+fWyv2x3rmE*_#I%DoJa9-^AfWg{)0zc>EEdtOTp$=a6hh>1hq?*%_&bzoteXGMsFWxb6({=g=$ph{bnLvWb*z#;ZO3nkE`paPQ+`^NSPt$x}Ns&FrnIG+Ymg2+I1gTOVlrjT3-BFNh9PMdd}r#dRe58k>D zOCh&aypM)%e|~EnuAnEc%w9PHw*t){Kl(Mg?2w3in200@0XRWBW)KM>0fzrC@&m8t zj}x@y8gH_kkPxE;I}Z2fX)11a7lg8jC$#S8LKy|A`5sU^j%D6g3a@o77%pW36C|Ra zIksQK(oun({7@8hSm3>&y-CM&$jlud{MS%UD|>Xsp2Ut;f-*V~upGUmHd&wPf#zy{!CmssYxaKzE$G{>_FMbw#g^ z31XhXfc&#nO$G-a*YIuywqGK7l-xu-E*-kY(0lVo{}c3T5nj0(Pvt{g)~bexFl`1$ zq{x`2$c07R+s8&v(!+)HW!OnxHN_coOhrXvgPn+4k!M=QYn^hB*l=8xM>WDtuq_M}e9 zp5?X6eUTrOOlBg5^Kx+5dE?#u6hAP~4c3cCGzvH2~LOT^=0qDoufM>N_^9jRnz zcMV#a^`+;X#8&zVm=iAhZlwKcOoK&d@GXbrJMlvhomn=ad!u^=*gnU$bo*j3TOky| zI>`KUs$-nJ=DeY_0aVPGrp51Xo)# zI|5PS)-5zn@)~>3zPCWI+HJ$i&4`lk0vLWQmj4Xvaa{^Bwj!1Smua+OJ0*BAGe-ss z;JJM4TMAdfSPLY_W`u26VTTrF#FB-YjFkR7PP;Xa;{5@1Bv838JZjMKm1}&79!K#~ zsj?n&DW7-yr%;m3zO0nw-<4Lcd2lb)VtM)B{Q7Txq64h*d&H-)D>&1DoBvHFx(QOq zk3`fRa&~&$=%9dcys_^bUOZa-BP5upfPoD@m76lQ`h6to+I*|(OshN4V6i(g+uxsf z%4$pt=sv;K)t&6O{D2v2rfLPXPre^-6jOp7mu4}B8+w1WG-0 z?vIhROb0CP*qzoQ!qhn^YuK9qmv6QU1Lr}s1?OL<<73waPi+P!lq?>N1`56Jm` zz}DuxlL|15#mM z8*2DrFcdyV^99vnBhTg9+2AjGaH2hj3LuSLIA>$>(~Qayd1$^tlQx)x=5)c2aSj+C zO;~&w5-H}AW+PS7wC4k+P=~&1I-YmPzB);CL^N49x<8*06;^H_jG}5R46aOsJeOcP zSNm^ce%{dM-LHIP&z_oN2dL<|!a_<$gqNLINMmnE+Nuh=8V$V8Sh3v>#hG<_Q$K|w zM#j7(w!y0NI3ZC-w>*fEa=m=%dSrdLSfdra9U=*Eq{z`Y0VqpFMaRcH^*A(Kf+ z%w%y@`aHFvjw~9;$v>D}s2o+mrjz^|`tte9OYNci3d`$12L%MDR<nXo z@3Ts@h8f2Sl{tV}8OWDhd5@RwFjb&)!=S<)JD;{`y+qyD!|98Gp{gnkFe?EeXK)T~ zpsryH&{gJ6cjx>*0J#QOxedNNFx=#MruFJrjY@oK75@bO<_7E#{nV-*HxNEw2LICJ zd2omS1G`vpv>O7TsAd{BVdl3d=P1AZXkhwBSf$oomGcvvLY`J4f$smu*jE76wPo94 z!7aEGAh;!12=4Cg5`sf;_n^Vu-QC@t;O_43?tgQ;pLA}&UtL9=;uLT;d#$WSYqx82jEafbD38GnDT1l44x! zNOyQF$93(+F7seCd$+|s4nyTTaIbF2$HSj8?lKUtBb;i%&m)XNHyP}z|U zU1W!WuLz_wf(O^5eq8oYLqp5;6MDYO&Bhx!Ky zwI=Zm$se2cKm0^8q92Hfu^w(u;oaRIB0S$<#9^gD7}G0lZC@wc8SSJMZV7TAr|JzW zS@YO|PSR!zsB+#b5r3o2IJ|A!cBb$;(8gEJ6K|~bdw+Iw;{yub$kXCXU9jp?TKM?JI&m;8yDa50dt{)@AtOK{HG_eOhKAPBAEjKs#}d{(X}Jev{&>#! zG+g%qXP-uf6$D2iQsMK{CFU(Dz4>dJZdonr-auBRvV_N68k4$wsDTA`P`$SY7K%jY?(y+##r_mH z>9NO26Ne8^0hF_W1goXTgL_|JBM@M&t;tP|O z3^|BBuDH?^kWa#l0aKdzn)~$-GEAPQEdXE=83zX%%gOlqfYTFb52GO|;&!5B`u64Ib9Q0cCyDp8uGOgCi9yyQ2HYiPXl zH>~X|-6NffBekUS%6twsk9$it*M#IW=iHW&q*jZrvKQN&=ssk{pAn1vw)Ez&bJg8t z=APbfXyVw!+T!bbc!zwV44c7~vv%+l+_Zu(6JX`tbZQ#LK~$!qO_@JK8A`aPm^-(n zn~Tdaz+e_{QF8tD$qnVsQSqdx@`7zl&xEt5L7W;A;S`Ry!>jmiEj24VA);qwn}Jr1 z#TOuRt12vt`$j~kw7gcXe#&s(0I5lh8r8}m?|RB7U-+!|Rm)Lg`(Ao}z|bS2kJDp=^v`a==f3n!c48teD% zE_4FNIS4Fjw49bRS8+lUjCCZuOG@kw5zBxiz%_Vk_#>RuB;7F zt0R?Jj`eJN!h;8Oq?{0yDnj@=1#xsZ0eY0)F%-^*tj$1QfBS4koUna)@m4<}B#8q| z%iSh*1WRmg6;57T(X94zkBy~_ed$Tt zQL6O^&X3Wpp_!7~dAvaRXcq z(#n)|cD%qa#;Jy@FQiY-mfj9}Jzq9Ak1Pq+)rjt=XV z*L-gaTVE?6Sq6YZRcOsv<-bhxzfe+W&=jwJC6>G;RAlQsC2Bt!RsZ~re{^iy{ocUt zZ!{H5KuDky;X~PaKEd7ImvqVZWTV!2peG_Fi7d6BAK?kK%K#F|u4P+00Dg~$g?5RC zb|gO^tegt_(;`d>=dywiPQSYoRNf!!S&;EJ-Pr9CSA`zSlXHBfgK0y>*xD;$az|SR zcbN<3_ zXAs8~v@jfb4OWS}Ma| z3JmmFz2YDa`!juJl{1#bUgLx*$Q1x^iBL1ea}bQ$@ia@h8|R~^uc%V^?0L$kC+moW zuaEpj)3v@@4HGazXLnm!ukF9msIu^W*Bnlja6Vm6BGs>fT@XX=GgFvgvFrF%$EN^b zLJC*M5FhYh1Ot;s%MZshs4DkM&Mht&X#jx{{IjNq5qAx9)6vd}gpkvG)3ft!5*v?>U?t+92D`PbNj&x{(K83Ev>f|_dYkhN=~nYGTqc2H~T?%haqF- z`m*>JW-SCA0Gxs7ZqP+y`EqYpR-TV|m7*$rnOr=5)_(Ev?6xHM#xtV0NxCFB`&-Gm zG2Y*{-IEKHzs>fruGHKRSVnVdsax^Cr7<7geBoT+8&8bj%IDq?cqD(e&!6@7XC>IR z=?x7r9Z!eL=4i&*&-z~7ecAJJ^kjgU6JQe9_SU+Li3Np@a6-;-FHQLfg8bAOPSLrK z9?_T2pmk&ngF1)c*cXnfUcd3?H|mDmGSOTU`~%bQJx4qTJC&hVG5{K82X&k2x(;re-B0B2&hD%kwsN<4Ehl;MXFsE8EZLc= zC}|*S->AboP9*{Zn?@}4pMzf&UQ^+!g4WJ+zEcd)`Z*tW5v6BH1(T)9O9FHU&x)3W z_CymP?qNH-=seB~sQHJgahQkoR$6VJ`(dECUPezd^x|q=Th2TZuvL*=j#;wvxV-#A z4*8RAXnFU*7vBQRsrU3}!s|HaR?nq5Ynw%np_H_=zL$A)T`lZL>B*=`OdAgZx-wa0 z2=`9xjh5})V@NCO00ZH#RPzYgb!38!%Wt8wN|6NpL1S-G!bo!c2O)|i2c|At8$rBJ z{+1wVG4#g?UB6VR#<@1R7=!wrKHz@d&RHJyw(y|?+}fyg$Gm>r_gMjrJ)=XCRG0E5 z+@~^ib?Eg_KDDH9h}CXo>EBK1qTuoPC5!{Fv4<82vt={iDc9RWEux4x1a4EN4TU%< zsAkA)C)jQeYT0XEKG(nC#lY_R*<=g+&1}o@L8+hGEt-;wZhix{&BAx^e=?6IzTpf} zbJZ3DfV=%Yfpo+O>THMGZ;+H40THD1^eaVPh#j=TlIMh~Lf6f#5{gD+UZga^EFsx4 zy}TR7y`d3wN$Of>7Q#8E1L^YATU#^b^GsT;aA&KE_DruF_WAQR#@DWZ(WG_%1G7b{J z)XRmF#puT`3o7*K6Ip{5?>D>zel)&8! zD+?}A_D}YN>|de_LM?UHOLsfy!VBkZ9biy#7bn}53C7B zth8v=n0@QCUs7Z=wvA5TLlsKIQ}4~!QM+FCTc}J23YzL14-6kIMF(eL=PpKBf;b`F zrxV)7&-i8Z1yCw`1pBgcb9zQLqXZlwBXaIIKq7H#PUSQ+(Ji!>qaWea1d|kpr4_+d z->FV*?rcG<+bw6HJ8)5OsGQDJo;BTflTrvM-;$9bNg*^;SYV!6kHZXOq&C2wH?VnS zXOz;C;@z!l8zWDu+)6P%mwc*!ESJ;KHi|Y?eF#4=sWVSmWS^`3DS^H5`iX#JsYLc6O$=Iv3X+HP>5IIGG6OCj z#A|AVwetza>_`MOe zm8K|=veq+P%Uer-=EXK}CNhkPx>rq5^_f#NDFg!3M4;-o&V78v?R`u8LkAd|Ss$5S zQLe7F#)2(-b|#`;Y;YoUz2E-^4AlUIkC-XEj!&A+ ztw8;|z2VxU2$)RF$Z|{!yuZ4GS9yLC8Xjd9R!IYDkY}R)SUoPifk6ph&xGkVeoxAo zkJ{vnteCw=Ks`JRUM@4>_Wd%lDq}*Y{sVB2${vnj%ZrEI5^WD3u|iWPTU>ri^@{mE zH=%cupML+DRCF?Ivme%ac5SkJTNRVb*M$uFXCNQ|Vf!ld9$oIn5SXb+FX8!{H~lLJ zKn6X0%V6wS*$*h*udn~J;bfdgy|>kkokDkq<=Xr_4}VJ9-I*AYR%J7QCET9a=@smX ziFKR(V_?l6yzZKe~6?a&Kr~`ZoFq0?cCUbr5pi!%@D9 zf<9UNwq94bp}0P1+b&tQ-2Lq~Upv7ExT-8}Y*xV>v#F}RH485p-;J07GruMg8>jZ2Y@~&jxN!97J z)mQRlwp_x9Z{77BXFE;wRV+v+Tt3P>FNrBhz`O$wDYNh=3CyxGfM)LOG4F5*aqM(B zb)!J3e|5zJ^XBRN6Hp;&o=`UaNNMSF{rMvvYjPr}cK5PBFxSFkCs!5@a#!CXl`b;^ z6S9-s2pvHMDU$-BxCt&@{uc`tlVY?3ucKG7Ajs ziRuahd`@_YQ#tKZE{<~TBPt*8UlhWY=4Cxc$VB#DqFi3l; z==NjdaO`6vLXti&FP&?}o!iJ>g|nYnahFr;7k8w^6lJ&d~D$G4m&ZP5mP=F!c%(PUKpS~hl!WR@WZc|LH};# zmHG+&7U?QR5LWD_f;7))(7!1<`(#>ny}kRMgt9te+|zm_K%3%3>}OAf))IS6^HYm} zlPEf3v1+SV>+1%2g_Jrm=c8RtTg&6md>q0!9_qMaOfAnTmlHBoUM1Pd_ z6m}^Y-dV{We^gv(PNm|LIbT;Aev$SvPn?6$yh0o6k#tjgVCSZ1(?d(C>N>Yna-L>| z->Fa{aZ&%lODj_zzm44O>uD6G@#1ll(xM498erGUhNwm=7MqNh!=1294uW5g&wL}f znu*y)S!srrJcj%+%BaO3Uu+Sip`FNn!T+l?o|l|Z`yKn4T&-nNEZn-MWV{T_zgIlF zkOV}wTuErg@!K~&;VdlLQaAG@g9yI)H8eCZ>8-e+rCF+P5q;zK>O@1N`HIFfS*KXR z+D}`i@j;v3JHly8(<1Rok2c6IMKK)%acF@yCA)=Q#+Gxpf})VnjCK1)pqifZ(gs16 zrRGpGaiU*mC^mLqsHH8Reqbtsg;_p85YmpyFu%%flh7PGgSjt6i`kZ1+;;>85v}{B zIK?W6j~Z8+@sy#JD#tKCxW9yUM8YY}3u(N_f;%biqjF`ui~9gBn$x{WYUM^^20G#r z2LXQ&Ss6w6HY+jS-n|ee>U&QT)F!(NmglG6)=7zMRCw_>k0c6QQ$|boszs;!UZICU z1&3s^JtjiuQh>3>tV?;Z938h03C&=1k(l zQOu|ft4Xhn3F!pePj^*&tV6KX9Yh~5{V4dexg}ppl{-1;U0Z&Ix7 zo0lyPTh(7$zHoYeJ4wV;#n|EajPb7$**wPNAnTqh_`nMRe)PW;xbyV91XNnrIATdF z^2Wk-s8HX3hG-kMtyn&u`T=si$DlEE;PoB7%5r|!miFQ2Z~pGjV7`=icWNjIG_l>_ zv7H70WwWwRrltKbgeYLdJ*#pe7O>_iz~s__L!hsw0}_TeSZ zBaa1NEs<&PbbpI1r9q@~8W3?ZK+HNgmf&Qb3!=Mu(oE#GFOVF@Lq}|`J{A22u7l`` zbGKXKF@|rk0oKpYE1%{6GZK(=_}@fJkv_2-DQIFDHYPnB5YF^5k|!OqAa+@%lbhEttbE(jE8*5!(_%;;yDxl zC7v_S@19$%u(_Kk$NCfms!-=Idlqj`pVI2KN1jvPtwlalEG8`m21c4K@-0o59Z8l& zy?haQz}ayu3_nq}JghFiWmk%$#pQ`(e#YB~jQAZXUi@thbfo;NHE1^48tk9qqrPJ8cQ#@f zi0TrM-j?a{W@x!EfBwpwx|7Ti3=gZ!QC&F?CNc&J*gB6FihcSeh9ui$dTUr>wMMHw zWJ;6c)JG(kK4`RDOPz-qs_L!s@lV?9dde7xc!3vradAFDl+q_y!jp}vfS-uBjY(8s zP8AzOsQO4!YI?3^OWyB|A{UZ(Fatf5E|dW|3aL?+@AJ0vt1S-x3(Sl-^PI1z9~L}@ zt^};Ks#?|u69{Fzg{~zMOEfA|RXAiKe-^<(kydO4r+;LE_(? z2S`f3?Z5S&vGpwzmh=KKz%r~yp=hBkIaH7h3k7M5O)_I-y4B>ACR4)3EO4DqJxfXS zk}9%@qI(J&v|GlcUZWfJaOH8@Cgv4G>!Cz}P{MhMj7X6kI6DnI@?`{0iAzf%ezQRY zn!I16yW`&-?5qpXAGvpSvv(9KyXT27k_^w#izaB-{E!%?#%`%@_BbFm5Bcnqus%TMxeTf1G`|dkMPj5qS!3!w5 z&ED*OU4iSohS#@UUTx=NkKR!uSx@MHeZ)ba(Qp6(z$+Pk)^uU0|3fr%jROoBku^+T z^oSCz6LfZACcwGGXe&HVf~-dOkeJQdHBXcPB^1@uat-X>5hu9#iUV?#6 z_?Kh^;_qDgI*7?aJ#=g6@90DrWR-DMF8`-Maq3u# z(pV#zH~RVm&1|XlP|7>kw{hU=6Vjh&zG%CzyOA~T3o>H;TCR@RKZM!-cqNSl@c}2b zR!^y=aKy938F+F~Xc8j9`LZEMMS_h%soS@rSbf`mDu$v=jtt1=-gkOAKrUy3N}dEQ z-=Ezgt2tz5ikOnnUj^q_nQsc(i(UZWI9MxQmb(JbbEfJ6ihZC5Q zb)eTSyvxD{1{jifEFqCma#Qm?EGGw@UL&KmS)r2)(5?PEFQV*iPyE>__@Cb+Cb#gz z_0m@v2lOdRrh6v#8lDB&Gg6Q<_I+E-W8o0n<{JpKhK7QGb^d;!l6W>J5^;W*$$Yix z04La(PCRX@ltr%X7_C-k`s3m$joT(dEA__1z+Vv#Dw9q-%Mbwl)e;!KG&@7SgHVbb zyrjl+{I(Qi%ee4VaD_pBrVQ!>E&zlcOAT1UmqPRv0Z$3EVG$nT>)9?gouC+k6|7<~ zw-z5Kig_Pyoo|)dWEcBsqsh=3c5!Gu+*3s2rbMz}xA&TACA%-|`+6sv$VY;zSvJVr zYF^WwAnf27IBYcB**U#a=RAH!R#nwZA4_5SShn{)f*#**`^Af!HM1imA2GSYHMc6SfX_sntf3YT^@X%p_O~Y_AN@HE^kANwBu0)5|awUos?V3jLTh zGB#1l!{r$+syS24^>R{JAP?i)@i164TmOiPY3oYo(=Sh(xBPDes8JlR{cJA7yZt`L~!5hsD`* z@1!K|MY1m}8O7Lz_-NRxg^faYd}+H{r&fQ{At!akZYzI<>AT36D60ASuU8L;X_GBJ zAER8AMYaHE<8B{ynmvZ(wxg;sA^w;bkr(E#@k5GL<~Bo(=}COEHMci!+<|jfT@#gJ(UB0y^FIF6h=0X^Y`^{Io54Mnh1_LbbJuP{VP<_CiZDs!U!++T5$eTLn zrAlmfDj)yP0tta`{N2cS>iA3XE_Tjq+QQf5hYEuONN z`Cg4e8(9Rz3$6n$lb21Y$YumvT;T|&lPB1|jG1FF5xe#rQmyE2Td554l}66Hsw~$F zhb5&`*2%IO*2ilIYHY*JpDnsQotf^sxX7LtsPN_*9?1(83X((r(qf-~Fr|*f+_*l47eqU@oxzVy3%y`ZP;Pg*aDT(tr#Cf}?GZ!YVrX$jNSewU z^|%Y$>(W~1Y3tzJ->yr=I5u>v@>HVHqMo=Bn5*gpve6*2#V^OXVXx0MBJ3#lN0+nt z(yNG0iLDO;pWbH8&{VAPdNb|UE7h&e@>!n8O z&*>ujv8GekB=S{s#up+OQ0GD)5IEwj{l3B3VX5mJ$eO+NGi2FV;$JE%Ozi|v$>^|r ztY@cj8^$^gS-&q!q`{2`xw=|*ik;ZA(wJBKWK_!6*)e^8V+du!sWCw^&JY3zQ8@*7;Z<@#&5i#tak2A?W`{Quv)00jjJg%^G363Cin^L zc`t!>;@67DzM{2_^}c`WB_;ES5k#RagWa(}A=ZyiEXo%j@MJ`=N(tSbrROL8BVDe> zU>@3(gZ*yBT5r^vv?L2v*^_I{mr7`fxjnQC)o7ztkh3udlqrORW`{9G+2AmG5g7uN zr_2rMo_=>ql|`l1ydq%n?3o8 z)VEAxN=A@E4K)TeEcq^S5-~X)PLy&XX9Cr(b3a0@7xj$M#>P1CBThY$lYx~1;M3Y` z@4vi-!%m52;j2y>iUT7R+7SCgc8P~@0E4qaIMly7G8tQl;%5a_TSgn-2qHX7U(I&B zKkV-xZ^giY_*txrj=fa~8Vb)a?lg8qi|cBmI_$T2BNY)fd(@11_3$K*Yo~eOubLk` zg;ya(UO;p;f{7?$geg6G9e`dp_srywSep(hNbdNnxpJl9YS_nAp-fCmuOSjB7+tvX zxL~Y)sB1eqEM2`w_fKO?WuZw|31Bi=@Z@?vS?!dGFKT(dc)Z$Ok0PV3-uPKuOq0m; zWSk_$2MFz2t%JCLI2s)4ZS9*8nhDqwo>z~?e<_`{yg{MEM1Y$z$>J8MZ|3xQ=TOK<{naA6%YPDEAT&#xrz z8QF{d){3c%K29VQ)yCzu1zYnvEc&_Mclu`yq9Mb;n+;f2w5tus8k%qs3N4nKB~P0l zK$nAAm?I5J--7OlO@Fyfk{-Oq3m@upzdZ$8b;Jcr^Y`&A-Z*djH2et z^|2u=_Nb8+)uC5bGt$w6Jintg9y}l9q79Ijg~l;jE07E6-pHIVW;_I(-m*rA^uB!uoq4i$~@>JDS$cSh-Ch!pd zYS%ibpo{LIRRu*TDlna1DD?#@D1WAeo*>Ykp5iD#WKUYxqfhn@m^Aw#xZVCKk>x%O zadb^?i&Cxf!qk5;ZpIJD3$rHUhk->_aK}T>X@rbbRsqiUk^yOn2C9=p@M zOJrWzI94L{JT=}L3y{M9Y#ziug&LXk1A^2%>L!HVbum?*60xhhfpalA4JSmiyJO86 z@zy{vMTkyN^x(G4s5Cs`O|r1x^5LnmM8CjM`nx8*pPrs1QZ-A>`uW>YN2M}lQT+m6 z<{Y^-8Lq&mWq#-X2VZ zj*RxxU@1RTAvK<~yW8`+ni3D&pJ{PBMC4?7_`1xdaW^z5Zv;#mI31ifTYt2`76SzehCc`-TKcIkC$}8dEN)z6;au-LAi%Fzxp$q6O0^vI2vL!0$j1=?h<5?NDpE zcG=b^TUQH-B+T&=ANO;Az;1GsQGG`d%P!W{Dd|9A6otPATlebh1H*ez&_8lrK9Ud5 z?IAi5fV;&QPyg58;lF)(vKUAwXx;{mRf;bplsq7cL3C$s@ZOBp2V4+D69?t5yvLEe zX-YCTOh`z5S%J&ddEAR9ajL@-hv7^4M?51X+K|>XBNKRYI}ky0B0< zVtLneGAT1nu36k}tX+;K^$g_4iqsGR4d~+XLfmB~WD_s?&)+k=9;$zEW_V4M1F$nl z_pu}#gu*hK<&(pIwl6NKO*t#R+IiGu{o3{7h+)DGg@&3lZ4a=3jPkG3U+?erFwcvk z9yJjn3EWeTgLIqg%jEIIJ^OVAqkQ@v4k22n$Z~D3qNh7=u87~+8M4NdQ8Y#IyVdpO zR)AHr{YFJIUrr76_Q3S;a0o_eB5w>B6@{un*&t?F&ipFP zC-vMB8sK@;x*d~#lzm>Yn}WXR)*D@oNxNS2^P9Y&g@LLkYO@h?|I#g>5|hU-*&E^8 zr*A=#Xjaw#Wi^oxl^kJ6{^r?1eE;z@cDtWG7#?uBEfuN?XJtSyv5H^kNlL)oRB=c8Q$kkdrpgtE|c$V=K1`^B5A7tHL3QAhlt4QfadE;asM8NlW)TK z)w5-CqhO)mN`n=!WD|o(&njis zZfc=(3c}B|FKl*YdIDhu>=prewk$dhqiOsRC@=dDdm+S<2q_o~woIbwLsfOOMWCHG zQB6ntu+2X%^lK=!CNkHfMnn{2|GMgQHBU$q_qUDZ6suN~r)Rs^RodukZemSQ5@(Bc z!Kl!s|I1yJ5{;^`S$-M9HSxi3Vu;qty!S1Z>qq(z8(pUq2LpC3CYeJMFfSVG135)k z6H|42K?$$uonFbwcsNU(We%|88B#v0>UxAu@jnY8BGuZvCC$(gEj4d`c4%QWa!q-G z{RI1H<)=>23g@9F#4Y|>?2GhzON@ZC#4KH-tbHF`YqpX`-j>g}a>C(z0EN|R+MTnU zH(Tp!d>ifDIv=AJVqsFdoC4=y+~K2byzw*Qg58I@0?KPt{Z*%y%dk@TuNVXJ`Cz|w z@kApatH$C-=G||IAU9(;rLyNplj%Z}$drf(!Q(}#OpSi5HVQtVfRrySf@}v}D3C!F z#=Wx>Nm4+EA-J5LmgBKc6^6CYc#n<)JEZ`El z*On!)O&rkt^_U>!B#lPsh=vxZ?DP_Evv+Z5I6XStzSFb3?Ha2t*z}DA|!2WHT$azE}&oWzW=Vy_9>!TDFtV$Sy8W0mR*Czu{ADS zWHu{vf2jWw&1KQvOT^Mh-((In?<~N%Z@dOqHnL#f$lHb*0CU{R;q&NL)-^W{_%qSi?+oHm(U(?L2SsGV+j%x z=RnH(I4e}E(B|fQn+=z~l@DKE(>dFgKJ+ZM=M<$foy6$4G*M1H43jYI_r3Ne2KN&W z$2XnWa`%UP+g#comcD=`mBje~`mezv2$&R!Of@$>n>qHoDeQ0LI{(E501V2$gTPq+ z2}Fv2|MK($PF$Q1B8hLsVF)OcoVSE0M>kH>FhslAEv~WlGnSIUZgj>E)TWp4i#GvEqRh0N zm%;A{#>UuF6>j_Q@e*iOI=aW4myHB`#@WMaafN-sN2Nrv`-HKZ5z1M&+R zwhZQ|vbKYP*?4hs+2QFOtaoZ~ZvENU^t1swn(2M-|Fg3ogQ!D+jQAV0dy*=O1Ewj1 zI1o)#sf4TYKn%nU=h!h?i00kmKssJVfh;(U)p-i>*L{$2gs9H*%M;79s;UEp0AiL$ zXpTu|H0C0WKC>)wnDMK5ue9M<^uD3nmuL78EL{%DwAr(JLpY<8Wab{LuYxSQD>Vz_ zzae*g!Hwq=u5LmtC^Z`waA#Zz`4xX!Zy7A!JWed8y_;JUjF`GTcjuv(Zk6VD$$_?j z84!4@3;fyQlVOE8%VXgjK}Osg=f(ba8wvu=8upg6>Zzi*{OtR$Cmb5e_B1Aqa>I@pLgB zMCO7tQS^cB$g)xJz`&byH;;#{I`wL(KaXErps|<^1^b`v2Qp%Gw`NbG?c3i3LHWMb z8+BsZn_lCEvih#xUq?pD<=gc23E;ecixoc!>pYIkZ=G5&ZMaC<4;GYe@9PU{ZswpQCy$epn+SC0iwFJ7{ye*YrL)A+EHJ{t z!GmAtDVQ^+kpsL5Px{|uxBrgh|M^Wj<=e7%LH~Wtc*aqnVPLW|_?zSt81%P(Jv|&M z1qL-KS4Va3{bS(~fhK0Vu5C`Hu8IgW;IqEvRfzBcPqY`hj}vx3j4PYw^vun zM@2+{@v}^q`K4D*3}8KYA}m13{`cnnyYESZ-)s^W_TQ}UI_lZQMK%oTLsVT|9EYR5 zeJCEikPwyk>qcmmPi zc{LJE=R3>fbJ%`Ak_-%P$zTsD_Z(mliay3YgTRs0S(tu>1%Z*J&Mn_9V5)%3AtEwT$Z%AMZ3^F} zSi22!x>Pe}AnrEr>Iz=1)+|WU&B@t0ys4>asW%srE#{8v^7b|(I@)$%czC+Nm2yLe7cPBtm_j8_EHY^seu&2qVoG#-ovSD1U6+iOv+a&Elju-YKj zH!#?rvC0lVUB;k2_{*YqghP{appcGUoj!1nA|o9g9!i=WrNzXU-!d#MFB?7Gy-ZYX zQrB_X?7%AK%fuEcl@R9z0qobKwaayUsgODM^9EQUxU&&yw4vJdmLVvpQD04z*G5TS z%!-xE*v<{ihs0 zldcS1t6uvi&-pU@&=T((ga8_G_z&~_`&%01@>QANe8Qv*xIlTTp`^!ImQczy6>Ly3 zeAe=8!#J_UF6P9U%Y93*a8FyRw9C({Z1goUazaO^5UwO15Nc728;H4#Q||Da?hDf{ zcM`;BV?u|#oGtBXI=Ch?0whuM_qNz0k8cUo2k7VJG8ZYps-(b)=6DP|aD7eEI23TBAM6ig6 z)vVv8q!5QwOJ09e&drH+c!H)BIA0?-V8Eg6jA)wX_!?VTT7q03%}Zgx<%<_zPgm;u zd>6o`<5njiII)&rxeX4cF&@p_TkDu)!BvEWR00N^lVn9i(m(Qif>p4#wx%Q_3%xnz z&M@6$b-F;lIRBhnSz1iQ!orKvg%=w_8q>VtzU@}3L_1fiU!L*2-eqXJ2zZ>Nxq20t z*|OE#RrkfLk8s-qx2SC-SDh0%NpRn}Nr+A4Zcj>j#;OcTs_$2}g+e*_&87e;Cbw_n z?}M~dn$saLkg{`gCyy7;s~t{1?1P*Dbo_%t*wU|#&pe{!?>+*ABax%gF$wbSCK<>_(Di!DJhF@`6ztmn{fw~I$#RRO_eTU+Qj z)4o#$j9+uOp#+?}XyaKen{0A~fXI7(Oeza-`lq(^ioFtptb_b`_y0Ni{_&$-pvK7) zM*f$}Rzib#`Lh2GmO@>NU;$&j{fhiWh$~eHe3G!PG#4CG8og&q(_I4Nd-N?W^(Z6D z=Ty0gNe%ar9kSnf+f_taD0k6wSGI2a5~YKXDbJ4%a*5-F9?2iL&Kp@FxriLb(&;t; z`C1$mKPe@Yq;)8ft9FKNQPp@ zSOUyajZ+OaHsX^t@fIh`A%=^@bgE04&&ks>bjPDCmzD}|5|(iN$St)!qh!EclL+5L za*|NGWRri>A;N@f#6ti`nbWD2Gq-oZq3mP3V<4?EYPm5m&F0GEHa3Uhu&S>$zdn!g zo9FX-d~5r%z1mw^o-xYU5Es z#RxI*pcHLGI0oKNs@j-<^Be5{7Y+TxBwV1^q|KeHfuknNyE5~GPMM{S#%}9Wm9e^0 zVZyxX)k8BqCFfw{dneiAlKj3Wg9F^H?R83Nz5ADv>hrmm%+yH!O+Y0kbxuVd#`U3= z6#RP*qV{yJl3D$bU$v_0`~g)Du~vgMDGtjKQB>5D)5ejT7XtHr_q^%skNwo-B9;ek z%QZB7cKel+nxe<@nhM%ZFSVLNxz$#DfjF~|BjmZ520GG+y1Frve#LU6JjolUCInD( zcOFM`ty{ahdJKlGZ5AudB+E#jE5|dGbh^mN05&~O4o*&qg$gT1s4ri#>OiCn4GboK zzB{DBIX0DU+-1RYBv|;&x*C~5IvR8ej`4WLWwdy)KS0By>wUL@2P?+fehUs)JQp4Y zp5_BcUykz$_T=4Ebu_S@8Luh=54~#XmR0+>;qPSLPadsjoTH=fZ;RYQuT}`%9&FPW zYKjJeQBdWL0RHaoXDbSrPEd};3#G*8G}_Gci5D|QR$(7-8l3G>RJ*m^K+d(Z+n~X? ztdhWuDf1+j65$j!W_d%^Cyq_AofTBFkdluq<_q-A4rh-(Ez?uQ*kn${1(Cu$1GnP` z)9jQiEGuGVrJC^To~EZ^Nl8%57E4~8e1|fY+l6RoSVE~2qw$mlqG&E(5>J*|cz5aX z^GwGvHC%}3IM@N8)B1476zp5>`mPJT<=>~a|DEje?32D>V^{uB77^ra*IEY|1rT}W zu%*a6k0WX6@BxrCKNcM(SBR0mI5ae~!a>~$*s&xl^8rj6w3>w;ft<5$Pv^RwAPLg? zn$>#_IYHlSwrh~ECx*L`V-OT>PdKG^M}_B`5o^ZV7=6H9aooOdNfvFc0u*T@$qnCX z2!cpd@T_+vvNd~MmDopw-_@+ArLJ3W`GqGTVvVV?z;5}F(t{p+oZ64doC8-w*=!tG@oR+Gh9thWyCasS;c%faUvh!zzta)tVE>!=8?A z@yIc~W}_6KToWxwYxXos&Q&Chywfn8)+RNp2aVfS zzAw1hk<-hQV$r021q5RahKj&MwXtSkXlM+CV8u)!rdY$x@-bDX*AoZ-fQ#2^mup)Z zJEs1(^1)k+!B~U~$MsL-0YFsM&}`U@{bUBXf>D9&$sBRGZx)PrcP%AYke4n$)0Th2 zs)d28fkx(7-Q(NtjTwvgTwDelE(So2)<*bh4QZ`=-q?gb8boZz(n5WgW^>j{pr%%z z3F{3zHN0r}eN6+iUSI6?NH$fh${D(VcGr>t-B#a`DTqr!N!eqj{vxqGs#Mb+$}L^& z#PWGD0Z!k<7yQY6`?k2I{WQ6{8s2nTBeS{Q84tQlEe`T3ktfC*p7;xAeBKB_IxV|L z48*&hRXD#DHn>c90w~y@R1dbsKnPTSb)#ebN0RsFTN(JbysXanueexM7THpORR-;n zCR8sLZ5^b_W8>F40AE1nE%(|E<1U@x)J;Pe2gD{QYmd8s1zq(Kk_$Al3e44CM+ieE zU=>wV>|JcD+l6!umkiH$dM(y%8UrqL$B*i>Q=r!;`riOF_2_fD##io+gch*!a6e9& zJ>Rjh4>)YqP5GVQ61Cmu!*{g`RVtD{O6-vYVY!b<=o^urQEA68X zoxW}Ji$gmPYU}O6*n{RjOvME`7Heg!kJ)^$%^2SS1$ebho0q4B|3}zcMa8vt>B0#R zEJ*Mmf#43o-7UcicXxMpcM0z9A-Dy1cXuh=-SuDD`<(8t&wtTla8YnUL9JKjd~E)? zKyGroN$lqDk315?po$-z)&jBrEUHdGs0$8APuEE0aWAm?h>3mfypo+AmWv+_gGqBg zN@yFfpASpC)Vk%IUU$YK!mb${L}%6Y-r3ptaNRt1+cZ1xUfN{1YkGRxWb3xq;n3pZ z+QE1j=VPw4$#>X<(2&o5>qeMRn%4)he;b`a%g+4&e|`Huev@dO&vUr7$OklSz}75yyeSR0xMl4Kn(C#jO!~k&CJ&q^ zM6~Mlq|rGIa^=o}Ks^)nzz8TMYE35jc?VTe7_$1B4MH6!-_08n3t`UK=s4O6}Pi*|4-hNgP7KRnAy+8d6(Z7YHNCQwv#-G{7|LMc6Psz{B@P^2N;`E1ttrgnQ;I|`tUSbtZCRlr+lEr+ql_SG=lGC`;%+)^=meF zBoJ!*nZ3qDXS(nO9m8;yE4CoRWVxQ9&J07henAzGT9TgcrEl793?<;Q@~mrk&lGE+ zf07XxsbCN1tXt>$iuY+*%^>DAHpzExG}2GrovdqoJ$FZ2Zl-SY!E-dTjVcvE+mX*e`1i;c75GOgEh3Pe4+pbCB9z%-hjE}~hH7YNy!WvrDVSXG znqKT61v@@IQSi2H6}UY&whl-usClTFw2x|vY%@4#Ln3JsbVk|DF1I_kx3*}NL7?&Y zi2QjfsWPqMq&srf0cnZhO11e!crP!2&8{mV(fB@j6PtZnYBc)oaK0jMV3TDet9^`c z@}09BP9WsNsu++XZo(r|DLc2>n?}LFpt?ft76UZ5L|O4;j+tm0CaKB5`th$VsR>S4&g;XeyXV1BPG!js4AsRXKoBpzo9$9u26r?t#%%#kv!R)0Oc&12J3 zo7JvQNax$<7F;bN4^}D~nU-3`YCd_!zbkG?a+T*XeV!4b@h#DbDs{v>yS%-gly^Jy zf;;&AFWNIw*2Se5;p_pB<#ke0xQ_WbF9gF(B|Z5r zO)fhmy=$!kgOpQDvXt?Dr3!h_j_TrqY`S}l6-W}$fRVX+Xpb%u5ds1HV82wO-+`q6j-vuEMU(L zz=Tt&IA9EHYUx5yEV~zPioHv22np3fay7JZrx*6XDQ=>h8a5p-zqas(!pt0YN}- zjYDR8sM4OY0@O8WKhQGAPuy)CSUF=8PnY%NjOf4?V4r5@tUc(6oW5DJ;qHt!ufVp| z%6Pl{>EVx@m9{f+AYKbS?vWz#w2y%Bak3{AQh2gAeBN}q-k+!}^4s2A8Lr$`U!5Vb z>dwqOa72nrOBVpO#^lXWL)|D%VPm`1s+ydNis9weRRV%(#M40E8V!UF@1s+;y*PWpgOi)+D!gng0TV76i_72h13@)}L z5Z2wz4IwbV{s)xcrTfJ6w9v{tHg?M8;LGkDhlvcL>Wxh>k&a|y_c{&JAHRxnzhY~*Ay@*Bzv^FD>b zsuMUbcr~?8B87*+9lr8NS=E{iR_t}=^U3!-zgj*%wDt=Anl7SW-Z|+^>3YBOP_9TV z$rx+MkO*$uykkx(S+7atM%uo^p0b;hFpt!6KIInN8xuKT8I(7Qw(+^6rlnmqvc*B} zTSHjbNaov{$*By0R|5-iv{b z^DUc|WQ>%wbXgj(r>q$R17{Rc2j+1bh)mZuqwz3#(;Sq#(EfwX=wibeX8NlE8h~K< zUmI!TPk*m53c-I)#xb%E^4<1tHZ&}-rMmL-YGD4W)SH9G*F4&KDQQri@}8Gf)ry>e z*IvPCgK1>bwMP47H8_`3`^)&yV4<~_HWlu3>DBB-(5%_yuz%l2At}d{#`{_V-fx4q z@zSopadE6-i-0`02Q-I`P0Bbs$Ia(RiXlS@ql}4CQ!bq4#^@iBBF$_QQPX0Qm+({! zb_Vou(^|9{YHK+{@@8u-ZOMJV6qiGJ(33xUzNukAst|m;P?Ob0^T7Z>< z15n7qtGx=UL2hN%9;2Kn&SFHI{^&E%@uaWNKX6ipaz`(|;DS6XOlFbW%iS5d#2PB= zkYW9E^l~+G9A8Gurn%IxjOFLEy_|d zFID}k3E3D&C{K%xrNfoDVlt(zG|>T?&FUQ1a2u=DChAnc%>^Kc9du&i%Qe8jT2dVL z%9`^}hT_E5L$Ou7>%$n-H~|CCjg=OQnBw2Rk1YE}a%eWy#`orn-+bYU`ks{{%j(W2 zvvi>O@G=1XHsnskcv-kjyMf$FhB+LBa6lHWm;nDA)#iE?6-$Gu4EPj`(|~}IA77TP zSyCqEG>1*!z*1zcO9ATG&TbAXM+z)6U%x32?LA-eHBrLBe!7MW2n+xU>S)*k(nz4W zBefK(!v9}ZG=&*NJ@zEV4NbV22P{ljn8yKb5(xa(zfVq?{ksh%H&s#>n76<{Z#eel zkgcHK9Z50vGNBb>1@xoPuFb0l;)A1$Id~pDD}Daw;Cg=&ulu#3F8c3U;5dmp9ez1) znSC)0grpgBvD_yqC|R9P@_LM4fDF)bah_3tM`p})8FQ>yYZM`%dHYe3GCh>~ZySvE zqS=foM1wy8tsZH=sAGA59xVB%rV~4BZm3Av93L1$}^`Z!$-yh?dpZ8sb=y|I5r>`w}s9meSZ7>`ab$!aLQU zdD|JBj-fy?82Xfz1%nx~fCfzBmMl{f2_bX8#ft-VU}5)u)jMh)T#`$-)EGHy4n>0* zDjF~CH}W0whiSZo+c@-KWz?=QDgt)l3U-$-a3}pBw_{^oZZDAhcZDYQP3=D2yG#ba zK}z!Sv0C%;E0+tKm#RtCz)GX6Ki@8E)dWqft!YhB?)wK`*viTMQoQgX!l6(e17$*b zpN?iUE4jE$!o$ePBW5B1WFQ6aYa30Eeg(mCU>Ej#pmEeQb*q1{4{AR<--|2zD^Jei zs3U*(+Saq4hKKn4(7TR^;r|MBQNT30FLusm;Jn7h1x_+=0t1GuOP)ABw7qfdy~@u~ zB4{+3GJ*~cG-cY_EiIS8@;la=)Gp$^r2C-E3d`rp%BBy9xcV>0ndXeJWGIvWv@WT4 zMtPc>oW0)6r6xXJ$^c~E3y;jY)7xKE;ICGM}hpTPZ} zB(D$JU&b5eU&h;>%-1{8Gfa^BLnz(iH_Mkv9tWCB2+ej}p*5{F1=?&z9zGqKDp_ur zGMkR+ZsO>39pR_UnQA4-#x`O=&d)%P-_laS=lMJkP6iD!M?Nd83w9o~-+DtdSq#Ou zS{KTX7SYT;W2HeeC7o(F6x(#91aH?#)LN`#hBaap84S+Ddl{Bku8Jy<=i`hn)OO|n z{uTf!wCt4ES=O8yQL0(-h)Y}!V6M}DW(m2u1D5K{X{wKJ*f&Nz(c^v+ z0aBamZfq{qhTkX6_ z5)Ui|bgJ{&THelZL)dFA%884{*wtFyQL%=iXLRb((WDs6M`8yN5@2xMJ1X@#o#OPk zZhS~3v*1}N^GKlw=7RzWwxfF6IzeMNn0wEy9mB^4^4o!2sJa26!RhAPqxl%KIRrGt zscEMCyIuSrl@6e>t&0Vwk7vH93;Iy*Q4=45KYONJGxG7;)S6LPPcMunJv5?)(47np zmI6dWWj>J|RBSBGp50pX#Wkqs9VXGUm$5b8F<1yLiC%0Pz*FK4d^Ox}%*fqYWI5;2 zkk9e@qfW%aOnjC#njA9ft$2!=$;)i`(M^avav#Pr#6s>!`2OxLEmPNePtR7vZr2^A zP&fnQy-(pwxw%$DMA&U&iy>L9^Aw%wRc&*_Gi$B=p}l5NP?#Uj_mVEl_0PTl*Cf{Z z#|%gt(V)-3F-R@_Ps6x3hn+k0; zU!$!8y}=ZD>E0egz#jZt+O^*&7?VKATu4O&$s*pFW>qeZegM{D+PfLrfSajQqh+Wm zcw(JSL=)}iD%iNl>@F7a+@id;rc0KFqx>OW<>9G%SURcgSyeSY&BVt{*+42g*`7YK zBE{$?Cs>1X6F}&G`~uaKp!4uEK1E$gPRXf$YUX%W4AuQv{*nkqWlX07kK9%0ets$? zJ~P3z-b=usTGr^{@-_DegSvh)gA0ym^)FjQ%t%_=6@TYZ{Ih*5`_4?=%WFb zh+tQD1yoh+8QiAr;MCiEr!s*eMr8MjF-)Y&99-OogAc5J>aA|US{RKHh>43fs&Kc! zz1kW*gm<4x05}%}J~MPtK)$Sp3)p4lwUrcN38P6+Y{G2 z5z2<0UtX-|7HO!|rsJ#?r{G|B@mwEqy&M{-E%_HFrW)Z)R9Ww}WHh7*?CD_ii!a`D zulHM22W{husmqSDVK%kvgk__>iJicCB}l7|12v)Xb`EB8*d9<-Pd%KQ#96z#t4RJV z|GwG^Mnt1HgU=iK!t3w@<`ZTSi<>KUPn?@M)nRqejrw#*=d@%YgXb=8e7r^TUdkrlDY6@Z=MBUT?>8^}P(fiRK!^YZKOgp|88^O(gX2+qNz;6;S0CWy&{udZ zSDsr3;joutSgn^pZ?E$RmaJ0`Unl;kLbX2L*4QCGf7oq*yZ0;Ch^Q{@XM24h3RFZ) zT*l*Z@Y@|%(^Ga=fcAa<^m*wSAJJTwor8#}8KL#noV~zc;S`6CPd~5B5hAdTFY^?6 zTi&YLKGIvGnzuQ(wDW4G-T#8;DB@d_t~HKAL(Ph!ni6Mg<$=7O^F|ajVAmlHb~1y= zQEd3AQt^)&h|IK$1!$>9G z-p3}J;vgxrs@?Na>hk%>`u8(iTmz<8M!$+yj%;$*d*jjJ<>~N3zSpL2*#_h9-c+9b zt4ZR_Ikfp6EiS2vtY4#UYWG$cVs;0ZUPR&O;nQFBR*5)qGv+&zhmYabrQE~0UpW^H z{$ZGsw}SJF`w`=~MQr|Hdy8dgQrMmhR;&5<%I3Mr>tqWoblls4u|>&kv>IQ{d92 z^K+m{b~sLcX&;xn(;XJRpE(i<_i9jM;RF7e(>$l#2Na}YMC`_OzE^fa_pFJiPE$bb z$$Gnk_L|ULK$$-` zJ&YqmL>DpBw<_C1ywRwGPWg!Wy9j^=C!=X&{z@=TmJ~&17i$*zXD-^oAyM#@xvWw3 zhTqrnQkbM#hgHmqym(IlLqRK|MeOnSduDHY0ovDztB%~-(P*1&{eGjr*nR=^qW$nv zooF;~E`OK`8WQ^vwp?^cB=VP~3_+nttwdl@jmcv5s1cuctz|~S92#;m8ltf1TE0}; z>i`q?nflROqkwwmlI_ZYK_&Ye~yfd z6mr-rsYB_Es%U#o@w!W-uvusHwq3&mS_?sdbHPv?as6G*iZ!_)7%)F}CM)8DLEx?J z2C}G#_$4tziPYumR+l!J3?7HmIyf-B zUUa4puceJi1*0(A)?OH||30R9ba}k0XQrKX{101gh!rFp@|m9qFBEOlWfT8?f2=(2HlJ?@b^kZ3j}9s;xw6Mq=2 zF;}3(b;pr!P9?3|?qXFQA`Y%hW~Xk5EF}yUgE(TtMg6;;w7NWT__lwjr{*BRvIlk? zdFtBS!PGr^@MZi%?7 zKW?+H7=5)g2N7-&vU+OF;Tx;ZpBRB-)t z;IK8WUVEXvOi8@>Ou$%Y@SJx2_0H}+QL~`?LA0dzOXnYES3KCn*K-z?$KI@@G+Fe9 z?L(E-QYmNpNRia8vu2}PkIQaSGlj+ z`WWqf!13A6Lv6)mg|3yRg>S%ZXagW0%+610;lmU_(gaWo$ZX9q=a_p}752KbP1eUH zf|eC=&#mvTkkxEEW{O)tHMh)7^460CV$xd8!hb2YB-=JO4P$s1NiwwoxU$LWrSB1l zRrd;}yu|Me>b{b zy>GH3UCNItgF7)jDOIv80{S9LORK8PrSnPv0sD(qkZW1jvXd8d$bx-p?qpM#pzuDpfC zYZkJ-K8A2+Yu9^sX)R^GEugr_bkX}`@oDU6hi7ZLiT1+{lWur1AzzoT7(O-$($IF5 z*Xx6`o;B{S9}Do=W)a$NA>+&>0O}N?u)h>GAkyf0QeF@+_ZiQG@p^GAJ$^58QYJ=w zSY;dq5aETlCuwqkZ0sURe#v^}9!5lDRUy_%^TI;CPjM>K@`R^JzaQntS8PKR9~OPj zI<^=<__1v~u4jx1e}4r!0SiAp6foDYVijaIQhT#yaJzo1`Wy_jt#lfH)^bf|H3-~X zX;KX@expifGzq)k;VZ||c{>WT148lp<{!3jy#nl?Fy-P3CGVU0hqBtUJg#E52`PoP zRl{pdrb3}_GH8CSf+F&)joN9aLXyefMMLg3qN|;5+gi)iRdTXMJKuK6s_;?vbQAzMkXfky@GhwI%A==vAUY zOo44U?yGWDiyo)VNBt;9!?h{XO{+O#oINp7bkfWzKyp}Ei+R@h&f;8Nv8IZCBl0cl zdCRutS8{?i=s(1DHeVlll*AVp)kjcJK24xf5U;Lu^B#d5@qZaA`ovMc2p(*t^O=sb z!PKs*NGZG9@+QOxPveOLQAR-y5OZ6OT_x@qdAJrIre+|?_hTW~F(5ptCyZMo$!Taf zTVq%?8Q}fnUu{49Pvr;&6~NT(#{>gx8`4m^0KdE2YdD{$_(4iyBqF>7&>Ol+9WVrN zvYL?D^pdU?MbHcfa|bBFqe450Rix(;u+m~#a%|Flap=&3dv5WRyyWQgJY)9^aLVNP zHOv4KqRb3#d{k z))jq7UWv|B8(?58P7k!B_(H=O6tLuEe=K=gPUV*$tMXTX2jh}V*4wu7+pHIuI@emE zJN-DbKt+4=%I4BlM1^5fyKZ z9L?+RH>}@=*@+STrKKX`-BVn?Rw~jMS#bjzwBpZ~;!`!F)N<~&Ljwc3z}u;4wJ78o z{~o_%xsvS~O>-WIHAXFnYCP)j&7kDw7DiV^?c5&~VXpr)877&rBP)(AeWV%)+T25X z2$z_EhlxMC?KOO@AE{x7g`tW^&fhC~oj2gP!t@rNWUENtJ|KO({VZXrev7VX)h9fe zE*p(x z!^xuX-*@6%P{(4>!DC*|j26V$TGpy{dd=WFZJu#|z;in^W#gFmqvb1* zdw|p?@`Ah0h7F=;uoOV#^qf|%2>YSRLC^Qtw7xF9Hgv^haub+AbYcQ{Sj7et#+|b)7I(Vs+FAQ%Fiv&kb>MB6tId*iT8!huP zaoBWJt>AJqM*FDq0CEj`@$@*D?L?#GreFtQ8&`tY8*ycO))1{oS6I}=(p2Su)wGoh zfZ?BThmf$RBD=b%hCe4`Wtn6qB$V|M;X2mXHV}8Fch;1kXcQVi883DR)iKnG7SAEr zm)#V_mm(rdgxT=B3)WRDsh98G$}W$|g88#}PfDCl%yOFzhgs!4JpP6q%>_W#!|T<) z92P8QH(M_ES{GqCFwQjf8CferW zrwk#D70-kCD_T2^hpZ+0;5^YQwJkk!az$$W=Aqfy0LUe@Une^~&`lP_=9KJIc*XKX zI8`xasPWD%v2$8wa4_(j*qa;vNmbuN@Uw}A;vUriaQo@6OsyCuel#>1M?QaRl9w5Q+S%2z(g&ntT)*_Hsp zBVfAf^NUa9>F9^LdA!#agEJ!Wkmz<0405JYEZIE&!C5>_t*oGh1w@%fqtp-1ox{^p zX<*1SmM6!dqOgO^0LqE8J1h*mE95KA*y^gkmiw*!0ax~g&)63E-Bi=)m1XMB=mvVJ zv}gPTYV+si=aj2plby#!*1CD6%XlO~i`871+0y9AxJiyEQ7Rxp-|Ms z8*l0RvJ)0P`sN!|lO++>{XqE-6ZinmJUii^aS=LC2 zt;i;O{ibC$UumZ!tyY*ax%JAW3b#8q1|L{PF+y8{)z^r%+Bqxt{OuWdulF4JnqoG7 zC{i2BzEB;^lq-eNgj6cCf2;Lk!Z!XE-2uRQWyF=U3FH_k$ z_-?%*TCawkn^rLsZ8}n}+*~JI%+R&ee^YDrFZ&c1S_^oXuJAewb|#}eTI zEVCIl7ZsjN*6(poKrJVT`D*f{JO%7gHUDm4Xxv05k2lrm$738E9ATkgC#;v6U^q-T z8=V-RtgL7}ki_uMNb=b=sK2$~;>QpTaK)Qc3Zo=v*_x1aRdDo&k8`ErmSHtwkhd1u z0sjJFy?dWH{%SDpk{hdwyXPu(yLt7j-zECxb@?XJ5*KT7`3Kvax&s=1hX=db0^Q$k zcoZSV#ir9uGbQr&4CK6)-au*DG~QIK0gJ*~Yy^%D4!$`1!4XS-MBuHlGs7VbXgC2` zI~w)lP7)$n-%M_YIMjH(A^+jHwR#|A`J;t-Yp6z}oj+h!uy(zW3(d6g!IjQg9UITS8Mo=vd3V#Jz5N1K{syDg_E^FeftXfd`Cj{<12z}H zT~b~n(ApH8i&XZMEV;j@-JV@+z9)G(LJEDGB21g+#6p=`v+wP|L^ee-o+x}pmQk$U zpQF&GL0UQ+7kf7OD0OvU7w>uzHH)N?Eofr%?XNgWZyA(N_YEA&8YqgmB)3?Nu|Jan z8K0VHpzCUA(z9VYQ|U!5n@O7ye=&Kz2Q0$nzm^p)YU3Fo01X?R_6`ilI(OD&^*#bX zlSPg}V^j^|G;MSMsu(Q0ySEB^hwgL={Pk+o5PFiuyFqWJd__EE^`g#O z4hT1-mNORv>G`ffM$Nn0uPIFE;)Y%$Kg_F7TcMB4ZA#zXeCmaHi@rHN%CptZ)Zqwf z$>tYCFWHV}oE*rLR>;u68q%;8VRNGRsbRlWlWA5FFG`4g<6D#5yv@jOU4>Rl#j4Zz z=8|jAvqqvaVIW(0zwt|STyqh4n`~_TuAA^7ORj&Zju{rF{`Qdnbc()j7Myzxv;A%` z(k{Em-23uh6cF_+Sz`yV@DMl-i%Rxo7mM` z!fofh2c#>ITDq(|X)m+JylfZT=d%%P6sm6DKOO&#Ve@RcR0o_k&!{ujKmrzTRu@H} z%~$XE49n!q_QmYFM4)dk(%uqYaxbAFHU1nKU8iryneTx~>;72n5!Nopd-=qPPKwIsJ=v?vKTOY+wLq~Hy}laB-(?1=Y3I*4sq}T?y&7( ztpl^CsW$S#>G6AwG8bJQ%|*{yngD$7Bf&CqRy4zTB;o0)qPdXTy6>iFNa?ZCpHplP zs50Wq-{$l2Z{XliLjWj#LpsxfBb`&3DU<5Ds}Dp$$)tE4{r|+A^2?&wL=j5Lj<#j@ z2ijz3!{vEnLb$l&Ah_tA^-JKxv^Bu)46Y#$2VIB<`k^DS+iMXo-Q~a1a#2ke0^M3s zp7L+tgTYxqv5ku$=M<~W2A?#^b7*_VC02|+0p=q)$;+8g>nsoX5TrT0C6S{j_VKR@ zJkqi~vqiO6_o1Pb3T-Vh$XPC#=@zed%wTXQ8Ik|dy!Eto`-!a!wwaLaDG8qzMC}^- zeY_>FC0DzErHhIjV7OfqqtyqNuSMl>ke_%mgMK6GsJ-RNA0)?vDZ%YDG$fmxrP9E> zqML95i;&96xtWsgz+!419(l$-4QjPppkw@toJ@)LOs%NZg=N=UMv@QwHFnio^W`(w zJa7W^;=wnW?t|U>5JrpBKR>COQf+*a%DmW2?l{_2YDL>6lB~#BH5xGg4Sw{??0?VO z>kfcHWI-0V8GFT*2?6uNBY7Q8=^V5D*c$@4e_SkO{e2vxdet z-)Fjebtf};lA_3ROh&TA1Q!>FZY<8dlO>%UF9wJYt8%M7s&S{@u$}EaMb)(g>dDyt z_gCw-_gb0Uu9csR>~Mi_&AOkF9~k&vYSltk;%%?XGc)n4=TT#kaB49*9T{IOSs5?M zR+}7^nZal2F)?hyYM~~*EDsM+#n+~%zg{$)u2q@moppf_W|`0NmIQg$(Y7=7hRAWy z&mLd6n*yaXdH9{Tti-!U@h&fJ^L|pzmZ%pIqHEQa|L~L^(Y~bAY`0|qwG|+EoxN`vgk?fUBczIO#?jE=yK83YIaMd7$ljD`%a9bxsOGi z{>`DRPjBo3&js@u3pG!mCY#O{!i>hpu_0>h9K4(T=Ew$!}tCc$BtRjS1P z?sf(sV-$-Oe9J{drgQkBrlM%wDNm)jOZufGC1ZzP6;cnia9J$*%a$9mgQT1;3yuD7 z7QmtW4UQtE@}r!M=Hb)Kg?+7g+IG-+(+&^Yye+>DY3ltw#V;kE2tO(qxkbCz_{>CU z02!QIUAC>&Khj!ieL;59>13g)h~CRb0w!h`q&h~l=c`rgkQS)i7o0yTrco%`VTJ3E z=tS$f=`30ts&J_?OUFxltvgQMOT3rO4!f5qnr}b=73|K+mB^|zS^V_6Y9r5R0!#Gg zz3g{jc$ZowD6WQ5WPMw2QNv+@fAPK@gWwJ;{n$zIlLP7z5nE6dwq~3z>wiGfg z=#)2!^%Iz0`6{-~&XlL<&JFw!zOZz@BLhTPTQ^3!tN>&xcX= z&a1ZIa$LPNl~*C=oV$L!tUyg`qOVufjl-3ZuM0HXk4HrTpO9!i?TwXjI?p%0|KmVU z_gwb+a6;QaD~IKHsqixxdFd64NSjmilNnIOs|`oPYO-IKMx+P!_6+@O1 zxnXSMhtFAhxdO?w2zyQ3!MJ6k<6c1{6YvNaZ0k=r`z27XpXT8C`Y7oG_w7Ad2UPMqG#v=w8xj%UUyicUjFWZgU8K`pX8v~ApHqlXZ1ls1FC*HT(1c~?D_1j`dr`U`LY5Po=!*hX2n~IzeNb3`SooDy+ zf*1SokX(NLSG$*e7yb)JL3LVM=~##Y)iQ1!Kh7Inl$pPM*c!hq9$v!CnXP(6_^fC{ zZ6b&LoWE9Z)c0F>m`nJlIJLT=VF`BkTO1^O=GwIEClUrl+}`Q0`d#l|=CfPR2tBLD z0r<~mJH)dcZUAdP8(|eqz5aJWD6}0Qveiv~sTf1!a3x8KCGLTA;6*iPO2a1XNOY{mOQun zInS$l7PNi4M-w@|9?^hNXc7)L;wIcWgE&vS*O~S+Mdb-GpGBeJIxyR_)?;gYhuX!dwMea00?%?&>^JZhKj!%+9rqk zxaj!uqa{0Q&MGz3J}GKnnf!q9BTVe|tqE2UJ)TOV7Zsf*PtHjXX*iS$yADc>5Er{3 z`%l^8K;!WTjO+f?5@D*EgJ~t*%>Yu-W@b}0#CF6Z{o2vgp~lVV*~z?J)#+JXPw{xl zFx|X1|2Ua69EnZ==SqWDDvuj|zQ#pBhzq7G{b0uw2`+I-Saq z>iJ?GL?NqoF_(QR`Eqsw)c;8^sxjXD+fnCXYWRYW(!(5~`gy0%n2oh4PV($EWPiYtn05{vY1lj&30EXz{*! znY(GnjSE5i#FW1rG88imQa*~bz~^l`$X)Nre(D29rQ!e-&ae4$h_Gy;g3lM9a=OV| z&%XzpvLqPMroDUnt-5W5n(4_mVLML<`kj;$%+QW_mdA8BZ_Y?D#q)9pW~#I}Q1WNh z$9QSC2#)HCgK~ANjIV#ShxLbvteP3sJ>eKO6e=~9+J1P=@IrVZPkG(C8t&%V*v7dN z%AzFpehGmz3WS^uw9)x}nJ0Na`+daH^npo5%?3EU{)7`CXu;>XzigX;eUj41`)2UJ zW*M!Y#3j2f@e=!fX)g5T9y%!66j47^woUn;XVtg&;ZP%d)Ya_VDb6}ze#Z3;5*t@- zR*Ud1fV-wnX*K6T(&QyhqLgPg*19g8U)pf?3qVKeG1h$3;vf%rZ_YCRt-2d)AX(bB zn8|e5E*+^U_aIOED7CD6G(z_cK;|YI1STwdJ3y%UFoRUd2ak;04|Q;UHD68Su*te*SEnb}LRq z2A33HMPA!YU>wt>3O%Vg&r@?_qfY?bp>VrrOlRVf3nOok`D345VMNued1bsZE1A@ttf|4ghpt{s!US7C78lvCmbkQ)4)afyS>nA+_x?^P~#_1LntF2<+J(gYkrQ zGx%A=a5w`4oTJ`RR+6F01H)V#Pcc(v>NyM5JJ|~H(c37Qxpv<}5fYQs9=Lp&gQ9i< zw-RDWF}5aJ-9a0GALfvb?8?{oJ%xPX&oP&{+&GIRhj$=FBx6Y=2OA+dbR=}TbeLE>N)n%80!(F%QV6|d!g_?>CWH4>N(dxJlP^>rkz z;XS=J)!2MK3(0Ma%TE*jmrMlsChBxA{MaP9W+zudvmv@90zR)ow8lkTSAM?Balm9y zX)SfOK_lOjKd9VzpprCAPI1q1kvV^}eUMB)`OT2mgwic#CVKn-Ec6gYJmhfY} zb0icc-}RVhi5O4DTeS*&_g>9fEBP$YwHgCi#Po|NS=-gmN@BveaEYaW^fj^LSKPI+Ixce zXLR2O3G*c_QJ(&L)Af*j;-&UQ=8RaC&UN~aavz{sH;Yn!Sm<9TZINgdL%Kx zT;KgB2-uspX&k{mf~~aa;FOL!#Lx!6=ZQqsnydGka=Vm8yl zYK8VFbX_pEkgZGUe^$gw|VcH(f z`n87hL+7X}3L;*60;V?IC}pn9XF6YmM{i3H7=m5pT(r-(a%Ka#Z`nH>{Pw95rhz89 zF4J5TN$LznZ1Y)5WXIRNv3Xi#%^Y@Pvb2IS3>A*^k{^kjye)3~3u~|?%jpSk`?m+L zaRywpY5n#&=l{%m%Xht3zTnI@&wr^SoqUb5#GpZbFn7nK;VXa6;TJ?F2Z!t_Mp#}A zp@l_A;22AmzGY}QG7!NMBVn)EZzHi99it#kP`15Ue*LAOocnbJu`#$ji%JjThVc>M zI4o$hMqC_}#Vxelw`vLBO%Yjlvrwb_@%nUnsZh7s`Ac`wi`Zc8DqpQ? zTDfLxN3yTi=Efk0LWooug8lzqa{Qr%#vp!jm$qY-=FX`PwV zc+{F#xC9hFXqnWY)%{4Q{(nlkWW5a&@Vg9S@m6av8Gfcrt_dl6T~8)3BG z3D^fH`t7lV&zi(Z1Q}!RJ!dUe;E`GWFeBeNn5Y!O|3aq<_3WDP9(=!}iWG|cJ>Zzmw5p&s@ zxA}HDKU$(f`@p4p+iiV}986E(U`$OGsrIsD-;I!`)TSOPW=pj%2Iu)%N)>B?m6b4$ zv#Hg=ApufBC9yf!EI=Qt>Jr6jz^_#{xc}#S3^!*U5u?!*=n)z;(bZKMSLr?Q%Tf)R#>k3={mJQgLFP(}-1-ghe%6__RB@b?NrZL1*OS1nT2*vH zLP3eHwA(?-OZxGoRVewD;;@GqEIFl7t<27Ee~60}9I(>g7y0B^lm|QOc%%e{?TaG; zJkcmOWB}q2ZVBGR(-IMB?RtI)%7W>e6RuD+*Q)62Q~Q;7hboUWk7G*w)yWt#t+*wsNx#g$Uj=I_);naZ9P7_2ooL0JWRVB25$}o4u=V zyWi~%r^xULPDitRrds7YDGIMc`~AI}cClq*yli(|Px_qkpuMh8 zI!~jYKLgsF(TGt+r}o~e^s#InuQ66pc^`|JzpIE=)HFL2SL*sJ?U%HV-N&!90T(6p z3!*~1k~=!BhSu|!3XEnPeF5mFWheT6AagW=C9RBFxe3}#d3q@WkEijYx-+)3T3P1r zAFXs0YTv8Ma_VWhFv0f+Jcm|4ILA@`7PvjMQ9Tfrya&<8s#im$o|o5zWWY+Nv+r|* zpuwxmOdxja6J!5Ln?}!SnNs{9Cz|OG^ebT!f`cIM@0b|K95e8SYFFtTN8gNQJVq~D zTbcHWkn&|+Wd>5Zkr%6Je$0yq-km{xuG7h+<1`3{@NYldk?;lbaAk|y`} zO*GiuKM*#Y*dcSwa`z+(;tf3eE;4M;vTRLg?wFhs-w^se1ea&yL<_8xy;=N41uY*CXP2o#{Sth^y37R%kWYkvN zHzQi~#;6`LeIsrQt~VXcW(cy@f5$w8fCL@3@);aFa1x-6g}41Ba;&}z-S~OQQf$AW z9%ydu0)l6o?Q}CR|8bfE(WWo8pNMcEF45-u1bYLw$U|SycHNKpi-T+KCNDWvPsxxM z*>P*Cv-DkBC*-K+BpCwKcOJ})6DAIDp5S*Uq@j}e>iZJ1g`VvWdT4Q+3+;?=pB`15b`MEh zJiQmqlr%WTK$1aE`A}LkfTCwIhFJRAna{v9JAITp^33sS+!%E_-O7p7b&~z5k&ck* z5oiJ#PRBC7t+co*ujmsHH=i#k7CI2xdMiT_)Mly@J)dgpW8rAxxC$*cdnXn~a(_Es zaz*d#ymLg3yxbf2UFo70A4~UY@)`u7yx*#XGcS!z9;<-uq-R^r{ycHb;a5CsT*!HK z8Jb+2*={>?^Mju7TIDNKWFTY;sl&e0Jwkc!bj{2>23L;E?mNS=$nH)K*LAP za60~9_`yG5!U)R5E3ILf-YJ&(UEltplDCSb&sc4%$^DzkF=BS;urtpd-j~l0(+@pz69={55pLqn1;A3A1v~_)b z?-!rh<`E@eKg#uOfSL&1qby8@U*CEKxpSwvp||ExWp#;yGa%e1-TCWjT2& zNc7DP?id+`8OiXnuj(w-K@pRjqC|OYjKyU1YuE1jOSPxfzL+O0*ne;&GK86x*w}h? z7k5WtY@X^bTUHi6+dG&;bhNMlbdy1`xdfFKHy@P}4PCBL z%M?#PdAFe3t8~zUfsL3R<_%A(R>L;pQhaC4>rwpe;bt#x)4bX4NQ#3WF5^lPYKHUa z3ZmTPAu;wX5C@@tY8}qDBc34c(}VMPuuv7w?)B{F<>;4)Cc(~s$6!ZMEREh6kW2en zB8ACV_-dDB`w8zR=LrovAvj2MKjIscVXnKbo2r6f1sw38uid<#lpds#$Ltl^gE(zA zmU5%Ti)3q(MT*#J z(ptr13>Hl*-!;d?k>6?6)Y42)PpvU1pqsG?G|{@azOD|7&dq$spoR>GaLn*$=dRw3 zEpiW-s5=O5mFinI*sqYjULu}MY5TmtKY5i+!7Il|iZ_8(Eei$tcx>nJcZeCjP`kbL zQtf)qaPGb;y5|q^YYuMba_~BhT=Qcw{b}qH9-&IYZuRXxEcjjM#&-`Q(#K~Yp%R%& znJzR}1cOWa(Ibt+zD)XEQIwU?7Qi7Ogb5~oI`BkC`i?hMaKj(df$Y%t@TzL}W41~E zw+*VzNuO$Z<2ijfOa{ySYtDg1;koG*J@X_YJ}-QCt!uj~21`e8FXHObn~x;6=QfkD zaFE{NN{$^ziLna%&j2S1#a}ZN3fZ+*A|>)g0-_-6`$q$zPLBc>Z;Hv_dB2@JTdr+0 zRJE6<8gxt~l2ff-G4@J}hwo6oUOyKhFN|}({boW^&=%3Z__RI0?skOlArouGk;^7RmZ%hE@QmomY>Jel-o|z}s?2F@nrPoFA z7H!LS82|qf_LWg}F4?vrxVyV+aDq$l;0^(TySoIp;1)bsaQEQu7TkinyZh#SobG@blL)j@0$j-W`( zI{l5p{mGwAo6+sa;(5_S<_EuEp{Yjk{8}L6p_%n>$6ouT8nYc%3Q_< zr>-|8G!5}XG3cakd2t8*iqa4BzyA>d;v|86ZTDDhklv55O74-2ts7+w% zJ^p?*ew0vT53Ablc>koWW0pZaUL zIv<+k^(2dsD7D7#S@6 z)LV1Kp_13#?T==aQwLm+3BpM(ql!-&5Q2egierJNMy} zyhhbyHFTS94(6uh+~(eM>`txIks=2HAw5mlRjTmQxOcvTtwQu1Hd5i@)g{G`9m z|^|?gK-(gy2C|vRZDCG@1AnnG&0;xczp!Tl`#&`5!Wn z9rwu)3&p^bK#gA!b?J!?N*nF4kc<*pxPe(m?nNer@vRMfyB*es=OjaC9x+RuqF;l+ zsXFX9{()To3&ySt_q{+EE?*z?L;*m`dnoGIVRFMSti_y3a0{fSC*S zCyX!R)Xp{pkZa9e3!*{_u}%X+tuS_u=L>!4mQVY1r(ZCPJZ-HP94=7Q2v=xbZ|&s6 zC(@w?j4fG>&Mr#M0L|EfFS|S@s_YNLS}3K7tqD9J3ALO^3tLB&72k-4O-=ls#z~4l z@FulINm~5bsT^fJ&1$pHOD%IYUa_hO_7`qWY|>3%WJzbggnOT<1yM04HNiM11bM*7 z{Us-iAwk(hS>yeTHNXG>VuC_N>V?w?n+XoKhNKr^WAtPikQNl_crDfJaxs2}Q&Qi* z*osV#n3xv7dq^M1;S}pHRLaXeqUZ+%xqLg{c0E733DwaJC)w8D@wPm)e6th!Y)U7u z^J6eMIU7G&ilY4fYKq6CLK#oG;xiZ|k)uc&4AP*3 zhJyM5Pza!8>sIIys48>Fssh3L66*|lUD9cWH1KD(N7E5&Et8VCQxB+tlx9o~h|OwV z6dy2Pb0YF?jt?!fRrhYQ=DKFfUJlZvRL;m@9Gk~!Y71P-s8}h1`gEu=XB<3pLk33>G5}t zVbV-U=rn*=0p+Euh`*AGhJ=D07M~T>WWP6#o5sDVp(STIm%T!LjiVeZH2rKnICblH zbBsUWuYzY8K0d7>O7y*Z$raT?K4c5%hYRu$YeIUa!Tt#Xbf>VS@zE!|$q!$SP>qZG zQ-|Bfy1=fTYi+qv8XYj=OX`j^82EsJ3&S@?d$x^!t$ks0)f;ZUWY`4URm%ta0yL`N z&c*e!S1+qQpba)gDdi*;cm@JVVcyN7Eq_v78Rq8f!a{6~CKEE@6CKXQXna|8y>(yn z_plQN6?VQVZRjr`H61kmp27!x^>pgDjY=n=nvUFPX)``tNDsp!bmb6SQ^* zi`_MylM=lU5q67KlBu72(2%YOmk1HR(}gWrlHm!pkG16+tqn$m3!g`92+kd@R5tdaeFc9M|b{ojDd?AoW|Rt?eKBGw-3i zHk3QN{O-mBMr(N8-IA30KQ-F(FJbo9wD^qPxLPmk}1oNW7(rJ$FJ7ZgyUl##u%Es7I@|K7|Mo zsCQKsCQPN}dto#87dbRYnxE6AlRNuO^;hk1w=ZOjF$GWG4dKjqpVfg7Ug_!+ks(^a zs?}%4aR1aqz~cM8UE>>izb!83P`wxzN&3O~^%YJFS+mMCK`>`)fCXgckaHGUvG14D zZ7A}Tws1#HN}@v4GcX>k5~eZc{W}MOR*w&i{ha84zA`wv*dzutI87;f^=NU>6Zf{w$ae(h;@_F9$I#=^d`du93yB>4 zn7kWTdl{PbH)7w-*qU*x6PbN{@+|WKy)LItUp}$R?+Bm}7ln!BshNu;FsiJ$5}bJ} z*Im~CRi|$YiHa2f-@v|>r2MmL_YdFa`x%1aCEa4~=b+K6>l+krgHHOea?)-!4plz7 zuv^Wf+uk+gPZ(fitV$XD`x)hx3mxxNt7tI*^xN+=&@e|b%y${XYq4>INH;@GlkEnPSFr?8b0R3*Z0CaR6CM^3MAa00wL&FqI(jB``Spg^YhYNrd6)_zuAfZ|(+3O=liSYDRX~TEA!2`U zA>a8{?<)S&OmRvZd#M4{-+B=V|5&6e_2HU+7?pcE-RuPrmFIfMPT0!-w4qI58;?Hq zkn|#sA2mH@Atgf^Z``j57l7~XCWfncnT6lQd;g>Wv&^~=A$-u9`%wDHiy=&M59D~Y z2bm_|)vF2NAn0ui$frzRT`f<0^r>}IRJAdDj?=zSz#MMq>|Tv(Sat6E79fO?OuT7D zreLA<@>WzK3A5NJG&ffE@fT8U)niR`=|0R3m93Nq?E zMbE4j<=&U_1Emt7W-M@&7&e8rqY(hg>DlLZn9o2EgWG$aXC4`c9PVFJJ2RF|ZxoKQ za-XQWMv+azy$oLWcB{;1EW-LDd+z&Yw>DFfq|su*cM|yB$L(7nBkf1VAb=m;{0^u9 zV=;n6&=;%+)~a61DD%C<0!=~%NBMFX8bq$2IkyjWcyuRBPAPK}C z73VrS^QC(4+UL6NzIyKPtWwg+YDHQgI{s6WJr$qW9uKY8#Icrk>lW9!suDU9z+%Vg zmEKBXoIBZUYo!kAk4<-pu6Oyt(tRbfZ@^rqTG?mkd5+2?gR$~BLV0?{qA|DoeTi}_ z3tm%xm^>-c_k$lHw^dM_eYfSAm@HeW;*5=>gt$}A>8lM zl&1KKj;&>X1`UzNGtD1FMJ*9ux8z??SZK^2^00=t#!tqk?lbFsLMk$|iw^`h67iNg zTFz78ga~gfaL~zAr%AZX6PF>A6+(TBq#cfq`O(=aZJrhe<5*ZQw!`2+hd2%8Efh5> zWRCX9?w%T@Fu6@5wJ21k%wUP&^pB-dJq~tS1=Vt5doxBxafrd~JCh9{mAyQah#>X* z-Ig9ao@c7%?k+EutPplX<_;c<|4qSUd@X8kx-qsT`?M`ZkKJm2i*_}q`mB;7uhDVV z@MnlgOuUjXEc71U=gGz&C*ub+8jDYduBQzmTs*fJE1IMW!s+|gdEI$t8 zUxr9bJt@rFYzj7&G5YJikU~qp{tSUwk&zII+p&HGoH*5loyc1hN%9CyIHQpu;dux0 z9a)C*Rrt<(z5ZuLz0+@HpXPpMtPW2x@irhXjuFHT=d&8!WwO-}d$PH$H{S85AY9PW z2}y2w^=O(OrOymYaIW-|leh4*?)3%1#=e1t^d~=A-yg=#m$R)}nc^pe3*CchtTE~> z*)tR6a}S&%yXcNf8haO&z_W$!AM@n}mh*i&*N;WzqGK^V)s&6- z&wLFAY=fbUv*;)97vfEi&sTUPwkKQjNeCW;smF(NVF3(UyK~sXF8uM%~8$7krLuseB+fXe?2JVgV#eojqwVI| zMn&eg#L4l?sI|aOTp|`~Ag3vQ504hkzby1OP~^o4g5OON;8Ow@#-MyDE@G zYjiZ7frZ%45`v6+W> z8NJ%{cE-NomMj!~03|ih62Nq)m-{8J^yPs}lYg3kjj9MBX{@AC%e$m2Y|qzVlXO3f zkHa)GsR=5fw3+~o&cZC`{Esu(Xpv+9srEp)fLD7w8m!XpSiJyVG<(#_A%z>IKIt(u zWz$$3$+nktc{wA=1hp4(i~VMl+!|G=bvVXgIGNbRUK>3jpPRTRar7fY&glU_K$`Kw z2Ab%j`TL{EA&hi^O;ykscKN|hwRgr9%))O^QpV--8t>9px+YUH&bYPchBJ(bjqVz`cQ~JI)jKaO**>AsrHv`YY-?gStlAjs# z?0w!%yEi5b9n?zz6L_6xj(Mk(683V%8B{lvi<&14fg-TyLJ>EQv-)hSRVUuBjOu~C zhoXzIbHNC+G zgEiUIO2A(8$MVT4TTE69u$qKpFdhf^_|-$-r^68|`qe+gV^jV6Xd>R(3b)zG&2 zNgg*H2=1cY#Y>oNn=D`)jSN&Hw8`<38wJ@+2o(?*W#2UzteE1=2)k-t? zs3HsaOL6ZY@W)Kv;NId(v+puj1uMlv9$W(8HG6W*s?eM2!bQZbi&+p3`u!3t24cW$ zjvNM|{)ahyJA0+ypteEXdMszbXV1f~T16&X%Em9hK`9M43hu{4h%P2TXe6;;=$cWE z{pe=Gk~pyFlQ|lI>a@8mc=cl}kbwJH6lgO812a&3zNVaOrlWvCP8)gW_#TV#HwEUl z;S1AZmSugu1Gkjur@QJG8Uj8$sdA6Glt45jBl_)VHD!GA$ncwM>w_;jMD1Nq?}yLs z%F$A~Rdk*C;i2Qls%(PC))st_gQp{`2ImHq!K}ll6M|*9>Ja)xn1+lk+3o|?bNSq> z&70a$ksk)6iKzcvt?YoXqWr-}T%;zPE$V{P8NT@vDm; zolU>ZH3@(k&>Xfe=CoDZ?#NN+DHw%A^Vj(qV*Gp>9mINWg}3XCSfg~CU68xFh<B|Lpk2i@l|~YZ`BY=1zb=Hf zi0;m9zRVxfOU+A{R1vMs4L5Kd-{|nno>=j$rQ8qs|I7Kt6?SVqMU;4y?fi#DsyV^) zOtc>rxtQ;Q+Y0Z`B`&t3hy-94Ia84;F1I=P73=j88+`yXdw;A&7rgl8>Wl>-Mln`& zYM69q^j;)MDtGx^H)HpEj4g5=h7Q;rV^3jOjCVSn#+rODKMEqrvOX4J&y`X5bW8wDv zqq$fs{v#FU!pN`4HE4S}N9E1ZGNy0Wl=;pLdHbN{1FPSTVv4=Wn@6J`Xkx_h?S09J z6t=((i{xvWf4-|&a*YZPfzV|bG%qdDRu|MY{z<1I%^1zyBIyR#PM5TqY9DClJG-`f z8jdU7H+XA%-IOnz91c(xic8S=btM9oh`0&^M#%$fsfh(`SWIMejmOhl`bss~#6ebZ zJmb-C)ytPAVPS4gxU=Bl8EYwTSV#lGU+GQ5MY284^y-&<8<0`IxI;a5yVs6(0tt+3 zvSc37>>N~2P^%_vnQKS2TiouHaLq{nkuQ9QR+HF#8glIuL>lefmOSPk?Mk#``!imp z_ZEq|1DK^O`H4#}3aOsb^lj#x&DU0JxsAzmlA5Ibqpd(i!gh>Nd2Q$C0i1%{D?u?M z)wcR%$o@!Jao-jzpNo%(0lVE8kih7gm~%FFH4amSGAtwlZU-U&0;y}zsa0kPPj3{- zMk5LMfQIb$#lnY)RB`b6F%|}?E&6aSz!{Yh%~{_i<5xF%*_l-aIJbil>I6y)g3h6Q zW?M(KvDPN0Gq-LVLE)#GG@GESg$x0&+CKY?bm?9FQ(C{R8zQOV#g5bC+$Od`kIzmO z1s-iY6JD8Q1xt zU&aQ8952ko`J}EkUF$Bj+O{9yeJ9yaI)Q+whohc(ul!tyj zhqHF23}CW}xZKsj{9S%m`^)xctM2DI;x$y@J>9TVL}_u^c6wJ^s|pPoAMO{RZ$YrT zW7^+_A5!W*YyJ?KQ*&@OfPrrDYN^z_#<)*=t;>2WGHq8^{W`F|T;CzOtz1mDBPqL6 zYPiU3?EAW)GMDo#w1mXEbcq|@PpXAf)}`)a0H@_{cB&Xb%srD2EyO@DPHnC=kJo)= zTA`FPnCnT^Iw)=WRD1q;n!)IWn3_GBCFc}1Gw_F$0Ohnr5;s_b!)kTR2hF4^L*ZW^ zVLT7Aw5y+0Fjo0 z>jJF`Pop!r8ybS4eEVRwlfHC<6=td)kwu=ty|3c~yxfI(<~V;pY-2W3T~)DZ$_&y2 zZ-nA48b2FK%+nh-xw1~s+EicDg6b}SV5GOWJX_%0eYPFpVN7d{_|WP-4LsmGWkp*) z)!3BE`h%H!`N(UA(Z;DuijAE2X9`G2pXUTOie`P2&u9oF_DRO5s7%-y)h~Vr7+a?8 z$s2heF+OxBW*qu26`UGxVPTreErYMxhLD9hZcv|`WKR*pBtLqrjFyA|8HLC&4{R|(&?x-cVKG7^O2YJ72&4K3n2xs zgqS(J$n}F#F2#8O)JP;=$Vi(wt8Imb7!XQp+4~mKp#cXA+YK-;A~^OBrn|u6(p30=9^GO z`q$6NyZPlQmnxAT%8`p_${7UHSU4t>&mrq|=PK+4Ragw(&mN zN}jT$D?+`6d-NEOpAT3kbEDXh+z8{~W+dp}G8=A&-S2e&xH#U*r`)YK7>*Qi;BLc} zfP)q|o+e0dqNg+0)Uq2V^jz^4@=P+ny{kQZnRMq?kQWl9S#Ez3+O1s=b<-nt&m)un z(D=4`=<^6HbfI!;9dXV}(s9iDGIx8FbldR`mfww=A$+dFrU0dQKywM%dY2<9iSUsJ z3tb@1FIm0HA)RMaxdT5vu{N<}V17B7HSRpo22PCv3yDjOYrNiyKK`1CBJQ-tm?A@h zKAXDHkL|D00tzy)Ly&+}4^4p12h(sI#OBV!nYQjwB91Qe7RU&_=>8<3-7GiBSIt(n)1dX&vi2nRWA zb|XsD}|Uho{BMir=xP-w>|^0?c}gg9yxn**gqlX|*|a zLar9vX57g+ion48q+O!YTdp=R1p__VACvh7Rf&nc*`Ue$T0eT`A zcV1_xvh)@upZf)YDxGuUaMTb5U`mw}t2r|FXcv^;f_7YT1T4ovO^(s;&6cxLmIPS> z!E^mr8`S4B2hO0)R!a+7pbS&4o3T0vxktwrG(b^z->?}`i1Q-g-`yn+%2Do>y!T|%DS*OVC2lNogHFO16JNb- zs|5-SN6`;YN^P2*1vo1z_zpJHYJ#N#=3g@Ns%{9vjl4q532g>NM2MPxT>s>ANMU`p zT#+cLM&53cHD%Mgqq;*h$eg{un}$M!lG!i^zKz0@TU^DBRWaRRm}FEx^#}8H z2=^%hVzi(O{iC1j;-_(ziMCzS_KpmtJ?A69)4Mm~li)D=RJTAF3A5LNRL+C}kE?lv zUjWa*Gr*h8joKc<7Mn7eQ7knRFv8vd|1yx1snZ&UwJOi!7Zn-{Oh-oGT0l2X+vL;a zgY^nCzuvJ-{8TPY>p9(?t?jyNM1$q}=^YScAC5o>C%Bl3y-RA+sNQ0Yw>bFZGk_eY zs>@H%@+wG|$ChyAcfIYnbvLv9DvRrGtc!p2|FSd!Fp!*0)H`_SUlZ$fTQX~lJL$2& zvtcI5?SAxK#OhCo2{kwFpI?+t1|SGTeGxtnPVvFQwzRTs0CV)XJnzy5=NIeBpm$%q ziP`5r&Y+;92L!=$mRNA5PQx}da2>XwnT{NQ6QkCGt`E(CK{K31Oj`8=9Lgy9PPg+Cf|+>E>^3Os^Uk31VXN;P=_ z$a_u2y(u}_ECOd$9pL`)ykaF$aXDWALe4+=)><6b6Pw{{2M48x(nDRWc?%jGqlt3B z%Wd3LPo3RLN4+f~G$-G1^SiafCqcHY0`EedU6Do}z*e1DYf;v=!;{Q(3gTWZb>uN= zaORcn(%`ZyJWkvfPqQJEb71X!ANbTiKaYEjHzyiwQx*$qXV#d!=czw3RGs$Ar6Ty< z=)uEk8r42=t&d7q<590mNdW6Ijg914OU8?&6Md;MRsBoE^V4CQr;UTfvdb4D2zWzV z!=j))L7^F+Nk&};o(<#D5?N%7arImPm4z4gwJFljc z`#B2OD6sBZ#6?Gf=Qm{L7L}aVTM!X^W zS*C1UT<~pI20hW8P3=ALjSE5xnZy7Ih&T!%eoUW^EpMgX&N0m6-Kr1hjqYihUUA9D zbrc{EX=*e$Z!))&x74J#Tuy3V->T|CEa7z@x-Hbs%l&1vqzlh7C9`+1`f@}q4CX^| zTd&4Nf_%p$vioEqNxe0g;|7zRABvu+w=kXXu#BEt%0%#r>E1-}n_XxJ$X_QeM!-Vj zJ?X#)9bm^irhRrqeL+~J1k8}zU;&Ok1)hy07=$MuTb9jW7)?Sm$z*e*B?7|U z#YYM|(mSYG;>F-a)LW%DsBOTdg-8~qlz1ZfPf>-%^{!oqNEyMyU%2ySTkViX|$bakne+jUl(&HiZq;CPl^0z8wbPlBO0 zCpKY>e#PJd45KWi#xDaKIkmW1ozm*^J+-N!mb`|QX88168@`bpE5@!$jaR2EN;<-& zZXs8@4FV1+Y_q7qZ+1>2mNFJr^EvG%}inUpt#sVjwu0% zW0g*uGHxbkF=LD6ACdV&RQQ~R5y?bQF=)Q|11x3eo$@FAwoe#2O<2UPeU8wljX$BD z$&0*pOOqjpb$f9frft!yHi?IcSgv~UKWkohw>F1WHG>+_nx5L=p?OA!A^~A3YkvFU zig+l&kVhsNDtl%c_SU_QAm$hdEpCx^WvlEQqe3va${~o)9_mCXmE-G{lCH9Z#8~5P z$*^P`&q=?zq0ZIyT$NV`W#QQC?V`41r1{xb6x}Z#%F-8gnf{ z^J7dP-*V^79QM`6f8J&_mQ?uA1xNcC-chVzUen%hnu-wF)9d5V#zZ)~88d0S_WJ}} zh4Ob&z&vy3MJ@yuTY^pNKA?YGyJ#*Di4v|q^SMHLxDW`9fj)0ZlZ|~K6zxf&js}X= zu<7%a=KiBO2~HW`dbLs!=i5BDUlc0!e5VNkEIt=jJPPm4g$h|7z}Q(2_a@IZ@F!y1 z9ttV9#8h0~E}N_L9#G5K{S`9ab)2fvn5(1Sa$0Qc=s{$%oRh=^B=~nDAr!Mn?uwZeW zt$`|=em?cWcqodv*eM~nqUk66VYwz~lnhzHn8wIuYKKFG!+t*2XSDBl(W+4I%~$$A zn~?7(z{N5L2LnpfJv%Ka^0oC0tTx;hK!gBEmcfTM)|_BkOB)HP%k);TRs(wBwR0Ia5Z0u7`Jy5ge5JJOKA?|9FD2+%rY9P_;Hs`=dqT zM>b4DZ6I$od9_;xqF~OGWkG4|jc6)w&=VzH;;GpTcfRZkDrO*>u9VgbcVK5gpQKnY z!R5|K)Qlx2cugc3#AN+ygzLc4jhwLfccW6;yjM2dc7IrGzKVRm6B)tY#YhcDhB=xv z7xR}3TN>@sA1!ItsV;`UPJM=royFTLXxBSV0lA!#armIf?J-Z_s*lQzgpK46ndULg z?AAM7sY_GF?(BK`-0Z2kt7icbcuS+c(Of@llQUMX5od22sXQDDLSMK~Lv=ZgIJhJ<^`fLY2b4 z+{0zQKlrLn2C*IjE12U}M5cu9Z@b*Ndki(O9XMJT6l5#v77Q3f3Lm9AgGMumN!G(N z2pmJo;c>L+BCSAP_d~Et=%p8*>VXQ5Q+AT)$++WfRpql(W9jHLjuK?vBHd~Z5D}zm z8N(oNt*Tq}-!+{Zv&N{H6x{Sg=I-2K{4s=(8KyaF@w_Obr z+>>o5l3`>(#RHyDW-%44npnk@6;JigZ1&Gt4#eN^O9y0nrm+Ix#sBIB z5WUXjwif?^U;SSZC(P3L4rz*vyfN zWPhKWC?4n41?tAHIvs~fFZ0g>eJtZI$t3=#w(L~T^kRz+K^L`YF=N}L-#&*Fc#hz(ejNCoP$EI@14_G~E#(j%!d#1XKI|W?aBrG_ zZ*dkRHdcVK{NwZEgGQ?bbp{{H>0m<3uS}jA6&)>&-{8~56C#Z5B@u0$FZ!#|eSZAG zh>24#heUS`in6j^0DOIcH4qtEBR;Sj6&l)${kgq!@ADW)u)q?Y^;_$X0&b5tP&+8?xdGOVW!F-*l8FzWowr zbl0;w_H$S)*Xn1!{UzQ=kSXH{q@H6Go!HOz6nPa7PZ5pfP;kFQc_Qe;1V+E0!lMvtV)k~ z69`yAJ6&v#6dYGV z=E6b+H;_yY-c)8=@af`fMg7L4pk`M5bXMOSakf*cffA~%!K$823a!Ju62SS43g==j zwO}&3y_zA7M)Q%lSz#4k0y-F&(P<6lEf$d+*9OMvIROKKTQp~M z(&&FSrWW;A3HzeU_LwpOhk#Youa!RQfa#i6Z^EEjEtB=3+NpzsTRK1-w6gBvrID2- zS0vn5#pyq~`Mc=FyGjG_cv?kS!w#|GjQ6+Lff6fT!Rlw;8Q)*2 zB(}>5rxeR#@qQmAl%=>t5lRNU*=t^>#tnp9ndFdt42Io@}53-5&=XX&90~Ib(;0@-dIwAMK`}=AyPVSnlVW0b z%A#U1I$#vuIQJi61W>xLXy@^^4J3P`XwtZWIcvBtHwza9BJl%2ItPrQue4ic2bx6y z{taq*aV9i-Ga{Ww=5qO}|3mxc@Abf2IRQHbXs44&4V}?&Y{k9bAZVbmopkwd{2+Qb z){CFj{3b#Pz(ZoewAY&tdm&|JexqKo(l<>@@3g8sy-6b<8w1LCB9yG8%JcS;&>K53 zRAT9jUgoTm_-d7L;ZA1D*VBbWfBxW}=GHsK#J|8~5&BantTKQHA00`-tKIAK`oX%? zr_W?89T`=@cAkD0(2Qbug|wpJl$w~B9*oxzU5sLSD+R;xaPW=sWCR1Ju}DOt3uCFH z?$tSN;>N3>CMWcI-Ii&T=67U{Vl7IdlHbKuJE^V3pDIjz8cm*Oyfoa+3$*Jvy1T%% zLhXJ2Y~OlOzE{&7+L`eHzU>#^eCxUWS489=fmLV&4S3ERw+wcgWcX$m-YYuZxml)9 z_x1Iy?Ho%te0gSPgzxOQ#14zBKQ{exOU|{?!Mg{`eA$N}A`)+@nB&EG+<9z1xbLj*@LMcT{b#ShTXn>EF#Z$3ktc8|!inB^cN-0U940jkx6`4yS7n)92d~8j&0h9#=rFoJZP3 z?w&_hZ`8JN-U~I|*=cExxIUhcUv$q&z z8wxp!iSqfTme6}Xv*M?nPM_D@)xFq?tJ$_#EcjOqpvH{+h!rKKh=^6n9MxTSQ3z4l z$|2D!v-7>89`?`<&K$aXRLg~57#%R|s8XE|-qXEo`|vzf$j)>A2G`TnDLN?lOszt0 z2LLxJq_<(5O3<>1vWMGI`tD=LHX}s60?j%`KFvD4j-hj6g z=G3Ji7|^g1@ghbs{!a5W^9~NWbUt%QIIZf(p=>LgmTs;YN31i6<;Jhxf0W~YTYs<~+D8=x(vb#|TxRa?*NY+wZKZw(PPgdo7Od-X{vs*GV zIro+hM6I1IuBU5D4$)b48$N(d6YEGj!iDjjZLCXkHFI}TribUt(g7k%qjx94?;TSu zA;8$Fc2pU3_}sF>ZDQWXH4|(LeP??AfS5m%8oY}druWf;%~^)F%Wq_6xbt&}hP5nu z1%!U@u|jya0BotB_`P8KqgSIf0+HDKOPk@kZ^XC1VDueeuh9>Q><`@V{hw{@e_8}_ zy(XnchAFfB`~s&}Y?FDaRt&bm+MSaeae?3)G>=rW{y9(2Pcd}Nm8gOvp3td=Qe1lG zwA5oK-TBt=49=S_=&yg~t;}1sBzkp>xWHhYT2r6rDa2=UR<$zLtHguNI9TQUXWyrL z#{*;*tvWR>p^Ux3HxrAz0rU4eqo1OIo-rxMn*-t(`GWd8nReJ@V2=A6jr1ho-n1#` zNLCB=)48O1>?_l;J$Q&Ebg|^Q-jMLy-7g@J7>=C{Xc+|Vgycwj;AXaDLXGDoAAr`*b`SvMK2l&;VT9P3r zr_Oz|>xgwc@Ob6~9vQ-R3OMU?3|FEjXh0(eStD~FpgRMJZgMOFtlZZS7z|S6NPLZw z;AFJ$!biz_FZHR-Mb-K35D2B#Mnk&XuD~W;(pKrER?ze_{oUb!ful&NMU4cPkfJ)x zLs-0|(ad_Bj@wnoPpL1j{Tn=LxwHJ(E6Af{zpHiIc0XvOh4t7S&Ypl>Cw;30lqBs% zh)v1p$gNM9;Ug&X|vOlPS+B`Rb?@nWi`OZE))E9)0`^8mrWz%q^GR{PF5 z;(^oK2fO?eCziXYD8%!0?nF`pFsaa+Bk9kK_BZIv(!M?xZ2yD;@c+Ym{}0bjAOX1H zx|-Y&@DV&GiLAHA>;bzBAaqw@>ze3snHUIo0{b;Wn=o}MT@@N-=tgij@|*(W)1UPs zD@9RO2p(qhro%on6v$3`ze z65XNB2(5qoeOaT;GgZM|OuBo7NEtq90vyUk=$Hp{?eH)NsJ`Ok{FVoK;8MM`E*9970JM)*oW2Whgi7E;Ui=tXCpsE$agK zbPfw_ym)>%)Lsm1>>5^99JEygcCLqs9ah?n!o}R0XrYIaKOh}^-N*~yBO@C>loE6{ zpOLp?xeBaUWui==Wql(C6Y~!lhjyqx>5O#$P9Bkg0^z&vow&;l5PJ6=lyK z4vJ0h8_gPThb>T#I#;Fg(L}Umjd`M^ zBT|tEa?n!_LzrlGwzA=oi>?({oFZFro^XIc^hAYt@`pe;bApk_%cj!bKJQvpjHk}K z)AGh^MGf75aq?1ctT-CmZb9L**1db-i`^Jz67QPtg=I4VF}UKM?^-cvSAJxaPU({^ z&JyW|8c5{~_WRH@9UOJlSYv%i`gC*1jvBkx*hD*VK>>B=q{eoK$$}vZTLv*&-sW4@ ze$tl|>@m>g7hF%IZxSa)HJ41u~q$M5qkXQ$;6G zmdftE(4W{4M3Cb&e6WZhHhsWDgrZRB?X%d9krS1u=JfKX|E1eC0 zo9ST@`?Gw84q4ly#hl0fhU`xZZqcde|IqTSx{b#655IaFk&#$P-5&5 z;bG;Jo?W}Oxfy>*oiQMr5`MGy5}WJG9>r)zliqI@*lD0T&?Q(>!8qjmg&Y5tF~~^L zXZ^?+;ov%_Y2GRW0;5+N^Fu6}pPi#4c;L3dz;Sbf`{w)6G(OwfSV6HQn&%Jp>8@7o zrJtO3b2*mVaQzR-;5r)Fu*))rkNg{1KBAG>-US8OyZbV;`&?3C61B?YTyptrx6u>v zxmH__MJ?x9@6PkssI`!A=t(OVW>?@U(NIpL*}D1?*{^7h z@-TqwQSP=@)o`-)U2-6{e;3n}-ynwDi#;t6Y2(DuR2u-+h7AOX+Co-qo10rELKVV7 z<|Ztw<}j+(x;}F*v5{$31+loJywr&68hzZ&!*_XFp_Jijnoguecqk46Z|dW$a>(4p zzFF^_EpoW`8jF9Yg_9=*ovqjT=6snO%k?BszyGGY!EXKQa+3Jh{obAzZpVkiHp>`# zP|VsR(AP8+9sB%fUb=bLWlQLkF9k(`mYUV*+Sl?6w&;py2pc^U$wBC|4_kw&U4Ty& z!+=r`G|{KYP3t2qJ_PRwkKL0^P>`LdM6ZSyEKip2-BE(F#Ww%l#w3`i&378k`EU0( z&E;lOH-;l9Veb{Pw1bWA`4yTJE^L_VX5Sb&U5={~HhK{G5bKh1EIQ}jjxM>k<3?Cd zcrur3HySI9Vg5Cz*C2bP%>@Ff1VEc(`S&J<{|2LV*uUl)gu~NClRxx8-|i}f!S9S_ zdDtQ&BReA?yPe-J&5qoFt}bqe{Z;(@BJa$19&1cnCIQaSx)2}Og&mb1IapXSv*9_H z`}AyNGxJUKAW#;0U5a149LYHEn^lzq3d1{>If_7m@R;E`6NEtUVptt*fxv;tO;wIG zf$|RMb(?c6!SX_AmqKs%*DCu+q%d2U4=19q3htbOWBJK}~l)po|k#n<_nf5*IVn>oy%;&_BC5xX$4+HUggFTMJQrlN_N!R{Xu{>S~XN zkGpzSBd)h8t{{m-7EkrcA6v1>3>*u(ZI=*A-7LBVcax96s>vG#XrK{(i)p>UiaKA@ zFQ)&E%5wS)ehCgPA6NURUom2bu*p> zoe&a#V_})lXtQCpfk=-YWB>T2^pyp51v=Y6^97P$v^R9}DP*v@a_M*_^<(`r(sNa2 zqf%*H4hy~_nSP%qD&0Rff+o8A&87;QwRaHyEo&*IR48^L*%^J5oqF~m-!%ZWAb#gz z)S!ytai&Y-s>N^u#53f(`QHX36Uv&`LQDU;+S(T(;A(*_7$xjU zn`132%^E~>i81E6$bUB%y>CMGCnM*B)Ewt(PZD2GltDUxgz)D3(HP>NFV(Br_x!Qi zmVeL*|BKrJR@;Fm4BR7==GfNGO=@J6`!xX~zLwz15Z(s2Z7u`DxOP3Ex0|O)kVS97 z@z1AZLScyG_yN3~;_=-MybQ%#pn*f*mU~VNN3)=$s2&}EglAx;Sgv15QhDk@*Y*Ee z*`GY1-)-8PmuV9l@V{NB5+Hf=c7ut^TnOU5wb_{1lx*=P+~$fIbfSvlijm<=WEy4% z7L{z${Z6Js=dJq%^bepF(b>p1T%DB^6e6rpcvHspmb;g0%S$=|kFmkc|H}{k(`$VU ze!a!Vkbn2c6auWmtEbBkqk6(K1rAsc4FgyIf6i0>%?kka zK>yc2r)c!IMsw2T*Dp_2WpoVzwAA21(uDgS8U#*jR&%ek2{haD)rI}<8~(@c`x?I1 z4hiIe?aJx4{=a^}j=*mVfT1sU5O>}YhVo`{5<)WsjcjF7{;TsJt<|RT#dftF3FWDP zF7Lm&kN>>2et{$%g0(3*L%=O}YKZ(d!~D|Ql3p5kVX{93F)4%Dyn zDUV9yell4?Y%B5(RA{Ts-qtv<*IO7MyCwbCi1Q~G`^Sfv+}BowOD?b9o6yGaFJAax z|KE2Lrlq3G59nB;kT0r=!K4j)ay`SBH6#s9&z5pEbTL-9Y<4qkqd;1!=p2d1w`|Cb)t0f*{ zCLMtq3k26Na=;aNQ|593!9&tsYZUu0vcCUt03?2YoVniLp82gTmjcBZID7FTuK6U_ zJi@x%o-Bhl3)aL$g%we%67=1p@rQp3wes5DDy=!!*pWcm@aVGs{RIGg9dH4>a}t2v$ zv%l4efr1N#A4)9!ibw4ImYUjBb}=jGMQ^uF6^NNQucC=f?S}Wb0Qpjgbw#_4{jYK5 ze;?Lq;Hxvx2l^ua_Xz5y8wj2-JSS2+B+G&Ay*Ue6s=)~foD=)?fw0MG>E7=z%uCCY zgENX1I(kdfq#>c9iHBTCCC}aG3rc~EWX5ZCq)1u&$qRkH)^dx~re3faSO8H}d zR`uHW{(gT#kn*m5X?v$E zT;t~j6OaaMLI8;*xvHKXx%YSTG~_udk=G0KHFYSd=WpHjS~}WV=EAS_zdtZyprK{B zns5M5Iy*MM^-2XybVE)Nn|h&A7HP4X1%+!?2xJa5V$5(Fi(H4GG!{_Qmgue>OaM;o z+u`Vi^Zymc|K-2lpaZ0?*93*{@4{BcBMCvFb{kQ#stuj4la&>Mc+|^V+m*f{=S0@~ zON{=P`DVB?n(}fl65$vaG*UBRUlgFc)5?WWAS^AtZ7KbzE>NtH^BjhCyK!=IQV#Z3 zaf6ifY%Jw!B|Rm5K7CoH3u<&V6?w;W8?gV#& zYj7vHgg|fz5Zom|aCdiihks?i&v&l&KHPBA&C{%&vue~BqpG^5;7PXvsoYBA{`rw6-hxe zm5cbgT&Gk50&4qy;3|{x<`!w}vB#dl)VBiH)RKf7%Gw_=Z?CDg0;NJruZkrCBOzH< z>58HGXd$Qs{kmBKOq@OLF~~Q>{6a&1`YeM8qriYCpTkWK(iV?6iV^LaU4+Jm(F)V=2{;_j>&``_iBUdQ<~I!E;>X0mO_&1<8qr7eD|^2bR#eoG z)8Q0T>kJv-PEHN(>N4HfOe9fN&F#9{T|S+%kfZ^A0Qw4rl9JLwH;1XE#oZ3%ebpqY zuFe=5CiTU)Hql z6d7MVED$UJ2)Y>l(}kp*H)N91!j`jcf5hN+ptb_5mEdvNoPv>NKQ80+4E^-A0FDBr zZzoEze9Hbu@pGdPZv~o5L8=+Fi~DPetw4TG^`|;=(Y~Pc+doq+gz!L%I`f5I7E0IX zqDJ=I7NVZ<$mmJH=ALZEg#JI*=r=tSetHF{x7-75wnWGm;rvq$lAH~l4j17e)qhWp?g|nhLA9ds?c_h@_NIID^)iw*6dz1VtiPH~#6eCjzr! z+fd!2%gJ0N-ouk6uZbD~tB??p<(Ph@X6j)bFcnFBD9OK&n3DF-*Pmn5IUOBg`BKsR zHzxq`c=rw#7fZf|V(SK0|+fHjWL ze#8wLFE0p)P~k|0q<8(#wOkM>S9tKxWO#73O`o5^EsYQ^Zx~6FB<&55gsV4P6%-S) zMG3o}Uws`vgtvXd3-BbX8XAgXbvR2;$iTG1+LOmR5}&{8H9zG()rO)OKS@kg86X7z z0^}U{)V3WUJa15mbk_?;h~|5SX-=?}7VxH?%*kEc=(H5LK5s3f6sQ}AN1Tq} zRCOz@f2frUn8n93Immc3%>#^xy}yiJAQCrKxDWFOfDn9fMb7Hk{|==D@hKB=rSqBf z5i{HtYgEMi+9witGhQruG}iSV{Rx?z@jJJ}5v0$TU)0u4-e=i&!hb55xwgQv zFr-OEDZDt}{v1wq1_&sYPPJk;e6Bpv`)LU`Q>GDwQ>1cyvS{ZmEPxLD{fokTv*S5; z3z(s_F$_akV0mwt-mfz~Tk5$<|9XVPg}}R_Z8TVX?h0z$LfA|+m<7C9auBIA9K2*& zUz$+g%}kX6TA3CLOBRYS$);-()!{@zfoRnqQXKl_{y~~8D?9`RNnfl`MxD8D-__0XD`J?PIonwz&3lqi2rbRMEWzLm$zW0zPQ(C z%4=3rJON#COpDLr2`Gy20IYE0h2lOjpcf3V2!+zfT4`Y5+05B|S1I=)vxt>f4?~-6 zxvUjgdK3ty8MqLn`Y$9xT8{P?dxS>u8p9KnaXJCmB3E5Tso$?s@RWw-P%>SLjZ zet5d-`-Vqca{{$xnn7z?B&75~&+P_RSRa{S32%q`gc=9Fo$mD&gVLX}_P>R|+XYr_k3@O3=oeL@?4Db5jfd$jJ(- zB%@J{aY(-})TxYrv(Co}e>cW0us7;W$3S%_B|$%2Dq*9QCTEnbE%a}*rY&04n&LM^ zN;-UZ68j~?R;-Lh4YoLvlG=^nUF(!DIfGUR#Rp^{(r%P{)5EDZ@f17~4hXs=+_{QH z$jwc@UN6Ha@DU8>K(foN_*~*JlfHcTjWn7(dv@o`$r_Voh$ipn;EI)@A-tn`Tg2ES zchtM6m#@c1vC}nvqegXiDJn8u>*FXc(6FB0e|rHEHq-|6>{DVlQdj@@F>Yi^U_;@E-v`Em=yp^UwxRijdowBnf1B zc$dHwfd?F(!`_PZ(dYuPX=mtS%h~)LKI3WGzSUhtM!ahh^|$R-;N&x`(f7BhBcTiQ z-+HwBJ=MKC6o^E~nwcp7jzKTbDMa&@7<2UhJ(`uqgdDx8_UW19V0x+`_dJ};RH;mUp-F1etp%pL?qD=84k;uD%al6GEzi*&QJP8K)#b2ZN#5mW+wt7p>^oPlAS}S?5Otxt94f- z@3wo8jl$OMF337RVqnqMbVT=he;g4KrhZ>~uM{N~CvhQU2ZbAl=a-=JPfJ!P=+r>7 zLFO_;4Cy1&ROKb*a1acBd3{k?#ZVt8kGuIGFmBLGMSWltfB?CEQ0N7~T{1 zCD?PbuxFi4(bB<;Sf*=*N<`E&X}1*LlE`Qh;tTIa7fpv-lriqav(QgGbmuN4^elu( zUSkaXnFlH<f3it!{jH$vR-UW_Edtv~W4aMEAzV~uX)49h%;7rPJr?lMVGf{1- zC%k*P91vEzqn%p<_3x)r6waIqA`N-3&SfV%KPwWFZHk8sLaKMp zA43fi^ua_1;P;e(U!47ZXvBkI>ky*7?U7odYzP`{1-uXAr{%^D9gdkG!yy%W zG7xP$e^KpKCReMJP~+RUtif>YBpm(_wtcL2UIJjYz}XXIN7KuiQN|M**%rFAz?Px< zdvYWl4-T+$65VI2pgH}{R$H;&JHCWZ;zr4Qv=%8VC^V3Mo@9%Jl;3t;LD6d@3>NV0 zsaMKX4jE6!wdo(C+vdZdaLHE*02YT=kodfl2sTmd3~djO@Hh%veI0vNkc9FZU{dXiE|#H{jaY90hDeLE$$4LLuNPdoPxj#0IeUY2^&AO&s4mpze6fF+RJ4XF_6|; zTx=+=z~NOZ-9R|Tv^P@z$Ih>Z>E5;MT$^#a5lt)XXCl|QP;nKsxreh0T|4bG! z8=&tdea9Oaj*F`&vB3D5&@B|GB9rkUt1+1BG4XH2@C?2)n>&#a1TT`g-`O`=?@%j4 zv#Sv+5GS4}PmjV4`4&{EhZ2nj*#r?QS*c4VuAYt1=5e|tl)3h-kahzZgV{$ZCtzvqj~a7_j)q1Noo~V740cQIov&4$*3%-i$eNHWmjjRjC6gw=d@w$- zBA?R&lXg&`O?y&GHrR;fJN$ibg$&_z`L%|%*3AExzCa$0%be$s4dq#FXtE_Tec*KX zjU@nH5wM)u-jyI_h=gn=O~-Y}541Bxt68o!x!7Gh)PXUyjDpnRuGNyQor%l@SP*fV zH!Q=ln@mq&oNaFAJEs>>Fk(9Txa!i+5?0G?&xAMq*WE1!KYE+Pf9FX=u}S}Cs30KP zDI6&0purumm3g{4L-WiFUhuN5MUrt;_5##H+f}QC{1HYZjv%FOYxGVZmnA-B6og;^ zEuGDeq^XLJX>@9Y0IV8;t+gcNGMA-savh;)RFabmYj&N#?3hC*CVm7*u1iklAP?8G zbF3t8A8Bc6iOjW|jIJbB@d+Q~0`@qqF;9WT-P5DPvilMf#q#FIhz3a~Dx}$mH8nvA zrKc=}qrpxbvq^7u1(XivHf;I#eI7F39nL9Kp&VIu;Z#j8c3xe*mgw|%;9Df4@wFba z2_NhWiQ98RxlP9_kpiG1Jx|2o#OK(5_&mwJ&BQ^vnbwcFZz(E zx43|ZJyd`6^OydIY;M;*dJ7C4|Gvmig8blTMZoetaOn%iR16?%BQ;g+@Zg`L?`7!-&p%NE9e{#|Qy< zc&47lX{V2y*UvAjhD5bKQ>7UO0@GO(G6P5F#9)C$8u?K~I4s%fUOblmqZa@j2@zV+ zfpQ;_`{*W#ECH)8=*wErg-N>@;SWBm`Q9@D8n3kjC^Xm@=^2mDsuZ^5poKV++uz=B zven)|yqsas#2c77=%XyChpsfvL!c zllxq?m5vks2gtfKJ~(_o{cJTDE5cLU%f7zKBK@8P+M#R~(rCPA<10%i?&yP!4?m@l zTu^8Vr582sMScZeBP?@xnJ>TOl=%G8$9(^0`oQZRxEodPCq^P-p*ZyY#*+;Xo79Z4 zBd$#3j3@|-3)5Z!c;5EeH?C4`L1o4k(v9c+J&S~@&DgKE99xkCDiksv>3A=NG}c^n z%A_PnxD&Zr2Cdi#z<`EbZwrWV5ueOTZ8l|2`~F(x2`@%6GI$UnFLFUae99{4b!oL+ z^3lfmIr8JxE>_IQhj|?Q3^MqQ&0gBcj1MDC$G7tW61jqSP{@SVuTP(y-u;{QGUn(B zqj)DO^;}IH@b1AHysLE^6f6Tv=A=j!aO?2*n+Z}N?4Yf}a6YTuLN`kVc@uCqv;FAX}{-uSxl zh`PB$>5AQS?1Q?*mTE+^ZtaFcSM!dg2QQ;@3&XNe_pKG(RoeRUPIKHWA9UGpwC4o(HnDnXYnhr(9Obo7;=ZjVhC8jR0s4o zPEgknR}*!;Y2Wfq5`KJA7f#*;XUdBG-_3qV1zusm3pSD5%s>^G)d4M~-yk_o`l} ztpz+U<`yOI*Lyt)ZI-pTY^oUr0f zM8NkIjm&|;0Eq@Go=zxmRoWmwzv#eLEAY#HSABcJKFPtJ%ia>>>m{NdL<|QQ%j!3S zhY>99gBl)~8cIpHW8Gh-9ODWYo}a?L>?`U@A67_!8NGQNO{SsLxc4&qJIq%Z5&7Jm zA_+^ihlvDVi?wZ_QJ-{EXtR4#>GRq>^(J|yQC{$u8Fa#^W87IbBZzJ2&A=w1CvVK% z3T$yj`Erm6t4#I!)QK0nOf*{wT(ImhoLq}Wy@?mKe|BJ-;c- z#H+8i{sL=@klzJp1gEzZcN?_ge^&v(#k;=q3{?~B1KP{&n72o@_GoEGil@p$%`vGW zuJRb&tqs0k#{)}bEoxAHO)E{?{11@_FKks0tx=Y>PH2xDkx!ah1zun=#|z^pLajQz z0f^EcbF4K}HS9PEXfdFrH>q;OM~2<=m3rr<$hGSoIZiqf+{enlh1{MjqJP1>+>*-T zCJJozB)Jk)m?_u71DZ_->uP*%nY-YiZB=XXBQDMwnyuR^HOGg?;LlA^hy{D=GG#as z?AAL9WJM?JZyce01hgnbXk)t)%xf%1KiM5I&IzD98{Zt{V()*MBumgYiN^g*>}(HE zB(r-fg;EZafeCnunkR(riQ3_5k zO%CfHbgU@c*>ck2BB{__F9fg<&U(LcXe|?s_D*T*=vbu`)N%4hFZrR62*X;9>1&iv zG4`Hk2>H~5t)!1c2?C13&n}4>w;`Z;%;=Yx4@tMEzxuXp_w$!q)=UOP?9e=} zn2p7@gx?p=tq~KAri`@qWI7oB^%hkYZFyaepU=g+1CXzp(dEc>NRit*+d^f@6EN+l z15Pmz8WgN>w-C4wCLRWj4^(g2{3iC$Nae&nCcv?Jd4PpVTX{k0u zA6qN(x+i4Fb$Z`i&_=wfvKbL^Ms}b8`qa z#=P_m{=3wWHkL^=B!P~R;qJN&g_$8+uTe%j}Y zX&yDNHQgS=Em4p2Jdq`TC}@3-F%35t!nRI>&qjKBjsWYuF(~)l7uT*+%FFq36ZY=V zoc}=f-(}D?Ke*D)>d%jJHtSyiT7l%#jDiS+e9@Df-G1-G7-S{>(I7QdSH$&)bG2fM zC!DKrC10djN@CgNx_W$P(oQ|Y3_&kKo(>BKwjNP8zlu^3BVwLKUVt2zu1Fm1k1SI0 zqT9dnu0~TXj9dYq%ZX&oXxehLm}VT(s?x^>$eA8@3LCH?Ncl;-t_T>r)fTQ~1C)nT z#P){5K^vU@dqO*y`v296qn$qrD#7u4iVD~{KJ@b0p~3RISwm6DXE?p>=bfTrVU_ED z|M-~xmDl*j+q`SZ_QUv7Ed@SK5son@<_v>wUl0NoL?|sbR}((x&cN{`ZLND{u{;*^ z%cpMh%Pl5U8r~G~?&CIXHj>1hka=Fi)+Q~sN^0pN)#++Y%~lVoQWc6`H0-(a^#I+Q z!0Nd6J23rkfwQ8O3|AdcHKJNmEA?c<;Rs@i=ut~6rI5y*gwRA?o>u=P$ zQ`zT9k1f$!(=`a2{KONyKln$sec(JiT4YJmR%=)Rt{2Ti*xhfXuvF|>9|XKgf2JqG{AXhxK3HL_BZs>6@`8UdLwTr7{jmBI*OzqQS{n+f)}@skt+qWP3Xj|$@w_rY6%kR_eGwq5O62q&MaLF@MFYD z5++E;b{(eEZo;}BsqZkLZzWT5q(i4)!2?`fb^k^a?N*=CHw&43TA*ax$3!`NM#H>kp-`k1-eh|bel=tJ*avPljs`Ff z{mC|J=&)y!%{>O)+pm?s0Rm|V{)zMDTYX{A)n`0$mqNA zzF>OMANWCFy(SlwS`8rb+w}#dVQp<3Go$ofF21AZ?t}vsR@bc)&&9nw*8JE_EqBN} zD3Ab+I-H9kofu$$7zg3t7^lFbNh1~Au$?_TePQ}Tov)fLy*?(O!}4p!h3URaGa0w& z!Mj>cVk_^*jZklU%)!L7$&Fn+gGg)=ZTU-;;WH+jaI5t^9Y`vg+LHHgsf8bH2{~;R zMdt{GB8&t*rC8ZLLg;5D%)=il3b?jJ(eipO9wnOxpL?Vb#7!Kh8%lV#z>)~k95X3i z#1f;CS>D8)Z>QXws3h^=;OL=uC4zZ>1o5Dw1G3Zz&uBQr*<q=!FI4oYaWDNI8xhTNFc`p!>jTds#li3p zl&^*Ic$==1llH_A1vws?6?Wo-C2T7xH6W_0ijK*w3bzuLZt+`HivrjhGKszB7tmzi zMAI|Uh}e_c=DrQWyuA-g7~btJ&(FTNddq-!I&6D+8%;6Z3C?scud8@~Wo>MCx?}G{ z%r*IveuMK4orJEo!4a?iSuJ+4GCR3W)L*j9n$TepB1-X<-0!)sr?I;YEx=q3a&PFGgKt zN{RhJ_a_*7UWULy2KSTN+nHf3FKd-r0301L>5)uPCfjspa%?dVFn{beuDm2C(Ti@- z)pZo|ecqQ6(_`EVg@{Vh=D~K+O%^SZaFOcusR0rNWtt~DmBCY7QWA-z!*{GfU&Iw2}cbN}JMOD(xI53mZkh{B|OZyq^MC+UT{oVoAb= z$4%j0p=d8wHdUPIAN#aiH~n1sL0*dJhcC`+vi5b9hlgIdb2_#?$KUP|D}9L_8RclJ zecNOBj_T$$jRc^eFHSt%L(4E0z_a{p=@umKfr8TbmY(pm;N!{JW1ptlJ zrXTy4ib_<{l;yP3R3jBmNy8qR@`~>kiu?x8Rq$FG{^gc^DZUX6w#CztECJ%f9kcA3 zZo3Z;%_n&@5}_8F@iH`6L*Gg+)+?g7#y^=2_!s_G(WKsJavUFkFb56`%C-cUTW6nJ z+o6DX9cby)Dc1mvwZRxl5H0fg<>T?D1Rq=G=sa5d@ZGR3!*6?g7agOxm;vc;yjL^05hoZl9EVcwd$Ff36b5L0eiWErT+Vk$i z|3tw?i@g}dhlh4x80f~y`5=8KOMk#yT%oIWV88)0AlG($-m(S4(8N)5I6i&D4Cjdn zmF-UO-|HYRbEr|aiF{v2mNJ=sjKPT=R+N>-o#uqVCSrn;ar|lsBfngL_P*y??HuyC zLgMqf<5fsWuvmw>G-q6B0W!HaS`x$4IMR-HL#Rtf1@hQ_vPOSsdZA0BM^*x_SLZDS z?yq_%eShmK&7P;)?VinF`EH3w@u-({iW6r%x2|1FdIEEdy5IwIKCwJmz8;V#Ew0)7 zx0F{1eSy1KRwGDf=}g+lKTDXC+}|(bSqfYlP<6fnNx0&&^J`W6a&Lxb#1(yeU(mg< zH{_BVXS?7>Ma3YV2tPO!k_>XO{KR+1=}u@XDPA3>t*+yuXR$o7)kpn_@;?D0Sx|$Z zoGptC(e~uG7lf<5k&%(k*B6|HUkYzxkPNW#Cy=k<8E^S~zDE##f_DsSN|_fJF`Y7< z98ji(&{IqC)5RLt(**ZSyDVB07X-2WMk&^KyvrPUOI**8F29Ov}#miHe#qrNt~!1@{P#z9)i_&!?z72fm6M}A5U;@)(=oo z8^=o2@GKAX^yK(39wfbWb=#Rah|P+WTrmWX@cOMTYIETh2~86X zp0E!al`I>z8&TpPUBvsq8@#P{-+mZXu*XzQhM))>-{3Fc*uE78@l42CpO!Oaq4Y$M zycAAGfBAPBCQery@ zxIzKL#>dJhuiuqI)_1*UI6mE(Px^APq)=7>qlT=P5zAmx_J#a)I^mdCIe2zbpe8FlN2Y^VRA!o~>34@dnI(pb36`>rG+ z#=cC_d~?uV!UtkXE*k%3|+~8V}6n9rL^#wiMmIEeLYJwQRxnsP_|Hz}uoeSIoiZdB* zPeH&MDaRVfEwnFt{O$1hp2GNePQo&Lsh*^m_E2fse@2N=8J$v1Ew2agf^l{?N)smd zEDgfZ$eRDuVAY*XAj>fi1*80Qw(6_;66@;X5-guOEV|l~+VnBb4e8~yYaP|Duq_@> zj*wREd(-Rz(%jxX`EhC=xRC2&{b3+#_hCWqchF>bBqYcgpU?w{O-_Mo+gm3H=k0uW z7m+TnbKcr5wg`E6Xt2DP;gmp@go`PYcY!bV5srOXd}mf?NbGaJ$<&4EF*WeYrAjak zl!T;~fI@kluk<>wa%hMvUvC4+vWv%2osE8wJ2)EX-bVVGTAjJ~k?Hw(TL z)psVr1v=Emr_nB(we|z{z<2{;T?4mNf{%pODlObdRVzX9ulrHEPAA<`x%0`m&zrIb z6AfIBUsPZQIxqPEVt0i5jmbyO%3=|4D>*v{r7rJ9k=-pU6v1|&csgeJ#@ej33Hv?8 z4#5#Dm!rX6A+8~_vV#df6m1};+)FD3GT_X%-=`xpLw1h8DB4HAN27@|-ktM*+1D}M z8qX7p<=4>B>_cbcQmvc1Se)~=1!6@R>~d#JUrQpp53zPHh|M zg1v>g`m&qu(G`CD3{|)qd^O%K}fg~T{m=5$q{ z1Vaxx0kpE~8XYOhav%Wc{0Gw|$X?#f(ftSc){}*!b=1I;8feZ1(>jM`v?lNA7hkOM zUv7P+W2p?Vm(+mSBUmQB(0gqybOx2TH4+jq=hm1<*@)PUt{MWCseKhb5xv+$oq!u# zVK-|WeVAVM{|SK?wp#)_l{QwHB2--~%Q9cH?_ zx?&Yt*C$9Jh;1PlM$iIu&Dfx-p`5rbPzTwsr9JVB=TwFdH%e+Gtg!%I5+stf*nn0U zc6gt+IrKxWl8Bjn&ZdSiURG;m8RJP-rwU;5G`RGcGKL+X2d~829*as?zpVBIgdupt zsldqxW$6*?s5Pw10_<+&Uv~G121F~T*Xo&v3YYYYtM>1;l?L;E;U%8HT_^5tj}4{9 zUX3{HI)GNaB1ofP1Hq$obl`P{#GyKa$^DegwQzZRaVN9w!Ao<0C<2TmF}$ewwl4I? z2ho&Ufkk-*I0Bu8{v8PnyiFYKqkcjA1-^rcsH(!f1Yp9v@7$*pvhs2J9OPSI5j_cj zWQG*PrX~+F6C2Kx73NAr@}fovTBYr}W@IZc!>zBz9I3nLTotalh6jR)45i6DGNmeZ$qH z03qSd5x^k4XPm5mhN4!?_)}LlXjGyUdPQc@&+j)mQEOx56^D~A+41#-Zl+8xHoZc- z4rKYpmbV`(%L{(Fc5GCPlWuG|Z7dbQnJCtt223C3b9I zg=E!eUgPn{bi+=soZC+#nCKWEzOn2Q`bGQnX72`EZ5b@z^~M{$jsf@jYIMl}(Hj_! z*HTYt?D2X9!~YlN!P0e&ki1o2${N<*?wM%2bw5R*uWhaaUr9=&a_+wynQKA=UtY6| z)2dC00HE6cGq>GN6Mt5Z<8Ck#;((6C;=Et2#`s)Xc=H^l4LNL;qixWvKFHJZweNTa zeJ&DGvDvkEhwR+BFj!qi-H-94!wKR}Ul5+|htf9+gbqfU}(%^~2mo>qojb zp>=m~Y$2L8K!X%W0eXi$6U(`LuW~)2tUP;scsHiWw@;Rw-}mse4h@7kaz`!=oPMZE zElvdEtTk8?7kC#V!~Yn9pK8D%EWu%va&Z;9QY_ z6lMYnbI}kZ&Qdd%iwzxKPsP0Ajr($7<22xjR?{hKU0sQ8eZy z`jEZ+RbV|zbmsIgM?J;J=%}a&i;)Tp?38;piofJZK|YQD&F?%lNjXB}@jE_?H=jK@ zAobF=w>Z#lbs-Ah_rH#GC3KKn>YNDIz<>zZ+B13pSCx|&k{rP2q2gB3i%lDFPOuoV zA_D?*aCGuvkz9=tf?KIQIh0?+7)hhy4eRt`i3exfbdMyftXC4jnvo_1AQ;=tX*zbF z1B%S3Ds}ICdHl-8Hda5G;F?Y~!W!r_`Tn!p*L}9&ZY8}JC?(MMhC2BlOxLgu#6fCj zG2BgQD%T3NmXWmA&3d0d)U;L+G6qo;1^HQASK^r+`QhMwRRpnWaA9xOe75m% zMx1KJ4`aH~E0)8x0ciVnlqaeYb6fkUQEq1IByK3*JnH z-PKbya307Z(9tspmpqY}b2^9XRyg3WS&JT8knO~r`s><8pUtR$KN4 z#dU69m4dyXwWN`M&RXj;nDiaVpnDAzI&6Yy*UrXT5v%uB9yxeq>Y@t}UHNqTSK3WX zfAVRs7c!N^P#LOx+aFjr?emcLpITc%jm@MZ*U}#tO!TkL69OBlBE4D9Zwe(QYq>~fswbT8Gc+q*Oq^F}e%Iq4qFVs0 zUE}KitL?(S1RT&ts>_%;{oDQCxf%ItuzF0%HI{LkrDwrU82=!&on*_Wd8pyzu=NfC zj^Dt}&R@LTxiXwBGAIY~Dn39HjNWk38i16CGk?c!s6taTx!3vVu{$i!!E%1HG9ArG zYxwDrf)CNj*au!TUW6|3H{ZU`RUSj&AF%6m_z_6T4AxdGPT~VFglynLEi!atRfGR4 zy>juF!)IEMJK!H)_;sGLSE|Y~J@wiA8}n$l20f4g>Dgfqrdv*$wxUd53$vwrER{myN+FLI(S7#0(Ts5Ro40XwF@r3Ht-p za<@Y36QhCU?Ahw#y@*l&n!A{E9e0&+PHpZd7}?mfS{3^`jO^At9MC!=7|r=!hyb@F z(g7_hw(u#cCAX$_1!6X66gJ}1UW8ik*n#A+Wbjm&n#m^IG-TmdRU&BFaBBuhx(Rf^ zWqjIC)&FSIFt7KC(>Y&4S;DJQoI&_yTn4*)(P_C&Q8sphWUx=XbBv-{xS=n+U7E)ZNla+ms_wNZ7qB4^x>Gc2s^5#i7ya6jF|6NJ z>vs!a$R_+gmdD6(GXOI*MCWw;M8xZSgG4Ccg1Lml^)3Z*`;FJ2tN{BCV; z38OSmf(M{#V8+I{HH%YiPKQf)_qW(R8~5+#2rB*U=J;2W*>41>opp~o)6ndSWWas= zc(NPQOkklNuc$GBnmcq!fMfF1sKHq7Gb2F=jM{m?ikQc*4+kSjPUJ2(lc8k)gA<7~ z-1XQ6QJ zI=C*C9Md%Ond{X1wYPDs7il3?2=DJZi4Fj6pjRzli$zWS46t1FyuB{LJn3$0)Bkd> z#9~Mbk`}Lb`oU<}2K~2lLaL4v9eINGD?CL%?vLohIahUj*=qe}403YQ8#nhz?l{X* z{r|b7ejpLJm44Tn=)x)jxBh$2Se)~HZjHO)n#i?UKR=^2IZG@b7XGj3?BLCTzezbe z{EBu6-%5%Xw^{_?RVbCon7bljz=iP2|>YClkTG*b)}HDPZT{x#-mrUHKw%| zEOG#tl&KCkVr^4f#w4=ne*9STI;V0St}g!XNeEb7O`h+NZM8uP?85+htWJK6uwn2F zmv$(FqdfR;Xm^~QTE+L&g)9SA+r=F%9L952VF2_0ygya;mTC8wjBrG@oWVG zx+JEsn}zQl7X-ZDZJgyQomAbt#fHhsi$O|J>ugFb%r#&$>>=mybD5~2$Ht{5@_Oxd zMgjdw`6j((Z5e=?G}E7YEL(4*h5`Zc0krG1zV$Xrw`CnZ{OMB_tGTQdh$IPYu6lj& z`s+tPb-zW2O@S)XDEAQa@~Yo+OBpC{3UDUj_y-z(zmG)2WxWDRZm_!-)z%JofH;*2 z|Kb4ncbbr9I3+J6hIu8eQw{DK1kTdwxcOC-R_Nt>Mx1MNB`6KThxKaEd5`5fw$U0{6O?4a5Lqqo-}1Fbo}!Uaet-ee~5-pi1QjH_v!d8;eW)jzl^U)7J_5s4V(m!s`^$TR2=TtfP8_jEQA5*X$U zVTCh&>wfgWw`S;QIy-gibTUCVyJz6j7%~riCBD}7sqD7mZ=W!=;sYM&RAAss1<*%e zj8%v;B`Qa_(0YyPci+##J!dliv`NL|CBP@}a9>~I=Wca}mFWIYKVBR3V0gD60M z*(2YWAm8gfA+oK&Ck{pC&1S#f35wYnQJ-fbA*-9S--=>Wf5N5g%Lx?wi-j`VT z+;9f*UTmM6aa8VWsEYQ1Z;yKbrPUvNmn?>X(#+0J_hGXo?dt8fo)v_Mc^+`vrvOyd zmLP;}f6=1Lg|hgZTZ;cm15f-S@M4IaAZh&31@R+343%h6zE>$@0ke~tJ{FJGGBTWfz{z@-_owGB;Vwfj2z}XjsOf2*1cXPtYW;k2JT?x&_G8=(=w$m zh$!p6=iB?6$#g>H5>MD=UVHyDX~FKa^ZG@bO4Liw$RxASvc&CDYJr=voYOsRei?od z=+BF5hiwi(hED}iZB%XJk`7)I-R`Dc!ex$QqvziU|uHPHUOHz_nQ@^5pJ0US_!{WIer5O9BI zkIbm#MXoin5n*G%zyDHy&H5DI!jx}t`?L@Cs+bYonHO^zQHo ztGM3MqXE=fRWpHo(LE!9Kb9*B)qfODR2e|d`p+S0PU1QM4IVE`b#sR9sB&b|ETZT4 zf4eflVs%fkvI(9&QZk<~Eyw5HbCm2p{17|;rvcbz`nF(4%-P{-%jXH8HT>B=`kW-5X>fy+v{~u#t8J1<&tW8RHDIL z0@5wrA}Jv#(kWfi-Oaan;@$7wpU>X!ALZb`hrc+!knT}AY;*nP82?+XGQT8)blWo4C7CQNmA93Jj1+> zts^;tK?#u59lDjf%@=d7rBL zvTu2im6zPC4dOhf zJ1kvjF24ME?5`7CPxGY3kP54QTP;w@I28C=g(+dhjtfx*Rc`x;x-$hE9p+tTx>#`> zokA-}e|9~P z{^YckoLPZRsDp2bs4za>>a2;ElT8iKxy6|`M~gYj0s)B&hc;lKqC4h^tSLuq38LBpxMUS zMTFer4`v~xC1>dYQ;33Qw6MO=hjWDm-)yjw_Sn*0od?1k6Ey|KAAX5Fx_5^y_@K62 z-V;hl-hBhMf_wu7u)9`=o=FgNm_x`7(+hg^PQ9bO+ZT;~SIClajtTev-l3dW;q*i^9*Uv8_;`RZ_>7f+4lx^)j zV1P!A4qxLXNMd?Y-|J=ED^4^r9K?ite3eyj(o)|I=9>kF&fgl9XjW`6t_{1Mi*c8J zdlsVhO^$%2TbSQEtT)hY^QGjzk!`I+LQbcb{HteI;_Xi!)3MoW|RwfVm zbtk^D2fQQ-ZRf!6l!0!o9MyNb7#c3aYa%oB?R>X9n?zFR*R}$0;axrmP>BtwaihDx z4&zaj$8=cgHyK+Nsr#1D5O0+@_@dT=;z&7N_8YkmOHRt|-6{9OkD%c`Ny~xSmNF0x z@q6-0MiaDu2ntOnETY|EJ=o9G=b$nPG1YL{9>?MFIOV4@3CJuSQT$}oBRoM4G^|~bd@Zzm1!$+ML@GLU0_#bJ-BYFI$N;qVm;5X)Q zyIMW@DRZag?m5M4t)Wgv*UGN$`B&-2kTKNJ=1ATPeda;;{kbz!r72Kqm+nU%sDVl} z6Ffw)!TxsLvex_KCY9;PQwKnCV0(w|!+djd!1?*QbgQoM2Qao6vm&A}SD+2;p^3PJ zULJ-`5SZS@KB%ty$Ly0JKDRI&aH z;dB#4dpkT>h+BVIOa_tP!-LXK3wJlKxBPNEvzCBZFcGU94D@CNes#wpSIKl>R$aS*#jm$@ zAY=JlfYVA1>(D1AKKf8FRt(c?Um}WS6zkrUM93#zD1p;L89EL*WaHvXpp-_tp2BIJ z8KagrXipW4oJ0X7e&QKtCHwU(nOBGzGhvXk_WChDrS)U)w1(w^ zqO*YIM$#pHiknljrtEL8s0v$Z+Os6_%iLbxOA3RUXT7UY!!C{2(?xevNFBZO5%i%A zw2OHj$WeC29z0`dt7(lD{kd2zjb$)|tx1Nrwqt(y(X|mE@@z-?4f-2#YLXhHvw=au zxz0c%oN3p@W;3YJV)b5TR?JQOc5^qrXxf$bD_y18`+g5@I9r-XFGaT-lEXB!^%hN# z36t=1Bfk0hI|Kq!Lx^x_We_g!q<{rCutjKl4n z)<%PxN-U>nfbC##Mdkm4!mHchc>duI(bJXi%})lQd)_;38WN-HORoHlbo6_gQR_Li zz4Xq@p@o%SNcVcENWs3AY~KF%S@B{&tCGMl)Gmg@Lnk~{YHG@tv0Q~4?Da@U(f7_Q8Kgx=|FFnhTs_EhLXeNvtuzeyr>Ma~rP&~509 z`A%6WEI%}Pd{OaVtQ%PLS0x?)IS-Zq{uQ8 zkcLegAQq2%n;C9%s27^K*VRcb9yk~Pd(@`oM^LruJ*u_>v7fOanr~O4)9iu=N~9?m zF~^?jLgmRxQ3~9p@-WjL!EX3ZRh$w6zKvpR-kzjOY%k}M-BTYd$6^3;$Xcwo?1lrSkb9DaMn$OO_Fkf<-_; z`I>5g{WR*0LFCf(QvpO)iDQWUoZ{l%l~yq7=P5Rhb&QU#yx5DDPzLP=)ib@Yp{PJO z*Ti`^2PUbfi3@MX_l+-+gOrd@BiJI~1=$BOnibStDdYW}*#fG*J8cO7BqI!k^X}FA zCA-0ydQT8ov{Wp4_w9(~o@yYiUgCMVG{vYv``&CUgoOBaM6wlOwe0KTO)&H2#gY!l zM@}b*0b=`+n9ZV7D;i_KYSYT{TiLE(p@k$8dSuV@pX`+B^wYE#o8dnf{D$H{t58<5 zT`m37?XsfRTaWv#FUEWCY=ix++!#Y>A)~fb(@kV77kv$sxHuaufTF9fV@bpFwL} z`sSHBlhLs~8J^aseVRnZ#6(OinOGtY`p}x@6m5No8k`%aASGk@kfWzS4L7m|m{EASS1^L=V!yAq^th!D4jN5b@E`bW z?!*hnrAQ>lB6t+rMA2_~SG5Jn7%^pmA9i?CcHqt@2hi1})&6rD-NNTntcJH@JPzHr zCZu)-WbvvuMRrmI=IVulBv%Q#zM>idFO~zitKNYO2995G+V68wOYrZbP~0&nc|i5e zlAxB-0^T`C3<>0sgdMS!-k7}j^i#E zww2U!4ZlqVNP1h;L0SWDc+#pVl^wR89`Eaj;K^-OWujPOaU?w#*Ani!YHWe-1{*7nl#1OLtIQX#GYr1W z;JXv2Do{H3CCSDDf55Ll#ZES|+X>LLyTfoTm0&ei*Vd>-pNH!X7b|hnXpX*OXaE{7 zwrO)O^Vwxy&M#2KDsJ~#uBhI)Kt?10QdiY#KxHQ{_}~LirZVWo^dlVYJFf|`G;TV# z72j7jJyg}VbNi_y3&GGfi zA)#6ERi}DhhL;p)AXV-7p?;>?u_f7t61A#rl;T}!+?xU_XgC+Y+lbYQJ{-N>qdsC#+8{uGqVNvWOX15#(ny}? zu`=Td1lQ(lV>ttU2WZ;3ysMcord=UM@g)ozf1<{yMc*LbAB1K!U-U1XkP2qaPN50Xe<@0)QdKYuD{c@t*>xi_gT$HW{Iqe@M{fKASnc+b58 z3M#6~z}`jVt~aKuyEVrpQrNXJN5Pr05*? zb~dc6S^=F3S&btJm&Nx6@U-ui8OeTG(6v-wc}DtP(mGe5=a#G`+#Y$I%-FbjUiNi7(k!udomz2JLP!9Ma5P486)xEE2 z@GB=O^~Ki<61R*cPb<6u|Ktn7nRDsao6ULrR?a+;cz0*{IfQ{l4Np zo`#nUYX7QGzd_G8b8|OW3@cttmfutv_p%=8kXt}~!=m}oC;y}P7EK_BLOKA!BS62w zx_POC!5>GH-xSsX0gL#Zd}qIFrR9Yfc7n@2(K%B=@MV zHJ}S$E(TX&t*l;t4x2|8XtUBW8H=&4T~~|}43iGUC^9t7Z5xC*g3knrX~7`9+^IJV z0>HBJOX~xkRe)7H!6miw*vXUb8|={HWO*p5-ep^u5}W;kTwNHn#(~rghQKEZ)~{*V z>KC9L*YymcF55XYTEtnht*1Xl2s=FS_dRvEv7(UQY&E^B zPatjv&6`1g-h6SR#8l8wq5pdgF9?fdYd4y9YRR2*;4#hl0Z%pZIgUjg<7Tf~G1$$Bu_}ccWHxEyoP&l`z(6!=QmBEQ*08Az~cGUUs*-&;=RLq0= zIYPjZGu&aa5Q!$dSy$y_Ov(zn`3s=2nG6gkc#C=kTrR0}%zxkyh-L(ZN~hM;n;Zk- zw7qa|NEissyk$&;rkes>5gTxoIa&l#xhF>QpR7r2wu+C|G zpxDDfG~dYp;nHg1&1(v>;I*gE(YoRFjOT*UbA0aGOg|UialxrEFVgH#4?MDW72B(~ zg7GJp35|nk5Q?z`?2E(80X?|d>%@Awr}?@yEc(co#2ZZ#E%S}$gt6Lg)^V`x7itu} zja7MpCMeWJp`nxW$BF|q%kj|Pp|GV1G2#3Di-~Sw&j~TY;jbZc#1Oyk zWJPt9$#vR%C0R7u?Jn}~klX`f&BzX2E&@9DGvk=hQ95DYL14m)QjmyxIfevcHp2~% zIOz2H_^tW>=(`#wIJ^m=G^LCB8i|6wrcO9BWlm$toyiZuO}t>N`r>|bLtTS|&g5$f zE2~4U`d%m+1uS&6O8V5s>T9V(Q!O!;btgQOXe_MPVqN1^Rq!Lj(`ljwl2+CamkurF z*$j8*#`0*BvM6YKl&G7}dNF1|GZXe^s+K3i(0rKE!+fkBwc@L~p5oHUU#uVnI(olu zQ=m=r0rH(~0Q*O+dOE+@!+EUsJ4B!I?z_N{C~mujM)Nl%<2LzB<9lRYS)EeQJj_&K zQ|~CXHPkLJhKvW&$UqaOj%XCX9zp@E^x^VVRm)ALY3#>NPcTp|MP%P#TjwbpzT;n! z(%XyXYCcal{`q$=fX6286rL@;`pvj$v=mB!0TE^wj0j1Nfe@XotD6DqIQkaD>^3m$&mSE-tv?^6~MkMs-xrpSGGiI66X|=Ee@C$i>C+`nKJ} z^OlidGR7Th7075Rxb=9lV=6nQEqS9!XI&D84G~Gnyd-}i(-UD5eLESS75RyNfrn6Q}$dabEP6_hI-e+Hxpw8za#9Php}9&QZP{zxl+ zMkhK0*gAaPkv^Hz9dWA5vgYeH+sf)FD z5MB?z?7kamF7!6S-?myu2)`L_dsxlRukr6bNP|Xb*H+*0ZQwBB_l`@v)n{n`_!($zG&3{iX5` z;}}|I{ul~zMg7-Mh(0}-ns+>Lt@Jq~HHeT04`v5~9eFe}Jbvw<+%rEeE-q+TR&=vf zDg`x1_8=fTAiyUgf~Y!xMjj@SoLoF&ly5Zh6jj9WPKm z=3%GOck}*^Co-$}RhA1sy8qs22NaWiF}+_0q_e<@mX!#k{~9(veoSMk3x7>c(zasd z;YdrU*&s>&nKs_Z+vh*9gqQr>_14EL@Dt@;e_q5@la9~gA5vS)`o#Ml5t07zf$fyn z?z;9&q;4-@8P}8<{tNuwcCQj=j&6f!TXo=`Sk-wx_g%|W?7N^P$O2>GE*^(YFW7eM zJU-H@9jw<#!~8j|oYWD|=39jy1g)$0xb>oPGV4hQb{VpDQ_dWG#gwjZ61g*4R5hCW zNbj77%q`VutqPvtI*shkDhBdSzNwnWl;HA~s+Xk>=VRPF28Zxi2sQ6LAO?Eao?TQ91wx=v2}`ziraK1T|BzxkBC zxZ#|g$Zje|S*&C%tSsg3Uo={$Cys`l&NR_p-Ylhb#90crAu)tJOc~#OYNTfp+T0>r zBA+da#_K+%C_}Dwof6TiHG&qvl{<~)BGOijZPP7w(p+{`u%bk|^I%xky@j2{g?o5*l2c%{aooH+ zSCdlc!y1{T{m#Ij5lhHDFs;GTx{AQh#dFtWqiGB;9CnUy#_UqaB{&%GyWJ}f-MMOt z<}8N>=qE@GKO1l2&KaW$H9NXbvx>s_kT=zh=ojT4EKzlNeUp4N1q1QybQ&{Eo@$-N88x4;A`&Q$@@g6JV1q>f!V5$E`D0uqY*c&K;1t7y06!Gm;cE!U z8w_{^x|d18zGW{e=9)xOf_?4RbRip{MJx5nuP*dQ6KZtPr)}Y_gY_C}K8`*&-|O(t zlN^dHU6#9_J!;)W>^7OiR`z8(!%rBltZ-I-oOL-RKIQ&+HyyN{Ho8#b<5 zBC~jbOj0%`r5Cb~-L&Nhy}B*MTi3nehEk1c?AA*oXx7NP%pY0ple?;O^|LQ$s$mar z%)M^GKXXqg8?X#~1#5+h>e0^4IA{0P&i8Is7E{Yo-&D?5l#em+gzssoG+1YUB7mAL z5VsQ5WP;kd12HJ_krI|GMl&;7vGK{J@jO8`R!sH(%K= zRgF5NC{^|CUiD+J;@OO$Xks~D6TG(0e4BHM?ffVCqE{cV0x7YXrf3mMl;cG^>68uZ z{i-HXN-t9mJY9`w=vG2_zNN$JE&2JCKrtv5hE#3)Xq=&c$EI2LB?OD!&) zlD7%bKo@D1sHgqpgboXH9jVV$mBBoXij%^uQ?R2{XtP-4{gQea?Sf_bqEI0)ZS7YE zPMph`@^hQny7bK2%~F1^7UgC*PJ{|FK||5)*J)LEo~14X1}No=hZIS)1o?R``#LZv zo1ybhoD}V${!57el4o?-P3TuN`Gxsg72C9_<%cok5IEeT=M!gE4sqI&(TqMoy8cWj8vZ%gnEU7i#vlk z@Ud{$mg8~4o>DU;ezu&5*=2g!1p6q65R4?cnAoJZ-o$*UYk3&Gd6?k7zJ!3BMGCr3 zF1ScT{>AO#dtv=K{W30VBc(e^zpYTFGh%Jmfd0nSc7eN{pEnZ=3cXJRQs0$1ZPJv# zeKpFsf2+H-|8{P)yM&z{#pV6xh)qD1kz!qe{0i^CG{oP^LYNpAn4Zxka(iKEIFm;` zva9{r&8FU5DW#<)V%te&uLH7y?V!co%4y#p>js;KZuiGrjfnsqh%Ocd!V~tb7zp*p z5>o>5Rd4r1p>y01ev7ffoT_vYmiCBpjaHXETb~#hu!qGr?Aatdev6X2Bp-gN`RzR! z&6h?&L}8@&8mb@=Uh4CQ1|F`{;gUt^27*e7a!eX$=|CXb*eIL%)@XEMW~Sk+|F@!Y z3VYHl)RypAB5d@q4oIwWMaEcoQhU-^Vq{x%a8J zVGDmYwQF|vrH=m#a&oQr8t$_7ss%Tv7X7Yw2BmD3nzh>Id}CZ_0cu2A0H$bmg)T~l z-m?y}XPAjV?;gm0`EtK@aPZO^uL#V)K}UL1eUay~r-+C|F9~KnbuOOmj~2*I(Na>X zCfRaZC?|YWucT>6diPF(fNK{EYL~cllnb8~8%RVBf+Gi%KO`kOPxi7fozUYR3q`SI zVn8+Eyg$4z9WS;Jm}zj}X4J0<$)uv%nybqqG5(;dc zp8%5F{fhJUWGm$Xxo7xD(<+aV_V&vEFjf4?q-rHhf}5M-hTc7MR@R?gtfSs8>aQqXOvUHH|y z4_18;U6S?+8yg)!F;0<@pbE$tFtlU+z++%Mm>A_!A3;<*AT; z=OP`>@`}J))wZgZxi}EW&S}+eyyz8uc(kzgkPWlK(P>GSSNGo3TjdC2e+kpSTVL?l zCiog*eX$!tf9M%}sh5A5<-af8AI5qX0j$KO!9B-hmnRNS0%OP^83GFK$BL*4*a!(> zK6~Lp4QhI_dxX}LYig$xk2(cGh5zv_f17&<|CZNa>sVS)aZX=O!Wh!?>jA*(i~dW5?C31#3_ z-)P1mK*DQ~F75A2IOC7!`;qz{qa9LgK*4T)FOcGWdki(-jAbkm`RRA}?+-<0F9+#0 zQOIEn-fUFl+26AA?_fyr6}YF{n(IBuRja~!gsS`rx!{|z-4)^g=ga*2U-|j{eB3Sa zm;cFj62Tp>6tARW!-A{Q7byDwY1sd^FFzi&E#@;QxI`z#C%;?5_@gJ_k?dYw(BTK+ zh=_vHJNu)B=d&A?COEFX8d9L_e{*sEC0~)ShpOqAME}9(Q$9+aB+gJi4UimhVr2_6`){SnD%*!w@_>wjBt?c=tIg>*nt5+#cKZV6{F z;DJmNJ!@K)Y?$AR&_4<&&^`<`TkJt2{-t=GFYS8w$*+2eyv%%#mW=7xqIhn1bejv5WV~`yX#n0&1 z;mPo@&-ria=Fi^>GXYI5j6n3y`vLas`ce27u^D-@!8!=-hlhbi`rXm|`CC8T zM_p&(_J1sw6pv?Vg~*=>ZoEh$@|*$e1DFYLit@jS^HSL3Lhw0(p4!Sr{)Z>Gk@{d! zKY2(30OM_0XFde}q-tYCWbuDJyO>9zFHrjD`wdPx`|%XUxy2(yt zMjA~0u~_2&W3gbXykFOC`hkb-J49Zlkp1S6{v~2A;7lP4mPP(i4gIl9Kb`G^9zR0c)RfwsTBQGXvMp&EpKJuS-rqAb83p24m`!RfS8VDMxMa%$C?0WCCr zvYBo7ziJ`LM+7liO!hBK`J2N~)PK~>>AB)?j}Jqm;(tc|oj&~gFRQE`%@qpTKWnx> z77K*1jaJJ^_;H!G!wt3%;fQ!H>>h!8v#4VmS$EcTgs_h*f9{{={BiV<6}o#2489vn zA$e64dSP5(ZE zpkBXG_Ew{1XHQTo%dl|tRvE_uZ(RWa!RC9j?a<8#OANaPWscTek4vIkSZD)buh1zm{1kYEl46j1Hcml|&*eR%<2q zzL0PelRgFY&TJ;sLga+MGE?mFH z`VI$NS^WXcg!cgkA-&jryECZDA36F8a=Y791OC3y)=7@vg|JX5bbk~Jy zjm0E9amkr7u_Qu@*4z~Afw)XJGS#Sv(SKNvXhs=@EZy)9NbQbXdH6L4e_j;JQP6Iz z01CDyAinL&`bh?l2XCy9^SyTql!C&kXTl8j=DwvL*k@?WH^0%h1}T3i7~KCv!IWx` zqY+M5fhOvLzQfxtBYqCxaGo|+iz7h6S?5G>J!+jbM*lCrWJddOWcmfM|3+i~^Lzc~ z9}%2|)c>X2NqEr36>@Tx-O@|i8l@S@?K_@RsnP=lpgL$x@N!AGL9F;u(UIyS!8A3G z)ZkgZ4eU`G;i&Us5t3YjC=!136aJ<4{`#>Qw?o}VrYrD&vzWr(kWC$sc$M%lsKu{r zJR;!|QR)4=)3j@G%1BNG`Eb>Lm%4hUF$X{E--g;*(G-1pBybRMB-cEk^r+4H{T&S6n z$2CW|tzKlIEMCQED%QqU=g z@W~GaXN{~gbLEJ_B1NQR2)btyFT_sRigJ~oU-m6@f0yIz?Je=c;++4_bzxy#<=%wZT{Tt6Nemz5lyKQW$+te>HXN|_2 zeOj}e)|9mod|!*Zho-w zERvR`FYEL9zPVz<+kU;Y>mG(cFWNx);mpe;g7Mq|B<1AYjjh#<&925GMG1*Q>n4|V zU}K8S7MWTWXH2fQyqnQRQb^>dhPZgtYX_tg7IUA7#sx*nk_Fyunu#s8yu^}tmD^4I z>MRm@&}y<=LSHUj%o?h74g<#_Fqv8JC5c>SN951xa_y%0tQ~4=R>A8F#Z{ewBg4BA z#U{dm>obC$3i%BFNH7PaR6~+Zr+ou0Z>;ztct#kj{@LaSa4v z{u$4L+j-UDPxIH^{0aq!h|3^mP3~lB8nP9gQz#7-8cVozz0BkjRHx11ph7Rs7{oj7 zRKeyL-4=9l?;AOe;`K0~tI-UNzGf2})0~TZ5ZO~Uks0M?7b-9k)O%v$_{{qFb{gX# zqWM{0Qf*7TG2H7)pXIL+D94qO^SSDwEjDqL896TLJ1(~p6}A;Kjf!$bh53jgSo#rs za8FC8tE~v|F9vBG9Ua-!#DBaB^A!aI#+Df6B|pZmp^F+lK{KmepV#62p<~zt=}_wj z(vLCkb@V6X;J0ZNg>OrNT zph($Elwm%yNxJ#GJg2+psOjfCzk?lka+gI5i$}30U&v;LE%HIIDpLqB5(hI|Wm1^* zC|q6DKdQCn9v%z3QU{1f-WMJe-4~u@x3%f5#6S;fm3E~t;RsU(-nqBVj$CZ5mwVpo z@Kx_#44e*S`qtN4znffe@SFFOuM_fF!>-?%i{R&v+@529L7Uo8wkU2mLH}Cn{kr(@ zL3>4RL8s7l@5ce8l441W4C~4J3EB9>#_*+5pVfxia zcv~vBZ?Jx$9XzD$FSJS8?%O^pVt`=>@GyjuCB1DXy>WyhoVe-6eHoCHF<= zkfll?Zs&CE>h;)je3#9-_YzBfeSW^b0sl+(P$37HMEjt2#SkE5bUb+wnnGnh+*{)A zYnGZ6Rk3#X^1yiIpZVMvs&)o?Hd{p1uy?yGV^Q9QkL#ROx6SKiwMUVHe2Gd4X3Jn3 zu#5p!)4B@8^;V&no`MC&zrOFCZYl*R<3xTJec7)ZiR*k-f3i`D(>CV&b*NSrjIw`O zuFZ`e2wS&Ph`6ZFtW~GSM?dQ%>{mHB=MP$fK1DHl%FFR#ZcfK(rp5OGV-I5;X1Y^) zLxRWu-_U_DD#G7BA8F{ViCrEE52^N6yWF{uI^RWkVP3Bdo@%+%LyK}qhyMp6X)7o= zwTkOx(bB|KBGdSLiV$z|it!HiV6iD%#;ucYl}F)o44<-9BjbPGqj{H3o^AC$J2);h zy}g8EeBU0DwILzOXHQD`BFUqUh{2_+UCcqN#Un-`CFJ56wL*Yds#OCcm8&5pKVOAj zOfI*c&nr3E2Yjy2LT5yCFL`m)6E2NLRd`*bTzfEa`B|LL%~}{;z5pXo1dPFvRY7;0 zl6T3&u-+(b#tZjMs+gOoEGufL0GZ)U?vT;EPrV$gv+gtEycXJZ-vS7f-@P`!dRJd} zWp^bc^fTzPsolG>mZ0lavwbA(R|s<+Rm;Q2r+sA3{F>%B^*INJ)dp8Tj(Xkol|@=J z*rH%jr;WVw?@K@#?h)%`yDhVGKC2z-M$bA7>R}`{=A*FzQ=Zm1HHQ1M_l?6~qc=&; z1qGYWf8eQCj~p~MZAhYK6(~^%abK`SZckmD6&_fsFHgnrI)+prHh-vs_J{rei|`u@ zPAKyjC+Q0Q+frQV8hq_Ph|?w;fLYgfxfhqL$m~C0_$c&1UwJyNDLjx!!0&S#q*gXv zs8KH&JGpkCA2rnk2BzCgSEXcah;?E>!%dT01DHm^^;3VJh1)(myULFg>4`zsXbyHa z00N}Cz3Y_LGj&vvBVChrFv{nXN=|1*)Oyl0OJaq{xs%kJuAd#x&DnkvWE58*E zRZOQzVpT$Jjp)Ji;-E9;d2sjJ21lR9Noe<Zv70ptnHYJVz8t!F=78CFYE8PNmm=w=L3=ZtMB(bJE7Qem-z#S$s0n0!wFx2b(I!_f`$eBks!_9>dyyM2iJnM$1%< zuMLi2GQ0pr){c%Hj8^5X?zw!rlg0=Q2Oy@%OZ}}OyNuLS=}P1er2ArD*dJthIxl&& zKs=#pwkvC3``w1GKWF;>T#+5Kn4<>_sy*WKf8Wo)ESx}p+CEy5Q~e8BfW)|dk_HZ`liDc zAKpD$Sk$WdC)3kOv{+IiB0ilLhp^5S@-i|QffCaf$NyajiUI7X>WWW*f}0jwNFPz( zwEck+;pwD5&9wLI3J33=x?GHyd zs+AS)O-K6=g=5K#a5&8_Ytza`%)-+S2YL)((zrx*8<2}C)%%~852HuD*H>%0KVzFV^(QEUyVzdS1qp18t;FFn)2$V^JqON5WE zISjcA^loCtu$INEoPeMBu7mnFLHHO-aJ34KME@1VLJv-&dqvsewYl_*JuxC1GW}M{ z9Dt7e{Ly+V*P7O$uF%-6&fsF}ovSOHJVcy;;LlSuWMr(f*DsX(%hwhG^T&tY6M@hG z>m208dfTVWh~6wpvTs1cDMVTZ;NYUSA{ zMC3?18r4Rnnvlpdd*mqp7IX5S!+i|koW(rV1#yeU&N5idF!+(Ffo&igi$xg zT6O!GV44o11yxpILBa58(*st6#7n2zrk9}|kZ*g+O6@F`I`(~W<0|bYpSw>6lK5Wg zx=CkmMIJuv-`YFUkru+C?28EsYED(*bX!gB_U`mi?h4o&%zmBcxGT+an@Xw3kZto3 zT)CR(W97&l;?X!&fmPyoqoVIK@V#Kie0k2~R!8Wlr%qa> z(NI2<5s$~9^k|t|l{Fq~mBM)n7r0oRS!l9qL(6=wRi5SA_2EAE4)IRffv+U-NdgCF z4JE{5$?LKWQ5I?sm?cIQ8=ckXtGVp`?i%xKTUmV2XdNzxpDmk!-lEu9|Bg-njtU5y zK$hc8(=~p;O9SBqy{F>*(2t_li#v=;7#1F$=e*;>cMfeb=IMz-_>?7^!rcvfz4euB ze|8dmJRBxg!eee$R`n1h zrT!}n5{mg}P!EKBD58wMbsTe)J&0vgROkt%FzE`EB!njHf{;;MYkV3f0}3IJgXIex zhD~c#^_eNu8=ZKQ=(OJf_K^mgFYH~lTm4``LI0Dnxy8aQ7~(2K(EZPeapegS z7>61|#4n=GY}Sq#I$p&%^Tx*0!+rQYcc@bBq`cgHlZ>=N0kI97tS**NJv4{L*E-r- zi{5o#r)~4BsJlg)?YUOQp?64xRTGzo=^x*N|N5MjK(?QnjK=gR>uulAV#?zFad0We zeUkL84%4#KD#etq=XYZV=}3a9Lgfb=KF`-~ko>2^w4)C)^D`iz?_qih`~L@W{BNIU zKO|EK33#3lMRfS@Ml1eFSh5E6IR8KJ;&xw7j!B7lPdqsYAwZ()>Juh6stW0$G3YUMl(T>lMj7|QqyLqzE&*Pp?~SQiSwO&+~{F87#gvrw>{sCjOmZLZ|X>*f|dp$q5| zP~s921U9{oC#Q4HeaB`-d+=~;BBW@)MS8@)z3uGbec9ZyJApt^Y*mT6;vRAsK-n?@ zq1Ry9FG{CX2QSq4+|b(O&fy>`S7Dw5YxUQR>D>$8bxd!N7XM6gqF523kU3*hgRq&m z_|z1ADf{i~RIG+T(F?B6{?KE-neX-#kZ0tpiJnc<-Hnogi`IV>zhAmuGy^<$J4-lJ zxy)<939I}oJ9E)nEc|hA4md5GG`6H&S^M_-aS`LE)yytoG&xn1d2G2YGaVN`L4wT{J_uJurd(4r2dX zlDZ=v<}qo}LQ%cymxpYWfj>}UOfW#V5)aVcaXmXx!Hvt%&2pXZrV`ykx3uAe$ z-AtZx?QF7QTDTYW+kt&26mAGY!s`6|yqkBEn5ik1{MDf+Z{5Lrooano3PM@>DKr7slMXYGr%PRl(Evn3F4zp>ot zRYH%*E*eCQLB86wT)2prS#h8ysmFAZa6vrfU@p7jEdBKcuYAt|+;q^8JKyAJcyZZ8 zZhFvowqCT}bF_=j70z@8!fa>Bi>G@MhU(ngT9q$25z3`U^Uc&=&~gUn?k^SwH%~@l zJw$UI>iefh(mP{aPkeGjsV1^GE^{J6G6-`gl*P-d9Qhu|mv8;^73b_bTXam7keLZf zF#sXHmD*!CINF6A3T6nH8OdM@U{PJwtX3L73vt;|ygV}U`Ebq1jpk#K78 z@P&f%iFlWzcKW;z?z5H=vK(#KEi>nXoo|k&_|9`ae)-kI^a0N&#MDkxI<@n_Oo7M} z78UfocnrKe+dBuSiI!2`eVMK)@@E!;^Nw21N7eoW5cIphGc{djmywgp1;}NjT$|M~ z<@-7~PSUe-ohH&FK0~~evWZL0MUB+jw`we4z)AJD>kK~bf~6(QmP$M(v5_|K<3*tp z8AOBz_71=@aw8QL?R?Gae*ErRGYJg`BW~-Vs64$QXM3&YgY}VmxR9r3_7ihJ1!`l3th>l zBd=~)QXb1Lb&%|AB)GCN4^Eb!2}kOw9X*M9ZV4ind%X3zaa9r6=xamNOIGS;%-{63 zS$?eDXwR;L0%%1=dx(S1*5-??Na?XKSb#Ty@3IVu4xF zfD3%KO)_Xli3b=#t6h9KMx8T9O4|`y{?HRv!6P6NxXi5nQ=rI;C;I&0MXr;l-a7p1 z$?QR=!qc2zZdr`-yVE}J7kuqP3R28G=QFvYYs(XKgJHMQbRwoHPh82uoDxNF z!a9<3kgDd@CY#@Fs~7SEvIWqQ>OS@-kc_f(|$Xh%sq%sw!*8*PTfb*vNTKmaPKwY6%o_Qti6bP&a7r9 z?5#hH;;7MJhqU*?SEm83p-+Y09?b65i-qkGgNuc$x;>JIgEOjy ztg!#N){J6v>C2d-_!D;^+$!Z-{`lOck(Cw!(8#uhNx(>T-0u>+sNs94fNKC3Av54G<01U#>gSV6DYEf_fw^s;0hu|AzPsrB~$!ms_xf5g4ZE zwKREJAXN5m$$@mf*88FnzGuF3@w!18dfGhMWcN;Fl$NTS-W~dl%VjK;*Qfedx>A$* zp{D2=J^ZK7#!?j8k<+DhM7P4}Vj92PDb^{I_m5y$PSH>qJ+C%6=AE;8z}wrer5W_J zh8wVM((6?hntm4Z67z726E}UCY#iL;9y@m2AfLN;f?_hwkD&peI9O#lbzXs~uNOCI zXD<`*aFszgv`Ub;zDmm5CDGoe8Jk-6R@XBeJo5bphCW72&0R z9oJ!c%gCelHTrA!FQV|i>qc!i06{prTHly$P1U9a+gt5sX3Y1T8&9%VW27#PyY61&YNy^bzb2p3cq~sQq9O4Rp%GO@D>0wZbcaHX zIod@%N2!^g&MetDGxB5%J{F>2xTAm2(ezo8UcKI^5f%DIhbbjklGDIyW`1l5KkVEC zLl03{vCLB1T>GALyK1=PcH_LzyP+mJu#|ZMLVi#^vk+2bC>&IOM6D#e+*whba54AF z;z>pODA(PcA*%7TgAhBC{vYAdKRza>Kr|T0wEQJN%)SQ~l$o0J?s)A?rTLTgX8oB89IGxJ*S54`|Wp@2=b6Aq+FHQg%ZAEsp1&>n% z0lS-kETPuOYgv)EWGv*1QjRCn@1376!{+z~2w-R4ofCE(aYPPgTl%{CXg)fQcYWUKb~G>-rr(w0fRAnSHFZe8?r=Y`xiNs%TZ;vTOZ_3i0$FF*` zMb&!GBX+#68fDlLikp3b4{P&x+&(Fg&4w@-NT%w4W82~0?7=Rx*kW>X6&=kwRi@9a z@fg5?(2)#r=(}}v6|4Pmjr*n30Ma~Ud{T2N;XI&517})_-K?p% z*UKAal!hwPnZA@QpzF^t{3q!{iU^3!0Bjzzi?*JIf{oX2iOybGlaJP@G@pLLX1xdT zhgr_Azjj?!f#GtF6$`*aVaVRJKjuymXo>V0Y9@2M+c)k_GohT1?FZzJ{;tNbJq8}j zPU`u5TywuAiI#tp`$6LFW_hLhS`Y@#^;b9>yxxA}gx}>k)2bY{UUScmpFf0IB5}yE zBj5WQoP${UzmO^pcou6Z+f%(aUmL5IVR^s6 zJ}zp4+~ouRuj~-4bu~yAf3Vj&#tRU5j%RO(2_&C9X-m2W5jkadnxx>x@Omx zLcsm=N&b$+nm#^2>%qS?Qe%jK04u7P@H`Dt=t+<6FlWp$M$OT9F1C)%ZFJ*7H{+Mm zXB{V9c}4rj>G)B$iWUA)X3u|grlS~5Lz#~gah7@ZSE!{5$LzLn-#&Dbt zSDs0wKkUBe7u`=eobBKS#GFS+RKEqAgQ-N^)=w!t?p{MfA{^j0((nI*&xLN2c5wPLmV=Obr{Zw3 z%Rc-COzz6acLIPN;(z}9DO+c4WW;exN+S|J`=P=Jt=zP5(tYOPuvoi9Tux$ODivdu zj-sMHka95hv*?UNg2YaI0@7EEwQ^hbDu+|D-6`vD=kG+KUn{weWD#;4xcA~J)>wsE zRZk{)z*(of;Gb#Ro=D2>jx>HgM#e6->908W%@z6SfIEk0!ro-_UI!G3**}>dAnE_` zD~<&8j$`Zkp1dbjWTC`59U+#wCa27fke!c~EQ#1LcU<049xRcR)0aIFOuv9+DIjO# z>ANG&Q)dsLlB4Q1W&3=B$!52J$;}c;A;TPer+!c3iqNS(=V|NWVKP^zyYqx0Y1ibH zl$}zMO)uR?YC;MESkKWc$%Mf=mR-Xb7KZkVgqxrFrDgZbL}H;&qNbKXoU`e)~j2bQ~@ zvBua~>I->Ob-^sI=Ms+tHr@n+oReY`1QPxL7YLjcFL%7=r7uNv?B%rCDQ;NrsdH4* zp$a>m0C-;Uj3@yQOxIYu_%u!mp1%VaXF}h|{B`~XkhZD+ia@FFFPrHHL|BI|^sJ}9 zhXI}A#vxD+~eR{ibma%xR-=xCc941-EJ=t6d z|7Z!k2 z2pSZ>6klnyrDvW_h6CSuhJ1ZOhw!<4P-)Ef#8L|cZV8*#O(wB4%bQ&6P4oY-LDQsHnP^s*?#$mfIAt-F&g>Jv zEH|<~hdmx2$cs^;H?emQ)^vw6ZRGTfWhxZ!>+4&}88Wio+zHg~hziLye6GHmAM3(R#d{eN=;?l&~a_Uq9}Sl>9=q2RxD5^Eqs-U7EIci+0r zlzQOMduhz2@-}Ir>uN{unX|%>k;Q!$p`gx>=p4G?+pWYhO);%j=~{Z7%zdNqP2OewYDdbBVS%c%_{-B<`Fxc zkdJDUhip4n1QdLWNCY`E+>rUf5w3EXW4)EZK{h69az%Y}Z~jlIZuJ7Bibwc*_|hKU zm5Afj1k-XJw>zB#XGZkn8lp!%$oOA_B-2*AD?d9@i$cS5aQaK%-^C`d5pvjx8IC>O zVdidh4jeqBP%M z`1EvVG8SZsonG$F7vgEQAz?kqOCaP_KHP^`wbA`qK#$mqM{6*W#WtF!6cbgbRvS0* z#q-a6pM`=O>}W=&?urRH{=y!}{FS~7g@Qk+Z6o`j%hjFC3#)eG|Km6JGhAHg%CmF^`=Wu6>%8ZUNpzxqg# zCN%T&gnHS*#OS^)=aWUF*Q90(_2+^EZteu>8JFaakfAZzlL(*Y`dx=+lI~VkD@61p zt?n%+=TExh({JRDqi%Vc(>;D1|D+z*+xhAXnB~LPY{UJ$^`B^W9#7{CAn3!Ml)Bt{ zQ^}4Q<#r!US^`-0UO!uZ>DU417*jrH07klq4XCTuXzl{L`0-J zOZ3q1?TaR=E72FGu@$2(>6sNOSd;CE>g>quGXGsqn&phdAhpzdt7r}nhq|aMcyFKH z1&*iqb~?drS6XvVi^j~YJoW4%dYVOME0>dNCsEDOI(bnW?%7P@tppC{-4}9#opoip zCcV`m>ywV9QE8?jR8~#co%NY;?J1m6{dM`vQE7ZFCoy{g!7Od#1)dMWiN}44)L( zu;=Y^t_KZs<48z}DCgLO7%AshG(2OCbmFZ0;Rtfca~G@Zlh?vAK)iEBeb{)oE!(F1 z0TEwnT79eWS(3qI_bET19=hJ8Z|<7S@|WICG)CAYT`TOJeN5z>#@j86fd!WeT9A~P z%$mGW2`&V>0=K)Dw##e&@k< zSYbNfAPK7Sme;QkZr7u^UGw`?BZGrqkAT`smr2k%!&42a;0VNR5)u-Opv^0nshiU6 z*|dPiEN;oRKjryQAqKX5;zJQ-=_IcwNLH<{3;lkuz$dKaLBz?heSA;<>`g`^nq6%` zhQzJhg`<^4pG}MV9@(#AjlaX@7$j(Y8Z(}3>SFVAy|$Xkla1oVGk1~k?_$41;rtTm z&ls}=N?osbbalYKb-g;2Y#~~yp)M+7Gg@`4KITdji4fV=NU<|9Sm1$N)YKx(D0xL=HkW9Y;J zf9tzxP*%NDXfN9{&R(0bF*ket5zXE7@Z(3d^d?C8#pS{|kXhftGma9MpD(^7GMLUL zEldR_?Ch@`SKCG1@gS?gY(q?2{Y=qk>fXCM92Y?BK+yM)9WGaL4@WkxDp;6%9(JxB z;%7`uHM#rlBWJoR)0d^g-|P2@T2BQk zl^hD=z9Z!(t_eq#sEqtQRl-sK3zNMQ`6zeU96N->%mmGOO4S-?wVBz4y;PqmemH%m zso3kT>mPHDrORWjPh)S^p**hjbSYp_oCfdZj?RzJb2D|)h9_X>4Kxgb!t4o!CxkGEl<4WpiNf>Y& z!(cl1tW6Z8FtendM}x5Q=QNk|6(RwbQzTHDmc-@tH>{h2>0}98px53;5AHDlEX;WV z83Uy3R%v&>2Yq$1Or$?v(8_^|5b#jgl56`C3ZC)(+qk1+S?-ZgB5Dsn2AR zvWt3%7SLHjs~(=aX!H2oQVbVvx03f*i>^bHIYYVf#+hYhR+|=W{UE5-YfX}UbVTkcemzc`8AWpDiE(fiq zKy!+eP|l#Bj~xVAMFl}P;<*^@j&*;ZF2t=|g&F{)n1z$6nE$nMM>bIKr=1hV`UpB2B)G5Eha{9FfM;t`5 zt$L@3kj+2He4~#0r?iv{mhSvBH!5A3rEOeu6sNF}o@#=u{GsUzY;2`-K9Ilw@Nv+3 zPdlN|%ZHtS)A?{GMmjvD%WV0iwuscqEDWWGl9 zLB2Xn-iIE?YPy9ko&LhhQ!cG(0m9s02;0saNL=~(XOb;5>0?JDtt88*PY!7xigA+% zibQ@48LfL5hOp>Ow{!lm;VU|$P@)Q6A|l^_5v+S_&S?E;yzbHVxK5g4N4R75Tds_m z{uwr)bFDt_-@O9D!Rp^d&om)5|HNhg(2=?A!j!}<8P>A1^LB$^ z(es1F43~kVo&1z}(@2v$!>;3p1dH$ZD*+k}Z>a6uG_ryyA97c$xNifB)vv&>Gv~W3x!A!0Ey&i^)tntqRJ6AFsw`9gt9Tl5%X$fkhgV z2B|@rngF&xttAq$`$a0GFJBI^%=e^XQ8Qyqrz63m|JC5-9k6~E41is-5YcR>*N{mM zK3dwH=psspJMYa_S>{6f83lE^=V|+3@-Xw<5G*r0?Zfjvk=Dh5Gxef`mSJybrq%X(nn$bh-9OD#tZVjS#8O;evb02K?0g_G>mZhvGk zbN-Swe?(Epo5Ceo`4^qGvE<@kTmfZl;xiwu$P|UxY#>Z{uwpt2K z&eIf_mWFw?%Q6g=`UA<+wfH)mj({TJ+3n*;2j7oYJ?t+RTXn4)i2QtgZ?(W>PwT5pB?r^+Ol9cmJ|mU=dalgHw*j0q_zCc;XWXEJnVO>NX);(7U6 z51HT0-zly%!V50&+~HYrFu+*w5ff*@(5k)-o4-0twc8@%@=MB1pa26cM-62~M?Whi z(I?iX7{uv4hN{54I5}3x*6kl-tGsZTGA+|voM*HD7d7-h(z#nm3%SgsGQaA>dXynS zs0a!THio$h4@?LeEFxWIb*^o{724YQ8bs#z?6d1`Cjr5cj*E%Ji`t4ZTLjJAA=i>6 z-O066@~~2o?sL;rwe`TmD~%J^fQhc_orfG|@o^)%?c;T~m-^1OdHFFcMAOdN^B&j6O zbHX2|uz!z1jINT=ESc9!(_lE`d1L=%Df&Yb5EnYq_=E)gCO4Faz%ZQD1+bDQTcvPfqkp`m7D&J-s8-v1zxCGFGlyki>f(UMBkc>TNFHCV$1;vilLkRMHwP%y-r z`AweZ8tDS!+k+JWwfdK#Oec&C!kL{GSX~3-Z@9=L=}{B!Z=)>rt}{4qRCj;;O<~BF zPyVIrO3pD(VR$qlt=T;`i77jjH2;EuAtsjbSxbM&Z$|hJZ4Lxf#e+5}l=g4og&T+^ z^0?P&_n4W)^YW^ZiS;VMTg%z=@E&upYhHWkC z>S^TO?0ZN0_?2~oiqqL(79f}SFn89`y*^amQG63mI#GZGko~=)trhA+C5mp){%g0E zFWra7UbW-H-@A1$-ipdntGTyZ@8p(Jz3t|9T8&2wKg1C|;-ht(OGZRs+Fh;D7GL0r zdxrc~jh>)Ci93^ioF)zA{X~?=T&JeECdQL$MhwZ$bXyy*DcH^$I-O-wQ|f^k)BVVf zQj`FnM)LY~>;&TdB!P(u-47__aFl-#2fOye`uezKX5aVrDX)(McNrHpvukA#YlTco zT!e&h4JJznO@|dmM!#7)5y=ZnK4vY=1_V zo2ZjEoaJnN8F7ego5BM1dI=(@R|yjFtc3{)rbD2i4GUiYyxfzA$N#I9h73%G*nRNU zNa1sN9eT}WBJzP&c=PbsP!UM?yA_$b=)Ug#zjU^RaYJQo)_|rB4oQ5sm=C8oL$3PB zJsx}FwjYX0`h96h?}%yCKv%@HMGamR(VyAwzc2Js8rX1=*1*3b2u+X(QC{WUC_k&W z9}g$fcC5nrP6x68!&K#ZNv{T7FClx`g|VHQ$h65bUJh3b`{=F5XfNg5)M@4aFl-5g zE-$mezpz^6sKGdQ8<#50|6uGWb&IG~x7*Rub&`}HD=F#aQd3rB#78c|{q$8iyO#&TUB_C;5G~n+fN)!OqfE zUrsm_J3CBt^3Bp&__XDf&TgxdjklZOt|eG^+hh5>U{-0S)gn7bg+b$xmi_{S;qhuz ztqhQ%)Lm|DJe{gCZ?q$Psu(|2X)0DwKnJcKs1TAHYoWI9Q~0-aTv5uWU$f+Y26W=_ z!J?WMmJyQQtY+hzU;OAiH2!Z#2SVa%5+H*a$Q@@be#7ZdmM$Eop8m0o1DUPk4(0rM ze|xibA6qJA4yqY0;#d(5bcmR6TQaA4*q%BjRfte%1CK!NM$ckZ2H{Y0BVnBTd)#>H zDs@))G#EN^wKIJ#Ye1We3rgU+eB1w6kbitUDuW;kp+u(tz1P8}S4^7lG-75@xz@zk zp%>6dg+~aY{F%uLdmWh!VXC37Gd`(Z;tnkh%Y1lJf%e#po-1;%oii=`M3!oAQT&wJ z8y$%!IFP(ka&xzhTPSlWlhIzSt1N>S=&=~Bf7&JR2mkO24Smv<@TiQOp_BklJx@L9 zd|wg3?ndYQ5#*1<37N=_{(d#p#~hKG@`){PiX%GHmv?#}QEigYG|4g=zJQ;Oaod$j zcFoc%oX=odH<;=^#=Kdc-=rdrS#DurHGFoRx1-xG;lXFXds6#76L6B zS!FV4dt{vWsTU{CbT3I}U_2t2PbH2+D13Yl1%ymTt11CppZ~_?2}j?)iTMQtWWu+! z=+IO(J@iGI)wr5Umc9i4w5?GlcL9*W$TRUl5zi8f;Q^v((CB?2k@H?8Gsh_hpszZ6f*fi7(zvdC zm228q{*19P0y(0d;Ga>4&=7x>H~~w85mGE!z4cso$X$(?^}a2 z(9NMgGX{~Ku{3{*AgK&q+k7E2nry@P%Q<=B2V*dzMW?P`q6 zwe}~fLhC;kx7SlkuYAhSF*QUG>0kbfuJK4|~&&gI3`xAT`HBI`M8x} z4(Yl|Va~p5KxR{81frW9R%llDKKMheU(OaSDsJ6wN z3RHZhX@lCWhqou^#jbkNh3K;F^oA{_J7IPX_vcD%<3IDA3T580YUM7Jjvh2(*k}fS zd3mP6!7q9@nWZ83n7L?dF8KrX>ye7_eLt54HDEP-Fm_7kKz4QiB19T@G8>;OB#(=~KCQ(?^Qe6=YCaN%fYzdt#6*9CJSz+?D(1n9n| z162;o%oLzdtp~%*);1N?`%Ci#b6>(=gp?kTwnT`ASgozV*4UD-SI9FJj%WQ@dhky? z38*=My@3b#jpN+M+3MbxcgbVN#WZTu68Kj&#ThA-qMw@Fq}VlVEuc)ftUaPP{5wC8 zm{eyI<1%$cPCqPB#LPBsEV4&eFg3~1OBS9O6!@az%XRwnxILFB(0XsnX7P&i8c6AN zmOKbEB&zQa;)Lp*?wPd&Jp`}#b1F!g=CUlTpUqLlO9n<9DVa5yIb+FnCg?vh;PG7;9i3fp4Qo+czdQY063Kdl}|!te4SsfNPdPneDcUja5f zSMdl}o~%8UTAinj%;!u~u5XL|g=?%`)@hC&kt5lCNEZAb=c?mE7`2BjJS+!3Wj#5U`>{=Wq{L9Fzv?0e{ zxLS+j)Bo{xj>Ar_79N!4p6pg0d%4XZFG(naQ3jL&pidWLtR1%hLo*i!0y@us9^*eC zMjm;99ZG2Kb^dDP7x@JFqkmpXF>x7XhW{)!v%NF))V8C}Vt=ILGK-HCGSD)m= za_L~_J3fo^1x7Ks38-E+ge^iTd%s$b7<7wAZxB)K7U+q*+mM6oTqHw1;}>48drD3LDV+>9OWvp zasDm9JM47Q+F(+59Nv+FWn4YGAIobYH1Hl)$cW9H=jKu>CrP7wLITpLgiGZ#k-&)m zZeqd?%yE34p0;x1cRn{qDEpnaXo6@g3h*9EOoNRemGomO90gnPa6O5gf=fMN0Ww=n zl#7$o-;W&`lV+obUiVhH!HN1`Lx6Lb9t8Yg2QcQ2^lB@&@~d+fK}S$hDk@jB1Ufp) ziXQz#5k}tpbTnO$8{c$s}3iCh+AyN zm**aRX!woZ-VC>1U%Za}-oDEt`~f24_;t|-mabc?gYQ4eU4Q3`jUJjEk*fX*A|FEq z(jJIR6PBH~2y112+4e(~&eD7r%l3(*y0*&iG!dbev5xuP(1A?ssr>aLbQ?}hb|OVf z)oM!*I;8sr@YqIh%s|6%kB3a)24iB(<+qO?KW>iYKlS@o$1j-}-GJmT!ABMkKp?h_ zG{x`6v7ViuV%6X#R(Iwen#Wde8;x<)HvG~?NF&X$IJFw8&-k{F(TjEid@MkXFB z(Gi$1+G)Fj@z8YOp?8|X{e3%u5~6eeFbLy*4!8RI{KqbJzp>!Tmj?|SFA!ipApeC4 zs8WFjQ94+pK|oX}J6fS8G~x^enTpi z%mevQ-55bfkX+i;m=+uxE8vfvA0;7okN>N*nbDO@fBvEAXob;RiXE2&F?PH^6m{;Z z*hS3X4qeXoj5k_QH2@T?OQX5#v}pws$S|Cbd!2x-S-A|1{Z*Fa2T*7#3dNOi49Hm0 zShmXjzLh^f%O4+~KA=_x+{nK)7w?*iVbegqIa3G=3ZC#K2R1aK69-4OCgf~H-SQKR z4G*1C-(5C0P^|NF9*W-|J0p^I`$K1_3me*7pxGh&3LGj=tqnmc*ro{$e^SIvTreYu z_?nW-yStQds3#kP@@I>L9A8uWx#IZaSoR}mZNtTWtI1fqAX1XH9-5L72$ubO^VdK3 zp4&t7G-2L7(AL9(>Zb?K9GV?)2~_JELn}VjPSPJN) zsNR8k(3SOdK7IYoGA)l?GJP;Y{e3je+Vg@Jc#!lPWW6zXt`c30GaFj5o&bz4?!r!Hp6MTnP=wjns@OLdQc;FUb zVKgxI=o5o4m$B}_yU$=9n9+*?PZAy-`W_X7P-d-Ew|e^d48i}b^}<(gl6QxGBBrPa z`(RtMmCL)NBm35~WpFk)DY>2o;9Cr}9PXfK5Zc+8`C%`2CrRQ+V?T#>w_XJ!+iVEr&{ z{UVkUH4)zNK6>(X*!6R3LV0cEY=HTi{X*buh%GZfj>idnFkn16G1PmvM`(~Qc>ems z`;npwYt#BVm;_>D><$2B^W@|QLt%4sbH$nyThPavaBz@8d6zPuSAvxX9bS5ha?l}9 z!p##}UQi6=Tr2pT7Op3K)0t5nlUSKrYALsnHC;Zo?9eAN97fziJup+>p41Q<+{rLa= zy7^#2`BwYbyn6tZbA;?=Bb+OF!qg#4@p?5#0;ta%NM5_kvR3j%iMujrwIxR2}Li zzL5GIBl|yAda^jN0-5Be{)GZPyJzTr!ROzn629dL?<*iMS8RTA?C|F1>nXi1F_smE z`RaBFvol-2xDX?`&O@}H(B^7h%r>3v{;+a_LlM$S%lRn#dHqPiM7@QpZny9{Rb{-t z&*T1y_+hOL{2y-Y$-_!V!Z~XzY((KZT!MXAYp2`iuRixYo~gmgl200eeY*(3k1w<( zlYANau*O1fAL&8<2v=O^xQVegnL*MJR)QZAdlf zRd{EL;gtV06c3x%5bXA=4XNKZ`tPg8?Ku>sFj%t&y1M>gAh7nvfQeAjoUq}gx%r`D zwFTZ69-13zAf;6A6`3BzbYKFv}LALB8ui zt>Y;IeM65jJX$F6oB3q@P8PqbXjZ((^N~nIDaPejANY^ZgmR9HE3Z-Si}%Ntxh7xs z3y8-@6pnk-Ml^n=4N=A^2=E58QM~=W1+<*QXTHHc^d*e-g!UE4v`)ov48w_l;_c9< z?dihWyg-UiXT9?f?tBSW+baYdYceQ(Z=P)w@ekDwZ`)j2G#cCbQ!_GR>h(?1G&bca zqEfR`uG)OYpITdSO@zgNW}(9P`!QbdA`HyGcZQez-%V6F8Gi6s+?!1Di>Vyfy9TM8 zr~=7E4!$^M542}j$~ht>xpij55X>$v0h_};FWKR4F(%(gmadAp1}u3|H!)etX9x*r zRxNUWF2lgX@fc8eUgfflZ!*K>vk^_-8m!38FsvfXs>$|7d%wzJ*LGrRi#esvdK2|>oIl+5H%>z%`{VYFFIf5q z>tw%}xka%T_V_nR2_~k8Lp@SlcDhLNRd(6O{1T#S!eAy&glh7&Uq&{MmX~B`anF*x zhjYvQRU}I|$D6i~vi0kTd@g*>>1S1Tbkc{BesG2AG1`L#GlnA+Cxiukhef60il!VG zY);>b+eS5Ycj_Z?GofEEh51U3nn>U>?#AY<1iB)m#`mTvblT3w(olYV^o+}qq#=&kji-mjHZLp&&(OIasc z191mBO4kq8Lk8_momOtJCY@ zpbu5YTu|FYaLYQf-^wFHm)i4JW3aGFtF!YBhXSq*bgFsT6eQE=Rrwj;+T(a@7r;QP zs0*XR!#wLkb7$n&?20E^)ToJ`kh!I*Aqn~_k;9H z+e07c=1YX=q*Wxudf-LCY8>_7Tpc!9u(TKS5HlW2f!E>kb@S}3T+WkmIAn*Vy;+}> zcv)QFT2c*(fyLQ^c-$*P;VR5%JdnWu)a=y=7J1)J%jWU*sa0?tKC8#1-XK^Cp5wm25{5k)UWi{%e}Q6hW3b!`#*0UUurC)jeKf9 z50&B};xut}9npCVX*mVA39@(V+6oC<@K<(7yWa_)d`I?A^e%qOa;my8=ivq`=gxX5qF(x-e9=c-s8TDJ{FuD$120w zvE>^kn7aF9xuj>*h`JWd=UzuCLZ5L`BVG4r;csl%(cJYl?=eI%dF6rtP>r*lLO;P5 zg-#@X&2@;*@GX%Bm;JN$i_l)V2I)tFvQZzFi#Gdx>C(4a@;@suJVt>i>@O3q z3CuDM%E77Yu{aiu>IL^2gxu^HB`E?tz7VVCMtUUUZX05h;PBNvF6ZQ=KfKw=2$8^f zW}pZ-x>QM#Ri6iL;^ZV{J^JzCIgu)b3ql5%%En|E(H39L{phjfx>3uKPUpQPe+$4- zUvXA(f{V+vu5jFFge&znkKdlQ5~MypkYll5)Pj3_$r)u3p*$Dc;(i#A_+5Rl(%^WL z`b0SjkA*M()cS*A_eL<`@m3(3HTxc6M}Z^x`?tlomR`_a(a)Vx$(~YBr@BRsg-3cq z?^>+$>_b(j=i^$het5)|1p216n~oq;mW5>lCm(REW%}yqXcx1Fq?^W2e)9#zYNV3k zt0yRW&%YxAFU5s%G2{t7hs${&8qlzG6QwcfJN!<8Kj!sskJzC4@L+Avz%N0csecE# zzrXhw{V78V{ME^&yj88mDzkFMje2<4o3OW*0^gpEA_3|PNtd)wmvHr>Bv zo;IvGd$`1mn2`;RWv;Hn8T$kAh4#ytnDa|NrCBKnKA}UJ*Rj6vbR>~JMMCMqa;!}x z6gPNxDD){Qhw-}sf|Cal``^~8OFSGG6vX}axZ5K;p6AHvlW7bO6Wmj)OWAP0#1C%c zjQ~&l71ZoinILc&)QUekTzRj_IEOi+aLeIaXWzd2gmtq=+Mb{ozIHB#_Y&&wWgiV_ z4_7@q)3tN3LoOfd!~8dt?6e7!BTE_3;5_}E*DDkh#-J#dJq_Lwdd8IFOXRwPYKvU^ zL|+R;?#)%^rH3Jt$uF&sx?}|nMIM%v@ReU=fMaW6lfuWPiwoAgYiGDbE=S_6a9li0 zYSqAcx4Gr!WtA?2E?jZ&`UFx-gan_ULUB*BzaVhjj+`m{$f)|A$5}kiRv?!Bmr$654IRId)3 z!b4pQ#4Y!@egqO*yZb8pV5(E}&z02_mvSCc;qPlNe^Thar$VI(S9new%;xmfU$Au< zC+%Zw^=BbnA7eSu)=7!$^?1`zPiXzMEPWF2o)q19QlaDqy6E50H2W;KK++K>zyI5i zEp-2iw$}ewD1!+_DvB*|x~0RQ+ovpJQmUt6y1x_sN{d)U@a?3zM>J<=5O-gDXW0GE z3UdDRc(bNvrr)q8jTJKHEj~d4L*6{iVhATQ9bEUA1kXjBRLq>L4g#<|9*OFR^Uvmcw&lNYVlBYYr-Og zKJm@6bMGhQMV{3g^ysPAiehafBvxt(I1J_pjY;o4aAQ&?kF?%id2)g3QxLrj)9%loLLFAQdwu6)hwNxkc>+;6+H(y4-UMp|>%5b;TD zY)QjAPyZNOKlPfSGZJ*!9MvBRiK-iQS+^fTPpdq4Rn1b}cJ&Dg*B-{OxnY$zL>ijA>+QaIE-L|v>n zDE(XW9^B;|xoHN+vuE%5`9()NEh2v5^iOXQSJTRKD+Mlx%K6(iEgj95_PK%GjRYYl zWcQm6T)Czy6; z)NrQp7^1e~)(ife2)CRN3>+UZzG1p)r1CQ&+%vQ8eyp-4cfNWY_2`rKOq~t;`JXwOueqejuGwha@{7K+kBXlQy-dT;L0nP)}Q^GZ2%a5N^>C4C5A zeT^G^ekEPa)iQrg;^_b%J;a z&z}}Waq!Nu62BTjXD_T;!j0<)|CG&8`sr&c3;*2rWb%1a{1TmSu;&x~0$zVpyj1COi=RJTxN?OU5iSBneGNvSe^cMR9y^RMv9`#{G1+mVNpu<|IFL#iQ#` zhd)u_0u4!*CRJoNekxu$S&3=Omy`CZhJ~%Q+-XZj38KaiTV>A4 zQ|@>48^uVoLM|r}$y^BJm2f;kQG}&N7Wpqw(w&jdZ5ny4D*_r8@q*a(Zl*}vCg6OZ z`|~~FBEofF#54(+EmeC14tRc#{IF?>Ys0t4_qBSWajUGUr78fmBFq{*@*^PH)b4(= zC%H*!@RIq$7f?I5G3Ul$g*sb7)t4k#9 zE%foaapdk$nG&+tO?snI97BYm{C@klO<{~N0SVS7SZJng*$_3V`RmZ?+?c~rjgsBE zP$XFIz}>=ETjP_Pme4q#XyVJE9BuI#N8|?Q@+BS1^rjze?hNDZ^dlFM1&tevr2yA@ zvrI#y#3Hjk@w~a8A?;i-%nJn$Cnpn{B{Z(s?1j_O+LIUXoU`U%a6gJ)>IXK^X;{S- zdvAh>vUso7XQBO6da3>1d2D@5|3ahofc<7-19!*B+ExF0r5qa0=;)oI=i+!FqbY6U z`P2DVFesZ=LEYDLuA81owvLO7)ajK5`j2~aN!yKNmyjJ~?b|$|%wt;(Bj#TZ^4|w= z!Hs|JJswmJ7iin5ct;T@s9)8v@t!%4d%a)~si@4%?Ra{Uo~Nf3mN*~#+OH)2b(p~k zU*9)`#9nRgw(sk6!jaFOtv!N|pNuQ`d104duJ2bTiIbBL8-sx;E&P0u++IPXl3}^BDK;LCX*k)-70af)1VG*Xp+on+}ytaw_o#w zNcVV(yT~R+i^RYrjq`3CokFcoj zKQ>%?H>OnU9U@*?U!mo%L0{jue_gDZ=Qfz-!TP;N1%E%5zpeE%*yLyIsJKmpDZtD#)rQ4dttLO(a4P~r34VBXJ@HyvjA9{d z;7ST+S$F-JxXZoLW8!aBfxyhfTp>|Z+~0m@-!#U+vSj2`WZyv`7SoncnjYVe0LYe> z@8ojh#e7j;vkAEx0s^LW{a^R9va&w+_JlDI>!A$>Jy5=6%$gRMx#6LSU7b3vxev0Y z&P`VmCL70vDEo``wzv;>^nDi*PggL=?{zMC%3}0{E}@ZX+UTd0&|ZdR=6$m#!|;iN z@-9O%hVtwFlJ*Qi)Gq^-$FCA*<-DM0WE$Cz$F^Q#^a)x+n53Pl#qG=G=Ebg~V~rVU z=ndi1CwhI9>K{Z5DkYn}fVE<7f5pjiyw+IKO~N`!%?}+#nAx#F%hj`7#Qf4^fctx= zj4W;PH;Lp~QKi=UL)7V5BquRvN=^(~F@rPA)g!8a$ry&RPy?eWn4mf5Hz><6jT-rPqhiuVm&u2O@u#Z-6vYe_Y(r6+XF+8E=r zd%%0>%&IZu%WAoALixXL<=oFa{bXwBKcDLaOBWw3y)6@k_Kbi8OXjp(*M-RH!f1rB z?{V$r+)^|H8?bRixf!6A$n;X4Fd&CNp|2!hw4uNgeHm^8GuQykEZKkFa4&J~>}Jr& zZsV}+xHsQF^UATe!V4}*L5>`^Y3%9GF{-G%)A+YrneVdHYfEOr z4XinMp@JHSNu66D4da6t3rPzjxhxwhMu_nNWqHXasbobp$AdrlMO4L{VI`6RFYfuG zTX9+z(O>8<%?au?k!<=6KlvmgFxj87^k4k9&?)U1)M|HBlO#9t`{pn9-;|H})?mz9 zJYiYwk5WF|if_u+=y6b8bfh}@vR+*4v(GXzV!wIQR9Pkz{r&ivY9%tiiQtVI* zVEJ=P2&0>uXTzB>@*Rbo@4Qc9URCuo4j!C)-Ll%-u;hfBx((eiE$qY}c->}7CLPed zz^CW)Fu(Z0?AaJ{E&(X-eUH2R zvHF9YE=M?n3)FAPKTBw2R^Zs4_xOEv>FZNW0^S7*E#nQc)G`&o#i~;Bz=$#p6o7eJ;sG zw9=J;@wTp%dg?Sk#8wImg~H_GW?r;ntjRG*vwOsg|MqraDTPsQNe%8C-=nrNg0Mwy zzuZcFIN2Xh(Y&x&Qi(N9S5ku*NjthxifcuCF;sTg!>yxddGFCERqo2TX*A6IO?409 zb(4GP#byWp=OAr82tM!4%Tj)ArFB7rxm!v2D$Ny}(&$brsr|y^q@N-oi_aMwj)k~dG zV4nGT`fImL00J)(Uq+4J<2YlznATT!3pl}&0E=fpLg#$RrkJnfBH{XQ`B0Yj5+2QVyl6M2K7sJ zU&zxD$%y!&#+7nt_REFUj^mxme4k~QW}UY`xN+n|+wCY2@%&FEyJ38!c&KQJ`9?(2 zk=(BcuhM2U{Ug-dW5UEIGFyTx4>W>4;^teF6JdLcn2Rez;+IEY89`ZvS0Zc20#dSwcy`LtX60dQeX&^m7)-ZtynR?h(KqEA1^U~|5H1zvQ2Y>wC8 zcVP56(xjI4{dn;I-tg%Vdikd_7>X0u=Um*66&&^K@Ac%>z1{;Nat~Ya;vVk%p_s3I z&59O@=GIEMJov;$DiDVjG`_fb`)V`Yj7Yc%iDc`qW-7p_`23J+b=x_WO0orEm>i4N zVD^QnODa~j;Jb^KJzqG1V-d zyn7xULMBi#$T(a=@L}ECGR-U#nGTnDB9PfFM+~Lt4(n*R9k=njcBYd3b%{9k)GdEc z-)m7e_K_{x`&>=gn}lnI@RO0cm{4s8Yt~5NhIGCNf7qAhQk+ZPUhWW%!E8$L!=dn< z6Hco8y6D@J3Jx7CsMq}Bd}q%Pj4MZ;3>D39MpfLEHv3d77*kOnq4lmtG%B=;KE!IT1J<@TV%oYTu@6HivvnrY%_13 z@sB8NG+91p&Kc#r(1OCR`VOx?uo|{?55kDj_okMRSj|;NQshEb5$en*dTduM!05>X z{iBr}+ckFwrvux|B|RZ+pgrT=1u(XkIpt!Uj3a2i?qC=Gi~<*g&XFvY+yc_NBc_Bm zC4dU8)H%VToIK|qUI?66MeK}feBALPik06R$~6i|=4MgcqsaYKD}c||RyZT2=k(X% zV62L4R%XB5lF0jcJZi0yh<=Y_KWXtp^Wv{xCQsviYm~6``pu3cxdb;y^YjN`T#kN`{+|HGTV5C$ zvk13cz1|l7Q$pXNWM$%l z`e|b_Pm`E%F1*iN)Gk$jlx3jzWoy*J=0xsw(>IyD%f&;V2;x7Ctgl>&4Rx|?wtahr zaSN}lVHeQaRGCfUznOSPLq12`13;Fn^*X#G-D-;5*4*<_D&)f}TBlK*YOS-o5$o;d7I&N*C?S=f_048{2 zV_`&9<;b#!>E-jL<_!FF4);$;_&HFQzP2&SOQ9Jf4yzod7cN6P5X^LC(QreYJG52M zQRC~(#d{R8ESW9mcX0i5SHPc>Df4!Bx&%!3QP6~GgcMew*p&)`*VUzJrO4RrgS8Tq zyn2ju_%)$&L0?Oc{Yp&y^0j`Orn9^MOH!2_R~l^8ug_ZgT;$vZCMG8&E`}~^JEEk9 z6HITd?7-zpK+Sx$u#Xx4U?(4&F#f;rG z6$kDYnof=%2ifATQ|VHSvD4gU$wH@DX{ziH!)CrUqw;gNn=@bGpon%iJeV(HT%Ekb z6LDFk@r#*IJdY?<%5`r^acBuW@qC?`;4F(g#*5QnHF@4qEZ%P>_dd%qAvGmkPd_{d z*d5Nrr!*_3wQgNYYHZ)Qlbu>`lCEvQHs{fsJw5z>r9jN7UwUxzph;;$gD2(b;UPq* zVOt0ph=0vuw(RK<*AyVbx;tePO-IXPGPE*nlY+b3ue(Ha^A!7f*>4*)# z?Tmo(eoc-Ph4KaWh4F?-wKU}oRGV1EY}GnQ6t5FF1U?>AhS~e@bE15XOmrRY&RIw@ zLu&)*7F06aGktC;Ne{_#-3-kBbU3+UlL4CDm3D4{@5JN8>fq=SNX1friGB~h$1gVA zW(Nz2L>g#oc@uL$v#%Ovx3-^8gwHLzav^x5w`gnOTwR!d zF>5rC*q)P~99~fUP#1DxciBl_{)1*5AC~k=Ozv08!0gtjLn;4zHNMGeSF%$JIJEyPlC!z%MtPG(KJg)Nj66P&xMZ#sh**=N#V; z`BLY9MRX{Qvn?}A5S!H_sd5spI#M8SbDlii$F|B7Ll4QwTi}8OvyY}da+s37XvVrG zRXpf%q0SGYfc<7rXPRFaIk4v67#$CRkJEi?Uc$0R`-F4%bFt;?Uie-s&EznvL&ucL z^%=C!J@VME^qY%5xvz?uem1AiM@Hu~8a!#v%bC@{LbF>R!1?~&Ig_6vI768A!7NOg zK1-IHJ%sB;6owrQd6&0($>to6KB!gC7QT1uzlaSZ06e+RuSD)>_#;K&VuBc!9It0m*XJkUouw=7ABU*fjSvZeF>z zCv!9xdc9nn+aGO59$$PV!*2Tsgxfpo%IYMydY1$3p-3sMwJ%3KMr9}{s=o-45N7Nw zaDd)r+`vS4v5C197_9ira=v8sT=|^FJS^lENA%zwMEl{#?qPnMN>T8+=L@h>)*MKr zKT_gg?efl3Yi9~ler+RK-jnNn@tyC%rJZV_Vu@1m_b6ZLo3UR4C> zlkW;r0Ux;%*8d|}^bzjuly_xil(C5(ADi{!H>nQy8%+hoY@1)r<`e9Hbc}i^s>5uC9rxjwd$eGjHc>rQLZxO$uUVBD#>* zdVE!0jl#ZJv>sryqb~qhMXP}MM~A4J)-ig!I`cKGCf~BS-Y&}Cs!^fur#U)g|7U)! z%!HySWmwv@#ko!A=dTYA)w-e#_g6XRMp)+CbWq?%femB!IkFYhp0w8Zj_!4 zXR7hmB1H6#hcjY7*ftZE!Yt^YZKXiX3YM5?Wx8#c;L26{##iwSYQ%BojKV_409$@h z$QpN4syif{vOSnW;<`X)W?RrJpTv@3!Lqi4d|a$5C%;|RKVJTjm(%q9;3uiI01x)( z<5%A|=J%Q$p(RC}+_?9) z3Hbb}W@ZQ8)rv3uPC&t(a22@RpPy#S+TCOdIVVU8_a zLE~%O)26fAmfGX0YL&aKv*v^+M7mh3dzVYIi}zpn)2tHu_39s2OpJdPs^WdUqeb2( zRe=iQqSBa_u@`J#E+MXlxt{D$zM4?54=;|D@Y}G3gl}4n(fM1@1Wzg?3XzRfZw+(m zBK#2VK+oA5lM$@bjyL>ntxD?^X!y|HvvQEq74fizMRtMrJy13N)yj@ZI5XimV>b1R zh51dGZ>^<4vd9n7Xc=1DuMDFsH>NOWok1^x9OW9~Zd~bSa~732R~uwWW^T&gmVo%% zU&5>p&sA8+ngUmA2wdSNPZr9!wnmpBe)5G4cE#$z6kes{Qj9=pv)x){>Q?UtwV#{H z8UMvYoVm8kNsO6O3FEV73EOaME5qzlB*8|5&=tBdHzj8b;F7Mke2vhLmf;U#rOSf7 z$yn@$!qYxl^H=TAnJKAsIO{(QP?}!YX(`wpoqq^_G`RD~V_;Y#xvgcx8<&4i7?)%% z`uxszu?0qN8EZvw*Nx@3v7*ga)_ijcR<_7;et%D+`1#%dSUoXvYSJU4{=8SWw-l-+ z91oV`4%@(qkc^n?t?gGKYpjZ(f&SE{sK{qjei1WkGz1U~dUN+>6vRb;isDBn|K!+b z;&37!oueHEF0b*o19wbTe7M&QNGG&Cl&i+`pYmm89Xwlxg?wcq2tRkOiWV0<++?n; zOKdqbSrTo9{3FrLQ+PSNGaKLB@mn7U5IiM`jw$}fo%=5>uTLIjbPPZ2N{1&Yu*tbj z0#RGpKXtz(7H)y(cW{$~AR@Yi_eVT<%P7w!9nL$y|JXPl01n+8n5$gPwuGSJ^HpKH z-Do!AZEg7AGAFB5Okg6N??lQ}&Q=$FlzJArpEU|;fGs?#zI1kn6H;lEsOgsIwRkv9zF&B;&sIQ)SPh5WOLrasgzJ+f4Gp|7XBi?5DKJ+iCGeI(oJ=U0 zEej@n66~gO=h4@%+n-}v_6LXw3y1^NUEhHjq90qagsJHp(i+k2q~!JaKJatjQ>&cP zkt&(Kmd3g_i*5CE``=}dD~^W+Y>sIagpiO>{dpTYofL;(?yDIvUC~YnrWN(B9!; zjSzTlp^bN_LZLB)vV7z*(O_Wf!QT*uwKm3sN7SJ2jv1tnsBQOnu7AKNi86Kzvrp-^$TZvgq65ye$V zT{+9=pnp(X2YCG$iGXiQuWPHkwlu#QJzwVk=F5UaJkus#?jCVII({prQ~O8V7TlR6 z^G7K>p8mhHN7J}Im{e}rN@PwFUL8Fj8(*sA1y?SbHQPT!N}Xr+9dXWgCL%l^5`DrD z^WO5(*#2s95|xh1{Besm$7(BdUl;an?3?|x^^lwJcM%d?>+hY*L5tVA08fa7!1Vs- zOA`$SEi&#>x^|}vioyvp|6P116K+T`Ku$+q?j^w6#F<}xX5CjR(WkTZ26~q$FKX-@ zjQwF*SjKN3mUL^irrY)~;Nam8h-vE)5iVWfx8SvwPUYlU0KQ5wQoF^g;Jq)*mr*B1 zuYrDzUwJu;R}YxNQ`uig>IKHci7z%rPz%iz4J;-x7FQhzJF z17t-I&TvQ7>$0r5)76d_7=W??p90{>-!ZQ=IdX3<(F#QIxH7ZIDpRxo>B`W>6!FLU zrDAdXdvz^ouQf$5g3Wo?7Wrp5bO_< zP%2NY4chyGFa;$t%g*jpSmoicTv(S`6XZulC~98v`&=d}cG$dgOY8fFWAwCqqZvqT z1YqG3l086pxDn=30a&M|Ac=fOflKaw!}c)Nu{U|iR??%aY6{)iP2c(5UqvzRYUo?r zG2*5l9afcFelfjeQf?m&?+?;zp%$b_0% zI7|jDdU_hQHk#2{QA*`1g;hB0wEC%&u=rz6WlLrjH|pjVO>pq7cP%Ze@$n3GaM(}Q z?vZ$Ys@W60GF&Gy8wHl&<-T6*U%W?&R+^=?za6&!AkJR#@8@}pSbqDzIdMNo#%#!` zL=@O{F8*K0*fRV1{rU^M$;w~V+LBXi4hDa7CNtdaIU@Yy1N0fNxN~I8vrboio}(?< zk2-j$@f815Z>N12x6>=kso$%BM-+i~Me{#Zb(XXM?}&gxviRYIr^#eD6zA9OQSO4v z>6%=*1$?Tfx)kg}t!MWn3;Pi17cqwjq4UmB2$~C2j^20j(8MOiA9fq7?(`}DsWf+G z?x?S*+MYdG;Kp4gal~Wp6H1$qLs&sy>2pxk+yo%o2}A;pFE%RqYuRu+au5vJW3@@3 zwlou`5co2x?OoFD+c<)|9%BEX10G66;HEGj2_)Y=Q%fNcUoVE>uX~r+Cvsu%lD;;9 zk6y1_-FR|0do$LSd;U9tvo*C)1?XI@fZva}?JgDyr)OLdsb1XCM5Kk(7P6fGGL0*- z6Z`p4wv9SOO}s1kTd*%vW3ESdq{ECNVufR~hu3yx9s5^l{IEJ5^=BLd7eMV~N5}Ck zaBs;%h-MsD@mdb{HY|$6=rmDppVumWyWZWm(nBP4v2=9zam(G>#AWIo&BaOF`u$-DB5JKD&?fg3-Dv$>`CqUSHB`bt$oc=7cOpLEO)$J6<|wCLk!oGOd2#LWphdm&zG0o%HPn=VmbK)0SXx!5A;P@v~oT z&Q}`%wQXa;5r4I=Zql>=?Vu8>Wdmfp`#dj6KJI{%wkKj+q%E>&Y2CK8piGA#+)#R6 zSk*b2c}wn_v(5Te7eWH&)~I6Yr?umvaJT8FNA9VUSFN;^ig)hnTo$H!a*%+JV--0( zf4Mv?WOy-1R8{)ltam0l30Aoc(NwqQAEelMw->mm;-LK=Nx zH}iV2C9SI?PQ?mU(yY}S&?6l}aM8|_!tj+!_NOOMNXz|BJn-;J{LaY7plD)-zRiIv6 zj-m$Fnh`q65n;r4R1?Q*rSZTI?j*@BotW&^vpTN;I;MwvDtCKa1B2uvI`$3N6EE;? zWFWXkufXZ27v1gwYih%>Zm_#g;)OCJykZYP?7Tf%r;+CC6lQ|Df3DTuZFMG7&R5k^ zX;9ym$3(jYX}nR}hAJ6tN*@dPc5c^$2HjMwAst99HwSe0L;&|p@+6Pf!;5N}qa5=% znAsySi_aMN6kw%a$#|>5lJB_g&CL1s>0%zfzCAKjai zS!*MBLoe69^pG)P5l6IKnXwOtN7Ac>^u)mcBzmVn-r3$}dwBL@v_mQH3qp;_4vtf< z*VG`#(rr4?4}j+sbyyob0?^jfT$avTzeR=^lzW@O2&=B~5Ynhsi#>LC4P>dL&`-u6 zfKFmp#0jg=DEYoRXKLn61P)rVgGkAR{2gJyiis+x)2EqBzlG|}nq!B=;}4_t7Ll?{ zN0-`5gv7_*4e}Qu91|$__pcV?RS2(+TQdpbu3d|-rkD=N9LWeW;(dQGfbaIMWKU8k zkiAN;+VQv5CvnP}YUKZhUqZfnU%#@|I%3A#X+WVam+MsbdQFiF^F+hgf9C*#8? zyUm4O^vj@U7-0>Rz5M%lucPBM0KdAup-^A65E zqrReY6*k}pXSCJMgk$}ImQ4QsRy0`5z;tMz#_IB!?iA_%I{^!?zotGs4VUF~jfh+V zbEMFnM>Km}m?oJ)S1uZpVU=R(7~w?9BY(VT)OqhJELYK@rj-FC^lwRkDtR9LpLV{A zeq8=JfUqF-I&yrI5lA(^9KX^8Zu>@6Si{r7t>&o8(D08J=9#}kVVP4bv-p)kHSh3T z_y3QxSbS{3*4`o52!0^*!W4rxmjG{X+Ej?!h--3OOFF;s=ZB(Dc$c7S1va)C^SZCA z@v8ivQ>dD}I|<(#@SXmnxK*&i;AJbQRf9)4;#Ip04S)1b2@4FZ(F;UIZCVqPv*J;b zvXjjQOj?+LCBz60@MWstpe=P=y|Dfj&SiNSmc-LN6*JVXy@81!m!wKah0pDcnb859 zNWBCm$^{FpgSlx;(DM^;JJT?O2~ z@^cosfjq!{?mI$1uU+O@kAr)ClmkJ7cy7)u2G4-3!bmL0dkSJcHxGguGw#*h`^V;k z-31e0?bdn@z{7y~tL@C$$oGdnps~i+dk9EQb#Ji+Gm-fwS2yZe_T8ha?19&k5qJy+ zDd!-0Es8kZlZ7qQe3y++3-viz$z0?IU-#IZFH!wv3gF&FQYdRSYqqLIciu1P0>AD<%7JBKu4%NmB!{v%x zqP5Otkt);hBo8SW&ga_3Mj6lR^toc0k4>iPc*{=K%d18+FwJ|MoM$Qz70grF3%qOa z%c`MfV6{}p@mrie2(YYE#l0I)TQ^CuwFE!FSOlbaB zQ;bjDhit9N27LaSLJi)bTs)7PW&8j&ZV{FDV7|wBR>BSu`d{x&*RVe-XzvCyI#27Q zNBu2O0g3{}MSmC>wDgH^M67Vi+oW!w0+@1l25cSOV-ShU6dn-9#S#hia6G#LP`(5b z=+q!Y5t9=P!y$mh{M=x}389@oq)yL=8Sa4`@>iIcWKs)QDf`^RMk^lq(VvDkthL!d zVbp; zzP};sc~x3SVF+DPb$+YKM~=#NztdH8?L=K1{71M27zjgaENOuC&NZ<;_dM1=LB(}%<-hgDNIN~}rr#G-pakF-q{U~fy zX4jbxoK&zkkH9<`6YiFk4LH&5-o3)p zl%-$Gr)nuv#lw@ep1v9#6ja5v6&;8b&;o)lj0eM5D?_l3X)l&`^ z&Bfi__;*o7)s%9~bMpZo8Y!REv`G1s>(}a+MREFfkC@|>zdf9f8Ezp-TIJF@a;o{elva5E-cw|=#8Jkp(Vvdx z?AcR%qCM_3L?1h@K#?f?l3*s+&3*@#eqGqj_*&uy1LjY|wKGZXPl>IV`Tr=fd3=Q3 z6SM6;8J60_%CHux?|oz+c)pkUAw_a=1XwjSN56_%O!UGk;${IZ?zLS)z^)V)$MdN( zvOK5;@nt|S$wz1A-gYrp+i#JPq}8 zrGKfFbe|oWh-8lO-MBPryW@h3dXcvHG zu5Mlvi&`597a@lhU$&*tc}R1&jdpqY_|+Dmr;_KTOF50)evPnH`?>3$B&;v}k5pz- zWD{`G#7zglrcU8Yqo#V7CfO9(y!&mPJO#OiwOTtYd-ThE)9s^KZk6j|pU+;S9UYQV z3o`73KD9_;(x4UAwgS?juua;-eKPX4<_jORrBQz*_iq*vDVYS_WXAAksv8@=@D9l2 zl#7}rax>iWfqaLg7}?)h@mSkj%qum`<+_0Va=#wQ)(yVtMgr>bsb=C2QTJ^o#sRPe z=}~p$t6!(IAy+XN0;l)Fa~zv<;2G50d}v}zfk}6$@G1g%Zrn8A^BlOQwz;0l(Lx%- zqSyb0Emguwj>eq&5bw*My7amDOK#i#nN}A?-($w+#y_HwghBAE<0i_w+vk)d7t$4g z4Z~%v35yMy^72})zu@bS`e-!XFxKI${G-hxQ=YzVlk}?D!dr&8N>aeyl6q+gD>EeT ztjTU3>_SwWoKty~ta4D4Be&j@;f%TKu-?+eJ@Jw4HKddgH7TFy{xZ`aJfRm8N`Kx` ze6Dr+O*3@Cl9)$-X+yPyWy$h8&I7W}BKjHTdd^oln*66nZFxR6*J~G`7oeBT?{vsS z6Quu(I)ttAGbQ6P(_V(s(uzYz*Kt8>hAMh5{89odcH+>#rYUo}yIlUn)D995cgD%XR z2W}df$DNjuV339zDps;N4dp!76D?APL>IEER8~A%X)ehU{7axfANoh4PJ-~i2QTDV zzobr}%HXN$ltrX0s*qcR1$O;5E=kFYGNwffz z1nfrK{I45EP$|YAD$6Vz70xF;?6wOQnzK;vX2l2|x|@V)WI*$gq+Ms()|AB2j|o_k z?9-VbRu7Q`bjqDFDCT7`+Q&i$w+=VN6!GhUPQWlRuHBSGB7Mi*`4@svzEB{HCD?U~ zW2yCbg4Hl0EURIM^pS4+RtwFSjQjvtprSyB8sgx%PnsWg*o|@7QL#btMzn`%qj*go zNo@_l8b=igmA{H`;iFWg)fO%WV3?BWZIxqzZ8WCKD|YfuzmF9CkUw-mEj|Zvs*^+b z-dTL)dxS=5^qX#&dL*@XUD0;lwm%dy(UX7j$XfH}J`aY(-9S+7T!Rc#g{8*CPo!4; z8adW?mKP~ur`8>4l0Kre)%LD^%?>k*%Mr?T!f=UyL9Y+m4~G(Yno^O;dhX-7hYeJc z*fB~jJCw}W46)yMfXHn2uu(QlHohunA<^U*V~028-Q6b05X>*IzF1l_l|R) z^7z)$&tVHiUyD|}OLzE7ZIq(s#4o+W$&UGgcS+QRk@Lc)^tr(vHOnU}#L{vIj(p-X z9C=)tsMMA3@Z1zMErs1as;%miNfg515h+#RGPIJ&jP(4?XMFszw2 z<_4|!u_1shadcZw!Mj&vFxWNGHqEqA;5v<$f~!)O`vN$XQaHPVT)&GvKH}VZT~VXc zBG^&6wNAV=TXFu6F*`l2IvfcpH-)n>l5v^aeGE6^70Ey3}c_HO{X%sAL*2>pD~clw&74{LVy7CDHFFjA7rW^xda1i&KV{M}R`Przb3U6*87 zPRLLE&07{TVTS%+Q}-XDHjDD*9A;$@N~=T#!3Y=w)OiT9I1e%NjGMJ~f(N z5yxGF0fl$X_sko*zwVkw{sfvw*-I$yE@RphL_1viQt<`B;;_9EbN(IIR9=Iq7VMd ziwNxbkXM!|4`12Zo|*nUR=^nCwK(<@1LsMlM7&-uLZx1uN@Y6*u#2s$OajF4fAw5* zWhQX(`HC{|IU-$8GI*T0UEq9fPOuF&vy!U`E9Vr{yomhaQA>j@kC{PtHKJ^w6*Cpu z&rOp?^Ht+Ty5(;U4-elMgf-t7OYCjrM;z`beUWw8=%rS;{Sva#Lm+XZTe>_A3^#ca z$kBM9F>VIzZTPe1LZt8_xK1exWK#~0=h2LYGcXioOTx+B^?>@TsB^zTUpzsdw#GoH0&$-dJo5JrrCA+K2Ek zt&O1243)Q527N~=_UyZ@pwNw*P%Z=GN&w2dHXZd2zup&R)xU7fg?so&k-y+fx-m2$ zTU^v6^2xBj&Dij9SDt>%M8MDr+g1tXYNW8?tIZoSIun<&**%AKGMel#a}YX8K9B-p z(I5Zl_jJxM>lN)v@f_w!e|6g<(-xu8VZUAJw_Rq4_Z?c21Jf~gZO%gFFRX?y_s0nP z0u+%%_2vGS;1jy#<8qUJXn)qd^szkz8%d3qLVfLV&pbpK}9 z>_P=(dnn+GZ9Utz)`=x$u7(mq%jOObBBLK_62mN#%CbU=1}(0cTy!bIT^_bGnv%?k zGl@G=vKbC{`@Kl1UxM)2U4`>-tUx` z+}KTZjxH0am(tl%yxbbb`MjD|41!u(>{eCrHo0rvwP)%*!=skag)iqfs1>qo&N5$3 zH0SeJ>8Di(*JfrO3j_f`c;I|X(5WHqr!v^<^X=_hkJZjsuYviEJYSl+11@qr?lvq} zi7sG#{S1vyR@7)BG^&hj0=)fJd%@^nucwZ~C!tisN(1JZ#oHYfU@%-CV0Iq%qRF34 zdHwGK6LK6(Kt(^A)`(VXUde8S02rtHua7Qz@nh|>9j9DI39v)L2~>f|&J)pjqe)Y9 zy_Vv-`ZsCvrhvWfFI?{EEf#Ig|B7uN9Qs#ZDx04z&> zyMY<<(TY8Yms=OgX4%6+(VVsfOUl6@KI$Yu+s>7XZTCs0_W{wr-7Vr-Tck9oHPu{0 zgooFAZ>FX)N8hF*9?8hrR$FzrycG}gV z;T1rtVelp5A%vL&oOiL&w7-o48cpq)cTe)LEEP@9)0hOwKfKHu z4CjE3ejQII!zd0KzJrKs3ZIFmvo+O+=Bq)-nFU5q3cffu5Gnx?$lQT{^mr~Y|FO6; zCbOO3_RgyvlPYk=%SSW6N5fX_68)1nCX4Lp=o{n?TS(odDNh-H>4Q8bq(jJXiAppu z;e@=zJag6CiWT;sqzw>+3BjDEo0EIY-15{~DRZyA-1S2D)-~teffelYJhlyLjiY+n zFhta-8AA35*HMbY#yeB&n>Uyfrq|%hZZ=@?U3(bn%_QRLd#Sog&`4%$x9yrCy767Ym~LRJLxw3qDPt5;R0l63X^o9)^f}l7@`(V%BA9jL|OS| zcY-Jax)c3nZaH>(oJ*)cVKbtQ`Xk7THquY|x#$oyMk+6~iOC{4CNWknlc2m?GP zbA+SCjh87G^^dm)h(HY@Zl!}ZTAI7Bb*wu>hgyz+)6b)s4;hz`B}g%nI?kimu@H45(5&kyV!RFqV$Y69^RHyv!1b{Cq+Q)dS-1!T~b2z!Y9Chbh{aq zTcQyvgl^vKvd8y^ZY>v5P}!{wECvC$-}9&}5#ItObZ#WAoA-@JxBNB1ZPMsiM zlwloca2E8|UyvM2wgI*h#4j7ewSWI7^K?MT*1cwE8Od`4kru>G!?G>_R6zkago7Cee+DrDd?x zbBLVA)taKGIMnSEliTt`LdI!g&ciPZ7dj5L=60=2Y@#UkhkZ6!D5G`^H zucfJ+$d3n9fAmK?1F79GXaZraM1LgbLLCL|=xmxmCJ>uphf9D>IfA#0Oq@I*<7oCO zaiZ0ZFQFG5Qz=&+`FOzpGa#hNwT><7aA9U!joD7(=D`_OrOnhA!F8oTp0D#4lP55M zSe_OzXZ$!r&EN-v#;yZSPe&~+=rWEZ9>RN$h;AfXfwjs-I5<-|Ep3i&m8F!R2i;Yv zNl6AhhV&u2nWtwfEak=0>(1b%H7KYQzaKOJlX{~aNn1hGUj4JCed6W+@K@tN6ZEe{ zIa#-tPV$=GR)OJ`*h#F)#4L(PSsJRnFx*_Y%mBF%&1uV3JioU*H{DNi~a{&JT7ZzlV{+lu6U*mQ;~ zWJJ8`2SCPfg+ zk-xxU4;OSD=h;s7=T8|`LatOQ;`!DKjBO=D8VZ%xL^Dvd4AeHd`=MC3XXoWj>hncW zag})tX%kFXyrZks7W~=RFCJeY=73p3cF(|TYv1f?=NZ_wm? zk(*%oS(!#xM2lA{hiV}O5#IYUy*f-m&?+)SSpVsUMjG{TL3czhVe@?Og^;*+yue@C zZ}~9=>a6-7<6jR{+0KaW+EfqXv91)$Y_C5xCq4Zk5Qg-~AieO@+S6Cmtv7=i;qy*? zIPawK<63KrUaINB^{~3fkJ|(4nCh!6rujjCR3S>AA3*vAQG$!#Cx!XWT!z7XP*$E6 zexoxTSs)-JlT2!5_QU3Aitns#ER=yk&XWG+a>G>%Iwh&e?k0Gu?!&B}@D+t?uqA2&`?9uQ1tE3;& zpLi>Gt9S|c?V(>YrV70<{8Z<=yV~)rzf=nCD~b=;>cP$3KKBDGi^0J7pbY;>)^`GyEK zlI46jAgjACs0&TNd5nn??vbN!4)}8h$NnpyKr&nhltvWpt$%;fJg>9GJ?9Y`nc4hpc%@|KzIqqrt6rwCjcreI~yWE#y$Ji z0=cGV(7hDAmQN-Rxw2>Cc1}N8#kW-&6G!#^)CN$(lmNH#5ZIAaq(W3Kk)74Wh%>?%^evX&N6)4O&T_zt}%yneWwy!o2gnVoywfML5LJWG59N)TWRE#(r{Q3Mvh@Ng9kAZ_KZjfSQ3*>?m)%9R6W=eL;-K>akU?zauU@<(Nd+>m9 zqMg?lA6=r!96%{`PM;VHWEm~Vg6%ggEd%eXnZ&n%{CfK&&(-u~^+Ls0+mcdG?kIp9 z9^#swPORMe{nhW$VwMg>B&5FMWm=6^)y#hn*2}YaIrOKy?m`mdBg`~Rd)tgEJytic z^>ML5{e;m`_dSi+6`Jc z*zH)Vt_VzazBLg6OoeJgbPWM385ogDW7|fn06x_>46$SeD+}wrD45L@(-+ZrE*PiD z1eDADGTSLs%rwVeX2Fi^zgd%&s!#A(#qHw=3^IhB*qLp3HBLg@dsw@}M=sbTE|jvq zp3+P!uG>RbkY(DS*%ffy-*b7&a-#&vc(DSKgUMM|;L#2F8T`?Ka54hHa9vUP@V!Av zSgCJS+^*7HwDKq`(hKVgMDFQLNtm_^9X~BM#3MZxa@J$m(RB2UV-)k9D`OU39~%8JDF(pLoxS@OoAFZo7T%mv~B$* z<6#V4PaxAEv|mXs_Zq!Xas8@`TKDFyM&DLxT8fy`6O&6G49lea$op~fSP;GT552-S zXNMb|E1=(>C$x1)NY=)~Yis!ysR9X%5o+1I-jU^=GI6|CIV%98(;MWx;Q5+f=2Ta% zi!#veK8u80#I1v+WyU{*MJYiB8BA|(SWt&Ajc)axkgU#WrJGYZ|4YQC`7%{em(6fHqp#b%lZDyMYq8o9U zizqp>W#4x5U{~gjYpAK{4fDQZVk(LOvy%ust(+u+Rk|x>VoX^hU^rpeW{O-ftjBn` zTi?}ddyH;cwVEbxy%~LXEQHkmUUfNw2*%=>S&AsyWisI(8w4`UeaVN__kd3PuOw%B zKY&(`-6jUM9;-oH|9Hkgtl5TXDmC?MgeD&^+U>eXkQsS?RaN7B$5U|kl&`w^Fl2yM zaDT3{%qfh%8)nZ;L=%YWm`OK0J?V}j%|9WdmUjF%^ONiXT&x-&22lg*6va;__}xmi z@A%L(8(L=o{VEu21T%y9wN23hw1Y4QM0sfYv!KRW0t#qy2r&-xzp|k_@!oY#2%}4D z>{DI~+|R!h3Ui?zE-2EXmiK?37C+{kf=n^e_@GS zYqvhJ?2sH&(em#1qib5VtVbpkGSTM3Q+!6IMI2d@o{hpF4 zYwegg{Gi`_Eyy{$hNb)2a>WtxC9GcpPW->}vZERPO3`Nj&u1^oSM`%2yLn24w`pf6 zy4nkbKRs^X|M2^X@0@ycjqtaG0+WY@jJ&&J9(qhb*mw-6=cok1qu;#$VmA>^Dqk3tHGppTXx5-HZT`LX2lqiQ^|A+hi<{COsr^vM?e^)a zOWd2RAMWSDzR0*YY6O9Z`>cBJks|Gb9_>-}EkPdu=q1C5^dE;P%TljbS zYPH7#8!DRhPZ^F;V-qO97c}Au5`8?iKMO$$j&Ns|>Q}T4&#y}ZGQ=l3m-}#7S9&Sw z`4`^O5q~{eJ6!YMsWjMN<}Bc$IpcQ;Rg&W}a|!I%PPHRf%3P9_lQWw_!VX__)(qoQ zj+biNgunY18l}`*^Z8deF6yc4VqGzUtu4B%oTSVErD2EqRC{$djEIhym1KM+;F$Je!3Zhpf!wQA}-S3W2T8z^ld!=6-_FBiZel5?+gj!xt-XD>isJV z;I~YHD%-nhK=9&#S`Z9NV)vrS9OvL4yh4O1wQOFHw=&+`eLS>t?e%aS^gd|K2X> z>030oqq;!+oI7#2oo$)o0DT;>t2>!gX37L+o_~z&&6QD-BKiC;A)_`L3}iwlvG#aW zhaL%mBgjFqUA}bK2}8RsukI*c%cK(T zaqhIvzNz;NCrx(5X%ob;=ljnNRkbqiJX+1O-4LJoy9j@Cb)#W)A%2 z=exHwG{stlAGar55*YGT8osjAb5KNy45-Nsf3)kl+8kh|;8K5f=23f%Kumn7Qm9D& z`pqDMj10oHejv^Rsb3|t)cG}QrAAV<7ud_1pizAtadoc2S_Tsrx2sl~8U!K(CUonE zw>uq(0Fdu8(u*A4atKh4hKFFhf!7*{GVHIndZ+8A#{EwT`0K2V+IkU zEX0UNwc3rT|N>a@B5{Gm zNO!j&-5^MJcXzjR$D+IQd)WKz_dR>R=lh+19Ak~a5MB3uUoo$F%_;P5FF&;HE;V%R zAnL9g{aNHL6wGwW&L8z?vjmgo&mT!;T>#7P{Qa@R|XPH`a?UW!0yps)Al$qAnG@agaUu;C}@boyZvsU8o2p|=flKTrG@_bThX|r zp*kbc+iDY9S{ynGq{&!5*)KeZisekPg$fO2#5ao-EEUpO0UfJ67#vfy1$FKBeV>;U z3lz}AvcE4THqMmmdTxvDv-^WG+|WFctd}{$+deoaOUb@rHV5-cS^U%-J?D^#GX_t7 z{A8CShTybze$}k<8Y&ZN@FWic7s~Z5JGaU^>n+;wTkA$7KlvvgT1!;Yblvp;FSO0!UOppS@W}CVgFq3cXfC zc<={w(f^gT{xc)Ap~KCx?B9ptaQRB0M^FBevMU3v3E7Tkm=}IVX12@eIPyburc*ug znlw+VyvFm*Va>BM5+zuDe@bhwWdCF|MjFd&4q%R8JU>kc2+Q3CSqg=i;bow@h-@B> zcaV5}gOdb}u=l+6Q?P4&xr=Puut-oVAN3#MPU&@>Le;lX2dd!!bK?B$UID}?{)DVViueg@C0(xwM;b(2PnR#c- zkTmu``5J43O^ioV_?o)!qVLC>^yTWdKR${)oNC0EUyN~<$6&{r{ZtO`$#lEad-FSe zHt+;TG4s3e881&(k|!?aHGNpUD`!C!4UWfw;A`#MvryWWkwcvCYf62Deu9XB0vf7% zwUBSeD;miqR|y#aq^*~Qi}IQ%TmM@LW6!D&%9iAt_@kBf1Fd~%j5I|Y6mm)`cpI3} zQl1)Eu#a`d8v~9471Z{Sh)1JK3rdo)!~6=Z>N^u{#?plRGafgBF30Du9jqo7mybiv;5guQ-t5JE!M?3U_UDK<(V0(dJ2c`=mcKVrB#_6+uW(QfmYXAkEf9mV zLGLZ@uhL1VcYfwgQYn||dR(=z5|@-9Y}~~U)s(v5>&>!0qAWv0Pa4zu-hYK%^J+F{ z1~%mco$++XUxpy#y%M4Sh*^J>Qe<#-I3N+-tqSU$UGIk>d?K9QV zrf~murzX%z)`Zn5L-5d1sJ|$TT)d6}w5uJgVxE`gd7SRnd0;s9gUq@I^A;~g4-0L` zE{N1s^Fyzu@gyxZ-!j|vs*fL6auR3C>Ivl!`NN2SA8F^2hS_OVu>|%xaXR%kmURc> z3{6GhnmV5a@n|y9zeIs_**8?~2$@}kZdCZf+<$`- z&;VXMmAYV)ov(WfFBNQy^}yf6@>5wzaT|@|`%(k>KoTp8Y{scT=)>w!l&aZx{c$o6 zO)k_^>2otY;{jgal6!uRj#mEmaa3U4dd^nTn0Wb1mB~AHYi*eeHc@T`z}aX!kxe+v z{ptYvJgrqHW=l4awY$y#mF)!8p0Kzd6hFd;+>Zzls9^hx9Wg0(lG67rImSh!WKbt? z5Vi;i2mqVWG+nV?YbF+v-sRGaMijGDr&q5#giv47#TRusDebOpQ* z+*_*n{wK`;(Z~ENsr^}N|La4d{7aFL0GLAk85``^+HPIB{I8!&s*^I+bdhaB`Q(Z8 z_u{sc>fg`N?x>Xd$FySOh2bvD1bL*hANxpqWQ*QLSQdiL6@#epqi)T|a7gzcxZIv> zyJfKqoVV)f6}fe-%>!|XBQV5=`Dr^H&9|woWj3`4Qkjd7O%y+KUCodatcpJZvZ{Vg zEFVXPh#n{`ii)z=<0=EoA0JrirNFTg^sBq4=aQ+9L*se$1Y^d4StA$VvTEF*#YRf7 zGntD_up&mgIl2RMM{kHr-OxTy4~`g!c9U7h>A_gia8ac8ZRnY+vTH-v;_Ig-{4nOP9HTS0H;Non6Q z6Wad`5+Q+16c>&+5!UYVN1S0X8hQqtUqnkLoGvfuy=atsvK%b^Gt`U-OsoqLh?802!3*Ca- zzKz7-oK>*9ZH6irqqbDHzrEO&Wq2Ch9|}3~)kdcyBvQ*yj^69lw&_aKD@s*r{QKhu zCv<#y;>hGL>^^5eKf$NCEk1|AMc>hByq5Wq=iKLSuh1wI*N}y3qXL?ZgKPH zFn7ppga&4W&S%_N#`0pKVSRl#vvn+%RS?b)OO2kdR8?{E-{RjV1>-sN%i)#w06-P@ zPtRwE50U7^>0a3~wWdw5rLv=6S{)9BW0;PadaRe--T)CC$IzXPpA{-%j3hS{$9iKZ z(cD(C0cg8pM_jN0*%$<_+9^p~euROC+vE!ZssHoX$TA{;16xYwvPs}RwZ?w5K#hv} zl!ZDwJDXv@4|B5?_+#~$xj||1aWoR$RY^4-soF3AwAv+x?{=1D5b&D@^*83r8)GGG z*nay`&#_3z;s4`O{Oxi5`5{v31wKGzhll+({<9_wT>%?K(*uO#d8kR1^9dV;-xG_> zxg;JRsGy39i^xZBDij;|19YEB7M|3+44|Ofg>=l`ix;#k*uK(=5I|LIOfUT}w*}EDVh3 za;H=5dZ~?RK)*{@S8A$AO?$}s;l@z25u1EqFYzMmQlly_oK&o-tE@ZpSxjF)ih4hK z4MtkP#f>CwB_UzvOEYgVh_tzS$ByYzuvmTCKSKV!;xI5jE~5Us?U{Fxv~V)Omma%Y zdEVB$*zv#*e_@pw%w;Hh5sHNu|Ie{ zP37!o{0aRS#&}hLgqOxN)wnZ`egJbpuTq*?U$UO6UFh@&1Ou(DYFq1@+KwX`qN{XK zA!RG{TOAh<$cQE@B_0+Ycrt8RY|A9pIBJ5oGc8=wF1Kd$9~&p(sn-{PS#B?i!^Fs=d`wPJzutkS|d zfUbsdgzmy^*1E$kJnZK8ZUMwBjYFf8bA`8rgO+Ep_{B`d;`@88@J|Oq*tSzXLGj}^ zKhKKR~7f(38<-0CwJP*K5FHK2y)@Ob z#GxOq7M!rP29--D_U|rCF!*MI;rB`}g0LLo9JaCFDEq=*9(4`cC zgeiA_a1pDkl!Tf+8S0u_lBtI7mun1dJib$jxVD3l_lk{;ouW#>?SHOu&fh6bTeBM> zIH}+9+&;3WpKVoZPM#aU;Jw6Iha-|&yDp}?Y?v9_PE`a%)-MRFK_TzgAy^^}ftMxQ zS=I!=mqeCc_|t>CH>ZPj4h)ivMmJ^_UEV|jNn}8uzWa3dcEIySPo}Q!7v80vFFBE- zFc0*faZ7airwG{Q{>okPH`TeIKUA}8tO9Zd!Z#=He=4Sjrln>CA1lM)jA!tt+}%iG<$x|*aXCD2jn@8Ih@dP%p2W7!XL z@|bHut7i=6NDsf^Eea{609gTe@uaSamzDkr`N-e$|23796u#^plwbgCm_M8DL<)k) z8PixgoW0kdL<9GbSJx9XO5#ck;-7+N>t-^)BSIWl9{#fJ8U)j?x2MQ53>QmZU6q)vJ+1=c`RNJB zK=IAu_7_p|7)-R9#ej0AO?S_GbjEumbP#J~%NtLpinhq}r!pP9aqcs{{UhMtf zhvOW6|L}kkjw5O7hMLUv^i?T*G8~5(+0|4&TKs4-S0R|hS&Xcs!-Pq@`OY22+^ zL-cKTLbND-#vE`lb7_$`H#biMaw;n}(=zFn++!=?ip(rRC2y*3Y0k2lw$~8^U>~bf z`GoU2WjCVs4Gjy9KfOKZ-h^wik0d--l42%kr$Ozn!aSJfURd zN^7tS$6ZbCEmLa=JhT6h`*dO0K)Hs)?b@n$C=ptp!}Bv#&XOAcGDI{2)ncL;B20U`jChgrBW^@b4Saa&)-Kc z{suU5tvnHcp%!YNl#cZS3W~$(2Pm!nKNLz073*!i67EhnBrTR)L}Q#O<%x{H9nNEN zau$el%>Qq-LonY3m+AWhR~Zqu;hQOf<8_lh2LM&d#W(K!F9iFq0#F2A_JJP75|4J7 zivduq@a4R5X~>d_m+pXwUjNlkANv9|Uv8Q#{fFXF`0v34$Q8hs%?k(|>|}~q;!*%; z!z(T?aKK6F`bHV}OzNN&SQNv~Fiq|J(&Zf@jRx!Sg$p$zT_U7JZa`Jyns-<ihoueZJdC>k*2S=~;NC|Lgi@8st*Bs~Cd=h6&j zJ*Fmaw%Dc$MV&gw92r~RF8M;XFqby~l(NpiPQfyE#9c30c&oK8rJ`+m9K2b^4d;%9Qx1cFdev04?v zReFjOp-xt9ZQ!SDpeDt?zp{jag>`kNb~xhy9*Ej0HiwA|41ioNOujccr*>C-C#d&I z<`VfoSgIGC3DwC`nbWr?uU34+$11%D29Kme-7|kPbd=e8n0Kne!W!N9S^d^B80g+b zFQW{#eSQ;;|Ncbi)Bk`waijj~lYtki`6t@o8`q6|=&7H!ADe~ThXFEhvHDa>r={Ps zZ+B(UhKao^j7>(+=nOxIVm-q#^ve=xk}NS$)7TU>qRD;UP8YxqU|vnmOEl}vbVtuR zsjr@V$Nr4lVZg|p34rTnZn z`1g2)pARR-p%lJzXB2O&b%Jg7e?PJhkBod&B7be@9qy7_SN9s5W0hp@cQKrfj(oma zJgBWVmqfj~fG%eZGIVRO*87}Ag*6uBC84rvOq%;ggQd=8UC*TT_xS$`?G-Hy`9`B2 zJoS-z!|46?D&EByp3rDbZ??v7%5-fYSuS%V00kqJ9EOqUOE){wD-!w_&EX$m=ihRs zuJ<3q@Ug!-qP8+@EyZ#ZDbu9ed(JhBF=Uo4SZ|K;?9UkafQFUeL9?xT)iiQmkRY;# z%vR{T+SBVJ&tLoz=nC~trc;b65D?2^dO&5_V=@5hJm=`WVlKTKPfE&eSWokBheo-o zaiDcyN5?2zoAMy2w^1y31^}jYkNeWJ_W2Fh>^lI=SWRxt82BQBh?*|&l6BMr!_8Ol zDVp7?YtCV`Iv|`$jF%gvwYcYLni>Wz9|pt#p{S+K<-uGVx`P&53|3MCg$V5o*3(9x z^gu4kZ+p2D7yCWkaKTPRKs@Wncuy$yD;RLhFgV}#rL0e_b%dxgRo2;%S?yLgK;MQTLe-uXi5X982D^L|iG#5kzm4rZx` zU7qfbh5Y>d^q!w8C)lQ&oEb0~)FqSDfqv3)HHDjr=k&2it5G6JqGYgd8rc2xKncfJ z#TX~N@c>i|19x^3!sx?${_gMXO(2nByf5m;L;X(X_}=sU5U>T=yN1qD2hd_!v8e5G z6X1l)b~duQ|8hJ2C%p8(2w|VkA1AEvuM@V1^j}11BP+#6sH^K(qF_==SaW@i0T22q zyi*D4>S-P~NBsTyGqqYCoV4rxyIV^HpHAX67n8DqS!a<}?5%%6yh>_31wK2vy4!e- zGhv73NCbv%j*JZ_!^bq`uBmTcW(i870TPPx6ahV>Ddj(&WV^;IYza-H2ywp4fFsLs zE5Ro6@{UHD$KGzYXOj4vF#C#%OlU+;NT;X4i7lo~r4J|P=K;RHgn%D4&-@iZWKZN3x&z9*4)wQj==dO1nW=TcwePC~!u{$?{17g%kOT75?-#T3q zNNfo&_lMpD5BOC`pQ401-PLOf2T9p=D9+ z$Gxb%YW4n^L&D*Hp|7H$+;N!Efn*oU;9gJwLjcza3J0tOzrKfjslo5BkX5>%XlSf$ zt`6)qTin3SMF|qBCcld)`T5WC7oXQUK?jS@n%Up~v8dz)wt{<=?P%#ec42pw#-ja7SP zsz00v@J%mJQXtL9;$yc@cBRJUBX2fK#_cAB|0)rAQoWkx)n@B{JtY^9tXp>AcR{Bs zm*%j(l-zClaGQ1@Z%;vD(`Gd5%+poFf}fe)7GBuWub27$sAM4jtYqdA+g6PTZ8m$v z77-dt?UHLaITPl=Y~1s6GLOp@c`%*ZbVGLL`|iWu$cI09?b5xNl;VlPENi+FH9WRh=W#&`uu=ZL}E;2m&gCxh;#Yt`V#f%-RQjP-=_UMo7 zbjLY4Rb|>j|H7kJyD~A+`|Gc;C5Lb|*N}bIB_Oak`1M%Y(t_W3oXVQyKWa?(if)(* z+uAB!bm|IMd{hqVub{p84U$N}!^{1r5+2L1CgJ>Kj6NiL0;r?YQet$voztuNZ3z5m zVP-Y}(kjucXLbTng`a~?$F>F?ZMKIv!Vf?F6Rh@Hd;Q?9%v`52;Tpl;Mut43VH}zf zm}KdSw32v*2DFq8&b~l2A;hizDgneH=9T=T<$ErG<59J&dz_{WxariZFXX?A6>c11 z7;7=7%a2Tm3}O*!eXprGazl2g6H3+D&T@%6-?%~Yn|>i?izxJ?zd&x8+;q2=jG=A) zGlMOo5~Qw1az7g4tU`W2+5gI4Pf#4`6Hvwt#mveD8lv%?38ZgjH6Qp9jnjJb#DS28a}`Pd)yiX8=fw7!G$;nLA_2pIBA}8w3C+Hub)yBmnBWckl>{nrCg|!|;9Q zsw_8V(>kjGoiV_#>mz{F*1jFjc#pL?ykBJ7IRtQ;)9=T|a=D#O2+(I&f@%Jarb3YF zszdqk)y%IeI&IJPngUf;BMeXkR7J!-RXV%g{(PT$b%(03 zpBVWj5Lu%iP6Jt{K@1(`S7_f3N_O(1?wj!T$J&>}H=;2}DY-L{9i=)njN3-g?^*`(+tq z13PQPbH+c2eaMtWSnxS9E;(T#>{3vljWcb8!^q#B_Q%7i=3;_G+|t&gi{9K0z|?jU zGH-anW_}TD{=_Vg-eQ5qhKP*wY?L*bDJFD}Ht&;X&RSZOA? z!waYltiNTzhzvsM*+;_WwFZFz3;iutG$h^JnkPotO zyGF){=0?Kl9bw?k4Rp}6J%NMZ_qOsru$nuaOX+A_swvI3BHb7VDlKb-x>#yzSIaGV zX`(RWfgHb8cS}b|q(qSJy9PUHGfie8vvTIO0n_#B#({2&AEie}A>RelsCT3AOAZpj zySVqho3~)5au#-i_vNLP-Gb9xYrSdqEys8k&$r8uMZM${8z>9MaT06XXo&(s3#aL~ zMrpTm#}_`-Lms1B<<;*sr4jhf$qC%U+u@ zY@(SJySxuhs>XNhCrD zL_cOi2;-~fAEl*Kb&+kU?v58;?l>NIRkuw~*9UWuG!OTQ#!MjahzP&_%BTYA-|0D` zZ4a*nzjk;N6-O)lQrHftuLZ5XWTdi~TZ*7G@?w^f*az>}M!Or=a>yV5D&XyiS11uFszTfVAC-2^86vD(@I1QT$a5 z8bnxat+|LDcf>9{>m*IA#-Mz~6dShP(k&l!FrHkCYtyYxXSa6Vy18y~ z)+LZMl0Wge+h$L!>N8sgBW6=-BX&SglJ+suu#Gec_cE=nsw#dE2WHP_n`44O!s?Jb z0L>#b*!bCm7q3Kib7*oph(wmJnUw*v08nQYh5{-86ED&NKy_I`ff5#-I+BVwNwvry zm?;DN*uIpEJdc%rF-Wct$9ntqSxz_f{IKYm4F9o!?Q?^~f{Ge1v04NcRYYD8V3xKxGM)N=1-Pl#cg35UOmP=-&RP=zkM)t z_0B*-OY76T=0OT`eC-w0MAH|!3;G4kHr&QzdC~6OXDf0T z;O%d6L&iPNji*S611=U5+4T(JUZGhS{AUeoDi8Slq0a3D!C${SipXq;?*GTJX(aAz zywCFDmGc0O^|ef>IEkEs9Mbjsg6@ix1_7^uK@?ATqUc|ZYfl?{cS2M*)OdyiUvsYQ zpz0e=&YH~U-Shjh;}aLfoG_1|ykE zPRi*K4WQNcF@!#(1fY#zDDhBKx*UzJJ zSc2(&+A|ErI5>hEzQ<~N9qaI%_QBS65Q12y;Pu99PDbMWqKHoOj;__wMc>jAn5b3= z^2VBxTd^1&6FU(RlMVD30c~f3b~Zia`Igl6?>RN@H~q`YCCx$ITG)!Kv%uM_88yIg!u6nX8Fdb|~C5Yugs1F0X$Bn3M z0^`QLQ+XMUo54Dh%N3!Z3{^#Iq_b<4x%IUm>J+23vHHAKDC~e{pTfOk?{Tp8s(zIr zKH}L9si!JaX@xb>`>)_=xF)HO?+t64Opa89v)#8InTL!OIq3!RpRMG-!q7AlNvxya zCtiQ6lDO}HRv|?L*?d|MF+QcF=+3N7Q+v59p&qg(r!TbG=j2m?hlE$*xv=kkz7<-B zrAgO%&SkV+#&Sb9w>s!AS;%%rgTO#9Tf7#*_%Ozc1rJ-5`4i@!75V>w58m{`8Pao@ zQ%JJ!!x8%P-bDI*q7#3_>2aUXs<#ZERJ8!@Rd9>|3IOHFPmX{I7v#NW3@|baI0t?ZxF4Qo^p1Y{$de55qQ5ok^G5>_ zVV&mf$e4_XX}$7PO|hy+uB^*5j@@rI4JH;GgYgfzQq-jx3pA$zjT#3hz5D5^vSTv%1DFc~$dF(`n;}q|&^~q%>IaXj#$=(H0aMHcu9eo5 z6ibV;y3ZLQl6@1Nt*6a~zY_s^kw?@H;EgTpKz3qy=m7^}BDJL3wPX!Ay1}A({=v0* z;4o4}tc;=X^v!gyp21TP7+^l(PN4kH+5dwS6R8aMi8|KjRVyauwL7BF@$>4BX9k3Z zR)1fu0!st8bx$WeL;oP-&v5XA{;PqX>#+10;9)D!0q2;n*^|s5hGlJ!otbkP7TR^( zbGNz!Dj-}uy@U%PI^_$13cH3W#r@*Vl8!e>sAtysZII#zhpN@yxv4fMOs{XsDQ&wn zI6&Z>T<>vAw|}F4vHX0c8*L=)dODiN56b>QN6*&SAB`a*Y|J{C7(H6qQz*Z*J;8Pk zvirn&cv*iZif#e4)9$P|BH8n)1LDZu6Ue)ohN|`(g9qdU)H}LNxWBbMag!Mxtb|vB z0jKxLp&HdAe-F#00b22H!tW`apDz`H?pL zkodVePQ7_?VowlSU@OI_zR1Bg3V04+UptNY0JYs%w6NUq*<1Dc=pBLcMcx!o+dnr_ zyeNS#a7)R%F z=1SQRu&(?t*uI9kToE{0FLlOwI;<}*-MbX`j*mkCyW8(y2$!pCC^K%(dfiCBNIS^h z3~kqWY+;Y2Hu$|22&0O%HtFQQxZ0V7hcJ}s$kO$w+q4%4wkJ5{kuuX;zuIYcm3UqQ z1PyKHFG&wqWafH?*|Y>@#xSYG74E8qD)1#G&U5vxhQ^O@IHTI_LWNIqY=QN3FU@Co z5=~mR_FGu5xdH%7ec|76#&^|1%mP+y^fWW|~l zp^n_!^#HuzaNC78L&Ue(;9UXvpN1Y_}{@bl#BaQ=W#M53sL5DZi z!Lo<+HBng=wUs|#-U_eC7V02pFKoA22v5(^J%*x9PB3{a+FDy$4+n^cSNyEWGZ1(u z$`05leXdhrb=nBF?axe14FS!;5~Ub8Vmy4tQ|aEPLka2{-(X}m71}vajXp8ZF$nl~ zqaL(}4Hz>+-5*dA+ypo;Sr=^DKlc_of=*0h27a7I=nu|;8Lg?NS6tb?_=SgK9T-e z-zl<@3&Rd?SXp7+U>dejem+Fl5Y{!RYW3J`iPhnKE^g-w0vFVSfPraRl0>oewPU`0 zRjc;OAnIL#YP->zJWYYf=1H}+k#G`vjWX)bn; zPjxCkBP$kKan!Ux_JOw%nDv$lXH^eUul+Bqo|s&@BAo7I)3@3+kGEqB;v`aGI239= zygIA6ehfTVx;^MeM;a}9IXL#QQn}Yn~!|#c5Bo&#a z3ghqYMbjd59FMpI@~9`9U};8WoJmif;KtJDzm-${!JrWT`;hc+aYR;L7^oOWH~=Hv z90|*LTuTcJ7LQHYtLwf`P(oJh_X}ZbQKvs#6o1<6 zI}v}d)XLM(&xOE!7_i zD)1Tx2kIoILy)+Or9FkswIl|EtA}{rm>#=06`pUdd!P|TN=oYOyes{UYjRCjc(Z5X z6-SCKpV>a7;3$)hd}ksCPSw>9{JQpQuGTRB2&LD^^>U9%eVXk^yBv6H@}yJAp9U`R zraOXcpsHKWX&gQ7$k87g%$|>4o?ZQHt6}TYZR#euu(N<|ny2;2VV%_5^J(U~dT?~X z3bUR9c-wu85wA2ykT{&HUpJd)d#gesU9x2yIVrpKqNUubwq+jS#Cu#q2PxKTa5*EVDkQsh3nG zjYNj~vTWQl9G z8_eygUfJ7-dhhuD7$RhaY|nrrR-TYAFuf(IqX}&O>^swjHWc|wqTpR{4$sv7(F$TW z)lipWby5!VkqmUjIK zRj~qM_7FLGZ}V0}G!pTT=|V}WoF8W>fs{WAu|g)KBZ4lpBGf<=M*h60`euJL7f~B@HM!sAmPm|}vA$DeRKKu~Y-Mt3W@8RF69469-9OaE+6Wk) z6&05iDk9r_Mx8TG5FE;CvONQpMPC&kIj3E(S@06^ z#LpBYP!9MswT~Pm2353yn?FKu$s*BHSVQ5@g7*_^k3(5Ur>;Sr@>rPeG5FU3LY-aa z0nT4liJk?|6P>qt0L0?JdAZI7-$3a0^Tx@$^ed5jMh5>Zr0~=jTCUO+@A}Dj>ghbA z`UKZqp~^}3z)wUR>l;jO+^9tBr71?qs2^Al>6&Ny$XKc@XO#_6wtRS&hn+8cTH`)? z8Ep++%*|pXlDA+$|WS)>-0y7vlWI9giq@3|4P7?X1rG{ao9`kb;gL z!621Zt1Tk-W3$qhpsn|_x5wd?KftiJjd0QgosiR~;F@zYqq?`8(5#1l@E6E-&@$g7 z1h$1Hyd0lHb;5u4| zz>v6!KNAUl<@trBG{QC{G*lwj$bB<&u7vLMM^PKk!!)$Z=2JlC<+Rh$bAMrJ$72^a z=N?D0?LqKbrRkm+(Y6N%|C8rYEY69@La)hZO7VJxpW_bPjw|qMEH%_4+eQ1&2#Y2< z9|7!t%uF0UReZbE-h(_my7$D~qKTuV41jCWxV`WT;(-#@w3M5BA$KG-4b&E6M~eun zw7jj4hI)WDKuWu4fQITcN2NoG2i+A^-G9Hs(wZVHIk` z_$p6`N7&#Sy#3lMAsI&2AD$2EUlP;ri$Sm_dCJBY*iD?#M0@7-ojT#9 z!blOntVho_(c`smNqkRee@)s{WPRp%-p*w&h`m(1ED(ZRixOlj&IVi(>i35mu-Mn# z5+2K$or{gAWq0iciX-1~tt_7G=g{+d!$P+(hni$ixh)gZz*_=+Go(YEmr6#=j8u%T z*L>cjRV0Hl-rQT%6nvy`F672LC0Nl`d??vM9?+go@1pQZZ>4`LrKcZ4IdtAak#mS# zNOs+L{Yvh~2bT(JM3H4NwblEk5y8pNpkcBa-UNYT$5}gT)BIG&_hB?zvh=R=!>+S< zSD#{>V-itGLbU$U0tmk>^wM$Wy`{&ij#-9fl;~ZZJf9Kslo|NCod^a{L@xf7%VUqt z;dmT-d|E%rvr3zzDKWot$GzW#Nx^wGi-n%|J+?0rc1ZYa!T=ZVoglz9!1NxlK_jN&G~yid-w_|692$74Pq`;_08j}uSlQ9JUu-U$*F716E5STEt-;;tc0IwYhDa~%Z)c+6S5Y7Z z?EjV*7d92Lxu=p(TifthA)}}NFm)>1c!hjAIojDp463ioQ1tZC_{{Gimn5XR)tn zd}CD#LOPK{8=kkiJBmI0F3PfhhbJ+X?R>*>Z9A{E9v|{Hq9qcm3<>Kl?L^E{x zX=VX+0KTXE{v8~a`*KjrVj=%EV<}DSX-NEQqyfu?lDhR;tu)h2B9QN8`z6_Up*ZFf zZypeC^TC@w@K&O&dtrP}dahDat7z#A$SZ~LbSO&>?K8<(0b>KM?3cIHqs4SWzuuGI zO@*OreMq=j%kuXC;09pdmAW<`T<^7BDgs^hSpqW%Eu3_$^3IN>YQ{%@VvY_`PIRLY z35PDsSU)Zl)n1}FvRCDlHvESQmZEN)_S|=0R9UKzFL26ym8z9+^G|IRJ+~oa;;yjZ zWAzM{a!S?*M^%;DDNmugYx^VT785I~D!mUM0V59KkBfPC(QK*=6G7_BzHVEs7&udT zUh9ncK3$c7Yi7OD>tLWWras>DNmLo^U+CpD5*CcbjT+4N4&?JF$~d9s_e(wZtLV-K z3m;7$w3(8#SWm~rp>Pya#685|QaTDbypNgAqJJ2E{r>36VP|`v;?B)^7yBbimWzxG zG0ydSVj=(8vwJ#65ch}E<9%*QT9eK$XnvdbVi6lSW!3LF%7@3ePIuhbGpRL{D1pA2 z3LX|1u!l~|a7ji0CrX+5DG`0Wyw05rE*<$vn$}TYaTlh1=bP=`C~22}rKFAR`tdjX zD|(Ec`=+!|6ptiktVL!Wo^U*&w+st3VR5&OrxJvLw2o-E46vU=cy6yD(*Z+^##*da zu&6}b+Hv1uV@*fh+KQ=L;I?}`oX7HjP!}_!;Ld{c+nf7zmv!yudz7R8gqxQxZ8}+O zZt8gZ3gv3H%oV<91>Wl>yvBMtR81Ki>UuU*8{PBM;c<`F9w38l^Ag7B${iyCmt}?qlmVF$cX5DixFpu>5nlm@;DS;0SVhFJ;rRDxXHANtQu8s<)67C^F zLoqk{Nt;)to7%Ha!rn6MSUI`d3&98z|PS*WoHZZ1)3qeHiq`L#nIgb=ld}! zBS`*M&O`NW2yMg^B?0r5TMYPj+_i&TM^bw`ss5{DF0*i^5uq`Q80#RAa0|F!*EsB6 zLQj%r=6t^kMbG4m^y&Q-d?;;(+tq;k^f}q*`#lOG6U{2TyBKSVfMvTqqN*AxcFZ`BmJJvPbOR55P8 zi{swzK-P4kVr3lW&wJKtZZg>rTIfxDfAvJkp*P$m!fzTfLIU%3P1Y#g`6D_C(hiGs zh*xh!>q=zW&T?ax6Z&U{Ij=3m-_nNiOsMqQLNZnEY!0L&=`HjFSyXO5sOntkTYLw` z2g*j9r3}NG7x)h{v>rOoSDoJo<{dS&`M7eHT}-O;5E0#dyaj}?3=LTMZGj%zw`=S$ z`AYY96?CSBMNiD}wwt?Ksx3kThR+XT&v!T~C3Lkz{>hE&OOnZ;HUOonc!Pm8WV786 zXT%5~Ii5xHtnw7|ZNX&5C+Dil9E?u*)h$;#s{xvmXd$5<49a{*jFcPT}o zL8^>VF1EN<83RWk0~_>@bN~O2umDyL0R+HIAlXEKGm%*_!s`VD;ERfiPP_BehC&-U zI>E=4y(ElJ)oRDdOH@=ZfK(Y^4j)_SDRz7Iq+h<#`{=FJ_%Wy=XK1&QZgt0miNilo zV$C-vsrk)-0fQJf==Oob?Ka}ZzWl-)(8?AuaQ;sQ~PymJjN=|)ZFt=m4ky#`yw&H#Cz zAL*&tDI=U`$owq@2a_Pa;)JbHY>4 z4z@H_f{jdnz01k(rCKD!l+d&uAM0iw1*w?ch2i2F$H%0BNLb&Y>9ATqw3jY_4P|JY zo6~o1rtJ`RQR^e4M?g`T({R8*`KJFNSW)@|JR{!fIz(Cw@{mDP)cE17b60Lt_b8V1 z&$PhgRn~_C4$T=7BP_xS-5g0P3Hsvfo1p<@a6N>8{GNUYL@uMusFTLFzbE~957Ylg z*jon08D(3bxVyUt32uP^jaz~f+#$FVv~hP&f&_wV@Zj#j-L-KD?k?|R=H7Sjo4Paa z4_(z=-Bf+&?6db-d+oIfdvC<6E)8_3%a$|LE85lH#*iGBn}A`pzu~bxuB%9C%$CDd zxSuF8X!C?1v;2ZlS|ZiZj3}LSgnZy!RpmM*foPYAm2zhFb3qRlS8S0G52XBajpJOj|`r6syTTzS#?%R8AgS_I5&*Qiy zQ!Q-m?9NU)zvr-}KC#)%Q&g%$YU#Q2^#pT>I#RisucME`VG zX1UY`W;a*($v(KqwcKq??Vxnasq`zv2etFhA88j>Y-O?;8%=_30QE4sxF^qHvAD;4 z!demDry@+~5W0MRyi&tgO3YCii~j9Ml_8b7H(y#mo3I@8=EigYSLEa!x~KJ#%^(7B zDJ8Ua>^2QqR4ocW2cyW_j^= zqnO|l*;lo4ry$QHbM$e$7V~se2ul?^@O;N}-)_^}E4jbrvYZOPUgbT_BY0rq)nQMv z!@Y6_c~mOw#sLc9t>-t~xY$aQ4$%Ap2lb%}3P^s4~ zT=mH^V0PH>`?nY`-?tO*OR~gi8g4Vbby4;mDZlvkuBc}gx7e4;3$+`em-o2g=`o(v zKmYP=f6OW_+<+LF#a$(R*~?F{cxwEZaivt<>eG3>XKELNepMnnL2zsEE%S(ASoUt{ z_p?dV-~_Q`KD=pxCR0?0H@N5H183*d)0~s4WJu>p20UBv{+COk9NbT|%yz#F@$XV9 zr4?gR97WG6()H46D_#RvivKj}Db`lMy_x?}0Nr{mE#k9OYSc>Op1z`BY`Ixx_a_m< zx2VgF&e2CQv;8Z1?u~DpHKW>E#3(G@_*q)Gwjy2|#qz4|dR?A!BKX{8 zac>rd?KTY85YGS5L2^Pk-Fr{oN_@Y`nKUw`&OO-~l->fMZ_Bx&WpgCX?6GoBV z2QNcRpN@}fW_Ei!1ihplgo{s5`0vE{G*|Eg>Mwc>pZ z<5+-|ebY&_cp}r#4331iUw((lcalyB6uB}$k^9Gq+wB{WSy7W`IX=6o!zxJ3jn|xE zNlIE?YmDvw23%1A(a6xLKHir@uLlp~&i%hI=y7Nrv~ZSkuPA-TI%Q{fwSOI()qe7* zZVd0xyZqu^X*}m7C0uFpFJXZ4Z!jFFADAgxwY(cn(b!74aiE8(vsOqg-SK9 zy`@s^*&ZK6ihp_)+o~%W_vE<7Ba;B7?-avWFVF?H`3df65ChyLQ!O*OeK_k%Axwks zLAmxR@V`N$~;*fA`eLs_sP~Wh}ZPyY(62>b^jO`{SCK-Xf&2Kq&KamaAallS?@nYg2y9NQeRI z6W0B*;JrE8oe zZ0sF>u6#vZs*d|g+jQ%h?Iae$@VV`z%vg!@sNngf&4O3M6{W%0#E7Yi<=E<{m!75) z(C-=l6_5BQnTf4#AeV*3*x>RzXpGW4v%RhTi_E18f!66@T&m2omq1HkRDd?)=DJas zt7}O({z@b`B-^6=BMU!nM#F@6{b#}ykF7Gbow29zM5%nC7fQk*N8^C}{F9+%N!t8O zNWyM8HH|QK$-WwnNm22k1(@_9HE9ZHICq3d3ccZ?pd?fFh);eny|Q^NZLu+mqH1AF zzIDG8_%Tp~slc|Wfp0$f*6UWg&?b#4;iEcc-$%o@M>_p61%#%;3RFj>^aidaH$v{w^O6^vlz;AS7 z6bByultUc|4W8Uaoim=<@Y~86blutw8voVW#b}U!rca#;KHHWiX;=CY6YkQ&%}koS z{COj2{YiL^Ctr>4D>C9^wLc))QC)B!2v$R&_$j8i#COzLYK6LG65GE2Ipz6nC(6;w z`P{yOLFnzL<~~bR0ckc8*BR5xC*56{qJx)@!*hNgRF=WNtcB=Eu7*=SD^h41skA>h z{+8qQoOWhXt*@(oJWB`9Xet#5QC>BEA8NIcHnxP7+Cn_O^qRId)I)PsJx*GG%!@U) zROZqcu8x+9YnZdwrH(?gr#i3c& ztX6t}8y}>eMzyd_{bld4`NJf{ z*kOb7%o7V>6bVgb{%ntxn}{64dWyQsa^MV!A6Lzdc07Y66zl(MbNx_4$~>fkbj*P{ z76jN@rsW@G7t~6=2Wn*f1#V+2rs^@FLL|gnFa0Iy$eSON+aEV>-nWPMNj$ZxFNpd) z4YF!oy8kMCxYpM-*(*e$HRTL|CbrACy4Z5cys^Ks*U8<^C zV!-PyXr)Ke+46;fB4%BzUxFpQm1YIWyTF^z-9o$pAFz?lw~Xqc5~v(5XdPN#OSxYF z5XO%5-CV!{WqZVFXK~j2<2sak&*{x?E1F1z_T6P-xhn9An_YeKKBVtX?HaA4$Qnfk zow@v|r!AgaVD3YVB$WkjRy$-*!0(@PP5&WBbzHvoKs^i0)2glzeUz*8$T?qTNDEI- z%6kD3&&^jg2@khF(L6xZ7c{ZCSV@%~y{+&d;ym;!LR+fYHd+ zLj7HWy*{+fnO2U$&28p(NRNYW-$1U#_Aj2-X^7LGSkbq)Q9M3dk) zoXKK@=^ARWpGZvY|m!PU4tl;gyt1SgQjWq>{-nR zfUPRU+)~-FQGZUs&b_UQD?>3=2kMz6#rhQ48crd~!6LY{so`Rp0_S&jTDtkq7z*;E zA<|PH|L(`0Q#=#t<|gk|@7z-yHo2<;N*UDaqSDCX=XIb$PcWXdM!05qdvtG+^IXFD zF=Qwj()-)|wz7y1;epwgzzr19^OAB_oB!s@$J>4TUDpN^oQIIPwdNZs?aX07g_h&4FqNeFi^O?mfxIZtM06#2xaf4ZN7$Sa!{r&x z)f!o9q7HJJb1g5jm*Du zPH!xzP5H>*(MzkhwsBZ`%Xhn#P<{Kigz1NUiRz8;XkmbZ*(Ihe^DO7XqX_@SZ4=|C zP+&&i6{Z-1UNoor{#7qw@58^=eE%ErOqF}B07rR|8esqiPkm~t7lFV@5_2;CpSbkS z=MHP;`w`uHTAEynC@!<;*S5*#0x|a_(YinS1(etNB#Bcqk@>V*y_*O?U7-igB^c)9 z-WPLk_R6O|Gzu_W08sDIq!KF@GuZcZz7Vk%0=?uf?HzBoPsK{48P)$#0+PK(BSQDT z3VV9!TVb7LFoDlGS4FrD%s7H=XT+ z*$PjdS%aVWF8ZZ%D@1(MT2$UZZYZQ zZtmtbYtEOpwfsD&mwYn%b793B@;+CGF5>;l*jqH&HVCByO>Ve2fr{>Gy*mHe@N<9P z?wK2}>Uy>CN8@QeQgNfdhDkT6^{!hw%q-;eF+9jV<#b-%YabFK%MA`vpn^ykqFQ+S zQi}^n$cX6@m6qA! zr~0{;MY@BRj}7r|lo=2|GOphWf%Lo5172lATreP={axyJyy$-h!!Ly(Qx#)s#Za`g zT|YDEBk7fe5|YKO9N4uM3#xX$DJ_2pNyg0JVAh?eLx%I%4&RtAtTGZ1-bec;w45nr;Z}&!*ZCJ`s-(%Zh&FLKr7;+~8$A zctz<*?lZ%a=anqzi*(iZziDm}Q(6l9o24sYzcLIbT4+b-c2I;|)b2}*dGvCK!Zc`j zY~J~G7UR;#skOvTKt@oxtH$<^3LuK(93A0pV;Ac@6|bmg&Wq4otq?|ojUNxgu4w`D zwVv>s++bJ{?vn`h=d+CPCrCpEQZ8Z}3+14R5Z|?h)a`~tSRxUJDHe@xIY#Z91`^Z{nCMsWQmOE{qZM2*CVa0w+Yu#QOH)hv5-vr z*8Bm$s&Axz(2P<8VYqu*U0@hi>*K#fwA=awPxv+%i6_lsS^Bzk?>C7H~8R0xvumg z@4&|^u6v#{lL!Q=exB|MsDD8UTiJD%1;LWO>26R&&Oz5{qP+H^(=#`9DC6*-@zdAv z>q??ZX@)eZE0?uxfoaniIxp-Lw#Hw|Q8_O@QdKy}E$;~@w>eS-!hC^yKkL|aH{wfA zK{IF`S-kj`%+cJh$LK}!hjdfLma!{(5yrhCFsl`;BdNZQoHFpBtro&xF zIMc`Mm_^vGy6WfCXVqWsuhci1v-b$!DpN;Ma zuZ^Vy0y3g4)POiX9+yHUxGTi zlwkK3GI#>Unzd{}T=``SnxGZ}k|ZW%2ygVew0Fnbrz?J)wBHfGmuGJqd6)gld5$bW zK3ys1|CgwsHtyuG%l_*e$EtB2({S^VOKh#O@Lu9ZSU;En?vU>*=;7XlHK+y?(z$`DgIiA~#lvWY+jlyhVVMn;Lq4z>d~_qTH!W44?HH-n*W?m zjnPwg_duTgkww@!a%>9kJ&sm(*Uk*cr6{doLK%$`Do?RexFDwZ6v%&N<&zOv`aFd$ zExF++K)-D{!CjXX)W5CY7?6|UW1M3A@e{&Wbg>=yTIs$kc6-}eA*n+#5Y-jHO6u03 znaRkRT=DneL#CVok;iIO?kWOZU&GriKpXb!C)%|~g|FMAhN1d_p7`<>lpyTOhgy`Z zZ7zK95MrFLY$xZ%gm$;Bviw3)Cv&o6d8QX!T`&jDPgQ#({(-tNuMI*`DLGW!VMxeP|+7j5#l zlSE=-@F>h4{KA>gT>k1oS+FTYfkYcW7P}qbl=uvy*EI) zq%HywGRudI`PW14GpqHR^L?14a}4OKH5^K=?i7Fi5XAKtm0NuFAp%qKO!Dg?yZ0o+V=li`4iaBner$PPXa>t@IIIbMQBg%`+$rTe)Cn3y>|Ek432#cX zUk-dp568EX!&X{yPT%F_EZd&s-Stpk&|C-gV#jqT0_RF}zBlmh${pS_WY zo_m}f1qZf3a(I=px#m}EB9I?&=sI1{rI~5K8ds<|Ta z4T0q*c!3e?d{5UeGmPgJ_b!N4u|z!ooWm*>8Mkz=)RC%Ek2t0El8ALRGE*Ujc(DUIo(ixnz@8A&)I`gCH?Z+6I<|-UMXM@$Kt;LuS@!{eto6qp*^TvC#nFDS(mf?eAj@i^wL5L8{y9#O-Mtpo_( zP85=R-)ANdC)Il|XxyF};trp$q@L--AnoaP($z>r-7}IX>l(YeC@#6lhfL-x04hR= zA_sFN-2D@5D~rcY<`Ive)Hml-(Jqfdw3NE2A_^Xui*kH3FCd_#!B>SqX5($xOGLP>dOpg?7cld%{ z{hbdhB@D-RbLiIMUqz>yM-!poM9RzU=kOjQw^~ldai#cntf=K=9EUsqD?m9p{t=*k z>Rka+Hicrysf6+D&rcxRm#HfV=oVB$;2vi}0KvtT-%vFe%Q*P0P~FkTxBJt0AS!^l zEzR%Ypb$&HFf?WV53lZe^OzsD6r*r!T-TkJ=MjdC_D%f;uu>mG#9t1Yk%gOUwA) za`4LXD;w4QAo%!iGaMB48ue>w*CLk@B(9>;x;H0}xsba^HRR&5K_C``dsej)4HcfM?{pf)Ul>cZ?t&`C!+V+xKnAtesRXU0Xp|G=No)&f z?k5EiPPH^4skNK5eex6tIJgcHg%|}Q;nuZIG8q$H0qlN{eA6~sa`I2DbHV|+VT$23 zLgz!#71TK&-++^()iB8oEns_e07-f*lPv8HDN@tlF5kZD}d{ zbk}%XyUN4m1rLvHtFOtZu1|p}b@XJS@+6vyL6iv0$X=Vt-@=ZnKEklgJ}G6EjJJhG zOgB)|LkSyLv2k@YohxU%5CkU=1cG+u`m3cLmjaTZTj5R}AAeD{@YLrN?7|up>K@@W zcr1~clrxnnTzsK@(7XD`@{KJ?B7DqK3#o~-Q}Pe2b0?f9vDWp*d(p0TA^x{y9m0nT zndkWw95&BRYx>id6V3<3ez!*2;5!%FSVRtND4ZLJF@({ofIVx z8BM#^4ycff$e{0 zE(JJp#x73R0@}hd6Z2uRKGoM%daQn&(4Z*(Bmy+FIdAA_DuSG9zx&EjAdcI}-<>Ll zgK!Iv#S^5XUM@5h2-*#`&U4m)?56%preo~W3-iuJJTj%B&kl&2-M#i;Cb+`)S#}tk zR0{{A!P>8f4n3Nvb=UKgvl_oY0{jk3W**ki;es!eF!;r$b)6-t(b+Fl(mkNBqz22G zxZMm!uYEVAALvKINJnmY!O6F~Z$0D-REh#&0!6}0{T~xtx~Qle|JtX0A>i)YF@0L# z(0)$q;dK}8OUUwsJlbo6>n-&ray|rrNe%;t{tkZra~-5p zU{?})d&_Hd*V_rgWqR{q+po&k%}$$bOQRxYfSusbh-P(%`9L7pRqtmB_yj0qBFM}0 z``}=N2u~rCfz6I!D?>5U+Cj&4AzBPYLo~sSt~UKJ z4^^j;k}hbxI`6BqxJuV4wknDqvZ~}Tr!ikB3lo!VZFph(C`TwU&*lU4y-zQ(9S34= z)O#R8`nhn6ayL_9DI?X_uYrtiyFykpf0eD46|0KVWYh?bgZ}Ygj~C+JI$4`uc8a zTyDJ5WRx2pk>9hCsO3(EKhGky2X-cqYi-v!c~}xw3qrgORabB37g^mZHUZ-qx(3~g zZg~Ysa!ifj7_Dsm7S?Bz-w_6?fQ@{kO!xay)+)Stn^h8b&n!#v&gIrW0zq8vZG`2X zRmbG~y1eY~HSITIXstBf11&VCf>6DfhF9${3MrK#h+eIFEmIpG!r9V3&j@T96SPzw z%MHMQPR1rms=ETLhhZw)lh0d(@QE>87n%dZPL%q(^B?{SG3m2&j*08cHgA6yO5m6l z1PbHu$Ztxx@pyj1Gd1;*;_4ezxZ~Mo_BNeU;k7}X+a(QEx}g6#0(ih@DEl8|v^MpSi?7CPad zv=8PV=pSY$uY7uF08RxI?#{8MYX$s=#$63AZ}54#%W74Nv#d4|`vqg*L7(g9y&ZDK zwfCm8TVKYe7C@U9Tc!ZkBBgOllaUZFWh&(sA9@xbAD(PiYH$mb(h-^5k0ZZCXUX`#JUp^m&QEUz(EZ)9Bbu0_ zb4lhSdV1s_;>p2WR{R(W(3?9coi$UQ+yWrk?N3La7;&|9cvk+j5&GUuVA71xmfzdQ z9{jnw@Nchpyzb_iZ*nTUT=+e;X}8`uC(SIhZmciY1A=An_n*+2%~v_9KD&r7;xjY{U0K5H5yPd3si#*;6_Gqr(PFRN z@-<5(_U3DOL_cZKNmTasTXhTA^N*QraQBh=g zPVxf)((rJ2oZ_(c-~->Nl0~d7a51NUrGJKQf4UFT25FiSv66`%ggGZE1n0cPvD}DB zitVJ&k%Jc^l`0$Q$DKB227druwlF z2;QyoJszNS{{){|bfqxEFvD-`h{}4{X?w7}#47D{x(>yDLO6_XpT0T*M({(=k{nKd z4lOonL+hC^Tg6&>d$7(sLSWB@qGaHN^mQ^Nb1RmAM{z{0A zJY!&B@R~oO|3OmNhozy;Xb?S2dW$@>0YlN-mId1e#(R4R8Gn+8Vjg)6KQo!u5K@7! zlzbq&?(W&e0_tgB{z22s!}SpqrQdy^HaXtIdb1#3mtV@8AL#q zxI%23Mn_SQA)eTp=QvlZ4ok;@wYK%H*?vi3V*kv=^v7Mzbtn{AUk8mj+kr>nKds4O zJkp}%O$U_SvC9IIsy;&PMKxct>{JFLx@Tp5rTCIyH#aC&^j9U zFb8+o@X~PnLgGuc%%fX!Xbgidb>z8YYjNAl%PBYLdd=eH$Meo)fK}c5@FixlB5Zy1 zw#-Xn2%D2n|Ed_EH9kxwYJB~3kTepX-CWvZ4hKUmwiK#;RL%2nc)0&t5A1;aRu}{< z(G@sy|KqmXMXSy-hRBdY1^4O9JrIrlNAn1rjMeZbIV<|E#3;&2)>*K952@qbhB{bN zby*!UlJM<$ggx^|*@!L7FEn6ZAg8@pV>J9ah? z9XKGW2nr<$y`mWUI;qaazXm_Qs?#tcX^ zRx+Id(DV=0E8*0!rZBlc!KORd5TdDdty%xF_u?D!M($lnb+%sPb1Z zmn}X99-AJ~^D-AQ46NL~DJ)Ux|=Rh}Ot7=NQ#Qt0Un-6!4lO9%=fP0{E+LiUn=(%=w z8F2o;(+SArd#5Nin^E76_So{KAMJa2?Qz$w{TzM<;_%yIu2LY<1Ux|=xz2%I7yiFU zr6zwkcJvF+XuRO#CcGI(?X;KfI?ohICwi}#^PTiPyq1Pdlvy$gM_?yoJS{-^YonPdCW zOuc-2>o@*OknB#CD0#*nFUH2^$cOLyo{gz)()`LL8u%&oYjV|pDMfrSLNUo5l1`4J zN3*xrV84fe%id?jZ-qSdt(TH#8ViBPmom!mbmOB7>g+ z)rT`PIfChlL;=ie6vRsXBTwFS$cq`lh}<42$=c`B5@0n9Ad@qR7ElI;tYm9z-IIh8zyD97MiByrZOMQn z#||C$*=w?;iiN6<9AkK!r`+tQ3_Aq_zN)s@&qTC*lfgd3%bUbe>iVrB>^z5vzYt5b0BbE)dPjE7p(FkQ(>oJiN6aW3npKq>} zz??qU(C;&y%kd;xVBnx^68g;S&B_N$7is=V&jsn*yEF*z+)|M?$m0f5@ga$z_U#&F zzKHJ$^Qno3R{P`nizL1r0g<4q4XO{~O>pG*?ve2L9L}h)Rb&SAxC9iY zl46Yi1)o9^8Cbae+9n>&!A&Lc_Wnr+9_RQ6uIc#Tv){d5$UXisa}PobyRU+&-HXr> z=1|dLJ*GnvoKl@Ej(U@?A-Ut|zR}h*kaOmTulKsQxA8yq2t)pQ!di!#YvD(j3NfO+ zEaAbG#C})3G-jR1RYYl-q@Ea-fOiOcdiIhcIuz0jv{-QUDK&xhC@^rC*l8UR>5k!< zS`CL0q!~p))Tp;X=}ncP^a^67K&(MZNKM7e(*BBl=mUru4y-l~9tbj%?bVryWc7Y& zRc=;PPZ2ypt$zE}&k#lAIbKkF-Dqc`Smut(l0u8*7orw}t^EhtK*P`MqB)_i`qd!= zTs26&g-X5MfG~Z(HQ3w*fON11guBnj?WZUxYCUVWW~}?O+^Om5N3Ffw)xym)vXzCD zGr*aXD3-3z)nBXb?8k;G$C@ao%MoIpsYid!#rE8dDP?>uJuVr#oCF(>oej5ZmHpn~ zrjVksDSy+Yboii_L_YRe$-AxiW;I;PK@*%-@@6Fg; z7LwxkUquTOIZJ2LD__jy@`XOtFP!V0zxe#Fqv_&)S4yo8OS|pdD?D7;H*@Id1e&1W z<3h0iS0u|9icQnPy8@uoc|M{sDgJL8gx#-(C}9rDY)b4bbiqK}wjlDfq`khG2F>Te zk4MF0r$I`>6K>+yaFf6yXlOKR{rNL{XD2Mfo5ZG0iJj)-{pq++ovR$G!~|ptF1Z}! zXvs>i2j6heCVvEa8R=|zIZF0A_1aK)IAcqL6D#z_sQ$Npa?1gZcEraZpK6VmEVHx% z|5g0>6eo`t739sww(FKk;hIp&k^b%P#1oGbx#Ertt>LCl&$fX;xzD^mj)PLAfe%{+ zaVhGQgu|>x6?R*G7HuIH2IvDqw- zZjT+cfB|hAWEDc=?QQRJJU>|&?N#>S%Sk%Gj-K zg&p=1%6v1me*T2kkdXmRyl{;wxIbNn;Jo8ouEj@>aOs6Qn$c>Rl$ps@)9=Kfm>y&- z@ddKvPhzqB@B{h{9oPHG^ka@M$Jq2F4c}Aq%dL5TCSQ{cY@w%mP=))rwT=nn(rQ1W zU6eYmVw|9S5j`Kw;2zPR=o{OtVr$yJ>C7;jnK@! zZqa)=Thq->Dn95cxY4e?sMM>FR02CptG4xnG_0JVtie0?3NiB0=s(B#nzb{ZHUJF+)pY15KT=m0=zapG|i~Flk3D%KnYNUMrL3sh;?2 zL!$%WSpLU`MhE{ZO^ht*u8ILr>h9Ce=lx3q6n*+0Me+ZWE|;T4)r`zMDz+Ip_itHo z*X}`mt%|A0h>~MaH)DqZc{3t|UzQ2WG%Ey#e_W)Rbht=s*i8&`_g_Rt5T|QZog)^J zVHfOO?Pl(D=S4)}gYQ_+I@|vMbAI~xLjt}m1fiH-h6%}(xH?-6ZAvW|yJLm}aYOhv zIUqjle(Z07mAmj~cLY3Jw5P}=)W=@n9^!RTF=3G}XiUcfaD$piAp;k_Sg^KQts&qK zx9WLJ;z(350lsh6mlQYQh(st-FvA?uv8`liVn}7Wr~gti{~-7V=zBJ}TQUd8DoPaDxc&;EG@elvslO8+)bVs9 zwFT}Y>~+i(@7!SU&aY=|zj`{pSzf7^uO6OBrS#?}K)^iCA=-I(83`!<^uf(C=rc3awrJeyL@8 z7*5S@!N^-iF9WPhhX9ZvEac<+qy`1S4BVk6Hv)UMrwW0k0v9om$h!cE?`UFcC7B{H zmtQ)%>oXPnfhzqhZ+))c`a3m$UV8Qug$NGp4cR5y=RL`DCyUl!AcU2Ynr|8&&!g(M z<|np=nhz5U?4s?y`2rEGZ7DXNSUNrG2EYM|F;%E!WUo{Q^^Ev0RKk9 z=&^4zOQA1a5U8`@`*VajF z;Ue(S+qu`6t!TFoUyGnd7?qF>O^$CQaDHmn`1rEd5jg9)kqkUHiT4)9w&3>^zo4KU z@y?cJBoJ38KO+Qe4-zfBVM(dQ1CSsK%gpK218l8&Uzp(Um^iTbz#cW}#A7mgwdHi^ z{=lCVhVdQyU#I|$VSpJhG>4I>Oa~;@L#xf3dAT8>=PS_9{6{M6{|tPYD$Xt?t&o%e z2j#zNT6=_y&>;};z~a5;r}~5a1+g4M9I2ikr97ObmyT0}W5iycxM-WyT7Ol@Az~jH z^P7f#cHvBzi=FbJ`h#uMhtrpgkboe83%Wcm=EgD|p*NFvypxqG3Ms;yxK%=Qs_tHo zZyMaL3i^jC5`fvl;E&(Md(W?rKp3C`sG0XL})A=61X;BVH7Q4&zts2hdFAgj0t2-B*f>GW|)G< zjTU(C+_{fWe`zbtmP{E9mbb8v(kjfmx(A30-kg%LND?w&8Xk&_0gRir3zY*k&wxELA%2!e%=-a_F(@LD2o^i zrd)FE2-7eCL!Xk{1qm2FKw)rwJwf2HpeZTYQ`%3NPp{5G4`t%$<_dD9eYL3KiMZ=1 zFHa!Y&*m>6&r80?>meGzWY(+AP*8{FJ@6JE+i4gjBw&Ar`Gd{a zs4v{-jAtSoobz^dip7h-`d#>|yPZfxZQu+n@2JkYDki|#E29kYwn?%J&TRi#syPz> zn>flZAZzV+=w+`U^*WSHU^#I5tTp~)FF62%YyZbyI+g)sVo*cTy~w=hAtxL{-YKkb za6rht_>PDFnEM}#7yYV>@9FX1jr!#{(XYQI)=qSnH#+7L9lHLO5V9nF66=(t zqRN7kl#vk)LFrU3O<~t-@gT3WuRR<&EH3oqWUmRaLtqAhaCb(Wf7rAty$QYYH2&}6aI+x@9R z5UrdSr-TfXOWj!l&>wg>z#gRwB zUyfS3ba>VKKJ6}?bdP7pRab8^ z)W$?a1SP(a6TLeZQx&K{LEN)l@?lQlW$d>g?F4LPB3_`LUmJZlR#AEKy;mFAK#&hMghw^4;G$YI?6o4%~9rlkZ7?jT0vL$qo zmv9nu7c`wZpJ_&EBbJ2oBuOgceMDUQFUIWZGJlDLEZ}dq$wuSueH53M2xV4E;paYc zECP+$L*EP`Fw>gg;H>{iQ7F1i2^&+xi1<01VfZ9qZCGB7wur{}}c^ zZMpyXjDz467+MD;yZ-?UIf}rP<6*$vCkI2d<4oq@oUDe{M7!LukNaa!=m_HwgGHIE zh>7nd^E}JvZElz7e)7&&>h&o109+g zG)S|%E)p6`@r?Xdc3&XiR;^Qe91XYmCnk49HZ_NqKuioqC!p`8Hjtn(Wln;ND{(C$ z`sZACrEwf8_O~*d34y5nx)>6vY7hsuPC~qp-6D!BZ*sv!j$=mxckrs1PQASmy-O7w z+cD&qx*(krNz(or7!94386VP;iYAw2o3zpRhO9`x`E8B0`q-ok>y8bm`LC^RupCZ( z5u|#55&p@RmICM<0j6PN;d==L5Cs4hyuUeN1WRvtA$>yIC z7K(<749rJREVBRj1r0212NYH@>i;a@zh?vcW5vI6eTj+8Rd6s;#Keh%MayZ%Yki#= z)t3!9!^~&)MUf3fm`*NdP}hq30y6trU{|GD^LzV5A`?sFX3xX}3=Z$a#$mqPoGa_@ARq^F z{nMXTW^}&Q4NO`0u1<;{3tNQ(FmYsSl1${+k$1NcRl+bf-o}$afd7uI@dz5(gi3rt z#5{)Sjbv*5zV_sEw5Z@FvK%iPW*va8BTw_U zc2QHeX*b4+VxO~e7-s@!H{=F7(#Mf<+HRtW3q|?72~k878S&ItGyuOID=w+B-a%xz zzMECEW-%_<87yi8*&afSU{N~fU)v*|q=DOffIk5X$;?;Wll=vW0ER~x2?^V8qK{V< zg6=Nnolq&U~PVXf|&&VF^Ml<-m>TwIrLH-2tQro2Kq;9 zXzz~d4>k95YwCGb7}()Ss?il4FS_E5rZ*a02yEr~(FVg>#s~{P|Cl**SZRXgGhl&z5q9F*u6`IcM!arEH zhxi0FS{DW6Rc9<||5cQRi3z?AAU;@blS$0{!q4(~_!IdslE{ae-uG4M8qd_#*6(=P zY`*!XfzDd&Ttvq^Nf+x+<4Ahr$GOi)iI9XWZJ*1!4KTUx+4MQilxxu~H z5%4uBGXFx}VhIlOryVYVC#P2k?EVeebqvjrLfUoJjZDF+@(;6D&^%kB`R!_y)}TYOMq>vCgke@FjZxsNBUp#ue@xSR zR0s8T_=8ftpJCb6LF5-;52L7EC}jfWxro;X<|=Gp)665`ssCpt=l{E-NUv?i zy!B&(VedS>teo!PfL};)*y0|$49Jmkmc){+Oy@F7Ef4Xb%egt26LWDn)M7bYaF)-z z5QiHmqm{WN>jxYG_BcBr?I|6^ ztuR^f2q5LXkn!=~Q!(ScGx4`p_7vZwHk$CLK49IjXtqQBy$1Ba4&O2KmXe`5A zJ^s^EpWkdorTWN%QfkS8UTu3R*d^uA zt`UR|sx2z2>QhIqX1W3*a0!=|XOn*!mKNn232}PM+1szoupN=jpyik& zOCr=I1gJDqYUhAaEcLoX>YMoZpHE0YUG!@SMhfAUxZZX_HHRV+(R@T_GoD}EB{2&7 zYIU7^baD5GUV6EN1Q7Yu^Cz9TFgx{9c8)}dBl|LfN9|MOzR@;WpE z68i7Eaz+4f77X8HTa5Lk<6?laX9hh0 z7NTQ-gh)lbOT}&lH?DqvJwhy#!8wjuzE32HZ$1luuKCk!6yQ1mH7k=yjbhC~)#>;r zTHT+ZqBihHhx_Ro6Tb_iw2JHH9)tha`vf=0DE|)J(IcxJBuM1Fcm+y4mH$gCF?+*T6pKZw7i%N zbjSz4A^Sta3NT4JkiI(%K_x8plKBm1@Rhzc%eb3y!~~elVf@%}Lw3WDMd&_La1rzW zu=kc>d2PwIFcvhp2G;<=EfAdGB)B^yxVyVskdOeuHMqM54I13tHMncO$=;{?cK6wx z)6aMB{dfQI!27b+nyY5js8OS8p2I!ehBxf{gmv2ApDA;`g@S<+VwocWDdfY8f))MY zt6}0Jx<^0?`!}iGzq}+L^v9$V6`b|;*wAnwmR=VHMrybVE0~2X2;&+Bd=dPmV6)}* zg4gA0_~7CM7R2IoC0p;Fp1y$bGxnbS+>l%(IFIHgsw-AvgI;|H(sei& z6x*zcedPN=RI-P%0wEJQSks}o?$FU>S7V!D2N_AMuDdTOQd+_QvHFadYnW0*uq|@F z{afYz>8tcke44P%#dvNzKc^BAP51%6nW?3Kv9qIs(^n$~^U z@h}~$z}xVe^2~G=<;7=YbP+kq)~|*}t+O4CdTZsLIQlf3Ye>xmwZ3uk=RUDh2Yfa*kDM?@=-&`^-yV4$rElted7ZkuFX|pTEOF!Q%DIXL% zDSy6Fz74DJ@L^q(*)p7;_I`p`7(x^SZ}UJ64%b>Iel_mlSZPHNJ!tIB0<2K+k%Z|k zHB)z6fFlxFk~)*KqoCdRbh&32pZ~gk)r(?msZTVp9l_3+qUGiB`lp-swHDC(RK`G3wj!Z7N}$Z=LCe18B=69QnkbTt+MBbHeQ@0I3AatKPF z@85MQBo02tHrb{t12O+ZVJJ>_mCCYYyDz`zEE`l~d>1W9M!PbpJiaWNS$uNqcYXD1{Et|jt` zduv?H$qm={ zRx2_Y6&VTRVSH+{B~!5eAvl+)J`!(4`y|5G^7s{-`-K@EM?w5{->8o+bADU@C@sRgL z?U0L-mUz_;tYs#F)Xt~d%jNwq0wJu$J)K*NxNASA8xWmR_zRS!*I$tn9=%Y&DhN_R z^VAb*8C)=qY-^du46}1x9hg8mg8wqWo$)^9D|4N)u_!XuYYRSCiI}YdkzWr)*poU8 z>I~M4O(^4qs@iYg&r~q@5F=JWX?)tBt8IQ>udeg!>e#-_z4oC+-;?qV@E1}Tq&;)y zT@0ZlUdYSgKQy(qRrG4EGY6Qs|`2aZd;w+D zT}mAH4CJ)Rg^1u3QdKgPVFFE{aQ^vhlt5NHNTnD3jn_~XTv9$5fL*9AJqMKW&ajix zZ~CKqMqQp4KfywNN<1%0Zk7eVb#tzn8Ojp~?NIaNr$Tmm%D&x(lH)75*me z8w1a#_&1+V;_;W=^+z(Lqmw+d?ihQ zdUdUo#TwH6EK!YqZ=&!6rOF2O(X+=SFm(V^6);|*mYI?bXpjKnzP8CX9})qCTWUQM zJKcYh43i#`>k))99+nK%gHw`PU)|+Z@5A>5LK8$C_~UQT35jkR3NitOIeV!NT5ban2#03o_C%H$tVbCl4oK zHtAw&R4$d5@GX>$rKQsMu%nf%j&q(|dQX*T%qv_XKaaS#8;9ra*02?n$)uPKR~TxK zEabf5gy@3a)uVfbi(u|~Q>8eMD#0aeUTo5=VM)7y{|wNa_`^l&mgf$XAK5TgV$LGg zwTS5v;2NB*k+EMS*gUL7Nk@PB)SmRp;dAW6DVVaj?YRyC1RR2_q#vQqi^NaMuO>d) zq#ON!-|Qk6FA8vs!HT@hDi&gZ;qtg*C*+D=dm>hddiLB$=gGQ|*ROuQcj?u3XJVwN zLPCa=LMTH#wyT69qM{+UBty`MB0mwN6UY66kfqxzQ+T2zaE|tOC~Eg+Z!9Wa%4YR; zT{RK#H#Hh~>hQlrRC;*0jWK}KN{ymC#ipz2tz=&lg9p9EVMaLh=4Kbxw2FUNUIlv` z_tMl9K@jrZR~K)K`8w3q)snnI4e2cS$Z+e)qM01Y0D#hsREFIVPi)P0JquSM8S#%U znV$cETyh`FEyYUJ0WHI~m-f8RvcG<9+n=NTh=$tim5z%@0R=LzuqQhfYwoZDhs(FU zlj&51t!&hnFg|JVNA|H3`RW-5vmob6V!CbcYxFa=OE6QaBaJDpbt zz7!JTbYz%7bz$;?&QU;`p7 z&u6&W(}Q$P|G42=b@v#4yA}Go3ac22)1EbC4Z&|lL@pSSy4-N#PS$<|&L2gH#iVfP zAf*e$gg-~yB-H7LW?Jd!r~mzNY_b(EawJLqYkPuV7;8ku#ulV8p=V zZzA|a6CzV3wz%porz;bY^WEC89+IDj)hfINtPb_%p%`++=JpZY)T6Yw!n_@O*R9 za|C1H$)r*xLBaun^?4#sUUGa440<{=6(5YoYkduqcDVfkn$P3N=HU)|y2_M9wqbp? z{JmmtA;O`SEU%9}A#WX>%?L!zE9V2&yX!5lMZ)oRNcr@zbkS4GITXl)P&VJe8iZy) zyPEqu56V?uZ)!3qR1zcTuvtc}y62$=N5rBS0&b-%Lg7WF7p&FhQ}Tl4i4>4sP*)GD zL)>SJEcTmb$MwTBD?ao7IEcqE&(99df zD#!Fimqm0M68}U;!E284Kyh&tfAN!J*Q)62hKOiU1kT!sT@!cSp{tJ9wVfYjQCn3qZ+?x%W9dn3y`(QG)OOg< z-o_@qCymRtJY)_(2J?`?^=Dai>3kk_nm}aKJIZDsC?rijww|H7oUKojYP;^(LmZL$ z{7W=9t=IN+^EEC+HsG#coGIJw!sodz0sm3vdJ0)-C?cvHRAWgH>cOz$gKjMGRF5nZ zhnn*F^N!J+B!=;qF9~`l=6N75B2oo6hn65DJT}S1*@pu|Vc#CgG3fgP@$2H@Ol7=Y zKhCM=%0`XWhOz%(4MzE$Q;n@7ApG?*eMaA9(7*^6KB%jsiV#QjEOBbI`wIDUL*p*3 zG&=QtGW^-%-I+A!s2pxHo<>&uqbsg0@ntbGGX6N4GAeO|FDUedR%M}c91{z8FVtmV ziSf+V^33LMMIDmP%QpMGTWYEjvpys|&R$H+D_SN;`=dDP*LeTLegho$mvAdn_&fdg z--&dsV4nw)1Z_^M#qBQv(5~;(PCDo(K$*w;&U!+N%k$jtovRx$oJUjBN_XxOhKT6d z(^Wx_sDd;FYp%K%E zprk{2h`GzcY$?s{XV;;~mH|12L_4f855%f$2GN}$x^z8VByPlNE}F&x&6BBu^V;)OgO&5LRraw6zc~XlgPLmKmN^n>(|h%5Ylb@1i?Oa4ni7T zkHN5U$nA+`WWj;%H9KJj(?RU6RrTeYIkW}8)l5NgGR?PxQob^Sw2w^-^;T?n(-pDX zs1)5yDgIsVoc?^2SAL9yauC~_0f|-g)S+bPNX1h2uW_ik3v)x(HvwrK_A`m|kU8^L z8ej@QUho7Ew(%8;ieq@syizj55{v3>*Z4!i*)HwUO$^8~Y9?DOW^e!%Y?)EBKV!F= z(HH>znA3FaovTpqfX4W3PbJxLd4LQgNcb9R>rgi?^W2`k)x<=}V`xv~btQH>PzuYe zx|!$`mW6PM%7@l47JFCD+u7Oq*mMzS668)O2*pwo1-O2_p6C*}3Il|E3Cnt7Q6w6j zW&PPI?8M0&*ZN6i211Pn2PDep+}`|E-|a+Go7*AX=}Y^E>iF7yjZ=3h(A#aB>x@l` z=MQMdU$ zApkPK1tIBH@gZpMyW`w-NYR60amL+pfGyMi+nIh|~gwYuNDP_^o8K2YyB zi~?Iz1-7>9?yxs0{Fc!5- zI-bwqNct$~dVih|iQ&N5NO9Y8GyYKd{N4Hey%O+GNY>ZmQSx$D^9L0y_h&Sh%%^4c z8XR(R7HIHfGLg?JF$umm;2*9*jQafgO+2i*iTOmbTGlA&q10wE@=xTw^}@l=-!aA&H5E#Jl{gr$ z^f=hsqLY}=h_juT+(;?+vx2GDfFKR;2@M7*h1`HoCM<;?(D`Rw$~WV@lEg2bU2B=e z*qtauk?`Aq40V@SuEYXOFlOUAk2U(=w ztcTg!O0-}3J#ppN$Q7qMHz6V}?mR6s>=)v4pIm3il1;lnBh(UmQ4{DecpIael|=!0 z85j&;S=-JSuFL3JDUb(qd~9hBk2wr2Zuf2fnG%w%=_;66pW~B>ewP-!Gp735=<9}a z1-RX6!MFV=e}d1fD_b?+f`}rt6ulw0{UY-Z)=CKOD_@A|etHnsL+w z2LBl8w0J;+q=>4IDD)E}QL98B5jHWr1$MaU2XSLfq|eDscMdhSqIOI&K-8~ybA}y< zJ0x}1bK{~*!OyGJ=!8k{`h3N)s$`+D)(^mMkr*oUh{ZpAS9Q-gO_YKMSG6&ARc92= z^{JE`65N&XZ4;}v<|dE_Xv)2)11?L>=CY-=BpE0Vo%T#u{u4*#6aGk(6PJOLXoZ9O zB18lk3Dvv}m@0RzXEGaeY?s{LQ7WMpo8+r;{#yO9cQmt_Z{!$ud(%e17c3lD(Pm@_ z<_0v5sWL&han_}Jys1P%fMeIU)%=>M<#4vGE|_TnN|U7YMak@_^Ws$|56f-Bm9F^= z1WC%ny~E_Zbdome5iqq1xp4R790(KmEUMo)74wr_5_G|2&%-Q);^c&^cHhC2Bq0NX zx@{}anqmWnUxF!Dz+Nn{JM(TQz-*UB$yg0EEG03FF4Aj^(4W1%&Qxu!K$P+^7qY3= zb-H|wNstK!Z(U_qUZlcmfl(0YSVE-xF)J@hw*I(!M^e9qA})DqW>?PfX2Qclw%#q# zM$j^2Rwp?+JzPZlO`H6R;O7@BK9rioS8S=d6RPIzkgCaK$-;#y=MaHhyy?}apcu1J zVB#|*de69W4a%0SyAwRFqxt48F3hAWiQyPOkE1h)69#MW(G@iw9%_`a*7J=CRH>+! z%$w=n(hY_7?)5JTtC>4qZ?4=JG8@bNG?XtrOzUCGU^a*i)^o3Ll4q zjGzCB`CLb%&11+#^#&67GnvGj!$MHz3FWhg?@X5R;5+t} z*Ez3ajf5%!La-16kFZE|?~78JJL-zlarA_0OOCv>7nFo@6dQ|GiR+&e6JbHgG!|Y_ zBDuZww907mtF&;QI@@b3vYF!%igU*BccL(R(n&R1F~K;Euk^VY7_?7(()0AhUrPt+ z0@WyK=-dn`HSs3yV{c;K_i1^MBUSKUyfPidkc`egNXsoofEy3!h^1cB+Zz1lVjr(9 zEZqP4aM4oq*S0>CC={L?rCgC(qUML4~H&TsrYhg&GUm?WJ#7 zCVhNxp-)kx0Fde64Eefc_o!azC;JM?i~Oiwn2j9v`0EJs&qm_g6zOBRKGf7ACBF42 zaEi%tx*!Mkv3y-}pP?`E3GZftR@a|z6>>z58bhgq-NAYqKT+ueY$^GxY!%n@l-#J+ zfnq7Yz2Qmi4A1@V3e}*3=3@W-^5l7#i~28xqwAx1k;RDOLcOV-Eb?OPXG_*qXzb~i z{y;xBQeeb!7;n7fpBZQ`;lF(h$tSQLPg;8tMOpL+-ukTZ&0RK6q>ezzuC$ah0)OI7YcRPFD7S=ERicXrLYv7CkLGBSvapC( zWpYq+tejmg#lI+e06}Irkq5(}KIguuC>Yo$eovoUItO3v)=HC&7niJ~Al;73#Zj}V zh@AV&>85LlPEgBfH0Pd+Ba8j^%@P~MWp_pe3IC`P*9*21YkUZ;x*O6Wc!*hxDJUZ1 zZ34^t&seqRB&tYYpLk*UXVQ#s%T=2_*`6-X-*y75jLoi6lZ3mQ$UPx-6m)9Kdv<}W zM`B?l1b(PtryU6GLRn)Go}s8O}A;Ji4SS*~Slqq(NKG z@IH-v6i3mz3a~+cm%fd|r7fWmxw7l*4|@RpunEi;+GIg$wt758+y;bXO69{^5Dzl`vEF8vl~9pEPz%^Jex9~LbrB#X#W zpL)uztiowJHX|35Mu2arv$(DlY91PF&wPFlxFYoEIxB|sTF5i*R{zv)$f{L77aLrm z)$W&OD-fH{T!YqcLoQcHyzflM8f=J5P+Xk;tcWLwK)W#32cr>^6jx-qKx`dY;IZc+ zVWMA$TNQ=!0u~`)um%xLUBOEXSc{&$E064}xg4qMH(Q^Xm>3TbPr1_XpTQk&$Y;4B z>OZd;!9eK=w4e$3!tGd(R)1`JRsw2%kfKQtfoBDhh76jM*p7fsYdhbo6~Py<|K6<& zgdg(Z=E>I)J-6cx1xx*rsiMU#3o+tAnx6jly$dUlrXQs*9A7daYtJJ44KGW{Wx7ai zPCoB$==2X({|OhwWApI(sl`k=Oa-xl+i=n8q7rBUxV(rkdi$Rg!dza03k=VZ{6p9x zEqeRv%4E3more1mad3W;YDiTfITIg0+OBd5W5X}QdlDPUhX{#Fi!;14t5Gp2V?GZE zCfNt6PrVe@7sQYp?Js&OTT|NqCgsgJu1)5GKXocm9iU>OKBMo{py*Mj5SpcZvm2QX~&YtFX@q6Dp;$Ix9x=o{DI@`mfg+jwQ zDY!s~hBx+w%(zp%SvQGEhNI|&<|0O9J?iUN@9j^(jkZXw+L1EWB>4&u?y*kW`BD0% ztK|MU>>QYj1!$rl0b(33@!&vT7A_}xmFy1@yq8+uBPo+wA{q3ob_S@&5pGAz8I%^$ z*aSEnqqm>+V=J;KfHn#eNBKuQd;lz*a7JF9&Y8JUiIp|k_M$1*ccA}A3-&aqe~~SqM@K>m<=d9`ZV3<6nGM*yA;`u z6>}Fov@?slgmJHlHX<7;+L4u(WJ{V_Qiemimlv!Si%q(zhB${nPLF^Ta!_6*wG5YYzO*%FcIZ695p6gOp^n-Bx- z6bjJQZA#jTh97Nz_J^aSXHqNkrM7H&X|6)BJH;6z3{;y>b7i{b#)*q1fi6Lj+&a_w zBZs+@kXb=*c!YEco?I_xAoR#cjL#)EstJetV$-=w@rXGek}JvGZk`RqQ1@`gS9h^J z;?7InMSnY4HV-XelJdq941x)-?6I?R>6U(Z5Fa#=6Vu>Fr}ZM?mbBfRyLYk!0qA!T z=@s&nk9M*c^{;ka1^2J1InMb9QW{mxtj&S5<5xJjtmVrjmF1nQ8wDf+^ojG*eEr@) zYPT82D{+bNqQkW{hS0t%V`e@UsKf4&LW*|CYhz{cCGW{i&984Ecz&gbR`WTACvgIV z(4F>kyBA@oasf6>6htoxK@pXh2Ld%AgRA(6{FF;hchhz`D=>C>c0Vc8%)rLgrSwpd zoXDPvxJ8#kRqEEowL@{={dHDPVx|_cZLW=;#a7F3wp10K*UG_%vYmvFa=HoX-j>+P zB*XUYp!lCzqLTe?2JLFf#~4dA?SbVPXACrT0>F1 zaCcWOOU&@OkqKo58yks4wmv2CiO;EMg~#!#7jJk}C7D!)CzkZ}Sj^yh-L zhLMW;8w=C(K$v#ww4eEC7wOf%K>2=dT&GZ0{FGOo%V#=M)Ivajq`XM8_MmmOYZg|? zc8P!^49sm~0a?w67~Lw^8@CFv;iX!VJ`;t`QXPOp4J^^tg!m{}?FZ&fMmA%{zRIHSKuI4cQ866Fr62w6O;PpPcx5rp(j!TX zpmua0yeRB;X7WSgCsoeI%9j=M?xzd@k`(ggE}tT$y%&QM4}d-y(&eid*q?+Mi`mtCpfYQyLok!VYG?2{HYur2=aFL)#5R zd1jC0y)gCcd1*gnO`O(h0^I3%3Z^`*3|I#%&JaE&J1y>ahIa}lYuV0Z`w!Zpno??y zV^SwsGV%WtwgiGq4;4zv?<*ez(raxD6s!R4iwF*UP_~|Enzuw7FIJK+FXJ)Iw+TbP zuwHD2);H{X?V`sV@QF3X=p~6jIxXRH%JPy+DJx6shfkDsUbi@)+7(dokdrPsVIl|# zg_^%(dbAmF=&I0=$?2c=y$M>dpkkxZx??YtPUZ*OEBLQo@PTtQ5(cS5KjF?^FC8Z7R^dCPOvcFMwRx~5Rc zzy7S$f#j*<3zg5tl8Yq6NFr7+)g0Q%&=`JS->MspIe-xZeqfXi?e4qV>};$$*VC7z zv_!?66kifCr=ml$!Sl)hLBRJ}n&MFM9b|+FldeXamDdqiTh#Cxb+r;U6No_aXmI{k zWDz7~Z9qg(RWSRBn!3cb!3N?Oin+Oeq@&~RdXJ%G49Fp%djp{pat(8w`s4h4V6+`w zRzsb9vQ1qD3wKPq(lk%HSqD1Yi#@JM^9z8;7N>wuo>T}owBaHE6dHuWaU8!)MZ5*| zgXic-BT%duJ~1}I?{FApTD^{2M9qie;D>f^u`Yp}oNGsiL9?ccvQB7~AC>C>lnGo$ zH|a2L!9WzW^ZoL#j}<(@@*r+PUKd0ZqA+>mM)rLm3*+yusI0^`7aF44wiKfK#Gr|I zbG}EKYOe;DCM)sco7a7uNIShYep(<^KpxB?0(c`X-+tX+QgmUx1Ctp*{)z3HAb5yj z5~@R7QAr|sx(bCs-{A^KV4d>r$AQ4+ozFgMqIUD|BZS1~R2s+%oHV%TOr+u#rJ!=4 zxacmk#SKZzP=4oY1k#~pd#JP@85zEB)0azKB3C@*^p@dQ;s$FxIIGuAE}gx&<|G!y zGQaUIHwS03RpULX+|o@?ejqm+XIxSd zAMx?N|1Om}-dxb7-Bf|@r@RI3`@TU5WCT1)1GLuSwqRz>}B+fNllXzw({j^kBBt2c|B@31d7$u zCvy5vqkTT85#7UTOlTT?Wc8{yUQ@EGVgFkDWgmYoELbv1|`P#h{gP9-|otFa7TKHEV$p{I?U zoQ{cjPQ6k{mmz9>7CF-3=$WU+BJyGq6D7S%t15XY5Kf{>tI7m3okLp_8W&|+fuvZF z;tO6gff^!B$urjM@B?mF&88*5J?X_X4esTaO0W`fmBn&xO%DO-3`Zd0ZcRdDSiw`v zxtFYT6_@JzlfKZ;4lqzmr1zr)UP@-t6tfP5htQycR0Weuv!MI*_@Ppjt97oglqTf0 z{wEbYmdwU>&Ad zjR?allo#{8R%D3coeo|!MyGccWq!?-&QIq-WX6Qc^$W1JtYk{BcGwa;_##7CH}=n0 z|75}cfjEsn2;|hciu6pUXB11TUpBAvI8T=7r(j5O^1mp)4Jk@sgVWpD?Io8+pL^ud zExF_we#5TrGUBoSZ5F^kP->Mt~%P36HfF$E1GN6R5Oa+IYR*iQAI z6&6XpWr}VlBybtc+$1ZX@N4vysFpVP^>p2?O#L!GuT$5^32DDnHhsv zi;uv6Tnr3+VXHj+30vXbVlQHR)!Au4uWNPd$zn3BJt}cV)<P8_-?7bYBbPV0E@q>Ps29Nrb zWU+a8Ps*X>LzF-iGJ#XyWrg(;&&J3@7mE}%r+iIKNX)6I#B24=yb~bEeh~#gU6a(c;{i6qB$^L?yzHIU|=v)@XoO@VgLPj-r_FJWu}5W zxBsH|FS&$B1*Tc_loVXXdD5_)noou{dO&#MBupSA#4(&Dt_cG>S!PNuR>|+mDwJ5f z0xxnUYL0-2*t+0+IbPtWm^-L{ekb>=INkE`jh$V0~)bvUcKQspd@S zgwVITL$a|n|Gw)p{%K;%p0}m8>(pfID}qLK80{H1W(LQxbs=m2 zN7HWi;$FVO`niKvOH zcMdhCdFzk`6O*{;<*8rUUi_?X_$8ZDm3tY}2HC9exsuqJkY?@n;Y3ka3djuMM1I|` z%kSV;`-$XiCG2#F}?^A|rh zLp?W+CfBR`ejUMh&y@}4*-=5pOhK6tO)oY{s(=`4qxMH(&|v7VZo z9P}o>M9?A5(E$M&`I8tMve=JAFT30g^QnprnG#$koj4wbIrAYq2Mc)t4i4dipG6T* zElZINznOM-WWvIIs?oY1nYk>l8&Yk8?KC-fXP`S$t{P#K6dS9#Zp6^Rx>`*tmMrt8 zziRF)Xj>#CBy}#rjPmt;I5AhWz$Mxd5tl}f97!~6os`VXY`5tqLe7Q|;<&nUkt7Sz zGBDDK74wLUHG-0?a!mNK|L`!e@ddUsWVeqzH7nss+lc6Nt`6 zCqkv(fdRj5HSgoq&*TE?{a^e#&7Ic1r-X#Rp|MLUaY+vjhU=ucS^X8?M-G+GX0)C5 zLHw0W)0F74G7M7JJzDtHD<&w~4a;NDum6qKeug^7> zb4>T%P0LHH$KK~o`&RM4n5vA2+g0;?NPMY@%oWH))Rs~~q zBK1#x8Z z5OsW!WxsIyCwPQ5CB~xWAKH9bTe2=ccs@Z7;@(vrNVp^$Os(Y4QP{eG9NLMJ}DEH!fSC08OB3zU_R z=QoOo|V>Xk$4; zr!JhA7~;g`IEy>%Hqi4-4#`xT?LDj;cT@7Gr>MLp!_%(6hF{e)8N~n{jO2rWqAmAK zQjO-QA-V%bCC)@XCgX=}lA=Mx^kmh5C%6o=A)h`u7{+!)k%w?uX?fG=H#_dtQ1CxA zb?hHOSVF?WRe8R+)d41Ug*}`ngf!1*P~e80ztekVH!G0ic&Z!X=~=LvAH3wBn;Wlj z8E*PZFi|Nj{5@DvuFbC5-}*wtX-wu{I^8<^eKl!qzI{&J*B-QjzG0-K+#%ue4x%Bg zvvvns7%C+Z)irMxjqnM})O!trw?kf9&SJze&^ILdG&b_wobC8%R6jK{78o;cvZa6% zc&M9qi0zM~md4l0W6QQH-x{QPJ%B>`8eLTTX%OjC{Bcoa%Aa%2gmbkdG-md^C4TTq z*_Y9Uix@4n+UJ8xcY9`!A*srQ@_wFkaN6LeC|6ojtH@jR?c9sr6CcQw4Y`zR`|KRU zcM}E060$>8HJVc|(ed%PtLX0T5dA`EX-yLZeyM7ASA6_)4U}ih#PQl!a4^{t(v0F( zQqnvQ`$L|V7U~G8sKCCwZ^6UG7-}8szy#ZMrGy7ADG=7_V20+$OJ-HRpeOf_hFSYg z`fn!q*J0%h{;2RJ0~L5x4s}r==J=Q+1q8!onNLlE!2=C5f!sV?q+*-*7jvRUTgzeQ zh)Sh8*b5ESKo$`)^CWI-*~jHxyE!rP1A%iNg-8+~3!JLkxf#eVvRsp0C`v^F5sD*RPE{rk$uDLvZiIv(Nh=nDViZ#RSm z9UQPJeuR?>$;m;0#&#cpU%V!#tKE_Ns7y?l+&@@wzISR+|k-NG|T~y;&Fl zwUAO$qP==muU7sp97(tQc(l_39}?2*B(K-jb}M@@8bO8t!R>5&arZ6JJIW1^2S{3O z5y%c=JbA#O8G77|p?_{httqP4(5t`R$jfKIBO?bSbeT>TaiHDvYkQi`u)=~!C2F+k zxbw>m*Y<4N)WCSjrq9s%=QQDuM2_gWzQMtmhaoh&z;lI^(U%0Y^sUQ+`j}QMh$48| z&pNr*e9*Itq;ygjw{553t4v3z`?tFhM*4a;R zp&`a(;)Pghy&iCJGA1$7CJ1#3Hm!60a2>ERLN00y#cxWOZzBnpKAb0Q)jO!sw4)6! z3#xxxZGWA~Vmp9@W{t-8M$G7i*VTrlfzF#T#S}++7(Bu9w7HEvi6*iG zf?LG-`T6nwy2QkQ>3zBwhihJ5K0fqkI;h$_D899|r5exH=k?_miwrfP zV=vui1hk=xrih*(4BRIw>PuMun_czwtu5N50b+C5fYp)PCAr##21g`oZ(e1Mt>BRt zVUbY&(A?2#;6%KCe@}vta4=WeA8>HMf^pA_{}nJvrd}>+^G)Kke%DXZpL{8zolQlO zR!~^ibH>^pWkL;joZIr(Ywi0K#;Cz570Qak5hyRj@7 z&<0VPkkEF=K6VDi_|~gV$(t-S#JhpH7b@?^4&RJBng@d1LA*F`Zd@BF?jE&!^`c;p ze)`eNwSe;!fBOoTLm;hv z1RtD=-3p`+`RcIgrtXVMH0gM`j(?%y6XqXi)EIFC8!IWP)YAnfaw}FO^j9z9@7MF8 z0Ly4%`o9q3-~aMoCe`Zt82^V9WZ!HsaJ4>zJDZ=tC>YC@2L}^frO&#Jem=cA*qWL+ zZ-2tsbhU1LbaJNR?8&)?OsNf|^#50G0?mrq<~t-GXAR}<)%8SYENaX9$+Mq1JuF zPynP#Nt!QoaBbln1gFCeGn)1RAq*!cr=$Db3`IiqGUoqpeEQ!u-4+5f*D~)LK8DY= z%Miwd!}h9g%4AGzD8IgS`9Y6HDia?Y`(gadz@^STGsv5im#=xCg#6L_dEc^@80)gl z4PyTv+djb7UE|y|UU;|pGS_u=KHbdy;&xz#Fkw2DNpRypqDaH5x98yMTA$Fo&6dpP zJg=D2{(gf$c;uK3T7`Y-QSj}}}#CuE1) zANngk{a=07_ag`s>FXB7)mn>w(8;!~v)b!5hztN*1cF2F?SAK_eOVt@AL&1NGT@t{ zF!toqpMp$`M%W`Y@ptZDjtqQixS{xWp%TQ>!OC&aFNTB?K+wMYCz9T+Z!A(I1HLW~ ztu{v1?|0W&ehqb3AiZYPy3hQ=fA8BTLCzZe+6g%^o74~ zXyA#(Lmg@4f`FxbBIB2p{+;{Ii+=Rp3o5_E0sr8#+B^Q=n*YQ5|G&QGz5QcI!f?Dk zV#pc=6W)KqFXbuid^9U~f6cc8dR~b;QqF->3Ym}ZnuduW_rJloe}E85uOYoAzB-OQ z`+HjSZ;W$C;tld%t>CxR%l1+?2(4FKe{6&0o)3l#CrrRYTz?Dpfc@9+{*y6IVE_-Y z=k-7CveA*are@EP=2K=uzH&HYjL27RF8SleHt9+Ut{AG|pR|5HfGzzkXp}<-8-KVV z{D1HoqLpXo`C+i<2ZDNn%ur@nclj$M5qEdI*qG~8Gf#!8l@PN+J&NTO?T_um@2 zWBEfH?hRGO`vQ++=rgGJ+l2E))g#8QjsG8fJxhOCv`Lla2$3{?t$%!Oe2IT$1#8*m zvdYidOM+201qY(F7oP!Q`^?j}N5cX9gw%&b>fdG;N*Lf%RaWf31I&*}N`KFh+xEn{ zm5Av0l(Wxg4NmLD&QQhMGOtq)S1~9$Wi2bk05U==78d_&)35yt_&Rcu03XOHR9M9S z&YQ}qKe+joL}JksX8gY=61nBugyn9y@tA-r(L5A+zN;f zbDFJ{@sF%z4#VK-Z?l&d4Ug;XobW%acTG-hEz@IS^&MGruIN+Dm#|NTp%T)Kwr4k+XHNut7X!#UQFib6(oSZIIf5;uDnZ7K?2^1XEx%KxzO z|J#p(8`w4rOwigZsQ^|XI{QuuLheEF^Dk~MG(!9NL)$!fU`&=SSv9{yuqxK(AtWQ? zuuNU~{(+E?3(eTx+tz8kXea9CJZt@ViFRWoh4^atc=9)IM|M*M+>sIeJdFulFx9Se z^G9RYxQZ=mfkGD?ASHT+zNr^|_3-qxIo*`5XJ|&2PGpDw@@2~Q`a~Htojo%i(y`li zY2MQ3`={G&eO%&6OAn6%y_1A^G(s$$Jin_2YDO;sKsJHNuTeO&r4}~Mmm&j}48t{s zsK91aUncPp3)GV!#d&U2toXtUbNNFXIY-c8>wwsW=pB9y^V{H+11Jk09Pph#vN}(w z{aKE{+}zxjKJO_h8JVu~_r~l|&zRFI=V!F@u?QOBf>F~HRNidZPM2B1BOz6|mfYQW zyiKa<++2y!!7S_A)(hjgF3wS$PiT>nV%0o|)JDm<>xx zA^_E-n8?dGXKJb(Qbxu$4dtXc5dtEfm*W1gBPWg_j^!aof*KmnFMhrXy1F`j8Kvsv z;!^j>l`-S_$rWp0*EKfwH+D*V+1Z(T9862d=cbXbvn`)|nGr0OV2Rre$a!MTnx95K zt#5LWb2?H}bG0xPkoDplj-?f$QUAC$>4FH4z6G!5{hZhKqG8<( z%CURjpF6LDdm*ZyHjFYNbC^O#w-pWv66Xt1hVtC#- z$T0!Nb}1%0I-zpYjmLK~vzJDMf{UNfFK^O_h2`P%(tzIE0!|xD= zTZ1XVW$u^qhRU8;Y>Z$|Pq`d36%&)LS7e;;=f_gBdWAaQVioCu`u)vFNEqU0W5%+k zpn!lkU{-qJ4%xq8cNrWI5(%rE94I1)uB&Dab;;SwY_2)EaB91o?9eAW58p2EpmwKN z-X;>IIPzgG4S8VhJX8Hq6Mv2fDwumbt*7yit}{nw5cBr|t?tpma{iZt_2GvcTNZS> zK<@0;Ept!b4)D4+*ma2q3 ztgKSmKR)mi@;J>hO7G9EpbZskRKHN!kouv$+ZoS)V)d=LMN8!&?8CzQ%W5h=$bx}6d$b&w!64J-C`|E-Jx@g<_ zk5WgbkI5B{x^H5Omx&yk2EU}jq;j%kqgn;E!QMYv{hxQE2GWRD#IT0YHptI@PY|)x zTiLmy6B5WjT(#u)Sbh$M)@I=%>&k)VIFQPOnu5`L0UJ{moQKF*>GVcTr*j znIOFsT3gI=D>Uj;MPq`;;ZmSg)M&Cs4V?`kq|8sPlf&!Ry@KiU01sE~yNUAm_Uz`9 zg8v^~Zy8s0+U*YyA{`r~8$_i;>244~Qo01ByPHi&m(tQ9-Q8VEZMwU=8#ep-F*Eo1 z&z$=_Z}|8^LG~4Et#4gALUwD6oYo5kr3?s)QWGligM*9@XJKy0BC{s9rdE>hTH6); z@&Eom`>X?fCE?JXc;DxRe3NjJpJTso;1ioRWOUm4evp|~DAhR|I$F+y&J(-?QWi+^ zbxRNd`~YLJ@UDR49KcR)U0BvNRRbGKiqU|2x?o^{I1gj0EiQ}Q&hv?R!lqulO3w5N z^oa^05Ij8R_Y`Q^)T{!P^vo&Gb40E%|5>(Z3xyLjnNEezsm@j#xXw=|8bROVl65Aw zTuJ{AG*#loikg+C<=@TV1#r=ni&Mn<*8p+N&qphC7`d!BR{AIs<}LMiMy#>eFlh%S zi*}z#n#I(QPm+a*R*FK>$k(T2(wZ z7x&eyI#bdK<_k45YuirdaDvU;lOIxsyJ=P~)t1T>AW`zUj?d}$?@fOF@+Rap_X$s- z_9{3(pe|dxbDG{~hYbxz{rsNJzq!9q&5X|mPCTwA2y9B`6WoO|K6s(Fme1O{(-@Q5 zrs{F0T#3(O{nt9S=lqET2>iK>dTuXHF6jwb>GD2-9D1_Z7z?6|pCR#zkGlmiF) z!rIo>UX<&1VLIv1CGv(^jps7O8w1%Po9zPwIM>TUp9(^~?jsHFui*C@jWVs~bD#K) zMbEtnw_?3A@CxhOdA~#_5e?EGHNrfB|E_L-@>!_i571|)Gw?PAY|SAh_n4MFp_fAQ zQWN6{lp~MoTfc7m(%94) z-D^x)>-Oy(KN7}x8hLW733EAb*Loq}91jbhh{FE{$zB5(lTSSBpZ$uOwr_rh zg6s~jg4Nbs#Qu%>rDrlcvC4UYU*BDnUrL z=6i)sI?o1U@-rKJ$M#z1@Txi<25weMbr(P*GTYqT6UIeFLo>bQt;N^v0%W6bNsi}U zt{fibc$3rf7KCB00U~(`QPHfDE;K8y!;`1iH=JBq?!w_)TLpo{SsqbQ^Q=&IpSZld zrioRGF_+U@tkmr|#giIi(nNl@@(ooiQfz`I0}00Aba{HmMNN{I)MDS#yfFaVp7CTT zP}ndMhw&-X1^{vJh6au-36SCQ?IHz@w@O`5=)qMe9FJo93>OhRtZKN+s|8qFR+W*aeMTM}N| zbTFPL)p+}Lsn=RF-uqeMB zVXm@z-LO`?(%uD%nrI$O_twOGo774jwu3C_>FUye(>i5`gxRe>U+bl*GT}9hig~d! zijP4oUbOnh<#Sckc(Q%KvA^2!q`j-8cPS-EJxHy=<=`GEQuCN7qsY+>OkC7|e7M|W z1bN*pgfu!)Pe3awJZypf$Vq3AoYELuA>O7<=O>T9Xtrd{Xo(g{PNTLO>H06FY)L1# zK!n%D-{a#$cpRp9$DZ&1yCZW3Y60yZ#4{!@G#8c>CmR!85rrPkY8$OJPp*sT5Gtzd zsPi)9b9M!DNMDeKpk$}Tbj~7Fp`?Th`2-BKiCCw(-3am*a~@G#gEm=j?S=w1W@?VP zs;rD^I*E|_KP+uDE5B(D1ptW{rn#O}4^I8XT%NhO`srMt^W!wD&whdOa3;*Z_nbf$ zBl-KGuXp;=R;yM#c9Qmy1O<^$ThBJ9T-0LWC8;WkUvdbgZ$xp2|g~n zliAvgx9$aQdqbc9o*tiY!vF7^0GC$!X?Ah^3y(5EettXIu5~R)k1?}V%E@Ij@BYxZ z>?yhNoyXhwK~&*ApvlID$niC9YOqKZGf0WJ)fTYk(g zM+cs_*-;aalOF|iJ_n>7?SSC$@D7&?XsmL>h942dzAZY?;aMUeA)@bu@0c3SeChDh z`<`;p4ABiE26|G;a0(&eB$3R|SLmnw-GlHz&6hgg!obGH&f@3LZb|ZWJD)1p&l;(> z(^+s*V{l5|X=mX*o7Ze~wu%7s55`SlFW8CRX63Qpi}}nA%L|o!>!e3m)+!{?_%})) z`o!eJE!HbSvg918Cr?+tAF4OHH#U~y7T&0Kb-!i#(AHG$?34BS z7U6^!-)1vWw&gzq2}EO#gxlD}q`p&3jhWUZy~a+eP+lC5tG*uyP$W9c zJcLmiTE+h#Yi(QlG*P7eS<%z0o@-8+1xwvWyt6oJ3(KVz-1TcNvHPUM&t_WOC`5Ni zubT|TyJ+?Zs%}B^*K|>g!VB00_j~}?CR#E4#xp@c^7D@%tL?dzlp+}^O|_eFlj8FO zT4pSjqPs$Q0YI8tqza8pN%6xb*q@3JFVj7o_$*>=P40c8U!b|HnpxlBOD-_CT01|8 zbj;_%j3O1;y?!toHfDPJjjt>$^4y^JdBqGWrtFV*+L$7^6Tf=^pdn_TZ@&ysHg@0k z)I^caDz^w)OveIw#SwdLa+r9DM<6t`SW$}JXQC`o ziehg2Q_OK`O9%DQtdBESTvzG(e3z%{-vNk~l% z0JXVG0{Yc9V6pB>)T`D|W5sRTF|5xl;U`3I^cqI-xOPl zWmL9;fyNaq(!5~z&cM%nfW#r^(>Zm~W7vZh*ARnU#D8OmzAvzz8uk3Y_5&M2uj7$F zd0m}Ka`X3O{{&-iOpw_dKbKx1I2Mq+o8HopXY;#rw4X+^gEXE3%v?6@?QuU2rxB6SerL<3Z z(z3IpniXMFQ_4UPmvd=Um`r;h#ul4~dhPCxyPY5Wnsz*7`r<*YEY}ct0B4LgaY~3L z?VV8~F(Lrfq1eGB>bvaUI=x$@d5U{5YW!`jF^K+hn87#zOXg3oW|NZJc~#x_pPv>l z&FzLm3%C+rLr}U*O{?jm(0ST$0uN9Vlj+7KQ(S=6# zCf`=*NY8i3#JOPK9rdqL;I2znY$*SqJz?Y+Y#At}8KJ=C{W#5k(C8|zWAd-x@UIi# z8~wx>$i)AN=sMK4w@C^G|7usLnRWOrZEs82Y+T-Wcs}W1w<4R;SXzgLP54r+<(s|6 zF_m1A2{Gx&xG9i`4?MJBRc8*Y0}c&lw4}ntcL){lHt^CM>K79&kn#ecj0e1w&$*a& zKGc$!)5_U?eiCjmK3iLRyfGBIIu_EO?5q}Ru37Qf+__2Sf(01%4Xaxf<&%VwkYcT3 zV6T^iF>WFd%_Yq+j39q ze8m5gwDf+|F&EY1sFB1#;$MwU#y6iL;64cm#k_u9!KimnjY+mxzInO`d)>l3S{`XM*(VH=`630QW8tT*t&Wi#_miB-#MQQ;3ffMN`cbb9~XmcZ`$7qB$%n@ogQMmb^RhFE*?mwU_RZ~NAiPc`dfTb zq=N~|=gsI#?l(1MH9(QMAe(@P23^F?UtU~v=b(jfyYDE6 zB%t$qzou2G+bOut&+V93U&@gRgur`%WUjY;*k4B6?w7tQ3$cQayM!7taNByE_tvZ3 z%~CC$s}!k;m5EShR9)mO$^52KqTOP~`+<2!yrpNW$ttrZ~0KGG)zJUY=n3b*p z@XjBJ6sDv?I`H2-or2w;gM8iJWgnsNR<)yP?BLl_nyzZ{lH&=>;GYtsL&L395m{}W#a82Z9v!}sWXahYRfv03fI~;rz#z2oDBQ0gqC|tlGBr=F>?&VR zsDH}Zt!d`~a9yScM+w=~q!suoiI7r12G7F9N)!WT7r>}ir@;A}jfb0e-G|#1Fd73d zlqGhE4F;Ypx`a(~{_j?fW~INwcTIq8UaD-FpMcNVhP~*d+aDItrkBxZ1xVGf?}HAP zL6?C*pf`DB< zIa7%>533YF(EFN+2}l$}HHCOKU$j1+>iy3d-Y_j86yww-bH#Sbk-NEy_bgDpZ%dHc zIr`=uH6ikt$*i`rZ{&sgar~TVK?jFocISE71dd2x!kv!ja<17}EPKD|epIBk1@s%q zjpLxk#6y!l};4RRVdO4!hiieOl$lMMvJ*)=1IZ<@FSx1KW2IhkYF*zD_J<>Te} z?d-73;gG=&nQHdLS0}mB#T--Ld#HSmi3tnp6Y!T&%KK~)23VcMk3+ELMKKW4;`QWe8CvSaCDb%Cr0%cJQC2dd3R~q{Ic{EnU7$;%6{q*z&I-KNwIu=k6QS& za`5ylJu8!=`)5HR?WR7BLU@21E1o0r=7!DFVPeJ)ZnRgsq+{qRROtiu&@xok{^l3*h>5N)6$ldmRlil zg=gzmI{Ou?bqG%&M*vV1qZ5Yt>`%Ia8%pBb5yDhG!>IdQ3ijg>DAMU&A1mZ1hIksd zP9C@A0p;ai;@Oz&n!q~T)L9lD$C)!PI9Hblk@K!uyF=v@Q?C8gl9E(QJy8{Y-hmsq zA2qyskpf}(W8ui|2{0(W-;a5J_9FS6Z{a~ljh0-&8SG~@6IJqo;4x+EzU*p!* z&ehXKbb?_!G?s_G(RwDrgPIvG*r{iFUPo4>3ohMTRF51FC*yu)2fE2-@KMXK7XY-! zwfHlh$shh@yF#6}l~Y@dXe)as>{n|HRcT#yynL${n!y%gOV|z8?R}ZC!Ro=LJ4nCf z&lx_-$adxWEnuR;3f#G@-_uxQ67@G`3(D5w-$(M4^DiFh zRwJ`LuAR>$UE4BUH^MXFeloIc*MCR;FHOi(^Ao4pUa`E)K!WEFA9onp)O$|0SV7>v zIJ$ITu%vw=Fsy5LaL-tU*l*i2=?9 zN{l~(-P-c@n9) z3*H{^j(Zc@0+w^tud$Xjcp8CVdLl(d0NmQ^z5D0Hy z5D(#qMf1f2@8{oz$99a?QH>0YuTTNFCbu-KGby5~0_ftNfSecqqXN=EbP;Yg)m$2t z5I8TqWUJT~E(R)QJ`t9m)G>J6@_e2`K2GrY=d&gBo?%pfcxRu``uTUB;k1pan)+p91*YnFo9dvP)8X>G7L-uID|ppc;O zJsZ{p+HVp65zAmAP`3dK_frqKL~JaGu?^gd!2+v68?y4x)gUo*&Az$V)7ZMyYS!br zwizbtj?ID_e%flc4CjG-wW-CC^{@2ApKttv=$OeDu$=}qa=U3Xi8TR}94U>lu!1o$ z7WBPxsayZBcPke*U20yTO9upge5JkJ*v$0&x)`Cq!9#kwc(9goZozhfn<-LBieGo= ztlNY{^dpxB6BlxLZ&B)z+BK69c36NESK+=pS}s}o$0+GWM~yv4F!9mYd-jjXeHnnu zCAaDH!XH-7Q`rLiaL1{ZR|pv0vIr<9!!qM=Dk7f_-?Lkcir+y(?CwGq7PQe1d6tY# z7P*8TXf*4%ylLqLxdrS`xG@ifLeY1-rt&qh&(aYi_6kz{FAEhme6x6VtlBSDM+b0FNQy#)pix@BrPU+pSx0II7eD=MX9aM|D~g z_4afjLoVv$$@+G6;m*=@%>ewFsR%?s2-`(Qc@y6Sn<9T%VC{B|)A0J{shXi<(ki%( zB^O{od6uiDvfLx;gNH(!~@u`{fp26&A``LAxQ_V`dj!TUw;0T)3qUX&C zJI1_V%+i$}`V+zb8bEa1giITJY-h;?9PW6y5FqzseJtmT))1bVhaxcEy+My{-9A%+ zT;Csi*zJr+1IU;7AHXyxxF-uTm@iP?uJDFBD>9B55bt1JYY*Zo#2Uq3KxICE#<(3> zt5%M6I4At)B=*S>P7M_kc|Skc9T_8pS@A0Wz-x-Y_QvW9Mix~?GnZO z3h?7~2m`4nB{h?EX1tF!-zs8SknzwT7VeO@vNiR3#@}S`+sIy4QvN3kfOjiMEkRt< z0-ws;%{5~jPy0LXn0y#Ut)`S^^+7OftKYU*Q2it`c2neK_{sa}fy5eWEWWiNzCs2k z37SU^`BVkIgRsM_78g~LZ!}K7xO8$I8~pBhbrrcnY-UZ$myL}Vv~Q{X45M5}tcQIb3Pp-C1(eq9N-$(Jm{53F03)M46$ z)69jdy1=TzuQ^k1Wq?XoesIGm_zN%9cpGMrBKbZ%{OtDnI7<$yXnns$n9{s@K3nK@k~U@JF`y!?KaVErJ0@jMt* zm+pOnm%{D*!3t8%>ME{>PR1de^mYsOEh*X<_}9t9+!>Y+7Wd>2%VEcf0R-gU=I;Wf zt>|Rp*((R$(-CQC4$P-F2TDee{EB32O*e6>h3BI4Ih~R!>xX-`*@$@{5Kce=tE<#} z8CA>6O|bdJN-HW(60N^SN(n1d&E}qZdY(^8vX{fL(V?=%yk|*IV_9g*5B_(8^2v-B z%S$E>9Ve^tRs>zx3SQMNaA!5sA0W!9k)h5TOybfj_PtF+KGX8Wmu@K zTbP}1>2NzARGY;aB#>cXe)?{_p9YX%?z*P}-NhOf6HWjN#w0SBl)F$<)L=2Y&T zWOJ8sx2YGq?OKhn-+W6=^-uSH=Q2Hyx&Lo5U;u(P6Su+*YHFL&#!66ydl%yuBcuF? zu#UOBrRzWqy-b~ZPQ#(VS#z=+N@GdFg=@i@(v}e)`;^UWK=qgAd7?wNV@3MwcCLoq z^J-D(XSr9J(d9c0Ky@lRgC?g*!;82sDUhuz@*ii4(?I8<7`aF`BGV8j`0XLZdl{Fb zU_@h@d>;ltb-VBpZ*k+&ljCf2rpgpMHAnZ?qTY004?7D$EeHGfA`{aNyfXXx$jwK6 z!Qf;i!0g5@he;F+ZMe1Gxe}q)QgN~(=GrQH|8lqN12( zZz$dmUC;6zh1=59X>BbZANNSMoR+Mm@xjRBX6fyWTgU0w6r;ZF!!}p|tocDVGY^}H zEYhq>NTn`}2<6Qp6d=VkvB+j^b_rLvY7_5nEa$+JV)8wy9fmP_3e$0Z$jpq(mJg7w z+pl4X_Q%ToM=u355s{S2a#xo+K$-y0b4PRny@%JpE+!o5Ar!81_rc-XtGPHk>+EzA z66Ft-2!9Z)Z+aWC+byG%TshgHQL-YOne`lre&D|E9XI6?fg$2Y5%;810=Ug9@WYT+ zu$ejf2k0^ytY=v0indASV)Pw1mJ2_*`~oa`(qUNhj>#MmA^%>`ZQG()r$SMuPOV=$Bj_`S~4rE%E`SPRN1|JTmI%@E#vF>=1^y7}U*S1mebqItRY*s~#X zu67(~M0%=;Hbm5EnSL(;w)U_LZ6xGdCxww`|18rf@=`;?6qgFpZ}%DAdrkA|mBA!^ zpHR>zl5$#r>|?e6BL5tTL`V<1l^lT2M530=SrRpzs+CVt?DQ3cQV@bo?Vj%>m&PBY z2Zk!^xJY)4uJ|PGiVWUlF#bh9ZB^AX+;?Z2C5je9 z`>WS<$X+^&Q{~=h;iP9+0A{>lC4qqv?4QhT?!O8@bPw&Z*7IeW*QQ|{TJdq;muXws zSH^<~7z=9dR}$XM(-$PI&*iO*T1%UsLs)`4 zDQM6n9~HPh1gIhDio@n3sH?#>Jh*KSM5dZOt7mHkG}wFTMdVy^#HX-o_A{_zLXA9y zlv8^0*=m-Jd%8UFntS9e;~%+JNGg!7%D(XIl0hf~R=QO=*|TzV%-~u;l?A_{HE8_V z9aF%_1PLjSYcDVpqp2{@`#LJ!cB9|SNynz+9XG4@4K)^Zkd_wUoQmA@M#v1?*O-2r z1$kvLNjg{SInLn145y?8(Sn{$B^keM(yVBZ+<2=xyalp>9n`4Nsfx0@DuB+XPg`AA{ zb_v_*_v#;9m&9py2cDfGh}k7LY{#68BC&_6@Qa+_(9=7-oz%G^Y_jZqT{ z1*SIW0}W%E*2VX@i9knOMt^vl$8#}D=+Iq)3{LKXe?^Q`Vad_512T)iDTULbZl<;H z^T)}an{$iBg2O~7(~_1h)I;E=!b0Ktw<_SNA7g)lblc(nlvZrZ`Vy-Yt+%aH1m!ga zEm;0dp|F>YW33sW>GU5db&p%GOpOAdZnvA0Y5#**Z{M2k?U( zrlD8bWodSZPs#LaXPceZ4ZsMs;N>EJdDjE212B{Mo^#x6$JGs%vC@saDXw!qv~_Arv=biWU)gzQsz4wviSJoPhj@xzMf%0q zm}d^15uW7b1!X5l``-lOHb5+|_}u7#M?-Cfup~@{zaq&d07RGJ>Y2^NM=y_Xkz{-X zs@=7(h=r;Jk#sn^{(4S^iO{VQPtVguhF`zful!ZywIu^i%KzgpGjfC%uM~eJ8~YmY zv;hg%pBZ*i=i;vI;a!I7}eg`^%yYhEGMZt7HAueq8PRhkGPwdb;ydN1VhcLfW%3J9Bxu(@rDG9psnv$S7symb(AE&Vmf zLxk*9n>xvSu&*g9iA5= z{)3c+CD?k;g4=T?(JW4h0pMz#vK8{I**jl~=3IYu-6mjMymu54j}_7A30}gngEp>H zh!$a#e^z^9hk(65-j){N#+IOd`6819yHznhPG)L74AiQu3X}zQAg92dcwy)YQcSPn zSz!u=>|JVeyP$x=-IRc;5PX5wp|wb^Qe-7qg$8g?nWg(o!9=RSqg(JUW*0Q$o86~X zPta~-h|xcL{l5>5<4~WL+Yi7n)_mQ{p5Act^)A9A?`3tM9abTWDx~X?jb>vYaqil> zy4d2tGBrMqE(le;UKsKM4>wfAs_~bK+5=vg!|&eX@(WVE1>REN96+Q7xhajw74TcK zPw(&C#hznS#{+cnfB42+7VY|Q;OVgZ{j8N^Ju)EG(C-F)!m$F6wEV!;h9Ug(Ts*ar zls|lpInhWPnC21}cu}u?t_GaHA0KeKj+CKU<9DnkA(eF8s%5FsbL0-lM}9)Ww2Cf) zKwUqh0dM7LGCJT`vOGtaQ~=%X=50Fbfy|Bmm*}GDSsVwqtyas{&0dh&_xQA8;*gR1 ziTfqtAs;NKWZJ)=X8Oci*@;~i3{p>?TYkV1@I_VrIMj4KZB;(QJ2lrhD3}O~u?yW8 zum5uZ8)htA<1SqhpjH@<^8Cyh?K_hWaLCo+UvAVu;&Jo{)aJ?Ld9gw_Yjg|0^rd8b z#30oE@b0mpx_KW{E$mHTcT`-CTxUnb@bQOi$L&m*m}TF?+(7BEd^^dU@y?6zNW*F! zSIUzW_m(OtcAtBFe3d@0Qou7OandBP)x60M7qIi{$6YH;+n?gSk zPfa3Dm{rCu<6F`L9GaU0&9lqd!ZMepDt~x*V#m&%-j<1R@WnRcfjs>UrS4*W_UF0N zTYIH3T2C%%2sNW~d>RE^OolQ%6L4gpM`cI27Gh8-{-V z;>evl^!Q1mr?;lU6&3-Ryo_c90O7ImceW7W^+BYm;ik2^**#=c!2H2lxa>7msIfT3 zT8{rX`db}*8=}t0-#BWnWBpG-^nbkB8qW~?d$@~gLZ>kO;qeY58@JAhrgJD7IXRpy zBLymv*u1rJbc=&7Zm+<70+BxE-g~uPc~R>F9OsixS^=6Jh|TVGw9h>rBvPes-Rk&81 z3pzLfHw0r3BXVw7i3XA^nFRFW!Xijoo*0YG2P`S=nX&LnQtOLJEySnm@d*uHX|aOxlapmJligKGKYO3LkOy z^lz*)yeRLRTo&t=!!FbyW|cY~vg4-CgIM|a?`EN@95Hs>vwYa6#{(4!sr!*GwQQwTPA#@jm7>aFv4W$NS8x`qu&4{>GUmjIGL5}y zOj~E$30JrNz-jojK*Y~^i;!&P$&7$RyaPn1kxf#7gm`8T zD7=#+k`C&rytsLb6E2BB;#m4y(Pcx%WUM@&m_kpw#%_zsZLjQ|l570TY$@gM$~|qb zE2av6&%Y6L1s$cbD=q3Jb@;m1O?LDxHzNSagWi-Mgt#_ruzK-%1Z9p%I3Y&)b*u?n}DUkk9kzk+d$MUC{C+_;h z1}g0>JLM}*3nKYHEJ?QUPa>q~zeOj{kMg}p$d83It@iBQ>2*^tjWQdAMIS8IBHl(N zda>Ng91uS^|GNA9>Vv;LdOgyRIfsJkrc_HNe3gG_g_356FA0}@P7(W~EDjs|;E$@Q zt++Z%B7f(@L8(u;Ri$re@>b6_af5AvXCu&PD|Q3lVQW&3yBEg$;ok>*-8>XFIDv4s#MzJ4qt+F)~iFiPwRju>*lTxOm(iA zG@A*er-*E0!VlGkEVn%=)BvS=U~=8XkfiC9PxacmGGO=l_+CFb5FHyNO16_22w z6H4j1P5VJ$G&=&Xn8W*>j$DPAv57w7tV7Tl4_`P4o|Zh#>8Ytv+~)Vi&B;s*dF06<}vd4 z@Tn$CqN95K{P+5)>%(ILqdB44C7?%gvJ3#X-AB&`{m4RiJLfVt>G<>K&zD?%HzE!L zwJ!P8o)!p5;v_HmPwwu^G63i}V2(MVI}gWa1#xn68`{h%lSOn#^H=j|~7I+N=%ozh;z^k$RJF zwp-0ArfMjdDS~eJ$}|Etm&9=qVN=kr^eSkNA1k99almrNfDD`dRc=_Bi&lGwVv0 zpM$Q*O9d5AZ<7-RfW*V-#-YlS4WGC5EvjBsLrBBDtLG{XL$z@mAIuPt7}LePA3p+S zz#03hS@*ukq0R3PL!9QtU%qH}6>Dr|FKMaPkvXu?FB~&KDcK;h+Uzj^^hhy)B~pB2#;}h62RieXwnxdmBIh;svul5dRgQ>{JpqOWtiEp;0{K zopG>E{V(mif}?LgDJhmYknbX0h2m+1NZUDb9qIXjtbWnar1LgLz3P5rFn1kNN|AzS5Xq1ub)$DL2YgTFfEu`b*O{D9;AOHQd~NlKA4PBmN#No+vD< zhz;IiWq$qom)gxBHARNGico_m2M3BC^du=v+wHai9mpL3ml>eX-gu9h)vP9u$;Zlj zV{}Egz^O{@Q_L`8zz9E+Lc4Trh0WAcq{7d@QlIdx(OUFt#=-IIelQ=1wLdcUjMhR9 zeJb&nEC+vd_83vns4oxJJYzx&ipuFsv1WYorr|P)8R?`uR3H4{;U-d^r?C0b8lVl4 zr8LCbe9v^Xu5(}T=b{mXQv#`(g_rlT=2tD|$d9=Ei%AChNwho#R<1?2?i-t^{yD+62Lb^kRdCccKu#%;(G$vWa&{t}Pk^7pc|LR} zG2?U=4Df%f4fmfNEggdz8B|b-nz_5Kf&-nzs89QYf17(L-W+G`2Fug8Ko-e8ZR>fJ z0XYk5Jmc&aX8{SFkZ|;l3=x-Ee(gc>6X>;!<&l~*yD1jN+br+NLWE^tzkj5VkUH%O zl3!iv;BF#2;&U{86P0M=-fT4cR8??ReOJ4h2pY+;aYLenSRxYJa9(~Ht2<7`qTUaa z0Ita^G@8{M#15mXQZ#G~ATWUVtAe zD1tCkC)`6huS2G#qR6c#JL>Ut!$^=UB){-iI#D9m|%f*Gb` z)gB6k<4ktVl6uiw{Za9H9|JQV=|Kl871}chw>IkN1}+i7WJhZ*b1GcluPAiQY~Q?VtPQ$)bH491@<=JEX6+d zIth1gUBjKLC1>#B`4$!>=#AOp?m{c4d{$ww@l(!4j@g;-ia5XcIQ@)}^jCE5D*3ES z^cE9GOS+o!7_0Ly|D?;0F0D(K*b`@<7_QCb#t=G&<=ExATqpJ1e>KW~?e5v9?2wRm zf9Ka`_)?)lwVa|^T>gxe{motf$;}Nj$1W8R+%J4@0dS*R$1FovdwXJLokqU{%XP+) z96P2pQovr3uf?Tw^aTv~)&ZlZ)H0;<-Sy=3B-BN#deK1k~E|*o~g-7=SAJWV{&@1XdxYNE?l66(TiaN z{(C>Ms#24+r{-j?U`-373z6T)-6;uwF3<8Cn4tfIn5KK^1&ziX*$58DPYnd~8w6?p z7i!9d8eR(krO!Uz?m4vFQB1BLNLv3K+ot&5m;6c?;mQbu2--pcaFaq-*y{Yd=oVtBTr zD5=!XhUNzW2#Pb#l_&4do`5A2y7wQhJ5OLgfbmkRm$CITo+#S@7^~Z9D|*N`OH;73UHLE=B_8;9~Q0)sLscCNQNs zXzQ(f^JU>-xk?GI;*If^l(|<)>a;DvtC)n#6S ztw}GAljHCH#Jiw;s?vrgf>=?FV3$fr+iqaflFgU%SBNle#uRhG03$npcsP~w3LvI< zj|Uu86q$feocB}31u)c)p3$f6$K;di?-M5o;+t^tb+DahRZ#2lspN7NW-BNG3vm*N z>=s-uWtX7qG_U_~2%R)uTH+<^QyvCbMAYGfn}t$ChKKEzgvs$zoeJ#q=JOgrMsjlk zw#krYTerA|%{GBywCZp%0~aIoz1Ia403}Gv~5UpMaMi z5n{8DX`OLJ6hD0u&!KLaRuJ8hMn-WNUXydKm35Ah_8GESg0y(drKH{tOCC4zK~lmI zktQB&h6t(Sic}|-`^SgQxkPQ-?KlFnr6Q$|<)U`CH??;rTC(~1Gm0{2#~VlxjWS*k zYbqdO92wkX3AT*4Jd8Qcx9ROqmC%p6aD6;xzw-_PQ6vzJ3ytspQ2A8~7UBX}BQHeb zHazX|PnN1CEXC4oi4N5=WtkZox$l?T{;E zA}()|Cm#=Qq967Cnj%iX12fOV$e(1W>(o`HT(pE$Axqp&v28J*#GbTnu?h71Y_o_g znU>n;k7vk~e|_=k0ls@PJd;{(XtnvB4=#gdXA>EJ_?*kr#`=RjQuNTd9F<lhFQI#jm=D`u4qAc^Ross2vfkZaXSpv{ zC}gT<9YhTk2%s$AypCHZRPk!C_1_iIsPF z^lVW2`TOJcm*2U5cdbXLKk!Ih8$X7NPji>FZLT2X7%lqpy9)>ebT*t1bgOn<=%^jQ zq_(UdRLeY+%g=hZJ#WwZvyP4TZ7iItno!a6SA5ou@k#3&j8$fo%j>y!23U zWuYe55zTAuCIef0@?>fQhtAg1TFkn^EZ39}t=UCBn{ADp50 z4S=dLZ+-ZE9VS#qG=qw&!6@?zf?2as<3%!aEVWv#w^t;wXRW>Gj zTR!Q?a7x;?T{cMYpIhN+XOJTB!-m(n>6_wFFuzPQK-{QT+MXexZp3j~5r?8;*Utk1 z^vuiNO=CI<%e4jHgxkEY8wJ{8V{xq{0#nbX^^zB_#)JRg`v3843I$lXp-RW!$v>@! zjFlRcKBAzrwy8}Dp6AG*RGRIlpFtYaYubhPpxIZC`?nnL_(h*(Wo6xRo)hclEG?$R zEi{O3Tu(0lo=UZ-p#i;`iOdy#e5sUa$ze9x9P=D$b0X@bKaSzu>Y;x0H&;cQ?3I>3 zI^d2=n#pEsmgtBk9_j@jWIZu=ul0#|a1-ttRJi)(58Z|7@!IZgr;scV-^!=EJBFR_4NOS+%L}5>;7jcNiYQFDO;9zr8%;mj8B*WQw?ZaG( z#O6xDgkg2muEKeBcrzOZG$-xXvVo_O^ap1O&YSmIc9{oY2IhR8VKb2+6G0ZL{pxk+ zr9z2bndN!;4~vxq)!^U3?Cy&X(y*s3l_8hHKl;u|BH&z8cpa>lP+k(BG0fF6{|iwd z#~c7f7IB#MqJ3rba4sQ?He3ugo~p+Uu05V8sF-8Rk41F?2&o1tCOOY^Ta?=bg{ z&bo1cUvA7urvJhX=M|NpqPOm2^5BnF;o%g)qP8IV{Nyb?QZ{Y#>7vZKzBKOFi^oJ6 z(`0B|ouTkH5HIC^720fy+QtyKn{1X}0p|rU=0azg*zmNI6d6^{~{s z51Z6>K$QA_?ux&6jrH?p82QA|u=g_H;5ue`OA{>boC81c;KF+xC(= z^BFu6h&%?5;qiNSx6JI7Lubf8E}n03z`Zk;mnx)3RiIswi_82;8x^0HJ^(fF!>XUe zYaSjAxpVbjUiMFG!R$*X1Tk@5(yX1rb|TxsCb zedCh|-IA|dq_rVD(d$tL;v${=zF5Wi%$$qZ?>LrQEhz=W{B-qw)fH>b;cI;ycU>RY z7uDrV7|BLOryytUKt8&K{`<$v&FVjVPuvG z#1EA!>EkN@?`Z0goAuEeAkaRe$3@JUnDL;m?AePe59p*hh%8s7+9l`K-QhI!> zCkvX}`n2j>uWTYdL_|*X#+ROGt^8v;yHu-ob$90L8dvJ)NY+*gGP-W(KPLRAKJ0;U zdq{bh8)`Dd)0(MXL~rj6^#_W;RFE<2l`CqQnjWFtu+-~)&QF*}@;U*%iaeEH>hs&U z?ytrx8{LkTbVU(D`bSFTQX7AQbPqwpnUIyQO$hewvCe0y&v#q%X^z>SXD5Y zs3GhJIq7{(7D6#XAdkAT))-~q6(1aAmH&Og@uL#9-CuGuCOc!1{>mjt8<(UcLD?85 zM^|oitk0(uF*T){ZG#0D>!wy5o7jlAB2icx-a~gbevelg_QCNu(q4)fbFJOl4*sXa z0o!&V7}#yr-9_a<-KF^^0vW4z}&k{Gt+b6~A;aY|Ikb%M2Nd^gTRn>SCQ5 zKTFxN3++(Lr2o=(8D`v3UC*9j3kK#6pDS&*9%}6UQVFGb54?Ob@IA)4J?@uc8h&mU z+pnGYxO*h5UgnlRC%SH@LK*Uy9Z6H&)I>mb}5Ixmk@w;rbD2cYyxw zk018i$WXKC{iwrlb&r}W+}+idml2S?p)D|n7^;QdNN!CJsem;a_E&r+OOibqTgs9K zu*PFk^VQg^s4nZTD?Yq81l20I#GD1&r~C(G($dnEM(fT6ig}GKA$UlRA%hHGrrXj< z-g4XWZ+*q3(-imfdUKtRovPovgjx*OgH4Yki*OG96%!H%Mpm+%g%Kv zQZ4agyt&~>Smd&rd%Z}`ju@hiIQoy?;m`X(xE6QBhbP(*L{=U!)`z^u5|GCzw1l;f3Lr!-X}c* z(d&yA=+6myuhH5uzw=P!{?*~SW)Nn+p_z7L{6&FX-T%kldjLh5ZSBG$D2fCV89@<5 zBuNGdjgkbEoTEq*5Sl1CD=3l?5J3%yL=l<%8qp74 zU8vX*;`^2$kUFs^q@kXxxiGS(-1X4r34s6hTI$mB@3C4$A1j>R(Rv8ERn52{*N?Wo z&n3^#;OH%Mq1kpCm&bEM<3;S{^HlQnuLIXMW)Rpw#6VgHY`Jr4DXdK$bZwukE?)br zeuGkIU-JC&17wExi4l z&L!WHI%bmnV?qR*4!W$U{}(GklWiPcdx3sY?uy34-@E!R_W4(H(Yh&SbKNo?I^gu? z90`aZ)w)%$^SLh5s%sOFo*T5L?_gTuhz~C`Q)mmBpk8w3Gc2BY%pM^bVzIscwv$mP zWZ^Q(*xi87F|wB6-aB?XnTBG)L9$Q9*Lp&ft}TTal~)rJmbp#QkG12f@!+BojS+_3 zZ(?v4dNQ6Uu~5(i$<_K*?p92W4P@fx6;rSpy1X4BwtD=XFLrCUW%%&!+m~d~V<_wJ z^vo!UD21lxHx+G17^oK+>gD0i^E(oXyCvGn1r8}MrLV?-`#PT6BVZn4CD2i344URq zchT^XC|qjkgCjKh&LA`*B9(q&ynNek$0n##xx8RM@st-N*6DF0eW1+tP;lylRJ#Ji z)U_>4ca~(1_w4FW-jGON{ybaBMAt3-uq4Q%)*cI7i_c9{k0Q8CZW09)xNNhpjJ+B> zOL$xLFS*8#)FU7G;9bmh@U1_IWs&SP1%<3hDY5hDReBF(rfrkPd~9j`F+T}M)^$env+zC z^<=mYvek$M?tifwbtSb)5ndJ9~VEYLEwY;`;v^fkE-)@%jpqgqPGHwul2)j|KfAr$$*GU z6D@Jo`Z#m z%ZqwYF(P70oT)QhHwEtIFGUtWyYEktEZ!Rt&*8I?3zj_yKP<8PNJxE8KTqXvNiO)K z8IGrgJ(+Fx6&jm?PeFf0hCfzjoB)35_d--atd?yZT`v97bR<}QVi^VaS^n2z)&9i` z3;YQsJI~G&Yqqx;50`@-_fz)7`~d6Z@rqCDgx|(<&Z76|6og z5fM*U;vf0{@)W~aV9kXr;{P$V13q#O819hP!Q7)uGc%XL+o3v3V~?F31({00lE({r zeoEZ__FOAu(`kI%_k3|p@9o_9JO2KW=H++w(V0DM0zR7=ADIDGqC$J`(yu4+CnKxF zy0xkC|FPHo`pf@Zs&_Yb>s!zLSFS)W{ODiyMwiFFG_MX$K!=R#H*383?BaBW`mu4!kKf#&Lv4d!08tw_+t(py1!>u=dM5b2u@{{^~m;?I?YB)R=Z#< z0r>HSq$|JT@W0Q^oebcMl+=HHa_nPg+R1+bi+<#?{0qqbAwa53Fr5C z5|WbJR$n0cdR=WfVC|)>JoNv3{$dqp_A)wyLjLQ9Y62I}yR&3`2kig50Qo;H@;@BY zh8D05O3MH0%$NTEu%DX$Z$JN^eg5C`ch<7Zg$~syxEfqeOw8M=Bqc?zz<6Ic(TtdY zAX>+=I82*=ihX~JGy^Kr(l7nI;kAP zgk82OCpw0wy$NUu-S!=IztL!QRh>1}o{143Y-`tr43v7ge z{}tHi8yY~=aWlL?e&?1)C4@uJevxmYX?R-fBtSpnbJC@QcxGni0{iZ>qm^E9>ATrV zIZ1|$r+H6amIlZ?n3eRPBbdZ0?URAwzd4fWV1UHjj94l#gg%3Z_t59G%fk2A?IHO! zv%)iH7sKAYGlmGU$o~@hde?2caB)x5o}q7SJWuRAU<<#kR}itY%rl;$Hs2FIq0Cj_ zJGm}r^GUJ(Ggo@|GYErnvKCpANF3i{J~C%fd*T2R8~#z>eVX1eW$BQ zZ~i87*HL5bM;2>8*{do^J~bXfvc(n)COgYocC!-kj**zQM~;?!F=UU|W2#&TT7ATY z=ChEKF_B-T59ikei2iO1=ZH5b?~=bJ3Fya+D$4xdE9=xU|=rxo5=!58kJe3W7aQ?gM zLB9)`P;BI9#)izVxp`WJ_xjQl@L?~R6cS2a;SaFD)O@O3wmMpGK&b^(ou`jnk_oMT z$-BzJ&uH3COVzl>daYfWF$859lMq47lQ7TQ~hZp}X_D^t6`(*2r#ZHqI6j0ib<80P7}yNO=E0gv~$M108nVhJUP^ z15~9n6>p|t+|IZ2q_oZkPmO|WMYMNACc9OW#yk7aA=dua*qIEXR|*ZnSjJAu zVYJHvnhk-`1C_*d6E_Xqg~aoO$WAke9az?;on1KB7dKe-wkG~givn#)F|YZ^{q-Jo zYMV)Ym*g0RGzKAlm0JJPyG2M2#)gh^n)>lqd2Qv}lL3aVPHy^JFXy?xM>_0BJWAaD zoD@v(6Iq&804(Nk#X{SQ{Rg3QH|&h!E@jY{wHa;VJT;97#AUq6(kiYe1-I9$a+beU zBcz)LyAZnLt`tblW|#-hTjf8xHFdm^q@Om8rCi8(idX+ev|stlBtcp2<|wTC$b+Wd zC%du%e&0A$*e;aETDKqlr6shb?LB8tkr7tx7=InntRf zUWQ^xMUuzhl$;eyFEv$$abVpQ+-6IXVU~5ST{)hoU&tS^O^Ql+ zgy_3gr&YJJ{Jd9A*u7eh&~|;0E%4-$nX4~usjAiWUMtrX>3kCmWK4z26z-^&u2mgg zWQjO_`?eatr2SA~iYq8Bq68#SMZpw4zuB7_kR;yirw90Jbcr$BgQNmxteknlaTMJ9$mg)3!F~K zV$iVF(nv50O)+-mGDrK!n%Is zjd(uj*=^F{jswamdoM3+jp^W4-SZA8vecVoK1_PS0J3R1kWK%O8%&=CoY@A~po&$% z!55e8MjDeqPx`T5U$zqd(K*lKBe{JKl#{E7M(#z*dr#u$_nrWM@&Yc z?K|ZhD~<$zh?Q@Yeuu#p&#<^j-in&e-->@4E z!trMeQ%x|rj&}QY5puN_0BWNdJE%l}+Klr(0iun*He%|^T3;`?P;Q8PL(nS##lw%6 zO#e$2#@o9VE4Y@jSRnQ#`~&2~R8@JVOig=uMe2PjwO_<&m+}lvxwMgN8&7*DIR-ejiYjk@Q89tUGD@!>ZaBD z@?!2=QvC7EpHt{#29RGQ=6*pfftngw>=!11X$nHcfX$U*%t9icMg zlOO_<>xf1@hdRu^CLsA9!EM%MwoY9ct2Z%{XT9Mx+5E^^;xY&0awpt;vm3HsqwmBX zL^TY{SN2ZZG`fY3{PxY+V>e#VUe2$=N%lzyjVo-WpJ@lb^3{A7OR4$M=hzAT+G@Nr zXIAG_4Oc_F=11Q6m6ULs8JQsb4R(&(NiAiJB?PN;2`q>5!U~sWQ*>i9`xGm~s~^tX zg~@I+DO`(69+Ta!Jt>v|EvQuJP1QGq9C#|ZpZ>fHfC;r)6wyOJ69F+WERT<8fKw1( zn`HDlE_}q^?B*o0y~upsL{3^J<8oArgoF!oSnX@RZW$$(DLUaQ6}N~JbtRHKIZKr+ zzISXYlCo0jVXgTE5jurJwp;l$_bPPa%wlK+%Jd@G4P-5;LXS-Aaf+YTWzAtIL~*;W zq!ypZUl9lP2*45}nzJB2;U9>G!)HsJB=g@wTsg?nK58a*Y zEb$mH%e3w!AbNPHC0Mv2IRUy~swcih(1?3zLgk|`1WJoD2=OGp9g#e$K z6yzlM>9hX=KENreNC7#yh2^VEmItmoWpdi3HJxZv@OqN`SNL5C)nUBQ^$qrQ^|P8A zqY|Sf_Bo=N77IG5+alsS754Qm?x_U4DHz;lwXs1W%v>7f(m57=Sz3e#ieCd0pVl4! z?hR7cT#+#tOm{AKfF!e)f%UwOt*o4hU~qpUR7k6Sbo8vUp*%82GMFUZxM_I ze#7={%?AqsC%43-Vw0F5P6XKPTn#6JC;-pIJYPK7jpTFyEd?0%`l2y8|e7ADbpmQ6s zYrg=%Y(hTc0g?DY?MjIPZ>IguRhrhsyf9(ssVmy)pM6`7)B?1bjTXma82H$mO?0zN zKQ5qQW7K0Bmi68un3E_>G94+YgKeqRRIxmnCwMV}a>RsV|IcEZGaK-1%gpD3cj^C> zF1h_;AOP2nNvatn#c%F7sn47uz5Hx+2M6&sF1q9es;TMSZbG>ISd5N^y*r&6ors|~ zdP-N+WCj7W6&>d0{~)UUiTvJ9rw`SQ7C}Ay5!31c7!_o(8so{Yo=FmK!GaoszcrhP zWPHjnY+QH2!n@vc5q{Rj@n)GvCBLipK>ARtzN3p??LPnhY}|^gTbxjZ3@+l$aR{v9 zG0Q>+)J%ZSoF8pwWg~V?JSUEtI;6h*v*!i%`ZL9@-rMR@I!QIdehQW(ALaCJ)Vl;3 z9jT6U#vV-egM@DM>!|uVYMQ$wibVIGFwgVR<5Ks-CS&ouiVV+BWMPU~WX_eb6Vp&W zX>H(T7LD&HZ%)YDI>k*Yr$Mkf3#lxP5R z+ZkI=6k?M4-D#Ab9-lY=mwREr<;gd#VEP`~hxL9f#%r3Sv}Ny2@Mo-feTzn0_g!6C@rECbpfBMZBFf1O0(D#d zSe={jjZQ5iq&l-UbEV!L>-8Adh0uQ`0M7w8Ury#XsgYF^1e^jmL8=p>Z%>uga?s$Q@rj>zBPI1`# zH%Agj>X{|v{@oFuK0T>6n4l1qjpNXAqpzBaR=4>CVpRs2T6c+DqpK68HJ?T>{EItE z!nydPh?6Ye^WzD1^%GP&n&CKqHWgJ-{V8OjCtPpL>xG=LvzAiUIRPlV4aHI0+tK4b zHG#Vjok>JwmAQq1Lrwal)gpwP^fPp_Ua5v=BDof?A^g2CwwYbWW8$lT|4}w0h8hSi z6s0XN>D4eKNj5Abyf{ixeKUYRnNc5Cddv(!^b2ygHr!q}p&44+CL(q^DJ~OlrBZJ#yiKsFz#`LenTOP|HTF z(?$0}d-~j+OG=Yg&u&&5UN|~+``_v_04%W*kducYAg8~a0$Jw2CMDUWE1X9BwC76awGOzLi+49G$!a$YYcqoqlQ=aj2+LR*a zojCS=1gnSZ#?{Dw)Ru8@FoMM)r@RKpXv=J7;K6&b9ui&rud;UIggstok_H1$hp^Dl3|kMJ98_lA2-O{H$L}VNGk<@Rc_pIiUQKAyV@l~M7hb%l$dNrH z-PZ8f0y}ID0QxVtv*NP%X=eo=&c5%=p&;2ceS?~ z0}5O_cW2#i9-=>5njD*5Ni~Z}OL;H~KM>U^Iboyo;?%Mjes<^D7KZefxgX#CgA}NH^h0ggv*C2J0WDMaQk7SXToV>K|8#*w>M3xn=|9q z3=(-1fw%Z7vRro0eRUzkT%mT?RHfypHf>QBsGx>>*r?iKAR2DJq(D8!t!+zA<80KI zyfq$)1RYiK9`p`uhGp%p7|>uXPRfV^dwFmLmT?A7rEhQ601{lYD6Eq-Sfn$ zMiw?9U%Ni_`1ql7VV$oU^#aqmN#a1kFqk#|r8KMIJBfaub;Fd^GnJ@pV&e3;w!y~> zU2&CZL)}_;)zRxyPpQX1COy`;6r@nAac#XzrZ^@j`;-0-0`!1x^{tAaT(SJdPY1RP z8iQR4Y6vc^LCPMhQZ(*2zi9dDhpD;M6c}yuysv|tJt3;`!~P^LxKJ)yjgt6s*#+YC zyAmw`ZihNGG};xRHD*($v6UN+Fk6AV#ZT(_iY!{`7ya|qm3z=?IXzlvBb?7aA3Gkrx#0 z`LTN7GrQUuEBR*HaOSZoTLDHESoW&32 zUVaD-&LZTRQ0l9*lQCsw+2@Hyx?fB;=E*v1Qo0SIE1XwY-@MRNyS~0->#{n*gyQT{ zfS;f-<#rlb3I3-VW|AvSn#W^yJZLUxU88wlxxtHgvv+cj);A|!-DY|ceUvJNg6W~R zQw`U2ju>yue6EoTf6$bA(wNMEC7p3v_j9gqpfTNn1P%h$YIlaCx~>IKe7}bI*kK{* zO#<;$QO->faEcwm3Ow>%I6kQmPX>7NVSSg6@22)AF$~*894UB#g`Mn@TfoEAE#60= z9eQc}^**RL^>*^9E}PQIfr`{O4ppm{#HUucnM`JdCSUOo3Mv1r5A%RccPIRgN8ixM zaQ)7m4^ZyTcU8-BP@8E$lZrU%Tvw2v65ex~H>;E|nJouv-w#$YLd0n=+8(DtN3*I9 zu7mBwXZ8P?d;Xay{m>D;$+5M1|9@pm@#45Gp;eC z5=6+E&TNqn!Q!E<)?Q{D<;6qSTu(r0>T|*uWZ6-A&dBjKO=Kb-@*`E zlNW1yd%ys>NGGKmSbS{rshH2;rj@=E4BV<@r*uJ?;Tg-_cdu_F?P|Ib`_((|bs$QM z`5!qlnCAVEv^(|9VFdx>>ceFX65qcbZFQ|JA;g^}f6~m{v8_N?G)kzy2jYtAZvpiI zs|uqfWr-UTt|8%BUge+FarX|X=ageDHWH3>^iJbg)TH@8x`MKp3i-AcDQQq`<>u>W zk*8BwUes4}-EN%BolhnX`qt=*2?w_S;e6BGpK$M|-+pmJxo$IBkHe`=s_`7LF6J$Q zhDyMezKe|Nbuh1}l4w?lYEJ9%vqa7>Q2uSd7;J^;;5`>m%$!P>cEGE@QdSzR_SWB9iP1m*U(6Lw3JptKUdhTsK9$*$pnHo@FUp`|P>xxzHKF$jCUF z+C6#kI?m-w`s!}VCv3`9KJXb1VT%{oVE4}`^U;E7p_aD>Rhlv`DJ>U7*Bu@AvO}9&l@KeZx^yDX z_V$(eY-ZqEJo=0t@SUF4^;h(ePa>G?7;v;3ry8U``HSF8OmoBTI#>WR>+}_vEzI|Jv2Krw1%q6MnQ5^tn6l3_7QcXN8EuC@G-c+?) z-Ax6DOA0hjgQv-PQ{2*K{Q1=OQK5PBEd~%r^E8~FwsS)YNO%0QqfUhNICOa)pe1eK zhC|H~XNG33>j&w`FdL=G1XR^GCf$ejthnRgKx~i?ex_wV2Za<3tYk~`hh%F{Oq5P~ zR^J14WJybJ=Br5z!u!qSsm9cr+NZrgK=-EGuqvwB0KX&dNs!=4B&ey4 zmRcyGQdC-6*3@9D`xngSL6o;z=xNYCmsHy;F|h_QS$z0L$m78jM*zC$cqyP3dAVJ2 zJAI#O5XzJj=hWMhD(x4^}eGx$aJPLrnJj}mD_E2N>;WBGnXAUuO6>nbq8ya_o$zserXZ$O`_ zw*Val7;ulVbjWp#>cQ%}SOs&EE5e_Lng5z-o;r;!4Q)uWV1k3sU5NBfPhTE@k{2ZU zD46LMJ$$)?L`5iIM)2LVZR;kdriX+ib3~`t+o&vQn-r!tC3V0|BwnEVndpQ|<(U7TyeX#4Xz%njYfoTMV)8>MS_` ziQfX?HRTAE;2I!^SWOvNsMby(bm4E%U(=3Vat3Zu(V-=!FDj% zf_zuFq#)P!dW#*4o;-qN*At(3K z!GzUUTTU*kMIdBGWIM&3;n}%wO)%vN(Kyzw)J3|acc>`WJa?>Kw=&!cx}@Dh-wc~6 zu?5h-ZVi)$4?Ht{uOX6ZAd)j>yM0TbCl@8IocBVmWul7R?FvnqyI>ol5qF~cjrQP! zSK=dosnvjH;~gMwJj~NYkAzN(q074BQ~f{<51%mbr%nC2Ao|1yTjrC%n%~fFx&PU& zH`?acZ`~R?;P03Y!jkuw^IewjCC{8y1>+)ocCD}oeg#kQ(jTLSqlB(}HmHt=R9P*t< zpKDdlkc&HY#h0hrx3r`@-Pa*RB;45?i#&kz05`Z?INX0R6ajYT!G(a1uwy9Iq7AW7 zx;)eu7jEe3Q32s#q`yP4XrG-s-uYFl>OApb^|~~hgVN&x&N!>}kg4aw zKes@7W9uC2s?Q1mn&!)Ge6RwAFylD6&Nu(e{K6yw9sm9bJ1U|HTLN6d;~+kW*2Ba9 z>_Dtj`L1n-or)fA!$D_l8~M~D{vtv3KmGBm44>7Jis+HWhosJOWmX>LvyiQdr2YAf zWG<8DhvIS75jXD!3}p4oQjL#EZ7s0fhA-M-z9g(cTn^5;4O+E6S#KCN?ym7daiSak zl@g&d5suB{Cv7vyZiH57*@+KqDYkykevpi|jZEvF6VIm8G1Nyyeno)<@(%F3nt@k| z3v=9&<9P{zUTfcQ_UAh?K0i2@P@b2^M*u)pUO6i;2I&C{SniGsXM4FqKV;#ZiSHhKvG^-4!ng)iIxL zz0}R`qqDny3{iO^_6hII(mP{>>7FrSaXWjGV4i~>r0ii;H})B%74zePpjp{_@`-^_ zZ);z1Ko{i4MH7d44^9Kjyz1Ck%Mn-6Tcg?gkU66>x#S8{=!iP`L;0Gu4xZr!O>mKs zv!)gdPITwd%v)O)HX0j@6@q9f)fX28DKRLEa(dbPaBh7R3$xwf{0*6h8=d-zuTTG! z+f8GM0QJ=m4mj#GJqqs|s(vOH#@L4P z*z4ezURyn|n{U10hwl^-XMHE<)mhVynZ}l!xcFGm3(f+TuFuJN?yp3L3w*lRt=WV6 z0npMhO>aj~gRS3pzU}?z$GeUdoN%~r{CGZe#s+xH+!?ZV@u7rF&KUhkQ3n$fIm-5p#;NbOvOy$WfY)*%rS>9j*|Kiwz zSCITKc4_?x{7?Qx$BxY85W>)~f0^YS+sUO507J?aW4v*Hw#L8yin?w*&^oZ@d--2o z$%itZu<}IF%!I$nfg2*Uz`9@d?;_6iLM$dlXM268zC;GtHH4kfYzEtr0Zpa&52I@T zCNJ2=j=T2QmX_c4Xn$$lumMl$Y$(Wt?Q0i?_4xf97JfSRl^;EdSdRbeQ1@Ru|3Qx; zINSf*4eAXbf!mvVwGb~1wtC~aILUA9GesI}pHx`;1Q!nc0vG?DkC5~axB|MWFLVgH zfOeYcQGeRM9QNP;;log@74l-O@c+BG^SpD1HH!-t}K-790t_68HEjARl z9Wt!xJzd9JbE-<@pZCHOTWws6_|xeA{!sbYmY3aVwzx?S&=axciO(V3++q+GD*Ewu zi#*MaU1&Bn86_<{j*?QZdt9vJVpTscw(%&K;}kUXA5F!3>imXE+$baC@9YoB%FV*_ z#A|nzR~VG@&@A69OMrcaS+Vmn1^(kFi;-as+wazIKG>&-ja!K43B-4m^T!8gHU3}v zU>y67YyzT@&{@|DBoZ(GY&9CoyO`(60rvRE9Qkh+egSL=B^WpRXOna;VKpfEAnc61 z*yt^3@HkQ~<}#5LWzInQq++H<`QyYz<29vN(Ot%BPqqu$0ybJGTvYzqV-A6NdXF~!)(X0#@$e=7N2n9=l3(8B1|aMC;=bUu zfBxv)@>7uFSiRG~bHCuSkQ!L;U9eUC>t5!MBv@KUYoK?(E_0TXx!2%ilP{ zQaNbMP{Jy|C%uo4#tHFw@I--0sKd(6yGr&kEF}4f1>ldhd$-UH!J30V+<~xT)bdnp zNHq(#jE=nCKA)%(_a}0mbPSVZ)1`ZRo$bRdO^g!~H}z(!($|K|lo2`vON1qzG>P%7 z((JcdhYFWWx)Q|gxHcmsk*z$x9L+R#CTEbL_|>#n<{8FcjqEx%R#zNi6u5r*678)& zpl@a0e)enHUwi*KHFBRRNF@l=^(;PS1TuoTVrel?`mmJU*(I(u!0rrV_v=}}H025y z?FH&>S0c01HT6DF9-L*SKhPB^&%iW0ZqNc;G0KznN@ zDFak7ghyd%?nv7BAor-@0iL-s=ift_kHa}Eto$a@<(7gs8r*i1(4<7*i9p`n6ERDS zx6=x_23hXU#lr_vO5hHEP;60NU4*4-B@;(5DR`D*FjO?HvD~FR<~{nLk&*q5@U-fD2MH_wmljLf>bi7WZQ6;lt+hnEfDQ!1Vb03AxeX97k?W^U1 z#FdL}PM$)5!BFcL$w;LvoBOevX_*RXoL*Cgv0qoc6)<-xej#O5KbXd!RP65oyzZXQ zDzm_+2e?ip&Wa#WwwO47@B2=Fd$-BpR0oar_qSL}eed-k-u2HJ1-O(~VQ0*=*kF69 zY(Pmh2l%v8t}Po>&GmWNQBgVrspDV8|-#Hg3^SD7REC1^7= zwXK;|>qNhy5B5HInTU^T{hixk&$2Zg%PH>vGmPnSfRRyRqLHTP<1(?K=X<02ydI1K zz#1fJVd~n8$hIqUQ(R19|<*QyQcL$3Wzd1UK<6Bk=GY z0tv|hnS9@qb{;UPZ%t2t<%6L^ElQe)yY~x{O-`4Rva#k32K6mfzKfvWJ+6@IGN{Q0 z{XIs-wnRmnXseapD_di=m1}DHzYIgvUB-f3&_9D*8Lcm_7AcjY1w3Ij56q=>>YO~f z5E}SJsivJcg7`ZY_g?a&yGEjWWK2PtMg3mwAlHz>@#?PVM1w z)OwO5X9Pr@$IZS&lMF%7q6)V=Q*8(J?Vk+hQS6W1khH+wR zk@@;gtSCq9an4s0cJ7ZrRE!ckH&;ak&hbV(`={mw&M~f*g6ryZ~O6B$H6(^4$KU+9Ky& zo~rsfyhm2{U0|4p8*%IIRCX9C-$Xs-W9q_P3AsZk`;L%SU@2C4XyS8gY`@9)t_BXyhZ@1^OB!ica zyWLEKq+tGT*mGEc3SaJ#M#=NBghg9H;@g_(-aOC=P54kA^TM6z=)Je+3tSr2_pO=| z3l@C0Iqj)UqcP@HVHdj&k=>$;C`z00p}Z;fIR39seUi`rtbYJ-C^C!ld4eso z;{~w)VxMAl4}2`I(cD)1{CQ_hxWPsWTS)Fo+;um2YnUGsHQOZHE`p%#l{N^1a9o*k zQO>Jmxkv+2oOOG{v1k@EYniHf*EvhzR)TiA)v-K+`vo%qIw%BKbAT51j=Qrh`yBDr zJMjjl-7b=Mo*CjO#A&Y>Ee3U)z~8;H4sci=c_dYRuhlA%KiGbWHF|SSbi?$Se>1B- zr-83;-m(4tS2zQ~HC?csHhV_;g0gWBFP$a!ecP2huJyqTb_DvY>>E7(_eH%;d0pC<-| zqC;Wh^muTGY8pSIz_iS*IS>oDtd~0oDM3vnj!Zh#dEel)~sGc-=0yTe9<{4n=)?DZ$3W*<51hop#>}Y0VrO>?|=* zAZrR6R{F{SLZ>Fmlre8`^RX)noaFj;oFK3>qE zwjSd%;w=kx^%P+B?@xN2wmP~`1o%v3M{S#M zP-o#%7~ZU5xz!h)i~uP8cY|svIONDr7}TIOGIum`Qg%IfEW^L|AJ0{>9axvlcO(w? zWG_yYTb1g{RxvEFL`9`rL=jGcR|kgqkPz{&8Rex@&q9vT9-w%fY5h(^K@j39AZ2IC zpBc?&pglk|9M0Hibp%SCTt@OO)7}-a_ZC|@1?_H25wAm7UAA66ltJuS=W3~)E*_*< z(*Zr~4rR%eu(*uvd7^O1sZP!y4HoXTkqSAqMHb<^EKR9OT2e-7;^C-s-+gg`&tS?m z_dieqCp74tP1MT~EzI=w-A1fG9~VtrO9EK_s}ILF?r&eNm>^c91W>y%QsW?L2Y*toM>aK&S{F zzB^u}KbTK7X7dC#lgR2S{y8&6^zr_3Z)9KP@QK7k*h<;NL{e-O7hLH`%9@n@*gjCle6lvJT57dqAq=>U7(9gu1DHeY5Z`{Z@#fp$f7N2yiKdFy6A~6nF*s^ z-+7O?&0IE;Fgxhr)#Io%S~o2~FB)z(rksQYFsu)l z#~wBlwy=u}a)Zm;{9o?jY3$4AFBd+*Gk3+(TN$}0+@pS0_&9$O z0ZL6%Z7CZ7<5T31s1DANx~@#xdZ+ii-hPp+OXpPYlLyhcgM1WkB9Iz0<*W_o&CV(P z2=Tg4`z+ET$LammJ~t1pIb_THU61t-cQ<9sN8x;lyY`ba@g+~?lFwGh$Fb!J<=wjo zQptWN+OWrI-Lk%947cO>Lw7P1W!slJl52ORa-gk{6S!mH>Hnza*kGkZ)>%z3ij{t! z^6y9kIKMF9$_Z9jXx=zn+4veT?H&4L*hT=%Wi}~5Z9d6Fj&Y$7W;=et!`7dnV`ZTX z3=D9qkXQAgj^Un1D$u7Vn=Q4*%X83s#J0=^$a)_=A#}S@bz)yAW|fos@&@a7Ebl?hq=AXHMiat=X$a&PH$V zfB-CfP1_avXqP9^$So#ew^jUzb|5aII4%d{NNu6><^l9D&*r8c-O{x8Hbk7J+v-?@ zJH_VNjq0}N#b-Ri#BYcUGjSh#(h#>r%m$OuC^Aj(6_g39q2Q2{{s)@2tm@5&l`0Wc zduE?Myqy-){=Dy~7qZ)v(5l!Wsb0^`KR~*GW|EE}(koY3a&(Ao_#Z(lC;f2Y%qMX36SP(RY^-v+ z&3zqSaO=*BrviMxTEO%r&ojHM9HhfIrOVxeOBn0EoL$^LJxr(v2C+2Ab+EsQ&V&t@ zng&I;tFeq8sP=r)>t|=@p9-Zq$sWitP?;^gA?*vl@Y%08+*4P5lOHp6F#u+^P0y0Q-y$@+^ z6;l#;*=&S{wD3w!s6?fg-W1LH{-E_K;-X?Fq;1SqW;;hZ6yBybZ^iQ51ZnQ>blvV$ zZrB^6sa_I~Nphs@v#<0e2fa62Qk>FSc51>oL~nKK@RtnEaWr>S=g{?G27=e&{(6`a zi-oBI$8BP_K^t7hPiP0v;z6jY#^+2uK&}q*ak{C|N>(H!%Xh%NcB+!F==7YkobYnn z0#npkx5)0iSkAQV`{_)rF(BY#6quQH8BlLlEyj=ZkVDX&TIU=3>y_MtN+`%)k4N8j zcvt2#m}u;7>UU44nrisg!M{MzwXzZrCq25O$84;|O{lMQ)~?o%N|CE8#bM~~01{C* z9fNZ5V2oYwtDVJ>si2>(k?d??7P_s$kV_+z?LJ_)Q!o@lJ z(mF1>@pcaH=q7?6mf03Nau<6TLbK(XbGtlM5hoA4_66fKH+OQ#BGjd~VNs>6m#S4* zarylmyw(gMgc!FKL$8ZG&)fP{%jd%h;K;vZ3nZTD2M=(6alGqc%z%su7ha}OtoJ#0 z{&isBBJ5Fj;*~sCU?)uudW7@>VZ|{wz!?{>k_T28a^BSGAUj$Bv)Z(2Wx&}Cyh>Cj zV+G_O$6ASx!e6)E)j6uG5$~(T+^vSTwtDuV*P}~Z0a+QJr~&|KG%3NXR`OC`L1D%( zmafCMJdyGRn_fvu6=ud+d_#=rb*MsEbtSsvLoHwHlvj4tN7ya}nmfz5IUasQaP>O} zE-jKVSchllz&`3?;_clcxS@bEfAf)SBM_O zX=?%~oLj%xZ*Npl>9S^pqkY?SJq;@Nbv6loYqZ8BO}G~+2=D}pfh{150Q}g#QgmHj z28vmXITImXxv6Wl)NK%kKPH^Lo2vG(8`Tz6n^+XmpNIi(aWm0x{BU_OKT~Erl_9pq zzLt$5?46zDardN4owS;R(qWkVxv(DP>kbc>uj>U~B~9_8zpjRdV}4qBt(__TYU=HW z(vnw|@|C{%KE3~rxKCW+-1!@?!ou453vH*?VbPt7R#awtD{<)c?}tlP5fjyB){IaN zLNdt7DkfT0(fAzci16MQr&zC%-*o?Ph zm71S8XW7YBwE*agTuCr_kjY;9$~CD|i9aRw>7wh7TK4gtm#iR!+voWBD)FU_|X|XfsLkSXuj#_S(D=|KK2Kx z?e{CT_6x~5kJ~6^VP(uWdslmNE9_jg@43Eke_#%)vcKl;E_>TVnrldYC{e(Jrvk+% z(5zFywzQ4xRdF_3=aSH?HOzRMDQ_9qSMTyY<&x=X{J~FT1YEuk67dHce4I4CxA9%} z`^roiem|ivPa*mxZr3>7QJJ=W@g$w4z5O>%(B+|ZQhTO5Su5+7EcuEiB&A#PVN!68 z>U(sHroa=bsBe8#rNje5=TIA?i09R#NA(rndWZ(?PWm% z{H+%OG?$16vehVcO4wCzeYi!_O|k|_dWY!z9{-i%>(}$@mE@)~Vk5B^-ru$RQcz;s zE&4`3ROB;D907jKMD-=>b!*uvaZD()iyOBNzej)kAO_-+|u4`Q#EVJDod)- zR(#B4{p8efuZC8&g-K!4(e6xfso94&XH)u}tDO0)CxgVbGiNS%-|D2k!<4UcnYgOf zQ#5m!M*to|%|}GuZSb`s#HSHsxVP*hTgf=%UAQYvsoNN&jxn;74H5XFQ)qT)I;?xP z-!JX@tVK$(&bxz#B1m(C&s)P7PU}7U_Is=*C2I#4o2uWgepnAwS*|Rmev%Q_bWa-H zLYYVRHnLLsj+4W4pV<@1pfEYw!=)Q9nI^uLxsq?b+Dx>Fsfvu-FL`tQYJ05L)WcaSL6_m+d^g@MjnOjNC34N&a{BZV~+QiNqWPO^1 zc`7>_8>#&i<;S#M;&=7P%}3{v5ui^$HqiGfN%pAXU3qWS$3bWQwyMjmI5-6O1p!eS zvp&AJsKShWPoHL{X+6HwR)hZvca;0iZIRnFt+0gUDCVa_HmX5AB9@IGX{{xfSzh0A}NbZgv!u0!|`-;58?5gn-Aba5@L#HnsBB@ zqG?LKhA+JcP1K3A6xH$lB#YC2l|D~$CYYT30RabVDjUQ-<=l1F^Z5F4{8!kJ(9H3B znEideR~M6;Nt%-V2;mZP4L((4bS8aNrgoK(k}tEbjVc)3h^U5}U~&d(Z=@HiiZ0fk z6m%;)6;}K>W*o{1qyI(NTZdJ(erv-jC?OKkUDDFEXq1xfZjf%IyFpq~B&AEbyFa=v-+8~YJ^Ovne+w@zT=RKG+~Xei81p)7kFi7w`wB9W-qzWvtx8YMc(DhX ztqPl}_}js!5V&WVZsyu?SgIQRo$;;1ny@0$_$cvd*@j(Tx3@Ww5{%|q^wZ#61RpB) ze|C6*na{kzNItNxrE6VYL`yt~FskNVKioVsg#GH%OIg@mLHo?0|C*ALE!_wgr{pXxA+ z%x5y0%y;PE0ST{>#PH_QreYkosw!s_i)X4r0`Bg?V^2q(l4F7}d}2vT$Gq$oHIz1l zJ&G8TpfGL1Ki7n6(t*4mc4PkrF+a$Qujez5ldGkP-lee+(qhA1yqhmtxc=ShPuJo(9y(77WW-ZsyU|sIdd{i8s5(#Zvgh43hv9U4CWxdqN znJ*1{EdX1PM-M>N%lH6ab_sIBs!&JE~y&z z3GJjft9LbX*?<=|;4UszdL{H-mU7tihIa2uH)_yh=R%piykf1X*2fq+f0ambST0nG zdwfupnxVQh=1tpSpEvei7yUadtaF)t^WI)Eb2<5`QqSSIKW$ZI#z!{eRE(QX@740l za!$_dfr=7&0wq=HcLK{&K!%0r5}w3FULsl9+5tt*;J@a0L2HDkP(kuns8~>u5>6-J zYhawOwkT#Lz{Cuz|1yJxbK16F$?s&^E*AM1`D^7@m-eY_(PX)mfP?9Rs?pKx*XHXr z{7#w+4MezXy|wOwqSYp=}Fo=P%E+WYPP)aa0ida^SoJS zy~LcY7uwi6x-SjWJ%reMHNTdtoh-joPE82pCLNN?GWU${Z+<)w9vnI#e^+E};QMEa zvqS~#{S`qhrO8(~#6nX+1_5dkpN{9T+igF|0%4H#2NIKkc%pg0V($}9LNYN0yzBPY zK5Qf92^3`j4M$C8*WJ4a{7Fhm;kU+TqMcDB$rV<3TosR#m&@KZu~@l;HK&J15w>5P zCzc-n11`F!3nRkh3hbxN#?HF#5y;tE;ZHl0b(-Nat{5?mW6`)q}^i6bGHi)gpht^#fyEk;6qAPH)UjOBq17uE4}Onp@|1b{J!#N> zYmgYhM1&O4*1tfJ3_;Y<;iP)O_hRef za-a<6QOiN`)3$th&(Ri7bnTJ+{A8E)i0Y6C>Ao$QZB8Cqf@s#T7PQDI;*p+I467E# zvx}3Cjq>A+0B7-~4n>9IRsU+nHRBXgg}j80=UktF>3~u36nn<2Pr1-w=kL+4jtde5&@0)zp~s}Wt&UcYvfnKmlsC-Z z{s1sODzJe4;EFSjtl<$UMzhyo%;)84UM~EeJE%Gy3W336&ao9Fk9O4y>hR_Dv1IE? zTbb#Y;oOIrSy+QTn5GZLt=1SR(SopORCA={HtpXvyIB8dFIc!uy{w(gmq*ZS^-`_Z zUvO>(->6H**?WCXAtl(ED}9}oWDcL?&Ps{p?`}eDtx^O5YR*^qHC`Ftd0g+2aw9#( zX?SAFdyT{Hjyw0yjcK-W)INq%7Uu*GJO($s`50#`+3{S_#9}vP>8hO+D+}jSyAK93 z4iyl(?w5(zZ_MIrO}s1c2he&Q43q7TH(4`eYu~c#X*;>vd%ih- z&W`8x(d3U0zVs^^kHsr!uejbVSg#{r<r*{5PdwFzhgr3levrEXV4meIL)nWC#Sx@2>~G(``R2jNe&|wO z5Dl0l3oMeD11X9KBVCs{s+hWYLRSS)NO^Z7fgmFR_EBcD z{3pWw`(Dt266gCfc}yAtIdESkfIY);#?Vu_*=HhL59i;I5~&5()!n0`sJ%t@s4^pp ziK)mWt*dt>$yPz1d~N2-;`Lx$ykX|~sL3>5ysVBnjEW4hD5)>E5C@U8CNJE?lCso% zmM^hH-3wxy`ARI+8;gd&)xtVo^}~o9_>Xbg^%DMRvTbI7ec0m7lj(z~NuG6o6mmbC zkm9LT`6X99&~YP4)m<>Tg{;xtERi8$Yo+}7 zb~$IP`kW+3CXJ)q;>tuq-FRGtI_%5#kZg;bqd|K(0Edh}rD!mlI|xYquyje7m|$h{ zUUut)e^F~~6c`gP^Q&T+tr>Fp*-(i%@<-JPH`{HZE^c@!Cf-oUGvn5p7h_}P)7Ez8&8z`1_8nG4re71cq!fod zA6q(I*1H3vkZ2|&IYd6c|edP zZm%=FQdQu8Ii_EibO<>Y-lqw)PA}1>uHsKx<#1*->Y%P{>oj>N6ijGkb7=5$dr|c-zZu9LFSa%wyKP6PP1J-pYLz1qm z_Y-aR!Z(lgr4IK8fMC$?Y`y+VB*_2gg{QW**6GG7Hkwv>LW@Lw$=rzmp>s|L^*Y0K zt?4aEp>oOVmGR9kFo({kCc@y^I#UAP2C;)A=!$zHJ-QFp6<>(G(F^dQ_S-RLv^smp zdt`!}mq|&uCyd154L{ZWaG@A7h?L*_BKZ4HH97ZRU}qge*SoZk3Gbv=x(_sH@+oOm z3j;$xu%qtiig>KVeeKI1sKY}Gq62StK~m=U#<4Tvap2afXKRd5v>jfr%TSFR^g?!u z{XN&v4Qx;PN_&P1J>&%00qqs)#;xS-HG;P?bIvf<;nOY>*@>UeZlPlQS$WiyMU#kzrTuQNVns2nlQFda_ za$tP!c1UEs;;UIq1%-)u z!2>8&IkSS?*@?89S@u$isfU=zgKl2$(s1rWez&~;^juCf2po45PuJ20H_*zopXV4@ zo^^+f-`f7eC@5A-#Y!tcBHrIYC@8hw2Z7v#wlm88QvAa0y#?Wa5NeKex~t9MLO#$u zXvuHNk2*T?`#W3y!m0es+1QTAF3qoGAKRr<4ZZ3S zjpPd*#B;Qb4Ki&EcCyx$BJX+#t7^Dhw{RQ|=jL4CQ{~9GX+^Ppe`dQlNzD`;TS|jS z7L1_ogE~=8-LZyn$!{2S7p#xe{dni7(=8-lWQ}lo z1j-8BE7Y${{?z8^+g?noI*GVCow#y;??jJuX9z@SZ1NdzR!U@sHT;jdq&U=+tu$;B zm6Rhr-(Y$p%#qZ8U5RNp_3ZaDt-DigGi3H#*0mzsOM8h?hOKRm@h^X9l-v7acK0}x#(_1l-t9g&H$pO@x-p;|uhe5%_!}c*K zC{fd2;VQxw55m>A|DrA;6Vo*^BRs<$8=92ms1j{(;dF5xuuH8SraH{dzwNJ^(ja@a@0P-khH)=d}{WW zCSC^_BL11yE0-xxpy=+ImtB^dGE(jP3Y=?IQ}cyZj$1@gGOoz8 zE<}=Ngii;Db-CM%qtb0$m#)29FbC(u=_1pv(KTDONs$woJ$egloG%>7jz@-WA?xQ7 z|19>Sou3dl-~OxW2~-94H&gx-@)S%kn7@rOKPDl$xPG2%iA5A%auAt(gRRxD6?(3^ zXnUK<6PYT1r?cfnIj^bL)1~Mpb^)Rbp!f_uPsDR%vqufo9FU zrAEZrCiJBJccNU8icuwpCgvp~+-Hn$=}x7>a}}a_jcQEy;9Hw=+jcQ5jjCVv0M;vU z?4d(HZS%LXXESpLxGG#}Ny*NTUP^d4#u3xw{Y8BH}40%HwrXTEjOI`9c;d15^Yz6nB@~@_t*2AGm4`;#-d*j1k zb+6h@_ol~TyiG3v#^*3653ae=m7OQ2zrUmf4F5erNNBidlD*NOuyRjW?8r_i#r`Q| zi?C;^uxa`(i?alHEwIg%PNNEDhY!WjURK<uGlv0~4@ZTdV?9H!Q3JS&DpP1>xDg@b zn>`r;HK|NYI>7yzd)2-*EMILFFXWa)9>ZoxE!)x3Q44_abPxZqejAj(SojhjGcxu5 z^&4T~m+NuZK2hH$@gWlqc?Dw_KH)%-2`}pyGXkjX>Ks??Xh&O@TU^AYUxlG;^nbA3JH<*}4VD)$t4f8Wq)t(qO|<7F-sz}q!f@5r9#%%Gb>3%y?Dq=V? z;%zXHdyMMLx@?(>!hiPc7J0|_safqHlW=!-$LOuUdmm3m=6lFUlA^CDTH#TNb!qbU5#u3H;g42B66Q160 zUk!Qcmc-Ut$%c%$6S;19EoA_*i{4}#iBmy^H0cfbsoaJB zSfZd|%*f^dJ7E zI9k6+=wiOH&$#r(>vll$Y9$St#+4i$>(0976Z?F^qn%t3EZDvfn=<$b5mlqAIEqsG zV<2lW)rKlFHe{v zGEuNYN-byLWWm?y26i+x8o_`($Rq+I8F|V2eZ3^eYvXgIzgUmbgm6Zl=dQtKX{1Sl?;R$z!hB(2= zRm;S|R57xgSQk*{o;6}MB0^&8V7%%=@MNpWNiXA_0~$y8-G;Ny{*0_wDV0EvGkT`4 zAn98K2GWOfhRBTzq<5i@CGRNK&BqDfnS9=G(OB`S#ZfMNRdB@Pbx)9n(`O1pOa1=e zTB4E91d?kYBu|{F%yR7;f@`J*=MnCqhqKL3Kv<0G>S{uJDmM=V?pb4Etakg}W2@o8a$q^iu<*zN87z4u%Tt+xr_XJm7( z^VbyA$}Du;6{kb?i(#rVq5hQ$B(lDoe22O#oQwf5K&wwE{YZ8{SgB;6W4OC}fXZSBr4?HbRz-1j$b7ER*XR5|2xns!tRp8R9 zvmD*M+u5FCv{;;K62;1Fv3P({_bSFfRRwg$J!SwkMmK!W37DRD7>og(O2M%e!;)xNL_}W- zi{+2MqW2nhKgmm8UBV##o&=%1r9OeZzU_ac=k~bxVrQVjJSBX4ndVnkR#eFSXFmBm zoxTHlU6y=se?=7mt0;j7RJTjZJpB9d91HH#wnG8}>W!UZdIY$NN(#cQsMN*Ru0K58 z8g4!aP7`G2=;?LeCI*_4jIW`-ln2Q*D5*<%8h@MfSYNZzI}P1-klDZOX^)Z?Is74g zA+otA1@@8sI&SKvYr<>mP0T9hS}zMU=OP9hlN##PLOG&cc!?M zOMBIt5tgQ6NV3H{FHPt3{R>xy(>c}(QN?ibOuD$C1914N;L2ZNc4rq}%Bk-$&>wZu zTBfDoPY;KyXT4o&3%wut(FeO=f}@`L2)JEXkxepHI<4m8e|j1yLhu6yvgLdO2Ey0! zA0_o4>m_!eeV@8>Jy34Ow$I}$s>3e=cIQa(_=rM2b|cbEH<)R7({x}aw_f$+5-$Ik zckEKr^AQLQ47%LNONty_=L?JhQhcEO0?6zwwFB%6zx(o|^PJyJ>jvx0`P-&B#g08o zON2uj?9-FQDd+v`CT!KNjQAGg#m}3%zMg30AldoduXqzp{?_uXh@B}-e^Gt?f&~|M z$prF4Rk@PO;RGWH#a&&w_m3$2H=pd22IQBC*fNJ4+qSpy_oeO!<-4{?!!0JB`lA2H?BcC$e(y2V$mehU0n3&b(`x`LI3x z@%!ZP08Mka*Q(g6QpshLgW@_szDWrnSO8(7f0O-{v+{V0JkF(?P8p@1!DFSXn=m8W zB@P7|a`>vdXDn{M;RbpsEz9M2a8dV#MR@$SxgiyDD|PC<>q|x>v>#u3W&!YBil}l_ zYIYU)W}-|k<{`JN^8Fik>6`|SKhu?8z(cQVO1RLF`||NSUk%D7#lDDfPL!3O>i5g( z!(o~C4R_0s{rh^o7@Gc227nTut{3FoeW}R^#{7!+d?nMZjpNQ{yetm`*`p>4R3h47 zX3B1o3-pJd2zZuDhJz>K^?;}ECRXqP{rlNHsLFf+sj=p;$As9b-6|G!c*mA}1HC zG(hU&tu;Qoc<=)}T=7}p=d#CUW}@ah2RUQ9R;2w0U_C*zn|6WWzsfAyzWdDAPuB&y z`gHjEU15BBJl5}YTWyEa+@Y*B>`kAov&);H+6%F5b5Rh0HsHEndKqfH1n@DZ0a=;D zl$5f-#7E#Tc+XZ*tIVfG*r$@qXB@{~DaAix-d^JP_dpg3e^dd?0n!3Bfj~8iu2-cN zCK@=$%wqNE=w}1nUXOBi?vL~>HRiI0x$W(qj{#q^R)WH%^inQ~A8=QWv`n;eHjE}; z;WoE}xX1`>X}0`Sjf**xldI?iSR=xsdN9$HSMLMYqme#OzSN|l>hJ~Di-5tA{)2oJ3mza6M(=wQ*Mz0ZNPEp+?@di= z)+aAo8a}iBN9*MO0PpB0Q-Kit4WMe0LSL2Ww$cd0SfJwXdyM&L6;PO*)nZ2}1_1XG z`V%Jz_ydWiEzp|CD zD-Q3WO+fLxLzOAc#`2Z&XRYBeWJtu&c3UQ=nXS8^wG+WP8{hO;$@4=pJ zv9@(cd;G-+XEsJhd=DEpuk+_ml#}r}C$tJ!OF**|Y7GxFs5m={*JRX@>GSgXwq%A;6!j?`0Nh*4iO6=fUx}vVh=Kc-rFgjus&E--4tB`inzi`FyK-G&*`U2YrE8Kb4-W|6<7b-wsGfI3LTm6BKqn+)fJN2|IkQ0VbKf{v{hBB8Ylmb z=b8>@xkE~9sDy+?=x>160&Ne$KHyX2b@-kc`7`NXEEs>$s<7uauom6hBXkU52)Vy;ywlv>HC@f z`&>~4Xt-+XPHJO&Gri$l&s1`}#CUa?8V)Esl(dc50s;pp>8SSh@rzV{1%qMUM3Ud$ zE+WERIBiS;$TQww2vuXBpAAd?IWO-Z%9{!d2pN!?wxqeU#1heZIuS`b#ETd5v3T;i zXQ@HA+~uFg#~_*&-Q0hKoWrL|K#yAv{#7nt5~n=Yo2;$(wR+m(pPqlvp%5kLqZ=p4 zYXb`9j5f=cR3HE8sQ*p6`nL>8mAn>KqvwOlCv88&Pgo>YF<9EjrGuVq(0 zLRKBNUyS5H0J4_J*+`s#>+qYT@x0L?(0o9xIM4*LaWysaRI*Ubn}G-?5r5G+X@Vt`hIjnwi9jFDlO8y7saqGUEPy`DI7biP+`hZA1+p!P2uUst zCC6L)Gz!VUn!FVK*G#EY5bhs`koPvjRL6WpZ&C_7it=5$qn_NI@hrdl2Y2g{}+7YJi-J&x=7 z>CueiC5D3SDG(Y+W^7VY=n10ze74h}&xnY0dOAN2Z~y9EkB%__4V7owntQul?-zPq zV(>3C;84jPbMDWVYXjoFiJ9W%x{qnA-tr-D@j^o2div48K25r!+$U`6MdjL(H#{`r zPXRsto4vXLz^Ra&k8 zR`3DGLbe9(a&fS5?yb*YuW5mDGB!0-RrRP04Kc6zaG_6Q*ge$+r*F2-{-qo7-_`9T zBTrHNHP>H?#iux2_YyEo78cRFc6(E1KnFB|eh3lb*+bQi22RUrbKup_yUtE|pxXsi zh%dr`^QiX3yr+XT)LZG7XdVPj&6Hm`otNYzih@hA&VX+U52-XqOGMCOupr^nY^yWJP z&;nOlSEmyO!s{zi>-toO0fR(la$|Dpj33<`Kx4<569tKoi4agJ(Jvcn;9`5j3{1CV zytVD)e>y~+)?E*E%72E{(X_T^1_D!{N%7jGD~#GXh}ua*0n0`}BuCms> zTw;Ax9OW97T9;(Te}~BmAGANsj+gjI;kO12qQ9p|Hk4t}lo| zrh+AGR6rJUA@p3QEdiLof=W0$_O%odwbTWHpv$W(mFh#D6PTLElW&E?RY2RD(ZMUD zwNQh**ya&zm@}-LQu6pa90F)q6cmp2j(D<^9UX;!7z(b47dCv^j@rv4$5Cy$U(~3p zuLsDZsoJ2z-?=a6T2oNbt`lg(>H=_t#dMWrVgPP{@76(aL`2j`?Qu6b*Vr=QZ{%PC zOvcGbv^7zzD?8r$Nit&({;23Uc;~g-B;fuK+pq(3+j2&mderFP_Tp#tQ6)7=^p&{I z%7K=DfM$75t;UeMt6LVJgMP{#JKy2EdPKn`*Hp0>w37u(QjW(mq|~!_BQiNMh^&<1 zUYaxmgAvIyWe%H2f74qH4qh)}Y6)i8`fsQxR>tQZt&VGRhYBk^ay8gIKT)-u(8%MJ zlvqnNhGUd{?smwrOfj3TL!YlcGE;7g_SoidI()zY7#MSpj){fk}ufOd!I8U8c;21Jmw<;Hqx<_bUbu6K5>184SovI zGG`B){_-lquWiwgFK7z-L&6)7TqRn5DUJ84h9LhaZ0Mp`Y?~`1FXZ;re@X+XXAu)Y zBiO44t^?4BJEO$J^AbQ?@q$iCbZbdUE3AncmH`dxii$_GLOYacagi_jTUsvsN6RJE z!q2#=dYJ*JQgw*(;ujINT2U>2RaHuwwPw?~kX!a-oHu{yI;mlgjKFQ<@rq^l@oH>H zV_SR*7efXvTaL~t@RcPC;I?b*f{kcZ<_sUv+e5Rpa|4 zoCUOgvckZdZr0V!(OS`9!#Y#)qWm-I0F~U+la~-v>K)>9vd^s*&4lUC zN}*`A%D>lihgcY`O*ktAf_N87=A3YD6mSW6vLaQ!A4+rWw1vQM`E-|v_&=pLL*jbG zKmYU}RzwZ@3q4@}$B2rG=qROJ2&yequ`+!f;ZdT-he7n4xV|;N+*W@g!wmK`x?eF^ zQ}iHgUkP6)FrdhyFjn%{##-wy$CwQ6g?;SqYiIl^VH+~1z{1LkiccKTn3QyK{_7d; zR2y3=ZzVvss9S0bvHY_xdxn4^{^F{_c6EG?`8(HM0_EN9JZ?aG6mgk47yQ8>A`M|>1LS=JQhp#1Cxsd!*lT3a= zz9jgcnxOv?P0x~nW2$iir0ALsRaUWUz7ZAm z%V~$67|5T)LI^|gfGiO@A8F6`-v{>o<#Y7PU)Kurbgi8nZ<(k764JM2sW(H% z^!2&uYj)=HZ?jcK2qj|J(qlee{YJvVzU|PwpIUPyp7BHbM=Ld}i2^nA2c4pSnh&-4 zlVu9VkIQB{uE%NFm~BKD?C^GwAgWzT&=Q&I6G>+*p~^0X54^2&wn+EAVVzuiAx2qo zRYpZQ1aaI@X*~LA+r}VupcMDSj>I{|TKoZz|FBOIUq8X8>uG!nb=;QZ&-Y;qVS_V) zwg!B@9Iny*y`wz*=g+=f3kVwStFZ}y23-bKsucuD3ZQUX~3DfTn1&;i{mAr9VPD3oG?G|LPzOQH$O;I$EFJ{_e zjL<(k2gXCyL_6m*B>?h0v!PN8`#+C|q4T0W=N?UtjBZfP#ej{>T7;(w=Xmr^ey}^k zl!GG<%l%Ek%rKrRv+RGlR5Z+sw06j<%DBDh=wRHtcfp4vzU)qaSGLrJu=h<8nYp@U z)~@p6<#ia|zw#O4_Z5IA5)ACj25blc73<#tR-5==hV4yCVCx8kBH{0^Z)G_`UTtuA zwQ|3Ve^GVgF?!SCD~Je}axtGZ_u}a}UNkVLVEyMg7PDY^wU^4T!P~2w36R{i3Gde4 ztL;bo@1iG__|LFa!>*`Om&3kWNxrWskNK~tMB@Ge^Hs~Mkqp!@Le`iyw9|IAFsuKJ z3D(uZR~0S7%Chg}XgX*Zb5kB`gk)FoTww*c+gHzfFsmtz0qsU5=Y)N-e}f6srhh$7 zBBju(sszE@akj&eJu@%!5PEIw8<=8mI?SPc3r=H8zt==W%F9eXJ@swRybcnTRO~n@GIai+^Xo@u8wo* zvU9Ty_GS~KQ#@=u%C8?9Tu*4zAV|GMNn3EYO8DK_a9n54-b=F6_Ef zXsl7XNJ>G%z(5hy&bc|PaZNVuV$eWCX2q;#!z1i#L2jstO4wxUu>X!uWyG*jh3&hf zItyRDZ{RXBL^uLb6Q``i=$|Y7qenxjbI)E@bx8BClI09wQ=ky-_x_~aJ8}&e9y>~^=BXIIPq6=(81}xp{{F-9i739plyhW}kOErVMa624FbXksdx0 zHzkBR4OKzt>T$q94uaeA^8{&lsWh%v+U`y&=IKPdFI5==={7S{=4wo0&yWKD zQlj28VQfMZEZmR4pDwIo0A1XszC{t~`*}s{E*(Q%ww3YAcb`}-}U+pA|7>^wp$O=is3zDxWm>#Gi zDtk7?I}V5}W69`%#hq>URLD5A>O%8*3Pg+#=jgLzy}7Cwj`Q|7wy)Kd>!~p`;vga` ztIsAG)L*o$B_-|#p_UNU;YQJ`lGzCc+b&GzQrRpan;uU4!{s2Tnud=}o7JZSLR6(Q4_C(54%mG$NmNiZ?u#=6wGp(!B9 z^ylvVqb2osX0F3VSRCl^rOinf-tVFLVRN@>(aq%2puu)>bZ+KS7qJfKuaUg5+?n>B zZDvN}OUXX29SJu4{-ky2@FB6tu7bHyuQphV?P}>jXyPu8Izr4n@&kaSLU-Dx9mdBM z%lEfOY_G40YZ}Vw@qvu>8HO5rC{P+C*ZM(vFkL`I{j~Aex~=5+igxqLUbs#0$#M4t#H->WP@ZkikDAwX8;TjA zpPY?NL>u^J#%zpSxJvH{hPD?|(8T@ta!=reMF#xynZd}wRVqTMd4yvh(4nW5MFwi2 zEm*`t!-gBF*P?F%Vbt$PQ1INY+G$QO{p^w=MN&IEJkvk3pFN7iJ)vX{V1iH6VOI3N z^6)=t_j$Cs#6Xx5=d_QH4E^)%7zrh!Se+T$lbOC0j5$BQ&>f1v*smqGVy_&Q(B$mm zg+~DJLaMXGgBjR|i$yqp5mf6u4fl{+I|ryjF_d6B;f9%-`4-MfmeRrLLXDMxEI;9i zOiBvFs>TdhPb#udsX}UQPHjw}>G40cAi7 zpT`YT*>vRYO<4dLUp8PH3*m}RT&ws((7TTaQa8M_brSK;xwfmDL^Gq+I!)%hnt^!& zC4S!Ip$mJN=5)K@h#c))I!8^}N59gM`@%TFp$!!fDjC797((y-76(CSSd6WnTs%p5 zfii_yUCx)ovgTmNywmNSo)^d9>P-F4^lbIq?c6Rx#p+H9wER>oEN@NilI zFbfr15Hwny{mXZ_u6q5uuP+_wPdr7Z^214|Xy@5S!fIog_DZupVV7QYr2R%@9t^s} z&#Egb(w1~R?K8WV7tSo&=uQKMtY-|nW!}l{0B00kNe|dcTahG01xKkgOo92ZS0qHd z=Fv0Po}4u>KoI=6FqV`-Ltsm(J^u%B^tBIR-9ntHX=6M!L-p`-2YXx|*I*Cjcx@{cHs`>UlC#6NTYCTJH{Hv^%}hWJ zE+gK3TBix3BSX}ZmI!jFv(g?OIrw}D><;+IYYT725+Fh<-P7?L_X9WsWCXoU#Sjjy z?x)6&Zwkw@v3<6IaNlEgGbb5MHHO_iIo4Wy^v+Atf{wPsw_Taez+vcjA;kk8roSiW zT2~8pXQ%Jx=%_06i{5WvbP$GS9pU0_y2&X{azsw>+Qs4V*acFc&F#sms#C z2)u&o>$K(Bk}y(#LHe`q`rH0|PVAi0ChD6#@TXddmx%FluOsK(b*=B>Pq`v4AtqbX z;YVIHSUCIxFsi~`&pkDuqO=;EPHCosk*sGqEFhX8fn)ePBHWfWN>`2H7F8hrilMC6LRaMpu*gPAl%meC>$WgN zmP6oIeVrKPEoVgc@OloK(hhDCTduf0XY zk`B9BkT~2e3(*lboSUboJO*-~qXZndwfpOBf&v{WkxP+XexX@8DA2PA6wzFkn?P0= zkUqO0VBNK*qS4T8)R(8P@QFRuM{osPV_R|bFtBoK@U1?<_DeVR;SUYI_+}tR)zsMJ zE;vJbejzmaMKNkkVLPfC%6~1MrAzy^jbZ{tQgwVU9EiO?(;@VHGY*$HLb7cG`q(jB zU6g2>k&dUKy&L;@t>meNEH+ z7oe>y`?mCn&d?)?p6Phr+4qu_bN>Mio?cUgv%YH3A$;k9lI4i|DlErOd`;(j{Hbo@ zRfpS`8c{?z`<}4o|4U^j|KjBb5Xp8L@!E6jM7FA5=PbYD2BB9AOT8{@26gknl#P5h zSB*S#j^%S+7gcaQLzhB@^>p<^clP6b0G*a$L`wkj-i38KTn_l2{Ovox9Rja#z_X+S zKxRTJxD>qwa#~}wi#lXP+?z4`x*Qd*WK7rn>O4_K;rHOIEI?k|shf?(cKD_!0>Y~O zM616R;wa!lbcdaNoi{L_|00|?H($Q`4bnK4O>{%JUQa=JtEgp3hD047G-TN6K$W)7 z!{5;KiY)NF#mqj)HbZ(yc+h=vRhW*PJX}rpO~EdgJF6bI>biaN*%Wj0lw6KEbnN(K zAO}W*ixc-(0LjR-Rzwai%|IXl0bYx z^a#yJ(#x$YqHEb7)!L<+VLcxe@7w-amP=IxXXpZv_h__I%p@*~hm|H zoJ%I8%7B7zRc7l!Nv|O;F_KoBm zlvrrsnbY-1-Lhj8D+0fBt8X%RAgah+BhT@%2DeO>SXjHWyR=G*_=+4U1Uom+*wuGz zg>eB5ADl@D$H~{0?0B0%ky?c~ZO_}Mp=(gj{1jz`ykn*v* zQ@NNNN73`A^Ey6P9>}5AUY^AYAzTyop4dPFx4mL^V$Uj1zb&Jc5!t#Q^4Zmj7?7RZ z_X{qnRVd7b*1S2S0f?l*N_70*KijWRoi$9zv1XK*yI|5 zsFvD+p?Mmh?q>FUWejg(ZZU?7NFE&2l2ewg&bKtldbHdv2v%wl2F+r#gWDw6CZTW< zGE3gjS=@sbYZqO*=Y0a*{8--3$`>k+i<)F)mX=t zDpA~55-SaT0J?hFrh=hxh!!jnOk;O5-l#Tj^|PBoBuOq}7gg|MisGiPt5>gHy)p*mPV(F`(=L$9KX9*q zrvAz2{ZSD7gcv5_muzCK=u)iG#o;Bej3Kb_V&Y{awH z*x}RLuWzQ8>yN+=n9I=^KVpg}$TV_Apf-tCtQs1cU0CDkd$i-g+u~>Hkj(m3XJh%K zLR37w7|8Te&8&$!7RoO2@~JvnX-DMxLwWHU8zL*%`h8wtlHF=|98Kla^bf4&6v%TY9 zz8u|*8%AxcKUOpO_xKaO1uSYbeH|8GjPkaLzK#@6Nx4Cd(m}RS*+$2_S(UNZ&m%T( zzN?S<&K&|$2k$j_rJAlUe%10H^^Y78D;egy>N~t=pwR^?B2)TKfd;S5nIiUEd*`SZ z4s~`KcaVkiK?_}oeL;A^Go`m2vQ^rPr&CNwdCTi4BK|Ha*JgVjB(2z8HBODr&iijE zawGXhQ@>~}^ye-h%ZUs|je8AD<-DM@5n#S|Rb?@A>HL5iGA5d3!sBehaOUCr9Zo)a zGL~;zXnfPi-NIh73dDm+< zjT#%w(d>4Px?`IL**Vw;WY;q*#_h=I_zc|m}~ zJiz%#=w&}w!v*}R7XMyFEXe8c*wT{n)Wb@pGOwVK{yk7Q#tp}=q)oZ#_Khl-eT>T( zlC|K;AG z>4`^LUV5NTp{p3ZZBe)0V%EuTS98MaHKlT=gMoE(C-%1P+|21?;hV+lHS-K0YE-Xn zJ;oEE5F0X^u>pQTp8-0L6{a$)?*<5MJ*P8cMT}ooK-|CBIQIV$#l%n{u;19Weu+j5 z?a5UsL`pWf?RT!Xu$|1VG=VRB%D_0C3vTm2Be}9swagJ z8gv!ruE6(*9V~F$+@M20|9<=IJLYTH$ptm>gr%QH0V^5juND0rGb%+@pU>~}PM}Iy zg9#lzY?l+FY5(va{eD|sJ%fnm=-3#Adb8=0Ta_%LkdQX_dO+m$cHh_~*;mT3B_Ctd zbBh>bFAT@D_vJ0Dtvx#wMEvI6D05CVOnL8phkViZQ~fOqjZu`$?)KyyiL3!ZTW6+x zIh|phP|W=sILQdBFXnDKucj;0K-$eZzY)!|!h-S7F?!-BxOWv=@;_<;&XUB9M0Ia4 zMXYy&G+*Pt9JSli1ys8ocR@yUi#(8aarfT#zYo9q+&lh`-_0d@t$7qma!#x#3!<`P z5R&$qIK>71?VTf_ZR^NZ&1`o>wFkc{QXg={L(th!BbPY+xuS}VWltO)R9+Y2vI&N;n3+PzSXcPXP1_uiaL}74x_CQJAU-#9_q;Q;@Ml{8w@_pH8L%}g^ z@n^Q=)Ng-)lp}{8ET)wBkl9|F@o~t4ozs?#h@|MF2{fc{knv6oq@jcHB%vQp$)LlN zx_whTIMLBp$N3Y<%0V{%6(bbf=LPlBL2yRMisi8M()w3ORAAgll`p11@kmw4w{Ayj ze5zmRJ=C%PpKPyxzl9hCz9D@;W>fgPYQFPxcV994UfY`>1`G3Xd%@456w<)@ zhJbiBtib9lHM7*?uRVvieJK!(KALM6Xgnub=gfTqxOV#|fN;Z^48K^j=3^t+TjBO| z2Or}5Voe>Ft8xY(1OCVfXK~o^+*~Z|eAW9GFx`V@!z9EMa6*dI+}c8OoA?`Au}1lG z0lqIgd~%hh0G#6vCGW)vqvsBlDXycC;NP2im%z-9Y~G3*BL@_#g_0cmX+2x+y>uSR zzEE`aDoKf*kphRI_^bTi!W|N>fq9*Oxz%ykUWOMm+g{M;xSj%D0(@s0*`IRHX%zb6 z1Abd?#rA0R%4AGx;QUpiAuU$^Rk=2gZwdp+{W=eRf5Rf*$93B$)eY>`>5#~QB<>on zMw&}@{hFafqzo*>%~bX9{y{WkfiNKDJM6P(#5Dmo7|%QGuGpcjrdo=hpHZBQ)#Hx{ z921U8n^%xv%c`n)x!(ZyTUXIF&56f1p5o6~Cn@|bHN;XYdVcqvfMM-Y?K+R5yw(Q6 z@~LwEZHCt{;;(+s^0>Rgm)M*YD;ka5LZA8^ckmtxa|a|1G-RoE*Vd?tAno3&8Dyr7Uijobrc?<>v{X%%r<@A_2hh4TH4V`Z^0%(vu$ z{+L2v!0JJZ7B5o4vMVX$W6QhO-ygv!DlV9mV61`q3Q>fF2e8ktf*&Nm#S(&;-^I25 zoV;kf$gNB^CDhIq^Vo0(sBN+w}-n@G6~W3-cRA>fPq_{RE|_A8>r`L#UiP zFs$+laK|kN=jsCPP6Ese1qvXcB75@iZx|xK5e$=Ez)B|^u>Mupqg-+X&uH?5g2w+0 zt?dRtRt`~{dX99SK0`8@n-l*O1MDd_fOB6$?;5DIMkH&f^N3Eda?7p{ z7~m-nWY{GE`@z}93+C${4F zRFEJJ8vMyBE#F4h<3252G#HuLuO-3SIqVpfwFgeEMz6E|}>#bV0v4xykjfd4y|I@+$za2&O|M-qDN3jOK zXDb|J5N+nJ$uHU;l>x(zC*XVCXYV#rue1Jb_3q-QKN3t}B1Pgpp}maa;S1jrJfI4` zwJv#+mJcUEi1`xfbiJruAT-cED`b*qF5ywP%?+ANvOWCbf~r8?%%W^I)CU>^aSlQ}=#hR-U$%F?T3gN6GMh1a=#!UY z&swu>+YO}4>G_tt+~lM36ur%X{Q^%Ga>t*B!Uba72UDba6FG2bIyZWQYp01>dL@v% zv$tYs)FhE#BT~@~G)4weKaj}&aWl3bFdS}A9FQw*hrB}`ukz=gdGg)a;6sXx&ZJ6D z9WFD~d5x{1zkbZtaeFn1^0;QZ)Sl^`I(b~ZKW1VVaxeVuCO%Gt?L_P#yYqAX&Fqc% zW*aN=nTlRHo9i2+c82m>e?(Jr8y2G?)#P^n+Ma{^0ayOJC%2oMF+j@B`FqR8ji|eg zxp`;&xM}=7pvt*y-Q|59x{Ow>@(!l~?S&B`C%BCa>7~vSzCb#0)e31s{dC;Qd)%3;sZcz zyzEB!>7bSokZ2CRYE(YYR*e58ywdpe!wI*RVUp$;Y9+3o$|vL)qfK&$V=0E~=S>P- z%1^SVN5G3kn5Ik$V$6e))lFScwbAv__gU*8E7%IXys76r0v*PU+A#tv&6liGC*e?{ zV}((=(d9@;Y~_kssyS4D^LUA6to*mo#yvZg%-s9ElG{Qv=CclU3sSrdQ_b>8xlk=6 z{rMQ}oD2;vDy#x%JWi~EKJ$*rltoM& zUocG^%L^C=U|pD?cO>n*?w@82 z*e|VCq(2_g9Ec6BzOxnn&^LXo*T(*Xj1{~qO|t`*^tmq6chCvGUwQ+}%Of+TfTP2B3U;?2nLXVEHsGz>k!ftJhP!zB28uHKWoy zR=Rj~KxS?+YI_T|#v-A4P!2`ls>$A7A%L+^OGVyesp3N}`0`GeU}`@iwwEPIxz&A2 z9bxgeyr3|-$37lLd17^GZ*==6%z3t^Kc>*h6*aN4B~-pBpMq3mRSuO}Ug|}*$>r6? zvK@3w$8)&g%vEF9$s@k$3xs z<16vjYDRuMz3;~X%xPha*rWtKt7Q#r|CV6yDY%X|2>Q_ptEj5ef|T-Uf86@X)dFIr(@_EG zb+TRZxIg~(AXLY_E!<+Yk*pcRMs&X~hVull8O}Sf=Dj4{@=onVXjDw#a@AsC&}a+I zyW6d$%eP-!P|;?NX~M&Bc|nDgG|5`dz)a14&NE>LsMy@d;c2YYo={3>{1Kqah;g%7q6axk4rl~IKhAVy*SIpMl&2h9yY1!dvfWf&9Aexpj@vlJ z);@=f?8l0*Rp0z-u@JJ1q#w|qc`Lu^1~o}O#&X-@t}k`=bJy~wVgB-Ypv zEcJF(vld3&+xQi;ddYJ9QnOr2Q}Q6F0_cU*C2Z>TeZEUD@XfcPD&?Qaf~?qmor<<) z1adsmYA=$CNqpV(J;O4~eoeN)+v6p}SFCJd{m$&Fm5ZxPbL)+-d5fDkUedX-j-vFr z9#C~=ooe4k{9AO}UxCHv2jaC_Tv5zQSIV8fAjxR#IOOGO1vG-d#C*Bx=u_bdE@GjN z%iwEyh1C(v~;w~frvKGpBmJ;~n z3z}l#vid?P1G*j!8oW>{Vw^lA^&H~^>#!(mdR{S0Wh|NvTqL2g`qF2l!*^a=Oaxy= zxe?~KrcR?tVk=Eo$CK0#h_sj2dtTL8pO>q0M|EWw6+u%TcJszMGHeO^h)j9cMuE== zdWfIFSN+sUD9Dbr7@5LZn}1V_*RK!1<6Au00#rWnt-lS>pD3PvncJl`d>boZGe0I( z;bs~Fn$Z!`uJ|7F{ka1R(2WY-*vJ9Z7$`^0`}pUDu^(4tj0=%-Y`ACdPe^Dtnmua+_n2Cr4oB)hEd z_1p_A+%lS+&6qiN5;b zSGq9sdX2sfz*Fe9aX;3-mKT1`*&|u2wUCH8_;9_8^(~I5PPBgau8ZOSjGUVBvX_U z{8`@SZrNguo6K!!Jj-2WmJ!2e3N395gr<&rRw*0s38HkJ8$EcEL$Cz@mHRTz_k;+& zB{X~9Qr6?|iQt>#{a8CZ>~?Ym+0JxdMYl3?y3U)7_C3_xs@3zF&`14mBbLaXu01Le z{<>eJmrM28;~ER!>n?Mh#bYk}2$~)b2YMEXH7s(hH153L*VpR>jsmKfSIL(j__sn* zGFX+lCB_F-;>pnv`^nw+VM21-f`03urY?8FfLl_sMIO#t*D{WA&grjz1h>L78PmmesMxgtWhnMuKWNT{gg1NgljI5?r7(*#9TgkYnNEA6oPbGKA0P99>2phZBkZt+|@*PnORX zjT~}|Q6q-|I+7}uNZ1{2&Zmeknta_-)<54K=B!H_d<26!J!M|65AtJPRiqhg?+EjD zIQq(Kf5Vuug#S>glP(5oEjeT%9&&4iQ>UHt*my;0pz1&HBG|u3r#ZgCkMr2m=c_IA z-*V;uWT4qk)|IbH4UOyRA&RD9sWK6U_qso=R}oV=q|T*r&{k z0}y5gLSqP)k{f)%ph?P^=uo}9`K3=bCN^!|^v>{%1Qtwsd$TU1t6Jm=S=3BJN7%Dg zw-Eukymfy1R8W{2F*tn9X}c&d`W&5PI?)$~9`d%xrZC@u2{tuItUCDPp`T?E`?y_p zHJhU&qXl5zWofXR6;E2rblhF?zN>{x&=!kSipyg4Z*K~&IhRhOemuq$Gp=v^j>0CEyb7scl%h0xERvQjjen7r(a-~kxttMoMcuv@=`6V^!ZL-9BT8XoOFkjtisO|b0t zFl#q^N79k)@Ik@CWSC>7vg%?$LVQ>fUtZ#dWn!LevzVy3zJ=DN{8#bMSZ}t&ruACe@o+>ow})?9jmd_{ zA(qo$l+CuJ*!624 zXkPp$I>Z@DJ1IGO=?!LdMA?fOp7`p= z!UVo8JS*zTqWqF=ZC1L97QrOr6`Gi0&!BK~Pl|Lxff(^QN|QQn&H{9> z;p66D{k{c@(T=Z9*dRXfonlyRw@+3`fBuMZtqz>fJ}0jN&TG$DaLYX%2YW_Vs)xXG z-xTUT2fu=&ahVOwD~?H8L?h6&)9qZL#ArUQ)~ox@(&gO$X*m&*!HgK+0W~9Yh6Ofn z2d5w52ek9~Jor%S>0n-u*WAOL9RX%OiI#@4$^$jP1B5OuM@&YF{c5$$sM0JgHgN z;ovX2C>NC4(ei83)N!vF2&^%hnw}1lo5J*iN+knPuY?wtV=tb{c^t5B?zM zBX!(JDhFmjQ3Oaog_Ne=<6iqG$t>r}-OTUmiy`Sz8LXUdFK0__s`9Wfvt3To73M^F zUZA`Phs_F)m~!3L#?=>P)9@(;Xx`DFL_mCU=nEV)YMx|LuLTRJ!h+D5kW``xmD(I%G>w5Nu0u`J3be@bKTcqvm#*U`Stu`$jDa(zP%cGoCA+>Qe;XXkz< zJ$pVo>y3)fYA>Y$rvn>hY~`07GLddL<;9kjJp9O|66W z+4$zwn4LNzyk1Iqrn`1yba_1aZf1_Zp5vR5O zOt9G*`hZW;t^d@l3uiT7DDVhV*tpPN>N~&tx!&Q;Vkq7Zs#tOeJ9*I&}M~@ZeEXtS`#Hf*B0(S6}46+^carut@ zoTxfNQEJgy+~72E3s;U8z1=8yNZN-a6L6E7%_%;H7I#(LWs-!IjK;Z`$e7TJ-l+LT0$-|llw@92Th&dnK9MP95 z9r~Z%?c07W<)mlTYa!B@7pI7)97jl7-Hcm85oA!1(U~azC})>3^+37@%jBoH8482{ z*sX4*a>p@8U-(wB#7K%(Z%~Z8zsysl+^DQdn`}kQ=5+7PX?P5^G1a*4`x z_ebu%`o$R@fNqm)Ck1|S!T_*!Z-ORySP9b%p7BW=bgdIe{7?04LR8TmK-+~*_y{`j z_zdyMD-HOQ^Z^*Bis5egQl1ESM3nb75J*bJo)kX^yBb+l5) z!IU`HPwM$bZw9N4`jDkM;N6nlR=FvgwRE%Qub|_+qBpdX1#~u_ZtB zKv^eovvlNQZt&%?h#P2gc7~+U1GcAj$-|Du#ODafOAGx&p})j(Koa4z)v-qYFtlZ? zjV3b&T4Zzb`(297lkDI^u58_%{bQ|4SQs=)d7=07w6kOKJxs+-ww<#E>>-*g? zWKpf_Xm2R7pSN66A^ET>v5z&lu1g*vDApm*hSIAbS7e`QAgvr9D-#4lGZ@zKP}hVG zuvU2Z!lU?!Cf2`B_#FE@)Gv5$sPBW1ne8EBSK|-;x*0q;&|b3c&87{9;x{ZbSmR-G zBCJ9&@TXFkpx8a+XIntsE&G-bbJuR4$OrO@X0P~jSTZMacqqKEx&zPX?(F0l(tTLf&&h`4GZlAW52ultNT( zv51I$6id%G45lzuNwUX#Dfx)>w*b}{pi%_CWP;g@qjL~it@nW`_T0BBID47@LSi?_ zRc&N0kugNq(|K|z4BBMrszuE@yVGM`I!(YZs|38jPODsoRJv7;-yIgo4|J*%94T3h=p*J09lJtwrt%HA0C6-1LZ7b^o$*DEv>AiW#>X*wT>!>4WWjKCl0#Iu ztM=1}TJ_ua?053abAXvQh(1G?7pKXPK&I_vB12xG)tNOAol^RZjE|@7A0>-4%_OJ$ zr`3DT@r}`WO+)=O>6SK)tqhsp+eHHcKAwNT zXMQk}3ISiODM)BEDdtZyfc)#XJ8+%tBsuTWkPDb2oY3OyFS<652#0L(4x+=60_&t1 z&P0f-T{i2Uwg8keZ}asI+fxBsNTB&WG>Z>aVydT&peVcEzc2P{LOvz_rhviRLjZ0B zkvn|{6U_mwWaoM}P-1$*7LZ6&_Rsz3wEwZ;wX#i(s9@U%4rbZSP9m|)r%?9yzMO%q zbHCvC1=vLvS@XQh>IG(MU;McbzTkebSw|(WwTQ?P=Tvx84~A!aOo@XgAlE(AN%vmh z&EVFq)7J@EOOIG5m=eOnE;Q0$Ku-!E%kUvHI>OHF-UhG^4UrEnN_>HEAcr@gcP80N z%paMRKTkDGtyd8uPF9}B@ar4w`~BdjU1zgCZy=?&6|g0Q>n{Ht5mZWwE4l!hvp(Yi zhVn3S_~{|O@6#j>mBNcZuusW{V&6>9R~AvLk6qkPLVn@m)zA#DHEN(A$FXjC)@j|v zgO|3uZ_)o0wsFHNcYV2!R5C_t?M^RK=vG}|t`5)pdzLGW%SzN^Gie$6+LHU8Ry=o$ zYNpP+Mw6E4cLHv?1^xE=mdG5UPw@tP?8j0wYo-3T^o|oV3&+pJ<4}C%EIM&wY!HE) zo0bC?6ssClcone->gwyXn#qX!EWQh;MyFx7BVYAx;zh5s`KJ0^*5mU*hl-dmp51zZ z|KM0&XFVu2wX_I1frxkAR?zgEA8uM$8e<~v^45hb! zK;t_KNi_z3mQF7s%5wsd;KTZ{YYH?~F1HIwpw7a>r1=^EJ4>Duz2W#V{R~7W^b<@a zS&+ek`8lrxi>D*OyRX5w`S>lh$GFee!{+kAiXr>_w$XVNCb42wZ<(QpQDoAiSQRl+MB)e!n8$+EBSY@`0V(h(Z^ z%WU@@Z)`%YN98V}kR@L6`TqjtL&I>D5}-sPlZI5p40@^k7VLMF4D^fN3uT!&kKsKB zhVE-ph>}<-XNRA1_`ahpJHIwVN=({MJzaPgiF4Q;NU$)G6ycZuL0X2?vfUgIXxTV# zExY(v9j$~j7Q2bh4UGBP@91Dw$o)WApvW%cBr^wKrltyWq2PF>IY*%(sg^6o!0$2A zOZw8b3&;aGL+#dDNKgKR;X^)(bP%*ur-zEvhe`Mo8DE=<@m3R?-AA>yh7K<=i~%yc z8+0|fIVC7#^A7i0GMc#B;f}?N{%a8(I-cW2%#?Ka53cMRT175KAN3Oay|K)wk24P{ zbX!vkEI~mXxoHM$6avfA>%{m!bHq-FI^$=Wu=a%ALbt~?Dm7{w(+X_xv@>bZi-%E@6xUo7Wg-0a!5)?S3}{Qa^s@=-hA?W|xSKA~inp z+<>I)o7_nPe8>$r4$yHm=X9~zy?u!31ugu5iOzc5`%>2d6r74tYLx<^G7XgG%y^}u zk2{W$Tb}P4i%!|;z+? zy$aT6V&v)yi@v1Jh(lI5r~SMP$bsM)+jjQ{X=Re(9iHv zBvgpj?n=I@S*G7wgZ85m=7@D^%h|6)*xdtf?qc0v!09O57eHtwpGMx)&=+KoDy*!y z;NG~MbJC1RTJ6+#&U{wZp7`$sL$JFPn`#?pC+xnS6y3`N~h&koJyBsL#BOS{JY3;D_6?24x2G>t?GN@9@`i zpEdP9|Keq$^!Nyq0^U1aUUCTF8V;~#$^V#>-BoUB#6zg z4Y82_ov^yC@o8f&-)kEfhdHh#&q_(J7>-Jqxklu^Du^mq>$@JVO5&`vQ~_?JcG=o; z)sh&{fHZ2d|NptDf2fu(Z6Gvzmxnw~fGPkO3UcZgEI6sLyflCd4T1*qe*XMPF>-U0 z=7EDXj@K~I0pdI@RV~#N=SJr^&%AziXmvWvC>N!;`UI&>`9#enCgEY^v$zx*te5VI zs|eh|Z;#&?_3>3OB=M4U5@v`l$uOwz{7pEFx4oZy&f&Eh$6U_Vq6Hpb zjJ3{+{|#5}U9g#|L@mG~3~#W!OAxRBIj?M+I0pFbKn$3mWAj@6$!*R7YuJmT05LFR ziIgaduOE8dSLV&OYY8?njw*9ax=QgpaeiMdE;8GKnXT!e=Z3j+F>sg0Y$c8Cur06` zdnaOh9kjhFdIXT(!CP6=+3Nil$DHGG8EO?OEB>_n7QGuq=_Qw#sX?Nb93b?)x zHZJIvp%WDUa_ENo;nrHMY5CLtCrvC((5WNs4LSRoYu`wNtk<&|K}p-AH-| zD3p%}0p{WMRl(C0IdOcK3is4A%iv_$gc`GjB$sY>$#7#+0Z>`AR}}UGK})>v^XTmT zAB#C$E4%>)K+9k9<_@w`1{g zckesP^thf^?Q{SY3YvTT9{&rC;JhCHZZ;^k9rs)37&85>#OfTE~mI$#W^^9vlnsQ2Ma`Dz_k9EjlCUTl&!jLB~ss z=VL^?*SC!kc{LV-Xp-a4dY@uQ+-5f|oJ{>9J0Zl;x{IZPN_6^zN)zXMM}e93m(We% zgBAs;-TFc_|J({IEX>Ve_J3@Ce)tqg22n(e=@53m=8c$&^mk$9KYA|K#1owMza=i% zND;sF7xfm|uC#M=B^a)uICc{!#9|joQ~=d!eD#xYlGp(&z}q878tbJQTctWY&=p}Od}>!YD)hYPWO?s z*`1Plj^8I9O?Hs`C>6A~w--PK+n}Vf9euQ17MY`Mrw8A!PP`_lw%j!JP05}XII@N) zB|+Nkb!9zJroXa$c$JT?$P-D=mOEo0nbkB9g8?Tj3|+@Gw^2rpUS8YvIwZD?i=Q=U zHH?xustS+_pRWP-i564z5m7e+-6v7|wPLa5tdnbU;dV&HE!g5At)FwL4lcfb zgvSV1nmYz+jyoQ1;{B`%P| z-uQYQSeI-uMQ&~1lsNOGvY@MFx)H?*p8jQQZn~sDFmso2u%skrTw40bH|7d+E|@D1 zBkwRzr{%SF@Z7aL?_=h3ios_K;@18o8C5MyFa9ZUCI+ajuCnpMT^!j3EO%$C4W{hu z5}(Urr6UW1&T@Nav!3~7HCtljCD)D_@J6$DJhC8J+0JdT+Xq!^^SIh-b^Y;CuN$(O z$nq)Bvla|o!ITmOaHLe~wUpR6yHh|KqJ?zW?Rq@A_YSs(y4s5iqUmrD|B`84<3O1{=rBms&;BvonFgu-RYO)jq`$KM>dZxM8I z`eGKj?5y#1reU<&JG1()M>6nI#D3b~Ti2%|8vqPqL4$!>y~GuN2v^%((YF|I9(IjA0} zjx)%5`E?uQ+t&7l#vF9@^vd`BsFvcV4taPinEzA&et^P)T#cLf_0!_|%8zI{s-2ot z{j5Wc@21#O1A3Q7M3?p_0&|!XEvYR#FTyzDr{RfqFbK38rK$ zT6Kkad$elC9qeUIN+7~g)K1g|2h*>4LC|$c(UkAM9LR>6dNADfrW?-WX?+Ij**vpG z0(m9Wxi*t|$j9{rl=M|VqFoTUdXS8Aj^#YS%?cjQ41fyAuiyVzdjnJLt<++2fp6K2rXE=iQ{V>i*X_i< z?SOyu&za=?FC)@zd4maI`ZJ}C`%`|J?G7i7_>v*j$L2RFE3KJv5)bBf{Y4b4tXg_1 zCDk{@pxa7FBF+-g-qSz5Oq?i2=^W^VDuK92UoC69?y4KkV{9E*TJ}IflIvddH1Z%2Fw4x|JK(cuJ z*Q|%e-2J=Pp6mAVxKslHrkKtt2$$u#y9l#sO1|vl2gPxb$T8}7+l*}3tSGoYuHZ(f z>a`=+V}W6$(_G(Z#<~2&sgDWy9YG-&W8y?lC4MKTa6(+9{b{x0)WYQ#UGDf{xw)bB zD1~HP>OVcUpLJBvxLKK$xi&5DV<0{Hh=qylzBDT0u9p`)+t-y>5aEdR{IpO@?8Src za}o`M+7q|4ZjB>Se+1E6qy=NRKX2B!r0k(W%k^G@%G$+&87{X)JDoLalFu0&%=$g# zkhW1>59N1pLq%Mh~ zUZ*2N|E{0}SXoS-F~N6aD)F_Zoiei7FTq3Rx9*1z;$ z&M6}E#*x}O9EF#$ty5g@rmiUD`OY@YB8`C_|LGtww~xMR=A`k3_DlhUVbW|jBmE~~ z^;g?Jn8sZSXz3}|d}DfI0@IFA=AssFX!CLq!ld$hQ81kGV}`g9g~`Au;O2`soL(W_ z2RL*asy-3uVIb{}D5aG8E%n%32y%`U{FZD=oy+<8PX!rid^Ge!COIbibcuDSG8#9` zbnKg$UpX#$2THA>UgF3Z*^TVz6yot*mDp(Vt7r_7VEjTuq9N!qB)S;&i`_=20)~K+ zUtgW8yy`3tJw7(ylR1qdRZNCg4ASf2#hY<$`FJFdJ(9(Iytty47Jujub>`2PKtg!? z3a_JUM!}qDe(hm~vgB9s9SQk(Q`-TZKLGl_ySis^eE5{c95Mjog@ux2% zs~h*sq+1_ARX+;N*-9H*{_2BFEiF&9oS}8kd9xvdrpYSoR=R$q)VF%3^emg5%LxMF z*ab2;61<{l)@7!?V&k-ZN)UnIM*E``tKjePAsA_3xF^w6!L#F0pR2R|jC5P9aiyJm zlJfVjoT117g5Naof5i@It^aKb~5XX~WK)osjGCukWDSx55%QdwO*Mm|bc zpv=*yowh34aS$j$fvVnmdqN+!!R@euc`CU=+~%Ju#1;AAG!y*6M6tnIl_KPcN86nb z=hnbhXOXFE$#dke2sev?|4)E~9qgq3fSG{!%lDg;qD+&OLYZfadDH{MklhbykfIb9 z8_6skp792eP5*F7u`vtgK~nneX9=DQ7riH+vN@#-qc`tjsT{D zfP3n`K|3Z1p#p0_ltWJ%QYR!lV$UB!oU!Og0W~??cp0?AI91PCtp`a{m=YO$3T2gR z%1IVHPjL6B2+|n&&1uC{cb~qPQFhXL zBp;-b_^8#2HG`8FG|548W>-Fc(xVA$qrc~#^?J%M?%a_gIn1Wi9|atq9$hDirX()y zy!Xth?A>GC+HYm zv#?g1ZnyMbz9jp&js3UC&LM3Pj{8-%XgfYvz3SaCAouQeu8+iPC)sjdHPo*l=)DL) zr9-x}A4Kc1!7KfbQc(LpH*4%hy*IbM(u8akmCVyZ0`no_W09B48}-H?CLj~UC`jY- z>`T|`!%3mniW)It5GGf|IDpy%wLmZ>;X5B4h4Kwsh&kYQ+2)Gupa&dCl9}4Nu-%L> z_hGg)F)gfhu~2ET+jHqyN}VjL%LQUVQQgUNv8+|@UDg}-t#9+<54HF2%12&Y{D zO~q3-_QtFn$Fw>?XuH@MZI0IU@oc9kfv3_NDqwLkwGZ`E7N4hGsUL0Rb@Mt4BH^?@ zjoRA}^}42)5|yN>MP>xk%s9gw;e!dpqn+u`a`%(~dNXi8YC*^Ifl9l_ar$i%{d0){ zP|D?C;<>FU5)+B-wNt=H<(@WdyFWtlc(t?%vKj^N=L?4^%I7bSqrTDu`hEV6tij4j zr>TcVp-lK0EeVab%Ox44F`pxL^L%^#zGJ#jl_=bk)B^^6Ax5+t%&iX!%hQX22EAl3 z(Rp%sjp-F(QJ=hS&VG;!y#1K~wu#x|y0cryoPUPIp^p`x9B`H#!yuDaYS}xuQMBUd_@=}L%UH8ApxQt@&f^PvC> z@|1z!BeYZSLLF5D|B})KT*3dm?488bI1(kXQHrZmVrWgY_|c+b%8^O<#1pAqg=cdm zR26w}%v{^!f7%1>5XZHDFSA2;MAh}6las$8ua`XvGN2NlxvUMPGZ_UmHO5m);6Kpl zeQ<@J$jzjvQSu%_ceNkKrH6kG{1wgbESbPJUfpQbDs+sUvBY+N%XvPzeBypMU3YME zIA`7R&6hwG{S`Otbs_vqv*;QD%u(xuEUMC#D%n-&{`eW>klJ)b#0dD6i;H|?^JCrd z9j^82JZt~{mm|?WB>3Ds=K0ifBX+k0@D>%?ILRSNQAh5=?-6&m2C(q5SD+TCLu5=y zUsUOci$*Zs=K$Cu*d+=q=TYKb_J0ciy6%OiogF|_GmDE$=fY=5cr>D{I6ph8xQAT3 zoqiYD@XY!~0=Hy;)^!6hJ}l*Vq{A;zN{7wIg1dVJL9G{HM=bIgXPTSc3hhaP?3U5z zVx63*SD)#dOY{ZO@TZU zt>NaArHqB~6Ad(w<90Dly%t~km3sn4CzU8YFB|jq{=`2Fr+NJFb&EMl{(RB7gS*uz zWnw2PgX@>m-Z)7}r{@j_d&!b|lHRaK?>{cgixWqSjj4LARWj*qaM{E9WMMSD*VGZZ zd4n42zTUqATq6bV-L+i;mtC`pe?*L(BCsOf*44Rr0$tFVi_a4X#=fk}0g+8=5hK9R zgo3yD*jWii)BlyOqZ6%TDuNr-g&DFh*d{B`hRzn{tWWUi)76}`Q_B~QT z>XRr(cXcAf>q?i0UR}L&6kp^5KKFT2*j0feL+Nbfs8N#mjawmiw(La?mV;O!#lJp{ zKY^j}Ecv`HLX8H(#o^HUiXe5(0%ZT;hrpzJ?U=7{W3_d(lSWb*yRlO}DSvyRVl`1QYY4W$5Rmbd#!l^#MC3zG5B*bNbxUaE zUdf^D(t^z8ka^6u0ax+5p(f!WbU9mvL?`QZCUCNJj^^i z^vtJi{;?&tyD@QXr^n!P%ehh?5ufL##^YWi!MBxHf$0xhVb5e+u`6>ob4eXHn#MNv z_V@~{HfC;emwK$vqw-HWmFy^BOpAH|E^{xeUQps7TvCC3 zH=R#$iNVvG{_P7t3P`2KsQ+8m@{V$k7+l5Ibz3+X7E>t&bhDU0I}X8Qy)_=2{wR-^ ztoA?CK9to^M5Rg5V!m6tWhqy)-5h}7M1m~NJ5liY(POohVNsLRU%C$*f&V!D6J-_> z40osWK>Icc0^Z(!Wevj!E5nD7*St=|s}ic(z1&6dl< z*d}VNAO35^<}g$mI!cc9O0uCVqa@UnZmZ;GkWHYpV##_+6=m(W$cTD)gLKGYd;AaE zpEY^^Q{^J0iuTKan8p&Y0Xaz>oIsE-Xelsm|6rH1z+$SM&8{cLEbEoteCogt-D0WM zkFJc`_4pf`U@henpfJ z1eH=6=~lW!1O%kJLAtvaEg+4gDd;frE zK8wYi;~nqF-x%Ya<(5PlxS`l+d$oK83b9;QXYTLimxv14A=B?R3hf;gfZA4t6X&AnEPqtQ&KH?9uBo(=ilR)Ls0`n*@Hi1Uo>{GC%n#yyk-Fgx>z)>lgM z(CH;B*1c61Liyex_O9pXDh|atAKq{@PIGPuW=mVe3ln~Q`Xm48N9)G6m-KbliIT77 z=cw+%9*|xhR4Jh61DdtB_jp@c0XS^7v#g_d?sMV?0h?p*GVdG%Be!>tJ3TaJ9NAtT zYs5;4kbNC)EoMMyNqq@+ibps@>?D5NEpcMilhJ@|DI{ zkt1$l1J4jit@oa$)UEpE*SaeOOtjxAarBi^z165HTTO2QQNzWcyR+Y$QvW$S^ z=|=4&Y8l-tHeVF!eD7Z}*|nN*sj=&g*?dZkzgG&)R!u}u$dJMslp9ZN5U;B4D3&A~ z8jq@uOal1jauc+J1CSV}0*?tEQjN-lg*`xwzCH;%c@9r?MeheMUN>Xo3@bW1)mGnj zv}rBj)TT3nn6sFJw1Y5Or;yaSKL$T{MHR(+`;qAqiVk!FfsK}cL|<27Cf1rby@+(e zjt>SjRc>)9eJ}<&+V2}YW8I)Qtsuw$V2p3+Nta$f1eqGLRFctmKZZm0}408paHFla+|MMb5tg3vlj9y3Ui0S4Igr}zcj;Tf&pIx z>zZHV0#y(J^Gt5QDWXRv_&6&Yq03?AVkJrm{_ck>ncV)ZpMK*R0blvZ;HJ>TG^FK1 z0yxR3as_JN9D_wZsT8c4>9r5=w4+kldX!EzEOBd~C-32RjZQHrrQS4)I;Lw>70Vnf zjm&HV{y%zr#7ZWf%RWDwmQN@A)JRI3>_n~Bc^~0{-3!9EZ|9jV0;C?U4fX~_HF%0g z83{@3LS;?*o<5szOu)i3-rA#nz*a(vSLFn&@@*eXS8cAj3!|E@Xq5>ft*v`Vc+5ZE zl7jf-0am9>*@X5@e`!7{$#VPiW@xedApC5aQg!J z=w#a<0iuT1*1L}rcnq;M>z#*_2luOsIGOHB`qwuBnfaCG@ z6~PfPmb~R`5xR^}_%B)+=mY%h7)rMu_ zloA6bThHgT_-CS9vb!-R-C1pVj?hbrN6Ux>| zTdE7^$n-4QcwUn}wBV0Z$+g`$v+?&CDs5CgNFs!VWG43Rv=`I<;rycM#RFfzCNhd5 zeT-=S>~%fpJSfOrMfDUbxxUtM{30GS**Fa`K7SAxjTrS?G!&_NS38JL{ z80S^|q4%peO^r=*!@aE7@Im|>aG1`1&Y<5I|ohLWmpP>krKR!y1dR(7xL+%9r7 zkZS zQ0GYR6In!zPf}HO>O2?Kk$gOIJmh zXx2yX9W@i>W%s88mz^wY+nN!>vmbgOX-S!+q-({Sa*|Kimi!Ni&wdLXRHQ&$mhYz z#Q4q)P_I1S+wIPGkY1oumNr*I5%>Hckc<=OpR02oljQ0T$>M8p$I<&AoC1p@4Fh7qNfY_s2)5!C>_+V z?-kB|PepNBjDL@wZMlJmJzkUgSnmh$_VX!|?@4!GQ44jvaK*$8w&V#BQtu&46MZ*b zpP8p-j@(1I$PaDdB7?(zMG3@3f3nzg*jB(RI8{OMSe$U5>j8#a_hS?( zjc{ppiw{akx?Qq}2%ZD6cb{akB$vZhsBuf2J~9ib91jDp0}ZuO%s4TkEbqTMj>_)| z{1W6Ffb+mRDKdO$?v^$qc+IVoq-RdqzFI@ z<~d%sb`}vnvKqHbi_ZdmTXYuD)hCbu8HSTyYkKZSp3Hh;rXh8VT<~69Npc%yobWh= zreN~B6FRJY+L$e4?%9efIb&3=F$WI7X7+luk* z1{)0@E>v~&e@9pr4#N3t|7_q9e@4-o7wMcPYSr6p=_H4?%Gm=oxaV@6h<=B%xcgm+ zYF)tFsI!kQ*X^pUubGlLToZO4kb;b&0JuGd^E<|_h2Zm!51A2yv?Hr7z&NTpC;7Jw zSt!Q5pT-(66zHUjyL*f&JwrtQ6q_59R`HW~EQ4t^qw0{YPOt9Zsr-b;F0<9r)nK{~ zD@{B@g9&H2HeUu2TS$mPDIB5k@P=&kOL<)8*C`jAgXWR_S%5aoC<$?-iXvM4-k1JF zxPwVEkx|qCF%^)-xWh1_9|fouTT$whp94p8ypNGiI$!gOvqdWJR+->WJG&~!CU>hK zBeKBr@YhA{6g(@)4XifzzRPLcQ|oPr10o4gl)yYhU34i4e5P@xH%!$LJw8=wwDrl4 z#brRww{S_d`S>FD+3@v=U5OY0uWjrGFw~mggM(83n&}^1%pmTAM<*Jr#H*tY_G5=@ zXV)t*3!h>Z zWd=TYFB${=9OC_ZdH|M4Mf{>HZIO`I-Y2E{>9ZewfoF|6V??WL{V~vN@ssdexsk{$ z6hEF?k!GiEhn;jbS3As++(8dEGfrm#P>=Z<6wKU>z1@EwU}-txH#lA{WBA`!VHbgl zjSjwqHa@ka4)J^V`pP1eD0cu{9W(O9ezbT0E-%#a^T2Ym=OT2U}Ra<}+WTOJH^@>n)rfNOj(wSsW@ABLZum zO1~nLmmrNi-)(^9*tR&@A8*iuwYg7tK*1TEDWa>V>`qf%{m0ifh@ER}hFzatnwIPRuqiu1T`=v@|XB`{3xHSv zOGQSTzVgp=@8+I{*Nqy$;4IcQzZe+U*`Ifj;A~+%yWtA*-{s1q-*9E;5t;^4)$3XI zjSUp%uLw5{&dyf%&llM)B^yvS%}wNA9P3B zo++Bm?^*NVMjedng862JhS!cdW{hrxF6w%IbLy7gC2}jI4r7aG5O5r?b)OX6IBG@$ zTGlWCiAb$L`OEQ%MU{(%H-g@9kI>E*e!P=B0V<_B;D>4gjW|d@o5+OX?#_FqalCwr z4c!spwAE)Rxav-<7}~0-x8EG${X>p^@~IpD zC!f@C`8Er{ejrcCn1G(>+u=>aHj56HL)2}-?x7w~v_*#KX85thjq7ESTu5pZDRluCX(9{?6^l?b9Bo!x~)V+M0I z_V#7gIunYA6Y;*Ze~XH63tqf)_ak{@ZzF~wdN#|t&xg>|?M%d1mFFcNSgcPJKGZSz zk5ImO1dgPTez*1sn5oH%OJjUUz+8>ye}z2jB34mU98#Q^B}H zGxAvin0(RXF~0YEbMR>I)m_zMqjaF5If3UY`$$62$#OOWLsY8YW}lI6D+ULGB#+>o zL+S;k+)#m@!~o2i)c#v`o9xDDD;H5{0>))+L6G!!D9pd|+iUPtd=6-)$ zIQz$kNjH;wgiL>rJ+JZiS~Ob{>;W}O4Mq(8eXQ^=Ftrp0Kn40UAbYp(`<-|JH~0a} zVKj2Y)^o_o=n%Icp+S7CnB8rhiYO9q9B1JDn7@1o14g5?h3nd1PICMxI%@d{blTxK zVVMowOh7UaviSV#ZvO6?LlO{0Uj6^~y#JiqcqiyFzQ+Ao!;6KTcam&3rVImx<}Wu` zgTf0uanvXHw-gYF?%rEB;;>_z6-xsPhnn}@{f$-lw=J1_0GO4doWFN};9&K+;7LF{ zZ>Dd;(I)$HvNBw3%?m5|5sD#MSCMq8Gp|Se*fkTgTHeJ23vk_SMl?eFI&@y&X~h9t^1y@ zTr;91*LaW69;DkpLZ$jZOq`zjT;)!YbqhniBj#&iVRNqsV@1S4K?GR$Ub_bLJwft5 zI_RO>(RYfP?lPookKN@7cj}Gjv|H&~-TGEHdftuv=FJbWTH3L$@_G>O(;LV%&!A5;k;Cl;ii=GHrH^xw-N1$0SL~8N+i@knrUql(JXx(UAMC7SS zJ`1}H%g@JiWXZmlz8*z)1bUK2EI%$r_I4vGJ;9QY&mK1jd*=z^x){qv5Ntkw7h!=2ooB1 z?FUuGyK`)b?g~f9kMR3eU(^04&HyayqXo!`$NWojURyW%sTL?_=W5sucRe@4=PFj; z_|86ZQu74AGG|i9PDTd`c^H|Pm~pJDiKOWBfL#lk`u(-?uw&jR=x~~{jO+dtz?&oW z{wM!&pWyBtAw9yk&H^44kJkE|&0M^Pg!a^EJ(%|~zO}B>wlBQfiB-(7*=*EObcXF} z!66wL$Iw(FZ4+EwoU$_aC)=wGwZr^;R2SDKmI5ikhC5AgS4yR?Z}a@uqj&)llc<5a zXEndP&9g=hI3M^Qsu%C1fldB2>?{u-Z+*;LtamDMeo?a==}Mu>U6bz%zu}i(m^K1V za>`p;GRzZZ!vUzS^b(f8eZyar^pV`iL?kZ$qju|&&#%JWuaCtKj}H%uY3%Ve@NYBB zTG?yPtH!6^-J_OT>h6;#PtHA=9eV7H-+abt--z1m;R7T3~( zsOj4Y0{?}g!Xo_ZxwrL&08kY+qc^73#5JEh8&l;67?lFrNjz-twb@8vzT%soSTZ@oL7J(QnTQH zmc^G|ORqmu+|@FJai5v3CT3uC2FB(zfV8C3=%o_3yeFRwcRr0QiC^y8JwlmV@r za~c4>c0^&%|JfZTZ;vTY z#v~?^N;7_dV>N&^U+W@`WU-Pm7co0%@)RLlN z)0nDCzd7f{)s+ZwmoZfc9~8M9uIO9}51ln}_AqR+uJ`d2;@HhKFWtF?Wi%{C$B zwb!qxJ>Q8^OZMRdB<>=~__k&GlUJ_y&y+2CJ_9Q9ccikDl%uc8o$=Y&s;R~IfUqrD zA9!iw{~i7~$|F39W9vv*Nk zx_&Hn&Wo{!=Jfq7`3UgyacA*-qSO5B3*ewm`gA!V{2Pt&^a=zKRBYj^u4_~(qK4Cq>STPpIbCb z{_HDmX?p!t{dW07tCDe1VK87=+@(ri{nFLjuC4CIScv_#v9JNWd*X;1>t@@-(W%u5 zF<6<0d%Iqwy?KKpgq$OF`>Zem3}&oy;e7H0-_-L>UPI9qGG|h$Xr6o@qky$Q0~Y;% zwvPe=;WsW${4ZS`1}$18N8U0(EgKoD!dKH{^_5}dq_V|NdVsixS#bE=V)&oCl==8Z zjYI!ZjRBLgkUx@xP023l+}Bdl!(O3nld4;XJ&?BDGLUL%hm0O>KVc*&=xsiAkdc4x z;RnRpTo#>zt2c~X#b;u{v;h0aKOrKyV zDn2LGTiU8p;EYcqqm&~2xFp|7#?N2qBf5?kN69gVci5)+KSTCEP1YReA90MG;9rK4 zMF6k~2=I~u;BDjy(f*6KxpUY5!BLfdrD$PJ0pzT}(#1v9e}+BzH->VV9*Bu*Tk1Al z?;+Nj)jutO12~2%@EPU5_!a?yY(O%SWmtZb6HH<54bKUThPh{&EIL|lM1wArXX#?1 zs6{0#*D~pm(45!5zuX$Q(c3x#qyM@B2;k(b6+=$`6qcV?0_*octQ;?LcAu^f?L-cw zRa%cM7=(9z{9MP7G_NVS_)d?Un1rOLvBtmK_%T)8ohI*rNgIm);D);6;@&Mr?x6sBu_MXc z1b2GLfd>~3WKnZA^54AvTeKMf1WjjqOSWb|dGL$SIoZ4QxcE8hHMv9igxRXfxf((Y zTTC)}tINwB4cym#8!cGr8DGS&s=F=FET-$V4h|1X-T5yM;*+@CU*$p7ys;2L79v&P zC%_Y;-@JyuMeZ*}c_h&I0-)SqcHr@CUd|~rD*M8puoGCBQjLaIiB2ZR$1wELeS!=J z)3TUNX^EiKjIZ_FQNSTw|df>99rNon&4WsABUZ#0~BZ{Dem0n`=6ZSQ3Q4%y9agg zw&T^lQ9)-BWVDCvmu@Q?8ohhuAbs%#9*6*jVR{t``hHCGk(`Vg*PFGm4(`s$TF41f;1D zNfaatEb8}B&7Yi0p#f--H}bc+#c{7fAWpXC$+(9IMCAWJizV@sf8;6ey!9x;i09OA zl+lc@3hx$^{4SW5kkX zQM7x=kd&ELGez}1X|@hud;=17?JT(4x*D+-nP$s z3O8KlVh%yM&1K0(Z6Ek%hB_^^inT0B?TPV0rrnsDGFKZv!_di&HeGoOH22GvcjE_z zFrLY$($01_kyDw6{Mk$nV^dg*8$i?}1WLR3l(nBk7QSIxQ7%~bv za|QI`uX)+G;3cjn@oNfR)xRYn#X4HvOY`D7{`(?0Kk-wJG+j|wpG+U=v zIAvRRcrSIZr*>^F-Dy<}L=ffjos*N3sq|EZ5x33>)!}>0mHto;+=Py{YNgyr{<&lx z^i`9#Cx6APdlahpR-WX1s9ttw(^WuObps~Dmq{s#vIdQs>6-dv%GTcGfep;t4Dk*E z47|+83k|$m5^*WkNaO=g8Zb@QjaxCA#R|x)mh-V^WK43}ww*%d*G_@;LA%QC`Cg?4 za!;%Cc0+KX(mxYJmAO22mDtnabXCfAw|yi48<}2Vx_|%v!Przg8&q5#S!RZidpnDg z{Q2{Vm7`^%1}bhxm_B!v$=q}$TV-q`Nr z(ZWlY4wTf3mx@NHA-59Qok-Vvf1VJF@=S4iS*H)Td>i0Lg*1Ap%>q z0K}42X40lLVU>)NR54HfPc!@Libfm=l!e>vG;c4E{bqq0ybF#d3JsZT1(19;9nN*1 z9{4WCu!^DKUdDP=8qcc&n5yWeU0p7|sI>shUa>$nT+myYC z9INoMStPWv;7TQc!~~oIclp$OA9$feO}gg9(~&+y5U;WvQ?D_w8Xy+5wYAkZ-Yp`R zwVAr{=L|kBvUWc3eo+QQGZ(g}j0Za-!+sB`Zn?!fcd^KTmHW(9(cSX?XUSfSh=>5P zN-?V85YU}eT|D8n{5>`GQI){}eqhLlqOC|~lkBCIQ6mYh6stlmz{N~N$7$pM<%k2n zMAgspX{0roQXoBxhR02CtSmBh31XXr)qr(TYd&Ubf*bATXg94+Z z>p>m}0OgOXf5E^)!7+C?Hjb5?Lx)@U)E-QXJiM9cv_jRHEqQ$jBP%4H zEq}~%%X6A^y9dtHe?JdtWp2<3iO;7msn#CEG!FA$fn6=gqG>j_7emS>0 zo^Ud_@-Jh9f(l*%^_6F3=xAHx4T?&I?$VL+VI9d8Jy8*JGImxw2#D48Yoze5;Y_DD z03sB9JL1PfH4{<*kC2W20Y&B%Tj^rtD#u9kAIwgZIlO+m^zb%aRXa&f3>+FO+{Tg0F5wFvm{v*3$Px;j|y3 zv6zghiD=s7bw|en>ytoi5^Q2JSjc#SrP@*~(UZbW&&ZHK&)HEjSzq1qGd)qZ>T81r zv9m@XE6fj6{-p&`Bn8pE{=+3pZw<-M zSKm~cb73;@?}kMlnO~jGh^%MS4+K<`XZp8+p`=~Qdl8^#)8xVv)T6Skkg)x|TpiU0 zMmeQRRYluF6aofgd#U~6tKh8_38^<&%O1n@>Mz|^HdXA#ulWZ`Xxl3$)3=>))QJX7 zQ&ElWy&bp0SbN26MZPGF$^&u9BV246J&KSB1{}u-8bulj50Cn-S*JH_`LkrSu^#E! z>UN1M^obp*-}#IRQ=li7SH z4lE>~9gg-85)xW6pWqbZbr(S+;CPjeE5{sq1CeNx_sF(d+i(Y%V8e><`E!j(=`5<*UfzTK zAV&S(9(M@Hy(8bkTtjjlB#3Cz|0SNqTtnJ)=nKR8`ZHU)_4iv7MWQqAhwyQpoBbq@ zSBACee6^f(eCaOKVc?k9!=c^}A6`@~1o1t2q;lpIRPDL=W4qYkkfE&YH`SmuAgN6a z4ixz_0r+|4HF>xGDLxVsQbeHMZp@gP11ph}65dz)flMK*W#Qcndd0)lgk9w}D^CxL zlHNF)d;oPb_xFD^3wFmBT*X*iHW#) z3@Uf9g_0ce;XY?w)|>6MgcVyU56IM4(=f<9pmDz|k@B=co8UapuP|}&W#HlKpxO#sqY9eOOy_Iamll_`G2Lo;9KWD1NY}U( zqdpy&Twfk4`l6dQ?$q`1$nKfcq#6-OcTV#O))UK_m`EED!~A$NcNHg>zSNzArNP1A zQrJc(c)2G&bF<`)(bg}|{VRrRcm(9ZID=i1w}3e>M5MEKz|q1bQcRb{coz24suqm{ zvYFz7Ov@LdVLACo?K8hHsW3O}ebW z-Foia>YagWgI=s9zQ;`ZQ*#-*p#q=~L;|EfF-@?+ABVf`?g!r)txO&2?r+XD>Mm~| zZ*<(zolldJmX@3O=6R)9F7Zeeyw{&lA?&_cJtOH(<#|F7-X7Zkb8r@ayKL-Y*Ah$~ z)?MwZ4rx$#+#NHk1CF~wL>#&!)coVPY_SFFAe#L${3(av(0=Ub$rP>KYt1Ub-7LPN z+O-xZ{pDim;}ki=?Mr%&Iq3!>=v-sCnP*I}D-OhCVND{FF9fz~<;l&ZVF*oRvmy)U zb1Tg83|pJF#AO#kqoOTXo6d5mQ=4eT_OH+KIRBi~*-ymtRU=@|OW5aj`1#Kwe9~|5 z%{7d@vN-`AozyAo?kl+r*CYg;@22pvJzQlquMv(3^l}sUM4#|d?%(e`N(6t5m^(zTxGu4%109{4t@HyBwP4hdHak$~J1#vkB*Gs9<3C4?W}n&R zeMXZ#NGqlhgTFqqmo$_YYr)xa$qAcOJJ`7}WrNbiwDo~hc`X7mO|N-8C{*gFa7o3o zeaSUcy;Eho%}wNJ<%h7)hYCK=2^6lFFs&Xmuaw8a#u6|oHXTJI4#tUdAIIb!#gi8- zFq1)S!>Yu@n!M}jd)k_X!d0r7JyW_LM1!3~+aZg(PqfJ=LA$ZnbPrn_W@H?}*4F|x z4%Py^(wcBh#RFc|cLh7Dh)eA3RqBV7AQ+ErJrofD>4;#<9XM}LM4=nq$qpGWp75Di zWuFZ4^g32>jp%}A#V)f1fF-{S$BgqrM#DU-?UM7SP70Jp)xV;lsetV+*N~Ufpzcpp zsAbg3BqDNh-fT8AZTrJGK_zv-v)vD2vgF385Cv;%m~P+)q&Ti)Gl|3#dh))z=|@RX zQKZcmVNrLnsy=P^8|}nMO;4DJ4k0eVri+EH$4ey#dXi$eju*ONEV6`;%Jc10**Q`j zzF_vG@KOSUd@oPO^M&DzDTK_I<7Qr#0A|6R3Zep++dbveGys6qy)D zTekUKETG3C=pJj*bPesOQ?`YP54=+sKBIEj)@qMI_bMSDNCSe;H@NK~zR?I|i6>2P z!Sa?;kgb`j(Mz{NO?IuJ*@)wEt6sq>mrH_?nvUB1YxH<5?K+bAd0nw5zAK`zEr=a=iy#7Q0 z{edW$eUAg{R(|=&TCj1{2i`(miz?8UVhrhN(S%oKvv>^6RmFzDyj}jYXRBR#X!-^< z*XdFc4ZukZIC!)-;7AMqv-RAJlSZ2=Jp89zYg{IS2bYsJc6N69JDtqr+|WzkK0u(8 zt$~&rS2b68q7*PMhnSKDgf7?JVENyciuB?mL;p*&_PxX|I?BD#|Q&XSUOPNcy&Z z={?<5UBYGk=!u|1C-R2SrS?p{yqk{pbm7VA84g9eB4Jy_48<`I^{4}X?zUVrmY$Z` zX8>m(3m+X2A?VFVH)UWeIYH;kLtkR_aLJ|9J}4RymXRTkZ&*V% z>8rFh2yB2|i0)KvlN5wH%-1OflQEOZ&$w?2oi9YOjs%nzU##rL&3Y((QKG$MsJ-|$ zdC*kT(m)Mcw2CwKzHBmg~HmlUzHEBCZ>Jz8N?NNwD;1+KG) z-(#BiboRJX(s@2>lCo`CTsxNjO2fZ+qp>X3G~Sc%WXgS@Rv4pXaWZ&@(Xq_;4!PIM z)$#F_JKhEYiR_j!HI^=Yas?5+1NRV9lHCH1Ha)L*4V@5@V;!YjHGp@%GT0|?7B+wv zeBj$D<@7fkoWwTPnG-ZuEw`8FvyH?A1m?lr%II}X^Sg4~{f*z~nXMv7;!a0h>2`WK zI-5Ea-!08&8^*(*SMj-$(!$Cb=w)6 zA9^X{nO#*rfAMhtGQm!T=id1!5iBBL$EElUbi!eAsm!3)3_6U345_pIqEx|(7 z#@Se{Ir#nu({34T&~babOgl@eQa(@pfvNTU;BKty7uz$P$hXxOjs;o?K7fxu%2lZ1 z+V_7~bFsHsK~8shDUP)v)avUNMpLe-x;13>3?HA4R=I&>BfC1+%0qg~q9#dRHKbP+ zeA48tj17s#S!vkZ!rVWZ^J=*C*aAA@cHbQtL@m8FRr`3JTw@(ZR%-fXy8+e%i#l#F z@7pj?&5_NkuB}=Sw!nfrR&#~SE=F1w@Ar1w?wHTdCX$DiICTGDI`sJlds%WhYo$L7 zA(G%KXtVKw7k)G5{mX>?1eG&ypfYg|soVMZJq=7i76h^#>-1%2^DOR#XaE71jgC&l z6sYcr%!gddaq{^1X9wTXRU>1T)$?wW=B%;0SL?p;aJG}7;S%-=Sci9{vQtA{ncT2HZ5aH99%DJREYVc;+=dbgfNB{ z{B}BX+jTeGlxY{QmE;a|&THaSz`((ar)C(n6wLuU1Px82zcvol%rVgMn9{>V{+hQ+ z8c>xnM8qGOFI9)w18^$nVeg7w3xm-$^*0Xr+3jjCE2y zG*+djrQ4b8;Br;0g@Itmmp&4{>+jo4&TYaIPgC44EL;{trAq;Lnh>Wf?lI{Fwo_ZC zdjJ{{&ataO-TCHjD<8++DOi0eWs-;?R$UWLzau1YqDXnAftsjZnXi3LtU`m7RD{xW zli)zJ;!hK>Qx`eHLh+n70lc9GIwD*r3?b_cNeBhLN@zj12H zjmkdozpx6I>vPlLOgbQMbFQ$O*7P6a^*viG%v*wjllk=wIcL@RkkOv9$Y(x81Hi$z zuJ*JbiPLnamA3vxL~{{g>qh_P?FUV8703<_sSv(YzEtgO)36~(Bz;p%boK^p^2ytH zrbYk$gOIB z`yy6Im=EZUErjGzTU}~=GNrZk=^+q7L=*vP5pirg5Y4N8BAj-Jd_ZaSjL)%eZ~QY= zTVa?PeoD3TIm){rZldb8&d#V#h9XI9F0PkMG_P=3zGPp@E8n;sMl4e(Z7XU_-`7K{ zLuvjv5>f;$b#r!8>&62%_p+v5{!515E>+?U&dl)}y`LPAm&SpV>rSs8sj_!^k<^uG zI!%GV=F_#Bd2vR9tU04!!&r<}l;16yiRlQ;Hu7A9II0YphRs zN8872{5)r`cT>&}#*O%+PhWH#xdJ7KB6Ku5z>(I%dPG~)^3w2FL3-m#?nh;{dq;VI zH}mL;=T_%(G|AIkoL7}@XWSeyb09@2%P{Lm(vmM_Xqbd$*A}fa7eFdfAzugbl#P({ z_3=JTRxNsMO}nXBv6ueFg1n!*qkZ?5`w>V5-2;(xSPQPg4V*4KW-~Sbf~4iU-f%Jc z2M*|B!!)e)HDuh}@>5!T<4^kDuGDG7sZ9g0Xjt+W#61Y$Z^^*fHk zY0#g6J^K!32|U{n!C3CjKr>I#EW=HxG;umo)x>$&d>v|LI8J1%;<+FZ1v;qMhMzKB ztnA=CxR$Jm3HWFsE!mwzmWUqES?HF&R0#jLc-9XK-k-|b5B5;1cv~iyJQJbwhwtNq z&XhOfsYmg)kR@V4-;MEN{ehI{?p4J?4~0+_l&GysfgIjaRjHSUPjvfuhnD zAn3jI@$8x$^luyEN%wJ5Joa@>hS{33@ElMn!;+&t*({=FJ+CE3VAX;>$@HkXV=U!) zDu{^U)p2H4Pw6uX2_VONjhf+!h1uIwi?4vx>il9Oagg-+ae(s=v>GwFNa>jUHGC(2 zn}KCpXlap}uh=L*Sp%$I_@BiWz%?Q6dc5+0uJ^vnE_IydaIqLaoG7wtP@|OQWzx=z zGnhXZN$1aBQUHI7g z6fKKLbau>#XgRHvn)Y&#O`Cy%(y`vIB!P1iO*ZDS=h6$gVsiMFj3%92cn>jQ4G9eu&n0)te)1mu=jE?L zHSntOsEqvGx_@Q&Gud8TLci_7574g)Z@lggwK0e)L|d(8J4y;8js zzkT+Q$h<=Ow{o#Y)~T|C2ux1%_{hk}$7Z5$KQ4-YO;0aRYj$nVu*))yldj!j z8*eBy{_w_Z&hNhG)fM3W{z&V$Xgyjm9I#N2PYuUAm^t?5N3A4O&d#wU4-QfE+gie! zrDFGEPD(PU1uZw_#$ep)^%1OYo{SbwNw-@skSc-Bu11SjT(@1#J$vM{SJtVkesS$B zI2038g}C@{BqhtojhuyD9)rxgx!9&xmh{_wgikxFj#v_}X4FUkB&Q2V(ttLjzz`zE zGl4DsNsl;wHV@@3B-!;ocfu2?(Md_1_S`ReP}tgt%V7o-z#w69E^Y4SBG$CnFUo5NnGU=n|fD2@u5wu zE^ex|GUMcF@o4cTlpd>Jo|z9M$A7`a(U+x!QI(-SKRGpfWmE>LT8(=mB75`|a4R)V zTQLBJQ)8R~J6S_Uv)`VPB?K+(JWumx&NYd2-3a;oIVm=2%X}M`pt>|hJf;VcIq5#) z5XR76W3zO3q#->n&bs#IUcKmMeMB1Nz>gJt z&be=l1vKMH&h>*nr|@Oe^43}V`Mp=_CZnl-v9Aim&O6hS?VW{PBOEpMxTLD8oNCiJ zKw<92flP5YR^v~3R*q2@w9Pdd%`y&NgKhvvU3Yv~PcT6(3O8f!s4T=x`HVkB>(b8e z(qoAT$2FDuRt<|-=cYkHk9YmyZ=wydx$P$Jy{~T$P2tnhAiTlrZOtF98enB2_1BhF zk2MvYiGT*dgu6humn3d^tgQ0X#>wz0(GNJHEX0dRa`z6 z^HfkM@lB{?j#V6^n?%vT@HEb{b|Zb@W5b3wbF9v-)G0L+MmlIt(qQ(>53hY!S+Q7O z%M4)Wu&nnlO175OD_pQ{0q?>#p3W;iEp?+^GESELjarM}%(q|~C;g`3Hu_cLq%XX?D_R^% z4Jutra@;gEXBFo`I*?3{+vOTKeYD+(pYsv~U=CCtx&TXHb}5|4?M!rrTj6HbPVn$O0q#|Vg}7}vBp=+Hb`#2oO*q`Y=~SK9qech?R<@B0IJE(tk{ zd5>ev>^l1HO9th0$uM&%^Cc8Zn4OM#BUL%5_Ir7cq+;H;0$Q#rP7X3l$Zi@@;#zw$ z+oorGoX*2SL3^t1lUE0f*k*cdZ+av8<0PLPdpn6vu5Qi@Dxvq`;eXwgaA$^nLMGMU zTZSj2TD#o1Z;EGh30%%2%cayi~OVCsWD%;kJq}B4Vj` zrHr%V4#7Yn>^?)1ENHN_)NWw$8*&Q~i*;ecwMQ*E$Jf1Nqf9Mdu8ELv0>`M&WZ;1f z5p2SG{ogg?rGdKHV0u_+-?!|9zb!n15RKpE`A%& zA?oAnBY`d>Vsu?(Qvf>Nt}z&LJ9ogd3X1Z2|Niwd?}+&hl7fP@&aAgZv3`q&lmCv> z>J$7I9v#EO2t&hn=n^lKfd4uRD?e8ziU>P?FngjoG3S*DGr0z@!g9Hk zh+6*I3EubuZ>8O!3=|9pdg|-d`q|qHYs?bM#hdn-n`~PoLd)zzsG>!!1vE3$ zU{i*c=uWPqF9#UPb4S82QcW#AQh*xXi-V?BM*{Ws#Y4hA2I!(wLwnMAudxHv=R%J7-ut_jvV^6jEQS>lno4hX?$RKW;aF&or8b{x&1)ZD z6c`GAlQ*(dT?mIUujs}XT+_##lZb zB9IPw{q6|+=_9H_v?^s9YVbxm^Kxp}`TDV(_DXwpme}jh+XGg>30tHnTF!+-?zYcw zS;4cLMtoSJ-OkfnR**)dG0_*^_d<(4dE&U(TmA6rff%>?87QZ_i7CI-vR^WR%`{Jw zgppBM3*=s&Bt{D>GWLCu`UnRP7=OAWY6BDnJ6H?xxl|L_*He>=FBfqPT6Y?N!NSp- z0lSsV>|<_>Q}JdPVR3IKdPFeTbZ3u6{%u%(txH9>jgv3D;`0Oj;R5b5KF=vEa;HK% zP+o~+qu;kZwCtrYxYkl;{LuZy+!}Kw%1_rGn&<9TpB+=*5jv-C7?zutxoazh|cG9f3ZS}sB2JHvm;^52>JJMvz#3kcF(?T7=nY1R#P_sewgO&txwP`$*eF~s| zl$0WD&g*f|@*pBG?v+gR%u(fn?6V1hG^oVeoEtZ5xBz7YJzxEV+m(tRs4FCOK8R+H z_w(FS>nXaftu#rvoZV}lfQ)+NlLUKyNdvwO@lvQ;5iAh%rK8Cg0lD(`DBL>nr;#ZSYc&0rZz0k(qVC#!3u0o^R8s9ZFG+frt6 zwFauZ6^S$Q^kuHI@!%V@bdUH z=oxtq6PgRbu07GuFqT98IsxEpezpWnS_Un|l2ZVIwO+=I$!zMfY;RE3dW6BGvC%tNqXXI`7_81b~msOvo*J75sZ+?5Z zyt+!Ahc-nCG-Y(!J@wb3{bL9ctG}^Pz+@`5EYi5hJ>vYJDXKJD0vHeh?TvqXKD{#z zD+ESFu*bCr(3J40#M;Uhfl->nfkD@|qnPm%(8>`O){MxhGe>t$`!9PWn#Y3YI6v?s zvdV(45%BLLu7vUtCvRlmq@~4Ys~ZQc_32gzR?p(7QGRU2MG!BSox?1ED1{lUm#gjx ziLV#(p}}ec+9G23%-Wiwzr9qOcxdLSwu9?DB!_hzdg-2G!oca$67VpVik5-p%&O)7 z@n;tDwHKSd6UBnhDy~Lo_=G5cREdk7Bb%)MwJXp3r6kM%3;*f(di2{(<;wJLs^qEr zjg)2GdE_~#>J!@#lvasO+=t!SMk|J$-P(K%8{j}!c2XjcQsMdfMCKDrjZXrX08-Bc zTUX9(+@J^umx>mXZxs{p+P>s^fHq_%BZ^)Be^`6VxT>~jZ}g1kPrz) zLApU2l@w5hsDlx)Um0P}w;9)yrk>GJj=sv|wJfyoLi?5Ue~oQ|bN;1k zOAP%wlZHDu<69fXm5XKG zgH*~k51tx;y>1p7`4bBuGw)=-o@kF;j_~s{0WyRfNCD`OQ1^U%15w_n`H)PmVGLww z2NN{&kS;Tig&XB>ckcCt^(bWFY-i@Vo9;x zNJtF^bI90~E#m3Yb&b^2w%>!%_k56a0E3fZeZxS{a8t=EKBG-^6;E3aE|_tQ?zfId zlO@K-^SRs+f#1uqfI4r4e=Ki?O<157ODe8FL3(KTj_@Io;d;68AR$P^zUoh{ifR}c z#`j4U7_5&hY|@U2^hb!K#U{X202q4^J|fe{)HALG&IUjos|g=<)tBaF8!!U<@@IsF-3C>VD41 ztJxi59un?%rjZICd?bk6YydhU4umwFAgE-Z*Y9?4O@G{UECg5Up0;KRu~cDEluN zGA+dr(IX$hO=Y!=bHYhM#??glTuMqrs#3_WEB=nX2TE7=aD>7nmF~gTwi@01aFbGr zy&mWc6iRO9K(I3EU?#3X_4PvpPk~f|6?y6w;VtJ;|31)wO7M;?oQu&r#FslluRnv3 z)&uSzf??#KFW(Mp4T-?xAx#&H|GaM}eFqv(o@sv#CgYxUFppyqQb*fWX0$2Ty?okc z!Y8M;Ten-j%(U+QAyskr5~>is9lv#gpKWDf1|2c`7|2Nmj4A9xb@N#@6J1rShNAq6 z9__-&?cG*BDK4h+iYAN$Ss7DaqR+13I&*Fx$uL^dh$WgZL7U4$iT_^XKbPMfqHGLI zS=VljVA!o6Xbp+Rgo7AJp?0J%P%S8HFq^$L&wf+4&&=XYg1vv%LRlW5%knY&0Z-d3 zCig}`B&gyKG)|}IBz->lnuoE`WKK#&B~WHO#<=#dv}_d%)R>+{;(%7zStHQNm}9w- z%$%ZxIeu0+yZSbfjos9u|(U#c2dTnv_;eLx^sXl$jl*5E`x#3ie zaI>lr0Xc2ooAbqtU%T=w6 zl=_wT-}Is0lu*3qsCraJQ%BK`+w&`mlhIdxG&K$WF-izq^F`r9RiQFcR83H^IBbEn z2TZycSY643W+S>f!iGRkfoAu-cdEAK7G_Vj$#lJg7SUy?sfPM*V@`_0m`?bx?DA$^ zE8#?MEdE=dZBCKyB+I1LrX-W@hGRvdFa2?1S_<74B%Sg1#xx743k1WRmKDhE_NSvc zymrr)HazOH9xB&1oIYH>x?r;6AmU}iuA-@hv{JTw3v&PB4Rh`iVDP^np_OuW{C;O? zbT`MY9>40_q2`ky*7a2hdExh#y@E>;u*sS}_Y+^Vtfq#=UC@P6xSKQln6iu@3w

    s83W8ncONK7obQ!h_YAl$hq! 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 literal 0 HcmV?d00001 diff --git a/docs/static/img/debugger/7-break.png b/docs/static/img/debugger/7-break.png new file mode 100644 index 0000000000000000000000000000000000000000..aca5121d722eba6a117f9400ec310103eb6e0f0f GIT binary patch literal 627627 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

  • 9}eY&+mwC?=ZXk0qV`9zn%e4CoF}ozA#P z%Z$=EcS+9QW<*)7a%!Wa3OsI~?Vr|~e=foff;C3Bw~wH9eHuLnK&GkoMg33TnC-7dMBjj6VQp_>svX@9Wv6<fhyhG$qSX~fiV_kU zy7TT-4B9csMzKO$1lLEXO!&CLa#`;%G22>}*;<_%W3T@fTR{h6_wwgzpO`-gX3CkC z10Ar0479#1%OqM%WHJ&p=KTp0II7FN8cAYOBZ-{2-CwAJVz=K5DTBA+v9+=%FC_&u zQAE4Wmn-?N4+lBEvH8lH^JTj6=aGAY) zWP#Q8_pVP`coYhL$;7tLPKmwylDDlp4%>aRQ51@t09p92+jan;{3aT0 zmFwx&JW$`?++hGUXZ!$#utIb|x%2g&Wz;}~RtxJ?6z&IqaLx2*C8KQlK7f9r{1NS9 zJU-+6ZVv!uu12)eorwjaL7qJBO7ahVcXMos9;)+46i6-NH4hfd3q_6kUUY2fsMQqX z)tK6OdD-2q_}uqU5Wv@#Fx82mTU`)>drSJZ=WvGUq}P^UTZzB`ZF3iGT#mm_K&bb; zzYP|rAv7QaHMG4Fi=c^0T*(#Ag0`4HF_PzFKm;FBr44_7eIv=ZZ{yNCSP^7c+CiZf z0QWj}qV*3w5)l$a0N*Q14ui({6PW()`N$Q4!<(0mE8B27g^kl(zvBu#f^7&#=X?=$ zN4ws)caJN>j%udtoUEs#{n1ELY-7TdsM#{{<1Lf>~tZk>MW2~|DcMs@V^dDi58#5zu>D%0THkL^g z$_P2!^4X9vO+3ery(wX1wZ&)5shYaD-T$>1a9C9T`3h#p<&|8v3cxIo#ul=Vni$%d z!u8@X!NO<12tl!-gGi7DjcU^tDIk$o1?hxK6PxM4WEi)6p+eo{N&A@`N*r{A%$tS> zzKrJYOtG;HvJI-DgkPy_?h?U{_x3&uaO2Gj17rqsJFg%#LigP{XBchU($yi4-X=dq zbLa_R03^=t*&x7~EV?f65MB4;>7?gA+@V;{BD^jn9$w)%zC;Vvj!Gm3&fVHbD~c64 z6E&8d5~Ih5Rkkzwh*6&K_n-I{2USS^Ap3wgd#A{M@6p00Q4CK#s3vQOCrVXK0>CU# zehJt8w}yIy8BlZnHIu`10=V0vPQ+A$;~dU+;|_*k>K(3UIfJ8&rhkHEYddoj(iM&>wJ2St6}f zDBhuv`z2yF5KY0P-Rcqij1Xxc_BSRu%n5@jDy+v=d&R1U?{m?s)X$bw-f}SqPy&NuMFm=l%^aXk7hU6F&z4K zu_X^Yy*0KcSvtq*a{Rf{LWL#q!G02!@_1rWX!Qr3Yz76{FC)yPPLo!pEjw6_B~8l` z7IJ$&VH?VM1MozkvkD$YP{g8EO9TNv{=z0Nl0moOV2n@e-t=`IwXkp+-}>DFmmL@9 zeDHy!zu*rz#O5(1gk3EpXJ+Kzb=r4@kFFn3F_%O$K)8X_cOEZNR3n#&RAZ#%B9P+*LU4PHo znD~F%srQ9zk-OIYxjvdEl8DJwuziNTWxq~c2qX!qi=TyQ2#%(oXjW8oI4U1;U-V^3 zDeWmHD zL$0zbZ04(qdz73G(qIj14U+#Y1~3un<_Q@MSSy^zY$sQwm@D;s#pMH7qGSe*nMW<_`* zVdtJn4PJy~hLR}V^vX7csykJECn<- z`>d%cE9rBUqHmBtxf3uAXRL{5_A7qE>=u5@z2@emIFcPO5PVUap@Cm)tHaal%4o{m zF2&A#EDXpdpgzEaWJCM1yw3V4X)k|#I9>K_PHSqZ+y9e}bF-!ayX zvfCTUp7a>izKB-kCin+1m;Z0l$HT@n$$TmW5B{A2f#l;w+mKQB`=a5+gct$xUPOk- zPA3Z;POgq`QIKGaGUvzf?e{UNo5&O~*IK#fjUXTdF{;K-GS9S7ILFGxaWUMWB^?s6 z^JXEH2x!#-OI+L>;>6oZ1bDch=g%B*YN|@9%fXIDYZb1ZR}x`0qsSfrMpT7Kl7VsV8 zMXu^6kKuT-YR$1~d0OXHowvo1a3AYOBVbKU&D$l{+t@iI!Q5*nx-L?0E9ec3?GZ;u zzw+dpdDt;%Z}Eo7@W)9R4V=VyaHN|tlS?}$L)O!se#_%7oy!S~oZOo}^MDl%hH*!` z4ivNZ_qlaP?ROnrW;+bHpg#pOyRL_s1PGp3v&mL4T8OQKtXOuti_e1R9DSIm{vb%E zr<4;|xmYY`yyJLiiSsTNQ&k>~eGlu&?eedi78DGI0U7xrkY8pRsJrJ(`tG2r(!F*V z-5V|^$wqyG;KS8s{LBAzMa5%GEmPk1?RRdA??R-nj3pft-J4l)70&T6Qx#P@>a@xD zIx*SfKL}8ltY~s+3_}@3^wh$#F(0j$kNLK#BBOFEkDX!rwEyJec(RVr*ZY%F^d+ zzuH&19f~^2`xaQ#L=F0ZJ4bQzFK|9Q*oHC~JW(P!ULn?)kD7W}$D$k<{QX14M!fa6 zy^SpIV%<(l(0Ut?mX&=+txa4CI&G+C8$aXgpX4a8xnGxSbZf*v7E_li_GD;mq?!*> zrPDDmKW%b(qgOC9B>^N1V}obeA3QZo6Tp4_3k>0P6gr6&uNK%en4I)qwJn~G$1)geZ@~3 zta{rSw5Qgnr;(A5x8ENN!DJZ{XUa8jf4Z5)40MH+(PDNNLvUFi%j#){-dd3*b54eoLCzakUrzu)g7KVB1KWraHnYWAE*JQM zwAJoXpL?86kFKn*m+N2sj_225h%8#1?`T*7naTA=OOvavO$Gz^H)J!b^%`g~*)!&K zaaF};VV0TleD(mZTS{;uGHBgsTYZP*V5VaL@h}<3fkyyG+`szN^a7RTI6clXozkSs ze)fB9CN4P4@uP}3MaO1r{OO|@jqI?WOte!ACUYmzPV)R+BS(QwbuquABUdTT+kP(e z^aA&u-qv&dB)m*JI+kPQaoil|$fYvCM z2fBQPbH1=h%ZO(gu4@#3nkA#k1-sgCmeK7NN6}@d`#D1IWuVBZ?E#>%cw+8$Eg8hU zp9m>1rH3r%+*`S~#$l{;L_zw!Tg#DRJ$~Q8+$2gYKOz+e2FL=|0f*mD#n$px%_g*vR z#qdysFSi~S9Cd;xa@USAm{j@?+q2K8c&BfVlw$@QL=CvTUXW8*afF5a_4y2PO^U(> zK*KSWeG*hpocAH%a19=>aN?7LI3a*j0c7~E<4=~)>NPu?lK0)`x_!Pni>~IY8|$;Z zC|b+!pUxeUT%O&tIu+bMoO_b~AZ!SLx}aQnl#)iSK5i@g?#D!jTD7@{ewtNw8sPsS zKAB<%lm%&ISSn?2p|Wvd*D}~0f3NFF+c7y|;qa2#aAm^$eFp8tI{6Z|lA5g(vLIVA z4V@COSf4+)bv%U4lL7v-ln%UnTJ+KWRX5H>N?BT*nB^>HcSX_m%G`a_P?_L*c{Kzi#s%8zqr#u^VF696;&J$@e{x z(SHG_o5xknNe;~PpxuR_#X)+-aP5padA?$dAqj_>K*5RqJo}~)q$7G7VmK1sX|#M< zO!98k9sjo**hBr9zJir;v4|9$eWOa=L&bK0;#u-Wm^b5G@j88~ictk~gt#F%Tx)}f zN%#$Umo0cFD3b0|{cN&V4B#fYGx?`v==7C1+&na~zNW36exe5RV>zwo4>Ig2^ifTP za7$wrLj|vtPbj2H={|{Q6)s7f(F7H2oHQ1_p^1vnVYDn5?~@{(;K0EItg26*Gl+PP zs2chmf@|pm?01?d^y1d6E>lGHD860W|<< ztnIwSJPlcuEFeNEj~~UDM^>!z3aMUgn#R~R><_~EHc$_Q<{L~k{BHcDRb#A<+V(fn zSwK4v$f_Q_CM1A&!TaboA#Pp2*c*_JCEGHtv&9KfD3L|pUfwXrcWWEY8TB?EugS4F z9s07TwKY^~`F*epHJd(@;fRX$>vsBvyfcp6m{ zjpK?G+gNxhwWSpuF9wWHI3CWTGCVy!0HZjFu^0s;SaQJk?@9lg+TbA>Nz}z)+t&L#IbXI2blYAP zx>ii5xz`Jnz)ClI0qF}tx9zqW)x3a-W};n!ju#@Q=V>)`V++$l*AiLp*PO>|dT;CD zG}-t>wiQ~svx@JYc8wK^Qejt@&+ed)!{PK5g#0bKz`Rwqa{<8-Va;KWD0*Ut4g2$D zboYseW!Xa2E1t)-<4f$+jlYj-MXc^8BJe2Hdy!Sp&PH6#x0NG5mV&yzm)01PfrHer zaihoe=8*;0$%W(aQiI_;GSsHbd`dfk^MFNLR^lca>Cq$hmT_Q<%2Ir^;avk%G59&p zIrAvi8^ABPNxuV#)*d6zneE}j;>UDd7XwSvCi!KL%BQ$T$M|K=YMM9>#A(iz4QV0V z=B(M;RBqW|5SFv7T`@S<#eDlB29MZXND>Wj&47eJeM2oXVJ|httg$#k98sn7@%{xo z4@@RmjrYk;EHw%CQ2M9}eygw~)|Cs+CgVra?>KDzL*G+%v;besNa8Kgd7}1}e9y1L zX%$S-z{8b10&tkKzqe%w;D)`7Mo>AnvF79o>})UShrhJX4t9G(6HXp;kxWzI_389B zLJiuYMp)+jM0(Q55>=erjgyU<!JrwtV=bjB*W06jGDRsRH^6mW7%%i)6#rt4kRQB!M?D?#Gl1XP zZ1-7WbJ(q&ywzK3pp{#(EoPh?y?womFR1{)r}D-7Q+ZSS#(B-EH%Y!82Yuteq5*pq5KdhP449%7`= zKF*KWlJLK<(S=i&VSkY(1EfH;boM?8Uq{$LA_ES&`E<63M&mSB0o*|ktW?}Ur0*B| zjl*g2yHaO}1llhIltUaHA7YN*gd%oQHQ|8&a2)y>Lt^~x{U}en0D5I>D}`dN z==fOo#^%`|iq8A}w`bV0{;Zt7eX7N9rJ@PQUEbodsvH8Yvm$$M{T;OZR4Na_%fw7b z;F^}xz|hdZAbR*n_Wq{hb8zWh&z_#vp8vIr3w_wdy9d>O00AMrP(G%!S5zs@yiWQE zE2~;0TKb6bl#_k=PIu%aqFwy!0$P)qW;pFwNMh3ceJsna+8O9i9dCsyEsS(BkX@f( z@<~4l^F|6!FQq+ThaslGQE&HtxGXhWEC>!D7yxm~L+uX@3Y?60O)V|?20a*ugSl@M z>;5e<{l_cW71HwHh*)~M^XZ4;8N6qGb7E@ZBWJ5#@tZ*nW$E-6yWjY*n^BLm@6D&golJVNa}L9lPAQM&XtCRh$q~pze_k>K3+k!{ zeqUVlEI;#v!BFhQq|xXO-fPZ0cH9xK;4WN}L=PH%I$)rT-Ntw`M<^u-_`aF|at+&$ z`S<8HExICgpW@9Fv#X`qYaQp*#QZDkQxJrEh0Eshthx(UPS2Qts*5mmSI5=Bi>P^W z^@mJ2Q0k=0<9rd!qt3!NpO+Q`aLTUmtW>$IG85l3%SJppZP2kha_e>P%cNi7P^wUl zZDO!F5g7k~lz&~udsQD;k8El$^9NbmkoN6+u`?yx87Xr4oLwA@6tBB9^T+MhW$^eb zSpQyn-x@>UNCeA{1D>{tFnZy;hBR_vLc(#XhCPBt;OU-gj&Q+lSrkE}U4+%E8QWM{ zyJ@bu!bc$zK{pah|HioO*hklKwL$8@N&QzZ`3KK0A`z%Cy2}&uB_^n_2!PY;b6fSK z{%4}Rpe2-R_mDqyKLw0dkl&hLY0rEUYXH1f_ZQHPVE7S4KtZ_R2cn6fB;!ZuL@3!k zpQ>?a0*Z+~@C(P8Z+p*>(nrp0^3d&$D8Lfa)mEv??dE}Jrce-Qoqtj&2MF?wCYHkF zw)0BJ2shlimc|mlFO7i`LZ|X{WN?fl`-5<0NwGWX*Fhg}#K1iqDMP=Z&nEy0J^z6P zM4~|l+_@!y^p=_D9@d!QxzkvZ`aJH)Z@ZjTkzcFmPIjvstx78CgONxnKmRULRMDO4 z$+JCWl{x+Q;C5eSxos8p=*c*Kj1+4eDLlsg*2)e*b01tm@r<^|ic`4TmO$fHbc$-_ zXOZuj17E8F-PefLBO9Z!LfB!l_Y}r>YU|z=_Kk-A{g^t#R}4l<(Wo4+q@Xm7GWWj| zv8>ItCy^};C`8V$YL{u=^IySmOm_bQK1Q#n4Y}eGP z)qW+rGrUh{vPrJTf2O-rNXMTpVYpcH%+7nxCpgx3%xZbZe9){yFs-$Q`s-Gx0V5)b zvj(l%T7i%X9TD8CA^_C!4@-MJ8)ON@PsS5j+LNj#Foj3T`}frx*sUJW>*%N*r&>o+ zy-eo}RPvr|zCx0ifUXz20dzL35# z6vqF=H1U4^*u3kNt>j9GLQslKRg$t{iKP)Op3QR|?z;3!*BSc@DdiJ-5y4^UyMDC< z0huML#XpOZ3)SW+a}|L^6fvxQu-2E zUm@ZQM%>lwEqTHS{KSUU3x3+~UUeVUh3GAcq8=TjRP=M@S&MNIY=38*irYP$HZi>n z)=#BKm}dO~_1xrf{Zl$9#h(!Rh2;?qnK~v?*5o;#2mN(}PcGPIFJPI1iw9bPVY5w3ACc?OjpdG4Pq_mCW5Pp(~? zw@5H3)VXwPz;bH~NWumNa9Z-Oc*j$ld|^)!_guLprijr!s@d?K#~$6)7Q9HtOOR#* zRRjm?hwS4J^fHA@x(YAD5j{J_#a79}`2cetvAuG6Q7BhMU)i+tjJfgj7%}w8Dqm=T zy-E-i^MUvKYroe(3Lox>3^fQPU{Ng003bo+!aAX{`H4Ly|5kx`Kx%|XRzE-MuRhtnBo>686x{MP z9394GW7Jycz?G<;3JUuhjhF2xzuhe6c~}8ZwOoyxY`V9`*S7mNosMTHho1;T?IqjWdyOI}f zg#7RbUCA?r*9^a3GKI$$TDI}u&{@ZRFv-7zgIM^3C_E3mWAxR1o724;Ev8;@R<@v) z{MNzR9$@~6Y`woso7*LOEIs%oc}$JeV#)L&FSHLpoo|&D-YQT#GYdaer?&jT_Czyc z3Uk0eo}qa`XTxpW>^j}Ci#9$U;bpt&2&ql4`h79UAR(84K$5LZDcW}R7gzMInIhg@scbwmUDEv3yV6rSwpUV-@9RD2aqI0zibK{5!T<75Hb|p1Sb{C4@g^Ov!-|fCiRuU?>}0*mVYNG0i{8c)>xSO#@B@x6 ztcWFO{jZ9G26Ky$7^NoeVhpS~{$@U>4tw|bPfb;!T&L$q}FY7sP{ zM}tfmN{3q=FPr7JZkAX!)+$Ry#h?^>D`Su>BS+V`ydJTLCM0)V&jE6wjaEpo4wXZg z4kZkdft-c&0bUK!N{h_9W-Cjc9XA2=sOW#%cktW)1!jw={Tq?{|5<+jr|Q;7KFY0q zb||)nyjayL$x_>QeuTm>VDlzNc3DCk%ci zM2E&*LHNT*+bNhw+zq!b+qJ~vzGGgqY(R$ExvF)E*zSD(^@aNblE;ieZfGF=r4%$4|@R^NKL9tF>r%c%vLiX;)eeh$HnCXlTcgYxX!vPGP#xX>cb< z5mHFyu*I#(r|znwnwNeuUoE2GbhI8FK5SlT6m=ICvk0ee@}-PsaYy~GQ79~!SueF; z))(_wLSnr_sT|GB{}+%Wu{>9gX))8j1>Iji5=MDf*^h~#+UkiTrgp?(qI1r55P2QrBy znFK?r??mg+f3y>Q%O59TAT}dyO%GxEb~n*TO(lUZG5V!as0@N`vIFopNSV4CQY&Rs{~2|x=uMl(ssO)h26!l z20-vFKiMuY?8cIkLuV=UE|NJ%pb?r%~VGm{P17E#d~U47%gI$u^^-Zz70HTyd- zK;CKyev&>?QWH7`FP$Dm()D)*B{qt(PQR42;ZnSOL+bgJaA-y?yw7U6vSmehDF*FG z-uqo&5ORVX!;l*A`)O%ad0bOBVwFC|1{JvU29XRdBp2K;YT19Vvn^<57a^_~U`NMq zuMihD&SIh&H>BHXytc6G&=0-3cYPw$s9^#s@6@D!gzxYL!>B#Eyn_MvmYvE^4A6Q+ z@F3^v)_s#vg*Rf#BwyUzoz76#0@YBGu1|-K zMJ{-&l&h+2(Np6|@?H-$YTFagVbmL&#idOo02}{IeS{V~c#7<2c9bHHqACR3sg7|_ zVEml2C;b`JY^vECnCup+c{$K23b6v!airA0-qLb4l~Bt^jpciVA=Hn zN9cD(iiTcD1f7}Iz`eZM9fqTOR-zzy1Omn4go(>6dP^Pf5uTGW+#&sD@xB#wO_j?F z-lqYnVEHwqJ@R9 zM-R-hM&)ca&!*fVb}y}fP7F3r{2%<*mth|f6Lf$7gR=jhiu=zjdO`bvNJo^#n_R^m zOdg8mUMT&fw*IZzImHs2xV=f<&nfL+=D}pt)#M#azd`%aGFI zrDswEfYKTHgcR9pG=$toycex{I;g|9Qu!|3SFD`BCeLIj9!fFYE|$){cq2`{2VlN{ zk&2P~=1#`4s-ww%Zo5MMn)-;nCZ=^?Ht`W6hRN<13BY}S8?ezq1)Mh0&k4yanQ_Z0 z%ZL&=0w^R3{-p^3l>72{FFRtRc&w*YUSKJwORvCG(QG!|tN$~Yp8x5vbz1-7;Cv%v zxSgfp4?VwuTFityu*{3_g=ck;cotueC!ru2hcgqDA1vRVG9vq^`rY*6IjpI4eex03 zUPp6=n?X0D<4uM|a!zqWs8A#aU7u;o31-%XF$LyMb6I&<@Y&`9;OinC;A@k+qWddr zD{W-Q4S1&Z3VD3rk{;lX;;3t6_!C24;4q29Pa2YkvLjqgEL8qj?ZK81E+ZGUj|$qANB0mS1-R^{IEOUuF^t?xYAtbF7Q;nAc-DzJ8DS*D!t0-xh^T3y~f2! zB;qD(uNXTNIm!`$Xc^Mg%QI)Qimmr6j_QUo{E9~^B?^25=7MvNWe#RBAE!;@;<+y}$k4d+x(O ztcS^5W4z|+T#1RXpOMHH zo+%{q)Az;~M^oG*$1%pcIaCWsm%Ztz_42%A=FQH(H2gphRH|9LewlyTSvV4;mL;jW zkv%njQVtAT5}iAz+%^RU#Skf={DQyiU8gBAs6y#Wbg(EXOLTULocq!*b4nZ}oTatr z{0Lv0sxt6#mt@{vJXOB;{@&%6W{@0c!f$_lKywT=v^3J@T7W|P0xqqm;SAGh;d?@5t6PQmYeko9;o4oYtJo~=CsWYRhGI7(h;#>Gw_RBr2v~nh#Umb!wt)Cm-z^D4Y{f&Fnl3kJ|yy2*)-E_q#wO-J#qV&aaE` zr*j8m#0r(Sp2F;JfQ^fyJU@OQ}srtWV$L#wQKeRcZAxsFNhzJ{ed z{}MZ=-Xc-f7ljDVvQF1n8($SKIrRq9E4AE1rE|feclHon1P|!y(a1Y*|A@)MJN0A9 zpCZy%1p75b{49qKkBl@~>eU6pZ(ZxkYSL`2f=~3iIkfIvAFAvc`e&Tp#_MA!F zCvs)~tZ2g@>I)BzF3yqex&9H)_BEWgp+s7*;lOsuouuVfG+KtnGrvr+f;SFMcgPwc zQ!4*0OLtuB_X*;Py-hI^&o4|&U{Tl+Di1lwQ<0u=zoKEsx0G*LliEtK&V1cA`E0M8 z1adNyW$B;J9jb&>p(2Jv{_4)@YDonPm|j~)QCs*?tB`fRZ0jyIiw%^K{-zD+Gc`^n z+*8&f9B-fjgHV8Ksm6mj`o4%qX}YT1y*ZK7P_BaS%>oIRPH;d%-qBtotXe*U#JOyiuK7i4c~x!?aL)JyuCX5*f?Ms z`OlR5mFBTqqK9wlrq8i*jG8_5#=OhBfgVl|Z=rBcwR~HX$I-<TplTa1s#qNg|y;>F2-}>L$ zEtGZHiDzS{gp7YxU>aZ^vJUPSfA*VyxEu(o}%{;wB`AX%?%WQkjDJ zYjUdXv>Rvs7lRTib?dIJ)!)?dBo8hYlGVLL%#988c2KU&-~+?15J_oc_3*YPlhlKT z8L8$tf;g2$)eP29cD>w;SPDG4T zYprc80uea6%9lXD{IHgPUl=Hv?=R1V8i+`mU6HvL1Jf9)6=Sd0!pS@}?onr$n-Y|6 zTRc@0%%#_=Y$p*5eXy7|h^3%ndNt>4P7Q1J%8qBG=q*M@Z10^V`>y2afiO^_q+hSdwV7qfJVyuVkKbWINVug%n{EVHa$EEv3 zpH-HRb*xIzn!G?7TPc81-1}9^g_`*==l5NxYcQa;e)te8nUd3P8VHT}?Qy)fO zwap-3Uo>Vbh@&WyM=XL3sr;xrgJscQr;c8ff{SZ-Lty z{p~|C{(vs8JS@yzT2vJ3MJ>TL?U-sa;Gvke0!D0{;%zj)o#|UU2SHtUpV6ZmV)&iP;V(PiIDX{G7RGU^y=7a+- zBx7E9AphlalfoL7^So6ANq6ZX*#x+O1L%hKC!DQ$F;*?Mc{a3?UPo+moTB?i@~fev z&4EI@`Na4{`;xy8>2Y7A*ld4~#S-Xc8CjW?>E;JS$2+$e%VXwe|GoeJ+GIr#u*Kah z%XttQC~9Vy?CKV|A;gSK(uysdb zft~J>AFg?{$SdgL{o{z}9j8KGX3I%=2XsxL_yxp=`Fo@)&6WtXa_RUdMCM2&NUwA= z*w?8LhC^Q?Sk*Nl``HrlHK|p5krLSJ7T5KfE!I+I^29e<*9ML{o4CU#68gegC9aLI zDNUcpfY`9^(EA>AH;;%)Fa5Bptr-Rzp78P8?}|C@ZdtJ?DE$Wspms>|Kya$K5GoJJ ziVS%g{!bqIFB<+~C|)ImfoBjLQe4bCP7 z?{)((aMAUm7_XoRTC8!aYRb5l3*+*^0k+XYJ>31Q2dem;Be(5Bbq9P;`o$2m@{?Te z)s+{r<3Rmu=M=hsVPl9=)&;V`3c%_%5iOVPauvq+0x6=3^^Y$@(W~P^(pNMY%U^(; zBrxSY;rP!r*Wv+(Q7TADM&AZS#QPf&0k$`6i^gl#*7{*#$KC?6EDc-k#co~ALiZ%e0AzUj95uSf2B6e3U-h^n4?PYE30&Wl52J&dnHk})x0_O0 zvi#3wt=hw_PW9;M)gMW5F{gz4W-Vp;(ZZTC@E+eqL}GiBzme;53J zJSyqJF#HM`k(}QTWpCCsB%x@}ZlNYb#;d|)4Sl-#M!T@Iw6F-ZmnFt$Wm zA?}1jTUsPwd=D+LldPIJ*d%VLiJ!2mX8IBQ_e9>l3O@c%9{KkLWTD|duFr$>e_7ur zV;cIMC$=;9JZZu%NXHn?S>L|RC%S6}MQNs)7*nR2&;*;4SjLJ;rcaUsnsS`ndb^Mm zcWbza(sh3l_v1%=8cuPdZ%^&hl z!%r0BpggY8~~rU;F~!ayu{}u$|)ac4vxKWV8Ck zK|v^)kG0~{-T4|S1Vm{C$grA7z;!7#!V{80>cMdg+<%dZztcE>78DDpZ2OYNk9UxtI-8ST*jKd3*v z^{Bxc8{Y$5x2{x*{}1E!@BetnfY8iLVmXx$XtaAQR4gpMkcP4{tb1V?Vza?_IK{pQ zm5FRAI$$fqal^L2A!N<$TFI|~ z$b*es00|A6$A$m)StGh-rN97}#`F&m~P;B&9H3v_Z#V8pF&xd)l_JpJ&lch%f?}VjQ z8|^Z}*mzkc*W^cQI9Y#K%w7mr;y_3-t7!oi+7VC;IPn0j7rO*X+3CUjzymSr8i;>Z zn8_e{L72JV#_RaEtefyCDNmm&5dsPWjhK+}@3uAk1?@7>nC8h7@P5h>MQ~{7&l`m3 z`=zTbG-g)L*pn!_P4^vaeAyRMCHL2J9wrC(5`gF6_#`&vBZ7!Wsc9AOFc|TJD|3MY zpqwN>&8Am#pUFePB0ISJZZeM~vIHS*0eMXKr*?FIcd7wZk4j)cI^chiW*U;M>1HP7 zRM$k%PB9;b$=GBjuh$@+K*ej&Y&8KTvFUgEp8Mbz#4W2O=%uDeqZ%3}J!LgdNV0OX ze*^YYa%`!mI%JVnGBka0MX~gcfTgg(HiT*Q)OH!=O-{!<|_-{d7O>UO;s{DuU(~Y%2`1P&iU|ey|5nh=2q5g|EtsIFh0JC!*-hsNjrn+v`yC0B5AH9;Um@WBxs*>?e(@DbU>XP|) zXPV}p44L()Q`_6gxdGD>#9E{9x1uHR$fwK4*<)D_2iZ@T0Yt&`=*&B-Y!KFR!?}4H z%ChE=I0w8Zs@>{&vI$L+If=^%ppsm8d%XBQv^MJ6eWH{&B9~t~BqonY| z8{vTU$1RY+)S@y~^c#Mcg1>&5TOOw|fDA;5IGa4v^n;NQpQ0DUjejPXuJIN_LQ28` zKd&te!oBNDF6+FjXX(?)_cRN})gF6FL~ku=%h=@6*{6kK^Iptw-;RIZ=Q2pM{MmR1 zj-$H~8nz|;&uoSER`jnCG>uPte$y;8l?&Q^*205FMDW`Ucp~(j;qnFrm4TJ5k5**v zRtY1>oFx@tLUOcaFIKd76*CPDy5xEV`TymO0o(+KLo|tSdwYvaKuBn&_R`d^_;jN; z=FvAidGrmJ@)zI9%R{0_MyT=d-Ykz7DeKg1U%XrTRcrT>mYaLBVksg*ak^$FYvtVw zt(%S8`(d@EjXz{m)2=Z#xNggVs7M?haH#);*d*IN zq3{G4{l0?44IYJ~4nLpUs~1tT>yu((G~2<4Fsx!|;!hN!;#NFD8n$in(Y(?KgJ2Oa ziU7X=$Cs&JX@3}1?>_K(;P0|evk|-WRN&kB*;5+9cig<{+tZ0&_8!4(L@+txdKR0Z zazXv|wovqO>8+{=gZpd-^dfD*#AYcEJ~uwd!=+SVVJYwKV){*liD`8pYCR1!^an1k zdcWY07fD!^_4UJ})$ch|3MK%a^^~V!W=3O< zG?~cOT*7EWcWrrtOJYA(^U?t71QEh-_Q|-FF*X{O?05IBBIYU}K{ol*C+wRGY8PFL z{v;(NdXJ*v2DdrmgT)SwZ-xo@OjssZWUH7rQYMoyFD3Al_qR8vTLHuQ(Vhl!ChcL= zoOR>+b@qEJIg#(Aji6A&2qX1dl+0;sZAG>{RX!db=+Lb(X|{Mi!~8`~6%r_;g8&CN|s zM+af9?jX$f=EL@6m-6n#q#l#ar0GIO*o(H=8a)NUEL!Qm;8DwB2+cGktxMP!3rBdt zA)$RVy}gRddodZ%kE2pTL(Ik}W%NFTK5iczUG*@UTOgt+T0U~xn&WLMFA`D*28 zC|hfNVyxJNsd?)36?+zysZ%nws4y3Ssu>^WNbBB(II>mgEQYk0vm=)ePG z{ORONDqKrT1W#~Sm>hZs5V8lPrS*qWeH@l()=^flV(`8(bHS)^`3-)hNg6pw*41i- z^T>1YlCG}oNGOp*@t$HohCIZ*e!wWhc%;X&s=D)YXlb#EU@(G+ z2y`+>*X9YnLBUW_?qxUR%moAFx71Wr3b?T zJ%EPxb61yQ04#GBQw4)j6c#djOnindlD8C{A2`rf8@*ghUB$)&{|J-&^dXD3_ARQt zyY$IPB+!fzuNwIxg%|wsBf-IafvVXfV&JCRvmXxx60ON-W%a9gZ54(wJwHE@0@0J9 zLwp)B=?ns+Dg|r5JdK&T#b(E9*Vnu+eP+(i6j7u+?+q-{BNgs1O}T7mjtfk@kB+z@ z_VamI?I_Jsva+1)M)#5XyU6bX;fur$YWkpwsDRaj6Yw1+3w-BwJy{NC3)F8uBLQhONkY@zAyk;q>H)(I}#Iu1gg*=HX+z0#dh>m%Oq zK~o=blDb$|=Jq-$U1P^In_mB9vW@BS(@o8>!l{GxA8u8OKtSEbL*dgb*SAi$Kj1Gf z*6Kvc4mCb}y-`ej?_zAZ%HVvEnV}C3(Y)?P)!Jus4g%mNDxCK*FGk19%F4_aXPx-~ z*Do%P8;8`2ousval$2>EO#3=}H!7E_L|Xb=63Nlkpe1Z#LbHDu{J`qlT|rq_B!i(2 zfzG*IpB~AY9ENV1%gZN42@43kgqVNgVIJK@%aXIyo-(qC9d2bvd_FUX!`JH;9$o~k ziZPaSy8Asg39I4njm~+73m=IzJgi4>^ScG*YjH8ob9bUtyM(S0H8DonBWUy+c%Ieg zPSo!M;gLKx`q47+QF9?D-db(VnRnJ4W5-SFeu45OVJ1dZ7JwS$Ei?p zxXq_)gZr2_JdNh51Q~AjRjR)|zCYe5*QumZP5Ti$c82?m>(0t5Jf-a8$JL|`e$&Bk>qnWTx($W&5 z@3BC_xP@0fG%agq$H7xlM1+Ji3WnF1OBFZ>DAl`AQ1A-pVUHr?Ykd~0F%TsPdgqQfOC3z9msj6GUXwvQ^X07W}JC<;On+zR~rSXg_R4 z9TgLoQb?WzJ<@q)PLsN0CD;24yGi(xAy>2G6a7(t@J(qBp$fHNH6;;;uhPp5ylbS- zq#!VGQ~VGsR6)A$*Q~$4P-xTinE!Wn;?eNua+B?*Jm|DflD;d8k~hC52E`0Tn^W6< zq0m)CqI$Ol+8Qs4|4fh|;!Y8)ZDjBTz&yNwm=7`(p~!E5AYh~hHoz+m!R%+of@=F| zM)^k7&c>S*ymELxinB$ss4BlbzzJn5A#umr)&y?rR)-05+_I2h6|m8(0+(;v(m>=GZpuizjLUXy z(20|t314IjQb~B-NVt*1ljk!mm*-K6Y98=-QHde1VLQ2?Af7tE&u}uIW{qw+tfp|D{Bd)I# zI{uPx=!4Hax>{@sGf0p|yaX7d@|#>%Ma8t))ipE3Qq5s&u_hO=*EzZ;UCmD&$vSY zL!h`a_M+v}N9XUU_GAUguZO`)p@1d_%if`>kW7xN#a^sgNj8tgb@I0$&b)Qi-* zwc`=bM#rkA*^zDX^8Zt*4j}o+K%l7$RW~vazj2NRq+T{fTDBVk0>VvcAAQy*-37f^ z9OHz?$5qR@Lz!V8HGG*qZ+{~|kylX&O>dm!Ir1vJ2O|zG#AD}zm|u#Xwg{23vX)|i zaL$*-2@hLtj;BAo9QLmlr7*lxzPijUv1+aq_iboR_fU0*1AHiyCkapg5s};HAQ@7` zqj2OtkEvnU)fKl1UnT6b{A57t{-&t{s#ae#!23OXz-!J}=^jgnL*|huTPn!x$;(ke z_f0eewRd@*igB~NRmvYqgDf04g0>uGvTH9a;Y4{P2&Nn7LD&G&acBVNFkr;t-7Dnd zhz&dZwSXzXOa_lz5iY508yHkqY+-b-EWRO@ZqmAaWxCNk%GMwPe!s(ne|gzS$6V%~ zCO+8fD`Ou;_qkIr9Rt5aZ5L9(&<2g!wI$1+Lj96gV`9g)bsv2r#@2)>Y_U@rxqyz2)xdkICMzrb6_Q)a2wzf7L;)4EpbIsk~ z(Mzv@T#z)i$#@GXf_0lDV2w`>@2irn#r5*J(Luv-_o3uv!!V11pDfU>NNpaIug7~o zQLmTVe6wt(+*2k5>ES>MP-7Vg#I*almwSL|k7~!o(nGNoIliC^Tm%7RnPkb^p~zgT z4<2zaC_`)rcXG|&|IUrV@7k&=ErCu4)TfwTUr!)@^_)#ScJfH6i4+`ly|SZ`@>e`} z2gyeP0@V}v9g#fTq-3;1-fR6iKE5?fnLRdj%Cn`kp~l3pw`e~%U>dL9T+Ov<{w*&?l$_r$FN*={r)zQs`7|q?KnM#va)w6kk*CzE&bg#~>ayrpO)vc)3-$wj_k9Ah9yp{+CI6cT>ROXgkh%&a0m`?~@7w z1NOzZf`8XZJRzrPeweJP@R*{;tYi_NE6@zG$Xrk(6T}r%lJxBt0F)Fne$GM(Ud|r# zG-AuTuwMya=_p6f6eRc5UoDgufELx&x%-2htLL9Qq734cE69jjgpI|;8jJZ*C|sLo z1aYi!Ku(U2Ueii%`Yeu>av(e}k@Om0cy_^N4yI>exU4b_FK^TU1jqDUa6o_=IVc7W zP7+Sx`LkEgN=n|m)zi@UVGjS3#~>5^ooXu*>e_&wX+jzVd1P4J7 zKbW8d9rWehc3weC5s<0OS2*fc1n?Ur*f$&57%EiD;Sbq4Ie6l?j^9lr@EO4-Ezby< z^u4@7DlA80@;ecxx27wDlInbAAn~bst)hG7L1_4(DpGWr%?NX3L}tU*WcV)?&g%!Z zli@ElGz?e!aXZ3{o)oBC1_t7@6w06=BX=EMZA!?0rAU}E6p0XOQri_G;?)0%bh9Dq znPX}4>qn#+3O}=<4(yWY$IqW0TVYTCC1#g=gjx=xpyggAg z5|U<}V^Es#-aB)#Dm(r#=c1oKD{QdQK~mdt5UAQgLj$V0vYKdcPWFbh%bVq}C8<xz+mO(;|$Xz-9%OxU5? zh>TdT=j~A(fKtwel0WdV13NX)cu|0rFaVZ+PnUrX+IIM&QpZ$t#0%s*T`zd-lBNE^ zS8yL(eYTn9F$g85fh2sZcaM0euIh@}dUKAgSl4@~8-k@^%IDAKYL!@{V=?3r8B9qMOGs9h ze6;Q9{?hUpQW~C`w?=p1lMUyHJ~p; z-)+SFYa9Xs)Vlen z&sfm`?+Ci&zr;{*CBM`xwj7Aj&^r@`of)Y4)h}_Zj83o-VelIbGFugayxV_TP#c71 zMv~ZCW{w&$WfDHh)i^*bJSU=z9ov2naF7Ek$dSE+voKVI(b`DEzgd`s z=z;DX+RK=_=2alA$2Ho0R~TKO05R?lpX8-P*fwphLa;gf1r)ahePUw9TA#f5H zNWsj)V(+Rqm4t*CjfI6-_!B@*Cf|tzfl6?{@<>v_Punlpw(luHwZORtP^w6uk_Ehf z&&*$?STgUYHEA_qD|Z~hNOTlxOvBGl)!t6`RwFt2_hVY1z(8iVKI8j#Ze=;sV}4rA zg&15N@gk5MWJD{?S;QP9(4vPlQN!n)A&f_b2lDvP93e!A*F=2Yii3T4 zVd0i96{AHN?B=aX5h~F9fM9Jc(_s^aZup}@2?B9iRpak#I;x8;Z@Et<)lX{aR!H3* zB02j#hWYMv3$Cs0**7LHURAk{Lrn78j&OWZ4W~q??(rX~OZpokhlxb!(M_+uQN@ zbB&9H=Q!Hn__H!7;p)uV0twsM(YCuCU{36F<|8x2Exv+P2!jOGd1k0ySyEZb&XEI1Gsk6(%UaZOCe>FN6PTL5I*Ti;dyxC|Z(h6?ahWbTbf3 z_1pu;f7PmK8o&dE2Wo0b7&G4sO#06G)HJN^Yk7vO_?w6{UQPq0MFQgoV8r%8vjY>u z8w)D^c_r>yc9xSM(idHA_om%ktSO6}YPDPIi3b5&mQJiK065h?S?b?5UL?Ja@Ps5* zy`B(ovh}8wwb8K(gZxTav-RE(J_((Y)Ysvt@UV|HB!CNZL0qA^5s9Tl%F~R%jY{rD z)H&)CPLS@oWR=G%57C*l=krEM)!xANTEDM2afR55l7i#;Hs+#H%YxL?d$Y`3F`rac z%7%=8bE-U_=3{Q;h2u{L2N+lcLYugFDIL?&@r$!CZgvQaFgbFqv+74*Oz+>>HrC~# zNDJZ1CSy)(>3+GC68ZWB?)+`URM&8;Fv-|=a07apO$c{}NtA~LQX4i($25j@Uf78S z8!i8<_%Qb%GAZCf_^r_239+{D^nLemo{4QXoR$H;Kz+?9B>f6-oRPEAoYcpj6WX(M$!;#WeumEV8jCpnmPaz@vnSfZ*ury_zlUxAJIsGIz-OO{qCujee&K?)l#4s@ooXI?O zcb0hY%k*acgfkG=G+#Pm zuxr3&=PPC|wt!DP*l0b!@{P2mS!{+BTTPvRyjO@#O6wMt>XOFc;gv2A>e|)sbn)BU zUU&R8oT-}HdBSy6g5n}NnU+rdlch9k?mKbs-d*L9#iVvXnmeJBHpIB+YHhojE?SJ2 zhQQvQelZB*U^HS>!RQ@St*evLR9caD|N6Go6(!e(Be? z$tn*@0wxxg*&G#(t;9yS8*6gg>kzfN$((@T2LD~Xr?pbeadGOqGHMmW)XrsPvJSxv zyDx6I4a8KVa!sV<<(bKCP;aKG>Au7%5T0DW_oYd_J{NTWLby3774%3*0o}}&Yyc*@ zhJB_XhipnzvWm;9r56b_O?x`DT$Pj{Y5ew<*O%m}VQ)lY*s1sDjc; zxxPisGl=WKk$UoJ5S1uZL}a8ZCfQMa*ILi(*2*^`T8QUdb^;$TdfxY7)-t8j|3Iok zkv22SvzG`62tW{Ef2pU}Gpj~nW}G2xtT$aiaOf%1U51bmb9+=KKFPh$YD~VpHlXQc zCHBqDO2c8uHaAx-jCq1?0>&(iK4c3^c;n{g66?Hw*? z2gIK9f{ia$&Oi_2@a4I9*!^5@sSyuhqZk?wx5D&oa8wT~E4b}$_(L+kDWsZzEGo9{ zBzaj@?w6lZaO9X)qC~xNLQ|&yq%rU29?ILR}9R-v$3K&0U*_Lzz4iLme7(m)`p5t1dFx zpU2s|EOhTSja8l{3=ayM`nq#JF6dfCw*QH_lFF& z65otecVswsX0Xes1f{CB$J~}WQpA9ow?*Y?29o-{TDEj7VD4l>)uyV=5Xcfv+-g+K zeGhVP9+192>xScZH!*(!*NBAJ5qcKlGHAu|kl<2#1bf!>il2`UzB`7RodG%b9dB73 zTRl$)+_{*~rOa?;?y)_(g^NzLC875{!GS}fkso(fl@v>fULEH7z8E`0$P9wxBpeIu zWKdi6KmsW74WX6(d@q(Hl?QABMJi3eEjRJ+(pxR@x(-?E270mIzO{W zoAi^6yhrmlUh-Jlff!4^zynX$UjP^cy|U&@%GKhP^Jbah2-2lSbiUoj{j?=|Gig`t z^%2N@aYi0adnamHdv8$o;fhw5rDC}#I*GY~4pL%usWBJInIf~7R`;xM0~r;mKB{_} zQ}Za{P>^(BTiB1Cp{HLh62BRUCRjjxW05YmJ)$o5-eUnza-%^xFpse&DM84315X z>tC2UcXw}u2IL&)EN=I3>nk20e({V={s;V?dm$)p61q0%-#R;scuWLO?t`h@4MI3l z{Q`g#j7Ht`JhH^^jnto_T`)PZyF+CjnW896q+#a!pTwJe9Cx+*C_1+@`kq@w-<6rO ze5OBPq($#8oS}%6Q7u=6@RFUc9Z`|xlW-j)fBjm#yf zx^MSqZVIERg=)<<$+vHL@s-~XjZZTzq736#sJHqix9*43^J2N#o=38`Z7tMCeFH@s z<_?K}V*RcNMlSjo_w6$<{?w@egnY{#1{&y6-U>JMaF5D9;-b>qh7Lg$EJED2ecVKu zm^zBozcrK)^q7isbB8dT`CDewGLXIQ?qM6Ey1JU_hr*s)OBw14WyYB~e|s~puAza7 zj65h~3v0`suVl^XQLWK^e{5KAbtY?Nv6-VzY1G%V#O;}`@d3qUN?WA%WuwvX7EBAW zuT>X4#gn#2lN|mk-W#fea3HgFMus^Njo@E8!WPyey11D}`ZZ!xrC6Ub`f6lZkQ1xC z=NcZl8Cm&PDe-Wt_Ryhrd5NK+`3B2^4J^Z$hJ|I~@Hz-w_3h9v8}@Z_2m8bkxM{>s zVk_RGcJJN#-j9wQp~cAlC~O(ZnVI?_gBIy;Cn+p;1p@n48gq;KIX{?aXyofH*1pfg z2DCirkYeoni|(f*H||TgOIYM5UsBpv8+exX4GuzdIX2gXo-7h~vTF}nI?HGVZ-x!k z3}&nydba!KFdeG#vB^34jDEN8IpO=D!ZH0(M3~%Z(+hw+L}k3k1`Hrj-nALAs~MYn zM$|Zl2BX^sbieuU3X14`Y&S+A`XOx!#gZEra`sEJko+jaKw?tkWYgV?*{aB;DYpH1 zST`$)WceMCG#;tw+58a^!RnrQ>VboMdw;_@UAYETk_7;P!|znlw=XpaXp_IHMk67b zcIzXQ>an$;wA>!cox^Mwog%;bEf9j2qfn-dBtpgJSeL2~>sou`TROZp-9oFYpMT$h z!RRhmBjcUP=ocDl!j!n0BnPECe0i4z&>wThWd?IrT8;FOWYMdq=j8fc4@V<%OBEMH zIX|9_2{QZ@KXHr%vitMxleVnce@gFn>)s-wf?}P0lsWqSpQ4Q7#V2m}sG_tFyy$%9 z7x(sdxcWj)N{UzX;g)hYuk6z4I*gjXy*)&ttx)|A2vXO|fOsz&qN##uvH0L%BD<^A zJ`^TxvPg}qDs~yOqYIikb>?k_Y+D#<;fS8ORF7EGJ{)Bu2U_?QguG&tm?OpNyT}e> zT7zWkkt&PT1*f-kn~mEL?W2d~`3pPX_nia=T+MZ?5+ER?3-7**OA0|dgP>_fu1_<2E-PK*AUp}(F@e?9MKvM~)qDdzmcVV^P* zuFZHFBemFfiJL9E*beYjRD1l?Jg4U;0o|d^T#xets+2Yh3@Z_Eh8ual@*LV>V1Qlf z)YI~7-!!tDH5$i+_hI|#8<32msUXBNskyqLtj!>}{H>^)5>dHF;{Qs1n<1!Th{( zV(1F25|7M22RB!^{wbMuV*Kis7rB^YDjMt=zF$8Un$ww2i9-cBAt4)yZOU)rrrMky zKi>SZ7-LOHD>ces)Wxu#qrN`E9N0xdN^zz-?ds}UwgC^rZv&1c(fgrL8#PAqH*s5r`_sU~C!U_K`{2Qj8^z<=b5g8Hn*7VJviA1(i0`H}Tg3z2wHYlN-cN&~ChS~1S_*Ht$+((bo{wN01n3)RQ+k*Q z4q<=}XITf~R&N`E1GC%PecZIAV9iK~o|*3ENNiXosyOh%_{us9+UA&_C|f;avzwPv ztUwn+WG_U#6Dn^~!q<+w#RmG%-)YDaEG~=4uY1b*F}rpoTanQnUQn}{I-d8NT-^2S zZ}wl~fZzA{=0uH0wZ7o=;cu4+TJxQqsKOC%TotnF{?u~qBi9CH8k!P(iw8dRt~dff z9Gd;ylR3B0mgok>s*a4CZA3xma}Sw1t-Y1z%BkKRo@AYi%ArNq>vTm{(dL6ZNbL}y zJJ}N-DzOqCDz!ds4g?zn1_UE@sgd{0HMpUl?i6??*IC2)bzo{k;&KF)h-wq#;xO2> zN-UQ{__b%EH=-t|MIqdkUm(M#>R!&Yc!&`0P zTXAL852kNovYq4Y=FU6vRT|f;g#%lR3SLn6T#wu*7}f3)pYN6s-hAEp-Q|@X7@y2^ zI-4u{*UGbMAbFp){)~kq0F-VbNv7CXZGCwduK)gn8jNI5oB*j#93RDuus719$mgY= z_(?x|{&5fXcxx12@5+9o5fK2D;XtO(sewu*=_0H5#NaQ)AXKXT7epW_S`mNz z@;1GK#o=q(2K}RNnNep!(kh;WNaXqE9ckK_#`gz;`0KXZ6DeA;Ii8W$ei3TZ#k2Ut z(rF_`9ui?!<2uNKXF=H}JQ?{(TRZ^ZapKzFv@r2ayh)0~zgKA61tT(BdtU9Ck>zsn zA_%{;q)vOAajIAI8+A{_HNFe1aZ~TgNOHL}pEn%nRj7%=yDR}rxhyLw37)ij-ALJJ z6IGk;wJ(?5uH?X>E`Y*pJY8vq__h*ToHpAewp;u%OYPb5;Jz}%ajX>ksiE$W)WTqs znmu*2?FWi^wt|T`#2a_?DvrXF-47T0zO53!sHbRck%EJqWJBvn933lkFK(Kq3!JS@R^Oe7>vedv@Me^YtsK>|ZXJXJG9 z{YdbVdsaWBdrLmy5!VbO&VXTHkyBs`zI=9(;k&+S`O5XOT+H#Xm6Mr+1w`ThRD^S` zdZ!a+6`s-HYw!nz*lnik5GDUjxq#0Zu|sd+?`=+)#{3c%mD*GuYb+@%k1K$hwRx09 zv!0A*f!Df&OZGi7W3Nkg;f$)E+%uUFeAt~b+K)2GuP(-hKt>xrM<*_)c;+e137b^( zx10|SHLc&fjgkln@#=T+$sUhd(Xbm1HdP~^Ga<{A8CI&U;axy!^W-m$k4J)GvXNNi!H24i@WSxh@NcMm@X%wIn}dzZu?HW7+T(>E_dk}m;45aqUdWPu;q|} z6Tu}#kZkbfZMJ}x1*lht3e(U4S#)T;2XCJ(cSc;lW}R)viHJy)C~vupM7h|(?6+mG zAydD7z@|(j(d?4;G^Zic;k)p&C$!iZFW>Pj9$t`@7`=SS(Eo#$QW{1pRyOow7~P|S z3M3jj?A>Boi2p%JG|X|{5?m%87S$crL$>HY4nHJe)d->XFCZm7%7w!!r}t4Cf@B$n zUGTu{Bc|$c+=p;8C-Q)ScUFRoN5bs!MCHlMDXn)a2M0{mVNr+JK~*Q}R$Yrp*M5=n zM;_U(5F~^~i*3U{^%arDF4OhKhxBJyKqQeTGP6f*+I(#Aq z%kLb`cdi)_@Pr78Hm+VC%}i*jWggsCCB$#q6j8&-st*(TmT=|oJ4J`T0%7t zKIC_`5@(pnChNgA>om{LAnFz@_I=BwCvs2X6Kq! z!O7qXAFR}X-Je{G0nU?tzQ{GkaGBx7Uu%};FP*YbZUGega0ctQ``)NAkd#~Z0h$?_ zeZ5^#M5joI3JOjT0KE7<+ZM!Rd}h7ZPk5{-Ko4}SRw(&-Pjvj~V+-bL(J#FziCQ-w zOk-YBvaz;?zmSL!zRcSAVi=|mTpn67=uFy^ELv&gL)00YY1B!0O@`Y(3Kw|jJ5 z_01LS)b>Wjp4OUEq!UG8`&V9LOP)`W4DU2=R&-dYiM24a&zT9Y4XVi{p6`T(frurC z-kK?1eNUw`;UKRv+``eu9DA40DsZFMI>?h#=hsX@cF@xC9P?wgcv;W+;vGHSv@(sb15c^Wu0KtXO%kKJm|snuAw*ZhYWkY^k86}E)xkpG3ubiGOZX9jk4 zHJ+2Qo@8SNgbW|Y1{hX!v!T}o`HV4&_9L{u(R`d_%u7%FHg3oMJGJ8mCd)4#uDFK= zi(&xVGxC4%{;qO6{4l@U(so3T!fW;g37shPLmHS%?8IN~hiKAoWTPbD;@o+yA!>^Y z!sa_wT*~Ed&)267J~dID6NV4<>P+tXh5!-jYsFNCR`PF0-P~5n1W!n%Quwx3ZZ@I? z2LMosvJ^KlSG}o$Yf+>z?kMM%7>NZY^BY+lOP_LL&mhmLxpF>B4c+eMh=^*#f;b5> zzFa3#2e{PE{VQw47pM6t@ouuF$`PQqmh8p+p8RWeMx^mK+!--(Qx3b&h7S3;xFSgr z*;;77#2xlvEcy>;kw(9o2)F?6^dBZ#6fudTSzJtRS@tYpO(G+XGVjm`2=uk#^X#+n z_IH@oewr!xqUAZL1#Ce{G&C;nI!SH?ol4p|*KD^@92pVT<2}DAWXsR6xCZ$^D8swG zwt$WuUR2$=S@7!@Z^A1-{OpU}>y$icGC5z)5vVXXZry9JdAW^-*Na4$aaIZk>~Kr*3%OMJ_q?r{2-*!bGf`zew$C#!S8RZQEV zG$9kqt529J)T{2WmUdIf~G^U!W}- zo$WJCGa1e~J`~D4H7|EqtT&%^dT|0$DpDb+?CfGtPzW+hrzz1`QUasD6H9H$*CCYj zTDxD{+f)uN_7nGT_e)uThsh%$M>989;$7~LPEJUK9g2U>@!E&D3@EGEl@`%Q4{V>* zyn5kKbidH{z&Oe_v>9g%WqRWPq?C0tstN`J6N-1v6wXD10s~&fccf3X=%F@(&EY0$ z^HuUHJz_{kXB?8Rv8>ym$<2}fC_i&~J&xbj8ZGd_tV%h2X-2IR;(sr&D`1#sp9!SX z1MbcVTMW{LKCNl=FNYf&Z;#_1m%s}qE&0v;Y;JnU6XC3(1wb6=MQDrp0Ck1`1NTDh z{J`le2s8(iz+nrSAi?KmPnc1%Z$uAMumiz9mq3mE@b02T>Rp3pR8Qyg_VvVzM6Xr9 z1n^zu;AH8I@yUjmhTE9ZSwF$Mc@=@@$bW4jc6_YWM|tu&n~vtOKFF8&AHCH7hpev* ziX)2Fgb>_a1`UDW?j9hJAi>=R2<{NvA-DyCJ0Z9`1b1g}cXx-u+0K*NxBIHPieEEb zL-#%RoX;-(?1LGqu&ahhB#9!6#(}RBhbkMjiro)G@8OMJ&ntg^qqy#BgD&vxQh96U zAift}0IQO|_swf^Y#^Wa+D7-m`3NV)pbqfe#6is10Eir*)POByLdHTaKtn;H4Woyf zfXVO*wJBvYhB3ekf~RBq+10MzheZCES1UNozvT$q3r|$Mu-V6oZ+CoJ%Uh;_9C=pk z#kJ-@OO$EA_*JKPj+|Aqqgu0{ZXX$;ZG{3M>lmx`1vKN7VAlZ7mR8ju!do*qby3?T z;Zt=~{d$F66P%%00gR^e8YsA^+2)ktVTgYKEdT|%FDstRuo^FoMP9^?^VC`3$8Nmb zLy3{Cy9k8?P7$*N_55p4qEoaddXGW3=x9ReZ_(O!!&zH+0m38+sI$fdRhYo zHmuoqr1~CCVNCHIN&Wj7&jUH4+|GHDOSgLKIR&e6aU-DugyfA@;efOYDs=wlYSSkt z{s&=JoAfyftJyqOmF2m$L{hLk(9(X)1vC(tS254e9s@P!hS7xmq{1zjFWr%kQC-~% z?aU)6lx4nWqh{bMJJBy+48x?j?YtV5$A-cBucoh8Qv4@h0pC#cw01~dCq8}BFeA^d zjla!(^LFjRBMPv&e#5K!EXHMtgZ`$H!45nKdUq+6YWjC8DzU7#6GXw!Nw?XWOG5l7 zjkrcNm{&#`vSlu|&9qO6GtJZ2=iRVJpIFGnoC{m3)BJ{mEPGArv(;eBxK<47DG$0H!fG2Dp;!#s*r?3mN- zS*-IP+2ra|crZmp8%%@>XiY_G1#0K=RI$IzZ&$Et_Bo_G{~YXTl?o1TVH-$Me7nSp zGS5l^Dv4@2lC?JG%6}M$F_HRa37Pf?PFy8Lv|BD|qc{(4wq)IXs`_CBs>}Y-4k1GB zvtM#^_&LGeV<1>r9y@2#Cf{mIc68<6-oHRa#P+w%Xl>CdzX~Zv^lK@J;nmW@xW&ug z99J(s{fPBarIsz>wPS9T3VDSyi&fJ>RM*G{3E21gq9g+6b9jvX#ak~$S&uQPDT0s` z%{ALj7}2FsdWgI*&MSqV&Lrq&2EY}EskvSOV+<=6Yg)wIB4bv zA)NkmSJpiK;M*4+)~A+7%scC6Opg*0=S%?P{`isI>!s=F(p-K?o=Q0BB}5ogpAzuU7JOcZa`9eT~Yuaqdb0gFF1Lvdth) z+YGXaPb1|UO^%Dvp{Y?5Oz2Y2XcRu!-;WD@_BJ$e+NVmj>#(Cql}v5al9V}rURGhe zY*jG*GEn zF*hapDW=fef=AxcqzE9qdm^C$tm?#`7;|{HIX$AU?#wDD4fs-#qSdb#F}#$l3Ktzf zbM5I0h{BF{4!&A#CHp^fkBsZ1u$D#yoD`5n?VjyxybWBW5AcV$1b1J08!B@*Bv!+v=8r@oKWe^(J;ADv(0FSFbT zJ20`%2g!wPGzj~jw+B6NDt$J&PK2~x^7r&H>hI}$ZhV|?wyM!rYP?KcGVCGh^>p_} zMi@Kd#5!$+TDZ&cFA$#(c+U-3h+HmuGE(yKsgBL!qD5wW$BoSJ`lV~Sv|D$6Yo#z? z(TX&tKi+1{Yly#!`sy-eA7uvCb%2V#!U2MYUnLdV7w}9k*5&E-)RTa^2pKsHP@JxD zv2Lxb^zCK&DxY^P6ex)9BJZR8*9;H9Qw>S2%Z*B){Wg14)>v_?&E)+i zo%3cj@Q+`xn()QcGJF87&AdA17 z#yz7+mt;}K{_Y#KrxUBcY<4S(4i3VT)1cO46h{^oHaq3_Ru0O_siu8=ZRNTb){-{X z5n6{1pH195#h7tnarMH4cyqw-btye7zmbe9z~6&AJ5ik^IED@c1tL2^ALH z<+mP-D{3D|3e3`vqpWDx@?-fv8G&~*X%lW{ae`D27l+HJby>rAy;CaHk%*9k$I!7 zw7mw~c%79Xkx*ld&|9tt7G~z8DX;fTO!0aOpSG@shAi*a1M|GFXDu6XQAFIZkFf{Gc`d zr|T(Go@Q14NMfh3-N%61)Adf=ztlSozgU0VXp}#Z*&_s086yssw7)!EC9ijV(t}LFC|F$waFE zD5R{t@;t(8TVuv$*LhS_Y~@CBF8girn2G)K{SbFHfJna7@Q`5ahE641t%f2H1o4)rpT|~rB z&zm-)#jGhkeSc1z!lU)@|C}V}hD^@8_m(wGLE&uM0R-;=eJt2;UcQYE0I|04xrQ1p zFJM|JTkj`gI7${%Ni?g?GriC@Q4Z$gqh&_`9{R5?Z6Gn~s87cQ^;SC6*RO-J*B<;x zakSCx`U1COa!Q;N2ZkB6lF+qBle6Jh8e7)+4l`@|%1|)6s$pbgw6W}j@1Hi^aeJbZ zJW>2^KH<{h%Ui&q7D&x?_fMu;>^{WBQB++$!R{O#f=%h#6 z?1u08_=s?D;xU)iqYB3PFdFca%U+t|y^Axm8c1>HxNjP0H;ZOCK?tZqy+%A$Dm84Jl$+chYIERESm zYFLQug<}5HuV`JS>k*5~dY?1tk>R%Z+XJP@$Y?X?6RL@N2%ra!+Hn&dWq`H8uEeCd3gNd5$hDb$wNf zPC>loyXd|%P{xh2QoEF0nil6whG$x^1NnHdyovk1ko)L3I0C zn*=B#trnYuMb)FCEn4-Du3n7yoM>$$9z2Zu+dhBkBzBE=odj;9E9z=)*5XiiRy*fD z2kG;8Tvp3`AXZe++%ERfG$)Nuh(O4DvNk-_a?-6cuFY%@)_In%^KHLdZOzZ`DvXfI zPzZp1Ug31^uqRqQdN(>`*5-v7U9*tv#m>dk2}81z>(C|$yQcLY(wUOKj6_82oA36= zKi-qIm`^b9;3igqSAb3k0M%eW;VT1N%Z^{y8^J(egXrneOcrua7DD^>02qqbr|B;q zb*-)AD@E889nyo(rU_Yed(+?I4c|=1vgj=$KXNfJ=wi8o)6~?sOGo8+YC2qaj!cO? z+dVMQNDEtxGkEQg(<~+zM+E2h@HGI^SvwXiHk!5h|G{J|Kz~#j@omal8V{}1to<2@Yuu#D#@>RUPyFix$X@2YH^HY;R1nFfQVaRk6yc*s z?B#D+2IE@NTW@JNacx1u(Uk4(n1mAJz^Kfr!xb9$YAi+3%d z?uyTQ85%}Y!5d}wqC*sWN3v}8!(^BeqPG*0C={(%LQnNyFA`aHWQVQn+6Y-%ty|*X z3U_uK?}_@UCYZ@KRR#@Iv#y2U%ZkoUf3{g5Aoo4>j`Dv;P)Nrb`0E1(YL~y>l5fbC zG=}-1)|W!)!bYIydeDcndgY8Ek1!Zkq5;Tou!>! zA=;{Zp@?Zk+54_8q;3fN^fPW4Ef|Ar9^Os&bhT>Yz#8FD>I>l1pz2eR_+==5Xzt$o z-#k#gqd_dA(hsEM_W(+?Ppo6Cq76sp9q4VQ=V7_r1#$cd5u*LAR3&h(|LZ;lXWAr> zkEGytl_?lp8NcILG{C|Xj(EdC^^|;kAj^3qaHuz-YJNG{D{GMJg$!$xwzFj zpQ|XtrOd_3$7b1FwRfd|H=1tXV)tvdQNwB;0@}ZldOCD!tz^@n4pps~YQ{KUGTePy z@6UC8Dqd1EcpMEsx^v*;yw!l_MO#&9Y3Cn062*jueha>GaIBwb#feIr*y0nFm1E|t z?YDCK^r`85Pi}zLb#n(P9On1$-zoMOWEXS+qGl_N$wq`ZCbQg%AVyJpx25N`&s@t6BL#iMpy>?TBaS;>kxX*D7Z%$%s`&Hfe z{9CvreGl00N3;5Up?&*xb-Rs5{C1=NA%4Ft%|CXp-PWWYOz>J9FEn zU@y&P|Bn%NXu9-vettNqG=or$n*QE+oi0j3?R%XJ|Cbw3tky33WmvZECmfW23~l{u z<6-dz79kdhZA*N1$8|F<8xl8y03Dm({?(v2FwEd>!c;X6Xo`W z>~0}^mGbb*3u~NqJv%c#_QM*x@Nf;RQGs13b4CbeOTkCS1ZdyG_z;6>_20QZI?Z~_ z8+a)KqY_~MMyDZ4<|{-g2T}QmIZFfSi^OoOZAEVVgM3an|9}eeAlheO@JXin>Bcgh z1YYl4aMJa-)xdO|ZnR1+c-3-D%mlCd+ObZ#i6x-wgc6N@w_mw_1dmrbeAN*TjFdoh zfeWV?2QSG0p(ps-HyVkAXr%ntAB0|eG7=HkjxPtjgb za9($Kp z6|zK-hvMR@a3S0u-i&QZMNT`HMt;&$Ak%0t?4y(6I#o3dV8ih(d_3nG169v)NK9@T zO_Wfba&nDGDL`6a3u65lqC-Syb=}i?1uWG)9`$XWOoD0bm=1=_qsH`>GbO z-Npd_*ON=*Wf#OVi&p?b1~G%Yg{Cy~xfj4&FJ-el zoK^ABTD_!4l1rHoP81ZnD5l;D8C|CS!;_ciL7H4(tH98Wif}#j*~Fo!jet8I>}OU? zPSH0}D57nmx_|5u+5R|@J*U<2GL$kv79{3|S$Kv;K!Cv~h~k^&z>OjN6##2Jwla4u zmxS~ns8}~kCB{-eJ}{4l7pz6^H9a+1?>2oroY8?&*om5L-p$NO04o?sWVjR*w9aaO zG0H6IKt<3!L49fJokC?0xItQ&+|S||Ijl5 zenLk_p@Fg@vBpY>I_N?KYHQ zw!x0KaXU`maV&+i>3qSg{0SIv~W8kgS~US_p20zlCPil?$KeYNS{} z82w{2hA78_PP}bXM1}q`ASjA;gjDGb{c!p7bd2N6_cO3hWEZaf@#5CL8|4WNZq6qb ztT{V`0yG3=-P&FF^wir_PpQqsE(FK1zU za=#u@6KrQxCF*JyJDe6(`tl;%N!_e}Peo1J^`2H6$q>P1Oq}`A?|ke?q3f3UCLN4Fr(AOP%W-{G0Y#zSAe zf$nA7gs@y*Wq7m-_*|ch+(C}pi)y$4H>|lmQkZRQhM|2IjC*hK2)BLVc47MeZ3wb? z!aEtvk3%TZ?)`%EEscdr5GJA#`ext4@sUeI3O*zw3OkgxIHWP2JZhcN=*PJ5nk6x{tK`=GdysS~v#7B#d$7A1G&~^QJIJ@y5Jwl&vN=m^z zd-=0o$tjzVPylg8&#;zM@OAWFjadNf$qOt`?vJffgv)HScLU1{Blw!=F}>T{iR)(- zBbyr9+C6U-Z%-HlH@L&C^(ocW)kjR928y6nMNyvafeNnhaBdlW3%Gy4yAS=K;=@t$ zbwFM{W=VPTfqACCI)xo9yxK84MU3E&-(m z8DPC&Th0ps&+{H??TN80tnE&y*<)%F0Y3Y6k)nZe_HxHa@AJ3Y>{n2pBlZHhEO6x<=Rh=%MS8c*tTAJR@ zKVKwvZdX?JU}0bW8OZO`M}`>#Slcw|8)W}i=9~~Uz_dW_xNjE!GqLk;A7^(*>6hN~ zhLZTANr&4m&sPxYQT+u1YFo3_s_!dR3}TGvwq5HDe9V%?S9Aa=d1+nocR%tCc0Wma zGj@1CQIDiKYKlI7H745V&A&XW!QT!Dul~4HWx>h`jnm|yEcU4$kvt}`$2>PMhauu& zHln;7x7wf8%ItT)2=r@wwETwd$-bj$^&isHn%w?IRLxdj-FF^{z1r>MnEw3j(gHl| zAEN=JNw-30nhzds;-iwie97Qt;wj?;>g+eWpZ{4q0GX(VOQFBCEuGm^4MVQ}UlAk` zed5w@$)PzP5lF^lsRWO>ggjoIm6cuj_q@N2Ko{wI8M8}D^$sn$L78)5T1l74{(e70hhrgwKM`;-*5^^_G-#a~a|wQ4 z^<8qCdgi6=6pJBmT`wxouZ3T#wJ;_QWN19d*JAT|!i!lN85;6ivTL8%av9+PHZ2}o zO?bdAFlOzXv)mgf)g|8`LO1u3YPbaQs`7OSAK3~ULqb|EpnUJvKe23y6EjcguY84e z6&8W-ZT+bLV*so^(nX&vW_S6?Zow~gimUz@Qa@i&pXSr#k#EII0{@lF@U`S7G>f(r z!u&dH!LRH7<>`~XttxNR^5(T2Wyw38uR2W?0||8EZ{@JoTYsk&{$|)Tk1x|cz;fnd zt7id}TPdih_}%iWfp(^*f5A^@Y*>k%Aiv+R{2o$LfyS*JAWkhpG*xlaU5nQIjNKyi z#1W#VD});LPh?*ajVt4Lzqy)LCdLH>FVYENOE@^dUhS7Xjm2HAuxqM-Mh}SnEgxOH zk1zG}fuswRAY++DT7@JbGr$hJx;~{Qyt+S$(Ft=lqYt42McrL^yoc9l$#Yasdh+46 z8n>y->eJTu&Ri$T(EHHZgnqacDQfVK(=gPvv;?`&o8B^ej7eiYD33SSUqMlCyrqi^ z^ev;bM)e-u;guGv|LS7nbJ!XBqSqvf-hX^{GUh%m<1>3PmfOS5r% z1t61qV@#d%KJFB^k1EPP1uLY5cGI^5@tx#@_ zKe4SxJ!1NG3`4a4NECd06@422g1v5nKEQL35fK#=CPR6}az-)(^P6 z06KX{B-C_nf3Lhf!dn8z;8KHg<5hwJ;(xX19DKw|#!M(_1{1@Yj+$}|EwA_i{E&{l zkrd7k)oaz8&*QeN7pEu)Qjz1NL1-^jw zWgM^cISr({GfLpY&o&Lh`~W2NvIzpIez#8Txdzy1mKs#NPh-1CnojQlIyUwQ!s^eS zF}!UJycSJe3!lwsNgL}m=+~9WR+rv|<4?PrZjPkDUt6ic&ebd+8g$z?e3JpC2K($~W45;>OT-%YafX_VnHlJ3rGUlWj0P{c!`Kkf`Tb@-bg0jnV=EoKZ55O?v-HvP}m@rHl$kx5_BivN+igdNw;PnH!_zPY&lC6QEnhL9sBFabA7<8secv=Uf(x3KkK*swNy<~c5UiecOG0z}m>FRS%Dp5#@8DQ2csKT1jc z^+=ZUD#}pm!k4{Pjedhh-^c)ou)B`j%&T7pP}0#%&lc>wBV*y{Fj)N9My*aiPRy<+ zpE=`h(50Xigv>`Xg!hM}v0-{^fauGzO?%ax1mw`XkqO>4k}~gaCd*7eja}_YIT)t@ z+m49ej$n10UR6Zl6qCvM%NXT^G0iH6j~#SJeNJ#hT!H_%wgw*0#H}Jq1*WFs^bgWQ3k) z|MuXlwq1XY>CR5eC0KIMAx}y4U89 ziY+We`UgfFH2{bx^d&%tsux79c#w=L9O&5v4JOF-Bw*T@Ud z!#^))k;P)37?SD5LaJE&vjp6~yBv!D6LOA-*wQ`Y$!pWzVAe7V=WyxLH7gJMD} zi21HMuBflFP2w*D?s}b-Do?mPLZ^Zi%7+roXa?wSLYmu^^u?`Y|0Wem>ikaj^f=2A8l{S{x)K7nz5t)I z-d9@Mwfk~*VlvVpE<|u){FuROU~6vwYq6Sn%L16DT^GkjUd-%VYaT_eSY|^J?ovDY%pce+mq{7)>Z{{~ z8_iT`%41rtI+-?1x}PqFwK_XSKDE;u|7B5!l0QqidJ|$4%y^OAKhHN;43Iq{b??FM zc*dRy!|gMEnKO5ezfIlT_oe{`69hD(VlDXi$AuPmIGeSXkY$>BN+4(ym66Ja+BDak zCw0&BZ(T>Q)zQOdK>k7Tx$aa98xk92}-7ta-GD8mQO>T>ckgQe z+W=QYygS+hPZZk7jInc1O~4KcL@91waV1)yoaghFmmNOZTLc6PAJDB&O>Yp)1O)Qr zn#0{G_VPs4b=ULMkW=QQ((X|?;dO|P=6+)-lBA1&ZA3y)A}MX)Fo?1s7Tn+K+xPiR9;iQW(lbalC+E66 zHQgW7m^a>?VkY-|e-!>0^8g*V+KS$jv(;cGvQN;UcTPJvC=#D{4{LVh+jl2fxtfXM zXPj3Q&EntuBDIo~>u}Tho^$hWc1Kry@;8tA8PVLuM^JAp`(YL|gMY2*d8=POA~7#7 zg;3h@&sw@NVgT%v+;_X_@XTUE9`jX~W4i~b8(V=GO=MqjXbxU6&Kn9L&j8bM16x;y z;ZW#iu3-D0BHr8n`|leMYVw(axco|NSU7Ppe_gz_TkLQt=1zm!Uq!xpLdj!)4JCeZ zaXwVdCE;cWZRb@!ShA}-yNeHpz613Bc=R|7?ih*<{Uvi(47a3~v!#+ZM{~7zIzhvw z$Oz$tta4<6sqKQg%>>dV>NdN2x;W|hXl$~#9@!YPEog=Oj?Z^@OSRt*JZ{Q+`up#{ zeCeC(2*;OIsxghR*u8%|^TmbKBd5BMe!*iI$s|i!XMtJoS;X)=@4{0^J7cygW0v?;e~+?# zcGKgG@7swGr_n=4jq5ENn_5e?EPc1mr#GAHou5;AMqeTr?F7FnL=gtS&i%@yi#(FW z=OPX+2+OilpdzXy2uCQGB7|6?>sK=n(P91kS_2%=C}h8oMQg(fwn4(!X()`B^rm31 zxHGkhX<)s3-U%p)n$5g5hkM8wVKLEhi8v($`k{(g(Y1%;xXG{+8F0u^x`j z5A>E~pJ<5N>0GBPe}S%kfGG_W5u$K2;bri7K`@!Eo3dxDNPl{oQ`+ae&kx?AuqsIz zUUu_xnqS#5Ce#XW+|9)7Z~RrD(*hP@CH7S#{TlsqwGvE)M`oN+Q#B?kye3;V2B1vt z8x>ScTJ27-=c>k0+3h+LvOVtDv*s?~)Rp z+I=lfuSq{E1%oe>O6U!Iri6c?V;X?YOP5=wbj*B3Z%-L7VZu=$Z!&=YF8^`FW7_Q+ zAKiz%vRr6_>8SRERYlbF@o<=Y+Uyjd&#{b+iF{0h0W}7PB&mNKz(SDUQ?nQ@r0#P` z^h|LCPALf5aAeP$Po=6>?v!~?N2ggDXJh+;Y9qMzFFWY%aA#&!-mLxXt;11F%AisxWvkUaFy4##6i}fRjI6OO;YJ1vP|E9dB9EawVCG< z$B-QfkyEaH1Lt@B{TaDc{|@+9s-@)_funzOB+<@o#)|eBGib`7mLAKg9@FfwSr)kc zWSz6V_9|Rdl*i2+wJQ?Bv{16KDcapkzwKG-0idF-up_?^GHe6RlgH$9DxWVNp2Kfz z8|ciWVi65IuL&iMd=p0p%^58U3Xy7i_;=G*+SR~y5eC@RH)oqji*Ph;E zX^Qw1+_Z`qCS({QSZFvx%E{Xv*rrH5b@+1tKU zpZW6S+Fo6u^i4C3u*ft;CiR_kFG6ZY_0RKo@cOBq{`K%vYo;(PlTLl-;egd#xlD|( zx`aw5gSt_8SQCBEK$Un$mV#TQk)g9l@8_r66Ul0Z`A}LRMR|@F8-rLv2U1C)rnf%xg;w_-OCY zNrWWTisS3(E})Q-O2Z1p!NJ|2x$Pk!e>=B3gnH*skSih!n5ugsx1i?@0rI9hTfF6r zhuCKM@%wkj_=$462r@*xmDrl7T8L?tnjC3DTZzw~iat@r#uD}j z&a@uVl1958Tyo(vB^wmAJ0HOcxEx2uNxgk52J=5I%A>cGL<0id%gf@Dn#q7N@CtQ+ zyi=@b)1r32n}|Vq+`6>sbvW`%u_N7XI-hL{#vZVk)rRr+;X`VBOPF|qjV+xwTWUi zktJCs3HOO^)T>B|kny53hGzWN&PYF(782GiDpi087lUU~@HA&dro(eRYm}zOyzOJB zJ7=NoU~}zFsXM&3_fonGAeIqZ(V6}ep!JsLG$!Etb*fEHpm6t?mwP~w8S&NdT`qFJ z#nFwPR7gUu5ZX3=!C~_17uB-TdsC;AV9cmYLP-Z?(_G?ixT;_GcPci8X0N;n+Ppm| z-DhnNzk3;?txF#du6Ias4HSTfYMK%nsN8uf3i}JOAIz}6>h7ds?U*yF!{Zop71mqbKB|0(L zJ2kCV+9#UU6d>M%NR9EdaT`TNb}(jhWw(47O=r&t3lzlEx#=h} zI_{avtqA2WlH%gxMLxNS*N=-7(+e_B{>63sJ-*s-E_sUhN}xTbdTvZO)>~33d2JWf z+vR2_T<@D5HyUPXftK+s?Ya|Mp!n4U__I?$3gYq@>1@2%9o~}(w(zM5aZe#Mar?=$-BoMb6M zHfFZ<-gAz)d~a{>gSr#rpc4JJ-fZ>O2YS$Dc2)urC`6uX_CoUw$BaM9N9dxo8I%k z;6e9CfrE+(5XLK~vI%|*4h+*k^(OsbO>XG#99O2+DpIHg1pJkiiJc>S&xHeYot}Xm zxDPS3D)BwixZkPL<}977NVYn0EvV?X(Ka^b-eY9AZ-B7Fe@(0T(FRAkk@z9^2`!uFdGQ!WQJtc>++kt%40@MZ3C5Z@1rzYgdW! z75A^G*lQ}T+RAfNoZsJcaK9ZywXRSB(I)n#&r$^s7}u+UGWvVNFVI)gz~;*qy||106!eTd7^IYAxHSH(m1H$J_ts)`oajc;;?y-gJU}tM{{H|O*vT+X{UM*5rt0qzGl?X z4P}XHUV3FifkR`Hlif7jkmtn}gf1$-;XzF5-@@g|hJ2BFa0D@x-tDFt^mrmIL4G)T zuZlccQ-4a&9&>#^MvgPD5D_ptyFVVjvA&@cT-RXFp3|H;+8%3q#GjacH04MR6g(!$<1n$- zLw?tdFP&od;WOsjh@9Ng1n!$`&yCx%0-lGW3_5Kn&L>yJL7IN9Y&dB4J|};p%*RV8 z995BD$rojv@}Ct-BaI2R>Bb6Ka2NY_z*hjL_%CKW~RJSvP*);40I5 zMn745EYSU9U4ra7C9#&8{Xi$jC0pzKx%S|*o82)fgGza|^37ki!eriOY5~{X#<-Bs zJ>*6}Kti2$e$T<6+Ec5IS8%o}9~XIndD`&Qq13vYJH2tAR9MURVLsc|9+KY@L0kEA z$2W^9esQ6}3itWC`;8&YcL|jc4Jhf;qGqvvO?e#T%{u}{BKUINr;{)lY|xRH@EPY~ zolRhz*?WR@aWjCIU2-nC-6Y!Spm<91<&D4yU7O2T%J-!5W!v%c>|9ujp@!n0)+j8L z5wwz*(PvsNu6 zASEJpany4jufQo~iHM2G)e-kIW86oNpPx_G`2JO%+W_@0P!NRvaIY0^NoJVGgE16- z0R#ZtrxKHm8gqlB(<0>~!XyYVeW3xuxjdr4pY6m<#FXwxD*>W{h+ma9%J0XH*d0`o!TGr&n~%Mdib)(YJBS z_gtH|*TK8DjeMhP}b&{$2!kF%L4*C3I*$Hz6D zowX-HNMy)c@8;IVdi{=hXgz`v<3(E5639)PrcxREyd>QCvj>6~2>F zaK{Xk;@NF-XC<|^pRezG>$)z;Axt^Z{r0m6^Ndfo089d}1) zs>EMJzQ8TC>E%WkA#j5DLX_1ccY^7QWHift{_tPN?EV?`DqO!6m>S} zA`M9&+13|{gTWnjLQQp)R0zp4EQxPar5@9LH)t#BrQ#u?87PQ(5rkstR>VLbf4zz{ zDXGJY<4r^LCM`Q;Pa*eRr#9ou@P+$+*-a3d+_d~N%Q0Rq@!kqomZ!~wa<=C8;`np1 zj!gvTd1n#7{4<^k;LMvXx1sOJjF?NCFrgY39#IXBcIkypEugtY^&O&XwkBjrBcemKQLySB-Y zvmr@76FoM8b_ad@e);?8RJ3f-B@crY?I)(*;$;YQsR8E?;iE^6pYB8=INKotWBQ`k z-%-tExa9b<-;=-cLORtyEfd`;aeOhWEurMgQbV8DM|=u5#b0}$yMT2HCF8b@KG#*$ znnG)b*`2Xd`C(3}7a65-1s%2`=hUdDB^s~zF-E$Ooq}HB-dot!g_PdPT^Pt+fpI8s z=$6O8eK0yPHLWG1Ys2P!xZTcW@`RL!h7HBB&jWXSrtHq$Tayhq*WY0Dk+#0;5htkg zrGHf&_$kC&h?vhCyvZTLL*T+j zcvF0F=~Ev$$`s`&?|H#Lg5a31M(pf6zH9}wHn6)|mDvYpxcA9!!&@q#W)8$$dzIy* zDD^k9pc3K}t9UbcwhrfoaRp!B9fu3|!K++R)}7ljWQ0atyC?I-4-$WQ9G0G-4N6PE zXfCyf1!7r5^@_e|+WtIn0LZuxZ${VwP?I`jw_d=~So_%xK&!?Le#s$rZjN&uJQzNB zDnAM~&~RM>YU2Jv!i$s*D~N`bzUeYOkzz~`|Bx^5C3nJW5!~zO#=f0&tAXhjJjxFg z>ZX9ymHYHfhjZ?XGu+woPY+HCE1`g%RBDcTN|V!7K_v$26H`(_KhmWIu+UpWkC<6b zr#@rb>_-&}>1G(aR)`g+6d;fdOEvD6nX`{+QedtxwY|4-r(Y4xy%(>Ci~N@+Mr5NM zkun=SdLr;d(F;l6vtFo8l`*b*4|Fslv2%^m`P7 zF2VJ-!jvDX;9;>~{>Noc1q&$skvIu?@Uh9ihG5v?u1yN>8;W@)We-;LDsBDUO$e@7 zJI`BvWfV`!W!88e3tA=>JT_q%~^b9Dm9dvb{>P=pkW zvgR)sANSDse_#J`bSAp;&?2S4V`bL)F{AU1|Mv^H*r;%8Agc(&C+SdlRlK!XT2fxRr(sQWglS7^AMS6ce8 z5Zs~f380m7S7y;QT4ft5WknjV4c4=Uu$`wT?Gq}aON=+9?8h1mYZ0nNFKp=ESGa@O z;s&$@?J1&RE>BJBU$s{Rt{xq>+a_D?R8;8=+>6?4(W>GWn(Xqd@_b-`t`7l&RLW*a zq+m_2QPe^>8 zRbf6qNt7oA}OGyWH-+8@ECE^ z_#ai~ac(>;-!gLQ2pI6c;#J08OG$-O_#36rk=;q>eoT~P~rHMO_BQ#2RDl)kAQ)T z1I^0kiSRG~x+7!z5b%0fY7tksF=fMljzFRq*BwJ^-qlVteNqu|Ds7^7*gV)^~4(n2Y2eGlyP&^|i^w ze!yU~kfuIXGVBj06l5S;7bbmvp2tPoh7L;$%ZOH>8EG7g2)JDt z(3Jh_pzbe+dsT~7iEqlg#NMZ5`nJ{eM>TK-ioNT{9tRO>an{%I*z3nmK_CY7^(LyN zs>+|{L&Kx^qtuCy|M2#fVR1EEw`g#J zyIXLF;I2Ue!8HVj1a}DT!6CQ@cL?t87J|FGyL)pNdG|j1mF)X{=g)bb^`q&gyVk0j zHs%~POQ7`tINqe^JSWET0JobC(*dj6CQON^(p1kT0d!YqhmCA z2G|qj%^e90RvJ0{@cSun{5CAQ9{Mm3QA$)8L9;J@iL>g-Mh?VospoLZS6!qJ^9^>} zH5YtVhS}c*dnepyg7b26U^|WOiJ-sZ=FXql>lNuZ#_S@J&ZZ}qTFl~3^fbFC5(T#kripeVdT!?uvQP_v{K;Wi%~!NPsX{^w1l zx>!+#wW}VM zPDCv~wTo)?OrL;yD!Hv)>pRZ@ar<8z$dNo~ezYfiE7ib|T3tF2pzm1JQfhR0tKsQc zHD90WV+ZgIW77n0kFl5V_0QeN(Ls?4BkISkZQ zw)K%F22kFh8Bay{$>5?B3Wa%~kZ#z&wx%W1?s~0zZUY=35y@e>U@#6qOZvlwO^^zWaav)^#>Q*OlL|IDuI>Y!C2A6$z z8Y7jV$L41ab0&t+hmdXQycewq{r0}|%@}C`Lp`^srC*NU>8^vM#1yGX$-{!7lLcmM ziNa6MnmHI4zsTCl%RAxat8=N`_0mmKg`8^D#|F$8fLS%ao?$&uCRUnO99C#=qExFi zI{KD{s8*XF(*GO}^-ubE!wlq&=4-#*$V>9i!GAQ_RGnW#90W6brO;qbVS?!i(se!j zK{3~8hQ3%)@xuhG!KS~YsgSp{t&pSP_;d_kME)dn3}|T~NQGNUgUdl`KB8Q1i&&^x z9A{V-p@yTX*=XyX`Q^juy2Mj%_M6JU5ph;Uh5BHX0!_a{Wxns^w=l5Rk{z&Xch%Gl zP9TgFjGo1zs*j~iG20&CetzC2v3u18K3sz1<73&F(50F!LfgZs>G0d6gwSnk-Y{m9 zS!ID^%2n!IIk2Mk9Ai!Ps|YFF4xx-XeCyU9Y{fV-^HnPuRf<7@PeKCu zIJ(H`aB9gt$D~~RLI@N*c1n>;G0KVE!g;~ihDp#2bX#s7T_YoM#nMHFfPHqVFU!0! z@#B-;px{Qmwop`zvJb0lQM-ycVf8j?THEvwtc+yCx35(AY>8~Hi;03lg8ROFG|6q@ zD^3}1xt;+70Mk1#xB7fS4mh$o@bVhZZf0Y{KUk~`h`XCw;i+|1evf?B;_>c`yB~U( zK=BI|y|&5>rtl(1+Id02+Of6X$O z8ui^unm(O5X}Qa-bD(NUx2aqcEAcL!expI(I8SMI>=Orl4Sd{iFSfW%qyrj8Vrr!BB@kEv z7QP(f?&QJ{5jFHP&@lk7Iidp!O8cdjxs{GX_?+ang!QF-H@XZ-%&T@X_~s`M9q<;K z`s6t#I4^%;BX7;OUd`$Wp|>|D-%C@I>5!j0tXA>ZsH)Zsb)l5O10?_sv^I8D{M9V8 zTJ#YY@`3dHeE6HjF|a+rD7>J7R|&>sbvFq6%E1JR^V=$;gZ1)42TpJd9#fK=%+$fkJDz4KL z63jYYpo!02$s)r-AE5rw;D&^FzaG#m+=dBxwvC+2^06Zb3Q{ci%UHc#yvcCty%qao ziPnd=M8zdKt-=6Q&I`dVZ2)JQ`!gN}UR(PBvo71#fLtX0C18A&Y~D{oAE1doHQ};3 zcOkm0=~a&A(s407a1*S6ccf!H1+z58K(2zRy_`pE1RA0})WfXOYZZ4K+tBJgy?zFI<^uE*AKQ+-PY4*vYH^%EMkJV}g? z&)MkV{L8Ucgp|yg;bA!(*DpcwpFH5QXJ*V|Vq)^MQAg2WQ9U`oeECwy!bv^My%o6^ux)1uuLl8~+ZTA6qZAle=!F67;=e$OKj4KIC#sijTk@xYbAyk_5HQGA zWP+_i2FMV67S3JHs5LD8^I?a_S z32xN@@Vzuoiwuv;aWZ~M2Sw5`>dQPBWKdfX8I@u+%)XQjM?^!cweOmKsgkWN8qjFl z7Yl(wMBpHIRXecWBUU_YhxT6yjl7pmcP(dwoOFJ#jU!lwpx|4w9ris`#{&Q0c`dX; z!eP?V0#O|#OLi=Zl9JLFstSbDO#0HBUJv>k?$wsH9d!ldU-pE=5q$)&bxvTas!F+mv8%F4tW1_f8TRYr z&%UAjyLW6kd7X`34*Jw!V8^cjNx1%rDGmqD9rjBSQ2#fQ!2S}t2EJX$%K;hUtjx_X zp`tX%U1n2WNK0u@++1t)#&45kjJR(Jq9PMuNc9i{_t%;5?5QeURD824~Jn}zrxjait zUl63*7dlqYrx$OzBB;Vm%0pse4A(qOOw z|IHeN{o!(>({$3^ks(rviCiGTrk{1yfqr!CQS60(%JsC{y7BXjv@Jqf>)3GWRU!ZN z*?-LgcfhYP;r};dvOD9Fu9ks%~Lw{?1Lj{>7T+nVvNQ60-y&!e$Kb0Pg2 z4HsCwe%x~}g})G4|7D(k<$bh&JP*mF0rRJ9@4r2r6)9l6I0xgldo_Ic5eQ}}At6qf zSDf#5MzfwLn%dGnE(EsEKc$2(+tVPfzJj|7>X($PhylZ%X7+c;|K=G;LV)k~kqYrP ziZbiMpalQsUseQwf!o~KW)r!T0j4cqQ`o};@Eu5jgSCIHaEH1b8W_&!t?b!0^-J8sef7*7 z0@-x^`lK<~aYdzF#fe=40S$yd%p{-{|NUx82AJX*K);DF0#}r`{f^n?IXR1iFoeb}0|GJ!jN>R<=Ne@J4f^Zuu@Zek`FCG4W9_n8owXs8rdlLIG@+7oy6i`oJJgryF`Z|Kr1GOT6#_D@|rW z!1*uJ+&1u19sQYpE#ve{y9c(hJ8MOOFY$WGBmBll{cS`>azLix^)hMxEFgb*ZSCdd z(BAg>70wA@tHAv8*SepDeXYN9R z?;mDD@d0B>(?M890xa9X3z66Vhz~zOk z(v$*xV@bdlNAQ38^?wI!@CJV?|NI}N>O+JCjhvxn+E*Y6>f$&BPHNP8B;Y0@cyT}7WzsC{Oi|bGz8fH zat9>wfO#Z|mt#g6<^U$z{JdXk*MGS+du2taNag`FZ+W-V$*nq|@JpCkyfwln(p!8R z-}YZzAl%(N7T7;(gldNVjo$UACGqmYes-QAl<=beb>1)`0XhH@;jFCl*&llbSZ%u_ zUD|~)U}@eBR~@_+5D-8L=U`w6Ytdm}T)oTBrvSt>E`UzyiQvxL&Am8nKq5Rfmys#d z{$so`JfsjwJeu*7n;G7Nsm|sOWmt-EGKaa!Sm1^%xUlotNRi5b649jHD6yJ>v4p+( z5uYvr=I^I*feH}rIyp+bfS3P8cOnd6aexD&;l&j<3PfW#t(YMpQoj|PeJoU{9V+FX z{3!0PZsNSBn|CGrkrkNSpY)o9=N#>AzU*|PMrmR4%=wD&&e+0a(@!9QZo%@XfAn!Mx>;#A9a8kM{VZ2gEL?AF9Fh?)3WD+1?V$R2+C{Sp~xlVJDoE zi-kQXh*op;cQ8+jySsV|YL5WxWp)=}y;L@h;y*8d6Pb7Fru3LJ<^T>7Q27w0?}PkJ z_yAbd(&q?5O$Hkp{`YrA5&{1#UEm^eP8`vUXDEmH%-iiD@%Q{qfq|J5 z4TI=OXHv1!PrdQ?-k6X2xXE3x{#sBzFZ3ghH`Gi0Ih|Mfg7_E8?U%Xh4N+ZNm3sPKF zK0f8S&@DDqE)l_ngG0*9msZ=EX{9nAzIwF3Ny|d2CQ~~*9}v}Y;g?V`;}h!c3|5qE ze~5{TlRf2Jr(wLJe5}5=(s(+jlzR6r%CWrB!68|tP%T&0A{mX?H8RtWCR@m(!Tla7 zMbIu1#Oo4;M0gYbl}NzSQF9LtN)%o`IxZ~VvN@o@Er%t>i7MzT0iXDM?LqG2J(8k7 zmqeQRRK(fc?b}9R2c=Gf2ZQ+H7rlkabAy?;Sgcyyf&wi96Zvl;{GvxkKy&BFi%qCj z%7&4>tzr7zAsQR=12^u6#P*wKDz1~+c73s}#EpboXKI|MOcGPYi?Xj@b8eE?{KtL#H5?&&_VqA&S#&5bG*0gp{@n}U z_o9k|^6c6`8Ie)CZGu(;X9XJ77SNK;{(wJ9UHyIpMFc&;wm|IYr%O^L56hr=fvSQ* z3TF0-jLdgyQ0pygE)(nbu@+GSJl-SBP8JCkY$grSXe#$Z$w~Uj_mv-C-fptv##h3U z&}Sxyx!Sd;zeA-6R%#q7X86HTyet*nGg#Ia&pN~Gx^dsRTd);A@ZNvk_da}NhqFh? z)HF%2o~QD7#qweyWSDkcHGX$}zUCk^y!YLt(T8lU*{%HY(s2V3g4N@+=}cnxp6>H_ zldg0Hd%D${mOfIgq9I>4%QI^IPUkFZo-+c54^2@^_`D)CTlF6vJ4>ZBh;4cwcJ8gk z=3>t~*9&fhoRdx`^Ed)HOb!s{s`QEmd2Cmc1miSPu%jwhyXM>$dOz#1Vgc zVJ-NK?1pjpe{0;I-}FlIiqs_`nb^>4W3oa4_OhuTef@xraC5s!!Qmwo5U@#w7cxF zq9$P$TJ#&dSWtkbQ(X_34OwEs-_M4Z z(KDRkUV2z+5b}1jKi^^)cG&adzrEW+P5pkzl(gX&;Q#73>A2@veA6b$yut(tkw=Og z1eFO)*N#ahHUc>n)yvME7u~Sz+?$Mxq2U>HYQy7O7IeE)hsi{QI+h0rKqs0ZQ=oE4 zA&r$+y>CR_3Pa@V;sQ-5;1&e}I72=@;5+R}vH2oU#oOLuAs)Fj(Ji9{#*w5@6vzHUbg}tARsJ`evT24oftt;SX zs|v7y3CJ6X9Z=gn>!!0`U7&zNLdsOscVj0mk!|U?_aRVJRLpm;B#L=8#OV$@g#Bh< znqh3hNDhJOtJoOss!F#I@L%l z7%P*s!&(z*$O*^E>?boVk+$;Er1d^s=tR-<_e`4mhv?a=)%d|AKJLiq?d|#F9;I%n zoXw0+E{%GfjCU)KOsvhVmAn8S4dNmu!4=8KYB@uo5lPAh7MdL4hWPhk(FW}^?)t>@ zo_?g&6Q)oh3p|YY2~$j>*E!dKX)oufQzm~mI)z5Z8jQ#D6yPdYf@y`rJi{?l`6=<|#EIxgU( zBjPx@?83<(bY#K74YH4S58;ej4Y?aO*;@BK7o#)rm=Km&m~764g5BNSW_!EnGD!kb z<1J1f`a$}0ib;Z2IZ;Dj12vc0!k zk8SI-8xuUx7_XuxNGDBLYqvX~*!OPYCUoC83hcyXkA`L{)6s}vo^BPUB3*si`U30!Jg*+Gq2GFa^76qKVC zrD~%MXqe`l`q?@A%%$5c(KErpiZBO z%sJ)^S`zqk=e4v0R z_ID1hkNr6y`l-xesTrW{QqVjxd%4d^KKjB6^&R9HRKUo5XZ5m%I$PPwe7O*iS^TRb z{o~L2ug}2v)hirWAQX&dJqpzoB2wE(B)fPl+_gpf@GGT9UPiAv3>;O|Peq)8Te+m- zB8Sm)F30W?6cu$u>%7-DqNioNLQ;8pmn-9H(p8jJP^8{v*Pu&s)`g2VWtGixlve9T z#TT&=J7#{e++yOGV?NjP@m7Z-;P!(mJMO3Ys{ zp&&u4RxGLWGcDb)yL%zs@(_mBgqy|V2er$_>&o^^e9@kf(azq@wRaYaqq*TH1iBkE zLV`k%HfdN(;M%+Aak+TsI=A=J8+m8Suk(BN?Lh}(GW@b^rej{uzB1YhXso-h{iSy)wr~Sy}Jd%DnXTE0%qoyU*AQ z99YjC5hBAT@QWju`;DH>{AxnOlpwt?2H24`i#}@QKI|i zPFkvSVTj-8<4pgt!lDZ@-F+!a(djUD(U}oNU6zbdPuA553bt!>Lp7`2#%DY>mzbqA zT`W}1Ml_jb-sEoFyB1Ze2Y+yInQiiTT;Tj)cEP*yI?}`bTbp>@^eHlS0;NJWP0{e5C&N{dY7V0c{dGE}=XA>@2D+%h8*m=6&PN zEefn#aa?-E-OJ3`@jA_b;VfiO=S2= zd`2t%q>DitS0p@KO-Q~Vm=Go#9UX1Fcfd)*&faZ|y)jDyNDCsDCG`(rd;S1Gk&r}< zw&6oIo7-M~`6|rWi}r(#-pxK8=EcseKO8p4VJDscbjf6Qldmv+9z3+iy#u^V%1I8( znU&gLE@?NM^ltuuTEQ}WXG}%yGBE(VIHJaa9MN{``m2s6?LL<*mbS9?61`$gx&)^2FgaMLS%!lOM8m+5gIM z>AQltKA=KP28zuU@e^h5PHC)GLU=e{kj<8rOXjxi2AqvTDHs#&{lm$jsRq~lUC6FY zw1h&6OLfehI1rKyhL07%lcgpQJJ%hdAbbhl@0_OlM3c%>S2{T`J@(BcWW$o7W{FJ@ z%XiotH&*0q^uYz0hAi3kfbG$3w=il;5@_?|cAT$N+mI_GdjO!)wRNix8)W|_g(1}T z2)G7oK?4V5U{B6+D+K?op#{zTT(vqL=Mi0SNQj!+xs%(({(0ZBp#1o8kDzC|MdOt5 z8J9wiqQ&0StwYFzn0|W!>-JZT4MBxtT+`7v_gd7uM}$et4iUMQrHo{luY?*-XxoV3 zksi(Jbteay+|A4L=POjkcVl;FTbJ&j1}BSV0k&)Wfl%D00kRLp$2>ByvvVs+MJ5btd*CGyLA;L*y4 z`(oKy=y14BhFJid9G#QFX2lbDLs3^9F%AVGSgJ0kBgvYd<602+$(~@}hx<)u*Mpy|osNerb6poUJvtBi5Ft3nxF^7156YCDfR%qb$ zxFB!lu#N$&rPKHW4uHir%ZW^EeCL#TW-f$Jnou?`mM+8nDo=saqix54w$i_K>cE@7 z%Fv*9D}M2Jmu7Nd_|uU*M#Dc3Oh_0R+s}RLrwxX&molX=f5Hb_U+q>(7@tdR34K()*I;|0B z+rxR;tmx%6Q6K=)$tPcZ@^#9paprp>x(m;B;|nR#YPM7si{9Sjm_2bDIT7$@@?$65 zM>}|(A!=6)90K?4^fNzGezG1iLKAH(0T4AGeIu^dQM1(I7o6(Z0?sO)d3&t5c$Yg_ zRX2Q-{Bk0zQ&-;+)Yr0&W_f$zoP*;@`? z%M)Wd)5)Jrb#VZuO z%MnEz!IM3=xk^*;lltVl{i=Bh$kj1X-|v%Yxd=)$!z5^t-~&D%KzXn0Ne>He%h|Ei z;Gi!$5CyPdJ`)SaGijHzLl*qR-|jsU{;1?9Gq|Tj1QMa?n4NbtK8Td;jGUlR_R{u9 z|E5?&#`CqM;hIGEEw^iGw#_d5(u2u@l!$9*AF&YK#5k=q z7`IlB6e@%T@vrtfdzGL^7B|Q#lGOr;AA7)oot&X56LRagZ5of^c}Urfj)TYOkB<-q z!i#zR*GrBsEU$aQjPLGK^GT?gi6^l)$rJb$j^D4AbCu@q8&H8Vi$-&;z&Y0qcG}tn zW6x+~O_hQI&!m#b?w@8wPG8z6x0pMaQX|a3W5b~@lRWIWm}39|hcxe131BdJlia^k zNPUUVb8*m+kL*~*h-yX2$<09tUtPYL+4etzuA_Ee7?Bm`>{(>@3-!&!oE#e(Nz& zrXZ01Vcqx5d7uMP`%_&%lgSX6JMY!i)5^H^ofezPC#Bp8h_P?}m~=8AXNY&bj_(PU zKiarq%c&o>I*mptXKC=uxISp5U9Z{|0;rh6k}uv7a(^db0D!Z{7<66C&z#V|Un^4J z{yyHWM9>fC(7*aXv1-n~nzKa&^LRa)S2Qb>pf-|lIf*mP=0RKy=&lhJFRKrjU@M>f zO4`RCLhI{R7wcyp}hL=azx2o<0M}kEbjOtfVvDBC)2&+;P?0B4}EafX5)ZZT-&BSg1vw5St zCRsRL_O)VP;@IPp(u>ij*wl#vvky5fz~Ko>=UE6Hev#jY!izJ2&Fk{#t9w8i?8)Q} z*}8;W?xE*F`DC-=7UKE&pkv3mn95-Q_2!Fjf#whHoXz(`*s=$!nv;S_oYs~wzH_G{ zLQ_X__nBnAT9JR$|Nl8v5n;~~EPiv{a5P(qSZ)4ZM@p*!gCPj|<~}6PHU;-yb%FgZ= z_rxY@cW}q!ClaD?%y=xHF|u|?Dswk7no?ytNFX&VIlWPYez{kL5i))`(!`_|Z}%TtlyNOD&DlIGo!o(a(W?R`2ZB_ zL(}3sdhl|Zy>z>ek0o-d_O1^RR2~cD5(lcUy~775r6#b*mw^_7}%(^|3uZ#?+BFuwN#;@gt?=| z4@yOJ4pP}N?q?}s2$PSF_mt8 z+vA8*<}pX|t{$x%-1FrOfKV5*=ibml=gd<^T^40?aU*c&5&PL27et7R+5vl|xME!P zamK0$5lBd0jWN3)&HKw#nN5c8@hx-#q66%|rHv^P=GSo=&|5N}Y2D~80C2Rhj1Dk) z<3{1IAodt5`6NQ+ARuo$df;b3E&34fPaS*#{+{AFnnEFhmK7<e<)Osv@_=nx|;Q zc}22GQ^Q2<4mRt0bB zSf}FirM1Q!nM=n%liwe)T z+6_BemE12eTbO47SFGXgeP-?jT1Mk=>;)paqcv|iKepEPAY|prCF@9fg8I$pP6j^Z zEG$Mk3c7`*nt#$Z`E-5Ny3^@M`KjQ8#N78wNyNL2kSChQli;(ail%D$Tuqv!^FxfB z8K6;dri_8hBY&m!{tAffVyI{e@jEs{+FL!;?idb$AU(@3_+v#A$Hvg(z#(3x-7CBl znE_;jg-NIVW_RyqRMN#_#Z_u9GLZA<@FO`+AiA~`kLBaQ)s1s&`W!{ovJ1Iv}wS>#puYifwS5k?Zi> zy+1K9w>G-l?O+35GGYv&RFxP9<$E^zFEIY;7|i$nYgS-cg%r0$%Ljo(*>86e(c32N zUb%MoFF7c6nhum?5IvuST+DXt*NRzJfnd(~b`Lj6%iLL3*G4#%FZWn6Ddmm+RmUg> z1;Wf`8{M&5vH4X8{dOgo01PoPF}L)r?fp7sv*Gx;x8i%uQJ_K{9o@Ai1ePSAZrn~p zdZBlL56@O~v;4M6tX=pv))WZkJE7gA#6n`^_qfsYm{8aA5JsG!0B#G{Se)kewO~u$ zVWVf{FIM*7wNfbeCD4kP0K>=I4PcO%R9G%Vs(r=bJZHs^Te7qm#Aq($y9j-}Ylit{m|8(mwvgqoZ6=x@Nhl7FBqR0xcIQi$uMxy^2P+lv5g&Fby8yHxO|X1_yd4Uh>+ zNwbn=H&f-nYf*A;{7jU6x$nqR9nMQD_^JeV-89iNM0!X&vp#a0@Tkx|poi`A0v*<+ zr^X{p7;f>SjE1rZ^gm$IAMn`g!!t_)>wBVQm2`aE#ZOi_$@?@xPq9E~ta`UmN;BVq zeV_rwvSvEXr>wJZfqlA7C`iarSF$bBAD-&`DcU>C@1n0yC%o;iEi+>{w+2hQ)>J;p zRhpv+s8+>Os(PuxxB{nc`gP=sPxfYO;R#HM8yF5HnuiY4wb=#A1?Kn`9#=7i#9ns> z$4CB{cv>&lY$BimrDUS5ZU#0(??99)Kdp8NBpt{e`j3cC%5)$(w14Gq9n&k%*VNS& zDE!b!!>?YQ+MpinNrUjXX=B@yEqwnw#Ghs-p$n}j!fHguYexk)l zz}B3T;i>;Y_7XTxs6yRchr{yv6)~+=A&bNQ0}oVCc|m7znh`3~m+f~W@X#|mf_HZm z?ZXd8PQ##Olfz>jvn2QQwa$shTh5rKMiFD{)3LXO>MglIiya(h9?m)<{3F()hmN8S zQFgpBh*(p)!@kHII>Edr*@O6KLtqoSg*G}kE|FS)Ae_H`*w0}47%-CGk>ajl0dWU- zngAu#Y2y;dhbuNqaFwm@LIE}@8P_Dw&!uoc3PyT|KohTxz^Qg`ITiJ^nt zc*G(>EV{aVF)+I6pSiwIN9iLzVkTWE=WFr{Q}hv_EwJAq8q zJH2ixvx@lQB=O#__^v`g>zbRp`yl(hpL#hdRD1lQ3`zD1W44AJ6T)4o7+htij|sIc z70i@6l_0vi{WOL%#<8ngBvFPp5dV%PCS3o8w6nQ0byCv&2o8jW4o6p#m`9$RQ+8ct zy-w^;miEutEV8r7$LspHpo;1uaBJP?$UzZw#CMKVhx}yz*d{jsVrhxEH#L@^)46pyFP9X7wCGECN&Ly z@XQu;1Z8i$M+Y|Y=lyh3VI79JcQbyqwc_9MsE3xq=Pqx8{<~UI9|G9E51129AHwjv zW2H(Lchl)Yrt+c=&!>557$gf!Ix@{TRofNu*&?9=G|ZrG@Qem*(c_GWfNn)I7Zg;fR^|c*{*k`6k=i9~E zxa0BfCq8<&<6B*1ipx!5G0C4x7D=9)j$L)MJW5lOv^DMhO!GX-3l~Tb9UZl~`s^TG z4;(+mH`1F{D3R5htfsu$)Oz(Gx-Kq5$q}j+;CAaF# zfJwuMKlEHfj-%HQp080aK~ie8l({n=bJhQt#`gn69QqJ*b--L&m5C7;STwJEzTq+T z*c6#7mn4C^rWw^~r!}Yeq~=1shQYm@q?-=Z8ZK@ZPHovu?vFE`jF&D-cW=ri3pJ}r z5#6&|fo^4VcrT&3mI=>04;y&uTs+Lfn1GIoKU8Q(s&3KQ&Tmf>3jDY8ib0bfmskY% zUc1^60Db6F`9leRg93nBLFhAu>?cK|9j~LM_~E=D1){;>93jy47&;tbH!-exw_GC0 z>`vqUp0mp6$ts)EOVH!&1;hK-u`Uj{Z!rh>^AGHPMW&P37m~$!`R@B5DFKPkM_Lm2 z989AdHXY+W-p@&ak92t^J$f<-%*Xr46mpQ7iE5q4muvG$kKbS7pKY9GT`#jRrFQX_ z+dSrt`%~ZJ2NBOev`f9Bt)o?KbR<8id*ZY$-&*87zP?_ysM;MSre2*wlt{9v`x>dM zyH*u%GJ$_iW2NnxLs(7XD~5OscZ>nBBc*P)D|O-_5w7?UsCj3C*eoW3QeB#SbEl44 zIYt`7mT8@Vn55EgJ0Rbch<4+LH(2|{*2r#>!CHkUBw*%D`7QeHb^pmi6Iwf_7L)R?RR^$;J}d**a2@36e_F{L0iTV^=NBl3}|dMo&KlX>lz zp;uz5PW`GSxnEu}u*Wirr9h)0qs)|>q51_*E?M|xFv+DCe?T@ayt&`bIpoELt1Sde za?@sb*c$v0)#rsc3XjoO`dV`Phk^JoxdbG{anpQwgM(GENgDdWC<(*h1Aa2^wnWn7 zW;oWRW=aN!_0z9JX51~*dJ{#OQUMD^Hlth$m#FA{qFOKRnTC$(^sGHW!J7CC5KM<; z4BqT172bGFZPF8OAI>{c-&$7KH)Z(21tu4~7itd!c+@{N9&lqz)m3)`pa^TxEMJC0 zmPZMDjIN303ke?vv%elKpQs#132Sc|^9rnM7*vx$$^>X*1&tAqXrt5mKWY1Z>VfI0F@e2e=rjxKwt*Kp(I;z z1z*NT`F&CB1tj0Ibs1)@ohF-CzTUf}XSV_+C_5}Rq*NL9ySO&iXE$jknoiWtyC&6u zcv{p4V#QYDT}U3gqkQGdQx}W;kLc(6nr9&K-mZ^LFH(3;cu#>+ zSdL-!GjimclL9yeO?&(yFhg25;p$&9IE~e&3Fn^BX;rJTd8C7uThQg=AnaliqJ*XHM)`58EaESb}*Y^y2-OgZz9lgC)?;0n+uNi_e7R;8k%nMn8zc} z=t{em$>xt`k*(^rWu2SIFzcG3ye?PUxu0aEIqG45CVD*Y z0_0e!E7&RTCCX0JX~}!vud_S}bL>gOxRQK1tNdO4RT94=n@WjhPv%cm|12Lc9UYyy zs*jAAG>Rjtc2!QJcu9QykJ|h|6Q~sf_xne^3+3}(3!Xq{)^F7o;E@Jb|qMXttM@1A98QNa}^|+1?6X~N_SciSM z4KKVot41VMBSHe~IIpIEe}r+ed_k^#qf zJdTBeVkl_d_U06^%c-2lVIC;3QTRTEb(pm+Hfs2W(8dyH zSf+^Q7kvop1@aWFpxJl4SKq5OdR4uF^A3P^9)jjxZ%Nwj&ubG-mF_}RD56ceA_|i1 z^cH)pzPx_2l6?uS9y)Rpw9R%ngGUw5O^Or9=N{N{yUN;`n`%Cjn#4c*UghpMHrvNX)r@~#t)Wuz=dvP>;RDL<#Mb^LRId@&~cG`_QJEqjNM zaUUHUX6w6}7^xeq?mlAZ_JR2Z$+eany#~)=!5fFiW2d{D#93>Hy=kHGsjvuME7?j| zWdD@SdV&Q<99Aj@6jWGk9FD|I!q>UVm`Jw`Oofw$h8$_%<)^OK9mmU@77+{wQpKi^ zH$za^p7OJ{_@zD;eU_0wUPq4LmKHx<;My7yZp-n-Bu9q=2*D(VXm7s1Pkgnb;(>x^OPp1>@vC-Q>w z;GDFzPh75fC)(oHqg|ri3@71tJ^z|V$^K+U797~L{?7}ERH)s{x2;ECU8%h7@jl#7 zUH5e=Ul4J*SH*^Qil>n3DW_s*)yI1@HjY4262g3$WKQ)%{k=rb{h`e@t75`XfMsnx zO$;CBnYUz+5MQJA!d9UXi+?n1(Lk>eumQu=de z)ER+qL8;JTiiJhL?OQh)i64P~fpMg*@nUuws$J*j@utuhWwwvxO&;fSRsE*3QMy7) zhO`JXEw1d@!X7$Gui7zQk)wDT)KYYK6@}nyaP9|)+`1Q+NEeM_wYT-N!jzfpgBQ;$ z+%@kRkEJ&%%#Rx(zji3GSVPFde@_C2_Ls}s@SX0@1d#D~Zuu5ct;Icjmc+2Q?ux~9i9!M9+dz^`-NJ`LYL=L>Nu_^ujSPS zdqqeHYj7O3PgG7w`-ec7g@r{F>wQMcMcB52b)#2^TNW|^eeq4CK2#)wk?QJZuhw>l}XxJ+u03bS1( zC8=1xII|wCi=q{MPaA<1x2aOd<%078;>mxo(!S@+|Ix5T9!y7>X8A=uz{Z-&So963 zeeF7`mMaQNrC36(l8Fc8Y(w7R+PXp0)dDJ3@f}h7 zwT!~zF!E7P)vOVyM1~b&%qJN*rNKCAbj=7ad!%2b!(yUh+Zqm7)#!|GS6L2D~q)T*jHC@w;=F%hKmw4@A2@T5em&$%=z9JL|i<>S5K-m*aWlAIEp73(QbMC@!oj+_ja z?fVkXYld*MSKfva>6-QW>JA=ptM3#;{@s~X;dZ# zLxDr2?R0^M6XCi>OgBuUI=pAu6ge(g|Blpb-R)TC-m7yMO%;#aP(@e}SIsiMjXa7w zY*tMH9GHV4CTa6UufgLSJg%gCXrQc>!5P;fU8p6hqtl&T4_2q`Adb^&N%taqXw9)G z@{@&>7+khc&39)%Bha;cPc3EB*hl50iTy09Yi0wGe6_iBPm*ri;vj?$qNc849nuGktxqDdi1%$!xMuTLU@b{;U3nQ=t_fTkGJUL3ggK zSFjMa#S@JK>aRU4vnWNg96J347JJC-dcOIqsMmbvD_P_!EgFarpu2My=+DzvOqUq9WMOixe*ab$BBb6v_s6;LH=FOMYSe#+zk3$I%?hTK|>Nx|_ zr8i@|ElO}ysJFb;3a&dv@e$jG3AFL0;o+vWufLDIL~2Q!>Cb0u@BtXLO=@rw*ECd<1x-+Gu0i4~5BulX&4IU>!Az)TzXcrD6J5_)Wn0G7 zTz3fB3@ehcK1GuD70Le~<}bb~T+lHldxt&V0ebu4_Qt- zDli2~6~{^)egbd1zanj})*^QSqeQn|7#ga3MQYM-Yn5c}SgEE_WJ^Z2$sMky*w7Lk zzW@2*y>(}Wg2&mu5_BkL$rlkqXTPedFy=V-v<6!&0{+JAgk<7Bx9yLIH z^VW_M&p*lFft!XB)Rdqq zD1za7x+dO9*Karzy2bzTiTg;>JUet^O#{a}HBaJX_EQUDHU6^Yd@I^Z+LD2Ln0h2| z%4*pJr}T14Ewdm}zBvJRPt1vj)^CUOSX3%M*>PqMis8$&jdFZ7z*LaX9?`g}wm2@F z8F`MMEx@k{Cq%WTQ3(6(Ms=PMXN490rwUtS8$7{tSichKVpV*oMK};p(F*<+ljRGi z|Gnv^jMQSAw?Osy6U%tV^npo*=Q6|=Mr!Fe4AgHTW|}YI?{!o1M)6I__5sC7bV4|1 zK?CS*Rvd}vC!h^8>m?*}&RCG684FeoZD0~KRGde#7P&&hJBojo0fV>de!kly*+Q4V zM5dnpn3lDpw3%B9xo;UK%4I8l;;|USbKOkkYCDF?A36k?d1S3AjbF_aHsdt~7z&6| zmkJAH!&VC8evS9(!80s%)&`eju0gTY9@|sEu3k$tSGT>ClMCzbX7}uGk9bas|(wNQ^2SP7#KM%+YO^zpq@y%aJg9d~l;ii}nMvpr-Hx^5GIgJC!uRRXKL(gQqmxv&r+LiMIaR2>Zl=GB^5p^3fGNB}jK5_yFk<-d z(697Jgjlrpt;i-eMvmxU6=qbX;dp4!bU6U}PQEUGw3NfM2WH95+?QOL;QoF0n?FMU z8jyT(P%KP?(UhAZaRh|>Db9t#(q_?s0=!neS4@ob|<4xIrZeF$^RKY z`~y@RN&_gOiL{0@vru(yZ*m8Vhutm|@;3#))eNC9Tq4WD*g)00ioKgV5vQO);|TZm zIw7Sln(u(BZF^*2EEuhhrvBu7dBopQaKFCUl+>{HIh#9o?eKT_xzRz^#KToIw-UEl zwmrGH%rnb2qEcwehTqnKclY$c&J@2yo=n|Y|97T*`6(?-x)%&Emogjx0Ndx9XRGJ- zo=POj6*4zUbUgy6kb-@S>x2SHon(3?u9I^!`j)bSW0I_8Z56TkYX--d9~z;m#0h#) zfVG`tmzUWD) zRB=f`g3BYFJ#|YP8{`VYvYK5s9J@JUZa{aD2_vS~(F?UM^~mZRE`#PtusH24F`W;! zBe>Z#vh^DfM{bAup%=VP3TNOfO_)4C=R2u-H9~XJh@d-`*Kur(+rYjVlh16KF8R1oLBk4zl=K@z))G#^VeuRYks zaU6n#lvJNtGV73*RMl|gn+j&hhfpg0&iYa$NX3%5ep4NJP`TACYz;$;ig6CI$?5ip z1uxJ$%E3krTWCyZBD zf|i%}?ZtLU&vz~hBn*ruF`)F0BB8QYw_OLCOq!`vP@jvew95{#7PfdAk&=JMp8xJr zfQRm7X+}#IeUy_PS_q4sFGyRkqgro<;w6jW@XFLPI_TA|=I4?rWf^=~y zum5-2_@BG!0o=fL0yHb!PoYrEhGN0*5D6BY!99STnu`Z+oO2I=V*loy2$CbIbbMi! zjiqn`nJY57dgLqooH|J5uqpR`VwX%RNBZ!g!`w@L#1sz7Fu->b@bafWZ8cg^0>sf# z@m%b`3DDm@{tLv01mMxuqx^mXwo*J0ScUjjPQCPB5rc1zNV_&LwA_ZVteSgVa5B${ zjR^vZzxQT!tdr2^aMn_WCSKhSjDt}iO}Bl74PE~Bmeo5}k-f|5o1oN?Dv=0SG?Fitzu% zx?Wuc%Yrk4zwXMvL?rMFfKcr%5fNSBv3?X@VtQ`6M3a@dKK1F{(eRNaim0h zNVai+ev>U$1q&w>z#^AGQwBET6{LqC3#?D%T8LbIZ2&eR8X2neHq9^9f<^>OY&f{D zrY8+Jbvu)OKK<*ZzrMQ~no}J_v=fI6#qDGs z3BgzUi)o0)58h zLD8y_Uk_~201bsFoUr-tJk_5j>Yoo^xPZ3~Cj)%Pe?EnF=eID#SkXln=MuD8FqcAA z#)Re!*TDQsEIxO3?-#E!V~>x@=76beek3A=uW!iH5AY4(#Tz|x1rC=poyp{{`u{%) zz^l@Lh0zxO>*@cw9$-@S58w#YX;l~^Dh%F{xEvo!#E)N?$5}}D4$5XY`1-N^tBmgMgCAn z(fxp3iSBara<~xxP^Vw$A(qm#n4Y?bPi^fcopd~` z-#KgYSq_z?c-bSULPGoVmt3At5c4i@e=7Rw1Z;&fQFQG`g`A>^!3{ANj7t&2Q`E2{ z%l}$ewp3TB>?$2NPx5x()zHc`fRTDM!J?^f0mv*3@ z?3f%h3{1^09yg^iZEj?ObU6WD7puiECt7y$G6Z`LV+j6huTU8YzUk>;;hgqp9sBhj zTFNT#Vt`pcbK)-@wK)~(S0dHU7fXuhwN-9dLDMfhPp%O)SF3)HgHfD?OGCzsrgQk6 zPF1Ma-4qr|KVtMy1o3R0ffYF*gheCCpH}lXID>q^W2a##G9c3W0byz4MDs|d^Vg_~ zg>u?;8I(~O>ksRwu_G&z91~5JaB*YWnkrr|JqGZrEN~lAqt&=We_l~C-Pb*4AuIlu z6#UCry49fyfti>Cxnp zhuNaB=$9>v-NnGM&-VN_4RyqXE1xDO7h|9=$kc}R&+7=XD)MvnIiGk?CIv9g(E$z-Uc8}|F%1Os7 zvPpJ5ioLunE8sy9_xU97@Z(+D$_V;sr!BvHbMPvhTi^lX$J4&FcbrLY2-om>MwPys z#Z(ur9mHegHcyh+?8oi5ZhdDASm0YTqGI4j?EPs`cSgiIEcaR5adT$$8*)6e8*Yik z;XJK~QHfue3Pui{x{}p-zZ%+cLl(S+5jiP*foT#DlMr6(+goj~vxISyxhhhRWX*Dx z$V!%^ZUOE2Kkx5jX516LU*i4o7pm_wfKAGUg0B8`t^O*hwMb&ceqcsNKt*Kltj>){ z5WKcTyt~z}OrF>N6$zWP25KN%pd+n3abT3pCRPe}&+J6(4X-e4LMJ6cV`#`*>v8ML zvNoTt4${S}qrUgsugKj*zbqw(0*gwnl88bIr@-20e@bbtVKZ7?x&R&$TrLY0@YMHV z!dKn!^`Z&dc*It!<{vy;tvuh=1dO+kAeD>A;H=p29!Jc3u#jqE-D*f|owv4M&eYMd zS4r4-t zhsaoop!8vopT&xmOjk_bl_$b9HyqV<)~F-RU%z<_RP&NArb=OFYo+>z*fWt^Yj&6D zudUk&>2piz{}km`2L01W+GeuVC||1XSkHG=DnO{0|83AnEV*u<cK!j2|oN+&E>C`Gj4Xu(R7QwD(e>$sv?hL5*Tm`c+tu(cj{>Feec7L`y& z1n2nlhpo_!0ZcH)c)9QYgaV-6e$@gI)t#;fD>0dj)^(m0R+K1gpu&SXzaT_?H)xEb zQU}YJI?=ya3w~bq<$LE{(;&dJ55TZY6TK5nV?EpL!{Qr$y1vy`hsgU8#tLDq0;LBp z$&9nyP`%#E8r5phdRr+K7T{ldQLXxhw;)Wcf*GDQ3VURX`7$yt7`oa3y*p;*_14B3 z;ETe~BhlbJmv5gHJSSn7=t zm0>#U4isu6eW0^2pVy~4N*lEV0Z=Kr+z&!*jo#Nq3KY#EW&9{xmFWH)CT-o@p^-lY z^z#1mif0M~DwLk4nDnoj^G}Uh9^d}!$MUbLoqY3~SW3oBw+qH8`;W#Gxu5;ly97js z60Q_I)Vi+7_&eT^!egb&G;g)EFlW0Y0{N&d$Rc9CQO9e_UkHzv+k6!ZCx{(-52+N} zRWN;lFD2Dv0{S8$k!socVeFa0y^0_tlB$M)_^RCh?D})2PNR)N13h6p_IsUfAR#Xl!H;=WxY+(wswQ>6~{4_ zGI^?J2_5#S)j(&;!w+3SR5)4yTSEAb@-1d+Zz4L<0cb6Iq;ENvFOk{_ipbw2u72P7 z7kK!+g>~gB3y0VrQh~$)VA$>m!V`e8{`1)W`@@@`%JgB{E&QK)LUV2Rr-WH8W;i-3 zSj`pTRy`-*VM`uL#05TGFItD=upux8B1m}VTbdoDNbMbOA-;x$?XHAi2{U@dXZL$7 zExxv8xor}hQY(sRnHm~GC_nQ;0`%KYr+Z0QK)AGdImhg-dfmL(9N19-kHuKgc~7iE zaF|IR1i;JTjFcRY;MY@S_}blI51c9*e1G+YC-?AtB21K@)-Zee!V?>YQ*$I>>C97E zr^>h*Le$xZlVG8Gt%ib1OosXVaaRmJdmmt`ti?0SAKer6qo*Zo^;995)JPAwHnxZi zRmzkW~{S8%I{{uOtY0gYXS7hX`K@X8MJ%$caTN~jzKjQ=QtL|nSmdGH%2eu0u zkb%?TK{8b=pi#dS`bzfORvttY2rl_AQPlkkT>XUp@_(lT|LOYXK}f{YgyOKR^j+@J z>bn;$-|q}3qi2iWpdL__ac$G<$pB65C(C_ad{O|^XeJJ_?TJE$+VF21$2&ew$6E6E zx$n!zO9Bt465bn|?Tb#IPgvb`pMLw5+q`*kD%6QjA~!vlVq zsqqI03wim-<8DM#r&9|ADO;9BmLFc8%(-kPsYib91!$H%BcE0vaRhlbh8*^X^-TDF zC7|$#!h(62VG-|l()m##Aa0J4baRozupQy zu+s6W9k~ge`Q3svD3;SBq1VN%k@bjE^P3t)|6yQWDTZcVc?0$34jPy{0@78Jq(7ZD zv33+-1L$J(`2KJa{zga$%98+$eZXj1szLh~=~LEnYT0Wf;_o29^dnE^(Tkey(&lo1 zC{2I_&tD&e20!zla5AqJ3aoUR5=SIh32l46%TPD&eSU=)0tyzS3ko3F#%e?wm9xMy z8@=C*(WqrQZ5`7^b#{+)kcV?;h}N2I7IxJ0l}18OP)ZRCa2SJuA`w4}oPrV4de^&5 zrYiNe*L+dyJ@10_;=9fhfL3gtVH;J_BQaW3lk%|OwW5fGn}a1^MfnUmICVnqV~mxD4AlJfl`4BCJ@V2KE|baB*jJ&AV(j(9?Atd4Xa`ZwUuF z@)55kZ0$_IxAUkXqxxUM7m~_S`;<#ii@Cb5`jAJc&kW|Etn7c@afey`&L~IcL6t*% zpJr0ZiM#eyp-y3=e_Am_N|k(1ZiT}HI@Ad6Lrn3H{#0W4jhMc)eCtz0$G+`Y?Rwhn zui}-nNqY*yYhC&usJ;sybG%KS2>9f_n|RP@JBw;^;=1vOk0{g}xpKQazGDzEwqH6M z&5`|_dly_Xe;RzcW|M+PjHODxJ!%4SncZ@;J4+AVo3f*+qkcx_AJ&n>H#p6hJf-@b z4BTxB20!1l8rrp~SfaH2r=iZx)^FdcbOq1RpI6b!uYzM0Q{N&I2@Fa}fe{X0wE zSbHKRgKBthG#Xd)PCN$7Ao?sl-}wlxr~7F=RU)882letKx4s5R@hf^*K3HLkq*%}*Or^V1r4M2V&llQ7bMhC7{Q?=me(tHe)0#uH4EbNaBzN=){ciyfq z$k#{DxbLnBI&sh3x@O2`XX@Sf498+1_gM0Rn~+E#!%nit(xKL`M|kEM-7p!_>ZEjk z7u*alxjAGRFVj#d)sj_yb>fHjEcVD>x!GB5978pm%=filS$yJu2-^ES@R`%2cdR_r?GLNyOC||$z+|4T zn%w_c+PlHQ%s7&0Rh-di;&371wtcJ0Et>7Of^J>#IeJz#bNTc?=A&$!bZ+({2%T*N zvL*s_+aD~1MQxu;E_uh748TpVcP9c|0fWUC8iLg#8RH9tJr*e}6xV&{D-kR{$z;Aa zWu3*iQxq%^XRY|KP#c&Hd_cSK6|-{3c~dkb60pzW-cNLzr?kc+n)&lvu@r1f+rj)P zR<()@I<=4r6n!$cH~>4320QgrL&>4g{^Y`h4pl_O$Fz1xf*J{nwK8fnX;nYJ4naPI z$z)&Oa=DtNv9$5}6;`Mnh-|zI`CR>!yFOiCMvoiwYFfQm&@WV;4ebtj>Q3}@qhgpa zIE9*IY4LOHmg2Y!qIq-}l$LaJd7e?AR0X^_memto8b_4^vD=LTB#IoJV#j{`1 zi_TS9Z_5-|p;uWO;Y=up%Gfvvn|_=uEV-iLJzW$HYb@??wOYHrvt~%>i}xzyJ#D|y zs9az8)Z$`p1V35C*K2vXqTpC`FAEJpA{opwgN!dRfi;&@may}5=$cdnW~xF4`K^(O^@V~#y(qI-IF!AT+H zJ3!nJs>5AeBoBoh;9Dp8`O!wNr)E)=b{|Wx{Tyb8Hu{}If{Z-RQi}uOe5Hxp5Hsm2 z?SmtZA9D6PVwDwO$m%)1^EqNwF^=-*u0!#vwqRDry13t*2g;A3=y2H_=?T5_I#Gn7 zm0MoZR6Ys4(=Dhk)nX>QHic2ei59q5UAk3IX3MA)S@xHvJV-ak{J+eDNLN`F9j$$@ zwKU8|X=yo1mq~=mvE@pX$!laTqPRDJ1m7CV6hN4gB5k57Nk<;2^P<=@w+z+_1|G`k(>9#8@=%a)4xh6am^obSEA@5{fRCiO zSiZdb6s-FEXzC13`=`xBVFOGi%GH^z-M^sApY8hO{K%=qZB%8gfZ=b{-B1Kjm?J}o-ro7^+bN-bzMQC$fCKVw zM8!j=$qvkdU0Pv?d2@T)7X(yLBz4yS=u;D>_wX6WU&~o3MK`+rj zV9_v?2L?Hcb_83ufmAh%c5fh#uPQH`bRiIw{rweAk&hrQdeb&CGCg0uF zC)%{&d93Bz_l~taM0`=FD{VQOPOkO)J#YK0bGpA=H3SChIfzX}`&{;c6)1}lMzl94 zwu-o~5;hZK{!W^ammpTgS|oe3b+H#QS8|nT#d2fV+vqc?acDfoL|IoKApvQ*Sfv?l z$nhLyIp<7~)^o+R;YlZLJV6w3jnIRb(EO+Yqy>cS2p-F&+em)nT`erJF07M$V1Z-D zoAkkod5eZgxuzsE+^h5s4V%M6h0YZ8v83_zl$!!iy)PI~5m#7cOog{a)9YoT!Jx8XV~2%KG7F zoEcN*4q44{M)qRG%P$PYl3#C(yev8=S@sdH0F)>qdy2{n-5eqCat|q!Ztg*<_)fo< z23TqFMumrgCImQ_yy9mBW290ZhM^?Ao zacv+dn;!s)dNp|;*ePA{{JBQ)OM zTJ;GxVOYzGHA+4jN*>5?Rt{~n0GNd6kN!k91fej(vILlCehlU zNm{x?+vWb$mk(`ISheG~2P?MeCpfDgmi0dbDb-(fBfMzIeD(NHN=4LLtPaAOP}LD< z?E3yoSA@8zcMCOTb!It#iL`9&Q<|N+5O=-nEck?Ai`a}T+6ac<)Ulj_C?!_;P7+Y* z(LYNq6aenBgo2D#8Vdcc^7{}Ee2u5yR1NjRt)rf|mO!qht5f$KR-_H}$^IkMLy)F? zYVNwv)mQ$MaWa6JIH$|47vhAU%|((+*zrC~e`gryx0@x-O8c%a%13Hg~2kyTendk7MMTOq0YZt~f zVp6|}`G}NEg(X{IY7Asq1a~tU5%772-)BsX2L`h?%Y}YG#J|NPoGJo4xpc2kHz9s( zri9200w%l-tDav-R*iurj_QnKFAm)dlL*75xLqY`pJR@&KjUSFYrJf7@7`?fNXWUw zTilpDj@~LW+=GWZ6*P1ssrl5ea^no^@gfqXDi{Bqw3l)jaJ< z2%sEddvEiy%&r8BxyI?S zzkhaj`GM#0ZbzVYMZZR8`PPXbSXQB#7kJyvvWu3r_wo>DRz^2c0>aI z$bUoItc|>G#oaK3D+;Y(;Ryr!;H(mT)LLUr&ODV5Ik}Jlp<#Q^y~1Gmzot)^800t z&8o7SF7qk40gt3^g#k~4>Dj+^{$k7z6u3?NJ9!BimK_@+7cVrDl0PC^uu_&%w@ z)h|YGvT$<;)SPj>H0X8Rn0Fx&#`dBSLW}uS?mBAzovZNZLsc{gKDE}i`CfAn$Rs~B zh-qn=sWVyqh@3J&JKBZMr_`{-onb7(MSvD&8J5f6eF^#puQqfsL43pIuX`

    tIqEmn?oDx@cc>M}fj+(lR!y+Y6SF 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%0Adzm7s7*Yi$K0xmCZ$7tVe*WCPMnevn)eY$EDg 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%0DMECRoFamaMbQ zoY<^kI8iEFYN$eDcjT$MHSG6cNjq>ZG0&j6lR|AIPC}xmw_L+OZP8BO- zak?sFu=gjYy!hL6MwGa?N^L6)Y;b`(@d0HsvqG&?R|oUHz3f+Y%qkwgr zM{Htv@+%Q1sq})5&oiDQ`w8g`(nm5+J2k6hheNlWGUb#0Cib35zW#xT=z${)b0(r7 zutS>fw03ow@?NN(=meyWhgNi>C6-wrsE0>K*QlDLRW(#*v^$5vVF&FBHIz5lTE;9N zlaU4WV_(&VGthdYomf&F#zm0`ZYMRkxFcVk`Krq&I63irc1sF5^Wp=$^p=8ISCZV) z%LH+K^Ja?^mM)g2qe|$w?jLgS^*^grg+Ec7lIK~uj0z_>O&y5E$BoizE50H+^m_cQ ze-`nju6xFa50uxq!pWsCmDlZmb=;rbt?12A5yLf`*dAO|bJ+HzIN4+|7*irQQjB{r zzf17A;OT~OR{wFuvu)(aq)}bnDW@W0qnT*-MxTaLW2$s3cx$KZv?OucT+Sj9cEJuw zzsaLU%1SBy!E}RFbCp(D)RW1Ry@c|e0!=2CZV%0UE0S0;_Ym*vPUT7#YD?7ZBx=Ig z_%x(?84KpBz1w7!rypIDQW^!)l+x;`EwdL)>)&c%j!$T+p4_utc2CQ(?{mEX%Ny;1 z73Clw05I)pVXX&_AH814gCW(|c`~QF9FC7TPQFK$tQw~n3CiIzYx!8dny@x$PyD=@ zZB!#U;bh4i`H#s|Kqh1Ds>T5BPw4IIdqhJv_bPsBc=uh0xO&nuD=rxSP_t{etv9lg zOrr#$mco%I&f2K~?bYHaKrtKRV@!9>e-L+XfJk{yV>S^*`e)nVT2Q;P<-mo-p*87g z<5I&`YHlR^osg4T>ut4`Lk*R2UADZHR!n0Nk@4A;-$5yyvWWS}dd;|uR|Q$od1p4u zY-1`47KY{l1}P2?7H*(d;7708Nkx?=xA$sK9&qN-A=VSQHF zPX@Y;H#Bv8S&Z#OzfJpfnohwV!P5FBoyz6w`NJ*Ius}ZL=fHN#W*uIzQ*jx!_TU8n zp^qlEIJwo~z{cHwM97?>Q$N(S9GPq+g8zlmsDMqfN+I?7 zRnEfdmaD(LKQV(>q~ZFGSKFqcVpmgER7_mOF26nq11C%M$fQJ0yn5YJDc7nI)Gej< zIFN$fWp`-Vz|w@hjxV{V8;@gc!aj6(%qEV3iAiD&qZwJf{rE)q{zLT*$}|9y~}^98er|GUu%;QQb|e;;pNxwI4e9e0|a2 z)?HS@9toN8ojj-MYNyTW<=z}24!W^MncOO0qvMXn4YIHN$k-VbxlXYiC!n zw-X_uda5ytDn2uiTXAqCKpt@9si;7AH2iL04PxBj%XzSyR8|So<6rNPeo>2JX4RlP zFepyW`|8v2q&J<5yeNG`LDH7Zyb%-Auklu1S}de+5A(y`(1Evml8FU|?`; z<_X$8-J`M7S0S9v(=UKXL`2@sn>TOpXt5vl8@#dzIca(cj_rNjP6D8gu-RmG1ga;$ z$pG?y;^_azRu_m;1(k8@3}Bm11|OdE#A-Blg`ON&;rWgf>v^&6L|n&aL?7Ii7E(dh zh}vG@YTU$ecyQJEv={X%fJ!Oc5B);`CQa>)8)s))c5i02lJR=66>F(eqo6{Cet}(} zM&=4E>S2iiQH!6Ch-{$wfpYHUT=e|Xw3==40*1t@(lOZ7M&<_HOzzX*oaF`P$vCyL8cIMlGd?5C(_e!v4$tTVa?9U%Sl&t&Wf;_ z;S$p5R%wqI-YU)M!NYC-$Q+$(UmUZ-7Zz>{=#2tebhE@%I?O)UH(F#^dU9-# zlAr#CaS#+5xL>Tx`^Jvtjy9_t))yb8nQ!y)#%;dDVeZ1ubw$`Tt4q(*aCAOk8fb`s zr)`B#uz5y!#juwg&YPvVohjQ0^f8ZV;pW2|J&K%6`HN%$5&+!hacG$gM}Tk7uek zMH!48rkhsDq}M&~7_RM$yDk$NN~xkp4_-9}CH)v1qw%F16Kx(p_WWh5ac1dU`0C%uC;q`-zE(Ik4_( zo2Zpmm^P?!hjd3&T4oYAO+t2zDwXDQ6r#vX92f3ZQ=>1LeoD_e(KWt9HQ}%|vYc&X zo|4#IVe6#rSa}u8VYiR}dmmLCb{2Q{vjp8}Q{^pTJgHQ%fNYg9d~3YrT;saajhv2s zJ5MPl1SHYBhQFM!UIO?TD{9IWKu-81Bwvn-m44bmL_wsD!7=-cZtt2M_-C%k&*OxD zB5P+65Y<~#az?6`)0h$4h(MXBtbAY2X5mYb_LunBCP$Ou{7w|?nc3CNba${pDv?zU zAN_p4n1l|wr{g2rEH`{UU~T?v8NVIZj?iFBk5YT^#UpTy9^d6A6LZKq+FPm-6$~$)~rsvrgqxaFCj`eDG+ZPG+GA0!!+I9Ar<7} zZkx5&3cG8l%@0ZuklyL1z5y*Q#rYhSvhfe0^`ndVG&^DMG98{6bUXU1Y3D?dV>@;NQh9hOyEhT<{b>$K9K%`5Q+9ZrXi z7Y8n4VI2MiXu2kW$Y_Hlh{<@9RGzp;f>|8nHHCQnHKde+*A%xQ?u2HZnb`kE%ip`O zCkgzCYC8k(cM-nfO~o#B869rK>99Y`ZMzH0V}1V(M0}IS&I^kM?b)0C!G{8;wau61 zOh$t!^Ew^s7g>i~2M=;~4_0@zpX9oK1<^170uL9T4Ovy$<2erO-7y=AXdSdAmwOVn zt1D7m+8KJ-T&IWTFd0}NkSZLdevgLTM6L+4U$O`*qTMU5h7^MDDiprWmn)^EH|z~$ zPXmX-in~ZC2ODB5WT0x+_lJe5ro~X{Zr6SygBxGIv>e|k*gm()+YOJ$!55dWv5IW$ z`JNLyvC7JH*wLOH%r5DeKc0dPfUZF#!(3&9j^coqp-$@=20YzeQ{~cIOxD$W;1mR0 z3t>V#zJQ=G7GnodC^5y4-CD9~?HJWJV#)RJdeiPW?cez#01&BSV;!kNd=UIt|g47s{7ZnH&x z0OD;=Zy_K#4aq%gX#`uzdaYyjT<_;hCkBNH_|*NO*>O`&2%SoYf49>dh`55w?hwDV zIyR7Q4XiLsuuEymAv&$rew4#QEaO6y<_z8Teq_V8X|+wAOoTQH_cVd?h!Y0l-u z#0R$Ptv6C?(I!|&famN^>}T?1F~FnR`+}Qaz$ajHI`85dsqjgjd?+dMv!@joPZ7)( z7N#q?#|Ju?mpGu>qIqz3K5WIZM$-!GLo@}I9;m<{WN7XbM3j81_kJ?7Of^vKbbZ+ zejdcHyAH0soYwe#5pxK1VK?G!D?Egsz{cu}c+O)yWh%aO&f~*pzhcjeJ#s9N8G3M2 znfw3U@@=GC)JklnqAD>+!R5Se-`~FxwH=~v*jZKh-tw?ddl#CO0)nr|o<4nj6ZZzujT?c39XE0+kk%$d92U+#Lhgf2crTTwiKxc)C5jeuU2qtdct|uR`kY2v#+8aUDxnzZ>Ug zu&NqbGV#hc}b7Pfw0s&bC!ON56H)22KaHXWH#EL8*eJS&Gz*sS-D< zV6Gad-HyX}>*=7Sy2htjOlg}0fdWnKRT~i8c)bMghQDW@_Zv$0u|z-$O=zL}<$Ljk zAd*U#zA{yz`j;w%y2E#e0m$fp7%Rtb&Y%t0pQK}NZ~oolfP58j-`So-3cwBk)xXi} z)4Xmv;e7M7>zKmfDpfl+DI36T>tauHDUMc!lq|){H)S$z;%kPY4RSV|!tG!-O0~90 z2O{tTU%2)Y_pX{jz5Hq_1f)t|KwoKJOuHTuELoZ1%G{GeZ{PFHmS(N0ET+w;;zR&& z30g;Y$Azq0;L*YcRS(Et7szPOgr3rk$_8$d-ywY=YDheEEsC(`dbg3lHV!s6b`-dm zwIMKXcf4lg`__z7%)*Cff`JBkt#&ew~uzSjjYLyq$=CK+}F&0uI6~^cl7;G z88pVrPdGA@eLicZ9=Ep&qj{fo;*7Tn#tS8hI!j5E?pNq64b0At&k(@6^pEBCxfbchN~dhcdpKFRVY_l%f>}rW;FxSz(UHpE6fWOMxsH~cClqXnC)=_UgQvYV0Sf|v{|@Lvd`#r7 zI4IoctzHz6twCqE^o7dL(LpHth@q6cg6GQkoa?X@2KqP6I!6G|y@HMq{xh|x|AE;m zcP(P#c^6!{{D5x6E8Bt^>881`i;umZXRABjPap3@J9Hg zriv``CE_|}RjbtrJpfI;MtcK5e2(W0g|kT699yp1Qz406dW&SRxqd*!8m!DrUEaVNKct>S>J zcYEOK%^NrL;5VCHH)B|~!WxeG6`E;i%35ZI;H9NPicE{2q*APM5U7z`gXwrOJS?AkWQKMo0=Z;lfSEDrU~Hduga5zE2%_h6S_)X`Fvbtx3v#C9CQ zjSR*tiMY3?o!9JOkN4trzkVrt%}5Ds5kF4?ceyq;As*^V9HMnAmKAFBanKyE@8u-yK!ksYgnY>rzi9}q+cefvjEM(FdzD?TeDYbJh8rd%QqSatt^ z0IwYQn}{Q6jNEKn_WCcal8kS>=i%d1I25w0s=0T;eD+I0Z=tPyR(8d=98Oo`Mi-~X zPJQh$Kj&Hheu5mZ5Pps+{v1pj#I5b!bnp3i`PyqvZ98dtS-_DIyAXLtP;u*6jc#jL z80TQ>F0pjtkeOs!Pg9!k4D6>-%SLCQ_+=U%yin_aoqw^B&pkC>iH3?`=ug5IJB zE*QlIpAwTd7vX^J1)ay|**ufx{;F+$SN1537kAxD5#aQ%s1dlpZSjOJSO({eDmVU<>ZsK;f3B14P&Vp+rGCFod zl~4Ek>%}`DS*wlo0~rR=yvmw|v)%LVj(m+ieJ1gIv84ec!qtAI{iGz#_o00()qu5R zc??dFvFCK!p@p^Q3v;$cibV@`IxA-6?LXCEi z)Un@1j|j5YX5IKwIhz)a-J%sLX1nmxok(&}FeUM`YAN@Pr*V+eJm(@A@Opmtz!acf zky~wyk15D3s*4LIDuMpXQ6ft;H`c)gTCBUaK>SJvB^0o+_3zw&!PjowU>C$KlA6KgYU26yY$Az%;DQC-CJLkj z+8_1$)@?SNonN?FG4k()KerE@7hE}UK*8=h;a<#94tb9CX_%Gu=SIYeL|x+HTDGj> zK31n}a4~eQ%|0j&8b68=ksCzu@bL;{&EZ3hfMgxtO3~E=(VE3$5(X1H^i;45Uxhr! zACRP2m{;xw(SzQp55r!Z(-LRaTL6K{snW2j>lWY;zQR-!%wa)e%_*jeu+pQ4w zLC`_ja@m0`s_|jBD#<~g@|Mq~1GMeAL|y6#Hj~Pu8vS>#sxG&7`p%0SGId?k2+Mo% zBiT-j6KK|M?(+S<7@pTUxVp^S{pm~b#=9mqi$zmU&8G%2oLq}QP*Clm_vw+SMZES< zF1}=rqKIB^e#sVxeYqW9rb6x<>i_8%GcDJU+Ch7EgRBGZ^`kFYb+)pTm>`6CFb-`& z_sThq(LKBN_Z_(ai1Qfn`20oBLC=@#twnLWQKjb@RHM@OIBpgOYAJ0&D^t z;eyWdEtg{eOJ|n3@I_T+i@|nU^L)c$Op$}^<|$H#sx(^lmUF4AV%8=ZYrPx7OsC@p zMrseKoc*O7VKBA2?K}-^BYF1!ZA`o%HZK+`Kg{!+tqbVJ8MG%Pb{LgaM7IHXm3>?Nn7xJ)Xe5f!T&`GVSTJypW{U($m>e^QNCbE$ z(6Rr`&~eACo+H^cc>p3|vhyT)&N6`NybF~(OQ*`q?ZbYw>ptC%xA?lx@Zyjj z7PC_|wr6)z1>{ax^l}eIlx=h#;iD5LXOs{F|GX4<6C{CyuWl$j;`H>kk>fE-SYHv>(MfYz;w3zhJ1H&eLd^66|31%O!_{u~~S&kHH>k!>|RRTMcBjGQDLj&wjy(3j47$U@7|u7lmeXw!1+gPk&vG!_;y%(IliFoy%$ zV`HVKSvn3aWUC>9OsTrzuRwInxTPj#wrPd8y~K$qG>fvZ4sLBj^Hpzfnypkp5}_$7 z70{5-n3b-V`IV{E|KK<~;_Mf@{z1gOL;F4w?SZ(u;5zP22s=rP`mY(_YSQ`(VpK1s zun}efCLnHH7|+P%g}T(Aai7H+j{5%aN=a0@N>S}N>(cAx(%l(2+4ky6r8OZaz%#6E(TuijAc*>qlj7yH27D< zXvzjW_uV{o)3`V-L{v$lg9R{;N6ZTDGBA$9MRluMt6;$awns^5_#YgldPsbZQIu1Z zzaE0pS=-t_-&l{}{8-(5Q6!{`xu>cYygKUz7E|vVe;UQr{n>eS%0B5UhSmY~|qwE!KiI z{P$gZ|3?CB0-kL>mEQ8cxGH#&S?nZMgp-HV-`h?Fr#(qRs<=3gSfPoLcYU^K!AdRp z-u|8PQx@xt48)@NR*zl#eA z60`LJK&UGC(60HD7TsYpXwX%wC2K!DDd{N0I+Irt{_0W4POuUE-!xN9(sb5WxihEG zaZ>01>Ksq|EJWlAdPs!%2i}98AE2dOehp{1jRZJ~LTOfPM2`zH`tydw^=0$O0gwvj zAxV)@cUl3a1Mz3UROlKQTue}ZG!=Vh8}h*)tiL7Ia*G+Fo81^K1zpGoz@v8xP&)Ad zz4F6xm&$)|Iv}t8{gVzT_4x020T&cy-?D@&U4_IqxW2smAMuSR;J=qfaF>4h&2qf1aj=$eszZu0 zlJeRld(XAo$g#8JLHSPuSIm;#a^HkX;NEI(L#ILQsXv0&yH2&ExxA zy+f%2As-S5l{>}NyF>{!^q>4JFcgNw3(A1M+9*XIoLS;CrU12WQLHXH4cuB`>o`U# z1%I_3&Y|dkc8gI6)uGEp9kawKYz;l#80j8!d2p$F0u0vU8|ICVyTU)uoZnJwzis^A zu5kTB$jLvp@!m*>AgNHqR_$|7bEFEsVA48J4DZ!t)WpP>V5Tq-Feh&u2Y2Q}>$a}f zEnYDMy-7?o>VLFq%%BaKXc$*@0l+ZI`t{`fbBkLF{>E@BYO5PFq*+ZIDDe~wNHI&A zYH4_a`nfv*K%p2nVI3lW4_;He;5$@5rII1GJpp}@ajoccBkUOQ;B66#r>px{l0P9n z{4v_1@%08@jz$wJl75(5NC?+aNS_cs7hNq>h99!WpPd*uzt7

    h!PXp3E4#iGX}M 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

    M-`vX58}jyPshKrv_{>k^b{;(J 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 z≧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

    S|4@f@+7pFiD6! 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 literal 0 HcmV?d00001 diff --git a/docs/static/img/debugger/2-icon.png b/docs/static/img/debugger/2-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..31706670ccbfd99a6323d1f6c66766e0f2a9170d GIT binary patch literal 27014 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 IN*V|MACM4BAOHXW literal 0 HcmV?d00001 diff --git a/docs/static/img/debugger/3-debug-pane.png b/docs/static/img/debugger/3-debug-pane.png new file mode 100644 index 0000000000000000000000000000000000000000..24c112da96f0548d80ae3cddfe1c76493bf79d42 GIT binary patch literal 83927 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

    5yZeNK3Yz@^{;+?$sU`v>e&IW)8R*R}IhYjm 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

    DLNZh-*Uy}H=KT-%y0KN>4}_v zN}Qg9n_#@O$fWtP!QgF7a-G6ifJ3@a2WwZV&|v$s0HuxR+(CD*F4oel44uBLt$scl zJJZlfbI`#PU52w%oMWy0VBNe@yN~+v6m*_BOO=6cZt2a^LkN!k2zPxPdV?%oacXFyU zd`dA8>OyZ~6)`V9d)Zea8;m!%n#yre^>}@|- z^eJUG(^y}9&6SDYDuy$_a|z?xivt51d1*Of-NaiNXLB?#hwgUtP(GT*q&#)gO_?Pk z`V)uk^s42CStsM5kyiZ$jZ-_z=uWjWRZq-H#l5V)LIWj1-BFnVbD?b-NfL9l+GS43 z>33>TqUbj)>k>Q$GVx$(6^cpRetG+)CI?8Pbyn%C;}!)hllI3PprfO&&VoR%w<>ZV zlK`ftr$e!{DOsGl+`iy|eC*tAEH-E=4a|REm6Na@unRHVVYCj`YY|-pW$kWG6!~+n zZjbsC<-J?C8jH%-De7+9&5yS-C`Bqw7UywLU1obzuKeM+QmcdUHgnYAB}0h}gb7++ zA1J#>o`i&`uhrkUvR#A*W9VlECbcZ4LEmohJPE8|RK7RtTLx`=7D^y3Y#e>atH=s& ze`q>rpg5^?Ws5T80y#PPLl@7?@(J|Wr@Y=k`4By4dE6lC_Ieq8E^gh^WF@}T+KoJx zYe_plCIBkuO2qK)ZMJWV)a#8U+O157)dv_4FQ+mXPVClQ$P(ntUh~!H*E%phal4c) zO@r80M*=2cH)q=xhY+1&<%=~m?516^HqUDxcCnD`iJrcliX>|hvz@x?06)N|P~qG! zyuD)Zqoa;^Xy1+xOmfy}gx^X|1Z253pTc~B@XAErA+4Tla^m54Z_c`iK?i56k#Lmt zs~1pF>9R>mZ0gCfi3ix?&#JY&2Z?>rzUT3xYT@|b(|UlOTvk)_=?ojr-yXwyZAej7Y1dV>AKk?$#Gqwbr#SEmA}fACmk?8oA+YZs z;99#gIBGbwD3Rouk36nWThV9wy1jpjGn*_&jRr6Hk&zbdI?;%H!kjt5Od~wN> zJwy<~z`6R!DZvjDDH+lc6oNcxq%(q0L;Qawg-1)Qx33~=QA*%8x<$84-+}MjPW9>8 zUk5p#u`+|K2PbBemm=?<+|Cs{XSJr!z-$|xV#ztkIYO`*^hP%T;?PkM#B6A6zOui! z{UvMIaNkZ5jLj$-M`q+ayiS;f-bK)yi!Mh~@D-ZK{oNBfV}qGpSJUzt4#s1s zsbcLaAST3-o2h31nxsNQ`+q|Y!KM4$h9ZEKqcMtOTut!(3r8sqE`^YjEk!Y#064n>KDc!4SAQ0&VlO` z^nEiJ)Tw=WaBu^_2pKH+y5WO4mdH{81_>l z0`tQVhYVf=R*_*Zp&%an-wF250A7hV{$}nu3cv7ehlDpj z3H`tI0>q2ZLhw&am(m;%Upm7BUgd}r138QUdo3QcaLt7Dw~Hd2u;1VB>J3ePfA-OM zfw1ct3y81ALg=-3ekis`HV9n8OW~y-!bgA0$P69yPg<0fmYwEi3d)dF9B)B2gw2Ug{e*kLup@m=d0QiNF zXp7HZ7U}BR0Y0QmIg5yZ!J8lh{r{&0HmV!{B55#ypXsq25gepnO8}eK0^xbWs(&Ri z;tmo3ePS}}wOvzEF13dfC2n_9Nt!PhCWue* zuptiZ|FN4;0CD2BJ+Pqo$M60n)JzXp>#^OwaGi}i3I##$hn5RD9w<8-JT!P^J@n?@ zhZpb0JKm%Ttb0+~df)mF;{N8}uc!YT`t3id-#M|E6*Ys9kun}pc6qtFpn>}I3vy%U zgtVM#LjBBbpFb&txYnQ3!ib*=1%MR1G+&PAN`8Rht#@028RS%}x5il4udA=q{x^r5 zXB-VC+JLJ$I5^Ztz4VP+0X{1bP8-j71vI?q%XWk)KG^=4a@?O`9N>rVC`T)>Xuw6x zjz!*Nav$J$A*|=u#v!u&8(+U2Li`l39yF&Y*Ui)YmMs!IS!XHzfxBrb`5SjrOFh1~ zuhPjx!29iFe2lq_s`ISd%fP~7O(Xy==CsoK#!&&jf8+9_c%-1+&VMRxMU3Fu0|>tM z|4^ltK$gx2S8EUjXUiD*==;)E8i0w&>oWT}gDZ0PZqi#W(Arl>Ph8xu0Fp9uEij_6 zvBJuQtp&U{mPC>*ubR7g&oLms53mUYLSe+*Y>TkG1|>ia+m+6+o{CwKLeM5CCI9Di ztQi&iJM-m=xdm;`wKLN`)kDWFPO)**&xO2T27-;4hcP!Ar~C1NWGMybVm$>Kp-sn) z-)}wV$?YX@OR+sZAR{*!Ow= zvZKvA;5FPVkrRj!l5GiaK?39Au8=6#{D;+m!;J;I&HEoj=50`Pxl2x5?J?upB8HA~ zPNUy1u_A$Nkw-{71A;Ab6@V;ItKQc8$Iyt5w1%ZA>C?;9b90)YCQ|p9EQR1LBrO*) zV?vGBt~A||pCqPpz__F&ws`XwQ~t*(`R|?okBqv-(#C%gwXeUQ9K;y}vfA2JDKXrP zaExO*07jcRx45X+LLDlWkTTQYBrVof;JjGUz2Q<9<#_7!TJF3qwm-CLf*OzqXFA1m zOvgGfrKSQs2R;7Gk^f@a9GH|EZhX#|aV?h2y5k-e&lu{2TFzn6T!?i+=n951Grg#B zJ*(`B7CcVtzE3CzOg7B%E~_VX=pXO+<&6FM9TBiUmZZHG5E*li7tjLf z4fn)FI?w`K2}ZHa!3@jrRG0bP!#6S9PEt?!no{KaANHk4D*5>O72ehY!BMBfJR?U$ z90XO&?FPDX^kJY+pgcf4tikmI_s;gyzV3@c`icoyq-gKtr{9JT@;t+4P54{P0g@t5 zfo-{gb)NClx(kfB$P1^m{{P&QZDK37^pcWBw=pwxiicUghU1lzw;ylyn`%V3^}&pc-9s&PIfH08j)nvvWm7FM3Z}-$6sYiQN*Empk7{Ecb8vPS)X9J zz~IR_vq8TJx#<9%`7@k<5GA@D8qVN&?69Qu6@ppd?^MzAK(WtvGCO#>WY?(0+C}2x zoHaK;Ken1IRpc562Imr5x!B@@JM7bipPO#E0muakrG8{Y*8F}uDSLwr;)}0SlYm9d z&#vam`+(NT&Vev67e>yTChjZ*Y)dIpecjxB!6TRbbrf#juN$-9VYQzx-|?K{ySYRv zh0};kD|+LCjEqdq$mzZ>PU96?YHBpJm(OklC9y2k9*F5)em4~t+hGx1J!j7tA-rCk zA2HRrzPi5Z5I7gyX|uSiQ7hJXA4$L+MS40QLL0rm3!_5A;Z4pY{@rTKOTuzxty3P4 zv0S(ta?o-9qU69HEx;xghzEYj>aS0`C4riogiKNDY(*#gOd9;fZX;6AxUtiVSIGPBDDo^q5&2WTY@8z&v$P!_aK31-Wc+dF|A!m#ulq0pKy=lo z&x3+=<%EtRU*Rq>(4f)lxm4~bTn`UI0*oA%xK!)yBI&=wIe%>50cyLgptixR$RIJC z4Y*!;;Q74vycElCUh+1Wfei!#?&LclVF|yUqe_ zKvr#f3?s5AhiIOgx&a=@Gl1c68VICU`1Dw<&O>k;8&W1@<5~8 zoAzEN{g2t&wI?&hROA#7+6;+s;K*>*KpQZ;c0b+x= z+jQcun9Hub8eosua_>$M95S$dupah>x$NARNl8C=8<3C8?l5_JdMcG~&3qXR?cVra zI03)@3Nrc%1VS=p5+7|OABN&>BV=!%RaZAH3a)bZ$|dJG49*!Zk+EJ^fGyq>6SlT4 zOB6LV&0sP9EM%|3{K6gCWA8*pqq`@*E$$?`Ye>>eq(RkGz0W`=MYNdZM7@(eL0aMu zaYWQBF}T1bV7bkC&go%*rW7ca?}x_)|q1ddDOthlg;;_aix@x6S;(WeHMn16NoU6}!^ zVtSJh6E~zYRlo@vUUSqCcSjMM(gH`s&`R&1&VPGnLrsuhyDC@Pu=tKT40PqK+?1+( zvYvx^JJPw&OyU87z^lI@w2`{HLJ)RkmLul?B~(BeG&sHRY!G0koqV=TKoDG@=Ifkl zW!Fv*`{xP`gQdR0z^5_+teyANR}y^idfEba>O*I#Fvs+pSy3+-UPQIGd>&*I&z(*} zu@zje%=}0$|2$}%Kh&Wql!viUE#XIG)ej)jtQeD3yqiQ6&Sqv#=8m%fzlrwb1SLgo z&r2%vWlAVaUhqRxhadp5h~cu45{u)e-_i*XwC9hqv97zY-_F8r$~UZ8S*jBpN*BTq zMyI{RjD8DdFdB95_Kx-I!ge~u^Yf=JfpSbS=Uxdg^COV_;;6}{O}H6E(51C-`TZqS zZs(;O%NnK!Cdq;{%pKyhV^aRm;(v@=WR)-L}_EWOQ+Hk4ArX#Qazt9-uH`&&i<@N5W;}cdCnMJV}06>zE zcIcZ!r@C{03v8R>~tI@>Vd*7);N!l?EO>8 zJp(zlgjOGp^9ou3nW2H8F`U=X((n7HPkU{JgKvz`LMef4r!EoYJd4o34AC(Z?0~CfEa18L6Jst^;a$@ zU;hArQ^Zy75oZg4d0eSNIZ*#5fA2u9+STS9b2kGa;Ki8%)EyuH0D!e7WkP&nEegKT zzT*oe?Pc-!DPy7%W7xc-~*hVyOwp(FlP=n2pb z6VzM>Mbvq(Z=?D?343K<3e^?rxGc{BsQYCEguulFZc^5Km4pcF-c$t3S9LI`NqPk& z;w$Ut%i<8PQdk$yX<+`f9yN$kD;xjhksuK95e1;HbRWgeqr%?auXuRFROy=5@v3Aq zHDIBOT_b4w#xjmmf-=38D4( zC|FSb`O6Vk`hblc#CfUzIkn1WlM(6;iK)nj&3osFJo)aqn zl*fON#URfp%8%%rFN}a3>5mJ{oS&Ww_^2r>#}md^zGNI&1iIk+vC=>PAkhAoP+s5s z0iOBNf)?R3n|fgV+)nT{RYyZhmPVv%Ar@*`NG%Pn&AmKl5kJ46p#!|u_7FYE;rH7A zdy>$HPF|Ff0y!QQD5O1C=E%T<3QJ$3`G)`UjR^z5A)>Y_550=WKo|rPp32rNT=mHh zMb(__o7WVz5nwMBA}s+3TluYf zTwVJCRbnV#q5h=6?*q|50cglrc-Sd+zgP0dIc(9%dw+G$4kGOt?mxdjiTLyg0>Aed zi9wV{M+lQyeA>Ahug4F?+Q+y2F)wez_rpU$ZEgN7>lEzRSdc)jid05SY5m$Lnk_)z zM#`^$W_cY!c?kJHipreq?KdW{d89JqT*|&2Gl!;sT&ybm{45WhdMzUYloK6aTk7^p zf5k5e-z+Hly0KxxE(W~hl=SrW-kWgEoidyvoip6Fwl?b}!n!)p&#mTyYI^b)I?f0J zqQng>oP#V3)hiqi*T?Ivfdco>Dbt-OC{^xHtF(v6#G^NQtS{+t+|<;eV7MY=nqaa~onjY)fg7l!H$M=$(Yh6aN^kTsmI!Rj+I7V5 zPXYYjgs6bX%HaNY6iM1uVZ0RnxQKS&3YN7h;^|WX}h^TL7}8j3s!MpYwhM*nQtxRO67hEtnz1* z$O)7REHLDn?Q?DYEMv;UQ&_uW%>V6PRTM2>Z6-4_^R5;46{rfi=|y->>& zkSulHRQ?qz2@q88t+0g1F;w~D+Ynv9{(_y9z=4W_qJ4jIMXSr}S#So|wK^6?z$r_4 zmCG0|2H-;X#p%K0b?c-?p5#hx<034J!N_ zLlHUr)y#JT&(BouyxSFA4Uki5*a2i5j=7aL0R?MSnTlUY33lZ>TYf3&kY{1i{lOH> zbbRg}AIMo`;4pIK4^8bLy%IFWH+f=24CCY!W;Icxp&O=u_xfWo>v(vHKeUk+<;xh# z<5sRECX><|jf>K1LDlQFazMN6Ssh+oTFTr$>4|UulI*Z;lC%!_3aE*?=S1wkk8TBY zK^Vbxjtl1;9ammGG|?+V)I&BJ`mv|EjX>hanB<`K~bhjnU@`fz4sy8+EORu zQ~R{Xasrh5&bSYRl|AI_QE!&$G0=tweqJfN(!c0O_DKC|Ycs1m3ajbJ-@Ht;3I(u> z=*Jv{h`UgKmOzdQ9ko<}MWxiPu*pw7J^2kds?Xm7BGnG;TDa5QK1rkKwJ3Uh?hnP zZC%ALhYrlRMrkF8=YIj&3r#n8sy5ztn?aKzgN;jOnsRQ)sHUdRUg+Ll`n&)zw~DGi z{nJ>cVxalus{)1nzT1yL3Zst1kkx{M{T|34Vo7q0tyV;YFt$fb*HE_6M05^iyPzGQ zJ18KR(lGgTn7D|AbdAg7XV*N0oSMhfQGZpw#t_=!rKcE(%`Ir`q!L&j07q>9UEJg) zocCHYPd?mMsQOAZTB+j{bm`J|an?VNTgqxvoK_46?#0(Nwc}Vyl@p9|Jm& z*L}7BLTXo60-zBqkb?MrpMzgd7-*p>Gp-;>_v4fqSQ)v|jn|qaA6KbIrKF}~k1S;3 z48B?ss*hfe0J=6Rmg&KRXA9sO5GYM*=p>l&@u>hPbGG=K16m*Nr$eUpWH*A5s#QSk zQS$H(-V=Ae`LQJ{N zRXe8`y1wudM)Yq3a?TSQ6az6+dm{vYNEdR@LSF-!)>G%5mh3B-3e%Pk3Js;+afV9; z5KbGE)m6~xPgq9;IL&8S*FM{uteBgd(>*zPV!bx0t~|e&57j=D=70~UQ~r2t7k~I0 z*GQ35>Ty=8xFN$UVnSGA;urf6HlAClVHO|-4+lqUm7w>p zK{O2I;S+1U@uKQc&y0eG8nyb+l)0zHdu6l$SzC%LElo5|`GX7x&)wQn#>?n!1TDG5$ULvsQO#FBB1HtQv=IRyDZ%lunTD{ghi(4D==}}_ z4%7UR@DB1?H!{|@^LM=Q7baHToyFtm+r3Le*w~>IJ#x84ipb9w&(@LDwWlh zG~{J+B%WP5aCk~9@7sr&+RbdVz-i8XM^$Yx*AocO*4wu$O=K%q?Yx0;ddmN?-Kj}! zsE5#H;l1PG+KT<8)R#jl?qPWWKLj33ekXuH$Gn>L+f+}Fc>OyMZ=3c` zI}%^}_f^Tl!`VmegG9z9qmO84Bnd)J(xjPhjXX+dYo-l*HMf4}ECj%eJFOjFO)uT4 zy%QO^bo9`K{vK4@@dZ^6tL0;6G{1OuekrLC`u!~D$p!OqhU7gXDlwwq{CWrBd}&jt zKkC)rZyvia0UZz`U&3>8Hw7?g()QiVy-In95qnSbu-&&6woC3Jk*8kL%qSoEhl9S9 z^DqFP^~fv-XMc5?+`>6d*T^Dqh|S-Cu6*KN-|8&`R{HSz@NQP6MI#(-LeqXCFe-g7 zVS^e1hM#(y%PH3ZyIME2lIAH%rWyvyMn_pz(c!n!`aa7`m_7%q=M!N;49Ix)OGBe24mAC1k)$HQQe&wjOEJuQ9dQe=Pep#K=r>yOn$^7Wt@fst~1mP(#2(V(a~LG#MP4TZ1l# zy0(7sMqv?;F_OZ7jq$#cPX5=$MJjRsQwewXC#w$z%VCPF@~28?5vn#ikNqh~tjDF3 zv>)GN#KZbJ|JFn`qO;R-mNwsB3-a3q&Z!jPjT}_1mkW=F+xgra)MEMhsi zX``ifHO)`UhxT9^3@GHeNosJ9?8JpXb?$t#KcqS8%0uoxKrQ=?*5!Z zN%Z|&T#cOwk{NeD&VNQ!a;`qEqOF=H*G&A_J%8ZW(#=ZE+`r-PRTxiwHOFf3{El^qDMLt1r5~<@E*av>uGOlE;k{iO2o8}lB ztS(>5i}c3Ly~nG^HirlVghKd@_9i3pz6;77A9$-Cgg^O_=QF9i-Ryh;)$@7RGCDbH zT7agOR{P=AE%UDfcLZx}yGLFRKY{Y!7l^C>8pnu1ZWET@0^**)1u0WtBo%j9%((6g z@rh7C>0mEqGzNelH#a;e&f_?L;926S; zA-A(imf|NGM;a#6w?Ibd&4}t4uu`FAob7wR+^%!p3waNl3)OtnzvcPArv#SEw$i5R zA7^w@qxP>`&2CR(-kh8H65E_5oUdQz2KTDJ`$a>`XgxkSiX8mhO=KK;+oI)d7T%tD z2Rd1*o-$aM%kfF9;YdwRhWN_BR-{=24I_R8)XSavh0z=xaW%gu+p=anf5;1{CXxN+M;VX#0Y$2r3nzj8s}p%D@i z7ZlS&gvHO?M*;v6mWJcP_Ll4EY6l?qqPf;gVP9L|30&Cc`NSR+Nm3=CFzpNyhAax{ zvFYL)H;x|4ZpzrI&wa)t&(cS4sK9Z{z@)tzhlU9*Zi$8Riq2C!cs-%Oz%B26WxO}bYc`G4y#Y0p5K}F_ zMxe$oijmGVfivp(eZgW0;X^Z$(7**$CMK>sFgmHiB${MkUT{npo>ZuC`A8=Pw1smk zaIV`y-yNweSvHQaZNig7wA)8=4n3;?Htq{r$ zl}fx#xFded!~I?*cp}Ex^qS!XeZ^Y9TRyTnE8TTaDE?wnyWZ_y3hw?DF!MwmphuGo z8bptXjEcHD%k9t^3$99e1upQ$CgF^D3P?e*mDhVtmhe{|n>rKFLTEWQdHPRUXgc2) z`-uhcV?`2u7i#XXf8NWggOVCnRgmQu2;AsjS*IVlf|0R+v%t2 z5q|Pjp;k4<69mqXn;0S;6FW13W((coM^(y!had^R6~cF@mErs>^b8HeoeAd^-AwuP z$}ho?Y@Ub{hwb{6+`Ur(Apfak#m`8XW-ed8O808WyMIeS3j~M3QQ`DBbTUCgICr6R zS`(?upox6Mki2!s6xXGd>a)$2MJS*KtG%dANG;~ntwIlI;_De3@)ynN8VHsCdI@^8 z5aafii-1;}-%d83k22w)4?C|P>qWBcrQWqLC1+0%Gp2d;4y1qqvz`!FpMm@o#vuHC zq9-A%u?nMb_!&9mm)Z+l1;J+f=6HN>H0*4{MxpGh4zxZ9=A)_F@!i!o{ zCTh1XeJ6lN)rSBtMr)xaaPG;6$m3|kA-*Rc=Gwd|zZsRSzM!aQe4jP^wiqP!*!MVW z3L%?=Yc2LSW6urM`2{~=1~l$g_}S^!I8R2G=f0c9bxt0hR57Jw zPw3A5fMRpaRyj??g^xebQ5ci`#mX>9YtKiIZa^&^rIZ(k93DQBOh93k1K~JSVk2qE zZ*FcJdCe;bk`)qI>sU^IX{~VUgPJQ#CIs^ko%b4*gbEK7NQ?mdvb2t3u}08wsbzqw zjZNbyIEpU*`sj1rk6*2NUGj4}ncdjXY+BH2&f?2|MI1w#n5f}Z_Mdh))vZr5{^*DD zjG=R@7V2dgp2T|iw;WgM3&3oex=Lkygfn9s1~D=zYv^4NpW0{9@Z4kB@cdKJ{Oal% zkT+Ra*$CG5=VYGUxOO0dZwFq+wlPiMFSTb{Elb~9Q7JLZJ1#$k)vm9* zgY@CwKZ0G;utCjjYξnD3BfW#Phcc?txrKWNgo226e4 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%0DVm6x5+LM`j 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