# `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`](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`, <space>, <tab>, <line feed>, <carriage return>, 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. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0). * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT). at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted 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. ### 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).