Start work on complex types
This commit is contained in:
parent
4b0e6e7e10
commit
445ae3f535
365
src/calc.rs
365
src/calc.rs
@ -1,8 +1,11 @@
|
||||
pub mod entries;
|
||||
pub mod errors;
|
||||
pub mod operations;
|
||||
pub mod types;
|
||||
use crate::calc::entries::CalculatorEntry;
|
||||
|
||||
use confy::{load, store};
|
||||
use entries::{Entry, Number};
|
||||
use errors::{CalculatorError, CalculatorResult};
|
||||
use operations::{CalculatorOperation, CalculatorStateChange, MacroState, OpArgs};
|
||||
use serde::ser::Serializer;
|
||||
@ -11,8 +14,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
|
||||
@ -37,7 +40,9 @@ pub struct Calculator {
|
||||
#[serde(skip)]
|
||||
l: String,
|
||||
/// The stack
|
||||
pub stack: VecDeque<f64>,
|
||||
// TODO: Serialize
|
||||
#[serde(skip)]
|
||||
pub stack: VecDeque<Entry>,
|
||||
/// True if the user would like to save on quit
|
||||
save_on_close: bool,
|
||||
/// Left or right aligned display
|
||||
@ -93,7 +98,7 @@ impl Default for Calculator {
|
||||
active_macros: HashSet::new(),
|
||||
registers: CalculatorRegisters::new(),
|
||||
state: CalculatorState::Normal,
|
||||
stack: vec![1.0, 2.0].into_iter().collect(),
|
||||
stack: vec![].into_iter().collect(),
|
||||
save_on_close: false,
|
||||
macros: [
|
||||
(
|
||||
@ -119,21 +124,27 @@ impl Default for Calculator {
|
||||
't',
|
||||
CalculatorConstant {
|
||||
help: String::from("Tau (2pi)"),
|
||||
value: Entry::Number(Number {
|
||||
value: std::f64::consts::TAU,
|
||||
}),
|
||||
},
|
||||
),
|
||||
(
|
||||
'e',
|
||||
CalculatorConstant {
|
||||
help: String::from("Euler's Number e"),
|
||||
value: Entry::Number(Number {
|
||||
value: std::f64::consts::E,
|
||||
}),
|
||||
},
|
||||
),
|
||||
(
|
||||
'p',
|
||||
CalculatorConstant {
|
||||
help: String::from("Pi"),
|
||||
value: Entry::Number(Number {
|
||||
value: std::f64::consts::PI,
|
||||
}),
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -177,7 +188,8 @@ impl Calculator {
|
||||
}
|
||||
fn normal_input(&mut self, c: char) -> CalculatorResult<()> {
|
||||
match c {
|
||||
c @ '0'..='9' | c @ '.' | c @ 'e' => match c {
|
||||
c @ '0'..='9' | c @ '.' | c @ 'e' => {
|
||||
match c {
|
||||
'0'..='9' => {
|
||||
self.l.push(c);
|
||||
Ok(())
|
||||
@ -202,7 +214,8 @@ impl Calculator {
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(CalculatorError::ParseError),
|
||||
},
|
||||
}
|
||||
}
|
||||
'+' => self.op(CalculatorOperation::Add),
|
||||
'-' => self.op(CalculatorOperation::Subtract),
|
||||
'*' => self.op(CalculatorOperation::Multiply),
|
||||
@ -234,11 +247,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(())
|
||||
}
|
||||
'`' => {
|
||||
@ -257,7 +272,8 @@ impl Calculator {
|
||||
.constants
|
||||
.get(&c)
|
||||
.ok_or(CalculatorError::NoSuchConstant(c))?
|
||||
.value;
|
||||
.value
|
||||
.clone();
|
||||
|
||||
self.push(f)?;
|
||||
self.state = CalculatorState::Normal;
|
||||
@ -296,7 +312,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()?;
|
||||
@ -306,8 +326,8 @@ impl Calculator {
|
||||
let f = self
|
||||
.registers
|
||||
.get(&c)
|
||||
.ok_or(CalculatorError::NoSuchRegister(c))?;
|
||||
let f = *f;
|
||||
.ok_or(CalculatorError::NoSuchRegister(c))?
|
||||
.clone();
|
||||
self.push(f)?;
|
||||
}
|
||||
}
|
||||
@ -323,8 +343,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,
|
||||
@ -416,7 +442,7 @@ impl Calculator {
|
||||
}
|
||||
|
||||
let f = self.l.parse::<f64>().or(Err(CalculatorError::ParseError))?;
|
||||
self.push(f)?;
|
||||
self.push(Entry::Number(Number { value: f }))?;
|
||||
self.l.clear();
|
||||
Ok(true)
|
||||
}
|
||||
@ -426,32 +452,36 @@ impl Calculator {
|
||||
}
|
||||
|
||||
/// Pushes a value onto the stack and makes a state change
|
||||
fn push(&mut self, f: f64) -> CalculatorResult<()> {
|
||||
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<f64> {
|
||||
pub fn pop(&mut self) -> CalculatorResult<Entry> {
|
||||
let f = self.peek(0)?;
|
||||
self.direct_state_change(CalculatorStateChange {
|
||||
pop: OpArgs::Unary(f),
|
||||
pop: OpArgs::Unary(f.clone()),
|
||||
push: OpArgs::None,
|
||||
})?;
|
||||
Ok(f)
|
||||
}
|
||||
/// Returns a calculator value
|
||||
fn peek(&mut self, idx: usize) -> CalculatorResult<f64> {
|
||||
fn peek(&mut self, idx: usize) -> CalculatorResult<Entry> {
|
||||
self.flush_l()?;
|
||||
match self.stack.get(idx) {
|
||||
None => Err(CalculatorError::NotEnoughStackEntries),
|
||||
Some(r) => Ok(*r),
|
||||
Some(r) => Ok(r.clone()),
|
||||
}
|
||||
}
|
||||
/// Pops a precision instead of an f64. Precisions are of type usize
|
||||
/// Pops a precision instead of an Entry. Precisions are of type usize
|
||||
pub fn pop_precision(&mut self) -> CalculatorResult<usize> {
|
||||
let f = self.peek(0)?;
|
||||
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);
|
||||
@ -464,7 +494,7 @@ impl Calculator {
|
||||
}
|
||||
|
||||
self.direct_state_change(CalculatorStateChange {
|
||||
pop: OpArgs::Unary(f),
|
||||
pop: OpArgs::Unary(entry),
|
||||
push: OpArgs::None,
|
||||
})?;
|
||||
Ok(u)
|
||||
@ -478,68 +508,58 @@ impl Calculator {
|
||||
}
|
||||
}
|
||||
let state_change = match op {
|
||||
CalculatorOperation::Add => self.binary_op(|[a, b]| OpArgs::Unary(b + a)),
|
||||
CalculatorOperation::Subtract => self.binary_op(|[a, b]| OpArgs::Unary(b - a)),
|
||||
CalculatorOperation::Multiply => self.binary_op(|[a, b]| OpArgs::Unary(b * a)),
|
||||
CalculatorOperation::Divide => self.binary_op(|[a, b]| OpArgs::Unary(b / a)),
|
||||
CalculatorOperation::Add => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.add(a)?)))
|
||||
}
|
||||
CalculatorOperation::Subtract => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.sub(a)?)))
|
||||
}
|
||||
CalculatorOperation::Multiply => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.mul(a)?)))
|
||||
}
|
||||
CalculatorOperation::Divide => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.div(a)?)))
|
||||
}
|
||||
CalculatorOperation::IntegerDivide => {
|
||||
self.binary_op(|[a, b]| OpArgs::Unary(b.div_euclid(a)))
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.int_divide(a)?)))
|
||||
}
|
||||
CalculatorOperation::Negate => self.unary_op(|a| OpArgs::Unary(-a)),
|
||||
CalculatorOperation::AbsoluteValue => self.unary_op(|a| OpArgs::Unary(a.abs())),
|
||||
CalculatorOperation::Inverse => self.unary_op(|a| OpArgs::Unary(a.recip())),
|
||||
CalculatorOperation::Modulo => self.binary_op(|[a, b]| OpArgs::Unary(b % a)),
|
||||
//CalculatorOperation::Remainder => self.binary_op(|[a, b]| OpArgs::Unary(b.rem_euclid(a))),
|
||||
CalculatorOperation::Dup => self.unary_op(|a| OpArgs::Binary([a, a])),
|
||||
CalculatorOperation::Drop => self.unary_op(|_| OpArgs::None),
|
||||
CalculatorOperation::Swap => self.binary_op(|[a, b]| OpArgs::Binary([b, a])),
|
||||
CalculatorOperation::Sin => self.unary_op(match self.angle_mode {
|
||||
CalculatorAngleMode::Degrees => |a: f64| OpArgs::Unary(a.to_radians().sin()),
|
||||
CalculatorAngleMode::Radians => |a: f64| OpArgs::Unary(a.sin()),
|
||||
CalculatorAngleMode::Grads => {
|
||||
|a: f64| OpArgs::Unary((a * std::f64::consts::PI / 200.0).sin())
|
||||
CalculatorOperation::Negate => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.negate()?)))
|
||||
}
|
||||
}),
|
||||
CalculatorOperation::Cos => self.unary_op(match self.angle_mode {
|
||||
CalculatorAngleMode::Degrees => |a: f64| OpArgs::Unary(a.to_radians().cos()),
|
||||
CalculatorAngleMode::Radians => |a: f64| OpArgs::Unary(a.cos()),
|
||||
CalculatorAngleMode::Grads => {
|
||||
|a: f64| OpArgs::Unary((a * std::f64::consts::PI / 200.0).cos())
|
||||
CalculatorOperation::AbsoluteValue => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.abs()?)))
|
||||
}
|
||||
}),
|
||||
CalculatorOperation::Tan => self.unary_op(match self.angle_mode {
|
||||
CalculatorAngleMode::Degrees => |a: f64| OpArgs::Unary(a.to_radians().tan()),
|
||||
CalculatorAngleMode::Radians => |a: f64| OpArgs::Unary(a.tan()),
|
||||
CalculatorAngleMode::Grads => {
|
||||
|a: f64| OpArgs::Unary((a * std::f64::consts::PI / 200.0).tan())
|
||||
CalculatorOperation::Inverse => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.inverse()?)))
|
||||
}
|
||||
}),
|
||||
CalculatorOperation::ASin => self.unary_op(match self.angle_mode {
|
||||
CalculatorAngleMode::Degrees => |a: f64| OpArgs::Unary(a.asin().to_degrees()),
|
||||
CalculatorAngleMode::Radians => |a: f64| OpArgs::Unary(a.asin()),
|
||||
CalculatorAngleMode::Grads => {
|
||||
|a: f64| OpArgs::Unary(a.asin() * std::f64::consts::PI / 200.0)
|
||||
CalculatorOperation::Modulo => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.modulo(a)?)))
|
||||
}
|
||||
}),
|
||||
CalculatorOperation::ACos => self.unary_op(match self.angle_mode {
|
||||
CalculatorAngleMode::Degrees => |a: f64| OpArgs::Unary(a.acos().to_degrees()),
|
||||
CalculatorAngleMode::Radians => |a: f64| OpArgs::Unary(a.acos()),
|
||||
CalculatorAngleMode::Grads => {
|
||||
|a: f64| OpArgs::Unary(a.acos() * std::f64::consts::PI / 200.0)
|
||||
CalculatorOperation::Sin => self.unary_op(|a| Ok(OpArgs::Unary(a.sin()?))),
|
||||
CalculatorOperation::Cos => self.unary_op(|a| Ok(OpArgs::Unary(a.cos()?))),
|
||||
CalculatorOperation::Tan => self.unary_op(|a| Ok(OpArgs::Unary(a.tan()?))),
|
||||
CalculatorOperation::ASin => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.asin()?)))
|
||||
}
|
||||
}),
|
||||
CalculatorOperation::ATan => self.unary_op(match self.angle_mode {
|
||||
CalculatorAngleMode::Degrees => |a: f64| OpArgs::Unary(a.atan().to_degrees()),
|
||||
CalculatorAngleMode::Radians => |a: f64| OpArgs::Unary(a.atan()),
|
||||
CalculatorAngleMode::Grads => {
|
||||
|a: f64| OpArgs::Unary(a.atan() * std::f64::consts::PI / 200.0)
|
||||
CalculatorOperation::ACos => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.acos()?)))
|
||||
}
|
||||
CalculatorOperation::ATan => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.atan()?)))
|
||||
}
|
||||
CalculatorOperation::Sqrt => {
|
||||
self.unary_op(|a| Ok(OpArgs::Unary(a.sqrt()?)))
|
||||
}
|
||||
CalculatorOperation::Log => self.unary_op(|a| Ok(OpArgs::Unary(a.log()?))),
|
||||
CalculatorOperation::Ln => self.unary_op(|a| Ok(OpArgs::Unary(a.ln()?))),
|
||||
CalculatorOperation::Pow => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.pow(a)?)))
|
||||
}
|
||||
CalculatorOperation::Dup => self.unary_op(|a| Ok(OpArgs::Binary([a, a]))),
|
||||
CalculatorOperation::Drop => self.unary_op(|_| Ok(OpArgs::None)),
|
||||
CalculatorOperation::Swap => {
|
||||
self.binary_op(|[a, b]| Ok(OpArgs::Binary([b, a])))
|
||||
}
|
||||
}),
|
||||
CalculatorOperation::Sqrt => self.unary_op(|a| OpArgs::Unary(a.sqrt())),
|
||||
CalculatorOperation::Log => self.unary_op(|a| OpArgs::Unary(a.log10())),
|
||||
CalculatorOperation::Ln => self.unary_op(|a| OpArgs::Unary(a.ln())),
|
||||
CalculatorOperation::Pow => self.binary_op(|[a, b]| OpArgs::Unary(b.powf(a))),
|
||||
CalculatorOperation::E => self.binary_op(|[a, b]| OpArgs::Unary(b * 10.0_f64.powf(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
|
||||
@ -570,7 +590,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 {
|
||||
@ -614,40 +638,6 @@ impl Calculator {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Performs a state change on a unary operation
|
||||
fn unary_op(
|
||||
&mut self,
|
||||
op: impl FnOnce(f64) -> OpArgs,
|
||||
) -> CalculatorResult<CalculatorStateChange> {
|
||||
let arg = self
|
||||
.stack
|
||||
.get(0)
|
||||
.ok_or(CalculatorError::NotEnoughStackEntries)?;
|
||||
Ok(CalculatorStateChange {
|
||||
pop: OpArgs::Unary(*arg),
|
||||
push: op(*arg),
|
||||
})
|
||||
}
|
||||
/// Performs a state change on a binary operation
|
||||
fn binary_op(
|
||||
&mut self,
|
||||
op: impl FnOnce([f64; 2]) -> OpArgs,
|
||||
) -> CalculatorResult<CalculatorStateChange> {
|
||||
let args: [f64; 2] = [
|
||||
*self
|
||||
.stack
|
||||
.get(0)
|
||||
.ok_or(CalculatorError::NotEnoughStackEntries)?,
|
||||
*self
|
||||
.stack
|
||||
.get(1)
|
||||
.ok_or(CalculatorError::NotEnoughStackEntries)?,
|
||||
];
|
||||
Ok(CalculatorStateChange {
|
||||
pop: OpArgs::Binary(args),
|
||||
push: op(args),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<()> {
|
||||
@ -673,12 +663,12 @@ impl Calculator {
|
||||
|
||||
match to_push {
|
||||
OpArgs::Unary(a) => {
|
||||
if a.is_nan() || a.is_infinite() {
|
||||
if !a.is_valid() {
|
||||
return Err(CalculatorError::ArithmeticError);
|
||||
}
|
||||
}
|
||||
OpArgs::Binary([a, b]) => {
|
||||
if a.is_nan() || b.is_nan() || a.is_infinite() || b.is_infinite() {
|
||||
if !a.is_valid() || !b.is_valid() {
|
||||
return Err(CalculatorError::ArithmeticError);
|
||||
}
|
||||
}
|
||||
@ -720,68 +710,107 @@ impl Calculator {
|
||||
}
|
||||
|
||||
/// Checks if a value on the stack is equal to a given value
|
||||
fn stack_eq(&mut self, idx: usize, value: f64) -> CalculatorResult<()> {
|
||||
if (self.peek(idx)? - value).abs() > f64::EPSILON {
|
||||
Err(CalculatorError::CorruptStateChange(format!(
|
||||
"Stack index {} should be {}, but is {}",
|
||||
idx,
|
||||
value,
|
||||
self.peek(idx)?,
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
fn stack_eq(&mut self, _idx: usize, _value: Entry) -> CalculatorResult<()> {
|
||||
return Ok(());
|
||||
// if (self.peek(idx)? - value).abs() > Entry::EPSILON {
|
||||
// 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<OpArgs>,
|
||||
) -> CalculatorResult<CalculatorStateChange> {
|
||||
// TODO: Use peek instead of stack.get()
|
||||
let arg = self
|
||||
.stack
|
||||
.get(0)
|
||||
.ok_or(CalculatorError::NotEnoughStackEntries)?;
|
||||
Ok(CalculatorStateChange {
|
||||
pop: OpArgs::Unary(*arg),
|
||||
push: op(*arg)?,
|
||||
})
|
||||
}
|
||||
/// Performs a state change on a binary operation
|
||||
fn binary_op(
|
||||
&mut self,
|
||||
op: impl FnOnce([Entry; 2]) -> CalculatorResult<OpArgs>,
|
||||
) -> CalculatorResult<CalculatorStateChange> {
|
||||
let args: [Entry; 2] = [
|
||||
*self.stack
|
||||
.get(0)
|
||||
.ok_or(CalculatorError::NotEnoughStackEntries)?,
|
||||
*self.stack
|
||||
.get(1)
|
||||
.ok_or(CalculatorError::NotEnoughStackEntries)?,
|
||||
];
|
||||
Ok(CalculatorStateChange {
|
||||
pop: OpArgs::Binary(args),
|
||||
push: op(args)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
|
||||
fn gen_sample_calculator() -> Calculator {
|
||||
let mut calc = Calculator::default();
|
||||
// Empty the stack and push a few numbers
|
||||
input_str(&mut calc, "\\\\123 456 789");
|
||||
calc
|
||||
}
|
||||
// fn gen_sample_calculator() -> Calculator {
|
||||
// let mut calc = Calculator::default();
|
||||
// // Empty the stack and push a few numbers
|
||||
// input_str(&mut calc, "\\\\123 456 789");
|
||||
// calc
|
||||
// }
|
||||
|
||||
fn input_str(calc: &mut Calculator, input: &str) {
|
||||
for c in input.chars() {
|
||||
assert!(calc.take_input(c).is_ok());
|
||||
}
|
||||
}
|
||||
// fn input_str(calc: &mut Calculator, input: &str) {
|
||||
// for c in input.chars() {
|
||||
// assert!(calc.take_input(c).is_ok());
|
||||
// }
|
||||
// }
|
||||
|
||||
fn assert_float_eq(a: f64, b: f64) {
|
||||
assert!(a - b < f64::EPSILON, "Value '{}' did not match '{}'", a, b);
|
||||
}
|
||||
// fn assert_float_eq(a: f64, b: f64) {
|
||||
// assert!(
|
||||
// (a - b).abs() < f64::EPSILON,
|
||||
// "Value '{}' did not match '{}'",
|
||||
// a,
|
||||
// b
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn basic_ops() {
|
||||
let mut calc = gen_sample_calculator();
|
||||
assert_float_eq(calc.peek(0).unwrap(), 789_f64);
|
||||
input_str(&mut calc, "+");
|
||||
assert_float_eq(calc.peek(0).unwrap(), 1_245_f64);
|
||||
input_str(&mut calc, "+");
|
||||
assert_float_eq(calc.peek(0).unwrap(), 1_368_f64);
|
||||
// The stack now only has one element
|
||||
assert!(!calc.take_input('+').is_ok());
|
||||
// #[test]
|
||||
// fn basic_ops() {
|
||||
// let mut calc = gen_sample_calculator();
|
||||
// assert_float_eq(calc.peek(0).unwrap(), 789_f64);
|
||||
// input_str(&mut calc, "+");
|
||||
// assert_float_eq(calc.peek(0).unwrap(), 1_245_f64);
|
||||
// input_str(&mut calc, "+");
|
||||
// assert_float_eq(calc.peek(0).unwrap(), 1_368_f64);
|
||||
// // The stack now only has one element
|
||||
// assert!(!calc.take_input('+').is_ok());
|
||||
|
||||
input_str(&mut calc, "n");
|
||||
assert_float_eq(calc.pop().unwrap(), -1_368_f64);
|
||||
// input_str(&mut calc, "n");
|
||||
// assert_float_eq(calc.pop().unwrap(), -1_368_f64);
|
||||
|
||||
input_str(&mut calc, "64v100v");
|
||||
assert_float_eq(calc.pop().unwrap(), 10_f64);
|
||||
assert_float_eq(calc.pop().unwrap(), 8_f64);
|
||||
}
|
||||
// input_str(&mut calc, "64v100v");
|
||||
// assert_float_eq(calc.pop().unwrap(), 10_f64);
|
||||
// assert_float_eq(calc.pop().unwrap(), 8_f64);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn peek() {
|
||||
let mut calc = gen_sample_calculator();
|
||||
// There should be three digits
|
||||
assert_float_eq(calc.peek(0).unwrap(), 789_f64);
|
||||
assert_float_eq(calc.peek(1).unwrap(), 456_f64);
|
||||
assert_float_eq(calc.peek(2).unwrap(), 123_f64);
|
||||
assert!(!calc.peek(3).is_ok());
|
||||
assert!(true);
|
||||
}
|
||||
}
|
||||
// #[test]
|
||||
// fn peek() {
|
||||
// let mut calc = gen_sample_calculator();
|
||||
// // There should be three digits
|
||||
// assert_float_eq(calc.peek(0).unwrap(), 789_f64);
|
||||
// assert_float_eq(calc.peek(1).unwrap(), 456_f64);
|
||||
// assert_float_eq(calc.peek(2).unwrap(), 123_f64);
|
||||
// assert!(!calc.peek(3).is_ok());
|
||||
// }
|
||||
// }
|
||||
|
336
src/calc/entries.rs
Normal file
336
src/calc/entries.rs
Normal file
@ -0,0 +1,336 @@
|
||||
// use super::operations::CalculatorStateChange;
|
||||
use super::errors::CalculatorResult;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub struct Number {
|
||||
pub value: f64,
|
||||
}
|
||||
|
||||
// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
// pub struct Vector {
|
||||
// pub value: Vec<Number>,
|
||||
// }
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
|
||||
pub enum Entry {
|
||||
Number(Number),
|
||||
// Vector(Vector),
|
||||
// Matrix(Vec<Vec<f64>>),
|
||||
}
|
||||
|
||||
impl CalculatorEntry for Entry {
|
||||
fn is_valid(&self) -> bool {
|
||||
match self {
|
||||
Entry::Number(number) => number.is_valid(),
|
||||
// Entry::Vector(vector) => vector.add(),
|
||||
}
|
||||
}
|
||||
fn add(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.add(arg),
|
||||
// Entry::Vector(vector) => vector.add(),
|
||||
}
|
||||
}
|
||||
fn sub(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.sub(arg),
|
||||
// Entry::Vector(vector) => vector.sub(),
|
||||
}
|
||||
}
|
||||
fn mul(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.mul(arg),
|
||||
// Entry::Vector(vector) => vector.mul(),
|
||||
}
|
||||
}
|
||||
fn div(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.div(arg),
|
||||
// Entry::Vector(vector) => vector.div(),
|
||||
}
|
||||
}
|
||||
fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.int_divide(arg),
|
||||
// Entry::Vector(vector) => vector.int_divide(),
|
||||
}
|
||||
}
|
||||
fn negate(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.negate(),
|
||||
// Entry::Vector(vector) => vector.negate(),
|
||||
}
|
||||
}
|
||||
fn abs(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.abs(),
|
||||
// Entry::Vector(vector) => vector.abs(),
|
||||
}
|
||||
}
|
||||
fn inverse(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.inverse(),
|
||||
// Entry::Vector(vector) => vector.inverse(),
|
||||
}
|
||||
}
|
||||
fn modulo(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.modulo(arg),
|
||||
// Entry::Vector(vector) => vector.modulo(),
|
||||
}
|
||||
}
|
||||
fn sin(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.sin(),
|
||||
// Entry::Vector(vector) => vector.sin(),
|
||||
}
|
||||
}
|
||||
fn cos(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.cos(),
|
||||
// Entry::Vector(vector) => vector.cos(),
|
||||
}
|
||||
}
|
||||
fn tan(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.tan(),
|
||||
// Entry::Vector(vector) => vector.tan(),
|
||||
}
|
||||
}
|
||||
fn asin(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.asin(),
|
||||
// Entry::Vector(vector) => vector.asin(),
|
||||
}
|
||||
}
|
||||
fn acos(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.acos(),
|
||||
// Entry::Vector(vector) => vector.acos(),
|
||||
}
|
||||
}
|
||||
fn atan(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.atan(),
|
||||
// Entry::Vector(vector) => vector.atan(),
|
||||
}
|
||||
}
|
||||
fn sqrt(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.sqrt(),
|
||||
// Entry::Vector(vector) => vector.sqrt(),
|
||||
}
|
||||
}
|
||||
fn log(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.log(),
|
||||
// Entry::Vector(vector) => vector.log(),
|
||||
}
|
||||
}
|
||||
fn ln(&self) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.ln(),
|
||||
// Entry::Vector(vector) => vector.ln(),
|
||||
}
|
||||
}
|
||||
fn pow(&self, arg: Entry) -> CalculatorResult<Entry> {
|
||||
match self {
|
||||
Entry::Number(number) => number.pow(arg),
|
||||
// Entry::Vector(vector) => vector.pow(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CalculatorEntry for Number {
|
||||
fn is_valid(&self) -> bool {
|
||||
!self.value.is_nan() && !self.value.is_infinite()
|
||||
}
|
||||
fn add(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64 + self.value,
|
||||
}))
|
||||
}
|
||||
fn sub(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64 - self.value,
|
||||
}))
|
||||
}
|
||||
fn mul(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64 * self.value,
|
||||
}))
|
||||
}
|
||||
fn div(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64 / self.value,
|
||||
}))
|
||||
}
|
||||
fn int_divide(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64.div_euclid(self.value),
|
||||
}))
|
||||
}
|
||||
fn negate(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: -self.value }))
|
||||
}
|
||||
fn abs(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: self.value.abs(),
|
||||
}))
|
||||
}
|
||||
fn inverse(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: self.value.recip(),
|
||||
}))
|
||||
}
|
||||
fn modulo(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64 % self.value,
|
||||
}))
|
||||
}
|
||||
fn sin(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: self.value }))
|
||||
// match self.angle_mode {
|
||||
// CalculatorAngleMode::Degrees => {
|
||||
// |self.value: f64| a.to_radians().sin()
|
||||
// }
|
||||
// CalculatorAngleMode::Radians => |a: f64| a.sin(),
|
||||
// CalculatorAngleMode::Grads => |a: f64| {
|
||||
// (a * std::f64::consts::PI / 200.0).sin()
|
||||
// },
|
||||
// }
|
||||
}
|
||||
fn cos(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: self.value }))
|
||||
// match self.angle_mode {
|
||||
// CalculatorAngleMode::Degrees => {
|
||||
// |self.value: f64| a.to_radians().cos()
|
||||
// }
|
||||
// CalculatorAngleMode::Radians => |a: f64| a.cos(),
|
||||
// CalculatorAngleMode::Grads => |a: f64| {
|
||||
// (a * std::f64::consts::PI / 200.0).cos()
|
||||
// },
|
||||
// }
|
||||
}
|
||||
fn tan(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: self.value }))
|
||||
// match self.angle_mode {
|
||||
// CalculatorAngleMode::Degrees => {
|
||||
// |self.value: f64| a.to_radians().tan()
|
||||
// }
|
||||
// CalculatorAngleMode::Radians => |a: f64| a.tan(),
|
||||
// CalculatorAngleMode::Grads => |a: f64| {
|
||||
// (a * std::f64::consts::PI / 200.0).tan()
|
||||
// },
|
||||
// }
|
||||
}
|
||||
fn asin(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: self.value }))
|
||||
// match self.angle_mode {
|
||||
// CalculatorAngleMode::Degrees => {
|
||||
// |self.value: f64| a.asin().to_degrees()
|
||||
// }
|
||||
// CalculatorAngleMode::Radians => |a: f64| a.asin(),
|
||||
// CalculatorAngleMode::Grads => |a: f64| {
|
||||
// a.asin() * std::f64::consts::PI / 200.0
|
||||
// },
|
||||
// }
|
||||
}
|
||||
fn acos(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: self.value }))
|
||||
// match self.angle_mode {
|
||||
// CalculatorAngleMode::Degrees => {
|
||||
// |self.value: f64| a.acos().to_degrees()
|
||||
// }
|
||||
// CalculatorAngleMode::Radians => |a: f64| a.acos(),
|
||||
// CalculatorAngleMode::Grads => |a: f64| {
|
||||
// a.acos() * std::f64::consts::PI / 200.0
|
||||
// },
|
||||
// }
|
||||
}
|
||||
fn atan(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number { value: self.value }))
|
||||
// match self.angle_mode {
|
||||
// CalculatorAngleMode::Degrees => {
|
||||
// |self.value: f64| a.atan().to_degrees()
|
||||
// }
|
||||
// CalculatorAngleMode::Radians => |a: f64| a.atan(),
|
||||
// CalculatorAngleMode::Grads => |a: f64| {
|
||||
// a.atan() * std::f64::consts::PI / 200.0
|
||||
// },
|
||||
// }
|
||||
}
|
||||
fn sqrt(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: self.value.sqrt(),
|
||||
}))
|
||||
}
|
||||
fn log(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: self.value.log10(),
|
||||
}))
|
||||
}
|
||||
fn ln(&self) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: self.value.ln(),
|
||||
}))
|
||||
}
|
||||
fn pow(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
Ok(Entry::Number(Number {
|
||||
value: 1.0f64.powf(self.value),
|
||||
}))
|
||||
}
|
||||
// fn e(&self, _arg: Entry) -> CalculatorResult<Entry> {
|
||||
// Ok(Entry::Number(Number { value:1.0f64 * 10.0_f64.powf(self.value) }))
|
||||
// }
|
||||
}
|
||||
|
||||
pub trait CalculatorEntry {
|
||||
fn is_valid(&self) -> bool;
|
||||
fn add(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
fn sub(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
fn mul(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
fn div(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
fn int_divide(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
fn negate(&self) -> CalculatorResult<Entry>;
|
||||
fn abs(&self) -> CalculatorResult<Entry>;
|
||||
fn inverse(&self) -> CalculatorResult<Entry>;
|
||||
fn modulo(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
fn sin(&self) -> CalculatorResult<Entry>;
|
||||
fn cos(&self) -> CalculatorResult<Entry>;
|
||||
fn tan(&self) -> CalculatorResult<Entry>;
|
||||
fn asin(&self) -> CalculatorResult<Entry>;
|
||||
fn acos(&self) -> CalculatorResult<Entry>;
|
||||
fn atan(&self) -> CalculatorResult<Entry>;
|
||||
fn sqrt(&self) -> CalculatorResult<Entry>;
|
||||
fn log(&self) -> CalculatorResult<Entry>;
|
||||
fn ln(&self) -> CalculatorResult<Entry>;
|
||||
fn pow(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
// fn e(&self, arg: Entry) -> CalculatorResult<Entry>;
|
||||
}
|
||||
|
||||
impl fmt::Display for Entry {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Number(Number { value }) => write!(f, "{}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Number {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
// impl fmt::Display for Vector {
|
||||
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// match self {
|
||||
// Self::Degrees => write!(f, "DEG"),
|
||||
// Self::Radians => write!(f, "RAD"),
|
||||
// Self::Grads => write!(f, "GRD"),
|
||||
// }
|
||||
// }
|
||||
// }
|
@ -11,6 +11,8 @@ pub enum CalculatorError {
|
||||
ArithmeticError,
|
||||
/// Not enough stck entries for operation
|
||||
NotEnoughStackEntries,
|
||||
/// Requested type does not match target type
|
||||
TypeMismatch,
|
||||
/// Thrown when an undo or redo cannot be performed
|
||||
CorruptStateChange(String),
|
||||
/// Cannot undo or redo
|
||||
@ -44,6 +46,7 @@ impl fmt::Display for CalculatorError {
|
||||
match self {
|
||||
Self::ArithmeticError => write!(f, "Arithmetic Error"),
|
||||
Self::NotEnoughStackEntries => write!(f, "Not enough items in the stack"),
|
||||
Self::TypeMismatch => write!(f, "Type mismatch"),
|
||||
Self::CorruptStateChange(msg) => {
|
||||
write!(f, "Corrupt state change: {}", msg)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::entries::Entry;
|
||||
use serde::{Deserialize, Serialize};
|
||||
/// Operations that can be sent to the calculator such as +, -, or undo
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize)]
|
||||
@ -12,9 +13,6 @@ pub enum CalculatorOperation {
|
||||
Modulo,
|
||||
IntegerDivide,
|
||||
//Remainder,
|
||||
Drop,
|
||||
Dup,
|
||||
Swap,
|
||||
Sin,
|
||||
Cos,
|
||||
Tan,
|
||||
@ -28,7 +26,9 @@ pub enum CalculatorOperation {
|
||||
// Factorial,
|
||||
Log,
|
||||
Ln,
|
||||
E,
|
||||
Drop,
|
||||
Dup,
|
||||
Swap,
|
||||
Macro(MacroState),
|
||||
}
|
||||
|
||||
@ -45,9 +45,9 @@ pub enum OpArgs {
|
||||
/// This is a macro start and end noop
|
||||
Macro(MacroState),
|
||||
/// Operation takes 1 argument, ex: sqrt or negate
|
||||
Unary(f64),
|
||||
Unary(Entry),
|
||||
/// Operation takes 2 arguments, ex: + or -
|
||||
Binary([f64; 2]),
|
||||
Binary([Entry; 2]),
|
||||
/// Operation takes no arguments, ex: push
|
||||
None,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::entries::Entry;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
@ -29,7 +30,7 @@ pub enum RegisterState {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CalculatorConstant {
|
||||
pub help: String,
|
||||
pub value: f64,
|
||||
pub value: Entry,
|
||||
}
|
||||
|
||||
/// One calculator macro containing a messsage and value
|
||||
@ -46,7 +47,7 @@ pub type CalculatorConstants = HashMap<char, CalculatorConstant>;
|
||||
pub type CalculatorMacros = HashMap<char, CalculatorMacro>;
|
||||
|
||||
/// Map of chars to registers
|
||||
pub type CalculatorRegisters = HashMap<char, f64>;
|
||||
pub type CalculatorRegisters = HashMap<char, Entry>;
|
||||
|
||||
/// Possible calculator angle modes
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@ -77,7 +78,7 @@ impl fmt::Display for CalculatorAngleMode {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "display_mode")]
|
||||
pub enum CalculatorDisplayMode {
|
||||
/// Rust's default f64 format
|
||||
/// Rust's default Entry format
|
||||
Default,
|
||||
/// Thousands separator
|
||||
Separated { separator: char },
|
||||
@ -110,7 +111,7 @@ impl Default for CalculatorDisplayMode {
|
||||
}
|
||||
|
||||
impl CalculatorDisplayMode {
|
||||
pub fn format_number(&self, number: f64) -> String {
|
||||
pub fn format_number(&self, number: &Entry) -> String {
|
||||
match self {
|
||||
Self::Default => format!("{}", number),
|
||||
Self::Separated { separator } => Self::separated(number, *separator),
|
||||
@ -123,19 +124,23 @@ impl CalculatorDisplayMode {
|
||||
}
|
||||
|
||||
// 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));
|
||||
fn scientific(_f: &Entry, _precision: usize) -> String {
|
||||
// TODO
|
||||
String::from("TODO")
|
||||
// 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
|
||||
fn engineering(_f: &Entry, _precision: usize) -> String {
|
||||
// TODO
|
||||
String::from("TODO")
|
||||
/*// 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
|
||||
@ -145,7 +150,7 @@ impl CalculatorDisplayMode {
|
||||
// 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 f64 max will be ~400
|
||||
// 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::<isize>().unwrap();
|
||||
// Sign of the exponent. If string representation starts with E-, then negative
|
||||
@ -183,9 +188,10 @@ impl CalculatorDisplayMode {
|
||||
display_exp,
|
||||
pad = 2
|
||||
)
|
||||
*/
|
||||
}
|
||||
|
||||
fn separated(f: f64, sep: char) -> String {
|
||||
fn separated(f: &Entry, 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());
|
||||
@ -234,9 +240,9 @@ mod tests {
|
||||
// i
|
||||
(1.0, 0, " 1 E+00"),
|
||||
// Precision
|
||||
(-0.123456789, 3, "-1.235 E-01"),
|
||||
(-0.123456789, 2, "-1.23 E-01"),
|
||||
(-0.123456789, 2, "-1.23 E-01"),
|
||||
(-0.123_456_789, 3, "-1.235 E-01"),
|
||||
(-0.123_456_789, 2, "-1.23 E-01"),
|
||||
(-0.123_456_789, 2, "-1.23 E-01"),
|
||||
(-1e99, 2, "-1.00 E+99"),
|
||||
(-1e100, 2, "-1.00 E+100"),
|
||||
// Rounding
|
||||
@ -268,10 +274,10 @@ mod tests {
|
||||
(-100_000.0, ',', "-100,000"),
|
||||
(1_000_000.0, ',', "1,000,000"),
|
||||
(-1_000_000.0, ',', "-1,000,000"),
|
||||
(1_000_000.123456789, ',', "1,000,000.123456789"),
|
||||
(-1_000_000.123456789, ',', "-1,000,000.123456789"),
|
||||
(1_000_000.123456789, ' ', "1 000 000.123456789"),
|
||||
(1_000_000.123456789, ' ', "1 000 000.123456789"),
|
||||
(1_000_000.123_456_789, ',', "1,000,000.123456789"),
|
||||
(-1_000_000.123_456_789, ',', "-1,000,000.123456789"),
|
||||
(1_000_000.123_456_789, ' ', "1 000 000.123456789"),
|
||||
(1_000_000.123_456_789, ' ', "1 000 000.123456789"),
|
||||
] {
|
||||
assert_eq!(
|
||||
CalculatorDisplayMode::Separated { separator }.format_number(f),
|
||||
|
@ -113,7 +113,7 @@ impl App {
|
||||
"{}: {} ({})",
|
||||
key,
|
||||
constant.help,
|
||||
self.calculator.display_mode.format_number(constant.value)
|
||||
self.calculator.display_mode.format_number(&constant.value)
|
||||
)
|
||||
})
|
||||
.fold(String::new(), |acc, s| acc + &s + "\n")
|
||||
@ -233,9 +233,11 @@ impl App {
|
||||
.enumerate()
|
||||
.rev()
|
||||
.map(|(i, m)| {
|
||||
let number = self.calculator.display_mode.format_number(*m);
|
||||
let number = self.calculator.display_mode.format_number(&*m);
|
||||
let content = match self.calculator.calculator_alignment {
|
||||
CalculatorAlignment::Left => format!("{:>2}: {}", i, number),
|
||||
CalculatorAlignment::Left => {
|
||||
format!("{:>2}: {}", i, number)
|
||||
}
|
||||
CalculatorAlignment::Right => {
|
||||
let ret = format!("{} :{:>2}", number, i);
|
||||
if ret.len() < chunk.width.saturating_sub(BORDER_SIZE) as usize {
|
||||
|
Loading…
Reference in New Issue
Block a user