use crate::integer::Integer;
use crate::natural::arithmetic::add::limbs_slice_add_limb_in_place;
use crate::natural::arithmetic::mul::limb::{
limbs_mul_limb_with_carry_to_out, limbs_slice_mul_limb_with_carry_in_place,
};
use crate::natural::arithmetic::mul::{
limbs_mul_greater_to_out, limbs_mul_greater_to_out_scratch_len,
};
use crate::natural::arithmetic::sub::{
limbs_slice_sub_in_place_right, limbs_sub_greater_in_place_left, limbs_sub_limb_in_place,
limbs_sub_limb_to_out,
};
use crate::natural::arithmetic::sub_mul::{
limbs_sub_mul_limb_same_length_in_place_left, limbs_sub_mul_limb_same_length_in_place_right,
};
use crate::natural::comparison::cmp::limbs_cmp;
use crate::natural::logic::not::limbs_not_in_place;
use crate::platform::Limb;
use malachite_base::num::arithmetic::traits::{
AddMul, AddMulAssign, NegAssign, SubMul, SubMulAssign, WrappingAddAssign, WrappingSubAssign,
};
use malachite_base::slices::slice_test_zero;
use std::cmp::Ordering;
pub_crate_test! {limbs_overflowing_sub_mul_limb(
xs: &[Limb],
ys: &[Limb],
z: Limb
) -> (Vec<Limb>, bool) {
let mut result;
let sign = if xs.len() >= ys.len() {
result = xs.to_vec();
limbs_overflowing_sub_mul_limb_greater_in_place_left(&mut result, ys, z)
} else {
result = ys.to_vec();
limbs_overflowing_sub_mul_limb_smaller_in_place_right(xs, &mut result, z)
};
(result, sign)
}}
pub_crate_test! {limbs_overflowing_sub_mul_limb_in_place_left(
xs: &mut Vec<Limb>,
ys: &[Limb],
z: Limb,
) -> bool {
let xs_len = xs.len();
let ys_len = ys.len();
if xs_len >= ys_len {
limbs_overflowing_sub_mul_limb_greater_in_place_left(xs, ys, z)
} else {
let (ys_lo, ys_hi) = ys.split_at(xs_len);
let mut borrow = limbs_sub_mul_limb_same_length_in_place_left(xs, ys_lo, z);
limbs_not_in_place(xs);
if !limbs_slice_add_limb_in_place(xs, 1) {
borrow.wrapping_sub_assign(1);
}
let negative_one = borrow == Limb::MAX;
if negative_one {
borrow.wrapping_add_assign(1);
}
xs.resize(ys_len + 1, 0);
let xs_hi = &mut xs[xs_len..];
let (xs_hi_last, xs_hi_init) = xs_hi.split_last_mut().unwrap();
*xs_hi_last = limbs_mul_limb_with_carry_to_out(xs_hi_init, ys_hi, z, borrow);
if negative_one {
assert!(!limbs_sub_limb_in_place(xs_hi, 1));
}
false
}
}}
fn limbs_overflowing_sub_mul_limb_greater_in_place_left(
xs: &mut Vec<Limb>,
ys: &[Limb],
z: Limb,
) -> bool {
let xs_len = xs.len();
let ys_len = ys.len();
xs.push(0);
let (xs_lo, xs_hi) = xs.split_at_mut(ys_len);
let mut borrow = limbs_sub_mul_limb_same_length_in_place_left(xs_lo, ys, z);
if xs_len != ys_len {
borrow = Limb::from(limbs_sub_limb_in_place(xs_hi, borrow));
}
if borrow == 0 {
true
} else {
let (xs_last, xs_init) = xs.split_last_mut().unwrap();
*xs_last = borrow.wrapping_sub(1);
limbs_not_in_place(xs_init);
limbs_slice_add_limb_in_place(xs, 1);
false
}
}
pub_test! {limbs_overflowing_sub_mul_limb_in_place_right(
xs: &[Limb],
ys: &mut Vec<Limb>,
z: Limb,
) -> bool {
let xs_len = xs.len();
let ys_len = ys.len();
if xs_len >= ys_len {
ys.resize(xs_len + 1, 0);
let (xs_lo, xs_hi) = xs.split_at(ys_len);
let (ys_lo, ys_hi) = ys.split_at_mut(ys_len);
let mut borrow = limbs_sub_mul_limb_same_length_in_place_right(xs_lo, ys_lo, z);
if xs_len != ys_len {
borrow = Limb::from(limbs_sub_limb_to_out(ys_hi, xs_hi, borrow));
}
if borrow == 0 {
true
} else {
let (ys_last, ys_init) = ys.split_last_mut().unwrap();
*ys_last = borrow.wrapping_sub(1);
limbs_not_in_place(ys_init);
limbs_slice_add_limb_in_place(ys, 1);
false
}
} else {
limbs_overflowing_sub_mul_limb_smaller_in_place_right(xs, ys, z)
}
}}
fn limbs_overflowing_sub_mul_limb_smaller_in_place_right(
xs: &[Limb],
ys: &mut Vec<Limb>,
z: Limb,
) -> bool {
ys.push(0);
let (ys_lo, ys_hi) = ys.split_at_mut(xs.len());
let mut borrow = limbs_sub_mul_limb_same_length_in_place_right(xs, ys_lo, z);
limbs_not_in_place(ys_lo);
if !limbs_slice_add_limb_in_place(ys_lo, 1) {
borrow.wrapping_sub_assign(1);
}
let negative_one = borrow == Limb::MAX;
if negative_one {
borrow.wrapping_add_assign(1);
}
let (ys_hi_last, ys_hi_init) = ys_hi.split_last_mut().unwrap();
*ys_hi_last = limbs_slice_mul_limb_with_carry_in_place(ys_hi_init, z, borrow);
if negative_one {
assert!(!limbs_sub_limb_in_place(ys_hi, 1));
}
false
}
pub_crate_test! {limbs_overflowing_sub_mul_limb_in_place_either(
xs: &mut Vec<Limb>,
ys: &mut Vec<Limb>,
z: Limb,
) -> (bool, bool) {
if xs.len() >= ys.len() {
(
false,
limbs_overflowing_sub_mul_limb_greater_in_place_left(xs, ys, z),
)
} else {
(
true,
limbs_overflowing_sub_mul_limb_smaller_in_place_right(xs, ys, z),
)
}
}}
pub_crate_test! {limbs_overflowing_sub_mul(
xs: &[Limb],
ys: &[Limb],
zs: &[Limb]
) -> (Vec<Limb>, bool) {
let mut xs = xs.to_vec();
let sign = limbs_overflowing_sub_mul_in_place_left(&mut xs, ys, zs);
(xs, sign)
}}
pub_crate_test! {limbs_overflowing_sub_mul_in_place_left(
xs: &mut Vec<Limb>,
ys: &[Limb],
zs: &[Limb],
) -> bool {
if ys.len() >= zs.len() {
limbs_overflowing_sub_mul_greater_in_place_left(xs, ys, zs)
} else {
limbs_overflowing_sub_mul_greater_in_place_left(xs, zs, ys)
}
}}
fn limbs_overflowing_sub_mul_greater_in_place_left(
xs: &mut Vec<Limb>,
ys: &[Limb],
zs: &[Limb],
) -> bool {
let xs_len = xs.len();
let product_len = ys.len() + zs.len();
let mut product = vec![0; product_len];
let mut mul_scratch = vec![0; limbs_mul_greater_to_out_scratch_len(ys.len(), zs.len())];
if limbs_mul_greater_to_out(&mut product, ys, zs, &mut mul_scratch) == 0 {
product.pop();
}
assert_ne!(*product.last().unwrap(), 0);
if limbs_cmp(xs, &product) == Ordering::Less {
if xs_len < product_len {
xs.resize(product.len(), 0);
}
assert!(!limbs_slice_sub_in_place_right(
&product,
&mut xs[..product.len()],
xs_len,
));
false
} else {
assert!(!limbs_sub_greater_in_place_left(xs, &product));
!slice_test_zero(xs)
}
}
impl SubMul<Integer, Integer> for Integer {
type Output = Integer;
#[inline]
fn sub_mul(mut self, y: Integer, z: Integer) -> Integer {
self.sub_mul_assign(y, z);
self
}
}
impl<'a> SubMul<Integer, &'a Integer> for Integer {
type Output = Integer;
#[inline]
fn sub_mul(mut self, y: Integer, z: &'a Integer) -> Integer {
self.sub_mul_assign(y, z);
self
}
}
impl<'a> SubMul<&'a Integer, Integer> for Integer {
type Output = Integer;
#[inline]
fn sub_mul(mut self, y: &'a Integer, z: Integer) -> Integer {
self.sub_mul_assign(y, z);
self
}
}
impl<'a, 'b> SubMul<&'a Integer, &'b Integer> for Integer {
type Output = Integer;
#[inline]
fn sub_mul(mut self, y: &'a Integer, z: &'b Integer) -> Integer {
self.sub_mul_assign(y, z);
self
}
}
impl<'a, 'b, 'c> SubMul<&'a Integer, &'b Integer> for &'c Integer {
type Output = Integer;
fn sub_mul(self, y: &'a Integer, z: &'b Integer) -> Integer {
if self.sign == (y.sign != z.sign) {
Integer {
sign: self.sign,
abs: (&self.abs).add_mul(&y.abs, &z.abs),
}
} else {
let (abs, abs_result_sign) = self.abs.add_mul_neg(&y.abs, &z.abs);
Integer {
sign: (self.sign == abs_result_sign) || abs == 0,
abs,
}
}
}
}
impl SubMulAssign<Integer, Integer> for Integer {
fn sub_mul_assign(&mut self, y: Integer, z: Integer) {
self.add_mul_assign(-y, z);
}
}
impl<'a> SubMulAssign<Integer, &'a Integer> for Integer {
fn sub_mul_assign(&mut self, y: Integer, z: &'a Integer) {
self.add_mul_assign(-y, z);
}
}
impl<'a> SubMulAssign<&'a Integer, Integer> for Integer {
fn sub_mul_assign(&mut self, y: &'a Integer, z: Integer) {
self.add_mul_assign(y, -z);
}
}
impl<'a, 'b> SubMulAssign<&'a Integer, &'b Integer> for Integer {
fn sub_mul_assign(&mut self, y: &'a Integer, z: &'b Integer) {
self.neg_assign();
self.add_mul_assign(y, z);
self.neg_assign();
}
}