use crate::integer::conversion::to_twos_complement_limbs::limbs_twos_complement_in_place;
use crate::natural::InnerNatural::{Large, Small};
use crate::natural::{Natural, bit_to_limb_count_ceiling, bit_to_limb_count_floor};
use crate::platform::Limb;
use alloc::vec::Vec;
use malachite_base::num::arithmetic::traits::{
ModPowerOf2, ModPowerOf2Assign, NegModPowerOf2, NegModPowerOf2Assign, RemPowerOf2,
RemPowerOf2Assign,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::conversion::traits::WrappingFrom;
use malachite_base::slices::slice_set_zero;
pub_test! {limbs_mod_power_of_2(xs: &[Limb], pow: u64) -> Vec<Limb> {
if pow == 0 {
return Vec::new();
}
let leftover_bits = pow & Limb::WIDTH_MASK;
let result_size = bit_to_limb_count_floor(pow);
if result_size >= xs.len() {
return xs.to_vec();
}
let mut result = xs[..result_size].to_vec();
if leftover_bits != 0 {
result.push(xs[result_size].mod_power_of_2(leftover_bits));
}
result
}}
pub_crate_test! {limbs_slice_mod_power_of_2_in_place(xs: &mut [Limb], pow: u64) {
if pow == 0 {
slice_set_zero(xs);
return;
}
let new_size = bit_to_limb_count_ceiling(pow);
if new_size > xs.len() {
return;
}
slice_set_zero(&mut xs[new_size..]);
let leftover_bits = pow & Limb::WIDTH_MASK;
if leftover_bits != 0 {
xs[new_size - 1].mod_power_of_2_assign(leftover_bits);
}
}}
pub_crate_test! {limbs_vec_mod_power_of_2_in_place(xs: &mut Vec<Limb>, pow: u64) {
if pow == 0 {
xs.clear();
return;
}
let new_size = bit_to_limb_count_ceiling(pow);
if new_size > xs.len() {
return;
}
xs.truncate(new_size);
let leftover_bits = pow & Limb::WIDTH_MASK;
if leftover_bits != 0 {
xs[new_size - 1].mod_power_of_2_assign(leftover_bits);
}
}}
pub_crate_test! {limbs_neg_mod_power_of_2(xs: &[Limb], pow: u64) -> Vec<Limb> {
let mut result = xs.to_vec();
limbs_neg_mod_power_of_2_in_place(&mut result, pow);
result
}}
pub_crate_test! {limbs_neg_mod_power_of_2_in_place(xs: &mut Vec<Limb>, pow: u64) {
let new_size = bit_to_limb_count_ceiling(pow);
xs.resize(new_size, 0);
limbs_twos_complement_in_place(xs);
let leftover_bits = pow & Limb::WIDTH_MASK;
if leftover_bits != 0 {
xs[new_size - 1].mod_power_of_2_assign(leftover_bits);
}
}}
impl ModPowerOf2 for Natural {
type Output = Self;
#[inline]
fn mod_power_of_2(mut self, pow: u64) -> Self {
self.mod_power_of_2_assign(pow);
self
}
}
impl ModPowerOf2 for &Natural {
type Output = Natural;
fn mod_power_of_2(self, pow: u64) -> Natural {
match self {
Natural(Small(small)) => Natural(Small(small.mod_power_of_2(pow))),
Natural(Large(limbs)) => {
Natural::from_owned_limbs_asc(limbs_mod_power_of_2(limbs, pow))
}
}
}
}
impl ModPowerOf2Assign for Natural {
fn mod_power_of_2_assign(&mut self, pow: u64) {
match &mut *self {
Self(Small(small)) => small.mod_power_of_2_assign(pow),
Self(Large(limbs)) => {
limbs_vec_mod_power_of_2_in_place(limbs, pow);
self.trim();
}
}
}
}
impl RemPowerOf2 for Natural {
type Output = Self;
#[inline]
fn rem_power_of_2(self, pow: u64) -> Self {
self.mod_power_of_2(pow)
}
}
impl RemPowerOf2 for &Natural {
type Output = Natural;
#[inline]
fn rem_power_of_2(self, pow: u64) -> Natural {
self.mod_power_of_2(pow)
}
}
impl RemPowerOf2Assign for Natural {
#[inline]
fn rem_power_of_2_assign(&mut self, pow: u64) {
self.mod_power_of_2_assign(pow);
}
}
impl NegModPowerOf2 for Natural {
type Output = Self;
#[inline]
fn neg_mod_power_of_2(mut self, pow: u64) -> Self {
self.neg_mod_power_of_2_assign(pow);
self
}
}
impl NegModPowerOf2 for &Natural {
type Output = Natural;
fn neg_mod_power_of_2(self, pow: u64) -> Natural {
match (self, pow) {
(&Natural::ZERO, _) => Natural::ZERO,
(_, pow) if pow <= Limb::WIDTH => {
Natural::from(Limb::wrapping_from(self).neg_mod_power_of_2(pow))
}
(Natural(Small(small)), pow) => {
Natural::from_owned_limbs_asc(limbs_neg_mod_power_of_2(&[*small], pow))
}
(Natural(Large(limbs)), pow) => {
Natural::from_owned_limbs_asc(limbs_neg_mod_power_of_2(limbs, pow))
}
}
}
}
impl NegModPowerOf2Assign for Natural {
fn neg_mod_power_of_2_assign(&mut self, pow: u64) {
if *self == 0 {
} else if pow <= Limb::WIDTH {
*self = Self::from(Limb::wrapping_from(&*self).neg_mod_power_of_2(pow));
} else {
let limbs = self.promote_in_place();
limbs_neg_mod_power_of_2_in_place(limbs, pow);
self.trim();
}
}
}