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::{mode, ErrorBounds, Round, Rounded};
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: ErrorBounds, 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 transcendental to its [`Context`] method, panicking on error and re-attaching
615/// the cache handle. The context method returns an [`FpResult`](crate::error::FpResult) whose
616/// rounding flag is dropped by
617/// [`unwrap_fp`](crate::Context::unwrap_fp); the cached wrapper never surfaces it.
618macro_rules! forward_to_context {
619    ($name:ident) => {
620        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
621        #[inline]
622        pub fn $name(&self) -> CachedFBig<R, B> {
623            let mut c = self.cache.borrow_mut();
624            let fbig = self
625                .fbig
626                .context
627                .unwrap_fp(self.fbig.context.$name::<B>(&self.fbig.repr, Some(&mut *c)));
628            CachedFBig::from_fbig(fbig, &self.cache)
629        }
630    };
631}
632
633// The ×u family: same shape as [`forward_to_context!`] plus the `u: usize` divisor argument
634// (the plain macro cannot forward extra parameters).
635macro_rules! forward_to_context_u {
636    ($name:ident) => {
637        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
638        #[inline]
639        pub fn $name(&self, u: usize) -> CachedFBig<R, B> {
640            let mut c = self.cache.borrow_mut();
641            let fbig = self.fbig.context.unwrap_fp(self.fbig.context.$name::<B>(
642                &self.fbig.repr,
643                u,
644                Some(&mut *c),
645            ));
646            CachedFBig::from_fbig(fbig, &self.cache)
647        }
648    };
649}
650
651/// Forward a unary function that delegates to the inner [`FBig`] (no cache needed).
652macro_rules! forward_to_fbig {
653    ($name:ident) => {
654        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
655        #[inline]
656        pub fn $name(&self) -> CachedFBig<R, B> {
657            CachedFBig::from_fbig(self.fbig.clone().$name(), &self.cache)
658        }
659    };
660    ($name:ident($arg:ident: $arg_ty:ty)) => {
661        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
662        #[inline]
663        pub fn $name(&self, $arg: $arg_ty) -> CachedFBig<R, B> {
664            CachedFBig::from_fbig(self.fbig.clone().$name($arg), &self.cache)
665        }
666    };
667}
668
669/// Forward a base conversion that maps `Rounded<FBig<OtherR, OtherB>>` to
670/// `Rounded<CachedFBig<...>>`, preserving the cache handle.
671macro_rules! forward_to_fbig_rounded {
672    ($name:ident, $target:ty, $method:ident) => {
673        #[doc = concat!("See [`FBig::", stringify!($name), "`].")]
674        #[inline]
675        pub fn $name(&self) -> Rounded<$target> {
676            self.fbig
677                .$method()
678                .map(|f| <$target>::from_fbig(f, &self.cache))
679        }
680    };
681}
682
683impl<R: Round, const B: Word> CachedFBig<R, B> {
684    forward_to_fbig!(inv);
685    forward_to_fbig!(sqr);
686    forward_to_fbig!(cubic);
687    forward_to_fbig!(round);
688    forward_to_fbig!(trunc);
689    forward_to_fbig!(ceil);
690    forward_to_fbig!(floor);
691    forward_to_fbig!(fract);
692    forward_to_fbig!(nth_root(n: usize));
693
694    /// Round to the nearest multiple of `BASE^exp` (see [`FBig::quantize`]).
695    pub fn quantize(&self, exp: isize) -> Rounded<Self> {
696        self.fbig
697            .quantize(exp)
698            .map(|f| Self::from_fbig(f, &self.cache))
699    }
700
701    forward_to_fbig_rounded!(to_decimal, CachedFBig<mode::HalfAway, 10>, to_decimal);
702    forward_to_fbig_rounded!(to_binary, CachedFBig<mode::Zero, 2>, to_binary);
703
704    /// Convert to another base, preserving the handle (see [`FBig::with_base`]).
705    #[allow(non_upper_case_globals)]
706    pub fn with_base<const NewB: Word>(self) -> Rounded<CachedFBig<R, NewB>> {
707        let CachedFBig { fbig, cache } = self;
708        fbig.with_base::<NewB>()
709            .map(|f| CachedFBig::from_fbig(f, &cache))
710    }
711
712    /// Convert to another base at a given precision, preserving the handle
713    /// (see [`FBig::with_base_and_precision`]).
714    #[allow(non_upper_case_globals)]
715    pub fn with_base_and_precision<const NewB: Word>(
716        self,
717        precision: usize,
718    ) -> Rounded<CachedFBig<R, NewB>> {
719        let CachedFBig { fbig, cache } = self;
720        fbig.with_base_and_precision::<NewB>(precision)
721            .map(|f| CachedFBig::from_fbig(f, &cache))
722    }
723}
724
725// Transcendentals that route through the Ziv-backed (or Ziv-dependent) Context methods require
726// `R: ErrorBounds` for their correctness guarantee.
727impl<R: ErrorBounds, const B: Word> CachedFBig<R, B> {
728    forward_to_fbig!(sqrt);
729    forward_to_fbig!(powi(exp: dashu_int::IBig));
730    forward_to_context!(ln);
731    forward_to_context!(ln_1p);
732    forward_to_context!(log2);
733    forward_to_context!(log10);
734    forward_to_context!(exp);
735    forward_to_context!(exp_m1);
736
737    forward_to_context!(sinh);
738    forward_to_context!(cosh);
739    forward_to_context!(tanh);
740    forward_to_context!(asinh);
741    forward_to_context!(acosh);
742    forward_to_context!(atanh);
743    forward_to_context!(sinh_pi);
744    forward_to_context!(cosh_pi);
745
746    forward_to_context!(sin);
747    forward_to_context!(cos);
748    forward_to_context!(tan);
749    forward_to_context!(asin);
750    forward_to_context!(acos);
751    forward_to_context!(atan);
752    forward_to_context!(sin_pi);
753    forward_to_context!(cos_pi);
754    forward_to_context!(tan_pi);
755
756    forward_to_context_u!(sin_unit);
757    forward_to_context_u!(cos_unit);
758    forward_to_context_u!(tan_unit);
759    forward_to_context_u!(asin_unit);
760    forward_to_context_u!(acos_unit);
761    forward_to_context_u!(atan_unit);
762
763    /// Fused multiply–add (see [`FBig::fma`](crate::FBig::fma)). Preserves the
764    /// LHS (`self`) cache handle; the `b`/`c` caches are dropped, matching the
765    /// binary-op convention. FMA uses no cached constant.
766    pub fn fma(&self, b: &Self, c: &Self, sign: Sign) -> Self {
767        Self::from_fbig(self.fbig.fma(&b.fbig, &c.fbig, sign), &self.cache)
768    }
769
770    /// Sine and cosine together (see [`FBig::sin_cos`]).
771    pub fn sin_cos(&self) -> (Self, Self) {
772        let mut guard = self.cache.borrow_mut();
773        let cache = Some(&mut *guard);
774        let (s, c) = self.fbig.context.sin_cos::<B>(&self.fbig.repr, cache);
775        (
776            Self::from_fbig(self.fbig.context.unwrap_fp(s), &self.cache),
777            Self::from_fbig(self.fbig.context.unwrap_fp(c), &self.cache),
778        )
779    }
780
781    /// Sine and cosine of the value multiplied by π together (see [`FBig::sin_cos_pi`]).
782    pub fn sin_cos_pi(&self) -> (Self, Self) {
783        self.sin_cos_pi_shared(2)
784    }
785
786    /// Sine and cosine of `self·2π/u` together (see [`FBig::sin_cos_unit`]).
787    pub fn sin_cos_unit(&self, u: usize) -> (Self, Self) {
788        self.sin_cos_pi_shared(u)
789    }
790
791    /// Shared body of `sin_cos_pi`/`sin_cos_unit` (the former is the u = 2 case).
792    fn sin_cos_pi_shared(&self, u: usize) -> (Self, Self) {
793        let mut guard = self.cache.borrow_mut();
794        let cache = Some(&mut *guard);
795        let (s, c) = self
796            .fbig
797            .context
798            .sin_cos_unit::<B>(&self.fbig.repr, u, cache);
799        (
800            Self::from_fbig(self.fbig.context.unwrap_fp(s), &self.cache),
801            Self::from_fbig(self.fbig.context.unwrap_fp(c), &self.cache),
802        )
803    }
804
805    /// `atan2_unit(y, x, u)` (see [`FBig::atan2_unit`]).
806    pub fn atan2_unit(&self, x: &Self, u: usize) -> Self {
807        let mut c = self.cache.borrow_mut();
808        let fbig = self
809            .fbig
810            .context
811            .unwrap_fp(self.fbig.context.atan2_unit::<B>(
812                &self.fbig.repr,
813                &x.fbig.repr,
814                u,
815                Some(&mut *c),
816            ));
817        Self::from_fbig(fbig, &self.cache)
818    }
819
820    /// `atan2(y, x)` (see [`FBig::atan2`]).
821    pub fn atan2(&self, x: &Self) -> Self {
822        let mut c = self.cache.borrow_mut();
823        let fbig = self.fbig.context.unwrap_fp(self.fbig.context.atan2::<B>(
824            &self.fbig.repr,
825            &x.fbig.repr,
826            Some(&mut *c),
827        ));
828        Self::from_fbig(fbig, &self.cache)
829    }
830
831    /// `self^exp` (see [`FBig::powf`]).
832    pub fn powf(&self, exp: &Self) -> Self {
833        let context = Context::max(self.fbig.context, exp.fbig.context);
834        let mut c = self.cache.borrow_mut();
835        let fbig =
836            context.unwrap_fp(context.powf::<B>(&self.fbig.repr, &exp.fbig.repr, Some(&mut *c)));
837        Self::from_fbig(fbig, &self.cache)
838    }
839
840    /// Hyperbolic sine and cosine together (see [`FBig::sinh_cosh`]).
841    pub fn sinh_cosh(&self) -> (Self, Self) {
842        let mut guard = self.cache.borrow_mut();
843        let cache = Some(&mut *guard);
844        let (s, c) = self.fbig.context.sinh_cosh::<B>(&self.fbig.repr, cache);
845        (
846            Self::from_fbig(self.fbig.context.unwrap_fp(s), &self.cache),
847            Self::from_fbig(self.fbig.context.unwrap_fp(c), &self.cache),
848        )
849    }
850
851    /// Hyperbolic sine and cosine of the value multiplied by π together
852    /// (see [`FBig::sinh_cosh_pi`]).
853    pub fn sinh_cosh_pi(&self) -> (Self, Self) {
854        let mut guard = self.cache.borrow_mut();
855        let cache = Some(&mut *guard);
856        let (s, c) = self.fbig.context.sinh_cosh_pi::<B>(&self.fbig.repr, cache);
857        (
858            Self::from_fbig(self.fbig.context.unwrap_fp(s), &self.cache),
859            Self::from_fbig(self.fbig.context.unwrap_fp(c), &self.cache),
860        )
861    }
862
863    /// The Euclidean norm `sqrt(x²+y²)` (see [`FBig::hypot`]).
864    pub fn hypot(&self, other: &Self) -> Self {
865        Self::from_fbig(self.fbig.hypot(&other.fbig), &self.cache)
866    }
867}