use crate::natural::InnerNatural::{Large, Small};
use crate::natural::Natural;
use crate::natural::arithmetic::shl::{limbs_shl, limbs_vec_shl_in_place};
use crate::platform::Limb;
use alloc::vec::Vec;
use core::iter::Sum;
use core::ops::{Add, AddAssign};
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
pub_crate_test! {limbs_add_limb(xs: &[Limb], mut y: Limb) -> Vec<Limb> {
let len = xs.len();
let mut out = Vec::with_capacity(len);
for i in 0..len {
let (sum, overflow) = xs[i].overflowing_add(y);
out.push(sum);
if overflow {
y = 1;
} else {
y = 0;
out.extend_from_slice(&xs[i + 1..]);
break;
}
}
if y != 0 {
out.push(y);
}
out
}}
pub_crate_test! {limbs_add_limb_to_out(out: &mut [Limb], xs: &[Limb], mut y: Limb) -> bool {
let len = xs.len();
assert!(out.len() >= len);
for i in 0..len {
let overflow;
(out[i], overflow) = xs[i].overflowing_add(y);
if overflow {
y = 1;
} else {
y = 0;
let copy_index = i + 1;
out[copy_index..len].copy_from_slice(&xs[copy_index..]);
break;
}
}
y != 0
}}
pub_crate_test! {limbs_slice_add_limb_in_place<T: PrimitiveUnsigned>(
xs: &mut [T],
mut y: T
) -> bool {
for x in &mut *xs {
if x.overflowing_add_assign(y) {
y = T::ONE;
} else {
return false;
}
}
y != T::ZERO
}}
pub_crate_test! {limbs_vec_add_limb_in_place(xs: &mut Vec<Limb>, y: Limb) {
assert!(!xs.is_empty());
if limbs_slice_add_limb_in_place(xs, y) {
xs.push(1);
}
}}
#[cfg(feature = "32_bit_limbs")]
#[inline]
pub(crate) fn add_with_carry_limb<T: PrimitiveUnsigned>(x: T, y: T, carry: T) -> (T, T) {
let result_no_carry = x.wrapping_add(y);
let result = result_no_carry.wrapping_add(carry);
let carry = T::from((result_no_carry < x) || (result < result_no_carry));
(result, carry)
}
#[inline]
pub(crate) fn add_with_carry<T: PrimitiveUnsigned>(x: T, y: T, carry: bool) -> (T, bool) {
let (sum, carry_1) = x.overflowing_add(y);
let (sum, carry_2) = sum.overflowing_add(if carry { T::ONE } else { T::ZERO });
(sum, carry_1 | carry_2)
}
pub_crate_test! {limbs_add_greater(xs: &[Limb], ys: &[Limb]) -> Vec<Limb> {
if core::ptr::eq(xs, ys) {
return limbs_shl(xs, 1);
}
let xs_len = xs.len();
let ys_len = ys.len();
assert!(xs_len >= ys_len);
let mut out = vec![0; xs_len];
let mut carry = limbs_add_same_length_to_out(&mut out, &xs[..ys_len], ys);
if xs_len != ys_len {
out[ys_len..].copy_from_slice(&xs[ys_len..]);
if carry {
carry = limbs_slice_add_limb_in_place(&mut out[ys_len..], 1);
}
}
if carry {
out.push(1);
}
out
}}
pub_crate_test! {limbs_add(xs: &[Limb], ys: &[Limb]) -> Vec<Limb> {
if xs.len() >= ys.len() {
limbs_add_greater(xs, ys)
} else {
limbs_add_greater(ys, xs)
}
}}
pub_crate_test! {limbs_add_same_length_to_out(out: &mut [Limb], xs: &[Limb], ys: &[Limb]) -> bool {
let len = xs.len();
assert_eq!(len, ys.len());
assert!(out.len() >= len);
let mut carry = false;
let mut out_chunks = out[..len].chunks_exact_mut(4);
let mut xs_chunks = xs.chunks_exact(4);
let mut ys_chunks = ys.chunks_exact(4);
for ((o, x), y) in (&mut out_chunks).zip(&mut xs_chunks).zip(&mut ys_chunks) {
for i in 0..4 {
(o[i], carry) = add_with_carry(x[i], y[i], carry);
}
}
for ((o, &x), &y) in out_chunks
.into_remainder()
.iter_mut()
.zip(xs_chunks.remainder().iter())
.zip(ys_chunks.remainder().iter())
{
(*o, carry) = add_with_carry(x, y, carry);
}
carry
}}
pub_crate_test! {limbs_add_greater_to_out(out: &mut [Limb], xs: &[Limb], ys: &[Limb]) -> bool {
let xs_len = xs.len();
let ys_len = ys.len();
assert!(xs_len >= ys_len);
assert!(out.len() >= xs_len);
let carry = limbs_add_same_length_to_out(out, &xs[..ys_len], ys);
if xs_len == ys_len {
carry
} else if carry {
limbs_add_limb_to_out(&mut out[ys_len..], &xs[ys_len..], 1)
} else {
out[ys_len..xs_len].copy_from_slice(&xs[ys_len..]);
false
}
}}
pub_crate_test! {limbs_add_to_out(out: &mut [Limb], xs: &[Limb], ys: &[Limb]) -> bool {
if xs.len() >= ys.len() {
limbs_add_greater_to_out(out, xs, ys)
} else {
limbs_add_greater_to_out(out, ys, xs)
}
}}
pub_crate_test! {limbs_add_to_out_aliased(xs: &mut [Limb], xs_len: usize, ys: &[Limb]) -> bool {
let ys_len = ys.len();
assert!(xs.len() >= ys_len);
assert!(xs_len <= ys_len);
let (ys_lo, ys_hi) = ys.split_at(xs_len);
xs[xs_len..ys_len].copy_from_slice(ys_hi);
limbs_slice_add_greater_in_place_left(&mut xs[..ys_len], ys_lo)
}}
pub_crate_test! {
limbs_add_to_out_aliased_2(xs: &mut [Limb], xs_offset: usize, ys: &[Limb]) -> bool {
let len = ys.len();
assert_eq!(xs.len(), len + xs_offset);
if xs_offset >= len {
let (xs_lo, xs_hi) = xs.split_at_mut(xs_offset);
limbs_add_same_length_to_out(&mut xs_lo[..len], xs_hi, ys)
} else {
let mut carry = false;
for i in 0..len {
(xs[i], carry) = add_with_carry(xs[i + xs_offset], ys[i], carry);
}
carry
}
}}
pub_crate_test! {limbs_slice_add_same_length_in_place_left<T: PrimitiveUnsigned>(
xs: &mut [T],
ys: &[T],
) -> bool {
let xs_len = xs.len();
assert_eq!(xs_len, ys.len());
let mut carry = false;
let mut xs_chunks = xs.chunks_exact_mut(4);
let mut ys_chunks = ys.chunks_exact(4);
for (x, y) in (&mut xs_chunks).zip(&mut ys_chunks) {
for i in 0..4 {
(x[i], carry) = add_with_carry(x[i], y[i], carry);
}
}
for (x, &y) in xs_chunks
.into_remainder()
.iter_mut()
.zip(ys_chunks.remainder().iter())
{
(*x, carry) = add_with_carry(*x, y, carry);
}
carry
}}
pub_crate_test! {limbs_slice_add_greater_in_place_left(xs: &mut [Limb], ys: &[Limb]) -> bool {
let xs_len = xs.len();
let ys_len = ys.len();
let (xs_lo, xs_hi) = xs.split_at_mut(ys_len);
let carry = limbs_slice_add_same_length_in_place_left(xs_lo, ys);
if xs_len == ys_len {
carry
} else if carry {
limbs_slice_add_limb_in_place(xs_hi, 1)
} else {
false
}
}}
pub_crate_test! {limbs_vec_add_in_place_left(xs: &mut Vec<Limb>, ys: &[Limb]) {
if core::ptr::eq(xs.as_slice(), ys) {
limbs_vec_shl_in_place(xs, 1);
return;
}
let xs_len = xs.len();
let ys_len = ys.len();
let carry = if xs_len >= ys_len {
limbs_slice_add_greater_in_place_left(xs, ys)
} else {
let (ys_lo, ys_hi) = ys.split_at(xs_len);
let mut carry = limbs_slice_add_same_length_in_place_left(xs, ys_lo);
xs.extend_from_slice(ys_hi);
if carry {
carry = limbs_slice_add_limb_in_place(&mut xs[xs_len..], 1);
}
carry
};
if carry {
xs.push(1);
}
}}
pub_test! {limbs_slice_add_in_place_either(xs: &mut [Limb], ys: &mut [Limb]) -> (bool, bool) {
if xs.len() >= ys.len() {
(false, limbs_slice_add_greater_in_place_left(xs, ys))
} else {
(true, limbs_slice_add_greater_in_place_left(ys, xs))
}
}}
pub_test! {limbs_vec_add_in_place_either(xs: &mut Vec<Limb>, ys: &mut Vec<Limb>) -> bool {
if xs.len() >= ys.len() {
if limbs_slice_add_greater_in_place_left(xs, ys) {
xs.push(1);
}
false
} else {
if limbs_slice_add_greater_in_place_left(ys, xs) {
ys.push(1);
}
true
}
}}
pub_crate_test! {limbs_add_same_length_with_carry_in_to_out(
out: &mut [Limb],
xs: &[Limb],
ys: &[Limb],
carry_in: bool,
) -> bool {
let mut carry = limbs_add_same_length_to_out(out, xs, ys);
if carry_in {
carry |= limbs_slice_add_limb_in_place(&mut out[..xs.len()], 1);
}
carry
}}
pub_crate_test! {limbs_add_same_length_with_carry_in_in_place_left(
xs: &mut [Limb],
ys: &[Limb],
carry_in: bool,
) -> bool {
let mut carry = limbs_slice_add_same_length_in_place_left(xs, ys);
if carry_in {
carry |= limbs_slice_add_limb_in_place(xs, 1);
}
carry
}}
impl Natural {
#[inline]
pub(crate) fn add_limb(mut self, other: Limb) -> Self {
self.add_assign_limb(other);
self
}
pub(crate) fn add_limb_ref(&self, other: Limb) -> Self {
match (self, other) {
(x, 0) => x.clone(),
(Self(Small(small)), other) => match small.overflowing_add(other) {
(sum, false) => Self::from(sum),
(sum, true) => Self(Large(vec![sum, 1])),
},
(Self(Large(limbs)), other) => Self(Large(limbs_add_limb(limbs, other))),
}
}
fn add_assign_limb(&mut self, other: Limb) {
match (&mut *self, other) {
(_, 0) => {}
(&mut Self::ZERO, _) => *self = Self::from(other),
(&mut Self(Small(ref mut small)), other) => {
let (sum, overflow) = small.overflowing_add(other);
if overflow {
*self = Self(Large(vec![sum, 1]));
} else {
*small = sum;
}
}
(&mut Self(Large(ref mut limbs)), other) => {
limbs_vec_add_limb_in_place(limbs, other);
}
}
}
#[cfg(feature = "float_helpers")]
pub fn add_assign_at_limb(&mut self, i: usize, y: Limb) {
if i == 0 {
*self += Self::from(y);
return;
}
let xs = self.promote_in_place();
if xs.len() <= i {
xs.resize(i + 1, 0);
}
if limbs_slice_add_limb_in_place(&mut xs[i..], y) {
xs.push(1);
}
}
}
impl Add<Self> for Natural {
type Output = Self;
fn add(mut self, other: Self) -> Self {
self += other;
self
}
}
impl<'a> Add<&'a Self> for Natural {
type Output = Self;
#[inline]
fn add(mut self, other: &'a Self) -> Self {
self += other;
self
}
}
impl Add<Natural> for &Natural {
type Output = Natural;
#[inline]
fn add(self, mut other: Natural) -> Natural {
other += self;
other
}
}
impl Add<&Natural> for &Natural {
type Output = Natural;
fn add(self, other: &Natural) -> Natural {
match (self, other) {
(x, &Natural(Small(y))) => x.add_limb_ref(y),
(&Natural(Small(x)), y) => y.add_limb_ref(x),
(&Natural(Large(ref xs)), &Natural(Large(ref ys))) => Natural(Large(limbs_add(xs, ys))),
}
}
}
impl AddAssign<Self> for Natural {
fn add_assign(&mut self, mut other: Self) {
match (&mut *self, &mut other) {
(x, &mut Self(Small(y))) => x.add_assign_limb(y),
(&mut Self(Small(x)), y) => *self = y.add_limb_ref(x),
(&mut Self(Large(ref mut xs)), &mut Self(Large(ref mut ys))) => {
if limbs_vec_add_in_place_either(xs, ys) {
*self = other;
}
}
}
}
}
impl<'a> AddAssign<&'a Self> for Natural {
fn add_assign(&mut self, other: &'a Self) {
match (&mut *self, other) {
(x, &Self(Small(y))) => x.add_assign_limb(y),
(&mut Self(Small(x)), y) => *self = y.add_limb_ref(x),
(&mut Self(Large(ref mut xs)), &Self(Large(ref ys))) => {
limbs_vec_add_in_place_left(xs, ys);
}
}
}
}
impl Sum for Natural {
fn sum<I>(xs: I) -> Self
where
I: Iterator<Item = Self>,
{
let mut s = Self::ZERO;
for x in xs {
s += x;
}
s
}
}
impl<'a> Sum<&'a Self> for Natural {
fn sum<I>(xs: I) -> Self
where
I: Iterator<Item = &'a Self>,
{
let mut s = Self::ZERO;
for x in xs {
s += x;
}
s
}
}