diff --git a/src/calc.rs b/src/calc.rs index 04a4c4c..17fe12c 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -7,15 +7,17 @@ use crate::calc::entries::CalculatorEntry; use confy::{load, store}; use entries::{Entry, Number}; use errors::{CalculatorError, CalculatorResult}; -use operations::{ArithmeticOperation,CalculatorOperation, CalculatorStateChange, MacroState, OpArgs}; +use operations::{ + ArithmeticOperation, CalculatorOperation, CalculatorStateChange, MacroState, OpArgs, +}; use serde::ser::Serializer; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; use std::collections::{HashSet, VecDeque}; use types::{ - CalculatorAlignment, CalculatorAngleMode, CalculatorConstant, CalculatorConstants, - CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorRegisters, - CalculatorState, RegisterState, + CalculatorAlignment, CalculatorAngleMode, CalculatorConstant, CalculatorConstants, + CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorRegisters, CalculatorState, + RegisterState, }; /// The maximum precision allowed for the calculator @@ -28,68 +30,68 @@ const DEFAULT_PRECISION: usize = 3; /// The history mode of the entry - either a single change or a macro bound #[derive(PartialEq, Debug, Serialize, Deserialize)] enum HistoryMode { - One, - Macro, + One, + Macro, } /// The main calculator struct that contains all fields internally #[derive(Serialize, Deserialize)] #[serde(default)] pub struct Calculator { - /// The entry buffer - #[serde(skip)] - l: String, - /// True if the user would like to save on quit - save_on_close: bool, - /// Left or right aligned display - pub calculator_alignment: CalculatorAlignment, - /// The angle mode, such as DEG or RAD - #[serde(flatten)] - pub angle_mode: CalculatorAngleMode, - /// The display format such as separated or scientific - #[serde(flatten)] - pub display_mode: CalculatorDisplayMode, - /// The stack - pub stack: VecDeque, - /// A set of the currently running macros, used for ensuring there are no recursive macro calls - #[serde(skip)] - active_macros: HashSet, - /// The map of chars to macros - #[serde(serialize_with = "ordered_char_map")] - pub macros: CalculatorMacros, - /// Map of chars to constants - #[serde(serialize_with = "ordered_char_map")] - pub constants: CalculatorConstants, - /// Map of chars to registers - #[serde(skip)] - pub registers: CalculatorRegisters, - /// Vec of state changes that can be undone - #[serde(skip)] - undo_buf: Vec, - /// Vec of state changes that can be redone - #[serde(skip)] - redo_buf: Vec, - /// The current state of the calculator, such as normal, or waiting for macro char - #[serde(skip)] - pub state: CalculatorState, + /// The entry buffer + #[serde(skip)] + l: String, + /// True if the user would like to save on quit + save_on_close: bool, + /// Left or right aligned display + pub calculator_alignment: CalculatorAlignment, + /// The angle mode, such as DEG or RAD + #[serde(flatten)] + pub angle_mode: CalculatorAngleMode, + /// The display format such as separated or scientific + #[serde(flatten)] + pub display_mode: CalculatorDisplayMode, + /// The stack + pub stack: VecDeque, + /// A set of the currently running macros, used for ensuring there are no recursive macro calls + #[serde(skip)] + active_macros: HashSet, + /// The map of chars to macros + #[serde(serialize_with = "ordered_char_map")] + pub macros: CalculatorMacros, + /// Map of chars to constants + #[serde(serialize_with = "ordered_char_map")] + pub constants: CalculatorConstants, + /// Map of chars to registers + #[serde(skip)] + pub registers: CalculatorRegisters, + /// Vec of state changes that can be undone + #[serde(skip)] + undo_buf: Vec, + /// Vec of state changes that can be redone + #[serde(skip)] + redo_buf: Vec, + /// The current state of the calculator, such as normal, or waiting for macro char + #[serde(skip)] + pub state: CalculatorState, } fn ordered_char_map(value: &HashMap, serializer: S) -> Result where - T: Serialize, - S: Serializer, + T: Serialize, + S: Serializer, { - // Convert chars to string for TOML map; insert into BTreeMap to be sorted - let ordered: BTreeMap<_, _> = value - .iter() - .map(|(key, t)| (String::from(*key), t)) - .collect(); - ordered.serialize(serializer) + // Convert chars to string for TOML map; insert into BTreeMap to be sorted + let ordered: BTreeMap<_, _> = value + .iter() + .map(|(key, t)| (String::from(*key), t)) + .collect(); + ordered.serialize(serializer) } impl Default for Calculator { - fn default() -> Self { - Self { + fn default() -> Self { + Self { l: String::new(), redo_buf: vec![], undo_buf: vec![], @@ -153,611 +155,631 @@ impl Default for Calculator { display_mode: CalculatorDisplayMode::default(), calculator_alignment: CalculatorAlignment::default(), } - } + } } impl Calculator { - pub fn close(&self) -> CalculatorResult<()> { - if self.save_on_close { - self.save_config() - } else { - Ok(()) + pub fn close(&self) -> CalculatorResult<()> { + if self.save_on_close { + self.save_config() + } else { + Ok(()) + } + } + + pub fn load_config() -> CalculatorResult { + load(APP_NAME).map_err(|e| CalculatorError::LoadError(Some(e))) + } + pub fn save_config(&self) -> CalculatorResult<()> { + store(APP_NAME, self).map_err(|e| CalculatorError::SaveError(Some(e))) + } + + pub fn take_input(&mut self, c: char) -> CalculatorResult<()> { + match &self.state { + CalculatorState::Normal => self.normal_input(c), + CalculatorState::WaitingForConstant => self.constant_input(c), + CalculatorState::WaitingForMacro => self.macro_input(c), + CalculatorState::WaitingForRegister(register_state) => { + let register_state = *register_state; + self.register_input(register_state, c) + } + CalculatorState::WaitingForSetting => self.setting_input(c), + } + } + fn normal_input(&mut self, c: char) -> CalculatorResult<()> { + match c { + c @ '0'..='9' | c @ '.' | c @ 'e' => match c { + '0'..='9' => { + self.l.push(c); + Ok(()) } - } + 'e' => { + if self.l.is_empty() { + let f = self.pop().or(Err(CalculatorError::NotEnoughStackEntries))?; - pub fn load_config() -> CalculatorResult { - load(APP_NAME).map_err(|e| CalculatorError::LoadError(Some(e))) - } - pub fn save_config(&self) -> CalculatorResult<()> { - store(APP_NAME, self).map_err(|e| CalculatorError::SaveError(Some(e))) - } + self.l = f.to_string(); + } - pub fn take_input(&mut self, c: char) -> CalculatorResult<()> { - match &self.state { - CalculatorState::Normal => self.normal_input(c), - CalculatorState::WaitingForConstant => self.constant_input(c), - CalculatorState::WaitingForMacro => self.macro_input(c), - CalculatorState::WaitingForRegister(register_state) => { - let register_state = *register_state; - self.register_input(register_state, c) - } - CalculatorState::WaitingForSetting => self.setting_input(c), + if !self.l.contains('e') { + self.l.push('e'); + } + Ok(()) } - } - fn normal_input(&mut self, c: char) -> CalculatorResult<()> { - match c { - c @ '0'..='9' | c @ '.' | c @ 'e' => { - match c { - '0'..='9' => { - self.l.push(c); - Ok(()) - } - 'e' => { - if self.l.is_empty() { - let f = self.pop().or(Err(CalculatorError::NotEnoughStackEntries))?; - self.l = f.to_string(); - } - - if !self.l.contains('e') { - self.l.push('e'); - } - Ok(()) - } - - '.' => { - if !self.l.contains('.') && !self.l.contains('e') { - self.l.push('.'); - } - Ok(()) - } - _ => Err(CalculatorError::ParseError), - } - } - '+' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Add)), - '-' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Subtract)), - '*' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Multiply)), - '/' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Divide)), - 'n' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Negate)), - '|' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::AbsoluteValue)), - 'i' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Inverse)), - '%' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Modulo)), - '?' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::IntegerDivide)), - 's' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Sin)), - 'c' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Cos)), - 't' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Tan)), - 'S' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ASin)), - 'C' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ACos)), - 'T' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ATan)), - 'v' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Sqrt)), - '^' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Pow)), - 'l' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Log)), - 'L' => self.op(CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Ln)), - // Special - '\\' => self.op(CalculatorOperation::Drop), - ' ' => self.op(CalculatorOperation::Dup), - '>' => self.op(CalculatorOperation::Swap), - 'u' => self.op(CalculatorOperation::Undo), - 'U' => self.op(CalculatorOperation::Redo), - // State modifiers - 'm' => { - self.state = CalculatorState::WaitingForMacro; - Ok(()) - } - 'r' => { - self.state = - CalculatorState::WaitingForRegister(RegisterState::Load); - Ok(()) - } - 'R' => { - self.state = - CalculatorState::WaitingForRegister(RegisterState::Save); - Ok(()) - } - '`' => { - self.state = CalculatorState::WaitingForConstant; - Ok(()) - } - '@' => { - self.state = CalculatorState::WaitingForSetting; - Ok(()) - } - _ => Err(CalculatorError::NoSuchOperator(c)), + '.' => { + if !self.l.contains('.') && !self.l.contains('e') { + self.l.push('.'); + } + Ok(()) } + _ => Err(CalculatorError::ParseError), + }, + '+' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Add, + )), + '-' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Subtract, + )), + '*' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Multiply, + )), + '/' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Divide, + )), + 'n' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Negate, + )), + '|' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::AbsoluteValue, + )), + 'i' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Inverse, + )), + '%' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Modulo, + )), + '?' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::IntegerDivide, + )), + 's' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Sin, + )), + 'c' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Cos, + )), + 't' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Tan, + )), + 'S' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::ASin, + )), + 'C' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::ACos, + )), + 'T' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::ATan, + )), + 'v' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Sqrt, + )), + '^' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Pow, + )), + 'l' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Log, + )), + 'L' => self.op(CalculatorOperation::ArithmeticOperation( + ArithmeticOperation::Ln, + )), + // Special + '\\' => self.op(CalculatorOperation::Drop), + ' ' => self.op(CalculatorOperation::Dup), + '>' => self.op(CalculatorOperation::Swap), + 'u' => self.op(CalculatorOperation::Undo), + 'U' => self.op(CalculatorOperation::Redo), + // State modifiers + 'm' => { + self.state = CalculatorState::WaitingForMacro; + Ok(()) + } + 'r' => { + self.state = CalculatorState::WaitingForRegister(RegisterState::Load); + Ok(()) + } + 'R' => { + self.state = CalculatorState::WaitingForRegister(RegisterState::Save); + Ok(()) + } + '`' => { + self.state = CalculatorState::WaitingForConstant; + Ok(()) + } + '@' => { + self.state = CalculatorState::WaitingForSetting; + Ok(()) + } + _ => Err(CalculatorError::NoSuchOperator(c)), } - fn constant_input(&mut self, c: char) -> CalculatorResult<()> { + } + fn constant_input(&mut self, c: char) -> CalculatorResult<()> { + let f = self + .constants + .get(&c) + .ok_or(CalculatorError::NoSuchConstant(c))? + .value + .clone(); + + self.push(f)?; + self.state = CalculatorState::Normal; + Ok(()) + } + fn macro_input(&mut self, c: char) -> CalculatorResult<()> { + let mac = self.macros.get(&c).ok_or(CalculatorError::NoSuchMacro(c))?; + // self.take_input below takes a mutable reference to self, so must clone the value here + let value = mac.value.clone(); + + if self.active_macros.contains(&c) { + return Err(CalculatorError::RecursiveMacro(c)); + } + + // Record the macro started, if this is the outer macro + self.op(CalculatorOperation::Macro(MacroState::Start))?; + + // Record that we are running macro c + self.active_macros.insert(c); + + // The macro needs to run in normal mode + self.state = CalculatorState::Normal; + + for c in value.chars() { + self.take_input(c).map_err(|e| { + // Try cancelling, but if you cannot, that is okay + self.cancel().unwrap_or(()); + e + })?; + } + // Macro c should be over now + self.active_macros.remove(&c); + + // Record the macro is over, if this is the outer macro + self.op(CalculatorOperation::Macro(MacroState::End))?; + + Ok(()) + } + fn register_input(&mut self, register_state: RegisterState, c: char) -> CalculatorResult<()> { + match register_state { + RegisterState::Save => { + let f = self.pop()?; + self.registers.insert(c, f); + } + RegisterState::Load => { let f = self - .constants - .get(&c) - .ok_or(CalculatorError::NoSuchConstant(c))? - .value - .clone(); - + .registers + .get(&c) + .ok_or(CalculatorError::NoSuchRegister(c))? + .clone(); self.push(f)?; - self.state = CalculatorState::Normal; - Ok(()) + } } - fn macro_input(&mut self, c: char) -> CalculatorResult<()> { - let mac = self.macros.get(&c).ok_or(CalculatorError::NoSuchMacro(c))?; - // self.take_input below takes a mutable reference to self, so must clone the value here - let value = mac.value.clone(); - if self.active_macros.contains(&c) { - return Err(CalculatorError::RecursiveMacro(c)); + self.state = CalculatorState::Normal; + Ok(()) + } + fn setting_input(&mut self, c: char) -> CalculatorResult<()> { + self.flush_l()?; + match c { + 'q' => self.state = CalculatorState::Normal, + 'd' => self.angle_mode = CalculatorAngleMode::Degrees, + 'r' => self.angle_mode = CalculatorAngleMode::Radians, + 'g' => self.angle_mode = CalculatorAngleMode::Grads, + '_' => self.display_mode = CalculatorDisplayMode::Default, + ',' => self.display_mode = CalculatorDisplayMode::Separated { separator: ',' }, + ' ' => self.display_mode = CalculatorDisplayMode::Separated { separator: ' ' }, + 's' => { + self.display_mode = CalculatorDisplayMode::Scientific { + precision: DEFAULT_PRECISION, + } + } + 'S' => { + self.display_mode = CalculatorDisplayMode::Scientific { + precision: self.pop_precision()?, + } + } + 'e' => { + self.display_mode = CalculatorDisplayMode::Engineering { + precision: DEFAULT_PRECISION, + } + } + 'E' => { + self.display_mode = CalculatorDisplayMode::Engineering { + precision: self.pop_precision()?, + } + } + 'f' => { + self.display_mode = CalculatorDisplayMode::Fixed { + precision: DEFAULT_PRECISION, + } + } + 'F' => { + self.display_mode = CalculatorDisplayMode::Fixed { + precision: self.pop_precision()?, + } + } + 'w' => self.save_on_close = false, + 'W' => self.save_on_close = true, + 'L' => self.calculator_alignment = CalculatorAlignment::Left, + 'R' => self.calculator_alignment = CalculatorAlignment::Right, + _ => return Err(CalculatorError::NoSuchSetting(c)), + }; + self.state = CalculatorState::Normal; + Ok(()) + } + + /// Resets the calculator state to normal, and exits out of a macro if one is running + pub fn cancel(&mut self) -> CalculatorResult<()> { + self.state = CalculatorState::Normal; + // We died in a macro. Quit and push an end macro state + if !self.active_macros.is_empty() { + self.active_macros.clear(); + // Should always be successful, but report the error if there is one + self.op(CalculatorOperation::Macro(MacroState::End))?; + } + Ok(()) + } + /// Handles the backspace key which only deletes a char a char from l + pub fn backspace(&mut self) -> CalculatorResult<()> { + self.l.pop(); + Ok(()) + } + /// Places the bottom of the stack into l for editing, only if the value is empty + pub fn edit(&mut self) -> CalculatorResult<()> { + if !self.l.is_empty() { + return Ok(()); + } + + self.l = self + .pop() + .or(Err(CalculatorError::NotEnoughStackEntries))? + .to_string(); + Ok(()) + } + /// Get the value of l + pub fn get_l(&mut self) -> &str { + self.l.as_ref() + } + + /// Returns a formatted string that shows the status of the calculator + pub fn get_status_line(&self) -> String { + format!( + "[{}] [{}] [{}] [{}]", + self.display_mode, + self.angle_mode, + self.calculator_alignment, + if self.save_on_close { "W" } else { "w" } + ) + } + + /// Pushes l onto the stack if parseable and not empty returns result true if the value was changed + pub fn flush_l(&mut self) -> CalculatorResult { + if self.l.is_empty() { + return Ok(false); + } + + let f = self.l.parse::().or(Err(CalculatorError::ParseError))?; + self.push(Entry::Number(Number { value: f }))?; + self.l.clear(); + Ok(true) + } + /// Checks if the calculator is currently running a macro + fn within_macro(&self) -> bool { + !self.active_macros.is_empty() + } + + /// Pushes a value onto the stack and makes a state change + fn push(&mut self, f: Entry) -> CalculatorResult<()> { + self.direct_state_change(CalculatorStateChange { + pop: OpArgs::None, + push: OpArgs::Unary(f), + }) + } + /// Returns the value of the bottom of the stack by popping it using a state change + pub fn pop(&mut self) -> CalculatorResult { + let f = self.peek(0)?; + self.direct_state_change(CalculatorStateChange { + pop: OpArgs::Unary(f.clone()), + push: OpArgs::None, + })?; + Ok(f) + } + /// Returns a calculator value + fn peek(&mut self, idx: usize) -> CalculatorResult { + self.flush_l()?; + match self.stack.get(idx) { + None => Err(CalculatorError::NotEnoughStackEntries), + Some(r) => Ok(r.clone()), + } + } + /// Pops a precision instead of an Entry. Precisions are of type usize + pub fn pop_precision(&mut self) -> CalculatorResult { + let entry = self.peek(0)?; + let f = match entry { + Entry::Number(Number { value }) => value, + Entry::Vector(_) => return Err(CalculatorError::TypeMismatch), + }; + // Ensure this can be cast to a usize + if !f.is_finite() || f.is_sign_negative() { + return Err(CalculatorError::ArithmeticError); + } + #[allow(clippy::cast_sign_loss)] + let u = f as usize; + + if u > MAX_PRECISION { + return Err(CalculatorError::PrecisionTooHigh); + } + + self.direct_state_change(CalculatorStateChange { + pop: OpArgs::Unary(entry), + push: OpArgs::None, + })?; + Ok(u) + } + + /// Performs a calculator operation such as undo, redo, operator, or dup + pub fn op(&mut self, op: CalculatorOperation) -> CalculatorResult<()> { + // Dup is special -- don't actually run it if l needs to be flushed + if self.flush_l()? { + if let CalculatorOperation::Dup = op { + return Ok(()); + } + } + let state_change = match op { + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Add) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.add(a)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Subtract) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.sub(a)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Multiply) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.mul(a)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Divide) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.div(a)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::IntegerDivide) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.int_divide(a)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Negate) => { + self.unary_op(|a| Ok(OpArgs::Unary(a.negate()?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::AbsoluteValue) => { + self.unary_op(|a| Ok(OpArgs::Unary(a.abs()?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Inverse) => { + self.unary_op(|a| Ok(OpArgs::Unary(a.inverse()?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Modulo) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.modulo(a)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Sin) => { + let angle_mode = self.angle_mode; + self.unary_op(|a| Ok(OpArgs::Unary(a.sin(angle_mode)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Cos) => { + let angle_mode = self.angle_mode; + self.unary_op(|a| Ok(OpArgs::Unary(a.cos(angle_mode)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Tan) => { + let angle_mode = self.angle_mode; + self.unary_op(|a| Ok(OpArgs::Unary(a.tan(angle_mode)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ASin) => { + let angle_mode = self.angle_mode; + self.unary_op(|a| Ok(OpArgs::Unary(a.asin(angle_mode)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ACos) => { + let angle_mode = self.angle_mode; + self.unary_op(|a| Ok(OpArgs::Unary(a.acos(angle_mode)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ATan) => { + let angle_mode = self.angle_mode; + self.unary_op(|a| Ok(OpArgs::Unary(a.atan(angle_mode)?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Sqrt) => { + self.unary_op(|a| Ok(OpArgs::Unary(a.sqrt()?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Log) => { + self.unary_op(|a| Ok(OpArgs::Unary(a.log()?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Ln) => { + self.unary_op(|a| Ok(OpArgs::Unary(a.ln()?))) + } + CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Pow) => { + self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.pow(a)?))) + } + CalculatorOperation::Dup => self.unary_op(|a| Ok(OpArgs::Binary([a.clone(), a]))), + CalculatorOperation::Drop => self.unary_op(|_| Ok(OpArgs::None)), + CalculatorOperation::Swap => self.binary_op(|[a, b]| Ok(OpArgs::Binary([b, a]))), + CalculatorOperation::Undo => return self.history_op(false), + CalculatorOperation::Redo => return self.history_op(true), + // Macros are a no-op operator; need to insert for undo/redo + CalculatorOperation::Macro(state) => { + if self.within_macro() { + // Do not push any states if we are already in a macro + return Ok(()); } - // Record the macro started, if this is the outer macro - self.op(CalculatorOperation::Macro(MacroState::Start))?; - - // Record that we are running macro c - self.active_macros.insert(c); - - // The macro needs to run in normal mode - self.state = CalculatorState::Normal; - - for c in value.chars() { - self.take_input(c).map_err(|e| { - // Try cancelling, but if you cannot, that is okay - self.cancel().unwrap_or(()); - e - })?; - } - // Macro c should be over now - self.active_macros.remove(&c); - - // Record the macro is over, if this is the outer macro - self.op(CalculatorOperation::Macro(MacroState::End))?; - - Ok(()) - } - fn register_input( - &mut self, - register_state: RegisterState, - c: char, - ) -> CalculatorResult<()> { - match register_state { - RegisterState::Save => { - let f = self.pop()?; - self.registers.insert(c, f); - } - RegisterState::Load => { - let f = self - .registers - .get(&c) - .ok_or(CalculatorError::NoSuchRegister(c))? - .clone(); - self.push(f)?; - } - } - - self.state = CalculatorState::Normal; - Ok(()) - } - fn setting_input(&mut self, c: char) -> CalculatorResult<()> { - self.flush_l()?; - match c { - 'q' => self.state = CalculatorState::Normal, - 'd' => self.angle_mode = CalculatorAngleMode::Degrees, - 'r' => self.angle_mode = CalculatorAngleMode::Radians, - 'g' => self.angle_mode = CalculatorAngleMode::Grads, - '_' => self.display_mode = CalculatorDisplayMode::Default, - ',' => { - self.display_mode = - CalculatorDisplayMode::Separated { separator: ',' } - } - ' ' => { - self.display_mode = - CalculatorDisplayMode::Separated { separator: ' ' } - } - 's' => { - self.display_mode = CalculatorDisplayMode::Scientific { - precision: DEFAULT_PRECISION, - } - } - 'S' => { - self.display_mode = CalculatorDisplayMode::Scientific { - precision: self.pop_precision()?, - } - } - 'e' => { - self.display_mode = CalculatorDisplayMode::Engineering { - precision: DEFAULT_PRECISION, - } - } - 'E' => { - self.display_mode = CalculatorDisplayMode::Engineering { - precision: self.pop_precision()?, - } - } - 'f' => { - self.display_mode = CalculatorDisplayMode::Fixed { - precision: DEFAULT_PRECISION, - } - } - 'F' => { - self.display_mode = CalculatorDisplayMode::Fixed { - precision: self.pop_precision()?, - } - } - 'w' => self.save_on_close = false, - 'W' => self.save_on_close = true, - 'L' => self.calculator_alignment = CalculatorAlignment::Left, - 'R' => self.calculator_alignment = CalculatorAlignment::Right, - _ => return Err(CalculatorError::NoSuchSetting(c)), - }; - self.state = CalculatorState::Normal; - Ok(()) - } - - /// Resets the calculator state to normal, and exits out of a macro if one is running - pub fn cancel(&mut self) -> CalculatorResult<()> { - self.state = CalculatorState::Normal; - // We died in a macro. Quit and push an end macro state - if !self.active_macros.is_empty() { - self.active_macros.clear(); - // Should always be successful, but report the error if there is one - self.op(CalculatorOperation::Macro(MacroState::End))?; - } - Ok(()) - } - /// Handles the backspace key which only deletes a char a char from l - pub fn backspace(&mut self) -> CalculatorResult<()> { - self.l.pop(); - Ok(()) - } - /// Places the bottom of the stack into l for editing, only if the value is empty - pub fn edit(&mut self) -> CalculatorResult<()> { - if !self.l.is_empty() { - return Ok(()); - } - - self.l = self - .pop() - .or(Err(CalculatorError::NotEnoughStackEntries))? - .to_string(); - Ok(()) - } - /// Get the value of l - pub fn get_l(&mut self) -> &str { - self.l.as_ref() - } - - /// Returns a formatted string that shows the status of the calculator - pub fn get_status_line(&self) -> String { - format!( - "[{}] [{}] [{}] [{}]", - self.display_mode, - self.angle_mode, - self.calculator_alignment, - if self.save_on_close { "W" } else { "w" } - ) - } - - /// Pushes l onto the stack if parseable and not empty returns result true if the value was changed - pub fn flush_l(&mut self) -> CalculatorResult { - if self.l.is_empty() { - return Ok(false); - } - - let f = self.l.parse::().or(Err(CalculatorError::ParseError))?; - self.push(Entry::Number(Number { value: f }))?; - self.l.clear(); - Ok(true) - } - /// Checks if the calculator is currently running a macro - fn within_macro(&self) -> bool { - !self.active_macros.is_empty() - } - - /// Pushes a value onto the stack and makes a state change - fn push(&mut self, f: Entry) -> CalculatorResult<()> { - self.direct_state_change(CalculatorStateChange { - pop: OpArgs::None, - push: OpArgs::Unary(f), - }) - } - /// Returns the value of the bottom of the stack by popping it using a state change - pub fn pop(&mut self) -> CalculatorResult { - let f = self.peek(0)?; - self.direct_state_change(CalculatorStateChange { - pop: OpArgs::Unary(f.clone()), - push: OpArgs::None, - })?; - Ok(f) - } - /// Returns a calculator value - fn peek(&mut self, idx: usize) -> CalculatorResult { - self.flush_l()?; - match self.stack.get(idx) { - None => Err(CalculatorError::NotEnoughStackEntries), - Some(r) => Ok(r.clone()), - } - } - /// Pops a precision instead of an Entry. Precisions are of type usize - pub fn pop_precision(&mut self) -> CalculatorResult { - let entry = self.peek(0)?; - let f = match entry { - Entry::Number(Number { value }) => value, - Entry::Vector(_) => return Err(CalculatorError::TypeMismatch), - }; - // Ensure this can be cast to a usize - if !f.is_finite() || f.is_sign_negative() { - return Err(CalculatorError::ArithmeticError); - } - #[allow(clippy::cast_sign_loss)] - let u = f as usize; - - if u > MAX_PRECISION { - return Err(CalculatorError::PrecisionTooHigh); - } - - self.direct_state_change(CalculatorStateChange { - pop: OpArgs::Unary(entry), - push: OpArgs::None, - })?; - Ok(u) - } - - /// Performs a calculator operation such as undo, redo, operator, or dup - pub fn op(&mut self, op: CalculatorOperation) -> CalculatorResult<()> { - // Dup is special -- don't actually run it if l needs to be flushed - if self.flush_l()? { - if let CalculatorOperation::Dup = op { - return Ok(()); - } - } - let state_change = match op { - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Add) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.add(a)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Subtract) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.sub(a)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Multiply) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.mul(a)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Divide) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.div(a)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::IntegerDivide) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.int_divide(a)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Negate) => { - self.unary_op(|a| Ok(OpArgs::Unary(a.negate()?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::AbsoluteValue) => { - self.unary_op(|a| Ok(OpArgs::Unary(a.abs()?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Inverse) => { - self.unary_op(|a| Ok(OpArgs::Unary(a.inverse()?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Modulo) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.modulo(a)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Sin) => { - let angle_mode = self.angle_mode; - self.unary_op(|a| Ok(OpArgs::Unary(a.sin(angle_mode)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Cos) => { - let angle_mode = self.angle_mode; - self.unary_op(|a| Ok(OpArgs::Unary(a.cos(angle_mode)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Tan) => { - let angle_mode = self.angle_mode; - self.unary_op(|a| Ok(OpArgs::Unary(a.tan(angle_mode)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ASin) => { - let angle_mode = self.angle_mode; - self.unary_op(|a| Ok(OpArgs::Unary(a.asin(angle_mode)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ACos) => { - let angle_mode = self.angle_mode; - self.unary_op(|a| Ok(OpArgs::Unary(a.acos(angle_mode)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::ATan) => { - let angle_mode = self.angle_mode; - self.unary_op(|a| Ok(OpArgs::Unary(a.atan(angle_mode)?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Sqrt) => { - self.unary_op(|a| Ok(OpArgs::Unary(a.sqrt()?))) - } - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Log) => self.unary_op(|a| Ok(OpArgs::Unary(a.log()?))), - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Ln) => self.unary_op(|a| Ok(OpArgs::Unary(a.ln()?))), - CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Pow) => { - self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.pow(a)?))) - } - CalculatorOperation::Dup => { - self.unary_op(|a| Ok(OpArgs::Binary([a.clone(), a]))) - } - CalculatorOperation::Drop => self.unary_op(|_| Ok(OpArgs::None)), - CalculatorOperation::Swap => { - self.binary_op(|[a, b]| Ok(OpArgs::Binary([b, a]))) - } - CalculatorOperation::Undo => return self.history_op(false), - CalculatorOperation::Redo => return self.history_op(true), - // Macros are a no-op operator; need to insert for undo/redo - CalculatorOperation::Macro(state) => { - if self.within_macro() { - // Do not push any states if we are already in a macro - return Ok(()); - } - - Ok(CalculatorStateChange { - pop: OpArgs::None, - push: OpArgs::Macro(state), - }) - } - }; - - self.direct_state_change(state_change?) - } - - /// Performs a history operation, either an undo or redo - /// - /// This will undo until it reaches a macro boundary, so this effictively undoes or redoes all macro operations in one stroke - fn history_op(&mut self, forward: bool) -> CalculatorResult<()> { - let s = if forward { - &self.redo_buf - } else { - &self.undo_buf - } - .last() - .ok_or_else(|| { - CalculatorError::EmptyHistory(String::from(if forward { - "redo" - } else { - "undo" - })) - })?; - - let target_history_mode = if forward { - MacroState::Start - } else { - MacroState::End - }; - - let history_mode = match s { - CalculatorStateChange { - push: OpArgs::Macro(m), - .. - } if *m == target_history_mode => Ok(HistoryMode::Macro), - CalculatorStateChange { - push: OpArgs::Macro(_), - .. - } => Err(CalculatorError::CorruptStateChange(String::from( - "macro start is out of bounds", - ))), - _ => Ok(HistoryMode::One), - }?; - - loop { - let s = if forward { - self.redo_buf.pop() - } else { - self.undo_buf.pop() - } - .ok_or_else(|| CalculatorError::EmptyHistory(String::from("redo")))?; - - let macro_end = s.push - == OpArgs::Macro(if forward { - MacroState::End - } else { - MacroState::Start - }); - self.apply_state_change(s, forward)?; - - if history_mode == HistoryMode::One || macro_end { - return Ok(()); - } - } - } - - /// Performs a state change and clears the redo buf. This is used when *not* undoing/redoing. - fn direct_state_change(&mut self, c: CalculatorStateChange) -> CalculatorResult<()> { - let result = self.apply_state_change(c, true); - if result.is_ok() { - // Clear the redo buffer since this was a direct state change - self.redo_buf = vec![]; - } - result - } - - /// Applies a state change to the stack. Pass in the state change and the state change is applied forward or backwards - fn apply_state_change( - &mut self, - c: CalculatorStateChange, - forward: bool, - ) -> CalculatorResult<()> { - let (to_pop, to_push) = if forward { - (&c.pop, &c.push) - } else { - (&c.push, &c.pop) - }; - - match to_push { - OpArgs::Unary(a) => { - if !a.is_valid() { - return Err(CalculatorError::ArithmeticError); - } - } - OpArgs::Binary([a, b]) => { - if !a.is_valid() || !b.is_valid() { - return Err(CalculatorError::ArithmeticError); - } - } - OpArgs::Macro(_) | OpArgs::None => {} - }; - - match to_pop { - OpArgs::Unary(a) => { - self.stack_eq(0, a)?; - self.stack.pop_front(); - } - OpArgs::Binary([a, b]) => { - self.stack_eq(0, a)?; - self.stack_eq(1, b)?; - self.stack.pop_front(); - self.stack.pop_front(); - } - OpArgs::Macro(_) | OpArgs::None => {} - }; - - match to_push { - OpArgs::Unary(a) => { - self.stack.push_front(a.clone()); // TODO: Remove the clones - } - OpArgs::Binary([a, b]) => { - self.stack.push_front(b.clone()); // TODO: Remove the clones - self.stack.push_front(a.clone()); // TODO: Remove the clones - } - OpArgs::Macro(_) | OpArgs::None => {} - }; - - if forward { - self.undo_buf.push(c); - } else { - self.redo_buf.push(c); - } - - Ok(()) - } - - /// Checks if a value on the stack is equal to a given value - fn stack_eq(&mut self, idx: usize, value: &Entry) -> CalculatorResult<()> { - if self.peek(idx)? == *value { - Err(CalculatorError::CorruptStateChange(format!( - "Stack index {} should be {}, but is {}", - idx, - value, - self.peek(idx)?, - ))) - } else { - Ok(()) - } - } - - /// Performs a state change on a unary operation - fn unary_op( - &mut self, - op: impl FnOnce(Entry) -> CalculatorResult, - ) -> CalculatorResult { - let arg = self.peek(0)?; Ok(CalculatorStateChange { - pop: OpArgs::Unary(arg.clone()), - push: op(arg)?, + pop: OpArgs::None, + push: OpArgs::Macro(state), }) + } + }; + + self.direct_state_change(state_change?) + } + + /// Performs a history operation, either an undo or redo + /// + /// This will undo until it reaches a macro boundary, so this effictively undoes or redoes all macro operations in one stroke + fn history_op(&mut self, forward: bool) -> CalculatorResult<()> { + let s = if forward { + &self.redo_buf + } else { + &self.undo_buf } - /// Performs a state change on a binary operation - fn binary_op( - &mut self, - op: impl FnOnce([Entry; 2]) -> CalculatorResult, - ) -> CalculatorResult { - let args: [Entry; 2] = [self.peek(0)?, self.peek(1)?]; - Ok(CalculatorStateChange { - pop: OpArgs::Binary(args.clone()), - push: op(args)?, - }) + .last() + .ok_or_else(|| { + CalculatorError::EmptyHistory(String::from(if forward { "redo" } else { "undo" })) + })?; + + let target_history_mode = if forward { + MacroState::Start + } else { + MacroState::End + }; + + let history_mode = match s { + CalculatorStateChange { + push: OpArgs::Macro(m), + .. + } if *m == target_history_mode => Ok(HistoryMode::Macro), + CalculatorStateChange { + push: OpArgs::Macro(_), + .. + } => Err(CalculatorError::CorruptStateChange(String::from( + "macro start is out of bounds", + ))), + _ => Ok(HistoryMode::One), + }?; + + loop { + let s = if forward { + self.redo_buf.pop() + } else { + self.undo_buf.pop() + } + .ok_or_else(|| CalculatorError::EmptyHistory(String::from("redo")))?; + + let macro_end = s.push + == OpArgs::Macro(if forward { + MacroState::End + } else { + MacroState::Start + }); + self.apply_state_change(s, forward)?; + + if history_mode == HistoryMode::One || macro_end { + return Ok(()); + } } + } + + /// Performs a state change and clears the redo buf. This is used when *not* undoing/redoing. + fn direct_state_change(&mut self, c: CalculatorStateChange) -> CalculatorResult<()> { + let result = self.apply_state_change(c, true); + if result.is_ok() { + // Clear the redo buffer since this was a direct state change + self.redo_buf = vec![]; + } + result + } + + /// Applies a state change to the stack. Pass in the state change and the state change is applied forward or backwards + fn apply_state_change( + &mut self, + c: CalculatorStateChange, + forward: bool, + ) -> CalculatorResult<()> { + let (to_pop, to_push) = if forward { + (&c.pop, &c.push) + } else { + (&c.push, &c.pop) + }; + + match to_push { + OpArgs::Unary(a) => { + if !a.is_valid() { + return Err(CalculatorError::ArithmeticError); + } + } + OpArgs::Binary([a, b]) => { + if !a.is_valid() || !b.is_valid() { + return Err(CalculatorError::ArithmeticError); + } + } + OpArgs::Macro(_) | OpArgs::None => {} + }; + + match to_pop { + OpArgs::Unary(a) => { + self.stack_eq(0, a)?; + self.stack.pop_front(); + } + OpArgs::Binary([a, b]) => { + self.stack_eq(0, a)?; + self.stack_eq(1, b)?; + self.stack.pop_front(); + self.stack.pop_front(); + } + OpArgs::Macro(_) | OpArgs::None => {} + }; + + match to_push { + OpArgs::Unary(a) => { + self.stack.push_front(a.clone()); // TODO: Remove the clones + } + OpArgs::Binary([a, b]) => { + self.stack.push_front(b.clone()); // TODO: Remove the clones + self.stack.push_front(a.clone()); // TODO: Remove the clones + } + OpArgs::Macro(_) | OpArgs::None => {} + }; + + if forward { + self.undo_buf.push(c); + } else { + self.redo_buf.push(c); + } + + Ok(()) + } + + /// Checks if a value on the stack is equal to a given value + fn stack_eq(&mut self, idx: usize, value: &Entry) -> CalculatorResult<()> { + if self.peek(idx)? == *value { + Err(CalculatorError::CorruptStateChange(format!( + "Stack index {} should be {}, but is {}", + idx, + value, + self.peek(idx)?, + ))) + } else { + Ok(()) + } + } + + /// Performs a state change on a unary operation + fn unary_op( + &mut self, + op: impl FnOnce(Entry) -> CalculatorResult, + ) -> CalculatorResult { + let arg = self.peek(0)?; + Ok(CalculatorStateChange { + pop: OpArgs::Unary(arg.clone()), + push: op(arg)?, + }) + } + /// Performs a state change on a binary operation + fn binary_op( + &mut self, + op: impl FnOnce([Entry; 2]) -> CalculatorResult, + ) -> CalculatorResult { + let args: [Entry; 2] = [self.peek(0)?, self.peek(1)?]; + Ok(CalculatorStateChange { + pop: OpArgs::Binary(args.clone()), + push: op(args)?, + }) + } } // #[cfg(test)] diff --git a/src/calc/entries.rs b/src/calc/entries.rs index 9a294a0..7b2c5f9 100644 --- a/src/calc/entries.rs +++ b/src/calc/entries.rs @@ -9,554 +9,537 @@ use std::fmt; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct Number { - pub value: f64, + pub value: f64, } impl Number { - fn binary_op(arg: &Entry, op: impl Fn(&Number) -> Number) -> CalculatorResult { - match arg { - Entry::Number(number) => Ok(Entry::Number(op(number))), - Entry::Vector(vector) => Ok(vector.map_into(op)), - } + fn binary_op(arg: &Entry, op: impl Fn(&Number) -> Number) -> CalculatorResult { + match arg { + Entry::Number(number) => Ok(Entry::Number(op(number))), + Entry::Vector(vector) => Ok(vector.map_into(op)), } + } } impl PartialEq for Number { - fn eq(&self, other: &Self) -> bool { - if self.value.is_nan() && other.value.is_nan() - || self.value.is_infinite() && other.value.is_infinite() - { - true - } else if self.value.is_nan() - || self.value.is_infinite() - || other.value.is_infinite() - || other.value.is_nan() - { - false - } else { - (self.value - other.value).abs() >= f64::EPSILON - } + fn eq(&self, other: &Self) -> bool { + if self.value.is_nan() && other.value.is_nan() + || self.value.is_infinite() && other.value.is_infinite() + { + true + } else if self.value.is_nan() + || self.value.is_infinite() + || other.value.is_infinite() + || other.value.is_nan() + { + false + } else { + (self.value - other.value).abs() >= f64::EPSILON } + } } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct Vector { - pub direction: bool, - pub values: Vec, + pub direction: bool, + pub values: Vec, } impl Vector { - fn binary_op( - &self, - arg: &Entry, - op: impl Fn((&Number, &Number)) -> Number, - ) -> CalculatorResult { - match arg { - // Entry::Number(number) => Ok(Entry::Number(op(number))), - Entry::Number(number) => Ok(self.map_into(|t| op((t, number)))), - Entry::Vector(vector) => { - if self.values.len() != vector.values.len() { - return Err(CalculatorError::ArithmeticError); - } - - Ok(Entry::Vector(Vector { - values: self - .values - .iter() - .zip(vector.values.iter()) - .map(op) - .collect(), - ..*self - })) - } + fn binary_op( + &self, + arg: &Entry, + op: impl Fn((&Number, &Number)) -> Number, + ) -> CalculatorResult { + match arg { + // Entry::Number(number) => Ok(Entry::Number(op(number))), + Entry::Number(number) => Ok(self.map_into(|t| op((t, number)))), + Entry::Vector(vector) => { + if self.values.len() != vector.values.len() { + return Err(CalculatorError::ArithmeticError); } - } - pub fn map_into(&self, op: impl Fn(&Number) -> Number) -> Entry { - Entry::Vector(Vector { - values: (self.values.iter().map(op).collect()), - ..*self - }) + Ok(Entry::Vector(Vector { + values: self + .values + .iter() + .zip(vector.values.iter()) + .map(op) + .collect(), + ..*self + })) + } } + } + + pub fn map_into(&self, op: impl Fn(&Number) -> Number) -> Entry { + Entry::Vector(Vector { + values: (self.values.iter().map(op).collect()), + ..*self + }) + } } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum Entry { - Number(Number), - Vector(Vector), - // Matrix(Vec>), + Number(Number), + Vector(Vector), + // Matrix(Vec>), } impl CalculatorEntry for Entry { - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { - match self { - Self::Number(number) => number.format_entry(display_mode), - Self::Vector(vector) => vector.format_entry(display_mode), - } + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { + match self { + Self::Number(number) => number.format_entry(display_mode), + Self::Vector(vector) => vector.format_entry(display_mode), } - fn is_valid(&self) -> bool { - match self { - Self::Number(number) => number.is_valid(), - Self::Vector(vector) => vector.is_valid(), - } + } + fn is_valid(&self) -> bool { + match self { + Self::Number(number) => number.is_valid(), + Self::Vector(vector) => vector.is_valid(), } - fn add(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.add(arg), - Self::Vector(vector) => vector.add(arg), - } + } + fn add(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.add(arg), + Self::Vector(vector) => vector.add(arg), } - fn sub(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.sub(arg), - Self::Vector(vector) => vector.sub(arg), - } + } + fn sub(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.sub(arg), + Self::Vector(vector) => vector.sub(arg), } - fn mul(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.mul(arg), - Self::Vector(vector) => vector.mul(arg), - } + } + fn mul(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.mul(arg), + Self::Vector(vector) => vector.mul(arg), } - fn div(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.div(arg), - Self::Vector(vector) => vector.div(arg), - } + } + fn div(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.div(arg), + Self::Vector(vector) => vector.div(arg), } - fn int_divide(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.int_divide(arg), - Self::Vector(vector) => vector.int_divide(arg), - } + } + fn int_divide(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.int_divide(arg), + Self::Vector(vector) => vector.int_divide(arg), } - fn negate(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.negate(), - Self::Vector(vector) => vector.negate(), - } + } + fn negate(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.negate(), + Self::Vector(vector) => vector.negate(), } - fn abs(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.abs(), - Self::Vector(vector) => vector.abs(), - } + } + fn abs(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.abs(), + Self::Vector(vector) => vector.abs(), } - fn inverse(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.inverse(), - Self::Vector(vector) => vector.inverse(), - } + } + fn inverse(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.inverse(), + Self::Vector(vector) => vector.inverse(), } - fn modulo(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.modulo(arg), - Self::Vector(vector) => vector.modulo(arg), - } + } + fn modulo(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.modulo(arg), + Self::Vector(vector) => vector.modulo(arg), } - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.sin(angle_mode), - Self::Vector(vector) => vector.sin(angle_mode), - } + } + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.sin(angle_mode), + Self::Vector(vector) => vector.sin(angle_mode), } - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.cos(angle_mode), - Self::Vector(vector) => vector.cos(angle_mode), - } + } + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.cos(angle_mode), + Self::Vector(vector) => vector.cos(angle_mode), } - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.tan(angle_mode), - Self::Vector(vector) => vector.tan(angle_mode), - } + } + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.tan(angle_mode), + Self::Vector(vector) => vector.tan(angle_mode), } - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.asin(angle_mode), - Self::Vector(vector) => vector.asin(angle_mode), - } + } + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.asin(angle_mode), + Self::Vector(vector) => vector.asin(angle_mode), } - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.acos(angle_mode), - Self::Vector(vector) => vector.acos(angle_mode), - } + } + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.acos(angle_mode), + Self::Vector(vector) => vector.acos(angle_mode), } - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - match self { - Self::Number(number) => number.atan(angle_mode), - Self::Vector(vector) => vector.atan(angle_mode), - } + } + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + match self { + Self::Number(number) => number.atan(angle_mode), + Self::Vector(vector) => vector.atan(angle_mode), } - fn sqrt(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.sqrt(), - Self::Vector(vector) => vector.sqrt(), - } + } + fn sqrt(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.sqrt(), + Self::Vector(vector) => vector.sqrt(), } - fn log(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.log(), - Self::Vector(vector) => vector.log(), - } + } + fn log(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.log(), + Self::Vector(vector) => vector.log(), } - fn ln(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.ln(), - Self::Vector(vector) => vector.ln(), - } + } + fn ln(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.ln(), + Self::Vector(vector) => vector.ln(), } - fn pow(&self, arg: Self) -> CalculatorResult { - match self { - Self::Number(number) => number.pow(arg), - Self::Vector(vector) => vector.pow(arg), - } + } + fn pow(&self, arg: Self) -> CalculatorResult { + match self { + Self::Number(number) => number.pow(arg), + Self::Vector(vector) => vector.pow(arg), } + } } impl CalculatorEntry for Number { - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { - match display_mode { - CalculatorDisplayMode::Default => format!("{}", self.value), - CalculatorDisplayMode::Separated { separator } => { - separated(self.value, *separator) - } - CalculatorDisplayMode::Scientific { 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 { - !self.value.is_nan() && !self.value.is_infinite() - } - fn add(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value + self.value, - }) - } - fn sub(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value - self.value, - }) - } - fn mul(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value * self.value, - }) - } - fn div(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value / self.value, - }) - } - fn int_divide(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value.div_euclid(self.value), - }) - } - fn negate(&self) -> CalculatorResult { - Ok(Entry::Number(Self { value: -self.value })) - } - fn abs(&self) -> CalculatorResult { - Ok(Entry::Number(Self { - value: self.value.abs(), - })) - } - fn inverse(&self) -> CalculatorResult { - Ok(Entry::Number(Self { - value: self.value.recip(), - })) - } - fn modulo(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value % self.value, - }) - } - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().sin(), - CalculatorAngleMode::Radians => self.value.sin(), - CalculatorAngleMode::Grads => { - (self.value * std::f64::consts::PI / 200.0).sin() - } - }, - })) - } - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().cos(), - CalculatorAngleMode::Radians => self.value.cos(), - CalculatorAngleMode::Grads => { - (self.value * std::f64::consts::PI / 200.0).cos() - } - }, - })) - } - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().tan(), - CalculatorAngleMode::Radians => self.value.tan(), - CalculatorAngleMode::Grads => { - (self.value * std::f64::consts::PI / 200.0).tan() - } - }, - })) - } - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), - CalculatorAngleMode::Radians => self.value.asin(), - CalculatorAngleMode::Grads => { - self.value.asin() * std::f64::consts::PI / 200.0 - } - }, - })) - } - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - 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 atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - 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 { - Ok(Entry::Number(Self { - value: self.value.sqrt(), - })) - } - fn log(&self) -> CalculatorResult { - Ok(Entry::Number(Self { - value: self.value.log10(), - })) - } - fn ln(&self) -> CalculatorResult { - Ok(Entry::Number(Self { - value: self.value.ln(), - })) - } - fn pow(&self, arg: Entry) -> CalculatorResult { - Number::binary_op(&arg, |b| Self { - value: b.value.powf(self.value), - }) + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { + match display_mode { + CalculatorDisplayMode::Default => format!("{}", self.value), + CalculatorDisplayMode::Separated { separator } => separated(self.value, *separator), + CalculatorDisplayMode::Scientific { 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 { + !self.value.is_nan() && !self.value.is_infinite() + } + fn add(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value + self.value, + }) + } + fn sub(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value - self.value, + }) + } + fn mul(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value * self.value, + }) + } + fn div(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value / self.value, + }) + } + fn int_divide(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value.div_euclid(self.value), + }) + } + fn negate(&self) -> CalculatorResult { + Ok(Entry::Number(Self { value: -self.value })) + } + fn abs(&self) -> CalculatorResult { + Ok(Entry::Number(Self { + value: self.value.abs(), + })) + } + fn inverse(&self) -> CalculatorResult { + Ok(Entry::Number(Self { + value: self.value.recip(), + })) + } + fn modulo(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value % self.value, + }) + } + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Self { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.to_radians().sin(), + CalculatorAngleMode::Radians => self.value.sin(), + CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).sin(), + }, + })) + } + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Self { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.to_radians().cos(), + CalculatorAngleMode::Radians => self.value.cos(), + CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).cos(), + }, + })) + } + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Self { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.to_radians().tan(), + CalculatorAngleMode::Radians => self.value.tan(), + CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).tan(), + }, + })) + } + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Self { + value: match angle_mode { + CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), + CalculatorAngleMode::Radians => self.value.asin(), + CalculatorAngleMode::Grads => self.value.asin() * std::f64::consts::PI / 200.0, + }, + })) + } + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Self { + 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 atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + Ok(Entry::Number(Self { + 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 { + Ok(Entry::Number(Self { + value: self.value.sqrt(), + })) + } + fn log(&self) -> CalculatorResult { + Ok(Entry::Number(Self { + value: self.value.log10(), + })) + } + fn ln(&self) -> CalculatorResult { + Ok(Entry::Number(Self { + value: self.value.ln(), + })) + } + fn pow(&self, arg: Entry) -> CalculatorResult { + Number::binary_op(&arg, |b| Self { + value: b.value.powf(self.value), + }) + } } impl CalculatorEntry for Vector { - // Misc - fn is_valid(&self) -> bool { - !self.values - .iter() - .find(|number| !number.is_valid()) - .is_some() - } - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { - format!( - "[{}]", - self.values - .iter() - .map(|number| number.format_entry(display_mode)) - .collect::>() - .join("; ") - ) - } - // Mathematical operations - fn add(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value + a.value, - }) - } - fn sub(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value - a.value, - }) - } - fn mul(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value * a.value, - }) - } - fn div(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value / a.value, - }) - } - fn int_divide(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value.div_euclid(a.value), - }) - } - fn negate(&self) -> CalculatorResult { - Ok(self.map_into(|n| Number { value: -n.value })) - } - fn abs(&self) -> CalculatorResult { - Ok(self.map_into(|n| Number { - value: n.value.abs(), - })) - } - fn inverse(&self) -> CalculatorResult { - Ok(Entry::Vector(Vector { - direction: !self.direction, - ..*self - })) - } - fn modulo(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value % a.value, - }) - } - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} - fn sqrt(&self) -> CalculatorResult {} - fn log(&self) -> CalculatorResult {} - fn ln(&self) -> CalculatorResult {} - fn pow(&self, arg: Entry) -> CalculatorResult { - self.binary_op(&arg, |(a, b)| Number { - value: b.value.powf(a.value), - }) - } + // Misc + fn is_valid(&self) -> bool { + !self + .values + .iter() + .find(|number| !number.is_valid()) + .is_some() + } + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { + format!( + "[{}]", + self.values + .iter() + .map(|number| number.format_entry(display_mode)) + .collect::>() + .join("; ") + ) + } + // Mathematical operations + fn add(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value + a.value, + }) + } + fn sub(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value - a.value, + }) + } + fn mul(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value * a.value, + }) + } + fn div(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value / a.value, + }) + } + fn int_divide(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value.div_euclid(a.value), + }) + } + fn negate(&self) -> CalculatorResult { + Ok(self.map_into(|n| Number { value: -n.value })) + } + fn abs(&self) -> CalculatorResult { + Ok(self.map_into(|n| Number { + value: n.value.abs(), + })) + } + fn inverse(&self) -> CalculatorResult { + Ok(Entry::Vector(Vector { + direction: !self.direction, + ..*self + })) + } + fn modulo(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value % a.value, + }) + } + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult {} + fn sqrt(&self) -> CalculatorResult {} + fn log(&self) -> CalculatorResult {} + fn ln(&self) -> CalculatorResult {} + fn pow(&self, arg: Entry) -> CalculatorResult { + self.binary_op(&arg, |(a, b)| Number { + value: b.value.powf(a.value), + }) + } } pub trait CalculatorEntry { - // Misc - fn is_valid(&self) -> bool; - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; - // Mathematical operations - fn add(&self, arg: Entry) -> CalculatorResult; - fn sub(&self, arg: Entry) -> CalculatorResult; - fn mul(&self, arg: Entry) -> CalculatorResult; - fn div(&self, arg: Entry) -> CalculatorResult; - fn int_divide(&self, arg: Entry) -> CalculatorResult; - fn negate(&self) -> CalculatorResult; - fn abs(&self) -> CalculatorResult; - fn inverse(&self) -> CalculatorResult; - fn modulo(&self, arg: Entry) -> CalculatorResult; - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; - fn sqrt(&self) -> CalculatorResult; - fn log(&self) -> CalculatorResult; - fn ln(&self) -> CalculatorResult; - fn pow(&self, arg: Entry) -> CalculatorResult; + // Misc + fn is_valid(&self) -> bool; + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; + // Mathematical operations + fn add(&self, arg: Entry) -> CalculatorResult; + fn sub(&self, arg: Entry) -> CalculatorResult; + fn mul(&self, arg: Entry) -> CalculatorResult; + fn div(&self, arg: Entry) -> CalculatorResult; + fn int_divide(&self, arg: Entry) -> CalculatorResult; + fn negate(&self) -> CalculatorResult; + fn abs(&self) -> CalculatorResult; + fn inverse(&self) -> CalculatorResult; + fn modulo(&self, arg: Entry) -> CalculatorResult; + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult; + fn sqrt(&self) -> CalculatorResult; + fn log(&self) -> CalculatorResult; + fn ln(&self) -> CalculatorResult; + fn pow(&self, arg: Entry) -> CalculatorResult; } impl fmt::Display for Entry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Number(Number { value }) => write!(f, "{}", value), - } + 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) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self) + } } // Based on https://stackoverflow.com/a/65266882 fn scientific(f: f64, precision: usize) -> String { - let mut ret = format!("{:.precision$E}", f, precision = precision); - let exp = ret.split_off(ret.find('E').unwrap_or(0)); - let (exp_sign, exp) = exp - .strip_prefix("E-") - .map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped)); + let mut ret = format!("{:.precision$E}", f, precision = precision); + let exp = ret.split_off(ret.find('E').unwrap_or(0)); + let (exp_sign, exp) = exp + .strip_prefix("E-") + .map_or_else(|| ('+', &exp[1..]), |stripped| ('-', stripped)); - let sign = if ret.starts_with('-') { "" } else { " " }; - format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) + let sign = if ret.starts_with('-') { "" } else { " " }; + format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) } 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 - // 1,000 => 1000E3 - let all = format!(" {:.precision$E}", f, precision = precision) - // Remove . since it can be moved - .replacen(".", "", 1) - // Add 00E before E here so the length is enough for slicing below - .replacen("E", "00E", 1); - // Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E - // 1000E3 => (1000, E3) - 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 - // E3 => 3 as isize - let exp = exp_str[1..].parse::().unwrap(); - // Sign of the exponent. If string representation starts with E-, then negative - let display_exp_sign = if exp_str.strip_prefix("E-").is_some() { - '-' - } else { - '+' - }; + // 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 + let all = format!(" {:.precision$E}", f, precision = precision) + // Remove . since it can be moved + .replacen(".", "", 1) + // Add 00E before E here so the length is enough for slicing below + .replacen("E", "00E", 1); + // Extract mantissa and the string representation of the exponent. Unwrap should be safe as formatter will insert E + // 1000E3 => (1000, E3) + 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 + // E3 => 3 as isize + let exp = exp_str[1..].parse::().unwrap(); + // Sign of the exponent. If string representation starts with E-, then negative + 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(); - // Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility - let num_whole_digits = exp.rem_euclid(3) as usize + 1; + // 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(); + // Number of whole digits. Always 1, 2, or 3 depending on exponent divisibility + 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) - let num_str = if num_str.strip_prefix(" -").is_some() { - &num_str[1..] - } else { - num_str - }; + // 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() { + &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]; - // 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)]; - // Right align whole portion, always have decimal point - format!( - "{: >4}.{} E{}{:0>pad$}", - // display_sign, - whole, - decimal, - display_exp_sign, - display_exp, - pad = 2 - ) + // 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]; + // 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)]; + // Right align whole portion, always have decimal point + format!( + "{: >4}.{} E{}{:0>pad$}", + // display_sign, + whole, + decimal, + 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()); - for i in 0..((end - start - 1).div_euclid(3)) { - ret.insert(end - (i + 1) * 3, sep); - } - ret + let mut ret = f.to_string(); + 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 }