use std::fmt;
use num_traits::sign::Signed;
use super::ir::Int;
use super::ir::Ty;
use super::ir::Val;
use super::Base::*;
use super::Num;
const MAX_FRAC_DIGITS: usize = 64;
impl fmt::Display for Num {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt(f, Dec)
}
}
impl fmt::LowerHex for Num {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt(f, Hex)
}
}
impl fmt::Binary for Num {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt(f, Bin)
}
}
impl fmt::Octal for Num {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.fmt(f, Oct)
}
}
impl Num {
fn fmt<F: fmt::Write>(&self, f: &mut F, base: super::Base) -> fmt::Result {
match base {
super::Base::Dec => self.fmt_dec(f),
super::Base::Bin | super::Base::Oct | super::Base::Hex => {
f.write_char('0')?;
f.write_char(base.prefix())?;
self.fmt_pow2(f, base)
}
}
}
fn fmt_dec<F: std::fmt::Write>(&self, f: &mut F) -> std::fmt::Result {
if self.negative() && self.ty.signed == Some(false) {
f.write_str("INF")
} else {
if self.negative() {
f.write_char('-')?;
}
let int = format!("{}", self.val.to_integer());
fmt_group(f, &int[int.starts_with('-').into()..], 3)?;
FracRepr::new(&self.val.fract().abs(), 10, 0, Some(MAX_FRAC_DIGITS)).fmt(f, 3)
}
}
fn fmt_pow2<F: std::fmt::Write>(&self, f: &mut F, base: super::Base) -> std::fmt::Result {
const GROUP_SIZE: usize = 4;
let bits_digit = u64::from(base.bits_per_digit());
if self.signed() {
return self.to_unsigned().fmt_pow2(f, base);
}
assert_eq!(self.ty.signed, Some(false));
if self.negative() {
assert_eq!(self.ty.width, None);
f.write_fmt(format_args!("({})", base.max_digit()))?;
let int = self.val.to_integer().abs();
let pos = int > 0.into();
let w = if self.val.fract() == Val::default() {
&int - Int::from(1)
} else {
int
}
.bits();
let w = (((w + (bits_digit - 1)) / bits_digit) * bits_digit)
.try_into()
.unwrap();
if w > 0 && pos {
let mut i = self.clone().with_ty(Ty::default().w(w).wf(0));
if self.val.fract() != Val::default() {
i.val -= Val::from_integer(1.into());
}
i.fmt_pow2(f, base)?;
}
} else {
let int = self.val.to_integer();
let max_digits = self
.ty
.width
.map(|w| (u64::from(w) + bits_digit - 1) / bits_digit);
let actual_digits = u64::max((int.bits() + bits_digit - 1) / bits_digit, 1);
let padding = max_digits.map_or(0, |m| m.saturating_sub(actual_digits));
let int: String = std::iter::repeat('0')
.take(padding as usize)
.chain(int.to_str_radix(base.radix().into()).chars())
.collect();
fmt_group(f, &int, GROUP_SIZE)?;
}
let frac_min_width = u64::from(self.ty.width_frac.unwrap_or(0));
let frac_min_digits = (frac_min_width + bits_digit - 1) / bits_digit;
FracRepr::new(
&self.val.fract(),
base.radix().into(),
frac_min_digits as usize,
Some(MAX_FRAC_DIGITS),
)
.fmt(f, GROUP_SIZE)
}
}
fn fmt_group<F: std::fmt::Write>(f: &mut F, s: &str, size: usize) -> std::fmt::Result {
let mut first = true;
for g in s.as_bytes().rchunks(size).rev() {
let g = std::str::from_utf8(g).unwrap();
if first {
first = false;
} else {
f.write_char('_')?;
}
f.write_str(g)?;
}
Ok(())
}
pub struct FracRepr {
digits: Vec<u8>,
non_repeating: usize,
radix: u32,
truncated: bool,
}
impl FracRepr {
fn new(frac: &Val, radix: u32, min_digits: usize, max_digits: Option<usize>) -> FracRepr {
assert!(frac.abs() < Val::from_integer(1.into()));
let frac = if frac < &Val::from_integer(0.into()) {
Val::from_integer(1.into()) + frac
} else {
frac.clone()
};
let mut digits = Vec::new();
let mut non_repeating = None;
let mut truncated = false;
let mut denom = Int::from(1);
let mut rem = frac.numer().clone();
let mut rems = Vec::new();
while rem > 0.into() {
if max_digits.map_or(false, |m| digits.len() >= m) {
truncated = true;
break;
}
denom *= Int::from(radix);
rems.push(rem.clone());
let numerc = &denom * &rem;
if frac.denom() <= &numerc {
let digit = &numerc / frac.denom();
rem = &numerc % frac.denom();
denom = Int::from(1);
digits.push(digit.try_into().unwrap());
if let Some(nr) = rems.iter().position(|x| x == &rem) {
non_repeating = Some(nr);
break;
}
} else {
digits.push(0);
}
}
if non_repeating.is_none() && digits.len() < min_digits {
digits.resize_with(min_digits, || 0);
}
let non_repeating = if let Some(mut nr) = non_repeating {
while nr > 0 && digits.last() == digits.get(nr - 1) {
digits.pop();
nr -= 1;
}
nr
} else {
digits.len()
};
Self {
digits,
non_repeating,
radix,
truncated,
}
}
fn fmt<F: std::fmt::Write>(&self, f: &mut F, group_size: usize) -> std::fmt::Result {
if self.digits.is_empty() {
Ok(())
} else {
f.write_char('.')?;
let mut pos = 0;
while pos < self.non_repeating {
let len = usize::min(self.non_repeating - pos, group_size);
let chunk = &self.digits[pos..pos + len];
for d in chunk {
f.write_char(char::from_digit(u32::from(*d), self.radix).unwrap())?;
}
pos += len;
if pos < self.non_repeating {
f.write_char('_')?;
}
}
if self.non_repeating < self.digits.len() {
f.write_char('(')?;
for d in &self.digits[self.non_repeating..] {
f.write_char(char::from_digit(u32::from(*d), self.radix).unwrap())?;
}
f.write_char(')')?;
} else if self.truncated {
f.write_str("..")?;
}
Ok(())
}
}
}
#[cfg(test)]
mod test {
use crate::Base::*;
use crate::Num;
macro_rules! test_fmt {
($n:expr, $b:expr, $expect:literal) => {
let mut s = String::new();
$n.fmt(&mut s, $b).unwrap();
assert_eq!(s, $expect);
};
}
#[test]
fn fmt() {
assert_eq!(format!("{}", Num::n(10)), "10");
assert_eq!(format!("{:b}", Num::n(10)), "0b1010");
assert_eq!(format!("{:o}", Num::n(10)), "0o12");
assert_eq!(format!("{:x}", Num::n(10)), "0xa");
}
#[test]
fn fmt_bin() {
test_fmt!(Num::u(0b0), Bin, "0b0");
test_fmt!(Num::u(0b1010), Bin, "0b1010");
test_fmt!(Num::u(0b00_1010).w(6), Bin, "0b00_1010");
test_fmt!(Num::u(0b0000_1010).w(8), Bin, "0b0000_1010");
test_fmt!(Num::i(-4).w(4), Bin, "0b1100");
}
#[test]
fn fmt_oct() {
test_fmt!(Num::u(0o377), Oct, "0o377");
test_fmt!(Num::u(0o01).w(6), Oct, "0o01");
test_fmt!(Num::u(0o01).w(7), Oct, "0o001");
test_fmt!(Num::i(-2).w(6), Oct, "0o76");
test_fmt!(Num::u(0o7_7777_7777), Oct, "0o7_7777_7777");
test_fmt!(Num::u(0o077_7777).w(21), Oct, "0o077_7777");
}
#[test]
fn fmt_hex() {
test_fmt!(Num::u(0xff), Hex, "0xff");
test_fmt!(Num::u(0x01).w(8), Hex, "0x01");
test_fmt!(Num::u(0x01).w(9), Hex, "0x001");
test_fmt!(Num::i(-2).w(8), Hex, "0xfe");
test_fmt!(Num::u(0x7fff_ffff).w(31), Hex, "0x7fff_ffff");
}
#[test]
fn fmt_dec() {
test_fmt!(Num::u(0), Dec, "0");
test_fmt!(Num::u(8), Dec, "8");
test_fmt!(Num::i(-1), Dec, "-1");
test_fmt!(Num::i(-150), Dec, "-150");
test_fmt!(Num::u(-1), Dec, "INF");
test_fmt!(Num::i(-1).w(4), Dec, "-1");
test_fmt!(Num::u(1).w(4), Dec, "1");
test_fmt!(Num::u(8_239_847), Dec, "8_239_847");
test_fmt!(Num::u(239_847), Dec, "239_847");
}
#[test]
fn fmt_bin_inf() {
test_fmt!(Num::i(-1), Bin, "0b(1)");
test_fmt!(Num::i(-2), Bin, "0b(1)0");
test_fmt!(Num::i(-3), Bin, "0b(1)01");
test_fmt!(Num::u(-3), Bin, "0b(1)01");
test_fmt!(Num::i(-4), Bin, "0b(1)00");
test_fmt!(Num::i(-234), Bin, "0b(1)0001_0110");
}
#[test]
fn fmt_hex_inf() {
test_fmt!(Num::i(-1), Hex, "0x(f)");
test_fmt!(Num::i(-2), Hex, "0x(f)e");
test_fmt!(Num::i(-3), Hex, "0x(f)d");
test_fmt!(Num::i(-234), Hex, "0x(f)16");
}
#[test]
fn fmt_bin_fix() {
test_fmt!(Num::nd(1, 10).wf(12), Bin, "0b0.0001_1001_1001");
test_fmt!(Num::n(5).w(6).wf(7), Bin, "0b00_0101.0000_000");
test_fmt!(Num::nd(1, 5), Bin, "0b0.(0011)");
test_fmt!(Num::nd(1, 10), Bin, "0b0.0(0011)");
test_fmt!(Num::nd(1, 1600), Bin, "0b0.0000_00(00001010001111010111)");
test_fmt!(Num::uq(0b1010_01, 0b1_00).w(0), Bin, "0b0.01");
}
#[test]
fn fmt_dec_fix() {
test_fmt!(Num::nd(-3, 4), Dec, "-0.75");
test_fmt!(Num::nd(5, 2), Dec, "2.5");
test_fmt!(Num::nd(-7, 5), Dec, "-1.4");
test_fmt!(Num::nd(1_234_567, 1_000_000), Dec, "1.234_567");
test_fmt!(Num::nd(4, 9), Dec, "0.(4)");
test_fmt!(Num::nd(1, 81), Dec, "0.(012345679)");
test_fmt!(Num::uq(5, 2).w(0), Dec, "0.5");
}
#[test]
fn fmt_hex_fix() {
test_fmt!(Num::nd(13, 9), Hex, "0x1.(71c)");
}
#[test]
fn fmt_bin_fix_inf() {
test_fmt!(Num::nd(-1, 4), Bin, "0b(1).11");
test_fmt!(Num::nd(-3, 4), Bin, "0b(1).01");
test_fmt!(Num::nd(-5, 2), Bin, "0b(1)01.1");
}
#[test]
fn fmt_oct_fix_inf() {
test_fmt!(Num::nd(-3, 4), Oct, "0o(7).2");
test_fmt!(Num::q(-3, 4), Oct, "0o(7).2");
test_fmt!(Num::uq(-3, 4), Oct, "0o(7).2");
}
#[test]
fn fmt_hex_fix_inf() {
test_fmt!(Num::nd(-23, 4), Hex, "0x(f)a.4");
test_fmt!(Num::nd(-5, 2), Hex, "0x(f)d.8");
test_fmt!(Num::nd(-13, 9), Hex, "0x(f)e.(8e3)");
test_fmt!(Num::nd(-5, 12), Hex, "0x(f).9(5)");
}
#[test]
fn fmt_truncate() {
test_fmt!(
Num::nd(1, 1 << super::MAX_FRAC_DIGITS),
Bin,
"0b0.0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0001"
);
test_fmt!(
Num::nd(1, 1 << (super::MAX_FRAC_DIGITS + 1)),
Bin,
"0b0.0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000.."
);
test_fmt!(
Num::nd(1, 1000),
Bin,
"0b0.0000_0000_0100_0001_1000_1001_0011_0111_0100_1011_1100_0110_1010_0111_1110_1111.."
);
}
}