use core::cmp::Ordering;
use core::fmt;
use alloc::vec::Vec;
use crate::int::Int;
use crate::rational::Rational;
#[derive(Clone)]
pub struct Padic {
p: Int,
val: i64,
unit: Int,
abs_prec: i64,
}
fn split_val(mut m: Int, p: &Int) -> (i64, Int) {
if m.is_zero() {
return (0, m);
}
let mut v = 0i64;
while m.rem_euclid(p).is_zero() {
m = m.div_exact(p);
v += 1;
}
(v, m)
}
impl Padic {
pub fn new(p: Int, precision: i64) -> Padic {
assert!(precision >= 1, "p-adic precision must be >= 1");
debug_assert!(p.is_prime_bpsw(), "p-adic modulus must be prime");
Padic {
p,
val: 0,
unit: Int::ZERO,
abs_prec: precision,
}
}
pub fn one(p: Int, precision: i64) -> Padic {
Padic::from_int(p, precision, Int::ONE)
}
fn zero_with(p: Int, abs_prec: i64) -> Padic {
Padic {
p,
val: 0,
unit: Int::ZERO,
abs_prec,
}
}
pub fn from_int(p: Int, precision: i64, n: Int) -> Padic {
assert!(precision >= 1, "p-adic precision must be >= 1");
debug_assert!(p.is_prime_bpsw(), "p-adic modulus must be prime");
if n.is_zero() {
return Padic::zero_with(p, precision);
}
let (v, cof) = split_val(n, &p);
let modulus = p.pow(precision as u32);
let unit = cof.rem_euclid(&modulus);
Padic {
p,
val: v,
unit,
abs_prec: v + precision,
}
}
pub fn from_rational(p: Int, precision: i64, r: &Rational) -> Padic {
assert!(precision >= 1, "p-adic precision must be >= 1");
debug_assert!(p.is_prime_bpsw(), "p-adic modulus must be prime");
if r.is_zero() {
return Padic::zero_with(p, precision);
}
let (va, ac) = split_val(r.numerator().clone(), &p);
let (vb, bc) = split_val(r.denominator().clone(), &p);
let v = va - vb;
let modulus = p.pow(precision as u32);
let binv = bc
.rem_euclid(&modulus)
.modinv(&modulus)
.expect("denominator cofactor is coprime to p");
let unit = ac.mul(&binv).rem_euclid(&modulus);
Padic {
p,
val: v,
unit,
abs_prec: v + precision,
}
}
#[inline]
pub fn prime(&self) -> &Int {
&self.p
}
#[inline]
pub fn is_zero(&self) -> bool {
self.unit.is_zero()
}
#[inline]
pub fn valuation(&self) -> Option<i64> {
if self.is_zero() { None } else { Some(self.val) }
}
#[inline]
pub fn precision(&self) -> i64 {
self.abs_prec - self.val
}
#[inline]
pub fn absolute_precision(&self) -> i64 {
self.abs_prec
}
pub fn abs_value(&self) -> Rational {
if self.is_zero() {
return Rational::ZERO;
}
if self.val >= 0 {
Rational::new(Int::ONE, self.p.pow(self.val as u32))
} else {
Rational::from_integer(self.p.pow((-self.val) as u32))
}
}
pub fn digits(&self) -> Vec<Int> {
let mut out = Vec::new();
if self.is_zero() {
return out;
}
let rel = self.abs_prec - self.val;
let mut u = self.unit.clone();
for _ in 0..rel {
let (q, r) = u.div_rem(&self.p).expect("p != 0");
out.push(r);
u = q;
}
out
}
fn reduce_to_abs(&self, a: i64) -> Padic {
if a >= self.abs_prec {
return self.clone();
}
if self.is_zero() {
return Padic::zero_with(self.p.clone(), a);
}
let rel = a - self.val;
if rel <= 0 {
return Padic::zero_with(self.p.clone(), a);
}
let modulus = self.p.pow(rel as u32);
Padic {
p: self.p.clone(),
val: self.val,
unit: self.unit.rem_euclid(&modulus),
abs_prec: a,
}
}
fn normalize(p: Int, v0: i64, c: Int, aprec: i64) -> Padic {
let rel = aprec - v0;
if rel <= 0 {
return Padic::zero_with(p, aprec);
}
let modulus = p.pow(rel as u32);
let cr = c.rem_euclid(&modulus);
if cr.is_zero() {
return Padic::zero_with(p, aprec);
}
let (t, unit_full) = split_val(cr, &p);
let unit_mod = p.pow((rel - t) as u32);
let unit = unit_full.rem_euclid(&unit_mod);
Padic {
p,
val: v0 + t,
unit,
abs_prec: aprec,
}
}
fn check_same(&self, other: &Padic) {
assert!(self.p == other.p, "p-adic operands have different primes");
}
pub fn neg(&self) -> Padic {
if self.is_zero() {
return self.clone();
}
let rel = self.abs_prec - self.val;
let modulus = self.p.pow(rel as u32);
Padic {
p: self.p.clone(),
val: self.val,
unit: modulus.sub(&self.unit),
abs_prec: self.abs_prec,
}
}
pub fn add(&self, rhs: &Padic) -> Padic {
self.check_same(rhs);
let a = self.abs_prec.min(rhs.abs_prec);
if self.is_zero() {
return rhs.reduce_to_abs(a);
}
if rhs.is_zero() {
return self.reduce_to_abs(a);
}
let vmin = self.val.min(rhs.val);
let c1 = self.unit.mul(&self.p.pow((self.val - vmin) as u32));
let c2 = rhs.unit.mul(&rhs.p.pow((rhs.val - vmin) as u32));
Padic::normalize(self.p.clone(), vmin, c1.add(&c2), a)
}
pub fn sub(&self, rhs: &Padic) -> Padic {
self.add(&rhs.neg())
}
pub fn mul(&self, rhs: &Padic) -> Padic {
self.check_same(rhs);
let veff = |x: &Padic| if x.is_zero() { x.abs_prec } else { x.val };
if self.is_zero() || rhs.is_zero() {
return Padic::zero_with(self.p.clone(), veff(self) + veff(rhs));
}
let v = self.val + rhs.val;
let rel = (self.abs_prec - self.val).min(rhs.abs_prec - rhs.val);
let modulus = self.p.pow(rel as u32);
let unit = self.unit.mul(&rhs.unit).rem_euclid(&modulus);
Padic {
p: self.p.clone(),
val: v,
unit,
abs_prec: v + rel,
}
}
pub fn div(&self, rhs: &Padic) -> Padic {
self.check_same(rhs);
assert!(!rhs.is_zero(), "p-adic division by zero");
if self.is_zero() {
return Padic::zero_with(self.p.clone(), self.abs_prec - rhs.val);
}
let v = self.val - rhs.val;
let rel = (self.abs_prec - self.val).min(rhs.abs_prec - rhs.val);
let modulus = self.p.pow(rel as u32);
let inv = rhs
.unit
.modinv(&modulus)
.expect("unit is coprime to p, hence invertible");
let unit = self.unit.mul(&inv).rem_euclid(&modulus);
Padic {
p: self.p.clone(),
val: v,
unit,
abs_prec: v + rel,
}
}
pub fn inv(&self) -> Padic {
Padic::one(self.p.clone(), self.abs_prec - self.val).div(self)
}
pub fn to_rational(&self) -> Rational {
if self.is_zero() {
return Rational::ZERO;
}
if self.val >= 0 {
Rational::from_integer(self.unit.mul(&self.p.pow(self.val as u32)))
} else {
Rational::new(self.unit.clone(), self.p.pow((-self.val) as u32))
}
}
pub fn sqrt(&self) -> Option<Padic> {
if self.is_zero() {
return Some(Padic::zero_with(self.p.clone(), (self.abs_prec + 1) / 2));
}
if self.val % 2 != 0 {
return None;
}
let rel = self.abs_prec - self.val;
let root_unit = if self.p == Int::from_i64(2) {
sqrt_unit_2adic(&self.unit, rel)?
} else {
sqrt_unit_odd(&self.p, &self.unit, rel)?
};
let v = self.val / 2;
Some(Padic {
p: self.p.clone(),
val: v,
unit: root_unit,
abs_prec: v + rel,
})
}
}
fn sqrt_unit_odd(p: &Int, u: &Int, rel: i64) -> Option<Int> {
let mut r = u.sqrt_mod(p)?;
let mut k = 1i64;
while k < rel {
let k2 = (2 * k).min(rel);
let modulus = p.pow(k2 as u32);
let two_r = r.add(&r).rem_euclid(&modulus);
let inv = two_r
.modinv(&modulus)
.expect("2r is a unit for odd p and unit r");
let f = r.mul(&r).sub(u).rem_euclid(&modulus);
r = r.sub(&f.mul(&inv)).rem_euclid(&modulus);
k = k2;
}
let modulus = p.pow(rel as u32);
Some(r.rem_euclid(&modulus))
}
fn sqrt_unit_2adic(u: &Int, rel: i64) -> Option<Int> {
let two = Int::from_i64(2);
let m0 = rel.min(3);
let seed_mod = two.pow(m0 as u32);
if !u.sub(&Int::ONE).rem_euclid(&seed_mod).is_zero() {
return None; }
let mut r = Int::ONE;
let mut k = m0;
while k < rel {
let mod_next = two.pow((k + 1) as u32);
let bit = two.pow((k - 1) as u32);
let cand2 = r.add(&bit);
if r.mul(&r).sub(u).rem_euclid(&mod_next).is_zero() {
} else if cand2.mul(&cand2).sub(u).rem_euclid(&mod_next).is_zero() {
r = cand2;
} else {
return None;
}
k += 1;
}
let modulus = two.pow(rel as u32);
Some(r.rem_euclid(&modulus))
}
impl PartialEq for Padic {
fn eq(&self, other: &Padic) -> bool {
if self.p != other.p {
return false;
}
let a = self.abs_prec.min(other.abs_prec);
let x = self.reduce_to_abs(a);
let y = other.reduce_to_abs(a);
match (x.is_zero(), y.is_zero()) {
(true, true) => true,
(false, false) => x.val == y.val && x.unit == y.unit,
_ => false,
}
}
}
impl Eq for Padic {}
impl fmt::Display for Padic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
return write!(f, "O({}^{})", self.p, self.abs_prec);
}
let digits = self.digits();
let mut first = true;
for (i, d) in digits.iter().enumerate() {
if d.is_zero() {
continue;
}
if !first {
f.write_str(" + ")?;
}
first = false;
let exp = self.val + i as i64;
match exp {
0 => write!(f, "{d}")?,
1 => write!(f, "{d}*{}", self.p)?,
_ => write!(f, "{d}*{}^{exp}", self.p)?,
}
}
if !first {
f.write_str(" + ")?;
}
write!(f, "O({}^{})", self.p, self.abs_prec)
}
}
impl fmt::Debug for Padic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
write!(f, "Padic(0 + O({}^{}))", self.p, self.abs_prec)
} else {
write!(
f,
"Padic({}^{} · {} + O({}^{}))",
self.p, self.val, self.unit, self.p, self.abs_prec
)
}
}
}
impl PartialOrd for Padic {
fn partial_cmp(&self, other: &Padic) -> Option<Ordering> {
if self == other {
Some(Ordering::Equal)
} else {
None
}
}
}
macro_rules! padic_binop {
($tr:ident, $m:ident, $atr:ident, $am:ident) => {
impl core::ops::$tr for Padic {
type Output = Padic;
#[inline]
fn $m(self, rhs: Padic) -> Padic {
Padic::$m(&self, &rhs)
}
}
impl core::ops::$tr<&Padic> for &Padic {
type Output = Padic;
#[inline]
fn $m(self, rhs: &Padic) -> Padic {
Padic::$m(self, rhs)
}
}
impl core::ops::$atr for Padic {
#[inline]
fn $am(&mut self, rhs: Padic) {
*self = Padic::$m(self, &rhs);
}
}
};
}
padic_binop!(Add, add, AddAssign, add_assign);
padic_binop!(Sub, sub, SubAssign, sub_assign);
padic_binop!(Mul, mul, MulAssign, mul_assign);
padic_binop!(Div, div, DivAssign, div_assign);
impl core::ops::Neg for Padic {
type Output = Padic;
#[inline]
fn neg(self) -> Padic {
Padic::neg(&self)
}
}
impl core::ops::Neg for &Padic {
type Output = Padic;
#[inline]
fn neg(self) -> Padic {
Padic::neg(self)
}
}