1use crate::{
2 air_private_input::{ModInput, ModInputInstance, ModInputMemoryVars, PrivateInput},
3 math_utils::{div_mod_unsigned, safe_div_usize},
4 stdlib::{
5 borrow::Cow,
6 collections::BTreeMap,
7 prelude::{Box, Vec},
8 },
9 types::{
10 builtin_name::BuiltinName,
11 errors::math_errors::MathError,
12 instance_definitions::mod_instance_def::{ModInstanceDef, CELLS_PER_MOD, N_WORDS},
13 relocatable::{relocate_address, MaybeRelocatable, Relocatable},
14 },
15 vm::{
16 errors::{
17 memory_errors::MemoryError, runner_errors::RunnerError, vm_errors::VirtualMachineError,
18 },
19 vm_core::VirtualMachine,
20 vm_memory::{memory::Memory, memory_segments::MemorySegmentManager},
21 },
22 Felt252,
23};
24use core::ops::Shl;
25use num_bigint::BigUint;
26use num_integer::div_ceil;
27use num_integer::Integer;
28use num_traits::One;
29use num_traits::Zero;
30
31const FILL_MEMORY_MAX: usize = 100000;
33
34const VALUES_PTR_OFFSET: u32 = 4;
35const OFFSETS_PTR_OFFSET: u32 = 5;
36const N_OFFSET: u32 = 6;
37
38#[derive(Debug, Clone)]
39pub struct ModBuiltinRunner {
40 builtin_type: ModBuiltinType,
41 base: usize,
42 pub(crate) stop_ptr: Option<usize>,
43 instance_def: ModInstanceDef,
44 pub(crate) included: bool,
45 zero_segment_index: usize,
46 zero_segment_size: usize,
47 shift: BigUint,
49 shift_powers: [BigUint; N_WORDS],
50 k_bound: BigUint,
51}
52
53#[derive(Debug, Clone)]
54pub enum ModBuiltinType {
55 Mul,
56 Add,
57}
58
59impl ModBuiltinType {
60 pub(crate) fn operation_string(&self) -> &'static str {
61 match self {
62 ModBuiltinType::Mul => "*",
63 ModBuiltinType::Add => "+",
64 }
65 }
66}
67
68#[derive(Debug, Default)]
69struct Inputs {
70 p: BigUint,
71 p_values: [Felt252; N_WORDS],
72 values_ptr: Relocatable,
73 offsets_ptr: Relocatable,
74 n: usize,
75}
76
77impl ModBuiltinRunner {
78 pub(crate) fn new_add_mod(instance_def: &ModInstanceDef, included: bool) -> Self {
79 Self::new(
80 instance_def.clone(),
81 included,
82 ModBuiltinType::Add,
83 Some(2u32.into()),
84 )
85 }
86
87 pub(crate) fn new_mul_mod(instance_def: &ModInstanceDef, included: bool) -> Self {
88 Self::new(instance_def.clone(), included, ModBuiltinType::Mul, None)
89 }
90
91 fn new(
92 instance_def: ModInstanceDef,
93 included: bool,
94 builtin_type: ModBuiltinType,
95 k_bound: Option<BigUint>,
96 ) -> Self {
97 let shift = BigUint::one().shl(instance_def.word_bit_len);
98 let shift_powers = core::array::from_fn(|i| shift.pow(i as u32));
99 let zero_segment_size = core::cmp::max(N_WORDS, instance_def.batch_size * 3);
100 let int_lim = BigUint::from(2_u32).pow(N_WORDS as u32 * instance_def.word_bit_len);
101 Self {
102 builtin_type,
103 base: 0,
104 stop_ptr: None,
105 instance_def,
106 included,
107 zero_segment_index: 0,
108 zero_segment_size,
109 shift,
110 shift_powers,
111 k_bound: k_bound.unwrap_or(int_lim),
112 }
113 }
114
115 pub fn name(&self) -> BuiltinName {
116 match self.builtin_type {
117 ModBuiltinType::Mul => BuiltinName::mul_mod,
118 ModBuiltinType::Add => BuiltinName::add_mod,
119 }
120 }
121
122 pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
123 self.base = segments.add().segment_index as usize; }
125
126 pub fn initialize_zero_segment(&mut self, segments: &mut MemorySegmentManager) {
127 self.zero_segment_index = segments.add_zero_segment(self.zero_segment_size);
128 }
129
130 pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
131 if self.included {
132 vec![MaybeRelocatable::from((self.base as isize, 0))]
133 } else {
134 vec![]
135 }
136 }
137
138 pub fn base(&self) -> usize {
139 self.base
140 }
141
142 pub fn ratio(&self) -> Option<u32> {
143 self.instance_def.ratio.map(|ratio| ratio.numerator)
144 }
145
146 pub fn ratio_den(&self) -> Option<u32> {
147 self.instance_def.ratio.map(|ratio| ratio.denominator)
148 }
149
150 pub fn batch_size(&self) -> usize {
151 self.instance_def.batch_size
152 }
153
154 pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
155 segments
156 .get_segment_used_size(self.base)
157 .ok_or(MemoryError::MissingSegmentUsedSizes)
158 }
159
160 pub fn get_used_instances(
161 &self,
162 segments: &MemorySegmentManager,
163 ) -> Result<usize, MemoryError> {
164 let used_cells = self.get_used_cells(segments)?;
165 Ok(div_ceil(used_cells, CELLS_PER_MOD as usize))
166 }
167
168 pub(crate) fn air_private_input(&self, segments: &MemorySegmentManager) -> Vec<PrivateInput> {
169 let segment_index = self.base as isize;
170 let segment_size = segments
171 .get_segment_used_size(self.base)
172 .unwrap_or_default();
173 let relocation_table = segments.relocate_segments().unwrap_or_default();
174 let mut instances = Vec::<ModInputInstance>::new();
175 for instance in 0..segment_size
176 .checked_div(CELLS_PER_MOD as usize)
177 .unwrap_or_default()
178 {
179 let instance_addr_offset = instance * CELLS_PER_MOD as usize;
180 let values_ptr = segments
181 .memory
182 .get_relocatable(
183 (
184 segment_index,
185 instance_addr_offset + VALUES_PTR_OFFSET as usize,
186 )
187 .into(),
188 )
189 .unwrap_or_default();
190 let offsets_ptr = segments
191 .memory
192 .get_relocatable(
193 (
194 segment_index,
195 instance_addr_offset + OFFSETS_PTR_OFFSET as usize,
196 )
197 .into(),
198 )
199 .unwrap_or_default();
200 let n = segments
201 .memory
202 .get_usize((segment_index, instance_addr_offset + N_OFFSET as usize).into())
203 .unwrap_or_default();
204 let p_values: [Felt252; N_WORDS] = core::array::from_fn(|i| {
205 segments
206 .memory
207 .get_integer((segment_index, instance_addr_offset + i).into())
208 .unwrap_or_default()
209 .into_owned()
210 });
211 let mut batch = BTreeMap::<usize, ModInputMemoryVars>::new();
212 let fetch_offset_and_words = |var_index: usize,
213 index_in_batch: usize|
214 -> (usize, [Felt252; N_WORDS]) {
215 let offset = segments
216 .memory
217 .get_usize((offsets_ptr + (3 * index_in_batch + var_index)).unwrap_or_default())
218 .unwrap_or_default();
219 let words: [Felt252; N_WORDS] = core::array::from_fn(|i| {
220 segments
221 .memory
222 .get_integer((values_ptr + (offset + i)).unwrap_or_default())
223 .unwrap_or_default()
224 .into_owned()
225 });
226 (offset, words)
227 };
228 for index_in_batch in 0..self.batch_size() {
229 let (a_offset, a_values) = fetch_offset_and_words(0, index_in_batch);
230 let (b_offset, b_values) = fetch_offset_and_words(1, index_in_batch);
231 let (c_offset, c_values) = fetch_offset_and_words(2, index_in_batch);
232 batch.insert(
233 index_in_batch,
234 ModInputMemoryVars {
235 a_offset,
236 b_offset,
237 c_offset,
238 a0: a_values[0],
239 a1: a_values[1],
240 a2: a_values[2],
241 a3: a_values[3],
242 b0: b_values[0],
243 b1: b_values[1],
244 b2: b_values[2],
245 b3: b_values[3],
246 c0: c_values[0],
247 c1: c_values[1],
248 c2: c_values[2],
249 c3: c_values[3],
250 },
251 );
252 }
253 instances.push(ModInputInstance {
254 index: instance,
255 p0: p_values[0],
256 p1: p_values[1],
257 p2: p_values[2],
258 p3: p_values[3],
259 values_ptr: relocate_address(values_ptr, &relocation_table).unwrap_or_default(),
260 offsets_ptr: relocate_address(offsets_ptr, &relocation_table).unwrap_or_default(),
261 n,
262 batch,
263 });
264 }
265
266 instances.sort_by_key(|input| input.index);
267
268 vec![PrivateInput::Mod(ModInput {
269 instances,
270 zero_value_address: relocation_table
271 .get(self.zero_segment_index)
272 .cloned()
273 .unwrap_or_default(),
274 })]
275 }
276
277 fn read_n_words_value(
281 &self,
282 memory: &Memory,
283 addr: Relocatable,
284 ) -> Result<([Felt252; N_WORDS], Option<BigUint>), RunnerError> {
285 let mut words = Default::default();
286 let mut value = BigUint::zero();
287 for i in 0..N_WORDS {
288 let addr_i = (addr + i)?;
289 match memory.get(&addr_i).map(Cow::into_owned) {
290 None => return Ok((words, None)),
291 Some(MaybeRelocatable::RelocatableValue(_)) => {
292 return Err(MemoryError::ExpectedInteger(Box::new(addr_i)).into())
293 }
294 Some(MaybeRelocatable::Int(word)) => {
295 let biguint_word = word.to_biguint();
296 if biguint_word >= self.shift {
297 return Err(RunnerError::WordExceedsModBuiltinWordBitLen(Box::new((
298 addr_i,
299 self.instance_def.word_bit_len,
300 word,
301 ))));
302 }
303 words[i] = word;
304 value += biguint_word * &self.shift_powers[i];
305 }
306 }
307 }
308 Ok((words, Some(value)))
309 }
310
311 fn read_inputs(&self, memory: &Memory, addr: Relocatable) -> Result<Inputs, RunnerError> {
315 let values_ptr = memory.get_relocatable((addr + VALUES_PTR_OFFSET)?)?;
316 let offsets_ptr = memory.get_relocatable((addr + OFFSETS_PTR_OFFSET)?)?;
317 let n = memory.get_usize((addr + N_OFFSET)?)?;
318 if n < 1 {
319 return Err(RunnerError::ModBuiltinNLessThanOne(Box::new((
320 self.name(),
321 n,
322 ))));
323 }
324 let (p_values, p) = self.read_n_words_value(memory, addr)?;
325 let p = p.ok_or_else(|| {
326 RunnerError::ModBuiltinMissingValue(Box::new((
327 self.name(),
328 (addr + N_WORDS).unwrap_or_default(),
329 )))
330 })?;
331 Ok(Inputs {
332 p,
333 p_values,
334 values_ptr,
335 offsets_ptr,
336 n,
337 })
338 }
339
340 fn read_memory_vars(
344 &self,
345 memory: &Memory,
346 values_ptr: Relocatable,
347 offsets_ptr: Relocatable,
348 index_in_batch: usize,
349 ) -> Result<(BigUint, BigUint, BigUint), RunnerError> {
350 let compute_value = |index: usize| -> Result<BigUint, RunnerError> {
351 let offset = memory.get_usize((offsets_ptr + (index + 3 * index_in_batch))?)?;
352 let value_addr = (values_ptr + offset)?;
353 let (_, value) = self.read_n_words_value(memory, value_addr)?;
354 let value = value.ok_or_else(|| {
355 RunnerError::ModBuiltinMissingValue(Box::new((
356 self.name(),
357 (value_addr + N_WORDS).unwrap_or_default(),
358 )))
359 })?;
360 Ok(value)
361 };
362
363 let a = compute_value(0)?;
364 let b = compute_value(1)?;
365 let c = compute_value(2)?;
366 Ok((a, b, c))
367 }
368
369 fn fill_inputs(
370 &self,
371 memory: &mut Memory,
372 builtin_ptr: Relocatable,
373 inputs: &Inputs,
374 ) -> Result<(), RunnerError> {
375 if inputs.n > FILL_MEMORY_MAX {
376 return Err(RunnerError::FillMemoryMaxExceeded(Box::new((
377 self.name(),
378 FILL_MEMORY_MAX,
379 ))));
380 }
381 let n_instances = safe_div_usize(inputs.n, self.instance_def.batch_size)?;
382 for instance in 1..n_instances {
383 let instance_ptr = (builtin_ptr + instance * CELLS_PER_MOD as usize)?;
384 for i in 0..N_WORDS {
385 memory.insert_as_accessed((instance_ptr + i)?, &inputs.p_values[i])?;
386 }
387 memory.insert_as_accessed((instance_ptr + VALUES_PTR_OFFSET)?, &inputs.values_ptr)?;
388 memory.insert_as_accessed(
389 (instance_ptr + OFFSETS_PTR_OFFSET)?,
390 (inputs.offsets_ptr + (3 * instance * self.instance_def.batch_size))?,
391 )?;
392 memory.insert_as_accessed(
393 (instance_ptr + N_OFFSET)?,
394 inputs
395 .n
396 .saturating_sub(instance * self.instance_def.batch_size),
397 )?;
398 }
399 Ok(())
400 }
401
402 fn fill_offsets(
404 &self,
405 memory: &mut Memory,
406 offsets_ptr: Relocatable,
407 index: usize,
408 n_copies: usize,
409 ) -> Result<(), RunnerError> {
410 if n_copies.is_zero() {
411 return Ok(());
412 }
413 for i in 0..3_usize {
414 let addr = (offsets_ptr + i)?;
415 let offset = memory
416 .get(&((offsets_ptr + i)?))
417 .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(addr)))?
418 .into_owned();
419 for copy_i in 0..n_copies {
420 memory.insert_as_accessed((offsets_ptr + (3 * (index + copy_i) + i))?, &offset)?;
421 }
422 }
423 Ok(())
424 }
425
426 fn write_n_words_value(
428 &self,
429 memory: &mut Memory,
430 addr: Relocatable,
431 value: BigUint,
432 ) -> Result<(), RunnerError> {
433 let mut value = value;
434 for i in 0..N_WORDS {
435 let word = value.mod_floor(&self.shift);
436 memory.insert_as_accessed((addr + i)?, Felt252::from(word))?;
437 value = value.div_floor(&self.shift)
438 }
439 if !value.is_zero() {
440 return Err(RunnerError::WriteNWordsValueNotZero(self.name()));
441 }
442 Ok(())
443 }
444
445 fn fill_value(
450 &self,
451 memory: &mut Memory,
452 inputs: &Inputs,
453 index: usize,
454 ) -> Result<bool, RunnerError> {
455 let mut addresses = Vec::new();
456 let mut values = Vec::new();
457 for i in 0..3 {
458 let addr = (inputs.values_ptr
459 + memory
460 .get_integer((inputs.offsets_ptr + (3 * index + i))?)?
461 .as_ref())?;
462 addresses.push(addr);
463 let (_, value) = self.read_n_words_value(memory, addr)?;
464 values.push(value)
465 }
466 let (a, b, c) = (&values[0], &values[1], &values[2]);
467 match (a, b, c) {
468 (Some(a), Some(b), None) => {
470 let value = self.apply_operation(a, b, &inputs.p)?;
471 self.write_n_words_value(memory, addresses[2], value)?;
472 Ok(true)
473 }
474 (Some(a), None, Some(c)) => {
476 let value = self.deduce_operand(a, c, &inputs.p)?;
477 self.write_n_words_value(memory, addresses[1], value)?;
478 Ok(true)
479 }
480 (None, Some(b), Some(c)) => {
482 let value = self.deduce_operand(b, c, &inputs.p)?;
483 self.write_n_words_value(memory, addresses[0], value)?;
484 Ok(true)
485 }
486 (Some(_), Some(_), Some(_)) => Ok(true),
488 _ => Ok(false),
489 }
490 }
491
492 pub fn fill_memory(
507 memory: &mut Memory,
508 add_mod: Option<(Relocatable, &ModBuiltinRunner, usize)>,
509 mul_mod: Option<(Relocatable, &ModBuiltinRunner, usize)>,
510 ) -> Result<(), RunnerError> {
511 if add_mod.is_none() && mul_mod.is_none() {
512 return Err(RunnerError::FillMemoryNoBuiltinSet);
513 }
514 if let (Some((_, add_mod, _)), Some((_, mul_mod, _))) = (add_mod, mul_mod) {
516 if add_mod.instance_def.word_bit_len != mul_mod.instance_def.word_bit_len {
517 return Err(RunnerError::ModBuiltinsMismatchedInstanceDef);
518 }
519 }
520 let (add_mod_inputs, add_mod_n) =
522 if let Some((add_mod_addr, add_mod, add_mod_index)) = add_mod {
523 let add_mod_inputs = add_mod.read_inputs(memory, add_mod_addr)?;
524 add_mod.fill_inputs(memory, add_mod_addr, &add_mod_inputs)?;
525 add_mod.fill_offsets(
526 memory,
527 add_mod_inputs.offsets_ptr,
528 add_mod_index,
529 add_mod_inputs.n.saturating_sub(add_mod_index),
530 )?;
531 (add_mod_inputs, add_mod_index)
532 } else {
533 Default::default()
534 };
535
536 let (mul_mod_inputs, mul_mod_n) =
537 if let Some((mul_mod_addr, mul_mod, mul_mod_index)) = mul_mod {
538 let mul_mod_inputs = mul_mod.read_inputs(memory, mul_mod_addr)?;
539 mul_mod.fill_inputs(memory, mul_mod_addr, &mul_mod_inputs)?;
540 mul_mod.fill_offsets(
541 memory,
542 mul_mod_inputs.offsets_ptr,
543 mul_mod_index,
544 mul_mod_inputs.n.saturating_sub(mul_mod_index),
545 )?;
546 (mul_mod_inputs, mul_mod_index)
547 } else {
548 Default::default()
549 };
550
551 let mut add_mod_index = 0;
553 let mut mul_mod_index = 0;
554
555 while add_mod_index < add_mod_n || mul_mod_index < mul_mod_n {
556 if add_mod_index < add_mod_n {
557 if let Some((_, add_mod_runner, _)) = add_mod {
558 if add_mod_runner.fill_value(memory, &add_mod_inputs, add_mod_index)? {
559 add_mod_index += 1;
560 continue;
561 }
562 }
563 }
564
565 if mul_mod_index < mul_mod_n {
566 if let Some((_, mul_mod_runner, _)) = mul_mod {
567 if mul_mod_runner.fill_value(memory, &mul_mod_inputs, mul_mod_index)? {
568 mul_mod_index += 1;
569 continue;
570 } else {
571 return Err(RunnerError::FillMemoryCoudNotFillTable(
572 add_mod_index,
573 mul_mod_index,
574 ));
575 }
576 }
577 }
578
579 return Err(RunnerError::FillMemoryCoudNotFillTable(
580 add_mod_index,
581 mul_mod_index,
582 ));
583 }
584 Ok(())
585 }
586
587 pub(crate) fn run_additional_security_checks(
589 &self,
590 vm: &VirtualMachine,
591 ) -> Result<(), VirtualMachineError> {
592 let segment_size = vm
593 .get_segment_used_size(self.base)
594 .ok_or(MemoryError::MissingSegmentUsedSizes)?;
595 let n_instances = div_ceil(segment_size, CELLS_PER_MOD as usize);
596 let mut prev_inputs = Inputs::default();
597 for instance in 0..n_instances {
598 let inputs = self.read_inputs(
599 &vm.segments.memory,
600 (self.base as isize, instance * CELLS_PER_MOD as usize).into(),
601 )?;
602 if !instance.is_zero() && prev_inputs.n > self.instance_def.batch_size {
603 for i in 0..N_WORDS {
604 if inputs.p_values[i] != prev_inputs.p_values[i] {
605 return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.p_values[i] != prev_inputs.p_values[i]. Got: i={}, inputs.p_values[i]={}, prev_inputs.p_values[i]={}",
606 i, inputs.p_values[i], prev_inputs.p_values[i])))).into());
607 }
608 }
609 if inputs.values_ptr != prev_inputs.values_ptr {
610 return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.values_ptr != prev_inputs.values_ptr. Got: inputs.values_ptr={}, prev_inputs.values_ptr={}",
611 inputs.values_ptr, prev_inputs.values_ptr)))).into());
612 }
613 if inputs.offsets_ptr
614 != (prev_inputs.offsets_ptr + (3 * self.instance_def.batch_size))?
615 {
616 return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.offsets_ptr != prev_inputs.offsets_ptr + 3 * batch_size. Got: inputs.offsets_ptr={}, prev_inputs.offsets_ptr={}, batch_size={}",
617 inputs.values_ptr, prev_inputs.values_ptr, self.instance_def.batch_size)))).into());
618 }
619 if inputs.n != prev_inputs.n.saturating_sub(self.instance_def.batch_size) {
620 return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.n != prev_inputs.n - batch_size. Got: inputs.n={}, prev_inputs.n={}, batch_size={}",
621 inputs.n, prev_inputs.n, self.instance_def.batch_size)))).into());
622 }
623 }
624 for index_in_batch in 0..self.instance_def.batch_size {
625 let (a, b, c) = self.read_memory_vars(
626 &vm.segments.memory,
627 inputs.values_ptr,
628 inputs.offsets_ptr,
629 index_in_batch,
630 )?;
631 let a_op_b = self.apply_operation(&a, &b, &inputs.p)?;
632 if a_op_b.mod_floor(&inputs.p) != c.mod_floor(&inputs.p) {
633 let p = inputs.p;
635 let op = self.builtin_type.operation_string();
636 let error_string = format!("Expected a {op} b == c (mod p). Got: instance={instance}, batch={index_in_batch}, p={p}, a={a}, b={b}, c={c}.");
637 return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((
638 self.name(),
639 error_string,
640 )))
641 .into());
642 }
643 }
644 prev_inputs = inputs;
645 }
646 if !n_instances.is_zero() && prev_inputs.n != self.instance_def.batch_size {
647 return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((
648 self.name(),
649 format!(
650 "prev_inputs.n != batch_size Got: prev_inputs.n={}, batch_size={}",
651 prev_inputs.n, self.instance_def.batch_size
652 ),
653 )))
654 .into());
655 }
656 Ok(())
657 }
658
659 #[cfg(test)]
660 #[cfg(feature = "mod_builtin")]
661 pub(crate) fn override_layout_params(&mut self, batch_size: usize, word_bit_len: u32) {
664 self.instance_def.batch_size = batch_size;
665 self.instance_def.word_bit_len = word_bit_len;
666 self.shift = BigUint::one().shl(word_bit_len);
667 self.shift_powers = core::array::from_fn(|i| self.shift.pow(i as u32));
668 self.zero_segment_size = core::cmp::max(N_WORDS, batch_size * 3);
669 }
670
671 pub(crate) fn apply_operation(
675 &self,
676 lhs: &BigUint,
677 rhs: &BigUint,
678 prime: &BigUint,
679 ) -> Result<BigUint, MathError> {
680 let full_value = match self.builtin_type {
681 ModBuiltinType::Mul => lhs * rhs,
682 ModBuiltinType::Add => lhs + rhs,
683 };
684
685 let value = if full_value < &self.k_bound * prime {
686 full_value.mod_floor(prime)
687 } else {
688 full_value - (&self.k_bound - 1u32) * prime
689 };
690
691 Ok(value)
692 }
693
694 pub(crate) fn deduce_operand(
698 &self,
699 known: &BigUint,
700 result: &BigUint,
701 prime: &BigUint,
702 ) -> Result<BigUint, MathError> {
703 let value = match self.builtin_type {
704 ModBuiltinType::Add => {
705 if known <= result {
706 result - known
707 } else {
708 result + prime - known
709 }
710 }
711 ModBuiltinType::Mul => div_mod_unsigned(result, known, prime)?,
712 };
713 Ok(value)
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720
721 #[test]
722 fn apply_operation_add() {
723 let builtin = ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(8), 8, 8), true);
724
725 assert_eq!(
726 builtin
727 .apply_operation(
728 &BigUint::from(2u32),
729 &BigUint::from(3u32),
730 &BigUint::from(7u32)
731 )
732 .unwrap(),
733 BigUint::from(5u32)
734 );
735
736 assert_eq!(
737 builtin
738 .apply_operation(
739 &BigUint::from(5u32),
740 &BigUint::from(5u32),
741 &BigUint::from(5u32)
742 )
743 .unwrap(),
744 BigUint::from(5u32)
745 );
746 }
747
748 #[test]
749 fn apply_operation_mul() {
750 let builtin = ModBuiltinRunner::new_mul_mod(&ModInstanceDef::new(Some(8), 8, 8), true);
751
752 assert_eq!(
753 builtin
754 .apply_operation(
755 &BigUint::from(2u32),
756 &BigUint::from(3u32),
757 &BigUint::from(7u32)
758 )
759 .unwrap(),
760 BigUint::from(6u32)
761 );
762 }
763
764 #[test]
765 fn deduce_operand_add() {
766 let builtin = ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(8), 8, 8), true);
767
768 assert_eq!(
769 builtin
770 .deduce_operand(
771 &BigUint::from(2u32),
772 &BigUint::from(5u32),
773 &BigUint::from(7u32)
774 )
775 .unwrap(),
776 BigUint::from(3u32)
777 );
778 assert_eq!(
779 builtin
780 .deduce_operand(
781 &BigUint::from(5u32),
782 &BigUint::from(2u32),
783 &BigUint::from(7u32)
784 )
785 .unwrap(),
786 BigUint::from(4u32)
787 );
788 }
789
790 #[test]
791 fn deduce_operand_mul() {
792 let builtin = ModBuiltinRunner::new_mul_mod(&ModInstanceDef::new(Some(8), 8, 8), true);
793
794 assert_eq!(
795 builtin
796 .deduce_operand(
797 &BigUint::from(2u32),
798 &BigUint::from(1u32),
799 &BigUint::from(7u32)
800 )
801 .unwrap(),
802 BigUint::from(4u32)
803 );
804 }
805
806 #[test]
807 #[cfg(feature = "mod_builtin")]
808 fn test_air_private_input_all_cairo() {
809 use crate::{
810 air_private_input::{ModInput, ModInputInstance, ModInputMemoryVars, PrivateInput},
811 hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor,
812 types::layout_name::LayoutName,
813 utils::test_utils::Program,
814 vm::runners::cairo_runner::CairoRunner,
815 Felt252,
816 };
817
818 let program_data = include_bytes!(
819 "../../../../../cairo_programs/mod_builtin_feature/proof/mod_builtin.json"
820 );
821
822 let mut hint_processor = BuiltinHintProcessor::new_empty();
823 let program = Program::from_bytes(program_data, Some("main")).unwrap();
824 let proof_mode = true;
825 let mut runner = CairoRunner::new(
826 &program,
827 LayoutName::all_cairo,
828 None,
829 proof_mode,
830 false,
831 false,
832 )
833 .unwrap();
834
835 let end = runner.initialize(false).unwrap();
836 runner.run_until_pc(end, &mut hint_processor).unwrap();
839 runner.run_for_steps(1, &mut hint_processor).unwrap();
840 runner
841 .end_run(false, false, &mut hint_processor, proof_mode)
842 .unwrap();
843 runner.read_return_values(false).unwrap();
844 runner.finalize_segments().unwrap();
845
846 let air_private_input = runner.get_air_private_input();
848 assert_eq!(
849 air_private_input.0.get(&BuiltinName::add_mod).unwrap()[0],
850 PrivateInput::Mod(ModInput {
851 instances: vec![
852 ModInputInstance {
853 index: 0,
854 p0: Felt252::ONE,
855 p1: Felt252::ONE,
856 p2: Felt252::ZERO,
857 p3: Felt252::ZERO,
858 values_ptr: 23023,
859 offsets_ptr: 23055,
860 n: 2,
861 batch: BTreeMap::from([(
862 0,
863 ModInputMemoryVars {
864 a_offset: 0,
865 a0: Felt252::ONE,
866 a1: Felt252::ZERO,
867 a2: Felt252::ZERO,
868 a3: Felt252::ZERO,
869 b_offset: 12,
870 b0: Felt252::ONE,
871 b1: Felt252::ONE,
872 b2: Felt252::ZERO,
873 b3: Felt252::ZERO,
874 c_offset: 4,
875 c0: Felt252::TWO,
876 c1: Felt252::ONE,
877 c2: Felt252::ZERO,
878 c3: Felt252::ZERO
879 }
880 ),])
881 },
882 ModInputInstance {
883 index: 1,
884 p0: Felt252::ONE,
885 p1: Felt252::ONE,
886 p2: Felt252::ZERO,
887 p3: Felt252::ZERO,
888 values_ptr: 23023,
889 offsets_ptr: 23058,
890 n: 1,
891 batch: BTreeMap::from([(
892 0,
893 ModInputMemoryVars {
894 a_offset: 16,
895 a0: Felt252::ZERO,
896 a1: Felt252::ZERO,
897 a2: Felt252::ZERO,
898 a3: Felt252::ZERO,
899 b_offset: 20,
900 b0: Felt252::TWO,
901 b1: Felt252::ZERO,
902 b2: Felt252::ZERO,
903 b3: Felt252::ZERO,
904 c_offset: 24,
905 c0: Felt252::TWO,
906 c1: Felt252::ZERO,
907 c2: Felt252::ZERO,
908 c3: Felt252::ZERO
909 }
910 ),])
911 }
912 ],
913 zero_value_address: 23019
914 })
915 );
916 assert_eq!(
917 air_private_input.0.get(&BuiltinName::mul_mod).unwrap()[0],
918 PrivateInput::Mod(ModInput {
919 instances: vec![
920 ModInputInstance {
921 index: 0,
922 p0: Felt252::ONE,
923 p1: Felt252::ONE,
924 p2: Felt252::ZERO,
925 p3: Felt252::ZERO,
926 values_ptr: 23023,
927 offsets_ptr: 23061,
928 n: 3,
929 batch: BTreeMap::from([(
930 0,
931 ModInputMemoryVars {
932 a_offset: 12,
933 a0: Felt252::ONE,
934 a1: Felt252::ONE,
935 a2: Felt252::ZERO,
936 a3: Felt252::ZERO,
937 b_offset: 8,
938 b0: Felt252::TWO,
939 b1: Felt252::ZERO,
940 b2: Felt252::ZERO,
941 b3: Felt252::ZERO,
942 c_offset: 16,
943 c0: Felt252::ZERO,
944 c1: Felt252::ZERO,
945 c2: Felt252::ZERO,
946 c3: Felt252::ZERO
947 }
948 ),])
949 },
950 ModInputInstance {
951 index: 1,
952 p0: Felt252::ONE,
953 p1: Felt252::ONE,
954 p2: Felt252::ZERO,
955 p3: Felt252::ZERO,
956 values_ptr: 23023,
957 offsets_ptr: 23064,
958 n: 2,
959 batch: BTreeMap::from([(
960 0,
961 ModInputMemoryVars {
962 a_offset: 0,
963 a0: Felt252::ONE,
964 a1: Felt252::ZERO,
965 a2: Felt252::ZERO,
966 a3: Felt252::ZERO,
967 b_offset: 8,
968 b0: Felt252::TWO,
969 b1: Felt252::ZERO,
970 b2: Felt252::ZERO,
971 b3: Felt252::ZERO,
972 c_offset: 20,
973 c0: Felt252::TWO,
974 c1: Felt252::ZERO,
975 c2: Felt252::ZERO,
976 c3: Felt252::ZERO
977 }
978 ),])
979 },
980 ModInputInstance {
981 index: 2,
982 p0: Felt252::ONE,
983 p1: Felt252::ONE,
984 p2: Felt252::ZERO,
985 p3: Felt252::ZERO,
986 values_ptr: 23023,
987 offsets_ptr: 23067,
988 n: 1,
989 batch: BTreeMap::from([(
990 0,
991 ModInputMemoryVars {
992 a_offset: 8,
993 a0: Felt252::TWO,
994 a1: Felt252::ZERO,
995 a2: Felt252::ZERO,
996 a3: Felt252::ZERO,
997 b_offset: 28,
998 b0: Felt252::ONE,
999 b1: Felt252::ZERO,
1000 b2: Felt252::ZERO,
1001 b3: Felt252::ZERO,
1002 c_offset: 24,
1003 c0: Felt252::TWO,
1004 c1: Felt252::ZERO,
1005 c2: Felt252::ZERO,
1006 c3: Felt252::ZERO
1007 }
1008 ),])
1009 }
1010 ],
1011 zero_value_address: 23019
1012 })
1013 )
1014 }
1015}