use alloc::collections::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;
use core::fmt::{self, Display, Formatter, LowerExp, UpperExp, Write};
use dashu_base::{DivRem, Sign, UnsignedAbs};
use dashu_int::{UBig, Word};
use crate::rbig::{RBig, Relaxed};
pub struct InExpanded<'a> {
sign: Sign,
num_abs: UBig,
denominator: &'a UBig,
radix: u8,
}
struct Expanded {
int_digits: Vec<u8>,
frac_prefix: Vec<u8>, repetend: Vec<u8>, }
fn expand(num: &UBig, den: &UBig, radix: u8, max_digits: usize, track_repetend: bool) -> Expanded {
let (int_part, mut rem) = num.div_rem(den);
let int_digits = ubig_to_digits(&int_part, radix);
let mut frac_digits: Vec<u8> = Vec::with_capacity(max_digits);
let mut seen: Option<BTreeMap<UBig, usize>> = if track_repetend {
Some(BTreeMap::new())
} else {
None
};
let mut repetend_start: Option<usize> = None;
while frac_digits.len() < max_digits {
if rem.is_zero() {
break;
}
if let Some(ref mut map) = seen {
if let Some(&pos) = map.get(&rem) {
repetend_start = Some(pos);
break;
}
map.insert(rem.clone(), frac_digits.len());
}
let scaled = &rem * radix;
let (digit, new_rem) = scaled.div_rem(den);
rem = new_rem;
frac_digits.push(u8::try_from(&digit).unwrap());
}
let (frac_prefix, repetend) = if let Some(start) = repetend_start {
let repetend = frac_digits.split_off(start);
(frac_digits, repetend)
} else {
(frac_digits, Vec::new())
};
Expanded {
int_digits,
frac_prefix,
repetend,
}
}
fn ubig_to_digits(n: &UBig, radix: u8) -> Vec<u8> {
let mut digits = Vec::new();
let mut cur = n.clone();
let radix_ubig = UBig::from(radix);
while !cur.is_zero() {
let (q, d) = cur.div_rem(&radix_ubig);
digits.push(u8::try_from(&d).unwrap());
cur = q;
}
if digits.is_empty() {
digits.push(0);
}
digits.reverse();
digits
}
fn default_precision(_radix: u8) -> usize {
Word::BITS as usize
}
fn propagate_carry(digits: &mut [u8], radix: u8) -> bool {
let carry = 1u8;
for d in digits.iter_mut().rev() {
*d += carry;
if *d >= radix {
*d -= radix;
} else {
return false;
}
}
true
}
fn round_and_carry(digits: &mut Vec<u8>, radix: u8, keep: usize) -> bool {
if digits.len() <= keep {
return false;
}
let extra = digits[keep];
digits.truncate(keep);
extra * 2 >= radix && propagate_carry(digits, radix)
}
impl Display for InExpanded<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.sign.as_sign_str(f.sign_plus()))?;
if self.num_abs.is_zero() {
f.write_char('0')?;
if let Some(prec) = f.precision() {
if prec > 0 {
f.write_char('.')?;
for _ in 0..prec {
f.write_char('0')?;
}
}
}
return Ok(());
}
let prec = f
.precision()
.unwrap_or_else(|| default_precision(self.radix));
let show_repetend = f.alternate();
let max_digits = if show_repetend {
(prec + 1).max(128)
} else {
prec + 1
};
let expanded =
expand(&self.num_abs, self.denominator, self.radix, max_digits, show_repetend);
let mut int_digits = expanded.int_digits;
if show_repetend && !expanded.repetend.is_empty() {
write_digits(f, &int_digits, self.radix, false)?;
if !expanded.frac_prefix.is_empty() || !expanded.repetend.is_empty() {
f.write_char('.')?;
}
write_digits(f, &expanded.frac_prefix, self.radix, false)?;
f.write_char('(')?;
write_digits(f, &expanded.repetend, self.radix, false)?;
f.write_char(')')?;
} else {
let total_frac: Vec<u8> = if !expanded.repetend.is_empty() {
[&expanded.frac_prefix[..], &expanded.repetend[..]].concat()
} else {
expanded.frac_prefix.clone()
};
let mut frac_digits = total_frac;
if round_and_carry(&mut frac_digits, self.radix, prec)
&& propagate_carry(&mut int_digits, self.radix)
{
int_digits.insert(0, 1);
}
write_digits(f, &int_digits, self.radix, false)?;
if !frac_digits.is_empty() || prec > 0 {
f.write_char('.')?;
let printed = frac_digits.len().min(prec);
write_digits(f, &frac_digits[..printed], self.radix, false)?;
for _ in printed..prec {
f.write_char('0')?;
}
}
}
Ok(())
}
}
impl LowerExp for InExpanded<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_scientific(f, 'e')
}
}
impl UpperExp for InExpanded<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_scientific(f, 'E')
}
}
impl InExpanded<'_> {
fn fmt_scientific(&self, f: &mut Formatter<'_>, exp_char: char) -> fmt::Result {
f.write_str(self.sign.as_sign_str(f.sign_plus()))?;
if self.num_abs.is_zero() {
f.write_char('0')?;
let prec = f.precision().unwrap_or(0);
if prec > 0 {
f.write_char('.')?;
for _ in 0..prec {
f.write_char('0')?;
}
}
return write!(f, "{}0", exp_char);
}
let prec = f
.precision()
.unwrap_or_else(|| default_precision(self.radix));
let exp_marker = if self.radix == 10 { exp_char } else { '@' };
let (int_part, rem) = (&self.num_abs).div_rem(self.denominator);
let exp: isize;
let mut significand_digits: Vec<u8>;
if !int_part.is_zero() {
let int_digits = ubig_to_digits(&int_part, self.radix);
exp = int_digits.len() as isize - 1;
significand_digits = int_digits;
let need_frac = (prec + 2).saturating_sub(significand_digits.len());
let more = expand_fraction(rem, self.denominator, self.radix, need_frac);
significand_digits.extend_from_slice(&more);
} else {
let mut cur_rem = rem.clone();
let mut leading_zeros: isize = 0;
loop {
if cur_rem.is_zero() {
exp = 0;
significand_digits = vec![0];
break;
}
let scaled = &cur_rem * self.radix;
let (d, new_rem) = scaled.div_rem(self.denominator);
cur_rem = new_rem;
if !d.is_zero() {
leading_zeros += 1;
exp = -leading_zeros;
significand_digits = vec![u8::try_from(&d).unwrap()];
let more = expand_fraction(cur_rem, self.denominator, self.radix, prec + 1);
significand_digits.extend_from_slice(&more);
break;
}
leading_zeros += 1;
}
};
if round_and_carry(&mut significand_digits, self.radix, prec + 1) {
significand_digits.insert(0, 1);
}
let actual_exp = if significand_digits.len() > prec + 1 {
significand_digits.truncate(prec + 1);
exp + 1
} else {
exp
};
let upper = exp_char == 'E';
let first = significand_digits.first().copied().unwrap_or(0);
write_digit_char(f, first, self.radix, upper)?;
let rest = &significand_digits[1..];
if !rest.is_empty() || prec > 0 {
f.write_char('.')?;
let end = prec.min(rest.len());
write_digits(f, &rest[..end], self.radix, upper)?;
for _ in end..prec {
f.write_char('0')?;
}
}
write!(f, "{}{}", exp_marker, actual_exp)
}
}
fn expand_fraction(mut rem: UBig, den: &UBig, radix: u8, n: usize) -> Vec<u8> {
let mut digits = Vec::with_capacity(n);
for _ in 0..n {
if rem.is_zero() {
digits.push(0);
} else {
let scaled = &rem * radix;
let (digit, new_rem) = scaled.div_rem(den);
rem = new_rem;
digits.push(u8::try_from(&digit).unwrap());
}
}
digits
}
fn write_digits(f: &mut Formatter<'_>, digits: &[u8], radix: u8, upper: bool) -> fmt::Result {
for &d in digits {
write_digit_char(f, d, radix, upper)?;
}
Ok(())
}
fn write_digit_char(f: &mut Formatter<'_>, digit: u8, _radix: u8, upper: bool) -> fmt::Result {
let ch = if digit < 10 {
(b'0' + digit) as char
} else if upper {
(b'A' + (digit - 10)) as char
} else {
(b'a' + (digit - 10)) as char
};
f.write_char(ch)
}
impl RBig {
#[inline]
pub fn in_expanded(&self, radix: u8) -> InExpanded<'_> {
assert!((2..=36).contains(&radix), "radix must be between 2 and 36");
InExpanded {
sign: self.0.numerator.sign(),
num_abs: self.0.numerator.clone().unsigned_abs(),
denominator: self.denominator(),
radix,
}
}
}
impl Relaxed {
#[inline]
pub fn in_expanded(&self, radix: u8) -> InExpanded<'_> {
assert!((2..=36).contains(&radix), "radix must be between 2 and 36");
InExpanded {
sign: self.0.numerator.sign(),
num_abs: self.0.numerator.clone().unsigned_abs(),
denominator: self.denominator(),
radix,
}
}
}