Get operations working with the calculator interface
This commit is contained in:
parent
e05b0726f1
commit
53891274f1
129
src/calc.rs
129
src/calc.rs
@ -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 => {}
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user