1use alloc::rc::Rc;
9use core::cell::RefCell;
10use core::cmp::Ordering;
11use core::str::FromStr;
12
13use dashu_base::{AbsOrd, ConversionError, ParseError};
14use dashu_float::round::{mode, Round};
15use dashu_float::{CachedFBig, ConstCache, FBig, Repr};
16use dashu_int::{IBig, UBig, Word};
17
18use crate::cbig::CBig;
19use crate::repr::Context;
20
21pub struct CachedCBig<R: Round = mode::Zero, const B: Word = 2> {
60 pub(crate) cbig: CBig<R, B>,
61 pub(crate) cache: Rc<RefCell<ConstCache>>,
62}
63
64impl<R: Round, const B: Word> CachedCBig<R, B> {
65 #[inline]
67 pub fn new(value: CBig<R, B>, cache: Rc<RefCell<ConstCache>>) -> Self {
68 Self { cbig: value, cache }
69 }
70
71 #[inline]
74 pub fn from_parts(re: FBig<R, B>, im: FBig<R, B>) -> Self {
75 Self::new(CBig::from_parts(re, im), Rc::new(RefCell::new(ConstCache::new())))
76 }
77
78 #[inline]
81 pub(crate) fn from_cbig(cbig: CBig<R, B>, cache: &Rc<RefCell<ConstCache>>) -> Self {
82 Self {
83 cbig,
84 cache: Rc::clone(cache),
85 }
86 }
87
88 #[inline]
90 pub fn as_cbig(&self) -> &CBig<R, B> {
91 &self.cbig
92 }
93
94 #[inline]
96 pub fn into_cbig(self) -> CBig<R, B> {
97 self.cbig
98 }
99
100 #[inline]
104 pub fn cache(&self) -> impl core::ops::Deref<Target = ConstCache> + '_ {
105 self.cache.borrow()
106 }
107
108 #[inline]
112 pub fn clear_cache(&self) {
113 self.cache.borrow_mut().clear();
114 }
115
116 #[inline]
120 pub const fn context(&self) -> Context<R> {
121 self.cbig.context
122 }
123
124 #[inline]
126 pub const fn precision(&self) -> usize {
127 self.cbig.context.precision()
128 }
129
130 #[inline]
132 pub const fn re(&self) -> &Repr<B> {
133 &self.cbig.re
134 }
135
136 #[inline]
138 pub const fn im(&self) -> &Repr<B> {
139 &self.cbig.im
140 }
141
142 #[inline]
149 pub fn into_parts(self) -> (CachedFBig<R, B>, CachedFBig<R, B>) {
150 let (re, im) = self.cbig.into_parts();
151 (CachedFBig::new(re, Rc::clone(&self.cache)), CachedFBig::new(im, self.cache))
152 }
153
154 #[inline]
156 pub fn is_zero(&self) -> bool {
157 self.cbig.is_zero()
158 }
159
160 #[inline]
162 pub fn is_infinite(&self) -> bool {
163 self.cbig.is_infinite()
164 }
165
166 #[inline]
168 pub fn is_finite(&self) -> bool {
169 self.cbig.is_finite()
170 }
171}
172
173impl<R: Round, const B: Word> From<CBig<R, B>> for CachedCBig<R, B> {
178 #[inline]
179 fn from(cbig: CBig<R, B>) -> Self {
180 Self::new(cbig, Rc::new(RefCell::new(ConstCache::new())))
181 }
182}
183
184impl<R: Round, const B: Word> From<CachedCBig<R, B>> for CBig<R, B> {
185 #[inline]
186 fn from(cached: CachedCBig<R, B>) -> Self {
187 cached.into_cbig()
188 }
189}
190
191impl<R: Round, const B: Word> CBig<R, B> {
192 #[inline]
194 pub fn into_cached(self, cache: Rc<RefCell<ConstCache>>) -> CachedCBig<R, B> {
195 CachedCBig::new(self, cache)
196 }
197}
198
199impl<R: Round, const B: Word> From<FBig<R, B>> for CachedCBig<R, B> {
207 #[inline]
208 fn from(re: FBig<R, B>) -> Self {
209 CBig::from(re).into()
210 }
211}
212
213impl<R: Round, const B: Word> From<UBig> for CachedCBig<R, B> {
214 #[inline]
215 fn from(v: UBig) -> Self {
216 CBig::from(v).into()
217 }
218}
219
220impl<R: Round, const B: Word> From<IBig> for CachedCBig<R, B> {
221 #[inline]
222 fn from(v: IBig) -> Self {
223 CBig::from(v).into()
224 }
225}
226
227macro_rules! impl_from_int_for_cached_cbig {
228 ($($t:ty)*) => {$(
229 impl<R: Round, const B: Word> From<$t> for CachedCBig<R, B> {
230 #[inline]
231 fn from(value: $t) -> Self {
232 CBig::from(value).into()
233 }
234 }
235 )*};
236}
237impl_from_int_for_cached_cbig!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize);
238
239impl<R: Round> TryFrom<f32> for CachedCBig<R, 2> {
240 type Error = ConversionError;
241
242 #[inline]
243 fn try_from(value: f32) -> Result<Self, Self::Error> {
244 CBig::try_from(value).map(Self::from)
245 }
246}
247
248impl<R: Round> TryFrom<f64> for CachedCBig<R, 2> {
249 type Error = ConversionError;
250
251 #[inline]
252 fn try_from(value: f64) -> Result<Self, Self::Error> {
253 CBig::try_from(value).map(Self::from)
254 }
255}
256
257impl<R: Round, const B: Word> FromStr for CachedCBig<R, B> {
258 type Err = ParseError;
259
260 #[inline]
261 fn from_str(s: &str) -> Result<Self, ParseError> {
262 Ok(CBig::from_str(s)?.into())
263 }
264}
265
266macro_rules! impl_try_from_cached_cbig_for_int {
267 ($($t:ty)*) => {$(
268 impl<R: Round, const B: Word> TryFrom<CachedCBig<R, B>> for $t {
269 type Error = ConversionError;
270
271 #[inline]
272 fn try_from(value: CachedCBig<R, B>) -> Result<Self, Self::Error> {
273 value.cbig.try_into()
274 }
275 }
276 )*};
277}
278impl_try_from_cached_cbig_for_int!(
279 u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize UBig IBig
280);
281
282impl<R: Round, const B: Word> TryFrom<CachedCBig<R, B>> for FBig<R, B> {
283 type Error = ConversionError;
284
285 #[inline]
286 fn try_from(value: CachedCBig<R, B>) -> Result<Self, Self::Error> {
287 value.cbig.try_into()
288 }
289}
290
291impl<R: Round> TryFrom<CachedCBig<R, 2>> for f32 {
292 type Error = ConversionError;
293
294 #[inline]
295 fn try_from(value: CachedCBig<R, 2>) -> Result<Self, Self::Error> {
296 value.cbig.try_into()
297 }
298}
299
300impl<R: Round> TryFrom<CachedCBig<R, 2>> for f64 {
301 type Error = ConversionError;
302
303 #[inline]
304 fn try_from(value: CachedCBig<R, 2>) -> Result<Self, Self::Error> {
305 value.cbig.try_into()
306 }
307}
308
309impl<R: Round, const B: Word> Clone for CachedCBig<R, B> {
314 #[inline]
315 fn clone(&self) -> Self {
316 Self {
317 cbig: self.cbig.clone(),
318 cache: Rc::clone(&self.cache),
319 }
320 }
321}
322
323impl<R: Round, const B: Word> Default for CachedCBig<R, B> {
324 #[inline]
326 fn default() -> Self {
327 Self::from(CBig::default())
328 }
329}
330
331impl<R: Round, const B: Word> core::fmt::Debug for CachedCBig<R, B> {
332 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
333 core::fmt::Debug::fmt(&self.cbig, f)
334 }
335}
336
337impl<R: Round, const B: Word> core::fmt::Display for CachedCBig<R, B> {
339 #[inline]
340 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
341 core::fmt::Display::fmt(&self.cbig, f)
342 }
343}
344
345impl<R1: Round, R2: Round, const B: Word> PartialEq<CachedCBig<R2, B>> for CachedCBig<R1, B> {
350 #[inline]
351 fn eq(&self, other: &CachedCBig<R2, B>) -> bool {
352 self.cbig.re == other.cbig.re && self.cbig.im == other.cbig.im
354 }
355}
356
357impl<R: Round, const B: Word> Eq for CachedCBig<R, B> {}
358
359impl<R1: Round, R2: Round, const B: Word> PartialOrd<CachedCBig<R2, B>> for CachedCBig<R1, B> {
360 #[inline]
361 fn partial_cmp(&self, other: &CachedCBig<R2, B>) -> Option<Ordering> {
362 self.cbig.partial_cmp(&other.cbig)
363 }
364}
365
366impl<R: Round, const B: Word> Ord for CachedCBig<R, B> {
367 #[inline]
368 fn cmp(&self, other: &Self) -> Ordering {
369 self.cbig.cmp(&other.cbig)
370 }
371}
372
373impl<R: Round, const B: Word> AbsOrd for CachedCBig<R, B> {
374 #[inline]
375 fn abs_cmp(&self, other: &Self) -> Ordering {
376 AbsOrd::abs_cmp(&self.cbig, &other.cbig)
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use alloc::format;
384 use dashu_float::round::mode;
385
386 type F = FBig<mode::HalfAway, 10>;
387 type C = CBig<mode::HalfAway, 10>;
388 type CC = CachedCBig<mode::HalfAway, 10>;
389
390 fn handle() -> Rc<RefCell<ConstCache>> {
391 Rc::new(RefCell::new(ConstCache::new()))
392 }
393
394 fn cached(re: i32, im: i32, h: &Rc<RefCell<ConstCache>>) -> CC {
396 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
397 CachedCBig::new(CBig::from_parts(mk(re), mk(im)), h.clone())
398 }
399
400 fn c(re: i32, im: i32) -> C {
402 let mk = |v: i32| -> F { F::from(v).with_precision(53).value() };
403 CBig::from_parts(mk(re), mk(im))
404 }
405
406 #[test]
407 fn constructors_and_accessors() {
408 let h = handle();
409 let z = cached(3, 4, &h);
410 assert_eq!(z.as_cbig().re().significand(), &3.into());
411 assert_eq!(z.as_cbig().im().significand(), &4.into());
412 assert_eq!(z.precision(), 53);
413 assert_eq!(z.context().precision(), 53);
414 assert!(!z.is_zero());
415 assert!(!z.is_infinite());
416 assert!(z.is_finite());
417
418 assert!(CC::default().is_zero());
420 let plain = c(3, 4);
421 assert_eq!(CC::from(plain.clone()).into_cbig(), plain);
422 let fp = CC::from_parts(F::from(3), F::from(4));
423 assert_eq!(fp.as_cbig().re().significand(), &3.into());
424 }
425
426 #[test]
427 fn fmt_matches_cbig() {
428 for &(re, im) in &[
429 (0, 0),
430 (5, 0),
431 (0, 1),
432 (0, -1),
433 (1, 2),
434 (-3, -4),
435 (1, 1),
436 (2, -1),
437 ] {
438 let cval = c(re, im);
439 let ccval = cached(re, im, &handle());
440 assert_eq!(format!("{}", ccval), format!("{}", cval), "Display {re}+{im}i");
441 assert_eq!(format!("{:?}", ccval), format!("{:?}", cval), "Debug {re}+{im}i");
442 }
443 }
444
445 #[test]
446 fn fromstr_matches_cbig() {
447 for s in &["0", "1", "i", "-i", "3+4i", "-3-4i", "1+i", "2-i"] {
448 assert_eq!(CC::from_str(s).unwrap().as_cbig(), &C::from_str(s).unwrap());
449 }
450 }
451
452 #[test]
453 fn ordering_matches_cbig() {
454 let (a, b) = (c(1, 9), c(2, 0));
455 let (ca, cb) = (cached(1, 9, &handle()), cached(2, 0, &handle()));
456 assert_eq!(ca.cmp(&cb), a.cmp(&b));
457 assert_eq!(ca.partial_cmp(&cb), a.partial_cmp(&b));
458 assert_eq!(AbsOrd::abs_cmp(&ca, &cb), AbsOrd::abs_cmp(&a, &b));
459 assert!(cached(3, 4, &handle()) == cached(3, 4, &handle()));
461 assert!(cached(3, 4, &handle()) != cached(3, 5, &handle()));
462 }
463
464 #[test]
465 fn conversions_match_cbig() {
466 assert_eq!(CC::from(F::from(7)).as_cbig(), &C::from(F::from(7)));
468 assert_eq!(CC::from(UBig::from(123u32)).as_cbig(), &C::from(UBig::from(123u32)));
469 assert_eq!(CC::from(IBig::from(-456)).as_cbig(), &C::from(IBig::from(-456)));
470 assert_eq!(CC::from(7u8).as_cbig(), &C::from(7u8));
471 assert_eq!(CC::from(-9i32).as_cbig(), &C::from(-9i32));
472
473 let ccval = cached(9, 0, &handle());
475 let cval = c(9, 0);
476 assert_eq!(IBig::try_from(ccval.clone()).ok(), IBig::try_from(cval.clone()).ok());
477 assert_eq!(i32::try_from(ccval.clone()).ok(), i32::try_from(cval.clone()).ok());
478 assert_eq!(F::try_from(ccval).ok(), F::try_from(cval).ok());
479 assert_eq!(F::try_from(cached(9, 1, &handle())).err(), F::try_from(c(9, 1)).err());
481 }
482
483 #[test]
484 fn float_conversions_match_cbig() {
485 type C2 = CBig<mode::HalfAway, 2>;
487 type CC2 = CachedCBig<mode::HalfAway, 2>;
488 let cv = C2::try_from(2.5f64).unwrap();
489 let ccv = CC2::try_from(2.5f64).unwrap();
490 assert_eq!(ccv.as_cbig(), &cv);
491 assert_eq!(f64::try_from(ccv).unwrap(), 2.5f64);
492 assert!(CC2::try_from(f32::NAN).is_err());
493 }
494
495 #[test]
496 fn into_parts_shares_cache() {
497 let h = handle();
498 let z = cached(2, 3, &h);
499 let _ = z.clone().ln();
501 let (re, im) = z.into_parts();
502 assert!(re.cache().total_terms() > 0);
503 assert!(im.cache().total_terms() > 0);
504 let (cre, cim) = c(2, 3).into_parts();
506 assert_eq!(re.as_fbig(), &cre);
507 assert_eq!(im.as_fbig(), &cim);
508 }
509
510 #[test]
511 fn cache_clear_zeros_and_recomputes() {
512 let h = handle();
513 let z = cached(2, 0, &h);
514 let before = z.ln().into_cbig();
515 assert!(z.cache().total_terms() > 0);
516
517 z.clear_cache();
518 assert_eq!(z.cache().total_terms(), 0);
519 assert_eq!(z.cache().total_words(), 0);
520
521 assert_eq!(z.ln().into_cbig(), before);
523 }
524}