calc_rational

CLI calculator for rational numbers.
git clone https://git.philomathiclife.com/repos/calc_rational
Log | Files | Refs | README

commit 7236333e9cb43b71b9605326d1612f6c7ba723e1
parent 1043f8f1846579fbfc853bee5494d00d9cf959e7
Author: Zack Newman <zack@philomathiclife.com>
Date:   Fri, 20 Mar 2026 15:13:21 -0600

update deps and lints. address new deps and lints

Diffstat:
MCargo.toml | 178++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
MREADME.md | 12+++++++-----
Msrc/cache.rs | 114++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/lib.rs | 2777++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/main.rs | 16+++++++---------
5 files changed, 1467 insertions(+), 1630 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0" name = "calc_rational" readme = "README.md" repository = "https://git.philomathiclife.com/repos/calc_rational/" -rust-version = "1.87.0" -version = "2.4.1" +rust-version = "1.93.1" +version = "3.0.0" [lib] name = "calc_lib" @@ -19,58 +19,108 @@ path = "src/lib.rs" [[bin]] name = "calc" path = "src/main.rs" +required-features = ["std"] [lints.rust] -ambiguous_negative_literals = { level = "deny", priority = -1 } -closure_returning_async_block = { level = "deny", priority = -1 } -deprecated_safe = { level = "deny", priority = -1 } -deref_into_dyn_supertrait = { level = "deny", priority = -1 } -ffi_unwind_calls = { level = "deny", priority = -1 } -future_incompatible = { level = "deny", priority = -1 } -#fuzzy_provenance_casts = { level = "deny", priority = -1 } -impl_trait_redundant_captures = { level = "deny", priority = -1 } -keyword_idents = { level = "deny", priority = -1 } -let_underscore = { level = "deny", priority = -1 } -#linker_messages = { level = "deny", priority = -1 } -#lossy_provenance_casts = { level = "deny", priority = -1 } -macro_use_extern_crate = { level = "deny", priority = -1 } -meta_variable_misuse = { level = "deny", priority = -1 } -missing_copy_implementations = { level = "deny", priority = -1 } -missing_debug_implementations = { level = "deny", priority = -1 } -missing_docs = { level = "deny", priority = -1 } -#multiple_supertrait_upcastable = { level = "deny", priority = -1 } -#must_not_suspend = { level = "deny", priority = -1 } -non_ascii_idents = { level = "deny", priority = -1 } -#non_exhaustive_omitted_patterns = { level = "deny", priority = -1 } -nonstandard_style = { level = "deny", priority = -1 } -redundant_imports = { level = "deny", priority = -1 } -redundant_lifetimes = { level = "deny", priority = -1 } -refining_impl_trait = { level = "deny", priority = -1 } -rust_2018_compatibility = { level = "deny", priority = -1 } -rust_2018_idioms = { level = "deny", priority = -1 } -rust_2021_compatibility = { level = "deny", priority = -1 } -rust_2024_compatibility = { level = "deny", priority = -1 } -single_use_lifetimes = { level = "deny", priority = -1 } -#supertrait_item_shadowing_definition = { level = "deny", priority = -1 } -trivial_casts = { level = "deny", priority = -1 } -trivial_numeric_casts = { level = "deny", priority = -1 } -unit_bindings = { level = "deny", priority = -1 } -unnameable_types = { level = "deny", priority = -1 } -#unqualified_local_imports = { level = "deny", priority = -1 } -unreachable_pub = { level = "deny", priority = -1 } -unsafe_code = { level = "deny", priority = -1 } -unstable_features = { level = "deny", priority = -1 } +deprecated-safe = { level = "deny", priority = -1 } +future-incompatible = { level = "deny", priority = -1 } +keyword-idents = { level = "deny", priority = -1 } +let-underscore = { level = "deny", priority = -1 } +nonstandard-style = { level = "deny", priority = -1 } +refining-impl-trait = { level = "deny", priority = -1 } +rust-2018-compatibility = { level = "deny", priority = -1 } +rust-2018-idioms = { level = "deny", priority = -1 } +rust-2021-compatibility = { level = "deny", priority = -1 } +rust-2024-compatibility = { level = "deny", priority = -1 } +unknown-or-malformed-diagnostic-attributes = { level = "deny", priority = -1 } unused = { level = "deny", priority = -1 } -unused_crate_dependencies = { level = "deny", priority = -1 } -unused_import_braces = { level = "deny", priority = -1 } -unused_lifetimes = { level = "deny", priority = -1 } -unused_qualifications = { level = "deny", priority = -1 } -unused_results = { level = "deny", priority = -1 } -variant_size_differences = { level = "deny", priority = -1 } warnings = { level = "deny", priority = -1 } +ambiguous-negative-literals = { level = "deny", priority = -1 } +closure-returning-async-block = { level = "deny", priority = -1 } +deprecated-in-future = { level = "deny", priority = -1 } +deref-into-dyn-supertrait = { level = "deny", priority = -1 } +ffi-unwind-calls = { level = "deny", priority = -1 } +#fuzzy-provenance-casts = { level = "deny", priority = -1 } +impl-trait-redundant-captures = { level = "deny", priority = -1 } +linker-messages = { level = "deny", priority = -1 } +#lossy-provenance-casts = { level = "deny", priority = -1 } +macro-use-extern-crate = { level = "deny", priority = -1 } +meta-variable-misuse = { level = "deny", priority = -1 } +missing-copy-implementations = { level = "deny", priority = -1 } +missing-debug-implementations = { level = "deny", priority = -1 } +missing-docs = { level = "deny", priority = -1 } +#multiple-supertrait-upcastable = { level = "deny", priority = -1 } +#must-not-suspend = { level = "deny", priority = -1 } +non-ascii-idents = { level = "deny", priority = -1 } +#non-exhaustive-omitted-patterns = { level = "deny", priority = -1 } +redundant-imports = { level = "deny", priority = -1 } +redundant-lifetimes = { level = "deny", priority = -1 } +#resolving-to-items-shadowing-supertrait-items = { level = "deny", priority = -1 } +#shadowing-supertrait-items = { level = "deny", priority = -1 } +single-use-lifetimes = { level = "deny", priority = -1 } +trivial-casts = { level = "deny", priority = -1 } +trivial-numeric-casts = { level = "deny", priority = -1 } +unit-bindings = { level = "deny", priority = -1 } +unnameable-types = { level = "deny", priority = -1 } +#unqualified-local-imports = { level = "deny", priority = -1 } +unreachable-pub = { level = "deny", priority = -1 } +unsafe-code = { level = "deny", priority = -1 } +unstable-features = { level = "deny", priority = -1 } +unused-crate-dependencies = { level = "deny", priority = -1 } +unused-import-braces = { level = "deny", priority = -1 } +unused-lifetimes = { level = "deny", priority = -1 } +unused-qualifications = { level = "deny", priority = -1 } +unused-results = { level = "deny", priority = -1 } +variant-size-differences = { level = "deny", priority = -1 } +# Before publishing to crates.io, comment above and uncomment below. +#warnings = { level = "allow", priority = -1 } +#ambiguous-associated-items = { level = "allow", priority = -1 } +#arithmetic-overflow = { level = "allow", priority = -1 } +#binary-asm-labels = { level = "allow", priority = -1 } +#bindings-with-variant-name = { level = "allow", priority = -1 } +#conflicting-repr-hints = { level = "allow", priority = -1 } +#dangerous-implicit-autorefs = { level = "allow", priority = -1 } +#default-overrides-default-fields = { level = "allow", priority = -1 } +#dependency-on-unit-never-type-fallback = { level = "allow", priority = -1 } +#deref-nullptr = { level = "allow", priority = -1 } +#elided-lifetimes-in-associated-constant = { level = "allow", priority = -1 } +#enum-intrinsics-non-enums = { level = "allow", priority = -1 } +#explicit-builtin-cfgs-in-flags = { level = "allow", priority = -1 } +#ill-formed-attribute-input = { level = "allow", priority = -1 } +#incomplete-include = { level = "allow", priority = -1 } +#ineffective-unstable-trait-impl = { level = "allow", priority = -1 } +#invalid-atomic-ordering = { level = "allow", priority = -1 } +#invalid-from-utf8-unchecked = { level = "allow", priority = -1 } +#invalid-macro-export-arguments = { level = "allow", priority = -1 } +#invalid-null-arguments = { level = "allow", priority = -1 } +#invalid-reference-casting = { level = "allow", priority = -1 } +#invalid-type-param-default = { level = "allow", priority = -1 } +#legacy-derive-helpers = { level = "allow", priority = -1 } +#let-underscore-lock = { level = "allow", priority = -1 } +#long-running-const-eval = { level = "allow", priority = -1 } +#macro-expanded-macro-exports-accessed-by-absolute-paths = { level = "allow", priority = -1 } +#mutable-transmutes = { level = "allow", priority = -1 } +#named-asm-labels = { level = "allow", priority = -1 } +#never-type-fallback-flowing-into-unsafe = { level = "allow", priority = -1 } +#no-mangle-const-items = { level = "allow", priority = -1 } +#out-of-scope-macro-calls = { level = "allow", priority = -1 } +#overflowing-literals = { level = "allow", priority = -1 } +#patterns-in-fns-without-body = { level = "allow", priority = -1 } +#proc-macro-derive-resolution-fallback = { level = "allow", priority = -1 } +#pub-use-of-private-extern-crate = { level = "allow", priority = -1 } +#repr-transparent-non-zst-fields = { level = "allow", priority = -1 } +#semicolon-in-expressions-from-macros = { level = "allow", priority = -1 } +#soft-unstable = { level = "allow", priority = -1 } +#test-unstable-lint = { level = "allow", priority = -1 } +#text-direction-codepoint-in-comment = { level = "allow", priority = -1 } +#text-direction-codepoint-in-literal = { level = "allow", priority = -1 } +#unconditional-panic = { level = "allow", priority = -1 } +#undropped-manually-drops = { level = "allow", priority = -1 } +#unknown-crate-types = { level = "allow", priority = -1 } +#useless-deprecated = { level = "allow", priority = -1 } +# Before publishing to crates.io, comment below. [lints.clippy] -all = { level = "deny", priority = -1 } cargo = { level = "deny", priority = -1 } complexity = { level = "deny", priority = -1 } correctness = { level = "deny", priority = -1 } @@ -91,30 +141,47 @@ pub_use = "allow" question_mark_used = "allow" ref_patterns = "allow" return_and_then = "allow" +semicolon_outside_block = "allow" single_call_fn = "allow" single_char_lifetime_names = "allow" unseparated_literal_suffix = "allow" +[lints.rustdoc] +# Before publishing to crates.io, comment below and uncomment below that. +all = { level = "deny", priority = -1 } +#all = "allow" + [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] +default-target = "x86_64-unknown-linux-gnu" +targets = [ + "aarch64-apple-darwin", + "aarch64-pc-windows-msvc", + "aarch64-unknown-linux-gnu", + "i686-pc-windows-msvc", + "i686-unknown-linux-gnu", + "x86_64-pc-windows-gnu", + "x86_64-pc-windows-msvc", + "x86_64-unknown-freebsd", + "x86_64-unknown-linux-musl", + "x86_64-unknown-netbsd" +] [dependencies] num-bigint = { version = "0.4.6", default-features = false } num-integer = { version = "0.1.46", default-features = false } num-rational = { version = "0.4.2", default-features = false, features = ["num-bigint"] } num-traits = { version = "0.2.19", default-features = false } -priv_sep = { version = "3.0.0-alpha.1.3", default-features = false, optional = true } -rand = { version = "0.9.2", default-features = false, features = ["thread_rng"], optional = true } +rand = { version = "0.10.0", default-features = false, features = ["thread_rng"], optional = true } + +[target.'cfg(target_os = "openbsd")'.dependencies] +priv_sep = { version = "3.0.0-alpha.5.0", default-features = false } ### FEATURES ################################################################# [features] -default = ["priv_sep"] - -# Provide pledge and unveil for OpenBSD. -priv_sep = ["dep:priv_sep", "std"] +default = ["rand"] # Provide random functions. rand = ["dep:rand", "std"] @@ -123,6 +190,7 @@ rand = ["dep:rand", "std"] std = [] [profile.release] +codegen-units = 1 lto = true panic = 'abort' strip = true diff --git a/README.md b/README.md @@ -188,11 +188,13 @@ at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -Before any PR is sent, `cargo clippy` and `cargo t` should be run _for each possible combination of "features"_ -using stable Rust. One easy way to achieve this is by building `ci` and invoking it with no commands in the -`calc_rational` directory or sub-directories. You can fetch `ci` via `git clone https://git.philomathiclife.com/repos/ci`, -and it can be built with `cargo build --release`. Additionally, -`RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure documentation can be built. +Before any PR is sent, `cargo clippy --all-targets`, `cargo test --all-targets -- --include-ignored`, and +`cargo test --doc` should be run _for each possible combination of "features"_ using the stable and MSRV toolchains. +One easy way to achieve this is by invoking [`ci-cargo`](https://crates.io/crates/ci-cargo) as +`ci-cargo clippy --all-targets test --all-targets --include-ignored` in the `calc_rational` directory. + +Last, `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure documentation can be +built. ### Status diff --git a/src/cache.rs b/src/cache.rs @@ -178,7 +178,7 @@ where mod tests { use super::Cache; #[test] - fn test_len() { + fn len() { let mut c = Cache::<bool, 1>::new(); assert_eq!(0, c.len()); c.push(false); @@ -187,65 +187,66 @@ mod tests { assert_eq!(1, c.len()); } #[test] - fn test_is_empty() { + fn is_empty() { let mut c = Cache::<bool, 1>::new(); assert!(c.is_empty()); c.push(false); assert!(!c.is_empty()); } + #[expect(clippy::cognitive_complexity, reason = "a lot to test")] #[test] - fn test_get() { + fn get() { let mut c = Cache::<bool, 4>::new(); - assert!(c.get(0).is_none()); - assert!(c.get(1).is_none()); - assert!(c.get(2).is_none()); - assert!(c.get(3).is_none()); - assert!(c.get(4).is_none()); - assert!(c.get(5).is_none()); - assert!(c.get(usize::MAX).is_none()); + assert_eq!(c.get(0), None); + assert_eq!(c.get(1), None); + assert_eq!(c.get(2), None); + assert_eq!(c.get(3), None); + assert_eq!(c.get(4), None); + assert_eq!(c.get(5), None); + assert_eq!(c.get(usize::MAX), None); c.push(true); - assert!(c.get(0).unwrap()); - assert!(c.get(1).is_none()); - assert!(c.get(2).is_none()); - assert!(c.get(3).is_none()); - assert!(c.get(4).is_none()); - assert!(c.get(5).is_none()); - assert!(c.get(usize::MAX).is_none()); + assert_eq!(c.get(0), Some(&true)); + assert_eq!(c.get(1), None); + assert_eq!(c.get(2), None); + assert_eq!(c.get(3), None); + assert_eq!(c.get(4), None); + assert_eq!(c.get(5), None); + assert_eq!(c.get(usize::MAX), None); c.push(false); - assert!(!c.get(0).unwrap()); - assert!(c.get(1).unwrap()); - assert!(c.get(2).is_none()); - assert!(c.get(3).is_none()); - assert!(c.get(4).is_none()); - assert!(c.get(5).is_none()); - assert!(c.get(usize::MAX).is_none()); + assert_eq!(c.get(0), Some(&false)); + assert_eq!(c.get(1), Some(&true)); + assert_eq!(c.get(2), None); + assert_eq!(c.get(3), None); + assert_eq!(c.get(4), None); + assert_eq!(c.get(5), None); + assert_eq!(c.get(usize::MAX), None); c.push(false); - assert!(!c.get(0).unwrap()); - assert!(!c.get(1).unwrap()); - assert!(c.get(2).unwrap()); - assert!(c.get(3).is_none()); - assert!(c.get(4).is_none()); - assert!(c.get(5).is_none()); - assert!(c.get(usize::MAX).is_none()); + assert_eq!(c.get(0), Some(&false)); + assert_eq!(c.get(1), Some(&false)); + assert_eq!(c.get(2), Some(&true)); + assert_eq!(c.get(3), None); + assert_eq!(c.get(4), None); + assert_eq!(c.get(5), None); + assert_eq!(c.get(usize::MAX), None); c.push(true); - assert!(c.get(0).unwrap()); - assert!(!c.get(1).unwrap()); - assert!(!c.get(2).unwrap()); - assert!(c.get(3).unwrap()); - assert!(c.get(4).is_none()); - assert!(c.get(5).is_none()); - assert!(c.get(usize::MAX).is_none()); + assert_eq!(c.get(0), Some(&true)); + assert_eq!(c.get(1), Some(&false)); + assert_eq!(c.get(2), Some(&false)); + assert_eq!(c.get(3), Some(&true)); + assert_eq!(c.get(4), None); + assert_eq!(c.get(5), None); + assert_eq!(c.get(usize::MAX), None); c.push(true); - assert!(c.get(0).unwrap()); - assert!(c.get(1).unwrap()); - assert!(!c.get(2).unwrap()); - assert!(!c.get(3).unwrap()); - assert!(c.get(4).is_none()); - assert!(c.get(5).is_none()); - assert!(c.get(usize::MAX).is_none()); + assert_eq!(c.get(0), Some(&true)); + assert_eq!(c.get(1), Some(&true)); + assert_eq!(c.get(2), Some(&false)); + assert_eq!(c.get(3), Some(&false)); + assert_eq!(c.get(4), None); + assert_eq!(c.get(5), None); + assert_eq!(c.get(usize::MAX), None); } #[test] - fn test_get_unsafe() { + fn get_unsafe() { let mut c = Cache::<bool, 4>::new(); assert!(!c.get_unchecked(0)); assert!(!c.get_unchecked(1)); @@ -259,36 +260,47 @@ mod tests { assert!(!c.get_unchecked(3)); assert!(c.get_unchecked(4)); } + #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] #[test] - fn test_index() { + fn index() { let mut c = Cache::<bool, 4>::new(); c.push(true); + // `c.len() > 0`. assert!(c[0]); } + #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] #[test] - #[should_panic] - fn test_index_panic() { + #[should_panic(expected = "called `Option::unwrap()` on a `None` value")] + fn index_panic() { let c = Cache::<bool, 4>::new(); + // `c.len() > 0`. assert!(c[0]); } + #[expect(clippy::indexing_slicing, reason = "comments justify correctness")] #[test] - fn test_push() { + fn push() { let mut c = Cache::<bool, 4>::new(); c.push(true); + // `c.len() > 0`. assert!(c[0]); c.push(true); + // `c.len() > 0`. assert!(c[0]); c.push(false); + // `c.len() > 0`. assert!(!c[0]); c.push(true); + // `c.len() > 0`. assert!(c[0]); c.push(false); + // `c.len() > 0`. assert!(!c[0]); c.push(false); + // `c.len() > 0`. assert!(!c[0]); } #[test] - fn test_new() { + fn new() { _ = Cache::<bool, 0>::new(); _ = Cache::<bool, 32>::new(); _ = Cache::<bool, 31>::new(); diff --git a/src/lib.rs b/src/lib.rs @@ -111,6 +111,10 @@ //! For a more precise specification of the “calc language”, one can read the //! [calc language specification](https://git.philomathiclife.com/calc_rational/lang.pdf). #![expect( + clippy::doc_paragraphs_missing_punctuation, + reason = "false positive for crate documentation having image links" +)] +#![expect( clippy::arithmetic_side_effects, reason = "calculator can't realistically avoid this" )] @@ -144,13 +148,12 @@ use num_rational::Ratio; #[cfg(feature = "rand")] use num_traits::ToPrimitive as _; use num_traits::{Inv as _, Pow as _}; -#[cfg(feature = "priv_sep")] +#[cfg(target_os = "openbsd")] use priv_sep as _; -#[cfg_attr(docsrs, doc(cfg(feature = "rand")))] #[cfg(feature = "rand")] pub use rand; #[cfg(feature = "rand")] -use rand::{RngCore as _, rngs::ThreadRng}; +use rand::{Rng as _, rngs::ThreadRng}; /// Fixed-sized cache that automatically overwrites the oldest data /// when a new item is added and the cache is full. /// @@ -163,6 +166,7 @@ pub mod cache; pub mod lending_iterator; /// Error due to a language violation. #[non_exhaustive] +#[cfg_attr(test, derive(Eq, PartialEq))] #[derive(Debug)] pub enum LangErr { /// The input began with a `q` but had non-whitespace @@ -217,15 +221,12 @@ pub enum LangErr { /// by symbols that could not be chained with the preceding expression. TrailingSyms(usize), /// The input contained an invalid random expression. - #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] #[cfg(feature = "rand")] InvalidRand(usize), /// Error when the second argument is less than first in the rand function. - #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] #[cfg(feature = "rand")] RandInvalidArgs(usize), /// Error when there are no 64-bit integers in the interval passed to the random function. - #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] #[cfg(feature = "rand")] RandNoInts(usize), } @@ -262,6 +263,7 @@ impl Display for LangErr { } } /// A successful evaluation of an input. +#[cfg_attr(test, derive(Eq, PartialEq))] #[derive(Debug)] pub enum O<'a> { /// The input only contained whitespace. @@ -402,6 +404,7 @@ pub struct Evaluator<'input, 'cache, 'prev, 'scratch, 'rand> { /// Random number generator. #[cfg(feature = "rand")] rng: &'rand mut ThreadRng, + /// Need to use `'rand`. #[cfg(not(feature = "rand"))] _rng: PhantomData<fn() -> &'rand ()>, } @@ -1136,7 +1139,6 @@ impl<'input, 'cache, 'prev, 'scratch, 'rand> Evaluator<'input, 'cache, 'prev, 's } } /// Reads data from `R` passing each line to an [`Evaluator`] to be evaluated. -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg(feature = "std")] #[derive(Debug)] pub struct EvalIter<R> { @@ -1156,11 +1158,9 @@ pub struct EvalIter<R> { #[cfg(feature = "rand")] rng: ThreadRng, } -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg(feature = "std")] impl<R> EvalIter<R> { /// Creates a new `EvalIter`. - #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] #[cfg(feature = "rand")] #[inline] pub fn new(reader: R) -> Self { @@ -1191,7 +1191,6 @@ extern crate std; #[cfg(feature = "std")] use std::io::{BufRead, Error}; /// Error returned from [`EvalIter`] when an expression has an error. -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg(feature = "std")] #[derive(Debug)] pub enum E { @@ -1215,7 +1214,6 @@ impl Display for E { } #[cfg(feature = "std")] use crate::lending_iterator::LendingIterator; -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg(feature = "std")] impl<R> LendingIterator for EvalIter<R> where @@ -1260,21 +1258,42 @@ where } #[cfg(test)] mod tests { - use super::*; + use super::{ + BigInt, BigUint, Cache, Evaluator, + LangErr::MissingTerm, + O::{Empty, Eval, Store}, + Sign, Vec, vec, + }; + #[cfg(feature = "rand")] + use super::{BufRead, E, EvalIter, LangErr, LendingIterator as _}; + #[cfg(not(feature = "rand"))] + use super::{ + LangErr::{ + DivByZero, ExpDivByZero, ExpIsNotIntOrOneHalf, InvalidAbs, InvalidDec, InvalidPar, + InvalidQuit, InvalidRound, InvalidStore, ModIsNotInt, ModZero, NotEnoughPrevResults, + NotNonNegIntFact, SqrtDoesNotExist, TrailingSyms, + }, + O::Exit, + Ratio, + }; + #[cfg(not(feature = "rand"))] + use alloc::{borrow::ToOwned as _, string::ToString as _}; + #[cfg(not(feature = "rand"))] + use num_traits::Pow as _; + #[cfg(feature = "rand")] + use num_traits::ToPrimitive as _; + #[cfg(feature = "rand")] + use std::io::{self, Error, Read}; + #[expect(clippy::too_many_lines, reason = "a lot to test")] #[cfg(not(feature = "rand"))] #[test] fn empty() { // Empty expressions without a previous result return nothing. - assert!( - match Evaluator::new(b"\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap() - { - Empty(o) => o.is_none(), - _ => false, - } + assert_eq!( + Evaluator::new(b"\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Ok(Empty(&None)) ); - assert!( + assert_eq!( Evaluator::new( b" \t \t \n", &mut Cache::new(), @@ -1285,11 +1304,10 @@ mod tests { &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> -12" + .map(|a| a.to_string()), + Ok("> -12".to_owned()) ); - assert!( + assert_eq!( Evaluator::new( b"\t\n", &mut Cache::new(), @@ -1300,41 +1318,38 @@ mod tests { &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> -0.666666667" + .map(|a| a.to_string()), + Ok("> -0.666666667".to_owned()) ); - assert!( + assert_eq!( Evaluator::new( b"\t\n", &mut Cache::new(), &mut Some(Ratio::new( BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4230196224, 6])) + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6])) )), &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> 0.000000000" + .map(|a| a.to_string()), + Ok("> 0.000000000".to_owned()) ); - assert!( + assert_eq!( Evaluator::new( b"\t\n", &mut Cache::new(), &mut Some(Ratio::new( BigInt::from_biguint(Sign::Plus, BigUint::new(vec![17])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4230196224, 6])) + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6])) )), &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> 0.000000001" + .map(|a| a.to_string()), + Ok("> 0.000000001".to_owned()) ); - assert!( + assert_eq!( Evaluator::new( b"\t\n", &mut Cache::new(), @@ -1345,11 +1360,10 @@ mod tests { &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> 0.3" + .map(|a| a.to_string()), + Ok("> 0.3".to_owned()) ); - assert!( + assert_eq!( Evaluator::new( b"\t\n", &mut Cache::new(), @@ -1360,11 +1374,10 @@ mod tests { &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> -20.3" + .map(|a| a.to_string()), + Ok("> -20.3".to_owned()) ); - assert!( + assert_eq!( Evaluator::new( &[0u8; 0], &mut Cache::new(), @@ -1375,49 +1388,48 @@ mod tests { &mut Vec::new() ) .evaluate() - .unwrap() - .to_string() - == "> -20.3" + .map(|a| a.to_string()), + Ok("> -20.3".to_owned()) ); } + #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] #[cfg(not(feature = "rand"))] #[test] fn number_literal() { // Normal 0 is fine. - assert!( - Evaluator::new(b"0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::NoSign, - BigUint::new(Vec::new()) - )) + assert_eq!( + Evaluator::new(b"0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(Vec::new()) + )))) ); // Leading 0s and trailing 0s are fine. - assert!( + assert_eq!( Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::NoSign, - BigUint::new(Vec::new()) - )) + .get_rational(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(Vec::new()) + )))) ); // Parsing stops at first non-(digit/period). - assert!( - Evaluator::new(b"1 0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); let int = b"3397450981271938475135134759823759835414"; let frac = b"913759810573549872354897210539127530981570"; - let left = Ratio::from_integer(BigInt::parse_bytes(int, 10).unwrap()); + let left = Ratio::from_integer( + BigInt::parse_bytes(int, 10) + .unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")), + ); let right = Ratio::new( - BigInt::parse_bytes(frac, 10).unwrap(), + BigInt::parse_bytes(frac, 10) + .unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")), BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]).pow(frac.len() + 1)), ); let mut vec = Vec::new(); @@ -1426,381 +1438,310 @@ mod tests { vec.push(b'0'); vec.extend_from_slice(frac); // Test a number whose integer and fraction portions are larger than u128::MAX. - assert!( + assert_eq!( Evaluator::new( vec.as_slice(), &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_rational() - .unwrap() - .unwrap() - == left + right + .get_rational(), + Ok(Some(left + right)) ); // Leading 0s and trailing 0s for a non-zero value are fine. - assert!( + assert_eq!( Evaluator::new( b"000000014.0000000000000", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_rational() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![14]))) + .get_rational(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![14]) + )))) ); // A sequence of digits followed immediately by a decimal point but no digits after is invalid. - assert!( - match Evaluator::new(b"1.", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap_err() - { - InvalidDec(i) => i == 2, - _ => false, - } + assert_eq!( + Evaluator::new(b"1.", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Err(InvalidDec(2)) ); // A sequence of digits followed immediately by a decimal point but no digits after is invalid. // This just shows that spaces are not ignored in number literals. - assert!( - match Evaluator::new(b"1. 2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap_err() - { - InvalidDec(i) => i == 2, - _ => false, - } + assert_eq!( + Evaluator::new(b"1. 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Err(InvalidDec(2)) ); // Non-whitespace starting the input is valid but produces no value. // This also shows that an invalid byte sequence does not produce an error here. - assert!( - Evaluator::new(b"a1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b"a1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(None) ); // A space starting the input is valid but produces no value. - assert!( - Evaluator::new(b" 1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b" 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(None) ); // A tab starting the input is valid but produces no value. - assert!( - Evaluator::new(b"\t1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b"\t1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(None) ); // Negative literals don't exist, so this should succeed but produce nothing. - assert!( - Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(None) ); // '/' is division and not part of a number literal so only the "numerator" is parsed. - assert!( - Evaluator::new(b"1/2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // A sequence of digits followed by invalid bytes is valid and produces a number equal to the digits before the invalid bytes. - assert!( - Evaluator::new(b"130alj", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_rational() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![130]))) + assert_eq!( + Evaluator::new(b"130alj", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![130]) + )))) ); } #[cfg(not(feature = "rand"))] #[test] fn par() { // Missing closing ')' - assert!( - match Evaluator::new(b"(1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_par() - .unwrap_err() - { - InvalidPar(i) => i == 2, - _ => false, - } + assert_eq!( + Evaluator::new(b"(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), + Err(InvalidPar(2)) ); - assert!( - match Evaluator::new(b"((1\t + 2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_par() - .unwrap_err() - { - InvalidPar(i) => i == 9, - _ => false, - } + assert_eq!( + Evaluator::new(b"((1\t + 2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), + Err(InvalidPar(9)) ); - assert!( - Evaluator::new(b"( 0 \t )", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_par() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::NoSign, - BigUint::new(Vec::new()) - )) + assert_eq!( + Evaluator::new(b"( 0 \t )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(Vec::new()) + )))) ); - assert!( - Evaluator::new(b"( - \t 5 )", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_par() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![5]))) + assert_eq!( + Evaluator::new(b"( - \t 5 )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![5]) + )))) ); - assert!( + assert_eq!( Evaluator::new( b"( ( 2 -\t 5) * 9 )", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_par() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![27]))) + .get_par(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![27]) + )))) ); } + #[expect(clippy::too_many_lines, reason = "a lot to test")] + #[expect( + clippy::indexing_slicing, + clippy::unwrap_used, + reason = "comments justify correctness" + )] #[cfg(not(feature = "rand"))] #[test] fn recall_expression() { // If the input does not start with '@', then it's valid but produces nothing. - assert!( - Evaluator::new(b"1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b"1", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Ok(None) ); - assert!( - Evaluator::new(b"a", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b"a", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Ok(None) ); - assert!( - Evaluator::new(b" @", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b" @", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Ok(None) ); - assert!( - Evaluator::new(b"\t@", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b"\t@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Ok(None) ); // Invalid recall expression since there are no previous results. - assert!( - match Evaluator::new(b"@", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap_err() - { - NotEnoughPrevResults(count) => count == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Err(NotEnoughPrevResults(0)) ); // Invalid recall expression since there are no previous results. - assert!( - match Evaluator::new(b"@4", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap_err() - { - NotEnoughPrevResults(count) => count == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"@4", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Err(NotEnoughPrevResults(0)) ); // Invalid recall expression since there are no previous results. // The input violates our grammar, but this error happens before that. - assert!( - match Evaluator::new(b"@0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_recall() - .unwrap_err() - { - NotEnoughPrevResults(count) => count == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"@0", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), + Err(NotEnoughPrevResults(0)) ); // Successfully extract previous expression. let mut prev = None; let mut cache = Cache::new(); - drop( - Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - drop( - Evaluator::new(b" s \r\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - assert!( - Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b" s \r\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + assert_eq!( + Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // Invalid characters are ignored at this stage. - assert!( - Evaluator::new(b"@&", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@&", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // 0 is not a valid stored value only 1-8 are. - assert!( - Evaluator::new(b"@0", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@0", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // 9 is not a valid stored value only 1-8 are. - assert!( - Evaluator::new(b"@9", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@9", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // Spaces are not cleaned; otherwise this would error since we only have 1 stored value. - assert!( - Evaluator::new(b"@ 2", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@ 2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // Tabs are not cleaned; otherwise this would error since we only have 1 stored value. - assert!( - Evaluator::new(b"@\t2", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@\t2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // One digits are looked at so this is not @<ten>. - assert!( - Evaluator::new(b"@10", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@10", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // Invalid recall expression since there is only one stored result. - assert!( - match Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap_err() - { - NotEnoughPrevResults(count) => count == 1, - _ => false, - } - ); - drop( - Evaluator::new(b"2\r\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - drop( - Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), + assert_eq!( + Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Err(NotEnoughPrevResults(1)) ); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"2\r\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); // Stored values correct. - assert!( - Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + )))) ); - assert!( - Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + )))) ); // Invalid recall expression since there are only three stored results. - assert!( - match Evaluator::new(b"@3", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap_err() - { - NotEnoughPrevResults(count) => count == 2, - _ => false, - } + assert_eq!( + Evaluator::new(b"@3", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Err(NotEnoughPrevResults(2)) ); let mut v = vec![0, b'\n']; for i in b'3'..=b'8' { + // `v.len() > 0`. v[0] = i; - drop( - Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - drop( - Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); + _ = Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); } v[0] = b'@'; for i in b'1'..=b'8' { + // `v.len() > 1`. v[1] = i; // Cache is filled up correctly storing all previous values. - assert!( - Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![(b'9' - i) as u32]) - )) + assert_eq!( + Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![u32::from(b'9' - i)]) + )))) ); } // Only parses the first @ since the second @ is not a digit between 1 and 8. - assert!( - Evaluator::new(b"@@", &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8]))) - ); - drop( - Evaluator::new(b"9\r\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - drop( - Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), + assert_eq!( + Evaluator::new(b"@@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![8]) + )))) ); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"9\r\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); // Oldest value is overwritten; all others remain. for i in b'1'..=b'8' { + // `v.len() > 1`. v[1] = i; - assert!( - Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()) - .get_recall() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![((b'9' + 1) - i) as u32]) - )) + assert_eq!( + Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![u32::from((b'9' + 1) - i)]) + )))) ); } } @@ -1808,229 +1749,182 @@ mod tests { #[test] fn abs() { // Missing closing '|' - assert!( - match Evaluator::new(b"|1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_abs() - .unwrap_err() - { - InvalidAbs(i) => i == 2, - _ => false, - } + assert_eq!( + Evaluator::new(b"|1", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), + Err(InvalidAbs(2)) ); - assert!( - match Evaluator::new(b"||1 + 2|", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_abs() - .unwrap_err() - { - InvalidAbs(i) => i == 8, - _ => false, - } + assert_eq!( + Evaluator::new(b"||1 + 2|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), + Err(InvalidAbs(8)) ); - assert!( + assert_eq!( Evaluator::new( b"| 0\t \t |", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_abs() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::NoSign, - BigUint::new(Vec::new()) - )) + .get_abs(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(Vec::new()) + )))) ); - assert!( - Evaluator::new(b"| - 5 |", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_abs() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![5]))) + assert_eq!( + Evaluator::new(b"| - 5 |", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![5]) + )))) ); - assert!( + assert_eq!( Evaluator::new( b"| \t| 2 - 5| * 9 |", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_abs() - .unwrap() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27]))) + .get_abs(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![27]) + )))) ); // If the input does not start with '|', then it's valid but produces nothing. - assert!( - Evaluator::new(b" \t|9|", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_abs() - .unwrap() - .is_none() + assert_eq!( + Evaluator::new(b" \t|9|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), + Ok(None) ); } #[cfg(not(feature = "rand"))] #[test] fn round() { // Missing ',<digit>)' - assert!( - match Evaluator::new(b"round(1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_round() - .unwrap_err() - { - InvalidRound(i) => i == 7, - _ => false, - } + assert_eq!( + Evaluator::new(b"round(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), + Err(InvalidRound(7)) ); - assert!( - match Evaluator::new(b"round(1,", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_round() - .unwrap_err() - { - InvalidRound(i) => i == 8, - _ => false, - } + assert_eq!( + Evaluator::new(b"round(1,", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), + Err(InvalidRound(8)) ); - assert!( - match Evaluator::new(b"round(1,2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_round() - .unwrap_err() - { - InvalidRound(i) => i == 9, - _ => false, - } + assert_eq!( + Evaluator::new(b"round(1,2", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), + Err(InvalidRound(9)) ); - assert!(match Evaluator::new( - b"round(1,10)", - &mut Cache::new(), - &mut None, - &mut Vec::new() - ) - .get_round() - .unwrap_err() - { - InvalidRound(i) => i == 9, - _ => false, - }); - assert!( - match Evaluator::new(b"round(1,a)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_round() - .unwrap_err() - { - InvalidRound(i) => i == 8, - _ => false, - } + assert_eq!( + Evaluator::new( + b"round(1,10)", + &mut Cache::new(), + &mut None, + &mut Vec::new() + ) + .get_round(), + Err(InvalidRound(9)) ); - assert!( + assert_eq!( + Evaluator::new(b"round(1,a)", &mut Cache::new(), &mut None, &mut Vec::new()) + .get_round(), + Err(InvalidRound(8)) + ); + assert_eq!( Evaluator::new( b"round(2, 7)", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_round() - .unwrap() - == Some(Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![2]) - ))) + .get_round(), + Ok(Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + )))) ); - assert!( + assert_eq!( Evaluator::new( b"round(2.677, 1)", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_round() - .unwrap() - == Some(Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) - )) + .get_round(), + Ok(Some(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) + ))) ); } + #[expect(clippy::too_many_lines, reason = "a lot to test")] #[cfg(feature = "rand")] #[test] fn rand() { - assert!(match Evaluator::new( - b"rand(1", - &mut Cache::new(), - &mut None, - &mut Vec::new(), - &mut rand::rng() - ) - .get_rand() - .unwrap_err() - { - LangErr::InvalidRand(i) => i == 6, - _ => false, - }); - assert!(match Evaluator::new( - b"rand(1,2", - &mut Cache::new(), - &mut None, - &mut Vec::new(), - &mut rand::rng() - ) - .get_rand() - .unwrap_err() - { - LangErr::InvalidRand(i) => i == 8, - _ => false, - }); - assert!(match Evaluator::new( - b"rand(1/2,3/4)", - &mut Cache::new(), - &mut None, - &mut Vec::new(), - &mut rand::rng(), - ) - .get_rand() - .unwrap_err() - { - LangErr::RandNoInts(i) => i == 13, - _ => false, - }); - assert!(match Evaluator::new( - b"rand(-100000000000000000000000,-1000000000000000000000)", - &mut Cache::new(), - &mut None, - &mut Vec::new(), - &mut rand::rng(), - ) - .get_rand() - .unwrap_err() - { - LangErr::RandNoInts(i) => i == 55, - _ => false, - }); - assert!(match Evaluator::new( - b"rand(2/3,1/3)", - &mut Cache::new(), - &mut None, - &mut Vec::new(), - &mut rand::rng(), - ) - .get_rand() - .unwrap_err() - { - LangErr::RandInvalidArgs(i) => i == 13, - _ => false, - }); + assert_eq!( + Evaluator::new( + b"rand(1", + &mut Cache::new(), + &mut None, + &mut Vec::new(), + &mut rand::rng() + ) + .get_rand(), + Err(LangErr::InvalidRand(6)) + ); + assert_eq!( + Evaluator::new( + b"rand(1,2", + &mut Cache::new(), + &mut None, + &mut Vec::new(), + &mut rand::rng() + ) + .get_rand(), + Err(LangErr::InvalidRand(8)) + ); + assert_eq!( + Evaluator::new( + b"rand(1/2,3/4)", + &mut Cache::new(), + &mut None, + &mut Vec::new(), + &mut rand::rng(), + ) + .get_rand(), + Err(LangErr::RandNoInts(13)) + ); + assert_eq!( + Evaluator::new( + b"rand(-100000000000000000000000,-1000000000000000000000)", + &mut Cache::new(), + &mut None, + &mut Vec::new(), + &mut rand::rng(), + ) + .get_rand(), + Err(LangErr::RandNoInts(55)) + ); + assert_eq!( + Evaluator::new( + b"rand(2/3,1/3)", + &mut Cache::new(), + &mut None, + &mut Vec::new(), + &mut rand::rng(), + ) + .get_rand(), + Err(LangErr::RandInvalidArgs(13)) + ); // If the input does not start with 'rand(', then it's invalid since get_rand must only be called as the last terminal expression which means whitespace must be consumed already. - assert!(match Evaluator::new( - b" rand(2/3,2)", - &mut Cache::new(), - &mut None, - &mut Vec::new(), - &mut rand::rng(), - ) - .get_rand() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - }); + assert_eq!( + Evaluator::new( + b" rand(2/3,2)", + &mut Cache::new(), + &mut None, + &mut Vec::new(), + &mut rand::rng(), + ) + .get_rand(), + Err(MissingTerm(0)) + ); assert!( Evaluator::new( b"rand(2, 7)", @@ -2040,12 +1934,11 @@ mod tests { &mut rand::rng() ) .get_rand() - .map(|r| { + .is_ok_and(|r| { let int = r.numer(); int >= &BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) && *int <= BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7])) }) - .unwrap() ); assert!( Evaluator::new( @@ -2056,13 +1949,12 @@ mod tests { &mut rand::rng() ) .get_rand() - .map(|r| { + .is_ok_and(|r| { let int = r.numer(); int >= &BigInt::from(i64::MIN) && *int <= BigInt::from(i64::MAX) }) - .unwrap() ); - for _ in 0..100 { + for _ in 0..100u8 { assert!( Evaluator::new( b"rand(2, 2)", @@ -2072,98 +1964,122 @@ mod tests { &mut rand::rng() ) .get_rand() - .map(|r| *r.numer() == BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) - .unwrap() + .is_ok_and( + |r| *r.numer() == BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) + ) ); } } + #[expect( + clippy::indexing_slicing, + clippy::unwrap_used, + reason = "comment justifies correctness" + )] #[cfg(feature = "rand")] #[test] - #[ignore] + #[ignore = "slow"] fn rand_uni() { + const COUNT: u32 = 999_999; + #[expect( + clippy::integer_division, + clippy::integer_division_remainder_used, + reason = "correct" + )] + const LOWER: u32 = COUNT * 33 / 100; + #[expect( + clippy::integer_division, + clippy::integer_division_remainder_used, + reason = "correct" + )] + const UPPER: u32 = COUNT * 101 / 300; // Test rand on an interval that is not a power of 2 in size. // This causes rand to adjust the interval to enforce uniformity. let mut vals = [0u32; 3]; let mut vec = Vec::new(); let mut cache = Cache::new(); let mut none = None; - const COUNT: u32 = 999999; for _ in 1..COUNT { - vals[(Evaluator::new( - b"rand(-1, 1)", - &mut cache, - &mut none, - &mut vec, - &mut rand::rng(), + // We want to `panic` if `rand` does not work correctly. + vals[usize::try_from( + Evaluator::new( + b"rand(-1, 1)", + &mut cache, + &mut none, + &mut vec, + &mut rand::rng(), + ) + .get_rand() + .unwrap() + .numer() + .to_i32() + .unwrap() + + 1i32, ) - .get_rand() - .unwrap() - .numer() - .to_i32() - .unwrap() - + 1) as usize] += 1; + .unwrap()] += 1; } // Test that the distribution is within 1% of what is expected. - assert!( - vals.into_iter() - .try_fold(false, |_, r| { - if r < COUNT * 33 / 100 || r > COUNT * 101 / 300 { - Err(false) - } else { - Ok(true) - } - }) - .unwrap() + assert_eq!( + vals.into_iter().try_fold(false, |_, r| { + if (LOWER..=UPPER).contains(&r) { + Ok(true) + } else { + Err(false) + } + }), + Ok(true) ); } + #[allow( + clippy::allow_attributes, + reason = "unwrap_used only fires when rand is not enabled" + )] + #[allow(clippy::unwrap_used, reason = "comments justify correctness")] #[test] fn term() { #[cfg(not(feature = "rand"))] - assert!( - Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_term() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::NoSign, - BigUint::new(Vec::new()) - )) + assert_eq!( + Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(Vec::new()) + ))) ); #[cfg(not(feature = "rand"))] - assert!( - Evaluator::new(b"(4)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_term() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))) + assert_eq!( + Evaluator::new(b"(4)", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![4]) + ))) ); #[cfg(not(feature = "rand"))] - assert!( + assert_eq!( Evaluator::new( b"round(-2/3,2)", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_term() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Minus, BigUint::new(vec![67])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![100])) - ) + .get_term(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Minus, BigUint::new(vec![67])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![100])) + )) ); #[cfg(feature = "rand")] - assert!( + drop( Evaluator::new( b"rand()", &mut Cache::new(), &mut None, &mut Vec::new(), - &mut rand::rng() + &mut rand::rng(), ) .get_term() - .is_ok() + .unwrap(), ); #[cfg(feature = "rand")] - assert!( + drop( Evaluator::new( b"rand(-13/93, 833)", &mut Cache::new(), @@ -2172,1181 +2088,1023 @@ mod tests { &mut rand::rng(), ) .get_term() - .is_ok() + .unwrap(), ); #[cfg(not(feature = "rand"))] - assert!( - match Evaluator::new(b"rand()", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_term() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"rand()", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), + Err(MissingTerm(0)) ); #[cfg(not(feature = "rand"))] - assert!( - Evaluator::new(b"|4|", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_term() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))) + assert_eq!( + Evaluator::new(b"|4|", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![4]) + ))) ); // Terminal expressions do no clean up before or after. #[cfg(not(feature = "rand"))] - assert!( - match Evaluator::new(b" 2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_term() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), + Err(MissingTerm(0)) ); #[cfg(not(feature = "rand"))] let mut prev = None; #[cfg(not(feature = "rand"))] let mut cache = Cache::new(); #[cfg(not(feature = "rand"))] - drop( - Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) + { + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) .evaluate() - .unwrap(), - ); - #[cfg(not(feature = "rand"))] - drop( - Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) + .unwrap(); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) .evaluate() - .unwrap(), - ); + .unwrap(); + } #[cfg(not(feature = "rand"))] - assert!( - Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()) - .get_term() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_term(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); } + #[expect(clippy::unwrap_used, reason = "comments justify correctness")] #[cfg(not(feature = "rand"))] #[test] fn factorial() { // Negative integer is not allowed. - assert!( - match Evaluator::new(b"(-1)!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap_err() - { - NotNonNegIntFact(i) => i == 5, - _ => false, - } + assert_eq!( + Evaluator::new(b"(-1)!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Err(NotNonNegIntFact(5)) ); // Non-integer is not allowed. - assert!( - match Evaluator::new(b"2.5!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap_err() - { - NotNonNegIntFact(i) => i == 4, - _ => false, - } + assert_eq!( + Evaluator::new(b"2.5!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Err(NotNonNegIntFact(4)) ); // factorials always become terminal expressions eventually. - assert!( - Evaluator::new(b"7", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7]))) + assert_eq!( + Evaluator::new(b"7", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![7]) + ))) ); - assert!( - Evaluator::new(b"(7)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7]))) + assert_eq!( + Evaluator::new(b"(7)", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![7]) + ))) ); - assert!( - Evaluator::new(b"|7|", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7]))) + assert_eq!( + Evaluator::new(b"|7|", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![7]) + ))) ); let mut prev = None; let mut cache = Cache::new(); - drop( - Evaluator::new(b"3\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - drop( - Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); - assert!( - Evaluator::new(b"@!", &mut cache, &mut prev, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6]))) + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"3\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); + assert_eq!( + Evaluator::new(b"@!", &mut cache, &mut prev, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![6]) + ))) ); - assert!( - Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3]))) + assert_eq!( + Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![3]) + ))) ); // 0! = 1. - assert!( - Evaluator::new(b"0.0!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"0.0!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); // 1! = 1. - assert!( - Evaluator::new(b"1!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); // 4! = 24, and whitespace is not consumed. - assert!( - Evaluator::new(b"4! \t", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![24]))) + assert_eq!( + Evaluator::new(b"4! \t", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![24]) + ))) ); // Factorials can be chained. - assert!( - Evaluator::new(b"3!! ", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![720]))) + assert_eq!( + Evaluator::new(b"3!! ", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![720]) + ))) ); // only factorial is consumed. - assert!( - Evaluator::new(b"2!+3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"2!+3", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + ))) ); // Error since leading/trailing whitespace is not consumed by factorial or higher precedence expressions. - assert!( - match Evaluator::new(b" 2!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" 2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(b"\t2!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"\t2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Err(MissingTerm(0)) ); // Error since negation is not consumed by factorial or higher precedence expressions. - assert!( - match Evaluator::new(b"-2!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_fact() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"-2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), + Err(MissingTerm(0)) ); } + #[expect( + clippy::cognitive_complexity, + clippy::too_many_lines, + reason = "a lot to test" + )] #[cfg(not(feature = "rand"))] #[test] fn exp() { // 1 can be raised to anything and return 1. // Also white space is ignored between operator. - assert!( - Evaluator::new(b"1 ^\t 0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1 ^\t 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"1^0.5", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"1^(-1/2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1^(-1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"1.0^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"1.0^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); // 0 can be raised to any non-negative value and will always return 0 unless raised to 0 which will return 1. - assert!( - Evaluator::new(b"0^0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"0^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"0^1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::NoSign, BigUint::new(vec![0]))) + assert_eq!( + Evaluator::new(b"0^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(vec![0]) + ))) ); - assert!( - Evaluator::new(b"0^0.5", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::NoSign, BigUint::new(vec![0]))) + assert_eq!( + Evaluator::new(b"0^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(vec![0]) + ))) ); // Anything else can only be raised to integers. - assert!( - Evaluator::new(b"4^0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) - ); - assert!( - Evaluator::new(b"4^1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))) - ); - assert!( - Evaluator::new(b"4^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) - ) - ); - assert!( - Evaluator::new(b"(-4)^0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) - ); - assert!( - Evaluator::new(b"(-4)^1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![4]))) - ); - assert!( - Evaluator::new(b"(-4)^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16]))) - ); - assert!( - Evaluator::new(b"(-4)^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) - ) + assert_eq!( + Evaluator::new(b"4^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"(-4)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) - ) + assert_eq!( + Evaluator::new(b"4^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![4]) + ))) ); - assert!( - Evaluator::new(b"(2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"4^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) + )) ); - assert!( - Evaluator::new(b"(2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) - ) + assert_eq!( + Evaluator::new(b"(-4)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"(2/3)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) - ) + assert_eq!( + Evaluator::new(b"(-4)^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![4]) + ))) ); - assert!( - Evaluator::new(b"(-2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"(-4)^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![16]) + ))) ); - assert!( - Evaluator::new(b"(-2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) - ) + assert_eq!( + Evaluator::new(b"(-4)^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) + )) + ); + assert_eq!( + Evaluator::new(b"(-4)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) + )) + ); + assert_eq!( + Evaluator::new(b"(2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"(-2/3)^(3)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Minus, BigUint::new(vec![8])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])) - ) + assert_eq!( + Evaluator::new(b"(2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) + )) + ); + assert_eq!( + Evaluator::new(b"(2/3)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) + )) + ); + assert_eq!( + Evaluator::new(b"(-2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( + assert_eq!( + Evaluator::new(b"(-2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) + )) + ); + assert_eq!( + Evaluator::new(b"(-2/3)^(3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Minus, BigUint::new(vec![8])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])) + )) + ); + assert_eq!( Evaluator::new( b"(-2/3)^(-2)", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) - ) + .get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) + )) ); - assert!( + assert_eq!( Evaluator::new( b"(-2/3)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Minus, BigUint::new(vec![27])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) - ) + .get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Minus, BigUint::new(vec![27])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) + )) ); - assert!( + assert_eq!( Evaluator::new( b"(4/9)^(-1/2)", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_exps() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) - ) + .get_exps(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) + )) ); // Error since 0 cannot be raised to a negative power. - assert!( - match Evaluator::new(b"0^(-1)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap_err() - { - ExpDivByZero(i) => i == 6, - _ => false, - } + assert_eq!( + Evaluator::new(b"0^(-1)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Err(ExpDivByZero(6)) ); // Error since anything other than 0 or 1 cannot be raised to a non-integer power or (+/-) 1/2. - assert!( - match Evaluator::new(b"2^(1/3)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap_err() - { - ExpIsNotIntOrOneHalf(i) => i == 7, - _ => false, - } + assert_eq!( + Evaluator::new(b"2^(1/3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Err(ExpIsNotIntOrOneHalf(7)) ); // When exponent is (+/-) 1/2, base has to be the square of a rational number. - assert!( - match Evaluator::new(b"2^(1/2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap_err() - { - SqrtDoesNotExist(i) => i == 7, - _ => false, - } + assert_eq!( + Evaluator::new(b"2^(1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Err(SqrtDoesNotExist(7)) ); // exps always become factorials eventually. - assert!( - Evaluator::new(b"3!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6]))) + assert_eq!( + Evaluator::new(b"3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![6]) + ))) ); // exponentiation has lower precedence than factorials. - assert!( - Evaluator::new(b"2^3!", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64]))) + assert_eq!( + Evaluator::new(b"2^3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![64]) + ))) ); - assert!( - Evaluator::new(b"3!^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![36]))) + assert_eq!( + Evaluator::new(b"3!^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![36]) + ))) ); // Error since leading/trailing whitespace is not consumed by exponentiation or higher precedence expressions. - assert!( - match Evaluator::new(b" 2^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" 2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(b"\t2^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_exps() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"\t2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), + Err(MissingTerm(0)) ); } #[cfg(not(feature = "rand"))] #[test] fn neg() { - assert!( - Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"- \t - 1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"- \t - 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"-0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint( - Sign::NoSign, - BigUint::new(Vec::new()) - )) + assert_eq!( + Evaluator::new(b"-0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::NoSign, + BigUint::new(Vec::new()) + ))) ); // negation has lower precedence than exponentiation. - assert!( - Evaluator::new(b"-2^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![4]))) + assert_eq!( + Evaluator::new(b"-2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![4]) + ))) ); // negation always becomes exponentiation eventually. - assert!( - Evaluator::new(b"2.0^2.0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))) + assert_eq!( + Evaluator::new(b"2.0^2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![4]) + ))) ); // Error since leading/trailing whitespace is not consumed by exponentiation or higher precedence expressions. - assert!( - match Evaluator::new(b" -2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" -2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(b"\t-2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_neg() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"\t-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), + Err(MissingTerm(0)) ); } + #[expect( + clippy::cognitive_complexity, + clippy::too_many_lines, + reason = "a lot to test" + )] #[cfg(not(feature = "rand"))] #[test] fn mult() { - assert!( - Evaluator::new(b"2 * 3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6]))) + assert_eq!( + Evaluator::new(b"2 * 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![6]) + ))) ); - assert!( + assert_eq!( Evaluator::new( b"-2 * \t 3", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![6]))) + .get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![6]) + ))) ); - assert!( + assert_eq!( Evaluator::new( b"2\t * -3.0", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![6]))) + .get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![6]) + ))) ); - assert!( - Evaluator::new(b"-2.5*-3.5", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![35])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) - ) + assert_eq!( + Evaluator::new(b"-2.5*-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![35])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) + )) ); - assert!( + assert_eq!( Evaluator::new(b"4.0\t / 6", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])) - ) - ); - assert!( - Evaluator::new(b"6/3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + .get_mults(), + Ok(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])) + )) + ); + assert_eq!( + Evaluator::new(b"6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + ))) ); - assert!( - Evaluator::new(b"-6/3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"-6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![2]) + ))) ); - assert!( - Evaluator::new(b"6/-3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"6/-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![2]) + ))) ); - assert!( + assert_eq!( Evaluator::new( b"- 6 /\t - 3", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + .get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + ))) ); // Number literals are not strictly equivalent to "ratios" as "ratios" don't exist (i.e., 2/3 is not the ratio of 2 to 3 but is the rational number two divided by the rational number 3). assert!( Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new()) .get_mults() - .unwrap() - != Evaluator::new(b"1/3/2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() + .is_ok_and(|r| { + Evaluator::new(b"1/3/2", &mut Cache::new(), &mut None, &mut Vec::new()) + .get_mults() + .is_ok_and(|r2| r != r2) + }) ); assert!( Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new()) .get_mults() - .unwrap() - == Evaluator::new(b"1/(3/2)", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() + .is_ok_and(|r| { + Evaluator::new(b"1/(3/2)", &mut Cache::new(), &mut None, &mut Vec::new()) + .get_mults() + .is_ok_and(|r2| r == r2) + }) ); // multiplication always becomes negation eventually. - assert!( - Evaluator::new(b"-2.0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"-2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![2]) + ))) ); // Error since leading/trailing whitespace is not consumed by multiplication or higher precedence expressions. - assert!( - match Evaluator::new(b" 2*2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" 2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(b"\t2/2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"\t2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Err(MissingTerm(0)) ); - assert!( + assert_eq!( Evaluator::new( b"4.0\t mod 6", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])),) + .get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![4]) + ),)) ); - assert!( - Evaluator::new(b"5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + ))) ); - assert!( - Evaluator::new(b"-5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"-5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"5 mod -3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"5 mod -3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + ))) ); - assert!( + assert_eq!( Evaluator::new( b"-5 mod\t -3", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + .get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); // Cannot divide by 0. - assert!( - match Evaluator::new(b"2/0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap_err() - { - DivByZero(i) => i == 3, - _ => false, - } + assert_eq!( + Evaluator::new(b"2/0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Err(DivByZero(3)) ); - assert!( - match Evaluator::new(b"2 mod 0", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap_err() - { - ModZero(i) => i == 7, - _ => false, - } + assert_eq!( + Evaluator::new(b"2 mod 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Err(ModZero(7)) ); // Right and left operands of mod must be integers. - assert!( - match Evaluator::new(b"3.2 mod 1", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap_err() - { - ModIsNotInt(i) => i == 4, - _ => false, - } + assert_eq!( + Evaluator::new(b"3.2 mod 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Err(ModIsNotInt(4)) ); - assert!( - match Evaluator::new(b"3 mod 3.2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap_err() - { - ModIsNotInt(i) => i == 9, - _ => false, - } + assert_eq!( + Evaluator::new(b"3 mod 3.2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Err(ModIsNotInt(9)) ); // multiplication has lower precedence than exponentiation. - assert!( - Evaluator::new(b"2*2^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8]))) + assert_eq!( + Evaluator::new(b"2*2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![8]) + ))) ); - assert!( - Evaluator::new(b"8/2^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"8/2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![2]) + ))) ); - assert!( - Evaluator::new(b"8 mod 3^2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_mults() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8]))) + assert_eq!( + Evaluator::new(b"8 mod 3^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![8]) + ))) ); } + #[expect(clippy::too_many_lines, reason = "a lot to test")] #[cfg(not(feature = "rand"))] #[test] fn add() { - assert!( - Evaluator::new(b"2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![5]))) + assert_eq!( + Evaluator::new(b"2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![5]) + ))) ); - assert!( - Evaluator::new(b"-2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"-2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); - assert!( + assert_eq!( Evaluator::new( b"2 + \t -3.0", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1]))) + .get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![1]) + ))) ); - assert!( - Evaluator::new(b"-2.5+-3.5", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![6]))) + assert_eq!( + Evaluator::new(b"-2.5+-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![6]) + ))) ); - assert!( - Evaluator::new(b"4.0\t - 6", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"4.0\t - 6", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![2]) + ))) ); - assert!( - Evaluator::new(b"6-3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3]))) + assert_eq!( + Evaluator::new(b"6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![3]) + ))) ); - assert!( - Evaluator::new(b"-6-3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![9]))) + assert_eq!( + Evaluator::new(b"-6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![9]) + ))) ); - assert!( - Evaluator::new(b"6--3", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9]))) + assert_eq!( + Evaluator::new(b"6--3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![9]) + ))) ); - assert!( + assert_eq!( Evaluator::new( b"- 6 -\t - 3", &mut Cache::new(), &mut None, &mut Vec::new() ) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![3]))) + .get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![3]) + ))) ); // addition always becomes multiplication eventually. - assert!( - Evaluator::new(b"2 * 8", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16]))) + assert_eq!( + Evaluator::new(b"2 * 8", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![16]) + ))) ); - assert!( - Evaluator::new(b"8 /\t 2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4]))) + assert_eq!( + Evaluator::new(b"8 /\t 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![4]) + ))) ); // Error since leading/trailing whitespace is not consumed by addition or higher precedence expressions. - assert!( - match Evaluator::new(b" 2+2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" 2+2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(b" 2-2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b" 2-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Err(MissingTerm(0)) ); // addition has lower precedence than multiplication. - assert!( - Evaluator::new(b"2+2*2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6]))) + assert_eq!( + Evaluator::new(b"2+2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![6]) + ))) ); - assert!( - Evaluator::new(b"2+2/2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3]))) + assert_eq!( + Evaluator::new(b"2+2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![3]) + ))) ); - assert!( - Evaluator::new(b"2-2*2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Minus, BigUint::new(vec![2]))) + assert_eq!( + Evaluator::new(b"2-2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Minus, + BigUint::new(vec![2]) + ))) ); - assert!( - Evaluator::new(b"2-2/2", &mut Cache::new(), &mut None, &mut Vec::new()) - .get_adds() - .unwrap() - == Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]))) + assert_eq!( + Evaluator::new(b"2-2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), + Ok(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))) ); } #[cfg(not(feature = "rand"))] #[test] fn exit() { - assert!( - match Evaluator::new(b" q \n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap() - { - Exit => true, - _ => false, - } + assert_eq!( + Evaluator::new(b" q \n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Ok(Exit) ); - assert!(match Evaluator::new( - b" q \r\n", - &mut Cache::new(), - &mut None, - &mut Vec::new() - ) - .evaluate() - .unwrap() - { - Exit => true, - _ => false, - }); - assert!( - match Evaluator::new(b"q\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap() - { - Exit => true, - _ => false, - } + assert_eq!( + Evaluator::new( + b" q \r\n", + &mut Cache::new(), + &mut None, + &mut Vec::new() + ) + .evaluate(), + Ok(Exit) ); - assert!( - match Evaluator::new(b"q\r\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap() - { - Exit => true, - _ => false, - } + assert_eq!( + Evaluator::new(b"q\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Ok(Exit) ); - assert!( - match Evaluator::new(b"\rq\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"q\r\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Ok(Exit) ); - assert!( - match Evaluator::new(b"\tq\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap() - { - Exit => true, - _ => false, - } + assert_eq!( + Evaluator::new(b"\rq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(b"q\n\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap_err() - { - InvalidQuit => true, - _ => false, - } + assert_eq!( + Evaluator::new(b"\tq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Ok(Exit) ); - assert!( - match Evaluator::new(b"\nq\n", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(b"q\n\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Err(InvalidQuit) ); - assert!( - match Evaluator::new(b"q", &mut Cache::new(), &mut None, &mut Vec::new()) - .evaluate() - .unwrap() - { - Exit => true, - _ => false, - } + assert_eq!( + Evaluator::new(b"\nq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Err(MissingTerm(0)) + ); + assert_eq!( + Evaluator::new(b"q", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Ok(Exit) ); } + #[expect(clippy::unwrap_used, reason = "comment justifies correctness")] #[cfg(not(feature = "rand"))] #[test] fn store() { let mut prev = None; let mut cache = Cache::new(); - drop( - Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap(), - ); + // Quick check that `Ok` is returned. + _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) + .evaluate() + .unwrap(); assert!(cache.is_empty()); - assert!( - match Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) - .evaluate() - .unwrap() - { - Store(o) => - o.as_ref().unwrap() - == &Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![1]) - )), - _ => false, - } + assert_eq!( + Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()).evaluate(), + Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![1]) + ))))) + ); + assert_eq!(cache.len(), 1); + assert_eq!( + Evaluator::new(b"s2\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), + Err(InvalidStore) ); - assert!(cache.len() == 1); } + #[expect(clippy::too_many_lines, reason = "a lot to test")] #[cfg(not(feature = "rand"))] #[test] fn eval() { - use core::str::FromStr; + use core::str::FromStr as _; let mut prev = None; let mut cache = Cache::new(); let mut exp = Vec::new(); - assert!( - match Evaluator::new(b"1+2\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Eval(i) => - i == &Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![3]) - )), - _ => false, - } - ); - assert!( - match Evaluator::new(b"\t s \n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Store(o) => - o.as_ref().unwrap() - == &Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![3]) - )), - _ => false, - } + assert_eq!( + Evaluator::new(b"1+2\n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![3]) + )))) ); - assert!( - match Evaluator::new(b"-1/2+2*@\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Eval(i) => - i == &Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) - ), - _ => false, - } + assert_eq!( + Evaluator::new(b"\t s \n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![3]) + ))))) + ); + assert_eq!( + Evaluator::new(b"-1/2+2*@\n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Eval(&Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) + ))) ); - assert!( - match Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Store(o) => - o == &Some(Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) - )), - _ => false, - } + assert_eq!( + Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Store(&Some(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) + )))) ); - assert!( - match Evaluator::new(b"@^@2!\r\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Eval(i) => - i == &Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1771561])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) - ), - _ => false, - } + assert_eq!( + Evaluator::new(b"@^@2!\r\n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Eval(&Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) + ))) ); - assert!( - match Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Store(o) => - o == &Some(Ratio::new( - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1771561])), - BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) - )), - _ => false, - } + assert_eq!( + Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Store(&Some(Ratio::new( + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])), + BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) + )))) ); // Verified with Wolfram Alpha. - assert!(match Evaluator::new(b" \t 1 + (2 * |(7.98\t - 12/7)|) / 4!^@3!^|1-3|\t \n", &mut cache, &mut prev, &mut exp).evaluate().unwrap() { - Eval(i) => i == &Ratio::from_str("2841328814244153299237884950647090899374680152474331/2841328814244153299237884950647090899374680152473600").unwrap(), - _ => false, - }); - assert!(match Evaluator::new( - b" \t round(19/6,0)!\t \r\n", - &mut cache, - &mut prev, - &mut exp - ) - .evaluate() - .unwrap() - { - Eval(i) => - i == &Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6]))), - _ => false, - }); - assert!(match Evaluator::new( - b" \t 2^round(19/6,0)!\t \r\n", - &mut cache, - &mut prev, - &mut exp - ) - .evaluate() - .unwrap() - { - Eval(i) => - i == &Ratio::from_integer(BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64]))), - _ => false, - }); assert!( - match Evaluator::new(b"round(19/6,0)^2\t\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Eval(i) => - i == &Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![9]) - )), - _ => false, - } + Evaluator::new( + b" \t 1 + (2 * |(7.98\t - 12/7)|) / 4!^@3!^|1-3|\t \n", + &mut cache, + &mut prev, + &mut exp + ) + .evaluate().is_ok_and(|r| { + Ratio::from_str("2841328814244153299237884950647090899374680152474331/2841328814244153299237884950647090899374680152473600").is_ok_and(|r2| { + r == Eval(&r2) + }) + }) + ); + assert_eq!( + Evaluator::new( + b" \t round(19/6,0)!\t \r\n", + &mut cache, + &mut prev, + &mut exp + ) + .evaluate(), + Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![6]) + )))) + ); + assert_eq!( + Evaluator::new( + b" \t 2^round(19/6,0)!\t \r\n", + &mut cache, + &mut prev, + &mut exp + ) + .evaluate(), + Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![64]) + )))) + ); + assert_eq!( + Evaluator::new(b"round(19/6,0)^2\t\n", &mut cache, &mut prev, &mut exp).evaluate(), + Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![9]) + )))) ); // Invalid UTF-8 does not cause a panic!. - assert!( - match Evaluator::new(&[255, 255, 255, b'\n'], &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap_err() - { - MissingTerm(i) => i == 0, - _ => false, - } + assert_eq!( + Evaluator::new(&[255, 255, 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(), + Err(MissingTerm(0)) ); - assert!( - match Evaluator::new(&[b'2', 255, b'\n'], &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap_err() - { - TrailingSyms(i) => i == 1, - _ => false, - } + assert_eq!( + Evaluator::new(&[b'2', 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(), + Err(TrailingSyms(1)) ); // Exactly one newline is required. - assert!( - match Evaluator::new(b"2\n\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap_err() - { - TrailingSyms(i) => i == 1, - _ => false, - } + assert_eq!( + Evaluator::new(b"2\n\n", &mut cache, &mut prev, &mut exp).evaluate(), + Err(TrailingSyms(1)) ); - assert!( - prev == Some(Ratio::from_integer(BigInt::from_biguint( + assert_eq!( + prev, + Some(Ratio::from_integer(BigInt::from_biguint( Sign::Plus, BigUint::new(vec![9]) ))) ); - assert!(match Evaluator::new(b"\n", &mut cache, &mut prev, &mut exp) - .evaluate() - .unwrap() - { - Empty(o) => - o == &Some(Ratio::from_integer(BigInt::from_biguint( - Sign::Plus, - BigUint::new(vec![9]) - ))), - _ => false, - }); - assert!( - prev == Some(Ratio::from_integer(BigInt::from_biguint( + assert_eq!( + Evaluator::new(b"\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(), + Ok(Empty(&Some(Ratio::from_integer(BigInt::from_biguint( + Sign::Plus, + BigUint::new(vec![9]) + ))))) + ); + assert_eq!( + prev, + Some(Ratio::from_integer(BigInt::from_biguint( Sign::Plus, BigUint::new(vec![9]) ))) ); - assert!( - match Evaluator::new(b"\r\n", &mut cache, &mut prev.clone(), &mut exp) - .evaluate() - .unwrap() - { - Empty(o) => *o == prev, - _ => false, - } + assert_eq!( + Evaluator::new(b"\r\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(), + Ok(Empty(&prev)) ); - assert!( - match Evaluator::new(&[0u8; 0], &mut cache, &mut prev.clone(), &mut exp) - .evaluate() - .unwrap() - { - Empty(o) => *o == prev, - _ => false, - } + assert_eq!( + Evaluator::new(&[0u8; 0], &mut cache, &mut prev.clone(), &mut exp).evaluate(), + Ok(Empty(&prev)) ); } #[cfg(feature = "rand")] #[test] fn eval_iter() { - use super::*; - use num_traits::ToPrimitive; - use std::io::{self, BufRead, Error, ErrorKind, Read}; struct Reader<'a> { data: &'a [u8], err: bool, @@ -3357,12 +3115,14 @@ mod tests { } } impl Read for Reader<'_> { + #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { if self.err { self.err = false; - Err(Error::new(ErrorKind::Other, "")) + Err(Error::other("")) } else { let len = usize::min(buf.len(), self.data.len()); + // `len <= buf.len()` and `len <= `self.data.len()`. buf[..len].copy_from_slice(&self.data[..len]); Ok(len) } @@ -3372,54 +3132,51 @@ mod tests { fn fill_buf(&mut self) -> io::Result<&[u8]> { if self.err { self.err = false; - Err(Error::new(ErrorKind::Other, "")) + Err(Error::other("")) } else { Ok(self.data) } } - fn consume(&mut self, amt: usize) { - self.data = &self.data[amt..]; + #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] + fn consume(&mut self, amount: usize) { + // This is just a test, so calling code passing in an invalid `amount` is fine. + self.data = &self.data[amount..]; } } let mut iter = EvalIter::new(Reader::new( b"1+2\n4\n\nq\n5\ns\nrand() + rand(-139/@, 2984/134)\nab", )); - assert!(match iter.lend_next().unwrap().unwrap_err() { - E::Error(_) => true, - _ => false, - }); - assert!(match iter.lend_next().unwrap().unwrap() { - Eval(r) => r.numer().to_i32().unwrap() == 3, - _ => false, - }); - assert!(match iter.lend_next().unwrap().unwrap() { - Eval(r) => r.numer().to_i32().unwrap() == 4, - _ => false, - }); - assert!(match iter.lend_next().unwrap().unwrap() { - Empty(r) => r.as_ref().unwrap().numer().to_i32().unwrap() == 4, - _ => false, - }); + assert!( + iter.lend_next() + .is_some_and(|res| res.map_or_else(|e| matches!(e, E::Error(_)), |_| false)) + ); + assert!(iter.lend_next().is_some_and(|res| { + res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(3i32))) + })); + assert!(iter.lend_next().is_some_and(|res| { + res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(4i32))) + })); + assert!(iter.lend_next().is_some_and(|res| { + res.is_ok_and( + |e| matches!(e, Empty(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(4i32))), + ) + })); assert!(iter.lend_next().is_none()); - assert!(match iter.lend_next().unwrap().unwrap() { - Eval(r) => r.numer().to_i32().unwrap() == 5, - _ => false, - }); - assert!(match iter.lend_next().unwrap().unwrap() { - Store(r) => r.as_ref().unwrap().numer().to_i32().unwrap() == 5, - _ => false, - }); - assert!(match iter.lend_next().unwrap().unwrap() { - Eval(r) => r.is_integer(), - _ => false, - }); - assert!(match iter.lend_next().unwrap().unwrap_err() { - E::LangErr(e) => match e { - MissingTerm(_) => true, - _ => false, - }, - _ => false, - }); + assert!(iter.lend_next().is_some_and(|res| { + res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(5i32))) + })); + assert!(iter.lend_next().is_some_and(|res| { + res.is_ok_and( + |e| matches!(e, Store(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(5i32))), + ) + })); + assert!( + iter.lend_next() + .is_some_and(|res| res.is_ok_and(|e| matches!(e, Eval(r) if r.is_integer()))) + ); + assert!(iter.lend_next().is_some_and(|res| { + res.is_err_and(|err| matches!(err, E::LangErr(ref e) if matches!(*e, MissingTerm(_)))) + })); assert!(iter.lend_next().is_none()); assert!(iter.lend_next().is_none()); assert!(iter.lend_next().is_none()); diff --git a/src/main.rs b/src/main.rs @@ -11,10 +11,8 @@ use num_bigint as _; use num_integer as _; use num_rational as _; use num_traits as _; -#[cfg(all(feature = "priv_sep", not(target_os = "openbsd")))] -use priv_sep as _; -#[cfg(all(feature = "priv_sep", target_os = "openbsd"))] -use priv_sep::{Promise, Promises}; +#[cfg(target_os = "openbsd")] +use priv_sep::{Errno, Promise, Promises}; #[cfg(feature = "rand")] use rand as _; #[cfg(feature = "std")] @@ -28,16 +26,16 @@ use std::{ enum Err { /// Error returned from [`writeln`]. Io(Error), - #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] /// Error returned from `pledge`. - Pledge(Error), + #[cfg(target_os = "openbsd")] + Pledge(Errno), } #[cfg(feature = "std")] impl Display for Err { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match *self { Self::Io(ref e) => e.fmt(f), - #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] + #[cfg(target_os = "openbsd")] Self::Pledge(ref e) => write!(f, "pledge(2)ing 'stdio' failed with {e}"), } } @@ -58,7 +56,7 @@ fn main() {} #[cfg(feature = "std")] fn main() -> Result<(), Err> { /// Calls `pledge(2)` with the "stdio" promise. - #[cfg(all(feature = "priv_sep", target_os = "openbsd"))] + #[cfg(target_os = "openbsd")] fn privsep() -> Result<(), Err> { Promises::new([Promise::Stdio]) .pledge() @@ -69,7 +67,7 @@ fn main() -> Result<(), Err> { clippy::unnecessary_wraps, reason = "consistent return type with priv_sep feature" )] - #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))] + #[cfg(not(target_os = "openbsd"))] const fn privsep() -> Result<(), Err> { Ok(()) }