Skip to main content

dashu_float/
fbig_cached_ops.rs

1//! Operators for [`CachedFBig`] — all binary/unary operators with cache preservation.
2
3use alloc::rc::Rc;
4use core::iter::{Product, Sum};
5use core::ops::{
6    Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Shl, ShlAssign, Shr,
7    ShrAssign, Sub, SubAssign,
8};
9
10use dashu_base::{Abs, CubicRoot, DivEuclid, DivRemEuclid, Inverse, RemEuclid, Sign, SquareRoot};
11
12use crate::fbig::FBig;
13use crate::fbig_cached::CachedFBig;
14use crate::repr::{Context, Word};
15use crate::round::Round;
16
17// ---------------------------------------------------------------------------
18// CachedFBig op CachedFBig (preserves LHS cache)
19// ---------------------------------------------------------------------------
20
21macro_rules! impl_cached_binop {
22    ($Op:ident, $op:ident) => {
23        impl<R: Round, const B: Word> $Op<CachedFBig<R, B>> for CachedFBig<R, B> {
24            type Output = CachedFBig<R, B>;
25            #[inline]
26            fn $op(self, rhs: CachedFBig<R, B>) -> Self::Output {
27                CachedFBig::from_fbig($Op::$op(self.fbig, rhs.fbig), &self.cache)
28            }
29        }
30        impl<'l, R: Round, const B: Word> $Op<CachedFBig<R, B>> for &'l CachedFBig<R, B> {
31            type Output = CachedFBig<R, B>;
32            #[inline]
33            fn $op(self, rhs: CachedFBig<R, B>) -> Self::Output {
34                CachedFBig::from_fbig($Op::$op(self.fbig.clone(), rhs.fbig), &self.cache)
35            }
36        }
37        impl<'r, R: Round, const B: Word> $Op<&'r CachedFBig<R, B>> for CachedFBig<R, B> {
38            type Output = CachedFBig<R, B>;
39            #[inline]
40            fn $op(self, rhs: &'r CachedFBig<R, B>) -> Self::Output {
41                CachedFBig::from_fbig($Op::$op(self.fbig, rhs.fbig.clone()), &self.cache)
42            }
43        }
44        impl<'l, 'r, R: Round, const B: Word> $Op<&'r CachedFBig<R, B>> for &'l CachedFBig<R, B> {
45            type Output = CachedFBig<R, B>;
46            #[inline]
47            fn $op(self, rhs: &'r CachedFBig<R, B>) -> Self::Output {
48                CachedFBig::from_fbig($Op::$op(self.fbig.clone(), rhs.fbig.clone()), &self.cache)
49            }
50        }
51    };
52}
53impl_cached_binop!(Add, add);
54impl_cached_binop!(Sub, sub);
55impl_cached_binop!(Mul, mul);
56impl_cached_binop!(Div, div);
57impl_cached_binop!(Rem, rem);
58
59macro_rules! impl_cached_binop_assign {
60    ($OpAssign:ident, $op_assign:ident, $Op:ident, $op:ident) => {
61        impl<R: Round, const B: Word> $OpAssign<CachedFBig<R, B>> for CachedFBig<R, B> {
62            #[inline]
63            fn $op_assign(&mut self, rhs: CachedFBig<R, B>) {
64                let res = $Op::$op(self.fbig.clone(), rhs.fbig);
65                self.fbig = res;
66            }
67        }
68    };
69}
70impl_cached_binop_assign!(AddAssign, add_assign, Add, add);
71impl_cached_binop_assign!(SubAssign, sub_assign, Sub, sub);
72impl_cached_binop_assign!(MulAssign, mul_assign, Mul, mul);
73impl_cached_binop_assign!(DivAssign, div_assign, Div, div);
74impl_cached_binop_assign!(RemAssign, rem_assign, Rem, rem);
75
76// ---------------------------------------------------------------------------
77// CachedFBig op FBig and FBig op CachedFBig (cache preserved from CachedFBig side)
78// ---------------------------------------------------------------------------
79
80macro_rules! impl_cached_binop_one_way_with_fbig {
81    ($Op:ident, $op:ident) => {
82        impl<R: Round, const B: Word> $Op<FBig<R, B>> for CachedFBig<R, B> {
83            type Output = CachedFBig<R, B>;
84            #[inline]
85            fn $op(self, rhs: FBig<R, B>) -> Self::Output {
86                CachedFBig::from_fbig($Op::$op(self.fbig, rhs), &self.cache)
87            }
88        }
89        impl<'l, R: Round, const B: Word> $Op<FBig<R, B>> for &'l CachedFBig<R, B> {
90            type Output = CachedFBig<R, B>;
91            #[inline]
92            fn $op(self, rhs: FBig<R, B>) -> Self::Output {
93                CachedFBig::from_fbig($Op::$op(self.fbig.clone(), rhs), &self.cache)
94            }
95        }
96        impl<'r, R: Round, const B: Word> $Op<&'r FBig<R, B>> for CachedFBig<R, B> {
97            type Output = CachedFBig<R, B>;
98            #[inline]
99            fn $op(self, rhs: &FBig<R, B>) -> Self::Output {
100                CachedFBig::from_fbig($Op::$op(self.fbig, rhs.clone()), &self.cache)
101            }
102        }
103        impl<'l, 'r, R: Round, const B: Word> $Op<&'r FBig<R, B>> for &'l CachedFBig<R, B> {
104            type Output = CachedFBig<R, B>;
105            #[inline]
106            fn $op(self, rhs: &FBig<R, B>) -> Self::Output {
107                CachedFBig::from_fbig($Op::$op(self.fbig.clone(), rhs.clone()), &self.cache)
108            }
109        }
110    };
111}
112
113macro_rules! impl_cached_binop_reverse_with_fbig {
114    ($Op:ident, $op:ident) => {
115        impl<R: Round, const B: Word> $Op<CachedFBig<R, B>> for FBig<R, B> {
116            type Output = CachedFBig<R, B>;
117            #[inline]
118            fn $op(self, rhs: CachedFBig<R, B>) -> Self::Output {
119                CachedFBig::from_fbig($Op::$op(self, rhs.fbig), &rhs.cache)
120            }
121        }
122        impl<'l, R: Round, const B: Word> $Op<CachedFBig<R, B>> for &'l FBig<R, B> {
123            type Output = CachedFBig<R, B>;
124            #[inline]
125            fn $op(self, rhs: CachedFBig<R, B>) -> Self::Output {
126                CachedFBig::from_fbig($Op::$op(self.clone(), rhs.fbig), &rhs.cache)
127            }
128        }
129        impl<'r, R: Round, const B: Word> $Op<&'r CachedFBig<R, B>> for FBig<R, B> {
130            type Output = CachedFBig<R, B>;
131            #[inline]
132            fn $op(self, rhs: &CachedFBig<R, B>) -> Self::Output {
133                CachedFBig::from_fbig($Op::$op(self, rhs.fbig.clone()), &rhs.cache)
134            }
135        }
136        impl<'l, 'r, R: Round, const B: Word> $Op<&'r CachedFBig<R, B>> for &'l FBig<R, B> {
137            type Output = CachedFBig<R, B>;
138            #[inline]
139            fn $op(self, rhs: &CachedFBig<R, B>) -> Self::Output {
140                CachedFBig::from_fbig($Op::$op(self.clone(), rhs.fbig.clone()), &rhs.cache)
141            }
142        }
143    };
144}
145
146impl_cached_binop_one_way_with_fbig!(Add, add);
147impl_cached_binop_one_way_with_fbig!(Sub, sub);
148impl_cached_binop_one_way_with_fbig!(Mul, mul);
149impl_cached_binop_one_way_with_fbig!(Div, div);
150impl_cached_binop_one_way_with_fbig!(Rem, rem);
151
152impl_cached_binop_reverse_with_fbig!(Add, add);
153impl_cached_binop_reverse_with_fbig!(Sub, sub);
154impl_cached_binop_reverse_with_fbig!(Mul, mul);
155impl_cached_binop_reverse_with_fbig!(Div, div);
156impl_cached_binop_reverse_with_fbig!(Rem, rem);
157
158// assign: CachedFBig op= FBig
159
160macro_rules! impl_cached_binop_assign_with_fbig {
161    ($OpAssign:ident, $op_assign:ident, $Op:ident, $op:ident) => {
162        impl<R: Round, const B: Word> $OpAssign<FBig<R, B>> for CachedFBig<R, B> {
163            #[inline]
164            fn $op_assign(&mut self, rhs: FBig<R, B>) {
165                self.fbig = $Op::$op(self.fbig.clone(), rhs);
166            }
167        }
168        impl<R: Round, const B: Word> $OpAssign<&FBig<R, B>> for CachedFBig<R, B> {
169            #[inline]
170            fn $op_assign(&mut self, rhs: &FBig<R, B>) {
171                self.fbig = $Op::$op(self.fbig.clone(), rhs.clone());
172            }
173        }
174    };
175}
176
177impl_cached_binop_assign_with_fbig!(AddAssign, add_assign, Add, add);
178impl_cached_binop_assign_with_fbig!(SubAssign, sub_assign, Sub, sub);
179impl_cached_binop_assign_with_fbig!(MulAssign, mul_assign, Mul, mul);
180impl_cached_binop_assign_with_fbig!(DivAssign, div_assign, Div, div);
181impl_cached_binop_assign_with_fbig!(RemAssign, rem_assign, Rem, rem);
182
183// ---------------------------------------------------------------------------
184// CachedFBig op Primitive and Primitive op CachedFBig
185// (delegate through the FBig-side impls above)
186// ---------------------------------------------------------------------------
187
188macro_rules! impl_cached_binop_one_way_with_primitive {
189    ($Op:ident, $op:ident, $target:ty) => {
190        impl<R: Round, const B: Word> $Op<$target> for CachedFBig<R, B> {
191            type Output = CachedFBig<R, B>;
192            #[inline]
193            fn $op(self, rhs: $target) -> Self::Output {
194                self.$op(FBig::<R, B>::from(rhs))
195            }
196        }
197        impl<'l, R: Round, const B: Word> $Op<$target> for &'l CachedFBig<R, B> {
198            type Output = CachedFBig<R, B>;
199            #[inline]
200            fn $op(self, rhs: $target) -> Self::Output {
201                self.$op(FBig::<R, B>::from(rhs))
202            }
203        }
204        impl<'r, R: Round, const B: Word> $Op<&'r $target> for CachedFBig<R, B> {
205            type Output = CachedFBig<R, B>;
206            #[inline]
207            fn $op(self, rhs: &$target) -> Self::Output {
208                self.$op(FBig::<R, B>::from(rhs.clone()))
209            }
210        }
211        impl<'l, 'r, R: Round, const B: Word> $Op<&'r $target> for &'l CachedFBig<R, B> {
212            type Output = CachedFBig<R, B>;
213            #[inline]
214            fn $op(self, rhs: &$target) -> Self::Output {
215                self.$op(FBig::<R, B>::from(rhs.clone()))
216            }
217        }
218    };
219}
220
221macro_rules! impl_cached_binop_reverse_with_primitive {
222    ($Op:ident, $op:ident, $target:ty) => {
223        impl<R: Round, const B: Word> $Op<CachedFBig<R, B>> for $target {
224            type Output = CachedFBig<R, B>;
225            #[inline]
226            fn $op(self, rhs: CachedFBig<R, B>) -> Self::Output {
227                FBig::<R, B>::from(self).$op(rhs)
228            }
229        }
230        impl<'l, R: Round, const B: Word> $Op<CachedFBig<R, B>> for &'l $target {
231            type Output = CachedFBig<R, B>;
232            #[inline]
233            fn $op(self, rhs: CachedFBig<R, B>) -> Self::Output {
234                FBig::<R, B>::from(self.clone()).$op(rhs)
235            }
236        }
237        impl<'r, R: Round, const B: Word> $Op<&'r CachedFBig<R, B>> for $target {
238            type Output = CachedFBig<R, B>;
239            #[inline]
240            fn $op(self, rhs: &CachedFBig<R, B>) -> Self::Output {
241                FBig::<R, B>::from(self).$op(rhs)
242            }
243        }
244        impl<'l, 'r, R: Round, const B: Word> $Op<&'r CachedFBig<R, B>> for &'l $target {
245            type Output = CachedFBig<R, B>;
246            #[inline]
247            fn $op(self, rhs: &CachedFBig<R, B>) -> Self::Output {
248                FBig::<R, B>::from(self.clone()).$op(rhs)
249            }
250        }
251    };
252}
253
254macro_rules! impl_cached_binop_assign_with_primitive {
255    ($OpAssign:ident, $op_assign:ident, $Op:ident, $op:ident, $target:ty) => {
256        impl<R: Round, const B: Word> $OpAssign<$target> for CachedFBig<R, B> {
257            #[inline]
258            fn $op_assign(&mut self, rhs: $target) {
259                self.$op_assign(FBig::<R, B>::from(rhs));
260            }
261        }
262        impl<R: Round, const B: Word> $OpAssign<&$target> for CachedFBig<R, B> {
263            #[inline]
264            fn $op_assign(&mut self, rhs: &$target) {
265                self.$op_assign(FBig::<R, B>::from(rhs.clone()));
266            }
267        }
268    };
269}
270
271macro_rules! impl_cached_binop_with_primitives {
272    ($Op:ident, $op:ident $(, $t:ty)*) => {
273        $(
274            impl_cached_binop_one_way_with_primitive!($Op, $op, $t);
275            impl_cached_binop_reverse_with_primitive!($Op, $op, $t);
276        )*
277    };
278}
279
280macro_rules! impl_cached_binop_assign_with_primitives {
281    ($OpAssign:ident, $op_assign:ident, $Op:ident, $op:ident $(, $t:ty)*) => {
282        $(
283            impl_cached_binop_assign_with_primitive!($OpAssign, $op_assign, $Op, $op, $t);
284        )*
285    };
286}
287
288// Unsigned
289impl_cached_binop_with_primitives!(Add, add, u8, u16, u32, u64, u128, usize, dashu_int::UBig);
290impl_cached_binop_with_primitives!(Sub, sub, u8, u16, u32, u64, u128, usize, dashu_int::UBig);
291impl_cached_binop_with_primitives!(Mul, mul, u8, u16, u32, u64, u128, usize, dashu_int::UBig);
292impl_cached_binop_with_primitives!(Div, div, u8, u16, u32, u64, u128, usize, dashu_int::UBig);
293impl_cached_binop_with_primitives!(Rem, rem, u8, u16, u32, u64, u128, usize, dashu_int::UBig);
294
295// Signed
296impl_cached_binop_with_primitives!(Add, add, i8, i16, i32, i64, i128, isize, dashu_int::IBig);
297impl_cached_binop_with_primitives!(Sub, sub, i8, i16, i32, i64, i128, isize, dashu_int::IBig);
298impl_cached_binop_with_primitives!(Mul, mul, i8, i16, i32, i64, i128, isize, dashu_int::IBig);
299impl_cached_binop_with_primitives!(Div, div, i8, i16, i32, i64, i128, isize, dashu_int::IBig);
300impl_cached_binop_with_primitives!(Rem, rem, i8, i16, i32, i64, i128, isize, dashu_int::IBig);
301
302// Assign variants
303#[rustfmt::skip]
304impl_cached_binop_assign_with_primitives!(AddAssign, add_assign, Add, add,
305    u8, u16, u32, u64, u128, usize, dashu_int::UBig,
306    i8, i16, i32, i64, i128, isize, dashu_int::IBig);
307#[rustfmt::skip]
308impl_cached_binop_assign_with_primitives!(SubAssign, sub_assign, Sub, sub,
309    u8, u16, u32, u64, u128, usize, dashu_int::UBig,
310    i8, i16, i32, i64, i128, isize, dashu_int::IBig);
311#[rustfmt::skip]
312impl_cached_binop_assign_with_primitives!(MulAssign, mul_assign, Mul, mul,
313    u8, u16, u32, u64, u128, usize, dashu_int::UBig,
314    i8, i16, i32, i64, i128, isize, dashu_int::IBig);
315#[rustfmt::skip]
316impl_cached_binop_assign_with_primitives!(DivAssign, div_assign, Div, div,
317    u8, u16, u32, u64, u128, usize, dashu_int::UBig,
318    i8, i16, i32, i64, i128, isize, dashu_int::IBig);
319#[rustfmt::skip]
320impl_cached_binop_assign_with_primitives!(RemAssign, rem_assign, Rem, rem,
321    u8, u16, u32, u64, u128, usize, dashu_int::UBig,
322    i8, i16, i32, i64, i128, isize, dashu_int::IBig);
323
324// ---------------------------------------------------------------------------
325// Unary operators
326// ---------------------------------------------------------------------------
327
328impl<R: Round, const B: Word> Neg for CachedFBig<R, B> {
329    type Output = CachedFBig<R, B>;
330    #[inline]
331    fn neg(self) -> Self::Output {
332        CachedFBig::from_fbig(-self.fbig, &self.cache)
333    }
334}
335
336impl<R: Round, const B: Word> Neg for &CachedFBig<R, B> {
337    type Output = CachedFBig<R, B>;
338    #[inline]
339    fn neg(self) -> Self::Output {
340        CachedFBig::from_fbig(-&self.fbig, &self.cache)
341    }
342}
343
344impl<R: Round, const B: Word> Abs for CachedFBig<R, B> {
345    type Output = CachedFBig<R, B>;
346    #[inline]
347    fn abs(self) -> Self::Output {
348        CachedFBig::from_fbig(Abs::abs(self.fbig), &self.cache)
349    }
350}
351
352// ---------------------------------------------------------------------------
353// Shifts (preserve the cache handle)
354// ---------------------------------------------------------------------------
355
356impl<R: Round, const B: Word> Shl<isize> for CachedFBig<R, B> {
357    type Output = Self;
358    #[inline]
359    fn shl(self, rhs: isize) -> Self::Output {
360        CachedFBig::from_fbig(self.fbig << rhs, &self.cache)
361    }
362}
363
364impl<R: Round, const B: Word> ShlAssign<isize> for CachedFBig<R, B> {
365    #[inline]
366    fn shl_assign(&mut self, rhs: isize) {
367        self.fbig <<= rhs;
368    }
369}
370
371impl<R: Round, const B: Word> Shr<isize> for CachedFBig<R, B> {
372    type Output = Self;
373    #[inline]
374    fn shr(self, rhs: isize) -> Self::Output {
375        CachedFBig::from_fbig(self.fbig >> rhs, &self.cache)
376    }
377}
378
379impl<R: Round, const B: Word> ShrAssign<isize> for CachedFBig<R, B> {
380    #[inline]
381    fn shr_assign(&mut self, rhs: isize) {
382        self.fbig >>= rhs;
383    }
384}
385
386// ---------------------------------------------------------------------------
387// Sign multiplication (preserve the cache handle; mirrors FBig's Mul<Sign>)
388// ---------------------------------------------------------------------------
389
390impl<R: Round, const B: Word> Mul<Sign> for CachedFBig<R, B> {
391    type Output = Self;
392    #[inline]
393    fn mul(self, rhs: Sign) -> Self::Output {
394        CachedFBig::from_fbig(self.fbig * rhs, &self.cache)
395    }
396}
397
398impl<R: Round, const B: Word> Mul<CachedFBig<R, B>> for Sign {
399    type Output = CachedFBig<R, B>;
400    #[inline]
401    fn mul(self, rhs: CachedFBig<R, B>) -> Self::Output {
402        CachedFBig::from_fbig(self * rhs.fbig, &rhs.cache)
403    }
404}
405
406impl<R: Round, const B: Word> MulAssign<Sign> for CachedFBig<R, B> {
407    #[inline]
408    fn mul_assign(&mut self, rhs: Sign) {
409        self.fbig *= rhs;
410    }
411}
412
413// ---------------------------------------------------------------------------
414// Root / Euclid / Inverse traits (delegate to the inner FBig, preserve cache)
415// ---------------------------------------------------------------------------
416
417impl<R: Round, const B: Word> SquareRoot for CachedFBig<R, B> {
418    type Output = Self;
419    #[inline]
420    fn sqrt(&self) -> Self::Output {
421        CachedFBig::from_fbig(SquareRoot::sqrt(&self.fbig), &self.cache)
422    }
423}
424
425impl<R: Round, const B: Word> CubicRoot for CachedFBig<R, B> {
426    type Output = Self;
427    #[inline]
428    fn cbrt(&self) -> Self::Output {
429        CachedFBig::from_fbig(CubicRoot::cbrt(&self.fbig), &self.cache)
430    }
431}
432
433// DivEuclid — all four ownership combinations (clone refs to owned FBig, then delegate).
434impl<R: Round, const B: Word> DivEuclid<CachedFBig<R, B>> for CachedFBig<R, B> {
435    type Output = dashu_int::IBig;
436    #[inline]
437    fn div_euclid(self, rhs: CachedFBig<R, B>) -> Self::Output {
438        DivEuclid::div_euclid(self.fbig, rhs.fbig)
439    }
440}
441impl<'r, R: Round, const B: Word> DivEuclid<&'r CachedFBig<R, B>> for CachedFBig<R, B> {
442    type Output = dashu_int::IBig;
443    #[inline]
444    fn div_euclid(self, rhs: &'r CachedFBig<R, B>) -> Self::Output {
445        DivEuclid::div_euclid(self.fbig, rhs.fbig.clone())
446    }
447}
448impl<R: Round, const B: Word> DivEuclid<CachedFBig<R, B>> for &CachedFBig<R, B> {
449    type Output = dashu_int::IBig;
450    #[inline]
451    fn div_euclid(self, rhs: CachedFBig<R, B>) -> Self::Output {
452        DivEuclid::div_euclid(self.fbig.clone(), rhs.fbig)
453    }
454}
455impl<'r, R: Round, const B: Word> DivEuclid<&'r CachedFBig<R, B>> for &CachedFBig<R, B> {
456    type Output = dashu_int::IBig;
457    #[inline]
458    fn div_euclid(self, rhs: &'r CachedFBig<R, B>) -> Self::Output {
459        DivEuclid::div_euclid(self.fbig.clone(), rhs.fbig.clone())
460    }
461}
462
463// RemEuclid — all four ownership combinations (result keeps the LHS cache).
464impl<R: Round, const B: Word> RemEuclid<CachedFBig<R, B>> for CachedFBig<R, B> {
465    type Output = CachedFBig<R, B>;
466    #[inline]
467    fn rem_euclid(self, rhs: CachedFBig<R, B>) -> Self::Output {
468        CachedFBig::from_fbig(RemEuclid::rem_euclid(self.fbig, rhs.fbig), &self.cache)
469    }
470}
471impl<'r, R: Round, const B: Word> RemEuclid<&'r CachedFBig<R, B>> for CachedFBig<R, B> {
472    type Output = CachedFBig<R, B>;
473    #[inline]
474    fn rem_euclid(self, rhs: &'r CachedFBig<R, B>) -> Self::Output {
475        CachedFBig::from_fbig(RemEuclid::rem_euclid(self.fbig, rhs.fbig.clone()), &self.cache)
476    }
477}
478impl<R: Round, const B: Word> RemEuclid<CachedFBig<R, B>> for &CachedFBig<R, B> {
479    type Output = CachedFBig<R, B>;
480    #[inline]
481    fn rem_euclid(self, rhs: CachedFBig<R, B>) -> Self::Output {
482        CachedFBig::from_fbig(RemEuclid::rem_euclid(self.fbig.clone(), rhs.fbig), &self.cache)
483    }
484}
485impl<'r, R: Round, const B: Word> RemEuclid<&'r CachedFBig<R, B>> for &CachedFBig<R, B> {
486    type Output = CachedFBig<R, B>;
487    #[inline]
488    fn rem_euclid(self, rhs: &'r CachedFBig<R, B>) -> Self::Output {
489        CachedFBig::from_fbig(
490            RemEuclid::rem_euclid(self.fbig.clone(), rhs.fbig.clone()),
491            &self.cache,
492        )
493    }
494}
495
496// DivRemEuclid — all four ownership combinations (rem keeps the LHS cache).
497impl<R: Round, const B: Word> DivRemEuclid<CachedFBig<R, B>> for CachedFBig<R, B> {
498    type OutputDiv = dashu_int::IBig;
499    type OutputRem = CachedFBig<R, B>;
500    #[inline]
501    fn div_rem_euclid(self, rhs: CachedFBig<R, B>) -> (Self::OutputDiv, Self::OutputRem) {
502        let (q, r) = DivRemEuclid::div_rem_euclid(self.fbig, rhs.fbig);
503        (q, CachedFBig::from_fbig(r, &self.cache))
504    }
505}
506impl<'r, R: Round, const B: Word> DivRemEuclid<&'r CachedFBig<R, B>> for CachedFBig<R, B> {
507    type OutputDiv = dashu_int::IBig;
508    type OutputRem = CachedFBig<R, B>;
509    #[inline]
510    fn div_rem_euclid(self, rhs: &'r CachedFBig<R, B>) -> (Self::OutputDiv, Self::OutputRem) {
511        let (q, r) = DivRemEuclid::div_rem_euclid(self.fbig, rhs.fbig.clone());
512        (q, CachedFBig::from_fbig(r, &self.cache))
513    }
514}
515impl<R: Round, const B: Word> DivRemEuclid<CachedFBig<R, B>> for &CachedFBig<R, B> {
516    type OutputDiv = dashu_int::IBig;
517    type OutputRem = CachedFBig<R, B>;
518    #[inline]
519    fn div_rem_euclid(self, rhs: CachedFBig<R, B>) -> (Self::OutputDiv, Self::OutputRem) {
520        let (q, r) = DivRemEuclid::div_rem_euclid(self.fbig.clone(), rhs.fbig);
521        (q, CachedFBig::from_fbig(r, &self.cache))
522    }
523}
524impl<'r, R: Round, const B: Word> DivRemEuclid<&'r CachedFBig<R, B>> for &CachedFBig<R, B> {
525    type OutputDiv = dashu_int::IBig;
526    type OutputRem = CachedFBig<R, B>;
527    #[inline]
528    fn div_rem_euclid(self, rhs: &'r CachedFBig<R, B>) -> (Self::OutputDiv, Self::OutputRem) {
529        let (q, r) = DivRemEuclid::div_rem_euclid(self.fbig.clone(), rhs.fbig.clone());
530        (q, CachedFBig::from_fbig(r, &self.cache))
531    }
532}
533
534impl<R: Round, const B: Word> Inverse for CachedFBig<R, B> {
535    type Output = Self;
536    #[inline]
537    fn inv(self) -> Self::Output {
538        CachedFBig::from_fbig(Inverse::inv(self.fbig), &self.cache)
539    }
540}
541
542// ---------------------------------------------------------------------------
543// Sum / Product
544//
545// The value computation is delegated to FBig's impls — so `Sum` inherits the
546// correctly-rounded `precise_sum` (exact accumulate, round once). The result
547// keeps the first element's cache handle (empty iterator → `Default`).
548// ---------------------------------------------------------------------------
549
550impl<R: Round, const B: Word> Sum for CachedFBig<R, B> {
551    fn sum<I: Iterator<Item = Self>>(mut iter: I) -> Self {
552        match iter.next() {
553            Some(first) => {
554                let cache = Rc::clone(&first.cache);
555                let fbig: FBig<R, B> = core::iter::once(first.fbig)
556                    .chain(iter.map(|c| c.fbig))
557                    .sum();
558                CachedFBig::from_fbig(fbig, &cache)
559            }
560            None => Self::default(),
561        }
562    }
563}
564
565impl<'a, R: Round, const B: Word> Sum<&'a CachedFBig<R, B>> for CachedFBig<R, B> {
566    fn sum<I: Iterator<Item = &'a CachedFBig<R, B>>>(mut iter: I) -> Self {
567        match iter.next() {
568            Some(first) => {
569                let cache = Rc::clone(&first.cache);
570                let fbig: FBig<R, B> = core::iter::once(first.fbig.clone())
571                    .chain(iter.map(|c| c.fbig.clone()))
572                    .sum();
573                CachedFBig::from_fbig(fbig, &cache)
574            }
575            None => Self::default(),
576        }
577    }
578}
579
580impl<R: Round, const B: Word> Product for CachedFBig<R, B> {
581    fn product<I: Iterator<Item = Self>>(mut iter: I) -> Self {
582        match iter.next() {
583            Some(first) => {
584                let cache = Rc::clone(&first.cache);
585                let fbig: FBig<R, B> = core::iter::once(first.fbig)
586                    .chain(iter.map(|c| c.fbig))
587                    .product();
588                CachedFBig::from_fbig(fbig, &cache)
589            }
590            None => Self::default(),
591        }
592    }
593}
594
595impl<'a, R: Round, const B: Word> Product<&'a CachedFBig<R, B>> for CachedFBig<R, B> {
596    fn product<I: Iterator<Item = &'a CachedFBig<R, B>>>(mut iter: I) -> Self {
597        match iter.next() {
598            Some(first) => {
599                let cache = Rc::clone(&first.cache);
600                let fbig: FBig<R, B> = core::iter::once(first.fbig.clone())
601                    .chain(iter.map(|c| c.fbig.clone()))
602                    .product();
603                CachedFBig::from_fbig(fbig, &cache)
604            }
605            None => Self::default(),
606        }
607    }
608}
609
610// ---------------------------------------------------------------------------
611// Math functions (forward to Context / FBig, preserve cache handle)
612// ---------------------------------------------------------------------------
613
614/// Forward a unary function to a [`Context`] method returning `FpResult<FBig>`, panicking on
615/// error and re-attaching the cache handle. Returns a bare `CachedFBig`.
616macro_rules! forward_to_context {
617    ($name:ident) => {
618        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
619        #[inline]
620        pub fn $name(&self) -> CachedFBig<R, B> {
621            let mut c = self.cache.borrow_mut();
622            let fbig = self
623                .fbig
624                .context
625                .unwrap_fp(self.fbig.context.$name::<B>(&self.fbig.repr, Some(&mut *c)));
626            CachedFBig::from_fbig(fbig, &self.cache)
627        }
628    };
629}
630
631/// Forward a unary function to a [`Context`] method returning `FpResult<FBig>`, panicking on
632/// error and discarding the rounding info. Returns a bare `CachedFBig`.
633macro_rules! forward_to_context_unwrap {
634    ($name:ident) => {
635        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
636        #[inline]
637        pub fn $name(&self) -> CachedFBig<R, B> {
638            let mut c = self.cache.borrow_mut();
639            let fbig = self
640                .fbig
641                .context
642                .unwrap_fp(self.fbig.context.$name::<B>(&self.fbig.repr, Some(&mut *c)));
643            CachedFBig::from_fbig(fbig, &self.cache)
644        }
645    };
646}
647
648/// Forward a unary function that delegates to the inner [`FBig`] (no cache needed).
649macro_rules! forward_to_fbig {
650    ($name:ident) => {
651        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
652        #[inline]
653        pub fn $name(&self) -> CachedFBig<R, B> {
654            CachedFBig::from_fbig(self.fbig.clone().$name(), &self.cache)
655        }
656    };
657    ($name:ident($arg:ident: $arg_ty:ty)) => {
658        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
659        #[inline]
660        pub fn $name(&self, $arg: $arg_ty) -> CachedFBig<R, B> {
661            CachedFBig::from_fbig(self.fbig.clone().$name($arg), &self.cache)
662        }
663    };
664}
665
666impl<R: Round, const B: Word> CachedFBig<R, B> {
667    forward_to_context!(ln);
668    forward_to_context!(ln_1p);
669    forward_to_context!(exp);
670    forward_to_context!(exp_m1);
671
672    forward_to_fbig!(sqrt);
673    forward_to_fbig!(inv);
674
675    forward_to_context_unwrap!(sin);
676    forward_to_context_unwrap!(cos);
677    forward_to_context_unwrap!(tan);
678    forward_to_context_unwrap!(asin);
679    forward_to_context_unwrap!(acos);
680    forward_to_context_unwrap!(atan);
681
682    forward_to_context!(sinh);
683    forward_to_context!(cosh);
684    forward_to_context!(tanh);
685    forward_to_context!(asinh);
686    forward_to_context!(acosh);
687    forward_to_context!(atanh);
688
689    forward_to_fbig!(powi(exp: dashu_int::IBig));
690    forward_to_fbig!(sqr);
691    forward_to_fbig!(cubic);
692
693    /// `self^exp` (see [`FBig::powf`]).
694    pub fn powf(&self, exp: &Self) -> Self {
695        let context = Context::max(self.fbig.context, exp.fbig.context);
696        let mut c = self.cache.borrow_mut();
697        let fbig =
698            context.unwrap_fp(context.powf::<B>(&self.fbig.repr, &exp.fbig.repr, Some(&mut *c)));
699        Self::from_fbig(fbig, &self.cache)
700    }
701
702    /// Sine and cosine together (see [`FBig::sin_cos`]).
703    pub fn sin_cos(&self) -> (Self, Self) {
704        let mut guard = self.cache.borrow_mut();
705        let cache = Some(&mut *guard);
706        let (s, c) = self.fbig.context.sin_cos::<B>(&self.fbig.repr, cache);
707        (
708            Self::from_fbig(self.fbig.context.unwrap_fp(s), &self.cache),
709            Self::from_fbig(self.fbig.context.unwrap_fp(c), &self.cache),
710        )
711    }
712
713    /// Hyperbolic sine and cosine together (see [`FBig::sinh_cosh`]).
714    pub fn sinh_cosh(&self) -> (Self, Self) {
715        let mut guard = self.cache.borrow_mut();
716        let cache = Some(&mut *guard);
717        let (s, c) = self.fbig.context.sinh_cosh::<B>(&self.fbig.repr, cache);
718        (
719            Self::from_fbig(self.fbig.context.unwrap_fp(s), &self.cache),
720            Self::from_fbig(self.fbig.context.unwrap_fp(c), &self.cache),
721        )
722    }
723
724    /// `atan2(y, x)` (see [`FBig::atan2`]).
725    pub fn atan2(&self, x: &Self) -> Self {
726        let mut c = self.cache.borrow_mut();
727        let fbig = self.fbig.context.unwrap_fp(self.fbig.context.atan2::<B>(
728            &self.fbig.repr,
729            &x.fbig.repr,
730            Some(&mut *c),
731        ));
732        Self::from_fbig(fbig, &self.cache)
733    }
734}