use std::cmp::Ordering;
use num_bigint::{BigInt, Sign};
use num_rational::BigRational;
use num_traits::One;
use crate::ball::Ball;
use crate::basis::Basis;
use crate::error::RangeError;
use crate::primes::mod_inverse;
use crate::reconstruct::rational_reconstruct;
use crate::rns::RnsInt;
pub trait Finite: Clone {
fn basis(&self) -> &Basis;
fn add(&self, o: &Self) -> Self;
fn sub(&self, o: &Self) -> Self;
fn mul(&self, o: &Self) -> Self;
fn try_reconstruct(&self) -> Option<(BigInt, BigInt)>;
fn reimage(p: &BigInt, q: &BigInt, basis: &Basis) -> Self;
}
impl Finite for RnsInt {
fn basis(&self) -> &Basis {
&self.basis
}
fn add(&self, o: &Self) -> Self {
RnsInt::add(self, o)
}
fn sub(&self, o: &Self) -> Self {
RnsInt::sub(self, o)
}
fn mul(&self, o: &Self) -> Self {
RnsInt::mul(self, o)
}
fn try_reconstruct(&self) -> Option<(BigInt, BigInt)> {
Some((self.to_bigint(), BigInt::one()))
}
fn reimage(p: &BigInt, q: &BigInt, basis: &Basis) -> Self {
debug_assert!(q.is_one(), "integer reimage with non-unit denominator");
RnsInt::from_bigint(p, basis.clone())
}
}
#[derive(Clone, Debug)]
pub struct RnsFrac {
pub residues: Vec<Option<u32>>,
pub basis: Basis,
}
impl RnsFrac {
pub fn from_fraction(p: &BigInt, q: &BigInt, basis: Basis) -> Self {
let residues = basis
.moduli()
.iter()
.map(|&m| {
let mm = BigInt::from(m);
let pm = (((p % &mm) + &mm) % &mm).to_u32();
let qm = (((q % &mm) + &mm) % &mm).to_u32();
match (pm, qm) {
(Some(pr), Some(qr)) if qr != 0 => {
let inv = mod_inverse(qr as u64, m as u64)? as u32;
Some(((pr as u64 * inv as u64) % m as u64) as u32)
}
_ => None,
}
})
.collect();
RnsFrac { residues, basis }
}
fn combine(&self, o: &Self, f: impl Fn(u32, u32, u32) -> u32) -> Self {
let residues = self
.residues
.iter()
.zip(&o.residues)
.zip(self.basis.moduli())
.map(|((a, b), &m)| match (a, b) {
(Some(x), Some(y)) => Some(f(*x, *y, m)),
_ => None,
})
.collect();
RnsFrac { residues, basis: self.basis.clone() }
}
}
trait ToU32 {
fn to_u32(&self) -> Option<u32>;
}
impl ToU32 for BigInt {
fn to_u32(&self) -> Option<u32> {
use num_traits::ToPrimitive;
ToPrimitive::to_u32(self)
}
}
impl Finite for RnsFrac {
fn basis(&self) -> &Basis {
&self.basis
}
fn add(&self, o: &Self) -> Self {
self.combine(o, crate::rns::add_channel)
}
fn sub(&self, o: &Self) -> Self {
self.combine(o, crate::rns::sub_channel)
}
fn mul(&self, o: &Self) -> Self {
self.combine(o, crate::rns::mul_channel)
}
fn try_reconstruct(&self) -> Option<(BigInt, BigInt)> {
let mut res = Vec::new();
let mut mods = Vec::new();
for (r, &m) in self.residues.iter().zip(self.basis.moduli()) {
if let Some(v) = r {
res.push(*v);
mods.push(m);
}
}
if mods.is_empty() {
return None;
}
let u = crate::rns::garner_crt(&res, &mods);
let m_prod: num_bigint::BigUint = mods.iter().map(|&p| num_bigint::BigUint::from(p)).product();
rational_reconstruct(&u, &m_prod)
}
fn reimage(p: &BigInt, q: &BigInt, basis: &Basis) -> Self {
RnsFrac::from_fraction(p, q, basis.clone())
}
}
#[derive(Clone)]
pub struct Adelic<F: Finite> {
finite: F,
infinite: Ball,
}
impl<F: Finite> Adelic<F> {
pub fn from_parts(finite: F, infinite: Ball) -> Self {
Adelic { finite, infinite }
}
pub fn ball(&self) -> &Ball {
&self.infinite
}
pub fn finite(&self) -> &F {
&self.finite
}
pub fn sign(&self) -> Option<Sign> {
self.infinite.sign()
}
pub fn cmp(&self, other: &Self) -> Option<Ordering> {
self.infinite.cmp(&other.infinite)
}
pub fn to_f64(&self) -> f64 {
self.infinite.to_f64()
}
pub fn add(&self, o: &Self) -> Self {
Adelic { finite: self.finite.add(&o.finite), infinite: self.infinite.add(&o.infinite) }
}
pub fn sub(&self, o: &Self) -> Self {
Adelic { finite: self.finite.sub(&o.finite), infinite: self.infinite.sub(&o.infinite) }
}
pub fn mul(&self, o: &Self) -> Self {
Adelic { finite: self.finite.mul(&o.finite), infinite: self.infinite.mul(&o.infinite) }
}
fn exact_point(&self) -> Option<(BigInt, BigInt)> {
if self.infinite.lo == self.infinite.hi {
Some((self.infinite.lo.numer().clone(), self.infinite.lo.denom().clone()))
} else {
None
}
}
pub fn with_basis(&self, basis: Basis) -> Self {
let (p, q) = self
.exact_point()
.expect("with_basis requires a point-valued infinite place");
Adelic { finite: F::reimage(&p, &q, &basis), infinite: self.infinite.clone() }
}
fn validate(&self, p: &BigInt, q: &BigInt) -> Result<(BigInt, BigInt), RangeError> {
let candidate = BigRational::new(p.clone(), q.clone());
if self.infinite.contains(&candidate) {
Ok((p.clone(), q.clone()))
} else {
let have_bits = self.finite.basis().capacity_bits();
Err(RangeError::ReconstructionFailed { have_bits, need_more: true })
}
}
}
pub type AdelicInt = Adelic<RnsInt>;
pub type AdelicRat = Adelic<RnsFrac>;
impl AdelicInt {
pub fn from_bigint(n: &BigInt, basis: Basis) -> Self {
Adelic {
finite: RnsInt::from_bigint(n, basis),
infinite: Ball::point(&BigRational::from_integer(n.clone())),
}
}
pub fn from_i64(n: i64, basis: Basis) -> Self {
Self::from_bigint(&BigInt::from(n), basis)
}
pub fn try_exact(&self) -> Result<BigInt, RangeError> {
let (p, q) = self
.finite
.try_reconstruct()
.ok_or(RangeError::ReconstructionFailed {
have_bits: self.finite.basis().capacity_bits(),
need_more: true,
})?;
self.validate(&p, &q).map(|(p, _)| p)
}
}
impl AdelicRat {
pub fn from_fraction(p: &BigInt, q: &BigInt, basis: Basis) -> Self {
Adelic {
finite: RnsFrac::from_fraction(p, q, basis),
infinite: Ball::point(&BigRational::new(p.clone(), q.clone())),
}
}
pub fn try_exact(&self) -> Result<(BigInt, BigInt), RangeError> {
let (p, q) = self
.finite
.try_reconstruct()
.ok_or(RangeError::ReconstructionFailed {
have_bits: self.finite.basis().capacity_bits(),
need_more: true,
})?;
self.validate(&p, &q)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn overflow_is_detected_then_fixed() {
let small = Basis::with_bits(40);
let big = AdelicInt::from_bigint(&(BigInt::one() << 60), small.clone());
assert!(matches!(big.try_exact(), Err(RangeError::ReconstructionFailed { .. })));
let grown = big.with_basis(small.extend_to_bits(80));
assert_eq!(grown.try_exact().unwrap(), BigInt::one() << 60);
}
#[test]
fn sign_from_infinite_place_with_mixed_signs() {
let b = Basis::standard();
let a = AdelicInt::from_i64(-7, b.clone());
let c = AdelicInt::from_i64(5, b);
assert_eq!(a.add(&c).sign(), Some(Sign::Minus)); assert_eq!(a.sub(&c).sign(), Some(Sign::Minus)); }
#[test]
fn integer_arithmetic_round_trips() {
let b = Basis::standard();
let a = AdelicInt::from_i64(1000, b.clone());
let c = AdelicInt::from_i64(337, b);
assert_eq!(a.add(&c).try_exact().unwrap(), BigInt::from(1337));
assert_eq!(a.sub(&c).try_exact().unwrap(), BigInt::from(663));
assert_eq!(a.mul(&c).try_exact().unwrap(), BigInt::from(337_000));
}
#[test]
fn rational_reconstructs() {
let b = Basis::standard();
let x = AdelicRat::from_fraction(&BigInt::from(1), &BigInt::from(6), b.clone());
let y = AdelicRat::from_fraction(&BigInt::from(1), &BigInt::from(4), b);
let sum = x.add(&y); assert_eq!(sum.try_exact().unwrap(), (BigInt::from(5), BigInt::from(12)));
}
}