use std::collections::HashMap;
use itertools::Itertools;
use num::{BigUint, One};
use crate::{
divisible::{Composite, Divisible, Prime},
error::{AdicError, AdicResult},
traits::{AdicPrimitive, CanApproximate, HasApproximateDigits, PrimedFrom},
ZAdic,
};
use super::{m_adic_digits::MAdicDigits, PowAdic};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MAdic<A>
where A: HasApproximateDigits + AdicPrimitive {
pub (super) pow_adics: HashMap<Prime, PowAdic<A>>,
}
impl<A> MAdic<A>
where A: HasApproximateDigits + AdicPrimitive {
pub fn new(adics: impl IntoIterator<Item=PowAdic<A>>) -> Self {
let pow_adics = adics.into_iter().map(
|a| (a.p(), a)
).collect::<HashMap<_, _>>();
MAdic {
pow_adics,
}
}
pub fn from_pure_p_adic<C>(c: C, adic: A) -> AdicResult<Self>
where C: Into<Composite> {
let c = c.into();
if c.has_prime(adic.p()) {
Ok(Self::new(c.prime_powers().map(move |pp| {
let (p, power) = (pp.p(), pp.power());
if p == adic.p() {
PowAdic::new(adic.clone(), power)
} else {
PowAdic::new(A::zero(p), power)
}
})))
} else {
Err(AdicError::IllDefined("`MAdic` must contain the prime of `adic`.".to_string()))
}
}
pub fn from_i32<C>(c: C, a: i32) -> AdicResult<Self>
where A: PrimedFrom<i32>, C: Into<Composite> {
let c = c.into();
if c.is_one() {
Err(AdicError::IllDefined("Nontrivial `MAdic` contain at least one prime.".to_string()))
} else {
Ok(Self::new(c.prime_powers().map(move |pp| {
let (p, power) = (pp.p(), pp.power());
PowAdic::new(A::primed_from(p, a), power)
})))
}
}
pub fn p_adic<P>(&self, p: P) -> PowAdic<A>
where P: Into<Prime> {
let p = p.into();
self.pow_adics.get(&p).cloned().unwrap_or(PowAdic::new(A::one(p), 0))
}
pub fn into_pow_adics(self) -> impl Iterator<Item = PowAdic<A>> {
self.pow_adics.into_values().sorted_by_key(PowAdic::p)
}
pub fn pow_adics(&self) -> impl Iterator<Item = &PowAdic<A>> {
self.pow_adics.values().sorted_by_key(|ap| ap.p())
}
pub fn idempotent_excluding<P, C>(p: P, c: C, precision: usize) -> AdicResult<BigUint>
where P: Into<Prime>, C: Into<Composite> {
let c = c.into();
let mdigits = MAdicDigits::idempotent_excluding(p, &c, precision)?;
let digit_vec = mdigits.digits().map(u8::try_from).collect::<Result<Vec<_>, _>>()?;
BigUint::from_radix_le(digit_vec.as_slice(), c.value32()).ok_or(
AdicError::Severe("Error during conversion to BigUint".to_string())
)
}
pub fn prime_idempotent<P, C>(p: P, c: C, precision: usize) -> AdicResult<BigUint>
where P: Into<Prime>, C: Into<Composite> {
let c = c.into();
let mdigits = MAdicDigits::prime_idempotent(p, &c, precision)?;
let digit_vec = mdigits.digits().map(u8::try_from).collect::<Result<Vec<_>, _>>()?;
BigUint::from_radix_le(digit_vec.as_slice(), c.value32()).ok_or(
AdicError::Severe("Error during conversion to BigUint".to_string())
)
}
pub fn prime_idempotents<C>(c: C, precision: usize) -> AdicResult<Vec<BigUint>>
where C: Into<Composite> {
let c = c.into();
c.clone().primes().map(|p| Self::prime_idempotent(p, c.clone(), precision)).collect()
}
pub fn all_idempotents<C>(c: C, precision: usize) -> AdicResult<Vec<BigUint>>
where C: Into<Composite> {
let c = c.into();
let prime_idemps = c.clone().primes()
.map(|p| Self::prime_idempotent(p, c.clone(), precision))
.collect::<Result<Vec<_>, _>>()?;
let modulus = BigUint::from(c).pow(u32::try_from(precision)?);
let idempotents = prime_idemps
.into_iter()
.powerset()
.map(|ps| ps.into_iter().product::<BigUint>() % modulus.clone())
.collect::<Vec<_>>();
Ok(idempotents)
}
}
impl MAdic<ZAdic> {
pub fn empty<C>(c: C) -> Self
where C: Into<Composite> {
let c = c.into();
Self::new(c.prime_powers().map(|pp| PowAdic::new(ZAdic::empty(pp.p()), pp.power())))
}
pub fn approx_from_i32<C>(c: C, a: i32, precision: usize) -> AdicResult<Self>
where C: Into<Composite> {
let c = c.into();
Ok(Self::new(c.clone().prime_powers().map(
move |pp| {
let (p, power) = (pp.p(), pp.power());
let adjusted_prec = precision * usize::try_from(c.log_ceil(&pp.into()))?;
Ok(PowAdic::new(ZAdic::primed_from(p, a).into_approximation(adjusted_prec), power))
}
).collect::<AdicResult<Vec<_>>>()?))
}
}
#[cfg(test)]
mod tests {
use assertables::assert_matches;
use crate::{num_adic::PowAdic, traits::HasDigits};
use super::MAdic;
#[test]
fn constructors() {
let ac = MAdic::new([
PowAdic::new(zadic_approx!(2, 24, [1]), 1),
PowAdic::new(zadic_approx!(5, 12, [2]), 1),
]);
assert_eq!(vec![7, 7, 3, 9, 0, 1], ac.digits().collect::<Vec<_>>());
assert_eq!("...109377._10", ac.to_string());
assert_eq!(Ok(9), ac.digit(3));
assert_matches!(ac.digit(6), Err(_));
let adic2 = ac.p_adic(2);
assert_eq!(vec![1, 0, 0, 0, 0, 0], adic2.digits().take(6).collect::<Vec<_>>());
let adic5 = ac.p_adic(5);
assert_eq!(vec![2, 0, 0, 0, 0, 0], adic5.digits().take(6).collect::<Vec<_>>());
let ac = MAdic::from_pure_p_adic(30, zadic_approx!(3, 24, [1])).unwrap();
assert_eq!(vec![10, 3, 1, 17, 24, 7], ac.digits().collect::<Vec<_>>());
assert_eq!(Ok(17), ac.digit(3));
assert_matches!(ac.digit(6), Err(_));
let adic2 = ac.p_adic(2);
assert_eq!(None, adic2.digits().next());
let adic3 = ac.p_adic(3);
assert_eq!(vec![1, 0, 0, 0, 0, 0], adic3.digits().take(6).collect::<Vec<_>>());
let adic5 = ac.p_adic(5);
assert_eq!(None, adic5.digits().next());
let ac = MAdic::approx_from_i32(10, 2, 6).unwrap();
assert_eq!(vec![2, 0, 0, 0, 0, 0], ac.digits().collect::<Vec<_>>());
assert_eq!(Ok(0), ac.digit(3));
assert_matches!(ac.digit(6), Err(_));
let ac = MAdic::approx_from_i32(10, 10, 6).unwrap();
assert_eq!(vec![0, 1, 0, 0, 0, 0], ac.digits().collect::<Vec<_>>());
let ac = MAdic::approx_from_i32(10, -1, 6).unwrap();
assert_eq!(vec![9, 9, 9, 9, 9, 9], ac.digits().collect::<Vec<_>>());
let ac = MAdic::new([
PowAdic::new(qadic!(zadic_approx!(2, 25, [1]), -5), 1),
PowAdic::new(qadic!(zadic_approx!(5, 12, [2]), -2), 1),
]);
assert_eq!(vec![7, 7, 3, 9, 0, 1], ac.digits().collect::<Vec<_>>());
assert_eq!("...10937.7_10", ac.to_string());
assert_eq!(Ok(9), ac.digit(2));
assert_matches!(ac.digit(5), Err(_));
let adic2 = ac.p_adic(2);
assert_eq!(vec![1, 0, 0, 0, 0, 0], adic2.digits().take(6).collect::<Vec<_>>());
let adic5 = ac.p_adic(5);
assert_eq!(vec![2, 0, 0, 0, 0, 0], adic5.digits().take(6).collect::<Vec<_>>());
}
#[test]
fn test_timing() {
let precision = 100;
let _ac = MAdic::new([
PowAdic::new(zadic_approx!(2, 5 * precision, [1]), 1),
PowAdic::new(zadic_approx!(5, 2 * precision, [2]), 1),
]);
}
}