Start work on Matrix

This commit is contained in:
Austen Adler 2021-06-02 22:16:03 -04:00
parent 7ee77f4c4c
commit 3b10aaf06f
6 changed files with 455 additions and 76 deletions

75
Cargo.lock generated
View File

@ -12,12 +12,6 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -27,12 +21,11 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "confy"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2913470204e9e8498a0f31f17f90a0de801ae92c8c5ac18c49af4819e6786697"
source = "git+https://github.com/rust-cli/confy/#6ae700bb0e6e2f9f7138d0c1871f604013c8f59f"
dependencies = [
"directories",
"directories-next",
"serde",
"toml",
"serde_yaml",
]
[[package]]
@ -61,33 +54,39 @@ dependencies = [
]
[[package]]
name = "directories"
version = "2.0.2"
name = "directories-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "551a778172a450d7fc12e629ca3b0428d00f6afa9a43da1b630d54604e97371c"
checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc"
dependencies = [
"cfg-if 0.1.10",
"dirs-sys",
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"wasi",
]
@ -98,7 +97,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@ -113,6 +112,12 @@ version = "0.2.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lock_api"
version = "0.4.4"
@ -128,7 +133,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@ -179,7 +184,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"instant",
"libc",
"redox_syscall",
@ -231,6 +236,7 @@ dependencies = [
"confy",
"crossterm",
"serde",
"toml",
"tui",
]
@ -260,6 +266,18 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
dependencies = [
"dtoa",
"linked-hash-map",
"serde",
"yaml-rust",
]
[[package]]
name = "signal-hook"
version = "0.1.17"
@ -299,9 +317,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.8"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
checksum = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f"
dependencies = [
"serde",
]
@ -364,3 +382,12 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@ -15,4 +15,12 @@ categories = ["command-line-utilities"]
crossterm = "0.18"
tui = { version = "0.14", default-features = false, features = ['crossterm'] }
serde = {version = "1.0", features = ["derive"]}
confy = "0.4.0"
# confy = "0.4.0"
toml = "0.4.2"
[dependencies.confy]
# TODO: Update this to v0.5.0 when it finally comes out -- for now, use latest git master
# version = "0.4.0"
git = "https://github.com/rust-cli/confy/"
features = ["yaml_conf"]
default-features = false

View File

@ -5,7 +5,7 @@ pub mod types;
use crate::calc::entries::CalculatorEntry;
use confy::{load, store};
use entries::{Entry, Number, Vector, VectorDirection};
use entries::{Entry, Matrix, Number, Vector};
use errors::{CalculatorError, CalculatorResult};
use operations::{
ArithmeticOperation, CalculatorOperation, CalculatorStateChange, MacroState, OpArgs,
@ -115,6 +115,13 @@ impl Default for Calculator {
value: String::from("RcRbRarbnrb2^4rarc**-v+2ra*/rbnrb2^4rarc**-v-2ra*/"),
},
),
(
't',
CalculatorMacro {
help: String::from("Testing"),
value: String::from("1 V1 "),
},
),
]
.iter()
.cloned()
@ -168,10 +175,10 @@ impl Calculator {
}
pub fn load_config() -> CalculatorResult<Self> {
load(APP_NAME).map_err(|e| CalculatorError::LoadError(Some(e)))
load(APP_NAME, None).map_err(|e| CalculatorError::LoadError(Some(e)))
}
pub fn save_config(&self) -> CalculatorResult<()> {
store(APP_NAME, self).map_err(|e| CalculatorError::SaveError(Some(e)))
store(APP_NAME, None, self).map_err(|e| CalculatorError::SaveError(Some(e)))
}
pub fn take_input(&mut self, c: char) -> CalculatorResult<()> {
@ -271,6 +278,7 @@ impl Calculator {
)),
// Temporary
'V' => self.op(CalculatorOperation::BuildVector),
'M' => self.op(CalculatorOperation::BuildMatrix),
// Special
'\\' => self.op(CalculatorOperation::Drop),
' ' => self.op(CalculatorOperation::Dup),
@ -508,8 +516,9 @@ impl Calculator {
pub fn peek_usize(&mut self) -> CalculatorResult<(usize, Entry)> {
let entry = self.peek(0)?;
let f = match entry {
Entry::Number(Number { value }) => value,
Entry::Matrix(_) => return Err(CalculatorError::TypeMismatch),
Entry::Vector(_) => return Err(CalculatorError::TypeMismatch),
Entry::Number(Number { value }) => value,
};
// Ensure this can be cast to a usize
if !f.is_finite() || f.is_sign_negative() {
@ -537,27 +546,30 @@ impl Calculator {
pub fn build_vector(&mut self) -> CalculatorResult<CalculatorStateChange> {
let (count, count_entry) = self.peek_usize()?;
let (mut entries, values): (VecDeque<Entry>, Vec<Number>) = (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::<CalculatorResult<Vec<(Entry, Number)>>>()?
// TODO: Do I have to iter again?
.into_iter()
.unzip();
let entry = Entry::Vector(Vector {
values,
direction: VectorDirection::Row,
});
entries.push_front(count_entry);
let mut entries: Vec<Entry> = (1..=count)
.map(|i| self.peek(i))
.collect::<CalculatorResult<Vec<Entry>>>()?;
let new_entry = Vector::from(&entries)?;
entries.splice(1..1, vec![count_entry]);
Ok(CalculatorStateChange {
pop: OpArgs::Variable(Vec::from(entries)),
push: OpArgs::Unary(entry),
push: OpArgs::Unary(new_entry),
})
}
pub fn build_matrix(&mut self) -> CalculatorResult<CalculatorStateChange> {
let (count, count_entry) = self.peek_usize()?;
let mut entries: Vec<Entry> = (1..=count)
.map(|i| self.peek(i))
.collect::<CalculatorResult<Vec<Entry>>>()?;
let new_entry = Matrix::from(&entries)?;
entries.splice(1..1, vec![count_entry]);
Ok(CalculatorStateChange {
pop: OpArgs::Variable(Vec::from(entries)),
push: OpArgs::Unary(new_entry),
})
}
@ -634,6 +646,7 @@ impl Calculator {
self.binary_op(|[a, b]| Ok(OpArgs::Unary(b.pow(&a)?)))
}
CalculatorOperation::BuildVector => self.build_vector(),
CalculatorOperation::BuildMatrix => self.build_matrix(),
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]))),

View File

@ -58,18 +58,19 @@ pub struct Vector {
pub direction: VectorDirection,
}
// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
// pub struct Matrix {
// pub values: Vec<Vector>,
// pub direction: VectorDirection,
// }
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct Matrix {
pub vectors: Vec<Vector>,
pub dimensions: (usize, usize),
pub direction: VectorDirection,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Entry {
Number(Number),
Vector(Vector),
// Matrix(Matrix),
Matrix(Matrix),
}
// macro_rules! op_child_call {
@ -92,90 +93,105 @@ impl CalculatorEntry for Entry {
match self {
Self::Number(number) => number.to_editable_string(),
Self::Vector(vector) => vector.to_editable_string(),
Self::Matrix(matrix) => matrix.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),
Self::Matrix(matrix) => matrix.format_entry(display_mode),
}
}
fn is_valid(&self) -> bool {
match self {
Self::Number(number) => number.is_valid(),
Self::Vector(vector) => vector.is_valid(),
Self::Matrix(matrix) => matrix.is_valid(),
}
}
fn negate(&self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.negate(),
Self::Vector(vector) => vector.negate(),
Self::Matrix(matrix) => matrix.negate(),
}
}
fn abs(&self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.abs(),
Self::Vector(vector) => vector.abs(),
Self::Matrix(matrix) => matrix.abs(),
}
}
fn inverse(&self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.inverse(),
Self::Vector(vector) => vector.inverse(),
Self::Matrix(matrix) => matrix.inverse(),
}
}
fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.sin(angle_mode),
Self::Vector(vector) => vector.sin(angle_mode),
Self::Matrix(matrix) => matrix.sin(angle_mode),
}
}
fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.cos(angle_mode),
Self::Vector(vector) => vector.cos(angle_mode),
Self::Matrix(matrix) => matrix.cos(angle_mode),
}
}
fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.tan(angle_mode),
Self::Vector(vector) => vector.tan(angle_mode),
Self::Matrix(matrix) => matrix.tan(angle_mode),
}
}
fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.asin(angle_mode),
Self::Vector(vector) => vector.asin(angle_mode),
Self::Matrix(matrix) => matrix.asin(angle_mode),
}
}
fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.acos(angle_mode),
Self::Vector(vector) => vector.acos(angle_mode),
Self::Matrix(matrix) => matrix.acos(angle_mode),
}
}
fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.atan(angle_mode),
Self::Vector(vector) => vector.atan(angle_mode),
Self::Matrix(matrix) => matrix.atan(angle_mode),
}
}
fn sqrt(&self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.sqrt(),
Self::Vector(vector) => vector.sqrt(),
Self::Matrix(matrix) => matrix.sqrt(),
}
}
fn log(&self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.log(),
Self::Vector(vector) => vector.log(),
Self::Matrix(matrix) => matrix.log(),
}
}
fn ln(&self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.ln(),
Self::Vector(vector) => vector.ln(),
Self::Matrix(matrix) => matrix.ln(),
}
}
@ -183,46 +199,291 @@ impl CalculatorEntry for Entry {
match self {
Self::Number(number) => number.add(arg),
Self::Vector(vector) => vector.add(arg),
Self::Matrix(matrix) => matrix.add(arg),
}
}
fn sub(&self, arg: &Self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.sub(arg),
Self::Vector(vector) => vector.sub(arg),
Self::Matrix(matrix) => matrix.sub(arg),
}
}
fn mul(&self, arg: &Self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.mul(arg),
Self::Vector(vector) => vector.mul(arg),
Self::Matrix(matrix) => matrix.mul(arg),
}
}
fn div(&self, arg: &Self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.div(arg),
Self::Vector(vector) => vector.div(arg),
Self::Matrix(matrix) => matrix.div(arg),
}
}
fn int_divide(&self, arg: &Self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.int_divide(arg),
Self::Vector(vector) => vector.int_divide(arg),
Self::Matrix(matrix) => matrix.int_divide(arg),
}
}
fn modulo(&self, arg: &Self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.modulo(arg),
Self::Vector(vector) => vector.modulo(arg),
Self::Matrix(matrix) => matrix.modulo(arg),
}
}
fn pow(&self, arg: &Self) -> CalculatorResult<Self> {
match self {
Self::Number(number) => number.pow(arg),
Self::Vector(vector) => vector.pow(arg),
Self::Matrix(matrix) => matrix.pow(arg),
}
}
}
impl CalculatorEntry for Matrix {
fn to_editable_string(&self) -> CalculatorResult<String> {
// TODO: Eventualy we can parse and edit a matrix as a string
Err(CalculatorError::TypeMismatch)
}
fn is_valid(&self) -> bool {
self.vectors.len() == self.dimensions.0
&& self
.vectors
.iter()
.all(|v| v.values.len() == self.dimensions.1 && v.is_valid())
}
fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String {
format!(
"[ {} ]",
self.vectors
.iter()
.map(|vector| vector.format_entry(display_mode))
.collect::<Vec<String>>()
.join(self.direction.get_separator())
)
}
// Mathematical operations
fn negate(&self) -> CalculatorResult<Entry> {
self.iterated_unary(Vector::negate)
}
fn abs(&self) -> CalculatorResult<Entry> {
// TODO: Compute determinant
// let value: Entry = self
// .vectors
// .iter()
// .try_fold(Entry::Number(Vector::ZERO), |acc, vector| {
// acc.add(&vector.abs()?)
// })?;
// value.sqrt()
Err(CalculatorError::NotYetImplemented)
}
fn inverse(&self) -> CalculatorResult<Entry> {
// TODO: Matrix inverse
// Ok(Entry::Vector(Self {
// vectors: self.vectors.clone(),
// direction: self.direction.swap(),
// }))
self.iterated_unary(Vector::inverse)
}
fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
self.iterated_unary(|v| v.sin(angle_mode))
}
fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
self.iterated_unary(|v| v.cos(angle_mode))
}
fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
self.iterated_unary(|v| v.tan(angle_mode))
}
fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
self.iterated_unary(|v| v.asin(angle_mode))
}
fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
self.iterated_unary(|v| v.acos(angle_mode))
}
fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult<Entry> {
self.iterated_unary(|v| v.atan(angle_mode))
}
fn sqrt(&self) -> CalculatorResult<Entry> {
self.iterated_unary(Vector::sqrt)
}
fn log(&self) -> CalculatorResult<Entry> {
self.iterated_unary(Vector::log)
}
fn ln(&self) -> CalculatorResult<Entry> {
self.iterated_unary(Vector::ln)
}
// Binary
fn add(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(m2) => self.iterated_binary_mat(m2, Vector::add),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::add),
}
}
fn sub(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(m2) => self.iterated_binary_mat(m2, Vector::sub),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::sub),
}
}
fn mul(&self, arg: &Entry) -> CalculatorResult<Entry> {
// TODO: Correct implementation
match arg {
Entry::Matrix(m2) => self.iterated_binary_mat(m2, Vector::mul),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::mul),
}
}
fn div(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(m2) => self.iterated_binary_mat(m2, Vector::div),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::div),
}
}
fn int_divide(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(m2) => self.iterated_binary_mat(m2, Vector::int_divide),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::int_divide),
}
}
fn modulo(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(m2) => self.iterated_binary_mat(m2, Vector::modulo),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::modulo),
}
}
fn pow(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(m2) => Err(CalculatorError::TypeMismatch),
Entry::Vector(vector) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => self.iterated_binary_num(number, Vector::pow),
}
}
}
impl Matrix {
pub fn from(entries: &Vec<Entry>) -> CalculatorResult<Entry> {
if entries.len() == 0 {
return Err(CalculatorError::NotEnoughStackEntries);
}
let vectors = entries
.iter()
.map(|e| match e {
Entry::Matrix(_) | Entry::Number(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(vector) => Ok(vector.clone()),
})
.collect::<CalculatorResult<Vec<Vector>>>()?;
// Get the depth of the matrix
let depth = vectors
.get(0)
.ok_or(CalculatorError::NotEnoughStackEntries)?
.values
.len();
if vectors.iter().any(|v| v.values.len() != depth) {
// One vector has a different length than another
return Err(CalculatorError::DimensionMismatch);
}
let dimensions: (usize, usize) = (vectors.len(), depth);
Self {
vectors,
dimensions,
direction: VectorDirection::Row,
}
.validate()
}
// TODO: Should validate be put in the trait?
pub fn validate(self) -> CalculatorResult<Entry> {
if self.is_valid() {
Ok(Entry::Matrix(self))
} else {
Err(CalculatorError::ArithmeticError)
}
}
fn iterated_unary(
&self,
op: impl Fn(&Vector) -> CalculatorResult<Entry>,
) -> CalculatorResult<Entry> {
Self {
vectors: self
.vectors
.iter()
.map(|v| op(v))
.map(|e| match e {
Ok(Entry::Vector(vector)) => Ok(vector),
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Vector>>>()?,
direction: self.direction,
dimensions: self.dimensions,
}
.validate()
}
fn iterated_binary_num(
&self,
number: &Number,
op: impl Fn(&Vector, &Entry) -> CalculatorResult<Entry>,
) -> CalculatorResult<Entry> {
Self {
vectors: self
.vectors
.iter()
.map(|v| op(v, &Entry::Number(*number)))
.map(|e| match e {
Ok(Entry::Vector(vector)) => Ok(vector),
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Vector>>>()?,
direction: self.direction,
dimensions: self.dimensions,
}
.validate()
}
fn iterated_binary_mat(
&self,
m2: &Self,
op: impl Fn(&Vector, &Entry) -> CalculatorResult<Entry>,
) -> CalculatorResult<Entry> {
if self.dimensions != m2.dimensions && self.direction != m2.direction {
return Err(CalculatorError::DimensionMismatch);
}
Self {
vectors: self
.vectors
.iter()
.zip(m2.vectors.iter())
.map(|(v1, v2)| op(v1, &Entry::Vector(v2.clone())))
.map(|e| match e {
Ok(Entry::Vector(vector)) => Ok(vector),
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Vector>>>()?,
direction: self.direction,
dimensions: self.dimensions,
}
.validate()
}
}
impl CalculatorEntry for Vector {
// Misc
fn to_editable_string(&self) -> CalculatorResult<String> {
@ -291,18 +552,22 @@ impl CalculatorEntry for Vector {
fn add(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => self.iterated_binary_vec(v2, Number::add),
Entry::Number(number) => self.iterated_binary_num(number, Number::add),
}
}
fn sub(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => self.iterated_binary_vec(v2, Number::sub),
Entry::Number(number) => self.iterated_binary_num(number, Number::sub),
}
}
fn mul(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
// TODO: Matrix multiplication should be allowed
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => {
if self.values.len() != v2.values.len() {
return Err(CalculatorError::DimensionMismatch);
@ -331,24 +596,28 @@ impl CalculatorEntry for Vector {
}
fn div(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => self.iterated_binary_vec(v2, Number::div),
Entry::Number(number) => self.iterated_binary_num(number, Number::div),
}
}
fn int_divide(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => self.iterated_binary_vec(v2, Number::int_divide),
Entry::Number(number) => self.iterated_binary_num(number, Number::int_divide),
}
}
fn modulo(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => self.iterated_binary_vec(v2, Number::modulo),
Entry::Number(number) => self.iterated_binary_num(number, Number::modulo),
}
}
fn pow(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(v2) => self.iterated_binary_vec(v2, Number::pow),
Entry::Number(number) => self.iterated_binary_num(number, Number::pow),
}
@ -356,6 +625,23 @@ impl CalculatorEntry for Vector {
}
impl Vector {
pub fn from(entries: &Vec<Entry>) -> CalculatorResult<Entry> {
if entries.len() == 0 {
return Err(CalculatorError::NotEnoughStackEntries);
}
Ok(Entry::Vector(Self {
values: entries
.iter()
.map(|e| match e {
Entry::Matrix(_) => Err(CalculatorError::TypeMismatch),
Entry::Vector(_) => Err(CalculatorError::TypeMismatch),
Entry::Number(number) => Ok(number.clone()),
})
.collect::<CalculatorResult<Vec<Number>>>()?,
direction: VectorDirection::Row,
})) //.validate() <- TODO
}
fn iterated_unary(
&self,
op: impl Fn(&Number) -> CalculatorResult<Entry>,
@ -525,7 +811,8 @@ impl CalculatorEntry for Number {
fn add(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::add),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::add),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::add),
Entry::Number(number) => Self {
value: self.value + number.value,
}
@ -534,7 +821,8 @@ impl CalculatorEntry for Number {
}
fn sub(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::sub),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::sub),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::sub),
Entry::Number(number) => Self {
value: self.value - number.value,
}
@ -543,7 +831,8 @@ impl CalculatorEntry for Number {
}
fn mul(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::mul),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::mul),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::mul),
Entry::Number(number) => Self {
value: self.value * number.value,
}
@ -552,7 +841,8 @@ impl CalculatorEntry for Number {
}
fn div(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::div),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::div),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::div),
Entry::Number(number) => Self {
value: self.value / number.value,
}
@ -561,7 +851,8 @@ impl CalculatorEntry for Number {
}
fn int_divide(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::int_divide),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::int_divide),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::int_divide),
Entry::Number(number) => Self {
value: self.value.div_euclid(number.value),
}
@ -570,7 +861,8 @@ impl CalculatorEntry for Number {
}
fn modulo(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::modulo),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::modulo),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::modulo),
Entry::Number(number) => Self {
value: self.value % number.value,
}
@ -579,7 +871,8 @@ impl CalculatorEntry for Number {
}
fn pow(&self, arg: &Entry) -> CalculatorResult<Entry> {
match arg {
Entry::Vector(vector) => self.iterated_binary(vector, Self::pow),
Entry::Matrix(matrix) => self.iterated_binary_mat(matrix, Self::pow),
Entry::Vector(vector) => self.iterated_binary_vec(vector, Self::pow),
Entry::Number(number) => Self {
value: self.value.powf(number.value),
}
@ -599,7 +892,7 @@ impl Number {
}
}
fn iterated_binary(
fn iterated_binary_vec(
self,
vector: &Vector,
op: impl Fn(&Self, &Entry) -> CalculatorResult<Entry>,
@ -618,6 +911,28 @@ impl Number {
direction: vector.direction,
}))
}
fn iterated_binary_mat(
self,
matrix: &Matrix,
op: impl Fn(&Self, &Entry) -> CalculatorResult<Entry>,
) -> CalculatorResult<Entry> {
Matrix {
vectors: matrix
.vectors
.iter()
.map(|v| op(&self, &Entry::Vector(v.clone()))) // TODO: Do I need to clone?
.map(|e| match e {
// Only numbers are valid in a vector
Ok(Entry::Vector(vector)) => Ok(vector),
_ => Err(CalculatorError::ArithmeticError),
})
.collect::<CalculatorResult<Vec<Vector>>>()?,
direction: matrix.direction,
dimensions: matrix.dimensions,
}
.validate()
}
}
pub trait CalculatorEntry
@ -629,15 +944,6 @@ where
fn format_entry(&self, display_mode: &CalculatorDisplayMode) -> String;
fn to_editable_string(&self) -> CalculatorResult<String>;
// Mathematical operations
// Binary
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 modulo(&self, arg: &Entry) -> CalculatorResult<Entry>;
fn pow(&self, arg: &Entry) -> CalculatorResult<Entry>;
// Unary
fn negate(&self) -> CalculatorResult<Entry>;
fn abs(&self) -> CalculatorResult<Entry>;
@ -651,20 +957,38 @@ where
fn sqrt(&self) -> CalculatorResult<Entry>;
fn log(&self) -> CalculatorResult<Entry>;
fn ln(&self) -> CalculatorResult<Entry>;
// Binary
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 modulo(&self, arg: &Entry) -> CalculatorResult<Entry>;
fn pow(&self, arg: &Entry) -> CalculatorResult<Entry>;
}
impl fmt::Display for Entry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Number(number) => write!(f, "{}", number),
Self::Matrix(matrix) => write!(f, "{}", matrix),
Self::Vector(vector) => write!(f, "{}", vector),
Self::Number(number) => write!(f, "{}", number),
}
}
}
impl fmt::Display for Number {
impl fmt::Display for Matrix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
write!(
f,
"[ {} ]",
self.vectors
.iter()
.map(|vector| format!("{}", vector))
.collect::<Vec<String>>()
.join("; ")
)
}
}
@ -682,6 +1006,12 @@ impl fmt::Display for Vector {
}
}
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
// Based on https://stackoverflow.com/a/65266882
fn scientific(f: f64, precision: usize) -> String {
let mut ret = format!("{:.precision$E}", f, precision = precision);

View File

@ -66,12 +66,12 @@ impl fmt::Display for CalculatorError {
Self::ParseError => write!(f, "Parse error"),
Self::PrecisionTooHigh => write!(f, "Precision too high"),
Self::SaveError(None) => write!(f, "Could not save"),
Self::SaveError(Some(ConfyError::SerializeTomlError(e))) => {
Self::SaveError(Some(ConfyError::SerializeYamlError(e))) => {
write!(f, "Save serialization error: {}", e)
}
Self::SaveError(Some(e)) => write!(f, "Could not save: {}", e),
Self::LoadError(None) => write!(f, "Could not load"),
Self::LoadError(Some(ConfyError::SerializeTomlError(e))) => {
Self::LoadError(Some(ConfyError::SerializeYamlError(e))) => {
write!(f, "Load serialization error: {}", e)
}
Self::LoadError(Some(e)) => write!(f, "Could not load: {}", e),

View File

@ -28,6 +28,7 @@ pub enum ArithmeticOperation {
pub enum CalculatorOperation {
ArithmeticOperation(ArithmeticOperation),
BuildVector,
BuildMatrix,
Undo,
Redo,
Drop,