use indicatif::{ProgressBar, ProgressStyle};
use rust_decimal::prelude::*;
use crate::{cli::RoundingStrategyWrapper, plot::axis::Scale};
pub mod compact_float {
use rust_decimal::prelude::*;
use rust_decimal::RoundingStrategy;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S>(float: &f64, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let parsed = Decimal::from_f64_retain(*float)
.and_then(|d| {
d.round_sf_with_strategy(3, RoundingStrategy::MidpointAwayFromZero)
.map(|rounded| rounded.normalize())
})
.and_then(|d| d.to_f64())
.unwrap_or(*float);
serializer.serialize_f64(parsed)
}
pub fn serialize_option<S>(float: &Option<f64>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match float {
Some(value) => serialize(value, serializer),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
f64::deserialize(deserializer)
}
}
pub fn format_si(value: &f64, digits: u32, rounding: Option<RoundingStrategyWrapper>) -> String {
fn set_suffix(thousands: i8) -> String {
const POSITIVE: [&str; 9] = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"];
const NEGATIVE: [&str; 8] = ["", "μ", "p", "n", "f", "a", "z", "y"];
let suffix = if (-8..0).contains(&thousands) {
NEGATIVE[-thousands as usize]
} else if (0..=9).contains(&thousands) {
POSITIVE[thousands as usize]
} else {
""
};
suffix.to_string()
}
let magnitude = if *value == 0.0 {
0
} else {
(*value).log10() as i8
};
let thousands = magnitude / 3;
let prefix = if thousands < 0 {
*value * 10u32.pow(3 * (thousands.unsigned_abs() as u32 + 1)) as f64
} else {
*value / 10u32.pow(3 * thousands as u32) as f64
};
let d = Decimal::from_f64_retain(prefix).unwrap();
let rounding_strategy = match rounding {
Some(RoundingStrategyWrapper::MidpointAwayFromZero) => {
RoundingStrategy::MidpointAwayFromZero
}
Some(RoundingStrategyWrapper::ToZero) => RoundingStrategy::ToZero,
Some(RoundingStrategyWrapper::AwayFromZero) => RoundingStrategy::AwayFromZero,
_ => RoundingStrategy::MidpointAwayFromZero,
};
let mut rounded = d
.round_sf_with_strategy(digits, rounding_strategy)
.unwrap()
.normalize()
.to_string();
if thousands == 0 && rounded.starts_with("0.") {
let rounded_value = (rounded.parse::<f64>().unwrap() * 1000.0).round() / 1000.0;
let digits_usize = &(digits as usize - 1);
rounded = format!("{:.digits_usize$}", rounded_value);
}
let suffix = set_suffix(thousands);
format!("{}{}", rounded, suffix)
}
pub fn format_pct(value: &f64, digits: u32, rounding: Option<RoundingStrategyWrapper>) -> String {
let mut rounded = Decimal::from_f64_retain(*value).unwrap();
let rounding_strategy = match rounding {
Some(RoundingStrategyWrapper::MidpointAwayFromZero) => {
RoundingStrategy::MidpointAwayFromZero
}
Some(RoundingStrategyWrapper::ToZero) => RoundingStrategy::ToZero,
Some(RoundingStrategyWrapper::AwayFromZero) => RoundingStrategy::AwayFromZero,
_ => RoundingStrategy::MidpointAwayFromZero,
};
rounded = rounded
.round_dp_with_strategy(digits, rounding_strategy)
.normalize();
format!("{}%", rounded)
}
pub fn indexed_sort<T: Ord>(list: &[T]) -> Vec<usize> {
let mut indices = (0..list.len()).collect::<Vec<_>>();
indices.sort_by_key(|&i| &list[i]);
indices.reverse();
indices
}
pub fn styled_bytes_progress_bar(total: u64, message: &str) -> ProgressBar {
let progress_bar = ProgressBar::new(total);
let format_string = format!(
"[+]\t{}: {{bar:40.cyan/blue}} {{bytes:>10}}/{{total_bytes:12}} ({{eta}})",
message
);
let pb_style_result = ProgressStyle::with_template(format_string.as_str());
let pb_style = match pb_style_result {
Ok(style) => style,
Err(error) => panic!("Problem with the progress bar: {:?}", error),
};
progress_bar.set_style(pb_style);
progress_bar
}
pub fn styled_progress_bar(total: usize, message: &str) -> ProgressBar {
let progress_bar = ProgressBar::new(total as u64);
let format_string = format!(
"[+]\t{}: {{bar:40.cyan/blue}} {{pos:>7}}/{{len:12}}",
message
);
let pb_style_result = ProgressStyle::with_template(format_string.as_str());
let pb_style = match pb_style_result {
Ok(style) => style,
Err(error) => panic!("Problem with the progress bar: {:?}", error),
};
progress_bar.set_style(pb_style);
progress_bar
}
pub fn linear_scale(value: usize, domain: &[usize; 2], range: &[f64; 2]) -> f64 {
let proportion = (value - domain[0]) as f64 / (domain[1] - domain[0]) as f64;
(range[1] - range[0]) * proportion + range[0]
}
pub fn linear_scale_float(value: f64, domain: &[f64; 2], range: &[f64; 2]) -> f64 {
let proportion = (value - domain[0]) / (domain[1] - domain[0]);
(range[1] - range[0]) * proportion + range[0]
}
pub fn log_scale(value: usize, domain: &[usize; 2], range: &[f64; 2]) -> f64 {
let proportion = ((value as f64).log10() - (domain[0] as f64).log10())
/ ((domain[1] as f64).log10() - (domain[0] as f64).log10());
(range[1] - range[0]) * proportion + range[0]
}
pub fn log_scale_float(value: f64, domain: &[f64; 2], range: &[f64; 2]) -> f64 {
let proportion = (value.log10() - domain[0].log10()) / (domain[1].log10() - domain[0].log10());
(range[1] - range[0]) * proportion + range[0]
}
pub fn sqrt_scale(value: usize, domain: &[usize; 2], range: &[f64; 2]) -> f64 {
let proportion = ((value as f64).sqrt() - (domain[0] as f64).sqrt())
/ ((domain[1] as f64).sqrt() - (domain[0] as f64).sqrt());
(range[1] - range[0]) * proportion + range[0]
}
pub fn sqrt_scale_float(value: f64, domain: &[f64; 2], range: &[f64; 2]) -> f64 {
let proportion = (value.sqrt() - domain[0].sqrt()) / (domain[1].sqrt() - domain[0].sqrt());
(range[1] - range[0]) * proportion + range[0]
}
pub fn scale_float(
value: f64,
domain: &[f64; 2],
range: &[f64; 2],
scale_type: &String,
clamp: Option<f64>,
) -> f64 {
let scale_log = &String::from("scaleLog");
let scale_sqrt = &String::from("scaleSqrt");
if let Some(clamp_value) = clamp {
if value < clamp_value {
return clamp_value;
}
}
if scale_type == scale_log {
log_scale_float(value, domain, range)
} else if scale_type == scale_sqrt {
sqrt_scale_float(value, domain, range)
} else {
linear_scale_float(value, domain, range)
}
}
pub fn scale_floats(
value: f64,
domain: &[f64; 2],
range: &[f64; 2],
scale_type: &Scale,
clamp: Option<f64>,
) -> f64 {
if let Some(clamp_value) = clamp {
if value < clamp_value {
return match scale_type {
Scale::LINEAR => linear_scale_float(clamp_value, domain, range),
Scale::SQRT => sqrt_scale_float(clamp_value, domain, range),
Scale::LOG => log_scale_float(clamp_value, domain, range),
};
}
}
match scale_type {
Scale::LINEAR => linear_scale_float(value, domain, range),
Scale::SQRT => sqrt_scale_float(value, domain, range),
Scale::LOG => log_scale_float(value, domain, range),
}
}
pub fn max_float<T: PartialOrd>(a: T, b: T) -> T {
if b > a {
b
} else {
a
}
}
pub fn min_float<T: PartialOrd>(a: T, b: T) -> T {
if a < b {
a
} else {
b
}
}
pub fn format_element_id(title: &str) -> String {
title
.to_lowercase()
.replace(' ', "_")
.chars()
.filter(|c| c.is_alphanumeric() || *c == '_')
.collect::<String>()
}