use std::cmp::Ordering::*;
use malachite::{Natural, Rational};
use malachite::base::num::arithmetic::traits::{Parity, Pow, Reciprocal, Sign};
use malachite::base::num::basic::traits::{Zero, One, Two};
use malachite::base::num::conversion::traits::{Digits, WrappingFrom};
use malachite::rational::arithmetic::traits::Approximate;
pub fn digits(r: &Rational, k: usize, o: &Natural) -> (bool, Vec<Natural>, Vec<Natural>, Vec<Natural>) {
let sign = r.sign() == Less;
let (mut ipart, temp) = r.to_digits(o);
ipart.reverse(); let (mut fpart, mut rpart) = temp.into_vecs();
if k == 0 || k >= ipart.len()+fpart.len()+rpart.len() { }
else if k >= ipart.len()+fpart.len() { rpart.truncate(1 + k - (ipart.len()+fpart.len()));
let next = fpart.pop().unwrap(); fpart.append(&mut rpart);
let ord = (next * Natural::TWO).cmp(o);
if ord != Less { let mut to_even = ord == Equal; for d in fpart.iter_mut().rev().chain(ipart.iter_mut().rev()) { if !to_even || d.odd() { *d += Natural::ONE; }
to_even = false;
if d == o { *d = Natural::ZERO;
continue;
}
else {
break;
}
}
if ipart[0] == Natural::ZERO { ipart.insert(0, Natural::ONE); }
}
while let Some(&Natural::ZERO) = fpart.last() { fpart.pop();
}
}
else if k >= ipart.len() { rpart.clear();
fpart.truncate(1 + k - ipart.len());
let next = fpart.pop().unwrap(); let ord = (next * Natural::TWO).cmp(o);
if ord != Less { let mut to_even = ord == Equal; for d in fpart.iter_mut().rev().chain(ipart.iter_mut().rev()) { if !to_even || d.odd() { *d += Natural::ONE; }
to_even = false;
if d == o { *d = Natural::ZERO;
continue;
}
else {
break;
}
}
if ipart[0] == Natural::ZERO { ipart.insert(0, Natural::ONE); }
}
while let Some(&Natural::ZERO) = fpart.last() { fpart.pop();
}
}
else { rpart.clear();
fpart.clear();
let mut len = ipart.len();
ipart.truncate(1 + k);
let next = ipart.pop().unwrap(); let ord = (next * Natural::TWO).cmp(o);
if ord != Less { let mut to_even = ord == Equal; for d in ipart.iter_mut().rev() { if !to_even || d.odd() { *d += Natural::ONE; }
to_even = false;
if d == o { *d = Natural::ZERO;
continue;
}
else {
break;
}
}
if ipart[0] == Natural::ZERO { ipart.insert(0, Natural::ONE); len += 1; }
}
ipart.resize(len, Natural::ZERO); }
(sign, ipart, fpart, rpart)
}
fn chr(d: &Natural) -> u8 {
let u = u8::wrapping_from(d);
if u < 10 { u + 48 } else { u + 87 }
}
pub fn nnorm(neg: bool, ipart: &[Natural], fpart: &[Natural], rpart: &[Natural], o: &Natural) -> String {
let mut res = Vec::new();
if *o > Natural::const_from(36) { res.push(b'\'');
if neg {
res.push(b'`'); }
if ipart.is_empty() {
res.push(b'0'); res.push(b' ');
}
else {
for id in ipart {
res.extend_from_slice(id.to_string().as_bytes()); res.push(b' ');
}
}
if !fpart.is_empty() {
*res.last_mut().unwrap() = b'.'; for fd in fpart {
res.extend_from_slice(fd.to_string().as_bytes()); res.push(b' ');
}
}
if !rpart.is_empty() {
if fpart.is_empty() {
res.push(b'.');
res.push(b'`');
}
else {
*res.last_mut().unwrap() = b'`'; }
for rd in rpart {
res.extend_from_slice(rd.to_string().as_bytes()); res.push(b' ');
}
}
*res.last_mut().unwrap() = b'\''; }
else { if *o > Natural::const_from(10) {
res.push(b'\''); }
if neg {
res.push(b'`'); }
if ipart.is_empty() {
res.push(b'0'); }
else {
for id in ipart {
res.push(chr(id)); }
}
if !fpart.is_empty() {
res.push(b'.'); for fd in fpart {
res.push(chr(fd)); }
}
if !rpart.is_empty() {
if fpart.is_empty() {
res.push(b'.');
}
res.push(b'`'); for rd in rpart {
res.push(chr(rd)); }
}
}
unsafe { String::from_utf8_unchecked(res) } }
pub fn nsci(neg: bool, ipart: &[Natural], fpart: &[Natural], rpart: &[Natural], o: &Natural) -> String {
if ipart.is_empty() { if let Some(pos) = fpart.iter().position(|n| *n != Natural::ZERO) { let (inew, fnew) = fpart.split_at(pos).1.split_at(1);
let mut res = nnorm(neg, inew, fnew, rpart, o);
if *o > Natural::const_from(36) {
res.pop();
res += &format!("@`{}'", pos+1);
}
else {
res += &format!("@`{}", pos+1);
}
res
}
else { let exp = fpart.len();
let mut res = nnorm(neg, ipart, &[], rpart, o);
if *o > Natural::const_from(36) {
res.pop();
res += &format!("@`{exp}'");
}
else {
res += &format!("@`{exp}");
}
res
}
}
else { let (inew, fpre) = ipart.split_at(1); let exp = fpre.len(); let mut fnew = [fpre, fpart].concat();
while let Some(&Natural::ZERO) = fnew.last() { fnew.pop();
}
let mut res = nnorm(neg, inew, &fnew, rpart, o);
if *o > Natural::const_from(36) {
res.pop();
res += &format!("@{exp}'");
}
else {
res += &format!("@{exp}");
}
res
}
}
pub fn nfrac(r: &Rational, k: usize, o: &Natural) -> String {
let (mut numer, mut denom) = r.to_numerator_and_denominator();
if k != 0 {
let max = o.pow(k as u64);
if numer > denom {
(denom, numer) = r.reciprocal().approximate(&max).to_numerator_and_denominator();
}
else {
(numer, denom) = r.approximate(&max).to_numerator_and_denominator();
}
}
let mut res = Vec::new();
let mut sign = r.sign() == Less;
for n in [numer, denom] {
if *o > Natural::const_from(10) {
res.push(b'\''); }
if sign {
res.push(b'`'); sign = false;
}
let mut digits = n.to_digits_desc(o);
if digits.is_empty() {
digits.push(Natural::ZERO);
}
if *o > Natural::const_from(36) { for d in digits {
res.extend_from_slice(d.to_string().as_bytes()); res.push(b' ');
}
*res.last_mut().unwrap() = b'\''; }
else { for d in digits {
res.push(chr(&d)); }
}
res.push(b' ');
}
*res.last_mut().unwrap() = b'/';
unsafe { String::from_utf8_unchecked(res) } }
pub fn nauto(r: &Rational, k: usize, o: &Natural) -> String {
let (neg, ipart, fpart, rpart) = digits(r, k, o);
let norm = nnorm(neg, &ipart, &fpart, &rpart, o);
let sci = nsci(neg, &ipart, &fpart, &rpart, o);
let frac = nfrac(r, k, o);
if norm.len() <= sci.len() && norm.len() <= frac.len() {
norm
}
else if sci.len() <= frac.len() {
sci
}
else {
frac
}
}