1use alloc::collections::BTreeMap;
8use alloc::vec;
9use alloc::vec::Vec;
10use core::fmt::{self, Display, Formatter, LowerExp, UpperExp, Write};
11use dashu_base::{DivRem, Sign, UnsignedAbs};
12use dashu_int::{fast_div::ConstDivisor, UBig, Word};
13
14use crate::rbig::{RBig, Relaxed};
15
16pub struct InExpanded<'a> {
29 sign: Sign,
30 num_abs: UBig,
31 denominator: &'a UBig,
32 radix: u8,
33}
34
35struct Expanded {
37 int_digits: Vec<u8>,
38 frac_prefix: Vec<u8>, repetend: Vec<u8>, }
41
42fn expand(num: &UBig, den: &UBig, radix: u8, max_digits: usize, track_repetend: bool) -> Expanded {
51 let (int_part, mut rem) = num.div_rem(den);
52 let int_digits: Vec<u8> = int_part
53 .to_digits(radix as Word)
54 .into_iter()
55 .map(|d| d as u8)
56 .collect();
57
58 if track_repetend {
59 let mut frac_digits: Vec<u8> = Vec::with_capacity(max_digits);
61 let mut seen: BTreeMap<UBig, usize> = BTreeMap::new();
62 let mut repetend_start: Option<usize> = None;
63
64 while frac_digits.len() < max_digits {
65 if rem.is_zero() {
66 break;
67 }
68 if let Some(&pos) = seen.get(&rem) {
69 repetend_start = Some(pos);
70 break;
71 }
72 seen.insert(rem.clone(), frac_digits.len());
73
74 let scaled = &rem * radix;
75 let (digit, new_rem) = scaled.div_rem(den);
76 rem = new_rem;
77 frac_digits.push(u8::try_from(&digit).unwrap());
79 }
80
81 let (frac_prefix, repetend) = if let Some(start) = repetend_start {
82 let repetend = frac_digits.split_off(start);
83 (frac_digits, repetend)
84 } else {
85 (frac_digits, Vec::new())
86 };
87 Expanded {
88 int_digits,
89 frac_prefix,
90 repetend,
91 }
92 } else {
93 let radix_word = radix as Word;
95 let (k, radix_k) = if radix_word == 10 {
96 DECIMAL_DIGITS_PER_WORD
97 } else {
98 digits_per_word(radix_word)
99 };
100 let den_div = ConstDivisor::new(den.clone());
101 let frac_digits = expand_frac_fast(rem, &den_div, radix_word, radix_k, k, max_digits);
102 Expanded {
103 int_digits,
104 frac_prefix: frac_digits,
105 repetend: Vec::new(),
106 }
107 }
108}
109
110const fn digits_per_word(radix: Word) -> (usize, Word) {
113 let mut k = 0usize;
114 let mut power: Word = 1;
115 while power <= Word::MAX / radix {
116 power *= radix;
117 k += 1;
118 }
119 (k, power)
120}
121
122const DECIMAL_DIGITS_PER_WORD: (usize, Word) = digits_per_word(10);
128
129fn expand_frac_fast(
135 mut rem: UBig,
136 den_div: &ConstDivisor,
137 radix: Word,
138 radix_k: Word,
139 k: usize,
140 limit: usize,
141) -> Vec<u8> {
142 let mut digits: Vec<u8> = Vec::with_capacity(limit);
143 let mut chunk: Vec<u8> = Vec::with_capacity(k); while digits.len() < limit && !rem.is_zero() {
145 let scaled = &rem * radix_k;
146 let (quot, new_rem) = scaled.div_rem(den_div);
147 rem = new_rem;
148
149 let mut word: Word = quot.try_into().unwrap();
151
152 chunk.clear();
154 for _ in 0..k {
155 chunk.push((word % radix) as u8);
156 word /= radix;
157 }
158 let take = (limit - digits.len()).min(k);
160 for i in 0..take {
161 digits.push(chunk[k - 1 - i]);
162 }
163 }
164 digits
165}
166
167fn default_precision(_radix: u8) -> usize {
172 Word::BITS as usize
173}
174
175fn propagate_carry(digits: &mut [u8], radix: u8) -> bool {
178 let carry = 1u8;
179 for d in digits.iter_mut().rev() {
180 *d += carry;
181 if *d >= radix {
182 *d -= radix;
183 } else {
184 return false;
185 }
186 }
187 true
188}
189
190fn round_and_carry(digits: &mut Vec<u8>, radix: u8, keep: usize) -> bool {
194 if digits.len() <= keep {
195 return false;
196 }
197 let extra = digits[keep];
198 digits.truncate(keep);
199 extra * 2 >= radix && propagate_carry(digits, radix)
200}
201
202impl Display for InExpanded<'_> {
203 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
204 f.write_str(self.sign.as_sign_str(f.sign_plus()))?;
205
206 if self.num_abs.is_zero() {
208 f.write_char('0')?;
209 if let Some(prec) = f.precision() {
210 if prec > 0 {
211 f.write_char('.')?;
212 for _ in 0..prec {
213 f.write_char('0')?;
214 }
215 }
216 }
217 return Ok(());
218 }
219
220 let prec = f
221 .precision()
222 .unwrap_or_else(|| default_precision(self.radix));
223 let show_repetend = f.alternate();
224
225 let max_digits = if show_repetend {
227 (prec + 1).max(128)
228 } else {
229 prec + 1
230 };
231
232 let expanded =
233 expand(&self.num_abs, self.denominator, self.radix, max_digits, show_repetend);
234
235 let mut int_digits = expanded.int_digits;
236
237 if show_repetend && !expanded.repetend.is_empty() {
239 write_digits(f, &int_digits, self.radix, false)?;
240 if !expanded.frac_prefix.is_empty() || !expanded.repetend.is_empty() {
241 f.write_char('.')?;
242 }
243 write_digits(f, &expanded.frac_prefix, self.radix, false)?;
244 f.write_char('(')?;
245 write_digits(f, &expanded.repetend, self.radix, false)?;
246 f.write_char(')')?;
247 } else {
248 let total_frac: Vec<u8> = if !expanded.repetend.is_empty() {
249 [&expanded.frac_prefix[..], &expanded.repetend[..]].concat()
250 } else {
251 expanded.frac_prefix.clone()
252 };
253
254 let mut frac_digits = total_frac;
255
256 if round_and_carry(&mut frac_digits, self.radix, prec)
257 && propagate_carry(&mut int_digits, self.radix)
258 {
259 int_digits.insert(0, 1);
260 }
261
262 write_digits(f, &int_digits, self.radix, false)?;
264
265 if !frac_digits.is_empty() || prec > 0 {
267 f.write_char('.')?;
268 let printed = frac_digits.len().min(prec);
269 write_digits(f, &frac_digits[..printed], self.radix, false)?;
270 for _ in printed..prec {
271 f.write_char('0')?;
272 }
273 }
274 }
275
276 Ok(())
277 }
278}
279
280impl LowerExp for InExpanded<'_> {
281 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
282 self.fmt_scientific(f, 'e')
283 }
284}
285
286impl UpperExp for InExpanded<'_> {
287 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
288 self.fmt_scientific(f, 'E')
289 }
290}
291
292impl InExpanded<'_> {
293 fn fmt_scientific(&self, f: &mut Formatter<'_>, exp_char: char) -> fmt::Result {
294 f.write_str(self.sign.as_sign_str(f.sign_plus()))?;
295
296 if self.num_abs.is_zero() {
297 f.write_char('0')?;
298 let prec = f.precision().unwrap_or(0);
299 if prec > 0 {
300 f.write_char('.')?;
301 for _ in 0..prec {
302 f.write_char('0')?;
303 }
304 }
305 return write!(f, "{}0", exp_char);
306 }
307
308 let prec = f
309 .precision()
310 .unwrap_or_else(|| default_precision(self.radix));
311 let exp_marker = if self.radix == 10 { exp_char } else { '@' };
312
313 let (int_part, rem) = (&self.num_abs).div_rem(self.denominator);
315
316 let exp: isize;
317 let mut significand_digits: Vec<u8>;
318
319 if !int_part.is_zero() {
320 let int_digits: Vec<u8> = int_part
322 .to_digits(self.radix as Word)
323 .into_iter()
324 .map(|d| d as u8)
325 .collect();
326 exp = int_digits.len() as isize - 1;
327 significand_digits = int_digits;
328 let need_frac = (prec + 2).saturating_sub(significand_digits.len());
330 let more = expand_fraction(rem, self.denominator, self.radix, need_frac);
331 significand_digits.extend_from_slice(&more);
332 } else {
333 let mut cur_rem = rem.clone();
335 let mut leading_zeros: isize = 0;
336 loop {
337 if cur_rem.is_zero() {
338 exp = 0;
340 significand_digits = vec![0];
341 break;
342 }
343 let scaled = &cur_rem * self.radix;
344 let (d, new_rem) = scaled.div_rem(self.denominator);
345 cur_rem = new_rem;
346 if !d.is_zero() {
347 leading_zeros += 1;
348 exp = -leading_zeros;
349 significand_digits = vec![u8::try_from(&d).unwrap()];
350 let more = expand_fraction(cur_rem, self.denominator, self.radix, prec + 1);
352 significand_digits.extend_from_slice(&more);
353 break;
354 }
355 leading_zeros += 1;
356 }
357 };
358
359 if round_and_carry(&mut significand_digits, self.radix, prec + 1) {
361 significand_digits.insert(0, 1);
362 }
363
364 let actual_exp = if significand_digits.len() > prec + 1 {
366 significand_digits.truncate(prec + 1);
368 exp + 1
369 } else {
370 exp
371 };
372
373 let upper = exp_char == 'E';
375 let first = significand_digits.first().copied().unwrap_or(0);
376 write_digit_char(f, first, self.radix, upper)?;
377
378 let rest = &significand_digits[1..];
379 if !rest.is_empty() || prec > 0 {
380 f.write_char('.')?;
381 let end = prec.min(rest.len());
382 write_digits(f, &rest[..end], self.radix, upper)?;
383 for _ in end..prec {
385 f.write_char('0')?;
386 }
387 }
388
389 write!(f, "{}{}", exp_marker, actual_exp)
390 }
391}
392
393fn expand_fraction(rem: UBig, den: &UBig, radix: u8, n: usize) -> Vec<u8> {
396 let radix_word = radix as Word;
397 let (k, radix_k) = if radix_word == 10 {
398 DECIMAL_DIGITS_PER_WORD
399 } else {
400 digits_per_word(radix_word)
401 };
402 let den_div = ConstDivisor::new(den.clone());
403 let mut digits = expand_frac_fast(rem, &den_div, radix_word, radix_k, k, n);
404 digits.resize(n, 0);
406 digits
407}
408
409fn write_digits(f: &mut Formatter<'_>, digits: &[u8], radix: u8, upper: bool) -> fmt::Result {
411 for &d in digits {
412 write_digit_char(f, d, radix, upper)?;
413 }
414 Ok(())
415}
416
417fn write_digit_char(f: &mut Formatter<'_>, digit: u8, _radix: u8, upper: bool) -> fmt::Result {
419 let ch = if digit < 10 {
420 (b'0' + digit) as char
421 } else if upper {
422 (b'A' + (digit - 10)) as char
423 } else {
424 (b'a' + (digit - 10)) as char
425 };
426 f.write_char(ch)
427}
428
429impl RBig {
430 #[inline]
447 pub fn in_expanded(&self, radix: u8) -> InExpanded<'_> {
448 assert!((2..=36).contains(&radix), "radix must be between 2 and 36");
449 InExpanded {
450 sign: self.0.numerator.sign(),
451 num_abs: self.0.numerator.clone().unsigned_abs(),
452 denominator: self.denominator(),
453 radix,
454 }
455 }
456}
457
458impl Relaxed {
459 #[inline]
463 pub fn in_expanded(&self, radix: u8) -> InExpanded<'_> {
464 assert!((2..=36).contains(&radix), "radix must be between 2 and 36");
465 InExpanded {
466 sign: self.0.numerator.sign(),
467 num_abs: self.0.numerator.clone().unsigned_abs(),
468 denominator: self.denominator(),
469 radix,
470 }
471 }
472}
473
474#[cfg(test)]
475mod tests {
476 use crate::RBig;
477 use alloc::format;
478 use core::str::FromStr;
479
480 #[test]
481 fn test_expanded_terminating() {
482 let r = RBig::from_str("1/8").unwrap(); assert_eq!(format!("{:.4}", r.in_expanded(10)), "0.1250");
485 assert_eq!(format!("{:.10}", r.in_expanded(10)), "0.1250000000");
486 let r = RBig::from_str("1/4").unwrap(); assert_eq!(format!("{:.3}", r.in_expanded(10)), "0.250");
488 }
489
490 #[test]
491 fn test_expanded_repeating() {
492 let third = RBig::from_str("1/3").unwrap();
493 assert_eq!(format!("{:.6}", third.in_expanded(10)), "0.333333");
494 assert_eq!(format!("{:#}", third.in_expanded(10)), "0.(3)");
495 assert_eq!(format!("{:#}", RBig::from_str("1/7").unwrap().in_expanded(10)), "0.(142857)");
496 assert_eq!(format!("{:#}", RBig::from_str("1/6").unwrap().in_expanded(10)), "0.1(6)");
497 }
498
499 #[test]
500 fn test_expanded_scientific() {
501 let third = RBig::from_str("1/3").unwrap();
502 assert_eq!(format!("{:.4e}", third.in_expanded(10)), "3.3333e-1");
503 let eighth = RBig::from_str("1/8").unwrap();
504 assert_eq!(format!("{:.2e}", eighth.in_expanded(10)), "1.25e-1");
505 }
506
507 #[test]
508 fn test_expanded_binary() {
509 let eighth = RBig::from_str("1/8").unwrap(); assert_eq!(format!("{:.5}", eighth.in_expanded(2)), "0.00100");
512 assert_eq!(format!("{:#}", RBig::from_str("1/3").unwrap().in_expanded(2)), "0.(01)");
513 }
514}