use crate::natural::InnerNatural::{Large, Small};
use crate::natural::arithmetic::add::limbs_vec_add_limb_in_place;
use crate::natural::arithmetic::divisible_by_power_of_2::limbs_divisible_by_power_of_2;
use crate::natural::arithmetic::shr::{
limbs_shr, limbs_slice_shr_in_place, limbs_vec_shr_in_place,
};
use crate::natural::logic::bit_access::limbs_get_bit;
use crate::natural::{Natural, bit_to_limb_count_floor};
use crate::platform::Limb;
use alloc::vec::Vec;
use core::cmp::Ordering::{self, *};
use core::ops::{Shl, ShlAssign};
use malachite_base::num::arithmetic::traits::{Parity, ShrRound, ShrRoundAssign, UnsignedAbs};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::signeds::PrimitiveSigned;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_base::slices::slice_test_zero;
use malachite_base::vecs::vec_delete_left;
pub_test! {limbs_shr_round_up(xs: &[Limb], bits: u64) -> (Vec<Limb>, Ordering) {
let delete_count = bit_to_limb_count_floor(bits);
if delete_count >= xs.len() {
(vec![1], Greater)
} else {
let (xs_lo, xs_hi) = xs.split_at(delete_count);
let mut exact = slice_test_zero(xs_lo);
let mut out = xs_hi.to_vec();
let small_bits = bits & Limb::WIDTH_MASK;
if small_bits != 0 {
exact &= limbs_slice_shr_in_place(&mut out, small_bits) == 0;
}
if !exact {
limbs_vec_add_limb_in_place(&mut out, 1);
}
(
out,
if exact {
Equal
} else {
Greater
},
)
}
}}
fn limbs_shr_round_half_integer_to_even(xs: &[Limb], bits: u64) -> (Vec<Limb>, Ordering) {
let delete_count = bit_to_limb_count_floor(bits);
if delete_count >= xs.len() {
(Vec::new(), if slice_test_zero(xs) { Equal } else { Less })
} else {
let small_bits = bits & Limb::WIDTH_MASK;
let (xs_lo, xs_hi) = xs.split_at(delete_count);
let mut exact = slice_test_zero(xs_lo);
let mut out = xs_hi.to_vec();
if small_bits != 0 {
exact &= limbs_slice_shr_in_place(&mut out, small_bits) == 0;
}
if !out.is_empty() && out[0].odd() {
limbs_vec_add_limb_in_place(&mut out, 1);
(out, Greater)
} else {
(out, if exact { Equal } else { Less })
}
}
}
pub_test! {limbs_shr_round_nearest(xs: &[Limb], bits: u64) -> (Vec<Limb>, Ordering) {
if bits == 0 {
(xs.to_vec(), Equal)
} else {
let d = slice_test_zero(xs) || limbs_divisible_by_power_of_2(xs, bits - 1);
if !limbs_get_bit(xs, bits - 1) {
(
limbs_shr(xs, bits),
if d { Equal } else { Less },
)
} else if d {
limbs_shr_round_half_integer_to_even(xs, bits)
} else {
limbs_shr_round_up(xs, bits)
}
}
}}
pub_test! {limbs_shr_exact(xs: &[Limb], bits: u64) -> Option<Vec<Limb>> {
if limbs_divisible_by_power_of_2(xs, bits) {
Some(limbs_shr(xs, bits))
} else {
None
}
}}
pub_test! {
limbs_shr_round(xs: &[Limb], bits: u64, rm: RoundingMode) -> Option<(Vec<Limb>, Ordering)> {
match rm {
Down | Floor => Some((
limbs_shr(xs, bits),
if limbs_divisible_by_power_of_2(xs, bits) {
Equal
} else {
Less
},
)),
Up | Ceiling => Some(limbs_shr_round_up(xs, bits)),
Nearest => Some(limbs_shr_round_nearest(xs, bits)),
Exact => limbs_shr_exact(xs, bits).map(|ss| (ss, Equal)),
}
}}
pub_test! {limbs_vec_shr_round_up_in_place(xs: &mut Vec<Limb>, bits: u64) -> Ordering {
let delete_count = bit_to_limb_count_floor(bits);
if delete_count >= xs.len() {
xs.truncate(1);
xs[0] = 1;
Greater
} else {
let mut exact = slice_test_zero(&xs[..delete_count]);
let small_bits = bits & Limb::WIDTH_MASK;
vec_delete_left(xs, delete_count);
if small_bits != 0 {
exact &= limbs_slice_shr_in_place(xs, small_bits) == 0;
}
if !exact {
limbs_vec_add_limb_in_place(xs, 1);
}
if exact {
Equal
} else {
Greater
}
}
}}
fn limbs_vec_shr_round_half_integer_to_even_in_place(xs: &mut Vec<Limb>, bits: u64) -> Ordering {
let delete_count = bit_to_limb_count_floor(bits);
if delete_count >= xs.len() {
let o = if slice_test_zero(xs) { Equal } else { Less };
xs.clear();
o
} else {
let small_bits = bits & Limb::WIDTH_MASK;
let mut exact = slice_test_zero(&xs[..delete_count]);
vec_delete_left(xs, delete_count);
if small_bits != 0 {
exact &= limbs_slice_shr_in_place(xs, small_bits) == 0;
}
if !xs.is_empty() && xs[0].odd() {
limbs_vec_add_limb_in_place(xs, 1);
Greater
} else if exact {
Equal
} else {
Less
}
}
}
pub_test! {limbs_vec_shr_round_nearest_in_place(xs: &mut Vec<Limb>, bits: u64) -> Ordering {
if bits == 0 {
Equal
} else {
let d = slice_test_zero(xs) || limbs_divisible_by_power_of_2(xs, bits - 1);
if !limbs_get_bit(xs, bits - 1) {
limbs_vec_shr_in_place(xs, bits);
if d {
Equal
} else {
Less
}
} else if d {
limbs_vec_shr_round_half_integer_to_even_in_place(xs, bits)
} else {
limbs_vec_shr_round_up_in_place(xs, bits)
}
}
}}
pub_test! {limbs_vec_shr_exact_in_place(xs: &mut Vec<Limb>, bits: u64) -> bool {
if limbs_divisible_by_power_of_2(xs, bits) {
limbs_vec_shr_in_place(xs, bits);
true
} else {
false
}
}}
pub_test! {limbs_vec_shr_round_in_place(
xs: &mut Vec<Limb>,
bits: u64,
rm: RoundingMode,
) -> (bool, Ordering) {
match rm {
Down | Floor => {
let exact = limbs_divisible_by_power_of_2(xs, bits);
limbs_vec_shr_in_place(xs, bits);
(
true,
if exact {
Equal
} else {
Less
},
)
}
Up | Ceiling => {
(true, limbs_vec_shr_round_up_in_place(xs, bits))
}
Nearest => (true, limbs_vec_shr_round_nearest_in_place(xs, bits)),
Exact => (limbs_vec_shr_exact_in_place(xs, bits), Equal),
}
}}
fn shr_round_unsigned_ref_n<T: PrimitiveUnsigned>(
x: &Natural,
bits: T,
rm: RoundingMode,
) -> (Natural, Ordering)
where
u64: ExactFrom<T>,
Limb: ShrRound<T, Output = Limb>,
{
match (x, bits) {
(&Natural::ZERO, _) => (x.clone(), Equal),
(_, bits) if bits == T::ZERO => (x.clone(), Equal),
(Natural(Small(small)), bits) => {
let (s, o) = small.shr_round(bits, rm);
(Natural(Small(s)), o)
}
(Natural(Large(limbs)), bits) => {
if let Some((out, o)) = limbs_shr_round(limbs, u64::exact_from(bits), rm) {
(Natural::from_owned_limbs_asc(out), o)
} else {
panic!("Right shift is not exact: {x} >> {bits}");
}
}
}
}
fn shr_round_assign_unsigned_n<T: PrimitiveUnsigned>(
x: &mut Natural,
bits: T,
rm: RoundingMode,
) -> Ordering
where
u64: ExactFrom<T>,
Limb: ShrRoundAssign<T>,
{
match (&mut *x, bits) {
(&mut Natural::ZERO, _) => Equal,
(_, bits) if bits == T::ZERO => Equal,
(Natural(Small(small)), bits) => small.shr_round_assign(bits, rm),
(Natural(Large(limbs)), bits) => {
let (b, o) = limbs_vec_shr_round_in_place(limbs, u64::exact_from(bits), rm);
assert!(b, "Right shift is not exact.");
x.trim();
o
}
}
}
macro_rules! impl_natural_shr_round_unsigned {
($t:ident) => {
impl ShrRound<$t> for Natural {
type Output = Natural;
#[inline]
fn shr_round(mut self, bits: $t, rm: RoundingMode) -> (Natural, Ordering) {
let o = self.shr_round_assign(bits, rm);
(self, o)
}
}
impl<'a> ShrRound<$t> for &Natural {
type Output = Natural;
#[inline]
fn shr_round(self, bits: $t, rm: RoundingMode) -> (Natural, Ordering) {
shr_round_unsigned_ref_n(self, bits, rm)
}
}
impl ShrRoundAssign<$t> for Natural {
#[inline]
fn shr_round_assign(&mut self, bits: $t, rm: RoundingMode) -> Ordering {
shr_round_assign_unsigned_n(self, bits, rm)
}
}
};
}
apply_to_unsigneds!(impl_natural_shr_round_unsigned);
fn shr_round_signed_ref_n<'a, U, S: PrimitiveSigned + UnsignedAbs<Output = U>>(
x: &'a Natural,
bits: S,
rm: RoundingMode,
) -> (Natural, Ordering)
where
&'a Natural: Shl<U, Output = Natural> + ShrRound<U, Output = Natural>,
{
if bits >= S::ZERO {
x.shr_round(bits.unsigned_abs(), rm)
} else {
(x << bits.unsigned_abs(), Equal)
}
}
fn shr_round_assign_signed_n<U, S: PrimitiveSigned + UnsignedAbs<Output = U>>(
x: &mut Natural,
bits: S,
rm: RoundingMode,
) -> Ordering
where
Natural: ShlAssign<U> + ShrRoundAssign<U>,
{
if bits >= S::ZERO {
x.shr_round_assign(bits.unsigned_abs(), rm)
} else {
*x <<= bits.unsigned_abs();
Equal
}
}
macro_rules! impl_natural_shr_round_signed {
($t:ident) => {
impl ShrRound<$t> for Natural {
type Output = Natural;
#[inline]
fn shr_round(mut self, bits: $t, rm: RoundingMode) -> (Natural, Ordering) {
let o = self.shr_round_assign(bits, rm);
(self, o)
}
}
impl<'a> ShrRound<$t> for &Natural {
type Output = Natural;
#[inline]
fn shr_round(self, bits: $t, rm: RoundingMode) -> (Natural, Ordering) {
shr_round_signed_ref_n(self, bits, rm)
}
}
impl ShrRoundAssign<$t> for Natural {
#[inline]
fn shr_round_assign(&mut self, bits: $t, rm: RoundingMode) -> Ordering {
shr_round_assign_signed_n(self, bits, rm)
}
}
};
}
apply_to_signeds!(impl_natural_shr_round_signed);