calc_rational 0.2.0

CLI calculator for rational numbers.
Documentation
# calc_rational  
  
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
@6
There are only 4 previous results.
s 1
Invalid store expression. A store expression must be of the extended regex form: ^[ \t]*s[ \t]*$.
q a
Invalid quit expression. A quit expression must be of the extended regex form: ^[ \t]*q[ \t]*$.
a
Missing terminal expression at position 0. A terminal expression is a decimal literal expression, recall expression, absolute value expression, parenthetical expression, or a round expression.
|1
Invalid absolute value expression ending at position 2. An absolute value expression is an addition expression enclosed in '||'.
(2
Invalid parenthetical expression ending at position 2. A parenthetical expression is an addition expression enclosed in '()'.
round(1,10)
Invalid round expression ending at position 9. A round expression is of the form 'round(<add expression>, digit)'
1/(2-2)
Division by zero ending at position 7.
2^(1/2)
Non-integer exponent with a base that was not 0 or 1 ending at position 7.
0^-1
Non-negative exponent with a base of 0 ending at position 4.
(-1)!
Factorial of a rational number that was not a non-negative integer ending at position 5.
4.
Invalid decimal literal expression ending at position 2. A decimal literal expression must be of the extended regex form: [0-9]+(\.[0-9]+)?.
1+2 a
Trailing symbols starting at position 4.
q
[zack@laptop ~]$
```
  
## Expressions  
  
The following are the list of expressions in descending order of precedence:  
  1. number literals, `@`, `()`, `||`, `round()`  
  2. `!`  
  3. `^`  
  4. `-` (unary negation operator)  
  5. `*`, `/`  
  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 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.  
  
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 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.  
  
## 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 possible ratios of non-negative integers to non-negative integers who 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 and non-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 and non-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`, `.`, `+`, `-`,
`*`, `/`, `^`, `!`, `|`, `(`, `)`, `round`, `,`, `@`, `s`, &lt;space&gt;, &lt;tab&gt;, &lt;line feed&gt;, &lt;carriage return&gt;,
`q`, and `d`. 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.  
  
## Exiting  
  
`q` with any number of spaces and tabs before and after will cause the program to terminate.  
  
### 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 crates are only tested on the `x86_64-unknown-linux-gnu` target, but
they should work on any [Tier 1 with Host Tools](https://doc.rust-lang.org/beta/rustc/platform-support.html)
target. Note one must be aware of the ASCII encoding requirement. In particular there are platforms
(e.g., Windows) where the default text encoding is not a superset of ASCII.  
  
### Installing  
  
```bash
[zack@laptop ~]$ cargo install calc_rational
    Updating crates.io index
  Installing calc_rational v0.2.0
   Compiling autocfg v1.1.0
   Compiling num-traits v0.2.15
   Compiling num-integer v0.1.45
   Compiling num-bigint v0.4.3
   Compiling num-rational v0.4.1
   Compiling calc_rational v0.1.2
    Finished release [optimized] target(s) in 6.09s
  Installing /home/zack/.cargo/bin/calc
   Installed package `calc_rational v0.2.0` (executable `calc`)
```
  
### Building and testing  
  
```bash
[zack@laptop ~]$ git clone https://git.philomathiclife.com/repos/calc_rational
Cloning into 'calc_rational'...
[zack@laptop ~]$ cd calc_rational/
[zack@laptop calc_rational]$ cargo build --release
    Updating crates.io index
   Compiling autocfg v1.1.0
   Compiling num-traits v0.2.15
   Compiling num-integer v0.1.45
   Compiling num-bigint v0.4.3
   Compiling num-rational v0.4.1
   Compiling calc_rational v0.2.0 (/home/zack/calc_rational)
    Finished release [optimized] target(s) in 8.25s
[zack@laptop calc_rational]$ cargo t
   Compiling autocfg v1.1.0
   Compiling num-traits v0.2.15
   Compiling num-integer v0.1.45
   Compiling num-bigint v0.4.3
   Compiling num-rational v0.4.1
   Compiling calc_rational v0.2.0 (/home/zack/calc_rational)
    Finished test [unoptimized + debuginfo] target(s) in 2.40s
     Running unittests src/lib.rs (target/debug/deps/calc_lib-381715bd94efc206)

running 23 tests
test cache::tests::test_get ... ok
test cache::tests::test_index ... ok
test cache::tests::test_is_empty ... ok
test cache::tests::test_get_unsafe ... ok
test cache::tests::test_len ... ok
test cache::tests::test_new ... ok
test tests::exit ... ok
test cache::tests::test_index_panic - should panic ... ok
test tests::abs ... ok
test cache::tests::test_push ... ok
test tests::factorial ... ok
test tests::neg ... ok
test tests::recall_expression ... ok
test tests::par ... ok
test tests::empty ... ok
test tests::eval ... ok
test tests::store ... ok
test tests::exp ... ok
test tests::add ... ok
test tests::mult ... ok
test tests::round ... ok
test tests::term ... ok
test tests::number_literal ... ok

test result: ok. 23 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/calc-42b0141dee0be10c)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests calc_lib

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```
  
#### 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).