Start work on complex types
This commit is contained in:
parent
4b0e6e7e10
commit
445ae3f535
1385
src/calc.rs
1385
src/calc.rs
File diff suppressed because it is too large
Load Diff
336
src/calc/entries.rs
Normal file
336
src/calc/entries.rs
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
// use super::operations::CalculatorStateChange;
|
||||||
|
use super::errors::CalculatorResult;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Number {
|
||||||
|
pub value: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
// pub struct Vector {
|
||||||
|
// pub value: Vec<Number>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||||
|
pub enum Entry {
|
||||||
|
Number(Number),
|
||||||
|
// Vector(Vector),
|
||||||
|
// Matrix(Vec<Vec<f64>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CalculatorEntry for Entry {
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.is_valid(),
|
||||||
|
// Entry::Vector(vector) => vector.add(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn add(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.add(arg),
|
||||||
|
// Entry::Vector(vector) => vector.add(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn sub(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.sub(arg),
|
||||||
|
// Entry::Vector(vector) => vector.sub(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn mul(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.mul(arg),
|
||||||
|
// Entry::Vector(vector) => vector.mul(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn div(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.div(arg),
|
||||||
|
// Entry::Vector(vector) => vector.div(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.int_divide(arg),
|
||||||
|
// Entry::Vector(vector) => vector.int_divide(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn negate(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.negate(),
|
||||||
|
// Entry::Vector(vector) => vector.negate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn abs(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.abs(),
|
||||||
|
// Entry::Vector(vector) => vector.abs(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn inverse(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.inverse(),
|
||||||
|
// Entry::Vector(vector) => vector.inverse(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn modulo(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.modulo(arg),
|
||||||
|
// Entry::Vector(vector) => vector.modulo(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn sin(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.sin(),
|
||||||
|
// Entry::Vector(vector) => vector.sin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn cos(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.cos(),
|
||||||
|
// Entry::Vector(vector) => vector.cos(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn tan(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.tan(),
|
||||||
|
// Entry::Vector(vector) => vector.tan(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn asin(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.asin(),
|
||||||
|
// Entry::Vector(vector) => vector.asin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn acos(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.acos(),
|
||||||
|
// Entry::Vector(vector) => vector.acos(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn atan(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.atan(),
|
||||||
|
// Entry::Vector(vector) => vector.atan(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn sqrt(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.sqrt(),
|
||||||
|
// Entry::Vector(vector) => vector.sqrt(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn log(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.log(),
|
||||||
|
// Entry::Vector(vector) => vector.log(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn ln(&self) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.ln(),
|
||||||
|
// Entry::Vector(vector) => vector.ln(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn pow(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
match self {
|
||||||
|
Entry::Number(number) => number.pow(arg),
|
||||||
|
// Entry::Vector(vector) => vector.pow(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CalculatorEntry for Number {
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
!self.value.is_nan() && !self.value.is_infinite()
|
||||||
|
}
|
||||||
|
fn add(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64 + self.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn sub(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64 - self.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn mul(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64 * self.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn div(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64 / self.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn int_divide(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64.div_euclid(self.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn negate(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: -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> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64 % self.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
fn sin(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: self.value }))
|
||||||
|
// match self.angle_mode {
|
||||||
|
// CalculatorAngleMode::Degrees => {
|
||||||
|
// |self.value: f64| a.to_radians().sin()
|
||||||
|
// }
|
||||||
|
// CalculatorAngleMode::Radians => |a: f64| a.sin(),
|
||||||
|
// CalculatorAngleMode::Grads => |a: f64| {
|
||||||
|
// (a * std::f64::consts::PI / 200.0).sin()
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
fn cos(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: self.value }))
|
||||||
|
// match self.angle_mode {
|
||||||
|
// CalculatorAngleMode::Degrees => {
|
||||||
|
// |self.value: f64| a.to_radians().cos()
|
||||||
|
// }
|
||||||
|
// CalculatorAngleMode::Radians => |a: f64| a.cos(),
|
||||||
|
// CalculatorAngleMode::Grads => |a: f64| {
|
||||||
|
// (a * std::f64::consts::PI / 200.0).cos()
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
fn tan(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: self.value }))
|
||||||
|
// match self.angle_mode {
|
||||||
|
// CalculatorAngleMode::Degrees => {
|
||||||
|
// |self.value: f64| a.to_radians().tan()
|
||||||
|
// }
|
||||||
|
// CalculatorAngleMode::Radians => |a: f64| a.tan(),
|
||||||
|
// CalculatorAngleMode::Grads => |a: f64| {
|
||||||
|
// (a * std::f64::consts::PI / 200.0).tan()
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
fn asin(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: self.value }))
|
||||||
|
// match self.angle_mode {
|
||||||
|
// CalculatorAngleMode::Degrees => {
|
||||||
|
// |self.value: f64| a.asin().to_degrees()
|
||||||
|
// }
|
||||||
|
// CalculatorAngleMode::Radians => |a: f64| a.asin(),
|
||||||
|
// CalculatorAngleMode::Grads => |a: f64| {
|
||||||
|
// a.asin() * std::f64::consts::PI / 200.0
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
fn acos(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: self.value }))
|
||||||
|
// match self.angle_mode {
|
||||||
|
// CalculatorAngleMode::Degrees => {
|
||||||
|
// |self.value: f64| a.acos().to_degrees()
|
||||||
|
// }
|
||||||
|
// CalculatorAngleMode::Radians => |a: f64| a.acos(),
|
||||||
|
// CalculatorAngleMode::Grads => |a: f64| {
|
||||||
|
// a.acos() * std::f64::consts::PI / 200.0
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
fn atan(&self) -> CalculatorResult<Entry> {
|
||||||
|
Ok(Entry::Number(Number { value: self.value }))
|
||||||
|
// match self.angle_mode {
|
||||||
|
// CalculatorAngleMode::Degrees => {
|
||||||
|
// |self.value: f64| a.atan().to_degrees()
|
||||||
|
// }
|
||||||
|
// CalculatorAngleMode::Radians => |a: f64| a.atan(),
|
||||||
|
// CalculatorAngleMode::Grads => |a: f64| {
|
||||||
|
// a.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> {
|
||||||
|
Ok(Entry::Number(Number {
|
||||||
|
value: 1.0f64.powf(self.value),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
// fn e(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||||
|
// Ok(Entry::Number(Number { value:1.0f64 * 10.0_f64.powf(self.value) }))
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CalculatorEntry {
|
||||||
|
fn is_valid(&self) -> bool;
|
||||||
|
fn add(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
fn sub(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
fn mul(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
fn div(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
fn negate(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn abs(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn inverse(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn modulo(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
fn sin(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn cos(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn tan(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn asin(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn acos(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn atan(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn sqrt(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn log(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn ln(&self) -> CalculatorResult<Entry>;
|
||||||
|
fn pow(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
// fn e(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Entry {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Number(Number { value }) => write!(f, "{}", value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Number {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl fmt::Display for Vector {
|
||||||
|
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
// match self {
|
||||||
|
// Self::Degrees => write!(f, "DEG"),
|
||||||
|
// Self::Radians => write!(f, "RAD"),
|
||||||
|
// Self::Grads => write!(f, "GRD"),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
@ -7,65 +7,68 @@ pub type CalculatorResult<T> = Result<T, CalculatorError>;
|
|||||||
/// All possible errors the calculator can throw
|
/// All possible errors the calculator can throw
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum CalculatorError {
|
pub enum CalculatorError {
|
||||||
/// Divide by zero, log(-1), etc
|
/// Divide by zero, log(-1), etc
|
||||||
ArithmeticError,
|
ArithmeticError,
|
||||||
/// Not enough stck entries for operation
|
/// Not enough stck entries for operation
|
||||||
NotEnoughStackEntries,
|
NotEnoughStackEntries,
|
||||||
/// Thrown when an undo or redo cannot be performed
|
/// Requested type does not match target type
|
||||||
CorruptStateChange(String),
|
TypeMismatch,
|
||||||
/// Cannot undo or redo
|
/// Thrown when an undo or redo cannot be performed
|
||||||
EmptyHistory(String),
|
CorruptStateChange(String),
|
||||||
/// Constant undefined
|
/// Cannot undo or redo
|
||||||
NoSuchConstant(char),
|
EmptyHistory(String),
|
||||||
/// Register undefined
|
/// Constant undefined
|
||||||
NoSuchRegister(char),
|
NoSuchConstant(char),
|
||||||
/// Macro undefined
|
/// Register undefined
|
||||||
NoSuchMacro(char),
|
NoSuchRegister(char),
|
||||||
/// Operator undefined
|
/// Macro undefined
|
||||||
NoSuchOperator(char),
|
NoSuchMacro(char),
|
||||||
/// Setting undefined
|
/// Operator undefined
|
||||||
NoSuchSetting(char),
|
NoSuchOperator(char),
|
||||||
/// Macro calls itself
|
/// Setting undefined
|
||||||
RecursiveMacro(char),
|
NoSuchSetting(char),
|
||||||
/// Could not convert l to number
|
/// Macro calls itself
|
||||||
ParseError,
|
RecursiveMacro(char),
|
||||||
/// Requested precision is too high
|
/// Could not convert l to number
|
||||||
PrecisionTooHigh,
|
ParseError,
|
||||||
/// Config serialization error
|
/// Requested precision is too high
|
||||||
SaveError(Option<ConfyError>),
|
PrecisionTooHigh,
|
||||||
/// Config deserialization error
|
/// Config serialization error
|
||||||
LoadError(Option<ConfyError>),
|
SaveError(Option<ConfyError>),
|
||||||
|
/// Config deserialization error
|
||||||
|
LoadError(Option<ConfyError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for CalculatorError {}
|
impl error::Error for CalculatorError {}
|
||||||
|
|
||||||
impl fmt::Display for CalculatorError {
|
impl fmt::Display for CalculatorError {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::ArithmeticError => write!(f, "Arithmetic Error"),
|
Self::ArithmeticError => write!(f, "Arithmetic Error"),
|
||||||
Self::NotEnoughStackEntries => write!(f, "Not enough items in the stack"),
|
Self::NotEnoughStackEntries => write!(f, "Not enough items in the stack"),
|
||||||
Self::CorruptStateChange(msg) => {
|
Self::TypeMismatch => write!(f, "Type mismatch"),
|
||||||
write!(f, "Corrupt state change: {}", msg)
|
Self::CorruptStateChange(msg) => {
|
||||||
}
|
write!(f, "Corrupt state change: {}", msg)
|
||||||
Self::EmptyHistory(msg) => write!(f, "No history to {}", msg),
|
}
|
||||||
Self::NoSuchOperator(c) => write!(f, "No such operator '{}'", c),
|
Self::EmptyHistory(msg) => write!(f, "No history to {}", msg),
|
||||||
Self::NoSuchConstant(c) => write!(f, "No such constant '{}'", c),
|
Self::NoSuchOperator(c) => write!(f, "No such operator '{}'", c),
|
||||||
Self::NoSuchRegister(c) => write!(f, "No such register '{}'", c),
|
Self::NoSuchConstant(c) => write!(f, "No such constant '{}'", c),
|
||||||
Self::NoSuchMacro(c) => write!(f, "No such macro '{}'", c),
|
Self::NoSuchRegister(c) => write!(f, "No such register '{}'", c),
|
||||||
Self::NoSuchSetting(c) => write!(f, "No such setting '{}'", c),
|
Self::NoSuchMacro(c) => write!(f, "No such macro '{}'", c),
|
||||||
Self::RecursiveMacro(c) => write!(f, "Recursive macro '{}'", c),
|
Self::NoSuchSetting(c) => write!(f, "No such setting '{}'", c),
|
||||||
Self::ParseError => write!(f, "Parse error"),
|
Self::RecursiveMacro(c) => write!(f, "Recursive macro '{}'", c),
|
||||||
Self::PrecisionTooHigh => write!(f, "Precision too high"),
|
Self::ParseError => write!(f, "Parse error"),
|
||||||
Self::SaveError(None) => write!(f, "Could not save"),
|
Self::PrecisionTooHigh => write!(f, "Precision too high"),
|
||||||
Self::SaveError(Some(ConfyError::SerializeTomlError(e))) => {
|
Self::SaveError(None) => write!(f, "Could not save"),
|
||||||
write!(f, "Save serialization error: {}", e)
|
Self::SaveError(Some(ConfyError::SerializeTomlError(e))) => {
|
||||||
}
|
write!(f, "Save serialization error: {}", e)
|
||||||
Self::SaveError(Some(e)) => write!(f, "Could not save: {}", e),
|
}
|
||||||
Self::LoadError(None) => write!(f, "Could not load"),
|
Self::SaveError(Some(e)) => write!(f, "Could not save: {}", e),
|
||||||
Self::LoadError(Some(ConfyError::SerializeTomlError(e))) => {
|
Self::LoadError(None) => write!(f, "Could not load"),
|
||||||
write!(f, "Load serialization error: {}", e)
|
Self::LoadError(Some(ConfyError::SerializeTomlError(e))) => {
|
||||||
}
|
write!(f, "Load serialization error: {}", e)
|
||||||
Self::LoadError(Some(e)) => write!(f, "Could not load: {}", e),
|
}
|
||||||
|
Self::LoadError(Some(e)) => write!(f, "Could not load: {}", e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::entries::Entry;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
/// Operations that can be sent to the calculator such as +, -, or undo
|
/// Operations that can be sent to the calculator such as +, -, or undo
|
||||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||||
@ -12,9 +13,6 @@ pub enum CalculatorOperation {
|
|||||||
Modulo,
|
Modulo,
|
||||||
IntegerDivide,
|
IntegerDivide,
|
||||||
//Remainder,
|
//Remainder,
|
||||||
Drop,
|
|
||||||
Dup,
|
|
||||||
Swap,
|
|
||||||
Sin,
|
Sin,
|
||||||
Cos,
|
Cos,
|
||||||
Tan,
|
Tan,
|
||||||
@ -28,7 +26,9 @@ pub enum CalculatorOperation {
|
|||||||
// Factorial,
|
// Factorial,
|
||||||
Log,
|
Log,
|
||||||
Ln,
|
Ln,
|
||||||
E,
|
Drop,
|
||||||
|
Dup,
|
||||||
|
Swap,
|
||||||
Macro(MacroState),
|
Macro(MacroState),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,9 +45,9 @@ pub enum OpArgs {
|
|||||||
/// This is a macro start and end noop
|
/// This is a macro start and end noop
|
||||||
Macro(MacroState),
|
Macro(MacroState),
|
||||||
/// Operation takes 1 argument, ex: sqrt or negate
|
/// Operation takes 1 argument, ex: sqrt or negate
|
||||||
Unary(f64),
|
Unary(Entry),
|
||||||
/// Operation takes 2 arguments, ex: + or -
|
/// Operation takes 2 arguments, ex: + or -
|
||||||
Binary([f64; 2]),
|
Binary([Entry; 2]),
|
||||||
/// Operation takes no arguments, ex: push
|
/// Operation takes no arguments, ex: push
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use super::entries::Entry;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@ -5,38 +6,38 @@ use std::fmt;
|
|||||||
/// The calculator state
|
/// The calculator state
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum CalculatorState {
|
pub enum CalculatorState {
|
||||||
Normal,
|
Normal,
|
||||||
WaitingForConstant,
|
WaitingForConstant,
|
||||||
WaitingForMacro,
|
WaitingForMacro,
|
||||||
WaitingForRegister(RegisterState),
|
WaitingForRegister(RegisterState),
|
||||||
WaitingForSetting,
|
WaitingForSetting,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CalculatorState {
|
impl Default for CalculatorState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Normal
|
Self::Normal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state of the requested register operation
|
/// The state of the requested register operation
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||||
pub enum RegisterState {
|
pub enum RegisterState {
|
||||||
Save,
|
Save,
|
||||||
Load,
|
Load,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One calculator constant containing a message and value
|
/// One calculator constant containing a message and value
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct CalculatorConstant {
|
pub struct CalculatorConstant {
|
||||||
pub help: String,
|
pub help: String,
|
||||||
pub value: f64,
|
pub value: Entry,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One calculator macro containing a messsage and value
|
/// One calculator macro containing a messsage and value
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct CalculatorMacro {
|
pub struct CalculatorMacro {
|
||||||
pub help: String,
|
pub help: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map of chars to constants
|
/// Map of chars to constants
|
||||||
@ -46,264 +47,269 @@ pub type CalculatorConstants = HashMap<char, CalculatorConstant>;
|
|||||||
pub type CalculatorMacros = HashMap<char, CalculatorMacro>;
|
pub type CalculatorMacros = HashMap<char, CalculatorMacro>;
|
||||||
|
|
||||||
/// Map of chars to registers
|
/// Map of chars to registers
|
||||||
pub type CalculatorRegisters = HashMap<char, f64>;
|
pub type CalculatorRegisters = HashMap<char, Entry>;
|
||||||
|
|
||||||
/// Possible calculator angle modes
|
/// Possible calculator angle modes
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(tag = "angle_mode")]
|
#[serde(tag = "angle_mode")]
|
||||||
pub enum CalculatorAngleMode {
|
pub enum CalculatorAngleMode {
|
||||||
Degrees,
|
Degrees,
|
||||||
Radians,
|
Radians,
|
||||||
Grads,
|
Grads,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CalculatorAngleMode {
|
impl Default for CalculatorAngleMode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Degrees
|
Self::Degrees
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CalculatorAngleMode {
|
impl fmt::Display for CalculatorAngleMode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Degrees => write!(f, "DEG"),
|
Self::Degrees => write!(f, "DEG"),
|
||||||
Self::Radians => write!(f, "RAD"),
|
Self::Radians => write!(f, "RAD"),
|
||||||
Self::Grads => write!(f, "GRD"),
|
Self::Grads => write!(f, "GRD"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The calculator digit display mode
|
/// The calculator digit display mode
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(tag = "display_mode")]
|
#[serde(tag = "display_mode")]
|
||||||
pub enum CalculatorDisplayMode {
|
pub enum CalculatorDisplayMode {
|
||||||
/// Rust's default f64 format
|
/// Rust's default Entry format
|
||||||
Default,
|
Default,
|
||||||
/// Thousands separator
|
/// Thousands separator
|
||||||
Separated { separator: char },
|
Separated { separator: char },
|
||||||
/// Aligned scientific format
|
/// Aligned scientific format
|
||||||
Scientific { precision: usize },
|
Scientific { precision: usize },
|
||||||
/// Scientific format, chunked by groups of 3
|
/// Scientific format, chunked by groups of 3
|
||||||
///
|
///
|
||||||
/// Example: 1 E+5 or 100E+5
|
/// Example: 1 E+5 or 100E+5
|
||||||
Engineering { precision: usize },
|
Engineering { precision: usize },
|
||||||
/// Fixed precision
|
/// Fixed precision
|
||||||
Fixed { precision: usize },
|
Fixed { precision: usize },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CalculatorDisplayMode {
|
impl fmt::Display for CalculatorDisplayMode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => write!(f, "DEF"),
|
Self::Default => write!(f, "DEF"),
|
||||||
Self::Separated { separator } => write!(f, "SEP({})", separator),
|
Self::Separated { separator } => write!(f, "SEP({})", separator),
|
||||||
Self::Scientific { precision } => write!(f, "SCI({})", precision),
|
Self::Scientific { precision } => write!(f, "SCI({})", precision),
|
||||||
Self::Engineering { precision } => write!(f, "ENG({})", precision),
|
Self::Engineering { precision } => write!(f, "ENG({})", precision),
|
||||||
Self::Fixed { precision } => write!(f, "FIX({})", precision),
|
Self::Fixed { precision } => write!(f, "FIX({})", precision),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CalculatorDisplayMode {
|
impl Default for CalculatorDisplayMode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Default
|
Self::Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CalculatorDisplayMode {
|
impl CalculatorDisplayMode {
|
||||||
pub fn format_number(&self, number: f64) -> String {
|
pub fn format_number(&self, number: &Entry) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Default => format!("{}", number),
|
Self::Default => format!("{}", number),
|
||||||
Self::Separated { separator } => Self::separated(number, *separator),
|
Self::Separated { separator } => Self::separated(number, *separator),
|
||||||
Self::Scientific { precision } => Self::scientific(number, *precision),
|
Self::Scientific { precision } => Self::scientific(number, *precision),
|
||||||
Self::Engineering { precision } => Self::engineering(number, *precision),
|
Self::Engineering { precision } => Self::engineering(number, *precision),
|
||||||
Self::Fixed { precision } => {
|
Self::Fixed { precision } => {
|
||||||
format!("{:0>.precision$}", number, precision = precision)
|
format!("{:0>.precision$}", number, precision = precision)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Based on https://stackoverflow.com/a/65266882
|
// Based on https://stackoverflow.com/a/65266882
|
||||||
fn scientific(f: f64, precision: usize) -> String {
|
fn scientific(_f: &Entry, _precision: usize) -> String {
|
||||||
let mut ret = format!("{:.precision$E}", f, precision = precision);
|
// TODO
|
||||||
let exp = ret.split_off(ret.find('E').unwrap_or(0));
|
String::from("TODO")
|
||||||
let (exp_sign, exp) = exp
|
// let mut ret = format!("{:.precision$E}", f, precision = precision);
|
||||||
.strip_prefix("E-")
|
// let exp = ret.split_off(ret.find('E').unwrap_or(0));
|
||||||
.map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped));
|
// let (exp_sign, exp) = exp
|
||||||
|
// .strip_prefix("E-")
|
||||||
|
// .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: &Entry, _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
|
// TODO
|
||||||
// 1,000 => 1000E3
|
String::from("TODO")
|
||||||
let all = format!(" {:.precision$E}", f, precision = precision)
|
/*// 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
|
||||||
// Remove . since it can be moved
|
// 1,000 => 1000E3
|
||||||
.replacen(".", "", 1)
|
let all = format!(" {:.precision$E}", f, precision = precision)
|
||||||
// Add 00E before E here so the length is enough for slicing below
|
// Remove . since it can be moved
|
||||||
.replacen("E", "00E", 1);
|
.replacen(".", "", 1)
|
||||||
// Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E
|
// Add 00E before E here so the length is enough for slicing below
|
||||||
// 1000E3 => (1000, E3)
|
.replacen("E", "00E", 1);
|
||||||
let (num_str, exp_str) = all.split_at(all.find('E').unwrap());
|
// Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E
|
||||||
// Extract the exponent as an isize. This should always be true because f64 max will be ~400
|
// 1000E3 => (1000, E3)
|
||||||
// E3 => 3 as isize
|
let (num_str, exp_str) = all.split_at(all.find('E').unwrap());
|
||||||
let exp = exp_str[1..].parse::<isize>().unwrap();
|
// Extract the exponent as an isize. This should always be true because Entry max will be ~400
|
||||||
// Sign of the exponent. If string representation starts with E-, then negative
|
// E3 => 3 as isize
|
||||||
let display_exp_sign = if exp_str.strip_prefix("E-").is_some() {
|
let exp = exp_str[1..].parse::<isize>().unwrap();
|
||||||
'-'
|
// Sign of the exponent. If string representation starts with E-, then negative
|
||||||
} else {
|
let display_exp_sign = if exp_str.strip_prefix("E-").is_some() {
|
||||||
'+'
|
'-'
|
||||||
};
|
} else {
|
||||||
|
'+'
|
||||||
// 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)
|
|
||||||
let display_exp = (exp.div_euclid(3) * 3).abs();
|
// The exponent to display. Always a multiple of 3 in engineering mode. Always positive because sign is added with display_exp_sign above
|
||||||
// Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility
|
// 100 => 0, 1000 => 3, .1 => 3 (but will show as -3)
|
||||||
let num_whole_digits = exp.rem_euclid(3) as usize + 1;
|
let display_exp = (exp.div_euclid(3) * 3).abs();
|
||||||
|
// Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility
|
||||||
// If this is a negative number, strip off the added space, otherwise keep the space (and next digit)
|
let num_whole_digits = exp.rem_euclid(3) as usize + 1;
|
||||||
let num_str = if num_str.strip_prefix(" -").is_some() {
|
|
||||||
&num_str[1..]
|
// If this is a negative number, strip off the added space, otherwise keep the space (and next digit)
|
||||||
} else {
|
let num_str = if num_str.strip_prefix(" -").is_some() {
|
||||||
num_str
|
&num_str[1..]
|
||||||
};
|
} else {
|
||||||
|
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)
|
};
|
||||||
// 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];
|
// 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)
|
||||||
// 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
|
// Original number is 1,000 => whole will be 1, if original is 0.01, whole will be 10
|
||||||
let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)];
|
let whole = &num_str[0..=num_whole_digits];
|
||||||
// Right align whole portion, always have decimal point
|
// 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
|
||||||
format!(
|
let decimal = &num_str[(num_whole_digits + 1)..=(precision + num_whole_digits)];
|
||||||
"{: >4}.{} E{}{:0>pad$}",
|
// Right align whole portion, always have decimal point
|
||||||
// display_sign,
|
format!(
|
||||||
whole,
|
"{: >4}.{} E{}{:0>pad$}",
|
||||||
decimal,
|
// display_sign,
|
||||||
display_exp_sign,
|
whole,
|
||||||
display_exp,
|
decimal,
|
||||||
pad = 2
|
display_exp_sign,
|
||||||
)
|
display_exp,
|
||||||
}
|
pad = 2
|
||||||
|
)
|
||||||
fn separated(f: f64, sep: char) -> String {
|
*/
|
||||||
let mut ret = f.to_string();
|
}
|
||||||
let start = if ret.starts_with('-') { 1 } else { 0 };
|
|
||||||
let end = ret.find('.').unwrap_or_else(|| ret.len());
|
fn separated(f: &Entry, sep: char) -> String {
|
||||||
for i in 0..((end - start - 1).div_euclid(3)) {
|
let mut ret = f.to_string();
|
||||||
ret.insert(end - (i + 1) * 3, sep);
|
let start = if ret.starts_with('-') { 1 } else { 0 };
|
||||||
|
let end = ret.find('.').unwrap_or_else(|| ret.len());
|
||||||
|
for i in 0..((end - start - 1).div_euclid(3)) {
|
||||||
|
ret.insert(end - (i + 1) * 3, sep);
|
||||||
|
}
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Left or right calculator alignment
|
/// Left or right calculator alignment
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum CalculatorAlignment {
|
pub enum CalculatorAlignment {
|
||||||
Right,
|
Right,
|
||||||
Left,
|
Left,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CalculatorAlignment {
|
impl Default for CalculatorAlignment {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Left
|
Self::Left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CalculatorAlignment {
|
impl fmt::Display for CalculatorAlignment {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Left => write!(f, "L"),
|
Self::Left => write!(f, "L"),
|
||||||
Self::Right => write!(f, "R"),
|
Self::Right => write!(f, "R"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scientific() {
|
fn test_scientific() {
|
||||||
for (f, precision, s) in vec![
|
for (f, precision, s) in vec![
|
||||||
// Basic
|
// Basic
|
||||||
(1.0, 0, " 1 E+00"),
|
(1.0, 0, " 1 E+00"),
|
||||||
(-1.0, 0, "-1 E+00"),
|
(-1.0, 0, "-1 E+00"),
|
||||||
(100.0, 0, " 1 E+02"),
|
(100.0, 0, " 1 E+02"),
|
||||||
(0.1, 0, " 1 E-01"),
|
(0.1, 0, " 1 E-01"),
|
||||||
(0.01, 0, " 1 E-02"),
|
(0.01, 0, " 1 E-02"),
|
||||||
(-0.1, 0, "-1 E-01"),
|
(-0.1, 0, "-1 E-01"),
|
||||||
// i
|
// i
|
||||||
(1.0, 0, " 1 E+00"),
|
(1.0, 0, " 1 E+00"),
|
||||||
// Precision
|
// Precision
|
||||||
(-0.123456789, 3, "-1.235 E-01"),
|
(-0.123_456_789, 3, "-1.235 E-01"),
|
||||||
(-0.123456789, 2, "-1.23 E-01"),
|
(-0.123_456_789, 2, "-1.23 E-01"),
|
||||||
(-0.123456789, 2, "-1.23 E-01"),
|
(-0.123_456_789, 2, "-1.23 E-01"),
|
||||||
(-1e99, 2, "-1.00 E+99"),
|
(-1e99, 2, "-1.00 E+99"),
|
||||||
(-1e100, 2, "-1.00 E+100"),
|
(-1e100, 2, "-1.00 E+100"),
|
||||||
// Rounding
|
// Rounding
|
||||||
(0.5, 2, " 5.00 E-01"),
|
(0.5, 2, " 5.00 E-01"),
|
||||||
(0.5, 1, " 5.0 E-01"),
|
(0.5, 1, " 5.0 E-01"),
|
||||||
(0.5, 0, " 5 E-01"),
|
(0.5, 0, " 5 E-01"),
|
||||||
(1.5, 2, " 1.50 E+00"),
|
(1.5, 2, " 1.50 E+00"),
|
||||||
(1.5, 1, " 1.5 E+00"),
|
(1.5, 1, " 1.5 E+00"),
|
||||||
(1.5, 0, " 2 E+00"),
|
(1.5, 0, " 2 E+00"),
|
||||||
] {
|
] {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
CalculatorDisplayMode::Scientific { precision }.format_number(f),
|
CalculatorDisplayMode::Scientific { precision }.format_number(f),
|
||||||
s
|
s
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_separated() {
|
fn test_separated() {
|
||||||
for (f, separator, s) in vec![
|
for (f, separator, s) in vec![
|
||||||
(100.0, ',', "100"),
|
(100.0, ',', "100"),
|
||||||
(100.0, ',', "100"),
|
(100.0, ',', "100"),
|
||||||
(-100.0, ',', "-100"),
|
(-100.0, ',', "-100"),
|
||||||
(1_000.0, ',', "1,000"),
|
(1_000.0, ',', "1,000"),
|
||||||
(-1_000.0, ',', "-1,000"),
|
(-1_000.0, ',', "-1,000"),
|
||||||
(10_000.0, ',', "10,000"),
|
(10_000.0, ',', "10,000"),
|
||||||
(-10_000.0, ',', "-10,000"),
|
(-10_000.0, ',', "-10,000"),
|
||||||
(100_000.0, ',', "100,000"),
|
(100_000.0, ',', "100,000"),
|
||||||
(-100_000.0, ',', "-100,000"),
|
(-100_000.0, ',', "-100,000"),
|
||||||
(1_000_000.0, ',', "1,000,000"),
|
(1_000_000.0, ',', "1,000,000"),
|
||||||
(-1_000_000.0, ',', "-1,000,000"),
|
(-1_000_000.0, ',', "-1,000,000"),
|
||||||
(1_000_000.123456789, ',', "1,000,000.123456789"),
|
(1_000_000.123_456_789, ',', "1,000,000.123456789"),
|
||||||
(-1_000_000.123456789, ',', "-1,000,000.123456789"),
|
(-1_000_000.123_456_789, ',', "-1,000,000.123456789"),
|
||||||
(1_000_000.123456789, ' ', "1 000 000.123456789"),
|
(1_000_000.123_456_789, ' ', "1 000 000.123456789"),
|
||||||
(1_000_000.123456789, ' ', "1 000 000.123456789"),
|
(1_000_000.123_456_789, ' ', "1 000 000.123456789"),
|
||||||
] {
|
] {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
CalculatorDisplayMode::Separated { separator }.format_number(f),
|
CalculatorDisplayMode::Separated { separator }.format_number(f),
|
||||||
s
|
s
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_engineering() {
|
fn test_engineering() {
|
||||||
for (f, precision, s) in vec![
|
for (f, precision, s) in vec![
|
||||||
(100.0, 3, " 100.000 E+00"),
|
(100.0, 3, " 100.000 E+00"),
|
||||||
(100.0, 3, " 100.000 E+00"),
|
(100.0, 3, " 100.000 E+00"),
|
||||||
(-100.0, 3, "-100.000 E+00"),
|
(-100.0, 3, "-100.000 E+00"),
|
||||||
(100.0, 0, " 100. E+00"),
|
(100.0, 0, " 100. E+00"),
|
||||||
(-100.0, 0, "-100. E+00"),
|
(-100.0, 0, "-100. E+00"),
|
||||||
(0.1, 2, " 100.00 E-03"),
|
(0.1, 2, " 100.00 E-03"),
|
||||||
(0.01, 2, " 10.00 E-03"),
|
(0.01, 2, " 10.00 E-03"),
|
||||||
(0.001, 2, " 1.00 E-03"),
|
(0.001, 2, " 1.00 E-03"),
|
||||||
(0.0001, 2, " 100.00 E-06"),
|
(0.0001, 2, " 100.00 E-06"),
|
||||||
// Rounding
|
// Rounding
|
||||||
(0.5, 2, " 500.00 E-03"),
|
(0.5, 2, " 500.00 E-03"),
|
||||||
(0.5, 1, " 500.0 E-03"),
|
(0.5, 1, " 500.0 E-03"),
|
||||||
(0.5, 0, " 500. E-03"),
|
(0.5, 0, " 500. E-03"),
|
||||||
(1.5, 2, " 1.50 E+00"),
|
(1.5, 2, " 1.50 E+00"),
|
||||||
(1.5, 1, " 1.5 E+00"),
|
(1.5, 1, " 1.5 E+00"),
|
||||||
(1.5, 0, " 2. E+00"),
|
(1.5, 0, " 2. E+00"),
|
||||||
] {
|
] {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
CalculatorDisplayMode::Engineering { precision }.format_number(f),
|
CalculatorDisplayMode::Engineering { precision }.format_number(f),
|
||||||
s
|
s
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ impl App {
|
|||||||
"{}: {} ({})",
|
"{}: {} ({})",
|
||||||
key,
|
key,
|
||||||
constant.help,
|
constant.help,
|
||||||
self.calculator.display_mode.format_number(constant.value)
|
self.calculator.display_mode.format_number(&constant.value)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.fold(String::new(), |acc, s| acc + &s + "\n")
|
.fold(String::new(), |acc, s| acc + &s + "\n")
|
||||||
@ -233,9 +233,11 @@ impl App {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(i, m)| {
|
.map(|(i, m)| {
|
||||||
let number = self.calculator.display_mode.format_number(*m);
|
let number = self.calculator.display_mode.format_number(&*m);
|
||||||
let content = match self.calculator.calculator_alignment {
|
let content = match self.calculator.calculator_alignment {
|
||||||
CalculatorAlignment::Left => format!("{:>2}: {}", i, number),
|
CalculatorAlignment::Left => {
|
||||||
|
format!("{:>2}: {}", i, number)
|
||||||
|
}
|
||||||
CalculatorAlignment::Right => {
|
CalculatorAlignment::Right => {
|
||||||
let ret = format!("{} :{:>2}", number, i);
|
let ret = format!("{} :{:>2}", number, i);
|
||||||
if ret.len() < chunk.width.saturating_sub(BORDER_SIZE) as usize {
|
if ret.len() < chunk.width.saturating_sub(BORDER_SIZE) as usize {
|
||||||
|
Loading…
Reference in New Issue
Block a user