From 0405d2599833d91ae1ffe6c2dd3a5ec7ec00dd92 Mon Sep 17 00:00:00 2001 From: Austen Adler Date: Tue, 1 Jun 2021 23:52:51 -0400 Subject: [PATCH] Get vectors working --- src/calc.rs | 1440 ++++++++++++++++++------------------- src/calc/entries.rs | 1522 ++++++++++++++++++++-------------------- src/calc/errors.rs | 3 + src/calc/operations.rs | 82 +-- 4 files changed, 1509 insertions(+), 1538 deletions(-) diff --git a/src/calc.rs b/src/calc.rs index 2bea4dc..426e00c 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -8,16 +8,16 @@ use confy::{load, store}; use entries::{Entry, Number, Vector}; use errors::{CalculatorError, CalculatorResult}; use operations::{ - ArithmeticOperation, CalculatorOperation, CalculatorStateChange, MacroState, OpArgs, + 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 @@ -30,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![], @@ -155,729 +155,693 @@ 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), + } + } + // This function is very long, but it is just a match, so it should be fine + #[allow(clippy::too_many_lines)] + 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' => { + self.edit()?; - 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), + if !self.l.contains('e') { + self.l.push('e'); + } + Ok(()) } - } - // This function is very long, but it is just a match, so it should be fine - #[allow(clippy::too_many_lines)] - 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' => { - self.edit()?; - 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, - )), - // Temporary - 'V' => self.op(CalculatorOperation::BuildVector), - // 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, + )), + // Temporary + 'V' => self.op(CalculatorOperation::BuildVector), + // 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(()); + } + + // Temporary check to see if we can get an editable string + let str = self.peek(0)?.to_editable_string()?; + + // If we got here, then there was no error. Pop + self.pop()?; + + // Set l after popping + self.l = str; + + 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 usize + pub fn peek_usize(&mut self) -> CalculatorResult<(usize, Entry)> { + 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; + + Ok((u, entry)) + } + /// Pops a precision instead of an Entry. Precisions are of type usize + pub fn pop_precision(&mut self) -> CalculatorResult { + let (u, entry) = self.peek_usize()?; + + if u > MAX_PRECISION { + return Err(CalculatorError::PrecisionTooHigh); + } + + self.direct_state_change(CalculatorStateChange { + pop: OpArgs::Unary(entry), + push: OpArgs::None, + })?; + Ok(u) + } + + pub fn build_vector(&mut self) -> CalculatorResult { + let (count, count_entry) = self.peek_usize()?; + let (mut entries, values): (VecDeque, Vec) = (1..=count) + .map(|i| { + let e = self.peek(i)?; + match e { + // TODO: For now, vectors only contain numbers + Entry::Number(number) => Ok((e, number)), + Entry::Vector(_vector) => Err(CalculatorError::TypeMismatch), + } + }) + .collect::>>()? + // TODO: Do I have to iter again? + .into_iter() + .unzip(); + let entry = Entry::Vector(Vector { + values, + direction: true, + }); + entries.push_front(count_entry); + Ok(CalculatorStateChange { + pop: OpArgs::Variable(Vec::from(entries)), + push: OpArgs::Unary(entry), + }) + } + + /// 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::BuildVector => self.build_vector(), + 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(()); - } - - // Temporary check to see if we can get an editable string - let str = self.peek(0)?.to_editable_string()?; - - // If we got here, then there was no error. Pop - self.pop()?; - - // Set l after popping - self.l = str; - - 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), + Ok(CalculatorStateChange { + pop: OpArgs::None, + push: OpArgs::Macro(state), }) - } - /// 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 usize - pub fn pop_usize(&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; + } + }; - self.direct_state_change(CalculatorStateChange { - pop: OpArgs::Unary(entry), - push: OpArgs::None, - })?; + self.direct_state_change(state_change?) + } - Ok(u) + /// 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 } - /// 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; + .last() + .ok_or_else(|| { + CalculatorError::EmptyHistory(String::from(if forward { "redo" } else { "undo" })) + })?; - if u > MAX_PRECISION { - return Err(CalculatorError::PrecisionTooHigh); - } + let target_history_mode = if forward { + MacroState::Start + } else { + MacroState::End + }; - self.direct_state_change(CalculatorStateChange { - pop: OpArgs::Unary(entry), - push: OpArgs::None, - })?; - Ok(u) - } + 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), + }?; - pub fn build_vector(&mut self) -> CalculatorResult { - let count = self.pop_usize()?; - let (entries, values): (Vec, Vec) = (0..count) - .map(|i| { - let e = self.peek(i)?; - match e { - // TODO: For now, vectors only contain numbers - Entry::Number(number) => Ok((e, number)), - Entry::Vector(_vector) => { - Err(CalculatorError::TypeMismatch) - } - } - }) - .collect::>>()? - // TODO: Do I have to iter again? - .into_iter() - .unzip(); - let entry = Entry::Vector(Vector { - values, - direction: true, + 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 }); - Ok(CalculatorStateChange { - pop: OpArgs::Variable(entries), - push: OpArgs::Unary(entry), - }) + self.apply_state_change(s, forward)?; + + if history_mode == HistoryMode::One || macro_end { + return Ok(()); + } } + } - /// 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::BuildVector => self.build_vector(), - 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 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 + } - /// 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 + /// 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); } - .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(()); - } + } + OpArgs::Binary([a, b]) => { + if !a.is_valid() || !b.is_valid() { + return Err(CalculatorError::ArithmeticError); } - } - - /// 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![]; + } + OpArgs::Macro(_) | OpArgs::None => {} + OpArgs::Variable(entries) => { + if entries.iter().any(|e| !e.is_valid()) { + return Err(CalculatorError::ArithmeticError); } - 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 => {} - OpArgs::Variable(entries) => { - if entries.iter().any(|e| !e.is_valid()) { - return Err(CalculatorError::ArithmeticError); - } - } - }; - - 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::Variable(entries) => { - if entries - .iter() - .enumerate() - .any(|(i, e)| self.stack_eq(i, e).is_err()) - { - // TODO: Return an error if there is one - return Err(CalculatorError::ArithmeticError); - } - - for _ in 0..entries.len() { - 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::Variable(entries) => { - for e in entries { - self.stack.push_front(e.clone()); - } - } - OpArgs::Macro(_) | OpArgs::None => {} - }; - - if forward { - self.undo_buf.push(c); - } else { - self.redo_buf.push(c); + 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::Variable(entries) => { + if entries + .iter() + .enumerate() + .any(|(i, e)| self.stack_eq(i, e).is_err()) + { + // TODO: Return an error if there is one + return Err(CalculatorError::ArithmeticError); } - 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(()) + for _ in 0..entries.len() { + 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::Variable(entries) => { + for e in entries.iter().rev() { + self.stack.push_front(e.clone()); + } + } + OpArgs::Macro(_) | OpArgs::None => {} + }; + + if forward { + self.undo_buf.push(c); + } else { + self.redo_buf.push(c); } - /// 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)?, - }) + 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 f330fab..c5b54c6 100644 --- a/src/calc/entries.rs +++ b/src/calc/entries.rs @@ -9,39 +9,39 @@ use std::fmt; #[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct Number { - pub value: f64, + pub value: f64, } 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, } #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum Entry { - Number(Number), - Vector(Vector), - // Matrix(Vec>), + Number(Number), + Vector(Vector), + // Matrix(Vec>), } // macro_rules! op_child_call { @@ -60,833 +60,837 @@ pub enum Entry { // } impl CalculatorEntry for Entry { - fn to_editable_string(&self) -> CalculatorResult { - match self { - Self::Number(number) => number.to_editable_string(), - Self::Vector(vector) => vector.to_editable_string(), - } + fn to_editable_string(&self) -> CalculatorResult { + match self { + Self::Number(number) => number.to_editable_string(), + Self::Vector(vector) => vector.to_editable_string(), } - 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 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 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 add(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.add_num(number), - Self::Vector(vector) => self.add_vec(vector), - } + fn add(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.add_num(number), + Self::Vector(vector) => self.add_vec(vector), } - fn sub(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.sub_num(number), - Self::Vector(vector) => self.sub_vec(vector), - } + } + fn sub(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.sub_num(number), + Self::Vector(vector) => self.sub_vec(vector), } - fn mul(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.mul_num(number), - Self::Vector(vector) => self.mul_vec(vector), - } + } + fn mul(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.mul_num(number), + Self::Vector(vector) => self.mul_vec(vector), } - fn div(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.div_num(number), - Self::Vector(vector) => self.div_vec(vector), - } + } + fn div(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.div_num(number), + Self::Vector(vector) => self.div_vec(vector), } - fn int_divide(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.int_divide_num(number), - Self::Vector(vector) => self.int_divide_vec(vector), - } + } + fn int_divide(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.int_divide_num(number), + Self::Vector(vector) => self.int_divide_vec(vector), } - fn modulo(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.modulo_num(number), - Self::Vector(vector) => self.modulo_vec(vector), - } + } + fn modulo(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.modulo_num(number), + Self::Vector(vector) => self.modulo_vec(vector), } - fn pow(&self, arg: &Self) -> CalculatorResult { - match arg { - Self::Number(number) => self.pow_num(number), - Self::Vector(vector) => self.pow_vec(vector), - } + } + fn pow(&self, arg: &Self) -> CalculatorResult { + match arg { + Self::Number(number) => self.pow_num(number), + Self::Vector(vector) => self.pow_vec(vector), } + } - fn add_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.add_vec(arg), - Self::Vector(vector) => vector.add_vec(arg), - } + fn add_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.add_vec(arg), + Self::Vector(vector) => vector.add_vec(arg), } - fn sub_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.sub_vec(arg), - Self::Vector(vector) => vector.sub_vec(arg), - } + } + fn sub_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.sub_vec(arg), + Self::Vector(vector) => vector.sub_vec(arg), } - fn mul_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.mul_vec(arg), - Self::Vector(vector) => vector.mul_vec(arg), - } + } + fn mul_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.mul_vec(arg), + Self::Vector(vector) => vector.mul_vec(arg), } - fn div_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.div_vec(arg), - Self::Vector(vector) => vector.div_vec(arg), - } + } + fn div_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.div_vec(arg), + Self::Vector(vector) => vector.div_vec(arg), } - fn int_divide_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.int_divide_vec(arg), - Self::Vector(vector) => vector.int_divide_vec(arg), - } + } + fn int_divide_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.int_divide_vec(arg), + Self::Vector(vector) => vector.int_divide_vec(arg), } - fn modulo_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.modulo_vec(arg), - Self::Vector(vector) => vector.modulo_vec(arg), - } + } + fn modulo_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.modulo_vec(arg), + Self::Vector(vector) => vector.modulo_vec(arg), } - fn pow_vec(&self, arg: &Vector) -> CalculatorResult { - match self { - Self::Number(number) => number.pow_vec(arg), - Self::Vector(vector) => vector.pow_vec(arg), - } + } + fn pow_vec(&self, arg: &Vector) -> CalculatorResult { + match self { + Self::Number(number) => number.pow_vec(arg), + Self::Vector(vector) => vector.pow_vec(arg), } + } - fn add_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.add_num(arg), - Self::Vector(vector) => vector.add_num(arg), - } + fn add_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.add_num(arg), + Self::Vector(vector) => vector.add_num(arg), } - fn sub_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.sub_num(arg), - Self::Vector(vector) => vector.sub_num(arg), - } + } + fn sub_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.sub_num(arg), + Self::Vector(vector) => vector.sub_num(arg), } - fn mul_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.mul_num(arg), - Self::Vector(vector) => vector.mul_num(arg), - } + } + fn mul_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.mul_num(arg), + Self::Vector(vector) => vector.mul_num(arg), } - fn div_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.div_num(arg), - Self::Vector(vector) => vector.div_num(arg), - } + } + fn div_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.div_num(arg), + Self::Vector(vector) => vector.div_num(arg), } - fn int_divide_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.int_divide_num(arg), - Self::Vector(vector) => vector.int_divide_num(arg), - } + } + fn int_divide_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.int_divide_num(arg), + Self::Vector(vector) => vector.int_divide_num(arg), } - fn modulo_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.modulo_num(arg), - Self::Vector(vector) => vector.modulo_num(arg), - } + } + fn modulo_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.modulo_num(arg), + Self::Vector(vector) => vector.modulo_num(arg), } - fn pow_num(&self, arg: &Number) -> CalculatorResult { - match self { - Self::Number(number) => number.pow_num(arg), - Self::Vector(vector) => vector.pow_num(arg), - } - } -} - -impl CalculatorEntry for Number { - fn to_editable_string(&self) -> CalculatorResult { - Ok(format!("{}", 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 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 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 add(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.add_vec(vector), - Entry::Number(number) => self.add_num(number), - } - } - fn sub(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.sub_vec(vector), - Entry::Number(number) => self.sub_num(number), - } - } - fn mul(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.mul_vec(vector), - Entry::Number(number) => self.mul_num(number), - } - } - fn div(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.div_vec(vector), - Entry::Number(number) => self.div_num(number), - } - } - fn int_divide(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.int_divide_vec(vector), - Entry::Number(number) => self.int_divide_num(number), - } - } - fn modulo(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.modulo_vec(vector), - Entry::Number(number) => self.modulo_num(number), - } - } - fn pow(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.pow_vec(vector), - Entry::Number(number) => self.pow_num(number), - } - } - - fn add_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::add_num) - } - fn sub_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::sub_num) - } - fn mul_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::mul_num) - } - fn div_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::div_num) - } - fn int_divide_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::int_divide_num) - } - fn modulo_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::modulo_num) - } - fn pow_vec(&self, arg: &Vector) -> CalculatorResult { - self.iterated_binary(arg, Self::pow_num) - } - - fn add_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value + self.value, - })) - } - fn sub_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value - self.value, - })) - } - fn mul_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value * self.value, - })) - } - fn div_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value / self.value, - })) - } - fn int_divide_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value.div_euclid(self.value), - })) - } - fn modulo_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value % self.value, - })) - } - fn pow_num(&self, arg: &Number) -> CalculatorResult { - Ok(Entry::Number(Self { - value: arg.value.powf(self.value), - })) - } -} - -impl Number { - fn iterated_binary( - self, - vector: &Vector, - op: impl Fn(&Self, &Self) -> CalculatorResult, - ) -> CalculatorResult { - Ok(Entry::Vector(Vector { - values: vector - .values - .iter() - .map(|n| op(&self, n)) - .map(|e| match e { - // Only numbers are valid in a vector - Ok(Entry::Number(number)) => Ok(number), - _ => Err(CalculatorError::ArithmeticError), - }) - .collect::>>()?, - direction: vector.direction, - })) + } + fn pow_num(&self, arg: &Number) -> CalculatorResult { + match self { + Self::Number(number) => number.pow_num(arg), + Self::Vector(vector) => vector.pow_num(arg), } + } } impl CalculatorEntry for Vector { - // Misc - fn to_editable_string(&self) -> CalculatorResult { - // TODO: Eventualy we can parse and edit a vector as a string - Err(CalculatorError::TypeMismatch) - } - fn is_valid(&self) -> bool { - self.values - .iter() - .find(|number| !number.is_valid()) - .is_none() - } - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { - format!( - "[{}]", - self.values - .iter() - .map(|number| number.format_entry(display_mode)) - .collect::>() - .join("; ") - ) - } - // Mathematical operations - fn negate(&self) -> CalculatorResult { - self.iterated_unary(Number::negate) - } - fn abs(&self) -> CalculatorResult { - self.iterated_unary(Number::abs) - } - fn inverse(&self) -> CalculatorResult { - self.iterated_unary(Number::inverse) - } - fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - self.iterated_unary(|n| n.sin(angle_mode)) - } - fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - self.iterated_unary(|n| n.cos(angle_mode)) - } - fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - self.iterated_unary(|n| n.tan(angle_mode)) - } - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - self.iterated_unary(|n| n.asin(angle_mode)) - } - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - self.iterated_unary(|n| n.acos(angle_mode)) - } - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - self.iterated_unary(|n| n.atan(angle_mode)) - } - fn sqrt(&self) -> CalculatorResult { - self.iterated_unary(Number::sqrt) - } - fn log(&self) -> CalculatorResult { - self.iterated_unary(Number::log) - } - fn ln(&self) -> CalculatorResult { - self.iterated_unary(Number::ln) - } + // Misc + fn to_editable_string(&self) -> CalculatorResult { + // TODO: Eventualy we can parse and edit a vector as a string + Err(CalculatorError::TypeMismatch) + } + fn is_valid(&self) -> bool { + self.values.iter().all(|number| number.is_valid()) + } + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String { + format!( + "[{}]", + self.values + .iter() + .map(|number| number.format_entry(display_mode)) + .collect::>() + .join(if self.direction { "; " } else { " " }) + ) + } + // Mathematical operations + fn negate(&self) -> CalculatorResult { + self.iterated_unary(Number::negate) + } + fn abs(&self) -> CalculatorResult { + let value: Entry = self + .values + .iter() + .try_fold(Entry::Number(Number::ZERO), |acc, n2| { + acc.add(&n2.pow_num(&Number { value: 2.0_f64 })?) + })?; + Ok(value.sqrt()?) + } + fn inverse(&self) -> CalculatorResult { + Ok(Entry::Vector(Vector { + values: self.values.clone(), + direction: !self.direction, + })) + } + fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|n| n.sin(angle_mode)) + } + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|n| n.cos(angle_mode)) + } + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|n| n.tan(angle_mode)) + } + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|n| n.asin(angle_mode)) + } + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|n| n.acos(angle_mode)) + } + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|n| n.atan(angle_mode)) + } + fn sqrt(&self) -> CalculatorResult { + self.iterated_unary(Number::sqrt) + } + fn log(&self) -> CalculatorResult { + self.iterated_unary(Number::log) + } + fn ln(&self) -> CalculatorResult { + self.iterated_unary(Number::ln) + } - fn add(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.add_vec(vector), - Entry::Number(number) => self.add_num(number), - } + fn add(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.add_vec(vector), + Entry::Number(number) => self.add_num(number), } - fn sub(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.sub_vec(vector), - Entry::Number(number) => self.sub_num(number), - } + } + fn sub(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.sub_vec(vector), + Entry::Number(number) => self.sub_num(number), } - fn mul(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.mul_vec(vector), - Entry::Number(number) => self.mul_num(number), - } + } + fn mul(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.mul_vec(vector), + Entry::Number(number) => self.mul_num(number), } - fn div(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.div_vec(vector), - Entry::Number(number) => self.div_num(number), - } + } + fn div(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.div_vec(vector), + Entry::Number(number) => self.div_num(number), } - fn int_divide(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.int_divide_vec(vector), - Entry::Number(number) => self.int_divide_num(number), - } + } + fn int_divide(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.int_divide_vec(vector), + Entry::Number(number) => self.int_divide_num(number), } - fn modulo(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.modulo_vec(vector), - Entry::Number(number) => self.modulo_num(number), - } + } + fn modulo(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.modulo_vec(vector), + Entry::Number(number) => self.modulo_num(number), } - fn pow(&self, arg: &Entry) -> CalculatorResult { - match arg { - Entry::Vector(vector) => self.pow_vec(vector), - Entry::Number(number) => self.pow_num(number), - } + } + fn pow(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.pow_vec(vector), + Entry::Number(number) => self.pow_num(number), } + } - fn add_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::add_num) - } - fn sub_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::sub_num) - } - fn mul_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::mul_num) - } - fn div_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::div_num) - } - fn int_divide_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::int_divide_num) - } - fn modulo_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::modulo_num) - } - fn pow_vec(&self, arg: &Self) -> CalculatorResult { - self.iterated_binary_vec(arg, Number::pow_num) - } + fn add_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::add_num) + } + fn sub_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::sub_num) + } + fn mul_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::mul_num) + } + fn div_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::div_num) + } + fn int_divide_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::int_divide_num) + } + fn modulo_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::modulo_num) + } + fn pow_vec(&self, arg: &Self) -> CalculatorResult { + self.iterated_binary_vec(arg, Number::pow_num) + } - fn add_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::add_num) - } - fn sub_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::sub_num) - } - fn mul_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::mul_num) - } - fn div_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::div_num) - } - fn int_divide_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::int_divide_num) - } - fn modulo_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::modulo_num) - } - fn pow_num(&self, arg: &Number) -> CalculatorResult { - self.iterated_binary_num(arg, Number::pow_num) - } + fn add_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::add_num) + } + fn sub_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::sub_num) + } + fn mul_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::mul_num) + } + fn div_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::div_num) + } + fn int_divide_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::int_divide_num) + } + fn modulo_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::modulo_num) + } + fn pow_num(&self, arg: &Number) -> CalculatorResult { + self.iterated_binary_num(arg, Number::pow_num) + } } impl Vector { - fn iterated_unary( - &self, - op: impl Fn(&Number) -> CalculatorResult, - ) -> CalculatorResult { - Ok(Entry::Vector(Self { - values: self - .values - .iter() - .map(|n| op(n)) - .map(|e| match e { - // Only numbers are valid in a vector - Ok(Entry::Number(number)) => Ok(number), - _ => Err(CalculatorError::ArithmeticError), - }) - .collect::>>()?, - direction: self.direction, - })) - } + fn iterated_unary( + &self, + op: impl Fn(&Number) -> CalculatorResult, + ) -> CalculatorResult { + Ok(Entry::Vector(Self { + values: self + .values + .iter() + .map(|n| op(n)) + .map(|e| match e { + // Only numbers are valid in a vector + Ok(Entry::Number(number)) => Ok(number), + _ => Err(CalculatorError::ArithmeticError), + }) + .collect::>>()?, + direction: self.direction, + })) + } - fn iterated_binary_vec( - &self, - v2: &Self, - op: impl Fn(&Number, &Number) -> CalculatorResult, - ) -> CalculatorResult { - if self.values.len() != v2.values.len() { - return Err(CalculatorError::ArithmeticError); - } - if self.direction != v2.direction { - // TODO: Return a different error - return Err(CalculatorError::ArithmeticError); - } - Ok(Entry::Vector(Self { - values: self - .values - .iter() - .zip(v2.values.iter()) - .map(|(n1, n2)| op(n1, n2)) - .map(|e| match e { - // Only numbers are valid in a vector - Ok(Entry::Number(number)) => Ok(number), - _ => Err(CalculatorError::ArithmeticError), - }) - .collect::>>()?, - direction: self.direction, - })) + fn iterated_binary_vec( + &self, + v2: &Self, + op: impl Fn(&Number, &Number) -> CalculatorResult, + ) -> CalculatorResult { + if self.values.len() != v2.values.len() { + return Err(CalculatorError::DimensionMismatch); } - fn iterated_binary_num( - &self, - n2: &Number, - op: impl Fn(&Number, &Number) -> CalculatorResult, - ) -> CalculatorResult { - Ok(Entry::Vector(Self { - values: self - .values - .iter() - .map(|n| op(n, n2)) - .map(|e| match e { - // Only numbers are valid in a vector - Ok(Entry::Number(number)) => Ok(number), - _ => Err(CalculatorError::ArithmeticError), - }) - .collect::>>()?, - direction: self.direction, - })) + if self.direction != v2.direction { + return Err(CalculatorError::DimensionMismatch); } + Ok(Entry::Vector(Self { + values: self + .values + .iter() + .zip(v2.values.iter()) + .map(|(n1, n2)| op(n1, n2)) + .map(|e| match e { + // Only numbers are valid in a vector + Ok(Entry::Number(number)) => Ok(number), + _ => Err(CalculatorError::ArithmeticError), + }) + .collect::>>()?, + direction: self.direction, + })) + } + fn iterated_binary_num( + &self, + n2: &Number, + op: impl Fn(&Number, &Number) -> CalculatorResult, + ) -> CalculatorResult { + Ok(Entry::Vector(Self { + values: self + .values + .iter() + .map(|n| op(n, n2)) + .map(|e| match e { + // Only numbers are valid in a vector + Ok(Entry::Number(number)) => Ok(number), + _ => Err(CalculatorError::ArithmeticError), + }) + .collect::>>()?, + direction: self.direction, + })) + } +} + +impl CalculatorEntry for Number { + fn to_editable_string(&self) -> CalculatorResult { + Ok(format!("{}", 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 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 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 add(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.add_vec(vector), + Entry::Number(number) => self.add_num(number), + } + } + fn sub(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.sub_vec(vector), + Entry::Number(number) => self.sub_num(number), + } + } + fn mul(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.mul_vec(vector), + Entry::Number(number) => self.mul_num(number), + } + } + fn div(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.div_vec(vector), + Entry::Number(number) => self.div_num(number), + } + } + fn int_divide(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.int_divide_vec(vector), + Entry::Number(number) => self.int_divide_num(number), + } + } + fn modulo(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.modulo_vec(vector), + Entry::Number(number) => self.modulo_num(number), + } + } + fn pow(&self, arg: &Entry) -> CalculatorResult { + match arg { + Entry::Vector(vector) => self.pow_vec(vector), + Entry::Number(number) => self.pow_num(number), + } + } + + fn add_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::add_num) + } + fn sub_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::sub_num) + } + fn mul_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::mul_num) + } + fn div_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::div_num) + } + fn int_divide_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::int_divide_num) + } + fn modulo_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::modulo_num) + } + fn pow_vec(&self, arg: &Vector) -> CalculatorResult { + self.iterated_binary(arg, Self::pow_num) + } + + fn add_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value + arg.value, + } + .validate() + } + fn sub_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value - arg.value, + } + .validate() + } + fn mul_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value * arg.value, + } + .validate() + } + fn div_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value / arg.value, + } + .validate() + } + fn int_divide_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value.div_euclid(arg.value), + } + .validate() + } + fn modulo_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value % arg.value, + } + .validate() + } + fn pow_num(&self, arg: &Number) -> CalculatorResult { + Self { + value: self.value.powf(arg.value), + } + .validate() + } +} + +impl Number { + pub const ZERO: Self = Self { value: 0.0_f64 }; + + fn validate(&self) -> CalculatorResult { + if self.is_valid() { + Ok(Entry::Number(*self)) + } else { + Err(CalculatorError::ArithmeticError) + } + } + + fn iterated_binary( + self, + vector: &Vector, + op: impl Fn(&Self, &Self) -> CalculatorResult, + ) -> CalculatorResult { + Ok(Entry::Vector(Vector { + values: vector + .values + .iter() + .map(|n| op(&self, n)) + .map(|e| match e { + // Only numbers are valid in a vector + Ok(Entry::Number(number)) => Ok(number), + _ => Err(CalculatorError::ArithmeticError), + }) + .collect::>>()?, + direction: vector.direction, + })) + } } pub trait CalculatorEntry where - Self: Sized, + Self: Sized, { - // Misc - fn is_valid(&self) -> bool; - fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; - fn to_editable_string(&self) -> CalculatorResult; - // Mathematical operations - // Binary - 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 modulo(&self, arg: &Entry) -> CalculatorResult; - fn pow(&self, arg: &Entry) -> CalculatorResult; + // Misc + fn is_valid(&self) -> bool; + fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String; + fn to_editable_string(&self) -> CalculatorResult; + // Mathematical operations + // Binary + 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 modulo(&self, arg: &Entry) -> CalculatorResult; + fn pow(&self, arg: &Entry) -> CalculatorResult; - fn add_vec(&self, arg: &Vector) -> CalculatorResult; - fn sub_vec(&self, arg: &Vector) -> CalculatorResult; - fn mul_vec(&self, arg: &Vector) -> CalculatorResult; - fn div_vec(&self, arg: &Vector) -> CalculatorResult; - fn int_divide_vec(&self, arg: &Vector) -> CalculatorResult; - fn modulo_vec(&self, arg: &Vector) -> CalculatorResult; - fn pow_vec(&self, arg: &Vector) -> CalculatorResult; + fn add_vec(&self, arg: &Vector) -> CalculatorResult; + fn sub_vec(&self, arg: &Vector) -> CalculatorResult; + fn mul_vec(&self, arg: &Vector) -> CalculatorResult; + fn div_vec(&self, arg: &Vector) -> CalculatorResult; + fn int_divide_vec(&self, arg: &Vector) -> CalculatorResult; + fn modulo_vec(&self, arg: &Vector) -> CalculatorResult; + fn pow_vec(&self, arg: &Vector) -> CalculatorResult; - fn add_num(&self, arg: &Number) -> CalculatorResult; - fn sub_num(&self, arg: &Number) -> CalculatorResult; - fn mul_num(&self, arg: &Number) -> CalculatorResult; - fn div_num(&self, arg: &Number) -> CalculatorResult; - fn int_divide_num(&self, arg: &Number) -> CalculatorResult; - fn modulo_num(&self, arg: &Number) -> CalculatorResult; - fn pow_num(&self, arg: &Number) -> CalculatorResult; + fn add_num(&self, arg: &Number) -> CalculatorResult; + fn sub_num(&self, arg: &Number) -> CalculatorResult; + fn mul_num(&self, arg: &Number) -> CalculatorResult; + fn div_num(&self, arg: &Number) -> CalculatorResult; + fn int_divide_num(&self, arg: &Number) -> CalculatorResult; + fn modulo_num(&self, arg: &Number) -> CalculatorResult; + fn pow_num(&self, arg: &Number) -> CalculatorResult; - // Unary - fn negate(&self) -> CalculatorResult; - fn abs(&self) -> CalculatorResult; - fn inverse(&self) -> 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; + // Unary + fn negate(&self) -> CalculatorResult; + fn abs(&self) -> CalculatorResult; + fn inverse(&self) -> 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; } impl fmt::Display for Entry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::Number(number) => write!(f, "{}", number), - Self::Vector(vector) => write!(f, "{}", vector), - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Number(number) => write!(f, "{}", number), + Self::Vector(vector) => write!(f, "{}", vector), } + } } 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.value) + } } impl fmt::Display for Vector { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "[{}]", - self.values - .iter() - .map(|number| format!("{}", number)) - .collect::>() - .join("; ") - ) - } + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "[{}]", + self.values + .iter() + .map(|number| format!("{}", number)) + .collect::>() + .join("; ") + ) + } } // 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 } diff --git a/src/calc/errors.rs b/src/calc/errors.rs index d2695b1..7421c8a 100644 --- a/src/calc/errors.rs +++ b/src/calc/errors.rs @@ -13,6 +13,8 @@ pub enum CalculatorError { NotEnoughStackEntries, /// Requested type does not match target type TypeMismatch, + /// Dimensions must match + DimensionMismatch, /// Thrown when an undo or redo cannot be performed CorruptStateChange(String), /// Cannot undo or redo @@ -47,6 +49,7 @@ impl fmt::Display for CalculatorError { Self::ArithmeticError => write!(f, "Arithmetic Error"), Self::NotEnoughStackEntries => write!(f, "Not enough items in the stack"), Self::TypeMismatch => write!(f, "Type mismatch"), + Self::DimensionMismatch => write!(f, "Dimension mismatch"), Self::CorruptStateChange(msg) => { write!(f, "Corrupt state change: {}", msg) } diff --git a/src/calc/operations.rs b/src/calc/operations.rs index 75d73f9..70d16c1 100644 --- a/src/calc/operations.rs +++ b/src/calc/operations.rs @@ -3,64 +3,64 @@ use serde::{Deserialize, Serialize}; #[derive(PartialEq, Debug, Serialize, Deserialize)] pub enum ArithmeticOperation { - Add, - Subtract, - Multiply, - Divide, - Negate, - AbsoluteValue, - Inverse, - Modulo, - IntegerDivide, - Sin, - Cos, - Tan, - ASin, - ACos, - ATan, - Sqrt, - Pow, - Log, - Ln, + Add, + Subtract, + Multiply, + Divide, + Negate, + AbsoluteValue, + Inverse, + Modulo, + IntegerDivide, + Sin, + Cos, + Tan, + ASin, + ACos, + ATan, + Sqrt, + Pow, + Log, + Ln, } /// Operations that can be sent to the calculator such as +, -, or undo #[derive(PartialEq, Debug, Serialize, Deserialize)] pub enum CalculatorOperation { - ArithmeticOperation(ArithmeticOperation), - BuildVector, - Undo, - Redo, - Drop, - Dup, - Swap, - Macro(MacroState), + ArithmeticOperation(ArithmeticOperation), + BuildVector, + Undo, + Redo, + Drop, + Dup, + Swap, + Macro(MacroState), } /// Macro bundary; defined by the start or end of a macro invocation #[derive(PartialEq, Debug, Serialize, Deserialize)] pub enum MacroState { - Start, - End, + Start, + End, } /// Arguments for a given operation #[derive(PartialEq, Debug, Serialize, Deserialize)] pub enum OpArgs { - /// This is a macro start and end noop - Macro(MacroState), - /// Operation takes 1 argument, ex: sqrt or negate - Unary(Entry), - /// Operation takes 2 arguments, ex: + or - - Binary([Entry; 2]), - /// Some variable number of changes - Variable(Vec), - /// Operation takes no arguments, ex: push - None, + /// This is a macro start and end noop + Macro(MacroState), + /// Operation takes 1 argument, ex: sqrt or negate + Unary(Entry), + /// Operation takes 2 arguments, ex: + or - + Binary([Entry; 2]), + /// Some variable number of changes + Variable(Vec), + /// Operation takes no arguments, ex: push + None, } /// Record of what to pop and push. Used for undo and redo buffers #[derive(PartialEq, Debug, Serialize, Deserialize)] pub struct CalculatorStateChange { - pub pop: OpArgs, - pub push: OpArgs, + pub pop: OpArgs, + pub push: OpArgs, }