Skip to main content

cairo_vm/vm/runners/builtin_runner/
bitwise.rs

1use crate::air_private_input::{PrivateInput, PrivateInputPair};
2
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    #[test]
181    fn get_used_instances() {
182        let builtin = BitwiseBuiltinRunner::new(Some(10), true);
183        let mut vm = vm!();
184
185        vm.segments = segments![
186            ((0, 0), (0, 0)),
187            ((0, 1), (0, 1)),
188            ((2, 0), (0, 0)),
189            ((2, 1), (0, 0))
190        ];
191
192        vm.segments.segment_used_sizes = Some(vec![1]);
193
194        assert_eq!(builtin.get_used_instances(&vm.segments), Ok(1));
195    }
196
197    #[test]
198    fn final_stack() {
199        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
200        let mut vm = vm!();
201
202        vm.segments = segments![
203            ((0, 0), (0, 0)),
204            ((0, 1), (0, 1)),
205            ((2, 0), (0, 0)),
206            ((2, 1), (0, 0))
207        ];
208
209        vm.segments.segment_used_sizes = Some(vec![0]);
210
211        let pointer = Relocatable::from((2, 2));
212
213        assert_eq!(
214            builtin.final_stack(&vm.segments, pointer).unwrap(),
215            Relocatable::from((2, 1))
216        );
217    }
218
219    #[test]
220    fn final_stack_error_stop_pointer() {
221        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
222        let mut vm = vm!();
223
224        vm.segments = segments![
225            ((0, 0), (0, 0)),
226            ((0, 1), (0, 1)),
227            ((2, 0), (0, 0)),
228            ((2, 1), (0, 0))
229        ];
230
231        vm.segments.segment_used_sizes = Some(vec![995]);
232
233        let pointer = Relocatable::from((2, 2));
234
235        assert_eq!(
236            builtin.final_stack(&vm.segments, pointer),
237            Err(RunnerError::InvalidStopPointer(Box::new((
238                BuiltinName::bitwise,
239                relocatable!(0, 995),
240                relocatable!(0, 0)
241            ))))
242        );
243    }
244
245    #[test]
246    fn final_stack_error_when_notincluded() {
247        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), false).into();
248        let mut vm = vm!();
249
250        vm.segments = segments![
251            ((0, 0), (0, 0)),
252            ((0, 1), (0, 1)),
253            ((2, 0), (0, 0)),
254            ((2, 1), (0, 0))
255        ];
256
257        vm.segments.segment_used_sizes = Some(vec![0]);
258
259        let pointer = Relocatable::from((2, 2));
260
261        assert_eq!(
262            builtin.final_stack(&vm.segments, pointer).unwrap(),
263            Relocatable::from((2, 2))
264        );
265    }
266
267    #[test]
268    fn final_stack_error_non_relocatable() {
269        let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
270        let mut vm = vm!();
271
272        vm.segments = segments![
273            ((0, 0), (0, 0)),
274            ((0, 1), (0, 1)),
275            ((2, 0), (0, 0)),
276            ((2, 1), 2)
277        ];
278
279        vm.segments.segment_used_sizes = Some(vec![0]);
280
281        let pointer = Relocatable::from((2, 2));
282
283        assert_eq!(
284            builtin.final_stack(&vm.segments, pointer),
285            Err(RunnerError::NoStopPointer(Box::new(BuiltinName::bitwise)))
286        );
287    }
288
289    #[test]
290    fn get_used_cells_and_allocated_size_test() {
291        let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
292
293        let program = program!(
294            builtins = vec![BuiltinName::bitwise],
295            data = vec_data!(
296                (4612671182993129469_i64),
297                (5189976364521848832_i64),
298                (18446744073709551615_i128),
299                (5199546496550207487_i64),
300                (4612389712311386111_i64),
301                (5198983563776393216_i64),
302                (2),
303                (2345108766317314046_i64),
304                (5191102247248822272_i64),
305                (5189976364521848832_i64),
306                (7),
307                (1226245742482522112_i64),
308                ((
309                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
310                    10
311                )),
312                (2345108766317314046_i64)
313            ),
314            main = Some(8),
315        );
316
317        let mut cairo_runner = cairo_runner!(program);
318        cairo_runner.vm.segments.segment_used_sizes = Some(vec![0]);
319
320        let mut hint_processor = BuiltinHintProcessor::new_empty();
321
322        let address = cairo_runner.initialize(false).unwrap();
323
324        cairo_runner
325            .run_until_pc(address, &mut hint_processor)
326            .unwrap();
327
328        assert_eq!(
329            builtin.get_used_cells_and_allocated_size(&cairo_runner.vm),
330            Ok((0, 5))
331        );
332    }
333
334    #[test]
335    fn get_allocated_memory_units() {
336        let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into();
337
338        let program = program!(
339            builtins = vec![BuiltinName::pedersen, BuiltinName::bitwise],
340            data = vec_data!(
341                (4612671182993129469_i64),
342                (5189976364521848832_i64),
343                (18446744073709551615_i128),
344                (5199546496550207487_i64),
345                (4612389712311386111_i64),
346                (5198983563776393216_i64),
347                (2),
348                (2345108766317314046_i64),
349                (5191102247248822272_i64),
350                (5189976364521848832_i64),
351                (7),
352                (1226245742482522112_i64),
353                ((
354                    "3618502788666131213697322783095070105623107215331596699973092056135872020470",
355                    10
356                )),
357                (2345108766317314046_i64)
358            ),
359            main = Some(8),
360        );
361
362        let mut cairo_runner = cairo_runner!(program);
363
364        let mut hint_processor = BuiltinHintProcessor::new_empty();
365
366        let address = cairo_runner.initialize(false).unwrap();
367
368        cairo_runner
369            .run_until_pc(address, &mut hint_processor)
370            .unwrap();
371
372        assert_eq!(builtin.get_allocated_memory_units(&cairo_runner.vm), Ok(5));
373    }
374
375    #[test]
376    fn deduce_memory_cell_bitwise_for_preset_memory_valid_and() {
377        let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 7), 0)];
378        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
379        let result = builtin.deduce_memory_cell(Relocatable::from((0, 7)), &memory);
380        assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(8)))));
381    }
382
383    #[test]
384    fn deduce_memory_cell_bitwise_for_preset_memory_valid_xor() {
385        let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 8), 0)];
386        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
387        let result = builtin.deduce_memory_cell(Relocatable::from((0, 8)), &memory);
388        assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(6)))));
389    }
390
391    #[test]
392    fn deduce_memory_cell_bitwise_for_preset_memory_valid_or() {
393        let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 9), 0)];
394        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
395        let result = builtin.deduce_memory_cell(Relocatable::from((0, 9)), &memory);
396        assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(14)))));
397    }
398
399    #[test]
400    fn deduce_memory_cell_bitwise_for_preset_memory_incorrect_offset() {
401        let memory = memory![((0, 3), 10), ((0, 4), 12), ((0, 5), 0)];
402        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
403        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
404        assert_eq!(result, Ok(None));
405    }
406
407    #[test]
408    fn deduce_memory_cell_bitwise_for_preset_memory_no_values_to_operate() {
409        let memory = memory![((0, 5), 12), ((0, 7), 0)];
410        let builtin = BitwiseBuiltinRunner::new(Some(256), true);
411        let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory);
412        assert_eq!(result, Ok(None));
413    }
414
415    #[test]
416    fn get_used_cells_missing_segment_used_sizes() {
417        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
418        let vm = vm!();
419
420        assert_eq!(
421            builtin.get_used_cells(&vm.segments),
422            Err(MemoryError::MissingSegmentUsedSizes)
423        );
424    }
425
426    #[test]
427    fn get_used_cells_empty() {
428        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
429        let mut vm = vm!();
430
431        vm.segments.segment_used_sizes = Some(vec![0]);
432        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(0));
433    }
434
435    #[test]
436    fn get_used_cells() {
437        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
438        let mut vm = vm!();
439
440        vm.segments.segment_used_sizes = Some(vec![4]);
441        assert_eq!(builtin.get_used_cells(&vm.segments), Ok(4));
442    }
443
444    #[test]
445    fn get_used_diluted_check_units_a() {
446        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
447        assert_eq!(builtin.get_used_diluted_check_units(12, 2), 535);
448    }
449
450    #[test]
451    fn get_used_diluted_check_units_b() {
452        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
453        assert_eq!(builtin.get_used_diluted_check_units(30, 56), 150);
454    }
455
456    #[test]
457    fn get_used_diluted_check_units_c() {
458        let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true));
459        assert_eq!(builtin.get_used_diluted_check_units(50, 25), 250);
460    }
461
462    #[test]
463    fn get_air_private_input() {
464        let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into();
465
466        let segments = segments![
467            ((0, 0), 0),
468            ((0, 1), 1),
469            ((0, 2), 2),
470            ((0, 3), 3),
471            ((0, 4), 4),
472            ((0, 5), 5),
473            ((0, 6), 6),
474            ((0, 7), 7),
475            ((0, 8), 8),
476            ((0, 9), 9),
477            ((0, 10), 10),
478            ((0, 11), 11),
479            ((0, 12), 12),
480            ((0, 13), 13),
481            ((0, 14), 14)
482        ];
483        assert_eq!(
484            builtin.air_private_input(&segments),
485            (vec![
486                PrivateInput::Pair(PrivateInputPair {
487                    index: 0,
488                    x: 0.into(),
489                    y: 1.into()
490                }),
491                PrivateInput::Pair(PrivateInputPair {
492                    index: 1,
493                    x: 5.into(),
494                    y: 6.into()
495                }),
496                PrivateInput::Pair(PrivateInputPair {
497                    index: 2,
498                    x: 10.into(),
499                    y: 11.into()
500                }),
501            ]),
502        );
503    }
504}