sp1_core_machine/operations/
fixed_rotate_right.rs1use slop_air::AirBuilder;
2use slop_algebra::{AbstractField, Field};
3use sp1_core_executor::{events::ByteRecord, ByteOpcode};
4use sp1_derive::AlignedBorrow;
5use sp1_hypercube::air::SP1AirBuilder;
6use sp1_primitives::consts::u32_to_u16_limbs;
7
8#[derive(AlignedBorrow, Default, Debug, Clone, Copy)]
12#[repr(C)]
13pub struct FixedRotateRightOperation<T> {
14 pub value: [T; 2],
16
17 pub higher_limb: [T; 2],
19}
20
21impl<F: Field> FixedRotateRightOperation<F> {
22 pub const fn nb_limbs_to_rotate(rotation: usize) -> usize {
23 rotation / 16
24 }
25
26 pub const fn nb_bits_to_rotate(rotation: usize) -> usize {
27 rotation % 16
28 }
29
30 pub const fn carry_multiplier(rotation: usize) -> u32 {
31 let nb_bits_to_rotate = Self::nb_bits_to_rotate(rotation);
32 1 << (16 - nb_bits_to_rotate)
33 }
34
35 pub fn populate(&mut self, record: &mut impl ByteRecord, input: u32, rotation: usize) -> u32 {
36 let input_limbs = u32_to_u16_limbs(input);
37 let expected = input.rotate_right(rotation as u32);
38 self.value = [
39 F::from_canonical_u16((expected & 0xFFFF) as u16),
40 F::from_canonical_u16((expected >> 16) as u16),
41 ];
42
43 let nb_limbs_to_rotate = Self::nb_limbs_to_rotate(rotation);
45 let nb_bits_to_rotate = Self::nb_bits_to_rotate(rotation);
46
47 let input_limbs_rotated =
49 [input_limbs[nb_limbs_to_rotate % 2], input_limbs[(1 + nb_limbs_to_rotate) % 2]];
50
51 for i in (0..2).rev() {
52 let limb = input_limbs_rotated[i];
53 let lower_limb = (limb & ((1 << nb_bits_to_rotate) - 1)) as u16;
54 let higher_limb = (limb >> nb_bits_to_rotate) as u16;
55 self.higher_limb[i] = F::from_canonical_u16(higher_limb);
56 record.add_bit_range_check(lower_limb, nb_bits_to_rotate as u8);
57 record.add_bit_range_check(higher_limb, (16 - nb_bits_to_rotate) as u8);
58 }
59
60 expected
61 }
62
63 pub fn eval<AB: SP1AirBuilder>(
67 builder: &mut AB,
68 input: [AB::Var; 2],
69 rotation: usize,
70 cols: FixedRotateRightOperation<AB::Var>,
71 is_real: AB::Var,
72 ) {
73 builder.assert_bool(is_real);
75
76 let nb_limbs_to_rotate = Self::nb_limbs_to_rotate(rotation);
78 let nb_bits_to_rotate = Self::nb_bits_to_rotate(rotation);
79 let carry_multiplier = AB::F::from_canonical_u32(Self::carry_multiplier(rotation));
80
81 let input_limbs_rotated =
83 [input[nb_limbs_to_rotate % 2], input[(1 + nb_limbs_to_rotate) % 2]];
84
85 let mut lower_limb = [AB::Expr::zero(), AB::Expr::zero()];
87 for i in 0..2 {
88 let limb = input_limbs_rotated[i];
89
90 lower_limb[i] =
95 limb - cols.higher_limb[i] * AB::Expr::from_canonical_u32(1 << nb_bits_to_rotate);
96
97 builder.send_byte(
99 AB::F::from_canonical_u32(ByteOpcode::Range as u32),
100 lower_limb[i].clone(),
101 AB::F::from_canonical_u32(nb_bits_to_rotate as u32),
102 AB::Expr::zero(),
103 is_real,
104 );
105 builder.send_byte(
107 AB::F::from_canonical_u32(ByteOpcode::Range as u32),
108 cols.higher_limb[i],
109 AB::Expr::from_canonical_u32(16 - nb_bits_to_rotate as u32),
110 AB::Expr::zero(),
111 is_real,
112 );
113 }
114
115 builder.when(is_real).assert_eq(
117 cols.value[1],
118 cols.higher_limb[1] + lower_limb[0].clone() * carry_multiplier,
119 );
120 builder.when(is_real).assert_eq(
121 cols.value[0],
122 cols.higher_limb[0] + lower_limb[1].clone() * carry_multiplier,
123 );
124 }
125}