pub(crate) fn shiftl(res: &mut [u64], copy: &[u64], amt: usize) { let digits = amt / 64; let bits = amt % 64; let mut carry = 0; let shift = 64 - bits; for i in 0..res.len() { let base = if i >= digits { copy[i-digits] } else { 0 }; let new_carry = if shift == 64 { 0 } else { base >> shift }; res[i] = (base << bits) | carry; carry = new_carry; } } pub(crate) fn shiftr(res: &mut [u64], copy: &[u64], amt: usize) { let digits = amt / 64; let bits = amt % 64; let mut carry = 0; let mask = !(0xFFFFFFFFFFFFFFFF << bits); let shift = (64 - bits) as u32; for (idx, val) in res.iter_mut().enumerate().rev() { let target = idx + digits; let base = if target >= copy.len() { 0 } else { copy[target] }; let (new_carry, _) = (base & mask).overflowing_shl(shift); *val = (base >> bits) | carry; carry = new_carry; } } macro_rules! shift_impls { ($name: ident, $size: expr) => { impl ShlAssign for $name { fn shl_assign(&mut self, amt: usize) { let copy = self.value.clone(); shiftl(&mut self.value, ©, amt); } } impl Shl for $name { type Output = $name; fn shl(mut self, amt: usize) -> $name { let copy = self.value.clone(); shiftl(&mut self.value, ©, amt); self } } impl<'a> Shl for &'a $name { type Output = $name; fn shl(self, amt: usize) -> $name { let mut res = $name{ value: self.value.clone() }; shiftl(&mut res.value, &self.value, amt); res } } impl ShrAssign for $name { fn shr_assign(&mut self, amt: usize) { let copy = self.value.clone(); shiftr(&mut self.value, ©, amt); } } impl Shr for $name { type Output = $name; fn shr(mut self, amt: usize) -> $name { let copy = self.value.clone(); shiftr(&mut self.value, ©, amt); self } } impl<'a> Shr for &'a $name { type Output = $name; fn shr(self, amt: usize) -> $name { let mut res = $name{ value: self.value.clone() }; shiftr(&mut res.value, &self.value, amt); res } } } } #[cfg(test)] macro_rules! generate_shiftl_tests { ($name: ident, $lname: ident) => { #[test] fn $lname() { generate_shiftl_tests!(body $name, $lname); } }; (ignore $name: ident, $lname: ident) => { #[test] #[ignore] fn $lname() { generate_shiftl_tests!(body $name, $lname); } }; (body $name: ident, $lname: ident) => { let fname = build_test_path("shiftl", stringify!($name)); run_test(fname.to_string(), 3, |case| { let (neg0, abytes) = case.get("a").unwrap(); let (neg1, lbytes) = case.get("l").unwrap(); let (neg2, rbytes) = case.get("r").unwrap(); assert!(!neg0 && !neg1 && !neg2); let a = $name::from_bytes(abytes); let l = $name::from_bytes(lbytes); let r = $name::from_bytes(rbytes); assert_eq!(r, a << usize::from(l)); }); }; } #[cfg(test)] macro_rules! generate_shiftr_tests { ($name: ident, $lname: ident) => { #[test] fn $lname() { generate_shiftr_tests!(body $name, $lname); } }; (ignore $name: ident, $lname: ident) => { #[test] #[ignore] fn $lname() { generate_shiftr_tests!(body $name, $lname); } }; (body $name: ident, $lname: ident) => { let fname = build_test_path("shiftr", stringify!($name)); run_test(fname.to_string(), 3, |case| { let (neg0, abytes) = case.get("a").unwrap(); let (neg1, lbytes) = case.get("l").unwrap(); let (neg2, rbytes) = case.get("r").unwrap(); assert!(!neg0 && !neg1 && !neg2); let a = $name::from_bytes(abytes); let l = $name::from_bytes(lbytes); let r = $name::from_bytes(rbytes); assert_eq!(r, a >> usize::from(l)); }); }; }