use std::{
iter::{once, repeat},
ops,
};
use num::traits::{Inv, Pow};
use itertools::Itertools;
use crate::{
divisible::{Composite, Divisible, Prime},
error::{AdicError, AdicResult},
normed::Valuation,
traits::{AdicPrimitive, CanApproximate, HasApproximateDigits, HasDigits, PrimedFrom},
ZAdic,
};
use super::PowAdic;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MAdicDigits {
b: Composite,
d: Vec<u32>,
}
#[allow(dead_code)]
impl MAdicDigits {
pub fn new(b: Composite, d: Vec<u32>) -> Self {
MAdicDigits { b, d }
}
pub fn from_p_adic<A>(b: &Composite, ap: PowAdic<A>) -> AdicResult<Self>
where A: AdicPrimitive, PowAdic<A>: HasApproximateDigits<DigitIndex = usize> {
let p = ap.p();
if !b.has_prime(p) {
return Err(AdicError::IllDefined(format!(
"MAdicDigits composite needs to include the prime: {} not in {}",
ap.p(), b
)));
}
let b_pow = b.prime_power(p);
let matched_ap = PowAdic::new(ap.adic, b_pow.power());
let Valuation::Finite(certainty) = matched_ap.certainty() else {
return Err(AdicError::InappropriatePrecision(
"No adic number uncertainty found; infinite n-adic digits".to_string()
));
};
let old_base = Self::new(b.clone(), vec![b_pow.value32()]);
let mut nadic_ap = Self::zero(b.clone(), certainty);
for d in matched_ap.digits().collect::<Vec<_>>().into_iter().rev() {
nadic_ap = nadic_ap * old_base.clone() + Self::new(b.clone(), vec![d]);
}
let all_but_p_idempotent = Self::idempotent_excluding(p, b, certainty)?;
Ok((nadic_ap * all_but_p_idempotent).into_truncation(certainty))
}
pub fn base(&self) -> &Composite {
&self.b
}
pub fn digits(&self) -> impl Iterator<Item=u32> + use<'_> {
self.d.iter().copied()
}
pub fn len(&self) -> usize {
self.d.len()
}
pub fn truncation(&self, n: usize) -> Self {
self.clone().into_truncation(n)
}
pub fn into_truncation(self, n: usize) -> Self {
let mut d = self.d;
d.truncate(n);
Self::new(self.b, d)
}
pub fn zero(b: Composite, prec: usize) -> Self {
MAdicDigits { b, d: vec![0; prec] }
}
pub fn one(b: Composite, prec: usize) -> Self {
MAdicDigits { b, d: once(1).chain(repeat(0)).take(prec).collect() }
}
pub fn idempotent_excluding<P>(p: P, c: &Composite, precision: usize) -> AdicResult<MAdicDigits>
where P: Into<Prime> {
let p = p.into();
let p_pow = c.prime_power(p).power();
if p_pow == 0 {
return Err(AdicError::IllDefined(format!("Prime {p} does not exist in composite {c}")));
}
let pow_prec = precision * usize::try_from(p_pow)?;
let bval = c.value32();
let b_wo_pp = Composite::new(c.prime_powers().filter(|qq| qq.p() != p)).value32();
let z = ZAdic::primed_from(p, b_wo_pp).into_approximation(pow_prec);
let r = z.inv();
let rpow = r.pow(precision.try_into()?);
let rpow = PowAdic::new(rpow, p_pow);
let mut a = Vec::new();
for d in rpow.digits() {
a.push(d);
let mut carry = 0;
for da in &mut a {
let new_d = b_wo_pp * (*da) + carry;
carry = new_d / bval;
*da = new_d % bval;
}
}
Ok(Self::new(c.clone(), a))
}
pub fn prime_idempotent<P>(p: P, c: &Composite, precision: usize) -> AdicResult<MAdicDigits>
where P: Into<Prime> {
let p = p.into();
if Composite::from(p) == *c {
return Ok(Self::zero(c.clone(), precision));
}
let nadic = Self::idempotent_excluding(p, c, precision)?;
let mut a = nadic.digits();
let first = a.next();
let Some(f) = first else {
return Ok(Self::zero(c.clone(), 0));
};
if [0, 1].contains(&f) {
return Err(AdicError::Severe("idempotent should not start with 0 or 1; something is wrong".to_string()));
}
let mut b = vec![];
b.push(c.value32() + 1 - f);
for d in a {
b.push(c.m1() - d);
}
Ok(Self::new(c.clone(), b))
}
}
impl ops::Add for MAdicDigits {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
assert_eq!(self.b, rhs.b, "Cannot add MAdicDigits with different bases");
let bval = self.b.value32();
let la = self.d.len();
let lb = rhs.d.len();
let mut summed_digits = Vec::with_capacity(std::cmp::max(la, lb) + 1);
summed_digits.extend(
self.d.iter().copied()
.zip_longest(rhs.d.iter().copied())
.map(|lr| lr.reduce(|l, r| l+r))
.collect::<Vec<_>>()
);
let mut carry = 0;
for digit in &mut summed_digits {
let bigger_digit = *digit + carry;
*digit = bigger_digit % bval;
carry = bigger_digit / bval;
}
while carry > 0 {
summed_digits.push(carry % bval);
carry = carry / bval;
}
MAdicDigits::new(self.b.clone(), summed_digits)
}
}
impl ops::Mul for MAdicDigits {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
assert_eq!(self.b, rhs.b, "Cannot mul MAdicDigits with different bases");
let bval = self.b.value32();
let la = self.d.len();
let lb = rhs.d.len();
if la * lb == 0 {
return MAdicDigits { b: self.b.clone(), d: vec![] };
}
let lt = la + lb - 1;
let a_digits = self.d.clone();
let mut rev_b_digits = rhs.d.clone();
rev_b_digits.reverse();
let mut summed_digits = Vec::with_capacity(lt + 1);
summed_digits.extend((0..lt).map(|digit_place| {
let (a_skip, b_skip) = if (digit_place >= lb) {
(digit_place + 1 - lb, 0)
} else {
(0, lb - digit_place - 1)
};
let mut d = 0;
for (&ds, &dr) in a_digits[a_skip..].iter().zip(rev_b_digits[b_skip..].iter()) {
d += ds * dr;
}
d
}));
let mut carry = 0;
for digit in &mut summed_digits {
let bigger_digit = *digit + carry;
*digit = bigger_digit % bval;
carry = bigger_digit / bval;
}
while carry > 0 {
summed_digits.push(carry % bval);
carry = carry / bval;
}
MAdicDigits::new(self.b.clone(), summed_digits)
}
}
#[cfg(test)]
mod tests {
use crate::divisible::Composite;
use super::MAdicDigits;
#[test]
fn two_primes() {
let b = 10;
let c = Composite::try_from(b).unwrap();
let prec = 10;
let two_to_inf = MAdicDigits::new(c.clone(), vec![6, 7, 3, 9, 0, 1, 7, 8, 7, 1]);
let five_to_inf = MAdicDigits::new(c.clone(), vec![5, 2, 6, 0, 9, 8, 2, 1, 2, 8]);
assert_eq!(Ok(two_to_inf.clone()), MAdicDigits::prime_idempotent(2, &c, prec));
assert_eq!(Ok(five_to_inf.clone()), MAdicDigits::prime_idempotent(5, &c, prec));
assert_eq!(Ok(two_to_inf.clone()), MAdicDigits::idempotent_excluding(5, &c, prec));
assert_eq!(Ok(five_to_inf.clone()), MAdicDigits::idempotent_excluding(2, &c, prec));
let zero = MAdicDigits::zero(c.clone(), prec);
assert_eq!(zero, (two_to_inf * five_to_inf).into_truncation(prec));
}
#[test]
fn three_primes() {
let b = 30;
let c = Composite::try_from(b).unwrap();
let prec = 10;
let two_to_inf = MAdicDigits::new(c.clone(), vec![16, 22, 3, 28, 15, 14, 26, 14, 23, 2]);
let three_to_inf = MAdicDigits::new(c.clone(), vec![21, 26, 28, 12, 5, 22, 29, 26, 27, 5]);
let five_to_inf = MAdicDigits::new(c.clone(), vec![25, 10, 27, 18, 8, 23, 3, 18, 8, 21]);
assert_eq!(Ok(two_to_inf.clone()), MAdicDigits::prime_idempotent(2, &c, prec));
assert_eq!(Ok(three_to_inf.clone()), MAdicDigits::prime_idempotent(3, &c, prec));
assert_eq!(Ok(five_to_inf.clone()), MAdicDigits::prime_idempotent(5, &c, prec));
let six_to_inf = MAdicDigits::new(c.clone(), vec![6, 19, 2, 11, 21, 6, 26, 11, 21, 8]);
let ten_to_inf = MAdicDigits::new(c.clone(), vec![10, 3, 1, 17, 24, 7, 0, 3, 2, 24]);
let fifteen_to_inf = MAdicDigits::new(c.clone(), vec![15, 7, 26, 1, 14, 15, 3, 15, 6, 27]);
assert_eq!(Ok(six_to_inf.clone()), MAdicDigits::idempotent_excluding(5, &c, prec));
assert_eq!(six_to_inf, (two_to_inf.clone() * three_to_inf.clone()).into_truncation(prec));
assert_eq!(Ok(ten_to_inf.clone()), MAdicDigits::idempotent_excluding(3, &c, prec));
assert_eq!(ten_to_inf, (two_to_inf.clone() * five_to_inf.clone()).into_truncation(prec));
assert_eq!(Ok(fifteen_to_inf.clone()), MAdicDigits::idempotent_excluding(2, &c, prec));
assert_eq!(fifteen_to_inf, (three_to_inf.clone() * five_to_inf.clone()).into_truncation(prec));
assert_eq!(
MAdicDigits::one(c.clone(), prec),
(
MAdicDigits::prime_idempotent(2, &c, prec).unwrap() +
MAdicDigits::idempotent_excluding(2, &c, prec).unwrap()
).into_truncation(prec)
);
assert_eq!(
MAdicDigits::one(c.clone(), prec),
(
MAdicDigits::prime_idempotent(3, &c, prec).unwrap() +
MAdicDigits::idempotent_excluding(3, &c, prec).unwrap()
).into_truncation(prec)
);
assert_eq!(
MAdicDigits::one(c.clone(), prec),
(
MAdicDigits::prime_idempotent(5, &c, prec).unwrap() +
MAdicDigits::idempotent_excluding(5, &c, prec).unwrap()
).into_truncation(prec)
);
assert_eq!(
MAdicDigits::zero(c.clone(), prec),
(two_to_inf * three_to_inf * five_to_inf).into_truncation(prec)
);
}
#[test]
fn prime_power() {
let b = 20;
let c = Composite::try_from(b).unwrap();
let prec = 10;
let two_to_inf = MAdicDigits::new(c.clone(), vec![16, 8, 13, 8, 9, 18, 17, 18, 6, 12]);
let five_to_inf = MAdicDigits::new(c.clone(), vec![5, 11, 6, 11, 10, 1, 2, 1, 13, 7]);
assert_eq!(Ok(two_to_inf), MAdicDigits::idempotent_excluding(5, &c, prec));
assert_eq!(Ok(five_to_inf), MAdicDigits::idempotent_excluding(2, &c, prec));
}
#[test]
fn big_idempotent() {
let b = 10;
let c = Composite::try_from(b).unwrap();
assert_eq!(10, MAdicDigits::prime_idempotent(2, &c, 10).unwrap().len());
assert_eq!(100, MAdicDigits::prime_idempotent(2, &c, 100).unwrap().len());
assert_eq!(1000, MAdicDigits::prime_idempotent(2, &c, 1000).unwrap().len());
}
}