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 inputs(&self) -> Vec<(DataType, String)> {
38        ["minimum", "supremum"]
39            .map(|s| (DataType::U32, s.to_string()))
40            .to_vec()
41    }
42
43    fn outputs(&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"), 0xf536cdedd1ce0903.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        ) {
126            let supremum = pop_encodable::<u32>(stack);
127            let minimum = pop_encodable::<u32>(stack);
128            assert!(minimum <= supremum);
129
130            let list_pointer = dynamic_allocator(memory);
131            let list = (minimum..supremum).collect_vec();
132            encode_to_memory(memory, list_pointer, &list);
133
134            stack.push(list_pointer);
135        }
136
137        fn pseudorandom_initial_state(
138            &self,
139            seed: [u8; 32],
140            bench_case: Option<BenchmarkCase>,
141        ) -> FunctionInitialState {
142            let (minimum, supremum) = match bench_case {
143                Some(BenchmarkCase::CommonCase) => (0, 45),
144                Some(BenchmarkCase::WorstCase) => (0, 250),
145                None => {
146                    let mut rng = StdRng::from_seed(seed);
147                    let supremum = rng.random_range(0..=400);
148                    let minimum = rng.random_range(0..=supremum);
149                    (minimum, supremum)
150                }
151            };
152
153            self.set_up_initial_state(minimum, supremum)
154        }
155
156        fn corner_case_initial_states(&self) -> Vec<FunctionInitialState> {
157            [(0, 0), (0, 1), (0, 10), (5, 15), (15, 15)]
158                .map(|(min, sup)| self.set_up_initial_state(min, sup))
159                .to_vec()
160        }
161    }
162
163    #[test]
164    fn rust_shadow() {
165        ShadowedFunction::new(Range).test();
166    }
167
168    #[proptest]
169    fn invalid_range_crashes_vm(
170        #[strategy(1_u32..)] minimum: u32,
171        #[strategy(..#minimum)] supremum: u32,
172    ) {
173        test_assertion_failure(
174            &ShadowedFunction::new(Range),
175            Range.set_up_initial_state(minimum, supremum).into(),
176            &[Range::INVALID_ERROR_ID],
177        );
178    }
179}
180
181#[cfg(test)]
182mod benches {
183    use super::*;
184    use crate::test_prelude::*;
185
186    #[test]
187    fn benchmark() {
188        ShadowedFunction::new(Range).bench();
189    }
190}