├── .github ├── ISSUE_TEMPLATE └── PULL_REQUEST_TEMPLATE ├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml └── src ├── bench.rs ├── bin.rs ├── error.rs ├── lib.rs ├── parse.rs ├── token.rs └── value.rs /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | **Reproduction**: [describe how you are able to reproduce ("trigger") this bug/issue.] 2 | 3 | **Expected behavior**: [describe the behavior you would expect the repro to yield.] 4 | 5 | **Actual behavior**: [describe the actual behavior, which is presented through the repro.]. 6 | 7 | **Build information**: [output of `rustc -V`, `git rev-parse HEAD`, `qemu-i386 -version`, `uname -a`, etc.] 8 | 9 | **Blocking/related**: [issues or PRs blocking or being related to this issue.] 10 | 11 | **Misc**: [optional: for other relevant information that should be known or cannot be described in the other fields.] 12 | 13 | ------ 14 | 15 | _If the above does not fit the nature of the issue feel free to modify it._ 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | **Problem**: [describe the problem you try to solve with this PR.] 2 | 3 | **Solution**: [describe carefully what you change by this PR.] 4 | 5 | **Changes introduced by this pull request**: 6 | 7 | - [...] 8 | - [...] 9 | - [...] 10 | 11 | **Drawbacks**: [if any, describe the drawbacks of this pull request.] 12 | 13 | **TODOs**: [what is not done yet.] 14 | 15 | **Fixes**: [what issues this fixes.] 16 | 17 | **State**: [the state of this PR, e.g. WIP, ready, etc.] 18 | 19 | **Blocking/related**: [issues or PRs blocking or being related to this issue.] 20 | 21 | **Other**: [optional: for other relevant information that should be known or cannot be described in the other fields.] 22 | 23 | ------ 24 | 25 | _The above template is not necessary for smaller PRs._ 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | # Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | /target/ 12 | **/*.rs.bk 13 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - test 4 | 5 | build:linux: 6 | image: rust:latest 7 | stage: build 8 | before_script: 9 | - apt-get update -qq 10 | - apt-get install -qq build-essential curl git 11 | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly 12 | - rustup component add rustfmt-preview 13 | script: 14 | - cargo build --release 15 | - cargo clippy -- -D warnings 16 | - cargo fmt -- --check 17 | artifacts: 18 | paths: 19 | - target/release/calc 20 | cache: 21 | paths: 22 | - cargo/ 23 | - target/ 24 | 25 | build:redox: 26 | image: redoxos/redoxer 27 | stage: build 28 | before_script: 29 | - apt-get update 30 | script: 31 | - redoxer build --verbose 32 | cache: 33 | paths: 34 | - cargo/ 35 | - target/ 36 | 37 | rustdoc:linux: 38 | image: rust:latest 39 | stage: build 40 | script: 41 | - cargo doc 42 | artifacts: 43 | paths: 44 | - target/doc 45 | 46 | test:linux: 47 | image: rust:latest 48 | stage: test 49 | before_script: 50 | - apt-get update -qq 51 | - apt-get install -qq build-essential curl git 52 | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly 53 | dependencies: 54 | - build:linux 55 | script: 56 | - cargo test 57 | 58 | test:redox: 59 | image: redoxos/redoxer 60 | stage: test 61 | before_script: 62 | - apt-get update 63 | dependencies: 64 | - build:redox 65 | script: 66 | - redoxer test +nightly --verbose 67 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - nightly 4 | sudo: false 5 | notifications: 6 | email: false 7 | cache: cargo 8 | before_script: 9 | - rustup component add rustfmt-preview 10 | script: 11 | - cargo fmt -- --check 12 | - cargo build 13 | - cargo test 14 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ansi_term" 7 | version = "0.12.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 10 | dependencies = [ 11 | "winapi", 12 | ] 13 | 14 | [[package]] 15 | name = "arrayvec" 16 | version = "0.5.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 19 | 20 | [[package]] 21 | name = "atty" 22 | version = "0.2.14" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 25 | dependencies = [ 26 | "hermit-abi", 27 | "libc", 28 | "winapi", 29 | ] 30 | 31 | [[package]] 32 | name = "autocfg" 33 | version = "1.0.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.2.1" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 42 | 43 | [[package]] 44 | name = "bitflags" 45 | version = "2.4.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 48 | 49 | [[package]] 50 | name = "bytecount" 51 | version = "0.6.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" 54 | 55 | [[package]] 56 | name = "calculate" 57 | version = "0.8.0" 58 | dependencies = [ 59 | "atty", 60 | "clap", 61 | "decimal", 62 | "num", 63 | "rand", 64 | "redox_liner", 65 | ] 66 | 67 | [[package]] 68 | name = "cc" 69 | version = "1.0.67" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" 72 | 73 | [[package]] 74 | name = "cfg-if" 75 | version = "1.0.0" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 78 | 79 | [[package]] 80 | name = "clap" 81 | version = "2.34.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 84 | dependencies = [ 85 | "ansi_term", 86 | "atty", 87 | "bitflags 1.2.1", 88 | "strsim", 89 | "textwrap", 90 | "unicode-width", 91 | "vec_map", 92 | ] 93 | 94 | [[package]] 95 | name = "decimal" 96 | version = "2.1.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "5a8ab77e91baeb15034c3be91e87bff4665c9036216148e4996d9a9f5792114d" 99 | dependencies = [ 100 | "bitflags 1.2.1", 101 | "cc", 102 | "libc", 103 | ] 104 | 105 | [[package]] 106 | name = "either" 107 | version = "1.6.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 110 | 111 | [[package]] 112 | name = "getrandom" 113 | version = "0.1.16" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" 116 | dependencies = [ 117 | "cfg-if", 118 | "libc", 119 | "wasi", 120 | ] 121 | 122 | [[package]] 123 | name = "hermit-abi" 124 | version = "0.1.18" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 127 | dependencies = [ 128 | "libc", 129 | ] 130 | 131 | [[package]] 132 | name = "itertools" 133 | version = "0.8.2" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" 136 | dependencies = [ 137 | "either", 138 | ] 139 | 140 | [[package]] 141 | name = "libc" 142 | version = "0.2.153" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 145 | 146 | [[package]] 147 | name = "libredox" 148 | version = "0.0.2" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" 151 | dependencies = [ 152 | "bitflags 2.4.2", 153 | "libc", 154 | "redox_syscall", 155 | ] 156 | 157 | [[package]] 158 | name = "num" 159 | version = "0.4.1" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" 162 | dependencies = [ 163 | "num-bigint", 164 | "num-complex", 165 | "num-integer", 166 | "num-iter", 167 | "num-rational", 168 | "num-traits", 169 | ] 170 | 171 | [[package]] 172 | name = "num-bigint" 173 | version = "0.4.4" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" 176 | dependencies = [ 177 | "autocfg", 178 | "num-integer", 179 | "num-traits", 180 | ] 181 | 182 | [[package]] 183 | name = "num-complex" 184 | version = "0.4.4" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" 187 | dependencies = [ 188 | "num-traits", 189 | ] 190 | 191 | [[package]] 192 | name = "num-integer" 193 | version = "0.1.45" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 196 | dependencies = [ 197 | "autocfg", 198 | "num-traits", 199 | ] 200 | 201 | [[package]] 202 | name = "num-iter" 203 | version = "0.1.43" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" 206 | dependencies = [ 207 | "autocfg", 208 | "num-integer", 209 | "num-traits", 210 | ] 211 | 212 | [[package]] 213 | name = "num-rational" 214 | version = "0.4.1" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" 217 | dependencies = [ 218 | "autocfg", 219 | "num-bigint", 220 | "num-integer", 221 | "num-traits", 222 | ] 223 | 224 | [[package]] 225 | name = "num-traits" 226 | version = "0.2.17" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 229 | dependencies = [ 230 | "autocfg", 231 | ] 232 | 233 | [[package]] 234 | name = "numtoa" 235 | version = "0.1.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 238 | 239 | [[package]] 240 | name = "ppv-lite86" 241 | version = "0.2.10" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 244 | 245 | [[package]] 246 | name = "proc-macro2" 247 | version = "1.0.78" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 250 | dependencies = [ 251 | "unicode-ident", 252 | ] 253 | 254 | [[package]] 255 | name = "quote" 256 | version = "1.0.35" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 259 | dependencies = [ 260 | "proc-macro2", 261 | ] 262 | 263 | [[package]] 264 | name = "rand" 265 | version = "0.7.3" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 268 | dependencies = [ 269 | "getrandom", 270 | "libc", 271 | "rand_chacha", 272 | "rand_core", 273 | "rand_hc", 274 | ] 275 | 276 | [[package]] 277 | name = "rand_chacha" 278 | version = "0.2.2" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 281 | dependencies = [ 282 | "ppv-lite86", 283 | "rand_core", 284 | ] 285 | 286 | [[package]] 287 | name = "rand_core" 288 | version = "0.5.1" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 291 | dependencies = [ 292 | "getrandom", 293 | ] 294 | 295 | [[package]] 296 | name = "rand_hc" 297 | version = "0.2.0" 298 | source = "registry+https://github.com/rust-lang/crates.io-index" 299 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 300 | dependencies = [ 301 | "rand_core", 302 | ] 303 | 304 | [[package]] 305 | name = "redox_liner" 306 | version = "0.5.2" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "63c3dc5996de1508c9dad60461f6a5b9611d9c31610c29d3ff4ec97df408671c" 309 | dependencies = [ 310 | "bytecount", 311 | "itertools", 312 | "strip-ansi-escapes", 313 | "termion", 314 | "unicode-width", 315 | ] 316 | 317 | [[package]] 318 | name = "redox_syscall" 319 | version = "0.4.1" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 322 | dependencies = [ 323 | "bitflags 1.2.1", 324 | ] 325 | 326 | [[package]] 327 | name = "redox_termios" 328 | version = "0.1.3" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" 331 | 332 | [[package]] 333 | name = "strip-ansi-escapes" 334 | version = "0.1.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" 337 | dependencies = [ 338 | "vte", 339 | ] 340 | 341 | [[package]] 342 | name = "strsim" 343 | version = "0.8.0" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 346 | 347 | [[package]] 348 | name = "termion" 349 | version = "2.0.3" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "c4648c7def6f2043b2568617b9f9b75eae88ca185dbc1f1fda30e95a85d49d7d" 352 | dependencies = [ 353 | "libc", 354 | "libredox", 355 | "numtoa", 356 | "redox_termios", 357 | ] 358 | 359 | [[package]] 360 | name = "textwrap" 361 | version = "0.11.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 364 | dependencies = [ 365 | "unicode-width", 366 | ] 367 | 368 | [[package]] 369 | name = "unicode-ident" 370 | version = "1.0.12" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 373 | 374 | [[package]] 375 | name = "unicode-width" 376 | version = "0.1.8" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 379 | 380 | [[package]] 381 | name = "utf8parse" 382 | version = "0.2.1" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 385 | 386 | [[package]] 387 | name = "vec_map" 388 | version = "0.8.2" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 391 | 392 | [[package]] 393 | name = "vte" 394 | version = "0.10.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" 397 | dependencies = [ 398 | "arrayvec", 399 | "utf8parse", 400 | "vte_generate_state_changes", 401 | ] 402 | 403 | [[package]] 404 | name = "vte_generate_state_changes" 405 | version = "0.1.1" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" 408 | dependencies = [ 409 | "proc-macro2", 410 | "quote", 411 | ] 412 | 413 | [[package]] 414 | name = "wasi" 415 | version = "0.9.0+wasi-snapshot-preview1" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 418 | 419 | [[package]] 420 | name = "winapi" 421 | version = "0.3.9" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 424 | dependencies = [ 425 | "winapi-i686-pc-windows-gnu", 426 | "winapi-x86_64-pc-windows-gnu", 427 | ] 428 | 429 | [[package]] 430 | name = "winapi-i686-pc-windows-gnu" 431 | version = "0.4.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 434 | 435 | [[package]] 436 | name = "winapi-x86_64-pc-windows-gnu" 437 | version = "0.4.0" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 440 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Hunter Goldstein "] 3 | categories = [ 4 | "science", 5 | "parsing", 6 | ] 7 | description = "Rust library for parsing and processing arithmetic expressions" 8 | homepage = "https://gitlab.redox-os.org/redox-os/calc" 9 | include = [ 10 | "**/*.rs", 11 | "Cargo.toml", 12 | ] 13 | keywords = [ 14 | "math", 15 | "calculator", 16 | ] 17 | license-file = "LICENSE" 18 | name = "calculate" 19 | readme = "README.md" 20 | repository = "https://gitlab.redox-os.org/redox-os/calc" 21 | version = "0.8.0" 22 | edition = "2018" 23 | 24 | [[bin]] 25 | name = "calc" 26 | path = "src/bin.rs" 27 | 28 | [dependencies] 29 | atty = "0.2" 30 | clap = "2.33" 31 | decimal = { version = "2.1", default-features = false } 32 | redox_liner = "0.5.2" 33 | num = "0.4" 34 | rand = "0.7" 35 | 36 | [lib] 37 | name = "calc" 38 | path = "src/lib.rs" 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Redox OS Developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # calc 2 | [![Build Status](https://travis-ci.org/redox-os/calc.svg?branch=master)](https://travis-ci.org/redox-os/calc) 3 | [![crates.io](https://meritbadge.herokuapp.com/calculate)](https://crates.io/crates/calculate) 4 | 5 | 6 | `calc` is a Rust library for tokenizing and evaluating arithmetic expressions with a command line application of the same name included. 7 | 8 | **NOTE**: The name of the project, binary, and library are `calc` but the package name is `calculate`. This will remain depending on if this project can acquire the `calc` crate which is currently being squatted on. 9 | 10 | # Usage 11 | 12 | ## As a Library 13 | 14 | Add `calc` as a dependency in your `Cargo.toml`: 15 | ```toml 16 | [dependencies] 17 | calculate = "0.5.*" 18 | ``` 19 | 20 | Then make use of the library functions: 21 | ```rust 22 | extern crate calc; 23 | 24 | use calc::eval; 25 | use std::io::{self, BufRead, stdout, stdin, Write}; 26 | 27 | fn main() { 28 | let stdout = stdout(); 29 | let mut stdout = stdout.lock(); 30 | let stdin = stdin(); 31 | for line in stdin.lock().lines() { 32 | match line.unwrap().trim() { 33 | "" => (), 34 | "exit" => break, 35 | s => writeln!(stdout, "{}", eval(s)).unwrap(), 36 | } 37 | } 38 | } 39 | ``` 40 | 41 | ## As an Executable 42 | 43 | ```bash 44 | $ cargo install calculate 45 | ... 46 | $ calc 47 | ``` 48 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | error_on_line_overflow = false 2 | max_width = 80 3 | wrap_comments = true 4 | -------------------------------------------------------------------------------- /src/bench.rs: -------------------------------------------------------------------------------- 1 | extern crate test; 2 | 3 | use super::eval; 4 | use test::Bencher; 5 | 6 | const SEXPR: &str = "4 ** (88 * 9 / (59 >> 3)) + 8*(10%(2/(2-3))"; 7 | const MEXPR: &str = "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))"; 8 | const LEXPR: &str = "log 4 * ln(e ** 9)/ln(e²) + log(20 - log 10)"; 9 | const TEXPR: &str = 10 | "sin(pi/3)**2 + cos(pi/3)² + (tan(0) * atanh(0)/(acos(0)<<2))"; 11 | 12 | #[bench] 13 | fn small_expr(bench: &mut Bencher) { 14 | bench.iter(|| eval(SEXPR)); 15 | } 16 | 17 | #[bench] 18 | fn medium_expr(bench: &mut Bencher) { 19 | bench.iter(|| eval(MEXPR)); 20 | } 21 | 22 | #[bench] 23 | fn log_expr(bench: &mut Bencher) { 24 | bench.iter(|| eval(LEXPR)); 25 | } 26 | 27 | #[bench] 28 | fn trig_expr(bench: &mut Bencher) { 29 | bench.iter(|| eval(TEXPR)); 30 | } 31 | -------------------------------------------------------------------------------- /src/bin.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::process::exit; 3 | 4 | use std::io::{self, stdout, BufRead, Write}; 5 | 6 | use calc::{eval, eval_polish, eval_polish_with_env, eval_with_env, CalcError}; 7 | 8 | use clap::{App, Arg}; 9 | 10 | use liner::Context; 11 | 12 | const PROMPT: &str = "[]> "; 13 | 14 | pub fn prompt(out: &mut W) -> io::Result<()> { 15 | write!(out, "{}", PROMPT)?; 16 | out.flush() 17 | } 18 | 19 | pub enum RuntimeError { 20 | Calc(CalcError), 21 | IO(io::Error), 22 | } 23 | 24 | impl From for RuntimeError { 25 | fn from(data: CalcError) -> RuntimeError { 26 | RuntimeError::Calc(data) 27 | } 28 | } 29 | 30 | impl From for RuntimeError { 31 | fn from(data: io::Error) -> RuntimeError { 32 | RuntimeError::IO(data) 33 | } 34 | } 35 | 36 | impl fmt::Display for RuntimeError { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | match *self { 39 | RuntimeError::Calc(ref c) => write!(f, "calc: {}", c), 40 | RuntimeError::IO(ref e) => write!(f, "calc: {}", e), 41 | } 42 | } 43 | } 44 | 45 | pub fn calc() -> Result<(), RuntimeError> { 46 | let stdout = stdout(); 47 | let mut stdout = stdout.lock(); 48 | 49 | let matches = App::new("calc") 50 | .version(env!("CARGO_PKG_VERSION")) 51 | .author("Hunter Goldstein ") 52 | .about("Floating point calculator") 53 | .set_term_width(80) 54 | .arg(Arg::with_name("polish") 55 | .short("p") 56 | .long("polish") 57 | .help("Parse expressions using polish notation versus infix notation")) 58 | .arg(Arg::with_name("expr") 59 | .help("Expression to evaluate by this program. If this argument is missing, enter interactive mode.") 60 | .multiple(true) 61 | .value_name("EXPR")) 62 | .get_matches(); 63 | 64 | // Check if the polish notation flag was given. 65 | let polish = matches.is_present("polish"); 66 | 67 | macro_rules! eval { 68 | ($expr:expr) => { 69 | match polish { 70 | true => eval_polish($expr)?, 71 | false => eval($expr)?, 72 | } 73 | }; 74 | } 75 | macro_rules! eval_with_env { 76 | ($expr:expr, $env:expr) => { 77 | match polish { 78 | true => eval_polish_with_env($expr, $env)?, 79 | false => eval_with_env($expr, $env)?, 80 | } 81 | }; 82 | } 83 | 84 | match matches.values_of("expr") { 85 | Some(values) => { 86 | writeln!( 87 | stdout, 88 | "{}", 89 | eval!(&values.fold(String::new(), |acc, s| acc + s)), 90 | )?; 91 | } 92 | None => { 93 | if atty::is(atty::Stream::Stdin) { 94 | let mut con = Context::new(); 95 | let mut ans = None; 96 | loop { 97 | let line = con.read_line( 98 | liner::Prompt::from(PROMPT), 99 | None, 100 | &mut liner::BasicCompleter::new( 101 | Vec::::default(), 102 | ), 103 | )?; 104 | match line.trim() { 105 | "" => (), 106 | "exit" => break, 107 | s => { 108 | let mut env = 109 | calc::parse::DefaultEnvironment::with_ans(ans); 110 | let evaluated = eval_with_env!(s, &mut env); 111 | writeln!(stdout, "{}", evaluated)?; 112 | ans = Some(evaluated); 113 | } 114 | } 115 | con.history.push(line.into())?; 116 | } 117 | } else { 118 | let stdin = io::stdin(); 119 | let lock = stdin.lock(); 120 | for line in lock.lines() { 121 | writeln!(stdout, "{}", eval!(&line?))?; 122 | } 123 | } 124 | } 125 | } 126 | Ok(()) 127 | } 128 | 129 | fn main() { 130 | let code = match calc() { 131 | Ok(()) => 0, 132 | Err(e) => { 133 | println!("{}", e); 134 | 1 135 | } 136 | }; 137 | exit(code) 138 | } 139 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use num::bigint::ParseBigIntError; 2 | use std::fmt; 3 | 4 | /// Represents a partial computation that can be captured as part of an 5 | /// error message. 6 | #[derive(Debug, PartialEq)] 7 | pub enum PartialComp { 8 | ToFloat(String), 9 | Unary { 10 | op: String, 11 | arg: String, 12 | }, 13 | Binary { 14 | op: String, 15 | lhs: String, 16 | rhs: String, 17 | }, 18 | } 19 | 20 | impl PartialComp { 21 | pub fn unary(op: T, arg: U) -> Self 22 | where 23 | T: ToString, 24 | U: ToString, 25 | { 26 | PartialComp::Unary { 27 | op: op.to_string(), 28 | arg: arg.to_string(), 29 | } 30 | } 31 | 32 | pub fn binary(op: T, lhs: &U, rhs: &V) -> Self 33 | where 34 | T: ToString, 35 | U: ToString, 36 | V: ToString, 37 | { 38 | PartialComp::Binary { 39 | op: op.to_string(), 40 | lhs: lhs.to_string(), 41 | rhs: rhs.to_string(), 42 | } 43 | } 44 | } 45 | 46 | impl fmt::Display for PartialComp { 47 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 48 | match *self { 49 | PartialComp::ToFloat(ref arg) => write!(f, "{} as float", arg), 50 | PartialComp::Unary { ref op, ref arg } => { 51 | write!(f, "{} {}", op, arg) 52 | } 53 | PartialComp::Binary { 54 | ref op, 55 | ref lhs, 56 | ref rhs, 57 | } => write!(f, "{} {} {}", lhs, op, rhs), 58 | } 59 | } 60 | } 61 | 62 | #[derive(Debug, PartialEq)] 63 | pub enum CalcError { 64 | BadTypes(PartialComp), 65 | DivideByZero, 66 | InvalidNumber(String), 67 | InvalidOperator(char), 68 | UnrecognizedToken(String), 69 | UnexpectedToken(String, &'static str), 70 | UnknownAtom(String), 71 | UnexpectedEndOfInput, 72 | UnmatchedParenthesis, 73 | WouldOverflow(PartialComp), 74 | WouldTruncate(PartialComp), 75 | RecursionLimitReached, 76 | ImpossibleDice, 77 | MissingAns, 78 | } 79 | 80 | use CalcError::*; 81 | 82 | impl fmt::Display for CalcError { 83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 84 | match *self { 85 | BadTypes(ref comp) => { 86 | write!(f, "expression '{}' is not well typed", comp) 87 | } 88 | DivideByZero => write!(f, "attempted to divide by zero"), 89 | InvalidNumber(ref number) => { 90 | write!(f, "invalid number: {}", number) 91 | } 92 | InvalidOperator(ref c) => write!(f, "invalid operator: {}", c), 93 | UnrecognizedToken(ref token) => { 94 | write!(f, "unrecognized token: {}", token) 95 | } 96 | UnexpectedToken(ref token, ref kind) => { 97 | write!(f, "expected {} token, got {} instead", kind, token) 98 | } 99 | UnknownAtom(ref atom) => { 100 | write!(f, "unknown variable or function '{}'", atom) 101 | } 102 | WouldOverflow(ref comp) => { 103 | write!(f, "expression '{}' would overflow", comp) 104 | } 105 | WouldTruncate(ref comp) => { 106 | write!(f, "expression '{}' would be truncated", comp) 107 | } 108 | UnexpectedEndOfInput => write!(f, "unexpected end of input"), 109 | UnmatchedParenthesis => write!(f, "unmatched patenthesis"), 110 | RecursionLimitReached => write!(f, "recursion limit reached"), 111 | ImpossibleDice => write!(f, "impossible dice"), 112 | MissingAns => write!(f, "no `ans` from a previous computation"), 113 | } 114 | } 115 | } 116 | 117 | impl From for CalcError { 118 | fn from(data: ParseBigIntError) -> CalcError { 119 | CalcError::InvalidNumber(data.to_string()) 120 | } 121 | } 122 | 123 | impl From for String { 124 | fn from(data: CalcError) -> String { 125 | format!("{}", data) 126 | } 127 | } 128 | 129 | impl std::error::Error for CalcError {} 130 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(test, feature(test))] 2 | 3 | #[cfg(test)] 4 | mod bench; 5 | 6 | mod error; 7 | pub mod parse; 8 | mod token; 9 | pub mod value; 10 | 11 | pub use error::CalcError; 12 | pub use value::Value; 13 | 14 | /// Evalulates a regular mathematical expression. 15 | pub fn eval(input: &str) -> Result { 16 | let mut env = parse::DefaultEnvironment::default(); 17 | token::tokenize(input).and_then(|x| parse::parse(&x, &mut env)) 18 | } 19 | 20 | /// Evalulates a regular mathematical expression, with extra environment 21 | /// variables. 22 | pub fn eval_with_env(input: &str, env: &mut E) -> Result 23 | where 24 | E: parse::Environment, 25 | { 26 | token::tokenize(input).and_then(|x| parse::parse(&x, env)) 27 | } 28 | 29 | /// Evalulates mathematical expressions that are written in Polish Notation. 30 | /// 31 | /// Polish Notation defines that a string of operators are given at the 32 | /// beginning of the 33 | /// expression, as prefixes, and are followed by a string of numbers to apply 34 | /// each operation 35 | /// to. Polish Notation enables writing mathematical expressions that don't 36 | /// require grouping 37 | /// via parenthesis. It's also referred to as Prefix Notation, or Normal Polish 38 | /// Notation (NPN). 39 | /// 40 | /// # Examples 41 | /// 42 | /// - `+ * 3 4 5` is equivalent to `3 * 4 + 5` 43 | /// - `+ / * 5 3 2 * + 1 3 5` is equivalent to `((5 * 3) / 2) + ((1 + 3) * 5)` 44 | pub fn eval_polish(input: &str) -> Result { 45 | let mut env = parse::DefaultEnvironment::default(); 46 | token::tokenize_polish(input).and_then(|x| parse::parse(&x, &mut env)) 47 | } 48 | 49 | /// Evalulates mathematical expressions that are written in Polish Notation, 50 | /// with extra 51 | /// environment variables. 52 | /// 53 | /// Polish Notation defines that a string of operators are given at the 54 | /// beginning of the 55 | /// expression, as prefixes, and are followed by a string of numbers to apply 56 | /// each operation 57 | /// to. Polish Notation enables writing mathematical expressions that don't 58 | /// require grouping 59 | /// via parenthesis. It's also referred to as prefix notation, or Normal Polish 60 | /// Notation (NPN). 61 | /// 62 | /// # Examples 63 | /// 64 | /// - `+ * 3 4 5` is equivalent to `3 * 4 + 5` 65 | /// - `+ / * 5 3 2 * + 1 3 5` is equivalent to `((5 * 3) / 2) + ((1 + 3) * 5)` 66 | pub fn eval_polish_with_env( 67 | input: &str, 68 | env: &mut E, 69 | ) -> Result 70 | where 71 | E: parse::Environment, 72 | { 73 | token::tokenize_polish(input).and_then(|x| parse::parse(&x, env)) 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::*; 79 | use decimal::d128; 80 | 81 | #[test] 82 | fn basics() { 83 | let cases = vec![ 84 | (" 1 + 1", Value::dec(2)), 85 | (" 4 * 7 - 14", Value::dec(14)), 86 | (" 2 << 16 ", Value::dec(131072)), 87 | ( 88 | " ((4 * 18) % 17) / 3", 89 | Value::Float(d128!(4.0) / d128!(3.0)), 90 | ), 91 | ("2²³²", Value::dec(4096)), 92 | ("4 ^ 3 ^ 2 ^ 3 ^ 4 ^ 2", Value::dec(0)), 93 | ("3 << (4 >> 2)", Value::dec(6)), 94 | ("~0", Value::dec(-1)), 95 | // ("cos pi + sin (tau * (3 / 4))", Value::Float(d128!(-2.0))), 96 | ("~~5", Value::dec(5)), 97 | ("4*(4)", Value::dec(16)), 98 | ]; 99 | for (input, expected) in cases { 100 | assert_eq!(eval(input), Ok(expected)); 101 | } 102 | } 103 | 104 | #[test] 105 | fn polish() { 106 | let cases = vec![ 107 | (" + 1 1", Value::dec(2)), 108 | (" - * 4 7 14", Value::dec(14)), 109 | (" << 2 16", Value::dec(131072)), 110 | (" / % * 4 18 17 3", Value::Float(d128!(4.0) / d128!(3.0))), 111 | ("* + 1 3 5", Value::dec(20)), 112 | ("+ / * 5 3 2 * + 1 3 5", Value::Float(d128!(27.5))), 113 | ("^ ^ ^ ^ ^ 4 3 2 3 4 2", Value::dec(0)), 114 | ("<< 3 >> 4 2", Value::dec(6)), 115 | ]; 116 | for (input, expected) in cases { 117 | assert_eq!(eval_polish(input), Ok(expected)); 118 | } 119 | } 120 | 121 | #[test] 122 | fn random() { 123 | let cases = vec![ 124 | ( 125 | "((15 * 10) - 26 * 19 - 30 / ((57 * 79 + 93 / 87 / 47))) / 8", 126 | Value::Float(d128!(-43.00083277394169075309321854399588)), 127 | ), 128 | ( 129 | "(3 << 6) * 7 + (40 / 3)", 130 | Value::Float(d128!(1357.333333333333333333333333333333)), 131 | ), 132 | ( 133 | "(21 & (5) ^ (20 & 81)) / (25 << 3)", 134 | Value::Float(d128!(0.105)), 135 | ), 136 | ( 137 | "(79 & 14) * ((3) - 76 + 67 / (62) - (85 ^ (7 - (32) >> 52)))", 138 | Value::Float(d128!(197.1290322580645161290322580645161)), 139 | ), 140 | ]; 141 | 142 | for (input, expected) in cases { 143 | assert_eq!(eval(input), Ok(expected)); 144 | } 145 | } 146 | 147 | #[test] 148 | fn recursion() { 149 | let cases = vec![ 150 | ( 151 | "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((", 152 | Err(CalcError::RecursionLimitReached) 153 | ), 154 | ]; 155 | 156 | for (input, expected) in cases { 157 | assert_eq!(eval(input), expected); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/parse.rs: -------------------------------------------------------------------------------- 1 | use crate::error::CalcError; 2 | use crate::token::*; 3 | use crate::value::{Value, IR}; 4 | use decimal::d128; 5 | use rand::Rng; 6 | 7 | const RECURSION_LIMIT: usize = 10; 8 | 9 | /// Represents an environment for evaluating a mathematical expression 10 | pub trait Environment { 11 | /// Look up the arity of an atom: 12 | /// - Variables have an implicit arity of zero 13 | /// - This library currently does not support varadic functions 14 | /// - If a symbol is not defined, return None 15 | fn arity(&self, atom: &str) -> Option; 16 | 17 | /// Resolve an atom given the name of the atom and some number of 18 | /// arguments 19 | /// Precondition: `args.len() == self.arity(atom)` 20 | fn resolve( 21 | &mut self, 22 | atom: &str, 23 | args: &[Value], 24 | ) -> Result; 25 | 26 | fn add_recursion_level(&mut self); 27 | fn subtract_recursion_level(&mut self); 28 | fn get_recursion_level(&self) -> usize; 29 | 30 | fn ans(&self) -> &Option; 31 | } 32 | 33 | fn d_expr(token_list: &[Token], env: &mut E) -> Result 34 | where 35 | E: Environment, 36 | { 37 | if !token_list.is_empty() && token_list[0] == Token::BitWiseNot { 38 | let mut e = d_expr(&token_list[1..], env)?; 39 | e.value = (!e.value)?; 40 | e.tokens += 1; 41 | return Ok(e); 42 | } 43 | 44 | let mut e1 = e_expr(token_list, env)?; 45 | let mut index = e1.tokens; 46 | 47 | while index < token_list.len() { 48 | match token_list[index] { 49 | Token::BitWiseAnd => { 50 | let e2 = e_expr(&token_list[index + 1..], env)?; 51 | e1.value = (e1.value & e2.value)?; 52 | e1.tokens += e2.tokens + 1; 53 | } 54 | Token::BitWiseOr => { 55 | let e2 = e_expr(&token_list[index + 1..], env)?; 56 | e1.value = (e1.value | e2.value)?; 57 | e1.tokens += e2.tokens + 1; 58 | } 59 | Token::BitWiseXor => { 60 | let e2 = e_expr(&token_list[index + 1..], env)?; 61 | e1.value = (e1.value ^ e2.value)?; 62 | e1.tokens += e2.tokens + 1; 63 | } 64 | Token::BitWiseLShift => { 65 | let e2 = e_expr(&token_list[index + 1..], env)?; 66 | e1.value = (e1.value << e2.value)?; 67 | e1.tokens += e2.tokens + 1; 68 | } 69 | Token::BitWiseRShift => { 70 | let e2 = e_expr(&token_list[index + 1..], env)?; 71 | e1.value = (e1.value >> e2.value)?; 72 | e1.tokens += e2.tokens + 1; 73 | } 74 | Token::Number(ref n) => { 75 | return Err(CalcError::UnexpectedToken( 76 | n.to_string(), 77 | "operator", 78 | )); 79 | } 80 | _ => break, 81 | }; 82 | index = e1.tokens; 83 | } 84 | Ok(e1) 85 | } 86 | // Addition and subtraction 87 | fn e_expr(token_list: &[Token], env: &mut E) -> Result 88 | where 89 | E: Environment, 90 | { 91 | let mut t1 = t_expr(token_list, env)?; 92 | let mut index = t1.tokens; 93 | 94 | while index < token_list.len() { 95 | match token_list[index] { 96 | Token::Plus => { 97 | let t2 = t_expr(&token_list[index + 1..], env)?; 98 | t1.value = (t1.value + t2.value)?; 99 | t1.tokens += t2.tokens + 1; 100 | } 101 | Token::Minus => { 102 | let t2 = t_expr(&token_list[index + 1..], env)?; 103 | t1.value = (t1.value - t2.value)?; 104 | t1.tokens += t2.tokens + 1; 105 | } 106 | Token::Number(ref n) => { 107 | return Err(CalcError::UnexpectedToken( 108 | n.to_string(), 109 | "operator", 110 | )) 111 | } 112 | _ => break, 113 | }; 114 | index = t1.tokens; 115 | } 116 | Ok(t1) 117 | } 118 | 119 | // Multiplication and division 120 | fn t_expr(token_list: &[Token], env: &mut E) -> Result 121 | where 122 | E: Environment, 123 | { 124 | let mut f1 = f_expr(token_list, env)?; 125 | let mut index = f1.tokens; 126 | 127 | while index < token_list.len() { 128 | match token_list[index] { 129 | Token::Multiply => { 130 | let f2 = f_expr(&token_list[index + 1..], env)?; 131 | f1.value = (f1.value * f2.value)?; 132 | f1.tokens += f2.tokens + 1; 133 | } 134 | Token::Divide => { 135 | let f2 = f_expr(&token_list[index + 1..], env)?; 136 | f1.value = (f1.value / f2.value)?; 137 | f1.tokens += f2.tokens + 1; 138 | } 139 | Token::Modulo => { 140 | let f2 = f_expr(&token_list[index + 1..], env)?; 141 | f1.value = (f1.value % f2.value)?; 142 | f1.tokens += f2.tokens + 1; 143 | } 144 | Token::Dice => { 145 | let f2 = f_expr(&token_list[index + 1..], env)?; 146 | let dice_rolls: i32 = f1.value.as_float()?.into(); 147 | let dice_max: i32 = f2.value.as_float()?.into(); 148 | if dice_rolls < 1 || dice_max < 1 { 149 | return Err(CalcError::ImpossibleDice); 150 | } 151 | let mut dice_result = 0; 152 | let mut rng = rand::thread_rng(); 153 | for _i in 0..dice_rolls { 154 | dice_result += rng.gen_range(1, dice_max + 1); 155 | } 156 | f1.value = Value::dec(dice_result); 157 | f1.tokens += f2.tokens + 1; 158 | } 159 | Token::Number(ref n) => { 160 | return Err(CalcError::UnexpectedToken( 161 | n.to_string(), 162 | "operator", 163 | )); 164 | } 165 | _ => break, 166 | } 167 | index = f1.tokens; 168 | } 169 | Ok(f1) 170 | } 171 | 172 | // Exponentiation 173 | fn f_expr(token_list: &[Token], env: &mut E) -> Result 174 | where 175 | E: Environment, 176 | { 177 | let mut g1 = g_expr(token_list, env)?; // was g1 178 | let mut index = g1.tokens; 179 | let token_len = token_list.len(); 180 | while index < token_len { 181 | match token_list[index] { 182 | Token::Exponent => { 183 | let f = f_expr(&token_list[index + 1..], env)?; 184 | g1.value = g1.value.pow(f.value)?; 185 | g1.tokens += f.tokens + 1; 186 | } 187 | Token::Square => { 188 | g1.value = (g1.value.clone() * g1.value)?; 189 | g1.tokens += 1; 190 | } 191 | Token::Cube => { 192 | g1.value = ((g1.value.clone() * g1.value.clone())? * g1.value)?; 193 | g1.tokens += 1; 194 | } 195 | Token::Number(ref n) => { 196 | return Err(CalcError::UnexpectedToken( 197 | n.to_string(), 198 | "operator", 199 | )); 200 | } 201 | _ => break, 202 | } 203 | index = g1.tokens; 204 | } 205 | Ok(g1) 206 | } 207 | 208 | // Numbers, parenthesized expressions, and atoms 209 | fn g_expr(token_list: &[Token], env: &mut E) -> Result 210 | where 211 | E: Environment, 212 | { 213 | if !token_list.is_empty() { 214 | match token_list[0] { 215 | Token::Ans => match env.ans() { 216 | Some(v) => Ok(IR::new(v.clone(), 1)), 217 | None => Err(CalcError::MissingAns), 218 | }, 219 | Token::Number(ref n) => Ok(IR::new(n.clone(), 1)), 220 | Token::Atom(ref s) => { 221 | if let Some(nargs) = env.arity(s) { 222 | let mut args: Vec = Vec::new(); 223 | let mut start = 1; 224 | for _ in 0..nargs { 225 | let ir = g_expr(&token_list[start..], env)?; 226 | start += ir.tokens; 227 | args.push(ir.value); 228 | } 229 | let res = env.resolve(s, &args); 230 | Ok(IR::new(res?, start)) 231 | } else { 232 | Err(CalcError::UnknownAtom(s.clone())) 233 | } 234 | } 235 | Token::Minus => { 236 | if token_list.len() >= 2 { 237 | if let Token::Number(ref n) = token_list[1] { 238 | Ok(IR::new(-n.clone(), 2)) 239 | } else { 240 | let mut ir = d_expr(&token_list[1..], env)?; 241 | ir.value = -ir.value; 242 | ir.tokens += 1; 243 | Ok(ir) 244 | } 245 | } else { 246 | Err(CalcError::UnexpectedEndOfInput) 247 | } 248 | } 249 | Token::OpenParen => { 250 | env.add_recursion_level(); 251 | if env.get_recursion_level() > RECURSION_LIMIT { 252 | Err(CalcError::RecursionLimitReached)? 253 | } 254 | let mut ir = d_expr(&token_list[1..], env)?; 255 | let close_paren = ir.tokens + 1; 256 | if close_paren < token_list.len() { 257 | match token_list[close_paren] { 258 | Token::CloseParen => { 259 | env.subtract_recursion_level(); 260 | ir.tokens = close_paren + 1; 261 | Ok(ir) 262 | } 263 | _ => Err(CalcError::UnexpectedToken( 264 | token_list[close_paren].to_string(), 265 | ")", 266 | )), 267 | } 268 | } else { 269 | Err(CalcError::UnmatchedParenthesis) 270 | } 271 | } 272 | _ => Err(CalcError::UnexpectedToken( 273 | token_list[0].to_string(), 274 | "number", 275 | )), 276 | } 277 | } else { 278 | Err(CalcError::UnexpectedEndOfInput) 279 | } 280 | } 281 | 282 | #[derive(Default)] 283 | pub struct DefaultEnvironment { 284 | recursion_level: usize, 285 | ans: Option, 286 | } 287 | 288 | impl DefaultEnvironment { 289 | pub fn with_ans(ans: Option) -> DefaultEnvironment { 290 | DefaultEnvironment { 291 | recursion_level: 0, 292 | ans, 293 | } 294 | } 295 | } 296 | 297 | impl Environment for DefaultEnvironment { 298 | fn arity(&self, atom: &str) -> Option { 299 | match atom { 300 | "pi" | "tau" | "e" => Some(0), 301 | "log" => Some(1), 302 | "ln" => Some(1), 303 | "abs" => Some(1), 304 | "sin" => Some(1), 305 | "cos" => Some(1), 306 | "tan" => Some(1), 307 | "asin" => Some(1), 308 | "acos" => Some(1), 309 | "atan" => Some(1), 310 | "sinh" => Some(1), 311 | "cosh" => Some(1), 312 | "tanh" => Some(1), 313 | "asinh" => Some(1), 314 | "acosh" => Some(1), 315 | "atanh" => Some(1), 316 | _ => None, 317 | } 318 | } 319 | 320 | fn resolve( 321 | &mut self, 322 | atom: &str, 323 | args: &[Value], 324 | ) -> Result { 325 | match atom { 326 | "pi" => { 327 | Ok(Value::Float(d128!(3.14159265358979323846264338327950288))) 328 | } 329 | "tau" => Ok(Value::Float( 330 | d128!(3.14159265358979323846264338327950288) * d128!(2.0), 331 | )), 332 | "e" => { 333 | Ok(Value::Float(d128!(2.71828182845904523536028747135266249))) 334 | } 335 | "log" => args[0].log(), 336 | "ln" => args[0].ln(), 337 | "abs" => Ok(args[0].abs()), 338 | "sin" => args[0].sin(), 339 | "cos" => args[0].cos(), 340 | "tan" => args[0].tan(), 341 | "asin" => args[0].asin(), 342 | "acos" => args[0].acos(), 343 | "atan" => args[0].atan(), 344 | "sinh" => args[0].sinh(), 345 | "cosh" => args[0].cosh(), 346 | "tanh" => args[0].tanh(), 347 | "asinh" => args[0].asinh(), 348 | "acosh" => args[0].acosh(), 349 | "atanh" => args[0].atanh(), 350 | // "tan" => Ok(Value::Float(args[0].as_float().tan())), 351 | _ => Err(CalcError::UnknownAtom(atom.to_owned())), 352 | } 353 | } 354 | 355 | fn get_recursion_level(&self) -> usize { 356 | self.recursion_level 357 | } 358 | 359 | fn add_recursion_level(&mut self) { 360 | self.recursion_level += 1; 361 | } 362 | fn subtract_recursion_level(&mut self) { 363 | self.recursion_level -= 1; 364 | } 365 | 366 | fn ans(&self) -> &Option { 367 | &self.ans 368 | } 369 | } 370 | 371 | pub fn parse(tokens: &[Token], env: &mut E) -> Result 372 | where 373 | E: Environment, 374 | { 375 | d_expr(tokens, env).map(|answer| answer.value) 376 | } 377 | 378 | #[cfg(test)] 379 | mod tests { 380 | 381 | use super::*; 382 | 383 | #[test] 384 | fn unary_minus() { 385 | let expr = [ 386 | Token::Minus, 387 | Token::Number(Value::dec(1)), 388 | Token::Plus, 389 | Token::Number(Value::dec(1)), 390 | ]; 391 | let expected = Value::dec(0); 392 | let mut env = DefaultEnvironment::default(); 393 | assert_eq!(super::parse(&expr, &mut env), Ok(expected)); 394 | } 395 | 396 | #[test] 397 | fn dice_parse() { 398 | let expr = [ 399 | Token::Number(Value::dec(3)), 400 | Token::Dice, 401 | Token::Number(Value::dec(6)), 402 | ]; 403 | let mut env = DefaultEnvironment::default(); 404 | let out = super::parse(&expr, &mut env); 405 | let out_float = out.unwrap().as_float().unwrap(); 406 | assert!(out_float >= d128!(3.0) && out_float <= d128!(18.0)); 407 | } 408 | 409 | #[test] 410 | fn ans_calculation() { 411 | let expr = [Token::Ans, Token::Multiply, Token::Number(Value::dec(3))]; 412 | let expected = Value::dec(12); 413 | let mut env = DefaultEnvironment::with_ans(Some(Value::dec(4))); 414 | assert_eq!(super::parse(&expr, &mut env), Ok(expected)); 415 | } 416 | 417 | #[test] 418 | fn function_binding() { 419 | let expr = [ 420 | Token::Atom("log".into()), 421 | Token::Number(Value::Float(d128!(4.0))), 422 | Token::Divide, 423 | Token::Atom("log".into()), 424 | Token::Number(Value::Float(d128!(2.0))), 425 | ]; 426 | let expected = Value::Float(d128!(2.0)); 427 | let mut env = DefaultEnvironment::default(); 428 | assert_eq!(super::parse(&expr, &mut env), Ok(expected)); 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use crate::error::CalcError; 2 | use crate::error::CalcError::*; 3 | use crate::value::{Integral, Value}; 4 | use decimal::d128; 5 | use num::Num; 6 | use std::fmt; 7 | use std::iter::Peekable; 8 | 9 | /// Tokens used for parsing an arithmetic expression 10 | #[derive(Debug, Clone, PartialEq)] 11 | pub enum Token { 12 | Ans, 13 | Plus, 14 | Minus, 15 | Divide, 16 | Multiply, 17 | Exponent, 18 | Square, 19 | Cube, 20 | BitWiseAnd, 21 | BitWiseOr, 22 | BitWiseXor, 23 | BitWiseNot, 24 | BitWiseRShift, 25 | BitWiseLShift, 26 | Modulo, 27 | OpenParen, 28 | CloseParen, 29 | Dice, 30 | Number(Value), 31 | Atom(String), 32 | } 33 | 34 | impl fmt::Display for Token { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | match *self { 37 | Token::Ans => write!(f, "Ans"), 38 | Token::Plus => write!(f, "Plus"), 39 | Token::Minus => write!(f, "Minus"), 40 | Token::Divide => write!(f, "Divide"), 41 | Token::Multiply => write!(f, "Multiply"), 42 | Token::Exponent => write!(f, "Exponent"), 43 | Token::Square => write!(f, "Square"), 44 | Token::Cube => write!(f, "Cube"), 45 | Token::BitWiseAnd => write!(f, "And"), 46 | Token::BitWiseOr => write!(f, "Or"), 47 | Token::BitWiseXor => write!(f, "Xor"), 48 | Token::BitWiseNot => write!(f, "Not"), 49 | Token::BitWiseRShift => write!(f, "RShift"), 50 | Token::BitWiseLShift => write!(f, "LShift"), 51 | Token::Modulo => write!(f, "Modulo"), 52 | Token::OpenParen => write!(f, "OpenParen"), 53 | Token::CloseParen => write!(f, "CloseParen"), 54 | Token::Dice => write!(f, "Dice"), 55 | Token::Number(ref n) => write!(f, "'{}'", n), 56 | Token::Atom(ref s) => write!(f, "'{}'", s), 57 | } 58 | } 59 | } 60 | 61 | enum OperatorState { 62 | PotentiallyIncomplete, 63 | Complete, 64 | NotAnOperator, 65 | } 66 | 67 | trait IsOperator { 68 | fn is_operator(&self) -> bool; 69 | } 70 | 71 | impl IsOperator for char { 72 | fn is_operator(&self) -> bool { 73 | matches!( 74 | self, 75 | '+' | '-' 76 | | '/' 77 | | '^' 78 | | '²' 79 | | '³' 80 | | '&' 81 | | '|' 82 | | '~' 83 | | '>' 84 | | '%' 85 | | '(' 86 | | ')' 87 | | '*' 88 | | '<' 89 | | 'd' 90 | ) 91 | } 92 | } 93 | 94 | trait CheckOperator { 95 | fn check_operator(self) -> OperatorState; 96 | } 97 | 98 | impl CheckOperator for char { 99 | fn check_operator(self) -> OperatorState { 100 | match self { 101 | '+' | '-' | '/' | '^' | '²' | '³' | '&' | '|' | '~' | '%' | '(' 102 | | ')' | 'd' => OperatorState::Complete, 103 | '*' | '<' | '>' => OperatorState::PotentiallyIncomplete, 104 | _ => OperatorState::NotAnOperator, 105 | } 106 | } 107 | } 108 | 109 | trait OperatorMatch { 110 | fn operator_type(self) -> Option; 111 | } 112 | 113 | impl OperatorMatch for [char; 2] { 114 | fn operator_type(self) -> Option { 115 | if self == ['*', '*'] { 116 | Some(Token::Exponent) 117 | } else if self == ['<', '<'] { 118 | Some(Token::BitWiseLShift) 119 | } else if self == ['>', '>'] { 120 | Some(Token::BitWiseRShift) 121 | } else { 122 | None 123 | } 124 | } 125 | } 126 | 127 | impl OperatorMatch for char { 128 | fn operator_type(self) -> Option { 129 | match self { 130 | '+' => Some(Token::Plus), 131 | '-' => Some(Token::Minus), 132 | '/' => Some(Token::Divide), 133 | '*' => Some(Token::Multiply), 134 | '^' => Some(Token::BitWiseXor), 135 | '²' => Some(Token::Square), 136 | '³' => Some(Token::Cube), 137 | '&' => Some(Token::BitWiseAnd), 138 | '|' => Some(Token::BitWiseOr), 139 | '~' => Some(Token::BitWiseNot), 140 | '%' => Some(Token::Modulo), 141 | '(' => Some(Token::OpenParen), 142 | ')' => Some(Token::CloseParen), 143 | 'd' => Some(Token::Dice), 144 | _ => None, 145 | } 146 | } 147 | } 148 | 149 | /// Tokenizes a mathematical expression written written with the standard infix 150 | /// notation. 151 | /// 152 | /// Returns a vector of `Token`s in the infix format, if the supplied 153 | /// expression is valid. This 154 | /// vector can then be pased into the `parse` function to be evaluated. 155 | pub fn tokenize(input: &str) -> Result, CalcError> { 156 | let mut tokens = Vec::with_capacity(input.len()); 157 | let mut chars = input.chars().peekable(); 158 | 159 | while let Some(&c) = chars.peek() { 160 | match c.check_operator() { 161 | OperatorState::Complete => { 162 | tokens.push(c.operator_type().ok_or(InvalidOperator(c))?); 163 | chars.next(); 164 | } 165 | OperatorState::PotentiallyIncomplete => { 166 | chars.next(); 167 | match chars.peek() { 168 | Some(&next_char) if next_char.is_operator() => { 169 | if let Some(op) = [c, next_char].operator_type() { 170 | tokens.push(op); 171 | chars.next(); 172 | } else if next_char == '(' { 173 | // This condition cannot be identified on the 174 | // current tokenize algorithm and is treated 175 | // as an exceptional case. 176 | tokens.push( 177 | c.operator_type().ok_or(InvalidOperator(c))?, 178 | ); 179 | } else { 180 | return Err(CalcError::InvalidOperator(next_char)); 181 | } 182 | } 183 | _ => { 184 | tokens 185 | .push(c.operator_type().ok_or(InvalidOperator(c))?); 186 | } 187 | } 188 | } 189 | OperatorState::NotAnOperator => { 190 | if c.is_whitespace() { 191 | chars.next(); 192 | } else if c.is_alphabetic() { 193 | tokens.push(consume_ans_or_atom(&mut chars)); 194 | } else if c.is_ascii_hexdigit() || c == '.' { 195 | tokens.push(Token::Number(consume_number(&mut chars)?)); 196 | } else { 197 | let token_string = consume_until_new_token(&mut chars); 198 | return Err(CalcError::UnrecognizedToken(token_string)); 199 | } 200 | } 201 | } 202 | } 203 | Ok(tokens) 204 | } 205 | 206 | /// Tokenizes a mathematical expression written with a polish (prefix) notation. 207 | /// 208 | /// Returns a vector of `Token`s in the infix format, if the supplied 209 | /// expression is valid. This 210 | /// vector can then be pased into the `parse` function to be evaluated. 211 | pub fn tokenize_polish(input: &str) -> Result, CalcError> { 212 | // NOTE: This function isn't as efficient as it could be. For sake of 213 | // compatibility with the 214 | // existing infix parser, this function stores and re-arranges tokens in 215 | // the infix format; 216 | // adding extra parenthesis so that the order of operations aren't followed. 217 | 218 | // A temporary enum that is used for collecting polish values before they 219 | // are converted into 220 | // tokens for the infix format. 221 | #[derive(Debug)] 222 | enum PolishValue { 223 | Ans, 224 | Atom(String), 225 | Number(Value), 226 | } 227 | impl From for Token { 228 | fn from(polish: PolishValue) -> Token { 229 | match polish { 230 | PolishValue::Ans => Token::Ans, 231 | PolishValue::Atom(atom) => Token::Atom(atom), 232 | PolishValue::Number(val) => Token::Number(val), 233 | } 234 | } 235 | } 236 | impl From for PolishValue { 237 | fn from(token: Token) -> PolishValue { 238 | match token { 239 | Token::Ans => PolishValue::Ans, 240 | Token::Atom(atom) => PolishValue::Atom(atom), 241 | Token::Number(val) => PolishValue::Number(val), 242 | _ => unreachable!(), 243 | } 244 | } 245 | } 246 | 247 | let mut chars = input.chars().peekable(); 248 | let mut operators: Vec = Vec::with_capacity(input.len() / 4); 249 | let mut values: Vec = Vec::with_capacity(input.len() / 3); 250 | let mut tokens = Vec::with_capacity(input.len() / 2); 251 | let mut parens = 0; 252 | 253 | 'outer: loop { 254 | // Collect the operators until a number is found. 255 | while let Some(&c) = chars.peek() { 256 | match c.check_operator() { 257 | OperatorState::Complete => { 258 | let token = c.operator_type().ok_or(InvalidOperator(c))?; 259 | if token != Token::OpenParen && token != Token::CloseParen { 260 | operators.push(token); 261 | } 262 | chars.next(); 263 | } 264 | OperatorState::PotentiallyIncomplete => { 265 | chars.next(); 266 | match chars.peek() { 267 | Some(&next_char) if next_char.is_operator() => { 268 | let token = [c, next_char] 269 | .operator_type() 270 | .ok_or(InvalidOperator(c))?; 271 | if token != Token::OpenParen 272 | && token != Token::CloseParen 273 | { 274 | operators.push(token); 275 | } 276 | chars.next(); 277 | } 278 | _ => { 279 | let token = 280 | c.operator_type().ok_or(InvalidOperator(c))?; 281 | if token != Token::OpenParen 282 | && token != Token::CloseParen 283 | { 284 | operators.push(token); 285 | } 286 | } 287 | } 288 | } 289 | OperatorState::NotAnOperator => { 290 | if c.is_whitespace() { 291 | chars.next(); 292 | } else if c.is_alphabetic() { 293 | values.push(consume_ans_or_atom(&mut chars).into()); 294 | break; 295 | } else if c.is_ascii_hexdigit() || c == '.' { 296 | values.push(PolishValue::Number(consume_number( 297 | &mut chars, 298 | )?)); 299 | break; 300 | } else { 301 | let token_string = consume_until_new_token(&mut chars); 302 | return Err(CalcError::UnrecognizedToken(token_string)); 303 | } 304 | } 305 | } 306 | } 307 | 308 | // Then collect the atoms/numbers, resetting the looping if more 309 | // operators are found. 310 | while let Some(&c) = chars.peek() { 311 | if c.is_alphabetic() { 312 | values.push(consume_ans_or_atom(&mut chars).into()); 313 | } else if c.is_ascii_hexdigit() || c == '.' { 314 | values.push(PolishValue::Number(consume_number(&mut chars)?)); 315 | } else if c.is_whitespace() || c == ')' { 316 | let _ = chars.next(); 317 | } else if operators.len() != values.len() { 318 | // Either too many or too few values were supplied 319 | return Err(CalcError::UnexpectedEndOfInput); 320 | } else { 321 | // This block executes when multiple sets of arithmetic 322 | // operations are entered. 323 | // Add parenthesis to work around the order of operations for 324 | // infix arithmetic. 325 | for _ in 1..operators.len() { 326 | tokens.push(Token::OpenParen); 327 | } 328 | 329 | // Operators are processed in reverse, while numbers are 330 | // processed forwardly. 331 | let mut iterator = values 332 | .drain(..) 333 | .map(Token::from) 334 | .zip(operators.drain(..).rev()); 335 | 336 | // The first iteration will not include any closing parenthesis. 337 | if let Some((value, operator)) = iterator.next() { 338 | tokens.push(value); 339 | tokens.push(operator); 340 | } 341 | 342 | // Each following iteration will append the second number in 343 | // the operation, 344 | // followed by a closing parenthesis and the next operation in 345 | // the list. 346 | for (value, operator) in iterator { 347 | tokens.push(value); 348 | tokens.push(Token::CloseParen); 349 | tokens.push(operator); 350 | } 351 | 352 | tokens.push(Token::OpenParen); 353 | parens += 1; 354 | continue 'outer; 355 | } 356 | } 357 | 358 | if values.is_empty() || operators.len() != values.len() - 1 { 359 | return Err(CalcError::UnexpectedEndOfInput); 360 | } else { 361 | // Removes the last value from the values vector so that they are 362 | // even. 363 | // It will be re-added at the end once everything's been collected. 364 | let last_value: Token = values.pop().unwrap().into(); 365 | // Add parenthesis to work around the order of operations for infix 366 | // arithmetic. 367 | for _ in 1..operators.len() { 368 | tokens.push(Token::OpenParen); 369 | } 370 | // Operators are processed in reverse, while numbers are processed 371 | // forwardly. 372 | let mut iterator = values 373 | .drain(..) 374 | .map(Token::from) 375 | .zip(operators.drain(..).rev()); 376 | 377 | // The first iteration will not include any closing parenthesis. 378 | if let Some((value, operator)) = iterator.next() { 379 | tokens.push(value); 380 | tokens.push(operator); 381 | } 382 | 383 | // Each following iteration will append the second number in the 384 | // operation, 385 | // followed by a closing parenthesis and the next operation in the 386 | // list. 387 | for (value, operator) in iterator { 388 | tokens.push(value); 389 | tokens.push(Token::CloseParen); 390 | tokens.push(operator); 391 | } 392 | 393 | tokens.push(last_value); 394 | for _ in 0..parens { 395 | tokens.push(Token::CloseParen); 396 | } 397 | break 'outer; 398 | } 399 | } 400 | 401 | Ok(tokens) 402 | } 403 | 404 | fn consume_ans_or_atom(input: &mut Peekable) -> Token 405 | where 406 | I: Iterator, 407 | { 408 | let atom = consume_atom(input); 409 | if atom.eq_ignore_ascii_case("ans") { 410 | Token::Ans 411 | } else { 412 | Token::Atom(atom) 413 | } 414 | } 415 | 416 | fn digits(input: &mut Peekable, radix: u32) -> String 417 | where 418 | I: Iterator, 419 | { 420 | let mut number = String::new(); 421 | while let Some(&c) = input.peek() { 422 | if c.is_digit(radix) { 423 | number.push(c); 424 | } else { 425 | break; 426 | } 427 | input.next(); 428 | } 429 | number 430 | } 431 | 432 | fn consume_number(input: &mut Peekable) -> Result 433 | where 434 | I: Iterator, 435 | { 436 | match input.peek() { 437 | Some(&'0') => { 438 | input.next(); 439 | match input.peek().cloned() { 440 | Some('x') | Some('X') => { 441 | input.next(); 442 | let digits = digits(input, 16); 443 | let num = Integral::from_str_radix(&digits, 16)?; 444 | return Ok(Value::hex(num)); 445 | } 446 | Some(c) if c.is_ascii_hexdigit() || c == '.' => (), 447 | _ => return Ok(Value::dec(0)), 448 | } 449 | } 450 | Some(_) => (), 451 | None => return Err(CalcError::UnexpectedEndOfInput), 452 | } 453 | let mut whole = digits(input, 10); 454 | if let Some(&'.') = input.peek() { 455 | input.next(); 456 | let frac = digits(input, 10); 457 | if whole.is_empty() && frac.is_empty() { 458 | whole = "0".to_string(); 459 | } 460 | let num = [whole, ".".into(), frac] 461 | .concat() 462 | .parse::() 463 | .map_err(|_| CalcError::InvalidNumber("invalid float".into()))?; 464 | Ok(Value::Float(num)) 465 | } else { 466 | let res: Integral = whole.parse()?; 467 | Ok(Value::dec(res)) 468 | } 469 | } 470 | 471 | /// Consume a valid atom. An atom is defined by: 472 | /// - Starting with an alphabetic character 473 | /// - Consisting of alphanumeric characters or underscores 474 | fn consume_atom>(input: &mut Peekable) -> String { 475 | let mut atom = String::new(); 476 | while let Some(&c) = input.peek() { 477 | if c.is_alphanumeric() || c == '_' { 478 | atom.push(c); 479 | input.next(); 480 | } else { 481 | break; 482 | } 483 | } 484 | atom 485 | } 486 | 487 | fn consume_until_new_token>(input: &mut I) -> String { 488 | input 489 | .take_while(|c| { 490 | !(c.is_whitespace() || c.is_operator() || c.is_ascii_digit()) 491 | }) 492 | .collect() 493 | } 494 | 495 | #[cfg(test)] 496 | mod tests { 497 | use super::*; 498 | 499 | #[test] 500 | fn normal() { 501 | let line = "(3 + 7) >> 10 * (7 % 2)"; 502 | let expected = vec![ 503 | Token::OpenParen, 504 | Token::Number(Value::dec(3)), 505 | Token::Plus, 506 | Token::Number(Value::dec(7)), 507 | Token::CloseParen, 508 | Token::BitWiseRShift, 509 | Token::Number(Value::dec(10)), 510 | Token::Multiply, 511 | Token::OpenParen, 512 | Token::Number(Value::dec(7)), 513 | Token::Modulo, 514 | Token::Number(Value::dec(2)), 515 | Token::CloseParen, 516 | ]; 517 | assert_eq!(tokenize(line), Ok(expected)); 518 | } 519 | 520 | #[test] 521 | fn zero() { 522 | let line = "0 + 0. + 0"; 523 | let expected = vec![ 524 | Token::Number(Value::dec(0)), 525 | Token::Plus, 526 | Token::Number(Value::Float(d128::from(0))), 527 | Token::Plus, 528 | Token::Number(Value::dec(0)), 529 | ]; 530 | assert_eq!(tokenize(line), Ok(expected)); 531 | } 532 | 533 | #[test] 534 | fn dice() { 535 | let line = "3d6"; 536 | let expected = vec![ 537 | Token::Number(Value::dec(3)), 538 | Token::Dice, 539 | Token::Number(Value::dec(6)), 540 | ]; 541 | assert_eq!(tokenize(line), Ok(expected)); 542 | } 543 | 544 | #[test] 545 | fn dice_polish() { 546 | let line = "d 3 6"; 547 | let expected = vec![ 548 | Token::Number(Value::dec(3)), 549 | Token::Dice, 550 | Token::Number(Value::dec(6)), 551 | ]; 552 | assert_eq!(tokenize_polish(line), Ok(expected)); 553 | } 554 | 555 | #[test] 556 | fn ans() { 557 | let line = "ans*3"; 558 | let expected = 559 | vec![Token::Ans, Token::Multiply, Token::Number(Value::dec(3))]; 560 | assert_eq!(tokenize(line), Ok(expected)); 561 | } 562 | 563 | #[test] 564 | fn ans_polish() { 565 | let line = "* ans 3"; 566 | let expected = 567 | vec![Token::Ans, Token::Multiply, Token::Number(Value::dec(3))]; 568 | assert_eq!(tokenize_polish(line), Ok(expected)); 569 | } 570 | 571 | #[test] 572 | fn ans_subtract_ans_polish() { 573 | let line = "- ans ans"; 574 | let expected = vec![Token::Ans, Token::Minus, Token::Ans]; 575 | assert_eq!(tokenize_polish(line), Ok(expected)); 576 | } 577 | 578 | #[test] 579 | fn function_chaining() { 580 | let line = "log 4 / log 2"; 581 | let expected = vec![ 582 | Token::Atom("log".into()), 583 | Token::Number(Value::dec(4)), 584 | Token::Divide, 585 | Token::Atom("log".into()), 586 | Token::Number(Value::dec(2)), 587 | ]; 588 | assert_eq!(tokenize(line), Ok(expected)); 589 | } 590 | 591 | #[test] 592 | fn hexadecimals() { 593 | let line = "0xDEADBEEF | 0xC0FFEE"; 594 | let expected = vec![ 595 | Token::Number(Value::hex(0xDEADBEEF as i64)), 596 | Token::BitWiseOr, 597 | Token::Number(Value::hex(0xC0FFEE)), 598 | ]; 599 | assert_eq!(tokenize(line), Ok(expected)); 600 | } 601 | } 602 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{CalcError, PartialComp}; 2 | use decimal::d128; 3 | use num::{BigInt, BigUint, ToPrimitive, Zero}; 4 | use std::convert::{TryFrom, TryInto}; 5 | use std::fmt; 6 | use std::ops::*; 7 | use std::str::FromStr; 8 | 9 | pub type Integral = BigInt; 10 | type UIntegral = BigUint; 11 | 12 | #[derive(Clone, Copy, Debug, PartialEq)] 13 | pub enum IntegralFmt { 14 | Dec, 15 | Hex, 16 | } 17 | 18 | impl Add for IntegralFmt { 19 | type Output = IntegralFmt; 20 | fn add(self, that: IntegralFmt) -> Self::Output { 21 | match (self, that) { 22 | (IntegralFmt::Hex, _) | (_, IntegralFmt::Hex) => IntegralFmt::Hex, 23 | _ => IntegralFmt::Dec, 24 | } 25 | } 26 | } 27 | 28 | impl<'a> Add<&'a IntegralFmt> for IntegralFmt { 29 | type Output = IntegralFmt; 30 | fn add(self, that: &IntegralFmt) -> Self::Output { 31 | match (self, *that) { 32 | (IntegralFmt::Hex, _) | (_, IntegralFmt::Hex) => IntegralFmt::Hex, 33 | _ => IntegralFmt::Dec, 34 | } 35 | } 36 | } 37 | 38 | impl IntegralFmt { 39 | pub fn merge(&self, that: &IntegralFmt) -> Self { 40 | match (*self, *that) { 41 | (IntegralFmt::Hex, _) | (_, IntegralFmt::Hex) => IntegralFmt::Hex, 42 | _ => IntegralFmt::Dec, 43 | } 44 | } 45 | } 46 | 47 | /// Represents a canonical value that can be calculated by this library 48 | #[derive(Clone, Debug, PartialEq)] 49 | pub enum Value { 50 | /// An integral value. The format of this value (hexadecimal versus 51 | /// decimal) is determined by the tag. 52 | Integral(Integral, IntegralFmt), 53 | /// A 128-bit decimal floating point number 54 | Float(d128), 55 | } 56 | 57 | pub mod ops { 58 | use super::{CalcError, Integral, PartialComp, UIntegral}; 59 | use decimal::d128; 60 | use num::{Integer, ToPrimitive, Zero}; 61 | use std::iter::repeat; 62 | 63 | macro_rules! bitwise_op { 64 | ($name:ident, $fun:expr) => { 65 | pub fn $name(n: &Integral, m: &Integral) -> Integral { 66 | bitwise(n, m, $fun) 67 | } 68 | }; 69 | } 70 | 71 | fn equalize(left: &mut Vec, right: &mut Vec) { 72 | match left.len().cmp(&right.len()) { 73 | std::cmp::Ordering::Greater => { 74 | for it in repeat(Zero::zero()).take(left.len() - right.len()) { 75 | right.push(it); 76 | } 77 | } 78 | std::cmp::Ordering::Equal => {} 79 | std::cmp::Ordering::Less => { 80 | for it in repeat(Zero::zero()).take(right.len() - left.len()) { 81 | left.push(it); 82 | } 83 | } 84 | } 85 | } 86 | 87 | pub fn bitwise u8>( 88 | n: &Integral, 89 | m: &Integral, 90 | fun: F, 91 | ) -> Integral { 92 | let mut n_bytes = n.to_signed_bytes_le(); 93 | let mut m_bytes = m.to_signed_bytes_le(); 94 | equalize(&mut n_bytes, &mut m_bytes); 95 | let res: Vec = n_bytes 96 | .iter() 97 | .zip(m_bytes) 98 | .map(|(n, m)| fun(*n, m)) 99 | .collect(); 100 | Integral::from_signed_bytes_le(&res) 101 | } 102 | 103 | bitwise_op!(and, |n, m| n & m); 104 | bitwise_op!(or, |n, m| n | m); 105 | bitwise_op!(xor, |n, m| n ^ m); 106 | 107 | pub fn not(n: Integral) -> Integral { 108 | let bytes = n.to_signed_bytes_le(); 109 | let res: Vec = bytes.iter().map(|n| !n).collect(); 110 | Integral::from_signed_bytes_le(&res) 111 | } 112 | 113 | pub fn int_powu(n: &Integral, m: &UIntegral) -> Integral { 114 | if m.is_zero() { 115 | 1.into() 116 | } else if m.is_even() { 117 | int_powu(&(n * n), &(m / (2_u8))) 118 | } else { 119 | n * int_powu(&(n * n), &((m - (1_u8)) / (2_u8))) 120 | } 121 | } 122 | 123 | pub fn int_pow(n: &Integral, m: &Integral) -> Option { 124 | if *m >= Zero::zero() { 125 | Some(int_powu(n, &m.to_biguint().unwrap())) 126 | } else { 127 | unimplemented!() 128 | } 129 | } 130 | 131 | pub fn to_float(n: &Integral) -> Result { 132 | n.to_i64().map(Into::into).ok_or(CalcError::WouldTruncate( 133 | PartialComp::ToFloat(n.to_string()), 134 | )) 135 | } 136 | } 137 | 138 | impl Value { 139 | pub fn hex>(n: T) -> Self { 140 | Value::Integral(n.into(), IntegralFmt::Hex) 141 | } 142 | 143 | pub fn dec>(n: T) -> Self { 144 | Value::Integral(n.into(), IntegralFmt::Dec) 145 | } 146 | 147 | pub fn is_zero(&self) -> bool { 148 | match self { 149 | Value::Integral(ref n, _) => n.is_zero(), 150 | Value::Float(ref f) => *f == d128!(0), 151 | } 152 | } 153 | 154 | // Check if Value is Not a Number 155 | pub fn is_nan(&self) -> bool { 156 | match self { 157 | // Integral is never NaN 158 | Value::Integral(_, _) => false, 159 | Value::Float(ref f) => f.is_nan(), 160 | } 161 | } 162 | 163 | pub fn as_float(&self) -> Result { 164 | match self { 165 | Value::Integral(ref n, _) => ops::to_float(n), 166 | Value::Float(ref n) => Ok(*n), 167 | } 168 | } 169 | 170 | /// Represents a computation that can only operate on, and return, 171 | /// integer values 172 | pub fn intmap( 173 | &self, 174 | that: &Value, 175 | op: T, 176 | f: F, 177 | ) -> Result 178 | where 179 | F: Fn(&Integral, &Integral) -> Result, 180 | T: ToString, 181 | { 182 | match (self, that) { 183 | (Value::Integral(n, t1), Value::Integral(m, t2)) => { 184 | Ok(Value::Integral(f(n, m)?, *t1 + t2)) 185 | } 186 | (v1 @ Value::Float(_), v2) | (v1, v2 @ Value::Float(_)) => { 187 | Err(CalcError::BadTypes(PartialComp::binary(op, v1, v2))) 188 | } 189 | } 190 | } 191 | 192 | /// Represents a computation that will cast integer types to floating 193 | /// point. There might be a possible truncation when we convert a BigInt 194 | /// into a floating point, so we have to be careful here. 195 | pub fn castmap( 196 | self, 197 | that: Value, 198 | f: F, 199 | g: G, 200 | ) -> Result 201 | where 202 | F: Fn(Integral, Integral) -> Integral, 203 | G: Fn(d128, d128) -> d128, 204 | { 205 | let ret = match (self, that) { 206 | (Value::Float(n), Value::Float(m)) => Value::Float(g(n, m)), 207 | (Value::Float(n), Value::Integral(m, _)) => { 208 | Value::Float(g(n, ops::to_float(&m)?)) 209 | } 210 | (Value::Integral(n, _), Value::Float(m)) => { 211 | Value::Float(g(ops::to_float(&n)?, m)) 212 | } 213 | (Value::Integral(n, t1), Value::Integral(m, t2)) => { 214 | Value::Integral(f(n, m), t1 + t2) 215 | } 216 | }; 217 | Ok(ret) 218 | } 219 | 220 | pub fn pow(self, that: Value) -> Result { 221 | let value = match (&self, &that) { 222 | (&Value::Float(n), &Value::Float(m)) => Value::Float(n.pow(m)), 223 | (&Value::Float(n), Value::Integral(m, _)) => { 224 | Value::Float(n.pow(ops::to_float(m)?)) 225 | } 226 | (Value::Integral(n, _), &Value::Float(m)) => { 227 | Value::Float(ops::to_float(n)?.pow(m)) 228 | } 229 | (&Value::Integral(ref n, t1), &Value::Integral(ref m, t2)) => { 230 | match ops::int_pow(n, m) { 231 | Some(v) => Value::Integral(v, t1 + t2), 232 | None => { 233 | return Err(CalcError::WouldOverflow( 234 | PartialComp::binary("**", &self, &that), 235 | )) 236 | } 237 | } 238 | } 239 | }; 240 | Ok(value) 241 | } 242 | 243 | /// Computes the absolute value of a number 244 | pub fn abs(&self) -> Self { 245 | use num::Signed; 246 | match self { 247 | Value::Float(n) => Value::Float(n.abs()), 248 | Value::Integral(n, t) => Value::Integral(Signed::abs(n), *t), 249 | } 250 | } 251 | 252 | /// Computes the natural logarithm of a number 253 | pub fn ln(&self) -> Result { 254 | match self { 255 | Value::Float(n) => Ok(Value::Float(n.ln())), 256 | Value::Integral(_, _) => Ok(Value::Float(self.as_float()?.ln())), 257 | } 258 | } 259 | 260 | /// Computes the logarithm base 10 of a number 261 | pub fn log(&self) -> Result { 262 | match self { 263 | Value::Float(n) => Ok(Value::Float(n.log10())), 264 | Value::Integral(_, _) => Ok(Value::Float(self.as_float()?.log10())), 265 | } 266 | } 267 | 268 | /// Computes the sine of a number in radians 269 | pub fn sin(&self) -> Result { 270 | let float_val: f64 = self.try_into()?; 271 | float_val.sin().try_into() 272 | } 273 | 274 | /// Computes the cosine of a number in radians 275 | pub fn cos(&self) -> Result { 276 | let float_val: f64 = self.try_into()?; 277 | float_val.cos().try_into() 278 | } 279 | 280 | /// Computes the tangent of a number in radians 281 | pub fn tan(&self) -> Result { 282 | let float_val: f64 = self.try_into()?; 283 | float_val.tan().try_into() 284 | } 285 | 286 | /// Computes the arc sine of a number in radians 287 | pub fn asin(&self) -> Result { 288 | let float_val: f64 = self.try_into()?; 289 | float_val.asin().try_into() 290 | } 291 | 292 | /// Computes the arc cosine of a number in radians 293 | pub fn acos(&self) -> Result { 294 | let float_val: f64 = self.try_into()?; 295 | float_val.acos().try_into() 296 | } 297 | 298 | /// Computes the arc tanget of a number in radians 299 | pub fn atan(&self) -> Result { 300 | let float_val: f64 = self.try_into()?; 301 | float_val.atan().try_into() 302 | } 303 | 304 | /// Computes the hyperbolic sine of a number in radians 305 | pub fn sinh(&self) -> Result { 306 | let float_val: f64 = self.try_into()?; 307 | float_val.sinh().try_into() 308 | } 309 | 310 | /// Computes the hyperbolic cosine of a number in radians 311 | pub fn cosh(&self) -> Result { 312 | let float_val: f64 = self.try_into()?; 313 | float_val.cosh().try_into() 314 | } 315 | 316 | /// Computes the hyperbolic tangent of a number in radians 317 | pub fn tanh(&self) -> Result { 318 | let float_val: f64 = self.try_into()?; 319 | float_val.tanh().try_into() 320 | } 321 | 322 | /// Computes the inverse hyperbolic sine of a number in radians 323 | pub fn asinh(&self) -> Result { 324 | let float_val: f64 = self.try_into()?; 325 | float_val.asinh().try_into() 326 | } 327 | 328 | /// Computes the inverse hyperbolic cosine of a number in radians 329 | pub fn acosh(&self) -> Result { 330 | let float_val: f64 = self.try_into()?; 331 | float_val.acosh().try_into() 332 | } 333 | 334 | /// Computes the inverse hyperbolic tanget of a number in radians 335 | pub fn atanh(&self) -> Result { 336 | let float_val: f64 = self.try_into()?; 337 | float_val.atanh().try_into() 338 | } 339 | } 340 | 341 | impl fmt::Display for Value { 342 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 343 | match self { 344 | Value::Integral(ref n, IntegralFmt::Dec) => write!(f, "{}", n), 345 | Value::Integral(ref n, IntegralFmt::Hex) => write!(f, "0x{:X}", n), 346 | Value::Float(ref n) => write!(f, "{}", n), 347 | } 348 | } 349 | } 350 | 351 | impl Default for Value { 352 | fn default() -> Self { 353 | Value::Float(d128!(0.0)) 354 | } 355 | } 356 | 357 | impl TryFrom<&Value> for f64 { 358 | type Error = CalcError; 359 | 360 | fn try_from(v: &Value) -> Result { 361 | match v { 362 | Value::Float(n) => n 363 | .to_string() 364 | .parse::() 365 | .map_err(|err| CalcError::InvalidNumber(err.to_string())), 366 | Value::Integral(n, _) => { 367 | n.to_f64().ok_or(CalcError::InvalidNumber(n.to_string())) 368 | } 369 | } 370 | } 371 | } 372 | 373 | impl TryFrom for Value { 374 | type Error = CalcError; 375 | 376 | fn try_from(v: f64) -> Result { 377 | let n = d128::from_str(&v.to_string()) 378 | .map_err(|err| CalcError::InvalidNumber(format!("{:?}", err)))?; 379 | Ok(Value::Float(n)) 380 | } 381 | } 382 | /// An intermediate result that can be computed by this library. 383 | /// - `value` represents the current computed data 384 | /// - `tokens` represents the number of tokens that have been consumed 385 | #[derive(Clone, Debug, PartialEq)] 386 | pub struct IR { 387 | pub value: Value, 388 | pub tokens: usize, 389 | } 390 | 391 | impl IR { 392 | pub fn new>>(value: Value, tokens: T) -> Self { 393 | IR { 394 | value, 395 | tokens: tokens.into().unwrap_or(0), 396 | } 397 | } 398 | } 399 | 400 | impl Add for Value { 401 | type Output = Result; 402 | 403 | fn add(self, that: Value) -> Self::Output { 404 | self.castmap(that, |x, y| x + y, |x, y| x + y) 405 | } 406 | } 407 | 408 | impl Sub for Value { 409 | type Output = Result; 410 | 411 | fn sub(self, that: Value) -> Self::Output { 412 | self.castmap(that, |x, y| x - y, |x, y| x - y) 413 | } 414 | } 415 | 416 | impl Mul for Value { 417 | type Output = Result; 418 | 419 | fn mul(self, that: Value) -> Self::Output { 420 | self.castmap(that, |x, y| x * y, |x, y| x * y) 421 | } 422 | } 423 | 424 | impl Div for Value { 425 | type Output = Result; 426 | 427 | fn div(self, that: Value) -> Self::Output { 428 | if that.is_zero() { 429 | return Err(CalcError::DivideByZero); 430 | } 431 | let value = match (self, that) { 432 | (Value::Float(n), Value::Float(m)) => Value::Float(n / m), 433 | (Value::Float(n), Value::Integral(m, _)) => { 434 | Value::Float(n / ops::to_float(&m)?) 435 | } 436 | (Value::Integral(n, _), Value::Float(m)) => { 437 | Value::Float(ops::to_float(&n)? / m) 438 | } 439 | (Value::Integral(ref n, t1), Value::Integral(ref m, t2)) => { 440 | if (n % m).is_zero() { 441 | Value::Integral(n / m, t1 + t2) 442 | } else { 443 | Value::Float(ops::to_float(n)? / ops::to_float(m)?) 444 | } 445 | } 446 | }; 447 | Ok(value) 448 | } 449 | } 450 | 451 | impl BitAnd for Value { 452 | type Output = Result; 453 | 454 | fn bitand(self, that: Value) -> Self::Output { 455 | self.intmap(&that, "&", |n, m| Ok(ops::and(n, m))) 456 | } 457 | } 458 | 459 | impl BitOr for Value { 460 | type Output = Result; 461 | 462 | fn bitor(self, that: Value) -> Self::Output { 463 | self.intmap(&that, "|", |n, m| Ok(ops::or(n, m))) 464 | } 465 | } 466 | 467 | impl BitXor for Value { 468 | type Output = Result; 469 | 470 | fn bitxor(self, that: Value) -> Self::Output { 471 | self.intmap(&that, "^", |n, m| Ok(ops::xor(n, m))) 472 | } 473 | } 474 | 475 | impl Neg for Value { 476 | type Output = Self; 477 | 478 | fn neg(self) -> Self::Output { 479 | match self { 480 | Value::Integral(n, t) => Value::Integral(-n, t), 481 | Value::Float(f) => Value::Float(-f), 482 | } 483 | } 484 | } 485 | 486 | impl Not for Value { 487 | type Output = Result; 488 | 489 | fn not(self) -> Self::Output { 490 | match self { 491 | Value::Integral(n, t) => Ok(Value::Integral(ops::not(n), t)), 492 | Value::Float(f) => { 493 | Err(CalcError::BadTypes(PartialComp::unary("~", f))) 494 | } 495 | } 496 | } 497 | } 498 | 499 | impl Rem for Value { 500 | type Output = Result; 501 | 502 | fn rem(self, that: Value) -> Self::Output { 503 | if that.is_zero() { 504 | return Err(CalcError::DivideByZero); 505 | } 506 | self.castmap(that, |x, y| x % y, |x, y| x % y) 507 | } 508 | } 509 | 510 | impl Shl for Value { 511 | type Output = Result; 512 | 513 | fn shl(self, that: Value) -> Self::Output { 514 | self.intmap(&that, "<<", |n, m| { 515 | m.to_i64() 516 | .map(|m| { 517 | if m < 0 { 518 | n >> ((-m) as usize) 519 | } else { 520 | n << (m as usize) 521 | } 522 | }) 523 | .ok_or(CalcError::WouldOverflow(PartialComp::binary( 524 | "<<", &self, &that, 525 | ))) 526 | }) 527 | } 528 | } 529 | 530 | impl Shr for Value { 531 | type Output = Result; 532 | 533 | fn shr(self, that: Value) -> Self::Output { 534 | self.intmap(&that, "<<", |n, m| { 535 | m.to_i64() 536 | .map(|m| { 537 | if m < 0 { 538 | n << ((-m) as usize) 539 | } else { 540 | n >> (m as usize) 541 | } 542 | }) 543 | .ok_or(CalcError::WouldOverflow(PartialComp::binary( 544 | ">>", &self, &that, 545 | ))) 546 | }) 547 | } 548 | } 549 | 550 | #[cfg(test)] 551 | mod tests { 552 | use super::*; 553 | 554 | #[test] 555 | fn float_override() { 556 | let cases = vec![ 557 | ( 558 | (Value::Float(d128!(3)) + Value::dec(1)).unwrap(), 559 | Value::Float(d128!(4)), 560 | ), 561 | ( 562 | (Value::hex(5) - Value::Float(d128!(4.5))).unwrap(), 563 | Value::Float(d128!(0.5)), 564 | ), 565 | ( 566 | ((Value::hex(24) * Value::dec(4)).unwrap() 567 | * Value::Float(d128!(1) / d128!(48))) 568 | .unwrap(), 569 | Value::Float(d128!(2)), 570 | ), 571 | ]; 572 | 573 | for (output, expected) in cases { 574 | assert_eq!(output, expected); 575 | } 576 | } 577 | 578 | #[test] 579 | fn hex_override() { 580 | let cases = vec![ 581 | ((Value::hex(3) * Value::dec(-2)).unwrap(), Value::hex(-6)), 582 | ( 583 | (Value::hex(0x100) >> Value::hex(0x2)).unwrap(), 584 | Value::hex(0x40), 585 | ), 586 | ]; 587 | for (output, expected) in cases { 588 | assert_eq!(output, expected); 589 | } 590 | } 591 | 592 | #[test] 593 | fn try_from_f64() { 594 | let cases: Vec<(Value, Value)> = vec![ 595 | ( 596 | Value::Float(d128!(0.0)), 597 | (0.0).try_into().unwrap(), 598 | ), 599 | ( 600 | Value::Float(d128!(0.0000001)), 601 | (0.0000001).try_into().unwrap(), 602 | ), 603 | ( 604 | Value::Float(d128!(-0.0000001)), 605 | (-0.0000001).try_into().unwrap(), 606 | ), 607 | ( 608 | Value::Float(d128!(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001)), 609 | (-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001).try_into().unwrap(), 610 | ), 611 | ( 612 | Value::Float(d128!(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)), 613 | (f64::MAX).try_into().unwrap(), 614 | ), 615 | ( 616 | Value::Float(d128!(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000)), 617 | (f64::MIN).try_into().unwrap(), 618 | ), 619 | ]; 620 | 621 | for (output, expected) in cases { 622 | assert_eq!(output, expected); 623 | } 624 | } 625 | 626 | #[test] 627 | fn try_into_f64() { 628 | let cases: Vec<(f64, f64)> = vec![ 629 | ( 630 | (&Value::Integral(BigInt::from(0), IntegralFmt::Dec)).try_into().unwrap(), 631 | 0.0f64, 632 | ), 633 | ( 634 | (&Value::Integral(BigInt::from(1), IntegralFmt::Dec)).try_into().unwrap(), 635 | 1.0f64, 636 | ), 637 | ( 638 | (&Value::Integral(BigInt::from(-1), IntegralFmt::Dec)).try_into().unwrap(), 639 | -1.0f64, 640 | ), 641 | ( 642 | (&Value::Float(d128!(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001))).try_into().unwrap(), 643 | -0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f64, 644 | ), 645 | ( 646 | (&Value::Float(d128!(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000))).try_into().unwrap(), 647 | f64::MAX, 648 | ), 649 | ( 650 | (&Value::Float(d128!(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000))).try_into().unwrap(), 651 | f64::MIN, 652 | ), 653 | ( 654 | (&Value::Float(d128!(0.0))).try_into().unwrap(), 655 | 0.0, 656 | ), 657 | ( 658 | (&Value::Float(d128!(0.0000001))).try_into().unwrap(), 659 | 0.0000001, 660 | ), 661 | ( 662 | (&Value::Float(d128!(-0.0000001))).try_into().unwrap(), 663 | -0.0000001, 664 | ), 665 | ( 666 | (&Value::Float(d128!(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001))).try_into().unwrap(), 667 | -0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001f64, 668 | ), 669 | ( 670 | (&Value::Float(d128!(179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000))).try_into().unwrap(), 671 | f64::MAX, 672 | ), 673 | ( 674 | (&Value::Float(d128!(-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000))).try_into().unwrap(), 675 | f64::MIN, 676 | ), 677 | ]; 678 | 679 | for (output, expected) in cases { 680 | assert_eq!(output, expected); 681 | } 682 | } 683 | 684 | #[test] 685 | fn function_abs() { 686 | let cases: Vec<(Value, Value)> = vec![ 687 | ( 688 | Value::Integral(BigInt::from(0), IntegralFmt::Dec).abs(), 689 | Value::Integral(BigInt::from(0), IntegralFmt::Dec), 690 | ), 691 | ( 692 | Value::Integral(BigInt::from(1), IntegralFmt::Dec).abs(), 693 | Value::Integral(BigInt::from(1), IntegralFmt::Dec), 694 | ), 695 | ( 696 | Value::Integral(BigInt::from(-1), IntegralFmt::Dec).abs(), 697 | Value::Integral(BigInt::from(1), IntegralFmt::Dec), 698 | ), 699 | (Value::Float(d128!(0)).abs(), Value::Float(d128!(0))), 700 | (Value::Float(d128!(1.0)).abs(), Value::Float(d128!(1.0))), 701 | (Value::Float(d128!(0.1)).abs(), Value::Float(d128!(0.1))), 702 | (Value::Float(d128!(-0.0)).abs(), Value::Float(d128!(0.0))), 703 | (Value::Float(d128!(-1.0)).abs(), Value::Float(d128!(1.0))), 704 | (Value::Float(d128!(-0.1)).abs(), Value::Float(d128!(0.1))), 705 | ]; 706 | 707 | for (output, expected) in cases { 708 | assert_eq!(output, expected); 709 | } 710 | } 711 | 712 | #[test] 713 | fn function_log() { 714 | let cases: Vec<(Value, Value)> = vec![ 715 | ( 716 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 717 | .log() 718 | .unwrap(), 719 | Value::Float(d128::neg_infinity()), 720 | ), 721 | ( 722 | Value::Integral(BigInt::from(1), IntegralFmt::Dec) 723 | .log() 724 | .unwrap(), 725 | Value::Float(d128!(0)), 726 | ), 727 | ( 728 | Value::Integral(BigInt::from(10), IntegralFmt::Dec) 729 | .log() 730 | .unwrap(), 731 | Value::Float(d128!(1)), 732 | ), 733 | ( 734 | Value::Integral(BigInt::from(100), IntegralFmt::Dec) 735 | .log() 736 | .unwrap(), 737 | Value::Float(d128!(2)), 738 | ), 739 | ( 740 | Value::Float(d128!(0.0)).log().unwrap(), 741 | Value::Float(d128::neg_infinity()), 742 | ), 743 | ( 744 | Value::Float(d128!(-0.0)).log().unwrap(), 745 | Value::Float(d128::neg_infinity()), 746 | ), 747 | ( 748 | Value::Float(d128!(0.1)).log().unwrap(), 749 | Value::Float(d128!(-1.0)), 750 | ), 751 | ( 752 | Value::Float(d128!(1.0)).log().unwrap(), 753 | Value::Float(d128!(0.0)), 754 | ), 755 | ( 756 | Value::Float(d128!(10.0)).log().unwrap(), 757 | Value::Float(d128!(1.0)), 758 | ), 759 | ( 760 | Value::Float(d128!(1000.0)).log().unwrap(), 761 | Value::Float(d128!(3.0)), 762 | ), 763 | ]; 764 | 765 | for (output, expected) in cases { 766 | assert_eq!(output, expected); 767 | } 768 | 769 | assert!(Value::Integral(BigInt::from(-1), IntegralFmt::Dec) 770 | .log() 771 | .unwrap() 772 | .is_nan()); 773 | assert!(Value::Float(d128!(-0.1)).log().unwrap().is_nan()); 774 | assert!(Value::Float(d128!(-1.0)).log().unwrap().is_nan()); 775 | } 776 | 777 | #[test] 778 | fn function_ln() { 779 | let cases: Vec<(Value, Value)> = vec![ 780 | ( 781 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 782 | .ln() 783 | .unwrap(), 784 | Value::Float(d128::neg_infinity()), 785 | ), 786 | ( 787 | Value::Integral(BigInt::from(1), IntegralFmt::Dec) 788 | .ln() 789 | .unwrap(), 790 | Value::Float(d128!(0)), 791 | ), 792 | ( 793 | Value::Integral(BigInt::from(2), IntegralFmt::Dec) 794 | .ln() 795 | .unwrap(), 796 | Value::Float(d128!(0.6931471805599453094172321214581766)), 797 | ), 798 | ( 799 | Value::Integral(BigInt::from(7), IntegralFmt::Dec) 800 | .ln() 801 | .unwrap(), 802 | Value::Float(d128!(1.945910149055313305105352743443180)), 803 | ), 804 | ( 805 | Value::Float(d128!(0.0)).ln().unwrap(), 806 | Value::Float(d128::neg_infinity()), 807 | ), 808 | ( 809 | Value::Float(d128!(-0.0)).ln().unwrap(), 810 | Value::Float(d128::neg_infinity()), 811 | ), 812 | ( 813 | Value::Float(d128!(0.36787944117144232159552377016146086)) 814 | .ln() 815 | .unwrap(), // 1/e 816 | Value::Float(d128!(-0.9999999999999999999999999999999999)), 817 | ), 818 | ( 819 | Value::Float(d128!(1.0)).ln().unwrap(), 820 | Value::Float(d128!(0.0)), 821 | ), 822 | ( 823 | Value::Float(d128!(2.71828182845904523536028747135266249)) 824 | .ln() 825 | .unwrap(), // e 826 | Value::Float(d128!(0.9999999999999999999999999999999998)), 827 | ), 828 | ( 829 | Value::Float(d128!(7.38905609893065022723042746057500781)) 830 | .ln() 831 | .unwrap(), // e² 832 | Value::Float(d128!(2.0)), 833 | ), 834 | ]; 835 | 836 | for (output, expected) in cases { 837 | assert_eq!(output, expected); 838 | } 839 | 840 | assert!(Value::Integral(BigInt::from(-1), IntegralFmt::Dec) 841 | .ln() 842 | .unwrap() 843 | .is_nan()); 844 | assert!(Value::Float(d128!(-0.1)).ln().unwrap().is_nan()); 845 | assert!(Value::Float(d128!(-1.0)).ln().unwrap().is_nan()); 846 | } 847 | 848 | #[test] 849 | fn function_sin() { 850 | let cases: Vec<(Value, Value)> = vec![ 851 | ( 852 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 853 | .sin() 854 | .unwrap(), 855 | Value::Float(d128!(0.0)), 856 | ), 857 | ( 858 | Value::Float(d128!(0)).sin().unwrap(), 859 | Value::Float(d128!(0)), 860 | ), 861 | ( 862 | Value::Float(d128!(0.52359877559829887307710723054658381)) 863 | .sin() 864 | .unwrap(), // pi/6 865 | Value::Float(d128!(0.5)), 866 | ), 867 | ( 868 | Value::Float(d128!(0.785398163397448309615660845819875721)) 869 | .sin() 870 | .unwrap(), // pi/4 871 | Value::Float(d128!(0.7071067811865475)), 872 | ), 873 | ( 874 | Value::Float(d128!(1.047197551196597746154214461093167628)) 875 | .sin() 876 | .unwrap(), // pi/3 877 | Value::Float(d128!(0.8660254037844387)), 878 | ), 879 | ( 880 | Value::Float(d128!(1.57079632679489661923132169163975144)) 881 | .sin() 882 | .unwrap(), // pi/2 883 | Value::Float(d128!(1.0)), 884 | ), 885 | ( 886 | Value::Float(d128!(3.14159265358979323846264338327950288)) 887 | .sin() 888 | .unwrap(), // pi 889 | Value::Float(d128!(0.0)), 890 | ), 891 | ( 892 | Value::Float(d128!(6.28318530717958647692528676655900576)) 893 | .sin() 894 | .unwrap(), // 2*pi 895 | Value::Float(d128!(0.0)), 896 | ), 897 | ( 898 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 899 | .sin() 900 | .unwrap(), // -pi/4 901 | Value::Float(d128!(-0.7071067811865475)), 902 | ), 903 | ]; 904 | 905 | for (output, expected) in cases { 906 | println!("output: {}, expected: {}", &output, &expected); 907 | let diff: Value = (output - expected).unwrap().abs(); 908 | let delta: Value = Value::Float(d128!(1e-15)); 909 | let verification = 910 | diff.as_float().unwrap() < delta.as_float().unwrap(); 911 | assert!(verification); 912 | } 913 | } 914 | 915 | #[test] 916 | fn function_cos() { 917 | let cases: Vec<(Value, Value)> = vec![ 918 | ( 919 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 920 | .cos() 921 | .unwrap(), 922 | Value::Float(d128!(1.0)), 923 | ), 924 | ( 925 | Value::Float(d128!(0)).cos().unwrap(), 926 | Value::Float(d128!(1)), 927 | ), 928 | ( 929 | Value::Float(d128!(0.52359877559829887307710723054658381)) 930 | .cos() 931 | .unwrap(), // pi/6 932 | Value::Float(d128!(0.8660254037844387)), 933 | ), 934 | ( 935 | Value::Float(d128!(0.785398163397448309615660845819875721)) 936 | .cos() 937 | .unwrap(), // pi/4 938 | Value::Float(d128!(0.7071067811865475)), 939 | ), 940 | ( 941 | Value::Float(d128!(1.047197551196597746154214461093167628)) 942 | .cos() 943 | .unwrap(), // pi/3 944 | Value::Float(d128!(0.5)), 945 | ), 946 | ( 947 | Value::Float(d128!(1.57079632679489661923132169163975144)) 948 | .cos() 949 | .unwrap(), // pi/2 950 | Value::Float(d128!(0.0)), 951 | ), 952 | ( 953 | Value::Float(d128!(3.14159265358979323846264338327950288)) 954 | .cos() 955 | .unwrap(), // pi 956 | Value::Float(d128!(-1.0)), 957 | ), 958 | ( 959 | Value::Float(d128!(6.28318530717958647692528676655900576)) 960 | .cos() 961 | .unwrap(), // 2*pi 962 | Value::Float(d128!(1.0)), 963 | ), 964 | ( 965 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 966 | .cos() 967 | .unwrap(), // -pi/4 968 | Value::Float(d128!(0.7071067811865475)), 969 | ), 970 | ]; 971 | 972 | for (output, expected) in cases { 973 | let diff: Value = (output - expected).unwrap().abs(); 974 | let delta: Value = Value::Float(d128!(1e-15)); 975 | let verification = 976 | diff.as_float().unwrap() < delta.as_float().unwrap(); 977 | assert!(verification); 978 | } 979 | } 980 | 981 | #[test] 982 | fn function_tan() { 983 | let cases: Vec<(Value, Value)> = vec![ 984 | ( 985 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 986 | .tan() 987 | .unwrap(), 988 | Value::Float(d128!(0.0)), 989 | ), 990 | ( 991 | Value::Float(d128!(0)).tan().unwrap(), 992 | Value::Float(d128!(0)), 993 | ), 994 | ( 995 | Value::Float(d128!(0.52359877559829887307710723054658381)) 996 | .tan() 997 | .unwrap(), // pi/6 998 | Value::Float(d128!(0.5773502691896257)), 999 | ), 1000 | ( 1001 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1002 | .tan() 1003 | .unwrap(), // pi/4 1004 | Value::Float(d128!(1.0)), 1005 | ), 1006 | ( 1007 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1008 | .tan() 1009 | .unwrap(), // pi/3 1010 | Value::Float(d128!(1.73205080756887729)), 1011 | ), 1012 | ( 1013 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1014 | .tan() 1015 | .unwrap(), // pi/2 1016 | Value::Float(d128!(16331239353195370)), 1017 | ), 1018 | ( 1019 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1020 | .tan() 1021 | .unwrap(), // pi 1022 | Value::Float(d128!(0.0)), 1023 | ), 1024 | ( 1025 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1026 | .tan() 1027 | .unwrap(), // 2*pi 1028 | Value::Float(d128!(0.0)), 1029 | ), 1030 | ( 1031 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1032 | .tan() 1033 | .unwrap(), // -pi/4 1034 | Value::Float(d128!(-1.0)), 1035 | ), 1036 | ]; 1037 | 1038 | for (output, expected) in cases { 1039 | print!("output: {}, expected: {}", &output, &expected); 1040 | let diff: Value = (output - expected).unwrap().abs(); 1041 | let delta: Value = Value::Float(d128!(1e-15)); 1042 | let verification = 1043 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1044 | println!(", verification: {}", &verification); 1045 | assert!(verification); 1046 | } 1047 | } 1048 | 1049 | #[test] 1050 | fn function_asin() { 1051 | let cases: Vec<(Value, Value)> = vec![ 1052 | ( 1053 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1054 | .asin() 1055 | .unwrap(), 1056 | Value::Float(d128!(0.0)), 1057 | ), 1058 | ( 1059 | Value::Float(d128!(0)).asin().unwrap(), 1060 | Value::Float(d128!(0)), 1061 | ), 1062 | ( 1063 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1064 | .asin() 1065 | .unwrap(), // pi/6 1066 | Value::Float(d128!(0.5510695830994464)), 1067 | ), 1068 | ( 1069 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1070 | .asin() 1071 | .unwrap(), // pi/4 1072 | Value::Float(d128!(0.9033391107665127)), 1073 | ), 1074 | ( 1075 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1076 | .asin() 1077 | .unwrap(), // -pi/4 1078 | Value::Float(d128!(-0.9033391107665127)), 1079 | ), 1080 | ]; 1081 | 1082 | for (output, expected) in cases { 1083 | println!("output: {}, expected: {}", &output, &expected); 1084 | let diff: Value = (output - expected).unwrap().abs(); 1085 | let delta: Value = Value::Float(d128!(1e-15)); 1086 | let verification = 1087 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1088 | assert!(verification); 1089 | } 1090 | 1091 | assert!(Value::Float(d128!(1.04719755119659774615421446109316762)) 1092 | .asin() 1093 | .unwrap() 1094 | .is_nan()); // pi/3 1095 | assert!(Value::Float(d128!(1.57079632679489661923132169163975144)) 1096 | .asin() 1097 | .unwrap() 1098 | .is_nan()); // pi/2 1099 | assert!(Value::Float(d128!(3.14159265358979323846264338327950288)) 1100 | .asin() 1101 | .unwrap() 1102 | .is_nan()); // pi 1103 | assert!(Value::Float(d128!(6.28318530717958647692528676655900576)) 1104 | .asin() 1105 | .unwrap() 1106 | .is_nan()); // 2*pi 1107 | } 1108 | 1109 | #[test] 1110 | fn function_acos() { 1111 | let cases: Vec<(Value, Value)> = vec![ 1112 | ( 1113 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1114 | .acos() 1115 | .unwrap(), 1116 | Value::Float(d128!(1.5707963267948966)), 1117 | ), 1118 | ( 1119 | Value::Float(d128!(0)).acos().unwrap(), 1120 | Value::Float(d128!(1.5707963267948966)), 1121 | ), 1122 | ( 1123 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1124 | .acos() 1125 | .unwrap(), // pi/6 1126 | Value::Float(d128!(1.0197267436954500)), 1127 | ), 1128 | ( 1129 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1130 | .acos() 1131 | .unwrap(), // pi/4 1132 | Value::Float(d128!(0.6674572160283840)), 1133 | ), 1134 | ( 1135 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1136 | .acos() 1137 | .unwrap(), // -pi/4 1138 | Value::Float(d128!(2.4741354375614100)), 1139 | ), 1140 | ]; 1141 | 1142 | for (output, expected) in cases { 1143 | println!("output: {}, expected: {}", &output, &expected); 1144 | let diff: Value = (output - expected).unwrap().abs(); 1145 | let delta: Value = Value::Float(d128!(1e-15)); 1146 | let verification = 1147 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1148 | assert!(verification); 1149 | } 1150 | 1151 | assert!(Value::Float(d128!(1.04719755119659774615421446109316762)) 1152 | .acos() 1153 | .unwrap() 1154 | .is_nan()); // pi/3 1155 | assert!(Value::Float(d128!(1.57079632679489661923132169163975144)) 1156 | .acos() 1157 | .unwrap() 1158 | .is_nan()); // pi/2 1159 | assert!(Value::Float(d128!(3.14159265358979323846264338327950288)) 1160 | .acos() 1161 | .unwrap() 1162 | .is_nan()); // pi 1163 | assert!(Value::Float(d128!(6.28318530717958647692528676655900576)) 1164 | .acos() 1165 | .unwrap() 1166 | .is_nan()); // 2*pi 1167 | } 1168 | 1169 | #[test] 1170 | fn function_atan() { 1171 | let cases: Vec<(Value, Value)> = vec![ 1172 | ( 1173 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1174 | .atan() 1175 | .unwrap(), 1176 | Value::Float(d128!(0.0)), 1177 | ), 1178 | ( 1179 | Value::Float(d128!(0)).atan().unwrap(), 1180 | Value::Float(d128!(0)), 1181 | ), 1182 | ( 1183 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1184 | .atan() 1185 | .unwrap(), // pi/6 1186 | Value::Float(d128!(0.4823479071010250)), 1187 | ), 1188 | ( 1189 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1190 | .atan() 1191 | .unwrap(), // pi/4 1192 | Value::Float(d128!(0.6657737500283540)), 1193 | ), 1194 | ( 1195 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1196 | .atan() 1197 | .unwrap(), // pi/3 1198 | Value::Float(d128!(0.8084487926300220)), 1199 | ), 1200 | ( 1201 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1202 | .atan() 1203 | .unwrap(), // pi/2 1204 | Value::Float(d128!(1.0038848218538872)), 1205 | ), 1206 | ( 1207 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1208 | .atan() 1209 | .unwrap(), // pi 1210 | Value::Float(d128!(1.2626272556789118)), 1211 | ), 1212 | ( 1213 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1214 | .atan() 1215 | .unwrap(), // 2*pi 1216 | Value::Float(d128!(1.4129651365067377)), 1217 | ), 1218 | ( 1219 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1220 | .atan() 1221 | .unwrap(), // -pi/4 1222 | Value::Float(d128!(-0.6657737500283540)), 1223 | ), 1224 | ]; 1225 | 1226 | for (output, expected) in cases { 1227 | println!("output: {}, expected: {}", &output, &expected); 1228 | let diff: Value = (output - expected).unwrap().abs(); 1229 | let delta: Value = Value::Float(d128!(1e-15)); 1230 | let verification = 1231 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1232 | assert!(verification); 1233 | } 1234 | } 1235 | 1236 | #[test] 1237 | fn function_sinh() { 1238 | let cases: Vec<(Value, Value)> = vec![ 1239 | ( 1240 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1241 | .sinh() 1242 | .unwrap(), 1243 | Value::Float(d128!(0.0)), 1244 | ), 1245 | ( 1246 | Value::Float(d128!(0)).sinh().unwrap(), 1247 | Value::Float(d128!(0)), 1248 | ), 1249 | ( 1250 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1251 | .sinh() 1252 | .unwrap(), // pi/6 1253 | Value::Float(d128!(0.5478534738880398)), 1254 | ), 1255 | ( 1256 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1257 | .sinh() 1258 | .unwrap(), // pi/4 1259 | Value::Float(d128!(0.8686709614860096)), 1260 | ), 1261 | ( 1262 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1263 | .sinh() 1264 | .unwrap(), // pi/3 1265 | Value::Float(d128!(1.2493670505239753)), 1266 | ), 1267 | ( 1268 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1269 | .sinh() 1270 | .unwrap(), // pi/2 1271 | Value::Float(d128!(2.3012989023072949)), 1272 | ), 1273 | ( 1274 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1275 | .sinh() 1276 | .unwrap(), // pi 1277 | Value::Float(d128!(11.5487393572577484)), 1278 | ), 1279 | ( 1280 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1281 | .sinh() 1282 | .unwrap(), // 2*pi 1283 | Value::Float(d128!(267.74489404101644)), 1284 | ), 1285 | ( 1286 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1287 | .sinh() 1288 | .unwrap(), // -pi/4 1289 | Value::Float(d128!(-0.8686709614860096)), 1290 | ), 1291 | ]; 1292 | 1293 | for (output, expected) in cases { 1294 | let diff: Value = (output - expected).unwrap().abs(); 1295 | let delta: Value = Value::Float(d128!(1e-15)); 1296 | let verification = 1297 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1298 | assert!(verification); 1299 | } 1300 | } 1301 | 1302 | #[test] 1303 | fn function_cosh() { 1304 | let cases: Vec<(Value, Value)> = vec![ 1305 | ( 1306 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1307 | .cosh() 1308 | .unwrap(), 1309 | Value::Float(d128!(1.0)), 1310 | ), 1311 | ( 1312 | Value::Float(d128!(0)).cosh().unwrap(), 1313 | Value::Float(d128!(1)), 1314 | ), 1315 | ( 1316 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1317 | .cosh() 1318 | .unwrap(), // pi/6 1319 | Value::Float(d128!(1.1402383210764289)), 1320 | ), 1321 | ( 1322 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1323 | .cosh() 1324 | .unwrap(), // pi/4 1325 | Value::Float(d128!(1.3246090892520057)), 1326 | ), 1327 | ( 1328 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1329 | .cosh() 1330 | .unwrap(), // pi/3 1331 | Value::Float(d128!(1.6002868577023863)), 1332 | ), 1333 | ( 1334 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1335 | .cosh() 1336 | .unwrap(), // pi/2 1337 | Value::Float(d128!(2.5091784786580567)), 1338 | ), 1339 | ( 1340 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1341 | .cosh() 1342 | .unwrap(), // pi 1343 | Value::Float(d128!(11.591953275521519)), 1344 | ), 1345 | ( 1346 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1347 | .cosh() 1348 | .unwrap(), // 2*pi 1349 | Value::Float(d128!(267.7467614837482)), 1350 | ), 1351 | ( 1352 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1353 | .cosh() 1354 | .unwrap(), // -pi/4 1355 | Value::Float(d128!(1.3246090892520057)), 1356 | ), 1357 | ]; 1358 | 1359 | for (output, expected) in cases { 1360 | println!("output: {}, expected: {}", &output, &expected); 1361 | let diff: Value = (output - expected).unwrap().abs(); 1362 | let delta: Value = Value::Float(d128!(1e-15)); 1363 | let verification = 1364 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1365 | assert!(verification); 1366 | } 1367 | } 1368 | 1369 | #[test] 1370 | fn function_tanh() { 1371 | let cases: Vec<(Value, Value)> = vec![ 1372 | ( 1373 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1374 | .tanh() 1375 | .unwrap(), 1376 | Value::Float(d128!(0.0)), 1377 | ), 1378 | ( 1379 | Value::Float(d128!(0)).tanh().unwrap(), 1380 | Value::Float(d128!(0)), 1381 | ), 1382 | ( 1383 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1384 | .tanh() 1385 | .unwrap(), // pi/6 1386 | Value::Float(d128!(0.4804727781564516)), 1387 | ), 1388 | ( 1389 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1390 | .tanh() 1391 | .unwrap(), // pi/4 1392 | Value::Float(d128!(0.6557942026326724)), 1393 | ), 1394 | ( 1395 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1396 | .tanh() 1397 | .unwrap(), // pi/3 1398 | Value::Float(d128!(0.7807144353592678)), 1399 | ), 1400 | ( 1401 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1402 | .tanh() 1403 | .unwrap(), // pi/2 1404 | Value::Float(d128!(0.9171523356672744)), 1405 | ), 1406 | ( 1407 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1408 | .tanh() 1409 | .unwrap(), // pi 1410 | Value::Float(d128!(0.99627207622075)), 1411 | ), 1412 | ( 1413 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1414 | .tanh() 1415 | .unwrap(), // 2*pi 1416 | Value::Float(d128!(0.9999930253396107)), 1417 | ), 1418 | ( 1419 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1420 | .tanh() 1421 | .unwrap(), // -pi/4 1422 | Value::Float(d128!(-0.6557942026326724)), 1423 | ), 1424 | ]; 1425 | 1426 | for (output, expected) in cases { 1427 | println!("output: {}, expected: {}", &output, &expected); 1428 | let diff: Value = (output - expected).unwrap().abs(); 1429 | let delta: Value = Value::Float(d128!(1e-15)); 1430 | let verification = 1431 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1432 | assert!(verification); 1433 | } 1434 | } 1435 | 1436 | #[test] 1437 | fn function_asinh() { 1438 | let cases: Vec<(Value, Value)> = vec![ 1439 | ( 1440 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1441 | .asinh() 1442 | .unwrap(), 1443 | Value::Float(d128!(0.0)), 1444 | ), 1445 | ( 1446 | Value::Float(d128!(0)).asinh().unwrap(), 1447 | Value::Float(d128!(0)), 1448 | ), 1449 | ( 1450 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1451 | .asinh() 1452 | .unwrap(), // pi/6 1453 | Value::Float(d128!(0.5022189850346117)), 1454 | ), 1455 | ( 1456 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1457 | .asinh() 1458 | .unwrap(), // pi/4 1459 | Value::Float(d128!(0.7212254887267798)), 1460 | ), 1461 | ( 1462 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1463 | .asinh() 1464 | .unwrap(), // pi/3 1465 | Value::Float(d128!(0.9143566553928861)), 1466 | ), 1467 | ( 1468 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1469 | .asinh() 1470 | .unwrap(), // pi/2 1471 | Value::Float(d128!(1.233403117511217)), 1472 | ), 1473 | ( 1474 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1475 | .asinh() 1476 | .unwrap(), // pi 1477 | Value::Float(d128!(1.8622957433108482)), 1478 | ), 1479 | ( 1480 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1481 | .asinh() 1482 | .unwrap(), // 2*pi 1483 | Value::Float(d128!(2.537297501373361)), 1484 | ), 1485 | ( 1486 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1487 | .asinh() 1488 | .unwrap(), // -pi/4 1489 | Value::Float(d128!(-0.7212254887267798)), 1490 | ), 1491 | ]; 1492 | 1493 | for (output, expected) in cases { 1494 | println!("output: {}, expected: {}", &output, &expected); 1495 | let diff: Value = (output - expected).unwrap().abs(); 1496 | let delta: Value = Value::Float(d128!(1e-15)); 1497 | let verification = 1498 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1499 | assert!(verification); 1500 | } 1501 | } 1502 | 1503 | #[test] 1504 | fn function_acosh() { 1505 | let cases: Vec<(Value, Value)> = vec![ 1506 | ( 1507 | Value::Float(d128!(1.047197551196597746154214461093167628)) 1508 | .acosh() 1509 | .unwrap(), // pi/3 1510 | Value::Float(d128!(0.30604210861326614)), 1511 | ), 1512 | ( 1513 | Value::Float(d128!(1.57079632679489661923132169163975144)) 1514 | .acosh() 1515 | .unwrap(), // pi/2 1516 | Value::Float(d128!(1.0232274785475506)), 1517 | ), 1518 | ( 1519 | Value::Float(d128!(3.14159265358979323846264338327950288)) 1520 | .acosh() 1521 | .unwrap(), // pi 1522 | Value::Float(d128!(1.8115262724608532)), 1523 | ), 1524 | ( 1525 | Value::Float(d128!(6.28318530717958647692528676655900576)) 1526 | .acosh() 1527 | .unwrap(), // 2*pi 1528 | Value::Float(d128!(2.524630659933467)), 1529 | ), 1530 | ]; 1531 | 1532 | for (output, expected) in cases { 1533 | println!("output: {}, expected: {}", &output, &expected); 1534 | let diff: Value = (output - expected).unwrap().abs(); 1535 | let delta: Value = Value::Float(d128!(1e-15)); 1536 | let verification = 1537 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1538 | assert!(verification); 1539 | } 1540 | 1541 | assert!(Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1542 | .acosh() 1543 | .unwrap() 1544 | .is_nan()); 1545 | assert!(Value::Float(d128!(0.0)).acosh().unwrap().is_nan()); 1546 | assert!(Value::Float(d128!(0.52359877559829887307710723054658381)) 1547 | .acosh() 1548 | .unwrap() 1549 | .is_nan()); // pi/6 1550 | assert!(Value::Float(d128!(0.785398163397448309615660845819875721)) 1551 | .acosh() 1552 | .unwrap() 1553 | .is_nan()); // pi/4 1554 | assert!(Value::Float(d128!(-0.785398163397448309615660845819875721)) 1555 | .acosh() 1556 | .unwrap() 1557 | .is_nan()); // -pi/4 1558 | } 1559 | 1560 | #[test] 1561 | fn function_atanh() { 1562 | let cases: Vec<(Value, Value)> = vec![ 1563 | ( 1564 | Value::Integral(BigInt::from(0), IntegralFmt::Dec) 1565 | .atanh() 1566 | .unwrap(), 1567 | Value::Float(d128!(0.0)), 1568 | ), 1569 | ( 1570 | Value::Float(d128!(0)).atanh().unwrap(), 1571 | Value::Float(d128!(0)), 1572 | ), 1573 | ( 1574 | Value::Float(d128!(0.52359877559829887307710723054658381)) 1575 | .atanh() 1576 | .unwrap(), // pi/6 1577 | Value::Float(d128!(0.5812850116947232)), 1578 | ), 1579 | ( 1580 | Value::Float(d128!(0.785398163397448309615660845819875721)) 1581 | .atanh() 1582 | .unwrap(), // pi/4 1583 | Value::Float(d128!(1.0593061708232432)), 1584 | ), 1585 | ( 1586 | Value::Float(d128!(-0.785398163397448309615660845819875721)) 1587 | .atanh() 1588 | .unwrap(), // -pi/4 1589 | Value::Float(d128!(-1.0593061708232432)), 1590 | ), 1591 | ]; 1592 | 1593 | for (output, expected) in cases { 1594 | println!("output: {}, expected: {}", &output, &expected); 1595 | let diff: Value = (output - expected).unwrap().abs(); 1596 | let delta: Value = Value::Float(d128!(1e-15)); 1597 | let verification = 1598 | diff.as_float().unwrap() < delta.as_float().unwrap(); 1599 | assert!(verification); 1600 | } 1601 | 1602 | assert!(Value::Float(d128!(1.047197551196597746154214461093167628)) 1603 | .atanh() 1604 | .unwrap() 1605 | .is_nan()); // pi/6 1606 | assert!(Value::Float(d128!(1.57079632679489661923132169163975144)) 1607 | .atanh() 1608 | .unwrap() 1609 | .is_nan()); // pi/6 1610 | assert!(Value::Float(d128!(3.14159265358979323846264338327950288)) 1611 | .atanh() 1612 | .unwrap() 1613 | .is_nan()); // pi/6 1614 | assert!(Value::Float(d128!(6.28318530717958647692528676655900576)) 1615 | .atanh() 1616 | .unwrap() 1617 | .is_nan()); // pi/6 1618 | } 1619 | } 1620 | --------------------------------------------------------------------------------