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#[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 {entrypoint}:
62 dup 0 addi 1 dup 2 lt assert error_id {Self::INVALID_ERROR_ID}
65
66 dup 0 dup 2 push -1 mul add call {new_list} dup 0 place 4 write_mem 1 call {inner_loop} pop 3 return
78
79 {inner_loop}:
83 dup 2 dup 2 eq skiz return dup 2 place 1 write_mem 1 pick 2 addi 1 place 2 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}