1use std::collections::BTreeMap;
11use std::sync::Arc;
12
13use num_bigint::BigInt;
14use num_rational::BigRational;
15use num_traits::One;
16use parking_lot::Mutex;
17
18use crate::algebraic::AlgebraicNumber;
19use crate::ball::Ball;
20use crate::basis::Basis;
21use crate::rational::RnsRational;
22
23pub trait Computable: Send + Sync {
25 fn evaluate(&self, precision: u64) -> RnsRational;
27}
28
29#[derive(Clone)]
31pub struct ComputableReal {
32 inner: Arc<dyn Computable>,
33 cache: Arc<Mutex<BTreeMap<u64, RnsRational>>>,
34 channels: Basis,
35}
36
37impl ComputableReal {
38 fn wrap(inner: Arc<dyn Computable>, channels: Basis) -> Self {
39 ComputableReal {
40 inner,
41 cache: Arc::new(Mutex::new(BTreeMap::new())),
42 channels,
43 }
44 }
45
46 pub fn evaluate(&self, precision: u64) -> RnsRational {
52 {
53 let cache = self.cache.lock();
54 if let Some((_, r)) = cache.range(precision..).next() {
55 return r.clone();
56 }
57 }
58 let r = self.inner.evaluate(precision);
59 self.cache.lock().insert(precision, r.clone());
60 r
61 }
62
63 pub fn enclose(&self, precision: u64) -> Ball {
72 let (p, q) = self.evaluate(precision).to_pair();
73 let center = BigRational::new(p, q);
74 let eps = BigRational::new(BigInt::one(), BigInt::from(10).pow(precision as u32));
75 Ball::new(¢er - &eps, ¢er + &eps)
76 }
77
78 pub fn evaluate_f64(&self) -> f64 {
80 self.evaluate(20).to_f64()
81 }
82
83 pub fn channels(&self) -> Basis {
85 self.channels.clone()
86 }
87
88 pub fn from_rational(r: RnsRational) -> Self {
92 let channels = r.channels.clone();
93 Self::wrap(Arc::new(RationalC { r }), channels)
94 }
95
96 pub fn from_algebraic(a: AlgebraicNumber) -> Self {
98 let channels = a.channels.clone();
99 Self::wrap(Arc::new(AlgebraicC { a }), channels)
100 }
101
102 pub fn pi(channels: Basis) -> Self {
104 Self::wrap(Arc::new(PiC { channels: channels.clone() }), channels)
105 }
106
107 pub fn e(channels: Basis) -> Self {
109 Self::wrap(Arc::new(EulerC { channels: channels.clone() }), channels)
110 }
111
112 pub fn sqrt(r: RnsRational) -> Self {
114 let channels = r.channels.clone();
115 Self::wrap(Arc::new(SqrtC { r }), channels)
116 }
117
118 pub fn exp(r: RnsRational) -> Self {
120 let channels = r.channels.clone();
121 Self::wrap(Arc::new(ExpC { r }), channels)
122 }
123
124 pub fn ln(r: RnsRational) -> Self {
126 let channels = r.channels.clone();
127 Self::wrap(Arc::new(LnC { r }), channels)
128 }
129
130 pub fn add(&self, other: &Self) -> Self {
134 Self::wrap(
135 Arc::new(BinOp {
136 a: self.clone(),
137 b: other.clone(),
138 kind: BinKind::Add,
139 }),
140 self.channels.clone(),
141 )
142 }
143
144 pub fn sub(&self, other: &Self) -> Self {
146 self.add(&other.neg())
147 }
148
149 pub fn mul(&self, other: &Self) -> Self {
151 Self::wrap(
152 Arc::new(BinOp {
153 a: self.clone(),
154 b: other.clone(),
155 kind: BinKind::Mul,
156 }),
157 self.channels.clone(),
158 )
159 }
160
161 pub fn neg(&self) -> Self {
163 Self::wrap(Arc::new(NegC { a: self.clone() }), self.channels.clone())
164 }
165
166 pub fn recip(&self) -> Self {
168 Self::wrap(Arc::new(RecipC { a: self.clone() }), self.channels.clone())
169 }
170}
171
172fn eps(prec: u64, channels: &Basis) -> RnsRational {
176 RnsRational::new(BigInt::one(), pow10(prec), channels.clone())
177}
178
179fn pow10(p: u64) -> BigInt {
180 BigInt::from(10u8).pow(p as u32)
181}
182
183fn magnitude_digits(cr: &ComputableReal) -> u64 {
185 let v = cr.evaluate(4).to_f64().abs();
186 if v < 1.0 {
187 1
188 } else {
189 v.log10().floor() as u64 + 1
190 }
191}
192
193struct RationalC {
196 r: RnsRational,
197}
198impl Computable for RationalC {
199 fn evaluate(&self, _precision: u64) -> RnsRational {
200 self.r.clone()
201 }
202}
203
204struct AlgebraicC {
205 a: AlgebraicNumber,
206}
207impl Computable for AlgebraicC {
208 fn evaluate(&self, precision: u64) -> RnsRational {
209 let mut clone = self.a.clone();
210 let target = eps(precision + 1, &self.a.channels);
211 clone.refine_interval(&target);
212 clone.interval.0.midpoint(&clone.interval.1)
213 }
214}
215
216fn atan_inv(x: i64, target: &RnsRational, channels: &Basis) -> RnsRational {
218 let mut acc = RnsRational::zero(channels.clone());
219 let mut n: i64 = 0;
220 loop {
221 let exp = (2 * n + 1) as u32;
222 let denom = BigInt::from(2 * n + 1) * BigInt::from(x).pow(exp);
223 let sign = if n % 2 == 0 { 1 } else { -1 };
224 let term = RnsRational::new(BigInt::from(sign), denom, channels.clone());
225 acc = acc.add(&term);
226 if term.abs() < *target {
227 break;
228 }
229 n += 1;
230 }
231 acc
232}
233
234struct PiC {
235 channels: Basis,
236}
237impl Computable for PiC {
238 fn evaluate(&self, precision: u64) -> RnsRational {
239 let target = eps(precision + 5, &self.channels);
240 let a = atan_inv(5, &target, &self.channels)
241 .mul(&RnsRational::from_int(16, self.channels.clone()));
242 let b = atan_inv(239, &target, &self.channels)
243 .mul(&RnsRational::from_int(4, self.channels.clone()));
244 a.sub(&b)
245 }
246}
247
248struct EulerC {
249 channels: Basis,
250}
251impl Computable for EulerC {
252 fn evaluate(&self, precision: u64) -> RnsRational {
253 let target = eps(precision + 3, &self.channels);
254 let mut acc = RnsRational::zero(self.channels.clone());
255 let mut fact = BigInt::one();
256 let mut k: u64 = 0;
257 loop {
258 if k > 0 {
259 fact *= BigInt::from(k);
260 }
261 let term = RnsRational::new(BigInt::one(), fact.clone(), self.channels.clone());
262 acc = acc.add(&term);
263 if term < target {
264 break;
265 }
266 k += 1;
267 }
268 acc
269 }
270}
271
272struct SqrtC {
273 r: RnsRational,
274}
275impl Computable for SqrtC {
276 fn evaluate(&self, precision: u64) -> RnsRational {
277 let channels = self.r.channels.clone();
278 let target = eps(precision + 2, &channels);
279 let guess = self.r.to_f64().max(0.0).sqrt();
281 let mut x = if guess > 0.0 {
282 RnsRational::from_f64(guess, channels.clone())
283 } else {
284 RnsRational::from_int(1, channels.clone())
285 };
286 let two = RnsRational::from_int(2, channels.clone());
287 for _ in 0..200 {
289 if x.is_zero() {
290 break;
291 }
292 let next = x.add(&self.r.div(&x)).div(&two);
293 let err = next.mul(&next).sub(&self.r).abs();
294 x = next;
295 if err < target {
296 break;
297 }
298 }
299 x
300 }
301}
302
303struct ExpC {
304 r: RnsRational,
305}
306impl Computable for ExpC {
307 fn evaluate(&self, precision: u64) -> RnsRational {
308 let channels = self.r.channels.clone();
309 let target = eps(precision + 3, &channels);
310 let mut acc = RnsRational::zero(channels.clone());
311 let mut term = RnsRational::from_int(1, channels.clone()); let mut k: u64 = 0;
313 loop {
314 acc = acc.add(&term);
315 if k > 0 && term.abs() < target {
316 break;
317 }
318 k += 1;
319 term = term.mul(&self.r).div(&RnsRational::from_int(k as i64, channels.clone()));
321 if k > 5000 {
322 break;
323 }
324 }
325 acc
326 }
327}
328
329struct LnC {
330 r: RnsRational,
331}
332impl Computable for LnC {
333 fn evaluate(&self, precision: u64) -> RnsRational {
334 let channels = self.r.channels.clone();
335 let target = eps(precision + 3, &channels);
336 let one = RnsRational::from_int(1, channels.clone());
338 let t = self.r.sub(&one).div(&self.r.add(&one));
339 let t2 = t.mul(&t);
340 let mut acc = RnsRational::zero(channels.clone());
341 let mut power = t.clone();
342 let mut k: u64 = 0;
343 loop {
344 let term = power.div(&RnsRational::from_int((2 * k + 1) as i64, channels.clone()));
345 acc = acc.add(&term);
346 if term.abs() < target {
347 break;
348 }
349 power = power.mul(&t2);
350 k += 1;
351 if k > 100_000 {
352 break;
353 }
354 }
355 acc.mul(&RnsRational::from_int(2, channels.clone()))
356 }
357}
358
359enum BinKind {
360 Add,
361 Mul,
362}
363
364struct BinOp {
365 a: ComputableReal,
366 b: ComputableReal,
367 kind: BinKind,
368}
369impl Computable for BinOp {
370 fn evaluate(&self, precision: u64) -> RnsRational {
371 match self.kind {
372 BinKind::Add => {
373 let pa = self.a.evaluate(precision + 1);
374 let pb = self.b.evaluate(precision + 1);
375 pa.add(&pb)
376 }
377 BinKind::Mul => {
378 let guard = magnitude_digits(&self.a) + magnitude_digits(&self.b) + 2;
379 let pa = self.a.evaluate(precision + guard);
380 let pb = self.b.evaluate(precision + guard);
381 pa.mul(&pb)
382 }
383 }
384 }
385}
386
387struct NegC {
388 a: ComputableReal,
389}
390impl Computable for NegC {
391 fn evaluate(&self, precision: u64) -> RnsRational {
392 self.a.evaluate(precision).neg()
393 }
394}
395
396struct RecipC {
397 a: ComputableReal,
398}
399impl Computable for RecipC {
400 fn evaluate(&self, precision: u64) -> RnsRational {
401 let v = self.a.evaluate(4).to_f64().abs();
403 let extra = if v > 0.0 && v < 1.0 {
404 (-v.log10()).ceil() as u64 * 2 + 2
405 } else {
406 2
407 };
408 self.a.evaluate(precision + extra).recip()
409 }
410}
411
412impl AlgebraicNumber {
414 pub fn to_computable(&self) -> ComputableReal {
416 ComputableReal::from_algebraic(self.clone())
417 }
418}
419
420#[cfg(test)]
421mod tests {
422 use super::*;
423
424 fn ch() -> Basis {
425 Basis::standard()
426 }
427
428 #[test]
429 fn pi_to_ten_places() {
430 let pi = ComputableReal::pi(ch());
431 assert!((pi.evaluate(10).to_f64() - std::f64::consts::PI).abs() < 1e-10);
432 }
433
434 #[test]
435 fn e_to_fifteen_places() {
436 let e = ComputableReal::e(ch());
437 assert!((e.evaluate(15).to_f64() - std::f64::consts::E).abs() < 1e-14);
438 }
439
440 #[test]
441 fn sqrt_two() {
442 let r2 = RnsRational::from_int(2, ch());
443 let s = ComputableReal::sqrt(r2);
444 assert!((s.evaluate(20).to_f64() - 2f64.sqrt()).abs() < 1e-12);
445 }
446
447 #[test]
448 fn rational_passes_through() {
449 let r = RnsRational::from_fraction(1, 3, ch());
450 let cr = ComputableReal::from_rational(r.clone());
451 assert_eq!(cr.evaluate(100), r);
452 }
453
454 #[test]
455 fn precision_contract() {
456 let pi = ComputableReal::pi(ch());
457 let lo = pi.evaluate(5).to_f64();
458 let hi = pi.evaluate(50).to_f64();
459 assert!((lo - hi).abs() < 1e-5);
460 }
461
462 #[test]
463 fn lazy_sum_of_pi_and_one() {
464 let pi = ComputableReal::pi(ch());
465 let one = ComputableReal::from_rational(RnsRational::from_int(1, ch()));
466 let sum = pi.add(&one);
467 assert!((sum.evaluate(20).to_f64() - (std::f64::consts::PI + 1.0)).abs() < 1e-12);
468 }
469
470 #[test]
471 fn exp_and_ln() {
472 let e = ComputableReal::exp(RnsRational::from_int(1, ch()));
473 assert!((e.evaluate(15).to_f64() - std::f64::consts::E).abs() < 1e-13);
474 let l = ComputableReal::ln(RnsRational::from_int(2, ch()));
475 assert!((l.evaluate(15).to_f64() - 2f64.ln()).abs() < 1e-13);
476 }
477
478 #[test]
479 fn algebraic_to_computable() {
480 let s2 = AlgebraicNumber::sqrt(2, ch()).to_computable();
481 assert!((s2.evaluate(15).to_f64() - 2f64.sqrt()).abs() < 1e-13);
482 }
483
484 #[test]
485 fn enclose_is_rigorous() {
486 use crate::ball::rational_to_f64;
487 let recip = ComputableReal::pi(ch()).enclose(50).recip().unwrap();
489 assert!(rational_to_f64(&recip.width()) < 1e-40);
490 assert!((recip.to_f64() - 1.0 / std::f64::consts::PI).abs() < 1e-40);
491
492 let prod = ComputableReal::e(ch()).mul(&ComputableReal::pi(ch())).enclose(30);
494 let epi = std::f64::consts::E * std::f64::consts::PI;
495 assert!(rational_to_f64(&prod.width()) < 1e-25);
496 assert!((prod.to_f64() - epi).abs() < 1e-9);
497 }
498
499 #[test]
500 fn cache_reuses_tighter_entry() {
501 let pi = ComputableReal::pi(ch());
502 let _ = pi.evaluate(40); assert!((pi.evaluate(5).to_f64() - std::f64::consts::PI).abs() < 1e-30);
505 }
506}