use crate::{ibig::IBig, ubig::UBig, Sign::*};
use core::{
mem,
ops::{Shl, ShlAssign, Shr, ShrAssign},
};
macro_rules! impl_shifts {
($t:ty) => {
impl Shl<&usize> for $t {
type Output = $t;
#[inline]
fn shl(self, rhs: &usize) -> $t {
self.shl(*rhs)
}
}
impl Shl<&usize> for &$t {
type Output = $t;
#[inline]
fn shl(self, rhs: &usize) -> $t {
self.shl(*rhs)
}
}
impl ShlAssign<usize> for $t {
#[inline]
fn shl_assign(&mut self, rhs: usize) {
*self = mem::take(self) << rhs;
}
}
impl ShlAssign<&usize> for $t {
#[inline]
fn shl_assign(&mut self, rhs: &usize) {
*self = mem::take(self) << rhs;
}
}
impl Shr<&usize> for $t {
type Output = $t;
#[inline]
fn shr(self, rhs: &usize) -> $t {
self.shr(*rhs)
}
}
impl Shr<&usize> for &$t {
type Output = $t;
#[inline]
fn shr(self, rhs: &usize) -> $t {
self.shr(*rhs)
}
}
impl ShrAssign<usize> for $t {
#[inline]
fn shr_assign(&mut self, rhs: usize) {
*self = mem::take(self).shr(rhs);
}
}
impl ShrAssign<&usize> for $t {
#[inline]
fn shr_assign(&mut self, rhs: &usize) {
*self = mem::take(self).shr(rhs);
}
}
};
}
impl_shifts!(UBig);
impl_shifts!(IBig);
impl Shl<usize> for UBig {
type Output = UBig;
#[inline]
fn shl(self, rhs: usize) -> UBig {
UBig(self.into_repr().shl(rhs))
}
}
impl Shl<usize> for &UBig {
type Output = UBig;
#[inline]
fn shl(self, rhs: usize) -> UBig {
UBig(self.repr().shl(rhs))
}
}
impl Shr<usize> for UBig {
type Output = UBig;
#[inline]
fn shr(self, rhs: usize) -> UBig {
UBig(self.into_repr().shr(rhs))
}
}
impl Shr<usize> for &UBig {
type Output = UBig;
#[inline]
fn shr(self, rhs: usize) -> UBig {
UBig(self.repr().shr(rhs))
}
}
impl Shl<usize> for IBig {
type Output = IBig;
#[inline]
fn shl(self, rhs: usize) -> IBig {
let (sign, mag) = self.into_sign_repr();
let repr = mag << rhs;
IBig(repr.with_sign(sign))
}
}
impl Shl<usize> for &IBig {
type Output = IBig;
#[inline]
fn shl(self, rhs: usize) -> IBig {
let (sign, mag) = self.as_sign_repr();
let repr = mag << rhs;
IBig(repr.with_sign(sign))
}
}
impl Shr<usize> for IBig {
type Output = IBig;
#[inline]
fn shr(self, rhs: usize) -> IBig {
let (sign, mag) = self.into_sign_repr();
match sign {
Positive => IBig(mag >> rhs),
Negative => {
let b = mag.as_ref().are_low_bits_nonzero(rhs);
-IBig(mag >> rhs) - IBig::from(b)
}
}
}
}
impl Shr<usize> for &IBig {
type Output = IBig;
#[inline]
fn shr(self, rhs: usize) -> IBig {
let (sign, mag) = self.as_sign_repr();
match sign {
Positive => IBig(mag >> rhs),
Negative => {
let b = mag.are_low_bits_nonzero(rhs);
-IBig(mag >> rhs) - IBig::from(b)
}
}
}
}
pub(crate) mod repr {
use super::*;
use crate::{
arch::word::{DoubleWord, Word},
buffer::Buffer,
math,
primitive::{double_word, DWORD_BITS_USIZE, WORD_BITS_USIZE},
repr::{
Repr,
TypedRepr::{self, *},
TypedReprRef::{self, *},
},
shift,
};
impl Shl<usize> for TypedRepr {
type Output = Repr;
#[inline]
fn shl(self, rhs: usize) -> Repr {
match self {
Small(0) => Repr::zero(),
Small(dword) => shl_dword(dword, rhs),
Large(buffer) => shl_large(buffer, rhs),
}
}
}
impl Shr<usize> for TypedRepr {
type Output = Repr;
#[inline]
fn shr(self, rhs: usize) -> Repr {
match self {
Small(dword) => shr_dword(dword, rhs),
Large(buffer) => shr_large(buffer, rhs),
}
}
}
impl<'a> Shl<usize> for TypedReprRef<'a> {
type Output = Repr;
#[inline]
fn shl(self, rhs: usize) -> Repr {
match self {
RefSmall(0) => Repr::zero(),
RefSmall(dword) => shl_dword(dword, rhs),
RefLarge(words) => shl_large_ref(words, rhs),
}
}
}
impl<'a> Shr<usize> for TypedReprRef<'a> {
type Output = Repr;
#[inline]
fn shr(self, rhs: usize) -> Repr {
match self {
RefSmall(dword) => shr_dword(dword, rhs),
RefLarge(words) => shr_large_ref(words, rhs),
}
}
}
#[inline]
fn shl_dword(dword: DoubleWord, rhs: usize) -> Repr {
debug_assert!(dword != 0);
if rhs <= dword.leading_zeros() as usize {
Repr::from_dword(dword << rhs)
} else if dword == 1 {
shl_one_spilled(rhs)
} else {
shl_dword_spilled(dword, rhs)
}
}
fn shl_one_spilled(rhs: usize) -> Repr {
debug_assert!(rhs >= DWORD_BITS_USIZE);
let idx = rhs / WORD_BITS_USIZE;
let mut buffer = Buffer::allocate(idx + 1);
buffer.push_zeros(idx);
buffer.push(1 << (rhs % WORD_BITS_USIZE));
Repr::from_buffer(buffer)
}
fn shl_dword_spilled(dword: DoubleWord, rhs: usize) -> Repr {
let shift_words = rhs / WORD_BITS_USIZE;
let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
let (n0, n1, n2) = math::shl_dword(dword, shift_bits);
let mut buffer = Buffer::allocate(shift_words + 3);
buffer.push_zeros(shift_words);
buffer.push(n0);
buffer.push(n1);
buffer.push(n2);
Repr::from_buffer(buffer)
}
fn shl_large(mut buffer: Buffer, rhs: usize) -> Repr {
let shift_words = rhs / WORD_BITS_USIZE;
if buffer.capacity() < buffer.len() + shift_words + 1 {
return shl_large_ref(&buffer, rhs);
}
let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
let carry = shift::shl_in_place(&mut buffer, shift_bits);
buffer.push(carry);
buffer.push_zeros_front(shift_words);
Repr::from_buffer(buffer)
}
pub(crate) fn shl_large_ref(words: &[Word], rhs: usize) -> Repr {
let shift_words = rhs / WORD_BITS_USIZE;
let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
let mut buffer = Buffer::allocate(shift_words + words.len() + 1);
buffer.push_zeros(shift_words);
buffer.push_slice(words);
let carry = shift::shl_in_place(&mut buffer[shift_words..], shift_bits);
buffer.push(carry);
Repr::from_buffer(buffer)
}
#[inline]
fn shr_dword(dword: DoubleWord, rhs: usize) -> Repr {
if rhs < DWORD_BITS_USIZE {
Repr::from_dword(dword >> rhs)
} else {
Repr::zero()
}
}
fn shr_large(mut buffer: Buffer, rhs: usize) -> Repr {
let shift_words = rhs / WORD_BITS_USIZE;
if shift_words >= buffer.len() {
return Repr::zero();
}
let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
buffer.erase_front(shift_words);
shift::shr_in_place(&mut buffer, shift_bits);
Repr::from_buffer(buffer)
}
pub(crate) fn shr_large_ref(words: &[Word], rhs: usize) -> Repr {
let shift_words = rhs / WORD_BITS_USIZE;
let shift_bits = (rhs % WORD_BITS_USIZE) as u32;
let words = &words[shift_words.min(words.len())..];
match words {
[] => Repr::zero(),
&[w] => Repr::from_word(w >> shift_bits),
&[lo, hi] => Repr::from_dword(double_word(lo, hi) >> shift_bits),
_ => {
let mut buffer = Buffer::allocate(words.len());
buffer.push_slice(words);
shift::shr_in_place(&mut buffer, shift_bits);
Repr::from_buffer(buffer)
}
}
}
}