Add rust_decimal support

This commit is contained in:
Austen Adler 2023-04-19 23:27:27 -04:00
parent 8346412f7a
commit b8c2f24f6e
10 changed files with 473 additions and 158 deletions

239
Cargo.lock generated
View File

@ -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",

View File

@ -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

View File

@ -179,11 +179,14 @@ impl eframe::App for TemplateApp {
});
ui.label(
self.calculator.stack.iter()
self.calculator
.stack
.iter()
.rev()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join("\n"));
.join("\n"),
);
// ui.label(format!("Calc details: {:#?}", self.calculator.stack));
ui.label(format!("Entry buffer: {}", self.calculator.get_l()));

View File

@ -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::<f64>().or(Err(CalculatorError::ParseError))?;
let f = self
.l
.parse::<Decimal>()
.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<usize> {

View File

@ -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) }],
})]),
),
] {

View File

@ -1,34 +1,27 @@
use super::{Entry, Matrix, Vector};
use crate::calc::{
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<String> {
@ -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<Entry> {
if self.is_valid() {
@ -66,7 +59,9 @@ impl CalculatorEntry for Number {
}
fn inverse(&self) -> CalculatorResult<Entry> {
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<Entry> {
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<Entry> {
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<Entry> {
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<Entry> {
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<Entry> {
// 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<Entry> {
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<Entry> {
// 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<Entry> {
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<Entry> {
// 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<Entry> {
Ok(Entry::Number(Self {
value: self.value.sqrt(),
value: self.value.sqrt().ok_or(CalculatorError::ArithmeticError)?,
}))
}
fn log(&self) -> CalculatorResult<Entry> {
Ok(Entry::Number(Self {
value: self.value.log10(),
value: self
.value
.checked_log10()
.ok_or(CalculatorError::ArithmeticError)?,
}))
}
fn ln(&self) -> CalculatorResult<Entry> {
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());

View File

@ -1,8 +1,11 @@
use super::{Entry, Matrix, Number};
use crate::calc::{
use crate::{
calc::{
entries::CalculatorEntry,
errors::{CalculatorError, CalculatorResult},
types::{CalculatorAngleMode, CalculatorDisplayMode},
},
constants,
};
use serde::{Deserialize, Serialize};
use std::fmt;
@ -74,11 +77,13 @@ impl CalculatorEntry for Vector {
self.iterated_unary(Number::negate)
}
fn abs(&self) -> CalculatorResult<Entry> {
let value: Entry = self
.values
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 }))?)
acc.add(&n2.pow(&Entry::Number(Number {
value: constants::TWO,
}))?)
})?;
value.sqrt()
}

26
src/constants.rs Normal file
View File

@ -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));
}
}

View File

@ -1,2 +1,3 @@
pub mod calc;
pub mod constants;
pub mod event;

View File

@ -6,6 +6,7 @@
#![allow(clippy::multiple_crate_versions)]
mod calc;
mod constants;
mod event;
const BORDER_SIZE: u16 = 2;