lib.rs (124663B)
1 //! [![git]](https://git.philomathiclife.com/calc_rational/log.html) [![crates-io]](https://crates.io/crates/calc_rational) [![docs-rs]](crate) 2 //! 3 //! [git]: https://git.philomathiclife.com/git_badge.svg 4 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust 5 //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs 6 //! 7 //! `calc_lib` is a library for performing basic rational number arithmetic using standard operator precedence 8 //! and associativity. Internally, it is based on 9 //! [`Ratio<T>`] and [`BigInt`]. 10 //! 11 //! ## Expressions 12 //! 13 //! The following are the list of expressions in descending order of precedence: 14 //! 1. number literals, `@`, `()`, `||`, `round()`, `rand()` 15 //! 2. `!` 16 //! 3. `^` 17 //! 4. `-` (unary negation operator) 18 //! 5. `*`, `/`, `mod` 19 //! 6. `+`, `-` 20 //! 21 //! All binary operators are left-associative sans `^` which is right-associative. 22 //! 23 //! Any expression is allowed to be enclosed in `()`. Note that parentheses are purely for grouping expressions; 24 //! in particular, you cannot use them to represent multiplication (e.g., `4(2)` is grammatically incorrect and 25 //! will result in an error message). 26 //! 27 //! Any expression is allowed to be enclosed in `||`. This unary operator represents absolute value. 28 //! 29 //! `!` is the factorial operator. Due to its high precedence, something like *-i!^j!* for *i, j ∈ ℕ* is 30 //! the same thing as *-((i!)^(j!))*. If the expression preceding it does not evaluate to a non-negative integer, 31 //! then an error will be displayed. Spaces and tabs are *not* ignored; so `1 !` is grammatically incorrect and 32 //! will result in an error message. 33 //! 34 //! `^` is the exponentiation operator. The expression left of the operator can evaluate to any rational number; 35 //! however the expression right of the operator must evaluate to an integer or ±1/2 unless the expression on 36 //! the left evaluates to `0` or `1`. In the event of the former, the expression right of the operator must evaluate 37 //! to a non-negative rational number. In the event of the latter, the expression right of the operator can evaluate to 38 //! any rational number. Note that `0^0` is defined to be 1. When the operand right of `^` evaluates to ±1/2, then 39 //! the left operand must be the square of a rational number. 40 //! 41 //! The unary operator `-` represents negation. 42 //! 43 //! The operators `*` and `/` represent multiplication and division respectively. Expressions right of `/` 44 //! must evaluate to any non-zero rational number; otherwise an error will be displayed. 45 //! 46 //! The binary operator `mod` represents modulo such that *n mod m = r = n - m\*q* for *n,q ∈ ℤ, m ∈ ℤ\\{0}, and r ∈ ℕ* 47 //! where *r* is the minimum non-negative solution. 48 //! 49 //! The binary operators `+` and `-` represent addition and subtraction respectively. 50 //! 51 //! With the aforementioned exception of `!`, all spaces and tabs before and after operators are ignored. 52 //! 53 //! ## Round expression 54 //! 55 //! `round(expression, digit)` rounds `expression` to `digit`-number of fractional digits. An error will 56 //! be displayed if called incorrectly. 57 //! 58 //! ## Rand expression 59 //! 60 //! `rand(expression, expression)` generates a random 64-bit integer inclusively between the passed expressions. 61 //! An error will be displayed if called incorrectly. `rand()` generates a random 64-bit integer. 62 //! 63 //! ## Numbers 64 //! 65 //! A number literal is a non-empty sequence of digits or a non-empty sequence of digits immediately followed by `.` 66 //! which is immediately followed by a non-empty sequence of digits (e.g., `134.901`). This means that number 67 //! literals represent precisely all rational numbers that are equivalent to a ratio of a non-negative integer 68 //! to a positive integer whose sole prime factors are 2 or 5. To represent all other rational numbers, the unary 69 //! operator `-` and binary operator `/` must be used. 70 //! 71 //! ## Empty expression 72 //! 73 //! The empty expression (i.e., expression that at most only consists of spaces and tabs) will return 74 //! the result from the previous non-(empty/store) expression in *decimal* form using the minimum number of digits. 75 //! In the event an infinite number of digits is required, it will be rounded to 9 fractional digits using normal rounding 76 //! rules first. 77 //! 78 //! ## Store expression 79 //! 80 //! To store the result of the previous non-(empty/store) expression, one simply passes `s`. In addition to storing the 81 //! result which will subsequently be available via `@`, it displays the result. At most 8 results can be stored at once; 82 //! at which point, results that are stored overwrite the oldest result. 83 //! 84 //! ## Recall expression 85 //! 86 //! `@` is used to recall previously stored results. It can be followed by any *digit* from `1` to `8`. 87 //! If such a digit does not immediately follow it, then it will be interpreted as if there were a `1`. 88 //! `@i` returns the *i*-th most-previous stored result where *i ∈ {1, 2, 3, 4, 5, 6, 7, 8}*. 89 //! Note that spaces and tabs are *not* ignored so `@ 2` is grammatically incorrect and will result in an error message. 90 //! As emphasized, it does not work on expressions; so both `@@` and `@(1)` are grammatically incorrect. 91 //! 92 //! ## Character encoding 93 //! 94 //! All inputs must only contain the ASCII encoding of the following Unicode scalar values: `0`-`9`, `.`, `+`, `-`, 95 //! `*`, `/`, `^`, `!`, `mod`, `|`, `(`, `)`, `round`, `rand`, `,`, `@`, `s`, <space>, <tab>, 96 //! <line feed>, <carriage return>, and `q`. Any other byte sequences are grammatically incorrect and will 97 //! lead to an error message. 98 //! 99 //! ## Errors 100 //! 101 //! Errors due to a language violation (e.g., dividing by `0`) manifest into an error message. `panic!`s 102 //! and [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)s caused by writing to the global 103 //! standard output stream lead to program abortion. 104 //! 105 //! ## Exiting 106 //! 107 //! `q` with any number of spaces and tabs before and after will cause the program to terminate. 108 //! 109 //! ### Formal language specification 110 //! 111 //! For a more precise specification of the “calc language”, one can read the 112 //! [calc language specification](https://git.philomathiclife.com/calc_rational/lang.pdf). 113 #![expect( 114 clippy::doc_paragraphs_missing_punctuation, 115 reason = "false positive for crate documentation having image links" 116 )] 117 #![expect( 118 clippy::arithmetic_side_effects, 119 reason = "calculator can't realistically avoid this" 120 )] 121 #![no_std] 122 #![cfg_attr(docsrs, feature(doc_cfg))] 123 extern crate alloc; 124 use LangErr::{ 125 DivByZero, ExpDivByZero, ExpIsNotIntOrOneHalf, InvalidAbs, InvalidDec, InvalidPar, InvalidQuit, 126 InvalidRound, InvalidStore, MissingTerm, ModIsNotInt, ModZero, NotEnoughPrevResults, 127 NotNonNegIntFact, SqrtDoesNotExist, TrailingSyms, 128 }; 129 use O::{Empty, Eval, Exit, Store}; 130 use alloc::{ 131 string::{String, ToString as _}, 132 vec, 133 vec::Vec, 134 }; 135 use cache::Cache; 136 #[cfg(not(feature = "rand"))] 137 use core::marker::PhantomData; 138 use core::{ 139 convert, 140 fmt::{self, Display, Formatter}, 141 ops::Index as _, 142 }; 143 pub use num_bigint; 144 use num_bigint::{BigInt, BigUint, Sign}; 145 use num_integer::Integer as _; 146 pub use num_rational; 147 use num_rational::Ratio; 148 #[cfg(feature = "rand")] 149 use num_traits::ToPrimitive as _; 150 use num_traits::{Inv as _, Pow as _}; 151 #[cfg(target_os = "openbsd")] 152 use priv_sep as _; 153 #[cfg(feature = "rand")] 154 pub use rand; 155 #[cfg(feature = "rand")] 156 use rand::{Rng as _, rngs::ThreadRng}; 157 /// Fixed-sized cache that automatically overwrites the oldest data 158 /// when a new item is added and the cache is full. 159 /// 160 /// One can think of 161 /// [`Cache`] as a very limited but more performant [`VecDeque`][alloc::collections::VecDeque] that only 162 /// adds new data or reads old data. 163 pub mod cache; 164 /// Generalizes [`Iterator`] by using 165 /// generic associated types. 166 pub mod lending_iterator; 167 /// Error due to a language violation. 168 #[non_exhaustive] 169 #[cfg_attr(test, derive(Eq, PartialEq))] 170 #[derive(Debug)] 171 pub enum LangErr { 172 /// The input began with a `q` but had non-whitespace 173 /// that followed it. 174 InvalidQuit, 175 /// The input began with an `s` but had non-whitespace 176 /// that followed it. 177 InvalidStore, 178 /// A sub-expression in the input would have led 179 /// to a division by zero. 180 DivByZero(usize), 181 /// A sub-expression in the input would have led 182 /// to a rational number that was not 0 or 1 to be 183 /// raised to a non-integer power that is not (+/-) 1/2. 184 ExpIsNotIntOrOneHalf(usize), 185 /// A sub-expression in the input would have led 186 /// to 0 being raised to a negative power which itself 187 /// would have led to a division by zero. 188 ExpDivByZero(usize), 189 /// A sub-expression in the input would have led 190 /// to a number modulo 0. 191 ModZero(usize), 192 /// A sub-expression in the input would have led 193 /// to the mod of two expressions with at least one 194 /// not being an integer. 195 ModIsNotInt(usize), 196 /// A sub-expression in the input would have led 197 /// to a non-integer factorial or a negative integer factorial. 198 NotNonNegIntFact(usize), 199 /// The input contained a non-empty sequence of digits followed 200 /// by `.` which was not followed by a non-empty sequence of digits. 201 InvalidDec(usize), 202 /// A recall expression was used to recall the *i*-th most-recent stored result, 203 /// but there are fewer than *i* stored where 204 /// *i ∈ {1, 2, 3, 4, 5, 6, 7, 8}*. 205 NotEnoughPrevResults(usize), 206 /// The input did not contain a closing `|`. 207 InvalidAbs(usize), 208 /// The input did not contain a closing `)`. 209 InvalidPar(usize), 210 /// The input contained an invalid round expression. 211 InvalidRound(usize), 212 /// A sub-expression in the input had a missing terminal expression 213 /// where a terminal expression is a decimal literal expression, 214 /// recall expression, absolute value expression, parenthetical 215 /// expression, or round expression. 216 MissingTerm(usize), 217 /// The expression that was passed to the square root does not have a solution 218 /// in the field of rational numbers. 219 SqrtDoesNotExist(usize), 220 /// The input started with a valid expression but was immediately followed 221 /// by symbols that could not be chained with the preceding expression. 222 TrailingSyms(usize), 223 /// The input contained an invalid random expression. 224 #[cfg(feature = "rand")] 225 InvalidRand(usize), 226 /// Error when the second argument is less than first in the rand function. 227 #[cfg(feature = "rand")] 228 RandInvalidArgs(usize), 229 /// Error when there are no 64-bit integers in the interval passed to the random function. 230 #[cfg(feature = "rand")] 231 RandNoInts(usize), 232 } 233 impl Display for LangErr { 234 #[inline] 235 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 236 match *self { 237 InvalidStore => f.write_str("Invalid store expression. A store expression must be of the extended regex form: ^[ \\t]*s[ \\t]*$."), 238 InvalidQuit => f.write_str("Invalid quit expression. A quit expression must be of the extended regex form: ^[ \\t]*q[ \\t]*$."), 239 DivByZero(u) => write!(f, "Division by zero ending at position {u}."), 240 ExpIsNotIntOrOneHalf(u) => write!(f, "Non-integer exponent that is not (+/-) 1/2 with a base that was not 0 or 1 ending at position {u}."), 241 ExpDivByZero(u) => write!(f, "Non-negative exponent with a base of 0 ending at position {u}."), 242 ModZero(u) => write!(f, "A number modulo 0 ending at position {u}."), 243 ModIsNotInt(u) => write!(f, "The modulo expression was applied to at least one non-integer ending at position {u}."), 244 NotNonNegIntFact(u) => write!(f, "Factorial of a rational number that was not a non-negative integer ending at position {u}."), 245 InvalidDec(u) => write!(f, "Invalid decimal literal expression ending at position {u}. A decimal literal expression must be of the extended regex form: [0-9]+(\\.[0-9]+)?."), 246 NotEnoughPrevResults(len) => write!(f, "There are only {len} previous results."), 247 InvalidAbs(u) => write!(f, "Invalid absolute value expression ending at position {u}. An absolute value expression is an addition expression enclosed in '||'."), 248 InvalidPar(u) => write!(f, "Invalid parenthetical expression ending at position {u}. A parenthetical expression is an addition expression enclosed in '()'."), 249 InvalidRound(u) => write!(f, "Invalid round expression ending at position {u}. A round expression is of the form 'round(<mod expression>, digit)'"), 250 SqrtDoesNotExist(u) => write!(f, "The square root of the passed expression does not have a solution in the field of rational numbers ending at position {u}."), 251 #[cfg(not(feature = "rand"))] 252 MissingTerm(u) => write!(f, "Missing terminal expression at position {u}. A terminal expression is a decimal literal expression, recall expression, absolute value expression, parenthetical expression, or round expression."), 253 #[cfg(feature = "rand")] 254 MissingTerm(u) => write!(f, "Missing terminal expression at position {u}. A terminal expression is a decimal literal expression, recall expression, absolute value expression, parenthetical expression, round expression, or rand expression."), 255 TrailingSyms(u) => write!(f, "Trailing symbols starting at position {u}."), 256 #[cfg(feature = "rand")] 257 Self::InvalidRand(u) => write!(f, "Invalid rand expression ending at position {u}. A rand expression is of the form 'rand()' or 'rand(<mod expression>, <mod expression>)'."), 258 #[cfg(feature = "rand")] 259 Self::RandInvalidArgs(u) => write!(f, "The second expression passed to the random function evaluated to rational number less than the first ending at position {u}."), 260 #[cfg(feature = "rand")] 261 Self::RandNoInts(u) => write!(f, "There are no 64-bit integers within the interval passed to the random function ending at position {u}."), 262 } 263 } 264 } 265 /// A successful evaluation of an input. 266 #[cfg_attr(test, derive(Eq, PartialEq))] 267 #[derive(Debug)] 268 pub enum O<'a> { 269 /// The input only contained whitespace. 270 /// This returns the previous `Eval`. 271 /// It is `None` iff there have been no 272 /// previous `Eval` results. 273 Empty(&'a Option<Ratio<BigInt>>), 274 /// The quit expression was issued to terminate the program. 275 Exit, 276 /// Result of a "normal" expression. 277 Eval(&'a Ratio<BigInt>), 278 /// The store expression stores and returns the previous `Eval`. 279 /// It is `None` iff there have been no previous `Eval` results. 280 Store(&'a Option<Ratio<BigInt>>), 281 } 282 impl Display for O<'_> { 283 #[expect( 284 unsafe_code, 285 reason = "manually construct guaranteed UTF-8; thus avoid the needless check" 286 )] 287 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] 288 #[inline] 289 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 290 match *self { 291 Empty(o) => { 292 o.as_ref().map_or(Ok(()), |val| { 293 if val.is_integer() { 294 write!(f, "> {val}") 295 } else { 296 // If the prime factors of the denominator are only 2 and 5, 297 // then the number requires a finite number of digits and thus 298 // will be represented perfectly using the fewest number of digits. 299 // Any other situation will be rounded to 9 fractional digits. 300 // max{twos, fives} represents the minimum number of fractional 301 // digits necessary to represent val. 302 let mut twos = 0; 303 let mut fives = 0; 304 let zero = BigInt::from_biguint(Sign::NoSign, BigUint::new(Vec::new())); 305 let one = BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])); 306 let two = BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])); 307 let five = BigInt::from_biguint(Sign::Plus, BigUint::new(vec![5])); 308 let mut denom = val.denom().clone(); 309 let mut div_rem; 310 while denom > one { 311 div_rem = denom.div_rem(&two); 312 if div_rem.1 == zero { 313 twos += 1; 314 denom = div_rem.0; 315 } else { 316 break; 317 } 318 } 319 while denom > one { 320 div_rem = denom.div_rem(&five); 321 if div_rem.1 == zero { 322 fives += 1; 323 denom = div_rem.0; 324 } else { 325 break; 326 } 327 } 328 // int < 0 iff val <= -1. frac < 0 iff val is a negative non-integer. 329 let (int, frac, digits) = if denom == one { 330 let (int, mut frac) = val.numer().div_rem(val.denom()); 331 while twos > fives { 332 frac *= &five; 333 fives += 1; 334 } 335 while fives > twos { 336 frac *= &two; 337 twos += 1; 338 } 339 (int, frac, twos) 340 } else { 341 // Requires an infinite number of decimal digits to represent, so we display 342 // 9 digits after rounding. 343 let mult = 344 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])).pow(9u8); 345 let (int, frac) = (val * &mult).round().numer().div_rem(&mult); 346 (int, frac, 9) 347 }; 348 let int_str = int.to_string().into_bytes(); 349 let (mut v, frac_str) = if val.numer().sign() == Sign::Minus { 350 // Guaranteed to be non-empty. 351 if int_str[0] == b'-' { 352 ( 353 Vec::with_capacity(int_str.len() + 1 + digits), 354 (-frac).to_string().into_bytes(), 355 ) 356 } else { 357 let mut tmp = Vec::with_capacity(int_str.len() + 2 + digits); 358 tmp.push(b'-'); 359 (tmp, (-frac).to_string().into_bytes()) 360 } 361 } else { 362 ( 363 Vec::with_capacity(int_str.len() + 1 + digits), 364 frac.to_string().into_bytes(), 365 ) 366 }; 367 v.extend_from_slice(int_str.as_slice()); 368 v.push(b'.'); 369 // digits >= frac_str.len(). 370 v.resize(v.len() + (digits - frac_str.len()), b'0'); 371 v.extend_from_slice(frac_str.as_slice()); 372 // SAFETY: 373 // v contains precisely the UTF-8 code units returned from Strings 374 // returned from the to_string function on the integer and fraction part of 375 // val plus optionally the single byte encodings of ".", "-", and "0". 376 write!(f, "> {}", unsafe { String::from_utf8_unchecked(v) }) 377 } 378 }) 379 } 380 Eval(r) => write!(f, "> {r}"), 381 Exit => Ok(()), 382 Store(o) => o.as_ref().map_or(Ok(()), |val| write!(f, "> {val}")), 383 } 384 } 385 } 386 /// Size of [`Evaluator::cache`]. 387 const CACHE_SIZE: usize = 8; 388 /// Evaluates the supplied input. 389 #[derive(Debug)] 390 pub struct Evaluator<'input, 'cache, 'prev, 'scratch, 'rand> { 391 /// The input to be evaluated. 392 utf8: &'input [u8], 393 /// The index within `utf8` that evaluation needs to continue. 394 /// We use this instead of slicing from `utf8` since we want 395 /// to be able to report the position within the input 396 /// that an error occurs. 397 i: usize, 398 /// The cache of previously stored results. 399 cache: &'cache mut Cache<Ratio<BigInt>, CACHE_SIZE>, 400 /// The last result. 401 prev: &'prev mut Option<Ratio<BigInt>>, 402 /// Buffer used to evaluate right-associative sub-expressions. 403 scratch: &'scratch mut Vec<Ratio<BigInt>>, 404 /// Random number generator. 405 #[cfg(feature = "rand")] 406 rng: &'rand mut ThreadRng, 407 /// Need to use `'rand`. 408 #[cfg(not(feature = "rand"))] 409 _rng: PhantomData<fn() -> &'rand ()>, 410 } 411 #[allow( 412 single_use_lifetimes, 413 clippy::allow_attributes, 414 clippy::elidable_lifetime_names, 415 reason = "unify rand and not rand" 416 )] 417 impl<'input, 'cache, 'prev, 'scratch, 'rand> Evaluator<'input, 'cache, 'prev, 'scratch, 'rand> { 418 /// Creates an `Evaluator<'input, 'cache, 'prev, 'scratch, 'rand>` based on the supplied arguments. 419 #[cfg(not(feature = "rand"))] 420 #[inline] 421 pub fn new( 422 utf8: &'input [u8], 423 cache: &'cache mut Cache<Ratio<BigInt>, 8>, 424 prev: &'prev mut Option<Ratio<BigInt>>, 425 scratch: &'scratch mut Vec<Ratio<BigInt>>, 426 ) -> Self { 427 Self { 428 utf8, 429 i: 0, 430 cache, 431 prev, 432 scratch, 433 _rng: PhantomData, 434 } 435 } 436 /// Creates an `Evaluator<'input, 'cache, 'prev, 'scratch, 'rand>` based on the supplied arguments. 437 #[cfg(feature = "rand")] 438 #[inline] 439 pub const fn new( 440 utf8: &'input [u8], 441 cache: &'cache mut Cache<Ratio<BigInt>, 8>, 442 prev: &'prev mut Option<Ratio<BigInt>>, 443 scratch: &'scratch mut Vec<Ratio<BigInt>>, 444 rng: &'rand mut ThreadRng, 445 ) -> Self { 446 Self { 447 utf8, 448 i: 0, 449 cache, 450 prev, 451 scratch, 452 rng, 453 } 454 } 455 /// Evaluates the input consuming the `Evaluator<'input, 'cache, 'exp>`. 456 /// 457 /// Requires the input to contain one expression (i.e., if there are 458 /// multiple newlines, it will error). 459 /// 460 /// # Errors 461 /// 462 /// Returns a [`LangErr`] iff the input violates the calc language. 463 #[expect(clippy::indexing_slicing, reason = "correct")] 464 #[inline] 465 pub fn evaluate(mut self) -> Result<O<'prev>, LangErr> { 466 self.utf8 = if self.utf8.last().is_none_or(|b| *b != b'\n') { 467 self.utf8 468 } else { 469 &self.utf8[..self.utf8.len() 470 - self 471 .utf8 472 .get(self.utf8.len().wrapping_sub(2)) 473 .map_or(1, |b| if *b == b'\r' { 2 } else { 1 })] 474 }; 475 self.consume_ws(); 476 let Some(b) = self.utf8.get(self.i) else { 477 return Ok(Empty(self.prev)); 478 }; 479 if *b == b'q' { 480 self.i += 1; 481 self.consume_ws(); 482 if self.i == self.utf8.len() { 483 Ok(Exit) 484 } else { 485 Err(InvalidQuit) 486 } 487 } else if *b == b's' { 488 self.i += 1; 489 self.consume_ws(); 490 if self.i == self.utf8.len() { 491 if let Some(ref val) = *self.prev { 492 self.cache.push(val.clone()); 493 } 494 Ok(Store(self.prev)) 495 } else { 496 Err(InvalidStore) 497 } 498 } else { 499 self.get_adds().and_then(move |val| { 500 self.consume_ws(); 501 if self.i == self.utf8.len() { 502 Ok(Eval(self.prev.insert(val))) 503 } else { 504 Err(TrailingSyms(self.i)) 505 } 506 }) 507 } 508 } 509 /// Reads from the input until the next non-{space/tab} byte value. 510 #[expect(clippy::indexing_slicing, reason = "correct")] 511 fn consume_ws(&mut self) { 512 // ControlFlow makes more sense to use in try_fold; however due to a lack 513 // of a map_or_else function, it is easier to simply return a Result with 514 // Err taking the role of ControlFlow::Break. 515 self.i += self.utf8[self.i..] 516 .iter() 517 .try_fold(0, |val, b| match *b { 518 b' ' | b'\t' => Ok(val + 1), 519 _ => Err(val), 520 }) 521 .map_or_else(convert::identity, convert::identity); 522 } 523 /// Evaluates addition expressions as defined in the calc language. 524 /// This function is used for both addition and subtraction operations which 525 /// themselves are based on multiplication expressions. 526 fn get_adds(&mut self) -> Result<Ratio<BigInt>, LangErr> { 527 let mut left = self.get_mults()?; 528 let mut j; 529 self.consume_ws(); 530 while let Some(i) = self.utf8.get(self.i) { 531 j = *i; 532 self.consume_ws(); 533 if j == b'+' { 534 self.i += 1; 535 self.consume_ws(); 536 left += self.get_mults()?; 537 } else if j == b'-' { 538 self.i += 1; 539 self.consume_ws(); 540 left -= self.get_mults()?; 541 } else { 542 break; 543 } 544 } 545 Ok(left) 546 } 547 /// Evaluates multiplication expressions as defined in the calc language. 548 /// This function is used for both multiplication and division operations which 549 /// themselves are based on negation expressions. 550 fn get_mults(&mut self) -> Result<Ratio<BigInt>, LangErr> { 551 let mut left = self.get_neg()?; 552 let mut right; 553 let mut j; 554 let mut mod_val; 555 let mut numer; 556 self.consume_ws(); 557 while let Some(i) = self.utf8.get(self.i) { 558 j = *i; 559 self.consume_ws(); 560 if j == b'*' { 561 self.i += 1; 562 self.consume_ws(); 563 left *= self.get_neg()?; 564 } else if j == b'/' { 565 self.i += 1; 566 self.consume_ws(); 567 right = self.get_neg()?; 568 if right.numer().sign() == Sign::NoSign { 569 return Err(DivByZero(self.i)); 570 } 571 left /= right; 572 } else if let Some(k) = self.utf8.get(self.i..self.i.saturating_add(3)) { 573 if k == b"mod" { 574 if !left.is_integer() { 575 return Err(ModIsNotInt(self.i)); 576 } 577 self.i += 3; 578 self.consume_ws(); 579 right = self.get_neg()?; 580 if !right.is_integer() { 581 return Err(ModIsNotInt(self.i)); 582 } 583 numer = right.numer(); 584 if numer.sign() == Sign::NoSign { 585 return Err(ModZero(self.i)); 586 } 587 mod_val = left.numer() % numer; 588 left = Ratio::from_integer(if mod_val.sign() == Sign::Minus { 589 if numer.sign() == Sign::Minus { 590 mod_val - numer 591 } else { 592 mod_val + numer 593 } 594 } else { 595 mod_val 596 }); 597 } else { 598 break; 599 } 600 } else { 601 break; 602 } 603 } 604 Ok(left) 605 } 606 /// Evaluates negation expressions as defined in the calc language. 607 /// This function is based on exponentiation expressions. 608 fn get_neg(&mut self) -> Result<Ratio<BigInt>, LangErr> { 609 let mut count = 0usize; 610 while let Some(b) = self.utf8.get(self.i) { 611 if *b == b'-' { 612 self.i += 1; 613 self.consume_ws(); 614 count += 1; 615 } else { 616 break; 617 } 618 } 619 self.get_exps() 620 .map(|val| if count & 1 == 0 { val } else { -val }) 621 } 622 /// Gets the square root of value so long as a solution exists. 623 #[expect( 624 clippy::unreachable, 625 reason = "code that shouldn't happen did, so we want to crash" 626 )] 627 fn sqrt(val: Ratio<BigInt>) -> Option<Ratio<BigInt>> { 628 /// Returns the square root of `n` if one exists; otherwise 629 /// returns `None`. 630 /// MUST NOT pass 0. 631 #[expect(clippy::suspicious_operation_groupings, reason = "false positive")] 632 fn calc(n: &BigUint) -> Option<BigUint> { 633 let mut shift = n.bits(); 634 shift += shift & 1; 635 let mut result = BigUint::new(Vec::new()); 636 let one = BigUint::new(vec![1]); 637 let zero = BigUint::new(Vec::new()); 638 loop { 639 shift -= 2; 640 result <<= 1u32; 641 result |= &one; 642 result ^= if &result * &result > (n >> shift) { 643 &one 644 } else { 645 &zero 646 }; 647 if shift == 0 { 648 break (&result * &result == *n).then_some(result); 649 } 650 } 651 } 652 let numer = val.numer(); 653 if numer.sign() == Sign::NoSign { 654 Some(val) 655 } else { 656 numer.try_into().map_or_else( 657 |_| None, 658 |num| { 659 calc(&num).and_then(|n| { 660 calc(&val.denom().try_into().unwrap_or_else(|_| { 661 unreachable!("Ratio must never have a negative denominator") 662 })) 663 .map(|d| Ratio::new(n.into(), d.into())) 664 }) 665 }, 666 ) 667 } 668 } 669 /// Evaluates exponentiation expressions as defined in the calc language. 670 /// This function is based on negation expressions. 671 fn get_exps(&mut self) -> Result<Ratio<BigInt>, LangErr> { 672 let mut t = self.get_fact()?; 673 let ix = self.scratch.len(); 674 let mut prev; 675 let mut numer; 676 self.scratch.push(t); 677 self.consume_ws(); 678 let mut j; 679 let one = BigInt::new(Sign::Plus, vec![1]); 680 let min_one = BigInt::new(Sign::Minus, vec![1]); 681 let two = BigInt::new(Sign::Plus, vec![2]); 682 while let Some(i) = self.utf8.get(self.i) { 683 j = *i; 684 self.consume_ws(); 685 if j == b'^' { 686 self.i += 1; 687 self.consume_ws(); 688 t = self.get_neg()?; 689 // Safe since we always push at least one value, and we always 690 // return immediately once we encounter an error. 691 prev = self.scratch.index(self.scratch.len() - 1); 692 numer = prev.numer(); 693 // Equiv to checking if prev is 0. 694 if numer.sign() == Sign::NoSign { 695 if t.numer().sign() == Sign::Minus { 696 self.scratch.clear(); 697 return Err(ExpDivByZero(self.i)); 698 } 699 self.scratch.push(t); 700 } else if prev.is_integer() { 701 let t_numer = t.numer(); 702 // 1 raised to anything is 1, so we don't bother 703 // storing the exponent. 704 if *numer == one { 705 } else if t.is_integer() 706 || ((*t_numer == one || *t_numer == min_one) && *t.denom() == two) 707 { 708 self.scratch.push(t); 709 } else { 710 self.scratch.clear(); 711 return Err(ExpIsNotIntOrOneHalf(self.i)); 712 } 713 } else if t.is_integer() 714 || ((*t.numer() == one || *t.numer() == min_one) && *t.denom() == two) 715 { 716 self.scratch.push(t); 717 } else { 718 self.scratch.clear(); 719 return Err(ExpIsNotIntOrOneHalf(self.i)); 720 } 721 } else { 722 break; 723 } 724 } 725 self.scratch 726 .drain(ix..) 727 .try_rfold(Ratio::from_integer(one.clone()), |exp, base| { 728 if exp.is_integer() { 729 Ok(base.pow(exp.numer())) 730 } else if base.numer().sign() == Sign::NoSign { 731 Ok(base) 732 } else if *exp.denom() == two { 733 if *exp.numer() == one { 734 Self::sqrt(base).map_or_else(|| Err(SqrtDoesNotExist(self.i)), Ok) 735 } else if *exp.numer() == min_one { 736 Self::sqrt(base) 737 .map_or_else(|| Err(SqrtDoesNotExist(self.i)), |v| Ok(v.inv())) 738 } else { 739 Err(ExpIsNotIntOrOneHalf(self.i)) 740 } 741 } else { 742 Err(ExpIsNotIntOrOneHalf(self.i)) 743 } 744 }) 745 } 746 /// Evaluates factorial expressions as defined in the calc language. 747 /// This function is based on terminal expressions. 748 fn get_fact(&mut self) -> Result<Ratio<BigInt>, LangErr> { 749 /// Calculates the factorial of `val`. 750 fn fact(mut val: BigUint) -> BigUint { 751 let zero = BigUint::new(Vec::new()); 752 let one = BigUint::new(vec![1]); 753 let mut calc = BigUint::new(vec![1]); 754 while val > zero { 755 calc *= &val; 756 val -= &one; 757 } 758 calc 759 } 760 let t = self.get_term()?; 761 let Some(b) = self.utf8.get(self.i) else { 762 return Ok(t); 763 }; 764 if *b == b'!' { 765 self.i += 1; 766 if t.is_integer() { 767 // We can make a copy of self.i here, or call map_or instead 768 // of map_or_else. 769 let i = self.i; 770 t.numer().try_into().map_or_else( 771 |_| Err(NotNonNegIntFact(i)), 772 |val| { 773 let mut factorial = fact(val); 774 while let Some(b2) = self.utf8.get(self.i) { 775 if *b2 == b'!' { 776 self.i += 1; 777 factorial = fact(factorial); 778 } else { 779 break; 780 } 781 } 782 Ok(Ratio::from_integer(BigInt::from_biguint( 783 Sign::Plus, 784 factorial, 785 ))) 786 }, 787 ) 788 } else { 789 Err(NotNonNegIntFact(self.i)) 790 } 791 } else { 792 Ok(t) 793 } 794 } 795 /// Evaluates terminal expressions as defined in the calc language. 796 /// This function is based on number literal expressions, parenthetical expressions, 797 /// recall expressions, absolute value expressions, round expressions, and possibly 798 /// rand expressions if that feature is enabled. 799 fn get_term(&mut self) -> Result<Ratio<BigInt>, LangErr> { 800 self.get_rational().map_or_else(Err, |o| { 801 o.map_or_else( 802 || { 803 self.get_par().map_or_else(Err, |o2| { 804 o2.map_or_else( 805 || { 806 self.get_recall().map_or_else(Err, |o3| { 807 o3.map_or_else( 808 || { 809 self.get_abs().map_or_else(Err, |o4| { 810 o4.map_or_else( 811 || { 812 self.get_round().and_then(|o5| { 813 o5.map_or_else( 814 #[cfg(not(feature = "rand"))] 815 || Err(MissingTerm(self.i)), 816 #[cfg(feature = "rand")] 817 || self.get_rand(), 818 Ok, 819 ) 820 }) 821 }, 822 Ok, 823 ) 824 }) 825 }, 826 Ok, 827 ) 828 }) 829 }, 830 Ok, 831 ) 832 }) 833 }, 834 Ok, 835 ) 836 }) 837 } 838 /// Generates a random 64-bit integer. This function is based on add expressions. This is the last terminal 839 /// expression attempted when needing a terminal expression; as a result, it is the only terminal expression 840 /// that does not return an `Option`. 841 #[cfg(feature = "rand")] 842 fn get_rand(&mut self) -> Result<Ratio<BigInt>, LangErr> { 843 /// Generates a random 64-bit integer. 844 #[expect(clippy::host_endian_bytes, reason = "must keep platform endianness")] 845 fn rand(rng: &mut ThreadRng) -> i64 { 846 let mut bytes = [0; 8]; 847 // `ThreadRng::try_fill_bytes` is infallible, so easier to call `fill_bytes`. 848 rng.fill_bytes(&mut bytes); 849 i64::from_ne_bytes(bytes) 850 } 851 /// Generates a random 64-bit integer inclusively between the passed arguments. 852 #[expect( 853 clippy::integer_division_remainder_used, 854 reason = "need for uniform randomness" 855 )] 856 #[expect( 857 clippy::as_conversions, 858 clippy::cast_possible_truncation, 859 clippy::cast_possible_wrap, 860 clippy::cast_sign_loss, 861 reason = "lossless conversions between signed integers" 862 )] 863 fn rand_range( 864 rng: &mut ThreadRng, 865 lower: &Ratio<BigInt>, 866 upper: &Ratio<BigInt>, 867 i: usize, 868 ) -> Result<i64, LangErr> { 869 if lower > upper { 870 return Err(LangErr::RandInvalidArgs(i)); 871 } 872 let lo = lower.ceil(); 873 let up = upper.floor(); 874 let lo_int = lo.numer(); 875 let up_int = up.numer(); 876 if lo_int > &BigInt::from(i64::MAX) || up_int < &BigInt::from(i64::MIN) { 877 return Err(LangErr::RandNoInts(i)); 878 } 879 let lo_min = lo_int.to_i64().unwrap_or(i64::MIN); 880 let up_max = up_int.to_i64().unwrap_or(i64::MAX); 881 if up_max > lo_min || upper.is_integer() || lower.is_integer() { 882 let low = i128::from(lo_min); 883 // `i64::MAX >= up_max >= low`; so underflow and overflow cannot happen. 884 // range is [1, 2^64] so casting to a u128 is fine. 885 let modulus = (i128::from(up_max) - low + 1) as u128; 886 // range is [0, i64::MAX] so converting to a `u64` is fine. 887 // rem represents how many values need to be removed 888 // when generating a random i64 in order for uniformity. 889 let rem = (0x0001_0000_0000_0000_0000 % modulus) as u64; 890 let mut low_adj; 891 loop { 892 low_adj = rand(rng) as u64; 893 // Since rem is in [0, i64::MAX], this is the same as low_adj < 0 || low_adj >= rem. 894 if low_adj >= rem { 895 return Ok( 896 // range is [i64::MIN, i64::MAX]; thus casts are safe. 897 // modulus is up_max - low + 1; so as low grows, 898 // % shrinks by the same factor. i64::MAX happens 899 // when low = up_max = i64::MAX or when low = 0, 900 // up_max = i64::MAX and low_adj is i64::MAX. 901 ((u128::from(low_adj) % modulus) as i128 + low) as i64, 902 ); 903 } 904 } 905 } else { 906 Err(LangErr::RandNoInts(i)) 907 } 908 } 909 // This is the last kind of terminal expression that is attempted. 910 // If there is no more data, then we have a missing terminal expression. 911 let Some(b) = self.utf8.get(self.i..self.i.saturating_add(5)) else { 912 return Err(MissingTerm(self.i)); 913 }; 914 if b == b"rand(" { 915 self.i += 5; 916 self.consume_ws(); 917 let i = self.i; 918 self.utf8.get(self.i).map_or_else( 919 || Err(LangErr::InvalidRand(i)), 920 |p| { 921 if *p == b')' { 922 self.i += 1; 923 Ok(Ratio::from_integer(BigInt::from(rand(self.rng)))) 924 } else { 925 let add = self.get_adds()?; 926 let Some(b2) = self.utf8.get(self.i) else { 927 return Err(LangErr::InvalidRand(self.i)); 928 }; 929 if *b2 == b',' { 930 self.i += 1; 931 self.consume_ws(); 932 let add2 = self.get_adds()?; 933 self.consume_ws(); 934 let Some(b3) = self.utf8.get(self.i) else { 935 return Err(LangErr::InvalidRand(self.i)); 936 }; 937 if *b3 == b')' { 938 self.i += 1; 939 rand_range(self.rng, &add, &add2, self.i) 940 .map(|v| Ratio::from_integer(BigInt::from(v))) 941 } else { 942 Err(LangErr::InvalidRand(self.i)) 943 } 944 } else { 945 Err(LangErr::InvalidRand(self.i)) 946 } 947 } 948 }, 949 ) 950 } else { 951 Err(MissingTerm(self.i)) 952 } 953 } 954 /// Rounds a value to the specified number of fractional digits. 955 /// This function is based on add expressions. 956 fn get_round(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> { 957 let Some(b) = self.utf8.get(self.i..self.i.saturating_add(6)) else { 958 return Ok(None); 959 }; 960 if b == b"round(" { 961 self.i += 6; 962 self.consume_ws(); 963 let val = self.get_adds()?; 964 self.consume_ws(); 965 let Some(b2) = self.utf8.get(self.i) else { 966 return Err(InvalidRound(self.i)); 967 }; 968 let b3 = *b2; 969 if b3 == b',' { 970 self.i += 1; 971 self.consume_ws(); 972 let Some(b4) = self.utf8.get(self.i) else { 973 return Err(InvalidRound(self.i)); 974 }; 975 let r = if b4.is_ascii_digit() { 976 self.i += 1; 977 *b4 - b'0' 978 } else { 979 return Err(InvalidRound(self.i)); 980 }; 981 self.consume_ws(); 982 let i = self.i; 983 self.utf8.get(self.i).map_or_else( 984 || Err(InvalidRound(i)), 985 |p| { 986 if *p == b')' { 987 self.i += 1; 988 let mult = 989 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])).pow(r); 990 Ok(Some((val * &mult).round() / &mult)) 991 } else { 992 Err(InvalidRound(self.i)) 993 } 994 }, 995 ) 996 } else { 997 Err(InvalidRound(self.i)) 998 } 999 } else { 1000 Ok(None) 1001 } 1002 } 1003 /// Evaluates absolute value expressions as defined in the calc language. 1004 /// This function is based on add expressions. 1005 fn get_abs(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> { 1006 let Some(b) = self.utf8.get(self.i) else { 1007 return Ok(None); 1008 }; 1009 if *b == b'|' { 1010 self.i += 1; 1011 self.consume_ws(); 1012 let r = self.get_adds()?; 1013 self.consume_ws(); 1014 let Some(b2) = self.utf8.get(self.i) else { 1015 return Err(InvalidAbs(self.i)); 1016 }; 1017 let b3 = *b2; 1018 if b3 == b'|' { 1019 self.i += 1; 1020 Ok(Some(if r.numer().sign() == Sign::Minus { 1021 -r 1022 } else { 1023 r 1024 })) 1025 } else { 1026 Err(InvalidAbs(self.i)) 1027 } 1028 } else { 1029 Ok(None) 1030 } 1031 } 1032 /// Evaluates recall expressions as defined in the calc language. 1033 // This does not return a Result<Option<&Ratio<BigInt>>, LangErr> 1034 // since the only place this function is called is in get_term which 1035 // would end up needing to clone the Ratio anyway. By not forcing 1036 // get_term to clone, it can rely on map_or_else over match expressions. 1037 fn get_recall(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> { 1038 let Some(b) = self.utf8.get(self.i) else { 1039 return Ok(None); 1040 }; 1041 if *b == b'@' { 1042 self.i += 1; 1043 self.cache 1044 .get(self.utf8.get(self.i).map_or(0, |b2| { 1045 if (b'1'..b'9').contains(b2) { 1046 self.i += 1; 1047 usize::from(*b2 - b'1') 1048 } else { 1049 0 1050 } 1051 })) 1052 .map_or_else( 1053 || Err(NotEnoughPrevResults(self.cache.len())), 1054 |p| Ok(Some(p.clone())), 1055 ) 1056 } else { 1057 Ok(None) 1058 } 1059 } 1060 /// Evaluates parenthetical expressions as defined in the calc language. 1061 /// This function is based on add expressions. 1062 fn get_par(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> { 1063 let Some(b) = self.utf8.get(self.i) else { 1064 return Ok(None); 1065 }; 1066 if *b == b'(' { 1067 self.i += 1; 1068 self.consume_ws(); 1069 let r = self.get_adds()?; 1070 self.consume_ws(); 1071 let Some(b2) = self.utf8.get(self.i) else { 1072 return Err(InvalidPar(self.i)); 1073 }; 1074 let b3 = *b2; 1075 if b3 == b')' { 1076 self.i += 1; 1077 Ok(Some(r)) 1078 } else { 1079 Err(InvalidPar(self.i)) 1080 } 1081 } else { 1082 Ok(None) 1083 } 1084 } 1085 /// Evaluates number literal expressions as defined in the calc language. 1086 #[expect(clippy::indexing_slicing, reason = "correct")] 1087 fn get_rational(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> { 1088 // ControlFlow makes more sense to use in try_fold; however due to a lack 1089 // of a map_or_else function, it is easier to simply return a Result with 1090 // Err taking the role of ControlFlow::Break. 1091 /// Used to parse a sequence of digits into an unsigned integer. 1092 fn to_biguint(v: &[u8]) -> (BigUint, usize) { 1093 v.iter() 1094 .try_fold((BigUint::new(Vec::new()), 0), |mut prev, d| { 1095 if d.is_ascii_digit() { 1096 prev.1 += 1; 1097 // `*d - b'0'` is guaranteed to return a integer between 0 and 9. 1098 prev.0 = prev.0 * 10u8 + (*d - b'0'); 1099 Ok(prev) 1100 } else { 1101 Err(prev) 1102 } 1103 }) 1104 .map_or_else(convert::identity, convert::identity) 1105 } 1106 let (int, len) = to_biguint(&self.utf8[self.i..]); 1107 if len == 0 { 1108 return Ok(None); 1109 } 1110 self.i += len; 1111 if let Some(b) = self.utf8.get(self.i) { 1112 if *b == b'.' { 1113 self.i += 1; 1114 let (numer, len2) = to_biguint(&self.utf8[self.i..]); 1115 if len2 == 0 { 1116 Err(InvalidDec(self.i)) 1117 } else { 1118 self.i += len2; 1119 Ok(Some( 1120 Ratio::from_integer(BigInt::from_biguint(Sign::Plus, int)) 1121 + Ratio::new( 1122 BigInt::from_biguint(Sign::Plus, numer), 1123 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]).pow(len2)), 1124 ), 1125 )) 1126 } 1127 } else { 1128 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1129 Sign::Plus, 1130 int, 1131 )))) 1132 } 1133 } else { 1134 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1135 Sign::Plus, 1136 int, 1137 )))) 1138 } 1139 } 1140 } 1141 /// Reads data from `R` passing each line to an [`Evaluator`] to be evaluated. 1142 #[cfg(feature = "std")] 1143 #[derive(Debug)] 1144 pub struct EvalIter<R> { 1145 /// Reader that contains input data. 1146 reader: R, 1147 /// Buffer that is used by `reader` to read 1148 /// data into. 1149 input_buffer: Vec<u8>, 1150 /// Cache of stored results. 1151 cache: Cache<Ratio<BigInt>, 8>, 1152 /// Result of the previous expression. 1153 prev: Option<Ratio<BigInt>>, 1154 /// Buffer used by [`Evaluator`] to process 1155 /// sub-expressions. 1156 exp_buffer: Vec<Ratio<BigInt>>, 1157 /// Random number generator. 1158 #[cfg(feature = "rand")] 1159 rng: ThreadRng, 1160 } 1161 #[cfg(feature = "std")] 1162 impl<R> EvalIter<R> { 1163 /// Creates a new `EvalIter`. 1164 #[cfg(feature = "rand")] 1165 #[inline] 1166 pub fn new(reader: R) -> Self { 1167 Self { 1168 reader, 1169 input_buffer: Vec::new(), 1170 cache: Cache::new(), 1171 prev: None, 1172 exp_buffer: Vec::new(), 1173 rng: rand::rng(), 1174 } 1175 } 1176 /// Creates a new `EvalIter`. 1177 #[cfg(any(doc, not(feature = "rand")))] 1178 #[inline] 1179 pub fn new(reader: R) -> Self { 1180 Self { 1181 reader, 1182 input_buffer: Vec::new(), 1183 cache: Cache::new(), 1184 prev: None, 1185 exp_buffer: Vec::new(), 1186 } 1187 } 1188 } 1189 #[cfg(feature = "std")] 1190 extern crate std; 1191 #[cfg(feature = "std")] 1192 use std::io::{BufRead, Error}; 1193 /// Error returned from [`EvalIter`] when an expression has an error. 1194 #[cfg(feature = "std")] 1195 #[derive(Debug)] 1196 pub enum E { 1197 /// Error containing [`Error`] which is returned 1198 /// from [`EvalIter`] when reading from the supplied 1199 /// [`BufRead`]er. 1200 Error(Error), 1201 /// Error containing [`LangErr`] which is returned 1202 /// from [`EvalIter`] when evaluating a single expression. 1203 LangErr(LangErr), 1204 } 1205 #[cfg(feature = "std")] 1206 impl Display for E { 1207 #[inline] 1208 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 1209 match *self { 1210 Self::Error(ref e) => e.fmt(f), 1211 Self::LangErr(ref e) => e.fmt(f), 1212 } 1213 } 1214 } 1215 #[cfg(feature = "std")] 1216 use crate::lending_iterator::LendingIterator; 1217 #[cfg(feature = "std")] 1218 impl<R> LendingIterator for EvalIter<R> 1219 where 1220 R: BufRead, 1221 { 1222 type Item<'a> 1223 = Result<O<'a>, E> 1224 where 1225 Self: 'a; 1226 #[inline] 1227 fn lend_next(&mut self) -> Option<Result<O<'_>, E>> { 1228 self.input_buffer.clear(); 1229 self.exp_buffer.clear(); 1230 self.reader 1231 .read_until(b'\n', &mut self.input_buffer) 1232 .map_or_else( 1233 |e| Some(Err(E::Error(e))), 1234 |c| { 1235 if c == 0 { 1236 None 1237 } else { 1238 Evaluator::new( 1239 self.input_buffer.as_slice(), 1240 &mut self.cache, 1241 &mut self.prev, 1242 &mut self.exp_buffer, 1243 #[cfg(feature = "rand")] 1244 &mut self.rng, 1245 ) 1246 .evaluate() 1247 .map_or_else( 1248 |e| Some(Err(E::LangErr(e))), 1249 |o| match o { 1250 Empty(_) | Eval(_) | Store(_) => Some(Ok(o)), 1251 Exit => None, 1252 }, 1253 ) 1254 } 1255 }, 1256 ) 1257 } 1258 } 1259 #[cfg(test)] 1260 mod tests { 1261 use super::{ 1262 BigInt, BigUint, Cache, Evaluator, 1263 LangErr::MissingTerm, 1264 O::{Empty, Eval, Store}, 1265 Sign, Vec, vec, 1266 }; 1267 #[cfg(feature = "rand")] 1268 use super::{BufRead, E, EvalIter, LangErr, LendingIterator as _}; 1269 #[cfg(not(feature = "rand"))] 1270 use super::{ 1271 LangErr::{ 1272 DivByZero, ExpDivByZero, ExpIsNotIntOrOneHalf, InvalidAbs, InvalidDec, InvalidPar, 1273 InvalidQuit, InvalidRound, InvalidStore, ModIsNotInt, ModZero, NotEnoughPrevResults, 1274 NotNonNegIntFact, SqrtDoesNotExist, TrailingSyms, 1275 }, 1276 O::Exit, 1277 Ratio, 1278 }; 1279 #[cfg(not(feature = "rand"))] 1280 use alloc::{borrow::ToOwned as _, string::ToString as _}; 1281 #[cfg(not(feature = "rand"))] 1282 use num_traits::Pow as _; 1283 #[cfg(feature = "rand")] 1284 use num_traits::ToPrimitive as _; 1285 #[cfg(feature = "rand")] 1286 use std::io::{self, Error, Read}; 1287 #[expect(clippy::too_many_lines, reason = "a lot to test")] 1288 #[cfg(not(feature = "rand"))] 1289 #[test] 1290 fn empty() { 1291 // Empty expressions without a previous result return nothing. 1292 assert_eq!( 1293 Evaluator::new(b"\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 1294 Ok(Empty(&None)) 1295 ); 1296 assert_eq!( 1297 Evaluator::new( 1298 b" \t \t \n", 1299 &mut Cache::new(), 1300 &mut Some(Ratio::from_integer(BigInt::from_biguint( 1301 Sign::Minus, 1302 BigUint::new(vec![12]) 1303 ))), 1304 &mut Vec::new() 1305 ) 1306 .evaluate() 1307 .map(|a| a.to_string()), 1308 Ok("> -12".to_owned()) 1309 ); 1310 assert_eq!( 1311 Evaluator::new( 1312 b"\t\n", 1313 &mut Cache::new(), 1314 &mut Some(Ratio::new( 1315 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![4])), 1316 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![6])) 1317 )), 1318 &mut Vec::new() 1319 ) 1320 .evaluate() 1321 .map(|a| a.to_string()), 1322 Ok("> -0.666666667".to_owned()) 1323 ); 1324 assert_eq!( 1325 Evaluator::new( 1326 b"\t\n", 1327 &mut Cache::new(), 1328 &mut Some(Ratio::new( 1329 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), 1330 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6])) 1331 )), 1332 &mut Vec::new() 1333 ) 1334 .evaluate() 1335 .map(|a| a.to_string()), 1336 Ok("> 0.000000000".to_owned()) 1337 ); 1338 assert_eq!( 1339 Evaluator::new( 1340 b"\t\n", 1341 &mut Cache::new(), 1342 &mut Some(Ratio::new( 1343 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![17])), 1344 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4_230_196_224, 6])) 1345 )), 1346 &mut Vec::new() 1347 ) 1348 .evaluate() 1349 .map(|a| a.to_string()), 1350 Ok("> 0.000000001".to_owned()) 1351 ); 1352 assert_eq!( 1353 Evaluator::new( 1354 b"\t\n", 1355 &mut Cache::new(), 1356 &mut Some(Ratio::new( 1357 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])), 1358 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 1359 )), 1360 &mut Vec::new() 1361 ) 1362 .evaluate() 1363 .map(|a| a.to_string()), 1364 Ok("> 0.3".to_owned()) 1365 ); 1366 assert_eq!( 1367 Evaluator::new( 1368 b"\t\n", 1369 &mut Cache::new(), 1370 &mut Some(Ratio::new( 1371 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![203])), 1372 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 1373 )), 1374 &mut Vec::new() 1375 ) 1376 .evaluate() 1377 .map(|a| a.to_string()), 1378 Ok("> -20.3".to_owned()) 1379 ); 1380 assert_eq!( 1381 Evaluator::new( 1382 &[0u8; 0], 1383 &mut Cache::new(), 1384 &mut Some(Ratio::new( 1385 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![203])), 1386 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 1387 )), 1388 &mut Vec::new() 1389 ) 1390 .evaluate() 1391 .map(|a| a.to_string()), 1392 Ok("> -20.3".to_owned()) 1393 ); 1394 } 1395 #[expect(clippy::unreachable, reason = "want to crash when there is a bug")] 1396 #[cfg(not(feature = "rand"))] 1397 #[test] 1398 fn number_literal() { 1399 // Normal 0 is fine. 1400 assert_eq!( 1401 Evaluator::new(b"0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1402 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1403 Sign::NoSign, 1404 BigUint::new(Vec::new()) 1405 )))) 1406 ); 1407 // Leading 0s and trailing 0s are fine. 1408 assert_eq!( 1409 Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()) 1410 .get_rational(), 1411 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1412 Sign::NoSign, 1413 BigUint::new(Vec::new()) 1414 )))) 1415 ); 1416 // Parsing stops at first non-(digit/period). 1417 assert_eq!( 1418 Evaluator::new(b"1 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1419 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1420 Sign::Plus, 1421 BigUint::new(vec![1]) 1422 )))) 1423 ); 1424 let int = b"3397450981271938475135134759823759835414"; 1425 let frac = b"913759810573549872354897210539127530981570"; 1426 let left = Ratio::from_integer( 1427 BigInt::parse_bytes(int, 10) 1428 .unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")), 1429 ); 1430 let right = Ratio::new( 1431 BigInt::parse_bytes(frac, 10) 1432 .unwrap_or_else(|| unreachable!("bug in BigInt::parse_bytes")), 1433 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]).pow(frac.len() + 1)), 1434 ); 1435 let mut vec = Vec::new(); 1436 vec.extend_from_slice(int); 1437 vec.push(b'.'); 1438 vec.push(b'0'); 1439 vec.extend_from_slice(frac); 1440 // Test a number whose integer and fraction portions are larger than u128::MAX. 1441 assert_eq!( 1442 Evaluator::new( 1443 vec.as_slice(), 1444 &mut Cache::new(), 1445 &mut None, 1446 &mut Vec::new() 1447 ) 1448 .get_rational(), 1449 Ok(Some(left + right)) 1450 ); 1451 // Leading 0s and trailing 0s for a non-zero value are fine. 1452 assert_eq!( 1453 Evaluator::new( 1454 b"000000014.0000000000000", 1455 &mut Cache::new(), 1456 &mut None, 1457 &mut Vec::new() 1458 ) 1459 .get_rational(), 1460 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1461 Sign::Plus, 1462 BigUint::new(vec![14]) 1463 )))) 1464 ); 1465 // A sequence of digits followed immediately by a decimal point but no digits after is invalid. 1466 assert_eq!( 1467 Evaluator::new(b"1.", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1468 Err(InvalidDec(2)) 1469 ); 1470 // A sequence of digits followed immediately by a decimal point but no digits after is invalid. 1471 // This just shows that spaces are not ignored in number literals. 1472 assert_eq!( 1473 Evaluator::new(b"1. 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1474 Err(InvalidDec(2)) 1475 ); 1476 // Non-whitespace starting the input is valid but produces no value. 1477 // This also shows that an invalid byte sequence does not produce an error here. 1478 assert_eq!( 1479 Evaluator::new(b"a1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1480 Ok(None) 1481 ); 1482 // A space starting the input is valid but produces no value. 1483 assert_eq!( 1484 Evaluator::new(b" 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1485 Ok(None) 1486 ); 1487 // A tab starting the input is valid but produces no value. 1488 assert_eq!( 1489 Evaluator::new(b"\t1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1490 Ok(None) 1491 ); 1492 // Negative literals don't exist, so this should succeed but produce nothing. 1493 assert_eq!( 1494 Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1495 Ok(None) 1496 ); 1497 // '/' is division and not part of a number literal so only the "numerator" is parsed. 1498 assert_eq!( 1499 Evaluator::new(b"1/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1500 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1501 Sign::Plus, 1502 BigUint::new(vec![1]) 1503 )))) 1504 ); 1505 // A sequence of digits followed by invalid bytes is valid and produces a number equal to the digits before the invalid bytes. 1506 assert_eq!( 1507 Evaluator::new(b"130alj", &mut Cache::new(), &mut None, &mut Vec::new()).get_rational(), 1508 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1509 Sign::Plus, 1510 BigUint::new(vec![130]) 1511 )))) 1512 ); 1513 } 1514 #[cfg(not(feature = "rand"))] 1515 #[test] 1516 fn par() { 1517 // Missing closing ')' 1518 assert_eq!( 1519 Evaluator::new(b"(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 1520 Err(InvalidPar(2)) 1521 ); 1522 assert_eq!( 1523 Evaluator::new(b"((1\t + 2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 1524 Err(InvalidPar(9)) 1525 ); 1526 assert_eq!( 1527 Evaluator::new(b"( 0 \t )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 1528 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1529 Sign::NoSign, 1530 BigUint::new(Vec::new()) 1531 )))) 1532 ); 1533 assert_eq!( 1534 Evaluator::new(b"( - \t 5 )", &mut Cache::new(), &mut None, &mut Vec::new()).get_par(), 1535 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1536 Sign::Minus, 1537 BigUint::new(vec![5]) 1538 )))) 1539 ); 1540 assert_eq!( 1541 Evaluator::new( 1542 b"( ( 2 -\t 5) * 9 )", 1543 &mut Cache::new(), 1544 &mut None, 1545 &mut Vec::new() 1546 ) 1547 .get_par(), 1548 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1549 Sign::Minus, 1550 BigUint::new(vec![27]) 1551 )))) 1552 ); 1553 } 1554 #[expect(clippy::too_many_lines, reason = "a lot to test")] 1555 #[expect( 1556 clippy::indexing_slicing, 1557 clippy::unwrap_used, 1558 reason = "comments justify correctness" 1559 )] 1560 #[cfg(not(feature = "rand"))] 1561 #[test] 1562 fn recall_expression() { 1563 // If the input does not start with '@', then it's valid but produces nothing. 1564 assert_eq!( 1565 Evaluator::new(b"1", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1566 Ok(None) 1567 ); 1568 assert_eq!( 1569 Evaluator::new(b"a", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1570 Ok(None) 1571 ); 1572 assert_eq!( 1573 Evaluator::new(b" @", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1574 Ok(None) 1575 ); 1576 assert_eq!( 1577 Evaluator::new(b"\t@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1578 Ok(None) 1579 ); 1580 // Invalid recall expression since there are no previous results. 1581 assert_eq!( 1582 Evaluator::new(b"@", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1583 Err(NotEnoughPrevResults(0)) 1584 ); 1585 // Invalid recall expression since there are no previous results. 1586 assert_eq!( 1587 Evaluator::new(b"@4", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1588 Err(NotEnoughPrevResults(0)) 1589 ); 1590 // Invalid recall expression since there are no previous results. 1591 // The input violates our grammar, but this error happens before that. 1592 assert_eq!( 1593 Evaluator::new(b"@0", &mut Cache::new(), &mut None, &mut Vec::new()).get_recall(), 1594 Err(NotEnoughPrevResults(0)) 1595 ); 1596 // Successfully extract previous expression. 1597 let mut prev = None; 1598 let mut cache = Cache::new(); 1599 // Quick check that `Ok` is returned. 1600 _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) 1601 .evaluate() 1602 .unwrap(); 1603 // Quick check that `Ok` is returned. 1604 _ = Evaluator::new(b" s \r\n", &mut cache, &mut prev, &mut Vec::new()) 1605 .evaluate() 1606 .unwrap(); 1607 assert_eq!( 1608 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1609 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1610 Sign::Plus, 1611 BigUint::new(vec![1]) 1612 )))) 1613 ); 1614 // Invalid characters are ignored at this stage. 1615 assert_eq!( 1616 Evaluator::new(b"@&", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1617 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1618 Sign::Plus, 1619 BigUint::new(vec![1]) 1620 )))) 1621 ); 1622 // 0 is not a valid stored value only 1-8 are. 1623 assert_eq!( 1624 Evaluator::new(b"@0", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1625 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1626 Sign::Plus, 1627 BigUint::new(vec![1]) 1628 )))) 1629 ); 1630 // 9 is not a valid stored value only 1-8 are. 1631 assert_eq!( 1632 Evaluator::new(b"@9", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1633 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1634 Sign::Plus, 1635 BigUint::new(vec![1]) 1636 )))) 1637 ); 1638 // Spaces are not cleaned; otherwise this would error since we only have 1 stored value. 1639 assert_eq!( 1640 Evaluator::new(b"@ 2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1641 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1642 Sign::Plus, 1643 BigUint::new(vec![1]) 1644 )))) 1645 ); 1646 // Tabs are not cleaned; otherwise this would error since we only have 1 stored value. 1647 assert_eq!( 1648 Evaluator::new(b"@\t2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1649 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1650 Sign::Plus, 1651 BigUint::new(vec![1]) 1652 )))) 1653 ); 1654 // One digits are looked at so this is not @<ten>. 1655 assert_eq!( 1656 Evaluator::new(b"@10", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1657 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1658 Sign::Plus, 1659 BigUint::new(vec![1]) 1660 )))) 1661 ); 1662 // Invalid recall expression since there is only one stored result. 1663 assert_eq!( 1664 Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1665 Err(NotEnoughPrevResults(1)) 1666 ); 1667 // Quick check that `Ok` is returned. 1668 _ = Evaluator::new(b"2\r\n", &mut cache, &mut prev, &mut Vec::new()) 1669 .evaluate() 1670 .unwrap(); 1671 // Quick check that `Ok` is returned. 1672 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 1673 .evaluate() 1674 .unwrap(); 1675 // Stored values correct. 1676 assert_eq!( 1677 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1678 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1679 Sign::Plus, 1680 BigUint::new(vec![2]) 1681 )))) 1682 ); 1683 assert_eq!( 1684 Evaluator::new(b"@2", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1685 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1686 Sign::Plus, 1687 BigUint::new(vec![1]) 1688 )))) 1689 ); 1690 // Invalid recall expression since there are only three stored results. 1691 assert_eq!( 1692 Evaluator::new(b"@3", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1693 Err(NotEnoughPrevResults(2)) 1694 ); 1695 let mut v = vec![0, b'\n']; 1696 for i in b'3'..=b'8' { 1697 // `v.len() > 0`. 1698 v[0] = i; 1699 _ = Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()) 1700 .evaluate() 1701 .unwrap(); 1702 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 1703 .evaluate() 1704 .unwrap(); 1705 } 1706 v[0] = b'@'; 1707 for i in b'1'..=b'8' { 1708 // `v.len() > 1`. 1709 v[1] = i; 1710 // Cache is filled up correctly storing all previous values. 1711 assert_eq!( 1712 Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1713 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1714 Sign::Plus, 1715 BigUint::new(vec![u32::from(b'9' - i)]) 1716 )))) 1717 ); 1718 } 1719 // Only parses the first @ since the second @ is not a digit between 1 and 8. 1720 assert_eq!( 1721 Evaluator::new(b"@@", &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1722 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1723 Sign::Plus, 1724 BigUint::new(vec![8]) 1725 )))) 1726 ); 1727 // Quick check that `Ok` is returned. 1728 _ = Evaluator::new(b"9\r\n", &mut cache, &mut prev, &mut Vec::new()) 1729 .evaluate() 1730 .unwrap(); 1731 // Quick check that `Ok` is returned. 1732 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 1733 .evaluate() 1734 .unwrap(); 1735 // Oldest value is overwritten; all others remain. 1736 for i in b'1'..=b'8' { 1737 // `v.len() > 1`. 1738 v[1] = i; 1739 assert_eq!( 1740 Evaluator::new(v.as_slice(), &mut cache, &mut prev, &mut Vec::new()).get_recall(), 1741 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1742 Sign::Plus, 1743 BigUint::new(vec![u32::from((b'9' + 1) - i)]) 1744 )))) 1745 ); 1746 } 1747 } 1748 #[cfg(not(feature = "rand"))] 1749 #[test] 1750 fn abs() { 1751 // Missing closing '|' 1752 assert_eq!( 1753 Evaluator::new(b"|1", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 1754 Err(InvalidAbs(2)) 1755 ); 1756 assert_eq!( 1757 Evaluator::new(b"||1 + 2|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 1758 Err(InvalidAbs(8)) 1759 ); 1760 assert_eq!( 1761 Evaluator::new( 1762 b"| 0\t \t |", 1763 &mut Cache::new(), 1764 &mut None, 1765 &mut Vec::new() 1766 ) 1767 .get_abs(), 1768 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1769 Sign::NoSign, 1770 BigUint::new(Vec::new()) 1771 )))) 1772 ); 1773 assert_eq!( 1774 Evaluator::new(b"| - 5 |", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 1775 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1776 Sign::Plus, 1777 BigUint::new(vec![5]) 1778 )))) 1779 ); 1780 assert_eq!( 1781 Evaluator::new( 1782 b"| \t| 2 - 5| * 9 |", 1783 &mut Cache::new(), 1784 &mut None, 1785 &mut Vec::new() 1786 ) 1787 .get_abs(), 1788 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1789 Sign::Plus, 1790 BigUint::new(vec![27]) 1791 )))) 1792 ); 1793 // If the input does not start with '|', then it's valid but produces nothing. 1794 assert_eq!( 1795 Evaluator::new(b" \t|9|", &mut Cache::new(), &mut None, &mut Vec::new()).get_abs(), 1796 Ok(None) 1797 ); 1798 } 1799 #[cfg(not(feature = "rand"))] 1800 #[test] 1801 fn round() { 1802 // Missing ',<digit>)' 1803 assert_eq!( 1804 Evaluator::new(b"round(1", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 1805 Err(InvalidRound(7)) 1806 ); 1807 assert_eq!( 1808 Evaluator::new(b"round(1,", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 1809 Err(InvalidRound(8)) 1810 ); 1811 assert_eq!( 1812 Evaluator::new(b"round(1,2", &mut Cache::new(), &mut None, &mut Vec::new()).get_round(), 1813 Err(InvalidRound(9)) 1814 ); 1815 assert_eq!( 1816 Evaluator::new( 1817 b"round(1,10)", 1818 &mut Cache::new(), 1819 &mut None, 1820 &mut Vec::new() 1821 ) 1822 .get_round(), 1823 Err(InvalidRound(9)) 1824 ); 1825 assert_eq!( 1826 Evaluator::new(b"round(1,a)", &mut Cache::new(), &mut None, &mut Vec::new()) 1827 .get_round(), 1828 Err(InvalidRound(8)) 1829 ); 1830 assert_eq!( 1831 Evaluator::new( 1832 b"round(2, 7)", 1833 &mut Cache::new(), 1834 &mut None, 1835 &mut Vec::new() 1836 ) 1837 .get_round(), 1838 Ok(Some(Ratio::from_integer(BigInt::from_biguint( 1839 Sign::Plus, 1840 BigUint::new(vec![2]) 1841 )))) 1842 ); 1843 assert_eq!( 1844 Evaluator::new( 1845 b"round(2.677, 1)", 1846 &mut Cache::new(), 1847 &mut None, 1848 &mut Vec::new() 1849 ) 1850 .get_round(), 1851 Ok(Some(Ratio::new( 1852 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), 1853 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])) 1854 ))) 1855 ); 1856 } 1857 #[expect(clippy::too_many_lines, reason = "a lot to test")] 1858 #[cfg(feature = "rand")] 1859 #[test] 1860 fn rand() { 1861 assert_eq!( 1862 Evaluator::new( 1863 b"rand(1", 1864 &mut Cache::new(), 1865 &mut None, 1866 &mut Vec::new(), 1867 &mut rand::rng() 1868 ) 1869 .get_rand(), 1870 Err(LangErr::InvalidRand(6)) 1871 ); 1872 assert_eq!( 1873 Evaluator::new( 1874 b"rand(1,2", 1875 &mut Cache::new(), 1876 &mut None, 1877 &mut Vec::new(), 1878 &mut rand::rng() 1879 ) 1880 .get_rand(), 1881 Err(LangErr::InvalidRand(8)) 1882 ); 1883 assert_eq!( 1884 Evaluator::new( 1885 b"rand(1/2,3/4)", 1886 &mut Cache::new(), 1887 &mut None, 1888 &mut Vec::new(), 1889 &mut rand::rng(), 1890 ) 1891 .get_rand(), 1892 Err(LangErr::RandNoInts(13)) 1893 ); 1894 assert_eq!( 1895 Evaluator::new( 1896 b"rand(-100000000000000000000000,-1000000000000000000000)", 1897 &mut Cache::new(), 1898 &mut None, 1899 &mut Vec::new(), 1900 &mut rand::rng(), 1901 ) 1902 .get_rand(), 1903 Err(LangErr::RandNoInts(55)) 1904 ); 1905 assert_eq!( 1906 Evaluator::new( 1907 b"rand(2/3,1/3)", 1908 &mut Cache::new(), 1909 &mut None, 1910 &mut Vec::new(), 1911 &mut rand::rng(), 1912 ) 1913 .get_rand(), 1914 Err(LangErr::RandInvalidArgs(13)) 1915 ); 1916 // 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. 1917 assert_eq!( 1918 Evaluator::new( 1919 b" rand(2/3,2)", 1920 &mut Cache::new(), 1921 &mut None, 1922 &mut Vec::new(), 1923 &mut rand::rng(), 1924 ) 1925 .get_rand(), 1926 Err(MissingTerm(0)) 1927 ); 1928 assert!( 1929 Evaluator::new( 1930 b"rand(2, 7)", 1931 &mut Cache::new(), 1932 &mut None, 1933 &mut Vec::new(), 1934 &mut rand::rng() 1935 ) 1936 .get_rand() 1937 .is_ok_and(|r| { 1938 let int = r.numer(); 1939 int >= &BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 1940 && *int <= BigInt::from_biguint(Sign::Plus, BigUint::new(vec![7])) 1941 }) 1942 ); 1943 assert!( 1944 Evaluator::new( 1945 b"rand()", 1946 &mut Cache::new(), 1947 &mut None, 1948 &mut Vec::new(), 1949 &mut rand::rng() 1950 ) 1951 .get_rand() 1952 .is_ok_and(|r| { 1953 let int = r.numer(); 1954 int >= &BigInt::from(i64::MIN) && *int <= BigInt::from(i64::MAX) 1955 }) 1956 ); 1957 for _ in 0..100u8 { 1958 assert!( 1959 Evaluator::new( 1960 b"rand(2, 2)", 1961 &mut Cache::new(), 1962 &mut None, 1963 &mut Vec::new(), 1964 &mut rand::rng() 1965 ) 1966 .get_rand() 1967 .is_ok_and( 1968 |r| *r.numer() == BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 1969 ) 1970 ); 1971 } 1972 } 1973 #[expect( 1974 clippy::indexing_slicing, 1975 clippy::unwrap_used, 1976 reason = "comment justifies correctness" 1977 )] 1978 #[cfg(feature = "rand")] 1979 #[test] 1980 #[ignore = "slow"] 1981 fn rand_uni() { 1982 const COUNT: u32 = 999_999; 1983 #[expect( 1984 clippy::integer_division, 1985 clippy::integer_division_remainder_used, 1986 reason = "correct" 1987 )] 1988 const LOWER: u32 = COUNT * 33 / 100; 1989 #[expect( 1990 clippy::integer_division, 1991 clippy::integer_division_remainder_used, 1992 reason = "correct" 1993 )] 1994 const UPPER: u32 = COUNT * 101 / 300; 1995 // Test rand on an interval that is not a power of 2 in size. 1996 // This causes rand to adjust the interval to enforce uniformity. 1997 let mut vals = [0u32; 3]; 1998 let mut vec = Vec::new(); 1999 let mut cache = Cache::new(); 2000 let mut none = None; 2001 for _ in 1..COUNT { 2002 // We want to `panic` if `rand` does not work correctly. 2003 vals[usize::try_from( 2004 Evaluator::new( 2005 b"rand(-1, 1)", 2006 &mut cache, 2007 &mut none, 2008 &mut vec, 2009 &mut rand::rng(), 2010 ) 2011 .get_rand() 2012 .unwrap() 2013 .numer() 2014 .to_i32() 2015 .unwrap() 2016 + 1i32, 2017 ) 2018 .unwrap()] += 1; 2019 } 2020 // Test that the distribution is within 1% of what is expected. 2021 assert_eq!( 2022 vals.into_iter().try_fold(false, |_, r| { 2023 if (LOWER..=UPPER).contains(&r) { 2024 Ok(true) 2025 } else { 2026 Err(false) 2027 } 2028 }), 2029 Ok(true) 2030 ); 2031 } 2032 #[allow( 2033 clippy::allow_attributes, 2034 reason = "unwrap_used only fires when rand is not enabled" 2035 )] 2036 #[allow(clippy::unwrap_used, reason = "comments justify correctness")] 2037 #[test] 2038 fn term() { 2039 #[cfg(not(feature = "rand"))] 2040 assert_eq!( 2041 Evaluator::new(b"0000.00000", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 2042 Ok(Ratio::from_integer(BigInt::from_biguint( 2043 Sign::NoSign, 2044 BigUint::new(Vec::new()) 2045 ))) 2046 ); 2047 #[cfg(not(feature = "rand"))] 2048 assert_eq!( 2049 Evaluator::new(b"(4)", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 2050 Ok(Ratio::from_integer(BigInt::from_biguint( 2051 Sign::Plus, 2052 BigUint::new(vec![4]) 2053 ))) 2054 ); 2055 #[cfg(not(feature = "rand"))] 2056 assert_eq!( 2057 Evaluator::new( 2058 b"round(-2/3,2)", 2059 &mut Cache::new(), 2060 &mut None, 2061 &mut Vec::new() 2062 ) 2063 .get_term(), 2064 Ok(Ratio::new( 2065 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![67])), 2066 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![100])) 2067 )) 2068 ); 2069 #[cfg(feature = "rand")] 2070 drop( 2071 Evaluator::new( 2072 b"rand()", 2073 &mut Cache::new(), 2074 &mut None, 2075 &mut Vec::new(), 2076 &mut rand::rng(), 2077 ) 2078 .get_term() 2079 .unwrap(), 2080 ); 2081 #[cfg(feature = "rand")] 2082 drop( 2083 Evaluator::new( 2084 b"rand(-13/93, 833)", 2085 &mut Cache::new(), 2086 &mut None, 2087 &mut Vec::new(), 2088 &mut rand::rng(), 2089 ) 2090 .get_term() 2091 .unwrap(), 2092 ); 2093 #[cfg(not(feature = "rand"))] 2094 assert_eq!( 2095 Evaluator::new(b"rand()", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 2096 Err(MissingTerm(0)) 2097 ); 2098 #[cfg(not(feature = "rand"))] 2099 assert_eq!( 2100 Evaluator::new(b"|4|", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 2101 Ok(Ratio::from_integer(BigInt::from_biguint( 2102 Sign::Plus, 2103 BigUint::new(vec![4]) 2104 ))) 2105 ); 2106 // Terminal expressions do no clean up before or after. 2107 #[cfg(not(feature = "rand"))] 2108 assert_eq!( 2109 Evaluator::new(b" 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_term(), 2110 Err(MissingTerm(0)) 2111 ); 2112 #[cfg(not(feature = "rand"))] 2113 let mut prev = None; 2114 #[cfg(not(feature = "rand"))] 2115 let mut cache = Cache::new(); 2116 #[cfg(not(feature = "rand"))] 2117 { 2118 // Quick check that `Ok` is returned. 2119 _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) 2120 .evaluate() 2121 .unwrap(); 2122 // Quick check that `Ok` is returned. 2123 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 2124 .evaluate() 2125 .unwrap(); 2126 } 2127 #[cfg(not(feature = "rand"))] 2128 assert_eq!( 2129 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_term(), 2130 Ok(Ratio::from_integer(BigInt::from_biguint( 2131 Sign::Plus, 2132 BigUint::new(vec![1]) 2133 ))) 2134 ); 2135 } 2136 #[expect(clippy::unwrap_used, reason = "comments justify correctness")] 2137 #[cfg(not(feature = "rand"))] 2138 #[test] 2139 fn factorial() { 2140 // Negative integer is not allowed. 2141 assert_eq!( 2142 Evaluator::new(b"(-1)!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2143 Err(NotNonNegIntFact(5)) 2144 ); 2145 // Non-integer is not allowed. 2146 assert_eq!( 2147 Evaluator::new(b"2.5!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2148 Err(NotNonNegIntFact(4)) 2149 ); 2150 // factorials always become terminal expressions eventually. 2151 assert_eq!( 2152 Evaluator::new(b"7", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2153 Ok(Ratio::from_integer(BigInt::from_biguint( 2154 Sign::Plus, 2155 BigUint::new(vec![7]) 2156 ))) 2157 ); 2158 assert_eq!( 2159 Evaluator::new(b"(7)", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2160 Ok(Ratio::from_integer(BigInt::from_biguint( 2161 Sign::Plus, 2162 BigUint::new(vec![7]) 2163 ))) 2164 ); 2165 assert_eq!( 2166 Evaluator::new(b"|7|", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2167 Ok(Ratio::from_integer(BigInt::from_biguint( 2168 Sign::Plus, 2169 BigUint::new(vec![7]) 2170 ))) 2171 ); 2172 let mut prev = None; 2173 let mut cache = Cache::new(); 2174 // Quick check that `Ok` is returned. 2175 _ = Evaluator::new(b"3\n", &mut cache, &mut prev, &mut Vec::new()) 2176 .evaluate() 2177 .unwrap(); 2178 // Quick check that `Ok` is returned. 2179 _ = Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()) 2180 .evaluate() 2181 .unwrap(); 2182 assert_eq!( 2183 Evaluator::new(b"@!", &mut cache, &mut prev, &mut Vec::new()).get_fact(), 2184 Ok(Ratio::from_integer(BigInt::from_biguint( 2185 Sign::Plus, 2186 BigUint::new(vec![6]) 2187 ))) 2188 ); 2189 assert_eq!( 2190 Evaluator::new(b"@", &mut cache, &mut prev, &mut Vec::new()).get_fact(), 2191 Ok(Ratio::from_integer(BigInt::from_biguint( 2192 Sign::Plus, 2193 BigUint::new(vec![3]) 2194 ))) 2195 ); 2196 // 0! = 1. 2197 assert_eq!( 2198 Evaluator::new(b"0.0!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2199 Ok(Ratio::from_integer(BigInt::from_biguint( 2200 Sign::Plus, 2201 BigUint::new(vec![1]) 2202 ))) 2203 ); 2204 // 1! = 1. 2205 assert_eq!( 2206 Evaluator::new(b"1!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2207 Ok(Ratio::from_integer(BigInt::from_biguint( 2208 Sign::Plus, 2209 BigUint::new(vec![1]) 2210 ))) 2211 ); 2212 // 4! = 24, and whitespace is not consumed. 2213 assert_eq!( 2214 Evaluator::new(b"4! \t", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2215 Ok(Ratio::from_integer(BigInt::from_biguint( 2216 Sign::Plus, 2217 BigUint::new(vec![24]) 2218 ))) 2219 ); 2220 // Factorials can be chained. 2221 assert_eq!( 2222 Evaluator::new(b"3!! ", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2223 Ok(Ratio::from_integer(BigInt::from_biguint( 2224 Sign::Plus, 2225 BigUint::new(vec![720]) 2226 ))) 2227 ); 2228 // only factorial is consumed. 2229 assert_eq!( 2230 Evaluator::new(b"2!+3", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2231 Ok(Ratio::from_integer(BigInt::from_biguint( 2232 Sign::Plus, 2233 BigUint::new(vec![2]) 2234 ))) 2235 ); 2236 // Error since leading/trailing whitespace is not consumed by factorial or higher precedence expressions. 2237 assert_eq!( 2238 Evaluator::new(b" 2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2239 Err(MissingTerm(0)) 2240 ); 2241 assert_eq!( 2242 Evaluator::new(b"\t2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2243 Err(MissingTerm(0)) 2244 ); 2245 // Error since negation is not consumed by factorial or higher precedence expressions. 2246 assert_eq!( 2247 Evaluator::new(b"-2!", &mut Cache::new(), &mut None, &mut Vec::new()).get_fact(), 2248 Err(MissingTerm(0)) 2249 ); 2250 } 2251 #[expect( 2252 clippy::cognitive_complexity, 2253 clippy::too_many_lines, 2254 reason = "a lot to test" 2255 )] 2256 #[cfg(not(feature = "rand"))] 2257 #[test] 2258 fn exp() { 2259 // 1 can be raised to anything and return 1. 2260 // Also white space is ignored between operator. 2261 assert_eq!( 2262 Evaluator::new(b"1 ^\t 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2263 Ok(Ratio::from_integer(BigInt::from_biguint( 2264 Sign::Plus, 2265 BigUint::new(vec![1]) 2266 ))) 2267 ); 2268 assert_eq!( 2269 Evaluator::new(b"1^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2270 Ok(Ratio::from_integer(BigInt::from_biguint( 2271 Sign::Plus, 2272 BigUint::new(vec![1]) 2273 ))) 2274 ); 2275 assert_eq!( 2276 Evaluator::new(b"1^(-1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2277 Ok(Ratio::from_integer(BigInt::from_biguint( 2278 Sign::Plus, 2279 BigUint::new(vec![1]) 2280 ))) 2281 ); 2282 assert_eq!( 2283 Evaluator::new(b"1.0^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2284 Ok(Ratio::from_integer(BigInt::from_biguint( 2285 Sign::Plus, 2286 BigUint::new(vec![1]) 2287 ))) 2288 ); 2289 // 0 can be raised to any non-negative value and will always return 0 unless raised to 0 which will return 1. 2290 assert_eq!( 2291 Evaluator::new(b"0^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2292 Ok(Ratio::from_integer(BigInt::from_biguint( 2293 Sign::Plus, 2294 BigUint::new(vec![1]) 2295 ))) 2296 ); 2297 assert_eq!( 2298 Evaluator::new(b"0^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2299 Ok(Ratio::from_integer(BigInt::from_biguint( 2300 Sign::NoSign, 2301 BigUint::new(vec![0]) 2302 ))) 2303 ); 2304 assert_eq!( 2305 Evaluator::new(b"0^0.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2306 Ok(Ratio::from_integer(BigInt::from_biguint( 2307 Sign::NoSign, 2308 BigUint::new(vec![0]) 2309 ))) 2310 ); 2311 // Anything else can only be raised to integers. 2312 assert_eq!( 2313 Evaluator::new(b"4^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2314 Ok(Ratio::from_integer(BigInt::from_biguint( 2315 Sign::Plus, 2316 BigUint::new(vec![1]) 2317 ))) 2318 ); 2319 assert_eq!( 2320 Evaluator::new(b"4^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2321 Ok(Ratio::from_integer(BigInt::from_biguint( 2322 Sign::Plus, 2323 BigUint::new(vec![4]) 2324 ))) 2325 ); 2326 assert_eq!( 2327 Evaluator::new(b"4^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2328 Ok(Ratio::new( 2329 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), 2330 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) 2331 )) 2332 ); 2333 assert_eq!( 2334 Evaluator::new(b"(-4)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2335 Ok(Ratio::from_integer(BigInt::from_biguint( 2336 Sign::Plus, 2337 BigUint::new(vec![1]) 2338 ))) 2339 ); 2340 assert_eq!( 2341 Evaluator::new(b"(-4)^1", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2342 Ok(Ratio::from_integer(BigInt::from_biguint( 2343 Sign::Minus, 2344 BigUint::new(vec![4]) 2345 ))) 2346 ); 2347 assert_eq!( 2348 Evaluator::new(b"(-4)^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2349 Ok(Ratio::from_integer(BigInt::from_biguint( 2350 Sign::Plus, 2351 BigUint::new(vec![16]) 2352 ))) 2353 ); 2354 assert_eq!( 2355 Evaluator::new(b"(-4)^(-2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2356 Ok(Ratio::new( 2357 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1])), 2358 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![16])) 2359 )) 2360 ); 2361 assert_eq!( 2362 Evaluator::new(b"(-4)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2363 Ok(Ratio::new( 2364 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![1])), 2365 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) 2366 )) 2367 ); 2368 assert_eq!( 2369 Evaluator::new(b"(2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2370 Ok(Ratio::from_integer(BigInt::from_biguint( 2371 Sign::Plus, 2372 BigUint::new(vec![1]) 2373 ))) 2374 ); 2375 assert_eq!( 2376 Evaluator::new(b"(2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2377 Ok(Ratio::new( 2378 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), 2379 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) 2380 )) 2381 ); 2382 assert_eq!( 2383 Evaluator::new(b"(2/3)^(-3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2384 Ok(Ratio::new( 2385 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])), 2386 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) 2387 )) 2388 ); 2389 assert_eq!( 2390 Evaluator::new(b"(-2/3)^0", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2391 Ok(Ratio::from_integer(BigInt::from_biguint( 2392 Sign::Plus, 2393 BigUint::new(vec![1]) 2394 ))) 2395 ); 2396 assert_eq!( 2397 Evaluator::new(b"(-2/3)^(2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2398 Ok(Ratio::new( 2399 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])), 2400 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])) 2401 )) 2402 ); 2403 assert_eq!( 2404 Evaluator::new(b"(-2/3)^(3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2405 Ok(Ratio::new( 2406 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![8])), 2407 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![27])) 2408 )) 2409 ); 2410 assert_eq!( 2411 Evaluator::new( 2412 b"(-2/3)^(-2)", 2413 &mut Cache::new(), 2414 &mut None, 2415 &mut Vec::new() 2416 ) 2417 .get_exps(), 2418 Ok(Ratio::new( 2419 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![9])), 2420 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) 2421 )) 2422 ); 2423 assert_eq!( 2424 Evaluator::new( 2425 b"(-2/3)^(-3)", 2426 &mut Cache::new(), 2427 &mut None, 2428 &mut Vec::new() 2429 ) 2430 .get_exps(), 2431 Ok(Ratio::new( 2432 BigInt::from_biguint(Sign::Minus, BigUint::new(vec![27])), 2433 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![8])) 2434 )) 2435 ); 2436 assert_eq!( 2437 Evaluator::new( 2438 b"(4/9)^(-1/2)", 2439 &mut Cache::new(), 2440 &mut None, 2441 &mut Vec::new() 2442 ) 2443 .get_exps(), 2444 Ok(Ratio::new( 2445 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])), 2446 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 2447 )) 2448 ); 2449 // Error since 0 cannot be raised to a negative power. 2450 assert_eq!( 2451 Evaluator::new(b"0^(-1)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2452 Err(ExpDivByZero(6)) 2453 ); 2454 // Error since anything other than 0 or 1 cannot be raised to a non-integer power or (+/-) 1/2. 2455 assert_eq!( 2456 Evaluator::new(b"2^(1/3)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2457 Err(ExpIsNotIntOrOneHalf(7)) 2458 ); 2459 // When exponent is (+/-) 1/2, base has to be the square of a rational number. 2460 assert_eq!( 2461 Evaluator::new(b"2^(1/2)", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2462 Err(SqrtDoesNotExist(7)) 2463 ); 2464 // exps always become factorials eventually. 2465 assert_eq!( 2466 Evaluator::new(b"3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2467 Ok(Ratio::from_integer(BigInt::from_biguint( 2468 Sign::Plus, 2469 BigUint::new(vec![6]) 2470 ))) 2471 ); 2472 // exponentiation has lower precedence than factorials. 2473 assert_eq!( 2474 Evaluator::new(b"2^3!", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2475 Ok(Ratio::from_integer(BigInt::from_biguint( 2476 Sign::Plus, 2477 BigUint::new(vec![64]) 2478 ))) 2479 ); 2480 assert_eq!( 2481 Evaluator::new(b"3!^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2482 Ok(Ratio::from_integer(BigInt::from_biguint( 2483 Sign::Plus, 2484 BigUint::new(vec![36]) 2485 ))) 2486 ); 2487 // Error since leading/trailing whitespace is not consumed by exponentiation or higher precedence expressions. 2488 assert_eq!( 2489 Evaluator::new(b" 2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2490 Err(MissingTerm(0)) 2491 ); 2492 assert_eq!( 2493 Evaluator::new(b"\t2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_exps(), 2494 Err(MissingTerm(0)) 2495 ); 2496 } 2497 #[cfg(not(feature = "rand"))] 2498 #[test] 2499 fn neg() { 2500 assert_eq!( 2501 Evaluator::new(b"-1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2502 Ok(Ratio::from_integer(BigInt::from_biguint( 2503 Sign::Minus, 2504 BigUint::new(vec![1]) 2505 ))) 2506 ); 2507 assert_eq!( 2508 Evaluator::new(b"- \t - 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2509 Ok(Ratio::from_integer(BigInt::from_biguint( 2510 Sign::Plus, 2511 BigUint::new(vec![1]) 2512 ))) 2513 ); 2514 assert_eq!( 2515 Evaluator::new(b"-0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2516 Ok(Ratio::from_integer(BigInt::from_biguint( 2517 Sign::NoSign, 2518 BigUint::new(Vec::new()) 2519 ))) 2520 ); 2521 // negation has lower precedence than exponentiation. 2522 assert_eq!( 2523 Evaluator::new(b"-2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2524 Ok(Ratio::from_integer(BigInt::from_biguint( 2525 Sign::Minus, 2526 BigUint::new(vec![4]) 2527 ))) 2528 ); 2529 // negation always becomes exponentiation eventually. 2530 assert_eq!( 2531 Evaluator::new(b"2.0^2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2532 Ok(Ratio::from_integer(BigInt::from_biguint( 2533 Sign::Plus, 2534 BigUint::new(vec![4]) 2535 ))) 2536 ); 2537 // Error since leading/trailing whitespace is not consumed by exponentiation or higher precedence expressions. 2538 assert_eq!( 2539 Evaluator::new(b" -2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2540 Err(MissingTerm(0)) 2541 ); 2542 assert_eq!( 2543 Evaluator::new(b"\t-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_neg(), 2544 Err(MissingTerm(0)) 2545 ); 2546 } 2547 #[expect( 2548 clippy::cognitive_complexity, 2549 clippy::too_many_lines, 2550 reason = "a lot to test" 2551 )] 2552 #[cfg(not(feature = "rand"))] 2553 #[test] 2554 fn mult() { 2555 assert_eq!( 2556 Evaluator::new(b"2 * 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2557 Ok(Ratio::from_integer(BigInt::from_biguint( 2558 Sign::Plus, 2559 BigUint::new(vec![6]) 2560 ))) 2561 ); 2562 assert_eq!( 2563 Evaluator::new( 2564 b"-2 * \t 3", 2565 &mut Cache::new(), 2566 &mut None, 2567 &mut Vec::new() 2568 ) 2569 .get_mults(), 2570 Ok(Ratio::from_integer(BigInt::from_biguint( 2571 Sign::Minus, 2572 BigUint::new(vec![6]) 2573 ))) 2574 ); 2575 assert_eq!( 2576 Evaluator::new( 2577 b"2\t * -3.0", 2578 &mut Cache::new(), 2579 &mut None, 2580 &mut Vec::new() 2581 ) 2582 .get_mults(), 2583 Ok(Ratio::from_integer(BigInt::from_biguint( 2584 Sign::Minus, 2585 BigUint::new(vec![6]) 2586 ))) 2587 ); 2588 assert_eq!( 2589 Evaluator::new(b"-2.5*-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2590 Ok(Ratio::new( 2591 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![35])), 2592 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![4])) 2593 )) 2594 ); 2595 assert_eq!( 2596 Evaluator::new(b"4.0\t / 6", &mut Cache::new(), &mut None, &mut Vec::new()) 2597 .get_mults(), 2598 Ok(Ratio::new( 2599 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])), 2600 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![3])) 2601 )) 2602 ); 2603 assert_eq!( 2604 Evaluator::new(b"6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2605 Ok(Ratio::from_integer(BigInt::from_biguint( 2606 Sign::Plus, 2607 BigUint::new(vec![2]) 2608 ))) 2609 ); 2610 assert_eq!( 2611 Evaluator::new(b"-6/3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2612 Ok(Ratio::from_integer(BigInt::from_biguint( 2613 Sign::Minus, 2614 BigUint::new(vec![2]) 2615 ))) 2616 ); 2617 assert_eq!( 2618 Evaluator::new(b"6/-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2619 Ok(Ratio::from_integer(BigInt::from_biguint( 2620 Sign::Minus, 2621 BigUint::new(vec![2]) 2622 ))) 2623 ); 2624 assert_eq!( 2625 Evaluator::new( 2626 b"- 6 /\t - 3", 2627 &mut Cache::new(), 2628 &mut None, 2629 &mut Vec::new() 2630 ) 2631 .get_mults(), 2632 Ok(Ratio::from_integer(BigInt::from_biguint( 2633 Sign::Plus, 2634 BigUint::new(vec![2]) 2635 ))) 2636 ); 2637 // 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). 2638 assert!( 2639 Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new()) 2640 .get_mults() 2641 .is_ok_and(|r| { 2642 Evaluator::new(b"1/3/2", &mut Cache::new(), &mut None, &mut Vec::new()) 2643 .get_mults() 2644 .is_ok_and(|r2| r != r2) 2645 }) 2646 ); 2647 assert!( 2648 Evaluator::new(b"1/1.5", &mut Cache::new(), &mut None, &mut Vec::new()) 2649 .get_mults() 2650 .is_ok_and(|r| { 2651 Evaluator::new(b"1/(3/2)", &mut Cache::new(), &mut None, &mut Vec::new()) 2652 .get_mults() 2653 .is_ok_and(|r2| r == r2) 2654 }) 2655 ); 2656 // multiplication always becomes negation eventually. 2657 assert_eq!( 2658 Evaluator::new(b"-2.0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2659 Ok(Ratio::from_integer(BigInt::from_biguint( 2660 Sign::Minus, 2661 BigUint::new(vec![2]) 2662 ))) 2663 ); 2664 // Error since leading/trailing whitespace is not consumed by multiplication or higher precedence expressions. 2665 assert_eq!( 2666 Evaluator::new(b" 2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2667 Err(MissingTerm(0)) 2668 ); 2669 assert_eq!( 2670 Evaluator::new(b"\t2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2671 Err(MissingTerm(0)) 2672 ); 2673 assert_eq!( 2674 Evaluator::new( 2675 b"4.0\t mod 6", 2676 &mut Cache::new(), 2677 &mut None, 2678 &mut Vec::new() 2679 ) 2680 .get_mults(), 2681 Ok(Ratio::from_integer(BigInt::from_biguint( 2682 Sign::Plus, 2683 BigUint::new(vec![4]) 2684 ),)) 2685 ); 2686 assert_eq!( 2687 Evaluator::new(b"5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2688 Ok(Ratio::from_integer(BigInt::from_biguint( 2689 Sign::Plus, 2690 BigUint::new(vec![2]) 2691 ))) 2692 ); 2693 assert_eq!( 2694 Evaluator::new(b"-5 mod 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2695 Ok(Ratio::from_integer(BigInt::from_biguint( 2696 Sign::Plus, 2697 BigUint::new(vec![1]) 2698 ))) 2699 ); 2700 assert_eq!( 2701 Evaluator::new(b"5 mod -3", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2702 Ok(Ratio::from_integer(BigInt::from_biguint( 2703 Sign::Plus, 2704 BigUint::new(vec![2]) 2705 ))) 2706 ); 2707 assert_eq!( 2708 Evaluator::new( 2709 b"-5 mod\t -3", 2710 &mut Cache::new(), 2711 &mut None, 2712 &mut Vec::new() 2713 ) 2714 .get_mults(), 2715 Ok(Ratio::from_integer(BigInt::from_biguint( 2716 Sign::Plus, 2717 BigUint::new(vec![1]) 2718 ))) 2719 ); 2720 // Cannot divide by 0. 2721 assert_eq!( 2722 Evaluator::new(b"2/0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2723 Err(DivByZero(3)) 2724 ); 2725 assert_eq!( 2726 Evaluator::new(b"2 mod 0", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2727 Err(ModZero(7)) 2728 ); 2729 // Right and left operands of mod must be integers. 2730 assert_eq!( 2731 Evaluator::new(b"3.2 mod 1", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2732 Err(ModIsNotInt(4)) 2733 ); 2734 assert_eq!( 2735 Evaluator::new(b"3 mod 3.2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2736 Err(ModIsNotInt(9)) 2737 ); 2738 // multiplication has lower precedence than exponentiation. 2739 assert_eq!( 2740 Evaluator::new(b"2*2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2741 Ok(Ratio::from_integer(BigInt::from_biguint( 2742 Sign::Plus, 2743 BigUint::new(vec![8]) 2744 ))) 2745 ); 2746 assert_eq!( 2747 Evaluator::new(b"8/2^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2748 Ok(Ratio::from_integer(BigInt::from_biguint( 2749 Sign::Plus, 2750 BigUint::new(vec![2]) 2751 ))) 2752 ); 2753 assert_eq!( 2754 Evaluator::new(b"8 mod 3^2", &mut Cache::new(), &mut None, &mut Vec::new()).get_mults(), 2755 Ok(Ratio::from_integer(BigInt::from_biguint( 2756 Sign::Plus, 2757 BigUint::new(vec![8]) 2758 ))) 2759 ); 2760 } 2761 #[expect(clippy::too_many_lines, reason = "a lot to test")] 2762 #[cfg(not(feature = "rand"))] 2763 #[test] 2764 fn add() { 2765 assert_eq!( 2766 Evaluator::new(b"2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2767 Ok(Ratio::from_integer(BigInt::from_biguint( 2768 Sign::Plus, 2769 BigUint::new(vec![5]) 2770 ))) 2771 ); 2772 assert_eq!( 2773 Evaluator::new(b"-2 + 3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2774 Ok(Ratio::from_integer(BigInt::from_biguint( 2775 Sign::Plus, 2776 BigUint::new(vec![1]) 2777 ))) 2778 ); 2779 assert_eq!( 2780 Evaluator::new( 2781 b"2 + \t -3.0", 2782 &mut Cache::new(), 2783 &mut None, 2784 &mut Vec::new() 2785 ) 2786 .get_adds(), 2787 Ok(Ratio::from_integer(BigInt::from_biguint( 2788 Sign::Minus, 2789 BigUint::new(vec![1]) 2790 ))) 2791 ); 2792 assert_eq!( 2793 Evaluator::new(b"-2.5+-3.5", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2794 Ok(Ratio::from_integer(BigInt::from_biguint( 2795 Sign::Minus, 2796 BigUint::new(vec![6]) 2797 ))) 2798 ); 2799 assert_eq!( 2800 Evaluator::new(b"4.0\t - 6", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2801 Ok(Ratio::from_integer(BigInt::from_biguint( 2802 Sign::Minus, 2803 BigUint::new(vec![2]) 2804 ))) 2805 ); 2806 assert_eq!( 2807 Evaluator::new(b"6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2808 Ok(Ratio::from_integer(BigInt::from_biguint( 2809 Sign::Plus, 2810 BigUint::new(vec![3]) 2811 ))) 2812 ); 2813 assert_eq!( 2814 Evaluator::new(b"-6-3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2815 Ok(Ratio::from_integer(BigInt::from_biguint( 2816 Sign::Minus, 2817 BigUint::new(vec![9]) 2818 ))) 2819 ); 2820 assert_eq!( 2821 Evaluator::new(b"6--3", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2822 Ok(Ratio::from_integer(BigInt::from_biguint( 2823 Sign::Plus, 2824 BigUint::new(vec![9]) 2825 ))) 2826 ); 2827 assert_eq!( 2828 Evaluator::new( 2829 b"- 6 -\t - 3", 2830 &mut Cache::new(), 2831 &mut None, 2832 &mut Vec::new() 2833 ) 2834 .get_adds(), 2835 Ok(Ratio::from_integer(BigInt::from_biguint( 2836 Sign::Minus, 2837 BigUint::new(vec![3]) 2838 ))) 2839 ); 2840 // addition always becomes multiplication eventually. 2841 assert_eq!( 2842 Evaluator::new(b"2 * 8", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2843 Ok(Ratio::from_integer(BigInt::from_biguint( 2844 Sign::Plus, 2845 BigUint::new(vec![16]) 2846 ))) 2847 ); 2848 assert_eq!( 2849 Evaluator::new(b"8 /\t 2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2850 Ok(Ratio::from_integer(BigInt::from_biguint( 2851 Sign::Plus, 2852 BigUint::new(vec![4]) 2853 ))) 2854 ); 2855 // Error since leading/trailing whitespace is not consumed by addition or higher precedence expressions. 2856 assert_eq!( 2857 Evaluator::new(b" 2+2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2858 Err(MissingTerm(0)) 2859 ); 2860 assert_eq!( 2861 Evaluator::new(b" 2-2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2862 Err(MissingTerm(0)) 2863 ); 2864 // addition has lower precedence than multiplication. 2865 assert_eq!( 2866 Evaluator::new(b"2+2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2867 Ok(Ratio::from_integer(BigInt::from_biguint( 2868 Sign::Plus, 2869 BigUint::new(vec![6]) 2870 ))) 2871 ); 2872 assert_eq!( 2873 Evaluator::new(b"2+2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2874 Ok(Ratio::from_integer(BigInt::from_biguint( 2875 Sign::Plus, 2876 BigUint::new(vec![3]) 2877 ))) 2878 ); 2879 assert_eq!( 2880 Evaluator::new(b"2-2*2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2881 Ok(Ratio::from_integer(BigInt::from_biguint( 2882 Sign::Minus, 2883 BigUint::new(vec![2]) 2884 ))) 2885 ); 2886 assert_eq!( 2887 Evaluator::new(b"2-2/2", &mut Cache::new(), &mut None, &mut Vec::new()).get_adds(), 2888 Ok(Ratio::from_integer(BigInt::from_biguint( 2889 Sign::Plus, 2890 BigUint::new(vec![1]) 2891 ))) 2892 ); 2893 } 2894 #[cfg(not(feature = "rand"))] 2895 #[test] 2896 fn exit() { 2897 assert_eq!( 2898 Evaluator::new(b" q \n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2899 Ok(Exit) 2900 ); 2901 assert_eq!( 2902 Evaluator::new( 2903 b" q \r\n", 2904 &mut Cache::new(), 2905 &mut None, 2906 &mut Vec::new() 2907 ) 2908 .evaluate(), 2909 Ok(Exit) 2910 ); 2911 assert_eq!( 2912 Evaluator::new(b"q\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2913 Ok(Exit) 2914 ); 2915 assert_eq!( 2916 Evaluator::new(b"q\r\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2917 Ok(Exit) 2918 ); 2919 assert_eq!( 2920 Evaluator::new(b"\rq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2921 Err(MissingTerm(0)) 2922 ); 2923 assert_eq!( 2924 Evaluator::new(b"\tq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2925 Ok(Exit) 2926 ); 2927 assert_eq!( 2928 Evaluator::new(b"q\n\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2929 Err(InvalidQuit) 2930 ); 2931 assert_eq!( 2932 Evaluator::new(b"\nq\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2933 Err(MissingTerm(0)) 2934 ); 2935 assert_eq!( 2936 Evaluator::new(b"q", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2937 Ok(Exit) 2938 ); 2939 } 2940 #[expect(clippy::unwrap_used, reason = "comment justifies correctness")] 2941 #[cfg(not(feature = "rand"))] 2942 #[test] 2943 fn store() { 2944 let mut prev = None; 2945 let mut cache = Cache::new(); 2946 // Quick check that `Ok` is returned. 2947 _ = Evaluator::new(b"1\n", &mut cache, &mut prev, &mut Vec::new()) 2948 .evaluate() 2949 .unwrap(); 2950 assert!(cache.is_empty()); 2951 assert_eq!( 2952 Evaluator::new(b"s\n", &mut cache, &mut prev, &mut Vec::new()).evaluate(), 2953 Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint( 2954 Sign::Plus, 2955 BigUint::new(vec![1]) 2956 ))))) 2957 ); 2958 assert_eq!(cache.len(), 1); 2959 assert_eq!( 2960 Evaluator::new(b"s2\n", &mut Cache::new(), &mut None, &mut Vec::new()).evaluate(), 2961 Err(InvalidStore) 2962 ); 2963 } 2964 #[expect(clippy::too_many_lines, reason = "a lot to test")] 2965 #[cfg(not(feature = "rand"))] 2966 #[test] 2967 fn eval() { 2968 use core::str::FromStr as _; 2969 let mut prev = None; 2970 let mut cache = Cache::new(); 2971 let mut exp = Vec::new(); 2972 assert_eq!( 2973 Evaluator::new(b"1+2\n", &mut cache, &mut prev, &mut exp).evaluate(), 2974 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 2975 Sign::Plus, 2976 BigUint::new(vec![3]) 2977 )))) 2978 ); 2979 assert_eq!( 2980 Evaluator::new(b"\t s \n", &mut cache, &mut prev, &mut exp).evaluate(), 2981 Ok(Store(&Some(Ratio::from_integer(BigInt::from_biguint( 2982 Sign::Plus, 2983 BigUint::new(vec![3]) 2984 ))))) 2985 ); 2986 assert_eq!( 2987 Evaluator::new(b"-1/2+2*@\n", &mut cache, &mut prev, &mut exp).evaluate(), 2988 Ok(Eval(&Ratio::new( 2989 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), 2990 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 2991 ))) 2992 ); 2993 assert_eq!( 2994 Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(), 2995 Ok(Store(&Some(Ratio::new( 2996 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![11])), 2997 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2])) 2998 )))) 2999 ); 3000 assert_eq!( 3001 Evaluator::new(b"@^@2!\r\n", &mut cache, &mut prev, &mut exp).evaluate(), 3002 Ok(Eval(&Ratio::new( 3003 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])), 3004 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) 3005 ))) 3006 ); 3007 assert_eq!( 3008 Evaluator::new(b"s\n", &mut cache, &mut prev, &mut exp).evaluate(), 3009 Ok(Store(&Some(Ratio::new( 3010 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1_771_561])), 3011 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![64])) 3012 )))) 3013 ); 3014 // Verified with Wolfram Alpha. 3015 assert!( 3016 Evaluator::new( 3017 b" \t 1 + (2 * |(7.98\t - 12/7)|) / 4!^@3!^|1-3|\t \n", 3018 &mut cache, 3019 &mut prev, 3020 &mut exp 3021 ) 3022 .evaluate().is_ok_and(|r| { 3023 Ratio::from_str("2841328814244153299237884950647090899374680152474331/2841328814244153299237884950647090899374680152473600").is_ok_and(|r2| { 3024 r == Eval(&r2) 3025 }) 3026 }) 3027 ); 3028 assert_eq!( 3029 Evaluator::new( 3030 b" \t round(19/6,0)!\t \r\n", 3031 &mut cache, 3032 &mut prev, 3033 &mut exp 3034 ) 3035 .evaluate(), 3036 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 3037 Sign::Plus, 3038 BigUint::new(vec![6]) 3039 )))) 3040 ); 3041 assert_eq!( 3042 Evaluator::new( 3043 b" \t 2^round(19/6,0)!\t \r\n", 3044 &mut cache, 3045 &mut prev, 3046 &mut exp 3047 ) 3048 .evaluate(), 3049 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 3050 Sign::Plus, 3051 BigUint::new(vec![64]) 3052 )))) 3053 ); 3054 assert_eq!( 3055 Evaluator::new(b"round(19/6,0)^2\t\n", &mut cache, &mut prev, &mut exp).evaluate(), 3056 Ok(Eval(&Ratio::from_integer(BigInt::from_biguint( 3057 Sign::Plus, 3058 BigUint::new(vec![9]) 3059 )))) 3060 ); 3061 // Invalid UTF-8 does not cause a panic!. 3062 assert_eq!( 3063 Evaluator::new(&[255, 255, 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(), 3064 Err(MissingTerm(0)) 3065 ); 3066 assert_eq!( 3067 Evaluator::new(&[b'2', 255, b'\n'], &mut cache, &mut prev, &mut exp).evaluate(), 3068 Err(TrailingSyms(1)) 3069 ); 3070 // Exactly one newline is required. 3071 assert_eq!( 3072 Evaluator::new(b"2\n\n", &mut cache, &mut prev, &mut exp).evaluate(), 3073 Err(TrailingSyms(1)) 3074 ); 3075 assert_eq!( 3076 prev, 3077 Some(Ratio::from_integer(BigInt::from_biguint( 3078 Sign::Plus, 3079 BigUint::new(vec![9]) 3080 ))) 3081 ); 3082 assert_eq!( 3083 Evaluator::new(b"\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(), 3084 Ok(Empty(&Some(Ratio::from_integer(BigInt::from_biguint( 3085 Sign::Plus, 3086 BigUint::new(vec![9]) 3087 ))))) 3088 ); 3089 assert_eq!( 3090 prev, 3091 Some(Ratio::from_integer(BigInt::from_biguint( 3092 Sign::Plus, 3093 BigUint::new(vec![9]) 3094 ))) 3095 ); 3096 assert_eq!( 3097 Evaluator::new(b"\r\n", &mut cache, &mut prev.clone(), &mut exp).evaluate(), 3098 Ok(Empty(&prev)) 3099 ); 3100 assert_eq!( 3101 Evaluator::new(&[0u8; 0], &mut cache, &mut prev.clone(), &mut exp).evaluate(), 3102 Ok(Empty(&prev)) 3103 ); 3104 } 3105 #[cfg(feature = "rand")] 3106 #[test] 3107 fn eval_iter() { 3108 struct Reader<'a> { 3109 data: &'a [u8], 3110 err: bool, 3111 } 3112 impl<'a> Reader<'a> { 3113 fn new(data: &'a [u8]) -> Self { 3114 Self { data, err: true } 3115 } 3116 } 3117 impl Read for Reader<'_> { 3118 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] 3119 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 3120 if self.err { 3121 self.err = false; 3122 Err(Error::other("")) 3123 } else { 3124 let len = usize::min(buf.len(), self.data.len()); 3125 // `len <= buf.len()` and `len <= `self.data.len()`. 3126 buf[..len].copy_from_slice(&self.data[..len]); 3127 Ok(len) 3128 } 3129 } 3130 } 3131 impl BufRead for Reader<'_> { 3132 fn fill_buf(&mut self) -> io::Result<&[u8]> { 3133 if self.err { 3134 self.err = false; 3135 Err(Error::other("")) 3136 } else { 3137 Ok(self.data) 3138 } 3139 } 3140 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")] 3141 fn consume(&mut self, amount: usize) { 3142 // This is just a test, so calling code passing in an invalid `amount` is fine. 3143 self.data = &self.data[amount..]; 3144 } 3145 } 3146 let mut iter = EvalIter::new(Reader::new( 3147 b"1+2\n4\n\nq\n5\ns\nrand() + rand(-139/@, 2984/134)\nab", 3148 )); 3149 assert!( 3150 iter.lend_next() 3151 .is_some_and(|res| res.map_or_else(|e| matches!(e, E::Error(_)), |_| false)) 3152 ); 3153 assert!(iter.lend_next().is_some_and(|res| { 3154 res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(3i32))) 3155 })); 3156 assert!(iter.lend_next().is_some_and(|res| { 3157 res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(4i32))) 3158 })); 3159 assert!(iter.lend_next().is_some_and(|res| { 3160 res.is_ok_and( 3161 |e| matches!(e, Empty(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(4i32))), 3162 ) 3163 })); 3164 assert!(iter.lend_next().is_none()); 3165 assert!(iter.lend_next().is_some_and(|res| { 3166 res.is_ok_and(|e| matches!(e, Eval(r) if r.numer().to_i32() == Some(5i32))) 3167 })); 3168 assert!(iter.lend_next().is_some_and(|res| { 3169 res.is_ok_and( 3170 |e| matches!(e, Store(r) if r.as_ref().is_some_and(|val| val.numer().to_i32() == Some(5i32))), 3171 ) 3172 })); 3173 assert!( 3174 iter.lend_next() 3175 .is_some_and(|res| res.is_ok_and(|e| matches!(e, Eval(r) if r.is_integer()))) 3176 ); 3177 assert!(iter.lend_next().is_some_and(|res| { 3178 res.is_err_and(|err| matches!(err, E::LangErr(ref e) if matches!(*e, MissingTerm(_)))) 3179 })); 3180 assert!(iter.lend_next().is_none()); 3181 assert!(iter.lend_next().is_none()); 3182 assert!(iter.lend_next().is_none()); 3183 } 3184 }