├── .gitattributes ├── .github └── workflows │ ├── deploy.yml │ ├── preview.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml ├── src ├── evaluator.rs ├── lexer.rs ├── lib.rs ├── lookup.rs ├── main.rs ├── parser.rs └── units.rs └── web ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── src ├── app.css ├── app.d.ts ├── app.html ├── lib │ └── helpers.ts └── routes │ ├── +layout.svelte │ ├── +layout.ts │ └── +page.svelte ├── static ├── apple-touch-icon.png ├── favicon-96x96.png ├── favicon.ico ├── favicon.svg ├── site.webmanifest ├── web-app-manifest-192x192.png └── web-app-manifest-512x512.png ├── svelte.config.js ├── tsconfig.json └── vite.config.ts /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - main 6 | workflow_dispatch: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | web: 13 | runs-on: ubuntu-latest 14 | defaults: 15 | run: 16 | working-directory: web 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: '22' 24 | 25 | - name: Install dependencies 26 | run: npm ci 27 | 28 | - name: Build SvelteKit project 29 | run: npm run build 30 | 31 | - name: Deploy Project Artifacts to Vercel 32 | run: npx vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} 33 | env: 34 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} 35 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} 36 | -------------------------------------------------------------------------------- /.github/workflows/preview.yml: -------------------------------------------------------------------------------- 1 | name: Preview 2 | on: 3 | push: 4 | branches-ignore: 5 | - main 6 | workflow_dispatch: 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | web: 13 | runs-on: ubuntu-latest 14 | defaults: 15 | run: 16 | working-directory: web 17 | steps: 18 | - uses: actions/checkout@v3 19 | 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: '22' 24 | 25 | - name: Install dependencies 26 | run: npm ci 27 | 28 | - name: Build SvelteKit project 29 | run: npm run build 30 | 31 | - name: Deploy Project Artifacts to Vercel 32 | run: npx vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} 33 | env: 34 | VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} 35 | VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} 36 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: write 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | create-release: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Create Release 19 | uses: softprops/action-gh-release@v1 20 | with: 21 | token: ${{ secrets.GITHUB_TOKEN }} 22 | name: '' 23 | draft: true 24 | 25 | upload: 26 | needs: create-release 27 | strategy: 28 | matrix: 29 | include: 30 | - os: macos-latest 31 | target: x86_64-apple-darwin 32 | archive: macos-x64 33 | - os: macos-latest 34 | target: aarch64-apple-darwin 35 | archive: macos-aarch64 36 | - os: ubuntu-latest 37 | target: x86_64-unknown-linux-gnu 38 | archive: linux-x64 39 | - os: ubuntu-latest 40 | target: aarch64-unknown-linux-gnu 41 | archive: linux-aarch64 42 | - os: windows-latest 43 | target: x86_64-pc-windows-gnu 44 | archive: windows-x64 45 | - os: windows-latest 46 | target: aarch64-pc-windows-msvc 47 | archive: windows-aarch64 48 | runs-on: ${{ matrix.os }} 49 | steps: 50 | - uses: actions/checkout@v3 51 | - name: Build & upload 52 | uses: taiki-e/upload-rust-binary-action@v1 53 | with: 54 | bin: cpc 55 | target: ${{ matrix.target }} 56 | archive: $bin-$tag-${{ matrix.archive }} 57 | zip: all 58 | tar: none 59 | token: ${{ secrets.GITHUB_TOKEN }} 60 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Rust setup 19 | uses: dtolnay/rust-toolchain@stable 20 | 21 | - run: cargo test 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": false 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 3.0.0 - 2025 May 30 4 | - Create a [website interface](https://cpc.kasper.space) 5 | - Add wasm version 6 | - Switch to the `fastnum` crate's d128 7 | - Fix `exp` function 8 | 9 | ## 2.0.0 - 2025 May 30 10 | - Remove the `degrees` keyword which referred to `celcius` by default 11 | - Remove the `default_degrees` argument from `eval()` and `lex()`. Not necessary now that the `degrees` keyword is removed 12 | - Fix trigonometry imprecision 13 | - Fix unnecessary scientific notation 14 | 15 | ## 1.9.3 - 2023 sep 20 16 | - Fix negative unary `-` always having higher precedence than `^`. This resulted in `-3^2` returning `9` instead of `-9` 17 | 18 | ## 1.9.2 - 2023 Jul 11 19 | - Fix automatic light year unit not chosen for large distances (@gcomte) 20 | 21 | ## 1.9.1 - 2023 Mar 30 22 | - Improve formatting of numbers 23 | - Remove unnecessary dependencies (@jqnatividad) 24 | 25 | ## 1.9.0 - 2022 Dec 30 26 | - Add `marathon` unit 27 | - Add `aarch64` binaries 28 | 29 | ## 1.8.0 - 2021 Aug 16 30 | - Add support for data transfer rate units (like mb/s) 31 | - Add support for dividing length by speed (like 10 km / 100 kph) 32 | - Fix implicit start/end parentheses 33 | 34 | ## 1.7.0 - 2021 Jul 14 35 | - Add operator words `plus`, `minus` and `times` 36 | - Add operator phrases `multiplied by` and `divided by` 37 | - Add operator symbol `÷` 38 | - Disallow named number followed by smaller named number (like 1 million thousand) 39 | - Fix/improve parsing of multi-word units 40 | - Fix light second parsed as light year 41 | - Fix `Ω` lexing 42 | - Fix lexing of rpm units 43 | 44 | ## 1.6.0 - 2021 Jul 3 45 | - Add support for non-US "metre" and "litre" spellings 46 | - Add help menu 47 | - Add `--version` flag 48 | - Freak out instead of ignoring unexpected arguments 49 | - Print errors to STDERR 50 | - Fix decimeter parsed as centimeter 51 | 52 | ## 1.5.1 - 2021 Jun 10 53 | - Fix numbers unnecessarily displayed in E notation 54 | 55 | ## 1.5.0 - 2021 Apr 21 56 | - Remove `TokenVector` type 57 | - Rename `--debug` to `--verbose` and `-v` 58 | - Allow CLI flags before input 59 | - Fix panic when input contains only whitespace and/or commas 60 | 61 | ## 1.4.2 - 2021 Apr 8 62 | - Fix d128 errors due to d128 error status not being cleared 63 | 64 | ## 1.4.1 - 2021 Apr 8 65 | - Fix panic when input is empty string 66 | 67 | ## 1.4.0 - 2021 Feb 8 68 | - Made cpc case insensitive 69 | - Switch back to official `decimal` because [decimal#59](https://github.com/alkis/decimal/issues/59) is fixed. 70 | 71 | ## 1.3.2 - 2021 Feb 8 72 | - Fix incorrect parsing of named numbers `Duodecillion` and greater 73 | 74 | ## 1.3.1 - 2021 Jan 14 75 | - Fix spelling of `Celsius` (@joseluis) 76 | 77 | ## 1.3.0 - 2020 Nov 29 78 | - Added unit of mass `Stone` 79 | - Added keyword `pounds-force` (used for `PoundsPerSquareInch`) 80 | - Fixed lexing of `Pound` 81 | 82 | ## 1.2.0 - 2020 Nov 26 83 | - Added units of electric current 84 | - Added units of voltage 85 | - Added units of resistance 86 | - Added support for `Voltage * ElectricCurrent` 87 | - Added support for `Voltage / ElectricCurrent` 88 | - Added support for `Voltage / Resistance` 89 | - Added support for `Power / ElectricCurrent` 90 | - Added support for `Power / Voltage` 91 | - Added support for `Power * Time` 92 | - Added support for `ElectricCurrent * Resistance` 93 | - Added support for `Energy / Time` 94 | - Fixed dividing a unit by `NoUnit` resulting in `NoUnit` 95 | - Fixed interpreting of `µs` 96 | - Fixed panics caused in Rust `1.48.0` by switching `decimal` dependency to `decimal_fixes_mirror` 97 | 98 | ## 1.1.0 - 2020 Nov 14 99 | - Added units of frequency 100 | - Added support using foot-inch syntax with addition, like `2"+6'4"` 101 | - Unsupported foot-inch syntax like `(6)'4"` and `6'4!"` now cause errors 102 | - Fixed README.md stating the performance is 1000x slower than it actually is 103 | - Fixed trailing percentage signs being ignored when `allow_trailing_operators` is true 104 | - Fixed error caused by consecutive percentage signs 105 | 106 | ## 1.0.2 - 2020 Oct 12 107 | - Fix parsing of unit `Quarter` (@ethwu) 108 | - Use division instead of multiplication when dividing numbers of the same unit `Quarter` (@ethwu) 109 | 110 | ## 1.0.1 - 2020 Aug 20 111 | - Fixed the library not working 112 | - Added documentation comments 113 | - Added docs.rs documentation link 114 | - Various fixes and improvements 115 | 116 | ## 1.0.0 - 2020 Aug 20 117 | - Initial release 118 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "autocfg" 16 | version = "1.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 19 | 20 | [[package]] 21 | name = "bnum" 22 | version = "0.12.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "f781dba93de3a5ef6dc5b17c9958b208f6f3f021623b360fb605ea51ce443f10" 25 | 26 | [[package]] 27 | name = "bumpalo" 28 | version = "3.17.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 31 | 32 | [[package]] 33 | name = "cfg-if" 34 | version = "1.0.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 37 | 38 | [[package]] 39 | name = "console_error_panic_hook" 40 | version = "0.1.7" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" 43 | dependencies = [ 44 | "cfg-if", 45 | "wasm-bindgen", 46 | ] 47 | 48 | [[package]] 49 | name = "cpc" 50 | version = "3.0.0" 51 | dependencies = [ 52 | "console_error_panic_hook", 53 | "fastnum", 54 | "js-sys", 55 | "regex", 56 | "unicode-segmentation", 57 | "wasm-bindgen", 58 | "web-time", 59 | ] 60 | 61 | [[package]] 62 | name = "fastnum" 63 | version = "0.2.9" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "b875e26379edd7866a74cec720c6ae7bea3107aaf9fd3b14d7449d87d4c1986b" 66 | dependencies = [ 67 | "autocfg", 68 | "bnum", 69 | ] 70 | 71 | [[package]] 72 | name = "js-sys" 73 | version = "0.3.77" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 76 | dependencies = [ 77 | "once_cell", 78 | "wasm-bindgen", 79 | ] 80 | 81 | [[package]] 82 | name = "log" 83 | version = "0.4.27" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 86 | 87 | [[package]] 88 | name = "memchr" 89 | version = "2.7.4" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 92 | 93 | [[package]] 94 | name = "once_cell" 95 | version = "1.21.3" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 98 | 99 | [[package]] 100 | name = "proc-macro2" 101 | version = "1.0.95" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 104 | dependencies = [ 105 | "unicode-ident", 106 | ] 107 | 108 | [[package]] 109 | name = "quote" 110 | version = "1.0.40" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 113 | dependencies = [ 114 | "proc-macro2", 115 | ] 116 | 117 | [[package]] 118 | name = "regex" 119 | version = "1.11.1" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 122 | dependencies = [ 123 | "aho-corasick", 124 | "memchr", 125 | "regex-automata", 126 | "regex-syntax", 127 | ] 128 | 129 | [[package]] 130 | name = "regex-automata" 131 | version = "0.4.9" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 134 | dependencies = [ 135 | "aho-corasick", 136 | "memchr", 137 | "regex-syntax", 138 | ] 139 | 140 | [[package]] 141 | name = "regex-syntax" 142 | version = "0.8.5" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 145 | 146 | [[package]] 147 | name = "rustversion" 148 | version = "1.0.21" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" 151 | 152 | [[package]] 153 | name = "syn" 154 | version = "2.0.101" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" 157 | dependencies = [ 158 | "proc-macro2", 159 | "quote", 160 | "unicode-ident", 161 | ] 162 | 163 | [[package]] 164 | name = "unicode-ident" 165 | version = "1.0.18" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 168 | 169 | [[package]] 170 | name = "unicode-segmentation" 171 | version = "1.12.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 174 | 175 | [[package]] 176 | name = "wasm-bindgen" 177 | version = "0.2.100" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 180 | dependencies = [ 181 | "cfg-if", 182 | "once_cell", 183 | "rustversion", 184 | "wasm-bindgen-macro", 185 | ] 186 | 187 | [[package]] 188 | name = "wasm-bindgen-backend" 189 | version = "0.2.100" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 192 | dependencies = [ 193 | "bumpalo", 194 | "log", 195 | "proc-macro2", 196 | "quote", 197 | "syn", 198 | "wasm-bindgen-shared", 199 | ] 200 | 201 | [[package]] 202 | name = "wasm-bindgen-macro" 203 | version = "0.2.100" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 206 | dependencies = [ 207 | "quote", 208 | "wasm-bindgen-macro-support", 209 | ] 210 | 211 | [[package]] 212 | name = "wasm-bindgen-macro-support" 213 | version = "0.2.100" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 216 | dependencies = [ 217 | "proc-macro2", 218 | "quote", 219 | "syn", 220 | "wasm-bindgen-backend", 221 | "wasm-bindgen-shared", 222 | ] 223 | 224 | [[package]] 225 | name = "wasm-bindgen-shared" 226 | version = "0.2.100" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 229 | dependencies = [ 230 | "unicode-ident", 231 | ] 232 | 233 | [[package]] 234 | name = "web-time" 235 | version = "1.1.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 238 | dependencies = [ 239 | "js-sys", 240 | "wasm-bindgen", 241 | ] 242 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cpc" 3 | version = "3.0.0" 4 | description = "evaluates math expressions, with support for units and conversion between units" 5 | authors = ["Kasper Henningsen"] 6 | edition = "2024" 7 | readme = "README.md" 8 | license = "MIT" 9 | homepage = "https://github.com/probablykasper/cpc#readme" 10 | repository = "https://github.com/probablykasper/cpc" 11 | documentation = "https://docs.rs/cpc" 12 | keywords = ["math", "expression", "evaluate", "units", "convert"] 13 | categories = [ 14 | "mathematics", 15 | "science", 16 | "parsing", 17 | "text-processing", 18 | "value-formatting", 19 | ] 20 | 21 | [lib] 22 | crate-type = ["cdylib", "rlib"] 23 | 24 | [dependencies] 25 | fastnum = "0.2" 26 | unicode-segmentation = "1.12" 27 | web-time = "1.1.0" 28 | 29 | [target.'cfg(target_arch = "wasm32")'.dependencies] 30 | console_error_panic_hook = "0.1.7" 31 | wasm-bindgen = "0.2" 32 | js-sys = "0.3" 33 | 34 | [dev-dependencies] 35 | regex = "1.11" 36 | 37 | [lints.clippy] 38 | comparison_chain = "allow" 39 | if_same_then_else = "allow" 40 | match_like_matches_macro = "allow" 41 | get_first = "allow" 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Kasper Henningsen 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpc 2 | 3 | calculation + conversion 4 | 5 | cpc parses and evaluates strings of math, with support for units and conversion. 128-bit decimal floating points are used for high accuracy. 6 | 7 | It also lets you mix units, so for example `1 km - 1m` results in `999 Meter`. 8 | 9 | [![Crates.io](https://img.shields.io/crates/v/cpc.svg)](https://crates.io/crates/cpc) 10 | [![Documentation](https://docs.rs/cpc/badge.svg)](https://docs.rs/cpc) 11 | 12 | [List of all supported units](https://docs.rs/cpc/latest/cpc/units/enum.Unit.html) 13 | 14 | ## [Web Interface](https://cpc.kasper.space) 15 | 16 | Try it out at [cpc.kasper.space](https://cpc.kasper.space) 17 | 18 | ## CLI Installation 19 | Install using `cargo`: 20 | ``` 21 | cargo install cpc 22 | ``` 23 | 24 | To install it manually, grab the appropriate binary from the [GitHub Releases page](https://github.com/probablykasper/cpc/releases) and place it wherever you normally place binaries on your OS. 25 | 26 | ## CLI Usage 27 | ``` 28 | cpc '2h/3 to min' 29 | ``` 30 | 31 | ## Examples 32 | ``` 33 | 3 + 4 * 2 34 | 35 | 8 % 3 36 | 37 | (4 + 1)km to light years 38 | 39 | 10m/2s * 5 trillion s 40 | 41 | 1 lightyear * 0.001mm in km2 42 | 43 | 1m/s + 1mi/h in kilometers per h 44 | 45 | round(sqrt(2)^4)! liters 46 | 47 | 10% of abs(sin(pi)) horsepower to watts 48 | ``` 49 | 50 | ## Supported unit types 51 | - Normal numbers 52 | - Time 53 | - Length 54 | - Area 55 | - Volume 56 | - Mass 57 | - Digital storage (bytes etc) 58 | - Energy 59 | - Power 60 | - Electric current 61 | - Resistance 62 | - Voltage 63 | - Pressure 64 | - Frequency 65 | - Speed 66 | - Temperature 67 | 68 | ## API Installation 69 | Add `cpc` as a dependency in `Cargo.toml`. 70 | 71 | ## API Usage 72 | 73 | ```rust 74 | use cpc::eval; 75 | use cpc::units::Unit; 76 | 77 | match eval("3m + 1cm", true, Unit::Celsius, false) { 78 | Ok(answer) => { 79 | // answer: Number { value: 301, unit: Unit::Centimeter } 80 | println!("Evaluated value: {} {:?}", answer.value, answer.unit) 81 | }, 82 | Err(e) => { 83 | println!("{e}") 84 | } 85 | } 86 | ``` 87 | 88 | ## Accuracy 89 | cpc uses 128-bit Decimal Floating Point (d128) numbers instead of Binary Coded Decimals for better accuracy. The result cpc gives will still not always be 100% accurate. I would recommend rounding the result to 20 decimals or less. 90 | 91 | ## Dev Instructions 92 | 93 | ### Get started 94 | Install [Rust](https://www.rust-lang.org). 95 | 96 | Run cpc with a CLI argument as input: 97 | ``` 98 | cargo run -- '100ms to s' 99 | ``` 100 | 101 | Run in verbose mode, which shows some extra logs: 102 | ``` 103 | cargo run -- '100ms to s' --verbose 104 | ``` 105 | 106 | Run tests: 107 | ``` 108 | cargo test 109 | ``` 110 | 111 | Build: 112 | ``` 113 | cargo build 114 | ``` 115 | 116 | ### Adding a unit 117 | 118 | Nice resources for adding units: 119 | - https://github.com/ryantenney/gnu-units/blob/master/units.dat 120 | - https://support.google.com/websearch/answer/3284611 (unit list) 121 | - https://translatorscafe.com/unit-converter (unit conversion) 122 | - https://calculateme.com (unit conversion) 123 | - https://wikipedia.org 124 | 125 | #### 1. Add the unit 126 | In `src/units.rs`, units are specified like this: 127 | ```rs 128 | pub enum UnitType { 129 | Time, 130 | // etc 131 | } 132 | 133 | // ... 134 | 135 | create_units!( 136 | Nanosecond: (Time, d128!(1)), 137 | Microsecond: (Time, d128!(1000)), 138 | // etc 139 | ) 140 | ``` 141 | 142 | The number associated with a unit is it's "weight". For example, if a second's weight is `1`, then a minute's weight is `60`. 143 | 144 | #### 2. Add a test for the unit 145 | Make sure to also add a test for each unit. The tests look like this: 146 | ```rs 147 | assert_eq!(convert_test(1000.0, Meter, Kilometer), 1.0); 148 | ``` 149 | Basically, 1000 Meter == 1 Kilometer. 150 | 151 | #### 3. Add the unit to the lexer 152 | Text is turned into tokens (some of which are units) in `lexer.rs`. Here's one example: 153 | ```rs 154 | // ... 155 | match string { 156 | "h" | "hr" | "hrs" | "hour" | "hours" => tokens.push(Token::Unit(Hour)), 157 | // etc 158 | } 159 | // ... 160 | ``` 161 | 162 | ### Potential Improvements 163 | - Support for conversion between Power, Current, Resistance and Voltage. Multiplication and division is currently supported, but not conversions using sqrt or pow. 164 | - E notation, like 2E+10 165 | - Unit types 166 | - Currency: How to go about dynamically updating the weights? 167 | - https://api.exchangerate-api.com/v4/latest/USD 168 | - https://www.coingecko.com/en/api 169 | - https://developers.coinbase.com/api/v2 170 | - Timezones 171 | - Binary/octal/decimal/hexadecimal/base32/base64 172 | - Fuel consumption 173 | - Data transfer rate 174 | - Color codes 175 | - Force 176 | - Roman numerals 177 | - Angles 178 | - Flow rate 179 | 180 | ### Releasing a new version 181 | 182 | 1. Update `CHANGELOG.md` 183 | 2. Bump the version number in `Cargo.toml` 184 | 3. Run `cargo test` 185 | 4. Create a git tag in format `v#.#.#` 186 | 5. Add release notes to the generated GitHub release and publish it 187 | 6. Run `cargo publish` 188 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs=true 2 | -------------------------------------------------------------------------------- /src/evaluator.rs: -------------------------------------------------------------------------------- 1 | use crate::lookup::{lookup_factorial, lookup_named_number}; 2 | use crate::parser::AstNode; 3 | use crate::units::{add, convert, divide, modulo, multiply, pow, subtract, Unit, UnitType}; 4 | use crate::Constant::{Pi, E}; 5 | use crate::FunctionIdentifier::*; 6 | use crate::Operator::{Caret, Divide, Minus, Modulo, Multiply, Plus}; 7 | use crate::TextOperator::{Of, To}; 8 | use crate::UnaryOperator::{Factorial, Percent}; 9 | use crate::{Number, Token}; 10 | use fastnum::{dec128 as d, D128}; 11 | 12 | /// Evaluate an [`AstNode`] into a [`Number`] 13 | pub fn evaluate(ast: &AstNode) -> Result { 14 | let answer = evaluate_node(ast)?; 15 | Ok(answer) 16 | } 17 | 18 | /// Returns the factorial of a [`struct@d128`] up to `1000!` without doing any math 19 | /// 20 | /// Factorials do not work with decimal numbers. 21 | /// 22 | /// All return values of this function are hard-coded. 23 | pub fn factorial(input: D128) -> D128 { 24 | lookup_factorial(input.try_into().unwrap()) 25 | } 26 | 27 | /// Returns the square root of a [`struct@d128`] 28 | pub fn sqrt(input: D128) -> D128 { 29 | let mut n = d!(1); 30 | let half = d!(0.5); 31 | for _ in 0..10 { 32 | n = (n + input / n) * half; 33 | } 34 | n 35 | } 36 | 37 | /// Returns the cube root of a [`struct@d128`] 38 | pub fn cbrt(input: D128) -> D128 { 39 | let mut n: D128 = input; 40 | // hope that 20 iterations makes it accurate enough 41 | let three = d!(3); 42 | for _ in 0..20 { 43 | let z2 = n * n; 44 | n = n - ((n * z2 - input) / (three * z2)); 45 | } 46 | n 47 | } 48 | 49 | /// Returns the sine of a [`struct@d128`] 50 | pub fn sin(input: D128) -> D128 { 51 | let result =input.sin(); 52 | match result.is_zero() { 53 | true => D128::ZERO, 54 | false => result, 55 | } 56 | } 57 | 58 | /// Returns the cosine of a [`struct@d128`] 59 | pub fn cos(input: D128) -> D128 { 60 | input.cos() 61 | } 62 | 63 | /// Returns the tangent of a [`struct@d128`] 64 | pub fn tan(input: D128) -> D128 { 65 | input.tan() 66 | } 67 | 68 | /// Evaluate an [`AstNode`] into a [`Number`] 69 | fn evaluate_node(ast_node: &AstNode) -> Result { 70 | let token = &ast_node.token; 71 | let children = &ast_node.children; 72 | match token { 73 | Token::Number(number) => Ok(Number::new(*number, Unit::NoUnit)), 74 | Token::Constant(constant) => match constant { 75 | Pi => Ok(Number::new( 76 | D128::PI, 77 | Unit::NoUnit, 78 | )), 79 | E => Ok(Number::new( 80 | D128::E, 81 | Unit::NoUnit, 82 | )), 83 | }, 84 | Token::FunctionIdentifier(function) => { 85 | let child_node = children.get(0).ok_or("Paren has no child[0]")?; 86 | let child_answer = evaluate_node(child_node)?; 87 | match function { 88 | Cbrt => { 89 | if child_answer.unit.category() == UnitType::NoType { 90 | let result = cbrt(child_answer.value); 91 | Ok(Number::new(result, child_answer.unit)) 92 | } else { 93 | Err("log() only accepts UnitType::NoType".to_string()) 94 | } 95 | } 96 | Sqrt => { 97 | if child_answer.unit.category() == UnitType::NoType { 98 | let result = sqrt(child_answer.value); 99 | Ok(Number::new(result, child_answer.unit)) 100 | } else { 101 | Err("log() only accepts UnitType::NoType".to_string()) 102 | } 103 | } 104 | Log => { 105 | if child_answer.unit.category() == UnitType::NoType { 106 | let result = child_answer.value.log10(); 107 | Ok(Number::new(result, child_answer.unit)) 108 | } else { 109 | Err("log() only accepts UnitType::NoType".to_string()) 110 | } 111 | } 112 | Ln => { 113 | if child_answer.unit.category() == UnitType::NoType { 114 | let result = child_answer.value.ln(); 115 | Ok(Number::new(result, child_answer.unit)) 116 | } else { 117 | Err("ln() only accepts UnitType::NoType".to_string()) 118 | } 119 | } 120 | Exp => { 121 | if child_answer.unit.category() == UnitType::NoType { 122 | let result = child_answer.value.exp(); 123 | Ok(Number::new(result, child_answer.unit)) 124 | } else { 125 | Err("exp() only accepts UnitType::NoType".to_string()) 126 | } 127 | } 128 | Round => { 129 | // .quantize() rounds .5 to nearest even integer, so we correct that 130 | let mut result = child_answer.value.quantize(d!(1)); 131 | let rounding_change = result - child_answer.value; 132 | // If the result was rounded down by 0.5, correct by +1 133 | if rounding_change == d!(-0.5) { 134 | result += d!(1); 135 | } 136 | Ok(Number::new(result, child_answer.unit)) 137 | } 138 | Ceil => { 139 | let mut result = child_answer.value.quantize(d!(1)); 140 | let rounding_change = result - child_answer.value; 141 | if rounding_change.is_negative() { 142 | result += d!(1); 143 | } 144 | Ok(Number::new(result, child_answer.unit)) 145 | } 146 | Floor => { 147 | let mut result = child_answer.value.quantize(d!(1)); 148 | let rounding_change = result - child_answer.value; 149 | if !rounding_change.is_negative() { 150 | result -= d!(1); 151 | } 152 | Ok(Number::new(result, child_answer.unit)) 153 | } 154 | Abs => { 155 | let mut result = child_answer.value.abs(); 156 | let rounding_change = result - child_answer.value; 157 | if rounding_change == d!(-0.5) { 158 | result += d!(1); 159 | } 160 | Ok(Number::new(result, child_answer.unit)) 161 | } 162 | Sin => { 163 | let result = sin(child_answer.value); 164 | Ok(Number::new(result, child_answer.unit)) 165 | } 166 | Cos => { 167 | let result = cos(child_answer.value); 168 | Ok(Number::new(result, child_answer.unit)) 169 | } 170 | Tan => { 171 | let result = tan(child_answer.value); 172 | Ok(Number::new(result, child_answer.unit)) 173 | } 174 | } 175 | } 176 | Token::Unit(unit) => { 177 | let child_node = children.get(0).ok_or("Unit has no child[0]")?; 178 | let child_answer = evaluate_node(child_node)?; 179 | Ok(Number::new(child_answer.value, *unit)) 180 | } 181 | Token::Negative => { 182 | let child_node = children.get(0).ok_or("Negative has no child[0]")?; 183 | let child_answer = evaluate_node(child_node)?; 184 | Ok(Number::new(-child_answer.value, child_answer.unit)) 185 | } 186 | Token::Paren => { 187 | let child_node = children.get(0).ok_or("Paren has no child[0]")?; 188 | evaluate_node(child_node) 189 | } 190 | Token::UnaryOperator(operator) => { 191 | let child_node = children 192 | .get(0) 193 | .ok_or(format!("Token {:?} has no child[0]", token))?; 194 | let child_answer = evaluate_node(child_node)?; 195 | match operator { 196 | Percent => Ok(Number::new( 197 | child_answer.value / d!(100), 198 | child_answer.unit, 199 | )), 200 | Factorial => { 201 | let result = factorial(child_answer.value); 202 | if result.is_nan() { 203 | return Err( 204 | "Can only perform factorial on integers from 0 to 1000".to_string() 205 | ); 206 | } 207 | Ok(Number::new(result, child_answer.unit)) 208 | } 209 | } 210 | } 211 | Token::NamedNumber(named_number) => { 212 | let child_node = children 213 | .get(0) 214 | .ok_or(format!("Token {:?} has no child[0]", token))?; 215 | let named_number_value = lookup_named_number(named_number); 216 | if let Token::NamedNumber(child_nn) = &child_node.token { 217 | let child_nn_value = lookup_named_number(child_nn); 218 | if child_nn_value > named_number_value { 219 | return Err(format!("Unexpected smaller token {:?}", token)); 220 | } 221 | } 222 | let child_answer = evaluate_node(child_node)?; 223 | let result = child_answer.value * named_number_value; 224 | Ok(Number::new(result, child_answer.unit)) 225 | } 226 | Token::TextOperator(operator) => { 227 | let left_child = children 228 | .get(0) 229 | .ok_or(format!("Token {:?} has no child[0]", token))?; 230 | let right_child = children 231 | .get(1) 232 | .ok_or(format!("Token {:?} has no child[1]", token))?; 233 | 234 | match operator { 235 | To => { 236 | if let Token::Unit(right_unit) = right_child.token { 237 | let left = evaluate_node(left_child)?; 238 | let result = convert(left, right_unit)?; 239 | Ok(result) 240 | } else { 241 | Err("Right side of To operator needs to be a unit".to_string()) 242 | } 243 | } 244 | Of => { 245 | let left = evaluate_node(left_child)?; 246 | let right = evaluate_node(right_child)?; 247 | if left.unit == Unit::NoUnit { 248 | Ok(Number::new(left.value * right.value, right.unit)) 249 | } else { 250 | Err("Left side of the Of operator must be NoUnit".to_string()) 251 | } 252 | } 253 | } 254 | } 255 | Token::Operator(operator) => { 256 | let left_child = children 257 | .get(0) 258 | .ok_or(format!("Token {:?} has no child[0]", token))?; 259 | let right_child = children 260 | .get(1) 261 | .ok_or(format!("Token {:?} has no child[1]", token))?; 262 | let left = evaluate_node(left_child)?; 263 | let right = evaluate_node(right_child)?; 264 | match operator { 265 | Plus => Ok(add(left, right)?), 266 | Minus => Ok(subtract(left, right)?), 267 | Multiply => Ok(multiply(left, right)?), 268 | Divide => Ok(divide(left, right)?), 269 | Modulo => Ok(modulo(left, right)?), 270 | Caret => Ok(pow(left, right)?), 271 | _ => Err(format!("Unexpected operator {:?}", operator)), 272 | } 273 | } 274 | _ => Err(format!("Unexpected token {:?}", token)), 275 | } 276 | } 277 | 278 | #[cfg(test)] 279 | mod tests { 280 | use crate::eval; 281 | use super::*; 282 | 283 | fn eval_default<'a>(input: &'a str) -> Number { 284 | let result = eval(input, true, false).unwrap(); 285 | result 286 | } 287 | fn eval_num<'a>(input: &'a str) -> String { 288 | let result = eval(input, true, false).unwrap(); 289 | assert_eq!(result.unit, Unit::NoUnit); 290 | 291 | result.to_string() 292 | } 293 | 294 | #[test] 295 | fn test_evaluations() { 296 | assert_eq!(eval_num("-2(-3)"), "6"); 297 | assert_eq!(eval_num("-2(3)"), "-6"); 298 | assert_eq!(eval_num("(3)-2"), "1"); 299 | assert_eq!(eval_default("-1km to m"), Number::new(d!(-1000), Unit::Meter)); 300 | assert_eq!(eval_num("2*-3*0.5"), "-3"); 301 | assert_eq!(eval_num("-3^2"), "-9"); 302 | assert_eq!(eval_num("e^2"), "7.3890560989306502272304274605750078132"); 303 | assert_eq!(eval_num("e^2.5"), "12.1824939607034734380701759511679661832"); 304 | assert_eq!(eval_num("-1+2"), "1"); 305 | } 306 | 307 | #[test] 308 | fn test_functions() { 309 | assert_eq!(eval_num("cbrt(125)"), "5"); 310 | 311 | assert_eq!(eval_num("sqrt(25)"), "5"); 312 | 313 | assert_eq!(eval_num("log(100)"), "2"); 314 | assert_eq!(eval_num("log(2)"), "0.301029995663981195213738894724493026768"); 315 | 316 | assert_eq!(eval_num("ln(1)"), "0"); 317 | assert_eq!(eval_num("ln(2)"), "0.69314718055994530941723212145817656808"); 318 | assert_eq!(eval_num("ln(e)"), "1"); 319 | assert_eq!(eval_num("ln(e^2)"), "2"); 320 | 321 | assert_eq!(eval_num("exp(1)"), "2.71828182845904523536028747135266249776"); 322 | 323 | assert_eq!(eval_num("round(1.4)"), "1"); 324 | assert_eq!(eval_num("round(1.6)"), "2"); 325 | assert_eq!(eval_num("round(1.5)"), "2"); 326 | assert_eq!(eval_num("round(2.5)"), "3"); 327 | 328 | assert_eq!(eval_num("ceil(1.5)"), "2"); 329 | assert_eq!(eval_num("ceil(-1.5)"), "-1"); 330 | 331 | assert_eq!(eval_num("floor(1.5)"), "1"); 332 | assert_eq!(eval_num("floor(-1.5)"), "-2"); 333 | 334 | assert_eq!(eval_num("abs(-3)"), "3"); 335 | 336 | assert_eq!(eval_num("sin(2)"), "0.9092974268256816953960198659117448427"); 337 | assert_eq!(eval_num("sin(-2)"), "-0.9092974268256816953960198659117448427"); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! calculation + conversion 2 | //! 3 | //! cpc parses and evaluates strings of math, with support for units and conversion. 128-bit decimal floating points are used for high accuracy. 4 | //! 5 | //! cpc lets you mix units, so for example 1 km - 1m results in Number { value: 999, unit: Meter }. 6 | //! 7 | //! Check out the [list of supported units](units/enum.Unit.html) 8 | //! 9 | //! # Example usage 10 | //! ```rust 11 | //! use cpc::eval; 12 | //! use cpc::units::Unit; 13 | //! 14 | //! match eval("3m + 1cm", true, false) { 15 | //! Ok(answer) => { 16 | //! // answer: Number { value: 301, unit: Unit::Centimeter } 17 | //! println!("Evaluated value: {} {:?}", answer.value, answer.unit) 18 | //! }, 19 | //! Err(e) => { 20 | //! println!("{e}") 21 | //! } 22 | //! } 23 | //! ``` 24 | 25 | use crate::units::Unit; 26 | use fastnum::{dec128 as d, D128}; 27 | use std::fmt::{self, Display}; 28 | use web_time::Instant; 29 | 30 | /// Turns an [`AstNode`](parser::AstNode) into a [`Number`] 31 | pub mod evaluator; 32 | /// Turns a string into [`Token`]s 33 | #[rustfmt::skip] 34 | pub mod lexer; 35 | #[rustfmt::skip] 36 | mod lookup; 37 | /// Turns [`Token`]s into an [`AstNode`](parser::AstNode) 38 | pub mod parser; 39 | /// Units, and functions you can use with them 40 | #[rustfmt::skip] 41 | pub mod units; 42 | 43 | #[derive(Clone, Debug, PartialEq)] 44 | /// A number with a `Unit`. 45 | /// 46 | /// Example: 47 | /// ```rust 48 | /// use cpc::{eval,Number}; 49 | /// use cpc::units::Unit; 50 | /// use fastnum::dec128; 51 | /// 52 | /// let x = Number { 53 | /// value: dec128!(100), 54 | /// unit: Unit::Meter, 55 | /// }; 56 | /// ``` 57 | pub struct Number { 58 | /// The number part of a [`Number`] struct 59 | pub value: D128, 60 | /// The unit of a [`Number`] struct. This can be [`NoType`](units::UnitType::NoType) 61 | pub unit: Unit, 62 | } 63 | 64 | impl Number { 65 | pub const fn new(value: D128, unit: Unit) -> Number { 66 | Number { value, unit } 67 | } 68 | pub fn get_simplified_value(&self) -> D128 { 69 | self.value.reduce() 70 | } 71 | } 72 | impl Display for Number { 73 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 74 | let value = self.get_simplified_value(); 75 | let word = match self.value == d!(1) { 76 | true => self.unit.singular(), 77 | false => self.unit.plural(), 78 | }; 79 | let output = match word { 80 | "" => format!("{value}"), 81 | _ => format!("{value} {word}"), 82 | }; 83 | write!(f, "{output}") 84 | } 85 | } 86 | 87 | #[derive(Clone, Debug, PartialEq)] 88 | /// Math operators like [`Multiply`](Operator::Multiply), parentheses, etc. 89 | pub enum Operator { 90 | Plus, 91 | Minus, 92 | Multiply, 93 | Divide, 94 | Modulo, 95 | Caret, 96 | LeftParen, // lexer only 97 | RightParen, // lexer only 98 | } 99 | 100 | #[derive(Clone, Debug, PartialEq)] 101 | /// Unary operators like [`Percent`](UnaryOperator::Percent) and [`Factorial`](UnaryOperator::Factorial). 102 | pub enum UnaryOperator { 103 | Percent, 104 | Factorial, 105 | } 106 | 107 | #[derive(Clone, Debug, PartialEq)] 108 | /// A Text operator like [`To`](TextOperator::To) or [`Of`](TextOperator::Of). 109 | pub enum TextOperator { 110 | To, 111 | Of, 112 | } 113 | 114 | #[derive(Clone, Debug, PartialEq)] 115 | /// A named number like [`Million`](NamedNumber::Million). 116 | pub enum NamedNumber { 117 | Hundred, 118 | Thousand, 119 | Million, 120 | Billion, 121 | Trillion, 122 | Quadrillion, 123 | Quintillion, 124 | Sextillion, 125 | Septillion, 126 | Octillion, 127 | Nonillion, 128 | Decillion, 129 | Undecillion, 130 | Duodecillion, 131 | Tredecillion, 132 | Quattuordecillion, 133 | Quindecillion, 134 | Sexdecillion, 135 | Septendecillion, 136 | Octodecillion, 137 | Novemdecillion, 138 | Vigintillion, 139 | Centillion, 140 | Googol, 141 | } 142 | 143 | #[derive(Clone, Debug, PartialEq)] 144 | /// A constant like [`Pi`](Constant::Pi) or [`E`](Constant::E). 145 | pub enum Constant { 146 | Pi, 147 | E, 148 | } 149 | 150 | #[derive(Clone, Debug, PartialEq)] 151 | /// Functions identifiers like [`Sqrt`](FunctionIdentifier::Sqrt), [`Sin`](FunctionIdentifier::Sin), [`Round`](FunctionIdentifier::Round), etc. 152 | pub enum FunctionIdentifier { 153 | Sqrt, 154 | Cbrt, 155 | 156 | Log, 157 | Ln, 158 | Exp, 159 | 160 | Round, 161 | Ceil, 162 | Floor, 163 | Abs, 164 | 165 | Sin, 166 | Cos, 167 | Tan, 168 | } 169 | 170 | #[derive(Clone, Debug, PartialEq)] 171 | /// A temporary enum used by the [`lexer`] to later determine what [`Token`] it is. 172 | /// 173 | /// For example, when a symbol like `%` is found, the lexer turns it into a 174 | /// the [`PercentChar`](LexerKeyword::PercentChar) variant 175 | /// and then later it checks the surrounding [`Token`]s and, 176 | /// dependingon them, turns it into a [`Percent`](UnaryOperator::Percent) or 177 | /// [`Modulo`](Operator::Modulo) [`Token`]. 178 | pub enum LexerKeyword { 179 | Per, 180 | PercentChar, 181 | In, 182 | DoubleQuotes, 183 | Mercury, 184 | Hg, 185 | PoundForce, 186 | Force, 187 | Revolution, 188 | } 189 | 190 | #[derive(Clone, Debug, PartialEq)] 191 | /// A token like a [`Number`](Token::Number), [`Operator`](Token::Operator), [`Unit`](Token::Unit) etc. 192 | /// 193 | /// Strings can be divided up into these tokens by the [`lexer`], and then put into the [`parser`]. 194 | pub enum Token { 195 | Operator(Operator), 196 | UnaryOperator(UnaryOperator), 197 | Number(D128), 198 | FunctionIdentifier(FunctionIdentifier), 199 | Constant(Constant), 200 | /// Used by the parser only 201 | Paren, 202 | /// Used by the lexer only 203 | Per, 204 | /// Used by the parser only 205 | LexerKeyword(LexerKeyword), 206 | TextOperator(TextOperator), 207 | NamedNumber(NamedNumber), 208 | /// The `-` symbol, specifically when used as `-5` and not `5-5`. Used by the parser only 209 | Negative, 210 | Unit(units::Unit), 211 | } 212 | 213 | #[macro_export] 214 | macro_rules! numtok { 215 | ( $num:literal ) => { 216 | Token::Number(fastnum::dec128!($num)) 217 | }; 218 | } 219 | 220 | /// Evaluates a string into a resulting [`Number`]. 221 | /// 222 | /// Example: 223 | /// ```rust 224 | /// use cpc::eval; 225 | /// use cpc::units::Unit; 226 | /// 227 | /// match eval("3m + 1cm", true, false) { 228 | /// Ok(answer) => { 229 | /// // answer: Number { value: 301, unit: Unit::Centimeter } 230 | /// println!("Evaluated value: {} {:?}", answer.value, answer.unit) 231 | /// }, 232 | /// Err(e) => { 233 | /// println!("{e}") 234 | /// } 235 | /// } 236 | /// ``` 237 | pub fn eval( 238 | input: &str, 239 | allow_trailing_operators: bool, 240 | verbose: bool, 241 | ) -> Result { 242 | let lex_start = Instant::now(); 243 | 244 | match lexer::lex(input, allow_trailing_operators) { 245 | Ok(tokens) => { 246 | let lex_time = Instant::now().duration_since(lex_start).as_nanos() as f32; 247 | if verbose { 248 | println!("Lexed TokenVector: {:?}", tokens); 249 | } 250 | 251 | let parse_start = Instant::now(); 252 | match parser::parse(&tokens) { 253 | Ok(ast) => { 254 | let parse_time = Instant::now().duration_since(parse_start).as_nanos() as f32; 255 | if verbose { 256 | println!("Parsed AstNode: {:#?}", ast); 257 | } 258 | 259 | let eval_start = Instant::now(); 260 | match evaluator::evaluate(&ast) { 261 | Ok(answer) => { 262 | let eval_time = 263 | Instant::now().duration_since(eval_start).as_nanos() as f32; 264 | 265 | if verbose { 266 | println!("Evaluated value: {} {:?}", answer.value, answer.unit); 267 | println!("\u{23f1} {:.3}ms lexing", lex_time / 1000.0 / 1000.0); 268 | println!("\u{23f1} {:.3}ms parsing", parse_time / 1000.0 / 1000.0); 269 | println!( 270 | "\u{23f1} {:.3}ms evaluation", 271 | eval_time / 1000.0 / 1000.0 272 | ); 273 | } 274 | 275 | Ok(answer) 276 | } 277 | Err(e) => Err(format!("Eval error: {}", e)), 278 | } 279 | } 280 | Err(e) => Err(format!("Parsing error: {}", e)), 281 | } 282 | } 283 | Err(e) => Err(format!("Lexing error: {}", e)), 284 | } 285 | } 286 | 287 | #[cfg(target_arch = "wasm32")] 288 | use wasm_bindgen::prelude::*; 289 | 290 | #[cfg(target_arch = "wasm32")] 291 | #[wasm_bindgen] 292 | pub fn wasm_eval(expression: &str) -> String { 293 | console_error_panic_hook::set_once(); 294 | 295 | let result = eval(expression, true, false); 296 | match result { 297 | Ok(result) => result.to_string(), 298 | Err(e) => format!("Error: {e}"), 299 | } 300 | } 301 | 302 | #[cfg(test)] 303 | mod tests { 304 | use super::*; 305 | 306 | fn default_eval(input: &str) -> Number { 307 | eval(input, true, false).unwrap() 308 | } 309 | 310 | #[test] 311 | fn test_simplify() { 312 | assert_eq!(&default_eval("sin(pi)").to_string(), "0"); 313 | assert_eq!(&default_eval("0.2/0.01").to_string(), "20"); 314 | } 315 | 316 | #[test] 317 | fn test_evaluations() { 318 | assert_eq!(default_eval("-2(-3)"), Number::new(d!(6), Unit::NoUnit)); 319 | assert_eq!(default_eval("-2(3)"), Number::new(d!(-6), Unit::NoUnit)); 320 | assert_eq!(default_eval("(3)-2"), Number::new(d!(1), Unit::NoUnit)); 321 | assert_eq!(default_eval("-1km to m"), Number::new(d!(-1000), Unit::Meter)); 322 | assert_eq!(default_eval("2*-3*0.5"), Number::new(d!(-3), Unit::NoUnit)); 323 | assert_eq!(default_eval("-3^2"), Number::new(d!(-9), Unit::NoUnit)); 324 | assert_eq!(default_eval("-1+2"), Number::new(d!(1), Unit::NoUnit)); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/lookup.rs: -------------------------------------------------------------------------------- 1 | use crate::NamedNumber::*; 2 | use crate::NamedNumber; 3 | use fastnum::{dec128 as d, D128}; 4 | 5 | /// Returns the corresponding [`D128`] of a [`NamedNumber`] 6 | pub fn lookup_named_number(named_number: &NamedNumber) -> D128 { 7 | match named_number { 8 | Hundred => d!(100), 9 | Thousand => d!(1000), 10 | Million => d!(1000000), 11 | Billion => d!(1000000000), 12 | Trillion => d!(1000000000000), 13 | Quadrillion => d!(1000000000000000), 14 | Quintillion => d!(1000000000000000000), 15 | Sextillion => d!(1000000000000000000000), 16 | Septillion => d!(1000000000000000000000000), 17 | Octillion => d!(1000000000000000000000000000), 18 | Nonillion => d!(1000000000000000000000000000000), 19 | Decillion => d!(1000000000000000000000000000000000), 20 | Undecillion => d!(1000000000000000000000000000000000000), 21 | Duodecillion => d!(1E+39), 22 | Tredecillion => d!(1E+42), 23 | Quattuordecillion => d!(1E+45), 24 | Quindecillion => d!(1E+48), 25 | Sexdecillion => d!(1E+51), 26 | Septendecillion => d!(1E+54), 27 | Octodecillion => d!(1E+57), 28 | Novemdecillion => d!(1E+60), 29 | Vigintillion => d!(1E+63), 30 | Googol => d!(1E+100), 31 | Centillion => d!(1E+303), 32 | } 33 | } 34 | 35 | /// Returns the factorial of an `i32` as a [`D128`] 36 | pub fn lookup_factorial(n: i32) -> D128 { 37 | match n { 38 | 0 => d!(1), 39 | 1 => d!(1), 40 | 2 => d!(2), 41 | 3 => d!(6), 42 | 4 => d!(24), 43 | 5 => d!(120), 44 | 6 => d!(720), 45 | 7 => d!(5040), 46 | 8 => d!(40320), 47 | 9 => d!(362880), 48 | 10 => d!(3628800), 49 | 11 => d!(39916800), 50 | 12 => d!(479001600), 51 | 13 => d!(6227020800), 52 | 14 => d!(87178291200), 53 | 15 => d!(1307674368000), 54 | 16 => d!(20922789888000), 55 | 17 => d!(355687428096000), 56 | 18 => d!(6402373705728000), 57 | 19 => d!(121645100408832000), 58 | 20 => d!(2432902008176640000), 59 | 21 => d!(51090942171709440000), 60 | 22 => d!(1124000727777607680000), 61 | 23 => d!(25852016738884976640000), 62 | 24 => d!(620448401733239439360000), 63 | 25 => d!(15511210043330985984000000), 64 | 26 => d!(403291461126605635584000000), 65 | 27 => d!(10888869450418352160768000000), 66 | 28 => d!(304888344611713860501504000000), 67 | 29 => d!(8841761993739701954543616000000), 68 | 30 => d!(265252859812191058636308480000000), 69 | 31 => d!(8222838654177922817725562880000000), 70 | 32 => d!(2.631308369336935301672180121600000E+35), 71 | 33 => d!(8.683317618811886495518194401280000E+36), 72 | 34 => d!(2.952327990396041408476186096435200E+38), 73 | 35 => d!(1.033314796638614492966665133752320E+40), 74 | 36 => d!(3.719933267899012174679994481508352E+41), 75 | 37 => d!(1.376375309122634504631597958158090E+43), 76 | 38 => d!(5.230226174666011117600072241000742E+44), 77 | 39 => d!(2.039788208119744335864028173990289E+46), 78 | 40 => d!(8.159152832478977343456112695961156E+47), 79 | 41 => d!(3.345252661316380710817006205344074E+49), 80 | 42 => d!(1.405006117752879898543142606244511E+51), 81 | 43 => d!(6.041526306337383563735513206851397E+52), 82 | 44 => d!(2.658271574788448768043625811014615E+54), 83 | 45 => d!(1.196222208654801945619631614956577E+56), 84 | 46 => d!(5.502622159812088949850305428800254E+57), 85 | 47 => d!(2.586232415111681806429643551536119E+59), 86 | 48 => d!(1.241391559253607267086228904737337E+61), 87 | 49 => d!(6.082818640342675608722521633212951E+62), 88 | 50 => d!(3.041409320171337804361260816606476E+64), 89 | 51 => d!(1.551118753287382280224243016469303E+66), 90 | 52 => d!(8.065817517094387857166063685640376E+67), 91 | 53 => d!(4.274883284060025564298013753389399E+69), 92 | 54 => d!(2.308436973392413804720927426830275E+71), 93 | 55 => d!(1.269640335365827592596510084756651E+73), 94 | 56 => d!(7.109985878048634518540456474637246E+74), 95 | 57 => d!(4.052691950487721675568060190543230E+76), 96 | 58 => d!(2.350561331282878571829474910515073E+78), 97 | 59 => d!(1.386831185456898357379390197203893E+80), 98 | 60 => d!(8.320987112741390144276341183223358E+81), 99 | 61 => d!(5.075802138772247988008568121766248E+83), 100 | 62 => d!(3.146997326038793752565312235495074E+85), 101 | 63 => d!(1.982608315404440064116146708361897E+87), 102 | 64 => d!(1.268869321858841641034333893351614E+89), 103 | 65 => d!(8.247650592082470666723170306785491E+90), 104 | 66 => d!(5.443449390774430640037292402478424E+92), 105 | 67 => d!(3.647111091818868528824985909660544E+94), 106 | 68 => d!(2.480035542436830599600990418569170E+96), 107 | 69 => d!(1.711224524281413113724683388812727E+98), 108 | 70 => d!(1.197857166996989179607278372168909E+100), 109 | 71 => d!(8.504785885678623175211676442399254E+101), 110 | 72 => d!(6.123445837688608686152407038527463E+103), 111 | 73 => d!(4.470115461512684340891257138125048E+105), 112 | 74 => d!(3.307885441519386412259530282212536E+107), 113 | 75 => d!(2.480914081139539809194647711659402E+109), 114 | 76 => d!(1.885494701666050254987932260861146E+111), 115 | 77 => d!(1.451830920282858696340707840863082E+113), 116 | 78 => d!(1.132428117820629783145752115873204E+115), 117 | 79 => d!(8.946182130782975286851441715398312E+116), 118 | 80 => d!(7.156945704626380229481153372318650E+118), 119 | 81 => d!(5.797126020747367985879734231578106E+120), 120 | 82 => d!(4.753643337012841748421382069894047E+122), 121 | 83 => d!(3.945523969720658651189747118012059E+124), 122 | 84 => d!(3.314240134565353266999387579130130E+126), 123 | 85 => d!(2.817104114380550276949479442260610E+128), 124 | 86 => d!(2.422709538367273238176552320344125E+130), 125 | 87 => d!(2.107757298379527717213600518699389E+132), 126 | 88 => d!(1.854826422573984391147968456455462E+134), 127 | 89 => d!(1.650795516090846108121691926245361E+136), 128 | 90 => d!(1.485715964481761497309522733620825E+138), 129 | 91 => d!(1.352001527678402962551665687594951E+140), 130 | 92 => d!(1.243841405464130725547532432587355E+142), 131 | 93 => d!(1.156772507081641574759205162306240E+144), 132 | 94 => d!(1.087366156656743080273652852567866E+146), 133 | 95 => d!(1.032997848823905926259970209939473E+148), 134 | 96 => d!(9.916779348709496892095714015418941E+149), 135 | 97 => d!(9.619275968248211985332842594956373E+151), 136 | 98 => d!(9.426890448883247745626185743057246E+153), 137 | 99 => d!(9.332621544394415268169923885626674E+155), 138 | 100 => d!(9.332621544394415268169923885626674E+157), 139 | 101 => d!(9.425947759838359420851623124482941E+159), 140 | 102 => d!(9.614466715035126609268655586972600E+161), 141 | 103 => d!(9.902900716486180407546715254581778E+163), 142 | 104 => d!(1.029901674514562762384858386476505E+166), 143 | 105 => d!(1.081396758240290900504101305800330E+168), 144 | 106 => d!(1.146280563734708354534347384148350E+170), 145 | 107 => d!(1.226520203196137939351751701038734E+172), 146 | 108 => d!(1.324641819451828974499891837121833E+174), 147 | 109 => d!(1.443859583202493582204882102462798E+176), 148 | 110 => d!(1.588245541522742940425370312709078E+178), 149 | 111 => d!(1.762952551090244663872161047107077E+180), 150 | 112 => d!(1.974506857221074023536820372759926E+182), 151 | 113 => d!(2.231192748659813646596607021218716E+184), 152 | 114 => d!(2.543559733472187557120132004189336E+186), 153 | 115 => d!(2.925093693493015690688151804817736E+188), 154 | 116 => d!(3.393108684451898201198256093588574E+190), 155 | 117 => d!(3.969937160808720895401959629498632E+192), 156 | 118 => d!(4.684525849754290656574312362808386E+194), 157 | 119 => d!(5.574585761207605881323431711741979E+196), 158 | 120 => d!(6.689502913449127057588118054090375E+198), 159 | 121 => d!(8.094298525273443739681622845449354E+200), 160 | 122 => d!(9.875044200833601362411579871448212E+202), 161 | 123 => d!(1.214630436702532967576624324188130E+205), 162 | 124 => d!(1.506141741511140879795014161993281E+207), 163 | 125 => d!(1.882677176888926099743767702491601E+209), 164 | 126 => d!(2.372173242880046885677147305139417E+211), 165 | 127 => d!(3.012660018457659544809977077527060E+213), 166 | 128 => d!(3.856204823625804217356770659234637E+215), 167 | 129 => d!(4.974504222477287440390234150412682E+217), 168 | 130 => d!(6.466855489220473672507304395536487E+219), 169 | 131 => d!(8.471580690878820510984568758152798E+221), 170 | 132 => d!(1.118248651196004307449963076076169E+224), 171 | 133 => d!(1.487270706090685728908450891181305E+226), 172 | 134 => d!(1.992942746161518876737324194182949E+228), 173 | 135 => d!(2.690472707318050483595387662146981E+230), 174 | 136 => d!(3.659042881952548657689727220519894E+232), 175 | 137 => d!(5.012888748274991661034926292112255E+234), 176 | 138 => d!(6.917786472619488492228198283114912E+236), 177 | 139 => d!(9.615723196941089004197195613529728E+238), 178 | 140 => d!(1.346201247571752460587607385894162E+241), 179 | 141 => d!(1.898143759076170969428526414110768E+243), 180 | 142 => d!(2.695364137888162776588507508037291E+245), 181 | 143 => d!(3.854370717180072770521565736493326E+247), 182 | 144 => d!(5.550293832739304789551054660550389E+249), 183 | 145 => d!(8.047926057471991944849029257798064E+251), 184 | 146 => d!(1.174997204390910823947958271638517E+254), 185 | 147 => d!(1.727245890454638911203498659308620E+256), 186 | 148 => d!(2.556323917872865588581178015776758E+258), 187 | 149 => d!(3.808922637630569726985955243507369E+260), 188 | 150 => d!(5.713383956445854590478932865261054E+262), 189 | 151 => d!(8.627209774233240431623188626544192E+264), 190 | 152 => d!(1.311335885683452545606724671234717E+267), 191 | 153 => d!(2.006343905095682394778288746989117E+269), 192 | 154 => d!(3.089769613847350887958564670363240E+271), 193 | 155 => d!(4.789142901463393876335775239063022E+273), 194 | 156 => d!(7.471062926282894447083809372938314E+275), 195 | 157 => d!(1.172956879426414428192158071551315E+278), 196 | 158 => d!(1.853271869493734796543609753051078E+280), 197 | 159 => d!(2.946702272495038326504339507351214E+282), 198 | 160 => d!(4.714723635992061322406943211761942E+284), 199 | 161 => d!(7.590705053947218729075178570936727E+286), 200 | 162 => d!(1.229694218739449434110178928491750E+289), 201 | 163 => d!(2.004401576545302577599591653441552E+291), 202 | 164 => d!(3.287218585534296227263330311644145E+293), 203 | 165 => d!(5.423910666131588774984495014212839E+295), 204 | 166 => d!(9.003691705778437366474261723593313E+297), 205 | 167 => d!(1.503616514864999040201201707840083E+300), 206 | 168 => d!(2.526075744973198387538018869171339E+302), 207 | 169 => d!(4.269068009004705274939251888899563E+304), 208 | 170 => d!(7.257415615307998967396728211129257E+306), 209 | 171 => d!(1.241018070217667823424840524103103E+309), 210 | 172 => d!(2.134551080774388656290725701457337E+311), 211 | 173 => d!(3.692773369739692375382955463521193E+313), 212 | 174 => d!(6.425425663347064733166342506526876E+315), 213 | 175 => d!(1.124449491085736328304109938642203E+318), 214 | 176 => d!(1.979031104310895937815233492010277E+320), 215 | 177 => d!(3.502885054630285809932963280858190E+322), 216 | 178 => d!(6.235135397241908741680674639927578E+324), 217 | 179 => d!(1.116089236106301664760840760547036E+327), 218 | 180 => d!(2.008960624991342996569513368984665E+329), 219 | 181 => d!(3.636218731234330823790819197862244E+331), 220 | 182 => d!(6.617918090846482099299290940109284E+333), 221 | 183 => d!(1.211079010624906224171770242039999E+336), 222 | 184 => d!(2.228385379549827452476057245353598E+338), 223 | 185 => d!(4.122512952167180787080705903904156E+340), 224 | 186 => d!(7.667874091030956263970112981261730E+342), 225 | 187 => d!(1.433892455022788821362411127495944E+345), 226 | 188 => d!(2.695717815442842984161332919692375E+347), 227 | 189 => d!(5.094906671186973240064919218218589E+349), 228 | 190 => d!(9.680322675255249156123346514615319E+351), 229 | 191 => d!(1.848941630973752588819559184291526E+354), 230 | 192 => d!(3.549967931469604970533553633839730E+356), 231 | 193 => d!(6.851438107736337593129758513310679E+358), 232 | 194 => d!(1.329178992900849493067173151582272E+361), 233 | 195 => d!(2.591899036156656511480987645585430E+363), 234 | 196 => d!(5.080122110867046762502735785347443E+365), 235 | 197 => d!(1.000784055840808212213038949713446E+368), 236 | 198 => d!(1.981552430564800260181817120432623E+370), 237 | 199 => d!(3.943289336823952517761816069660920E+372), 238 | 200 => d!(7.886578673647905035523632139321840E+374), 239 | 201 => d!(1.585202313403228912140250060003690E+377), 240 | 202 => d!(3.202108673074522402523305121207454E+379), 241 | 203 => d!(6.500280606341280477122309396051132E+381), 242 | 204 => d!(1.326057243693621217332951116794431E+384), 243 | 205 => d!(2.718417349571923495532549789428584E+386), 244 | 206 => d!(5.599939740118162400797052566222883E+388), 245 | 207 => d!(1.159187526204459616964989881208137E+391), 246 | 208 => d!(2.411110054505276003287178952912925E+393), 247 | 209 => d!(5.039220013916026846870204011588013E+395), 248 | 210 => d!(1.058236202922365637842742842433483E+398), 249 | 211 => d!(2.232878388166191495848187397534649E+400), 250 | 212 => d!(4.733702182912325971198157282773456E+402), 251 | 213 => d!(1.008278564960325431865207501230746E+405), 252 | 214 => d!(2.157716129015096424191544052633796E+407), 253 | 215 => d!(4.639089677382457312011819713162661E+409), 254 | 216 => d!(1.002043370314610779394553058043135E+412), 255 | 217 => d!(2.174434113582705391286180135953603E+414), 256 | 218 => d!(4.740266367610297753003872696378855E+416), 257 | 219 => d!(1.038118334506655207907848120506969E+419), 258 | 220 => d!(2.283860335914641457397265865115332E+421), 259 | 221 => d!(5.047331342371357620847957561904884E+423), 260 | 222 => d!(1.120507558006441391828246578742884E+426), 261 | 223 => d!(2.498731854354364303776989870596631E+428), 262 | 224 => d!(5.597159353753776040460457310136453E+430), 263 | 225 => d!(1.259360854594599609103602894780702E+433), 264 | 226 => d!(2.846155531383795116574142542204387E+435), 265 | 227 => d!(6.460773056241214914623303570803958E+437), 266 | 228 => d!(1.473056256822997000534113214143302E+440), 267 | 229 => d!(3.373298828124663131223119260388162E+442), 268 | 230 => d!(7.758587304686725201813174298892773E+444), 269 | 231 => d!(1.792233667382633521618843263044231E+447), 270 | 232 => d!(4.157982108327709770155716370262616E+449), 271 | 233 => d!(9.688098312403563764462819142711895E+451), 272 | 234 => d!(2.267015005102433920884299679394583E+454), 273 | 235 => d!(5.327485261990719714078104246577270E+456), 274 | 236 => d!(1.257286521829809852522432602192236E+459), 275 | 237 => d!(2.979769056736649350478165267195599E+461), 276 | 238 => d!(7.091850355033225454138033335925526E+463), 277 | 239 => d!(1.694952234852940883538989967286201E+466), 278 | 240 => d!(4.067885363647058120493575921486882E+468), 279 | 241 => d!(9.803603726389410070389517970783386E+470), 280 | 242 => d!(2.372472101786237237034263348929579E+473), 281 | 243 => d!(5.765107207340556485993259937898877E+475), 282 | 244 => d!(1.406686158591095782582355424847326E+478), 283 | 245 => d!(3.446381088548184667326770790875949E+480), 284 | 246 => d!(8.478097477828534281623856145554835E+482), 285 | 247 => d!(2.094090077023647967561092467952044E+485), 286 | 248 => d!(5.193343391018646959551509320521069E+487), 287 | 249 => d!(1.293142504363643092928325820809746E+490), 288 | 250 => d!(3.232856260909107732320814552024365E+492), 289 | 251 => d!(8.114469214881860408125244525581156E+494), 290 | 252 => d!(2.044846242150228822847561620446451E+497), 291 | 253 => d!(5.173460992640078921804330899729521E+499), 292 | 254 => d!(1.314059092130580046138300048531298E+502), 293 | 255 => d!(3.350850684932979117652665123754810E+504), 294 | 256 => d!(8.578177753428426541190822716812314E+506), 295 | 257 => d!(2.204591682631105621086041438220765E+509), 296 | 258 => d!(5.687846541188252502401986910609574E+511), 297 | 259 => d!(1.473152254167757398122114609847880E+514), 298 | 260 => d!(3.830195860836169235117497985604488E+516), 299 | 261 => d!(9.996811196782401703656669742427714E+518), 300 | 262 => d!(2.619164533556989246358047472516061E+521), 301 | 263 => d!(6.888402723254881717921664852717240E+523), 302 | 264 => d!(1.818538318939288773531319521117351E+526), 303 | 265 => d!(4.819126545189115249857996730960980E+528), 304 | 266 => d!(1.281887661020304656462227130435621E+531), 305 | 267 => d!(3.422640054924213432754146438263108E+533), 306 | 268 => d!(9.172675347196891999781112454545129E+535), 307 | 269 => d!(2.467449668395963947941119250272640E+538), 308 | 270 => d!(6.662114104669102659441021975736128E+540), 309 | 271 => d!(1.805432922365326820708516955424491E+543), 310 | 272 => d!(4.910777548833688952327166118754616E+545), 311 | 273 => d!(1.340642270831597083985316350420010E+548), 312 | 274 => d!(3.673359822078576010119766800150827E+550), 313 | 275 => d!(1.010173951071608402782935870041477E+553), 314 | 276 => d!(2.788080104957639191680903001314477E+555), 315 | 277 => d!(7.722981890732660560956101313641101E+557), 316 | 278 => d!(2.146988965623679635945796165192226E+560), 317 | 279 => d!(5.990099214090066184288771300886311E+562), 318 | 280 => d!(1.677227779945218531600855964248167E+565), 319 | 281 => d!(4.713010061646064073798405259537349E+567), 320 | 282 => d!(1.329068837384190068811150283189532E+570), 321 | 283 => d!(3.761264809797257894735555301426376E+572), 322 | 284 => d!(1.068199205982421242104897705605091E+575), 323 | 285 => d!(3.044367737049900539998958460974509E+577), 324 | 286 => d!(8.706891727962715544397021198387096E+579), 325 | 287 => d!(2.498877925925299361241945083937097E+582), 326 | 288 => d!(7.196768426664862160376801841738839E+584), 327 | 289 => d!(2.079866075306145164348895732262524E+587), 328 | 290 => d!(6.031611618387820976611797623561320E+589), 329 | 291 => d!(1.755198980950855904194033108456344E+592), 330 | 292 => d!(5.125181024376499240246576676692524E+594), 331 | 293 => d!(1.501678040142314277392246966270910E+597), 332 | 294 => d!(4.414933438018403975533206080836475E+599), 333 | 295 => d!(1.302405364215429172782295793846760E+602), 334 | 296 => d!(3.855119878077670351435595549786410E+604), 335 | 297 => d!(1.144970603789068094376371878286564E+607), 336 | 298 => d!(3.412012399291422921241588197293961E+609), 337 | 299 => d!(1.020191707388135453451234870990894E+612), 338 | 300 => d!(3.060575122164406360353704612972682E+614), 339 | 301 => d!(9.212331117714863144664650885047773E+616), 340 | 302 => d!(2.782123997549888669688724567284427E+619), 341 | 303 => d!(8.429835712576162669156835438871814E+621), 342 | 304 => d!(2.562670056623153451423677973417031E+624), 343 | 305 => d!(7.816143672700618026842217818921945E+626), 344 | 306 => d!(2.391739963846389116213718652590115E+629), 345 | 307 => d!(7.342641689008414586776116263451653E+631), 346 | 308 => d!(2.261533640214591692727043809143109E+634), 347 | 309 => d!(6.988138948263088330526565370252207E+636), 348 | 310 => d!(2.166323073961557382463235264778184E+639), 349 | 311 => d!(6.737264760020443459460661673460152E+641), 350 | 312 => d!(2.102026605126378359351726442119567E+644), 351 | 313 => d!(6.579343274045564264770903763834245E+646), 352 | 314 => d!(2.065913788050307179138063781843953E+649), 353 | 315 => d!(6.507628432358467614284900912808452E+651), 354 | 316 => d!(2.056410584625275766114028688447471E+654), 355 | 317 => d!(6.518821553262124178581470942378483E+656), 356 | 318 => d!(2.072985253937355488788907759676358E+659), 357 | 319 => d!(6.612822960060164009236615753367582E+661), 358 | 320 => d!(2.116103347219252482955717041077626E+664), 359 | 321 => d!(6.792691744573800470287851701859179E+666), 360 | 322 => d!(2.187246741752763751432688247998656E+669), 361 | 323 => d!(7.064806975861426917127583041035659E+671), 362 | 324 => d!(2.288997460179102321149336905295554E+674), 363 | 325 => d!(7.439241745582082543735344942210550E+676), 364 | 326 => d!(2.425192809059758909257722451160639E+679), 365 | 327 => d!(7.930380485625411633272752415295290E+681), 366 | 328 => d!(2.601164799285135015713462792216855E+684), 367 | 329 => d!(8.557832189648094201697292586393453E+686), 368 | 330 => d!(2.824084622583871086560106553509839E+689), 369 | 331 => d!(9.347720100752613296513952692117567E+691), 370 | 332 => d!(3.103443073449867614442632293783032E+694), 371 | 333 => d!(1.033446543458805915609396553829750E+697), 372 | 334 => d!(3.451711455152411758135384489791365E+699), 373 | 335 => d!(1.156323337476057938975353804080107E+702), 374 | 336 => d!(3.885246413919554674957188781709160E+704), 375 | 337 => d!(1.309328041490889925460572619435987E+707), 376 | 338 => d!(4.425528780239207948056735453693636E+709), 377 | 339 => d!(1.500254256501091494391233318802143E+712), 378 | 340 => d!(5.100864472103711080930193283927286E+714), 379 | 341 => d!(1.739394784987365478597195909819205E+717), 380 | 342 => d!(5.948730164656789936802410011581681E+719), 381 | 343 => d!(2.040414446477278948323226633972517E+722), 382 | 344 => d!(7.019025695881839582231899620865458E+724), 383 | 345 => d!(2.421563865079234655870005369198583E+727), 384 | 346 => d!(8.378610973174151909310218577427097E+729), 385 | 347 => d!(2.907378007691430712530645846367203E+732), 386 | 348 => d!(1.011767546676617887960664754535787E+735), 387 | 349 => d!(3.531068737901396428982719993329897E+737), 388 | 350 => d!(1.235874058265488750143951997665464E+740), 389 | 351 => d!(4.337917944511865513005271511805779E+742), 390 | 352 => d!(1.526947116468176660577855572155634E+745), 391 | 353 => d!(5.390123321132663611839830169709388E+747), 392 | 354 => d!(1.908103655680962918591299880077123E+750), 393 | 355 => d!(6.773767977667418360999114574273787E+752), 394 | 356 => d!(2.411461400049600936515684788441468E+755), 395 | 357 => d!(8.608917198177075343360994694736041E+757), 396 | 358 => d!(3.081992356947392972923236100715503E+760), 397 | 359 => d!(1.106435256144114077279441760156866E+763), 398 | 360 => d!(3.983166922118810678205990336564718E+765), 399 | 361 => d!(1.437923258884890654832362511499863E+768), 400 | 362 => d!(5.205282197163304170493152291629504E+770), 401 | 363 => d!(1.889517437570279413889014281861510E+773), 402 | 364 => d!(6.877843472755817066556011985975896E+775), 403 | 365 => d!(2.510412867555873229292944374881202E+778), 404 | 366 => d!(9.188111095254496019212176412065199E+780), 405 | 367 => d!(3.372036771958400039050868743227928E+783), 406 | 368 => d!(1.240909532080691214370719697507878E+786), 407 | 369 => d!(4.578956173377750581027955683804070E+788), 408 | 370 => d!(1.694213784149767714980343603007506E+791), 409 | 371 => d!(6.285533139195638222577074767157847E+793), 410 | 372 => d!(2.338218327780777418798671813382719E+796), 411 | 373 => d!(8.721554362622299772119045863917542E+798), 412 | 374 => d!(3.261861331620740114772523153105161E+801), 413 | 375 => d!(1.223197999357777543039696182414435E+804), 414 | 376 => d!(4.599224477585243561829257645878276E+806), 415 | 377 => d!(1.733907628049636822809630132496110E+809), 416 | 378 => d!(6.554170834027627190220401900835296E+811), 417 | 379 => d!(2.484030746096470705093532320416577E+814), 418 | 380 => d!(9.439316835166588679355422817582993E+816), 419 | 381 => d!(3.596379714198470286834416093499120E+819), 420 | 382 => d!(1.373817050823815649570746947716664E+822), 421 | 383 => d!(5.261719304655213937855960809754823E+824), 422 | 384 => d!(2.020500212987602152136688950945852E+827), 423 | 385 => d!(7.778925820002268285726252461141530E+829), 424 | 386 => d!(3.002665366520875558290333450000631E+832), 425 | 387 => d!(1.162031496843578841058359045150244E+835), 426 | 388 => d!(4.508682207753085903306433095182947E+837), 427 | 389 => d!(1.753877378815950416386202474026166E+840), 428 | 390 => d!(6.840121777382206623906189648702047E+842), 429 | 391 => d!(2.674487614956442789947320152642500E+845), 430 | 392 => d!(1.048399145062925573659349499835860E+848), 431 | 393 => d!(4.120208640097297504481243534354930E+850), 432 | 394 => d!(1.623362204198335216765609952535842E+853), 433 | 395 => d!(6.412280706583424106224159312516576E+855), 434 | 396 => d!(2.539263159807035946064767087756564E+858), 435 | 397 => d!(1.008087474443393270587712533839356E+861), 436 | 398 => d!(4.012188148284705216939095884680637E+863), 437 | 399 => d!(1.600863071165597381558699257987574E+866), 438 | 400 => d!(6.403452284662389526234797031950296E+868), 439 | 401 => d!(2.567784366149618200020153609812069E+871), 440 | 402 => d!(1.032249315192146516408101751144452E+874), 441 | 403 => d!(4.159964740224350461124650057112142E+876), 442 | 404 => d!(1.680625755050637586294358623073305E+879), 443 | 405 => d!(6.806534307955082224492152423446885E+881), 444 | 406 => d!(2.763452929029763383143813883919435E+884), 445 | 407 => d!(1.124725342115113696939532250755210E+887), 446 | 408 => d!(4.588879395829663883513291583081257E+889), 447 | 409 => d!(1.876851672894332528356936257480234E+892), 448 | 410 => d!(7.695091858866763366263438655668959E+894), 449 | 411 => d!(3.162682753994239743534273287479942E+897), 450 | 412 => d!(1.303025294645626774336120594441736E+900), 451 | 413 => d!(5.381494466886438578008178055044370E+902), 452 | 414 => d!(2.227938709290985571295385714788369E+905), 453 | 415 => d!(9.245945643557590120875850716371731E+907), 454 | 416 => d!(3.846313387719957490284353898010640E+910), 455 | 417 => d!(1.603912682679222273448575575470437E+913), 456 | 418 => d!(6.704355013599149103015045905466427E+915), 457 | 419 => d!(2.809124750698043474163304234390433E+918), 458 | 420 => d!(1.179832395293178259148587778443982E+921), 459 | 421 => d!(4.967094384184280471015554547249164E+923), 460 | 422 => d!(2.096113830125766358768564018939147E+926), 461 | 423 => d!(8.866561501431991697591025800112592E+928), 462 | 424 => d!(3.759422076607164479778594939247739E+931), 463 | 425 => d!(1.597754382558044903905902849180289E+934), 464 | 426 => d!(6.806433669697271290639146137508031E+936), 465 | 427 => d!(2.906347176960734841102915400715929E+939), 466 | 428 => d!(1.243916591739194511992047791506418E+942), 467 | 429 => d!(5.336402178561144456445885025562533E+944), 468 | 430 => d!(2.294652936781292116271730560991889E+947), 469 | 431 => d!(9.889954157527369021131158717875042E+949), 470 | 432 => d!(4.272460196051823417128660566122018E+952), 471 | 433 => d!(1.849975264890439539616710025130834E+955), 472 | 434 => d!(8.028892649624507601936521509067820E+957), 473 | 435 => d!(3.492568302586660806842386856444502E+960), 474 | 436 => d!(1.522759779927784111783280669409803E+963), 475 | 437 => d!(6.654460238284416568492936525320839E+965), 476 | 438 => d!(2.914653584368574456999906198090527E+968), 477 | 439 => d!(1.279532923537804186622958820961741E+971), 478 | 440 => d!(5.629944863566338421141018812231660E+973), 479 | 441 => d!(2.482805684832755243723189296194162E+976), 480 | 442 => d!(1.097400112696077817725649668917820E+979), 481 | 443 => d!(4.861482499243624732524628033305943E+981), 482 | 444 => d!(2.158498229664169381240934846787839E+984), 483 | 445 => d!(9.605317122005553746522160068205884E+986), 484 | 446 => d!(4.283971436414476970948883390419824E+989), 485 | 447 => d!(1.914935232077271206014150875517661E+992), 486 | 448 => d!(8.578909839706175002943395922319121E+994), 487 | 449 => d!(3.851930518028072576321584769121285E+997), 488 | 450 => d!(1.733368733112632659344713146104578E+1000), 489 | 451 => d!(7.817492986337973293644656288931647E+1002), 490 | 452 => d!(3.533506829824763928727384642597104E+1005), 491 | 453 => d!(1.600678593910618059713505243096488E+1008), 492 | 454 => d!(7.267080816354205991099313803658056E+1010), 493 | 455 => d!(3.306521771441163725950187780664415E+1013), 494 | 456 => d!(1.507773927777170659033285627982973E+1016), 495 | 457 => d!(6.890526849941669911782115319882187E+1018), 496 | 458 => d!(3.155861297273284819596208816506042E+1021), 497 | 459 => d!(1.448540335448437732194659846776273E+1024), 498 | 460 => d!(6.663285543062813568095435295170856E+1026), 499 | 461 => d!(3.071774635351957054891995671073765E+1029), 500 | 462 => d!(1.419159881532604159360102000036079E+1032), 501 | 463 => d!(6.570710251495957257837272260167046E+1034), 502 | 464 => d!(3.048809556694124167636494328717509E+1037), 503 | 465 => d!(1.417696443862767737950969862853642E+1040), 504 | 466 => d!(6.606465428400497658851519560897972E+1042), 505 | 467 => d!(3.085219355063032406683659634939353E+1045), 506 | 468 => d!(1.443882658169499166327952709151617E+1048), 507 | 469 => d!(6.771809666814951090078098205921084E+1050), 508 | 470 => d!(3.182750543403027012336706156782909E+1053), 509 | 471 => d!(1.499075505942825722810588599844750E+1056), 510 | 472 => d!(7.075636388050137411665978191267220E+1058), 511 | 473 => d!(3.346776011547714995718007684469395E+1061), 512 | 474 => d!(1.586371829473616907970335642438493E+1064), 513 | 475 => d!(7.535266189999680312859094301582842E+1066), 514 | 476 => d!(3.586786706439847828920928887553433E+1069), 515 | 477 => d!(1.710897258971807414395283079362988E+1072), 516 | 478 => d!(8.178088897885239440809453119355083E+1074), 517 | 479 => d!(3.917304582087029692147728044171085E+1077), 518 | 480 => d!(1.880306199401774252230909461202121E+1080), 519 | 481 => d!(9.044272819122534153230674508382202E+1082), 520 | 482 => d!(4.359339498817061461857185113040221E+1085), 521 | 483 => d!(2.105560977928640686077020409598427E+1088), 522 | 484 => d!(1.019091513317462092061277878245639E+1091), 523 | 485 => d!(4.942593839589691146497197709491349E+1093), 524 | 486 => d!(2.402100606040589897197638086812796E+1096), 525 | 487 => d!(1.169822995141767279935249748277832E+1099), 526 | 488 => d!(5.708736216291824326084018771595820E+1101), 527 | 489 => d!(2.791572009766702095455085179310356E+1104), 528 | 490 => d!(1.367870284785684026772991737862074E+1107), 529 | 491 => d!(6.716243098297708571455389432902783E+1109), 530 | 492 => d!(3.304391604362472617156051600988169E+1112), 531 | 493 => d!(1.629065060950699000257933439287167E+1115), 532 | 494 => d!(8.047581401096453061274191190078605E+1117), 533 | 495 => d!(3.983552793542744265330724639088909E+1120), 534 | 496 => d!(1.975842185597201155604039420988099E+1123), 535 | 497 => d!(9.819935662418089743352075922310852E+1125), 536 | 498 => d!(4.890327959884208692189333809310804E+1128), 537 | 499 => d!(2.440273651982220137402477570846091E+1131), 538 | 500 => d!(1.220136825991110068701238785423046E+1134), 539 | 501 => d!(6.112885498215461444193206314969460E+1136), 540 | 502 => d!(3.068668520104161644984989570114669E+1139), 541 | 503 => d!(1.543540265612393307427449753767679E+1142), 542 | 504 => d!(7.779442938686462269434346758989102E+1144), 543 | 505 => d!(3.928618684036663446064345113289497E+1147), 544 | 506 => d!(1.987881054122551703708558627324485E+1150), 545 | 507 => d!(1.007855694440133713780239224053514E+1153), 546 | 508 => d!(5.119906927755879266003615258191851E+1155), 547 | 509 => d!(2.606032626227742546395840166419652E+1158), 548 | 510 => d!(1.329076639376148698661878484874023E+1161), 549 | 511 => d!(6.791581627212119850162199057706258E+1163), 550 | 512 => d!(3.477289793132605363283045917545604E+1166), 551 | 513 => d!(1.783849663877026551364202555700895E+1169), 552 | 514 => d!(9.168987272327916474012001136302600E+1171), 553 | 515 => d!(4.722028445248876984116180585195839E+1174), 554 | 516 => d!(2.436566677748420523803949181961053E+1177), 555 | 517 => d!(1.259704972395933410806641727073864E+1180), 556 | 518 => d!(6.525271757010935067978404146242616E+1182), 557 | 519 => d!(3.386616041888675300280791751899918E+1185), 558 | 520 => d!(1.761040341782111156146011710987957E+1188), 559 | 521 => d!(9.175020180684799123520721014247256E+1190), 560 | 522 => d!(4.789360534317465142477816369437068E+1193), 561 | 523 => d!(2.504835559448034269515897961215587E+1196), 562 | 524 => d!(1.312533833150769957226330531676968E+1199), 563 | 525 => d!(6.890802624041542275438235291304082E+1201), 564 | 526 => d!(3.624562180245851236880511763225947E+1204), 565 | 527 => d!(1.910144268989563601836029699220074E+1207), 566 | 528 => d!(1.008556174026489581769423681188199E+1210), 567 | 529 => d!(5.335262160600129887560251273485573E+1212), 568 | 530 => d!(2.827688945118068840406933174947354E+1215), 569 | 531 => d!(1.501502829857694554256081515897045E+1218), 570 | 532 => d!(7.987995054842935028642353664572279E+1220), 571 | 533 => d!(4.257601364231284370266374503217025E+1223), 572 | 534 => d!(2.273559128499505853722243984717891E+1226), 573 | 535 => d!(1.216354133747235631741400531824072E+1229), 574 | 536 => d!(6.519658156885182986133906850577026E+1231), 575 | 537 => d!(3.501056430247343263553907978759863E+1234), 576 | 538 => d!(1.883568359473070675792002492572806E+1237), 577 | 539 => d!(1.015243345755985094251889343496742E+1240), 578 | 540 => d!(5.482314067082319508960202454882407E+1242), 579 | 541 => d!(2.965931910291534854347469528091382E+1245), 580 | 542 => d!(1.607535095378011891056328484225529E+1248), 581 | 543 => d!(8.728915567902604568435863669344622E+1250), 582 | 544 => d!(4.748530068939016885229109836123474E+1253), 583 | 545 => d!(2.587948887571764202449864860687293E+1256), 584 | 546 => d!(1.413020092614183254537626213935262E+1259), 585 | 547 => d!(7.729219906599582402320815390225883E+1261), 586 | 548 => d!(4.235612508816571156471806833843784E+1264), 587 | 549 => d!(2.325351267340297564903021951780237E+1267), 588 | 550 => d!(1.278943197037163660696662073479130E+1270), 589 | 551 => d!(7.046977015674771770438608024870006E+1272), 590 | 552 => d!(3.889931312652474017282111629728243E+1275), 591 | 553 => d!(2.151132015896818131557007731239718E+1278), 592 | 554 => d!(1.191727136806837244882582283106804E+1281), 593 | 555 => d!(6.614085609277946709098331671242762E+1283), 594 | 556 => d!(3.677431598758538370258672409210976E+1286), 595 | 557 => d!(2.048329400508505872234080531930514E+1289), 596 | 558 => d!(1.142967805483746276706616936817227E+1292), 597 | 559 => d!(6.389190032654141686789988676808299E+1294), 598 | 560 => d!(3.577946418286319344602393659012647E+1297), 599 | 561 => d!(2.007227940658625152321942842706095E+1300), 600 | 562 => d!(1.128062102650147335604931877600825E+1303), 601 | 563 => d!(6.350989637920329499455766470892645E+1305), 602 | 564 => d!(3.581958155787065837693052289583452E+1308), 603 | 565 => d!(2.023806358019692198296574543614650E+1311), 604 | 566 => d!(1.145474398639145784235861191685892E+1314), 605 | 567 => d!(6.494839840283956596617332956859008E+1316), 606 | 568 => d!(3.689069029281287346878645119495917E+1319), 607 | 569 => d!(2.099080277661052500373949072993177E+1322), 608 | 570 => d!(1.196475758266799925213150971606111E+1325), 609 | 571 => d!(6.831876579703427572967092047870894E+1327), 610 | 572 => d!(3.907833403590360571737176651382151E+1330), 611 | 573 => d!(2.239188540257276607605402221241973E+1333), 612 | 574 => d!(1.285294222107676772765500874992893E+1336), 613 | 575 => d!(7.390441777119141443401630031209135E+1338), 614 | 576 => d!(4.256894463620625471399338897976462E+1341), 615 | 577 => d!(2.456228105509100896997418544132419E+1344), 616 | 578 => d!(1.419699844984260318464507918508538E+1347), 617 | 579 => d!(8.220062102458867243909500848164435E+1349), 618 | 580 => d!(4.767636019426143001467510491935372E+1352), 619 | 581 => d!(2.769996527286589083852623595814451E+1355), 620 | 582 => d!(1.612137978880794846802226932764010E+1358), 621 | 583 => d!(9.398764416875033956856983018014178E+1360), 622 | 584 => d!(5.488878419455019830804478082520280E+1363), 623 | 585 => d!(3.210993875381186601020619678274364E+1366), 624 | 586 => d!(1.881642410973375348198083131468777E+1369), 625 | 587 => d!(1.104524095241371329392274798172172E+1372), 626 | 588 => d!(6.494601680019263416826575813252371E+1374), 627 | 589 => d!(3.825320389531346152510853154005647E+1377), 628 | 590 => d!(2.256939029823494229981403360863332E+1380), 629 | 591 => d!(1.333850966625685089919009386270229E+1383), 630 | 592 => d!(7.896397722424055732320535566719756E+1385), 631 | 593 => d!(4.682563849397465049266077591064815E+1388), 632 | 594 => d!(2.781442926542094239264050089092500E+1391), 633 | 595 => d!(1.654958541292546072362109803010038E+1394), 634 | 596 => d!(9.863552906103574591278174425939826E+1396), 635 | 597 => d!(5.888541084943834030993070132286076E+1399), 636 | 598 => d!(3.521347568796412750533855939107073E+1402), 637 | 599 => d!(2.109287193709051237569779707525137E+1405), 638 | 600 => d!(1.265572316225430742541867824515082E+1408), 639 | 601 => d!(7.606089620514838762676625625335643E+1410), 640 | 602 => d!(4.578865951549932935131328626452057E+1413), 641 | 603 => d!(2.761056168784609559884191161750590E+1416), 642 | 604 => d!(1.667677925945904174170051461697356E+1419), 643 | 605 => d!(1.008945145197272025372881134326900E+1422), 644 | 606 => d!(6.114207579895468473759659674021014E+1424), 645 | 607 => d!(3.711324000996549363572113422130755E+1427), 646 | 608 => d!(2.256484992605902013051844960655499E+1430), 647 | 609 => d!(1.374199360496994325948573581039199E+1433), 648 | 610 => d!(8.382616099031665388286298844339114E+1435), 649 | 611 => d!(5.121778436508347552242928593891199E+1438), 650 | 612 => d!(3.134528403143108701972672299461414E+1441), 651 | 613 => d!(1.921465911126725634309248119569847E+1444), 652 | 614 => d!(1.179780069431809539465878345415886E+1447), 653 | 615 => d!(7.255647427005628667715151824307699E+1449), 654 | 616 => d!(4.469478815035467259312533523773543E+1452), 655 | 617 => d!(2.757668428876883298995833184168276E+1455), 656 | 618 => d!(1.704239089045913878779424907815995E+1458), 657 | 619 => d!(1.054923996119420690964464017938101E+1461), 658 | 620 => d!(6.540528775940408283979676911216226E+1463), 659 | 621 => d!(4.061668369858993544351379361865276E+1466), 660 | 622 => d!(2.526357726052293984586557963080202E+1469), 661 | 623 => d!(1.573920863330579152397425610998966E+1472), 662 | 624 => d!(9.821266187182813910959935812633548E+1474), 663 | 625 => d!(6.138291366989258694349959882895968E+1477), 664 | 626 => d!(3.842570395735275942663074886692876E+1480), 665 | 627 => d!(2.409291638126018016049747953956433E+1483), 666 | 628 => d!(1.513035148743139314079241715084640E+1486), 667 | 629 => d!(9.516991085594346285558430387882386E+1488), 668 | 630 => d!(5.995704383924438159901811144365903E+1491), 669 | 631 => d!(3.783289466256320478898042832094885E+1494), 670 | 632 => d!(2.391038942673994542663563069883967E+1497), 671 | 633 => d!(1.513527650712638545506035423236551E+1500), 672 | 634 => d!(9.595765305518128378508264583319733E+1502), 673 | 635 => d!(6.093310969004011520352748010408030E+1505), 674 | 636 => d!(3.875345776286551326944347734619507E+1508), 675 | 637 => d!(2.468595259494533195263549506952626E+1511), 676 | 638 => d!(1.574963775557512178578144585435775E+1514), 677 | 639 => d!(1.006401852581250282111434390093460E+1517), 678 | 640 => d!(6.440971856520001805513180096598144E+1519), 679 | 641 => d!(4.128662960029321157333948441919410E+1522), 680 | 642 => d!(2.650601620338824183008394899712261E+1525), 681 | 643 => d!(1.704336841877863949674397920514984E+1528), 682 | 644 => d!(1.097592926169344383590312260811650E+1531), 683 | 645 => d!(7.079474373792271274157514082235142E+1533), 684 | 646 => d!(4.573340445469807243105754097123902E+1536), 685 | 647 => d!(2.958951268218965286289422900839165E+1539), 686 | 648 => d!(1.917400421805889505515546039743779E+1542), 687 | 649 => d!(1.244392873752022289079589379793713E+1545), 688 | 650 => d!(8.088553679388144879017330968659134E+1547), 689 | 651 => d!(5.265648445281682316240282460597096E+1550), 690 | 652 => d!(3.433202786323656870188664164309307E+1553), 691 | 653 => d!(2.241881419469347936233197699293977E+1556), 692 | 654 => d!(1.466190448332953550296511295338261E+1559), 693 | 655 => d!(9.603547436580845754442148984465610E+1561), 694 | 656 => d!(6.299927118397034814914049733809440E+1564), 695 | 657 => d!(4.139052116786851873398530675112802E+1567), 696 | 658 => d!(2.723496292845748532696233184224224E+1570), 697 | 659 => d!(1.794784056985348283046817668403764E+1573), 698 | 660 => d!(1.184557477610329866810899661146484E+1576), 699 | 661 => d!(7.829924927004280419620046760178259E+1578), 700 | 662 => d!(5.183410301676833637788470955238007E+1581), 701 | 663 => d!(3.436601030011740701853756243322799E+1584), 702 | 664 => d!(2.281903083927795826030894145566339E+1587), 703 | 665 => d!(1.517465550811984224310544606801615E+1590), 704 | 666 => d!(1.010632056840781493390822708129876E+1593), 705 | 667 => d!(6.740915819128012560916787463226273E+1595), 706 | 668 => d!(4.502931767177512390692414025435150E+1598), 707 | 669 => d!(3.012461352241755789373224983016115E+1601), 708 | 670 => d!(2.018349106001976378880060738620797E+1604), 709 | 671 => d!(1.354312250127326150228520755614555E+1607), 710 | 672 => d!(9.100978320855631729535659477729810E+1609), 711 | 673 => d!(6.124958409935840153977498828512162E+1612), 712 | 674 => d!(4.128221968296756263780834210417197E+1615), 713 | 675 => d!(2.786549828600310478052063092031608E+1618), 714 | 676 => d!(1.883707684133809883163194650213367E+1621), 715 | 677 => d!(1.275270102158589290901482778194449E+1624), 716 | 678 => d!(8.646331292635235392312053236158364E+1626), 717 | 679 => d!(5.870858947699324831379884147351529E+1629), 718 | 680 => d!(3.992184084435540885338321220199040E+1632), 719 | 681 => d!(2.718677361500603342915396750955546E+1635), 720 | 682 => d!(1.854137960543411479868300584151682E+1638), 721 | 683 => d!(1.266376227051150040750049298975599E+1641), 722 | 684 => d!(8.662013393029866278730337204993097E+1643), 723 | 685 => d!(5.933479174225458400930280985420271E+1646), 724 | 686 => d!(4.070366713518664463038172755998306E+1649), 725 | 687 => d!(2.796341932187322486107224683370836E+1652), 726 | 688 => d!(1.923883249344877870441770582159135E+1655), 727 | 689 => d!(1.325555558798620852734379931107644E+1658), 728 | 690 => d!(9.146333355710483883867221524642744E+1660), 729 | 691 => d!(6.320116348795944363752250073528136E+1663), 730 | 692 => d!(4.373520513366793499716557050881470E+1666), 731 | 693 => d!(3.030849715763187895303574036260859E+1669), 732 | 694 => d!(2.103409702739652399340680381165036E+1672), 733 | 695 => d!(1.461869743404058417541772864909700E+1675), 734 | 696 => d!(1.017461341409224658609073913977151E+1678), 735 | 697 => d!(7.091705549622295870505245180420742E+1680), 736 | 698 => d!(4.950010473636362517612661135933678E+1683), 737 | 699 => d!(3.460057321071817399811250134017641E+1686), 738 | 700 => d!(2.422040124750272179867875093812349E+1689), 739 | 701 => d!(1.697850127449940798087380440762457E+1692), 740 | 702 => d!(1.191890789469858440257341069415245E+1695), 741 | 703 => d!(8.378992249973104835009107717989172E+1697), 742 | 704 => d!(5.898810543981065803846411833464377E+1700), 743 | 705 => d!(4.158661433506651391711720342592386E+1703), 744 | 706 => d!(2.936014972055695882548474561870225E+1706), 745 | 707 => d!(2.075762585243376988961771515242249E+1709), 746 | 708 => d!(1.469639910352310908184934232791512E+1712), 747 | 709 => d!(1.041974696439788433903118371049182E+1715), 748 | 710 => d!(7.398020344722497880712140434449192E+1717), 749 | 711 => d!(5.259992465097695993186331848893376E+1720), 750 | 712 => d!(3.745114635149559547148668276412084E+1723), 751 | 713 => d!(2.670266734861635957117000481081816E+1726), 752 | 714 => d!(1.906570448691208073381538343492417E+1729), 753 | 715 => d!(1.363197870814213772467799915597078E+1732), 754 | 716 => d!(9.760496755029770610869447395675078E+1734), 755 | 717 => d!(6.998276173356345527993393782699031E+1737), 756 | 718 => d!(5.024762292469856089099256735977904E+1740), 757 | 719 => d!(3.612804088285826528062365593168113E+1743), 758 | 720 => d!(2.601218943565795100204903227081041E+1746), 759 | 721 => d!(1.875478858310938267247735226725431E+1749), 760 | 722 => d!(1.354095735700497428952864833695761E+1752), 761 | 723 => d!(9.790112169114596411329212747620352E+1754), 762 | 724 => d!(7.088041210438967801802350029277135E+1757), 763 | 725 => d!(5.138829877568251656306703771225923E+1760), 764 | 726 => d!(3.730790491114550702478666937910020E+1763), 765 | 727 => d!(2.712284687040278360701990863860585E+1766), 766 | 728 => d!(1.974543252165322646591049348890506E+1769), 767 | 729 => d!(1.439442030828520209364874975341179E+1772), 768 | 730 => d!(1.050792682504819752836358731999061E+1775), 769 | 731 => d!(7.681294509110232393233782330913136E+1777), 770 | 732 => d!(5.622707580668690111847128666228416E+1780), 771 | 733 => d!(4.121444656630149851983945312345429E+1783), 772 | 734 => d!(3.025140377966529991356215859261545E+1786), 773 | 735 => d!(2.223478177805399543646818656557236E+1789), 774 | 736 => d!(1.636479938864774064124058531226126E+1792), 775 | 737 => d!(1.206085714943338485259431137513655E+1795), 776 | 738 => d!(8.900912576281838021214601794850774E+1797), 777 | 739 => d!(6.577774393872278297677590726394722E+1800), 778 | 740 => d!(4.867553051465485940281417137532094E+1803), 779 | 741 => d!(3.606856811135925081748530098911282E+1806), 780 | 742 => d!(2.676287753862856410657409333392171E+1809), 781 | 743 => d!(1.988481801120102313118455134710383E+1812), 782 | 744 => d!(1.479430460033356120960130620224525E+1815), 783 | 745 => d!(1.102175692724850310115297312067271E+1818), 784 | 746 => d!(8.222230667727383313460117948021842E+1820), 785 | 747 => d!(6.142006308792355335154708107172316E+1823), 786 | 748 => d!(4.594220718976681790695721664164892E+1826), 787 | 749 => d!(3.441071318513534661231095526459504E+1829), 788 | 750 => d!(2.580803488885150995923321644844628E+1832), 789 | 751 => d!(1.938183420152748397938414555278316E+1835), 790 | 752 => d!(1.457513931954866795249687745569294E+1838), 791 | 753 => d!(1.097507990762014696823014872413678E+1841), 792 | 754 => d!(8.275210250345590814045532137999132E+1843), 793 | 755 => d!(6.247783739010921064604376764189345E+1846), 794 | 756 => d!(4.723324506692256324840908833727145E+1849), 795 | 757 => d!(3.575556651566038037904567987131449E+1852), 796 | 758 => d!(2.710271941887056832731662534245638E+1855), 797 | 759 => d!(2.057096403892276136043331863492439E+1858), 798 | 760 => d!(1.563393266958129863392932216254254E+1861), 799 | 761 => d!(1.189742276155136826042021416569487E+1864), 800 | 762 => d!(9.065836144302142614440203194259491E+1866), 801 | 763 => d!(6.917232978102534814817875037219992E+1869), 802 | 764 => d!(5.284765995270336598520856528436074E+1872), 803 | 765 => d!(4.042845986381807497868455244253597E+1875), 804 | 766 => d!(3.096820025568464543367236717098255E+1878), 805 | 767 => d!(2.375260959611012304762670562014362E+1881), 806 | 768 => d!(1.824200416981257450057730991627030E+1884), 807 | 769 => d!(1.402810120658586979094395132561186E+1887), 808 | 770 => d!(1.080163792907111973902684252072113E+1890), 809 | 771 => d!(8.328062843313833318789695583475991E+1892), 810 | 772 => d!(6.429264515038279322105644990443465E+1895), 811 | 773 => d!(4.969821470124589915987663577612798E+1898), 812 | 774 => d!(3.846641817876432594974451609072306E+1901), 813 | 775 => d!(2.981147408854235261105199997031037E+1904), 814 | 776 => d!(2.313370389270886562617635197696085E+1907), 815 | 777 => d!(1.797488792463478859153902548609858E+1910), 816 | 778 => d!(1.398446280536586552421736182818470E+1913), 817 | 779 => d!(1.089389652538000924336532486415588E+1916), 818 | 780 => d!(8.497239289796407209824953394041586E+1918), 819 | 781 => d!(6.636343885330994030873288600746479E+1921), 820 | 782 => d!(5.189620918328837332142911685783747E+1924), 821 | 783 => d!(4.063473179051479631067899849968674E+1927), 822 | 784 => d!(3.185762972376360030757233482375440E+1930), 823 | 785 => d!(2.500823933315442624144428283664720E+1933), 824 | 786 => d!(1.965647611585937902577520630960470E+1936), 825 | 787 => d!(1.546964670318133129328508736565890E+1939), 826 | 788 => d!(1.219008160210688905910864884413921E+1942), 827 | 789 => d!(9.617974384062335467636723938025837E+1944), 828 | 790 => d!(7.598199763409245019433011911040411E+1947), 829 | 791 => d!(6.010176012856712810371512421632965E+1950), 830 | 792 => d!(4.760059402182516545814237837933308E+1953), 831 | 793 => d!(3.774727105930735620830690605481113E+1956), 832 | 794 => d!(2.997133322109004082939568340752004E+1959), 833 | 795 => d!(2.382720991076658245936956830897843E+1962), 834 | 796 => d!(1.896645908897019963765817637394683E+1965), 835 | 797 => d!(1.511626789390924911121356657003562E+1968), 836 | 798 => d!(1.206278177933958079074842612288842E+1971), 837 | 799 => d!(9.638162641692325051807992472187848E+1973), 838 | 800 => d!(7.710530113353860041446393977750278E+1976), 839 | 801 => d!(6.176134620796441893198561576177973E+1979), 840 | 802 => d!(4.953259965878746398345246384094734E+1982), 841 | 803 => d!(3.977467752600633357871232846428071E+1985), 842 | 804 => d!(3.197884073090909219728471208528169E+1988), 843 | 805 => d!(2.574296678838181921881419322865176E+1991), 844 | 806 => d!(2.074883123143574629036423974229332E+1994), 845 | 807 => d!(1.674430680376864725632394147203071E+1997), 846 | 808 => d!(1.352939989744506698310974470940081E+2000), 847 | 809 => d!(1.094528451703305918933578346990526E+2003), 848 | 810 => d!(8.865680458796777943361984610623261E+2005), 849 | 811 => d!(7.190066852084186912066569519215465E+2008), 850 | 812 => d!(5.838334283892359772598054449602958E+2011), 851 | 813 => d!(4.746565772804488495122218267527205E+2014), 852 | 814 => d!(3.863704539062853635029485669767145E+2017), 853 | 815 => d!(3.148919199336225712549030820860223E+2020), 854 | 816 => d!(2.569518066658360181440009149821942E+2023), 855 | 817 => d!(2.099296260459880268236487475404527E+2026), 856 | 818 => d!(1.717224341056182059417446754880903E+2029), 857 | 819 => d!(1.406406735325013106662888892247460E+2032), 858 | 820 => d!(1.153253522966510747463568891642917E+2035), 859 | 821 => d!(9.468211423555053236675900600388349E+2037), 860 | 822 => d!(7.782869790162253760547590293519223E+2040), 861 | 823 => d!(6.405301837303534844930666811566321E+2043), 862 | 824 => d!(5.277968713938112712222869452730649E+2046), 863 | 825 => d!(4.354324188998942987583867298502785E+2049), 864 | 826 => d!(3.596671780113126907744274388563300E+2052), 865 | 827 => d!(2.974447562153555952704514919341849E+2055), 866 | 828 => d!(2.462842581463144328839338353215051E+2058), 867 | 829 => d!(2.041696500032946648607811494815277E+2061), 868 | 830 => d!(1.694608095027345718344483540696680E+2064), 869 | 831 => d!(1.408219326967724291944265822318941E+2067), 870 | 832 => d!(1.171638480037146610897629164169359E+2070), 871 | 833 => d!(9.759748538709431268777250937530760E+2072), 872 | 834 => d!(8.139630281283665678160227281900654E+2075), 873 | 835 => d!(6.796591284871860841263789780387046E+2078), 874 | 836 => d!(5.681950314152875663296528256403570E+2081), 875 | 837 => d!(4.755792412945956930179194150609788E+2084), 876 | 838 => d!(3.985354042048711907490164698211002E+2087), 877 | 839 => d!(3.343712041278869290384248181799031E+2090), 878 | 840 => d!(2.808718114674250203922768472711186E+2093), 879 | 841 => d!(2.362131934441044421499048285550107E+2096), 880 | 842 => d!(1.988915088799359402902198656433190E+2099), 881 | 843 => d!(1.676655419857859976646553467373179E+2102), 882 | 844 => d!(1.415097174360033820289691126462963E+2105), 883 | 845 => d!(1.195757112334228578144789001861204E+2108), 884 | 846 => d!(1.011610517034757377110491495574579E+2111), 885 | 847 => d!(8.568341079284394984125862967516684E+2113), 886 | 848 => d!(7.265953235233166946538731796454148E+2116), 887 | 849 => d!(6.168794296712958737611383295189572E+2119), 888 | 850 => d!(5.243475152206014926969675800911136E+2122), 889 | 851 => d!(4.462197354527318702851194106575377E+2125), 890 | 852 => d!(3.801792146057275534829217378802221E+2128), 891 | 853 => d!(3.242928700586856031209322424118295E+2131), 892 | 854 => d!(2.769461110301175050652761350197024E+2134), 893 | 855 => d!(2.367889249307504668308110954418456E+2137), 894 | 856 => d!(2.026913197407223996071742976982198E+2140), 895 | 857 => d!(1.737064610177990964633483731273744E+2143), 896 | 858 => d!(1.490401435532716247655529041432872E+2146), 897 | 859 => d!(1.280254833122603256736099446590837E+2149), 898 | 860 => d!(1.101019156485438800793045524068120E+2152), 899 | 861 => d!(9.479774937339628074828121962226513E+2154), 900 | 862 => d!(8.171565995986759400501841131439254E+2157), 901 | 863 => d!(7.052061454536573362633088896432076E+2160), 902 | 864 => d!(6.092981096719599385314988806517314E+2163), 903 | 865 => d!(5.270428648662453468297465317637477E+2166), 904 | 866 => d!(4.564191209741684703545604965074055E+2169), 905 | 867 => d!(3.957153778846040637974039504719206E+2172), 906 | 868 => d!(3.434809480038363273761466290096271E+2175), 907 | 869 => d!(2.984849438153337684898714206093659E+2178), 908 | 870 => d!(2.596819011193403785861881359301483E+2181), 909 | 871 => d!(2.261829358749454697485698663951592E+2184), 910 | 872 => d!(1.972315200829524496207529234965788E+2187), 911 | 873 => d!(1.721831170324174885189173022125133E+2190), 912 | 874 => d!(1.504880442863328849655337221337366E+2193), 913 | 875 => d!(1.316770387505412743448420068670195E+2196), 914 | 876 => d!(1.153490859454741563260815980155091E+2199), 915 | 877 => d!(1.011611483741808350979735614596015E+2202), 916 | 878 => d!(8.881948827253077321602078696153012E+2204), 917 | 879 => d!(7.807233019155454965688227173918498E+2207), 918 | 880 => d!(6.870365056856800369805639913048278E+2210), 919 | 881 => d!(6.052791615090841125798768763395533E+2213), 920 | 882 => d!(5.338562204510121872954514049314860E+2216), 921 | 883 => d!(4.713950426582437613818835905545021E+2219), 922 | 884 => d!(4.167132177098874850615850940501799E+2222), 923 | 885 => d!(3.687911976732504242795028082344092E+2225), 924 | 886 => d!(3.267490011384998759116394880956866E+2228), 925 | 887 => d!(2.898263640098493899336242259408740E+2231), 926 | 888 => d!(2.573658112407462582610583126354961E+2234), 927 | 889 => d!(2.287982061930234235940808399329560E+2237), 928 | 890 => d!(2.036304035117908469987319475403308E+2240), 929 | 891 => d!(1.814346895290056446758701652584347E+2243), 930 | 892 => d!(1.618397430598730350508761874105238E+2246), 931 | 893 => d!(1.445228905524666203004324353575978E+2249), 932 | 894 => d!(1.292034641539051585485865972096924E+2252), 933 | 895 => d!(1.156371004177451169009850045026747E+2255), 934 | 896 => d!(1.036108419742996247432825640343965E+2258), 935 | 897 => d!(9.293892525094676339472445993885366E+2260), 936 | 898 => d!(8.345915487535019352846256502509059E+2263), 937 | 899 => d!(7.502978023293982398208784595755644E+2266), 938 | 900 => d!(6.752680220964584158387906136180080E+2269), 939 | 901 => d!(6.084164879089090326707503428698252E+2272), 940 | 902 => d!(5.487916720938359474690168092685823E+2275), 941 | 903 => d!(4.955588799007338605645221787695298E+2278), 942 | 904 => d!(4.479852274302634099503280496076549E+2281), 943 | 905 => d!(4.054266308243883860050468848949277E+2284), 944 | 906 => d!(3.673165275268958777205724777148045E+2287), 945 | 907 => d!(3.331560904668945610925592372873277E+2290), 946 | 908 => d!(3.025057301439402614720437874568936E+2293), 947 | 909 => d!(2.749777087008416976780878027983163E+2296), 948 | 910 => d!(2.502297149177659448870599005464678E+2299), 949 | 911 => d!(2.279592702900847757921115693978322E+2302), 950 | 912 => d!(2.078988545045573155224057512908230E+2305), 951 | 913 => d!(1.898116541626608290719564509285214E+2308), 952 | 914 => d!(1.734878519046719977717681961486686E+2311), 953 | 915 => d!(1.587413844927748779611678994760318E+2314), 954 | 916 => d!(1.454071081953817882124297959200451E+2317), 955 | 917 => d!(1.333383182151650997907981228586814E+2320), 956 | 918 => d!(1.224045761215215616079526767842695E+2323), 957 | 919 => d!(1.124898054556783151177085099647437E+2326), 958 | 920 => d!(1.034906210192240499082918291675642E+2329), 959 | 921 => d!(9.531486195870534996553677466332663E+2331), 960 | 922 => d!(8.788030272592633266822490623958715E+2334), 961 | 923 => d!(8.111351941603000505277158845913894E+2337), 962 | 924 => d!(7.494889194041172466876094773624438E+2340), 963 | 925 => d!(6.932772504488084531860387665602605E+2343), 964 | 926 => d!(6.419747339155966276502718978348012E+2346), 965 | 927 => d!(5.951105783397580738318020492928607E+2349), 966 | 928 => d!(5.522626166992954925159123017437747E+2352), 967 | 929 => d!(5.130519709136455125472825283199667E+2355), 968 | 930 => d!(4.771383329496903266689727513375690E+2358), 969 | 931 => d!(4.442157879761616941288136314952767E+2361), 970 | 932 => d!(4.140091143937826989280543045535979E+2364), 971 | 933 => d!(3.862705037293992580998746661485068E+2367), 972 | 934 => d!(3.607766504832589070652829381827054E+2370), 973 | 935 => d!(3.373261682018470781060395472008295E+2373), 974 | 936 => d!(3.157372934369288651072530161799764E+2376), 975 | 937 => d!(2.958458439504023466054960761606379E+2379), 976 | 938 => d!(2.775034016254774011159553194386784E+2382), 977 | 939 => d!(2.605756941263232796478820449529190E+2385), 978 | 940 => d!(2.449411524787438828690091222557439E+2388), 979 | 941 => d!(2.304896244824979937797375840426550E+2391), 980 | 942 => d!(2.171212262625131101405128041681810E+2394), 981 | 943 => d!(2.047453163655498628625035743305947E+2397), 982 | 944 => d!(1.932795786490790705422033741680814E+2400), 983 | 945 => d!(1.826492018233797216623821885888369E+2403), 984 | 946 => d!(1.727861449249172166926135504050397E+2406), 985 | 947 => d!(1.636284792438966042079050322335726E+2409), 986 | 948 => d!(1.551197983232139807890939705574268E+2412), 987 | 949 => d!(1.472086886087300677688501780589980E+2415), 988 | 950 => d!(1.398482541782935643804076691560481E+2418), 989 | 951 => d!(1.329956897235571797257676933674017E+2421), 990 | 952 => d!(1.266118966168264350989308440857664E+2424), 991 | 953 => d!(1.206611374758355926492810944137354E+2427), 992 | 954 => d!(1.151107251519471553874141640707036E+2430), 993 | 955 => d!(1.099307425201095333949805266875219E+2433), 994 | 956 => d!(1.050937898492247139256013835132709E+2436), 995 | 957 => d!(1.005747568857080512268005240222003E+2439), 996 | 958 => d!(9.635061709650831307527490201326789E+2441), 997 | 959 => d!(9.240024179555147223918863103072391E+2444), 998 | 960 => d!(8.870423212372941334962108578949495E+2447), 999 | 961 => d!(8.524476707090396622898586344370465E+2450), 1000 | 962 => d!(8.200546592220961551228440063284387E+2453), 1001 | 963 => d!(7.897126368308785973832987780942865E+2456), 1002 | 964 => d!(7.612829819049669678775000220828922E+2459), 1003 | 965 => d!(7.346380775382931240017875213099910E+2462), 1004 | 966 => d!(7.096603829019911577857267455854513E+2465), 1005 | 967 => d!(6.862415902662254495787977629811314E+2468), 1006 | 968 => d!(6.642818593777062351922762345657352E+2471), 1007 | 969 => d!(6.436891217369973419013156712941974E+2474), 1008 | 970 => d!(6.243784480848874216442762011553715E+2477), 1009 | 971 => d!(6.062714730904256864165921913218657E+2480), 1010 | 972 => d!(5.892958718438937671969276099648535E+2483), 1011 | 973 => d!(5.733848833041086354826105644958025E+2486), 1012 | 974 => d!(5.584768763382018109600626898189116E+2489), 1013 | 975 => d!(5.445149544297467656860611225734388E+2492), 1014 | 976 => d!(5.314465955234328433095956556316763E+2495), 1015 | 977 => d!(5.192233238263938879134749555521477E+2498), 1016 | 978 => d!(5.078004107022132223793785065300005E+2501), 1017 | 979 => d!(4.971366020774667447094115578928705E+2504), 1018 | 980 => d!(4.871938700359174098152233267350131E+2507), 1019 | 981 => d!(4.779371865052349790287340835270479E+2510), 1020 | 982 => d!(4.693343171481407494062168700235610E+2513), 1021 | 983 => d!(4.613556337566223566663111832331605E+2516), 1022 | 984 => d!(4.539739436165163989596502043014299E+2519), 1023 | 985 => d!(4.471643344622686529752554512369085E+2522), 1024 | 986 => d!(4.409040337797968918336018749195918E+2525), 1025 | 987 => d!(4.351722813406595322397650505456371E+2528), 1026 | 988 => d!(4.299502139645716178528878699390895E+2531), 1027 | 989 => d!(4.252207616109613300565061033697595E+2534), 1028 | 990 => d!(4.209685539948517167559410423360619E+2537), 1029 | 991 => d!(4.171798370088980513051375729550373E+2540), 1030 | 992 => d!(4.138423983128268668946964723713970E+2543), 1031 | 993 => d!(4.109455015246370788264335970647972E+2546), 1032 | 994 => d!(4.084798285154892563534749954824084E+2549), 1033 | 995 => d!(4.064374293729118100717076205049964E+2552), 1034 | 996 => d!(4.048116796554201628314207900229764E+2555), 1035 | 997 => d!(4.035972446164539023429265276529075E+2558), 1036 | 998 => d!(4.027900501272209945382406745976017E+2561), 1037 | 999 => d!(4.023872600770937735437024339230041E+2564), 1038 | 1000 => d!(4.023872600770937735437024339230041E+2567), 1039 | _ => D128::NAN, 1040 | } 1041 | } 1042 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use cpc::eval; 2 | use std::env; 3 | use std::process::exit; 4 | 5 | const VERSION: &str = env!("CARGO_PKG_VERSION"); 6 | 7 | fn print_help() { 8 | println!(concat!( 9 | "Usage: cpc '' [options]", 10 | "\n", 11 | "\nOptions:", 12 | "\n --verbose Enable verbose logging", 13 | "\n --version Show cpc version", 14 | "\n --help Show this help page", 15 | )); 16 | } 17 | 18 | fn get_args() -> env::Args { 19 | let mut args = env::args(); 20 | args.next(); // skip binary name 21 | args 22 | } 23 | 24 | /// CLI interface 25 | fn main() { 26 | // parse these first so they work if there are unexpected args 27 | for arg in get_args() { 28 | match arg.as_str() { 29 | "--version" => { 30 | println!("{VERSION}"); 31 | exit(0); 32 | } 33 | "--help" => { 34 | print_help(); 35 | exit(0); 36 | } 37 | _ => {} 38 | } 39 | } 40 | let mut verbose = false; 41 | let mut expression_opt = None; 42 | for arg in get_args() { 43 | match arg.as_str() { 44 | "-v" | "--verbose" => verbose = true, 45 | _ => { 46 | if expression_opt.is_none() { 47 | expression_opt = Some(arg); 48 | } else { 49 | eprintln!("Unexpected argument: {}", arg); 50 | exit(1); 51 | } 52 | } 53 | } 54 | } 55 | let expression = match expression_opt { 56 | Some(expression) => expression, 57 | None => { 58 | print_help(); 59 | exit(0); 60 | } 61 | }; 62 | 63 | match eval(&expression, true, verbose) { 64 | Ok(answer) => { 65 | if !verbose { 66 | println!("{answer}"); 67 | } 68 | } 69 | Err(e) => { 70 | eprintln!("{e}"); 71 | exit(1); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::units::Unit::{Foot, Inch}; 2 | use crate::Operator::{Caret, Divide, LeftParen, Minus, Modulo, Multiply, Plus, RightParen}; 3 | use crate::TextOperator::{Of, To}; 4 | use crate::Token; 5 | use crate::UnaryOperator::{Factorial, Percent}; 6 | 7 | #[derive(Debug)] 8 | /// A struct with a [`Token`](AstNode::token) and [`AstNode`] [`children`](AstNode::children) 9 | pub struct AstNode { 10 | /// The children of the [`AstNode`] 11 | pub children: Vec, 12 | /// The token of the [`AstNode`] 13 | pub token: Token, 14 | } 15 | 16 | impl AstNode { 17 | pub const fn new(token: Token) -> AstNode { 18 | AstNode { 19 | children: Vec::new(), 20 | token, 21 | } 22 | } 23 | } 24 | 25 | /// Parse [`Token`]s into an Abstract Syntax Tree ([`AstNode`]) 26 | pub fn parse(tokens: &[Token]) -> Result { 27 | parse_text_operators(tokens, 0).and_then(|(ast, next_pos)| { 28 | if next_pos == tokens.len() { 29 | Ok(ast) 30 | } else { 31 | Err(format!( 32 | "Expected end of input, found {:?} at {}", 33 | tokens[next_pos], next_pos 34 | )) 35 | } 36 | }) 37 | } 38 | 39 | // level 1 precedence (lowest): to, of 40 | /// Parse [`To`](crate::TextOperator::To) and [`Of`](crate::TextOperator::Of) 41 | pub fn parse_text_operators(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 42 | // do higher precedences first, then come back down 43 | let (mut node, mut pos) = parse_plus(tokens, pos)?; 44 | // now we loop through the next tokens 45 | loop { 46 | let token = tokens.get(pos); 47 | match token { 48 | // if there's a match, we once again do higher precedences, then come 49 | // back down again and continue the loop 50 | Some(&Token::TextOperator(To)) | Some(&Token::TextOperator(Of)) => { 51 | let (right_node, next_pos) = parse_plus(tokens, pos + 1)?; 52 | let mut new_node = AstNode::new(token.unwrap().clone()); 53 | new_node.children.push(node); 54 | new_node.children.push(right_node); 55 | node = new_node; 56 | pos = next_pos; 57 | } 58 | // if there's no match, we go down to a lower precedence 59 | _ => { 60 | return Ok((node, pos)); 61 | } 62 | } 63 | } 64 | } 65 | 66 | /// Parse [`+`](crate::Operator::Plus), [`-`](crate::Operator::Minus) 67 | pub fn parse_plus(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 68 | let (mut node, mut pos) = parse_unary(tokens, pos)?; 69 | loop { 70 | let token = tokens.get(pos); 71 | match token { 72 | Some(&Token::Operator(Plus)) | Some(&Token::Operator(Minus)) => { 73 | let (right_node, next_pos) = parse_unary(tokens, pos + 1)?; 74 | let mut new_node = AstNode::new(token.unwrap().clone()); 75 | new_node.children.push(node); 76 | new_node.children.push(right_node); 77 | node = new_node; 78 | pos = next_pos; 79 | } 80 | _ => { 81 | return Ok((node, pos)); 82 | } 83 | } 84 | } 85 | } 86 | 87 | /// Parse [`unary -`](Token::Negative) (for example -5) 88 | pub fn parse_unary(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 89 | // Since a unary operator has no left side, we parse the the unary operator immediately 90 | let token = tokens.get(pos); 91 | match token { 92 | Some(&Token::Operator(Minus)) => { 93 | let (right_node, next_pos) = parse_mult_level(tokens, pos + 1)?; 94 | let mut new_node = AstNode::new(Token::Negative); 95 | new_node.children.push(right_node); 96 | Ok((new_node, next_pos)) 97 | } 98 | _ => parse_mult_level(tokens, pos), 99 | } 100 | } 101 | 102 | /// Parse [`*`](crate::Operator::Multiply), [`/`](crate::Operator::Divide), [`Modulo`](crate::Operator::Modulo), implicative multiplication (for example`2pi`), foot-inch syntax (for example `6'4"`) 103 | pub fn parse_mult_level(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 104 | // parse foot-inch syntax 6'4" 105 | let token0 = tokens.get(pos); 106 | if let Some(Token::Number(_number)) = token0 { 107 | let token1 = tokens.get(pos + 1); 108 | if let Some(Token::Unit(Foot)) = token1 { 109 | let token2 = tokens.get(pos + 2); 110 | if let Some(Token::Number(_number)) = token2 { 111 | let token3 = tokens.get(pos + 3); 112 | if let Some(Token::Unit(Inch)) = token3 { 113 | let new_node = AstNode { 114 | children: vec![ 115 | AstNode { 116 | children: vec![AstNode::new(token0.unwrap().clone())], 117 | token: Token::Unit(Foot), 118 | }, 119 | AstNode { 120 | children: vec![AstNode::new(token2.unwrap().clone())], 121 | token: Token::Unit(Inch), 122 | }, 123 | ], 124 | token: Token::Operator(Plus), 125 | }; 126 | return Ok((new_node, pos + 4)); 127 | } 128 | } 129 | } 130 | } 131 | 132 | let (mut node, mut pos) = parse_caret(tokens, pos)?; 133 | 134 | loop { 135 | let token = tokens.get(pos); 136 | match token { 137 | Some(&Token::Operator(Multiply)) 138 | | Some(&Token::Operator(Divide)) 139 | | Some(&Token::Operator(Modulo)) => { 140 | let (right_node, next_pos) = parse_caret(tokens, pos + 1)?; 141 | let mut new_node = AstNode::new(token.unwrap().clone()); 142 | new_node.children.push(node); 143 | new_node.children.push(right_node); 144 | node = new_node; 145 | pos = next_pos; 146 | } 147 | 148 | // Below is implicative multiplication, for example '2pi'. Constants and 149 | // such will only end up here if they were unable to be parsed as part of 150 | // other operators. 151 | // Note that this match statement matches an AstNode token, but the 152 | // matches nested inside check the [`Token`]s. That's why we for example 153 | // match a FunctionIdentifier, and inside that, a RightParen. 154 | 155 | // pi2, )2 156 | Some(&Token::Number(_)) => { 157 | let last_token = tokens.get(pos - 1); 158 | match last_token { 159 | Some(&Token::Constant(_)) | Some(&Token::Operator(RightParen)) => { 160 | let (right_node, next_pos) = parse_caret(tokens, pos)?; 161 | let mut new_node = AstNode::new(Token::Operator(Multiply)); 162 | new_node.children.push(node); 163 | new_node.children.push(right_node); 164 | node = new_node; 165 | pos = next_pos; 166 | } 167 | _ => { 168 | return Ok((node, pos)); 169 | } 170 | } 171 | } 172 | // 2pi, )pi 173 | Some(&Token::Constant(_)) => { 174 | let last_token = tokens.get(pos - 1); 175 | match last_token { 176 | Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => { 177 | let (right_node, next_pos) = parse_caret(tokens, pos)?; 178 | let mut new_node = AstNode::new(Token::Operator(Multiply)); 179 | new_node.children.push(node); 180 | new_node.children.push(right_node); 181 | node = new_node; 182 | pos = next_pos; 183 | } 184 | _ => { 185 | return Ok((node, pos)); 186 | } 187 | } 188 | } 189 | // 2log(1), )log(1) 190 | Some(&Token::FunctionIdentifier(_)) => { 191 | let last_token = tokens.get(pos - 1); 192 | match last_token { 193 | Some(&Token::Number(_)) | Some(&Token::Operator(RightParen)) => { 194 | let (right_node, next_pos) = parse_caret(tokens, pos)?; 195 | let mut new_node = AstNode::new(Token::Operator(Multiply)); 196 | new_node.children.push(node); 197 | new_node.children.push(right_node); 198 | node = new_node; 199 | pos = next_pos; 200 | } 201 | _ => { 202 | return Ok((node, pos)); 203 | } 204 | } 205 | } 206 | // 2(3), pi(3), )(3) 207 | Some(&Token::Operator(LeftParen)) => { 208 | let last_token = tokens.get(pos - 1); 209 | match last_token { 210 | Some(&Token::Number(_)) 211 | | Some(&Token::Constant(_)) 212 | | Some(&Token::Operator(RightParen)) => { 213 | let (right_node, next_pos) = parse_caret(tokens, pos)?; 214 | let mut new_node = AstNode::new(Token::Operator(Multiply)); 215 | new_node.children.push(node); 216 | new_node.children.push(right_node); 217 | node = new_node; 218 | pos = next_pos; 219 | } 220 | _ => { 221 | return Ok((node, pos)); 222 | } 223 | } 224 | } 225 | _ => { 226 | return Ok((node, pos)); 227 | } 228 | } 229 | } 230 | } 231 | 232 | /// Parse [`^`](crate::Operator::Caret) 233 | pub fn parse_caret(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 234 | let (mut node, mut pos) = parse_unary_high(tokens, pos)?; 235 | loop { 236 | let token = tokens.get(pos); 237 | match token { 238 | Some(&Token::Operator(Caret)) => { 239 | let (right_node, next_pos) = parse_unary_high(tokens, pos + 1)?; 240 | let mut new_node = AstNode::new(token.unwrap().clone()); 241 | new_node.children.push(node); 242 | new_node.children.push(right_node); 243 | node = new_node; 244 | pos = next_pos; 245 | } 246 | _ => { 247 | return Ok((node, pos)); 248 | } 249 | } 250 | } 251 | } 252 | 253 | /// Parse [`unary -`](Token::Negative) at high precedence (for example in 3^-2) 254 | pub fn parse_unary_high(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 255 | let token = tokens.get(pos); 256 | match token { 257 | Some(&Token::Operator(Minus)) => { 258 | let (right_node, next_pos) = parse_suffix(tokens, pos + 1)?; 259 | let mut new_node = AstNode::new(Token::Negative); 260 | new_node.children.push(right_node); 261 | Ok((new_node, next_pos)) 262 | } 263 | _ => parse_suffix(tokens, pos), 264 | } 265 | } 266 | 267 | /// Parse [`!!`](crate::UnaryOperator::Factorial), [`Percent`](crate::UnaryOperator::Percent), units attached to values 268 | pub fn parse_suffix(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 269 | let (mut node, mut pos) = parse_highest(tokens, pos)?; 270 | loop { 271 | let token = tokens.get(pos); 272 | match token { 273 | Some(&Token::UnaryOperator(Factorial)) 274 | | Some(&Token::UnaryOperator(Percent)) 275 | | Some(&Token::NamedNumber(_)) => { 276 | // Here we are handling unary operators, aka stuff written as 277 | // "Number Operator" (3!) instead of "Number Operator Number" (3+3). 278 | // Therefore, if we find a match, we don't parse what comes after it. 279 | let mut new_node = AstNode::new(token.unwrap().clone()); 280 | new_node.children.push(node); 281 | node = new_node; 282 | pos += 1; 283 | } 284 | Some(&Token::Unit(_unit)) => { 285 | // We won't allow units to repeat, like "1min min", so we end the loop if it's found. 286 | let mut new_node = AstNode::new(token.unwrap().clone()); 287 | new_node.children.push(node); 288 | return Ok((new_node, pos + 1)); 289 | } 290 | _ => { 291 | // let's say we parse 1+2. parse_level_7 then returns 1, and token 292 | // is set to plus. Plus has lower precedence than level 4, so we 293 | // don't do anything, and pass the number down to a lower precedence. 294 | return Ok((node, pos)); 295 | } 296 | } 297 | } 298 | } 299 | 300 | /// Parse [`Number`](Token::Number), standalone [`Unit`](Token::Unit), [`Constant`](Token::Constant), [`FunctionIdentifier`](Token::FunctionIdentifier), [`Paren`](Token::Paren) 301 | pub fn parse_highest(tokens: &[Token], pos: usize) -> Result<(AstNode, usize), String> { 302 | let token: &Token = tokens 303 | .get(pos) 304 | .ok_or(format!("Unexpected end of input at {}", pos))?; 305 | match token { 306 | &Token::Number(_number) => { 307 | let node = AstNode::new(token.clone()); 308 | Ok((node, pos + 1)) 309 | } 310 | &Token::Unit(_unit) => { 311 | let node = AstNode::new(token.clone()); 312 | Ok((node, pos + 1)) 313 | } 314 | Token::Constant(_constant) => { 315 | let node = AstNode::new(token.clone()); 316 | Ok((node, pos + 1)) 317 | } 318 | Token::FunctionIdentifier(_function_identifier) => { 319 | let left_paren_pos = pos + 1; 320 | let left_paren_token = tokens.get(left_paren_pos); 321 | // check if '(' comes after function identifier, like 'log(' 322 | match left_paren_token { 323 | Some(&Token::Operator(LeftParen)) => { 324 | // parse everything inside as you would with normal parentheses, 325 | // then put it inside an ast node. 326 | parse_text_operators(tokens, left_paren_pos + 1).and_then(|(node, next_pos)| { 327 | if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) { 328 | let mut function_node = AstNode::new(token.clone()); 329 | function_node.children.push(node); 330 | Ok((function_node, next_pos + 1)) 331 | } else { 332 | Err(format!( 333 | "Expected closing paren at {} but found {:?}", 334 | next_pos, 335 | tokens.get(next_pos) 336 | )) 337 | } 338 | }) 339 | } 340 | _ => Err(format!( 341 | "Expected ( after {} at {:?} but found {:?}", 342 | left_paren_pos, token, left_paren_token 343 | )), 344 | } 345 | } 346 | Token::Operator(LeftParen) => { 347 | parse_text_operators(tokens, pos + 1).and_then(|(node, next_pos)| { 348 | if let Some(&Token::Operator(RightParen)) = tokens.get(next_pos) { 349 | let mut paren_node = AstNode::new(Token::Paren); 350 | paren_node.children.push(node); 351 | Ok((paren_node, next_pos + 1)) 352 | } else { 353 | Err(format!( 354 | "Expected closing paren at {} but found {:?}", 355 | next_pos, 356 | tokens.get(next_pos) 357 | )) 358 | } 359 | }) 360 | } 361 | _ => Err(format!( 362 | "Unexpected token {:?}, expected paren or number", 363 | token 364 | )), 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /src/units.rs: -------------------------------------------------------------------------------- 1 | use fastnum::{dec128 as d, D128}; 2 | use crate::Number; 3 | 4 | #[derive(Clone, Copy, PartialEq, Debug)] 5 | /// An enum of all possible unit types, like [`Length`], [`DigitalStorage`] etc. 6 | /// There is also a [`NoType`] unit type for normal numbers. 7 | pub enum UnitType { 8 | /// A normal number, for example `5` 9 | NoType, 10 | /// A unit of time, for example [`Hour`] 11 | Time, 12 | /// A unit of length, for example [`Mile`] 13 | Length, 14 | /// A unit of area, for example [`SquareKilometer`] 15 | Area, 16 | /// A unit of volume, for example [`Liter`] or [`Tablespoon`] 17 | Volume, 18 | /// A unit of mass, for example [`Gram`] 19 | Mass, 20 | /// A unit of digital storage, for example [`Kilobyte`] 21 | DigitalStorage, 22 | /// A unit of data rate transfer, for example [`KilobytesPerSecond`] 23 | DataTransferRate, 24 | /// A unit of energy, for example [`Joule`] or [`KilowattHour`] 25 | Energy, 26 | /// A unit of power, for example [`Watt`] 27 | Power, 28 | /// A unit of electrical current, for example [`Ampere`] 29 | ElectricCurrent, 30 | /// A unit of electric resistance, for example [`Ohm`] 31 | Resistance, 32 | /// A unit of voltage, for example [`Volt`] 33 | Voltage, 34 | /// A unit of pressure, for example [`Bar`] 35 | Pressure, 36 | /// A unit of frequency, for example [`Hertz`] 37 | Frequency, 38 | /// A unit of x, for example [`KilometersPerHour`] 39 | Speed, 40 | /// A unit of temperature, for example [`Kelvin`] 41 | Temperature, 42 | } 43 | use UnitType::*; 44 | 45 | // Macro for creating units. Not possible to extend/change the default units 46 | // with this because the default units are imported into the lexer, parser 47 | // and evaluator 48 | macro_rules! create_units { 49 | ( $( $variant:ident : $properties:expr ),*, ) => { 50 | #[derive(Clone, Copy, PartialEq, Debug)] 51 | /// A Unit enum. Note that it can also be [`NoUnit`]. 52 | pub enum Unit { 53 | $($variant),* 54 | } 55 | use Unit::*; 56 | 57 | impl Unit { 58 | pub fn category(&self) -> UnitType { 59 | match self { 60 | $( 61 | Unit::$variant => $properties.0 62 | ),* 63 | } 64 | } 65 | pub fn weight(&self) -> D128 { 66 | match self { 67 | $( 68 | Unit::$variant => $properties.1 69 | ),* 70 | } 71 | } 72 | pub(crate) fn singular(&self) -> &str { 73 | match self { 74 | $( 75 | Unit::$variant => $properties.2 76 | ),* 77 | } 78 | } 79 | pub(crate) fn plural(&self) -> &str { 80 | match self { 81 | $( 82 | Unit::$variant => $properties.3 83 | ),* 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | create_units!( 91 | NoUnit: (NoType, d!(1), "", ""), 92 | 93 | Nanosecond: (Time, d!(1), "nanosecond", "nanoseconds"), 94 | Microsecond: (Time, d!(1000), "microsecond", "microseconds"), 95 | Millisecond: (Time, d!(1000000), "millisecond", "milliseconds"), 96 | Second: (Time, d!(1000000000), "second", "seconds"), 97 | Minute: (Time, d!(60000000000), "minute", "minutes"), 98 | Hour: (Time, d!(3600000000000), "hour", "hours"), 99 | Day: (Time, d!(86400000000000), "day", "days"), 100 | Week: (Time, d!(604800000000000), "week", "weeks"), 101 | Month: (Time, d!(2629746000000000), "month", "months"), 102 | Quarter: (Time, d!(7889238000000000), "quarter", "quarters"), 103 | Year: (Time, d!(31556952000000000), "year", "years"), 104 | Decade: (Time, d!(315569520000000000), "decade", "decades"), 105 | Century: (Time, d!(3155695200000000000), "century", "centuries"), 106 | Millenium: (Time, d!(31556952000000000000), "millenium", "millenia"), 107 | 108 | Millimeter: (Length, d!(1), "millimeter", "millimeters"), 109 | Centimeter: (Length, d!(10), "centimeter", "centimeters"), 110 | Decimeter: (Length, d!(100), "decimeter", "decimeters"), 111 | Meter: (Length, d!(1000), "meter", "meters"), 112 | Kilometer: (Length, d!(1000000), "kilometer", "kilometers"), 113 | Inch: (Length, d!(25.4), "inch", "inches"), 114 | Foot: (Length, d!(304.8), "foot", "feet"), 115 | Yard: (Length, d!(914.4), "yard", "yards"), 116 | Mile: (Length, d!(1609344), "mile", "miles"), 117 | // 1-dimensional only: 118 | Marathon: (Length, d!(42195000), "marathon", "marathons"), 119 | NauticalMile: (Length, d!(1852000), "nautical mile", "nautical miles"), 120 | LightYear: (Length, d!(9460730472580800000), "light year", "light years"), 121 | LightSecond: (Length, d!(299792458000), "light second", "light seconds"), 122 | 123 | SquareMillimeter: (Area, d!(1), "square millimeter", "square millimeters"), 124 | SquareCentimeter: (Area, d!(100), "square centimeter", "square centimeters"), 125 | SquareDecimeter: (Area, d!(10000), "square decimeter", "square decimeters"), 126 | SquareMeter: (Area, d!(1000000), "square meter", "square meters"), 127 | SquareKilometer: (Area, d!(1000000000000), "square kilometer", "square kilometers"), 128 | SquareInch: (Area, d!(645.16), "square inch", "square inches"), 129 | SquareFoot: (Area, d!(92903.04), "square foot", "square feet"), 130 | SquareYard: (Area, d!(836127.36), "square yard", "square yards"), 131 | SquareMile: (Area, d!(2589988110336.00), "square mile", "square miles"), 132 | // 2-dimensional only 133 | Are: (Area, d!(100000000), "are", "ares"), 134 | Decare: (Area, d!(1000000000), "decare", "decare"), 135 | Hectare: (Area, d!(10000000000), "hectare", "hectares"), 136 | Acre: (Area, d!(4046856422.40), "acre", "acres"), 137 | 138 | CubicMillimeter: (Volume, d!(1), "cubic millimeter", "cubic millimeters"), 139 | CubicCentimeter: (Volume, d!(1000), "cubic centimeter", "cubic centimeters"), 140 | CubicDecimeter: (Volume, d!(1000000), "cubic decimeter", "cubic decimeters"), 141 | CubicMeter: (Volume, d!(1000000000), "cubic meter", "cubic meters"), 142 | CubicKilometer: (Volume, d!(1000000000000000000), "cubic kilometer", "cubic kilometers"), 143 | CubicInch: (Volume, d!(16387.064), "cubic inch", "cubic inches"), 144 | CubicFoot: (Volume, d!(28316846.592), "cubic foot", "cubic feet"), 145 | CubicYard: (Volume, d!(764554857.984), "cubic yard", "cubic yards"), 146 | CubicMile: (Volume, d!(4168181825440579584), "cubic mile", "cubic miles"), 147 | // 3-dimensional only 148 | Milliliter: (Volume, d!(1000), "milliliter", "milliliters"), 149 | Centiliter: (Volume, d!(10000), "centiliter", "centiliters"), 150 | Deciliter: (Volume, d!(100000), "deciliter", "deciliters"), 151 | Liter: (Volume, d!(1000000), "liter", "liters"), 152 | Teaspoon: (Volume, d!(4928.92159375), "teaspoon", "teaspoons"), 153 | Tablespoon: (Volume, d!(14786.76478125), "tablespoon", "tablespoons"), 154 | FluidOunce: (Volume, d!(29573.5295625), "fluid ounce", "fluid ounces"), 155 | Cup: (Volume, d!(236588.2365), "cup", "cups"), 156 | Pint: (Volume, d!(473176.473), "pint", "pints"), 157 | Quart: (Volume, d!(946352.946), "quart", "quarts"), 158 | Gallon: (Volume, d!(3785411.784), "gallon", "gallons"), 159 | OilBarrel: (Volume, d!(158987294.928), "oil barrel", "oil barrels"), 160 | 161 | Milligram: (Mass, d!(0.001), "milligram", "milligrams"), 162 | Gram: (Mass, d!(1), "gram", "grams"), 163 | Hectogram: (Mass, d!(100), "hectogram", "hectograms"), 164 | Kilogram: (Mass, d!(1000), "kilogram", "kilograms"), 165 | MetricTon: (Mass, d!(1000000), "metric ton", "metric tons"), 166 | Ounce: (Mass, d!(28.349523125), "ounce", "ounces"), 167 | Pound: (Mass, d!(453.59237), "pound", "pounds"), 168 | Stone: (Mass, d!(6350.29318), "stone", "stones"), 169 | ShortTon: (Mass, d!(907184.74), "short ton", "short tons"), 170 | LongTon: (Mass, d!(1016046.9088), "long ton", "long tons"), 171 | 172 | Bit: (DigitalStorage, d!(1), "bit", "bits"), 173 | Kilobit: (DigitalStorage, d!(1000), "kilobit", "kilobits"), 174 | Megabit: (DigitalStorage, d!(1000000), "megabit", "megabits"), 175 | Gigabit: (DigitalStorage, d!(1000000000), "gigabit", "gigabits"), 176 | Terabit: (DigitalStorage, d!(1000000000000), "terabit", "terabits"), 177 | Petabit: (DigitalStorage, d!(1000000000000000), "petabit", "petabits"), 178 | Exabit: (DigitalStorage, d!(1000000000000000000), "exabit", "exabits"), 179 | Zettabit: (DigitalStorage, d!(1000000000000000000000), "zettabit", "zettabits"), 180 | Yottabit: (DigitalStorage, d!(1000000000000000000000000), "yottabit", "yottabits"), 181 | Kibibit: (DigitalStorage, d!(1024), "kibibit", "kibibits"), 182 | Mebibit: (DigitalStorage, d!(1048576), "mebibit", "mebibits"), 183 | Gibibit: (DigitalStorage, d!(1073741824), "gibibit", "gibibits"), 184 | Tebibit: (DigitalStorage, d!(1099511627776), "tebibit", "tebibits"), 185 | Pebibit: (DigitalStorage, d!(1125899906842624), "pebibit", "pebibits"), 186 | Exbibit: (DigitalStorage, d!(1152921504606846976), "exbibit", "exbibits"), 187 | Zebibit: (DigitalStorage, d!(1180591620717411303424), "zebibit", "zebibits"), 188 | Yobibit: (DigitalStorage, d!(1208925819614629174706176), "yobibit", "yobibits"), 189 | Byte: (DigitalStorage, d!(8), "byte", "bytes"), 190 | Kilobyte: (DigitalStorage, d!(8000), "kilobyte", "kilobytes"), 191 | Megabyte: (DigitalStorage, d!(8000000), "megabyte", "megabytes"), 192 | Gigabyte: (DigitalStorage, d!(8000000000), "gigabyte", "gigabytes"), 193 | Terabyte: (DigitalStorage, d!(8000000000000), "terabyte", "terabytes"), 194 | Petabyte: (DigitalStorage, d!(8000000000000000), "petabyte", "petabytes"), 195 | Exabyte: (DigitalStorage, d!(8000000000000000000), "exabyte", "exabytes"), 196 | Zettabyte: (DigitalStorage, d!(8000000000000000000000), "zettabyte", "zettabytes"), 197 | Yottabyte: (DigitalStorage, d!(8000000000000000000000000), "yottabyte", "yottabytes"), 198 | Kibibyte: (DigitalStorage, d!(8192), "kibibyte", "kibibytes"), 199 | Mebibyte: (DigitalStorage, d!(8388608), "mebibyte", "mebibytes"), 200 | Gibibyte: (DigitalStorage, d!(8589934592), "gibibyte", "gibibytes"), 201 | Tebibyte: (DigitalStorage, d!(8796093022208), "tebibyte", "tebibytes"), 202 | Pebibyte: (DigitalStorage, d!(9007199254740992), "pebibyte", "pebibytes"), 203 | Exbibyte: (DigitalStorage, d!(9223372036854775808), "exbibyte", "exbibytes"), 204 | Zebibyte: (DigitalStorage, d!(9444732965739290427392), "zebibyte", "zebibytes"), 205 | Yobibyte: (DigitalStorage, d!(9671406556917033397649408), "yobibyte", "yobibytes"), 206 | 207 | BitsPerSecond: (DataTransferRate, d!(1), "bit per second", "bits per second"), 208 | KilobitsPerSecond: (DataTransferRate, d!(1000), "kilobit per second", "kilobits per second"), 209 | MegabitsPerSecond: (DataTransferRate, d!(1000000), "megabit per second", "megabits per second"), 210 | GigabitsPerSecond: (DataTransferRate, d!(1000000000), "gigabit per second", "gigabits per second"), 211 | TerabitsPerSecond: (DataTransferRate, d!(1000000000000), "terabit per second", "terabits per second"), 212 | PetabitsPerSecond: (DataTransferRate, d!(1000000000000000), "petabit per second", "petabits per second"), 213 | ExabitsPerSecond: (DataTransferRate, d!(1000000000000000000), "exabit per second", "exabits per second"), 214 | ZettabitsPerSecond: (DataTransferRate, d!(1000000000000000000000), "zettabit per second", "zettabits per second"), 215 | YottabitsPerSecond: (DataTransferRate, d!(1000000000000000000000000), "yottabit per second", "yottabits per second"), 216 | KibibitsPerSecond: (DataTransferRate, d!(1024), "kibibit per second", "kibibits per second"), 217 | MebibitsPerSecond: (DataTransferRate, d!(1048576), "mebibit per second", "mebibits per second"), 218 | GibibitsPerSecond: (DataTransferRate, d!(1073741824), "gibibit per second", "gibibits per second"), 219 | TebibitsPerSecond: (DataTransferRate, d!(1099511627776), "tebibit per second", "tebibits per second"), 220 | PebibitsPerSecond: (DataTransferRate, d!(1125899906842624), "pebibit per second", "pebibits per second"), 221 | ExbibitsPerSecond: (DataTransferRate, d!(1152921504606846976), "exbibit per second", "exbibits per second"), 222 | ZebibitsPerSecond: (DataTransferRate, d!(1180591620717411303424), "zebibit per second", "zebibits per second"), 223 | YobibitsPerSecond: (DataTransferRate, d!(1208925819614629174706176), "yobibit per second", "yobibits per second"), 224 | BytesPerSecond: (DataTransferRate, d!(8), "byte per second", "bytes per second"), 225 | KilobytesPerSecond: (DataTransferRate, d!(8000), "kilobyte per second", "kilobytes per second"), 226 | MegabytesPerSecond: (DataTransferRate, d!(8000000), "megabyte per second", "megabytes per second"), 227 | GigabytesPerSecond: (DataTransferRate, d!(8000000000), "gigabyte per second", "gigabytes per second"), 228 | TerabytesPerSecond: (DataTransferRate, d!(8000000000000), "terabyte per second", "terabytes per second"), 229 | PetabytesPerSecond: (DataTransferRate, d!(8000000000000000), "petabyte per second", "petabytes per second"), 230 | ExabytesPerSecond: (DataTransferRate, d!(8000000000000000000), "exabyte per second", "exabytes per second"), 231 | ZettabytesPerSecond: (DataTransferRate, d!(8000000000000000000000), "zettabyte per second", "zettabytes per second"), 232 | YottabytesPerSecond: (DataTransferRate, d!(8000000000000000000000000), "yottabyte per second", "yottabytes per second"), 233 | KibibytesPerSecond: (DataTransferRate, d!(8192), "kibibyte per second", "kibibytes per second"), 234 | MebibytesPerSecond: (DataTransferRate, d!(8388608), "mebibyte per second", "mebibytes per second"), 235 | GibibytesPerSecond: (DataTransferRate, d!(8589934592), "gibibyte per second", "gibibytes per second"), 236 | TebibytesPerSecond: (DataTransferRate, d!(8796093022208), "tebibyte per second", "tebibytes per second"), 237 | PebibytesPerSecond: (DataTransferRate, d!(9007199254740992), "pebibyte per second", "pebibytes per second"), 238 | ExbibytesPerSecond: (DataTransferRate, d!(9223372036854775808), "exbibyte per second", "exbibytes per second"), 239 | ZebibytesPerSecond: (DataTransferRate, d!(9444732965739290427392), "zebibyte per second", "zebibytes per second"), 240 | YobibytesPerSecond: (DataTransferRate, d!(9671406556917033397649408), "yobibyte per second", "yobibytes per second"), 241 | 242 | Millijoule: (Energy, d!(0.001), "millijoule", "millijoules"), 243 | Joule: (Energy, d!(1), "joule", "joules"), 244 | NewtonMeter: (Energy, d!(1), "newton meter", "newton meters"), 245 | Kilojoule: (Energy, d!(1000), "kilojoule", "kilojoules"), 246 | Megajoule: (Energy, d!(1000000), "megajoule", "megajoules"), 247 | Gigajoule: (Energy, d!(1000000000), "gigajoule", "gigajoules"), 248 | Terajoule: (Energy, d!(1000000000000), "terajoule", "terajoules"), 249 | Calorie: (Energy, d!(4.1868), "calorie", "calories"), 250 | KiloCalorie: (Energy, d!(4186.8), "kilocalorie", "kilocalories"), 251 | BritishThermalUnit: (Energy, d!(1055.05585262), "British thermal unit", "British thermal units"), 252 | WattHour: (Energy, d!(3600), "watt-hour", "watt-hours"), 253 | KilowattHour: (Energy, d!(3600000), "kilowatt-hour", "kilowatt-hours"), 254 | MegawattHour: (Energy, d!(3600000000), "megawatt-hour", "megawatt-hours"), 255 | GigawattHour: (Energy, d!(3600000000000), "gigawatt-hour", "gigawatt-hours"), 256 | TerawattHour: (Energy, d!(3600000000000000), "terawatt-hour", "terawatt-hours"), 257 | PetawattHour: (Energy, d!(3600000000000000000), "petawatt-hour", "petawatt-hours"), 258 | 259 | Milliwatt: (Power, d!(0.001), "milliwatt", "milliwatts"), 260 | Watt: (Power, d!(1), "watt", "watts"), 261 | Kilowatt: (Power, d!(1000), "kilowatt", "kilowatts"), 262 | Megawatt: (Power, d!(1000000), "megawatt", "megawatts"), 263 | Gigawatt: (Power, d!(1000000000), "gigawatt", "gigawatts"), 264 | Terawatt: (Power, d!(1000000000000), "terawatt", "terawatts"), 265 | Petawatt: (Power, d!(1000000000000000), "petawatt", "petawatts"), 266 | // probably inexact: 267 | BritishThermalUnitsPerMinute: (Power, d!(0.0568690272188), "british thermal unit per minute", "british thermal units per minute"), 268 | // probably inexact: 269 | BritishThermalUnitsPerHour: (Power, d!(3.412141633128), "british thermal unit per hour", "british thermal units per hour"), 270 | // exact according to wikipedia: 271 | Horsepower: (Power, d!(745.69987158227022), "horsepower", "horsepower"), 272 | MetricHorsepower: (Power, d!(735.49875), "metric horsepower", "metric horsepower"), 273 | 274 | Milliampere: (ElectricCurrent, d!(0.001), "milliampere", "milliamperes"), 275 | Ampere: (ElectricCurrent, d!(1), "ampere", "amperes"), 276 | Kiloampere: (ElectricCurrent, d!(1000), "kiloampere", "kiloamperes"), 277 | Abampere: (ElectricCurrent, d!(10), "abampere", "abamperes"), 278 | 279 | Milliohm: (Resistance, d!(0.001), "milliohm", "milliohms"), 280 | Ohm: (Resistance, d!(1), "ohm", "ohms"), 281 | Kiloohm: (Resistance, d!(1000), "kiloohm", "kiloohms"), 282 | 283 | Millivolt: (Voltage, d!(0.001), "millivolt", "millivolts"), 284 | Volt: (Voltage, d!(1), "volt", "volts"), 285 | Kilovolt: (Voltage, d!(1000), "kilovolt", "kilovolts"), 286 | 287 | Pascal: (Pressure, d!(1), "pascal", "pascals"), 288 | Kilopascal: (Pressure, d!(1000), "kilopascal", "kilopascals"), 289 | Atmosphere: (Pressure, d!(101325), "atmosphere", "atmospheres"), 290 | Millibar: (Pressure, d!(100), "millibar", "millibars"), 291 | Bar: (Pressure, d!(100000), "bar", "bars"), 292 | InchOfMercury: (Pressure, d!(3386.389), "inch of mercury", "inches of mercury"), 293 | // inexact: 294 | PoundsPerSquareInch: (Pressure, d!(6894.757293168361), "pound per square inch", "pounds per square inch"), 295 | Torr: (Pressure, d!(162.12), "torr", "torr"), 296 | 297 | Hertz: (Frequency, d!(1), "hertz", "hertz"), 298 | Kilohertz: (Frequency, d!(1000), "kilohertz", "kilohertz"), 299 | Megahertz: (Frequency, d!(1000000), "megahertz", "megahertz"), 300 | Gigahertz: (Frequency, d!(1000000000), "gigahertz", "gigahertz"), 301 | Terahertz: (Frequency, d!(1000000000000), "terahertz", "terahertz"), 302 | Petahertz: (Frequency, d!(1000000000000000), "petahertz", "petahertz"), 303 | RevolutionsPerMinute: (Frequency, d!(60), "revolution per minute", "revolutions per minute"), 304 | 305 | KilometersPerHour: (Speed, d!(1), "kilometer per hour", "kilometers per hour"), 306 | MetersPerSecond: (Speed, d!(3.6), "meter per second", "meters per second"), 307 | MilesPerHour: (Speed, d!(1.609344), "mile per hour", "miles per hour"), 308 | FeetPerSecond: (Speed, d!(1.09728), "foot per second", "feet per second"), 309 | Knot: (Speed, d!(1.852), "knot", "knots"), 310 | 311 | Kelvin: (Temperature, d!(0), "kelvin", "kelvin"), 312 | Celsius: (Temperature, d!(0), "celsius", "celsius"), 313 | Fahrenheit: (Temperature, d!(0), "fahrenheit", "fahrenheit"), 314 | ); 315 | 316 | /// Returns the conversion factor between two units. 317 | /// 318 | /// The conversion factor is what you need to multiply `unit` with to get 319 | /// `to_unit`. For example, the conversion factor from 1 minute to 1 second 320 | /// is 60. 321 | /// 322 | /// This is not sufficient for [`Temperature`] units. 323 | pub fn get_conversion_factor(unit: Unit, to_unit: Unit) -> D128 { 324 | unit.weight() / to_unit.weight() 325 | } 326 | 327 | /// Convert a [`Number`] to a specified [`Unit`]. 328 | pub fn convert(number: Number, to_unit: Unit) -> Result { 329 | if number.unit.category() != to_unit.category() { 330 | return Err(format!("Cannot convert from {:?} to {:?}", number.unit, to_unit)); 331 | } 332 | let value = number.value; 333 | let ok = |new_value| { 334 | Ok(Number::new(new_value, to_unit)) 335 | }; 336 | if number.unit.category() == UnitType::Temperature { 337 | match (number.unit, to_unit) { 338 | (Kelvin, Kelvin) => ok(value), 339 | (Kelvin, Celsius) => ok(value-d!(273.15)), 340 | (Kelvin, Fahrenheit) => ok(value*d!(1.8)-d!(459.67)), 341 | (Celsius, Celsius) => ok(value), 342 | (Celsius, Kelvin) => ok(value+d!(273.15)), 343 | (Celsius, Fahrenheit) => ok(value*d!(1.8)+d!(32)), 344 | (Fahrenheit, Fahrenheit) => ok(value), 345 | (Fahrenheit, Kelvin) => ok((value+d!(459.67))*d!(5)/d!(9)), 346 | (Fahrenheit, Celsius) => ok((value-d!(32))/d!(1.8)), 347 | _ => Err(format!("Error converting temperature {:?} to {:?}", number.unit, to_unit)), 348 | } 349 | } else { 350 | let conversion_factor = get_conversion_factor(number.unit, to_unit); 351 | ok(number.value * conversion_factor) 352 | } 353 | } 354 | 355 | /// If one of two provided [`Number`]s has a larger [`Unit`] than the other, convert 356 | /// the large one to the unit of the small one. 357 | pub fn convert_to_lowest(left: Number, right: Number) -> Result<(Number, Number), String> { 358 | if left.unit.weight() == right.unit.weight() { 359 | Ok((left, right)) 360 | } else if left.unit.weight() > right.unit.weight() { 361 | let left_converted = convert(left, right.unit)?; 362 | Ok((left_converted, right)) 363 | } else { 364 | let right_converted = convert(right, left.unit)?; 365 | Ok((left, right_converted)) 366 | } 367 | } 368 | 369 | /// Return the sum of two [`Number`]s 370 | pub fn add(left: Number, right: Number) -> Result { 371 | if left.unit == right.unit { 372 | Ok(Number::new(left.value + right.value, left.unit)) 373 | } else if left.unit.category() == right.unit.category() && left.unit.category() != Temperature { 374 | let (left, right) = convert_to_lowest(left, right)?; 375 | Ok(Number::new(left.value + right.value, left.unit)) 376 | } else { 377 | Err(format!("Cannot add {:?} and {:?}", left.unit, right.unit)) 378 | } 379 | } 380 | 381 | /// Subtract a [`Number`] from another [`Number`] 382 | pub fn subtract(left: Number, right: Number) -> Result { 383 | if left.unit == right.unit { 384 | Ok(Number::new(left.value - right.value, left.unit)) 385 | } else if left.unit.category() == right.unit.category() && left.unit.category() != Temperature { 386 | let (left, right) = convert_to_lowest(left, right)?; 387 | Ok(Number::new(left.value - right.value, left.unit)) 388 | } else { 389 | Err(format!("Cannot subtract {:?} by {:?}", left.unit, right.unit)) 390 | } 391 | } 392 | 393 | /// Convert a [`Number`] to an ideal unit. 394 | /// 395 | /// If you have 1,000,000 millimeters, this will return 1 kilometer. 396 | /// 397 | /// This only affects units of `Length`, `Time`, `Area`, `Volume`, 398 | /// `Energy`, `Power`, `ElectricCurrent`, `Resistance`, and `Voltage`. 399 | /// Other units are passed through. 400 | pub fn to_ideal_unit(number: Number) -> Number { 401 | let value = number.value * number.unit.weight(); 402 | if number.unit.category() == Length { 403 | if value >= d!(1000000000000000000) { // ≈ 0.1 light years 404 | return Number::new(value/LightYear.weight(), LightYear) 405 | } else if value >= d!(1000000) { // 1 km 406 | return Number::new(value/Kilometer.weight(), Kilometer) 407 | } else if value >= d!(1000) { // 1 m 408 | return Number::new(value/Meter.weight(), Meter) 409 | } else if value >= d!(10) { // 1 cm 410 | return Number::new(value/Centimeter.weight(), Centimeter) 411 | } else { 412 | return Number::new(value, Millimeter) 413 | } 414 | } else if number.unit.category() == Time { 415 | if value >= d!(31556952000000000) { 416 | return Number::new(value/Year.weight(), Year); 417 | } else if value >= d!(86400000000000) { 418 | return Number::new(value/Day.weight(), Day); 419 | } else if value >= d!(3600000000000) { 420 | return Number::new(value/Hour.weight(), Hour); 421 | } else if value >= d!(60000000000) { 422 | return Number::new(value/Minute.weight(), Minute); 423 | } else if value >= d!(1000000000) { 424 | return Number::new(value/Second.weight(), Second); 425 | } else if value >= d!(1000000) { 426 | return Number::new(value/Millisecond.weight(), Millisecond); 427 | } else if value >= d!(1000) { 428 | return Number::new(value/Microsecond.weight(), Microsecond); 429 | } else { 430 | return Number::new(value, Nanosecond); 431 | } 432 | } else if number.unit.category() == Area { 433 | if value >= d!(1000000000000) { // 1 km2 434 | return Number::new(value/SquareKilometer.weight(), SquareKilometer) 435 | } else if value >= d!(10000000000) { // 1 hectare 436 | return Number::new(value/Hectare.weight(), Hectare) 437 | } else if value >= d!(1000000) { // 1 m2 438 | return Number::new(value/SquareMeter.weight(), SquareMeter) 439 | } else if value >= d!(100) { // 1 cm2 440 | return Number::new(value/SquareCentimeter.weight(), SquareCentimeter) 441 | } else { 442 | return Number::new(value, SquareMillimeter) 443 | } 444 | } else if number.unit.category() == Volume { 445 | if value >= d!(1000000000000000000) { // 1 km3 446 | return Number::new(value/CubicKilometer.weight(), CubicKilometer) 447 | } else if value >= d!(1000000000) { // 1 m3 448 | return Number::new(value/CubicMeter.weight(), CubicMeter) 449 | } else if value >= d!(1000000) { // 1 l 450 | return Number::new(value/Liter.weight(), Liter) 451 | } else if value >= d!(1000) { // 1 ml 452 | return Number::new(value/Milliliter.weight(), Milliliter) 453 | } else { 454 | return Number::new(value, CubicMillimeter) 455 | } 456 | } else if number.unit.category() == Energy { 457 | if value >= d!(3600000000000000000) { // 1 petawatthour 458 | return Number::new(value/PetawattHour.weight(), PetawattHour) 459 | } else if value >= d!(3600000000000000) { // 1 terawatthour 460 | return Number::new(value/TerawattHour.weight(), TerawattHour) 461 | } else if value >= d!(3600000000000) { // 1 gigawatthour 462 | return Number::new(value/GigawattHour.weight(), GigawattHour) 463 | } else if value >= d!(3600000000) { // 1 megawatthour 464 | return Number::new(value/MegawattHour.weight(), MegawattHour) 465 | } else if value >= d!(3600000) { // 1 kilowatthour 466 | return Number::new(value/KilowattHour.weight(), KilowattHour) 467 | } else if value >= d!(3600) { // 1 watthour 468 | return Number::new(value/WattHour.weight(), WattHour) 469 | } else if value >= d!(1) { // 1 joule 470 | return Number::new(value, Joule) 471 | } else { 472 | return Number::new(value/Millijoule.weight(), Millijoule) 473 | } 474 | } else if number.unit.category() == Power { 475 | if value >= d!(1000000000000000) { // 1 petawatt 476 | return Number::new(value/Petawatt.weight(), Petawatt) 477 | } else if value >= d!(1000000000000) { // 1 terawatt 478 | return Number::new(value/Terawatt.weight(), Terawatt) 479 | } else if value >= d!(1000000000) { // 1 gigawatt 480 | return Number::new(value/Gigawatt.weight(), Gigawatt) 481 | } else if value >= d!(1000000) { // megawatt 482 | return Number::new(value/Megawatt.weight(), Megawatt) 483 | } else if value >= d!(1000) { // 1 kilowatt 484 | return Number::new(value/Kilowatt.weight(), Kilowatt) 485 | } else if value >= d!(1) { // 1 watt 486 | return Number::new(value, Watt) 487 | } else { 488 | return Number::new(value/Milliwatt.weight(), Milliwatt) 489 | } 490 | } else if number.unit.category() == ElectricCurrent { 491 | if value >= d!(1000) { // 1 kiloampere 492 | return Number::new(value/Kiloampere.weight(), Kiloampere) 493 | } else if value >= d!(1) { // 1 ampere 494 | return Number::new(value, Ampere) 495 | } else { 496 | return Number::new(value/Milliampere.weight(), Milliampere) 497 | } 498 | } else if number.unit.category() == Resistance { 499 | if value >= d!(1000) { // 1 kiloohm 500 | return Number::new(value/Kiloohm.weight(), Kiloohm) 501 | } else if value >= d!(1) { // 1 ohm 502 | return Number::new(value, Ohm) 503 | } else { 504 | return Number::new(value/Milliohm.weight(), Milliohm) 505 | } 506 | } else if number.unit.category() == Voltage { 507 | if value >= d!(1000) { // 1 kilovolt 508 | return Number::new(value/Kilovolt.weight(), Kilovolt) 509 | } else if value >= d!(1) { // 1 volt 510 | return Number::new(value, Volt) 511 | } else { 512 | return Number::new(value/Millivolt.weight(), Millivolt) 513 | } 514 | } 515 | number 516 | } 517 | 518 | /// Convert a [`Number`] to an ideal [`Joule`] unit, if the number is a unit of [`Energy`]. 519 | pub fn to_ideal_joule_unit(number: Number) -> Number { 520 | let value = number.value * number.unit.weight(); 521 | if number.unit.category() == Energy { 522 | if value >= d!(1000000000000) { // 1 terajoule 523 | return Number::new(value/Terajoule.weight(), Terajoule) 524 | } else if value >= d!(1000000000) { // 1 gigajoule 525 | return Number::new(value/Gigajoule.weight(), Gigajoule) 526 | } else if value >= d!(1000000) { // 1 megajoule 527 | return Number::new(value/Megajoule.weight(), Megajoule) 528 | } else if value >= d!(1000) { // 1 kilojoule 529 | return Number::new(value/Kilojoule.weight(), Kilojoule) 530 | } else if value >= d!(1) { // 1 joule 531 | return Number::new(value/Joule.weight(), Joule) 532 | } else { 533 | return Number::new(value/Millijoule.weight(), Millijoule) 534 | } 535 | } 536 | number 537 | } 538 | 539 | /// Multiply two [`Number`]s 540 | /// 541 | /// - Temperatures don't work 542 | /// - If you multiply [`NoType`] with any other unit, the result gets that other unit 543 | /// - If you multiply [`Length`] with [`Length`], the result has a unit of [`Area`], etc. 544 | /// - If you multiply [`Speed`] with [`Time`], the result has a unit of [`Length`] 545 | /// - If you multiply [`Voltage`] with [`ElectricCurrent`], the result has a unit of [`Power`] 546 | /// - If you multiply [`ElectricCurrent`] with [`Resistance`], the result has a unit of [`Voltage`] 547 | /// - If you multiply [`Power`] with [`Time`], the result has a unit of [`Energy`] 548 | pub fn multiply(left: Number, right: Number) -> Result { 549 | actual_multiply(left, right, false) 550 | } 551 | 552 | fn actual_multiply(left: Number, right: Number, swapped: bool) -> Result { 553 | let lcat = left.unit.category(); 554 | let rcat = right.unit.category(); 555 | if left.unit == NoUnit && right.unit == NoUnit { 556 | // 3 * 2 557 | Ok(Number::new(left.value * right.value, left.unit)) 558 | } else if left.unit.category() == Temperature || right.unit.category() == Temperature { 559 | // if temperature 560 | Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) 561 | } else if left.unit == NoUnit && right.unit != NoUnit { 562 | // 3 * 2 anyunit 563 | Ok(Number::new(left.value * right.value, right.unit)) 564 | } else if lcat == Length && rcat == Length { 565 | // length * length 566 | let result = (left.value * left.unit.weight()) * (right.value * right.unit.weight()); 567 | Ok(to_ideal_unit(Number::new(result, SquareMillimeter))) 568 | } else if lcat == Length && rcat == Area { 569 | // length * area 570 | let result = (left.value * left.unit.weight()) * (right.value * right.unit.weight()); 571 | Ok(to_ideal_unit(Number::new(result, CubicMillimeter))) 572 | } else if lcat == Speed && rcat == Time { 573 | // 1 km/h * 1h 574 | let kph_value = left.value * left.unit.weight(); 575 | let hours = convert(right, Hour)?; 576 | let result = kph_value * hours.value; 577 | let final_unit = match left.unit { 578 | KilometersPerHour => Kilometer, 579 | MetersPerSecond => Meter, 580 | MilesPerHour => Mile, 581 | FeetPerSecond => Foot, 582 | Knot => NauticalMile, 583 | _ => Meter, 584 | }; 585 | let kilometers = Number::new(result, Kilometer); 586 | Ok(convert(kilometers, final_unit)?) 587 | } else if lcat == DataTransferRate && rcat == Time { 588 | // 8 megabytes per second * 1 minute 589 | let data_rate_value = left.value * left.unit.weight(); 590 | let seconds = convert(right, Second)?; 591 | let result = data_rate_value * seconds.value; 592 | let final_unit = match left.unit { 593 | BitsPerSecond => Bit, 594 | KilobitsPerSecond => Kilobit, 595 | MegabitsPerSecond => Megabit, 596 | GigabitsPerSecond => Gigabit, 597 | TerabitsPerSecond => Terabit, 598 | PetabitsPerSecond => Petabit, 599 | ExabitsPerSecond => Exabit, 600 | ZettabitsPerSecond => Zettabit, 601 | YottabitsPerSecond => Yottabit, 602 | KibibitsPerSecond => Kibibit, 603 | MebibitsPerSecond => Mebibit, 604 | GibibitsPerSecond => Gibibit, 605 | TebibitsPerSecond => Tebibit, 606 | PebibitsPerSecond => Pebibit, 607 | ExbibitsPerSecond => Exbibit, 608 | ZebibitsPerSecond => Zebibit, 609 | YobibitsPerSecond => Yobibit, 610 | BytesPerSecond => Byte, 611 | KilobytesPerSecond => Kilobyte, 612 | MegabytesPerSecond => Megabyte, 613 | GigabytesPerSecond => Gigabyte, 614 | TerabytesPerSecond => Terabyte, 615 | PetabytesPerSecond => Petabyte, 616 | ExabytesPerSecond => Exabyte, 617 | ZettabytesPerSecond => Zettabyte, 618 | YottabytesPerSecond => Yottabyte, 619 | KibibytesPerSecond => Kibibyte, 620 | MebibytesPerSecond => Mebibyte, 621 | GibibytesPerSecond => Gibibyte, 622 | TebibytesPerSecond => Tebibyte, 623 | PebibytesPerSecond => Pebibyte, 624 | ExbibytesPerSecond => Exbibyte, 625 | ZebibytesPerSecond => Zebibyte, 626 | YobibytesPerSecond => Yobibyte, 627 | _ => Bit, 628 | }; 629 | let data_storage = Number::new(result, Bit); 630 | Ok(convert(data_storage, final_unit)?) 631 | } else if lcat == Voltage && rcat == ElectricCurrent { 632 | // 1 volt * 1 ampere = 1 watt 633 | let result = (left.value * left.unit.weight()) * (right.value * right.unit.weight()); 634 | Ok(to_ideal_unit(Number::new(result, Watt))) 635 | } else if lcat == ElectricCurrent && rcat == Resistance { 636 | // 1 amp * 1 ohm = 1 volt 637 | let result = (left.value * left.unit.weight()) * (right.value * right.unit.weight()); 638 | Ok(to_ideal_unit(Number::new(result, Watt))) 639 | } else if lcat == Power && rcat == Time { 640 | // 1 watt * 1 second = 1 joule 641 | let result = (left.value * left.unit.weight()) * (right.value * right.unit.weight() / Unit::Second.weight()); 642 | match right.unit { 643 | Second => Ok(to_ideal_joule_unit(Number::new(result, Joule))), 644 | _ => Ok(to_ideal_unit(Number::new(result, Joule))), 645 | } 646 | } else if swapped { 647 | Err(format!("Cannot multiply {:?} and {:?}", right.unit, left.unit)) 648 | } else { 649 | actual_multiply(right, left, true) 650 | } 651 | } 652 | 653 | /// Divide a [`Number`] by another [`Number`] 654 | /// 655 | /// - Temperatures don't work 656 | /// - If you divide a unit by that same unit, the result has a unit of [`NoType`] 657 | /// - If you divide [`Volume`] by [`Length`], the result has a unit of [`Area`], etc. 658 | /// - If you divide [`Length`] by [`Time`], the result has a unit of [`Speed`] 659 | /// - If you divide [`Length`] by [`Speed`], the result has a unit of [`Time`] 660 | /// - If you divide [`Power`] by [`ElectricCurrent`], the result has a unit of [`Volt`] 661 | /// - If you divide [`Voltage`] by [`ElectricCurrent`], the result has a unit of [`Ohm`] 662 | /// - If you divide [`Voltage`] by [`Resistance`], the result has a unit of [`Ampere`] 663 | /// - If you divide [`Power`] by [`Voltage`], the result has a unit of [`Ampere`] 664 | /// - If you divide [`Energy`] by [`Time`], the result has a unit of [`Power`] 665 | pub fn divide(left: Number, right: Number) -> Result { 666 | let lcat = left.unit.category(); 667 | let rcat = right.unit.category(); 668 | if left.unit == NoUnit && right.unit == NoUnit { 669 | // 3 / 2 670 | Ok(Number::new(left.value / right.value, left.unit)) 671 | } else if lcat == Temperature || rcat == Temperature { 672 | // if temperature 673 | Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) 674 | } else if left.unit != NoUnit && right.unit == NoUnit { 675 | // 1 km / 2 676 | Ok(Number::new(left.value / right.value, left.unit)) 677 | } else if lcat == rcat { 678 | // 4 km / 2 km 679 | let (left, right) = convert_to_lowest(left, right)?; 680 | Ok(Number::new(left.value / right.value, NoUnit)) 681 | } else if (lcat == Area && rcat == Length) || (lcat == Volume && rcat == Area) { 682 | // 1 km2 / 1 km, 1 km3 / 1 km2 683 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight()); 684 | Ok(to_ideal_unit(Number::new(result, Millimeter))) 685 | } else if lcat == Volume && rcat == Length { 686 | // 1 km3 / 1 km 687 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight()); 688 | Ok(to_ideal_unit(Number::new(result, SquareMillimeter))) 689 | } else if lcat == Length && rcat == Time { 690 | // 1 km / 2s 691 | let final_unit = match (left.unit, right.unit) { 692 | (Kilometer, Hour) => KilometersPerHour, 693 | (Meter, Second) => MetersPerSecond, 694 | (Mile, Hour) => MilesPerHour, 695 | (Foot, Second) => FeetPerSecond, 696 | (NauticalMile, Hour) => Knot, 697 | _ => KilometersPerHour, 698 | }; 699 | let kilometers = convert(left, Kilometer)?; 700 | let hours = convert(right, Hour)?; 701 | let kph = Number::new(kilometers.value / hours.value, KilometersPerHour); 702 | Ok(convert(kph, final_unit)?) 703 | } else if lcat == Length && rcat == Speed { 704 | // 12 km / 100 kph 705 | let kilometers = convert(left, Kilometer)?; 706 | let kilometers_per_hour = convert(right, KilometersPerHour)?; 707 | let hour = Number::new(kilometers.value / kilometers_per_hour.value, Hour); 708 | Ok(to_ideal_unit(hour)) 709 | } else if lcat == DigitalStorage && rcat == DataTransferRate { 710 | // 1 kilobit / 1 bit per second 711 | let bits = convert(left, Bit)?; 712 | let bits_per_second = convert(right, BitsPerSecond)?; 713 | let seconds = Number::new(bits.value / bits_per_second.value, Second); 714 | Ok(to_ideal_unit(seconds)) 715 | } else if lcat == Power && rcat == ElectricCurrent { 716 | // 1 watt / 1 ampere = 1 volt 717 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight()); 718 | Ok(to_ideal_unit(Number::new(result, Volt))) 719 | } else if lcat == Voltage && rcat == ElectricCurrent { 720 | // 1 volt / 1 ampere = 1 ohm 721 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight()); 722 | Ok(to_ideal_unit(Number::new(result, Ohm))) 723 | } else if lcat == Voltage && rcat == Resistance { 724 | // 1 volt / 1 ohm = 1 amp 725 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight()); 726 | Ok(to_ideal_unit(Number::new(result, Ampere))) 727 | } else if lcat == Power && rcat == Voltage { 728 | // 1 watt / 1 volt = 1 amp 729 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight()); 730 | Ok(to_ideal_unit(Number::new(result, Ampere))) 731 | } else if lcat == Energy && rcat == Time { 732 | // 1 joule / 1 second = 1 watt 733 | let result = (left.value * left.unit.weight()) / (right.value * right.unit.weight() / Unit::Second.weight()); 734 | Ok(to_ideal_unit(Number::new(result, Watt))) 735 | } else { 736 | Err(format!("Cannot divide {:?} by {:?}", left.unit, right.unit)) 737 | } 738 | } 739 | /// Modulo a [`Number`] by another [`Number`]. 740 | /// 741 | /// `left` and `right` need to have the same [`UnitType`], and the result will have that same [`UnitType`]. 742 | /// 743 | /// Temperatures don't work. 744 | pub fn modulo(left: Number, right: Number) -> Result { 745 | if left.unit.category() == Temperature || right.unit.category() == Temperature { 746 | // if temperature 747 | Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit)) 748 | } else if left.unit.category() == right.unit.category() { 749 | // 5 km % 3 m 750 | let (left, right) = convert_to_lowest(left, right)?; 751 | Ok(Number::new(left.value % right.value, left.unit)) 752 | } else { 753 | Err(format!("Cannot modulo {:?} by {:?}", left.unit, right.unit)) 754 | } 755 | } 756 | 757 | /// Returns a [`Number`] to the power of another [`Number`] 758 | /// 759 | /// - If you take [`Length`] to the power of [`NoType`], the result has a unit of [`Area`]. 760 | /// - If you take [`Length`] to the power of [`Length`], the result has a unit of [`Area`] 761 | /// - If you take [`Length`] to the power of [`Area`], the result has a unit of [`Volume`] 762 | /// - etc. 763 | pub fn pow(left: Number, right: Number) -> Result { 764 | // I tried converting `right` to use powi, but somehow that was slower 765 | let lcat = left.unit.category(); 766 | let rcat = left.unit.category(); 767 | if left.unit == NoUnit && right.unit == NoUnit { 768 | // 3 ^ 2 769 | Ok(Number::new(left.value.pow(right.value), left.unit)) 770 | } else if right.value == d!(1) && right.unit == NoUnit { 771 | Ok(left) 772 | } else if lcat == Length && right.unit == NoUnit && right.value == d!(2) { 773 | // x km ^ 2 774 | let result = (left.value * left.unit.weight()).pow(right.value); 775 | Ok(to_ideal_unit(Number::new(result, SquareMillimeter))) 776 | } else if lcat == Length && right.unit == NoUnit && right.value == d!(3) { 777 | // x km ^ 3 778 | let result = (left.value * left.unit.weight()).pow(right.value); 779 | Ok(to_ideal_unit(Number::new(result, CubicMillimeter))) 780 | } else if lcat == Length && rcat == Length && right.value == d!(1) { 781 | // x km ^ 1 km 782 | Ok(multiply(left, right)?) 783 | } else if lcat == Length && rcat == Length && right.value == d!(2) { 784 | // x km ^ 2 km 785 | let pow2 = multiply(left, Number::new(d!(1), right.unit))?; 786 | let pow3 = multiply(pow2, Number::new(d!(1), right.unit))?; 787 | Ok(pow3) 788 | } else if lcat == Length && rcat == Area && right.value == d!(1) { 789 | // x km ^ km2 790 | Ok(multiply(left, Number::new(d!(1), right.unit))?) 791 | } else { 792 | Err(format!("Cannot multiply {:?} and {:?}", left.unit, right.unit)) 793 | } 794 | } 795 | 796 | #[cfg(test)] 797 | mod tests { 798 | use fastnum::decimal::Context; 799 | 800 | use super::*; 801 | 802 | macro_rules! assert_float_eq { 803 | ( $actual:expr, $expected:literal ) => { 804 | assert!(($actual - $expected).abs() < f64::EPSILON); 805 | } 806 | } 807 | 808 | #[test] 809 | fn test_convert() { 810 | pub fn convert_test(value: f64, unit: Unit, to_unit: Unit) -> f64 { 811 | use std::str::FromStr; 812 | 813 | let value_string = &value.to_string(); 814 | let value_d128 = D128::from_str(value_string, Context::default()).unwrap(); 815 | let number = Number::new(value_d128, unit); 816 | 817 | let result = convert(number, to_unit); 818 | let string_result = &result.unwrap().value.to_string(); 819 | let float_result = f64::from_str(string_result).unwrap(); 820 | 821 | return float_result; 822 | } 823 | 824 | assert_float_eq!(convert_test(1000.0, Nanosecond, Microsecond), 1.0); 825 | assert_float_eq!(convert_test(1000.0, Nanosecond, Microsecond), 1.0); 826 | assert_float_eq!(convert_test(1000.0, Microsecond, Millisecond), 1.0); 827 | assert_float_eq!(convert_test(1000.0, Millisecond, Second), 1.0); 828 | assert_float_eq!(convert_test(60.0, Second, Minute), 1.0); 829 | assert_float_eq!(convert_test(60.0, Minute, Hour), 1.0); 830 | assert_float_eq!(convert_test(24.0, Hour, Day), 1.0); 831 | assert_float_eq!(convert_test(7.0, Day, Week), 1.0); 832 | assert_float_eq!(convert_test(30.436875, Day, Month), 1.0); 833 | assert_float_eq!(convert_test(3.0, Month, Quarter), 1.0); 834 | assert_float_eq!(convert_test(4.0, Quarter, Year), 1.0); 835 | assert_float_eq!(convert_test(10.0, Year, Decade), 1.0); 836 | assert_float_eq!(convert_test(10.0, Decade, Century), 1.0); 837 | assert_float_eq!(convert_test(10.0, Century, Millenium), 1.0); 838 | 839 | assert_float_eq!(convert_test(10.0, Millimeter, Centimeter), 1.0); 840 | assert_float_eq!(convert_test(10.0, Centimeter, Decimeter), 1.0); 841 | assert_float_eq!(convert_test(10.0, Decimeter, Meter), 1.0); 842 | assert_float_eq!(convert_test(1000.0, Meter, Kilometer), 1.0); 843 | assert_float_eq!(convert_test(2.54, Centimeter, Inch), 1.0); 844 | assert_float_eq!(convert_test(12.0, Inch, Foot), 1.0); 845 | assert_float_eq!(convert_test(3.0, Foot, Yard), 1.0); 846 | assert_float_eq!(convert_test(1760.0, Yard, Mile), 1.0); 847 | assert_float_eq!(convert_test(42195.0, Meter, Marathon), 1.0); 848 | assert_float_eq!(convert_test(1852.0, Meter, NauticalMile), 1.0); 849 | assert_float_eq!(convert_test(9460730472580800.0, Meter, LightYear), 1.0); 850 | assert_float_eq!(convert_test(299792458.0, Meter, LightSecond), 1.0); 851 | 852 | assert_float_eq!(convert_test(100.0, SquareMillimeter, SquareCentimeter), 1.0); 853 | assert_float_eq!(convert_test(100.0, SquareCentimeter, SquareDecimeter), 1.0); 854 | assert_float_eq!(convert_test(100.0, SquareDecimeter, SquareMeter), 1.0); 855 | assert_float_eq!(convert_test(1000000.0, SquareMeter, SquareKilometer), 1.0); 856 | assert_float_eq!(convert_test(645.16, SquareMillimeter, SquareInch), 1.0); 857 | assert_float_eq!(convert_test(144.0, SquareInch, SquareFoot), 1.0); 858 | assert_float_eq!(convert_test(9.0, SquareFoot, SquareYard), 1.0); 859 | assert_float_eq!(convert_test(3097600.0, SquareYard, SquareMile), 1.0); 860 | assert_float_eq!(convert_test(100.0, SquareMeter, Are), 1.0); 861 | assert_float_eq!(convert_test(10.0, Are, Decare), 1.0); 862 | assert_float_eq!(convert_test(10.0, Decare, Hectare), 1.0); 863 | assert_float_eq!(convert_test(640.0, Acre, SquareMile), 1.0); 864 | 865 | assert_float_eq!(convert_test(1000.0, CubicMillimeter, CubicCentimeter), 1.0); 866 | assert_float_eq!(convert_test(1000.0, CubicCentimeter, CubicDecimeter), 1.0); 867 | assert_float_eq!(convert_test(1000.0, CubicDecimeter, CubicMeter), 1.0); 868 | assert_float_eq!(convert_test(1000000000.0, CubicMeter, CubicKilometer), 1.0); 869 | assert_float_eq!(convert_test(1728.0, CubicInch, CubicFoot), 1.0); 870 | assert_float_eq!(convert_test(27.0, CubicFoot, CubicYard), 1.0); 871 | assert_float_eq!(convert_test(5451776000.0, CubicYard, CubicMile), 1.0); 872 | assert_float_eq!(convert_test(1.0, Milliliter, CubicCentimeter), 1.0); 873 | assert_float_eq!(convert_test(10.0, Milliliter, Centiliter), 1.0); 874 | assert_float_eq!(convert_test(10.0, Centiliter, Deciliter), 1.0); 875 | assert_float_eq!(convert_test(10.0, Deciliter, Liter), 1.0); 876 | assert_float_eq!(convert_test(4.92892159375, Milliliter, Teaspoon), 1.0); 877 | assert_float_eq!(convert_test(3.0, Teaspoon, Tablespoon), 1.0); 878 | assert_float_eq!(convert_test(2.0, Tablespoon, FluidOunce), 1.0); 879 | assert_float_eq!(convert_test(8.0, FluidOunce, Cup), 1.0); 880 | assert_float_eq!(convert_test(2.0, Cup, Pint), 1.0); 881 | assert_float_eq!(convert_test(2.0, Pint, Quart), 1.0); 882 | assert_float_eq!(convert_test(4.0, Quart, Gallon), 1.0); 883 | assert_float_eq!(convert_test(42.0, Gallon, OilBarrel), 1.0); 884 | 885 | assert_float_eq!(convert_test(1000.0, Milligram, Gram), 1.0); 886 | assert_float_eq!(convert_test(100.0, Gram, Hectogram), 1.0); 887 | assert_float_eq!(convert_test(1000.0, Gram, Kilogram), 1.0); 888 | assert_float_eq!(convert_test(1000.0, Kilogram, MetricTon), 1.0); 889 | assert_float_eq!(convert_test(0.45359237, Kilogram, Pound), 1.0); 890 | assert_float_eq!(convert_test(16.0, Ounce, Pound), 1.0); 891 | assert_float_eq!(convert_test(14.0, Pound, Stone), 1.0); 892 | assert_float_eq!(convert_test(2000.0, Pound, ShortTon), 1.0); 893 | assert_float_eq!(convert_test(2240.0, Pound, LongTon), 1.0); 894 | 895 | assert_float_eq!(convert_test(1000.0, Bit, Kilobit), 1.0); 896 | assert_float_eq!(convert_test(1000.0, Kilobit, Megabit), 1.0); 897 | assert_float_eq!(convert_test(1000.0, Megabit, Gigabit), 1.0); 898 | assert_float_eq!(convert_test(1000.0, Gigabit, Terabit), 1.0); 899 | assert_float_eq!(convert_test(1000.0, Terabit, Petabit), 1.0); 900 | assert_float_eq!(convert_test(1000.0, Petabit, Exabit), 1.0); 901 | assert_float_eq!(convert_test(1000.0, Exabit, Zettabit), 1.0); 902 | assert_float_eq!(convert_test(1000.0, Zettabit, Yottabit), 1.0); 903 | assert_float_eq!(convert_test(1024.0, Bit, Kibibit), 1.0); 904 | assert_float_eq!(convert_test(1024.0, Kibibit, Mebibit), 1.0); 905 | assert_float_eq!(convert_test(1024.0, Mebibit, Gibibit), 1.0); 906 | assert_float_eq!(convert_test(1024.0, Gibibit, Tebibit), 1.0); 907 | assert_float_eq!(convert_test(1024.0, Tebibit, Pebibit), 1.0); 908 | assert_float_eq!(convert_test(1024.0, Pebibit, Exbibit), 1.0); 909 | assert_float_eq!(convert_test(1024.0, Exbibit, Zebibit), 1.0); 910 | assert_float_eq!(convert_test(1024.0, Zebibit, Yobibit), 1.0); 911 | assert_float_eq!(convert_test(8.0, Bit, Byte), 1.0); 912 | assert_float_eq!(convert_test(1000.0, Byte, Kilobyte), 1.0); 913 | assert_float_eq!(convert_test(1000.0, Kilobyte, Megabyte), 1.0); 914 | assert_float_eq!(convert_test(1000.0, Megabyte, Gigabyte), 1.0); 915 | assert_float_eq!(convert_test(1000.0, Gigabyte, Terabyte), 1.0); 916 | assert_float_eq!(convert_test(1000.0, Terabyte, Petabyte), 1.0); 917 | assert_float_eq!(convert_test(1000.0, Petabyte, Exabyte), 1.0); 918 | assert_float_eq!(convert_test(1000.0, Exabyte, Zettabyte), 1.0); 919 | assert_float_eq!(convert_test(1000.0, Zettabyte, Yottabyte), 1.0); 920 | assert_float_eq!(convert_test(1024.0, Kibibyte, Mebibyte), 1.0); 921 | assert_float_eq!(convert_test(1024.0, Mebibyte, Gibibyte), 1.0); 922 | assert_float_eq!(convert_test(1024.0, Gibibyte, Tebibyte), 1.0); 923 | assert_float_eq!(convert_test(1024.0, Tebibyte, Pebibyte), 1.0); 924 | assert_float_eq!(convert_test(1024.0, Pebibyte, Exbibyte), 1.0); 925 | assert_float_eq!(convert_test(1024.0, Exbibyte, Zebibyte), 1.0); 926 | assert_float_eq!(convert_test(1024.0, Zebibyte, Yobibyte), 1.0); 927 | 928 | assert_float_eq!(convert_test(1000.0, BitsPerSecond, KilobitsPerSecond), 1.0); 929 | assert_float_eq!(convert_test(1000.0, KilobitsPerSecond, MegabitsPerSecond), 1.0); 930 | assert_float_eq!(convert_test(1000.0, MegabitsPerSecond, GigabitsPerSecond), 1.0); 931 | assert_float_eq!(convert_test(1000.0, GigabitsPerSecond, TerabitsPerSecond), 1.0); 932 | assert_float_eq!(convert_test(1000.0, TerabitsPerSecond, PetabitsPerSecond), 1.0); 933 | assert_float_eq!(convert_test(1000.0, PetabitsPerSecond, ExabitsPerSecond), 1.0); 934 | assert_float_eq!(convert_test(1000.0, ExabitsPerSecond, ZettabitsPerSecond), 1.0); 935 | assert_float_eq!(convert_test(1000.0, ZettabitsPerSecond, YottabitsPerSecond), 1.0); 936 | assert_float_eq!(convert_test(1024.0, BitsPerSecond, KibibitsPerSecond), 1.0); 937 | assert_float_eq!(convert_test(1024.0, KibibitsPerSecond, MebibitsPerSecond), 1.0); 938 | assert_float_eq!(convert_test(1024.0, MebibitsPerSecond, GibibitsPerSecond), 1.0); 939 | assert_float_eq!(convert_test(1024.0, GibibitsPerSecond, TebibitsPerSecond), 1.0); 940 | assert_float_eq!(convert_test(1024.0, TebibitsPerSecond, PebibitsPerSecond), 1.0); 941 | assert_float_eq!(convert_test(1024.0, PebibitsPerSecond, ExbibitsPerSecond), 1.0); 942 | assert_float_eq!(convert_test(1024.0, ExbibitsPerSecond, ZebibitsPerSecond), 1.0); 943 | assert_float_eq!(convert_test(1024.0, ZebibitsPerSecond, YobibitsPerSecond), 1.0); 944 | assert_float_eq!(convert_test(8.0, BitsPerSecond, BytesPerSecond), 1.0); 945 | assert_float_eq!(convert_test(1000.0, BytesPerSecond, KilobytesPerSecond), 1.0); 946 | assert_float_eq!(convert_test(1000.0, KilobytesPerSecond, MegabytesPerSecond), 1.0); 947 | assert_float_eq!(convert_test(1000.0, MegabytesPerSecond, GigabytesPerSecond), 1.0); 948 | assert_float_eq!(convert_test(1000.0, GigabytesPerSecond, TerabytesPerSecond), 1.0); 949 | assert_float_eq!(convert_test(1000.0, TerabytesPerSecond, PetabytesPerSecond), 1.0); 950 | assert_float_eq!(convert_test(1000.0, PetabytesPerSecond, ExabytesPerSecond), 1.0); 951 | assert_float_eq!(convert_test(1000.0, ExabytesPerSecond, ZettabytesPerSecond), 1.0); 952 | assert_float_eq!(convert_test(1000.0, ZettabytesPerSecond, YottabytesPerSecond), 1.0); 953 | assert_float_eq!(convert_test(1024.0, KibibytesPerSecond, MebibytesPerSecond), 1.0); 954 | assert_float_eq!(convert_test(1024.0, MebibytesPerSecond, GibibytesPerSecond), 1.0); 955 | assert_float_eq!(convert_test(1024.0, GibibytesPerSecond, TebibytesPerSecond), 1.0); 956 | assert_float_eq!(convert_test(1024.0, TebibytesPerSecond, PebibytesPerSecond), 1.0); 957 | assert_float_eq!(convert_test(1024.0, PebibytesPerSecond, ExbibytesPerSecond), 1.0); 958 | assert_float_eq!(convert_test(1024.0, ExbibytesPerSecond, ZebibytesPerSecond), 1.0); 959 | assert_float_eq!(convert_test(1024.0, ZebibytesPerSecond, YobibytesPerSecond), 1.0); 960 | 961 | assert_float_eq!(convert_test(1000.0, Millijoule, Joule), 1.0); 962 | assert_float_eq!(convert_test(1000.0, Joule, Kilojoule), 1.0); 963 | assert_float_eq!(convert_test(1.0, NewtonMeter, Joule), 1.0); 964 | assert_float_eq!(convert_test(1000.0, Kilojoule, Megajoule), 1.0); 965 | assert_float_eq!(convert_test(1000.0, Megajoule, Gigajoule), 1.0); 966 | assert_float_eq!(convert_test(1000.0, Gigajoule, Terajoule), 1.0); 967 | assert_float_eq!(convert_test(4.1868, Joule, Calorie), 1.0); 968 | assert_float_eq!(convert_test(1000.0, Calorie, KiloCalorie), 1.0); 969 | assert_float_eq!(convert_test(1055.05585262, Joule, BritishThermalUnit), 1.0); 970 | assert_float_eq!(convert_test(3600.0, Joule, WattHour), 1.0); 971 | assert_float_eq!(convert_test(1000.0, WattHour, KilowattHour), 1.0); 972 | assert_float_eq!(convert_test(1000.0, KilowattHour, MegawattHour), 1.0); 973 | assert_float_eq!(convert_test(1000.0, MegawattHour, GigawattHour), 1.0); 974 | assert_float_eq!(convert_test(1000.0, GigawattHour, TerawattHour), 1.0); 975 | assert_float_eq!(convert_test(1000.0, TerawattHour, PetawattHour), 1.0); 976 | 977 | assert_float_eq!(convert_test(1000.0, Milliwatt, Watt), 1.0); 978 | assert_float_eq!(convert_test(1000.0, Watt, Kilowatt), 1.0); 979 | assert_float_eq!(convert_test(1000.0, Kilowatt, Megawatt), 1.0); 980 | assert_float_eq!(convert_test(1000.0, Megawatt, Gigawatt), 1.0); 981 | assert_float_eq!(convert_test(1000.0, Gigawatt, Terawatt), 1.0); 982 | assert_float_eq!(convert_test(1000.0, Terawatt, Petawatt), 1.0); 983 | assert_float_eq!(convert_test(0.0568690272188, Watt, BritishThermalUnitsPerMinute), 1.0); 984 | assert_float_eq!(convert_test(60.0, BritishThermalUnitsPerMinute, BritishThermalUnitsPerHour), 1.0); 985 | assert_float_eq!(convert_test(745.6998715822702, Watt, Horsepower), 1.0); 986 | assert_float_eq!(convert_test(735.49875, Watt, MetricHorsepower), 1.0); 987 | 988 | assert_float_eq!(convert_test(1000.0, Milliampere, Ampere), 1.0); 989 | assert_float_eq!(convert_test(1000.0, Ampere, Kiloampere), 1.0); 990 | assert_float_eq!(convert_test(10.0, Ampere, Abampere), 1.0); 991 | 992 | assert_float_eq!(convert_test(1000.0, Milliohm, Ohm), 1.0); 993 | assert_float_eq!(convert_test(1000.0, Ohm, Kiloohm), 1.0); 994 | 995 | assert_float_eq!(convert_test(1000.0, Millivolt, Volt), 1.0); 996 | assert_float_eq!(convert_test(1000.0, Volt, Kilovolt), 1.0); 997 | 998 | assert_float_eq!(convert_test(1000.0, Pascal, Kilopascal), 1.0); 999 | assert_float_eq!(convert_test(101325.0, Pascal, Atmosphere), 1.0); 1000 | assert_float_eq!(convert_test(100.0, Pascal, Millibar), 1.0); 1001 | assert_float_eq!(convert_test(1000.0, Millibar, Bar), 1.0); 1002 | assert_float_eq!(convert_test(3386.389, Pascal, InchOfMercury), 1.0); 1003 | assert_float_eq!(convert_test(6894.757293168361, Pascal, PoundsPerSquareInch), 1.0); 1004 | assert_float_eq!(convert_test(162.12, Pascal, Torr), 1.0); 1005 | 1006 | assert_float_eq!(convert_test(1000.0, Hertz, Kilohertz), 1.0); 1007 | assert_float_eq!(convert_test(1000.0, Kilohertz, Megahertz), 1.0); 1008 | assert_float_eq!(convert_test(1000.0, Megahertz, Gigahertz), 1.0); 1009 | assert_float_eq!(convert_test(1000.0, Gigahertz, Terahertz), 1.0); 1010 | assert_float_eq!(convert_test(1000.0, Terahertz, Petahertz), 1.0); 1011 | assert_float_eq!(convert_test(60.0, Hertz, RevolutionsPerMinute), 1.0); 1012 | 1013 | assert_float_eq!(convert_test(3.6, KilometersPerHour, MetersPerSecond), 1.0); 1014 | assert_float_eq!(convert_test(0.3048, MetersPerSecond, FeetPerSecond), 1.0); 1015 | assert_float_eq!(convert_test(1.609344, KilometersPerHour, MilesPerHour), 1.0); 1016 | assert_float_eq!(convert_test(1.852, KilometersPerHour, Knot), 1.0); 1017 | 1018 | assert_float_eq!(convert_test(274.15, Kelvin, Celsius), 1.0); 1019 | assert_float_eq!(convert_test(300.0, Kelvin, Fahrenheit), 80.33); 1020 | assert_float_eq!(convert_test(-272.15, Celsius, Kelvin), 1.0); 1021 | assert_float_eq!(convert_test(-15.0, Celsius, Fahrenheit), 5.0); 1022 | assert_float_eq!(convert_test(80.33, Fahrenheit, Kelvin), 300.0); 1023 | assert_float_eq!(convert_test(5.0, Fahrenheit, Celsius), -15.0); 1024 | } 1025 | } 1026 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | .netlify 7 | .wrangler 8 | /.svelte-kit 9 | /build 10 | 11 | # OS 12 | .DS_Store 13 | Thumbs.db 14 | 15 | # Env 16 | .env 17 | .env.* 18 | !.env.example 19 | !.env.test 20 | 21 | # Vite 22 | vite.config.js.timestamp-* 23 | vite.config.ts.timestamp-* 24 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # sv 2 | 3 | Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```bash 10 | # create a new project in the current directory 11 | npx sv create 12 | 13 | # create a new project in my-app 14 | npx sv create my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```bash 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```bash 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "scripts": { 5 | "build-wasm": "npx wasm-pack build --target bundler", 6 | "dev": "npm run build-wasm && vite dev", 7 | "build": "npm run build-wasm && vite build", 8 | "preview": "vite preview", 9 | "prepare": "svelte-kit sync || echo ''", 10 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 11 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" 12 | }, 13 | "devDependencies": { 14 | "@sveltejs/adapter-vercel": "^5.7.2", 15 | "@sveltejs/kit": "^2.21.1", 16 | "@sveltejs/vite-plugin-svelte": "^5.0.3", 17 | "@tailwindcss/vite": "^4.1.8", 18 | "cpc": "file:../pkg", 19 | "svelte": "^5.33.13", 20 | "svelte-check": "^4.2.1", 21 | "tailwindcss": "^4.1.8", 22 | "typescript": "^5.8.3", 23 | "vercel": "^42.3.0", 24 | "vite": "^6.3.5", 25 | "vite-plugin-top-level-await": "^1.5.0", 26 | "vite-plugin-wasm": "^3.4.1", 27 | "wasm-pack": "^0.13.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /web/src/app.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss'; 2 | -------------------------------------------------------------------------------- /web/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /web/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | %sveltekit.head% 15 | 16 | 17 |
%sveltekit.body%
18 | 19 | 20 | -------------------------------------------------------------------------------- /web/src/lib/helpers.ts: -------------------------------------------------------------------------------- 1 | type ShortcutOptions = { 2 | shift?: boolean 3 | alt?: boolean 4 | cmd_or_ctrl?: boolean 5 | } 6 | const is_mac = navigator.userAgent.indexOf('Mac') !== -1 7 | 8 | function check_modifiers(e: KeyboardEvent | MouseEvent, options: ShortcutOptions) { 9 | const target = { 10 | shift: options.shift || false, 11 | alt: options.alt || false, 12 | ctrl: (!is_mac && options.cmd_or_ctrl) || false, 13 | meta: (is_mac && options.cmd_or_ctrl) || false, 14 | } 15 | 16 | const pressed = { 17 | shift: !!e.shiftKey, 18 | alt: !!e.altKey, 19 | ctrl: !!e.ctrlKey, 20 | meta: !!e.metaKey, 21 | } 22 | 23 | const ignore_ctrl = is_mac && e instanceof MouseEvent 24 | 25 | return ( 26 | pressed.shift === target.shift && 27 | pressed.alt === target.alt && 28 | (pressed.ctrl === target.ctrl || ignore_ctrl) && 29 | pressed.meta === target.meta 30 | ) 31 | } 32 | 33 | export function check_shortcut(e: KeyboardEvent, key: string, options: ShortcutOptions = {}) { 34 | if (e.key.toUpperCase() !== key.toUpperCase()) return false 35 | return check_modifiers(e, options) 36 | } 37 | export function check_mouse_shortcut(e: MouseEvent, options: ShortcutOptions = {}) { 38 | return check_modifiers(e, options) 39 | } 40 | -------------------------------------------------------------------------------- /web/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /web/src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; 2 | -------------------------------------------------------------------------------- /web/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 | 28 | cpc 29 | 33 | 34 | 35 |
36 | 61 | { 66 | if (check_shortcut(e, "Enter")) { 67 | const input = e.currentTarget.value; 68 | let out; 69 | try { 70 | out = wasm_eval(e.currentTarget.value); 71 | } catch (e) { 72 | out = ""; 73 | } 74 | console.log(out); 75 | saved_queries.unshift({ 76 | id: saved_queries.length, 77 | in: input, 78 | out, 79 | }); 80 | e.currentTarget.value = ""; 81 | output = ""; 82 | } 83 | }} 84 | placeholder="10km/h * 1 decade in light seconds" 85 | /> 86 |
87 |
88 | {output} 89 |
90 | {#each saved_queries as query (query.id)} 91 |
96 |

{query.in}

97 |

{query.out}

98 |
99 | {/each} 100 |
101 |
102 | 103 | 112 | -------------------------------------------------------------------------------- /web/static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/probablykasper/cpc/9b49d8c41882792cb97d6faba646e89eff0d7549/web/static/apple-touch-icon.png -------------------------------------------------------------------------------- /web/static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/probablykasper/cpc/9b49d8c41882792cb97d6faba646e89eff0d7549/web/static/favicon-96x96.png -------------------------------------------------------------------------------- /web/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/probablykasper/cpc/9b49d8c41882792cb97d6faba646e89eff0d7549/web/static/favicon.ico -------------------------------------------------------------------------------- /web/static/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/static/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cpc", 3 | "short_name": "cpc", 4 | "icons": [ 5 | { 6 | "src": "/web-app-manifest-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png", 9 | "purpose": "maskable" 10 | }, 11 | { 12 | "src": "/web-app-manifest-512x512.png", 13 | "sizes": "512x512", 14 | "type": "image/png", 15 | "purpose": "maskable" 16 | } 17 | ], 18 | "theme_color": "#faff00", 19 | "background_color": "#000000", 20 | "display": "standalone" 21 | } -------------------------------------------------------------------------------- /web/static/web-app-manifest-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/probablykasper/cpc/9b49d8c41882792cb97d6faba646e89eff0d7549/web/static/web-app-manifest-192x192.png -------------------------------------------------------------------------------- /web/static/web-app-manifest-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/probablykasper/cpc/9b49d8c41882792cb97d6faba646e89eff0d7549/web/static/web-app-manifest-512x512.png -------------------------------------------------------------------------------- /web/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-vercel'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | const config = { 5 | preprocess: vitePreprocess(), 6 | kit: { adapter: adapter() } 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 16 | // 17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 18 | // from the referenced tsconfig.json - TypeScript does not merge them in 19 | } 20 | -------------------------------------------------------------------------------- /web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import tailwindcss from '@tailwindcss/vite'; 2 | import { sveltekit } from '@sveltejs/kit/vite'; 3 | import { defineConfig } from 'vite'; 4 | import wasm from 'vite-plugin-wasm'; 5 | import top_level_await from 'vite-plugin-top-level-await'; 6 | 7 | export default defineConfig({ 8 | plugins: [tailwindcss(), sveltekit(), wasm(), top_level_await()], 9 | server: { 10 | fs: { 11 | allow: ['../pkg'] 12 | } 13 | } 14 | }); 15 | --------------------------------------------------------------------------------