1use alloc::rc::Rc;
10use core::iter::{Product, Sum};
11use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
12
13use dashu_base::Inverse;
14use dashu_float::round::Round;
15use dashu_float::FBig;
16use dashu_int::{IBig, Word};
17
18use crate::cbig::CBig;
19use crate::cbig_cached::CachedCBig;
20
21macro_rules! impl_cached_cbig_binop {
26 ($Op:ident, $op:ident) => {
27 impl<R: Round, const B: Word> $Op<CachedCBig<R, B>> for CachedCBig<R, B> {
28 type Output = CachedCBig<R, B>;
29 #[inline]
30 fn $op(self, rhs: CachedCBig<R, B>) -> Self::Output {
31 CachedCBig::from_cbig($Op::$op(self.cbig, rhs.cbig), &self.cache)
32 }
33 }
34 };
35}
36macro_rules! impl_cached_cbig_binop_assign {
37 ($OpAssign:ident, $op_assign:ident, $Op:ident, $op:ident) => {
38 impl<R: Round, const B: Word> $OpAssign<CachedCBig<R, B>> for CachedCBig<R, B> {
39 #[inline]
40 fn $op_assign(&mut self, rhs: CachedCBig<R, B>) {
41 self.cbig = $Op::$op(self.cbig.clone(), rhs.cbig);
42 }
43 }
44 };
45}
46impl_cached_cbig_binop!(Add, add);
47impl_cached_cbig_binop!(Sub, sub);
48impl_cached_cbig_binop!(Mul, mul);
49impl_cached_cbig_binop!(Div, div);
50impl_cached_cbig_binop_assign!(AddAssign, add_assign, Add, add);
51impl_cached_cbig_binop_assign!(SubAssign, sub_assign, Sub, sub);
52impl_cached_cbig_binop_assign!(MulAssign, mul_assign, Mul, mul);
53impl_cached_cbig_binop_assign!(DivAssign, div_assign, Div, div);
54
55macro_rules! impl_cached_binop_one_way_with_cbig {
60 ($Op:ident, $op:ident) => {
61 impl<R: Round, const B: Word> $Op<CBig<R, B>> for CachedCBig<R, B> {
62 type Output = CachedCBig<R, B>;
63 #[inline]
64 fn $op(self, rhs: CBig<R, B>) -> Self::Output {
65 CachedCBig::from_cbig($Op::$op(self.cbig, rhs), &self.cache)
66 }
67 }
68 impl<'l, R: Round, const B: Word> $Op<CBig<R, B>> for &'l CachedCBig<R, B> {
69 type Output = CachedCBig<R, B>;
70 #[inline]
71 fn $op(self, rhs: CBig<R, B>) -> Self::Output {
72 CachedCBig::from_cbig($Op::$op(self.cbig.clone(), rhs), &self.cache)
73 }
74 }
75 impl<'r, R: Round, const B: Word> $Op<&'r CBig<R, B>> for CachedCBig<R, B> {
76 type Output = CachedCBig<R, B>;
77 #[inline]
78 fn $op(self, rhs: &CBig<R, B>) -> Self::Output {
79 CachedCBig::from_cbig($Op::$op(self.cbig, rhs.clone()), &self.cache)
80 }
81 }
82 impl<'l, 'r, R: Round, const B: Word> $Op<&'r CBig<R, B>> for &'l CachedCBig<R, B> {
83 type Output = CachedCBig<R, B>;
84 #[inline]
85 fn $op(self, rhs: &CBig<R, B>) -> Self::Output {
86 CachedCBig::from_cbig($Op::$op(self.cbig.clone(), rhs.clone()), &self.cache)
87 }
88 }
89 };
90}
91
92macro_rules! impl_cached_binop_reverse_with_cbig {
93 ($Op:ident, $op:ident) => {
94 impl<R: Round, const B: Word> $Op<CachedCBig<R, B>> for CBig<R, B> {
95 type Output = CachedCBig<R, B>;
96 #[inline]
97 fn $op(self, rhs: CachedCBig<R, B>) -> Self::Output {
98 CachedCBig::from_cbig($Op::$op(self, rhs.cbig), &rhs.cache)
99 }
100 }
101 impl<'l, R: Round, const B: Word> $Op<CachedCBig<R, B>> for &'l CBig<R, B> {
102 type Output = CachedCBig<R, B>;
103 #[inline]
104 fn $op(self, rhs: CachedCBig<R, B>) -> Self::Output {
105 CachedCBig::from_cbig($Op::$op(self.clone(), rhs.cbig), &rhs.cache)
106 }
107 }
108 impl<'r, R: Round, const B: Word> $Op<&'r CachedCBig<R, B>> for CBig<R, B> {
109 type Output = CachedCBig<R, B>;
110 #[inline]
111 fn $op(self, rhs: &CachedCBig<R, B>) -> Self::Output {
112 CachedCBig::from_cbig($Op::$op(self, rhs.cbig.clone()), &rhs.cache)
113 }
114 }
115 impl<'l, 'r, R: Round, const B: Word> $Op<&'r CachedCBig<R, B>> for &'l CBig<R, B> {
116 type Output = CachedCBig<R, B>;
117 #[inline]
118 fn $op(self, rhs: &CachedCBig<R, B>) -> Self::Output {
119 CachedCBig::from_cbig($Op::$op(self.clone(), rhs.cbig.clone()), &rhs.cache)
120 }
121 }
122 };
123}
124
125macro_rules! impl_cached_binop_assign_with_cbig {
127 ($OpAssign:ident, $op_assign:ident, $Op:ident, $op:ident) => {
128 impl<R: Round, const B: Word> $OpAssign<CBig<R, B>> for CachedCBig<R, B> {
129 #[inline]
130 fn $op_assign(&mut self, rhs: CBig<R, B>) {
131 self.cbig = $Op::$op(self.cbig.clone(), rhs);
132 }
133 }
134 impl<R: Round, const B: Word> $OpAssign<&CBig<R, B>> for CachedCBig<R, B> {
135 #[inline]
136 fn $op_assign(&mut self, rhs: &CBig<R, B>) {
137 self.cbig = $Op::$op(self.cbig.clone(), rhs.clone());
138 }
139 }
140 };
141}
142
143macro_rules! impl_cached_binop_with_cbig {
144 ($Op:ident, $op:ident, $OpAssign:ident, $op_assign:ident) => {
145 impl_cached_binop_one_way_with_cbig!($Op, $op);
146 impl_cached_binop_reverse_with_cbig!($Op, $op);
147 impl_cached_binop_assign_with_cbig!($OpAssign, $op_assign, $Op, $op);
148 };
149}
150
151impl_cached_binop_with_cbig!(Add, add, AddAssign, add_assign);
152impl_cached_binop_with_cbig!(Sub, sub, SubAssign, sub_assign);
153impl_cached_binop_with_cbig!(Mul, mul, MulAssign, mul_assign);
154impl_cached_binop_with_cbig!(Div, div, DivAssign, div_assign);
155
156macro_rules! impl_cached_binop_one_way_with_fbig {
161 ($Op:ident, $op:ident) => {
162 impl<R: Round, const B: Word> $Op<FBig<R, B>> for CachedCBig<R, B> {
163 type Output = CachedCBig<R, B>;
164 #[inline]
165 fn $op(self, rhs: FBig<R, B>) -> Self::Output {
166 CachedCBig::from_cbig($Op::$op(self.cbig, rhs), &self.cache)
167 }
168 }
169 impl<'l, R: Round, const B: Word> $Op<FBig<R, B>> for &'l CachedCBig<R, B> {
170 type Output = CachedCBig<R, B>;
171 #[inline]
172 fn $op(self, rhs: FBig<R, B>) -> Self::Output {
173 CachedCBig::from_cbig($Op::$op(self.cbig.clone(), rhs), &self.cache)
174 }
175 }
176 impl<'r, R: Round, const B: Word> $Op<&'r FBig<R, B>> for CachedCBig<R, B> {
177 type Output = CachedCBig<R, B>;
178 #[inline]
179 fn $op(self, rhs: &FBig<R, B>) -> Self::Output {
180 CachedCBig::from_cbig($Op::$op(self.cbig, rhs.clone()), &self.cache)
181 }
182 }
183 impl<'l, 'r, R: Round, const B: Word> $Op<&'r FBig<R, B>> for &'l CachedCBig<R, B> {
184 type Output = CachedCBig<R, B>;
185 #[inline]
186 fn $op(self, rhs: &FBig<R, B>) -> Self::Output {
187 CachedCBig::from_cbig($Op::$op(self.cbig.clone(), rhs.clone()), &self.cache)
188 }
189 }
190 };
191}
192
193macro_rules! impl_cached_binop_reverse_with_fbig {
194 ($Op:ident, $op:ident) => {
195 impl<R: Round, const B: Word> $Op<CachedCBig<R, B>> for FBig<R, B> {
196 type Output = CachedCBig<R, B>;
197 #[inline]
198 fn $op(self, rhs: CachedCBig<R, B>) -> Self::Output {
199 CachedCBig::from_cbig($Op::$op(self, rhs.cbig), &rhs.cache)
200 }
201 }
202 impl<'l, R: Round, const B: Word> $Op<CachedCBig<R, B>> for &'l FBig<R, B> {
203 type Output = CachedCBig<R, B>;
204 #[inline]
205 fn $op(self, rhs: CachedCBig<R, B>) -> Self::Output {
206 CachedCBig::from_cbig($Op::$op(self.clone(), rhs.cbig), &rhs.cache)
207 }
208 }
209 impl<'r, R: Round, const B: Word> $Op<&'r CachedCBig<R, B>> for FBig<R, B> {
210 type Output = CachedCBig<R, B>;
211 #[inline]
212 fn $op(self, rhs: &CachedCBig<R, B>) -> Self::Output {
213 CachedCBig::from_cbig($Op::$op(self, rhs.cbig.clone()), &rhs.cache)
214 }
215 }
216 impl<'l, 'r, R: Round, const B: Word> $Op<&'r CachedCBig<R, B>> for &'l FBig<R, B> {
217 type Output = CachedCBig<R, B>;
218 #[inline]
219 fn $op(self, rhs: &CachedCBig<R, B>) -> Self::Output {
220 CachedCBig::from_cbig($Op::$op(self.clone(), rhs.cbig.clone()), &rhs.cache)
221 }
222 }
223 };
224}
225
226impl_cached_binop_one_way_with_fbig!(Mul, mul);
227impl_cached_binop_reverse_with_fbig!(Mul, mul);
228impl_cached_binop_one_way_with_fbig!(Div, div);
229impl_cached_binop_reverse_with_fbig!(Div, div);
230
231impl<R: Round, const B: Word> Neg for CachedCBig<R, B> {
236 type Output = CachedCBig<R, B>;
237 #[inline]
238 fn neg(self) -> Self::Output {
239 CachedCBig::from_cbig(-self.cbig, &self.cache)
240 }
241}
242
243impl<R: Round, const B: Word> Neg for &CachedCBig<R, B> {
244 type Output = CachedCBig<R, B>;
245 #[inline]
246 fn neg(self) -> Self::Output {
247 CachedCBig::from_cbig(-&self.cbig, &self.cache)
248 }
249}
250
251impl<R: Round, const B: Word> Inverse for CachedCBig<R, B> {
252 type Output = CachedCBig<R, B>;
253 #[inline]
254 fn inv(self) -> Self::Output {
255 CachedCBig::from_cbig(Inverse::inv(self.cbig), &self.cache)
256 }
257}
258
259impl<R: Round, const B: Word> Inverse for &CachedCBig<R, B> {
260 type Output = CachedCBig<R, B>;
261 #[inline]
262 fn inv(self) -> Self::Output {
263 CachedCBig::from_cbig(Inverse::inv(&self.cbig), &self.cache)
264 }
265}
266
267impl<R: Round, const B: Word> Sum for CachedCBig<R, B> {
275 fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
276 match iter.next() {
277 Some(first) => {
278 let cache = Rc::clone(&first.cache);
279 let cbig: CBig<R, B> = core::iter::once(first.cbig)
280 .chain(iter.map(|c| c.cbig))
281 .sum();
282 CachedCBig::from_cbig(cbig, &cache)
283 }
284 None => Self::default(),
285 }
286 }
287}
288
289impl<'a, R: Round, const B: Word> Sum<&'a CachedCBig<R, B>> for CachedCBig<R, B> {
290 fn sum<I: Iterator<Item = &'a CachedCBig<R, B>>>(mut iter: I) -> Self {
291 match iter.next() {
292 Some(first) => {
293 let cache = Rc::clone(&first.cache);
294 let cbig: CBig<R, B> = core::iter::once(first.cbig.clone())
295 .chain(iter.map(|c| c.cbig.clone()))
296 .sum();
297 CachedCBig::from_cbig(cbig, &cache)
298 }
299 None => Self::default(),
300 }
301 }
302}
303
304impl<R: Round, const B: Word> Product for CachedCBig<R, B> {
305 fn product<I: Iterator<Item = Self>>(mut iter: I) -> Self {
306 match iter.next() {
307 Some(first) => {
308 let cache = Rc::clone(&first.cache);
309 let cbig: CBig<R, B> = core::iter::once(first.cbig)
310 .chain(iter.map(|c| c.cbig))
311 .product();
312 CachedCBig::from_cbig(cbig, &cache)
313 }
314 None => Self::default(),
315 }
316 }
317}
318
319impl<'a, R: Round, const B: Word> Product<&'a CachedCBig<R, B>> for CachedCBig<R, B> {
320 fn product<I: Iterator<Item = &'a CachedCBig<R, B>>>(mut iter: I) -> Self {
321 match iter.next() {
322 Some(first) => {
323 let cache = Rc::clone(&first.cache);
324 let cbig: CBig<R, B> = core::iter::once(first.cbig.clone())
325 .chain(iter.map(|c| c.cbig.clone()))
326 .product();
327 CachedCBig::from_cbig(cbig, &cache)
328 }
329 None => Self::default(),
330 }
331 }
332}
333
334macro_rules! forward_cached {
342 ($name:ident => $ctx:ident) => {
343 #[doc = concat!("See [`CBig::", stringify!($name), "`].")]
344 #[inline]
345 pub fn $name(&self) -> CachedCBig<R, B> {
346 let ctx = self.cbig.context();
347 let cbig = {
348 let mut c = self.cache.borrow_mut();
349 ctx.unwrap_cfp(ctx.$ctx::<B>(&self.cbig, Some(&mut *c)))
350 };
351 CachedCBig::from_cbig(cbig, &self.cache)
352 }
353 };
354}
355
356macro_rules! delegate_to_cbig {
358 ($name:ident) => {
359 #[doc = concat!("See [`CBig::", stringify!($name), "`].")]
360 #[inline]
361 pub fn $name(&self) -> CachedCBig<R, B> {
362 CachedCBig::from_cbig(self.cbig.$name(), &self.cache)
363 }
364 };
365 ($name:ident($arg:ident: $arg_ty:ty)) => {
366 #[doc = concat!("See [`CBig::", stringify!($name), "`].")]
367 #[inline]
368 pub fn $name(&self, $arg: $arg_ty) -> CachedCBig<R, B> {
369 CachedCBig::from_cbig(self.cbig.$name($arg), &self.cache)
370 }
371 };
372}
373
374impl<R: Round, const B: Word> CachedCBig<R, B> {
375 forward_cached!(ln => log);
377 forward_cached!(exp => exp);
378 forward_cached!(sin => sin);
379 forward_cached!(cos => cos);
380 forward_cached!(tan => tan);
381 forward_cached!(asin => asin);
382 forward_cached!(acos => acos);
383 forward_cached!(atan => atan);
384
385 delegate_to_cbig!(sqr);
387 delegate_to_cbig!(sqrt);
388 delegate_to_cbig!(conj);
389 delegate_to_cbig!(proj);
390 delegate_to_cbig!(mul_i(negative: bool));
391 delegate_to_cbig!(powi(exp: IBig));
392
393 #[inline]
395 pub fn norm(&self) -> FBig<R, B> {
396 self.cbig.norm()
397 }
398
399 #[inline]
401 pub fn abs(&self) -> FBig<R, B> {
402 self.cbig.abs()
403 }
404
405 #[inline]
408 pub fn arg(&self) -> FBig<R, B> {
409 let ctx = self.cbig.context();
410 let mut c = self.cache.borrow_mut();
411 ctx.float()
412 .unwrap_fp(ctx.arg::<B>(&self.cbig, Some(&mut *c)))
413 }
414
415 #[inline]
417 pub fn powf(&self, w: &Self) -> Self {
418 let ctx = self.cbig.context();
419 let cbig = {
420 let mut c = self.cache.borrow_mut();
421 ctx.unwrap_cfp(ctx.powf::<B>(&self.cbig, &w.cbig, Some(&mut *c)))
422 };
423 CachedCBig::from_cbig(cbig, &self.cache)
424 }
425
426 #[inline]
429 pub fn sin_cos(&self) -> (Self, Self) {
430 let ctx = self.cbig.context();
431 let (s, c) = {
432 let mut guard = self.cache.borrow_mut();
433 ctx.sin_cos::<B>(&self.cbig, Some(&mut *guard))
434 };
435 (
436 CachedCBig::from_cbig(ctx.unwrap_cfp(s), &self.cache),
437 CachedCBig::from_cbig(ctx.unwrap_cfp(c), &self.cache),
438 )
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445 use alloc::vec::Vec;
446 use core::cell::RefCell;
447 use dashu_float::round::mode;
448 use dashu_float::ConstCache;
449
450 type F = FBig<mode::HalfAway, 10>;
451 type C = CBig<mode::HalfAway, 10>;
452 type CC = CachedCBig<mode::HalfAway, 10>;
453
454 fn c(re: i32, im: i32) -> C {
455 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
456 CBig::from_parts(mk(re), mk(im))
457 }
458 fn cc(re: i32, im: i32) -> CC {
459 CachedCBig::from(c(re, im))
460 }
461 fn eq(ccval: CC, expected: &C) {
463 assert_eq!(ccval.as_cbig(), expected);
464 }
465
466 #[test]
467 fn arithmetic_matches_cbig() {
468 let (a, b) = (c(2, 1), c(3, 4));
469 let (ca, cb) = (cc(2, 1), cc(3, 4));
470 eq(ca.clone() + cb.clone(), &(&a + &b));
471 eq(ca.clone() - cb.clone(), &(&a - &b));
472 eq(ca.clone() * cb.clone(), &(&a * &b));
473 eq(ca.clone() / cb.clone(), &(&a / &b));
474
475 eq(ca.clone() + a.clone(), &(&a + &a));
477 eq(a.clone() + ca.clone(), &(&a + &a));
478
479 let s = F::from(2);
481 eq(ca.clone() * s.clone(), &(&a * &s));
482 eq(s.clone() * ca.clone(), &(&s * &a));
483 eq(ca.clone() / s.clone(), &(&a / &s));
484 }
485
486 #[test]
487 fn neg_inverse_matches_cbig() {
488 let (a, ca) = (c(2, 1), cc(2, 1));
489 eq(-ca.clone(), &(-&a));
490 eq(Inverse::inv(ca.clone()), &Inverse::inv(&a));
491 }
492
493 #[test]
494 fn sum_product_matches_cbig() {
495 let vals = [(1, 2), (3, 4), (-1, 1)];
496 let cvals: Vec<C> = vals.iter().map(|&(r, i)| c(r, i)).collect();
497 let ccvals: Vec<CC> = vals.iter().map(|&(r, i)| cc(r, i)).collect();
498
499 eq(ccvals.iter().sum::<CC>(), &cvals.iter().sum::<C>());
500 eq(ccvals.clone().into_iter().sum::<CC>(), &cvals.clone().into_iter().sum::<C>());
501 eq(ccvals.iter().product::<CC>(), &cvals.iter().product::<C>());
502 eq(
503 ccvals.clone().into_iter().product::<CC>(),
504 &cvals.clone().into_iter().product::<C>(),
505 );
506 eq(core::iter::empty::<CC>().sum::<CC>(), &C::default());
508 }
509
510 #[test]
511 fn transcendentals_match_cbig() {
512 let (a, ca) = (c(2, 1), cc(2, 1));
513 eq(ca.clone().ln(), &a.clone().ln());
515 eq(ca.clone().exp(), &a.clone().exp());
516 eq(ca.clone().sin(), &a.clone().sin());
517 eq(ca.clone().cos(), &a.clone().cos());
518 eq(ca.clone().tan(), &a.clone().tan());
519 eq(ca.clone().asin(), &a.clone().asin());
520 eq(ca.clone().acos(), &a.clone().acos());
521 eq(ca.clone().atan(), &a.clone().atan());
522 eq(ca.clone().powf(&ca.clone()), &a.clone().powf(&a));
523
524 let (s_c, c_c) = ca.clone().sin_cos();
525 let (s, c) = a.clone().sin_cos();
526 eq(s_c, &s);
527 eq(c_c, &c);
528
529 eq(ca.clone().sqr(), &a.clone().sqr());
531 eq(ca.clone().sqrt(), &a.clone().sqrt());
532 eq(ca.clone().conj(), &a.clone().conj());
533 eq(ca.clone().proj(), &a.clone().proj());
534 eq(ca.clone().powi(3.into()), &a.clone().powi(3.into()));
535
536 assert_eq!(ca.norm(), a.norm());
538 assert_eq!(ca.abs(), a.abs());
539 assert_eq!(ca.arg(), a.arg());
540 }
541
542 #[test]
543 fn cache_populated_and_survives_arithmetic() {
544 let z = cc(2, 1);
546 assert_eq!(z.cache().total_terms(), 0);
547 let _ = z.clone().ln();
548 assert!(z.cache().total_terms() > 0);
549 assert!(z.cache().total_words() > 0);
550
551 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
554 let h = Rc::new(RefCell::new(ConstCache::new()));
555 let a = CC::new(C::from_parts(mk(2), mk(0)), h.clone());
556 let b = CC::new(C::from_parts(mk(3), mk(0)), h.clone());
557 let sum_ln = (a.clone() + b.clone()).ln().into_cbig();
558 let expected = (C::from_parts(mk(2), F::ZERO) + C::from_parts(mk(3), F::ZERO)).ln();
559 assert_eq!(sum_ln, expected);
560 assert!(h.borrow().total_terms() > 0);
561 }
562}