cairo_vm/vm/runners/builtin_runner/
bitwise.rs

1use crate::air_private_input::{PrivateInput, PrivateInputPair};
2use crate::stdlib::{boxed::Box, vec::Vec};
3use crate::Felt252;
4use crate::{
5    types::{
6        instance_definitions::bitwise_instance_def::{CELLS_PER_BITWISE, TOTAL_N_BITS},
7        relocatable::{MaybeRelocatable, Relocatable},
8    },
9    vm::{
10        errors::{memory_errors::MemoryError, runner_errors::RunnerError},
11        vm_memory::{memory::Memory, memory_segments::MemorySegmentManager},
12    },
13};
14use num_integer::div_ceil;
15
16#[derive(Debug, Clone)]
17pub struct BitwiseBuiltinRunner {
18    ratio: Option<u32>,
19    pub base: usize,
20    pub(crate) stop_ptr: Option<usize>,
21    pub(crate) included: bool,
22}
23
24impl BitwiseBuiltinRunner {
25    pub(crate) fn new(ratio: Option<u32>, included: bool) -> Self {
26        BitwiseBuiltinRunner {
27            base: 0,
28            ratio,
29            stop_ptr: None,
30            included,
31        }
32    }
33
34    pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) {
35        self.base = segments.add().segment_index as usize // segments.add() always returns a positive index
36    }
37
38    pub fn initial_stack(&self) -> Vec<MaybeRelocatable> {
39        if self.included {
40            vec![MaybeRelocatable::from((self.base as isize, 0))]
41        } else {
42            vec![]
43        }
44    }
45
46    pub fn base(&self) -> usize {
47        self.base
48    }
49
50    pub fn ratio(&self) -> Option<u32> {
51        self.ratio
52    }
53
54    pub fn deduce_memory_cell(
55        &self,
56        address: Relocatable,
57        memory: &Memory,
58    ) -> Result<Option<MaybeRelocatable>, RunnerError> {
59        let index = address.offset % CELLS_PER_BITWISE as usize;
60        if index <= 1 {
61            return Ok(None);
62        }
63        let x_addr = (address - index)?;
64        let y_addr = (x_addr + 1_usize)?;
65
66        let (Ok(num_x), Ok(num_y)) = (memory.get_integer(x_addr), memory.get_integer(y_addr))
67        else {
68            return Ok(None);
69        };
70
71        // NOTE: we could operate on bytes here, but it caused a 20% slowdown
72        // on several benchmarks.
73        let to_limbs = |x_addr, x: &Felt252| -> Result<[u64; 4], RunnerError> {
74            const LEADING_BITS: u64 = 0xf800000000000000;
75            let limbs = x.to_le_digits();
76            if limbs[3] & LEADING_BITS != 0 {
77                return Err(RunnerError::IntegerBiggerThanPowerOfTwo(Box::new((
78                    x_addr,
79                    TOTAL_N_BITS,
80                    *x,
81                ))));
82            }
83            Ok(limbs)
84        };
85        let (limbs_x, limbs_y) = (to_limbs(x_addr, &num_x)?, to_limbs(y_addr, &num_y)?);
86        let mut limbs_xy = [0u64; 4];
87        for (xy, (x, y)) in limbs_xy
88            .iter_mut()
89            .zip(limbs_x.into_iter().zip(limbs_y.into_iter()))
90        {
91            *xy = match index {
92                2 => x & y,
93                3 => x ^ y,
94                4 => x | y,
95                _ => {
96                    return Ok(None);
97                }
98            };
99        }
100        let mut bytes_xy = [0u8; 32];
101        bytes_xy[..8].copy_from_slice(limbs_xy[0].to_le_bytes().as_slice());
102        bytes_xy[8..16].copy_from_slice(limbs_xy[1].to_le_bytes().as_slice());
103        bytes_xy[16..24].copy_from_slice(limbs_xy[2].to_le_bytes().as_slice());
104        bytes_xy[24..].copy_from_slice(limbs_xy[3].to_le_bytes().as_slice());
105        Ok(Some(MaybeRelocatable::from(Felt252::from_bytes_le_slice(
106            &bytes_xy,
107        ))))
108    }
109
110    pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result<usize, MemoryError> {
111        segments
112            .get_segment_used_size(self.base)
113            .ok_or(MemoryError::MissingSegmentUsedSizes)
114    }
115
116    pub fn get_used_diluted_check_units(&self, diluted_spacing: u32, diluted_n_bits: u32) -> usize {
117        let total_n_bits = TOTAL_N_BITS;
118        let mut partition = Vec::with_capacity(total_n_bits as usize);
119        for i in (0..total_n_bits).step_by((diluted_spacing * diluted_n_bits) as usize) {
120            for j in 0..diluted_spacing {
121                if i + j < total_n_bits {
122                    partition.push(i + j)
123                };
124            }
125        }
126        let partition_lengh = partition.len();
127        let num_trimmed = partition
128            .into_iter()
129            .filter(|elem| elem + diluted_spacing * (diluted_n_bits - 1) + 1 > total_n_bits)
130            .count();
131        4 * partition_lengh + num_trimmed
132    }
133
134    pub fn get_used_instances(
135        &self,
136        segments: &MemorySegmentManager,
137    ) -> Result<usize, MemoryError> {
138        let used_cells = self.get_used_cells(segments)?;
139        Ok(div_ceil(used_cells, CELLS_PER_BITWISE as usize))
140    }
141
142    pub fn air_private_input(&self, memory: &Memory) -> Vec<PrivateInput> {
143        let mut private_inputs = vec![];
144        if let Some(segment) = memory.data.get(self.base) {
145            let segment_len = segment.len();
146            for (index, off) in (0..segment_len)
147                .step_by(CELLS_PER_BITWISE as usize)
148                .enumerate()
149            {
150                // Add the input cells of each bitwise instance to the private inputs
151                if let (Ok(x), Ok(y)) = (
152                    memory.get_integer((self.base as isize, off).into()),
153                    memory.get_integer((self.base as isize, off + 1).into()),
154                ) {
155                    private_inputs.push(PrivateInput::Pair(PrivateInputPair {
156                        index,
157                        x: *x,
158                        y: *y,
159                    }))
160                }
161            }
162        }
163        private_inputs
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170    use crate::relocatable;
171    use crate::types::builtin_name::BuiltinName;
172    use crate::vm::errors::memory_errors::MemoryError;
173    use crate::vm::runners::builtin_runner::BuiltinRunner;
174    use crate::Felt252;
175    use crate::{
176        hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor,
177        types::program::Program, utils::test_utils::*,
178    };
179
180    #[cfg(target_arch = "wasm32")]
181    use wasm_bindgen_test::*;
182
183    #[test]
184    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
185    fn get_used_instances() {
186        let builtin = BitwiseBuiltinRunner::new(Some(10), true);
187        let mut vm = vm!();
188
189        vm.segments = segments![
190            ((0, 0), (0, 0)),
191            ((0, 1), (0, 1)),
192            ((2, 0), (0, 0)),
193            ((2, 1), (0, 0))
194        ];
195
196        vm.segments.segment_used_sizes = Some(vec![1]);
197
198        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
199    }
200
201    #[test]
202    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
203    fn final_stack() {
204        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
205        let mut vm = vm!();
206
207        vm.segments = segments![
208            ((0, 0), (0, 0)),
209            ((0, 1), (0, 1)),
210            ((2, 0), (0, 0)),
211            ((2, 1), (0, 0))
212        ];
213
214        vm.segments.segment_used_sizes = Some(vec![0]);
215
216        let pointer = Relocatable::from((2, 2));
217
218        assert_eq!(
219            builtin.final_stack(&vm.segments, pointer).unwrap(),
220            Relocatable::from((2, 1))
221        );
222    }
223
224    #[test]
225    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
226    fn final_stack_error_stop_pointer() {
227        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
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![995]);
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::bitwise,
245                relocatable!(0, 995),
246                relocatable!(0, 0)
247            ))))
248        );
249    }
250
251    #[test]
252    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
253    fn final_stack_error_when_notincluded() {
254        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), false).into();
255        let mut vm = vm!();
256
257        vm.segments = segments![
258            ((0, 0), (0, 0)),
259            ((0, 1), (0, 1)),
260            ((2, 0), (0, 0)),
261            ((2, 1), (0, 0))
262        ];
263
264        vm.segments.segment_used_sizes = Some(vec![0]);
265
266        let pointer = Relocatable::from((2, 2));
267
268        assert_eq!(
269            builtin.final_stack(&vm.segments, pointer).unwrap(),
270            Relocatable::from((2, 2))
271        );
272    }
273
274    #[test]
275    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
276    fn final_stack_error_non_relocatable() {
277        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
278        let mut vm = vm!();
279
280        vm.segments = segments![
281            ((0, 0), (0, 0)),
282            ((0, 1), (0, 1)),
283            ((2, 0), (0, 0)),
284            ((2, 1), 2)
285        ];
286
287        vm.segments.segment_used_sizes = Some(vec![0]);
288
289        let pointer = Relocatable::from((2, 2));
290
291        assert_eq!(
292            builtin.final_stack(&vm.segments, pointer),
293            Err(RunnerError::NoStopPointer(Box::new(BuiltinName::bitwise)))
294        );
295    }
296
297    #[test]
298    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
299    fn get_used_cells_and_allocated_size_test() {
300        let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
301
302        let program = program!(
303            builtins = vec![BuiltinName::bitwise],
304            data = vec_data!(
305                (4612671182993129469_i64),
306                (5189976364521848832_i64),
307                (18446744073709551615_i128),
308                (5199546496550207487_i64),
309                (4612389712311386111_i64),
310                (5198983563776393216_i64),
311                (2),
312                (2345108766317314046_i64),
313                (5191102247248822272_i64),
314                (5189976364521848832_i64),
315                (7),
316                (1226245742482522112_i64),
317                ((
318                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
319                    10
320                )),
321                (2345108766317314046_i64)
322            ),
323            main = Some(8),
324        );
325
326        let mut cairo_runner = cairo_runner!(program);
327        cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
328
329        let mut hint_processor = BuiltinHintProcessor::new_empty();
330
331        let address = cairo_runner.initialize(false).unwrap();
332
333        cairo_runner
334            .run_until_pc(address, &mut hint_processor)
335            .unwrap();
336
337        assert_eq!(
338            builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
339            Ok((0, 5))
340        );
341    }
342
343    #[test]
344    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
345    fn get_allocated_memory_units() {
346        let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
347
348        let program = program!(
349            builtins = vec![BuiltinName::pedersen, BuiltinName::bitwise],
350            data = vec_data!(
351                (4612671182993129469_i64),
352                (5189976364521848832_i64),
353                (18446744073709551615_i128),
354                (5199546496550207487_i64),
355                (4612389712311386111_i64),
356                (5198983563776393216_i64),
357                (2),
358                (2345108766317314046_i64),
359                (5191102247248822272_i64),
360                (5189976364521848832_i64),
361                (7),
362                (1226245742482522112_i64),
363                ((
364                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
365                    10
366                )),
367                (2345108766317314046_i64)
368            ),
369            main = Some(8),
370        );
371
372        let mut cairo_runner = cairo_runner!(program);
373
374        let mut hint_processor = BuiltinHintProcessor::new_empty();
375
376        let address = cairo_runner.initialize(false).unwrap();
377
378        cairo_runner
379            .run_until_pc(address, &mut hint_processor)
380            .unwrap();
381
382        assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(5));
383    }
384
385    #[test]
386    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
387    fn deduce_memory_cell_bitwise_for_preset_memory_valid_and() {
388        let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 7), 0)];
389        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
390        let result = builtin.deduce_memory_cell(Relocatable::from((0, 7)), &memory);
391        assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(8)))));
392    }
393
394    #[test]
395    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
396    fn deduce_memory_cell_bitwise_for_preset_memory_valid_xor() {
397        let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 8), 0)];
398        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
399        let result = builtin.deduce_memory_cell(Relocatable::from((0, 8)), &memory);
400        assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(6)))));
401    }
402
403    #[test]
404    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
405    fn deduce_memory_cell_bitwise_for_preset_memory_valid_or() {
406        let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 9), 0)];
407        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
408        let result = builtin.deduce_memory_cell(Relocatable::from((0, 9)), &memory);
409        assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(14)))));
410    }
411
412    #[test]
413    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
414    fn deduce_memory_cell_bitwise_for_preset_memory_incorrect_offset() {
415        let memory = memory![((0, 3), 10), ((0, 4), 12), ((0, 5), 0)];
416        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
417        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
418        assert_eq!(result, Ok(None));
419    }
420
421    #[test]
422    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
423    fn deduce_memory_cell_bitwise_for_preset_memory_no_values_to_operate() {
424        let memory = memory![((0, 5), 12), ((0, 7), 0)];
425        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
426        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
427        assert_eq!(result, Ok(None));
428    }
429
430    #[test]
431    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
432    fn get_used_cells_missing_segment_used_sizes() {
433        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
434        let vm = vm!();
435
436        assert_eq!(
437            builtin.get_used_cells(&vm.segments),
438            Err(MemoryError::MissingSegmentUsedSizes)
439        );
440    }
441
442    #[test]
443    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
444    fn get_used_cells_empty() {
445        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
446        let mut vm = vm!();
447
448        vm.segments.segment_used_sizes = Some(vec![0]);
449        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
450    }
451
452    #[test]
453    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
454    fn get_used_cells() {
455        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
456        let mut vm = vm!();
457
458        vm.segments.segment_used_sizes = Some(vec![4]);
459        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
460    }
461
462    #[test]
463    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
464    fn get_used_diluted_check_units_a() {
465        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
466        assert_eq!(builtin.get_used_diluted_check_units(12, 2), 535);
467    }
468
469    #[test]
470    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
471    fn get_used_diluted_check_units_b() {
472        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
473        assert_eq!(builtin.get_used_diluted_check_units(30, 56), 150);
474    }
475
476    #[test]
477    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
478    fn get_used_diluted_check_units_c() {
479        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
480        assert_eq!(builtin.get_used_diluted_check_units(50, 25), 250);
481    }
482
483    #[test]
484    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
485    fn get_air_private_input() {
486        let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into();
487
488        let segments = segments![
489            ((0, 0), 0),
490            ((0, 1), 1),
491            ((0, 2), 2),
492            ((0, 3), 3),
493            ((0, 4), 4),
494            ((0, 5), 5),
495            ((0, 6), 6),
496            ((0, 7), 7),
497            ((0, 8), 8),
498            ((0, 9), 9),
499            ((0, 10), 10),
500            ((0, 11), 11),
501            ((0, 12), 12),
502            ((0, 13), 13),
503            ((0, 14), 14)
504        ];
505        assert_eq!(
506            builtin.air_private_input(&segments),
507            (vec![
508                PrivateInput::Pair(PrivateInputPair {
509                    index: 0,
510                    x: 0.into(),
511                    y: 1.into()
512                }),
513                PrivateInput::Pair(PrivateInputPair {
514                    index: 1,
515                    x: 5.into(),
516                    y: 6.into()
517                }),
518                PrivateInput::Pair(PrivateInputPair {
519                    index: 2,
520                    x: 10.into(),
521                    y: 11.into()
522                }),
523            ]),
524        );
525    }
526}