calc_lib/lib.rs
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))]
123extern crate alloc;
124/// Unit tests.
125#[cfg(test)]
126mod tests;
127use LangErr::{
128 DivByZero, ExpDivByZero, ExpIsNotIntOrOneHalf, InvalidAbs, InvalidDec, InvalidPar, InvalidQuit,
129 InvalidRound, InvalidStore, MissingTerm, ModIsNotInt, ModZero, NotEnoughPrevResults,
130 NotNonNegIntFact, SqrtDoesNotExist, TrailingSyms,
131};
132use O::{Empty, Eval, Exit, Store};
133use alloc::{
134 string::{String, ToString as _},
135 vec,
136 vec::Vec,
137};
138use cache::Cache;
139#[cfg(not(feature = "rand"))]
140use core::marker::PhantomData;
141use core::{
142 convert,
143 fmt::{self, Display, Formatter},
144 ops::Index as _,
145};
146pub use num_bigint;
147use num_bigint::{BigInt, BigUint, Sign};
148use num_integer::Integer as _;
149pub use num_rational;
150use num_rational::Ratio;
151#[cfg(feature = "rand")]
152use num_traits::ToPrimitive as _;
153use num_traits::{Inv as _, Pow as _};
154#[cfg(target_os = "openbsd")]
155use priv_sep as _;
156#[cfg(feature = "rand")]
157pub use rand;
158#[cfg(feature = "rand")]
159use rand::{Rng as _, rngs::ThreadRng};
160/// Fixed-sized cache that automatically overwrites the oldest data
161/// when a new item is added and the cache is full.
162///
163/// One can think of
164/// [`Cache`] as a very limited but more performant [`VecDeque`][alloc::collections::VecDeque] that only
165/// adds new data or reads old data.
166pub mod cache;
167/// Generalizes [`Iterator`] by using
168/// generic associated types.
169pub mod lending_iterator;
170/// Error due to a language violation.
171#[non_exhaustive]
172#[cfg_attr(test, derive(Eq, PartialEq))]
173#[derive(Debug)]
174pub enum LangErr {
175 /// The input began with a `q` but had non-whitespace
176 /// that followed it.
177 InvalidQuit,
178 /// The input began with an `s` but had non-whitespace
179 /// that followed it.
180 InvalidStore,
181 /// A sub-expression in the input would have led
182 /// to a division by zero.
183 DivByZero(usize),
184 /// A sub-expression in the input would have led
185 /// to a rational number that was not 0 or 1 to be
186 /// raised to a non-integer power that is not (+/-) 1/2.
187 ExpIsNotIntOrOneHalf(usize),
188 /// A sub-expression in the input would have led
189 /// to 0 being raised to a negative power which itself
190 /// would have led to a division by zero.
191 ExpDivByZero(usize),
192 /// A sub-expression in the input would have led
193 /// to a number modulo 0.
194 ModZero(usize),
195 /// A sub-expression in the input would have led
196 /// to the mod of two expressions with at least one
197 /// not being an integer.
198 ModIsNotInt(usize),
199 /// A sub-expression in the input would have led
200 /// to a non-integer factorial or a negative integer factorial.
201 NotNonNegIntFact(usize),
202 /// The input contained a non-empty sequence of digits followed
203 /// by `.` which was not followed by a non-empty sequence of digits.
204 InvalidDec(usize),
205 /// A recall expression was used to recall the *i*-th most-recent stored result,
206 /// but there are fewer than *i* stored where
207 /// *i ∈ {1, 2, 3, 4, 5, 6, 7, 8}*.
208 NotEnoughPrevResults(usize),
209 /// The input did not contain a closing `|`.
210 InvalidAbs(usize),
211 /// The input did not contain a closing `)`.
212 InvalidPar(usize),
213 /// The input contained an invalid round expression.
214 InvalidRound(usize),
215 /// A sub-expression in the input had a missing terminal expression
216 /// where a terminal expression is a decimal literal expression,
217 /// recall expression, absolute value expression, parenthetical
218 /// expression, or round expression.
219 MissingTerm(usize),
220 /// The expression that was passed to the square root does not have a solution
221 /// in the field of rational numbers.
222 SqrtDoesNotExist(usize),
223 /// The input started with a valid expression but was immediately followed
224 /// by symbols that could not be chained with the preceding expression.
225 TrailingSyms(usize),
226 /// The input contained an invalid random expression.
227 #[cfg(feature = "rand")]
228 InvalidRand(usize),
229 /// Error when the second argument is less than first in the rand function.
230 #[cfg(feature = "rand")]
231 RandInvalidArgs(usize),
232 /// Error when there are no 64-bit integers in the interval passed to the random function.
233 #[cfg(feature = "rand")]
234 RandNoInts(usize),
235}
236impl Display for LangErr {
237 #[inline]
238 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
239 match *self {
240 InvalidStore => f.write_str("Invalid store expression. A store expression must be of the extended regex form: ^[ \\t]*s[ \\t]*$."),
241 InvalidQuit => f.write_str("Invalid quit expression. A quit expression must be of the extended regex form: ^[ \\t]*q[ \\t]*$."),
242 DivByZero(u) => write!(f, "Division by zero ending at position {u}."),
243 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}."),
244 ExpDivByZero(u) => write!(f, "Non-negative exponent with a base of 0 ending at position {u}."),
245 ModZero(u) => write!(f, "A number modulo 0 ending at position {u}."),
246 ModIsNotInt(u) => write!(f, "The modulo expression was applied to at least one non-integer ending at position {u}."),
247 NotNonNegIntFact(u) => write!(f, "Factorial of a rational number that was not a non-negative integer ending at position {u}."),
248 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]+)?."),
249 NotEnoughPrevResults(len) => write!(f, "There are only {len} previous results."),
250 InvalidAbs(u) => write!(f, "Invalid absolute value expression ending at position {u}. An absolute value expression is an addition expression enclosed in '||'."),
251 InvalidPar(u) => write!(f, "Invalid parenthetical expression ending at position {u}. A parenthetical expression is an addition expression enclosed in '()'."),
252 InvalidRound(u) => write!(f, "Invalid round expression ending at position {u}. A round expression is of the form 'round(<mod expression>, digit)'"),
253 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}."),
254 #[cfg(not(feature = "rand"))]
255 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."),
256 #[cfg(feature = "rand")]
257 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."),
258 TrailingSyms(u) => write!(f, "Trailing symbols starting at position {u}."),
259 #[cfg(feature = "rand")]
260 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>)'."),
261 #[cfg(feature = "rand")]
262 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}."),
263 #[cfg(feature = "rand")]
264 Self::RandNoInts(u) => write!(f, "There are no 64-bit integers within the interval passed to the random function ending at position {u}."),
265 }
266 }
267}
268/// A successful evaluation of an input.
269#[cfg_attr(test, derive(Eq, PartialEq))]
270#[derive(Debug)]
271pub enum O<'a> {
272 /// The input only contained whitespace.
273 /// This returns the previous `Eval`.
274 /// It is `None` iff there have been no
275 /// previous `Eval` results.
276 Empty(&'a Option<Ratio<BigInt>>),
277 /// The quit expression was issued to terminate the program.
278 Exit,
279 /// Result of a "normal" expression.
280 Eval(&'a Ratio<BigInt>),
281 /// The store expression stores and returns the previous `Eval`.
282 /// It is `None` iff there have been no previous `Eval` results.
283 Store(&'a Option<Ratio<BigInt>>),
284}
285impl Display for O<'_> {
286 #[expect(
287 unsafe_code,
288 reason = "manually construct guaranteed UTF-8; thus avoid the needless check"
289 )]
290 #[expect(clippy::indexing_slicing, reason = "comment justifies correctness")]
291 #[inline]
292 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
293 match *self {
294 Empty(o) => {
295 o.as_ref().map_or(Ok(()), |val| {
296 if val.is_integer() {
297 write!(f, "> {val}")
298 } else {
299 // If the prime factors of the denominator are only 2 and 5,
300 // then the number requires a finite number of digits and thus
301 // will be represented perfectly using the fewest number of digits.
302 // Any other situation will be rounded to 9 fractional digits.
303 // max{twos, fives} represents the minimum number of fractional
304 // digits necessary to represent val.
305 let mut twos = 0;
306 let mut fives = 0;
307 let zero = BigInt::from_biguint(Sign::NoSign, BigUint::new(Vec::new()));
308 let one = BigInt::from_biguint(Sign::Plus, BigUint::new(vec![1]));
309 let two = BigInt::from_biguint(Sign::Plus, BigUint::new(vec![2]));
310 let five = BigInt::from_biguint(Sign::Plus, BigUint::new(vec![5]));
311 let mut denom = val.denom().clone();
312 let mut div_rem;
313 while denom > one {
314 div_rem = denom.div_rem(&two);
315 if div_rem.1 == zero {
316 twos += 1;
317 denom = div_rem.0;
318 } else {
319 break;
320 }
321 }
322 while denom > one {
323 div_rem = denom.div_rem(&five);
324 if div_rem.1 == zero {
325 fives += 1;
326 denom = div_rem.0;
327 } else {
328 break;
329 }
330 }
331 // int < 0 iff val <= -1. frac < 0 iff val is a negative non-integer.
332 let (int, frac, digits) = if denom == one {
333 let (int, mut frac) = val.numer().div_rem(val.denom());
334 while twos > fives {
335 frac *= &five;
336 fives += 1;
337 }
338 while fives > twos {
339 frac *= &two;
340 twos += 1;
341 }
342 (int, frac, twos)
343 } else {
344 // Requires an infinite number of decimal digits to represent, so we display
345 // 9 digits after rounding.
346 let mult =
347 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])).pow(9u8);
348 let (int, frac) = (val * &mult).round().numer().div_rem(&mult);
349 (int, frac, 9)
350 };
351 let int_str = int.to_string().into_bytes();
352 let (mut v, frac_str) = if val.numer().sign() == Sign::Minus {
353 // Guaranteed to be non-empty.
354 if int_str[0] == b'-' {
355 (
356 Vec::with_capacity(int_str.len() + 1 + digits),
357 (-frac).to_string().into_bytes(),
358 )
359 } else {
360 let mut tmp = Vec::with_capacity(int_str.len() + 2 + digits);
361 tmp.push(b'-');
362 (tmp, (-frac).to_string().into_bytes())
363 }
364 } else {
365 (
366 Vec::with_capacity(int_str.len() + 1 + digits),
367 frac.to_string().into_bytes(),
368 )
369 };
370 v.extend_from_slice(int_str.as_slice());
371 v.push(b'.');
372 // digits >= frac_str.len().
373 v.resize(v.len() + (digits - frac_str.len()), b'0');
374 v.extend_from_slice(frac_str.as_slice());
375 // SAFETY:
376 // v contains precisely the UTF-8 code units returned from Strings
377 // returned from the to_string function on the integer and fraction part of
378 // val plus optionally the single byte encodings of ".", "-", and "0".
379 write!(f, "> {}", unsafe { String::from_utf8_unchecked(v) })
380 }
381 })
382 }
383 Eval(r) => write!(f, "> {r}"),
384 Exit => Ok(()),
385 Store(o) => o.as_ref().map_or(Ok(()), |val| write!(f, "> {val}")),
386 }
387 }
388}
389/// Size of [`Evaluator::cache`].
390const CACHE_SIZE: usize = 8;
391/// Evaluates the supplied input.
392#[derive(Debug)]
393pub struct Evaluator<'input, 'cache, 'prev, 'scratch, 'rand> {
394 /// The input to be evaluated.
395 utf8: &'input [u8],
396 /// The index within `utf8` that evaluation needs to continue.
397 /// We use this instead of slicing from `utf8` since we want
398 /// to be able to report the position within the input
399 /// that an error occurs.
400 i: usize,
401 /// The cache of previously stored results.
402 cache: &'cache mut Cache<Ratio<BigInt>, CACHE_SIZE>,
403 /// The last result.
404 prev: &'prev mut Option<Ratio<BigInt>>,
405 /// Buffer used to evaluate right-associative sub-expressions.
406 scratch: &'scratch mut Vec<Ratio<BigInt>>,
407 /// Random number generator.
408 #[cfg(feature = "rand")]
409 rng: &'rand mut ThreadRng,
410 /// Need to use `'rand`.
411 #[cfg(not(feature = "rand"))]
412 _rng: PhantomData<fn() -> &'rand ()>,
413}
414#[allow(
415 single_use_lifetimes,
416 clippy::allow_attributes,
417 clippy::elidable_lifetime_names,
418 reason = "unify rand and not rand"
419)]
420impl<'input, 'cache, 'prev, 'scratch, 'rand> Evaluator<'input, 'cache, 'prev, 'scratch, 'rand> {
421 /// Creates an `Evaluator<'input, 'cache, 'prev, 'scratch, 'rand>` based on the supplied arguments.
422 #[cfg(not(feature = "rand"))]
423 #[inline]
424 pub fn new(
425 utf8: &'input [u8],
426 cache: &'cache mut Cache<Ratio<BigInt>, 8>,
427 prev: &'prev mut Option<Ratio<BigInt>>,
428 scratch: &'scratch mut Vec<Ratio<BigInt>>,
429 ) -> Self {
430 Self {
431 utf8,
432 i: 0,
433 cache,
434 prev,
435 scratch,
436 _rng: PhantomData,
437 }
438 }
439 /// Creates an `Evaluator<'input, 'cache, 'prev, 'scratch, 'rand>` based on the supplied arguments.
440 #[cfg(feature = "rand")]
441 #[inline]
442 pub const fn new(
443 utf8: &'input [u8],
444 cache: &'cache mut Cache<Ratio<BigInt>, 8>,
445 prev: &'prev mut Option<Ratio<BigInt>>,
446 scratch: &'scratch mut Vec<Ratio<BigInt>>,
447 rng: &'rand mut ThreadRng,
448 ) -> Self {
449 Self {
450 utf8,
451 i: 0,
452 cache,
453 prev,
454 scratch,
455 rng,
456 }
457 }
458 /// Evaluates the input consuming the `Evaluator<'input, 'cache, 'exp>`.
459 ///
460 /// Requires the input to contain one expression (i.e., if there are
461 /// multiple newlines, it will error).
462 ///
463 /// # Errors
464 ///
465 /// Returns a [`LangErr`] iff the input violates the calc language.
466 #[expect(clippy::indexing_slicing, reason = "correct")]
467 #[inline]
468 pub fn evaluate(mut self) -> Result<O<'prev>, LangErr> {
469 self.utf8 = if self.utf8.last().is_none_or(|b| *b != b'\n') {
470 self.utf8
471 } else {
472 &self.utf8[..self.utf8.len()
473 - self
474 .utf8
475 .get(self.utf8.len().wrapping_sub(2))
476 .map_or(1, |b| if *b == b'\r' { 2 } else { 1 })]
477 };
478 self.consume_ws();
479 let Some(b) = self.utf8.get(self.i) else {
480 return Ok(Empty(self.prev));
481 };
482 if *b == b'q' {
483 self.i += 1;
484 self.consume_ws();
485 if self.i == self.utf8.len() {
486 Ok(Exit)
487 } else {
488 Err(InvalidQuit)
489 }
490 } else if *b == b's' {
491 self.i += 1;
492 self.consume_ws();
493 if self.i == self.utf8.len() {
494 if let Some(ref val) = *self.prev {
495 self.cache.push(val.clone());
496 }
497 Ok(Store(self.prev))
498 } else {
499 Err(InvalidStore)
500 }
501 } else {
502 self.get_adds().and_then(move |val| {
503 self.consume_ws();
504 if self.i == self.utf8.len() {
505 Ok(Eval(self.prev.insert(val)))
506 } else {
507 Err(TrailingSyms(self.i))
508 }
509 })
510 }
511 }
512 /// Reads from the input until the next non-{space/tab} byte value.
513 #[expect(clippy::indexing_slicing, reason = "correct")]
514 fn consume_ws(&mut self) {
515 // ControlFlow makes more sense to use in try_fold; however due to a lack
516 // of a map_or_else function, it is easier to simply return a Result with
517 // Err taking the role of ControlFlow::Break.
518 self.i += self.utf8[self.i..]
519 .iter()
520 .try_fold(0, |val, b| match *b {
521 b' ' | b'\t' => Ok(val + 1),
522 _ => Err(val),
523 })
524 .unwrap_or_else(convert::identity);
525 }
526 /// Evaluates addition expressions as defined in the calc language.
527 /// This function is used for both addition and subtraction operations which
528 /// themselves are based on multiplication expressions.
529 fn get_adds(&mut self) -> Result<Ratio<BigInt>, LangErr> {
530 let mut left = self.get_mults()?;
531 let mut j;
532 self.consume_ws();
533 while let Some(i) = self.utf8.get(self.i) {
534 j = *i;
535 self.consume_ws();
536 if j == b'+' {
537 self.i += 1;
538 self.consume_ws();
539 left += self.get_mults()?;
540 } else if j == b'-' {
541 self.i += 1;
542 self.consume_ws();
543 left -= self.get_mults()?;
544 } else {
545 break;
546 }
547 }
548 Ok(left)
549 }
550 /// Evaluates multiplication expressions as defined in the calc language.
551 /// This function is used for both multiplication and division operations which
552 /// themselves are based on negation expressions.
553 fn get_mults(&mut self) -> Result<Ratio<BigInt>, LangErr> {
554 let mut left = self.get_neg()?;
555 let mut right;
556 let mut j;
557 let mut mod_val;
558 let mut numer;
559 self.consume_ws();
560 while let Some(i) = self.utf8.get(self.i) {
561 j = *i;
562 self.consume_ws();
563 if j == b'*' {
564 self.i += 1;
565 self.consume_ws();
566 left *= self.get_neg()?;
567 } else if j == b'/' {
568 self.i += 1;
569 self.consume_ws();
570 right = self.get_neg()?;
571 if right.numer().sign() == Sign::NoSign {
572 return Err(DivByZero(self.i));
573 }
574 left /= right;
575 } else if let Some(k) = self.utf8.get(self.i..self.i.saturating_add(3)) {
576 if k == b"mod" {
577 if !left.is_integer() {
578 return Err(ModIsNotInt(self.i));
579 }
580 self.i += 3;
581 self.consume_ws();
582 right = self.get_neg()?;
583 if !right.is_integer() {
584 return Err(ModIsNotInt(self.i));
585 }
586 numer = right.numer();
587 if numer.sign() == Sign::NoSign {
588 return Err(ModZero(self.i));
589 }
590 mod_val = left.numer() % numer;
591 left = Ratio::from_integer(if mod_val.sign() == Sign::Minus {
592 if numer.sign() == Sign::Minus {
593 mod_val - numer
594 } else {
595 mod_val + numer
596 }
597 } else {
598 mod_val
599 });
600 } else {
601 break;
602 }
603 } else {
604 break;
605 }
606 }
607 Ok(left)
608 }
609 /// Evaluates negation expressions as defined in the calc language.
610 /// This function is based on exponentiation expressions.
611 fn get_neg(&mut self) -> Result<Ratio<BigInt>, LangErr> {
612 let mut count = 0usize;
613 while let Some(b) = self.utf8.get(self.i) {
614 if *b == b'-' {
615 self.i += 1;
616 self.consume_ws();
617 count += 1;
618 } else {
619 break;
620 }
621 }
622 self.get_exps()
623 .map(|val| if count & 1 == 0 { val } else { -val })
624 }
625 /// Gets the square root of value so long as a solution exists.
626 #[expect(
627 clippy::unreachable,
628 reason = "code that shouldn't happen did, so we want to crash"
629 )]
630 fn sqrt(val: Ratio<BigInt>) -> Option<Ratio<BigInt>> {
631 /// Returns the square root of `n` if one exists; otherwise
632 /// returns `None`.
633 /// MUST NOT pass 0.
634 #[expect(clippy::suspicious_operation_groupings, reason = "false positive")]
635 fn calc(n: &BigUint) -> Option<BigUint> {
636 let mut shift = n.bits();
637 shift += shift & 1;
638 let mut result = BigUint::new(Vec::new());
639 let one = BigUint::new(vec![1]);
640 let zero = BigUint::new(Vec::new());
641 loop {
642 shift -= 2;
643 result <<= 1u32;
644 result |= &one;
645 result ^= if &result * &result > (n >> shift) {
646 &one
647 } else {
648 &zero
649 };
650 if shift == 0 {
651 break (&result * &result == *n).then_some(result);
652 }
653 }
654 }
655 let numer = val.numer();
656 if numer.sign() == Sign::NoSign {
657 Some(val)
658 } else {
659 numer.try_into().map_or_else(
660 |_| None,
661 |num| {
662 calc(&num).and_then(|n| {
663 calc(&val.denom().try_into().unwrap_or_else(|_| {
664 unreachable!("Ratio must never have a negative denominator")
665 }))
666 .map(|d| Ratio::new(n.into(), d.into()))
667 })
668 },
669 )
670 }
671 }
672 /// Evaluates exponentiation expressions as defined in the calc language.
673 /// This function is based on negation expressions.
674 fn get_exps(&mut self) -> Result<Ratio<BigInt>, LangErr> {
675 let mut t = self.get_fact()?;
676 let ix = self.scratch.len();
677 let mut prev;
678 let mut numer;
679 self.scratch.push(t);
680 self.consume_ws();
681 let mut j;
682 let one = BigInt::new(Sign::Plus, vec![1]);
683 let min_one = BigInt::new(Sign::Minus, vec![1]);
684 let two = BigInt::new(Sign::Plus, vec![2]);
685 while let Some(i) = self.utf8.get(self.i) {
686 j = *i;
687 self.consume_ws();
688 if j == b'^' {
689 self.i += 1;
690 self.consume_ws();
691 t = self.get_neg()?;
692 // Safe since we always push at least one value, and we always
693 // return immediately once we encounter an error.
694 prev = self.scratch.index(self.scratch.len() - 1);
695 numer = prev.numer();
696 // Equiv to checking if prev is 0.
697 if numer.sign() == Sign::NoSign {
698 if t.numer().sign() == Sign::Minus {
699 self.scratch.clear();
700 return Err(ExpDivByZero(self.i));
701 }
702 self.scratch.push(t);
703 } else if prev.is_integer() {
704 let t_numer = t.numer();
705 // 1 raised to anything is 1, so we don't bother
706 // storing the exponent.
707 if *numer == one {
708 } else if t.is_integer()
709 || ((*t_numer == one || *t_numer == min_one) && *t.denom() == two)
710 {
711 self.scratch.push(t);
712 } else {
713 self.scratch.clear();
714 return Err(ExpIsNotIntOrOneHalf(self.i));
715 }
716 } else if t.is_integer()
717 || ((*t.numer() == one || *t.numer() == min_one) && *t.denom() == two)
718 {
719 self.scratch.push(t);
720 } else {
721 self.scratch.clear();
722 return Err(ExpIsNotIntOrOneHalf(self.i));
723 }
724 } else {
725 break;
726 }
727 }
728 self.scratch
729 .drain(ix..)
730 .try_rfold(Ratio::from_integer(one.clone()), |exp, base| {
731 if exp.is_integer() {
732 Ok(base.pow(exp.numer()))
733 } else if base.numer().sign() == Sign::NoSign {
734 Ok(base)
735 } else if *exp.denom() == two {
736 if *exp.numer() == one {
737 Self::sqrt(base).map_or_else(|| Err(SqrtDoesNotExist(self.i)), Ok)
738 } else if *exp.numer() == min_one {
739 Self::sqrt(base)
740 .map_or_else(|| Err(SqrtDoesNotExist(self.i)), |v| Ok(v.inv()))
741 } else {
742 Err(ExpIsNotIntOrOneHalf(self.i))
743 }
744 } else {
745 Err(ExpIsNotIntOrOneHalf(self.i))
746 }
747 })
748 }
749 /// Evaluates factorial expressions as defined in the calc language.
750 /// This function is based on terminal expressions.
751 fn get_fact(&mut self) -> Result<Ratio<BigInt>, LangErr> {
752 /// Calculates the factorial of `val`.
753 fn fact(mut val: BigUint) -> BigUint {
754 let zero = BigUint::new(Vec::new());
755 let one = BigUint::new(vec![1]);
756 let mut calc = BigUint::new(vec![1]);
757 while val > zero {
758 calc *= &val;
759 val -= &one;
760 }
761 calc
762 }
763 let t = self.get_term()?;
764 let Some(b) = self.utf8.get(self.i) else {
765 return Ok(t);
766 };
767 if *b == b'!' {
768 self.i += 1;
769 if t.is_integer() {
770 // We can make a copy of self.i here, or call map_or instead
771 // of map_or_else.
772 let i = self.i;
773 t.numer().try_into().map_or_else(
774 |_| Err(NotNonNegIntFact(i)),
775 |val| {
776 let mut factorial = fact(val);
777 while let Some(b2) = self.utf8.get(self.i) {
778 if *b2 == b'!' {
779 self.i += 1;
780 factorial = fact(factorial);
781 } else {
782 break;
783 }
784 }
785 Ok(Ratio::from_integer(BigInt::from_biguint(
786 Sign::Plus,
787 factorial,
788 )))
789 },
790 )
791 } else {
792 Err(NotNonNegIntFact(self.i))
793 }
794 } else {
795 Ok(t)
796 }
797 }
798 /// Evaluates terminal expressions as defined in the calc language.
799 /// This function is based on number literal expressions, parenthetical expressions,
800 /// recall expressions, absolute value expressions, round expressions, and possibly
801 /// rand expressions if that feature is enabled.
802 fn get_term(&mut self) -> Result<Ratio<BigInt>, LangErr> {
803 self.get_rational().map_or_else(Err, |o| {
804 o.map_or_else(
805 || {
806 self.get_par().map_or_else(Err, |o2| {
807 o2.map_or_else(
808 || {
809 self.get_recall().map_or_else(Err, |o3| {
810 o3.map_or_else(
811 || {
812 self.get_abs().map_or_else(Err, |o4| {
813 o4.map_or_else(
814 || {
815 self.get_round().and_then(|o5| {
816 o5.map_or_else(
817 #[cfg(not(feature = "rand"))]
818 || Err(MissingTerm(self.i)),
819 #[cfg(feature = "rand")]
820 || self.get_rand(),
821 Ok,
822 )
823 })
824 },
825 Ok,
826 )
827 })
828 },
829 Ok,
830 )
831 })
832 },
833 Ok,
834 )
835 })
836 },
837 Ok,
838 )
839 })
840 }
841 /// Generates a random 64-bit integer. This function is based on add expressions. This is the last terminal
842 /// expression attempted when needing a terminal expression; as a result, it is the only terminal expression
843 /// that does not return an `Option`.
844 #[cfg(feature = "rand")]
845 fn get_rand(&mut self) -> Result<Ratio<BigInt>, LangErr> {
846 /// Generates a random 64-bit integer.
847 #[expect(clippy::host_endian_bytes, reason = "must keep platform endianness")]
848 fn rand(rng: &mut ThreadRng) -> i64 {
849 let mut bytes = [0; 8];
850 // `ThreadRng::try_fill_bytes` is infallible, so easier to call `fill_bytes`.
851 rng.fill_bytes(&mut bytes);
852 i64::from_ne_bytes(bytes)
853 }
854 /// Generates a random 64-bit integer inclusively between the passed arguments.
855 #[expect(
856 clippy::integer_division_remainder_used,
857 reason = "need for uniform randomness"
858 )]
859 #[expect(
860 clippy::as_conversions,
861 clippy::cast_possible_truncation,
862 clippy::cast_possible_wrap,
863 clippy::cast_sign_loss,
864 reason = "lossless conversions between signed integers"
865 )]
866 fn rand_range(
867 rng: &mut ThreadRng,
868 lower: &Ratio<BigInt>,
869 upper: &Ratio<BigInt>,
870 i: usize,
871 ) -> Result<i64, LangErr> {
872 if lower > upper {
873 return Err(LangErr::RandInvalidArgs(i));
874 }
875 let lo = lower.ceil();
876 let up = upper.floor();
877 let lo_int = lo.numer();
878 let up_int = up.numer();
879 if lo_int > &BigInt::from(i64::MAX) || up_int < &BigInt::from(i64::MIN) {
880 return Err(LangErr::RandNoInts(i));
881 }
882 let lo_min = lo_int.to_i64().unwrap_or(i64::MIN);
883 let up_max = up_int.to_i64().unwrap_or(i64::MAX);
884 if up_max > lo_min || upper.is_integer() || lower.is_integer() {
885 let low = i128::from(lo_min);
886 // `i64::MAX >= up_max >= low`; so underflow and overflow cannot happen.
887 // range is [1, 2^64] so casting to a u128 is fine.
888 let modulus = (i128::from(up_max) - low + 1) as u128;
889 // range is [0, i64::MAX] so converting to a `u64` is fine.
890 // rem represents how many values need to be removed
891 // when generating a random i64 in order for uniformity.
892 let rem = (0x0001_0000_0000_0000_0000 % modulus) as u64;
893 let mut low_adj;
894 loop {
895 low_adj = rand(rng) as u64;
896 // Since rem is in [0, i64::MAX], this is the same as low_adj < 0 || low_adj >= rem.
897 if low_adj >= rem {
898 return Ok(
899 // range is [i64::MIN, i64::MAX]; thus casts are safe.
900 // modulus is up_max - low + 1; so as low grows,
901 // % shrinks by the same factor. i64::MAX happens
902 // when low = up_max = i64::MAX or when low = 0,
903 // up_max = i64::MAX and low_adj is i64::MAX.
904 ((u128::from(low_adj) % modulus) as i128 + low) as i64,
905 );
906 }
907 }
908 } else {
909 Err(LangErr::RandNoInts(i))
910 }
911 }
912 // This is the last kind of terminal expression that is attempted.
913 // If there is no more data, then we have a missing terminal expression.
914 let Some(b) = self.utf8.get(self.i..self.i.saturating_add(5)) else {
915 return Err(MissingTerm(self.i));
916 };
917 if b == b"rand(" {
918 self.i += 5;
919 self.consume_ws();
920 let i = self.i;
921 self.utf8.get(self.i).map_or_else(
922 || Err(LangErr::InvalidRand(i)),
923 |p| {
924 if *p == b')' {
925 self.i += 1;
926 Ok(Ratio::from_integer(BigInt::from(rand(self.rng))))
927 } else {
928 let add = self.get_adds()?;
929 let Some(b2) = self.utf8.get(self.i) else {
930 return Err(LangErr::InvalidRand(self.i));
931 };
932 if *b2 == b',' {
933 self.i += 1;
934 self.consume_ws();
935 let add2 = self.get_adds()?;
936 self.consume_ws();
937 let Some(b3) = self.utf8.get(self.i) else {
938 return Err(LangErr::InvalidRand(self.i));
939 };
940 if *b3 == b')' {
941 self.i += 1;
942 rand_range(self.rng, &add, &add2, self.i)
943 .map(|v| Ratio::from_integer(BigInt::from(v)))
944 } else {
945 Err(LangErr::InvalidRand(self.i))
946 }
947 } else {
948 Err(LangErr::InvalidRand(self.i))
949 }
950 }
951 },
952 )
953 } else {
954 Err(MissingTerm(self.i))
955 }
956 }
957 /// Rounds a value to the specified number of fractional digits.
958 /// This function is based on add expressions.
959 fn get_round(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> {
960 let Some(b) = self.utf8.get(self.i..self.i.saturating_add(6)) else {
961 return Ok(None);
962 };
963 if b == b"round(" {
964 self.i += 6;
965 self.consume_ws();
966 let val = self.get_adds()?;
967 self.consume_ws();
968 let Some(b2) = self.utf8.get(self.i) else {
969 return Err(InvalidRound(self.i));
970 };
971 let b3 = *b2;
972 if b3 == b',' {
973 self.i += 1;
974 self.consume_ws();
975 let Some(b4) = self.utf8.get(self.i) else {
976 return Err(InvalidRound(self.i));
977 };
978 let r = if b4.is_ascii_digit() {
979 self.i += 1;
980 *b4 - b'0'
981 } else {
982 return Err(InvalidRound(self.i));
983 };
984 self.consume_ws();
985 let i = self.i;
986 self.utf8.get(self.i).map_or_else(
987 || Err(InvalidRound(i)),
988 |p| {
989 if *p == b')' {
990 self.i += 1;
991 let mult =
992 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10])).pow(r);
993 Ok(Some((val * &mult).round() / &mult))
994 } else {
995 Err(InvalidRound(self.i))
996 }
997 },
998 )
999 } else {
1000 Err(InvalidRound(self.i))
1001 }
1002 } else {
1003 Ok(None)
1004 }
1005 }
1006 /// Evaluates absolute value expressions as defined in the calc language.
1007 /// This function is based on add expressions.
1008 fn get_abs(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> {
1009 let Some(b) = self.utf8.get(self.i) else {
1010 return Ok(None);
1011 };
1012 if *b == b'|' {
1013 self.i += 1;
1014 self.consume_ws();
1015 let r = self.get_adds()?;
1016 self.consume_ws();
1017 let Some(b2) = self.utf8.get(self.i) else {
1018 return Err(InvalidAbs(self.i));
1019 };
1020 let b3 = *b2;
1021 if b3 == b'|' {
1022 self.i += 1;
1023 Ok(Some(if r.numer().sign() == Sign::Minus {
1024 -r
1025 } else {
1026 r
1027 }))
1028 } else {
1029 Err(InvalidAbs(self.i))
1030 }
1031 } else {
1032 Ok(None)
1033 }
1034 }
1035 /// Evaluates recall expressions as defined in the calc language.
1036 // This does not return a Result<Option<&Ratio<BigInt>>, LangErr>
1037 // since the only place this function is called is in get_term which
1038 // would end up needing to clone the Ratio anyway. By not forcing
1039 // get_term to clone, it can rely on map_or_else over match expressions.
1040 fn get_recall(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> {
1041 let Some(b) = self.utf8.get(self.i) else {
1042 return Ok(None);
1043 };
1044 if *b == b'@' {
1045 self.i += 1;
1046 self.cache
1047 .get(self.utf8.get(self.i).map_or(0, |b2| {
1048 if (b'1'..b'9').contains(b2) {
1049 self.i += 1;
1050 usize::from(*b2 - b'1')
1051 } else {
1052 0
1053 }
1054 }))
1055 .map_or_else(
1056 || Err(NotEnoughPrevResults(self.cache.len())),
1057 |p| Ok(Some(p.clone())),
1058 )
1059 } else {
1060 Ok(None)
1061 }
1062 }
1063 /// Evaluates parenthetical expressions as defined in the calc language.
1064 /// This function is based on add expressions.
1065 fn get_par(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> {
1066 let Some(b) = self.utf8.get(self.i) else {
1067 return Ok(None);
1068 };
1069 if *b == b'(' {
1070 self.i += 1;
1071 self.consume_ws();
1072 let r = self.get_adds()?;
1073 self.consume_ws();
1074 let Some(b2) = self.utf8.get(self.i) else {
1075 return Err(InvalidPar(self.i));
1076 };
1077 let b3 = *b2;
1078 if b3 == b')' {
1079 self.i += 1;
1080 Ok(Some(r))
1081 } else {
1082 Err(InvalidPar(self.i))
1083 }
1084 } else {
1085 Ok(None)
1086 }
1087 }
1088 /// Evaluates number literal expressions as defined in the calc language.
1089 #[expect(clippy::indexing_slicing, reason = "correct")]
1090 fn get_rational(&mut self) -> Result<Option<Ratio<BigInt>>, LangErr> {
1091 // ControlFlow makes more sense to use in try_fold; however due to a lack
1092 // of a map_or_else function, it is easier to simply return a Result with
1093 // Err taking the role of ControlFlow::Break.
1094 /// Used to parse a sequence of digits into an unsigned integer.
1095 fn to_biguint(v: &[u8]) -> (BigUint, usize) {
1096 v.iter()
1097 .try_fold((BigUint::new(Vec::new()), 0), |mut prev, d| {
1098 if d.is_ascii_digit() {
1099 prev.1 += 1;
1100 // `*d - b'0'` is guaranteed to return a integer between 0 and 9.
1101 prev.0 = prev.0 * 10u8 + (*d - b'0');
1102 Ok(prev)
1103 } else {
1104 Err(prev)
1105 }
1106 })
1107 .unwrap_or_else(convert::identity)
1108 }
1109 let (int, len) = to_biguint(&self.utf8[self.i..]);
1110 if len == 0 {
1111 return Ok(None);
1112 }
1113 self.i += len;
1114 if let Some(b) = self.utf8.get(self.i) {
1115 if *b == b'.' {
1116 self.i += 1;
1117 let (numer, len2) = to_biguint(&self.utf8[self.i..]);
1118 if len2 == 0 {
1119 Err(InvalidDec(self.i))
1120 } else {
1121 self.i += len2;
1122 Ok(Some(
1123 Ratio::from_integer(BigInt::from_biguint(Sign::Plus, int))
1124 + Ratio::new(
1125 BigInt::from_biguint(Sign::Plus, numer),
1126 BigInt::from_biguint(Sign::Plus, BigUint::new(vec![10]).pow(len2)),
1127 ),
1128 ))
1129 }
1130 } else {
1131 Ok(Some(Ratio::from_integer(BigInt::from_biguint(
1132 Sign::Plus,
1133 int,
1134 ))))
1135 }
1136 } else {
1137 Ok(Some(Ratio::from_integer(BigInt::from_biguint(
1138 Sign::Plus,
1139 int,
1140 ))))
1141 }
1142 }
1143}
1144/// Reads data from `R` passing each line to an [`Evaluator`] to be evaluated.
1145#[cfg(feature = "std")]
1146#[derive(Debug)]
1147pub struct EvalIter<R> {
1148 /// Reader that contains input data.
1149 reader: R,
1150 /// Buffer that is used by `reader` to read
1151 /// data into.
1152 input_buffer: Vec<u8>,
1153 /// Cache of stored results.
1154 cache: Cache<Ratio<BigInt>, 8>,
1155 /// Result of the previous expression.
1156 prev: Option<Ratio<BigInt>>,
1157 /// Buffer used by [`Evaluator`] to process
1158 /// sub-expressions.
1159 exp_buffer: Vec<Ratio<BigInt>>,
1160 /// Random number generator.
1161 #[cfg(feature = "rand")]
1162 rng: ThreadRng,
1163}
1164#[cfg(feature = "std")]
1165impl<R> EvalIter<R> {
1166 /// Creates a new `EvalIter`.
1167 #[cfg(feature = "rand")]
1168 #[inline]
1169 pub fn new(reader: R) -> Self {
1170 Self {
1171 reader,
1172 input_buffer: Vec::new(),
1173 cache: Cache::new(),
1174 prev: None,
1175 exp_buffer: Vec::new(),
1176 rng: rand::rng(),
1177 }
1178 }
1179 /// Creates a new `EvalIter`.
1180 #[cfg(any(doc, not(feature = "rand")))]
1181 #[inline]
1182 pub fn new(reader: R) -> Self {
1183 Self {
1184 reader,
1185 input_buffer: Vec::new(),
1186 cache: Cache::new(),
1187 prev: None,
1188 exp_buffer: Vec::new(),
1189 }
1190 }
1191}
1192#[cfg(feature = "std")]
1193extern crate std;
1194#[cfg(feature = "std")]
1195use std::io::{BufRead, Error};
1196/// Error returned from [`EvalIter`] when an expression has an error.
1197#[cfg(feature = "std")]
1198#[derive(Debug)]
1199pub enum E {
1200 /// Error containing [`Error`] which is returned
1201 /// from [`EvalIter`] when reading from the supplied
1202 /// [`BufRead`]er.
1203 Error(Error),
1204 /// Error containing [`LangErr`] which is returned
1205 /// from [`EvalIter`] when evaluating a single expression.
1206 LangErr(LangErr),
1207}
1208#[cfg(feature = "std")]
1209impl Display for E {
1210 #[inline]
1211 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1212 match *self {
1213 Self::Error(ref e) => e.fmt(f),
1214 Self::LangErr(ref e) => e.fmt(f),
1215 }
1216 }
1217}
1218#[cfg(feature = "std")]
1219use crate::lending_iterator::LendingIterator;
1220#[cfg(feature = "std")]
1221impl<R> LendingIterator for EvalIter<R>
1222where
1223 R: BufRead,
1224{
1225 type Item<'a>
1226 = Result<O<'a>, E>
1227 where
1228 Self: 'a;
1229 #[inline]
1230 fn lend_next(&mut self) -> Option<Result<O<'_>, E>> {
1231 self.input_buffer.clear();
1232 self.exp_buffer.clear();
1233 self.reader
1234 .read_until(b'\n', &mut self.input_buffer)
1235 .map_or_else(
1236 |e| Some(Err(E::Error(e))),
1237 |c| {
1238 if c == 0 {
1239 None
1240 } else {
1241 Evaluator::new(
1242 self.input_buffer.as_slice(),
1243 &mut self.cache,
1244 &mut self.prev,
1245 &mut self.exp_buffer,
1246 #[cfg(feature = "rand")]
1247 &mut self.rng,
1248 )
1249 .evaluate()
1250 .map_or_else(
1251 |e| Some(Err(E::LangErr(e))),
1252 |o| match o {
1253 Empty(_) | Eval(_) | Store(_) => Some(Ok(o)),
1254 Exit => None,
1255 },
1256 )
1257 }
1258 },
1259 )
1260 }
1261}