calc_rational 2.1.0

CLI calculator for rational numbers.
Documentation
# `calc_rational`

[<img alt="git" src="https://git.philomathiclife.com/badges/calc_rational.svg" height="20">](https://git.philomathiclife.com/calc_rational/log.html)
[<img alt="crates.io" src="https://img.shields.io/crates/v/calc_rational.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/calc_rational)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-calc_rational-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/calc_rational/latest/calc_lib/)

`calc_rational` consists of a binary crate `calc` and a library crate
[`calc_lib`](https://docs.rs/calc_rational/latest/calc_lib). `calc` is a CLI calculator for basic
rational number arithmetic using standard operator precedence and associativity. Internally, it is
based on [`Ratio<T>`](https://docs.rs/num/latest/num/rational/struct.Ratio.html)
and [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html).

## Calc in action

```bash
[zack@laptop ~]$ calc
2.71828^0^3.14159 + -1!
> 0
s
> 0
@^0
> 1
s
> 1
@/3 * 3
> 1
s
> 1
|@2 - 9|^(1 - 2*3)
> 1/32768
s
> 1/32768

> 0.000030517578125
round(@, 3)
> 0
round(@, 6)
> 31/1000000

> 0.000031
2/3
> 2/3

> 0.666666667
rand()
> 939435294927814822
rand(1+9,10!)
> 2660936
1+4 mod 2 + 1
> 2
-5 mod 2
> 1
-5 mod -2
> 1
5 mod -2
> 1
9^0.5
> 3
(4/9)^(-1/2)
> 3/2
q
[zack@laptop ~]$
```

## Expressions

The following are the list of expressions in descending order of precedence:
  1. number literals, `@`, `()`, `||`, `round()`, `rand()`
  2. `!`
  3. `^`
  4. `-` (unary negation operator)
  5. `*`, `/`, `mod`
  6. `+`, `-`

All binary operators are left-associative sans `^` which is right-associative.

Any expression is allowed to be enclosed in `()`. Note that parentheses are purely for grouping expressions;
in particular, you cannot use them to represent multiplication (e.g., `4(2)` is grammatically incorrect and
will result in an error message).

Any expression is allowed to be enclosed in `||`. This unary operator represents absolute value.

`!` is the factorial operator. Due to its high precedence, something like *-i!^j!* for *i, j ∈ ℕ* is
the same thing as *-((i!)^(j!))*. If the expression preceding it does not evaluate to a non-negative integer,
then an error will be displayed. Spaces and tabs are *not* ignored; so `1 !` is grammatically incorrect and
will result in an error message.

`^` is the exponentiation operator. The expression left of the operator can evaluate to any rational number;
however the expression right of the operator must evaluate to an integer or ±1/2 unless the expression on
the left evaluates to `0` or `1`. In the event of the former, the expression right of the operator must evaluate
to a non-negative rational number. In the event of the latter, the expression right of the operator can evaluate to
any rational number. Note that `0^0` is defined to be 1. When the operand right of `^` evaluates to ±1/2, then
the left operand must be the square of a rational number.

The unary operator `-` represents negation.

The operators `*` and `/` represent multiplication and division respectively. Expressions right of `/`
must evaluate to any non-zero rational number; otherwise an error will be displayed.

The binary operator `mod` represents modulo such that *n mod m = r = n - m\*q* for *n,q ∈ ℤ, m ∈ ℤ\\{0}, and r ∈ ℕ*
where *r* is the minimum non-negative solution.

The binary operators `+` and `-` represent addition and subtraction respectively.

With the aforementioned exception of `!`, all spaces and tabs before and after operators are ignored.

## Round expression

`round(expression, digit)` rounds `expression` to `digit`-number of fractional digits. An error will
be displayed if called incorrectly.

## Rand expression

`rand(expression, expression)` generates a random 64-bit integer inclusively between the passed expressions.
An error will be displayed if called incorrectly. `rand()` generates a random 64-bit integer.

## Numbers

A number literal is a non-empty sequence of digits or a non-empty sequence of digits immediately followed by `.`
which is immediately followed by a non-empty sequence of digits (e.g., `134.901`). This means that number
literals represent precisely all rational numbers that are equivalent to a ratio of a non-negative integer to
a positive integer whose sole prime factors are 2 or 5. To represent all other rational numbers, the unary
operator `-` and binary operator `/` must be used.

## Empty expression

The empty expression (i.e., expression that at most only consists of spaces and tabs) will return
the result from the previous non-(empty/store) expression in *decimal* form using the minimum number of digits.
In the event an infinite number of digits is required, it will be rounded to 9 fractional digits using normal rounding
rules first.

## Store expression

To store the result of the previous non-(empty/store) expression, one simply passes `s`. In addition to storing the
result which will subsequently be available via `@`, it displays the result. At most 8 results can be stored at once;
at which point, results that are stored overwrite the oldest result.

## Recall expression

`@` is used to recall previously stored results. It can be followed by any *digit* from `1` to `8`.
If such a digit does not immediately follow it, then it will be interpreted as if there were a `1`.
`@i` returns the *i*-th most-previous stored result where *i ∈ {1, 2, 3, 4, 5, 6, 7, 8}*.
Note that spaces and tabs are *not* ignored so `@ 2` is grammatically incorrect and will result in an error message.
As emphasized, it does not work on expressions; so both `@@` and `@(1)` are grammatically incorrect.

## Character encoding

All inputs must only contain the ASCII encoding of the following Unicode scalar values: `0`-`9`, `.`, `+`, `-`,
`*`, `/`, `^`, `!`, `mod`, `|`, `(`, `)`, `round`, `rand`, `,`, `@`, `s`, &lt;space&gt;, &lt;tab&gt;,
&lt;line feed&gt;, &lt;carriage return&gt;, and `q`. Any other byte sequences are grammatically incorrect and will
lead to an error message.

## Errors

Errors due to a language violation (e.g., dividing by `0`) manifest into an error message. `panic!`s
and [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)s caused by writing to the global
standard output stream lead to program abortion. On OpenBSD-stable when compiled with the `priv_sep` feature,
it will error if [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) errors with the promise of `"stdio"`.

## Exiting

`q` with any number of spaces and tabs before and after or sending `EOF` will cause the program to terminate.

## Minimum Supported Rust Version (MSRV)

This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that
update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV
will be updated.

MSRV changes will correspond to a SemVer minor version bump.

## SemVer Policy

* All on-by-default features of this library are covered by SemVer
* MSRV is considered exempt from SemVer as noted above

## License

Licensed under either of

* Apache License, Version 2.0 ([LICENSE-APACHE](https://www.apache.org/licenses/LICENSE-2.0))
* MIT license ([LICENSE-MIT](https://opensource.org/licenses/MIT))

at your option.

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you,
as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Before any PR is sent, `cargo clippy` and `cargo t` should be run _for each possible combination of "features"_
using stable Rust. One easy way to achieve this is by building `ci` and invoking it with no commands in the
`calc_rational` directory or sub-directories. You can fetch `ci` via `git clone https://git.philomathiclife.com/repos/ci`,
and it can be built with `cargo build --release`. Additionally,
`RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure documentation can be built.

### Status

This package will be actively maintained until it is deemed “feature complete”.
There are really only two properties that will always be true. First,
the grammar that generates a “reasonable” superset of the language will
be an unambiguous context-free grammar with expression precedence and binary operator
associativity embedded within. Last, the language will only deal with the field of
rational numbers.

The crate is only tested on `x86_64-unknown-linux-gnu` and `x86_64-unknown-openbsd` targets, but it should work
on most platforms.

#### Formal language specification

For a more precise specification of the “calc language”, one can read the
[calc language specification](https://git.philomathiclife.com/calc_rational/lang.pdf).