use std::cmp::Ordering;
use itertools::{EitherOrBoth, Itertools};
use crate::{
error::{validate_matching_p, AdicError, AdicResult},
divisible::Prime,
traits::{AdicInteger, AdicPrimitive, CanApproximate, HasApproximateDigits, PrimedFrom, TryPrimedFrom},
QAdic,
};
use super::Variety;
impl<A> Variety<A>
where A: AdicPrimitive {
pub fn p(&self) -> AdicResult<Prime> {
validate_matching_p(self.elements.iter().map(A::p));
self.elements.first().map(A::p).ok_or(AdicError::NoPrimeSet)
}
pub fn primed_from_roots<P, B, IB>(p: P, roots: IB) -> Self
where P: Into<Prime>, A: PrimedFrom<B>, IB: IntoIterator<Item = B> {
let p = p.into();
let roots = roots.into_iter().map(|root| A::primed_from(p, root)).collect();
Variety::new(roots)
}
pub fn try_primed_from_roots<P, B, IB>(p: P, roots: IB) -> AdicResult<Self>
where P: Into<Prime>, A: TryPrimedFrom<B>, IB: IntoIterator<Item = B>, AdicError: From<A::Error> {
let p = p.into();
let roots = roots
.into_iter()
.map(|root| A::try_primed_from(p, root))
.collect::<Result<Vec<_>, _>>()?;
Ok(Variety::new(roots))
}
pub fn approximation(&self, n: A::DigitIndex) -> Variety<A::Approximation>
where A: CanApproximate, A::Approximation: AdicPrimitive {
Variety::new(self.roots().map(|r| r.approximation(n)).collect())
}
pub fn into_approximation(self, n: A::DigitIndex) -> Variety<A::Approximation>
where A: CanApproximate, A::Approximation: AdicPrimitive {
Variety::new(self.into_roots().map(|r| r.into_approximation(n)).collect())
}
pub fn adic_sort(&mut self)
where A: HasApproximateDigits {
validate_matching_p(self.elements.iter().map(A::p));
let sorted_roots = sort_roots(self.elements.clone()).collect::<Vec<_>>();
self.elements = sorted_roots;
}
#[must_use]
pub fn adic_sorted(self) -> Self
where A: HasApproximateDigits {
validate_matching_p(self.elements.iter().map(A::p));
let sorted_roots = sort_roots(self.elements).collect::<Vec<_>>();
Self {
elements: sorted_roots,
}
}
}
impl<A> Variety<A>
where A: AdicPrimitive + AdicInteger {
pub fn into_fractional(self) -> Variety<QAdic<A>> {
Variety::new(self.into_roots().map(|a| QAdic::new(a, 0)).collect())
}
}
impl<A> Variety<QAdic<A>>
where A: AdicPrimitive + AdicInteger {
pub fn try_into_integer(self) -> AdicResult<Variety<A>> {
let zadic_roots = self.into_roots().map(QAdic::try_into_integer).collect::<AdicResult<Vec<_>>>()?;
Ok(Variety::new(zadic_roots))
}
}
impl<A, B> PrimedFrom<Variety<B>> for Variety<A>
where A: AdicPrimitive + PrimedFrom<B> {
fn primed_from<P>(p: P, n: Variety<B>) -> Self
where P: Into<Prime> {
let p = p.into();
let roots = n.into_roots().map(|root| A::primed_from(p, root)).collect();
Variety::new(roots)
}
}
fn sort_roots<A, I: IntoIterator<Item = A>>(roots: I) -> impl Iterator<Item = A>
where A: AdicPrimitive + HasApproximateDigits {
roots.into_iter().sorted_by(|z1, z2| {
match z1.digits().zip_longest(z2.digits()).find(
|dd| dd.as_ref().both().is_none_or(|(l, r)| l != r)
) {
None => {
if z1.is_certain() && !z2.is_certain() {
Ordering::Less
} else if !z1.is_certain() && z2.is_certain() {
Ordering::Greater
} else {
Ordering::Equal
}
},
Some(EitherOrBoth::Left(_)) => Ordering::Greater,
Some(EitherOrBoth::Right(_)) => Ordering::Less,
Some(EitherOrBoth::Both(d1, d2)) => d1.cmp(&d2),
}
})
}