Continue work on separating complex types

This commit is contained in:
Austen Adler 2021-05-30 23:02:32 -04:00
parent c47287b4e6
commit dab0333b31

View File

@ -8,25 +8,25 @@ use std::fmt;
#[derive(Copy, Clone, Debug, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Number { pub struct Number {
pub value: f64, pub value: f64,
} }
impl PartialEq for Number { impl PartialEq for Number {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
if self.value.is_nan() && other.value.is_nan() if self.value.is_nan() && other.value.is_nan()
|| self.value.is_infinite() && other.value.is_infinite() || self.value.is_infinite() && other.value.is_infinite()
{ {
true true
} else if self.value.is_nan() } else if self.value.is_nan()
|| self.value.is_infinite() || self.value.is_infinite()
|| other.value.is_infinite() || other.value.is_infinite()
|| other.value.is_nan() || other.value.is_nan()
{ {
false false
} else { } else {
(self.value - other.value).abs() >= f64::EPSILON (self.value - other.value).abs() >= f64::EPSILON
}
} }
}
} }
// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] // #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
@ -34,400 +34,418 @@ impl PartialEq for Number {
// pub value: Vec<Number>, // pub value: Vec<Number>,
// } // }
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum Entry { pub enum Entry {
Number(Number), Number(Number),
// Vector(Vec<Number>), // Vector(Vec<Number>),
// Matrix(Vec<Vec<Number>>), // Matrix(Vec<Vec<Number>>),
} }
impl CalculatorEntry for Entry { impl CalculatorEntry for Entry {
fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String {
match self { match self {
Self::Number(number) => number.format_entry(display_mode), Self::Number(number) => number.format_entry(display_mode),
// Self::Vector(vector) => vector.add(), // Self::Vector(vector) => vector.add(),
}
} }
} fn is_valid(&self) -> bool {
fn is_valid(&self) -> bool { match self {
match self { Self::Number(number) => number.is_valid(),
Self::Number(number) => number.is_valid(), // Self::Vector(vector) => vector.add(),
// Self::Vector(vector) => vector.add(), }
} }
} fn add(&self, arg: Self) -> CalculatorResult<Self> {
fn add(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.add(arg),
Self::Number(number) => number.add(arg), // Self::Vector(vector) => vector.add(),
// Self::Vector(vector) => vector.add(), }
} }
} fn sub(&self, arg: Self) -> CalculatorResult<Self> {
fn sub(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.sub(arg),
Self::Number(number) => number.sub(arg), // Self::Vector(vector) => vector.sub(),
// Self::Vector(vector) => vector.sub(), }
} }
} fn mul(&self, arg: Self) -> CalculatorResult<Self> {
fn mul(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.mul(arg),
Self::Number(number) => number.mul(arg), // Self::Vector(vector) => vector.mul(),
// Self::Vector(vector) => vector.mul(), }
} }
} fn div(&self, arg: Self) -> CalculatorResult<Self> {
fn div(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.div(arg),
Self::Number(number) => number.div(arg), // Self::Vector(vector) => vector.div(),
// Self::Vector(vector) => vector.div(), }
} }
} fn int_divide(&self, arg: Self) -> CalculatorResult<Self> {
fn int_divide(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.int_divide(arg),
Self::Number(number) => number.int_divide(arg), // Self::Vector(vector) => vector.int_divide(),
// Self::Vector(vector) => vector.int_divide(), }
} }
} fn negate(&self) -> CalculatorResult<Self> {
fn negate(&self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.negate(),
Self::Number(number) => number.negate(), // Self::Vector(vector) => vector.negate(),
// Self::Vector(vector) => vector.negate(), }
} }
} fn abs(&self) -> CalculatorResult<Self> {
fn abs(&self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.abs(),
Self::Number(number) => number.abs(), // Self::Vector(vector) => vector.abs(),
// Self::Vector(vector) => vector.abs(), }
} }
} fn inverse(&self) -> CalculatorResult<Self> {
fn inverse(&self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.inverse(),
Self::Number(number) => number.inverse(), // Self::Vector(vector) => vector.inverse(),
// Self::Vector(vector) => vector.inverse(), }
} }
} fn modulo(&self, arg: Self) -> CalculatorResult<Self> {
fn modulo(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.modulo(arg),
Self::Number(number) => number.modulo(arg), // Self::Vector(vector) => vector.modulo(),
// Self::Vector(vector) => vector.modulo(), }
} }
} fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.sin(angle_mode),
Self::Number(number) => number.sin(angle_mode), // Self::Vector(vector) => vector.sin(),
// Self::Vector(vector) => vector.sin(), }
} }
} fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.cos(angle_mode),
Self::Number(number) => number.cos(angle_mode), // Self::Vector(vector) => vector.cos(),
// Self::Vector(vector) => vector.cos(), }
} }
} fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.tan(angle_mode),
Self::Number(number) => number.tan(angle_mode), // Self::Vector(vector) => vector.tan(),
// Self::Vector(vector) => vector.tan(), }
} }
} fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.asin(angle_mode),
Self::Number(number) => number.asin(angle_mode), // Self::Vector(vector) => vector.asin(),
// Self::Vector(vector) => vector.asin(), }
} }
} fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.acos(angle_mode),
Self::Number(number) => number.acos(angle_mode), // Self::Vector(vector) => vector.acos(),
// Self::Vector(vector) => vector.acos(), }
} }
} fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.atan(angle_mode),
Self::Number(number) => number.atan(angle_mode), // Self::Vector(vector) => vector.atan(),
// Self::Vector(vector) => vector.atan(), }
} }
} fn sqrt(&self) -> CalculatorResult<Self> {
fn sqrt(&self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.sqrt(),
Self::Number(number) => number.sqrt(), // Self::Vector(vector) => vector.sqrt(),
// Self::Vector(vector) => vector.sqrt(), }
} }
} fn log(&self) -> CalculatorResult<Self> {
fn log(&self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.log(),
Self::Number(number) => number.log(), // Self::Vector(vector) => vector.log(),
// Self::Vector(vector) => vector.log(), }
} }
} fn ln(&self) -> CalculatorResult<Self> {
fn ln(&self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.ln(),
Self::Number(number) => number.ln(), // Self::Vector(vector) => vector.ln(),
// Self::Vector(vector) => vector.ln(), }
} }
} fn pow(&self, arg: Self) -> CalculatorResult<Self> {
fn pow(&self, arg: Self) -> CalculatorResult<Self> { match self {
match self { Self::Number(number) => number.pow(arg),
Self::Number(number) => number.pow(arg), // Self::Vector(vector) => vector.pow(),
// Self::Vector(vector) => vector.pow(), }
} }
}
} }
impl CalculatorEntry for Number { impl CalculatorEntry for Number {
fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String {
match display_mode { match display_mode {
CalculatorDisplayMode::Default => format!("{}", self.value), CalculatorDisplayMode::Default => format!("{}", self.value),
CalculatorDisplayMode::Separated { separator } => separated(self.value, *separator), CalculatorDisplayMode::Separated { separator } => {
CalculatorDisplayMode::Scientific { precision } => scientific(self.value, *precision), separated(self.value, *separator)
CalculatorDisplayMode::Engineering { precision } => engineering(self.value, *precision), }
CalculatorDisplayMode::Fixed { precision } => { CalculatorDisplayMode::Scientific { precision } => {
format!("{:0>.precision$}", self.value, precision = precision) scientific(self.value, *precision)
} }
CalculatorDisplayMode::Engineering { precision } => {
engineering(self.value, *precision)
}
CalculatorDisplayMode::Fixed { precision } => {
format!("{:0>.precision$}", self.value, precision = precision)
}
}
} }
} fn is_valid(&self) -> bool {
fn is_valid(&self) -> bool { !self.value.is_nan() && !self.value.is_infinite()
!self.value.is_nan() && !self.value.is_infinite()
}
fn add(&self, arg: Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Number(Number { value }) => Ok(Entry::Number(Number {
value: value + self.value,
})),
} }
} fn add(&self, arg: Entry) -> CalculatorResult<Entry> {
fn sub(&self, arg: Entry) -> CalculatorResult<Entry> { match arg {
match arg { Entry::Number(Number { value }) => Ok(Entry::Number(Number {
Entry::Number(Number { value }) => Ok(Entry::Number(Number { value: value + self.value,
value: value - self.value, })),
})), }
} }
} fn sub(&self, arg: Entry) -> CalculatorResult<Entry> {
fn mul(&self, arg: Entry) -> CalculatorResult<Entry> { match arg {
match arg { Entry::Number(Number { value }) => Ok(Entry::Number(Number {
Entry::Number(Number { value }) => Ok(Entry::Number(Number { value: value - self.value,
value: value * self.value, })),
})), }
} }
} fn mul(&self, arg: Entry) -> CalculatorResult<Entry> {
fn div(&self, arg: Entry) -> CalculatorResult<Entry> { match arg {
match arg { Entry::Number(Number { value }) => Ok(Entry::Number(Number {
Entry::Number(Number { value }) => Ok(Entry::Number(Number { value: value * self.value,
value: value / self.value, })),
})), }
} }
} fn div(&self, arg: Entry) -> CalculatorResult<Entry> {
fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry> { match arg {
match arg { Entry::Number(Number { value }) => Ok(Entry::Number(Number {
Entry::Number(Number { value }) => Ok(Entry::Number(Number { value: value / self.value,
value: value.div_euclid(self.value), })),
})), }
} }
} fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry> {
fn negate(&self) -> CalculatorResult<Entry> { match arg {
Ok(Entry::Number(Number { value: -self.value })) Entry::Number(Number { value }) => Ok(Entry::Number(Number {
} value: value.div_euclid(self.value),
fn abs(&self) -> CalculatorResult<Entry> { })),
Ok(Entry::Number(Number { }
value: self.value.abs(),
}))
}
fn inverse(&self) -> CalculatorResult<Entry> {
Ok(Entry::Number(Number {
value: self.value.recip(),
}))
}
fn modulo(&self, arg: Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Number(Number { value }) => Ok(Entry::Number(Number {
value: value % self.value,
})),
} }
} fn negate(&self) -> CalculatorResult<Entry> {
fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> { Ok(Entry::Number(Number { value: -self.value }))
Ok(Entry::Number(Number { }
value: match angle_mode { fn abs(&self) -> CalculatorResult<Entry> {
CalculatorAngleMode::Degrees => self.value.to_radians().sin(), Ok(Entry::Number(Number {
CalculatorAngleMode::Radians => self.value.sin(), value: self.value.abs(),
CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).sin(), }))
}, }
})) fn inverse(&self) -> CalculatorResult<Entry> {
} Ok(Entry::Number(Number {
fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> { value: self.value.recip(),
Ok(Entry::Number(Number { }))
value: match angle_mode { }
CalculatorAngleMode::Degrees => self.value.to_radians().cos(), fn modulo(&self, arg: Entry) -> CalculatorResult<Entry> {
CalculatorAngleMode::Radians => self.value.cos(), match arg {
CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).cos(), Entry::Number(Number { value }) => Ok(Entry::Number(Number {
}, value: value % self.value,
})) })),
} }
fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> { }
Ok(Entry::Number(Number { fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
value: match angle_mode { Ok(Entry::Number(Number {
CalculatorAngleMode::Degrees => self.value.to_radians().tan(), value: match angle_mode {
CalculatorAngleMode::Radians => self.value.tan(), CalculatorAngleMode::Degrees => self.value.to_radians().sin(),
CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).tan(), CalculatorAngleMode::Radians => self.value.sin(),
}, CalculatorAngleMode::Grads => {
})) (self.value * std::f64::consts::PI / 200.0).sin()
} }
fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> { },
Ok(Entry::Number(Number { }))
value: match angle_mode { }
CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
CalculatorAngleMode::Radians => self.value.asin(), Ok(Entry::Number(Number {
CalculatorAngleMode::Grads => self.value.asin() * std::f64::consts::PI / 200.0, value: match angle_mode {
}, CalculatorAngleMode::Degrees => self.value.to_radians().cos(),
})) CalculatorAngleMode::Radians => self.value.cos(),
} CalculatorAngleMode::Grads => {
fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> { (self.value * std::f64::consts::PI / 200.0).cos()
Ok(Entry::Number(Number { }
value: match angle_mode { },
CalculatorAngleMode::Degrees => self.value.acos().to_degrees(), }))
CalculatorAngleMode::Radians => self.value.acos(), }
CalculatorAngleMode::Grads => self.value.acos() * std::f64::consts::PI / 200.0, fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
}, Ok(Entry::Number(Number {
})) value: match angle_mode {
} CalculatorAngleMode::Degrees => self.value.to_radians().tan(),
fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> { CalculatorAngleMode::Radians => self.value.tan(),
Ok(Entry::Number(Number { CalculatorAngleMode::Grads => {
value: match angle_mode { (self.value * std::f64::consts::PI / 200.0).tan()
CalculatorAngleMode::Degrees => self.value.atan().to_degrees(), }
CalculatorAngleMode::Radians => self.value.atan(), },
CalculatorAngleMode::Grads => self.value.atan() * std::f64::consts::PI / 200.0, }))
}, }
})) fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
} Ok(Entry::Number(Number {
fn sqrt(&self) -> CalculatorResult<Entry> { value: match angle_mode {
Ok(Entry::Number(Number { CalculatorAngleMode::Degrees => self.value.asin().to_degrees(),
value: self.value.sqrt(), CalculatorAngleMode::Radians => self.value.asin(),
})) CalculatorAngleMode::Grads => {
} self.value.asin() * std::f64::consts::PI / 200.0
fn log(&self) -> CalculatorResult<Entry> { }
Ok(Entry::Number(Number { },
value: self.value.log10(), }))
})) }
} fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
fn ln(&self) -> CalculatorResult<Entry> { Ok(Entry::Number(Number {
Ok(Entry::Number(Number { value: match angle_mode {
value: self.value.ln(), CalculatorAngleMode::Degrees => self.value.acos().to_degrees(),
})) CalculatorAngleMode::Radians => self.value.acos(),
} CalculatorAngleMode::Grads => {
fn pow(&self, arg: Entry) -> CalculatorResult<Entry> { self.value.acos() * std::f64::consts::PI / 200.0
match arg { }
Entry::Number(Number { value }) => Ok(Entry::Number(Number { },
value: value.powf(self.value), }))
})), }
fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
Ok(Entry::Number(Number {
value: match angle_mode {
CalculatorAngleMode::Degrees => self.value.atan().to_degrees(),
CalculatorAngleMode::Radians => self.value.atan(),
CalculatorAngleMode::Grads => {
self.value.atan() * std::f64::consts::PI / 200.0
}
},
}))
}
fn sqrt(&self) -> CalculatorResult<Entry> {
Ok(Entry::Number(Number {
value: self.value.sqrt(),
}))
}
fn log(&self) -> CalculatorResult<Entry> {
Ok(Entry::Number(Number {
value: self.value.log10(),
}))
}
fn ln(&self) -> CalculatorResult<Entry> {
Ok(Entry::Number(Number {
value: self.value.ln(),
}))
}
fn pow(&self, arg: Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Number(Number { value }) => Ok(Entry::Number(Number {
value: value.powf(self.value),
})),
}
} }
}
} }
pub trait CalculatorEntry { pub trait CalculatorEntry {
// Misc // Misc
fn is_valid(&self) -> bool; fn is_valid(&self) -> bool;
fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String;
// Mathematical operations // Mathematical operations
fn add(&self, arg: Entry) -> CalculatorResult<Entry>; fn add(&self, arg: Entry) -> CalculatorResult<Entry>;
fn sub(&self, arg: Entry) -> CalculatorResult<Entry>; fn sub(&self, arg: Entry) -> CalculatorResult<Entry>;
fn mul(&self, arg: Entry) -> CalculatorResult<Entry>; fn mul(&self, arg: Entry) -> CalculatorResult<Entry>;
fn div(&self, arg: Entry) -> CalculatorResult<Entry>; fn div(&self, arg: Entry) -> CalculatorResult<Entry>;
fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry>; fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry>;
fn negate(&self) -> CalculatorResult<Entry>; fn negate(&self) -> CalculatorResult<Entry>;
fn abs(&self) -> CalculatorResult<Entry>; fn abs(&self) -> CalculatorResult<Entry>;
fn inverse(&self) -> CalculatorResult<Entry>; fn inverse(&self) -> CalculatorResult<Entry>;
fn modulo(&self, arg: Entry) -> CalculatorResult<Entry>; fn modulo(&self, arg: Entry) -> CalculatorResult<Entry>;
fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>; fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>;
fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>; fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>;
fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>; fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>;
fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>; fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>;
fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>; fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>;
fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>; fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry>;
fn sqrt(&self) -> CalculatorResult<Entry>; fn sqrt(&self) -> CalculatorResult<Entry>;
fn log(&self) -> CalculatorResult<Entry>; fn log(&self) -> CalculatorResult<Entry>;
fn ln(&self) -> CalculatorResult<Entry>; fn ln(&self) -> CalculatorResult<Entry>;
fn pow(&self, arg: Entry) -> CalculatorResult<Entry>; fn pow(&self, arg: Entry) -> CalculatorResult<Entry>;
} }
impl fmt::Display for Entry { impl fmt::Display for Entry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Self::Number(Number { value }) => write!(f, "{}", value), Self::Number(Number { value }) => write!(f, "{}", value),
}
} }
}
} }
impl fmt::Display for Number { impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self) write!(f, "{}", self)
} }
} }
// Based on https://stackoverflow.com/a/65266882 // Based on https://stackoverflow.com/a/65266882
fn scientific(f: f64, precision: usize) -> String { fn scientific(f: f64, precision: usize) -> String {
let mut ret = format!("{:.precision$E}", f, precision = precision); let mut ret = format!("{:.precision$E}", f, precision = precision);
let exp = ret.split_off(ret.find('E').unwrap_or(0)); let exp = ret.split_off(ret.find('E').unwrap_or(0));
let (exp_sign, exp) = exp let (exp_sign, exp) = exp
.strip_prefix("E-") .strip_prefix("E-")
.map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped)); .map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped));
let sign = if ret.starts_with('-') { "" } else { " " }; let sign = if ret.starts_with('-') { "" } else { " " };
format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2)
} }
fn engineering(f: f64, precision: usize) -> String { fn engineering(f: f64, precision: usize) -> String {
// Format the string so the first digit is always in the first column, and remove '.'. Requested precision + 2 to account for using 1, 2, or 3 digits for the whole portion of the string // Format the string so the first digit is always in the first column, and remove '.'. Requested precision + 2 to account for using 1, 2, or 3 digits for the whole portion of the string
// 1,000 => 1000E3 // 1,000 => 1000E3
let all = format!(" {:.precision$E}", f, precision = precision) let all = format!(" {:.precision$E}", f, precision = precision)
// Remove . since it can be moved // Remove . since it can be moved
.replacen(".", "", 1) .replacen(".", "", 1)
// Add 00E before E here so the length is enough for slicing below // Add 00E before E here so the length is enough for slicing below
.replacen("E", "00E", 1); .replacen("E", "00E", 1);
// Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E // Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E
// 1000E3 => (1000, E3) // 1000E3 => (1000, E3)
let (num_str, exp_str) = all.split_at(all.find('E').unwrap()); let (num_str, exp_str) = all.split_at(all.find('E').unwrap());
// Extract the exponent as an isize. This should always be true because Entry max will be ~400 // Extract the exponent as an isize. This should always be true because Entry max will be ~400
// E3 => 3 as isize // E3 => 3 as isize
let exp = exp_str[1..].parse::<isize>().unwrap(); let exp = exp_str[1..].parse::<isize>().unwrap();
// Sign of the exponent. If string representation starts with E-, then negative // Sign of the exponent. If string representation starts with E-, then negative
let display_exp_sign = if exp_str.strip_prefix("E-").is_some() { let display_exp_sign = if exp_str.strip_prefix("E-").is_some() {
'-' '-'
} else { } else {
'+' '+'
}; };
// The exponent to display. Always a multiple of 3 in engineering mode. Always positive because sign is added with display_exp_sign above // The exponent to display. Always a multiple of 3 in engineering mode. Always positive because sign is added with display_exp_sign above
// 100 => 0, 1000 => 3, .1 => 3 (but will show as -3) // 100 => 0, 1000 => 3, .1 => 3 (but will show as -3)
let display_exp = (exp.div_euclid(3) * 3).abs(); let display_exp = (exp.div_euclid(3) * 3).abs();
// Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility // Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility
let num_whole_digits = exp.rem_euclid(3) as usize + 1; let num_whole_digits = exp.rem_euclid(3) as usize + 1;
// If this is a negative number, strip off the added space, otherwise keep the space (and next digit) // If this is a negative number, strip off the added space, otherwise keep the space (and next digit)
let num_str = if num_str.strip_prefix(" -").is_some() { let num_str = if num_str.strip_prefix(" -").is_some() {
&num_str[1..] &num_str[1..]
} else { } else {
num_str num_str
}; };
// Whole portion of number. Slice is safe because the num_whole_digits is always 3 and the num_str will always have length >= 3 since precision in all=2 (+original whole digit) // Whole portion of number. Slice is safe because the num_whole_digits is always 3 and the num_str will always have length >= 3 since precision in all=2 (+original whole digit)
// Original number is 1,000 => whole will be 1, if original is 0.01, whole will be 10 // Original number is 1,000 => whole will be 1, if original is 0.01, whole will be 10
let whole = &num_str[0..=num_whole_digits]; let whole = &num_str[0..=num_whole_digits];
// Decimal portion of the number. Sliced from the number of whole digits to the *requested* precision. Precision generated in all will be requested precision + 2 // Decimal portion of the number. Sliced from the number of whole digits to the *requested* precision. Precision generated in all will be requested precision + 2
let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)]; let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)];
// Right align whole portion, always have decimal point // Right align whole portion, always have decimal point
format!( format!(
"{: >4}.{} E{}{:0>pad$}", "{: >4}.{} E{}{:0>pad$}",
// display_sign, // display_sign,
whole, whole,
decimal, decimal,
display_exp_sign, display_exp_sign,
display_exp, display_exp,
pad = 2 pad = 2
) )
} }
fn separated(f: f64, sep: char) -> String { fn separated(f: f64, sep: char) -> String {
let mut ret = f.to_string(); let mut ret = f.to_string();
let start = if ret.starts_with('-') { 1 } else { 0 }; let start = if ret.starts_with('-') { 1 } else { 0 };
let end = ret.find('.').unwrap_or_else(|| ret.len()); let end = ret.find('.').unwrap_or_else(|| ret.len());
for i in 0..((end - start - 1).div_euclid(3)) { for i in 0..((end - start - 1).div_euclid(3)) {
ret.insert(end - (i + 1) * 3, sep); ret.insert(end - (i + 1) * 3, sep);
} }
ret ret
} }