use crate::natural::InnerNatural::Small;
use crate::natural::Natural;
use crate::natural::arithmetic::mul::product_of_limbs::limbs_product;
use crate::platform::Limb;
use alloc::vec::Vec;
use core::iter::Product;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::num::conversion::traits::{ConvertibleFrom, WrappingFrom};
pub(crate) fn product_of_unsigneds<T: PrimitiveUnsigned, I: Iterator<Item = T>>(xs: I) -> Natural
where
Limb: ConvertibleFrom<T> + WrappingFrom<T>,
Natural: From<T>,
{
let mut packed = Vec::new();
let mut wide = Vec::new();
let mut acc: Limb = 1;
for x in xs {
if x == T::ZERO {
return Natural::ZERO;
}
if Limb::convertible_from(x) {
let x = Limb::wrapping_from(x);
acc = if let Some(p) = acc.checked_mul(x) {
p
} else {
packed.push(acc);
x
};
} else {
wide.push(Natural::from(x));
}
}
let product = if packed.is_empty() {
Natural(Small(acc))
} else {
packed.push(acc);
let mut out = Vec::new();
let (out_len, new_out) = limbs_product(&mut out, &mut packed);
let mut out = new_out.unwrap();
out.truncate(out_len);
Natural::from_owned_limbs_asc(out)
};
if wide.is_empty() {
product
} else {
product * Natural::product(wide.into_iter())
}
}
macro_rules! impl_product_of_unsigneds {
($t:ident) => {
impl Product<$t> for Natural {
#[inline]
fn product<I: Iterator<Item = $t>>(xs: I) -> Natural {
product_of_unsigneds(xs)
}
}
impl<'a> Product<&'a $t> for Natural {
#[inline]
fn product<I: Iterator<Item = &'a $t>>(xs: I) -> Natural {
product_of_unsigneds(xs.copied())
}
}
};
}
apply_to_unsigneds!(impl_product_of_unsigneds);