diff --git a/Cargo.lock b/Cargo.lock index 93716b4..3603ef7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", +] diff --git a/Cargo.toml b/Cargo.toml index daa02c4..d096133 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/src/calc.rs b/src/calc.rs index 703c7b7..4386c96 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -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 { - 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 { let (count, count_entry) = self.peek_usize()?; - let (mut entries, values): (VecDeque, Vec) = (1..=count) - .map(|i| { - let e = self.peek(i)?; - match e { - // TODO: For now, vectors only contain numbers - Entry::Number(number) => Ok((e, number)), - Entry::Vector(_vector) => Err(CalculatorError::TypeMismatch), - } - }) - .collect::>>()? - // TODO: Do I have to iter again? - .into_iter() - .unzip(); - let entry = Entry::Vector(Vector { - values, - direction: VectorDirection::Row, - }); - entries.push_front(count_entry); + let mut entries: Vec = (1..=count) + .map(|i| self.peek(i)) + .collect::>>()?; + + 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 { + let (count, count_entry) = self.peek_usize()?; + let mut entries: Vec = (1..=count) + .map(|i| self.peek(i)) + .collect::>>()?; + + 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]))), diff --git a/src/calc/entries.rs b/src/calc/entries.rs index 6e2df79..f0e2ea4 100644 --- a/src/calc/entries.rs +++ b/src/calc/entries.rs @@ -58,18 +58,19 @@ pub struct Vector { pub direction: VectorDirection, } -// #[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -// pub struct Matrix { -// pub values: Vec, -// pub direction: VectorDirection, -// } +#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +pub struct Matrix { + pub vectors: Vec, + 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 { match self { Self::Number(number) => number.negate(), Self::Vector(vector) => vector.negate(), + Self::Matrix(matrix) => matrix.negate(), } } fn abs(&self) -> CalculatorResult { match self { Self::Number(number) => number.abs(), Self::Vector(vector) => vector.abs(), + Self::Matrix(matrix) => matrix.abs(), } } fn inverse(&self) -> CalculatorResult { match self { Self::Number(number) => number.inverse(), Self::Vector(vector) => vector.inverse(), + Self::Matrix(matrix) => matrix.inverse(), } } fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { 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 { 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 { 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 { 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 { 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 { 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 { match self { Self::Number(number) => number.sqrt(), Self::Vector(vector) => vector.sqrt(), + Self::Matrix(matrix) => matrix.sqrt(), } } fn log(&self) -> CalculatorResult { match self { Self::Number(number) => number.log(), Self::Vector(vector) => vector.log(), + Self::Matrix(matrix) => matrix.log(), } } fn ln(&self) -> CalculatorResult { 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 { 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 { 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 { 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 { 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 { 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 { 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 { + // 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::>() + .join(self.direction.get_separator()) + ) + } + + // Mathematical operations + fn negate(&self) -> CalculatorResult { + self.iterated_unary(Vector::negate) + } + fn abs(&self) -> CalculatorResult { + // 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 { + // 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 { + self.iterated_unary(|v| v.sin(angle_mode)) + } + fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|v| v.cos(angle_mode)) + } + fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|v| v.tan(angle_mode)) + } + fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|v| v.asin(angle_mode)) + } + fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|v| v.acos(angle_mode)) + } + fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { + self.iterated_unary(|v| v.atan(angle_mode)) + } + fn sqrt(&self) -> CalculatorResult { + self.iterated_unary(Vector::sqrt) + } + fn log(&self) -> CalculatorResult { + self.iterated_unary(Vector::log) + } + fn ln(&self) -> CalculatorResult { + self.iterated_unary(Vector::ln) + } + + // Binary + fn add(&self, arg: &Entry) -> CalculatorResult { + 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 { + 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 { + // 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 { + 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 { + 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 { + 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 { + 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) -> CalculatorResult { + 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::>>()?; + + // 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 { + if self.is_valid() { + Ok(Entry::Matrix(self)) + } else { + Err(CalculatorError::ArithmeticError) + } + } + + fn iterated_unary( + &self, + op: impl Fn(&Vector) -> CalculatorResult, + ) -> CalculatorResult { + Self { + vectors: self + .vectors + .iter() + .map(|v| op(v)) + .map(|e| match e { + Ok(Entry::Vector(vector)) => Ok(vector), + _ => Err(CalculatorError::ArithmeticError), + }) + .collect::>>()?, + direction: self.direction, + dimensions: self.dimensions, + } + .validate() + } + + fn iterated_binary_num( + &self, + number: &Number, + op: impl Fn(&Vector, &Entry) -> CalculatorResult, + ) -> CalculatorResult { + 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::>>()?, + direction: self.direction, + dimensions: self.dimensions, + } + .validate() + } + + fn iterated_binary_mat( + &self, + m2: &Self, + op: impl Fn(&Vector, &Entry) -> CalculatorResult, + ) -> CalculatorResult { + 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::>>()?, + direction: self.direction, + dimensions: self.dimensions, + } + .validate() + } +} + impl CalculatorEntry for Vector { // Misc fn to_editable_string(&self) -> CalculatorResult { @@ -291,18 +552,22 @@ impl CalculatorEntry for Vector { fn add(&self, arg: &Entry) -> CalculatorResult { 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 { 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 { 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 { 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 { 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 { 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 { 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) -> CalculatorResult { + 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::>>()?, + direction: VectorDirection::Row, + })) //.validate() <- TODO + } + fn iterated_unary( &self, op: impl Fn(&Number) -> CalculatorResult, @@ -525,7 +811,8 @@ impl CalculatorEntry for Number { fn add(&self, arg: &Entry) -> CalculatorResult { 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 { 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 { 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 { 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 { 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 { 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 { 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, @@ -618,6 +911,28 @@ impl Number { direction: vector.direction, })) } + + fn iterated_binary_mat( + self, + matrix: &Matrix, + op: impl Fn(&Self, &Entry) -> CalculatorResult, + ) -> CalculatorResult { + 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::>>()?, + 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; // Mathematical operations - // Binary - fn add(&self, arg: &Entry) -> CalculatorResult; - fn sub(&self, arg: &Entry) -> CalculatorResult; - fn mul(&self, arg: &Entry) -> CalculatorResult; - fn div(&self, arg: &Entry) -> CalculatorResult; - fn int_divide(&self, arg: &Entry) -> CalculatorResult; - fn modulo(&self, arg: &Entry) -> CalculatorResult; - fn pow(&self, arg: &Entry) -> CalculatorResult; - // Unary fn negate(&self) -> CalculatorResult; fn abs(&self) -> CalculatorResult; @@ -651,20 +957,38 @@ where fn sqrt(&self) -> CalculatorResult; fn log(&self) -> CalculatorResult; fn ln(&self) -> CalculatorResult; + + // Binary + fn add(&self, arg: &Entry) -> CalculatorResult; + fn sub(&self, arg: &Entry) -> CalculatorResult; + fn mul(&self, arg: &Entry) -> CalculatorResult; + fn div(&self, arg: &Entry) -> CalculatorResult; + fn int_divide(&self, arg: &Entry) -> CalculatorResult; + fn modulo(&self, arg: &Entry) -> CalculatorResult; + fn pow(&self, arg: &Entry) -> CalculatorResult; } 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::>() + .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); diff --git a/src/calc/errors.rs b/src/calc/errors.rs index 6882f60..9a07c5e 100644 --- a/src/calc/errors.rs +++ b/src/calc/errors.rs @@ -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), diff --git a/src/calc/operations.rs b/src/calc/operations.rs index 70d16c1..c480f9a 100644 --- a/src/calc/operations.rs +++ b/src/calc/operations.rs @@ -28,6 +28,7 @@ pub enum ArithmeticOperation { pub enum CalculatorOperation { ArithmeticOperation(ArithmeticOperation), BuildVector, + BuildMatrix, Undo, Redo, Drop,