diff --git a/Cargo.lock b/Cargo.lock index e94a7a6..57c5750 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,6 +102,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.3" @@ -361,12 +372,79 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytecheck" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.13.0" @@ -746,7 +824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6412a21e0bde7c0918f7fb44bbbb86b5e1f88e63c026a4e747cc7af02f76dfbe" dependencies = [ "accesskit", - "ahash", + "ahash 0.8.3", "epaint", "nohash-hasher", "ron", @@ -836,7 +914,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12e78b5c58a1f7f621f9d546add2adce20636422c9b251e29f749e8a2f713c95" dependencies = [ "ab_glyph", - "ahash", + "ahash 0.8.3", "atomic_refcell", "bytemuck", "ecolor", @@ -1102,6 +1180,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] [[package]] name = "hermit-abi" @@ -1158,6 +1239,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + [[package]] name = "jni" version = "0.20.0" @@ -1436,6 +1523,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_enum" version = "0.5.11" @@ -1451,7 +1547,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.0", "proc-macro2", "quote", "syn 1.0.109", @@ -1671,6 +1767,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "proc-macro-crate" version = "1.3.0" @@ -1690,6 +1795,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.26" @@ -1783,6 +1908,40 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "rend" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" +dependencies = [ + "bytecheck", + "hashbrown", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ron" version = "0.8.0" @@ -1801,8 +1960,10 @@ dependencies = [ "confy", "crossterm", "lazy_static", + "rust_decimal", + "rust_decimal_macros", "serde", - "toml", + "toml 0.4.10", "tui", ] @@ -1820,6 +1981,34 @@ dependencies = [ "wasm-bindgen-futures", ] +[[package]] +name = "rust_decimal" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc" +dependencies = [ + "arrayvec", + "borsh", + "bytecheck", + "byteorder", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rust_decimal_macros" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e773fd3da1ed42472fdf3cfdb4972948a555bc3d73f5e0bdb99d17e7b54c687" +dependencies = [ + "quote", + "rust_decimal", +] + [[package]] name = "rustix" version = "0.36.11" @@ -1834,6 +2023,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + [[package]] name = "same-file" version = "1.0.6" @@ -1868,6 +2063,12 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "serde" version = "1.0.126" @@ -1900,6 +2101,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.10" @@ -1963,6 +2175,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "slab" version = "0.4.8" @@ -2158,6 +2376,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.5.1" @@ -2835,7 +3062,7 @@ version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414cd9f07964695e00bfef8e589d1752ea0480b8a619f2064cbaccb8a6c2ed59" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.0", "proc-macro2", "quote", "regex", @@ -2873,7 +3100,7 @@ version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fd4aafc0dee96ae7242a24249ce9babf21e1562822f03df650d4e68c20e41ed" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.0", "proc-macro2", "quote", "syn 1.0.109", diff --git a/Cargo.toml b/Cargo.toml index dae4e47..cff02fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,8 @@ serde = {version = "1.0", features = ["derive"]} # confy = "0.4.0" toml = "0.4.2" lazy_static = "1.4.0" +rust_decimal = { version = "1.29.1", features=["maths"] } +rust_decimal_macros = "1.29.1" [dependencies.confy] # TODO: Update this to v0.5.0 when it finally comes out -- for now, use latest git master diff --git a/rpn_rs_gui/src/app.rs b/rpn_rs_gui/src/app.rs index 3e091bb..6ff2ede 100644 --- a/rpn_rs_gui/src/app.rs +++ b/rpn_rs_gui/src/app.rs @@ -179,11 +179,14 @@ impl eframe::App for TemplateApp { }); ui.label( - self.calculator.stack.iter() - .rev() + self.calculator + .stack + .iter() + .rev() .map(|e| e.to_string()) .collect::>() - .join("\n")); + .join("\n"), + ); // ui.label(format!("Calc details: {:#?}", self.calculator.stack)); ui.label(format!("Entry buffer: {}", self.calculator.get_l())); diff --git a/src/calc.rs b/src/calc.rs index 636a21e..dfae543 100644 --- a/src/calc.rs +++ b/src/calc.rs @@ -1,9 +1,12 @@ +use rust_decimal::prelude::ToPrimitive; pub mod entries; +use crate::constants; use lazy_static::lazy_static; pub mod errors; pub mod operations; pub mod types; use crate::calc::entries::CalculatorEntry; +use rust_decimal::Decimal; use confy::{load, store}; use entries::{Entry, Matrix, Number, Vector}; @@ -197,7 +200,7 @@ impl Default for Calculator { CalculatorConstant { help: String::from("Tau (2pi)"), value: Entry::Number(Number { - value: std::f64::consts::TAU, + value: constants::TAU, }), }, ), @@ -206,7 +209,7 @@ impl Default for Calculator { CalculatorConstant { help: String::from("Euler's Number e"), value: Entry::Number(Number { - value: std::f64::consts::E, + value: constants::E, }), }, ), @@ -215,7 +218,7 @@ impl Default for Calculator { CalculatorConstant { help: String::from("Pi"), value: Entry::Number(Number { - value: std::f64::consts::PI, + value: constants::PI, }), }, ), @@ -479,7 +482,10 @@ impl Calculator { return Ok(false); } - let f = self.l.parse::().or(Err(CalculatorError::ParseError))?; + let f = self + .l + .parse::() + .or(Err(CalculatorError::ParseError))?; self.push(Entry::Number(Number { value: f }))?; self.l.clear(); Ok(true) @@ -520,14 +526,11 @@ impl Calculator { Entry::Matrix(_) | 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() { - return Err(CalculatorError::ArithmeticError); - } - #[allow(clippy::cast_sign_loss)] - let u = f as usize; - Ok((u, entry)) + match f.to_usize() { + Some(u) => Ok((u, entry)), + None => Err(CalculatorError::ArithmeticError), + } } /// Pops a precision instead of an Entry. Precisions are of type usize pub fn pop_precision(&mut self) -> CalculatorResult { diff --git a/src/calc/entries.rs b/src/calc/entries.rs index d20d35f..89e8f7f 100644 --- a/src/calc/entries.rs +++ b/src/calc/entries.rs @@ -255,6 +255,8 @@ impl fmt::Display for Entry { #[cfg(test)] mod tests { use super::*; + use rust_decimal::Decimal; + use rust_decimal_macros::dec; fn valid_square_matrix() -> Entry { Entry::Matrix(Matrix { @@ -266,25 +268,25 @@ mod tests { Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 1.0_f64 }, - Number { value: 2.0_f64 }, - Number { value: -3.0_f64 }, + Number { value: dec!(1.0) }, + Number { value: dec!(2.0) }, + Number { value: dec!(-3.0) }, ], }, Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 4.0_f64 }, - Number { value: -5.0_f64 }, - Number { value: 0.0_f64 }, + Number { value: dec!(4.0) }, + Number { value: dec!(-5.0) }, + Number { value: dec!(0.0) }, ], }, Vector { direction: VectorDirection::Column, values: vec![ - Number { value: -7.0_f64 }, - Number { value: 8.0_f64 }, - Number { value: 9.0_f64 }, + Number { value: dec!(-7.0) }, + Number { value: dec!(8.0) }, + Number { value: dec!(9.0) }, ], }, ], @@ -301,25 +303,25 @@ mod tests { Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: -1.0_f64 }, - Number { value: -2.0_f64 }, - Number { value: 3.0_f64 }, + Number { value: dec!(-1.0) }, + Number { value: dec!(-2.0) }, + Number { value: dec!(3.0) }, ], }), Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: -4.0_f64 }, - Number { value: 5.0_f64 }, - Number { value: -0.0_f64 }, + Number { value: dec!(-4.0) }, + Number { value: dec!(5.0) }, + Number { value: dec!(-0.0) }, ], }), Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 7.0_f64 }, - Number { value: -8.0_f64 }, - Number { value: -9.0_f64 }, + Number { value: dec!(7.0) }, + Number { value: dec!(-8.0) }, + Number { value: dec!(-9.0) }, ], }), ]), @@ -331,25 +333,25 @@ mod tests { // Entry::Vector(Vector { // direction: VectorDirection::Column, // values: vec![ - // Number { value: 1.0_f64 }, - // Number { value: 2.0_f64 }, - // Number { value: 3.0_f64 }, + // Number { value: dec!(1.0) }, + // Number { value: dec!(2.0) }, + // Number { value: dec!(3.0) }, // ], // }), // Entry::Vector(Vector { // direction: VectorDirection::Column, // values: vec![ - // Number { value: 4.0_f64 }, - // Number { value: 5.0_f64 }, - // Number { value: 0.0_f64 }, + // Number { value: dec!(4.0) }, + // Number { value: dec!(5.0) }, + // Number { value: dec!(0.0) }, // ], // }), // Entry::Vector(Vector { // direction: VectorDirection::Column, // values: vec![ - // Number { value: 7.0_f64 }, - // Number { value: 8.0_f64 }, - // Number { value: 9.0_f64 }, + // Number { value: dec!(7.0) }, + // Number { value: dec!(8.0) }, + // Number { value: dec!(9.0) }, // ], // }), // ]), @@ -358,23 +360,23 @@ mod tests { // Entry::Vector(Vector { // direction: VectorDirection::Column, // values: vec![ - // Number{value: 1.0_f64}, - // Number{value: 2.0_f64}, - // Number{value: -3.0_f64}, + // Number{value: dec!(1.0},) + // Number{value: dec!(2.0},) + // Number{value: dec!(-3.0},) // ]}), // Entry::Vector (Vector{ // direction: VectorDirection::Column, // values: vec![ - // Number{value: 4.0_f64}, - // Number{value: -5.0_f64}, - // Number{value: 0.0_f64}, + // Number{value: dec!(4.0},) + // Number{value: dec!(-5.0},) + // Number{value: dec!(0.0},) // ]}), // Entry::Vector (Vector{ // direction: VectorDirection::Column, // values: vec![ - // Number{value: -7.0_f64}, - // Number{value: 8.0_f64}, - // Number{value: 9.0_f64}, + // Number{value: dec!(-7.0},) + // Number{value: dec!(8.0},) + // Number{value: dec!(9.0},) // ])), ( "transpose", @@ -383,25 +385,25 @@ mod tests { Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 1.0_f64 }, - Number { value: 4.0_f64 }, - Number { value: -7.0_f64 }, + Number { value: dec!(1.0) }, + Number { value: dec!(4.0) }, + Number { value: dec!(-7.0) }, ], }), Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 2.0_f64 }, - Number { value: -5.0_f64 }, - Number { value: 8.0_f64 }, + Number { value: dec!(2.0) }, + Number { value: dec!(-5.0) }, + Number { value: dec!(8.0) }, ], }), Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: -3.0_f64 }, - Number { value: 0.0_f64 }, - Number { value: 9.0_f64 }, + Number { value: dec!(-3.0) }, + Number { value: dec!(0.0) }, + Number { value: dec!(9.0) }, ], }), ]), @@ -416,9 +418,9 @@ mod tests { vectors: vec![Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 1.0_f64 }, - Number { value: 100.0_f64 }, - Number { value: 64.0_f64 }, + Number { value: dec!(1.0) }, + Number { value: dec!(100.0) }, + Number { value: dec!(64.0) }, ], }], }) @@ -426,9 +428,9 @@ mod tests { Matrix::from(&[Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 1.0_f64 }, - Number { value: 10.0_f64 }, - Number { value: 8.0_f64 }, + Number { value: dec!(1.0) }, + Number { value: dec!(10.0) }, + Number { value: dec!(8.0) }, ], })]), ), @@ -443,10 +445,10 @@ mod tests { vectors: vec![Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 1.0_f64 }, - Number { value: 100.0_f64 }, + Number { value: dec!(1.0) }, + Number { value: dec!(100.0) }, Number { - value: 100_000.0_f64, + value: dec!(100_000.0), }, ], }], @@ -455,9 +457,9 @@ mod tests { Matrix::from(&[Entry::Vector(Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 0.0_f64 }, - Number { value: 2.0_f64 }, - Number { value: 5.0_f64 }, + Number { value: dec!(0.0) }, + Number { value: dec!(2.0) }, + Number { value: dec!(5.0) }, ], })]), ), @@ -471,9 +473,9 @@ mod tests { vectors: vec![Vector { direction: VectorDirection::Column, values: vec![ - Number { value: 1.0_f64 }, + Number { value: dec!(1.0) }, Number { - value: std::f64::consts::E, + value: constants::E, }, ], }], @@ -481,7 +483,7 @@ mod tests { .ln(), Matrix::from(&[Entry::Vector(Vector { direction: VectorDirection::Column, - values: vec![Number { value: 0.0_f64 }, Number { value: 1.0_f64 }], + values: vec![Number { value: dec!(0.0) }, Number { value: dec!(1.0) }], })]), ), ] { diff --git a/src/calc/entries/number.rs b/src/calc/entries/number.rs index d2eaca3..e9ff726 100644 --- a/src/calc/entries/number.rs +++ b/src/calc/entries/number.rs @@ -1,34 +1,27 @@ use super::{Entry, Matrix, Vector}; -use crate::calc::{ - entries::CalculatorEntry, - errors::{CalculatorError, CalculatorResult}, - types::{CalculatorAngleMode, CalculatorDisplayMode}, +use crate::{ + calc::{ + entries::CalculatorEntry, + errors::{CalculatorError, CalculatorResult}, + types::{CalculatorAngleMode, CalculatorDisplayMode}, + }, + constants, }; +use rust_decimal::{Decimal, MathematicalOps}; use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct Number { - pub value: f64, + pub value: Decimal, } -impl PartialEq for Number { - fn eq(&self, other: &Self) -> bool { - if self.value.is_nan() && other.value.is_nan() - || self.value.is_infinite() && other.value.is_infinite() - { - true - } else if self.value.is_nan() - || self.value.is_infinite() - || other.value.is_infinite() - || other.value.is_nan() - { - false - } else { - (self.value - other.value).abs() < f64::EPSILON - } - } -} +// impl PartialEq for Number { +// fn eq(&self, other: &Self) -> bool { +// (self.value - other.value).abs() < f64::EPSILON +// } +// } +// } impl CalculatorEntry for Number { fn to_editable_string(&self) -> CalculatorResult { @@ -46,7 +39,7 @@ impl CalculatorEntry for Number { } } fn is_valid(&self) -> bool { - !self.value.is_nan() && !self.value.is_infinite() + true } fn validate(self) -> CalculatorResult { if self.is_valid() { @@ -66,7 +59,9 @@ impl CalculatorEntry for Number { } fn inverse(&self) -> CalculatorResult { Self { - value: self.value.recip(), + value: constants::ONE + .checked_div(self.value) + .ok_or(CalculatorError::ArithmeticError)?, } .validate() } @@ -76,70 +71,103 @@ impl CalculatorEntry for Number { fn sin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { Ok(Entry::Number(Self { value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().sin(), - CalculatorAngleMode::Radians => self.value.sin(), - CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).sin(), + CalculatorAngleMode::Degrees => (self.value * constants::DEC_TO_RAD_MULTIPLIER) + .checked_sin() + .ok_or(CalculatorError::ArithmeticError)?, + CalculatorAngleMode::Radians => self + .value + .checked_sin() + .ok_or(CalculatorError::ArithmeticError)?, + CalculatorAngleMode::Grads => (self.value * constants::GRAD_TO_RAD_MULTIPLIER) + .checked_sin() + .ok_or(CalculatorError::ArithmeticError)?, }, })) } fn cos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { Ok(Entry::Number(Self { value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().cos(), - CalculatorAngleMode::Radians => self.value.cos(), - CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).cos(), + CalculatorAngleMode::Degrees => (self.value * constants::DEC_TO_RAD_MULTIPLIER) + .checked_cos() + .ok_or(CalculatorError::ArithmeticError)?, + CalculatorAngleMode::Radians => self + .value + .checked_cos() + .ok_or(CalculatorError::ArithmeticError)?, + CalculatorAngleMode::Grads => (self.value * constants::GRAD_TO_RAD_MULTIPLIER) + .checked_cos() + .ok_or(CalculatorError::ArithmeticError)?, }, })) } fn tan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { Ok(Entry::Number(Self { value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.to_radians().tan(), - CalculatorAngleMode::Radians => self.value.tan(), - CalculatorAngleMode::Grads => (self.value * std::f64::consts::PI / 200.0).tan(), + CalculatorAngleMode::Degrees => (self.value * constants::DEC_TO_RAD_MULTIPLIER) + .checked_tan() + .ok_or(CalculatorError::ArithmeticError)?, + CalculatorAngleMode::Radians => self + .value + .checked_tan() + .ok_or(CalculatorError::ArithmeticError)?, + CalculatorAngleMode::Grads => (self.value * constants::GRAD_TO_RAD_MULTIPLIER) + .checked_tan() + .ok_or(CalculatorError::ArithmeticError)?, }, })) } - fn asin(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), - CalculatorAngleMode::Radians => self.value.asin(), - CalculatorAngleMode::Grads => self.value.asin() * 200.0 / std::f64::consts::PI, - }, - })) + fn asin(&self, _angle_mode: CalculatorAngleMode) -> CalculatorResult { + // TODO: Implement this + Err(CalculatorError::NotYetImplemented) + // Ok(Entry::Number(Self { + // value: match angle_mode { + // CalculatorAngleMode::Degrees => self.value.asin().to_degrees(), + // CalculatorAngleMode::Radians => self.value.asin(), + // CalculatorAngleMode::Grads => self.value.asin() * 200.0 / std::f64::consts::PI, + // }, + // })) } - fn acos(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.acos().to_degrees(), - CalculatorAngleMode::Radians => self.value.acos(), - CalculatorAngleMode::Grads => self.value.acos() * 200.0 / std::f64::consts::PI, - }, - })) + fn acos(&self, _angle_mode: CalculatorAngleMode) -> CalculatorResult { + // TODO: Implement this + Err(CalculatorError::NotYetImplemented) + // Ok(Entry::Number(Self { + // value: match angle_mode { + // CalculatorAngleMode::Degrees => self.value.acos().to_degrees(), + // CalculatorAngleMode::Radians => self.value.acos(), + // CalculatorAngleMode::Grads => self.value.acos() * 200.0 / std::f64::consts::PI, + // }, + // })) } - fn atan(&self, angle_mode: CalculatorAngleMode) -> CalculatorResult { - Ok(Entry::Number(Self { - value: match angle_mode { - CalculatorAngleMode::Degrees => self.value.atan().to_degrees(), - CalculatorAngleMode::Radians => self.value.atan(), - CalculatorAngleMode::Grads => self.value.atan() * 200.0 / std::f64::consts::PI, - }, - })) + fn atan(&self, _angle_mode: CalculatorAngleMode) -> CalculatorResult { + // TODO: Implement this + Err(CalculatorError::NotYetImplemented) + // Ok(Entry::Number(Self { + // value: match angle_mode { + // CalculatorAngleMode::Degrees => self.value.atan().to_degrees(), + // CalculatorAngleMode::Radians => self.value.atan(), + // CalculatorAngleMode::Grads => self.value.atan() * 200.0 / std::f64::consts::PI, + // }, + // })) } fn sqrt(&self) -> CalculatorResult { Ok(Entry::Number(Self { - value: self.value.sqrt(), + value: self.value.sqrt().ok_or(CalculatorError::ArithmeticError)?, })) } fn log(&self) -> CalculatorResult { Ok(Entry::Number(Self { - value: self.value.log10(), + value: self + .value + .checked_log10() + .ok_or(CalculatorError::ArithmeticError)?, })) } fn ln(&self) -> CalculatorResult { Ok(Entry::Number(Self { - value: self.value.ln(), + value: self + .value + .checked_ln() + .ok_or(CalculatorError::ArithmeticError)?, })) } @@ -187,8 +215,20 @@ impl CalculatorEntry for Number { match arg { 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), + Entry::Number(number) => { + let q = (self.value / number.value).trunc(); + Self { + // Implementation based on https://doc.rust-lang.org/src/std/f64.rs.html#263-269 + value: if self.value % number.value < constants::ZERO { + if number.value > constants::ZERO { + q - constants::ONE + } else { + q + constants::ONE + } + } else { + q + }, + } } .validate(), } @@ -208,7 +248,10 @@ impl CalculatorEntry for Number { 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), + value: self + .value + .checked_powd(number.value) + .ok_or(CalculatorError::ArithmeticError)?, } .validate(), } @@ -216,7 +259,9 @@ impl CalculatorEntry for Number { } impl Number { - pub const ZERO: Self = Self { value: 0.0_f64 }; + pub const ZERO: Self = Self { + value: constants::ZERO, + }; fn iterated_binary_vec( self, @@ -266,7 +311,7 @@ impl fmt::Display for Number { } // Based on https://stackoverflow.com/a/65266882 -fn scientific(f: f64, precision: usize) -> String { +fn scientific(f: Decimal, 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 @@ -277,7 +322,7 @@ fn scientific(f: f64, precision: usize) -> String { format!("{}{} E{}{:0>pad$}", sign, ret, exp_sign, exp, pad = 2) } -fn engineering(f: f64, precision: usize) -> String { +fn engineering(f: Decimal, 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 // 1,000 => 1000E3 let all = format!(" {:.precision$E}", f, precision = precision) @@ -328,7 +373,7 @@ fn engineering(f: f64, precision: usize) -> String { ) } -fn separated(f: f64, sep: char) -> String { +fn separated(f: Decimal, 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()); diff --git a/src/calc/entries/vector.rs b/src/calc/entries/vector.rs index 6f01110..b2f75cb 100644 --- a/src/calc/entries/vector.rs +++ b/src/calc/entries/vector.rs @@ -1,8 +1,11 @@ use super::{Entry, Matrix, Number}; -use crate::calc::{ - entries::CalculatorEntry, - errors::{CalculatorError, CalculatorResult}, - types::{CalculatorAngleMode, CalculatorDisplayMode}, +use crate::{ + calc::{ + entries::CalculatorEntry, + errors::{CalculatorError, CalculatorResult}, + types::{CalculatorAngleMode, CalculatorDisplayMode}, + }, + constants, }; use serde::{Deserialize, Serialize}; use std::fmt; @@ -74,12 +77,14 @@ impl CalculatorEntry for Vector { self.iterated_unary(Number::negate) } fn abs(&self) -> CalculatorResult { - let value: Entry = self - .values - .iter() - .try_fold(Entry::Number(Number::ZERO), |acc, n2| { - acc.add(&n2.pow(&Entry::Number(Number { value: 2.0_f64 }))?) - })?; + let value: Entry = + self.values + .iter() + .try_fold(Entry::Number(Number::ZERO), |acc, n2| { + acc.add(&n2.pow(&Entry::Number(Number { + value: constants::TWO, + }))?) + })?; value.sqrt() } fn inverse(&self) -> CalculatorResult { diff --git a/src/constants.rs b/src/constants.rs new file mode 100644 index 0000000..d6c40a0 --- /dev/null +++ b/src/constants.rs @@ -0,0 +1,26 @@ +use rust_decimal::Decimal; +use rust_decimal_macros::dec; + +pub const ZERO: Decimal = Decimal::ZERO; +pub const ONE: Decimal = Decimal::ONE; +pub const TWO: Decimal = Decimal::TWO; + +pub const PI: Decimal = Decimal::PI; +pub const E: Decimal = Decimal::E; +pub const TAU: Decimal = Decimal::TWO_PI; + +pub const RAD_TO_DEC_MULTIPLIER: Decimal = dec!(57.295779513082320876798154814); +pub const DEC_TO_RAD_MULTIPLIER: Decimal = dec!(0.0174532925199432957692369077); +pub const GRAD_TO_RAD_MULTIPLIER: Decimal = dec!(0.0157079632679489661923132169); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_constants() { + assert_eq!(RAD_TO_DEC_MULTIPLIER, dec!(180.0) / PI); + assert_eq!(DEC_TO_RAD_MULTIPLIER, PI / dec!(180.0)); + assert_eq!(GRAD_TO_RAD_MULTIPLIER, PI / dec!(200.0)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 374e645..64a6613 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod calc; +pub mod constants; pub mod event; diff --git a/src/main.rs b/src/main.rs index 457eecc..33c2c6d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ #![allow(clippy::multiple_crate_versions)] mod calc; +mod constants; mod event; const BORDER_SIZE: u16 = 2;