1use std::cmp::{max, min};
2
3use crate::{
4 air_private_input::{PrivateInput, PrivateInputValue},
5 types::{builtin_name::BuiltinName, instance_definitions::LowRatio},
6};
7
8use crate::Felt252;
9use crate::{
10 types::relocatable::{MaybeRelocatable, Relocatable},
11 vm::{
12 errors::memory_errors::MemoryError,
13 vm_memory::{
14 memory::{Memory, ValidationRule},
15 memory_segments::MemorySegmentManager,
16 },
17 },
18};
19
20use lazy_static::lazy_static;
21
22const INNER_RC_BOUND_SHIFT: u64 = 16;
23const INNER_RC_BOUND_MASK: u64 = u16::MAX as u64;
24
25pub const RC_N_PARTS_STANDARD: u64 = 8;
26pub const RC_N_PARTS_96: u64 = 6;
27
28lazy_static! {
29 pub static ref BOUND_STANDARD: Felt252 =
30 Felt252::TWO.pow(INNER_RC_BOUND_SHIFT * RC_N_PARTS_STANDARD);
31 pub static ref BOUND_96: Felt252 = Felt252::TWO.pow(INNER_RC_BOUND_SHIFT * RC_N_PARTS_96);
32}
33
34#[derive(Debug, Clone)]
35pub struct RangeCheckBuiltinRunner<const N_PARTS: u64> {
36 ratio: Option<LowRatio>,
37 base: usize,
38 pub(crate) stop_ptr: Option<usize>,
39 pub(crate) included: bool,
40}
41
42impl<const N_PARTS: u64> RangeCheckBuiltinRunner<N_PARTS> {
43 pub fn new(ratio: Option<u32>, included: bool) -> RangeCheckBuiltinRunner<N_PARTS> {
44 RangeCheckBuiltinRunner {
45 ratio: ratio.map(LowRatio::new_int),
46 base: 0,
47 stop_ptr: None,
48 included,
49 }
50 }
51
52 pub fn new_with_low_ratio(
53 ratio: Option<LowRatio>,
54 included: bool,
55 ) -> RangeCheckBuiltinRunner<N_PARTS> {
56 RangeCheckBuiltinRunner {
57 ratio,
58 base: 0,
59 stop_ptr: None,
60 included,
61 }
62 }
63
64 pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
65 self.base = segments.add().segment_index as usize }
67
68 pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
69 if self.included {
70 vec![MaybeRelocatable::from((self.base as isize, 0))]
71 } else {
72 vec![]
73 }
74 }
75
76 pub fn base(&self) -> usize {
77 self.base
78 }
79
80 pub fn ratio(&self) -> Option<u32> {
81 self.ratio.map(|ratio| ratio.numerator)
82 }
83
84 pub fn ratio_den(&self) -> Option<u32> {
85 self.ratio.map(|ratio| ratio.denominator)
86 }
87
88 pub fn name(&self) -> BuiltinName {
89 match N_PARTS {
90 RC_N_PARTS_96 => BuiltinName::range_check96,
91 _ => BuiltinName::range_check,
92 }
93 }
94
95 pub fn n_parts(&self) -> u64 {
96 N_PARTS
97 }
98
99 pub fn bound(&self) -> &'static Felt252 {
100 match N_PARTS {
101 RC_N_PARTS_96 => &BOUND_96,
102 _ => &BOUND_STANDARD,
103 }
104 }
105
106 pub fn add_validation_rule(&self, memory: &mut Memory) {
107 let rule = ValidationRule(Box::new(
108 |memory: &Memory, address: Relocatable| -> Result<Vec<Relocatable>, MemoryError> {
109 let num = memory
110 .get_integer(address)
111 .map_err(|_| MemoryError::RangeCheckFoundNonInt(Box::new(address)))?;
112 if num.bits() as u64 <= N_PARTS * INNER_RC_BOUND_SHIFT {
113 Ok(vec![address.to_owned()])
114 } else {
115 Err(MemoryError::RangeCheckNumOutOfBounds(Box::new((
116 num.into_owned(),
117 Felt252::TWO.pow((N_PARTS * INNER_RC_BOUND_SHIFT) as u128),
118 ))))
119 }
120 },
121 ));
122 memory.add_validation_rule(self.base, rule);
123 }
124
125 pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
126 segments
127 .get_segment_used_size(self.base)
128 .ok_or(MemoryError::MissingSegmentUsedSizes)
129 }
130
131 pub fn get_range_check_usage(&self, memory: &Memory) -> Option<(usize, usize)> {
132 let range_check_segment = memory.data.get(self.base)?;
133 let mut rc_bounds =
134 (!range_check_segment.is_empty()).then_some((usize::MAX, usize::MIN))?;
135
136 for value in range_check_segment {
138 rc_bounds = value
139 .get_value()?
140 .get_int_ref()?
141 .to_le_digits()
142 .into_iter()
144 .flat_map(|digit| {
145 (0..=3)
146 .rev()
147 .map(move |i| ((digit >> (i * INNER_RC_BOUND_SHIFT)) & INNER_RC_BOUND_MASK))
148 })
149 .take(N_PARTS as usize)
150 .fold(rc_bounds, |mm, x| {
151 (min(mm.0, x as usize), max(mm.1, x as usize))
152 });
153 }
154 Some(rc_bounds)
155 }
156
157 pub fn get_used_instances(
158 &self,
159 segments: &MemorySegmentManager,
160 ) -> Result<usize, MemoryError> {
161 self.get_used_cells(segments)
162 }
163
164 pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
165 let mut private_inputs = vec![];
166 if let Some(segment) = memory.data.get(self.base) {
167 for (index, cell) in segment.iter().enumerate() {
168 if let Some(value) = cell.get_value().and_then(|value| value.get_int()) {
169 private_inputs.push(PrivateInput::Value(PrivateInputValue { index, value }))
170 }
171 }
172 }
173 private_inputs
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use crate::relocatable;
181 use crate::types::builtin_name::BuiltinName;
182 use crate::vm::errors::runner_errors::RunnerError;
183 use crate::vm::vm_memory::memory::Memory;
184 use crate::{
185 hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor,
186 types::program::Program, utils::test_utils::*, vm::runners::builtin_runner::BuiltinRunner,
187 };
188
189 #[test]
190 fn get_used_instances() {
191 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), true);
192
193 let mut vm = vm!();
194 vm.segments.segment_used_sizes = Some(vec![1]);
195
196 assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
197 }
198
199 #[test]
200 fn final_stack() {
201 let mut builtin: BuiltinRunner =
202 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), true).into();
203
204 let mut vm = vm!();
205
206 vm.segments = segments![
207 ((0, 0), (0, 0)),
208 ((0, 1), (0, 1)),
209 ((2, 0), (0, 0)),
210 ((2, 1), (0, 0))
211 ];
212
213 vm.segments.segment_used_sizes = Some(vec![0]);
214
215 let pointer = Relocatable::from((2, 2));
216
217 assert_eq!(
218 builtin.final_stack(&vm.segments, pointer).unwrap(),
219 Relocatable::from((2, 1))
220 );
221 }
222
223 #[test]
224 fn final_stack_error_stop_pointer() {
225 let mut builtin: BuiltinRunner =
226 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), true).into();
227
228 let mut vm = vm!();
229
230 vm.segments = segments![
231 ((0, 0), (0, 0)),
232 ((0, 1), (0, 1)),
233 ((2, 0), (0, 0)),
234 ((2, 1), (0, 0))
235 ];
236
237 vm.segments.segment_used_sizes = Some(vec![998]);
238
239 let pointer = Relocatable::from((2, 2));
240
241 assert_eq!(
242 builtin.final_stack(&vm.segments, pointer),
243 Err(RunnerError::InvalidStopPointer(Box::new((
244 BuiltinName::range_check,
245 relocatable!(0, 998),
246 relocatable!(0, 0)
247 ))))
248 );
249 }
250
251 #[test]
252 fn final_stack_error_when_notincluded() {
253 let mut builtin: BuiltinRunner =
254 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), false).into();
255
256 let mut vm = vm!();
257
258 vm.segments = segments![
259 ((0, 0), (0, 0)),
260 ((0, 1), (0, 1)),
261 ((2, 0), (0, 0)),
262 ((2, 1), (0, 0))
263 ];
264
265 vm.segments.segment_used_sizes = Some(vec![0]);
266
267 let pointer = Relocatable::from((2, 2));
268
269 assert_eq!(
270 builtin.final_stack(&vm.segments, pointer).unwrap(),
271 Relocatable::from((2, 2))
272 );
273 }
274
275 #[test]
276 fn final_stack_error_non_relocatable() {
277 let mut builtin: BuiltinRunner =
278 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), true).into();
279
280 let mut vm = vm!();
281
282 vm.segments = segments![
283 ((0, 0), (0, 0)),
284 ((0, 1), (0, 1)),
285 ((2, 0), (0, 0)),
286 ((2, 1), 2)
287 ];
288
289 vm.segments.segment_used_sizes = Some(vec![0]);
290
291 let pointer = Relocatable::from((2, 2));
292
293 assert_eq!(
294 builtin.final_stack(&vm.segments, pointer),
295 Err(RunnerError::NoStopPointer(Box::new(
296 BuiltinName::range_check
297 )))
298 );
299 }
300
301 #[test]
302 fn get_used_cells_and_allocated_size_test() {
303 let builtin: BuiltinRunner =
304 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), true).into();
305
306 let program = program!(
307 builtins = vec![BuiltinName::range_check],
308 data = vec_data!(
309 (4612671182993129469_i64),
310 (5189976364521848832_i64),
311 (18446744073709551615_i128),
312 (5199546496550207487_i64),
313 (4612389712311386111_i64),
314 (5198983563776393216_i64),
315 (2),
316 (2345108766317314046_i64),
317 (5191102247248822272_i64),
318 (5189976364521848832_i64),
319 (7),
320 (1226245742482522112_i64),
321 ((
322 "3618502788666131213697322783095070105623107215331596699973092056135872020470",
323 10
324 )),
325 (2345108766317314046_i64)
326 ),
327 main = Some(8),
328 );
329
330 let mut cairo_runner = cairo_runner!(program);
331
332 cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
333
334 let mut hint_processor = BuiltinHintProcessor::new_empty();
335
336 let address = cairo_runner.initialize(false).unwrap();
337
338 cairo_runner
339 .run_until_pc(address, &mut hint_processor)
340 .unwrap();
341
342 assert_eq!(
343 builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
344 Ok((0, 1))
345 );
346 }
347
348 #[test]
349 fn get_allocated_memory_units() {
350 let builtin: BuiltinRunner =
351 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(10), true).into();
352
353 let program = program!(
354 builtins = vec![BuiltinName::range_check],
355 data = vec_data!(
356 (4612671182993129469_i64),
357 (5189976364521848832_i64),
358 (18446744073709551615_i128),
359 (5199546496550207487_i64),
360 (4612389712311386111_i64),
361 (5198983563776393216_i64),
362 (2),
363 (2345108766317314046_i64),
364 (5191102247248822272_i64),
365 (5189976364521848832_i64),
366 (7),
367 (1226245742482522112_i64),
368 ((
369 "3618502788666131213697322783095070105623107215331596699973092056135872020470",
370 10
371 )),
372 (2345108766317314046_i64)
373 ),
374 main = Some(8),
375 );
376
377 let mut cairo_runner = cairo_runner!(program);
378
379 let mut hint_processor = BuiltinHintProcessor::new_empty();
380
381 let address = cairo_runner.initialize(false).unwrap();
382
383 cairo_runner
384 .run_until_pc(address, &mut hint_processor)
385 .unwrap();
386
387 assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(1));
388 }
389
390 #[test]
391 fn initialize_segments_for_range_check() {
392 let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
393 let mut segments = MemorySegmentManager::new();
394 builtin.initialize_segments(&mut segments);
395 assert_eq!(builtin.base, 0);
396 }
397
398 #[test]
399 fn get_initial_stack_for_range_check_with_base() {
400 let mut builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
401 builtin.base = 1;
402 let initial_stack = builtin.initial_stack();
403 assert_eq!(
404 initial_stack[0].clone(),
405 MaybeRelocatable::RelocatableValue((builtin.base() as isize, 0).into())
406 );
407 assert_eq!(initial_stack.len(), 1);
408 }
409
410 #[test]
411 fn test_base() {
412 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
413 assert_eq!(builtin.base(), 0);
414 }
415
416 #[test]
417 fn test_ratio() {
418 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
419 assert_eq!(builtin.ratio(), Some(8));
420 }
421
422 #[test]
423 fn get_used_cells_missing_segment_used_sizes() {
424 let builtin = BuiltinRunner::RangeCheck(
425 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(256), true),
426 );
427 let vm = vm!();
428
429 assert_eq!(
430 builtin.get_used_cells(&vm.segments),
431 Err(MemoryError::MissingSegmentUsedSizes)
432 );
433 }
434
435 #[test]
436 fn get_used_cells_empty() {
437 let builtin = BuiltinRunner::RangeCheck(
438 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(256), true),
439 );
440 let mut vm = vm!();
441
442 vm.segments.segment_used_sizes = Some(vec![0]);
443 assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
444 }
445
446 #[test]
447 fn get_used_cells() {
448 let builtin = BuiltinRunner::RangeCheck(
449 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(256), true),
450 );
451 let mut vm = vm!();
452
453 vm.segments.segment_used_sizes = Some(vec![4]);
454 assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
455 }
456
457 #[test]
458 fn get_range_check_usage_succesful_a() {
459 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
460 let memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3), ((0, 3), 4)];
461 assert_eq!(builtin.get_range_check_usage(&memory), Some((0, 4)));
462 }
463
464 #[test]
465 fn get_range_check_usage_succesful_b() {
466 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
467 let memory = memory![
468 ((0, 0), 1465218365),
469 ((0, 1), 2134570341),
470 ((0, 2), 31349610736_i64),
471 ((0, 3), 413468326585859_i64)
472 ];
473 assert_eq!(builtin.get_range_check_usage(&memory), Some((0, 62821)));
474 }
475
476 #[test]
477 fn get_range_check_usage_succesful_c() {
478 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
479 let memory = memory![
480 ((0, 0), 634834751465218365_i64),
481 ((0, 1), 42876922134570341_i64),
482 ((0, 2), 23469831349610736_i64),
483 ((0, 3), 23468413468326585859_i128),
484 ((0, 4), 75346043276073460326_i128),
485 ((0, 5), 87234598724867609478353436890268_i128)
486 ];
487 assert_eq!(builtin.get_range_check_usage(&memory), Some((0, 61576)));
488 }
489
490 #[test]
491 fn get_range_check_empty_memory() {
492 let builtin = RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true);
493 let memory = Memory::new();
494 assert_eq!(builtin.get_range_check_usage(&memory), None);
495 }
496
497 #[test]
499 fn get_used_perm_range_check_units() {
500 let builtin_runner: BuiltinRunner =
501 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(Some(8), true).into();
502 let mut vm = vm!();
503
504 vm.current_step = 8;
505 vm.segments.segment_used_sizes = Some(vec![1]);
506 assert_eq!(builtin_runner.get_used_perm_range_check_units(&vm), Ok(8));
507 }
508
509 #[test]
510 fn get_air_private_input() {
511 let builtin: BuiltinRunner =
512 RangeCheckBuiltinRunner::<RC_N_PARTS_STANDARD>::new(None, true).into();
513
514 let segments = segments![((0, 0), 0), ((0, 1), 1), ((0, 2), 2)];
515 assert_eq!(
516 builtin.air_private_input(&segments),
517 (vec![
518 PrivateInput::Value(PrivateInputValue {
519 index: 0,
520 value: 0.into(),
521 }),
522 PrivateInput::Value(PrivateInputValue {
523 index: 1,
524 value: 1.into(),
525 }),
526 PrivateInput::Value(PrivateInputValue {
527 index: 2,
528 value: 2.into(),
529 }),
530 ]),
531 );
532 }
533}