use crate::prelude::*;
pub fn f64_equal(target: f64, num: usize, denom: usize) -> bool {
let fnum = num as f64;
let fdenom = denom as f64;
let lower = 2.0f64.mul_add(fnum, -1.0) / (2.0 * fdenom);
let upper = 2.0f64.mul_add(fnum, 1.0) / (2.0 * fdenom);
(lower..upper).contains(&target)
}
pub fn f64_greater(target: f64, num: usize, denom: usize) -> bool {
let fnum = num as f64;
let fdenom = denom as f64;
let lower = 2.0f64.mul_add(fnum, -1.0) / (2.0 * fdenom);
target > lower
}
pub fn f64_less(target: f64, num: usize, denom: usize) -> bool {
let fnum = num as f64;
let fdenom = denom as f64;
let upper = 2.0f64.mul_add(fnum, 1.0) / (2.0 * fdenom);
target < upper
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum JunkType {
Any,
Trailing,
None,
}
impl Default for JunkType {
fn default() -> Self {
Self::Any
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum JunkVal {
Min,
Max,
}
impl Default for JunkVal {
fn default() -> Self {
Self::Max
}
}
pub trait JunkValue {
fn val(j: JunkVal) -> Self;
}
macro_rules! jv {
($e:ty) => {
impl JunkValue for $e {
fn val(j: JunkVal) -> Self {
if j == JunkVal::Min {
Self::MIN
} else {
Self::MAX
}
}
}
};
}
jv!(f64);
jv!(i64);
jv!(u64);
jv!(isize);
jv!(usize);
#[derive(Debug, Default, Copy, Clone)]
pub struct Junk {
pub junk_type: JunkType,
pub junk_val: JunkVal,
}
impl Junk {
pub fn val<T: JunkValue>(&self) -> T {
T::val(self.junk_val)
}
}
pub fn fcmp(x: f64, y: f64) -> Ordering {
if x == y {
return Ordering::Equal;
}
if x > y {
return Ordering::Greater;
}
Ordering::Less
}
pub fn ulp_to_ulong(d: f64) -> u64 {
let x = u64::from_ne_bytes(d.to_ne_bytes());
if (x & 0x8000000000000000u64) != 0 {
x ^ 0xffffffffffffffffu64
} else {
x | 0x8000000000000000u64
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum NumFormat {
Short(Option<usize>),
Plain(Option<usize>),
Float(Option<usize>),
Power2,
Power10,
}
impl Default for NumFormat {
fn default() -> Self {
Self::Short(None)
}
}
impl NumFormat {
pub fn new(spec: &str) -> Result<Self> {
if spec.eq_ignore_ascii_case("plain") {
Ok(Self::Plain(None))
} else if spec.eq_ignore_ascii_case("short") {
Ok(Self::Short(None))
} else if spec.eq_ignore_ascii_case("float") {
Ok(Self::Float(None))
} else if spec.eq_ignore_ascii_case("power2") || spec.eq_ignore_ascii_case("p2") {
Ok(Self::Power2)
} else if spec.eq_ignore_ascii_case("power10") || spec.eq_ignore_ascii_case("p10") {
Ok(Self::Power10)
} else if let Some((a, b)) = spec.split_once('.') {
let p = b.to_usize_whole(spec.as_bytes(), "Num Format")?;
if a.eq_ignore_ascii_case("plain") {
Ok(Self::Plain(Some(p)))
} else if a.eq_ignore_ascii_case("short") {
Ok(Self::Short(Some(p)))
} else if a.eq_ignore_ascii_case("float") {
Ok(Self::Float(Some(p)))
} else {
err!("Number format must be short[.N], plain[.N], float[.N], power2 or power10")
}
} else {
err!("Number format must be short[.N], plain[.N], float[.N], power2 or power10")
}
}
fn new_format(self, num: f64) -> Self {
match self {
NumFormat::Short(x) => {
let num = num.abs();
if (0.0001..1000.0).contains(&num) {
Self::Plain(x)
} else {
Self::Float(x)
}
}
_ => self,
}
}
pub fn print(self, mut num: f64, mut w: impl Write) -> Result<()> {
let nfmt = self.new_format(num);
if let NumFormat::Plain(n) = nfmt {
if let Some(prec) = n {
write!(w, "{num:.0$}", prec)?;
} else {
write!(w, "{num}")?;
}
return Ok(());
}
if let NumFormat::Float(n) = nfmt {
if let Some(prec) = n {
write!(w, "{num:.0$e}", prec)?;
} else {
write!(w, "{num:e}")?;
}
return Ok(());
}
num = num.round();
if num.abs() < 1000.0 || num.is_nan() || num.is_infinite() {
write!(w, "{num}")?;
return Ok(());
}
let sign = if num < 0.0 {
num = -num;
"-"
} else {
""
};
let (exp, letters) = if nfmt == Self::Power2 {
(1024.0, P2_LETTERS)
} else {
(1000.0, P10_LETTERS)
};
let mut curr_exp: f64 = 1.0;
let mut exp_num: usize = 0;
loop {
if num < (999.0 * curr_exp) {
if num >= (10.0 * curr_exp) {
write!(
w,
"{}{:.0}{}",
sign,
num / curr_exp,
letters[exp_num] as char
)?;
} else if num >= (1.1 * curr_exp) {
write!(
w,
"{}{:.1}{}",
sign,
num / curr_exp,
letters[exp_num] as char
)?;
} else {
write!(w, "{}1{}", sign, letters[exp_num] as char)?;
}
return Ok(());
}
exp_num += 1;
curr_exp *= exp;
if exp_num >= 8 {
write!(w, "{}{:.1}{}", sign, num / curr_exp, letters[8] as char)?;
return Ok(());
}
}
}
}
const P2_LETTERS: &[u8] = b"0KMGTPEZY";
const P10_LETTERS: &[u8] = b"0kmgtpezy";
const P2_VALUES_U: [usize; 9] = [
1,
1024,
1024 ^ 2,
1024 ^ 3,
1024 ^ 4,
1024 ^ 5,
1024 ^ 6,
usize::MAX,
usize::MAX,
];
const P2_VALUES_F: [f64; 9] = [
1.0,
1024.0,
1024.0 * 1024.0,
1024.0 * 1024.0 * 1024.0,
1024.0 * 1024.0 * 1024.0 * 1024.0,
1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0,
1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0,
1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0,
1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0,
];
const P10_VALUES_U: [usize; 9] = [
1,
1000,
1000 ^ 2,
1000 ^ 3,
1000 ^ 4,
1000 ^ 5,
1000 ^ 6,
usize::MAX,
usize::MAX,
];
const P10_VALUES_F: [f64; 9] = [
1.0,
1000.0,
1000.0 * 1000.0,
1000.0 * 1000.0 * 1000.0,
1000.0 * 1000.0 * 1000.0 * 1000.0,
1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0,
1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0,
1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0,
1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0 * 1000.0,
];
pub fn suffix_valf(ch: u8) -> Option<f64> {
for (i, x) in P2_LETTERS.iter().enumerate() {
if *x == ch {
return Some(P2_VALUES_F[i]);
}
}
for (i, x) in P10_LETTERS.iter().enumerate() {
if *x == ch {
return Some(P10_VALUES_F[i]);
}
}
None
}
pub fn suffix_valu(ch: u8) -> Option<usize> {
for (i, x) in P2_LETTERS.iter().enumerate() {
if *x == ch {
return Some(P2_VALUES_U[i]);
}
}
for (i, x) in P10_LETTERS.iter().enumerate() {
if *x == ch {
return Some(P10_VALUES_U[i]);
}
}
None
}