binius_core/constraint_system/
shift.rs1use binius_utils::serialization::{DeserializeBytes, SerializationError, SerializeBytes};
3use bytes::{Buf, BufMut};
4
5use super::{ValueIndex, ValueVec};
6use crate::word::Word;
7
8#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
12pub enum ShiftVariant {
13 Sll,
15 Slr,
17 Sar,
22 Rotr,
26 Sll32,
31 Srl32,
36 Sra32,
42 Rotr32,
48}
49
50impl ShiftVariant {
51 #[inline]
61 pub fn apply(self, word: Word, amount: usize) -> Word {
62 let amount = amount as u32;
64 match self {
70 ShiftVariant::Sll => word << amount,
71 ShiftVariant::Slr => word >> amount,
72 ShiftVariant::Sar => word.sar(amount),
73 ShiftVariant::Rotr => word.rotr(amount),
74 ShiftVariant::Sll32 => word.sll32(amount),
75 ShiftVariant::Srl32 => word.srl32(amount),
76 ShiftVariant::Sra32 => word.sra32(amount),
77 ShiftVariant::Rotr32 => word.rotr32(amount),
78 }
79 }
80}
81
82impl SerializeBytes for ShiftVariant {
83 fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
84 let index = match self {
85 ShiftVariant::Sll => 0u8,
86 ShiftVariant::Slr => 1u8,
87 ShiftVariant::Sar => 2u8,
88 ShiftVariant::Rotr => 3u8,
89 ShiftVariant::Sll32 => 4u8,
90 ShiftVariant::Srl32 => 5u8,
91 ShiftVariant::Sra32 => 6u8,
92 ShiftVariant::Rotr32 => 7u8,
93 };
94 index.serialize(write_buf)
95 }
96}
97
98impl DeserializeBytes for ShiftVariant {
99 fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
100 where
101 Self: Sized,
102 {
103 let index = u8::deserialize(read_buf)?;
104 match index {
105 0 => Ok(ShiftVariant::Sll),
106 1 => Ok(ShiftVariant::Slr),
107 2 => Ok(ShiftVariant::Sar),
108 3 => Ok(ShiftVariant::Rotr),
109 4 => Ok(ShiftVariant::Sll32),
110 5 => Ok(ShiftVariant::Srl32),
111 6 => Ok(ShiftVariant::Sra32),
112 7 => Ok(ShiftVariant::Rotr32),
113 _ => Err(SerializationError::UnknownEnumVariant {
114 name: "ShiftVariant",
115 index,
116 }),
117 }
118 }
119}
120
121#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
128pub struct ShiftedValueIndex {
129 pub value_index: ValueIndex,
131 pub shift_variant: ShiftVariant,
133 pub amount: u8,
138}
139
140impl ShiftedValueIndex {
141 pub const fn plain(value_index: ValueIndex) -> Self {
144 Self {
145 value_index,
146 shift_variant: ShiftVariant::Sll,
147 amount: 0,
148 }
149 }
150
151 pub fn sll(value_index: ValueIndex, amount: usize) -> Self {
156 assert!(amount < 64, "shift amount n={amount} out of range");
157 Self {
158 value_index,
159 shift_variant: ShiftVariant::Sll,
160 amount: amount as u8,
161 }
162 }
163
164 pub fn srl(value_index: ValueIndex, amount: usize) -> Self {
169 assert!(amount < 64, "shift amount n={amount} out of range");
170 Self {
171 value_index,
172 shift_variant: ShiftVariant::Slr,
173 amount: amount as u8,
174 }
175 }
176
177 pub fn sar(value_index: ValueIndex, amount: usize) -> Self {
185 assert!(amount < 64, "shift amount n={amount} out of range");
186 Self {
187 value_index,
188 shift_variant: ShiftVariant::Sar,
189 amount: amount as u8,
190 }
191 }
192
193 pub fn rotr(value_index: ValueIndex, amount: usize) -> Self {
200 assert!(amount < 64, "shift amount n={amount} out of range");
201 Self {
202 value_index,
203 shift_variant: ShiftVariant::Rotr,
204 amount: amount as u8,
205 }
206 }
207
208 pub fn sll32(value_index: ValueIndex, amount: usize) -> Self {
216 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit shift");
217 Self {
218 value_index,
219 shift_variant: ShiftVariant::Sll32,
220 amount: amount as u8,
221 }
222 }
223
224 pub fn srl32(value_index: ValueIndex, amount: usize) -> Self {
232 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit shift");
233 Self {
234 value_index,
235 shift_variant: ShiftVariant::Srl32,
236 amount: amount as u8,
237 }
238 }
239
240 pub fn sra32(value_index: ValueIndex, amount: usize) -> Self {
249 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit shift");
250 Self {
251 value_index,
252 shift_variant: ShiftVariant::Sra32,
253 amount: amount as u8,
254 }
255 }
256
257 pub fn rotr32(value_index: ValueIndex, amount: usize) -> Self {
266 assert!(amount < 32, "shift amount n={amount} out of range for 32-bit rotate");
267 Self {
268 value_index,
269 shift_variant: ShiftVariant::Rotr32,
270 amount: amount as u8,
271 }
272 }
273
274 #[inline]
279 pub fn eval(&self, witness: &ValueVec) -> Word {
280 self.shift_variant
282 .apply(witness[self.value_index], self.amount as usize)
283 }
284}
285
286impl SerializeBytes for ShiftedValueIndex {
287 fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
288 self.value_index.serialize(&mut write_buf)?;
289 self.shift_variant.serialize(&mut write_buf)?;
290 (self.amount as usize).serialize(write_buf)
292 }
293}
294
295impl DeserializeBytes for ShiftedValueIndex {
296 fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
297 where
298 Self: Sized,
299 {
300 let value_index = ValueIndex::deserialize(&mut read_buf)?;
301 let shift_variant = ShiftVariant::deserialize(&mut read_buf)?;
302 let amount = usize::deserialize(read_buf)?;
303
304 if amount >= 64 {
307 return Err(SerializationError::InvalidConstruction {
308 name: "ShiftedValueIndex::amount",
309 });
310 }
311
312 Ok(ShiftedValueIndex {
313 value_index,
314 shift_variant,
315 amount: amount as u8,
316 })
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
325 fn test_shift_variant_serialization_round_trip() {
326 let variants = [
327 ShiftVariant::Sll,
328 ShiftVariant::Slr,
329 ShiftVariant::Sar,
330 ShiftVariant::Rotr,
331 ];
332
333 for variant in variants {
334 let mut buf = Vec::new();
335 variant.serialize(&mut buf).unwrap();
336
337 let deserialized = ShiftVariant::deserialize(&mut buf.as_slice()).unwrap();
338 match (variant, deserialized) {
339 (ShiftVariant::Sll, ShiftVariant::Sll)
340 | (ShiftVariant::Slr, ShiftVariant::Slr)
341 | (ShiftVariant::Sar, ShiftVariant::Sar)
342 | (ShiftVariant::Rotr, ShiftVariant::Rotr) => {}
343 _ => panic!("ShiftVariant round trip failed: {:?} != {:?}", variant, deserialized),
344 }
345 }
346 }
347
348 #[test]
349 fn test_shift_variant_unknown_variant() {
350 let mut buf = Vec::new();
352 255u8.serialize(&mut buf).unwrap();
353
354 let result = ShiftVariant::deserialize(&mut buf.as_slice());
355 assert!(result.is_err());
356 match result.unwrap_err() {
357 SerializationError::UnknownEnumVariant { name, index } => {
358 assert_eq!(name, "ShiftVariant");
359 assert_eq!(index, 255);
360 }
361 _ => panic!("Expected UnknownEnumVariant error"),
362 }
363 }
364
365 #[test]
366 fn test_shifted_value_index_serialization_round_trip() {
367 let shifted_value_index = ShiftedValueIndex::srl(ValueIndex(42), 23);
368
369 let mut buf = Vec::new();
370 shifted_value_index.serialize(&mut buf).unwrap();
371
372 let deserialized = ShiftedValueIndex::deserialize(&mut buf.as_slice()).unwrap();
373 assert_eq!(shifted_value_index.value_index, deserialized.value_index);
374 assert_eq!(shifted_value_index.amount, deserialized.amount);
375 match (shifted_value_index.shift_variant, deserialized.shift_variant) {
376 (ShiftVariant::Slr, ShiftVariant::Slr) => {}
377 _ => panic!("ShiftVariant mismatch"),
378 }
379 }
380
381 #[test]
382 fn test_shifted_value_index_invalid_amount() {
383 let mut buf = Vec::new();
385 ValueIndex(0).serialize(&mut buf).unwrap();
386 ShiftVariant::Sll.serialize(&mut buf).unwrap();
387 64usize.serialize(&mut buf).unwrap(); let result = ShiftedValueIndex::deserialize(&mut buf.as_slice());
390 assert!(result.is_err());
391 match result.unwrap_err() {
392 SerializationError::InvalidConstruction { name } => {
393 assert_eq!(name, "ShiftedValueIndex::amount");
394 }
395 _ => panic!("Expected InvalidConstruction error"),
396 }
397 }
398
399 #[test]
400 fn shifted_value_index_fits_in_a_word() {
401 assert_eq!(size_of::<ShiftedValueIndex>(), 8);
405 }
406}