use std::collections::VecDeque;
use astro_float::{BigFloat, Sign};
use crate::{BASE_10_PREC, RM};
fn round_to_digit(
mut exponent: i32,
mantissa: Vec<u8>,
precision: usize,
) -> Option<(i32, Vec<u8>)> {
let mut mantissa = VecDeque::from(mantissa);
let i = match mantissa.iter().enumerate().find(|&(_, &digit)| digit != 0) {
Some((i, _)) => i,
None => return Some((exponent, Vec::from(mantissa))),
};
let rounding_digit = match mantissa.get(i + precision) {
Some(digit) => digit,
None => {
return Some((exponent, Vec::from(mantissa)));
}
};
if *rounding_digit >= 5 {
let old_len = mantissa.len();
mantissa.truncate(i + precision);
mantissa = add1_to_vec(mantissa)?;
if mantissa.len() > old_len {
exponent += 1;
}
} else {
mantissa.truncate(i + precision);
}
let mantissa = Vec::from(mantissa);
Some((exponent, mantissa))
}
fn add1_to_vec(mut digits: VecDeque<u8>) -> Option<VecDeque<u8>> {
*digits.get_mut(digits.len() - 1)? += 1;
let mut i = digits.len() - 1;
while digits[i] == 10 {
digits[i] = 0;
if i == 0 {
digits.push_front(1);
break;
} else {
digits[i - 1] += 1;
i -= 1;
}
}
Some(digits)
}
fn format_num(sign: astro_float::Sign, mantissa: &[u8], mut expt: i32) -> String {
let mut mantissa = Vec::from(mantissa);
if mantissa.is_empty() {
return "0".to_string();
}
while *mantissa.last().unwrap() == 0 {
mantissa.pop();
}
if expt.abs() > 3 {
expt -= 1;
let mut bytes: Vec<u8> = Vec::new();
if sign == Sign::Neg {
bytes.reserve(mantissa.len() + expt.abs() as usize / 10 + 3); bytes.push(b'-');
} else {
bytes.reserve(mantissa.len() + expt.abs() as usize / 10 + 2);
}
bytes.push(mantissa[0] + 48);
bytes.push(b'.');
if mantissa.len() > 1 {
for d in &mantissa[1..] {
bytes.push(d + 48);
}
} else {
bytes.push(b'0');
}
bytes.push(b'e');
if expt < 0 {
bytes.push(b'-');
}
let mut exponent_digits: Vec<u8> =
expt.abs().to_string().chars().map(|d| d as u8).collect();
bytes.append(&mut exponent_digits);
String::from_utf8(bytes).unwrap()
} else {
let mut bytes = Vec::new();
if sign == Sign::Neg {
bytes.push(b'-');
}
if expt >= mantissa.len() as i32 {
for d in &mantissa {
bytes.push(d + 48);
}
for _ in 0..(expt - mantissa.len() as i32) {
bytes.push(b'0');
}
} else if expt <= 0 {
bytes.push(b'0');
bytes.push(b'.');
for _ in 0..expt.abs() {
bytes.push(b'0');
}
for d in mantissa {
bytes.push(d + 48);
}
} else {
for d in &mantissa[0..expt as usize] {
bytes.push(d + 48);
}
bytes.push(b'.');
for d in &mantissa[expt as usize..] {
bytes.push(d + 48);
}
}
String::from_utf8(bytes).unwrap()
}
}
pub fn float_to_string(num: &BigFloat) -> String {
let (s, m, e) = num.convert_to_radix(astro_float::Radix::Dec, RM).unwrap();
let (e, m) = round_to_digit(e, m, BASE_10_PREC).unwrap();
format_num(s, &m, e)
}
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use astro_float::{BigFloat, Radix};
use crate::{
formatting::{add1_to_vec, format_num, round_to_digit},
PREC, RM,
};
#[test]
fn test_add1() {
let digits = VecDeque::from([0, 0, 0, 0, 9]);
let digits = add1_to_vec(digits).unwrap();
assert_eq!(digits, vec![0, 0, 0, 1, 0]);
let digits = VecDeque::from([0, 0, 0, 0, 5]);
let digits = add1_to_vec(digits).unwrap();
assert_eq!(digits, vec![0, 0, 0, 0, 6]);
let digits = VecDeque::from([9, 9, 9]);
let digits = add1_to_vec(digits).unwrap();
assert_eq!(digits, vec![1, 0, 0, 0]);
}
#[test]
fn test_round() {
let (_s, m, e) = BigFloat::from_f64(0.1 + 0.2, PREC)
.convert_to_radix(astro_float::Radix::Dec, RM)
.unwrap();
let (e, m) = round_to_digit(e, m, 15).unwrap();
assert_eq!(m, vec![3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(e, 0);
let (_s, m, e) = BigFloat::from_f64(0.3, PREC)
.convert_to_radix(astro_float::Radix::Dec, RM)
.unwrap();
let (e, m) = round_to_digit(e, m, 15).unwrap();
assert_eq!(m, vec![3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(e, 0);
}
#[test]
fn test_format() {
let (s, m, e) = BigFloat::from_f64(0.3, PREC)
.convert_to_radix(Radix::Dec, RM)
.unwrap();
let (e, m) = round_to_digit(e, m, 15).unwrap();
let str_num = format_num(s, &m, e);
assert_eq!(str_num, "0.3");
}
}