calc_rational

CLI calculator for rational numbers.
git clone https://git.philomathiclife.com/repos/calc_rational
Log | Files | Refs | README

README.md (8902B)


      1 # `calc_rational`
      2 
      3 [<img alt="git" src="https://git.philomathiclife.com/badges/calc_rational.svg" height="20">](https://git.philomathiclife.com/calc_rational/log.html)
      4 [<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)
      5 [<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/)
      6 
      7 `calc_rational` consists of a binary crate `calc` and a library crate
      8 [`calc_lib`](https://docs.rs/calc_rational/latest/calc_lib). `calc` is a CLI calculator for basic
      9 rational number arithmetic using standard operator precedence and associativity. Internally, it is
     10 based on [`Ratio<T>`](https://docs.rs/num/latest/num/rational/struct.Ratio.html)
     11 and [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html).
     12 
     13 ## Calc in action
     14 
     15 ```bash
     16 [zack@laptop ~]$ calc
     17 2.71828^0^3.14159 + -1!
     18 > 0
     19 s
     20 > 0
     21 @^0
     22 > 1
     23 s
     24 > 1
     25 @/3 * 3
     26 > 1
     27 s
     28 > 1
     29 |@2 - 9|^(1 - 2*3)
     30 > 1/32768
     31 s
     32 > 1/32768
     33 
     34 > 0.000030517578125
     35 round(@, 3)
     36 > 0
     37 round(@, 6)
     38 > 31/1000000
     39 
     40 > 0.000031
     41 2/3
     42 > 2/3
     43 
     44 > 0.666666667
     45 rand()
     46 > 939435294927814822
     47 rand(1+9,10!)
     48 > 2660936
     49 1+4 mod 2 + 1
     50 > 2
     51 -5 mod 2
     52 > 1
     53 -5 mod -2
     54 > 1
     55 5 mod -2
     56 > 1
     57 9^0.5
     58 > 3
     59 (4/9)^(-1/2)
     60 > 3/2
     61 q
     62 [zack@laptop ~]$
     63 ```
     64 
     65 ## Expressions
     66 
     67 The following are the list of expressions in descending order of precedence:
     68   1. number literals, `@`, `()`, `||`, `round()`, `rand()`
     69   2. `!`
     70   3. `^`
     71   4. `-` (unary negation operator)
     72   5. `*`, `/`, `mod`
     73   6. `+`, `-`
     74 
     75 All binary operators are left-associative sans `^` which is right-associative.
     76 
     77 Any expression is allowed to be enclosed in `()`. Note that parentheses are purely for grouping expressions;
     78 in particular, you cannot use them to represent multiplication (e.g., `4(2)` is grammatically incorrect and
     79 will result in an error message).
     80 
     81 Any expression is allowed to be enclosed in `||`. This unary operator represents absolute value.
     82 
     83 `!` is the factorial operator. Due to its high precedence, something like *-i!^j!* for *i, j ∈ ℕ* is
     84 the same thing as *-((i!)^(j!))*. If the expression preceding it does not evaluate to a non-negative integer,
     85 then an error will be displayed. Spaces and tabs are *not* ignored; so `1 !` is grammatically incorrect and
     86 will result in an error message.
     87 
     88 `^` is the exponentiation operator. The expression left of the operator can evaluate to any rational number;
     89 however the expression right of the operator must evaluate to an integer or ±1/2 unless the expression on
     90 the left evaluates to `0` or `1`. In the event of the former, the expression right of the operator must evaluate
     91 to a non-negative rational number. In the event of the latter, the expression right of the operator can evaluate to
     92 any rational number. Note that `0^0` is defined to be 1. When the operand right of `^` evaluates to ±1/2, then
     93 the left operand must be the square of a rational number.
     94 
     95 The unary operator `-` represents negation.
     96 
     97 The operators `*` and `/` represent multiplication and division respectively. Expressions right of `/`
     98 must evaluate to any non-zero rational number; otherwise an error will be displayed.
     99 
    100 The binary operator `mod` represents modulo such that *n mod m = r = n - m\*q* for *n,q ∈ ℤ, m ∈ ℤ\\{0}, and r ∈ ℕ*
    101 where *r* is the minimum non-negative solution.
    102 
    103 The binary operators `+` and `-` represent addition and subtraction respectively.
    104 
    105 With the aforementioned exception of `!`, all spaces and tabs before and after operators are ignored.
    106 
    107 ## Round expression
    108 
    109 `round(expression, digit)` rounds `expression` to `digit`-number of fractional digits. An error will
    110 be displayed if called incorrectly.
    111 
    112 ## Rand expression
    113 
    114 `rand(expression, expression)` generates a random 64-bit integer inclusively between the passed expressions.
    115 An error will be displayed if called incorrectly. `rand()` generates a random 64-bit integer.
    116 
    117 ## Numbers
    118 
    119 A number literal is a non-empty sequence of digits or a non-empty sequence of digits immediately followed by `.`
    120 which is immediately followed by a non-empty sequence of digits (e.g., `134.901`). This means that number
    121 literals represent precisely all rational numbers that are equivalent to a ratio of a non-negative integer to
    122 a positive integer whose sole prime factors are 2 or 5. To represent all other rational numbers, the unary
    123 operator `-` and binary operator `/` must be used.
    124 
    125 ## Empty expression
    126 
    127 The empty expression (i.e., expression that at most only consists of spaces and tabs) will return
    128 the result from the previous non-(empty/store) expression in *decimal* form using the minimum number of digits.
    129 In the event an infinite number of digits is required, it will be rounded to 9 fractional digits using normal rounding
    130 rules first.
    131 
    132 ## Store expression
    133 
    134 To store the result of the previous non-(empty/store) expression, one simply passes `s`. In addition to storing the
    135 result which will subsequently be available via `@`, it displays the result. At most 8 results can be stored at once;
    136 at which point, results that are stored overwrite the oldest result.
    137 
    138 ## Recall expression
    139 
    140 `@` is used to recall previously stored results. It can be followed by any *digit* from `1` to `8`.
    141 If such a digit does not immediately follow it, then it will be interpreted as if there were a `1`.
    142 `@i` returns the *i*-th most-previous stored result where *i ∈ {1, 2, 3, 4, 5, 6, 7, 8}*.
    143 Note that spaces and tabs are *not* ignored so `@ 2` is grammatically incorrect and will result in an error message.
    144 As emphasized, it does not work on expressions; so both `@@` and `@(1)` are grammatically incorrect.
    145 
    146 ## Character encoding
    147 
    148 All inputs must only contain the ASCII encoding of the following Unicode scalar values: `0`-`9`, `.`, `+`, `-`,
    149 `*`, `/`, `^`, `!`, `mod`, `|`, `(`, `)`, `round`, `rand`, `,`, `@`, `s`, &lt;space&gt;, &lt;tab&gt;,
    150 &lt;line feed&gt;, &lt;carriage return&gt;, and `q`. Any other byte sequences are grammatically incorrect and will
    151 lead to an error message.
    152 
    153 ## Errors
    154 
    155 Errors due to a language violation (e.g., dividing by `0`) manifest into an error message. `panic!`s
    156 and [`io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html)s caused by writing to the global
    157 standard output stream lead to program abortion. On OpenBSD-stable when compiled with the `priv_sep` feature,
    158 it will error if [`pledge(2)`](https://man.openbsd.org/amd64/pledge.2) errors with the promise of `"stdio"`.
    159 
    160 ## Exiting
    161 
    162 `q` with any number of spaces and tabs before and after or sending `EOF` will cause the program to terminate.
    163 
    164 ## Minimum Supported Rust Version (MSRV)
    165 
    166 This will frequently be updated to be the same as stable. Specifically, any time stable is updated and that
    167 update has "useful" features or compilation no longer succeeds (e.g., due to new compiler lints), then MSRV
    168 will be updated.
    169 
    170 MSRV changes will correspond to a SemVer minor version bump.
    171 
    172 ## SemVer Policy
    173 
    174 * All on-by-default features of this library are covered by SemVer
    175 * MSRV is considered exempt from SemVer as noted above
    176 
    177 ## License
    178 
    179 Licensed under either of
    180 
    181 * Apache License, Version 2.0 ([LICENSE-APACHE](https://www.apache.org/licenses/LICENSE-2.0))
    182 * MIT license ([LICENSE-MIT](https://opensource.org/licenses/MIT))
    183 
    184 at your option.
    185 
    186 ## Contribution
    187 
    188 Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you,
    189 as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
    190 
    191 Before any PR is sent, `cargo clippy` and `cargo t` should be run _for each possible combination of "features"_
    192 using stable Rust. One easy way to achieve this is by building `ci` and invoking it with no commands in the
    193 `calc_rational` directory or sub-directories. You can fetch `ci` via `git clone https://git.philomathiclife.com/repos/ci`,
    194 and it can be built with `cargo build --release`. Additionally,
    195 `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features` should be run to ensure documentation can be built.
    196 
    197 ### Status
    198 
    199 This package will be actively maintained until it is deemed “feature complete”.
    200 There are really only two properties that will always be true. First,
    201 the grammar that generates a “reasonable” superset of the language will
    202 be an unambiguous context-free grammar with expression precedence and binary operator
    203 associativity embedded within. Last, the language will only deal with the field of
    204 rational numbers.
    205 
    206 The crate is only tested on `x86_64-unknown-linux-gnu` and `x86_64-unknown-openbsd` targets, but it should work
    207 on most platforms.
    208 
    209 #### Formal language specification
    210 
    211 For a more precise specification of the “calc language”, one can read the
    212 [calc language specification](https://git.philomathiclife.com/calc_rational/lang.pdf).