Get operations working with the calculator interface

This commit is contained in:
Austen Adler 2021-06-01 22:01:07 -04:00
parent e05b0726f1
commit 53891274f1
3 changed files with 1543 additions and 1427 deletions

View File

@ -5,7 +5,7 @@ pub mod types;
use crate::calc::entries::CalculatorEntry;
use confy::{load, store};
use entries::{Entry, Number};
use entries::{Entry, Number, Vector};
use errors::{CalculatorError, CalculatorResult};
use operations::{
ArithmeticOperation, CalculatorOperation, CalculatorStateChange, MacroState, OpArgs,
@ -16,8 +16,8 @@ use std::collections::{BTreeMap, HashMap};
use std::collections::{HashSet, VecDeque};
use types::{
CalculatorAlignment, CalculatorAngleMode, CalculatorConstant, CalculatorConstants,
CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorRegisters, CalculatorState,
RegisterState,
CalculatorDisplayMode, CalculatorMacro, CalculatorMacros, CalculatorRegisters,
CalculatorState, RegisterState,
};
/// The maximum precision allowed for the calculator
@ -269,6 +269,8 @@ impl Calculator {
'L' => self.op(CalculatorOperation::ArithmeticOperation(
ArithmeticOperation::Ln,
)),
// Temporary
'V' => self.op(CalculatorOperation::BuildVector),
// Special
'\\' => self.op(CalculatorOperation::Drop),
' ' => self.op(CalculatorOperation::Dup),
@ -281,11 +283,13 @@ impl Calculator {
Ok(())
}
'r' => {
self.state = CalculatorState::WaitingForRegister(RegisterState::Load);
self.state =
CalculatorState::WaitingForRegister(RegisterState::Load);
Ok(())
}
'R' => {
self.state = CalculatorState::WaitingForRegister(RegisterState::Save);
self.state =
CalculatorState::WaitingForRegister(RegisterState::Save);
Ok(())
}
'`' => {
@ -344,7 +348,11 @@ impl Calculator {
Ok(())
}
fn register_input(&mut self, register_state: RegisterState, c: char) -> CalculatorResult<()> {
fn register_input(
&mut self,
register_state: RegisterState,
c: char,
) -> CalculatorResult<()> {
match register_state {
RegisterState::Save => {
let f = self.pop()?;
@ -371,8 +379,14 @@ impl Calculator {
'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: ' ' },
',' => {
self.display_mode =
CalculatorDisplayMode::Separated { separator: ',' }
}
' ' => {
self.display_mode =
CalculatorDisplayMode::Separated { separator: ' ' }
}
's' => {
self.display_mode = CalculatorDisplayMode::Scientific {
precision: DEFAULT_PRECISION,
@ -502,6 +516,27 @@ impl Calculator {
Some(r) => Ok(r.clone()),
}
}
/// Pops a usize
pub fn pop_usize(&mut self) -> CalculatorResult<usize> {
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,
})?;
Ok(u)
}
/// Pops a precision instead of an Entry. Precisions are of type usize
pub fn pop_precision(&mut self) -> CalculatorResult<usize> {
let entry = self.peek(0)?;
@ -527,6 +562,33 @@ impl Calculator {
Ok(u)
}
pub fn build_vector(&mut self) -> CalculatorResult<CalculatorStateChange> {
let count = self.pop_usize()?;
let (entries, values): (Vec<Entry>, Vec<Number>) = (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::<CalculatorResult<Vec<(Entry, Number)>>>()?
// TODO: Do I have to iter again?
.into_iter()
.unzip();
let entry = Entry::Vector(Vector {
values,
direction: true,
});
Ok(CalculatorStateChange {
pop: OpArgs::Variable(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
@ -548,15 +610,15 @@ impl Calculator {
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::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::AbsoluteValue,
) => self.unary_op(|a| Ok(OpArgs::Unary(a.abs()?))),
CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Inverse) => {
self.unary_op(|a| Ok(OpArgs::Unary(a.inverse()?)))
}
@ -599,9 +661,14 @@ impl Calculator {
CalculatorOperation::ArithmeticOperation(ArithmeticOperation::Pow) => {
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.pow(&a)?)))
}
CalculatorOperation::Dup => self.unary_op(|a| Ok(OpArgs::Binary([a.clone(), a]))),
CalculatorOperation::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::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
@ -632,7 +699,11 @@ impl Calculator {
}
.last()
.ok_or_else(|| {
CalculatorError::EmptyHistory(String::from(if forward { "redo" } else { "undo" }))
CalculatorError::EmptyHistory(String::from(if forward {
"redo"
} else {
"undo"
}))
})?;
let target_history_mode = if forward {
@ -711,6 +782,11 @@ impl Calculator {
}
}
OpArgs::Macro(_) | OpArgs::None => {}
OpArgs::Variable(entries) => {
if entries.iter().any(|e| !e.is_valid()) {
return Err(CalculatorError::ArithmeticError);
}
}
};
match to_pop {
@ -724,6 +800,20 @@ impl Calculator {
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 => {}
};
@ -735,6 +825,11 @@ impl Calculator {
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 => {}
};

View File

@ -288,9 +288,15 @@ impl CalculatorEntry for Number {
fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String {
match display_mode {
CalculatorDisplayMode::Default => format!("{}", self.value),
CalculatorDisplayMode::Separated { separator } => separated(self.value, *separator),
CalculatorDisplayMode::Scientific { precision } => scientific(self.value, *precision),
CalculatorDisplayMode::Engineering { precision } => engineering(self.value, *precision),
CalculatorDisplayMode::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)
}
@ -317,7 +323,9 @@ impl CalculatorEntry for Number {
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(),
CalculatorAngleMode::Grads => {
(self.value * std::f64::consts::PI / 200.0).sin()
}
},
}))
}
@ -326,7 +334,9 @@ impl CalculatorEntry for Number {
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(),
CalculatorAngleMode::Grads => {
(self.value * std::f64::consts::PI / 200.0).cos()
}
},
}))
}
@ -335,7 +345,9 @@ impl CalculatorEntry for Number {
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(),
CalculatorAngleMode::Grads => {
(self.value * std::f64::consts::PI / 200.0).tan()
}
},
}))
}
@ -344,7 +356,9 @@ impl CalculatorEntry for Number {
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,
CalculatorAngleMode::Grads => {
self.value.asin() * std::f64::consts::PI / 200.0
}
},
}))
}
@ -353,7 +367,9 @@ impl CalculatorEntry for Number {
value: match angle_mode {
CalculatorAngleMode::Degrees => self.value.acos().to_degrees(),
CalculatorAngleMode::Radians => self.value.acos(),
CalculatorAngleMode::Grads => self.value.acos() * std::f64::consts::PI / 200.0,
CalculatorAngleMode::Grads => {
self.value.acos() * std::f64::consts::PI / 200.0
}
},
}))
}
@ -362,7 +378,9 @@ impl CalculatorEntry for Number {
value: match angle_mode {
CalculatorAngleMode::Degrees => self.value.atan().to_degrees(),
CalculatorAngleMode::Radians => self.value.atan(),
CalculatorAngleMode::Grads => self.value.atan() * std::f64::consts::PI / 200.0,
CalculatorAngleMode::Grads => {
self.value.atan() * std::f64::consts::PI / 200.0
}
},
}))
}
@ -670,7 +688,7 @@ impl Vector {
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Number>>>()?,
direction: true,
direction: self.direction,
}))
}
@ -698,7 +716,7 @@ impl Vector {
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Number>>>()?,
direction: true,
direction: self.direction,
}))
}
fn iterated_binary_num(
@ -717,7 +735,7 @@ impl Vector {
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Number>>>()?,
direction: true,
direction: self.direction,
}))
}
}

View File

@ -27,6 +27,7 @@ pub enum ArithmeticOperation {
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub enum CalculatorOperation {
ArithmeticOperation(ArithmeticOperation),
BuildVector,
Undo,
Redo,
Drop,
@ -51,6 +52,8 @@ pub enum OpArgs {
Unary(Entry),
/// Operation takes 2 arguments, ex: + or -
Binary([Entry; 2]),
/// Some variable number of changes
Variable(Vec<Entry>),
/// Operation takes no arguments, ex: push
None,
}