Skip to main content

tasm_lib/list/
range.rs

1use std::collections::HashMap;
2
3use triton_vm::prelude::*;
4
5use crate::list::new::New;
6use crate::prelude::*;
7use crate::traits::basic_snippet::Reviewer;
8use crate::traits::basic_snippet::SignOffFingerprint;
9
10/// Generates a list containing all integers between the minimum (inclusive
11/// lower bound) and the supremum (exclusive upper bound).
12///
13/// ### Behavior
14///
15/// ```text
16/// BEFORE: _ [minimum: u32] [supremum: u32]
17/// AFTER:  _ *list
18/// ```
19///
20/// ### Preconditions
21///
22/// - all input arguments are properly [`BFieldCodec`] encoded
23/// - the `minimum` is less than or equal to the `supremum`
24///
25/// ### Postconditions
26///
27/// - `*list` is a pointer to a [`BFieldCodec`] encoded list of properly encoded
28///   `u32`s
29#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
30pub struct Range;
31
32impl Range {
33    pub const INVALID_ERROR_ID: i128 = 550;
34}
35
36impl BasicSnippet for Range {
37    fn parameters(&self) -> Vec<(DataType, String)> {
38        ["minimum", "supremum"]
39            .map(|s| (DataType::U32, s.to_string()))
40            .to_vec()
41    }
42
43    fn return_values(&self) -> Vec<(DataType, String)> {
44        let list_type = DataType::List(Box::new(DataType::U32));
45        vec![(list_type, "*list".to_string())]
46    }
47
48    fn entrypoint(&self) -> String {
49        "tasmlib_list_range".into()
50    }
51
52    fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
53        let new_list = library.import(Box::new(New));
54
55        let entrypoint = self.entrypoint();
56        let inner_loop = format!("{entrypoint}_loop");
57
58        triton_asm!(
59            // BEFORE: _ minimum supremum
60            // AFTER:  _ *list
61            {entrypoint}:
62                dup 0 addi 1 dup 2      // _ minimum supremum (supremum + 1) minimum
63                lt                      // _ minimum supremum (minimum <= supremum)
64                assert error_id {Self::INVALID_ERROR_ID}
65
66                // calculate length
67                dup 0 dup 2             // _ minimum supremum supremum minimum
68                push -1 mul add         // _ minimum supremum (supremum - minimum)
69                                        // _ minimum supremum length
70
71                // create list object
72                call {new_list}         // _ minimum supremum length *list
73                dup 0 place 4           // _ *list minimum supremum length *list
74                write_mem 1             // _ *list minimum supremum *list[0]
75                call {inner_loop}       // _ *list supremum supremum *list[n]
76                pop 3                   // _ *list
77                return
78
79            // BEFORE:    _ minimum     supremum *list[0]
80            // INVARIANT: _ (minimum+i) supremum *list[i]
81            // AFTER:     _ supremum    supremum *list[n]
82            {inner_loop}:
83                dup 2 dup 2 eq          // _ (minimum+i) supremum *list[i] (minimum+i == supremum)
84                skiz return             // _ (minimum+i) supremum *list[i]
85
86                dup 2 place 1           // _ (minimum+i) supremum (minimum+i) *list[i]
87                write_mem 1             // _ (minimum+i) supremum *list[i+1]
88
89                pick 2 addi 1 place 2   // _ (minimum+i+1) supremum *list[i+1]
90                recurse
91        )
92    }
93
94    fn sign_offs(&self) -> HashMap<Reviewer, SignOffFingerprint> {
95        let mut sign_offs = HashMap::new();
96        sign_offs.insert(Reviewer("ferdinand"), 0x39a164b082c968d.into());
97        sign_offs
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use crate::rust_shadowing_helper_functions::dyn_malloc::dynamic_allocator;
105    use crate::test_prelude::*;
106
107    impl Range {
108        fn set_up_initial_state(&self, minimum: u32, supremum: u32) -> FunctionInitialState {
109            let mut stack = self.init_stack_for_isolated_run();
110            stack.push(bfe!(minimum));
111            stack.push(bfe!(supremum));
112
113            FunctionInitialState {
114                stack,
115                ..Default::default()
116            }
117        }
118    }
119
120    impl Function for Range {
121        fn rust_shadow(
122            &self,
123            stack: &mut Vec<BFieldElement>,
124            memory: &mut HashMap<BFieldElement, BFieldElement>,
125        ) -> Result<(), RustShadowError> {
126            let supremum = pop_encodable::<u32>(stack)?;
127            let minimum = pop_encodable::<u32>(stack)?;
128            if minimum > supremum {
129                return Err(RustShadowError::Other);
130            }
131
132            let list_pointer = dynamic_allocator(memory);
133            let list = (minimum..supremum).collect_vec();
134            encode_to_memory(memory, list_pointer, &list);
135
136            stack.push(list_pointer);
137            Ok(())
138        }
139
140        fn pseudorandom_initial_state(
141            &self,
142            seed: [u8; 32],
143            bench_case: Option<BenchmarkCase>,
144        ) -> FunctionInitialState {
145            let (minimum, supremum) = match bench_case {
146                Some(BenchmarkCase::CommonCase) => (0, 45),
147                Some(BenchmarkCase::WorstCase) => (0, 250),
148                None => {
149                    let mut rng = StdRng::from_seed(seed);
150                    let supremum = rng.random_range(0..=400);
151                    let minimum = rng.random_range(0..=supremum);
152                    (minimum, supremum)
153                }
154            };
155
156            self.set_up_initial_state(minimum, supremum)
157        }
158
159        fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
160            [(0, 0), (0, 1), (0, 10), (5, 15), (15, 15)]
161                .map(|(min, sup)| self.set_up_initial_state(min, sup))
162                .to_vec()
163        }
164    }
165
166    #[macro_rules_attr::apply(test)]
167    fn rust_shadow() {
168        ShadowedFunction::new(Range).test();
169    }
170
171    #[macro_rules_attr::apply(proptest)]
172    fn invalid_range_crashes_vm(
173        #[strategy(1_u32..)] minimum: u32,
174        #[strategy(..#minimum)] supremum: u32,
175    ) {
176        test_assertion_failure(
177            &ShadowedFunction::new(Range),
178            Range.set_up_initial_state(minimum, supremum).into(),
179            &[Range::INVALID_ERROR_ID],
180        );
181    }
182}
183
184#[cfg(test)]
185mod benches {
186    use super::*;
187    use crate::test_prelude::*;
188
189    #[macro_rules_attr::apply(test)]
190    fn benchmark() {
191        ShadowedFunction::new(Range).bench();
192    }
193}