├── rustfmt.toml
├── .gitignore
├── web
├── src
│ ├── app.css
│ ├── routes
│ │ ├── +layout.ts
│ │ ├── +layout.svelte
│ │ └── +page.svelte
│ ├── app.d.ts
│ ├── app.html
│ └── lib
│ │ └── helpers.ts
├── static
│ ├── favicon.ico
│ ├── favicon-96x96.png
│ ├── apple-touch-icon.png
│ ├── web-app-manifest-192x192.png
│ ├── web-app-manifest-512x512.png
│ ├── site.webmanifest
│ └── favicon.svg
├── svelte.config.js
├── .gitignore
├── vite.config.ts
├── tsconfig.json
├── package.json
└── README.md
├── .vscode
└── settings.json
├── .gitattributes
├── .github
└── workflows
│ ├── test.yml
│ ├── deploy.yml
│ ├── preview.yml
│ └── release.yml
├── Cargo.toml
├── LICENSE
├── src
├── main.rs
├── lib.rs
├── evaluator.rs
├── parser.rs
├── units.rs
└── lookup.rs
├── CHANGELOG.md
├── README.md
└── Cargo.lock
/rustfmt.toml:
--------------------------------------------------------------------------------
1 | hard_tabs=true
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | **/*.rs.bk
3 |
--------------------------------------------------------------------------------
/web/src/app.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 |
--------------------------------------------------------------------------------
/web/src/routes/+layout.ts:
--------------------------------------------------------------------------------
1 | export const prerender = true;
2 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": false
3 | }
4 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/web/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/probablykasper/cpc/HEAD/web/static/favicon.ico
--------------------------------------------------------------------------------
/web/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/web/static/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/probablykasper/cpc/HEAD/web/static/favicon-96x96.png
--------------------------------------------------------------------------------
/web/static/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/probablykasper/cpc/HEAD/web/static/apple-touch-icon.png
--------------------------------------------------------------------------------
/web/static/web-app-manifest-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/probablykasper/cpc/HEAD/web/static/web-app-manifest-192x192.png
--------------------------------------------------------------------------------
/web/static/web-app-manifest-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/probablykasper/cpc/HEAD/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/.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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/web/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 | cpc
31 |
35 |
36 |
37 |
38 |
63 |
64 | {
69 | const input_el = e.currentTarget;
70 | const input = e.currentTarget.value;
71 | if (check_shortcut(e, "Enter")) {
72 | const out = wasm_eval(input)
73 | console.log(out);
74 | saved_queries.unshift({
75 | id: saved_queries.length,
76 | in: input,
77 | out,
78 | });
79 | input_el.value = "";
80 | output = "";
81 | }
82 | }}
83 | placeholder="10km/h * 1 decade in light seconds"
84 | autofocus
85 | />
86 |
87 |
88 | {output}x
89 |
90 | {#each saved_queries as query (query.id)}
91 |
96 |
{query.in}
97 |
{query.out}
98 |
99 | {/each}
100 |
101 |
102 |
103 |
112 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | [](https://crates.io/crates/cpc)
10 | [](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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/web/static/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------