1use std::cmp::Ordering;
24
25use num_bigint::{BigInt, Sign};
26use num_rational::BigRational;
27use num_traits::One;
28
29use crate::ball::Ball;
30use crate::basis::Basis;
31use crate::error::RangeError;
32use crate::primes::mod_inverse;
33use crate::reconstruct::rational_reconstruct;
34use crate::rns::RnsInt;
35
36pub trait Finite: Clone {
39 fn basis(&self) -> &Basis;
40 fn add(&self, o: &Self) -> Self;
41 fn sub(&self, o: &Self) -> Self;
42 fn mul(&self, o: &Self) -> Self;
43 fn try_reconstruct(&self) -> Option<(BigInt, BigInt)>;
46 fn reimage(p: &BigInt, q: &BigInt, basis: &Basis) -> Self;
48}
49
50impl Finite for RnsInt {
53 fn basis(&self) -> &Basis {
54 &self.basis
55 }
56 fn add(&self, o: &Self) -> Self {
57 RnsInt::add(self, o)
58 }
59 fn sub(&self, o: &Self) -> Self {
60 RnsInt::sub(self, o)
61 }
62 fn mul(&self, o: &Self) -> Self {
63 RnsInt::mul(self, o)
64 }
65 fn try_reconstruct(&self) -> Option<(BigInt, BigInt)> {
66 Some((self.to_bigint(), BigInt::one()))
69 }
70 fn reimage(p: &BigInt, q: &BigInt, basis: &Basis) -> Self {
71 debug_assert!(q.is_one(), "integer reimage with non-unit denominator");
72 RnsInt::from_bigint(p, basis.clone())
73 }
74}
75
76#[derive(Clone, Debug)]
85pub struct RnsFrac {
86 pub residues: Vec<Option<u32>>,
88 pub basis: Basis,
89}
90
91impl RnsFrac {
92 pub fn from_fraction(p: &BigInt, q: &BigInt, basis: Basis) -> Self {
93 let residues = basis
94 .moduli()
95 .iter()
96 .map(|&m| {
97 let mm = BigInt::from(m);
98 let pm = (((p % &mm) + &mm) % &mm).to_u32();
99 let qm = (((q % &mm) + &mm) % &mm).to_u32();
100 match (pm, qm) {
101 (Some(pr), Some(qr)) if qr != 0 => {
102 let inv = mod_inverse(qr as u64, m as u64)? as u32;
103 Some(((pr as u64 * inv as u64) % m as u64) as u32)
104 }
105 _ => None,
106 }
107 })
108 .collect();
109 RnsFrac { residues, basis }
110 }
111
112 fn combine(&self, o: &Self, f: impl Fn(u32, u32, u32) -> u32) -> Self {
113 let residues = self
114 .residues
115 .iter()
116 .zip(&o.residues)
117 .zip(self.basis.moduli())
118 .map(|((a, b), &m)| match (a, b) {
119 (Some(x), Some(y)) => Some(f(*x, *y, m)),
120 _ => None,
121 })
122 .collect();
123 RnsFrac { residues, basis: self.basis.clone() }
124 }
125}
126
127trait ToU32 {
128 fn to_u32(&self) -> Option<u32>;
129}
130impl ToU32 for BigInt {
131 fn to_u32(&self) -> Option<u32> {
132 use num_traits::ToPrimitive;
133 ToPrimitive::to_u32(self)
134 }
135}
136
137impl Finite for RnsFrac {
138 fn basis(&self) -> &Basis {
139 &self.basis
140 }
141 fn add(&self, o: &Self) -> Self {
142 self.combine(o, crate::rns::add_channel)
143 }
144 fn sub(&self, o: &Self) -> Self {
145 self.combine(o, crate::rns::sub_channel)
146 }
147 fn mul(&self, o: &Self) -> Self {
148 self.combine(o, crate::rns::mul_channel)
149 }
150 fn try_reconstruct(&self) -> Option<(BigInt, BigInt)> {
151 let mut res = Vec::new();
152 let mut mods = Vec::new();
153 for (r, &m) in self.residues.iter().zip(self.basis.moduli()) {
154 if let Some(v) = r {
155 res.push(*v);
156 mods.push(m);
157 }
158 }
159 if mods.is_empty() {
160 return None;
161 }
162 let u = crate::rns::garner_crt(&res, &mods);
163 let m_prod: num_bigint::BigUint = mods.iter().map(|&p| num_bigint::BigUint::from(p)).product();
164 rational_reconstruct(&u, &m_prod)
165 }
166 fn reimage(p: &BigInt, q: &BigInt, basis: &Basis) -> Self {
167 RnsFrac::from_fraction(p, q, basis.clone())
168 }
169}
170
171#[derive(Clone)]
175pub struct Adelic<F: Finite> {
176 finite: F,
177 infinite: Ball,
178}
179
180impl<F: Finite> Adelic<F> {
181 pub fn from_parts(finite: F, infinite: Ball) -> Self {
183 Adelic { finite, infinite }
184 }
185
186 pub fn ball(&self) -> &Ball {
188 &self.infinite
189 }
190
191 pub fn finite(&self) -> &F {
193 &self.finite
194 }
195
196 pub fn sign(&self) -> Option<Sign> {
198 self.infinite.sign()
199 }
200
201 pub fn cmp(&self, other: &Self) -> Option<Ordering> {
203 self.infinite.cmp(&other.infinite)
204 }
205
206 pub fn to_f64(&self) -> f64 {
208 self.infinite.to_f64()
209 }
210
211 pub fn add(&self, o: &Self) -> Self {
213 Adelic { finite: self.finite.add(&o.finite), infinite: self.infinite.add(&o.infinite) }
214 }
215
216 pub fn sub(&self, o: &Self) -> Self {
218 Adelic { finite: self.finite.sub(&o.finite), infinite: self.infinite.sub(&o.infinite) }
219 }
220
221 pub fn mul(&self, o: &Self) -> Self {
223 Adelic { finite: self.finite.mul(&o.finite), infinite: self.infinite.mul(&o.infinite) }
224 }
225
226 fn exact_point(&self) -> Option<(BigInt, BigInt)> {
228 if self.infinite.lo == self.infinite.hi {
229 Some((self.infinite.lo.numer().clone(), self.infinite.lo.denom().clone()))
230 } else {
231 None
232 }
233 }
234
235 pub fn with_basis(&self, basis: Basis) -> Self {
239 let (p, q) = self
240 .exact_point()
241 .expect("with_basis requires a point-valued infinite place");
242 Adelic { finite: F::reimage(&p, &q, &basis), infinite: self.infinite.clone() }
243 }
244
245 fn validate(&self, p: &BigInt, q: &BigInt) -> Result<(BigInt, BigInt), RangeError> {
247 let candidate = BigRational::new(p.clone(), q.clone());
248 if self.infinite.contains(&candidate) {
249 Ok((p.clone(), q.clone()))
250 } else {
251 let have_bits = self.finite.basis().capacity_bits();
252 Err(RangeError::ReconstructionFailed { have_bits, need_more: true })
253 }
254 }
255}
256
257pub type AdelicInt = Adelic<RnsInt>;
259pub type AdelicRat = Adelic<RnsFrac>;
261
262impl AdelicInt {
263 pub fn from_bigint(n: &BigInt, basis: Basis) -> Self {
265 Adelic {
266 finite: RnsInt::from_bigint(n, basis),
267 infinite: Ball::point(&BigRational::from_integer(n.clone())),
268 }
269 }
270
271 pub fn from_i64(n: i64, basis: Basis) -> Self {
273 Self::from_bigint(&BigInt::from(n), basis)
274 }
275
276 pub fn try_exact(&self) -> Result<BigInt, RangeError> {
279 let (p, q) = self
280 .finite
281 .try_reconstruct()
282 .ok_or(RangeError::ReconstructionFailed {
283 have_bits: self.finite.basis().capacity_bits(),
284 need_more: true,
285 })?;
286 self.validate(&p, &q).map(|(p, _)| p)
287 }
288}
289
290impl AdelicRat {
291 pub fn from_fraction(p: &BigInt, q: &BigInt, basis: Basis) -> Self {
293 Adelic {
294 finite: RnsFrac::from_fraction(p, q, basis),
295 infinite: Ball::point(&BigRational::new(p.clone(), q.clone())),
296 }
297 }
298
299 pub fn try_exact(&self) -> Result<(BigInt, BigInt), RangeError> {
301 let (p, q) = self
302 .finite
303 .try_reconstruct()
304 .ok_or(RangeError::ReconstructionFailed {
305 have_bits: self.finite.basis().capacity_bits(),
306 need_more: true,
307 })?;
308 self.validate(&p, &q)
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
318 fn overflow_is_detected_then_fixed() {
319 let small = Basis::with_bits(40);
320 let big = AdelicInt::from_bigint(&(BigInt::one() << 60), small.clone());
321 assert!(matches!(big.try_exact(), Err(RangeError::ReconstructionFailed { .. })));
322
323 let grown = big.with_basis(small.extend_to_bits(80));
324 assert_eq!(grown.try_exact().unwrap(), BigInt::one() << 60);
325 }
326
327 #[test]
328 fn sign_from_infinite_place_with_mixed_signs() {
329 let b = Basis::standard();
330 let a = AdelicInt::from_i64(-7, b.clone());
331 let c = AdelicInt::from_i64(5, b);
332 assert_eq!(a.add(&c).sign(), Some(Sign::Minus)); assert_eq!(a.sub(&c).sign(), Some(Sign::Minus)); }
335
336 #[test]
337 fn integer_arithmetic_round_trips() {
338 let b = Basis::standard();
339 let a = AdelicInt::from_i64(1000, b.clone());
340 let c = AdelicInt::from_i64(337, b);
341 assert_eq!(a.add(&c).try_exact().unwrap(), BigInt::from(1337));
342 assert_eq!(a.sub(&c).try_exact().unwrap(), BigInt::from(663));
343 assert_eq!(a.mul(&c).try_exact().unwrap(), BigInt::from(337_000));
344 }
345
346 #[test]
347 fn rational_reconstructs() {
348 let b = Basis::standard();
349 let x = AdelicRat::from_fraction(&BigInt::from(1), &BigInt::from(6), b.clone());
350 let y = AdelicRat::from_fraction(&BigInt::from(1), &BigInt::from(4), b);
351 let sum = x.add(&y); assert_eq!(sum.try_exact().unwrap(), (BigInt::from(5), BigInt::from(12)));
353 }
354}